[
  {
    "path": ".gitignore",
    "content": "### Node ###\n# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directory\n# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-\nnode_modules\n\n\n### Intellij ###\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm\n\n*.iml\n\n## Directory-based project format:\n.idea/\n# if you remove the above rule, at least ignore the following:\n\n# User-specific stuff:\n# .idea/workspace.xml\n# .idea/tasks.xml\n# .idea/dictionaries\n\n# Sensitive or high-churn files:\n# .idea/dataSources.ids\n# .idea/dataSources.xml\n# .idea/sqlDataSources.xml\n# .idea/dynamic.xml\n# .idea/uiDesigner.xml\n\n# Gradle:\n# .idea/gradle.xml\n# .idea/libraries\n\n# Mongo Explorer plugin:\n# .idea/mongoSettings.xml\n\n## File-based project format:\n*.ipr\n*.iws\n\n## Plugin-specific files:\n\n# IntelliJ\nout/\n\n# mpeltonen/sbt-idea plugin\n.idea_modules/\n\n# JIRA plugin\natlassian-ide-plugin.xml\n\n# Crashlytics plugin (for Android Studio and IntelliJ)\ncom_crashlytics_export_strings.xml\ncrashlytics.properties\ncrashlytics-build.properties\n\n\n### SublimeText ###\n# cache files for sublime text\n*.tmlanguage.cache\n*.tmPreferences.cache\n*.stTheme.cache\n\n# workspace files are user-specific\n*.sublime-workspace\n\n# project files should be checked into the repository, unless a significant\n# proportion of contributors will probably not be using SublimeText\n# *.sublime-project\n\n# sftp configuration file\nsftp-config.json\n"
  },
  {
    "path": "Procfile",
    "content": "worker: node worker.js\nweb: npm start\nclock: node clock.js\n"
  },
  {
    "path": "README.md",
    "content": "champion.gg\n========\nWEBSITE: http://champion.gg\n\nA MEAN project (with a dash of angular).\nIn 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)\n\nTo 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. \nThe commands to enter are listed below.\n\n# Getting set up\n\nClone champion.gg:\n```sh\ngit clone https://github.com/joel1st/championweb.git\n```\n\nInstall dependencies from project directory: \n```sh\nnpm install\n```\n\nRestore database from project directory\n```sh\nmongorestore --db championgg --collection webchampionpages --drop db/championgg/webchampionpages.bson\nmongorestore --db championgg --collection webchampionroles --drop db/championgg/webchampionroles.bson\nmongorestore --db championgg --collection webmatchuppages --drop db/championgg/webmatchuppages.bson\nmongorestore --db championgg --collection weboverallroledatas --drop db/championgg/weboverallroledatas.bson\nmongorestore --db championgg --collection weboverallstats --drop db/championgg/weboverallstats.bson\nmongorestore --db championgg --collection webhomepagesummaries --drop db/championgg/webhomepagesummaries.bson\nmongorestore --db championgg --collection webstatisticspages --drop db/championgg/webstatisticspages.bson\n```\n\nStart Champion.gg\n```sh\nnpm start\n#if you have another web server running on port 80 you can set the port as such\nPORT=8888 npm start\n```\nYou can now access champion.gg on http://localhost/ or if you set a port number http://localhost:8888/\n\n# Development \n\nIn 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).\n```sh\ngrunt watch\n```\n\nTo get assets ready for production:\n```sh\ngrunt production\n```\n"
  },
  {
    "path": "api_data/champions.json",
    "content": "{\"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\"}}"
  },
  {
    "path": "api_data/dd_patch.json",
    "content": "{\"ddPatch\":\"7.11.1\"}"
  },
  {
    "path": "api_data/index.js",
    "content": "var items = require('./items');\nvar masteries = require('./masteries.json');\nvar runes = require('./runes');\nvar skills = require('./skills');\nvar summoners = require('./summoners');\n\nmodule.exports = {\n\titems: items,\n\tmasteries: masteries,\n\trunes: runes,\n\tskills: skills,\n\tsummoners: summoners\n};"
  },
  {
    "path": "api_data/items.json",
    "content": "{\"1001\":{\"name\":\"Boots of Speed\",\"description\":\"<groupLimit>Limited to 1.</groupLimit><br><br><unique>UNIQUE Passive - Enhanced Movement:</unique> +25 Movement Speed\",\"colloq\":\";\",\"plaintext\":\"Slightly increases Movement Speed\",\"into\":[\"3006\",\"3047\",\"3020\",\"3158\",\"3111\",\"3117\",\"3009\"],\"image\":{\"full\":\"1001.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":0,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":300,\"purchasable\":true,\"total\":300,\"sell\":210},\"tags\":[\"Boots\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMovementSpeedMod\":25},\"id\":1001},\"1004\":{\"name\":\"Faerie Charm\",\"description\":\"<stats><mana>+25% Base Mana Regen </mana></stats>\",\"colloq\":\";\",\"plaintext\":\"Slightly increases Mana Regen\",\"into\":[\"3028\",\"3070\",\"3073\",\"3114\",\"3098\"],\"image\":{\"full\":\"1004.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":48,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":125,\"purchasable\":true,\"total\":125,\"sell\":88},\"tags\":[\"ManaRegen\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"id\":1004},\"1006\":{\"name\":\"Rejuvenation Bead\",\"description\":\"<stats>+50% Base Health Regen </stats>\",\"colloq\":\";\",\"plaintext\":\"Slightly increases Health Regen\",\"into\":[\"3077\",\"3097\",\"2053\",\"3801\",\"3096\",\"3194\"],\"image\":{\"full\":\"1006.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":96,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":150,\"purchasable\":true,\"total\":150,\"sell\":105},\"tags\":[\"HealthRegen\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"id\":1006},\"1011\":{\"name\":\"Giant's Belt\",\"description\":\"<stats>+380 Health</stats>\",\"colloq\":\";\",\"plaintext\":\"Greatly increases Health\",\"from\":[\"1028\"],\"into\":[\"3083\",\"3143\",\"3084\",\"3022\",\"3742\"],\"image\":{\"full\":\"1011.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":144,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":600,\"purchasable\":true,\"total\":1000,\"sell\":700},\"tags\":[\"Health\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":380},\"depth\":2,\"id\":1011},\"1018\":{\"name\":\"Cloak of Agility\",\"description\":\"<stats>+20% Critical Strike Chance</stats>\",\"colloq\":\";\",\"plaintext\":\"Increases critical strike chance\",\"into\":[\"3031\",\"3185\",\"3508\"],\"image\":{\"full\":\"1018.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":192,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":800,\"purchasable\":true,\"total\":800,\"sell\":560},\"tags\":[\"CriticalStrike\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatCritChanceMod\":0.2},\"id\":1018},\"1026\":{\"name\":\"Blasting Wand\",\"description\":\"<stats>+40 Ability Power</stats>\",\"colloq\":\";\",\"plaintext\":\"Moderately increases Ability Power\",\"into\":[\"3089\",\"3135\",\"3124\",\"3029\",\"3151\",\"3027\",\"3100\",\"3102\",\"3116\"],\"image\":{\"full\":\"1026.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":240,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":850,\"purchasable\":true,\"total\":850,\"sell\":595},\"tags\":[\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMagicDamageMod\":40},\"id\":1026},\"1027\":{\"name\":\"Sapphire Crystal\",\"description\":\"<stats><mana>+250 Mana</mana></stats>\",\"colloq\":\";blue\",\"plaintext\":\"Increases Mana\",\"into\":[\"3057\",\"3070\",\"3010\",\"3024\",\"3073\",\"3802\"],\"image\":{\"full\":\"1027.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":288,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":350,\"purchasable\":true,\"total\":350,\"sell\":245},\"tags\":[\"Mana\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMPPoolMod\":250},\"id\":1027},\"1028\":{\"name\":\"Ruby Crystal\",\"description\":\"<stats>+150 Health</stats>\",\"colloq\":\";red\",\"plaintext\":\"Increases Health\",\"into\":[\"1011\",\"3211\",\"3136\",\"2045\",\"2049\",\"3010\",\"3801\",\"3044\",\"3052\",\"3067\",\"3116\",\"3143\",\"3748\",\"3751\"],\"image\":{\"full\":\"1028.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":336,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":400,\"purchasable\":true,\"total\":400,\"sell\":280},\"tags\":[\"Health\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":150},\"id\":1028},\"1029\":{\"name\":\"Cloth Armor\",\"description\":\"<stats>+15 Armor</stats>\",\"colloq\":\";\",\"plaintext\":\"Slightly increases Armor\",\"into\":[\"3047\",\"1031\",\"3191\",\"3024\",\"3082\",\"3075\",\"2053\",\"3105\",\"3026\"],\"image\":{\"full\":\"1029.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":384,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":300,\"purchasable\":true,\"total\":300,\"sell\":210},\"tags\":[\"Armor\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatArmorMod\":15},\"id\":1029},\"1031\":{\"name\":\"Chain Vest\",\"description\":\"<stats>+40 Armor</stats>\",\"colloq\":\";\",\"plaintext\":\"Greatly increases Armor\",\"from\":[\"1029\"],\"into\":[\"3075\",\"3068\",\"3109\",\"2053\",\"3193\",\"3742\"],\"image\":{\"full\":\"1031.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":432,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":500,\"purchasable\":true,\"total\":800,\"sell\":560},\"tags\":[\"Armor\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatArmorMod\":40},\"depth\":2,\"id\":1031},\"1033\":{\"name\":\"Null-Magic Mantle\",\"description\":\"<stats>+25 Magic Resist</stats>\",\"colloq\":\";\",\"plaintext\":\"Slightly increases Magic Resist\",\"into\":[\"3111\",\"3211\",\"1057\",\"3028\",\"3140\",\"3155\",\"3105\",\"3102\",\"3814\",\"3190\",\"3194\"],\"image\":{\"full\":\"1033.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":0,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":450,\"purchasable\":true,\"total\":450,\"sell\":315},\"tags\":[\"SpellBlock\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatSpellBlockMod\":25},\"id\":1033},\"1036\":{\"name\":\"Long Sword\",\"description\":\"<stats>+10 Attack Damage</stats>\",\"colloq\":\";\",\"plaintext\":\"Slightly increases Attack Damage\",\"into\":[\"3134\",\"3077\",\"3123\",\"1053\",\"3133\",\"3034\",\"3035\",\"3044\",\"3052\",\"3053\",\"3072\",\"3122\",\"3144\",\"3155\",\"3252\"],\"image\":{\"full\":\"1036.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":48,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":350,\"purchasable\":true,\"total\":350,\"sell\":245},\"tags\":[\"Damage\",\"Lane\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":10},\"id\":1036},\"1037\":{\"name\":\"Pickaxe\",\"description\":\"<stats>+25 Attack Damage</stats>\",\"colloq\":\";\",\"plaintext\":\"Moderately increases Attack Damage\",\"into\":[\"3124\",\"3004\",\"3008\",\"3031\",\"3074\",\"3814\",\"3812\",\"3139\",\"3181\"],\"image\":{\"full\":\"1037.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":96,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":875,\"purchasable\":true,\"total\":875,\"sell\":613},\"tags\":[\"Damage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":25},\"id\":1037},\"1038\":{\"name\":\"B. F. Sword\",\"description\":\"<stats>+40 Attack Damage</stats>\",\"colloq\":\";bf\",\"plaintext\":\"Greatly increases Attack Damage\",\"into\":[\"3026\",\"3031\",\"3072\",\"3147\",\"3508\"],\"image\":{\"full\":\"1038.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":144,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":1300,\"purchasable\":true,\"total\":1300,\"sell\":910},\"tags\":[\"Damage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":40},\"id\":1038},\"1039\":{\"name\":\"Hunter's Talisman\",\"description\":\"<stats><mana>+150% Base Mana Regen while in Jungle  </mana></stats><br><br><unique>UNIQUE Passive - Tooth:</unique> Damaging a monster with a spell or attack  steals 25 Health over 5 seconds. Killing monsters grants <font color='#99BBBB'><a href='SpecialJungleExperience'>special bonus experience</a></font>.\",\"colloq\":\";jungle;Jungle\",\"plaintext\":\"Provides damage against Monsters and Mana Regen in the Jungle\",\"into\":[\"3706\",\"3711\",\"3715\"],\"image\":{\"full\":\"1039.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":192,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":350,\"purchasable\":true,\"total\":350,\"sell\":245},\"tags\":[\"LifeSteal\",\"ManaRegen\",\"OnHit\",\"Jungle\"],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"25\",\"Effect2Amount\":\"0\",\"Effect3Amount\":\"0\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"0\",\"Effect6Amount\":\"1.5\",\"Effect7Amount\":\"15\"},\"id\":1039},\"1041\":{\"name\":\"Hunter's Machete\",\"description\":\"<stats>+10% Life Steal vs. Monsters</stats><br><br><unique>UNIQUE Passive - Nail:</unique> Basic attacks deal 25 bonus damage on hit vs. Monsters. Killing monsters grants <font color='#99BBBB'><a href='SpecialJungleExperience'>special bonus experience</a></font>.\",\"colloq\":\";jungle;Jungle\",\"plaintext\":\"Provides damage and life steal versus Monsters\",\"into\":[\"3706\",\"3711\",\"3715\"],\"image\":{\"full\":\"1041.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":240,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":350,\"purchasable\":true,\"total\":350,\"sell\":245},\"tags\":[\"LifeSteal\",\"OnHit\",\"Jungle\"],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"12\",\"Effect2Amount\":\"25\",\"Effect3Amount\":\"0.1\",\"Effect4Amount\":\"2\",\"Effect5Amount\":\"0\",\"Effect6Amount\":\"0.1\",\"Effect7Amount\":\"15\"},\"id\":1041},\"1042\":{\"name\":\"Dagger\",\"description\":\"<stats>+12% Attack Speed</stats>\",\"colloq\":\";\",\"plaintext\":\"Slightly increases Attack Speed\",\"into\":[\"1043\",\"3091\",\"3006\",\"3085\",\"2015\",\"3046\",\"3086\",\"3101\"],\"image\":{\"full\":\"1042.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":288,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":300,\"purchasable\":true,\"total\":300,\"sell\":210},\"tags\":[\"AttackSpeed\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"PercentAttackSpeedMod\":0.12},\"id\":1042},\"1043\":{\"name\":\"Recurve Bow\",\"description\":\"<stats>+25% Attack Speed</stats><br><br><unique>UNIQUE Passive:</unique> Basic attacks deal an additional 15 physical damage on hit.\",\"colloq\":\";\",\"plaintext\":\"Greatly increases Attack Speed\",\"from\":[\"1042\",\"1042\"],\"into\":[\"3091\",\"3153\",\"3124\",\"3675\",\"1416\",\"1418\",\"1419\"],\"image\":{\"full\":\"1043.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":336,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":400,\"purchasable\":true,\"total\":1000,\"sell\":700},\"tags\":[\"AttackSpeed\",\"OnHit\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"PercentAttackSpeedMod\":0.25},\"effect\":{\"Effect1Amount\":\"15\"},\"depth\":2,\"id\":1043},\"1051\":{\"name\":\"Brawler's Gloves\",\"description\":\"<stats>+10% Critical Strike Chance</stats>\",\"colloq\":\";\",\"plaintext\":\"Slightly increases Critical Strike Chance\",\"into\":[\"3086\",\"3122\"],\"image\":{\"full\":\"1051.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":384,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":400,\"purchasable\":true,\"total\":400,\"sell\":280},\"tags\":[\"CriticalStrike\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatCritChanceMod\":0.1},\"id\":1051},\"1052\":{\"name\":\"Amplifying Tome\",\"description\":\"<stats>+20 Ability Power</stats>\",\"colloq\":\";amptome\",\"plaintext\":\"Slightly increases Ability Power\",\"into\":[\"3108\",\"3191\",\"3136\",\"3135\",\"3145\",\"3113\",\"3090\",\"3116\",\"1402\",\"1410\",\"1414\",\"3050\",\"3089\",\"3165\",\"3673\",\"3802\"],\"image\":{\"full\":\"1052.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":432,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":435,\"purchasable\":true,\"total\":435,\"sell\":305},\"tags\":[\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMagicDamageMod\":20},\"id\":1052},\"1053\":{\"name\":\"Vampiric Scepter\",\"description\":\"<stats>+15 Attack Damage<br>+10% Life Steal</stats>\",\"colloq\":\";\",\"plaintext\":\"Basic attacks restore Health\",\"from\":[\"1036\"],\"into\":[\"3072\",\"3074\",\"3812\",\"3139\",\"3144\",\"3181\"],\"image\":{\"full\":\"1053.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":0,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":550,\"purchasable\":true,\"total\":900,\"sell\":630},\"tags\":[\"Damage\",\"LifeSteal\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":15,\"PercentLifeStealMod\":0.1},\"depth\":2,\"id\":1053},\"1054\":{\"name\":\"Doran's Shield\",\"description\":\"<stats>+80 Health</stats><br><br><passive>Passive: </passive>Restores 6 Health every 5 seconds.<br><passive>Passive: </passive>Basic attacks deal an additional 5 physical damage to minions on hit.<br><unique>UNIQUE Passive:</unique> Regain an additional 20 health over 10 seconds after taking damage from an enemy champion.\",\"colloq\":\";dshield\",\"plaintext\":\"Good defensive starting item\",\"image\":{\"full\":\"1054.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":48,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":400,\"purchasable\":true,\"total\":400,\"sell\":160},\"tags\":[\"Health\",\"HealthRegen\",\"Lane\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":80,\"FlatHPRegenMod\":1.2},\"effect\":{\"Effect1Amount\":\"0\",\"Effect2Amount\":\"10\",\"Effect3Amount\":\"5\",\"Effect4Amount\":\"2\"},\"id\":1054},\"1055\":{\"name\":\"Doran's Blade\",\"description\":\"<stats>+8 Attack Damage<br>+80 Health<br>+3% Life Steal</stats>\",\"colloq\":\";dblade\",\"plaintext\":\"Good starting item for attackers\",\"image\":{\"full\":\"1055.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":96,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":450,\"purchasable\":true,\"total\":450,\"sell\":180},\"tags\":[\"Damage\",\"Health\",\"Lane\",\"LifeSteal\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":8,\"FlatHPPoolMod\":80,\"PercentLifeStealMod\":0.03},\"effect\":{\"Effect1Amount\":\"10\"},\"id\":1055},\"1056\":{\"name\":\"Doran's Ring\",\"description\":\"<stats>+60 Health<br>+15 Ability Power<br><mana>+50% Base Mana Regen </mana></stats><br><br><mana><passive>Passive:</passive> Restores 4 Mana upon killing a unit.</mana>\",\"colloq\":\";dring\",\"plaintext\":\"Good starting item for casters\",\"image\":{\"full\":\"1056.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":144,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":400,\"purchasable\":true,\"total\":400,\"sell\":160},\"tags\":[\"Health\",\"Lane\",\"ManaRegen\",\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":60,\"FlatMagicDamageMod\":15},\"effect\":{\"Effect1Amount\":\"4\"},\"id\":1056},\"1057\":{\"name\":\"Negatron Cloak\",\"description\":\"<stats>+40 Magic Resist</stats>\",\"colloq\":\";\",\"plaintext\":\"Moderately increases Magic Resist\",\"from\":[\"1033\"],\"into\":[\"3170\",\"3091\",\"3512\",\"3001\",\"3193\"],\"image\":{\"full\":\"1057.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":192,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":270,\"purchasable\":true,\"total\":720,\"sell\":504},\"tags\":[\"SpellBlock\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatSpellBlockMod\":40},\"depth\":2,\"id\":1057},\"1058\":{\"name\":\"Needlessly Large Rod\",\"description\":\"<stats>+60 Ability Power</stats>\",\"colloq\":\";nlr\",\"plaintext\":\"Greatly increases Ability Power\",\"into\":[\"3089\",\"3090\",\"3003\",\"3007\",\"3285\"],\"image\":{\"full\":\"1058.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":240,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":1250,\"purchasable\":true,\"total\":1250,\"sell\":875},\"tags\":[\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMagicDamageMod\":60},\"id\":1058},\"1082\":{\"name\":\"The Dark Seal\",\"description\":\"<stats>+15 Ability Power<br>+25% Increased Healing from Potions<br><mana>+100 Mana</mana></stats><br><br><unique>UNIQUE Passive - Dread:</unique> Grants +3 Ability Power per Glory.  <br><unique>UNIQUE Passive - Do or Die:</unique> Grants 2 Glory for a champion kill or 1 Glory for an assist, up to 10 Glory total. Lose 4 Glory on death.\",\"colloq\":\";Noxian\",\"plaintext\":\"Provides Ability Power and Mana.  Increases in power as you kill enemies.\",\"into\":[\"3041\"],\"image\":{\"full\":\"1082.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":288,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":350,\"purchasable\":true,\"total\":350,\"sell\":245},\"tags\":[\"HealthRegen\",\"SpellDamage\",\"Mana\",\"Lane\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatMPPoolMod\":100,\"FlatMagicDamageMod\":15},\"effect\":{\"Effect1Amount\":\"0.25\",\"Effect2Amount\":\"2\",\"Effect3Amount\":\"1\",\"Effect4Amount\":\"10\",\"Effect5Amount\":\"3\",\"Effect6Amount\":\"4\"},\"id\":1082},\"1083\":{\"name\":\"Cull\",\"description\":\"<stats>+7 Attack Damage<br>+3 Life on Hit</stats><br><br><unique>UNIQUE Passive:</unique> Killing a lane minion grants 1 additional Gold. Killing 100 lane minions grants an additional 350 bonus gold immediately and disables this passive.\",\"colloq\":\";dblade\",\"plaintext\":\"Provides damage and Life Steal on hit - Killing minions grant bonus Gold\",\"image\":{\"full\":\"1083.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":336,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":450,\"purchasable\":true,\"total\":450,\"sell\":180},\"tags\":[\"Damage\",\"LifeSteal\",\"Lane\"],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":7},\"effect\":{\"Effect1Amount\":\"3\",\"Effect2Amount\":\"1\",\"Effect3Amount\":\"100\",\"Effect4Amount\":\"350\"},\"id\":1083},\"1400\":{\"name\":\"Enchantment: Warrior\",\"description\":\"<stats>+60 Attack Damage<br>+10% Cooldown Reduction</stats>\",\"colloq\":\"\",\"plaintext\":\"Grants Attack Damage and Cooldown Reduction\",\"from\":[\"3133\",\"3706\"],\"hideFromAll\":true,\"image\":{\"full\":\"1400.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":384,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":525,\"purchasable\":true,\"total\":2625,\"sell\":1838},\"tags\":[],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":60},\"effect\":{\"Effect1Amount\":\"30\",\"Effect2Amount\":\"25\",\"Effect3Amount\":\"1.8\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"30\",\"Effect6Amount\":\"-0.2\",\"Effect7Amount\":\"2\",\"Effect8Amount\":\"3\",\"Effect9Amount\":\"0.1\"},\"depth\":3,\"id\":1400},\"1401\":{\"name\":\"Enchantment: Cinderhulk\",\"description\":\"<stats>+400 Health<br>+15% Bonus Health</stats><br><br><unique>UNIQUE Passive - Immolate:</unique> Deals 11 (+1 per champion level) magic damage a second to nearby enemies while in combat. Deals 200% bonus damage to minions and monsters. \",\"colloq\":\"\",\"plaintext\":\"Grants Health and Immolate Aura\",\"from\":[\"3751\",\"3706\"],\"hideFromAll\":true,\"image\":{\"full\":\"1401.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":432,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":525,\"purchasable\":true,\"total\":2625,\"sell\":1838},\"tags\":[],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":400},\"effect\":{\"Effect1Amount\":\"30\",\"Effect2Amount\":\"25\",\"Effect3Amount\":\"1.8\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"30\",\"Effect6Amount\":\"-0.2\",\"Effect7Amount\":\"2\",\"Effect8Amount\":\"3\",\"Effect9Amount\":\"0.1\"},\"depth\":3,\"id\":1401},\"1402\":{\"name\":\"Enchantment: Runic Echoes\",\"description\":\"<stats>+60 Ability Power<br>+7% Movement Speed</stats><br><br><unique>UNIQUE Passive - Echo:</unique> Gain charges upon moving or casting. At 100 charges, the next damaging spell hit expends all charges to deal 60 (+10% of Ability Power) bonus magic damage to up to 4 targets on hit.<br><br>This effect deals 250% damage to Large Monsters. Hitting a Large Monster with this effect will restore 18% of your missing Mana.\",\"colloq\":\"\",\"plaintext\":\"Grants Ability Power and periodically empowers your Spells\",\"from\":[\"3113\",\"1052\",\"3706\"],\"hideFromAll\":true,\"image\":{\"full\":\"1402.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":0,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":340,\"purchasable\":true,\"total\":2625,\"sell\":1838},\"tags\":[],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"PercentMovementSpeedMod\":0.07,\"FlatMagicDamageMod\":60},\"effect\":{\"Effect1Amount\":\"30\",\"Effect2Amount\":\"25\",\"Effect3Amount\":\"1.8\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"30\",\"Effect6Amount\":\"-0.2\",\"Effect7Amount\":\"2\",\"Effect8Amount\":\"3\",\"Effect9Amount\":\"0.1\"},\"depth\":3,\"id\":1402},\"1408\":{\"name\":\"Enchantment: Warrior\",\"description\":\"<stats>+60 Attack Damage<br>+10% Cooldown Reduction</stats>\",\"colloq\":\"\",\"plaintext\":\"Grants Attack Damage and Cooldown Reduction\",\"from\":[\"3133\",\"3711\"],\"hideFromAll\":true,\"image\":{\"full\":\"1408.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":48,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":525,\"purchasable\":true,\"total\":2625,\"sell\":1838},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":60},\"effect\":{\"Effect1Amount\":\"30\",\"Effect2Amount\":\"25\",\"Effect3Amount\":\"1.8\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"30\",\"Effect6Amount\":\"3\",\"Effect7Amount\":\"20\",\"Effect8Amount\":\"30\",\"Effect9Amount\":\"0.1\",\"Effect10Amount\":\"150\"},\"depth\":3,\"id\":1408},\"1409\":{\"name\":\"Enchantment: Cinderhulk\",\"description\":\"<stats>+400 Health<br>+15% Bonus Health</stats><br><br><unique>UNIQUE Passive - Immolate:</unique> Deals 11 (+1 per champion level) magic damage a second to nearby enemies while in combat. Deals 200% bonus damage to minions and monsters. \",\"colloq\":\"\",\"plaintext\":\"Grants Health and Immolate Aura\",\"from\":[\"3751\",\"3711\"],\"hideFromAll\":true,\"image\":{\"full\":\"1409.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":96,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":525,\"purchasable\":true,\"total\":2625,\"sell\":1838},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":400},\"effect\":{\"Effect1Amount\":\"30\",\"Effect2Amount\":\"25\",\"Effect3Amount\":\"1.8\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"30\",\"Effect6Amount\":\"3\",\"Effect7Amount\":\"20\",\"Effect8Amount\":\"30\",\"Effect9Amount\":\"0.1\",\"Effect10Amount\":\"150\"},\"depth\":3,\"id\":1409},\"1410\":{\"name\":\"Enchantment: Runic Echoes\",\"description\":\"<stats>+60 Ability Power<br>+7% Movement Speed</stats><br><br><unique>UNIQUE Passive - Echo:</unique> Gain charges upon moving or casting. At 100 charges, the next damaging spell hit expends all charges to deal 60 (+10% of Ability Power) bonus magic damage to up to 4 targets on hit.<br><br>This effect deals 250% damage to Large Monsters. Hitting a Large Monster with this effect will restore 18% of your missing Mana.\",\"colloq\":\"\",\"plaintext\":\"Grants Ability Power and periodically empowers your Spells\",\"from\":[\"3113\",\"1052\",\"3711\"],\"hideFromAll\":true,\"image\":{\"full\":\"1410.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":144,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":340,\"purchasable\":true,\"total\":2625,\"sell\":1838},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"PercentMovementSpeedMod\":0.07,\"FlatMagicDamageMod\":60},\"effect\":{\"Effect1Amount\":\"30\",\"Effect2Amount\":\"25\",\"Effect3Amount\":\"1.8\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"30\",\"Effect6Amount\":\"3\",\"Effect7Amount\":\"20\",\"Effect8Amount\":\"30\",\"Effect9Amount\":\"0.1\",\"Effect10Amount\":\"150\"},\"depth\":3,\"id\":1410},\"1412\":{\"name\":\"Enchantment: Warrior\",\"description\":\"<stats>+60 Attack Damage<br>+10% Cooldown Reduction</stats>\",\"colloq\":\"\",\"plaintext\":\"Grants Attack Damage and Cooldown Reduction\",\"from\":[\"3133\",\"3715\"],\"hideFromAll\":true,\"image\":{\"full\":\"1412.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":192,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":525,\"purchasable\":true,\"total\":2625,\"sell\":1838},\"tags\":[],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":60},\"effect\":{\"Effect1Amount\":\"30\",\"Effect2Amount\":\"25\",\"Effect3Amount\":\"1.8\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"30\",\"Effect6Amount\":\"3\",\"Effect7Amount\":\"20\",\"Effect8Amount\":\"18\",\"Effect9Amount\":\"0.1\",\"Effect10Amount\":\"4\"},\"depth\":3,\"id\":1412},\"1413\":{\"name\":\"Enchantment: Cinderhulk\",\"description\":\"<stats>+400 Health<br>+15% Bonus Health</stats><br><br><unique>UNIQUE Passive - Immolate:</unique> Deals 11 (+1 per champion level) magic damage a second to nearby enemies while in combat. Deals 200% bonus damage to minions and monsters. \",\"colloq\":\"\",\"plaintext\":\"Grants Health and Immolate Aura\",\"from\":[\"3751\",\"3715\"],\"hideFromAll\":true,\"image\":{\"full\":\"1413.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":240,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":525,\"purchasable\":true,\"total\":2625,\"sell\":1838},\"tags\":[],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":400},\"effect\":{\"Effect1Amount\":\"30\",\"Effect2Amount\":\"25\",\"Effect3Amount\":\"1.8\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"30\",\"Effect6Amount\":\"3\",\"Effect7Amount\":\"20\",\"Effect8Amount\":\"18\",\"Effect9Amount\":\"0.1\",\"Effect10Amount\":\"4\"},\"depth\":3,\"id\":1413},\"1414\":{\"name\":\"Enchantment: Runic Echoes\",\"description\":\"<stats>+60 Ability Power<br>+7% Movement Speed</stats><br><br><unique>UNIQUE Passive - Echo:</unique> Gain charges upon moving or casting. At 100 charges, the next damaging spell hit expends all charges to deal 60 (+10% of Ability Power) bonus magic damage to up to 4 targets on hit.<br><br>This effect deals 250% damage to Large Monsters. Hitting a Large Monster with this effect will restore 18% of your missing Mana.\",\"colloq\":\"\",\"plaintext\":\"Grants Ability Power and periodically empowers your Spells\",\"from\":[\"3113\",\"1052\",\"3715\"],\"hideFromAll\":true,\"image\":{\"full\":\"1414.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":288,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":340,\"purchasable\":true,\"total\":2625,\"sell\":1838},\"tags\":[],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"PercentMovementSpeedMod\":0.07,\"FlatMagicDamageMod\":60},\"effect\":{\"Effect1Amount\":\"30\",\"Effect2Amount\":\"25\",\"Effect3Amount\":\"1.8\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"30\",\"Effect6Amount\":\"3\",\"Effect7Amount\":\"20\",\"Effect8Amount\":\"18\",\"Effect9Amount\":\"0.1\",\"Effect10Amount\":\"4\"},\"depth\":3,\"id\":1414},\"1416\":{\"name\":\"Enchantment: Bloodrazor\",\"description\":\"<stats>+50% Attack Speed</stats><br><br><unique>UNIQUE Passive:</unique> Basic attacks deal 4% of the target's maximum Health in bonus physical damage (max 75 vs. monsters and minions) on hit.\",\"colloq\":\"\",\"plaintext\":\"Increases Attack Speed and deals damage based on the target's Health\",\"from\":[\"1043\",\"3706\"],\"hideFromAll\":true,\"image\":{\"full\":\"1416.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":336,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":625,\"purchasable\":true,\"total\":2625,\"sell\":1838},\"tags\":[],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"PercentAttackSpeedMod\":0.5},\"effect\":{\"Effect1Amount\":\"30\",\"Effect2Amount\":\"25\",\"Effect3Amount\":\"1.8\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"30\",\"Effect6Amount\":\"-0.2\",\"Effect7Amount\":\"2\",\"Effect8Amount\":\"3\",\"Effect9Amount\":\"0.1\"},\"depth\":3,\"id\":1416},\"1418\":{\"name\":\"Enchantment: Bloodrazor\",\"description\":\"<stats>+50% Attack Speed</stats><br><br><unique>UNIQUE Passive:</unique> Basic attacks deal 4% of the target's maximum Health in bonus physical damage (max 75 vs. monsters and minions) on hit.\",\"colloq\":\"\",\"plaintext\":\"Increases Attack Speed and deals damage based on the target's Health\",\"from\":[\"1043\",\"3711\"],\"hideFromAll\":true,\"image\":{\"full\":\"1418.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":384,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":625,\"purchasable\":true,\"total\":2625,\"sell\":1838},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"PercentAttackSpeedMod\":0.5},\"effect\":{\"Effect1Amount\":\"30\",\"Effect2Amount\":\"25\",\"Effect3Amount\":\"1.8\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"30\",\"Effect6Amount\":\"3\",\"Effect7Amount\":\"20\",\"Effect8Amount\":\"30\",\"Effect9Amount\":\"0.1\",\"Effect10Amount\":\"150\"},\"depth\":3,\"id\":1418},\"1419\":{\"name\":\"Enchantment: Bloodrazor\",\"description\":\"<stats>+50% Attack Speed</stats><br><br><unique>UNIQUE Passive:</unique> Basic attacks deal 4% of the target's maximum Health in bonus physical damage (max 75 vs. monsters and minions) on hit.\",\"colloq\":\"\",\"plaintext\":\"Increases Attack Speed and deals damage based on the target's Health\",\"from\":[\"1043\",\"3715\"],\"hideFromAll\":true,\"image\":{\"full\":\"1419.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":432,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":625,\"purchasable\":true,\"total\":2625,\"sell\":1838},\"tags\":[],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"PercentAttackSpeedMod\":0.5},\"effect\":{\"Effect1Amount\":\"30\",\"Effect2Amount\":\"25\",\"Effect3Amount\":\"1.8\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"30\",\"Effect6Amount\":\"3\",\"Effect7Amount\":\"20\",\"Effect8Amount\":\"18\",\"Effect9Amount\":\"0.1\",\"Effect10Amount\":\"4\"},\"depth\":3,\"id\":1419},\"2003\":{\"name\":\"Health Potion\",\"description\":\"<groupLimit>Limited to 5 at one time. Limited to 1 type of Healing Potion.</groupLimit><br><br><consumable>Click to Consume:</consumable> Restores 150 Health over 15 seconds.\",\"colloq\":\";\",\"plaintext\":\"Consume to restore Health over time\",\"stacks\":5,\"consumed\":true,\"image\":{\"full\":\"2003.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":0,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":50,\"purchasable\":true,\"total\":50,\"sell\":20},\"tags\":[\"Consumable\",\"Jungle\",\"Lane\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"150\",\"Effect2Amount\":\"15\"},\"id\":2003},\"2009\":{\"name\":\"Total Biscuit of Rejuvenation\",\"description\":\"<consumable>Click to Consume:</consumable> Restores 80 Health and 50 Mana over 10 seconds.\",\"colloq\":\";\",\"plaintext\":\"\",\"consumed\":true,\"inStore\":false,\"image\":{\"full\":\"2009.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":48,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"id\":2009},\"2010\":{\"name\":\"Total Biscuit of Rejuvenation\",\"description\":\"<consumable>Click to Consume:</consumable> Restores 15 Health and 15 Mana immediately and then 150 Health over 15 seconds.\",\"colloq\":\";\",\"plaintext\":\"\",\"stacks\":5,\"consumed\":true,\"inStore\":false,\"image\":{\"full\":\"2010.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":96,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":50,\"purchasable\":false,\"total\":50,\"sell\":20},\"tags\":[],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"15\",\"Effect2Amount\":\"15\",\"Effect3Amount\":\"150\",\"Effect4Amount\":\"15\"},\"id\":2010},\"2011\":{\"name\":\"Elixir Of Skill\",\"description\":\"<consumable>Click to Consume:</consumable> Grants <font color='#29E3D6'>+1 Skill Point</font>.\",\"colloq\":\";\",\"plaintext\":\"\",\"stacks\":5,\"consumed\":true,\"inStore\":false,\"image\":{\"full\":\"2011.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":144,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[\"Consumable\",\"Lane\",\"Jungle\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"id\":2011},\"2015\":{\"name\":\"Kircheis Shard\",\"description\":\"<stats>+15% Attack Speed</stats><br><br><passive>Passive:</passive> Moving and attacking will make an attack <a href='Energized'>Energized</a>.<br><br><unique>UNIQUE Passive - Energized Strike:</unique> Your Energized attacks deal 50 bonus magic damage on hit.\",\"colloq\":\";\",\"plaintext\":\"Attack speed and a chargable magic hit\",\"from\":[\"1042\"],\"into\":[\"3094\",\"3087\"],\"image\":{\"full\":\"2015.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":192,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":500,\"purchasable\":true,\"total\":800,\"sell\":560},\"tags\":[\"AttackSpeed\",\"OnHit\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"PercentAttackSpeedMod\":0.15},\"effect\":{\"Effect1Amount\":\"50\"},\"depth\":2,\"id\":2015},\"2031\":{\"name\":\"Refillable Potion\",\"description\":\"<groupLimit>Limited to 1 type of Healing Potion.</groupLimit><br><br><active>UNIQUE Active:</active> Consumes a charge to restore 125 Health over 12 seconds. Holds up to 2 charges and refills upon visiting the shop.\",\"colloq\":\";\",\"plaintext\":\"Restores Health over time. Refills at shop.\",\"into\":[\"2032\",\"2033\"],\"image\":{\"full\":\"2031.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":240,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":150,\"purchasable\":true,\"total\":150,\"sell\":60},\"tags\":[\"HealthRegen\",\"Consumable\",\"Active\",\"Lane\",\"Jungle\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"125\",\"Effect2Amount\":\"0\",\"Effect3Amount\":\"12\",\"Effect4Amount\":\"2\"},\"id\":2031},\"2032\":{\"name\":\"Hunter's Potion\",\"description\":\"<groupLimit>Limited to 1 type of Healing Potion.</groupLimit><br><br><active>UNIQUE Active:</active> Consumes a charge to restore 60 Health and 35 Mana over 8 seconds. Holds up to 5 charges and refills upon visiting the shop.<br><br>Killing a Large Monster grants 1 charge.<br><br><rules>(Killing a Large Monster at full charges will automatically consume the newest charge.)</rules>\",\"colloq\":\";\",\"plaintext\":\"Restores Health and Mana over time - Refills at shop and has increased capacity\",\"from\":[\"2031\"],\"image\":{\"full\":\"2032.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":288,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":250,\"purchasable\":true,\"total\":400,\"sell\":160},\"tags\":[\"HealthRegen\",\"ManaRegen\",\"Consumable\",\"Active\",\"Jungle\"],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"60\",\"Effect2Amount\":\"35\",\"Effect3Amount\":\"8\",\"Effect4Amount\":\"5\"},\"depth\":2,\"id\":2032},\"2033\":{\"name\":\"Corrupting Potion\",\"description\":\"<groupLimit>Limited to 1 type of Healing Potion.</groupLimit><br><br><active>UNIQUE Active:</active> Consumes a charge to restore 125 Health and 75 Mana over 12 seconds and grants <font color='#FF8811'><u>Touch of Corruption</u></font> during that time. Holds up to 3 charges that refills upon visiting the shop.<br><br><font color='#FF8811'><u>Touch of Corruption:</u></font> Damaging spells and attacks burn enemy champions for <scaleLevel>15 - 30</scaleLevel> magic damage over 3 seconds. (Half Damage for Area of Effect or Damage over Time spells. Damage increases with champion level.)<br><br><rules>(Corrupting Potion can be used even at full Health and Mana.)</rules>\",\"colloq\":\";\",\"plaintext\":\"Restores Health and Mana over time and boosts combat power - Refills at Shop\",\"from\":[\"2031\"],\"image\":{\"full\":\"2033.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":336,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":350,\"purchasable\":true,\"total\":500,\"sell\":200},\"tags\":[\"Active\",\"Consumable\",\"HealthRegen\",\"Lane\",\"ManaRegen\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"125\",\"Effect2Amount\":\"75\",\"Effect3Amount\":\"12\",\"Effect4Amount\":\"3\",\"Effect5Amount\":\"10\",\"Effect6Amount\":\"0.1\",\"Effect7Amount\":\"15\",\"Effect8Amount\":\"3\",\"Effect9Amount\":\"30\"},\"depth\":2,\"id\":2033},\"2045\":{\"name\":\"Ruby Sightstone\",\"description\":\"<stats>+500 Health</stats><br><br><unique>UNIQUE Passive:</unique> Item Active cooldowns are reduced by 20%.<br><active>UNIQUE Active - Warding:</active> Consumes a charge to place a <font color='#BBFFFF'>Stealth Ward</font> that reveals the surrounding area for 150 seconds. Holds up to 4 charges and refills when visiting the shop.<br><br><rules>(A player may only have 3 <font color='#BBFFFF'>Stealth Wards</font> on the map at one time. Unique Passives with the same name don't stack.)</rules>\",\"colloq\":\";\",\"plaintext\":\"Greatly increases Health and provides Stealth Wards over time\",\"from\":[\"2049\",\"1028\"],\"image\":{\"full\":\"2045.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":384,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":400,\"purchasable\":true,\"total\":1600,\"sell\":640},\"tags\":[\"Active\",\"Health\",\"Vision\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":500},\"effect\":{\"Effect1Amount\":\"-0.2\",\"Effect2Amount\":\"4\",\"Effect3Amount\":\"150\"},\"depth\":3,\"id\":2045},\"2047\":{\"name\":\"Oracle's Extract\",\"description\":\"<consumable>Click to Consume:</consumable> Grants detection of nearby invisible or unseen enemy units for 5 minutes.\",\"colloq\":\";\",\"plaintext\":\"Allows champion to see invisible or unseen enemy units\",\"consumed\":true,\"consumeOnFull\":true,\"image\":{\"full\":\"2047.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":432,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":300,\"purchasable\":true,\"total\":300,\"sell\":120},\"tags\":[\"Consumable\",\"Stealth\",\"Vision\"],\"maps\":{\"8\":true,\"10\":false,\"11\":false,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"id\":2047},\"2049\":{\"name\":\"Sightstone\",\"description\":\"<stats>+150 Health</stats><br><br><active>UNIQUE Active - Warding:</active> Consumes a charge to place a <font color='#BBFFFF'>Stealth Ward</font> that reveals the surrounding area for 150 seconds.  Holds up to 3 charges which refill upon visiting the shop. <br><br><rules>(A player may only have 3 <font color='#BBFFFF'>Stealth Wards</font> on the map at one time. Unique Passives with the same name don't stack.)</rules>\",\"colloq\":\";\",\"plaintext\":\"Increases Health and provides Stealth Wards over time\",\"from\":[\"1028\"],\"into\":[\"2045\",\"2301\",\"2302\",\"2303\"],\"image\":{\"full\":\"2049.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":0,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":400,\"purchasable\":true,\"total\":800,\"sell\":320},\"tags\":[\"Active\",\"Health\",\"Vision\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":150},\"effect\":{\"Effect1Amount\":\"3\",\"Effect2Amount\":\"150\"},\"depth\":2,\"id\":2049},\"2050\":{\"name\":\"Explorer's Ward\",\"description\":\"<consumable>Click to Consume:</consumable> Places an invisible ward that reveals the surrounding area for 60 seconds.\",\"colloq\":\";\",\"plaintext\":\"\",\"inStore\":false,\"image\":{\"full\":\"2050.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":48,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[\"Consumable\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"id\":2050},\"2051\":{\"name\":\"Guardian's Horn\",\"description\":\"<stats>+150 Health</stats><br><br><passive>Passive: </passive>Restores 20 Health every 5 seconds.<br><unique>UNIQUE Passive:</unique> Blocks 12 damage from attacks and spells from champions (25% effectiveness vs. damage over time abilities).<br><br><groupLimit>Limited to 1 Guardian's Item.</groupLimit>\",\"colloq\":\"Golden Arm of Kobe;Golden Bicep of Kobe;Horn; Horn of the ManWolf; ManWolf\",\"plaintext\":\"Good starting item for tanks\",\"image\":{\"full\":\"2051.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":96,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":950,\"purchasable\":true,\"total\":950,\"sell\":380},\"tags\":[\"Health\",\"HealthRegen\",\"Lane\"],\"maps\":{\"8\":false,\"10\":false,\"11\":false,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":150,\"FlatHPRegenMod\":4},\"effect\":{\"Effect1Amount\":\"12\",\"Effect2Amount\":\"0.25\"},\"id\":2051},\"2052\":{\"name\":\"Poro-Snax\",\"description\":\"This savory blend of free-range, grass-fed Avarosan game hens and organic, non-ZMO Freljordian herbs contains the essential nutrients necessary to keep your Poro purring with pleasure.<br><br><i>All proceeds will be donated towards fighting Noxian animal cruelty.</i>\",\"colloq\":\";\",\"plaintext\":\"\",\"stacks\":2,\"consumed\":true,\"inStore\":false,\"image\":{\"full\":\"2052.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":144,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"id\":2052},\"2053\":{\"name\":\"Raptor Cloak\",\"description\":\"<stats>+40 Armor<br>+125% Base Health Regen </stats><br><br><unique>UNIQUE Passive - Point Runner:</unique> Builds up to +20% Movement Speed over 2 seconds while near turrets, fallen turrets and Void Gates.\",\"colloq\":\";\",\"plaintext\":\"Enhances Movement Speed near turrets\",\"from\":[\"1006\",\"1031\"],\"into\":[\"3512\",\"3056\",\"3069\"],\"image\":{\"full\":\"2053.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":192,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":250,\"purchasable\":true,\"total\":1200,\"sell\":840},\"tags\":[\"Armor\",\"HealthRegen\",\"NonbootsMovement\"],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatArmorMod\":40},\"effect\":{\"Effect1Amount\":\"20\",\"Effect2Amount\":\"2\"},\"depth\":3,\"id\":2053},\"2054\":{\"name\":\"Diet Poro-Snax\",\"description\":\"All the flavor of regular Poro-Snax, without the calories! Keeps your Poro happy AND healthy.<br><br><consumable>Click to Consume:</consumable> Gives your Poros a delicious healthy treat.\",\"colloq\":\"\",\"plaintext\":\"\",\"consumed\":true,\"inStore\":false,\"image\":{\"full\":\"2054.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":240,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"id\":2054},\"2055\":{\"name\":\"Control Ward\",\"description\":\"<groupLimit>Can only carry 3 Control Wards in inventory.</groupLimit><br><br><consumable>Click to Consume:</consumable> Places a ward that grants vision of the surrounding area. This device will also reveal invisible traps and reveal / disable wards. Control Wards do not disable other Control Wards. Camouflaged units will also be revealed. <br><br>Limit 1 <font color='#BBFFFF'>Control Ward</font> on the map per player.\",\"colloq\":\"orange;\",\"plaintext\":\"Used to disable wards and invisible traps in an area.\",\"stacks\":3,\"consumed\":true,\"image\":{\"full\":\"2055.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":288,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":75,\"purchasable\":true,\"total\":75,\"sell\":30},\"tags\":[\"Consumable\",\"Lane\",\"Stealth\",\"Vision\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"1\",\"Effect2Amount\":\"3\"},\"id\":2055},\"2138\":{\"name\":\"Elixir of Iron\",\"description\":\"<stats><levelLimit>Level 9 required to purchase.</levelLimit></stats><br><br><consumable>Click to Consume:</consumable> Grants +300 Health, 25% Tenacity, increased champion size, and <font color='#FF8811'><u>Path of Iron</u></font> for 3 minutes.<br><br><font color='#FF8811'><u>Path of Iron:</u></font> Moving leaves a path behind that boosts allied champion's Movement Speed by 15%.<br><br><rules>(Only one Elixir effect may be active at a time.)</rules>\",\"colloq\":\";white\",\"plaintext\":\"Temporarily increases defenses. Leaves a trail for allies to follow.\",\"consumed\":true,\"consumeOnFull\":true,\"image\":{\"full\":\"2138.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":336,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":500,\"purchasable\":true,\"total\":500,\"sell\":200},\"tags\":[\"Health\",\"Consumable\",\"NonbootsMovement\",\"Tenacity\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"300\",\"Effect2Amount\":\"0.25\",\"Effect3Amount\":\"3\",\"Effect4Amount\":\"0.15\",\"Effect5Amount\":\"0.15\",\"Effect6Amount\":\"0\",\"Effect7Amount\":\"0\",\"Effect8Amount\":\"9\"},\"id\":2138},\"2139\":{\"name\":\"Elixir of Sorcery\",\"description\":\"<stats><levelLimit>Level 9 required to purchase.</levelLimit></stats><br><br><consumable>Click to Consume:</consumable> Grants +50 Ability Power, 15 bonus Mana Regen per 5 seconds and <font color='#FF8811'><u>Sorcery</u></font> for 3 minutes. <br><br><font color='#FF8811'><u>Sorcery:</u></font> Damaging a champion or turret deals 25 bonus True Damage. This effect has a 5 second cooldown versus champions but no cooldown versus turrets.<br><br><rules>(Only one Elixir effect may be active at a time.)</rules><br>\",\"colloq\":\";blue\",\"plaintext\":\"Temporarily grants Ability Power and Bonus Damage to champions and turrets.\",\"consumed\":true,\"consumeOnFull\":true,\"image\":{\"full\":\"2139.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":384,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":500,\"purchasable\":true,\"total\":500,\"sell\":200},\"tags\":[\"Consumable\",\"ManaRegen\",\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"50\",\"Effect2Amount\":\"50\",\"Effect3Amount\":\"25\",\"Effect4Amount\":\"3\",\"Effect5Amount\":\"5\",\"Effect6Amount\":\"3\",\"Effect7Amount\":\"0\",\"Effect8Amount\":\"9\"},\"id\":2139},\"2140\":{\"name\":\"Elixir of Wrath\",\"description\":\"<stats><levelLimit>Level 9 required to purchase.</levelLimit></stats><br><br><consumable>Click to Consume:</consumable> Grants +30 Attack Damage and <font color='#FF8811'><u>Bloodlust</u></font> for 3 minutes.<br><br><font color='#FF8811'><u>Bloodlust:</u></font> Dealing physical damage to champions heals for 15% of the damage dealt.<br><br><rules>(Only one Elixir effect may be active at a time.)</rules>\",\"colloq\":\";red\",\"plaintext\":\"Temporarily grants Attack Damage and heals you when dealing physical damage to champions.\",\"consumed\":true,\"consumeOnFull\":true,\"image\":{\"full\":\"2140.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":432,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":500,\"purchasable\":true,\"total\":500,\"sell\":200},\"tags\":[\"Consumable\",\"Damage\",\"LifeSteal\",\"SpellVamp\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"30\",\"Effect2Amount\":\"30\",\"Effect3Amount\":\"0.15\",\"Effect4Amount\":\"3\",\"Effect5Amount\":\"0\",\"Effect6Amount\":\"0\",\"Effect7Amount\":\"0\",\"Effect8Amount\":\"9\"},\"id\":2140},\"2301\":{\"name\":\"Eye of the Watchers\",\"description\":\"<stats>+200 Health<br><mana>+50% Base Mana Regen </mana><br>+35 Ability Power<br>+10% Cooldown Reduction<br>+2 Gold per 10 seconds</stats><br><br><unique>UNIQUE Passive - Tribute:</unique> Damaging spells and attacks against champions or buildings deal 15 additional damage and grant 15 Gold. This can occur up to 3 times every 30 seconds.<br><active>UNIQUE Active - Warding:</active> Consumes a charge to place a <font color='#BBFFFF'>Stealth Ward</font> that reveals the surrounding area for 150 seconds. Holds up to 4 charges which refill upon visiting the shop.<hr><passive>QUEST:</passive> Earn 650 gold using this item.<br><passive>REWARD:</passive> <font color='#CFBF84'>Tribute</font> is upgraded into <font color='#CFBF84'><a href='frostqueenslinequestreward'>Queen's Tribute</a></font>.<br><br><groupLimit>Limited to 1 Gold Income Item.</groupLimit>\",\"colloq\":\";\",\"plaintext\":\"Provides Ability Power and Stealth Wards over time\",\"from\":[\"2049\",\"3098\"],\"image\":{\"full\":\"2301.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":0,\"y\":288,\"w\":48,\"h\":48},\"gold\":{\"base\":550,\"purchasable\":true,\"total\":2200,\"sell\":880},\"tags\":[\"Health\",\"SpellDamage\",\"ManaRegen\",\"Vision\",\"Active\",\"GoldPer\",\"CooldownReduction\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":200,\"FlatMagicDamageMod\":35},\"effect\":{\"Effect1Amount\":\"2\",\"Effect2Amount\":\"15\",\"Effect3Amount\":\"15\",\"Effect4Amount\":\"4\",\"Effect5Amount\":\"30\",\"Effect6Amount\":\"12\",\"Effect7Amount\":\"150\",\"Effect8Amount\":\"3\"},\"depth\":3,\"id\":2301},\"2302\":{\"name\":\"Eye of the Oasis\",\"description\":\"<stats>+200 Health<br>+125% Base Health Regen <br>+10% Cooldown Reduction<br>+2 Gold per 10 seconds</stats><br><br><unique>UNIQUE Passive - Favor: </unique>Enemy minions killed by your allies sometimes drop coins that give either <font color='#D4AF37'>30</font> gold or <font color='#44DDFF'>8%</font> missing mana (minimum 15). Cannon minions always drop coins.<br><active>UNIQUE Active - Warding:</active> Consumes a charge to place a <font color='#BBFFFF'>Stealth Ward</font> that reveals the surrounding area for 150 seconds. Holds up to 4 charges which refill upon visiting the shop<hr><passive>QUEST:</passive> Earn 650 gold using this item.<br><passive>REWARD:</passive> <font color='#CFBF84'>Favor</font> is upgraded to <font color='#CFBF84'><a href='coinlinequestreward'>Emperor's Favor</a></font> and you receive an <font color='#29E3D6'><a href='coinlinequestrewardelixir'>Elixir Of Skill</a></font>.<br><br><groupLimit>Limited to 1 Gold Income Item.</groupLimit>\",\"colloq\":\";\",\"plaintext\":\"Provides Gold, Mana, and Stealth Wards over time\",\"from\":[\"2049\",\"3096\"],\"image\":{\"full\":\"2302.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":48,\"y\":288,\"w\":48,\"h\":48},\"gold\":{\"base\":250,\"purchasable\":true,\"total\":1900,\"sell\":760},\"tags\":[\"Health\",\"HealthRegen\",\"ManaRegen\",\"Vision\",\"Active\",\"GoldPer\",\"CooldownReduction\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":200},\"effect\":{\"Effect1Amount\":\"2\",\"Effect2Amount\":\"15\",\"Effect3Amount\":\"25\",\"Effect4Amount\":\"4\",\"Effect5Amount\":\"0\",\"Effect6Amount\":\"0\",\"Effect7Amount\":\"150\"},\"depth\":3,\"id\":2302},\"2303\":{\"name\":\"Eye of the Equinox\",\"description\":\"<stats>+500 Health<br>+200% Base Health Regen <br>+10% Cooldown Reduction<br>+2 Gold per 10 seconds</stats><br><br><unique>UNIQUE Passive - Spoils of War:</unique> Melee basic attacks execute minions below 320 (+20 per level) Health. Killing a minion heals the owner and the nearest allied champion for 50 Health and grants them kill Gold. These effects require a nearby ally. Recharges every 30 seconds. Max 4 charges.<br><active>UNIQUE Active - Warding:</active> Consumes a charge to place a <font color='#BBFFFF'>Stealth Ward</font> that reveals the surrounding area for 150 seconds. Holds up to 4 charges which refill upon visiting the shop.<hr><passive>QUEST:</passive> Earn 650 gold using this item.<br><passive>REWARD:</passive> <font color='#CFBF84'>Shield Battery</font>, a permanent shield that regenerates slowly outside of combat.<br><br><groupLimit>Limited to 1 Gold Income Item.</groupLimit>\",\"colloq\":\";\",\"plaintext\":\"Provides Health and Stealth Wards over time\",\"from\":[\"2049\",\"3097\"],\"image\":{\"full\":\"2303.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":96,\"y\":288,\"w\":48,\"h\":48},\"gold\":{\"base\":650,\"purchasable\":true,\"total\":2300,\"sell\":920},\"tags\":[\"Health\",\"HealthRegen\",\"Vision\",\"Active\",\"GoldPer\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":500},\"effect\":{\"Effect1Amount\":\"320\",\"Effect2Amount\":\"50\",\"Effect3Amount\":\"30\",\"Effect4Amount\":\"4\",\"Effect5Amount\":\"0\",\"Effect6Amount\":\"0\",\"Effect7Amount\":\"150\",\"Effect8Amount\":\"0\",\"Effect9Amount\":\"2\",\"Effect10Amount\":\"20\"},\"depth\":3,\"id\":2303},\"3001\":{\"name\":\"Abyssal Mask\",\"description\":\"<stats>+300 Health<br>+65 Magic Resist<br>+100% Base Health Regeneration <br>+10% Cooldown Reduction</stats><br><br><aura>UNIQUE Aura:</aura> Nearby enemy champions take 10% more magic damage.\",\"colloq\":\";\",\"plaintext\":\"Nearby enemies take more magic damage\",\"from\":[\"3211\",\"1057\"],\"image\":{\"full\":\"3001.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":144,\"y\":288,\"w\":48,\"h\":48},\"gold\":{\"base\":880,\"purchasable\":true,\"total\":2800,\"sell\":1960},\"tags\":[\"Health\",\"SpellBlock\",\"HealthRegen\",\"Aura\",\"CooldownReduction\",\"MagicPenetration\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":300,\"FlatSpellBlockMod\":65},\"effect\":{\"Effect1Amount\":\"-10\",\"Effect2Amount\":\"-25\",\"Effect3Amount\":\"0.1\"},\"depth\":3,\"id\":3001},\"3003\":{\"name\":\"Archangel's Staff\",\"description\":\"<stats>+80 Ability Power<br><mana>+250 Mana</mana></stats><br><br><mana><unique>UNIQUE Passive - Awe:</unique> Grants Ability Power equal to 3% of maximum Mana. Refunds 25% of Mana spent.<br><unique>UNIQUE Passive - Mana Charge:</unique> Grants +8 maximum Mana (max +750 Mana) for each spell cast or Mana expenditure (occurs up to 2 times every 8 seconds).<br><br>Transforms into Seraph's Embrace at +750 Mana.</mana>\",\"colloq\":\";aa\",\"plaintext\":\"Increases Ability Power based on maximum Mana\",\"from\":[\"3070\",\"1058\"],\"image\":{\"full\":\"3003.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":192,\"y\":288,\"w\":48,\"h\":48},\"gold\":{\"base\":1100,\"purchasable\":true,\"total\":3100,\"sell\":2170},\"tags\":[\"Mana\",\"ManaRegen\",\"SpellDamage\"],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMPPoolMod\":250,\"FlatMagicDamageMod\":80},\"effect\":{\"Effect1Amount\":\"0.03\",\"Effect2Amount\":\"8\",\"Effect3Amount\":\"750\",\"Effect4Amount\":\"2\",\"Effect5Amount\":\"8\",\"Effect6Amount\":\"0\",\"Effect7Amount\":\"0.25\"},\"depth\":3,\"id\":3003},\"3004\":{\"name\":\"Manamune\",\"description\":\"<stats>+25 Attack Damage<br><mana>+250 Mana</mana></stats><br><br><mana><unique>UNIQUE Passive - Awe:</unique> Grants bonus Attack Damage equal to 2% of maximum Mana. Refunds 15% of Mana spent.<br><unique>UNIQUE Passive - Mana Charge:</unique> Grants +4 maximum Mana (max +750 Mana) for each basic attack, spell cast or Mana expenditure (occurs up to 2 times every 8 seconds).<br><br>Transforms into Muramana at +750 Mana.</mana>\",\"colloq\":\";\",\"plaintext\":\"Increases Attack Damage based on maximum Mana\",\"from\":[\"3070\",\"1037\"],\"image\":{\"full\":\"3004.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":240,\"y\":288,\"w\":48,\"h\":48},\"gold\":{\"base\":775,\"purchasable\":true,\"total\":2400,\"sell\":1680},\"tags\":[\"Damage\",\"Mana\",\"ManaRegen\",\"OnHit\"],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":25,\"FlatMPPoolMod\":250},\"effect\":{\"Effect1Amount\":\"0.02\",\"Effect2Amount\":\"4\",\"Effect3Amount\":\"750\",\"Effect4Amount\":\"2\",\"Effect5Amount\":\"8\",\"Effect6Amount\":\"1\",\"Effect7Amount\":\"0.15\"},\"depth\":3,\"id\":3004},\"3006\":{\"name\":\"Berserker's Greaves\",\"description\":\"<stats> +35% Attack Speed</stats><br><br><unique>UNIQUE Passive - Enhanced Movement:</unique> +45 Movement Speed\",\"colloq\":\";\",\"plaintext\":\"Enhances Movement Speed and Attack Speed\",\"from\":[\"1001\",\"1042\"],\"image\":{\"full\":\"3006.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":288,\"y\":288,\"w\":48,\"h\":48},\"gold\":{\"base\":500,\"purchasable\":true,\"total\":1100,\"sell\":770},\"tags\":[\"AttackSpeed\",\"Boots\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMovementSpeedMod\":45,\"PercentAttackSpeedMod\":0.35},\"depth\":2,\"id\":3006},\"3007\":{\"name\":\"Archangel's Staff (Quick Charge)\",\"description\":\"<stats>+80 Ability Power<br><mana>+250 Mana</mana></stats><br><br><mana><unique>UNIQUE Passive - Awe:</unique> Grants Ability Power equal to 3% of maximum Mana. Refunds 25% of Mana spent. <br><unique>UNIQUE Passive - Mana Charge:</unique> Grants +12 maximum Mana (max +750 Mana) for each spell cast or Mana expenditure (occurs up to 2 times every 8 seconds).<br><br>Transforms into Seraph's Embrace at +750 Mana.</mana>\",\"colloq\":\";aa\",\"plaintext\":\"Increases Ability Power based on maximum Mana\",\"from\":[\"3073\",\"1058\"],\"image\":{\"full\":\"3007.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":336,\"y\":288,\"w\":48,\"h\":48},\"gold\":{\"base\":1100,\"purchasable\":true,\"total\":3100,\"sell\":2170},\"tags\":[\"Mana\",\"ManaRegen\",\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":false,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMPPoolMod\":250,\"FlatMagicDamageMod\":80},\"effect\":{\"Effect1Amount\":\"0.03\",\"Effect2Amount\":\"12\",\"Effect3Amount\":\"750\",\"Effect4Amount\":\"2\",\"Effect5Amount\":\"8\",\"Effect6Amount\":\"0\",\"Effect7Amount\":\"0.25\"},\"depth\":3,\"id\":3007},\"3008\":{\"name\":\"Manamune (Quick Charge)\",\"description\":\"<stats>+25 Attack Damage<br><mana>+250 Mana</mana></stats><br><br><mana><unique>UNIQUE Passive - Awe:</unique> Grants bonus Attack Damage equal to 2% of maximum Mana. Refunds 15% of Mana spent.<br><unique>UNIQUE Passive - Mana Charge:</unique> Grants +6 maximum Mana (max +750 Mana) for each basic attack, spell cast or Mana expenditure (occurs up to 2 times every 8 seconds).<br><br>Transforms into Muramana at +750 Mana.</mana>\",\"colloq\":\";\",\"plaintext\":\"Increases Attack Damage based on maximum Mana\",\"from\":[\"3073\",\"1037\"],\"image\":{\"full\":\"3008.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":384,\"y\":288,\"w\":48,\"h\":48},\"gold\":{\"base\":775,\"purchasable\":true,\"total\":2400,\"sell\":1680},\"tags\":[\"Damage\",\"Mana\",\"ManaRegen\",\"OnHit\"],\"maps\":{\"8\":true,\"10\":false,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":25,\"FlatMPPoolMod\":250},\"effect\":{\"Effect1Amount\":\"0.02\",\"Effect2Amount\":\"6\",\"Effect3Amount\":\"750\",\"Effect4Amount\":\"2\",\"Effect5Amount\":\"8\",\"Effect6Amount\":\"1\",\"Effect7Amount\":\"0.15\"},\"depth\":3,\"id\":3008},\"3009\":{\"name\":\"Boots of Swiftness\",\"description\":\"<unique>UNIQUE Passive - Enhanced Movement:</unique> +55 Movement Speed<br><unique>UNIQUE Passive - Slow Resist:</unique> Movement slowing effects are reduced by 25%.\",\"colloq\":\";\",\"plaintext\":\"Enhances Movement Speed and reduces the effect of slows\",\"from\":[\"1001\"],\"image\":{\"full\":\"3009.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":432,\"y\":288,\"w\":48,\"h\":48},\"gold\":{\"base\":600,\"purchasable\":true,\"total\":900,\"sell\":630},\"tags\":[\"Boots\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMovementSpeedMod\":55},\"effect\":{\"Effect1Amount\":\"0.25\"},\"depth\":2,\"id\":3009},\"3010\":{\"name\":\"Catalyst of Aeons\",\"description\":\"<stats>+225 Health<br><mana>+300 Mana</mana></stats><br><br><unique>UNIQUE Passive - Eternity:</unique> 15% of damage taken from champions is gained as Mana. <br><br>Spending Mana restores 20% of the cost as Health, up to 15 per spell cast.  <br><br><rules>(Toggled Spells heal for a maximum of 15 per second.)</rules>\",\"colloq\":\";\",\"plaintext\":\"Spend Mana to recover Health\",\"from\":[\"1028\",\"1027\"],\"into\":[\"3027\",\"3029\",\"3030\",\"3800\"],\"image\":{\"full\":\"3010.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":0,\"y\":336,\"w\":48,\"h\":48},\"gold\":{\"base\":350,\"purchasable\":true,\"total\":1100,\"sell\":770},\"tags\":[\"Health\",\"HealthRegen\",\"Mana\",\"ManaRegen\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":225,\"FlatMPPoolMod\":300},\"effect\":{\"Effect1Amount\":\"150\",\"Effect2Amount\":\"200\",\"Effect3Amount\":\"8\",\"Effect4Amount\":\"15\",\"Effect5Amount\":\"0.2\",\"Effect6Amount\":\"0.15\"},\"depth\":2,\"id\":3010},\"3020\":{\"name\":\"Sorcerer's Shoes\",\"description\":\"<stats>+15 <a href='FlatMagicPen'>Magic Penetration</a></stats><br><br><unique>UNIQUE Passive - Enhanced Movement:</unique> +45 Movement Speed\",\"colloq\":\";\",\"plaintext\":\"Enhances Movement Speed and magic damage\",\"from\":[\"1001\"],\"image\":{\"full\":\"3020.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":48,\"y\":336,\"w\":48,\"h\":48},\"gold\":{\"base\":800,\"purchasable\":true,\"total\":1100,\"sell\":770},\"tags\":[\"Boots\",\"MagicPenetration\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMovementSpeedMod\":45},\"effect\":{\"Effect1Amount\":\"15\"},\"depth\":2,\"id\":3020},\"3022\":{\"name\":\"Frozen Mallet\",\"description\":\"<stats>+700 Health<br>+30 Attack Damage</stats><br><br><unique>UNIQUE Passive - Icy:</unique> Basic attacks slow the target's Movement Speed for 1.5 seconds on hit (40% slow for melee attacks, 30% slow for ranged attacks).\",\"colloq\":\";fm\",\"plaintext\":\"Basic attacks slow enemies\",\"from\":[\"3052\",\"1011\"],\"image\":{\"full\":\"3022.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":96,\"y\":336,\"w\":48,\"h\":48},\"gold\":{\"base\":900,\"purchasable\":true,\"total\":3100,\"sell\":2170},\"tags\":[\"Damage\",\"Health\",\"OnHit\",\"Slow\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":30,\"FlatHPPoolMod\":700},\"effect\":{\"Effect1Amount\":\"1.5\",\"Effect2Amount\":\"0.4\",\"Effect3Amount\":\"0.3\"},\"depth\":3,\"id\":3022},\"3024\":{\"name\":\"Glacial Shroud\",\"description\":\"<stats>+25 Armor<br><mana>+250 Mana</mana></stats><br><br><unique>UNIQUE Passive:</unique> +10% Cooldown Reduction\",\"colloq\":\";\",\"plaintext\":\"Increases Armor and Cooldown Reduction\",\"from\":[\"1027\",\"1029\"],\"into\":[\"3110\",\"3025\",\"3050\",\"3060\",\"3187\"],\"image\":{\"full\":\"3024.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":144,\"y\":336,\"w\":48,\"h\":48},\"gold\":{\"base\":350,\"purchasable\":true,\"total\":1000,\"sell\":700},\"tags\":[\"Armor\",\"CooldownReduction\",\"Mana\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMPPoolMod\":250,\"FlatArmorMod\":25},\"effect\":{\"Effect1Amount\":\"-0.1\"},\"depth\":2,\"id\":3024},\"3025\":{\"name\":\"Iceborn Gauntlet\",\"description\":\"<stats>+65 Armor<br>+20% Cooldown Reduction<br><mana>+500 Mana</mana></stats><br><br><unique>UNIQUE Passive - Spellblade:</unique> After using an ability, the next basic attack deals bonus physical damage equal to 100% of base Attack Damage in an area and creates an icy zone for 2 seconds that slows Movement Speed by 30% (1.5 second cooldown).<br><br>Size of zone increases with bonus armor.\",\"colloq\":\";frozen fist\",\"plaintext\":\"Basic attacks create a slow field after spell cast\",\"from\":[\"3057\",\"3024\"],\"image\":{\"full\":\"3025.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":192,\"y\":336,\"w\":48,\"h\":48},\"gold\":{\"base\":650,\"purchasable\":true,\"total\":2700,\"sell\":1890},\"tags\":[\"Armor\",\"Mana\",\"CooldownReduction\",\"Slow\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMPPoolMod\":500,\"FlatArmorMod\":65},\"effect\":{\"Effect1Amount\":\"-0.2\",\"Effect2Amount\":\"1\",\"Effect3Amount\":\"2\",\"Effect4Amount\":\"-0.3\",\"Effect5Amount\":\"1.5\"},\"depth\":3,\"id\":3025},\"3026\":{\"name\":\"Guardian Angel\",\"description\":\"<stats>+40 Attack Damage<br>+30 Armor</stats><br><br><unique>UNIQUE Passive:</unique> Upon taking lethal damage, restores 50% of base Health and 30% of maximum Mana after 4 seconds of stasis (300 second cooldown).\",\"colloq\":\";ga\",\"plaintext\":\"Periodically revives champion upon death\",\"from\":[\"1038\",\"1029\"],\"image\":{\"full\":\"3026.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":240,\"y\":336,\"w\":48,\"h\":48},\"gold\":{\"base\":800,\"purchasable\":true,\"total\":2400,\"sell\":960},\"tags\":[\"Armor\",\"Damage\"],\"maps\":{\"8\":true,\"10\":false,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":40,\"FlatArmorMod\":30},\"effect\":{\"Effect1Amount\":\"0.5\",\"Effect2Amount\":\"4\",\"Effect3Amount\":\"300\",\"Effect4Amount\":\"0.3\"},\"depth\":2,\"id\":3026},\"3027\":{\"name\":\"Rod of Ages\",\"description\":\"<stats>+300 Health<br><mana>+300 Mana</mana><br>+60 Ability Power</stats><br><br><passive>Passive:</passive> Grants +20 Health, +10 Mana, and +4 Ability Power per stack (max +200 Health, +100 Mana, and +40 Ability Power). Grants 1 stack per minute (max 10 stacks).<br><unique>UNIQUE Passive - Eternity:</unique> 15% of damage taken from champions is gained as Mana. Spending Mana restores 20% of the cost as Health, up to 25 per spell cast.\",\"colloq\":\";roa\",\"plaintext\":\"Greatly increases Health, Mana, and Ability Power\",\"from\":[\"3010\",\"1026\"],\"image\":{\"full\":\"3027.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":288,\"y\":336,\"w\":48,\"h\":48},\"gold\":{\"base\":750,\"purchasable\":true,\"total\":2700,\"sell\":1890},\"tags\":[\"Health\",\"HealthRegen\",\"Mana\",\"ManaRegen\",\"SpellDamage\"],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":300,\"FlatMPPoolMod\":300,\"FlatMagicDamageMod\":60},\"effect\":{\"Effect1Amount\":\"20\",\"Effect2Amount\":\"10\",\"Effect3Amount\":\"4\",\"Effect4Amount\":\"1\",\"Effect5Amount\":\"10\",\"Effect6Amount\":\"150\",\"Effect7Amount\":\"200\",\"Effect8Amount\":\"8\",\"Effect9Amount\":\"25\",\"Effect10Amount\":\"0.2\",\"Effect11Amount\":\"0.15\"},\"depth\":3,\"id\":3027},\"3028\":{\"name\":\"Chalice of Harmony\",\"description\":\"<stats>+30 Magic Resist<br><mana>+50% Base Mana Regen </mana></stats><br><br><unique>UNIQUE Passive - Harmony:</unique> Grants bonus % Base Health Regen equal to your bonus % Base Mana Regen.</unique>\",\"colloq\":\";\",\"plaintext\":\"Increases Mana and Health Regeneration\",\"from\":[\"1004\",\"1033\",\"1004\"],\"into\":[\"3174\",\"3222\"],\"image\":{\"full\":\"3028.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":336,\"y\":336,\"w\":48,\"h\":48},\"gold\":{\"base\":100,\"purchasable\":true,\"total\":800,\"sell\":560},\"tags\":[\"SpellBlock\",\"HealthRegen\",\"ManaRegen\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatSpellBlockMod\":30},\"effect\":{\"Effect1Amount\":\"2\",\"Effect2Amount\":\"5\",\"Effect3Amount\":\"600\",\"Effect4Amount\":\"180\",\"Effect5Amount\":\"8\",\"Effect6Amount\":\"1\",\"Effect7Amount\":\"1\"},\"depth\":2,\"id\":3028},\"3029\":{\"name\":\"Rod of Ages (Quick Charge)\",\"description\":\"<stats>+300 Health<br><mana>+300 Mana</mana><br>+60 Ability Power</stats><br><br><passive>Passive:</passive> Grants +20 Health, +10 Mana, and +4 Ability Power per stack (max +200 Health, +100 Mana, and +40 Ability Power). Grants 1 stack per 40 seconds (max 10 stacks).<br><unique>UNIQUE Passive - Eternity:</unique> 15% of damage taken from champions is gained as Mana. Spending Mana restores 20% of the cost as Health, up to 25 per spell cast.\",\"colloq\":\";roa\",\"plaintext\":\"Greatly increases Health, Mana, and Ability Power\",\"from\":[\"3010\",\"1026\"],\"image\":{\"full\":\"3029.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":384,\"y\":336,\"w\":48,\"h\":48},\"gold\":{\"base\":750,\"purchasable\":true,\"total\":2700,\"sell\":1890},\"tags\":[\"Health\",\"HealthRegen\",\"Mana\",\"ManaRegen\",\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":false,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":300,\"FlatMPPoolMod\":300,\"FlatMagicDamageMod\":60},\"effect\":{\"Effect1Amount\":\"20\",\"Effect2Amount\":\"10\",\"Effect3Amount\":\"4\",\"Effect4Amount\":\"1\",\"Effect5Amount\":\"10\",\"Effect6Amount\":\"150\",\"Effect7Amount\":\"200\",\"Effect8Amount\":\"8\",\"Effect9Amount\":\"25\",\"Effect10Amount\":\"0.2\",\"Effect11Amount\":\"0.15\"},\"depth\":3,\"id\":3029},\"3030\":{\"name\":\"Hextech GLP-800\",\"description\":\"<stats>+300 Health<br><mana>+400 Mana</mana><br>+80 Ability Power</stats><br><br><unique>UNIQUE Passive - Eternity:</unique> 15% of damage taken from champions is gained as Mana. Spending Mana restores 20% of the cost as Health, up to 25 per spell cast.<br><unique>UNIQUE Active - Frost Bolt:</unique> Fires a spray of icy bolts that explode, dealing <scaleLevel>100 - 200</scaleLevel> (+35% of your Ability Power) magic damage to all enemies hit. (40 second cooldown, shared with other <font color='#9999FF'><a href='itembolt'>Hextech</a></font> items).<br><br>Enemies hit are slowed by 65% decaying over 0.5 seconds.<br><br><rules>(Frost Bolt has a cast time, in contrast to most actives.)</rules> \",\"colloq\":\"frost cannon;\",\"plaintext\":\"Activate to fire icy bolts to slow enemies\",\"from\":[\"3010\",\"3145\"],\"image\":{\"full\":\"3030.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":432,\"y\":336,\"w\":48,\"h\":48},\"gold\":{\"base\":850,\"purchasable\":true,\"total\":3000,\"sell\":2100},\"tags\":[\"Health\",\"HealthRegen\",\"SpellDamage\",\"Mana\",\"ManaRegen\",\"Active\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":300,\"FlatMPPoolMod\":400,\"FlatMagicDamageMod\":80},\"effect\":{\"Effect1Amount\":\"0\",\"Effect2Amount\":\"0\",\"Effect3Amount\":\"0\",\"Effect4Amount\":\"0.5\",\"Effect5Amount\":\"0.65\",\"Effect6Amount\":\"100\",\"Effect7Amount\":\"200\",\"Effect8Amount\":\"0.35\",\"Effect9Amount\":\"25\",\"Effect10Amount\":\"0.2\",\"Effect11Amount\":\"0.15\",\"Effect12Amount\":\"40\"},\"depth\":3,\"id\":3030},\"3031\":{\"name\":\"Infinity Edge\",\"description\":\"<stats>+70 Attack Damage<br>+20% Critical Strike Chance</stats><br><br><unique>UNIQUE Passive:</unique> Critical strike bonus damage is increased by 50%.\",\"colloq\":\";ie\",\"plaintext\":\"Massively enhances critical strikes\",\"from\":[\"1038\",\"1037\",\"1018\"],\"image\":{\"full\":\"3031.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":0,\"y\":384,\"w\":48,\"h\":48},\"gold\":{\"base\":425,\"purchasable\":true,\"total\":3400,\"sell\":2380},\"tags\":[\"CriticalStrike\",\"Damage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":70,\"FlatCritChanceMod\":0.2},\"effect\":{\"Effect1Amount\":\"0.5\"},\"depth\":2,\"id\":3031},\"3033\":{\"name\":\"Mortal Reminder\",\"description\":\"<stats>+50 Attack Damage</stats><br><br><unique>UNIQUE Passive - Executioner:</unique> Physical damage inflicts <a href='GrievousWounds'>Grievous Wounds</a> on enemy champions for 5 seconds.<br><unique>UNIQUE Passive - Last Whisper:</unique> +35% <a href='BonusArmorPen'>Bonus Armor Penetration</a>.\",\"colloq\":\";lw;grievous\",\"plaintext\":\"Overcomes enemies with high Health recovery and Armor\",\"from\":[\"3035\",\"3123\"],\"image\":{\"full\":\"3033.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":48,\"y\":384,\"w\":48,\"h\":48},\"gold\":{\"base\":500,\"purchasable\":true,\"total\":2600,\"sell\":1820},\"tags\":[\"ArmorPenetration\",\"Damage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":50},\"effect\":{\"Effect1Amount\":\"5\",\"Effect2Amount\":\"0.35\"},\"depth\":3,\"id\":3033},\"3034\":{\"name\":\"Giant Slayer\",\"description\":\"<stats>+10 Attack Damage</stats><br><br><unique>UNIQUE Passive - Giant Slayer:</unique> Grants up to +10% physical damage against enemy champions with greater maximum Health than you (+1% damage per 100 Health difference, maxing at 1000 Health difference).<br><br><rules>(Unique Passives with the same name don't stack.)</rules>\",\"colloq\":\";gs\",\"plaintext\":\"Overcomes enemies with high Health\",\"from\":[\"1036\"],\"into\":[\"3036\"],\"image\":{\"full\":\"3034.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":96,\"y\":384,\"w\":48,\"h\":48},\"gold\":{\"base\":650,\"purchasable\":true,\"total\":1000,\"sell\":700},\"tags\":[\"Damage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":10},\"effect\":{\"Effect1Amount\":\"0.1\",\"Effect2Amount\":\"100\",\"Effect3Amount\":\"0.01\",\"Effect4Amount\":\"1000\"},\"depth\":2,\"id\":3034},\"3035\":{\"name\":\"Last Whisper\",\"description\":\"<stats>+10 Attack Damage</stats><br><br><unique>UNIQUE Passive - Last Whisper:</unique> +35% <a href='BonusArmorPen'>Bonus Armor Penetration</a>\",\"colloq\":\";lw\",\"plaintext\":\"Overcomes enemies with high Armor\",\"from\":[\"1036\"],\"into\":[\"3033\",\"3036\"],\"image\":{\"full\":\"3035.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":144,\"y\":384,\"w\":48,\"h\":48},\"gold\":{\"base\":950,\"purchasable\":true,\"total\":1300,\"sell\":910},\"tags\":[\"ArmorPenetration\",\"Damage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":10},\"effect\":{\"Effect1Amount\":\"0.35\",\"Effect2Amount\":\"0.35\"},\"depth\":2,\"id\":3035},\"3036\":{\"name\":\"Lord Dominik's Regards\",\"description\":\"<stats>+50 Attack Damage</stats><br><br><unique>UNIQUE Passive - Giant Slayer:</unique> Grants up to +20% physical damage against enemy champions with greater maximum Health than you (+2% damage per 100 Health difference, maxing at 1000 Health difference).<br><unique>UNIQUE Passive - Last Whisper:</unique> +35% <a href='BonusArmorPen'>Bonus Armor Penetration</a>\",\"colloq\":\";lw\",\"plaintext\":\"Overcomes enemies with high health and armor\",\"from\":[\"3035\",\"3034\"],\"image\":{\"full\":\"3036.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":192,\"y\":384,\"w\":48,\"h\":48},\"gold\":{\"base\":300,\"purchasable\":true,\"total\":2600,\"sell\":1820},\"tags\":[\"Damage\",\"ArmorPenetration\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":50},\"effect\":{\"Effect1Amount\":\"0.35\",\"Effect2Amount\":\"0.2\",\"Effect3Amount\":\"0\",\"Effect4Amount\":\"100\",\"Effect5Amount\":\"0.02\",\"Effect6Amount\":\"1000\"},\"depth\":3,\"id\":3036},\"3040\":{\"name\":\"Seraph's Embrace\",\"description\":\"<stats>+80 Ability Power<br><mana>+1000 Mana</mana></stats><br><br><mana><unique>UNIQUE Passive - Awe:</unique> Grants Ability Power equal to 3% of maximum Mana. Refunds 25% of Mana spent.</mana><br><active>UNIQUE Active - Mana Shield:</active> Consumes 20% of current Mana to grant a shield for 3 seconds that absorbs damage equal to 150 plus the amount of Mana consumed (120 second cooldown).\",\"colloq\":\";\",\"plaintext\":\"\",\"specialRecipe\":3003,\"inStore\":false,\"image\":{\"full\":\"3040.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":240,\"y\":384,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[\"Active\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMPPoolMod\":1000,\"FlatMagicDamageMod\":80},\"effect\":{\"Effect1Amount\":\"0.03\",\"Effect2Amount\":\"0.2\",\"Effect3Amount\":\"3\",\"Effect4Amount\":\"150\",\"Effect5Amount\":\"120\",\"Effect6Amount\":\"0\",\"Effect7Amount\":\"0.25\"},\"id\":3040},\"3041\":{\"name\":\"Mejai's Soulstealer\",\"description\":\"<stats>+20 Ability Power<br><mana>+200 Mana</mana></stats><br><br><unique>UNIQUE Passive - Dread:</unique> Grants +5 Ability Power per Glory. Grants 10% Movement Speed if you have at least 15 Glory.<br><unique>UNIQUE Passive - Do or Die:</unique> Grants 4 Glory for a champion kill or 2 Glory for an assist, up to 25 Glory total. Lose 10 stacks of Glory upon dying.\",\"colloq\":\";\",\"plaintext\":\"Grants Ability Power for kills and assists\",\"from\":[\"1082\"],\"image\":{\"full\":\"3041.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":288,\"y\":384,\"w\":48,\"h\":48},\"gold\":{\"base\":1050,\"purchasable\":true,\"total\":1400,\"sell\":980},\"tags\":[\"SpellDamage\",\"Mana\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatMPPoolMod\":200,\"FlatMagicDamageMod\":20},\"effect\":{\"Effect1Amount\":\"5\",\"Effect2Amount\":\"4\",\"Effect3Amount\":\"2\",\"Effect4Amount\":\"25\",\"Effect5Amount\":\"0.5\",\"Effect6Amount\":\"0.1\",\"Effect7Amount\":\"10\",\"Effect8Amount\":\"15\"},\"depth\":2,\"id\":3041},\"3042\":{\"name\":\"Muramana\",\"description\":\"<stats>+25 Attack Damage<br><mana>+1000 Mana</mana></stats><br><br><mana><unique>UNIQUE Passive - Awe:</unique> Grants bonus Attack Damage equal to 2% of maximum Mana. Refunds 15% of Mana spent.</mana><br><mana><unique>UNIQUE Passive - Shock:</unique> Single target spells and attacks (on hit) on <font color='#FFFFFF'>Champions</font> consume 3% of current Mana to deal bonus physical damage equal to twice the amount of Mana consumed.<br><br>This effect only activates while you have greater than 20% maximum Mana.</mana>\",\"colloq\":\";\",\"plaintext\":\"\",\"specialRecipe\":3004,\"inStore\":false,\"image\":{\"full\":\"3042.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":336,\"y\":384,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[\"OnHit\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":25,\"FlatMPPoolMod\":1000},\"effect\":{\"Effect1Amount\":\"0.15\"},\"id\":3042},\"3043\":{\"name\":\"Muramana\",\"description\":\"<stats>+25 Attack Damage<br><mana>+1000 Mana</mana></stats><br><br><mana><unique>UNIQUE Passive - Awe:</unique> Grants bonus Attack Damage equal to 2% of maximum Mana. Refunds 15% of Mana spent.</mana><br><mana><unique>UNIQUE Passive - Shock:</unique> Single target spells and attacks (on hit) on <font color='#FFFFFF'>Champions</font> consume 3% of current Mana to deal bonus physical damage equal to twice the amount of Mana consumed.<br><br>This effect only activates while you have greater than 20% maximum Mana.</mana>\",\"colloq\":\"\",\"plaintext\":\"\",\"specialRecipe\":3008,\"inStore\":false,\"image\":{\"full\":\"3043.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":384,\"y\":384,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[\"OnHit\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":25,\"FlatMPPoolMod\":1000},\"effect\":{\"Effect1Amount\":\"0.15\"},\"id\":3043},\"3044\":{\"name\":\"Phage\",\"description\":\"<stats>+200 Health<br>+15 Attack Damage</stats><br><br><unique>UNIQUE Passive - Rage:</unique> Basic attacks grant 20 Movement Speed for 2 seconds. Kills grant 60 Movement Speed instead. This Movement Speed bonus is halved for ranged champions.\",\"colloq\":\";\",\"plaintext\":\"Attacks and kills give a small burst of speed\",\"from\":[\"1028\",\"1036\"],\"into\":[\"3078\",\"3071\"],\"image\":{\"full\":\"3044.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":432,\"y\":384,\"w\":48,\"h\":48},\"gold\":{\"base\":500,\"purchasable\":true,\"total\":1250,\"sell\":875},\"tags\":[\"Damage\",\"Health\",\"NonbootsMovement\",\"OnHit\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":15,\"FlatHPPoolMod\":200},\"effect\":{\"Effect1Amount\":\"20\",\"Effect2Amount\":\"2\",\"Effect3Amount\":\"60\"},\"depth\":2,\"id\":3044},\"3046\":{\"name\":\"Phantom Dancer\",\"description\":\"<stats>+45% Attack Speed<br>+30% Critical Strike Chance<br>+5% Movement Speed</stats><br><br><unique>UNIQUE Passive - Spectral Waltz:</unique> While within 550 units of an enemy champion you can see, +7% Movement Speed and you can move through units.<br><unique>UNIQUE Passive - Lament:</unique> The last champion hit deals 12% less damage to you (ends after 10 seconds of not hitting).\",\"colloq\":\";pd\",\"plaintext\":\"Move faster near enemies and reduce incoming damage\",\"from\":[\"1042\",\"3086\",\"1042\"],\"image\":{\"full\":\"3046.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":0,\"y\":432,\"w\":48,\"h\":48},\"gold\":{\"base\":800,\"purchasable\":true,\"total\":2600,\"sell\":1820},\"tags\":[\"AttackSpeed\",\"CriticalStrike\",\"NonbootsMovement\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatCritChanceMod\":0.3,\"PercentMovementSpeedMod\":0.05,\"PercentAttackSpeedMod\":0.45},\"effect\":{\"Effect1Amount\":\"0.12\",\"Effect2Amount\":\"10\",\"Effect3Amount\":\"550\",\"Effect4Amount\":\"0.07\"},\"depth\":3,\"id\":3046},\"3047\":{\"name\":\"Ninja Tabi\",\"description\":\"<stats>+30 Armor</stats><br><br><unique>UNIQUE Passive:</unique> Blocks 10% of the damage from basic attacks.<br><unique>UNIQUE Passive - Enhanced Movement:</unique> +45 Movement Speed\",\"colloq\":\";\",\"plaintext\":\"Enhances Movement Speed and reduces incoming basic attack damage\",\"from\":[\"1001\",\"1029\"],\"image\":{\"full\":\"3047.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":48,\"y\":432,\"w\":48,\"h\":48},\"gold\":{\"base\":500,\"purchasable\":true,\"total\":1100,\"sell\":770},\"tags\":[\"Armor\",\"Boots\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMovementSpeedMod\":45,\"FlatArmorMod\":30},\"effect\":{\"Effect1Amount\":\"0.1\"},\"depth\":2,\"id\":3047},\"3048\":{\"name\":\"Seraph's Embrace\",\"description\":\"<stats>+80 Ability Power<br><mana>+1000 Mana</mana></stats><br><br><mana><unique>UNIQUE Passive - Awe:</unique> Grants Ability Power equal to 3% of maximum Mana. Refunds 25% of Mana spent.</mana><br><active>UNIQUE Active - Mana Shield:</active> Consumes 20% of current Mana to grant a shield for 3 seconds that absorbs damage equal to 150 plus the amount of Mana consumed (120 second cooldown).\",\"colloq\":\"\",\"plaintext\":\"\",\"specialRecipe\":3007,\"inStore\":false,\"image\":{\"full\":\"3048.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":96,\"y\":432,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[\"Active\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMPPoolMod\":1000,\"FlatMagicDamageMod\":80},\"effect\":{\"Effect1Amount\":\"0.03\",\"Effect2Amount\":\"0.2\",\"Effect3Amount\":\"3\",\"Effect4Amount\":\"150\",\"Effect5Amount\":\"120\",\"Effect6Amount\":\"0\",\"Effect7Amount\":\"0.25\"},\"id\":3048},\"3050\":{\"name\":\"Zeke's Harbinger\",\"description\":\"<stats><mana>+250 Mana</mana><br>+30 Armor<br>+50 Ability Power<br>+10% Cooldown Reduction</stats><br><br><active>UNIQUE Active - Conduit:</active> Bind to target ally (60 second cooldown).<br><unique>UNIQUE Passive:</unique> When within 1000 units of each other, you and your ally generate Charges. Attacking or casting spells generates extra Charges. At 100 Charges, causing damage consumes them, increasing your and your ally's Ability Power by 20% and Critical Strike Chance by 50% for 8 seconds.<br><br><rules>(Champions can only be linked by one Zeke's Harbinger at a time.)</rules>\",\"colloq\":\";haroldandkumar\",\"plaintext\":\"Grants an ally bursts of Critical Strike Chance and Ability Power\",\"from\":[\"1052\",\"3024\",\"1052\"],\"image\":{\"full\":\"3050.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":144,\"y\":432,\"w\":48,\"h\":48},\"gold\":{\"base\":380,\"purchasable\":true,\"total\":2250,\"sell\":1575},\"tags\":[\"Armor\",\"SpellDamage\",\"Mana\",\"Aura\",\"Active\",\"CooldownReduction\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMPPoolMod\":250,\"FlatMagicDamageMod\":50,\"FlatArmorMod\":30},\"effect\":{\"Effect1Amount\":\"-0.1\",\"Effect2Amount\":\"1\",\"Effect3Amount\":\"0.5\",\"Effect4Amount\":\"0.2\",\"Effect5Amount\":\"1000\",\"Effect6Amount\":\"100\",\"Effect7Amount\":\"8\",\"Effect8Amount\":\"10\",\"Effect9Amount\":\"4\",\"Effect10Amount\":\"2\",\"Effect11Amount\":\"2\",\"Effect12Amount\":\"6\",\"Effect13Amount\":\"60\",\"Effect14Amount\":\"5\"},\"depth\":3,\"id\":3050},\"3052\":{\"name\":\"Jaurim's Fist\",\"description\":\"<stats>+15 Attack Damage<br>+150 Health</stats><br><br><unique>UNIQUE Passive:</unique> Killing a unit grants 5 maximum Health. This bonus stacks up to 30 times.\",\"colloq\":\";enforcer\",\"plaintext\":\"Attack Damage and stacking Health on Unit Kill\",\"from\":[\"1036\",\"1028\"],\"into\":[\"3104\",\"3022\",\"3053\",\"3748\"],\"image\":{\"full\":\"3052.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":192,\"y\":432,\"w\":48,\"h\":48},\"gold\":{\"base\":450,\"purchasable\":true,\"total\":1200,\"sell\":840},\"tags\":[\"Health\",\"Damage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":15,\"FlatHPPoolMod\":150},\"effect\":{\"Effect1Amount\":\"30\",\"Effect2Amount\":\"5\",\"Effect3Amount\":\"2\",\"Effect4Amount\":\"150\"},\"depth\":2,\"id\":3052},\"3053\":{\"name\":\"Sterak's Gage\",\"description\":\"<stats>+400 Health<br>+30% Base Attack Damage </stats><br><br><unique>UNIQUE Passive - Lifeline:</unique> Upon taking at least 400 to 1800 damage (based on level) within 5 seconds, gain a shield for 75% of your bonus Health that decays over 3 seconds (60 second cooldown).<br><br><unlockedPassive>Sterak's Fury:</unlockedPassive> When <i>Lifeline</i> triggers, grow in size and strength, gaining +30% additional Base Attack Damage for 8 seconds.\",\"colloq\":\";juggernaut;primal\",\"plaintext\":\"Shields against large bursts of damage\",\"from\":[\"3052\",\"1036\"],\"image\":{\"full\":\"3053.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":240,\"y\":432,\"w\":48,\"h\":48},\"gold\":{\"base\":1050,\"purchasable\":true,\"total\":2600,\"sell\":1820},\"tags\":[\"Health\",\"Damage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":400},\"effect\":{\"Effect1Amount\":\"400\",\"Effect2Amount\":\"0.3\",\"Effect3Amount\":\"5\",\"Effect4Amount\":\"0.75\",\"Effect5Amount\":\"0\",\"Effect6Amount\":\"8\",\"Effect7Amount\":\"60\",\"Effect8Amount\":\"3\",\"Effect9Amount\":\"1800\"},\"depth\":3,\"id\":3053},\"3056\":{\"name\":\"Ohmwrecker\",\"description\":\"<stats>+300 Health<br>+50 Armor<br>+150% Base Health Regen <br>+10% Cooldown Reduction</stats><br><br><active>UNIQUE Active:</active> Prevents nearby enemy turrets from attacking for 3 seconds (120 second cooldown). This effect cannot be used against the same turret more than once every 8 seconds.<br><br><unique>UNIQUE Passive - Point Runner:</unique> Builds up to +20% Movement Speed over 2 seconds while near turrets (including fallen turrets) and Void Gates.\",\"colloq\":\";\",\"plaintext\":\"Temporarily disables enemy turrets\",\"from\":[\"2053\",\"3067\"],\"image\":{\"full\":\"3056.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":288,\"y\":432,\"w\":48,\"h\":48},\"gold\":{\"base\":650,\"purchasable\":true,\"total\":2650,\"sell\":1855},\"tags\":[\"Active\",\"Armor\",\"CooldownReduction\",\"Health\",\"HealthRegen\",\"NonbootsMovement\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":300,\"FlatArmorMod\":50},\"effect\":{\"Effect1Amount\":\"3\",\"Effect2Amount\":\"120\",\"Effect3Amount\":\"8\",\"Effect4Amount\":\"0.2\",\"Effect5Amount\":\"1\"},\"depth\":4,\"id\":3056},\"3057\":{\"name\":\"Sheen\",\"description\":\"<stats><mana>+250 Mana</mana><br>+10% Cooldown Reduction</stats><br><br><unique>UNIQUE Passive - Spellblade:</unique> After using an ability, the next basic attack deals bonus physical damage equal to 100% base Attack Damage on hit (1.5 second cooldown).\",\"colloq\":\";\",\"plaintext\":\"Grants a bonus to next attack after spell cast\",\"from\":[\"1027\"],\"into\":[\"3078\",\"3100\",\"3025\"],\"image\":{\"full\":\"3057.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":336,\"y\":432,\"w\":48,\"h\":48},\"gold\":{\"base\":700,\"purchasable\":true,\"total\":1050,\"sell\":735},\"tags\":[\"Mana\",\"CooldownReduction\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMPPoolMod\":250},\"effect\":{\"Effect1Amount\":\"1.5\",\"Effect2Amount\":\"1\"},\"depth\":2,\"id\":3057},\"3060\":{\"name\":\"Banner of Command\",\"description\":\"<stats>+60 Armor<br>+30 Magic Resist<br><mana>+400 Mana</mana><br>+10% Cooldown Reduction</stats><br><br><active>UNIQUE Active - Promote:</active> Greatly increases the power of a lane minion and grants it immunity to magic damage (120 second cooldown).\",\"colloq\":\";flag\",\"plaintext\":\"Promotes a siege minion to a more powerful unit\",\"from\":[\"3105\",\"3024\"],\"image\":{\"full\":\"3060.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":384,\"y\":432,\"w\":48,\"h\":48},\"gold\":{\"base\":100,\"purchasable\":true,\"total\":2200,\"sell\":1540},\"tags\":[\"SpellBlock\",\"Armor\",\"Mana\",\"Active\",\"CooldownReduction\"],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatSpellBlockMod\":30,\"FlatMPPoolMod\":400,\"FlatArmorMod\":60},\"effect\":{\"Effect1Amount\":\"0.5\",\"Effect2Amount\":\"15\",\"Effect3Amount\":\"0.75\"},\"depth\":3,\"id\":3060},\"3065\":{\"name\":\"Spirit Visage\",\"description\":\"<stats>+450 Health<br>+55 Magic Resist<br>+100% Base Health Regen <br>+10% Cooldown Reduction</stats><br><br><unique>UNIQUE Passive:</unique> Increases all healing received by 30%.\",\"colloq\":\";sv\",\"plaintext\":\"Increases Health and healing effects\",\"from\":[\"3211\",\"3067\"],\"image\":{\"full\":\"3065.png\",\"sprite\":\"item0.png\",\"group\":\"item\",\"x\":432,\"y\":432,\"w\":48,\"h\":48},\"gold\":{\"base\":800,\"purchasable\":true,\"total\":2800,\"sell\":1960},\"tags\":[\"CooldownReduction\",\"Health\",\"HealthRegen\",\"SpellBlock\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":450,\"FlatSpellBlockMod\":55},\"effect\":{\"Effect1Amount\":\"0\",\"Effect2Amount\":\"0.3\",\"Effect3Amount\":\"0.5\"},\"depth\":3,\"id\":3065},\"3067\":{\"name\":\"Kindlegem\",\"description\":\"<stats>+200 Health  </stats><br><br><unique>UNIQUE Passive:</unique> +10% Cooldown Reduction\",\"colloq\":\";\",\"plaintext\":\"Increases Health and Cooldown Reduction\",\"from\":[\"1028\"],\"into\":[\"3187\",\"3401\",\"3065\",\"3071\",\"3056\",\"3083\",\"3152\"],\"image\":{\"full\":\"3067.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":0,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":400,\"purchasable\":true,\"total\":800,\"sell\":560},\"tags\":[\"CooldownReduction\",\"Health\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":200},\"effect\":{\"Effect1Amount\":\"-0.1\"},\"depth\":2,\"id\":3067},\"3068\":{\"name\":\"Sunfire Cape\",\"description\":\"<stats>+425 Health<br>+60 Armor  </stats><br><br><unique>UNIQUE Passive - Immolate:</unique> Deals 11 (+1 per champion level) magic damage per second to nearby enemies. Deals 200% bonus damage to minions and monsters.\",\"colloq\":\";\",\"plaintext\":\"Constantly deals damage to nearby enemies\",\"from\":[\"1031\",\"3751\"],\"image\":{\"full\":\"3068.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":48,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":1000,\"purchasable\":true,\"total\":2900,\"sell\":2030},\"tags\":[\"Armor\",\"Health\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":425,\"FlatArmorMod\":60},\"effect\":{\"Effect1Amount\":\"11\",\"Effect2Amount\":\"1\",\"Effect3Amount\":\"2\"},\"depth\":3,\"id\":3068},\"3069\":{\"name\":\"Talisman of Ascension\",\"description\":\"<stats>+45 Armor<br>+175% Base Health Regen <br>+10% Cooldown Reduction<br>+2 Gold per 10 seconds</stats><br><br><unique>UNIQUE Passive - Point Runner:</unique> Builds up to +20% Movement Speed over 2 seconds while near turrets, fallen turrets and Void Gates.<br><unique>UNIQUE Passive - Favor: </unique>Enemy minions killed by your allies sometimes drop coins that give either <font color='#D4AF37'>30</font> gold or <font color='#44DDFF'>8%</font> missing mana (minimum 15). Cannon minions always drop coins.<br><active>UNIQUE Active:</active> Grants nearby allies +40% Movement Speed for 3 seconds (60 second cooldown).<hr><passive>QUEST:</passive> Earn 650 gold using this item.<br><passive>REWARD:</passive> <font color='#CFBF84'>Favor</font> is upgraded to <font color='#CFBF84'><a href='coinlinequestreward'>Emperor's Favor</a></font> and you receive an <font color='#29E3D6'><a href='coinlinequestrewardelixir'>Elixir Of Skill</a></font>.<br><br><groupLimit>Limited to 1 Gold Income Item.</groupLimit><br><br><rules><font color='#447777'>''Praise the sun.'' - Historian Shurelya, 22 September, 25 CLE</font></rules>\",\"colloq\":\";shurelya;reverie\",\"plaintext\":\"Increases Health / Mana Regeneration and Cooldown Reduction. Activate to speed up nearby allies.\",\"from\":[\"3096\",\"2053\"],\"image\":{\"full\":\"3069.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":96,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":350,\"purchasable\":true,\"total\":2400,\"sell\":960},\"tags\":[\"HealthRegen\",\"Armor\",\"ManaRegen\",\"Active\",\"GoldPer\",\"CooldownReduction\",\"NonbootsMovement\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatArmorMod\":45},\"effect\":{\"Effect1Amount\":\"-0.1\",\"Effect2Amount\":\"2\",\"Effect3Amount\":\"15\",\"Effect4Amount\":\"25\",\"Effect5Amount\":\"0.4\",\"Effect6Amount\":\"3\",\"Effect7Amount\":\"60\",\"Effect8Amount\":\"20\",\"Effect9Amount\":\"2\"},\"depth\":4,\"id\":3069},\"3070\":{\"name\":\"Tear of the Goddess\",\"description\":\"<stats><mana>+250 Mana</mana></stats><br><br><mana><unique>UNIQUE Passive - Awe:</unique> Refunds 15% of Mana spent.<br><unique>UNIQUE Passive - Mana Charge:</unique> Grants 4 maximum Mana on spell cast or Mana expenditure (up to 2 times per 8 seconds).<br><br>Caps at +750 Mana.</mana>\",\"colloq\":\";\",\"plaintext\":\"Increases maximum Mana as Mana is spent\",\"from\":[\"1027\",\"1004\"],\"into\":[\"3003\",\"3004\"],\"image\":{\"full\":\"3070.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":144,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":275,\"purchasable\":true,\"total\":750,\"sell\":525},\"tags\":[\"Mana\",\"ManaRegen\"],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMPPoolMod\":250},\"effect\":{\"Effect1Amount\":\"4\",\"Effect2Amount\":\"8\",\"Effect3Amount\":\"1\",\"Effect4Amount\":\"8\",\"Effect5Amount\":\"750\",\"Effect6Amount\":\"2\",\"Effect7Amount\":\"0.15\"},\"depth\":2,\"id\":3070},\"3071\":{\"name\":\"The Black Cleaver\",\"description\":\"<stats>+400 Health<br>+40 Attack Damage<br>+20% Cooldown Reduction</stats><br><br><unique>UNIQUE Passive:</unique> Dealing physical damage to an enemy champion Cleaves them, reducing their Armor by 4% for 6 seconds (stacks up to 6 times, up to 24%).<br><unique>UNIQUE Passive - Rage:</unique> Dealing physical damage grants 20 movement speed for 2 seconds. Assists on Cleaved enemy champions or kills on any unit grant 60 movement speed for 2 seconds instead. This Movement Speed is halved for ranged champions.\",\"colloq\":\";bc\",\"plaintext\":\"Dealing physical damage to enemy champions reduces their Armor\",\"from\":[\"3044\",\"3067\"],\"image\":{\"full\":\"3071.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":192,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":950,\"purchasable\":true,\"total\":3000,\"sell\":2100},\"tags\":[\"ArmorPenetration\",\"CooldownReduction\",\"Damage\",\"Health\",\"NonbootsMovement\",\"OnHit\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":40,\"FlatHPPoolMod\":400},\"effect\":{\"Effect1Amount\":\"-0.2\",\"Effect2Amount\":\"0.04\",\"Effect3Amount\":\"6\",\"Effect4Amount\":\"6\",\"Effect5Amount\":\"0.24\",\"Effect6Amount\":\"20\",\"Effect7Amount\":\"2\",\"Effect8Amount\":\"60\",\"Effect9Amount\":\"2\"},\"depth\":3,\"id\":3071},\"3072\":{\"name\":\"The Bloodthirster\",\"description\":\"<stats>+80 Attack Damage</stats><br><br><unique>UNIQUE Passive:</unique> +20% Life Steal<br><unique>UNIQUE Passive:</unique> Your basic attacks can now overheal you. Excess life is stored as a shield that can block 50-350 damage, based on champion level.<br><br>This shield decays slowly if you haven't dealt or taken damage in the last 25 seconds.\",\"colloq\":\";bt\",\"plaintext\":\"Grants Attack Damage, Life Steal and Life Steal now overheals\",\"from\":[\"1038\",\"1036\",\"1053\"],\"image\":{\"full\":\"3072.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":240,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":1150,\"purchasable\":true,\"total\":3700,\"sell\":2590},\"tags\":[\"Damage\",\"LifeSteal\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":80},\"effect\":{\"Effect1Amount\":\"50\",\"Effect2Amount\":\"350\",\"Effect3Amount\":\"25\",\"Effect4Amount\":\"0.2\"},\"depth\":3,\"id\":3072},\"3073\":{\"name\":\"Tear of the Goddess (Quick Charge)\",\"description\":\"<stats><mana>+250 Mana</mana></stats><br><br><mana><unique>UNIQUE Passive - Awe:</unique> Refunds 15% of Mana spent.<br><unique>UNIQUE Passive - Mana Charge:</unique> Grants 6 maximum Mana on spell cast or Mana expenditure (up to 2 times per 8 seconds).<br><br>Caps at +750 Mana.</mana>\",\"colloq\":\";\",\"plaintext\":\"Increases maximum Mana as Mana is spent\",\"from\":[\"1027\",\"1004\"],\"into\":[\"3007\",\"3008\"],\"image\":{\"full\":\"3073.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":288,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":275,\"purchasable\":true,\"total\":750,\"sell\":525},\"tags\":[\"Mana\",\"ManaRegen\"],\"maps\":{\"8\":true,\"10\":false,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMPPoolMod\":250},\"effect\":{\"Effect1Amount\":\"6\",\"Effect2Amount\":\"8\",\"Effect3Amount\":\"1\",\"Effect4Amount\":\"8\",\"Effect5Amount\":\"750\",\"Effect6Amount\":\"2\",\"Effect7Amount\":\"0.15\"},\"depth\":2,\"id\":3073},\"3074\":{\"name\":\"Ravenous Hydra\",\"description\":\"<stats>+80 Attack Damage<br>+100% Base Health Regen <br>+12% Life Steal</stats><br><br><passive>Passive:</passive> 50% of total Life Steal applies to damage dealt by this item.<br><unique>UNIQUE Passive - Cleave:</unique> Basic attacks deal 20% to 60% of total Attack Damage as bonus physical damage to enemies near the target on hit (enemies closest to the target take the most damage).<br><active>UNIQUE Active - Crescent:</active> Deals 60% to 100% of total Attack Damage as physical damage to nearby enemy units (closest enemies take the most damage) (10 second cooldown).\",\"colloq\":\";\",\"plaintext\":\"Melee attacks hit nearby enemies, dealing damage and restoring Health\",\"from\":[\"3077\",\"1053\",\"1037\"],\"image\":{\"full\":\"3074.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":336,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":525,\"purchasable\":true,\"total\":3500,\"sell\":2450},\"tags\":[\"Active\",\"Damage\",\"HealthRegen\",\"LifeSteal\",\"OnHit\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":80,\"PercentLifeStealMod\":0.12},\"effect\":{\"Effect1Amount\":\"0.2\",\"Effect2Amount\":\"0.6\",\"Effect3Amount\":\"0.6\",\"Effect4Amount\":\"1\",\"Effect5Amount\":\"10\"},\"depth\":3,\"id\":3074},\"3075\":{\"name\":\"Thornmail\",\"description\":\"<stats>+100 Armor  </stats><br><br><unique>UNIQUE Passive:</unique> Upon being hit by a basic attack, reflects magic damage back to the attacker equal to 25% of your bonus Armor plus 15% of the incoming damage.<br><br><rules>(Bonus Armor is Armor from items, buffs, runes and masteries.)</rules><br><rules>(Reflect damage is calculated based on damage taken before being reduced by Armor.)</rules>\",\"colloq\":\";\",\"plaintext\":\"Returns damage taken from basic attacks as magic damage\",\"from\":[\"1029\",\"1031\"],\"image\":{\"full\":\"3075.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":384,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":1250,\"purchasable\":true,\"total\":2350,\"sell\":1645},\"tags\":[\"Armor\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatArmorMod\":100},\"effect\":{\"Effect1Amount\":\"0.15\",\"Effect2Amount\":\"0.25\"},\"depth\":3,\"id\":3075},\"3077\":{\"name\":\"Tiamat\",\"description\":\"<stats>+20 Attack Damage<br>+50% Base Health Regen </stats><br><br><unique>UNIQUE Passive - Cleave:</unique> Basic attacks deal 20% to 60% of total Attack Damage as bonus physical damage to enemies near the target on hit (enemies closest to the target take the most damage).<br><active>UNIQUE Active - Crescent:</active> Deals 60% to 100% of total Attack Damage as physical damage to nearby enemy units (enemies closest to the target take the most damage) (10 second cooldown).\",\"colloq\":\";\",\"plaintext\":\"Melee attacks hit nearby enemies\",\"from\":[\"1036\",\"1006\",\"1036\"],\"into\":[\"3074\",\"3748\"],\"image\":{\"full\":\"3077.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":432,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":350,\"purchasable\":true,\"total\":1200,\"sell\":840},\"tags\":[\"HealthRegen\",\"Damage\",\"Active\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":20},\"effect\":{\"Effect1Amount\":\"0.2\",\"Effect2Amount\":\"0.6\",\"Effect3Amount\":\"0.6\",\"Effect4Amount\":\"1\",\"Effect5Amount\":\"10\"},\"depth\":2,\"id\":3077},\"3078\":{\"name\":\"Trinity Force\",\"description\":\"<stats>+250 Health<br><mana>+250 Mana</mana><br>+25 Attack Damage<br>+40% Attack Speed<br>+20% Cooldown Reduction<br>+5% Movement Speed</stats><br><br><unique>UNIQUE Passive - Rage:</unique> Basic attacks grant 20 Movement Speed for 2 seconds. Kills grant 60 Movement Speed instead. This Movement Speed bonus is halved for ranged champions.<br><unique>UNIQUE Passive - Spellblade:</unique> After using an ability, the next basic attack deals bonus physical damage equal to 200% of base Attack Damage on hit (1.5 second cooldown).\",\"colloq\":\";triforce;tons of damage\",\"plaintext\":\"Tons of Damage\",\"from\":[\"3101\",\"3057\",\"3044\"],\"image\":{\"full\":\"3078.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":0,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":333,\"purchasable\":true,\"total\":3733,\"sell\":2613},\"tags\":[\"Health\",\"Damage\",\"AttackSpeed\",\"Mana\",\"CooldownReduction\",\"OnHit\",\"NonbootsMovement\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":25,\"PercentMovementSpeedMod\":0.05,\"FlatHPPoolMod\":250,\"FlatMPPoolMod\":250,\"PercentAttackSpeedMod\":0.4},\"effect\":{\"Effect1Amount\":\"20\",\"Effect2Amount\":\"60\",\"Effect3Amount\":\"2\",\"Effect4Amount\":\"2\",\"Effect5Amount\":\"1.5\"},\"depth\":3,\"id\":3078},\"3082\":{\"name\":\"Warden's Mail\",\"description\":\"<stats>+40 Armor</stats><br><br><unique>UNIQUE Passive - Cold Steel:</unique> When hit by basic attacks, reduces the attacker's Attack Speed by 15% for 1 seconds.\",\"colloq\":\";\",\"plaintext\":\"Slows Attack Speed of enemy champions when receiving basic attacks\",\"from\":[\"1029\",\"1029\"],\"into\":[\"3110\",\"3143\"],\"image\":{\"full\":\"3082.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":48,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":400,\"purchasable\":true,\"total\":1000,\"sell\":700},\"tags\":[\"Armor\",\"Slow\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatArmorMod\":40},\"effect\":{\"Effect1Amount\":\"-0.15\",\"Effect2Amount\":\"1\"},\"depth\":2,\"id\":3082},\"3083\":{\"name\":\"Warmog's Armor\",\"description\":\"<stats>+800 Health<br>+200% Base Health Regen </stats><br><br><unique>UNIQUE Passive:</unique> +10% Cooldown Reduction<br><unique>UNIQUE Passive:</unique> Grants <unlockedPassive>Warmog's Heart</unlockedPassive> if you have at least 2750 maximum Health.<br><br><unlockedPassive>Warmog's Heart:</unlockedPassive> Restores 25% of maximum Health every 5 seconds if damage hasn't been taken within 6 seconds (3 seconds for damage from minions and monsters).\",\"colloq\":\";\",\"plaintext\":\"Grants massive Health and Health Regen\",\"from\":[\"1011\",\"3067\",\"3801\"],\"image\":{\"full\":\"3083.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":96,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":400,\"purchasable\":true,\"total\":2850,\"sell\":1995},\"tags\":[\"Health\",\"HealthRegen\",\"CooldownReduction\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":800},\"effect\":{\"Effect1Amount\":\"0.015\",\"Effect2Amount\":\"5\",\"Effect3Amount\":\"0.25\",\"Effect4Amount\":\"2750\",\"Effect5Amount\":\"6\",\"Effect6Amount\":\"-0.1\",\"Effect7Amount\":\"6\",\"Effect8Amount\":\"3\"},\"depth\":3,\"id\":3083},\"3084\":{\"name\":\"Overlord's Bloodmail\",\"description\":\"<stats>+800 Health<br>+100% Base Health Regen </stats><br><br><unique>UNIQUE Passive:</unique> Upon champion kill or assist, restores 300 Health over 5 seconds.\",\"colloq\":\";\",\"plaintext\":\"Restores Health on kill or assist\",\"from\":[\"1011\",\"3801\"],\"image\":{\"full\":\"3084.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":144,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":900,\"purchasable\":true,\"total\":2550,\"sell\":1785},\"tags\":[\"Health\",\"HealthRegen\"],\"maps\":{\"8\":false,\"10\":false,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":800},\"effect\":{\"Effect1Amount\":\"300\",\"Effect2Amount\":\"5\"},\"depth\":3,\"id\":3084},\"3085\":{\"name\":\"Runaan's Hurricane\",\"description\":\"<stats>+40% Attack Speed<br>+30% Critical Strike Chance<br>+7% Movement Speed</stats><br><br><unique>UNIQUE Passive - Wind's Fury:</unique> When basic attacking, bolts are fired at up to 2 enemies near the target, each dealing (40% of Attack Damage) physical damage. Bolts can critically strike and apply on hit effects.\",\"colloq\":\";\",\"plaintext\":\"Ranged attacks fire two bolts at nearby enemies\",\"from\":[\"1042\",\"3086\",\"1042\"],\"image\":{\"full\":\"3085.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":192,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":800,\"purchasable\":true,\"total\":2600,\"sell\":1820},\"tags\":[\"CriticalStrike\",\"AttackSpeed\",\"OnHit\",\"NonbootsMovement\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatCritChanceMod\":0.3,\"PercentMovementSpeedMod\":0.07,\"PercentAttackSpeedMod\":0.4},\"effect\":{\"Effect1Amount\":\"0\",\"Effect2Amount\":\"40\",\"Effect3Amount\":\"2\",\"Effect4Amount\":\"0\",\"Effect5Amount\":\"40\",\"Effect6Amount\":\"1\"},\"depth\":3,\"id\":3085},\"3086\":{\"name\":\"Zeal\",\"description\":\"<stats>+15% Attack Speed<br>+20% Critical Strike Chance</stats><br><br><unique>UNIQUE Passive:</unique> +5% Movement Speed\",\"colloq\":\";\",\"plaintext\":\"Slight bonuses to Critical Strike Chance, Movement Speed and Attack Speed\",\"from\":[\"1051\",\"1042\"],\"into\":[\"3094\",\"3085\",\"3046\",\"3087\"],\"image\":{\"full\":\"3086.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":240,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":500,\"purchasable\":true,\"total\":1200,\"sell\":840},\"tags\":[\"AttackSpeed\",\"CriticalStrike\",\"NonbootsMovement\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatCritChanceMod\":0.2,\"PercentAttackSpeedMod\":0.15},\"effect\":{\"Effect1Amount\":\"0.05\"},\"depth\":2,\"id\":3086},\"3087\":{\"name\":\"Statikk Shiv\",\"description\":\"<stats>+35% Attack Speed<br>+30% Critical Strike Chance<br>+5% Movement Speed</stats><br><br><passive>Passive:</passive> Moving and attacking will make an attack <a href='Energized'>Energized</a>.<br><br><unique>UNIQUE Passive - Shiv Lightning:</unique> Your Energized attacks deal 60~160 bonus magic damage (based on level) to up to 5 targets on hit (deals +65% bonus damage to minions and can critically strike).\",\"colloq\":\";\",\"plaintext\":\"Movement builds charges that release chain lightning on basic attack\",\"from\":[\"3086\",\"2015\"],\"image\":{\"full\":\"3087.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":288,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":600,\"purchasable\":true,\"total\":2600,\"sell\":1820},\"tags\":[\"AttackSpeed\",\"CriticalStrike\",\"NonbootsMovement\",\"OnHit\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatCritChanceMod\":0.3,\"PercentMovementSpeedMod\":0.05,\"PercentAttackSpeedMod\":0.35},\"effect\":{\"Effect1Amount\":\"100\",\"Effect2Amount\":\"80\",\"Effect3Amount\":\"5\",\"Effect4Amount\":\"750\",\"Effect5Amount\":\"60\",\"Effect6Amount\":\"160\",\"Effect7Amount\":\"5\",\"Effect8Amount\":\"0.65\"},\"depth\":3,\"id\":3087},\"3089\":{\"name\":\"Rabadon's Deathcap\",\"description\":\"<stats>+120 Ability Power  </stats><br><br><unique>UNIQUE Passive:</unique> Increases Ability Power by 35%.\",\"colloq\":\";dc;banksys;hat\",\"plaintext\":\"Massively increases Ability Power\",\"from\":[\"1026\",\"1058\",\"1052\"],\"image\":{\"full\":\"3089.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":336,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":1265,\"purchasable\":true,\"total\":3800,\"sell\":2660},\"tags\":[\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":false,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMagicDamageMod\":120},\"effect\":{\"Effect1Amount\":\"35\"},\"depth\":2,\"id\":3089},\"3090\":{\"name\":\"Wooglet's Witchcap\",\"description\":\"<stats>+100 Ability Power<br>+45 Armor  </stats><br><br><unique>UNIQUE Passive:</unique> Increases Ability Power by 25%<br><active>UNIQUE Active:</active> Champion becomes invulnerable and untargetable for 2.5 seconds, but is unable to move, attack, cast spells, or use items during this time (120 second cooldown).\",\"colloq\":\";hat\",\"plaintext\":\"Massively increases Ability Power and can be activated to enter stasis\",\"from\":[\"3191\",\"1058\"],\"image\":{\"full\":\"3090.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":384,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":1050,\"purchasable\":true,\"total\":3500,\"sell\":2450},\"tags\":[\"Active\",\"Armor\",\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatMagicDamageMod\":100,\"FlatArmorMod\":45},\"effect\":{\"Effect1Amount\":\"25\",\"Effect2Amount\":\"2.5\",\"Effect3Amount\":\"120\"},\"depth\":3,\"id\":3090},\"3091\":{\"name\":\"Wit's End\",\"description\":\"<stats>+40% Attack Speed<br>+40 Magic Resist</stats><br><br><unique>UNIQUE Passive:</unique> Basic attacks deal 40 bonus magic damage on hit.<br><unique>UNIQUE Passive:</unique> Basic attacks steal 5 Magic Resist from the target on hit (stacks up to 5 times.)\",\"colloq\":\";\",\"plaintext\":\"Deals bonus magic damage on basic attacks\",\"from\":[\"1043\",\"1057\",\"1042\"],\"image\":{\"full\":\"3091.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":432,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":480,\"purchasable\":true,\"total\":2500,\"sell\":1750},\"tags\":[\"AttackSpeed\",\"OnHit\",\"SpellBlock\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatSpellBlockMod\":40,\"PercentAttackSpeedMod\":0.4},\"effect\":{\"Effect1Amount\":\"40\",\"Effect2Amount\":\"5\",\"Effect3Amount\":\"5\"},\"depth\":3,\"id\":3091},\"3092\":{\"name\":\"Frost Queen's Claim\",\"description\":\"<stats>+60 Ability Power<br>+10% Cooldown Reduction<br>+2 Gold per 10 seconds<br><mana>+50% Base Mana Regen </mana></stats><br><br><unique>UNIQUE Passive - Tribute:</unique> Damaging spells and attacks against champions or buildings deal 15 additional damage and grant 15 Gold. This can occur up to 3 times every 30 seconds.<br><active>UNIQUE Active:</active> Summon 2 icy ghosts for 6 seconds that seek out nearby enemy champions. Ghosts reveal enemies on contact and slow them by 40% for between 2 and 5 seconds based on how far the ghosts have traveled (90 second cooldown).<hr><passive>QUEST:</passive> Earn 650 gold using this item.<br><passive>REWARD:</passive> <font color='#CFBF84'>Tribute</font> is upgraded into <font color='#CFBF84'><a href='frostqueenslinequestreward'>Queen's Tribute</a></font>.<br><br><groupLimit>Limited to 1 Gold Income Item.</groupLimit>\",\"colloq\":\"spooky ghosts;\",\"plaintext\":\"Sends out seeking wraiths that track hidden enemies and slow them\",\"from\":[\"3098\",\"3108\"],\"image\":{\"full\":\"3092.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":0,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":450,\"purchasable\":true,\"total\":2200,\"sell\":880},\"tags\":[\"Active\",\"CooldownReduction\",\"GoldPer\",\"ManaRegen\",\"Slow\",\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMagicDamageMod\":60},\"effect\":{\"Effect1Amount\":\"15\",\"Effect2Amount\":\"15\",\"Effect3Amount\":\"12\",\"Effect4Amount\":\"3\",\"Effect5Amount\":\"30\",\"Effect6Amount\":\"2\",\"Effect7Amount\":\"1\",\"Effect8Amount\":\"2\",\"Effect9Amount\":\"2\",\"Effect10Amount\":\"-0.4\",\"Effect11Amount\":\"5\",\"Effect12Amount\":\"6\",\"Effect13Amount\":\"50\",\"Effect14Amount\":\"90\",\"Effect15Amount\":\"0.25\",\"Effect16Amount\":\"2\"},\"depth\":3,\"id\":3092},\"3094\":{\"name\":\"Rapid Firecannon\",\"description\":\"<stats>+30% Attack Speed<br>+30% Critical Strike Chance<br>+5% Movement Speed</stats><br><br><passive>Passive:</passive> Moving and attacking will make an attack <a href='Energized'>Energized</a>.<br><br><unique>UNIQUE Passive - Firecannon:</unique> Your Energized attacks gain 35% bonus Range (+150 range maximum) and deal 50~120 bonus magic damage (based on level) on hit.<br><br>Attacks become Energized 25% faster. Energized attacks function on structures.\",\"colloq\":\";canon;rapidfire;rfc\",\"plaintext\":\"Movement builds charges that release a sieging fire attack on release\",\"from\":[\"3086\",\"2015\"],\"image\":{\"full\":\"3094.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":48,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":600,\"purchasable\":true,\"total\":2600,\"sell\":1820},\"tags\":[\"AttackSpeed\",\"CriticalStrike\",\"NonbootsMovement\",\"OnHit\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatCritChanceMod\":0.3,\"PercentMovementSpeedMod\":0.05,\"PercentAttackSpeedMod\":0.3},\"effect\":{\"Effect1Amount\":\"0.35\",\"Effect2Amount\":\"150\",\"Effect3Amount\":\"50\",\"Effect4Amount\":\"120\",\"Effect5Amount\":\"5\",\"Effect6Amount\":\"0.25\"},\"depth\":3,\"id\":3094},\"3096\":{\"name\":\"Nomad's Medallion\",\"description\":\"<stats>+50% Base Health Regen <br>+10% Cooldown Reduction<br>+2 Gold per 10 seconds</stats><br><br><unique>UNIQUE Passive - Favor:</unique> Enemy minions killed by your allies sometimes drop coins that give either <font color='#D4AF37'>30</font> gold or <font color='#44DDFF'>8%</font> missing mana (minimum 15). Cannon minions always drop coins.<hr><passive>QUEST:</passive> Earn 650 gold using this item.<br><passive>REWARD:</passive> <font color='#CFBF84'>Favor</font> is upgraded to <font color='#CFBF84'><a href='coinlinequestreward'>Emperor's Favor</a></font> and you receive an <font color='#29E3D6'><a href='coinlinequestrewardelixir'>Elixir Of Skill</a></font>.<br><br><groupLimit>Limited to 1 Gold Income Item.</groupLimit><br><br><rules><font color='#447777'>''The medallion shines with the glory of a thousand voices when exposed to the sun.'' - Historian Shurelya, 22 June, 24 CLE</font></rules>\",\"colloq\":\";\",\"plaintext\":\"Grants gold and mana when nearby minions die that you didn't kill\",\"from\":[\"1006\",\"3301\"],\"into\":[\"2302\",\"3069\"],\"image\":{\"full\":\"3096.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":96,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":350,\"purchasable\":true,\"total\":850,\"sell\":340},\"tags\":[\"HealthRegen\",\"ManaRegen\",\"Active\",\"GoldPer\",\"CooldownReduction\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"2\",\"Effect2Amount\":\"15\",\"Effect3Amount\":\"50\",\"Effect4Amount\":\"10\"},\"depth\":2,\"id\":3096},\"3097\":{\"name\":\"Targon's Brace\",\"description\":\"<stats>+175 Health<br>+50% Base Health Regen <br>+2 Gold per 10 seconds </stats><br><br><unique>UNIQUE Passive - Spoils of War:</unique> Melee basic attacks execute minions below 200 (+10 per level) Health. Killing a minion heals the owner and the nearest allied champion for 40 Health and grants them kill Gold.<br><br>These effects require a nearby ally. Recharges every 30 seconds. Max 3 charges.<hr><passive>QUEST:</passive> Earn 650 gold using this item.<br><passive>REWARD:</passive> <font color='#CFBF84'>Shield Battery</font>, a permanent shield that regenerates slowly outside of combat.<br><br><groupLimit>Limited to 1 Gold Income Item.</groupLimit>\",\"colloq\":\";\",\"plaintext\":\"Periodically kill enemy minions to heal and grant gold to a nearby ally\",\"from\":[\"3302\",\"1006\"],\"into\":[\"2303\",\"3401\"],\"image\":{\"full\":\"3097.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":144,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":350,\"purchasable\":true,\"total\":850,\"sell\":340},\"tags\":[\"Aura\",\"GoldPer\",\"Health\",\"HealthRegen\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":175},\"effect\":{\"Effect1Amount\":\"200\",\"Effect2Amount\":\"40\",\"Effect3Amount\":\"30\",\"Effect4Amount\":\"3\",\"Effect5Amount\":\"2\",\"Effect6Amount\":\"10\"},\"depth\":2,\"id\":3097},\"3098\":{\"name\":\"Frostfang\",\"description\":\"<stats>+20 Ability Power<br>+2 Gold per 10 seconds<br><mana>+50% Base Mana Regen </mana></stats><br><br><unique>UNIQUE Passive - Tribute:</unique> Damaging spells and attacks against champions or buildings deal 15 additional damage and grant 15 Gold. This can occur up to 3 times every 30 seconds. Killing minions slows Tribute generation.<hr><passive>QUEST:</passive> Earn 650 gold using this item.<br><passive>REWARD:</passive> <font color='#CFBF84'>Tribute</font> is upgraded into <font color='#CFBF84'><a href='frostqueenslinequestreward'>Queen's Tribute</a></font>.<br><br><groupLimit>Limited to 1 Gold Income Item.</groupLimit>\",\"colloq\":\";\",\"plaintext\":\"Grants gold when you damage an enemy\",\"from\":[\"3303\",\"1004\"],\"into\":[\"2301\",\"3092\"],\"image\":{\"full\":\"3098.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":192,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":375,\"purchasable\":true,\"total\":850,\"sell\":340},\"tags\":[\"Active\",\"GoldPer\",\"ManaRegen\",\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMagicDamageMod\":20},\"effect\":{\"Effect1Amount\":\"15\",\"Effect2Amount\":\"15\",\"Effect3Amount\":\"12\",\"Effect4Amount\":\"3\",\"Effect5Amount\":\"30\",\"Effect6Amount\":\"2\"},\"depth\":2,\"id\":3098},\"3100\":{\"name\":\"Lich Bane\",\"description\":\"<stats>+80 Ability Power<br>+7% Movement Speed<br>+10% Cooldown Reduction<br><mana>+250 Mana</mana></stats><br><br><unique>UNIQUE Passive - Spellblade:</unique> After using an ability, the next basic attack deals 75% Base Attack Damage (+50% of Ability Power) bonus magic damage on hit (1.5 second cooldown).\",\"colloq\":\";\",\"plaintext\":\"Grants a bonus to next attack after spell cast\",\"from\":[\"3057\",\"3113\",\"1026\"],\"image\":{\"full\":\"3100.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":240,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":450,\"purchasable\":true,\"total\":3200,\"sell\":2240},\"tags\":[\"SpellDamage\",\"Mana\",\"CooldownReduction\",\"NonbootsMovement\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"PercentMovementSpeedMod\":0.07,\"FlatMPPoolMod\":250,\"FlatMagicDamageMod\":80},\"effect\":{\"Effect1Amount\":\"0.75\",\"Effect2Amount\":\"0.5\",\"Effect3Amount\":\"1.5\"},\"depth\":3,\"id\":3100},\"3101\":{\"name\":\"Stinger\",\"description\":\"<stats>+35% Attack Speed</stats><br><br><unique>UNIQUE Passive:</unique> +10% Cooldown Reduction\",\"colloq\":\";\",\"plaintext\":\"Increased Attack Speed and Cooldown Reduction\",\"from\":[\"1042\",\"1042\"],\"into\":[\"3115\",\"3137\",\"3078\"],\"image\":{\"full\":\"3101.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":288,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":500,\"purchasable\":true,\"total\":1100,\"sell\":770},\"tags\":[\"AttackSpeed\",\"CooldownReduction\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"PercentAttackSpeedMod\":0.35},\"effect\":{\"Effect1Amount\":\"10\"},\"depth\":2,\"id\":3101},\"3102\":{\"name\":\"Banshee's Veil\",\"description\":\"<stats>+70 Ability Power<br>+60 Magic Resist<br>+10% Cooldown Reduction</stats><br><br><unique>UNIQUE Passive:</unique> Grants a spell shield that blocks the next enemy ability. This shield refreshes after no damage is taken from enemy champions for 40 seconds.\",\"colloq\":\";bv\",\"plaintext\":\"Periodically blocks enemy abilities\",\"from\":[\"3108\",\"1033\",\"1026\"],\"image\":{\"full\":\"3102.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":336,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":800,\"purchasable\":true,\"total\":3000,\"sell\":2100},\"tags\":[\"SpellBlock\",\"SpellDamage\",\"CooldownReduction\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatSpellBlockMod\":60,\"FlatMagicDamageMod\":70},\"effect\":{\"Effect1Amount\":\"40\",\"Effect2Amount\":\"45\",\"Effect3Amount\":\"10\",\"Effect4Amount\":\"-0.1\",\"Effect5Amount\":\"8\",\"Effect6Amount\":\"2\"},\"depth\":3,\"id\":3102},\"3104\":{\"name\":\"Lord Van Damm's Pillager\",\"description\":\"<stats>+300 Health<br>+50 Attack Damage<br>+10% Cooldown Reduction</stats><br><br><unique>UNIQUE Passive - Ashes to Ashes:</unique> Controlling the nearest Altar sets you aflame, dealing 25 (+1 per champion level) magic damage per second to nearby enemies (Deals 50% bonus damage to minions and monsters). Controlling the furthest Altar causes your basic attacks to burn targets for up to 114 true damage (based on champion level) over 3 seconds.\",\"colloq\":\"lvd;\",\"plaintext\":\"Reduces Armor of nearby enemies\",\"from\":[\"3133\",\"3052\"],\"image\":{\"full\":\"3104.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":384,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":700,\"purchasable\":true,\"total\":3000,\"sell\":2100},\"tags\":[\"Health\",\"Damage\",\"Aura\",\"CooldownReduction\",\"OnHit\",\"ArmorPenetration\"],\"maps\":{\"8\":false,\"10\":true,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":50,\"FlatHPPoolMod\":300},\"effect\":{\"Effect1Amount\":\"25\",\"Effect2Amount\":\"1\",\"Effect3Amount\":\"0.5\"},\"depth\":3,\"id\":3104},\"3105\":{\"name\":\"Aegis of the Legion\",\"description\":\"<stats>+30 Armor<br>+30 Magic Resist</stats>\",\"colloq\":\";\",\"plaintext\":\"Grants Armor and Magic Resistance\",\"from\":[\"1033\",\"1029\"],\"into\":[\"3190\",\"3060\"],\"image\":{\"full\":\"3105.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":432,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":350,\"purchasable\":true,\"total\":1100,\"sell\":770},\"tags\":[\"SpellBlock\",\"Armor\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatSpellBlockMod\":30,\"FlatArmorMod\":30},\"depth\":2,\"id\":3105},\"3107\":{\"name\":\"Redemption\",\"description\":\"<stats>+300 Health<br>+75% Base Health Regen <br>+125% Base Mana Regen <br>+10% Cooldown Reduction</stats><br><br><passive>UNIQUE Passive:</passive> +10% Heal and Shield Power<br><active>UNIQUE Active:</active> Target an area within 5500 range. After 2.5 seconds, call down a beam of light to heal allies for 40 (+25 per level of target) Health, burn enemy champions for 10% of their maximum Health as <font color='#FFFFFF'>true</font> damage and deal 250 <font color='#FFFFFF'>true</font> damage to enemy minions (120 second cooldown).<br><br>Can be used while dead.<br><br><rules>(Half effect if the target has been affected by another Redemption recently.)</rules>\",\"colloq\":\";\",\"plaintext\":\"Further improves defenses for nearby allies\",\"from\":[\"3114\",\"3801\"],\"image\":{\"full\":\"3107.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":0,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":650,\"purchasable\":true,\"total\":2100,\"sell\":1470},\"tags\":[\"Health\",\"HealthRegen\",\"ManaRegen\",\"CooldownReduction\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":300},\"effect\":{\"Effect1Amount\":\"0.1\",\"Effect2Amount\":\"40\",\"Effect3Amount\":\"25\",\"Effect4Amount\":\"0.1\",\"Effect5Amount\":\"250\",\"Effect6Amount\":\"120\",\"Effect7Amount\":\"550\",\"Effect8Amount\":\"5500\",\"Effect9Amount\":\"0.5\",\"Effect10Amount\":\"8\"},\"depth\":3,\"id\":3107},\"3108\":{\"name\":\"Fiendish Codex\",\"description\":\"<stats>+30 Ability Power</stats><br><br><unique>UNIQUE Passive:</unique> +10% Cooldown Reduction\",\"colloq\":\";\",\"plaintext\":\"Increases Ability Power and Cooldown Reduction\",\"from\":[\"1052\"],\"into\":[\"3174\",\"3092\",\"3115\",\"3165\",\"3102\",\"3157\"],\"image\":{\"full\":\"3108.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":48,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":465,\"purchasable\":true,\"total\":900,\"sell\":630},\"tags\":[\"CooldownReduction\",\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMagicDamageMod\":30},\"effect\":{\"Effect1Amount\":\"-0.1\"},\"depth\":2,\"id\":3108},\"3109\":{\"name\":\"Knight's Vow\",\"description\":\"<stats>+400 Health<br>+100% Base Health Regen <br>+40 Armor</stats><br><br><active>UNIQUE Active:</active> Designate an allied champion as your <a href='KnightsVowPartner'>Partner</a> (90 second cooldown).<br><passive>UNIQUE Passive:</passive> If your <a href='KnightsVowPartner'>Partner</a> is nearby, gain +20 additional Armor and +15% Movement Speed towards them.<br><passive>UNIQUE Passive:</passive> If your <a href='KnightsVowPartner'>Partner</a> is nearby, heal for 12% of the damage your <a href='KnightsVowPartner'>Partner</a> deals to champions and redirect 12% of the damage your <a href='KnightsVowPartner'>Partner</a> takes from champions to you as <font color='#FFFFFF'>true</font> damage (healing and damage redirection are reduced by 50% if you are ranged).<br><br><rules>(Champions can only be linked by one Knight's Vow at a time.)</rules>\",\"colloq\":\";\",\"plaintext\":\"Partner with an ally to protect each other\",\"from\":[\"3801\",\"1031\"],\"image\":{\"full\":\"3109.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":96,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":850,\"purchasable\":true,\"total\":2300,\"sell\":1610},\"tags\":[\"Health\",\"HealthRegen\",\"Armor\",\"Aura\",\"NonbootsMovement\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":400,\"FlatArmorMod\":40},\"effect\":{\"Effect1Amount\":\"20\",\"Effect2Amount\":\"0.15\",\"Effect3Amount\":\"0.12\",\"Effect4Amount\":\"0.12\",\"Effect5Amount\":\"90\",\"Effect6Amount\":\"0.5\",\"Effect7Amount\":\"1000\"},\"depth\":3,\"id\":3109},\"3110\":{\"name\":\"Frozen Heart\",\"description\":\"<stats>+90 Armor<br>+20% Cooldown Reduction<br><mana>+400 Mana</mana></stats><br><br><aura>UNIQUE Aura:</aura> Reduces the Attack Speed of nearby enemies by 15%.\",\"colloq\":\";fh\",\"plaintext\":\"Massively increases Armor and slows enemy basic attacks\",\"from\":[\"3082\",\"3024\"],\"image\":{\"full\":\"3110.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":144,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":700,\"purchasable\":true,\"total\":2700,\"sell\":1890},\"tags\":[\"Armor\",\"Aura\",\"CooldownReduction\",\"Mana\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMPPoolMod\":400,\"FlatArmorMod\":90},\"effect\":{\"Effect1Amount\":\"-0.2\",\"Effect2Amount\":\"-0.15\"},\"depth\":3,\"id\":3110},\"3111\":{\"name\":\"Mercury's Treads\",\"description\":\"<stats>+25 Magic Resist</stats><br><br><unique>UNIQUE Passive - Enhanced Movement:</unique> +45 Movement Speed<br><unique>UNIQUE Passive - Tenacity:</unique> Reduces the duration of stuns, slows, taunts, fears, silences, blinds, polymorphs, and immobilizes by 30%.\",\"colloq\":\";\",\"plaintext\":\"Increases Movement Speed and reduces duration of disabling effects\",\"from\":[\"1001\",\"1033\"],\"image\":{\"full\":\"3111.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":192,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":350,\"purchasable\":true,\"total\":1100,\"sell\":770},\"tags\":[\"Boots\",\"SpellBlock\",\"Tenacity\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMovementSpeedMod\":45,\"FlatSpellBlockMod\":25},\"depth\":2,\"id\":3111},\"3112\":{\"name\":\"Guardian's Orb\",\"description\":\"<stats>+150 Health<br>+35 Ability Power<br><mana>+10 Mana regen per 5 seconds</mana></stats><br><br><groupLimit>Limited to 1 Guardian's Item.</groupLimit>\",\"colloq\":\";\",\"plaintext\":\"Good starting item for mages\",\"image\":{\"full\":\"3112.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":240,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":950,\"purchasable\":true,\"total\":950,\"sell\":380},\"tags\":[\"Health\",\"SpellDamage\",\"Mana\",\"Lane\"],\"maps\":{\"8\":false,\"10\":false,\"11\":false,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":150,\"FlatMagicDamageMod\":35},\"id\":3112},\"3113\":{\"name\":\"Aether Wisp\",\"description\":\"<stats>+30 Ability Power</stats><br><br><unique>UNIQUE Passive:</unique> +5% Movement Speed\",\"colloq\":\";\",\"plaintext\":\"Increases Ability Power and Movement Speed\",\"from\":[\"1052\"],\"into\":[\"1402\",\"1410\",\"1414\",\"3100\",\"3285\",\"3504\",\"3673\"],\"image\":{\"full\":\"3113.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":288,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":415,\"purchasable\":true,\"total\":850,\"sell\":595},\"tags\":[\"NonbootsMovement\",\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMagicDamageMod\":30},\"effect\":{\"Effect1Amount\":\"0.05\"},\"depth\":2,\"id\":3113},\"3114\":{\"name\":\"Forbidden Idol\",\"description\":\"<stats><mana>+50% Base Mana Regen </mana></stats><br><br><unique>UNIQUE Passive:</unique> +10% Cooldown Reduction<br><unique>UNIQUE Passive:</unique> +8% Heal and Shield Power\",\"colloq\":\";\",\"plaintext\":\"Increases Mana Regeneration and Cooldown Reduction\",\"from\":[\"1004\",\"1004\"],\"into\":[\"3107\",\"3222\",\"3504\"],\"image\":{\"full\":\"3114.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":336,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":550,\"purchasable\":true,\"total\":800,\"sell\":560},\"tags\":[\"CooldownReduction\",\"ManaRegen\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"-0.1\",\"Effect2Amount\":\"0.08\"},\"depth\":2,\"id\":3114},\"3115\":{\"name\":\"Nashor's Tooth\",\"description\":\"<stats>+50% Attack Speed<br>+80 Ability Power</stats><br><br><unique>UNIQUE Passive:</unique> +20% Cooldown Reduction<br><unique>UNIQUE Passive:</unique> Basic attacks deal 15 (+15% of Ability Power) bonus magic damage on hit.<br>\",\"colloq\":\";\",\"plaintext\":\"Increases Attack Speed, Ability Power, and Cooldown Reduction\",\"from\":[\"3101\",\"3108\"],\"image\":{\"full\":\"3115.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":384,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":1000,\"purchasable\":true,\"total\":3000,\"sell\":2100},\"tags\":[\"AttackSpeed\",\"CooldownReduction\",\"OnHit\",\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMagicDamageMod\":80,\"PercentAttackSpeedMod\":0.5},\"depth\":3,\"id\":3115},\"3116\":{\"name\":\"Rylai's Crystal Scepter\",\"description\":\"<stats>+300 Health<br>+75 Ability Power</stats><br><br><unique>UNIQUE Passive:</unique> Damaging spells and abilities reduce enemy movement speed by 20% for 1 second.\",\"colloq\":\";\",\"plaintext\":\"Abilities slow enemies\",\"from\":[\"1026\",\"1052\",\"1028\"],\"image\":{\"full\":\"3116.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":432,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":915,\"purchasable\":true,\"total\":2600,\"sell\":1820},\"tags\":[\"Health\",\"SpellDamage\",\"Slow\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":300,\"FlatMagicDamageMod\":75},\"effect\":{\"Effect1Amount\":\"-0.2\",\"Effect2Amount\":\"-0.2\",\"Effect3Amount\":\"-0.2\",\"Effect4Amount\":\"1\",\"Effect5Amount\":\"1\",\"Effect6Amount\":\"1\"},\"depth\":2,\"id\":3116},\"3117\":{\"name\":\"Boots of Mobility\",\"description\":\"<unique>UNIQUE Passive - Enhanced Movement:</unique> +25 Movement Speed. Increases to +115 Movement Speed when out of combat for 5 seconds.\",\"colloq\":\";\",\"plaintext\":\"Greatly enhances Movement Speed when out of combat\",\"from\":[\"1001\"],\"image\":{\"full\":\"3117.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":0,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":600,\"purchasable\":true,\"total\":900,\"sell\":630},\"tags\":[\"Boots\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMovementSpeedMod\":115},\"effect\":{\"Effect1Amount\":\"0\",\"Effect2Amount\":\"0\",\"Effect3Amount\":\"0\",\"Effect4Amount\":\"0\",\"Effect5Amount\":\"0\",\"Effect6Amount\":\"0\",\"Effect7Amount\":\"0\",\"Effect8Amount\":\"25\"},\"depth\":2,\"id\":3117},\"3122\":{\"name\":\"Wicked Hatchet\",\"description\":\"<stats>+20 Attack Damage<br>+10% Critical Strike Chance</stats><br><br><unique>UNIQUE Passive:</unique> Critical Strikes cause your target to bleed for an additional 60% of your bonus Attack Damage as magic damage over 3 seconds.\",\"colloq\":\";ie\",\"plaintext\":\"Critical Strikes cause your target to bleed\",\"from\":[\"1051\",\"1036\"],\"into\":[\"3104\",\"3185\"],\"image\":{\"full\":\"3122.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":48,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":450,\"purchasable\":true,\"total\":1200,\"sell\":840},\"tags\":[\"CriticalStrike\",\"Damage\",\"OnHit\"],\"maps\":{\"8\":true,\"10\":false,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":20,\"FlatCritChanceMod\":0.1},\"effect\":{\"Effect1Amount\":\"0.6\",\"Effect2Amount\":\"3\"},\"depth\":2,\"id\":3122},\"3123\":{\"name\":\"Executioner's Calling\",\"description\":\"<stats>+15 Attack Damage</stats><br><br><unique>UNIQUE Passive - Executioner:</unique> Physical damage inflicts <a href='GrievousWounds'>Grievous Wounds</a> on enemy champions for 3 seconds.\",\"colloq\":\";grievous\",\"plaintext\":\"Overcomes enemies with high health gain\",\"from\":[\"1036\"],\"into\":[\"3033\"],\"image\":{\"full\":\"3123.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":96,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":450,\"purchasable\":true,\"total\":800,\"sell\":560},\"tags\":[\"Damage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":15},\"effect\":{\"Effect1Amount\":\"3\"},\"depth\":2,\"id\":3123},\"3124\":{\"name\":\"Guinsoo's Rageblade\",\"description\":\"<stats>+35 Attack Damage<br>+50 Ability Power<br>+25% Attack Speed</stats><br><br><passive>Passive: </passive>Basic attacks deal an additional 15 magic damage on hit.<br><unique>UNIQUE Passive:</unique> Basic attacks grant +8% Attack Speed, +3 Attack Damage, and +4 Ability Power for 5 seconds (stacks up to 6 times). While you have 6 stacks, gain <unlockedPassive>Guinsoo's Rage</unlockedPassive>.<br><br><unlockedPassive>Guinsoo's Rage:</unlockedPassive> Every other basic attack will trigger on hit effects an additional time.\",\"colloq\":\";\",\"plaintext\":\"Increases Ability Power and Attack Damage\",\"from\":[\"1026\",\"1043\",\"1037\"],\"image\":{\"full\":\"3124.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":144,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":875,\"purchasable\":true,\"total\":3600,\"sell\":2520},\"tags\":[\"Damage\",\"AttackSpeed\",\"SpellDamage\",\"OnHit\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":35,\"FlatMagicDamageMod\":50,\"PercentAttackSpeedMod\":0.25},\"effect\":{\"Effect1Amount\":\"0.08\",\"Effect2Amount\":\"4\",\"Effect3Amount\":\"3\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"6\",\"Effect6Amount\":\"0\",\"Effect7Amount\":\"0\",\"Effect8Amount\":\"15\",\"Effect9Amount\":\"1\"},\"depth\":3,\"id\":3124},\"3133\":{\"name\":\"Caulfield's Warhammer\",\"description\":\"<stats>+25 Attack Damage</stats><br><br><unique>UNIQUE Passive:</unique> +10% Cooldown Reduction\",\"colloq\":\";\",\"plaintext\":\"Attack Damage and Cooldown Reduction\",\"stacks\":0,\"from\":[\"1036\",\"1036\"],\"into\":[\"3142\",\"1400\",\"3104\",\"1408\",\"1412\",\"3812\",\"3156\",\"3508\",\"3671\"],\"image\":{\"full\":\"3133.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":192,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":400,\"purchasable\":true,\"total\":1100,\"sell\":770},\"tags\":[\"Damage\",\"CooldownReduction\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":25},\"effect\":{\"Effect1Amount\":\"-0.1\"},\"depth\":2,\"id\":3133},\"3134\":{\"name\":\"Serrated Dirk\",\"description\":\"<stats>+25 Attack Damage</stats><br><br><unique>UNIQUE Passive:</unique> +10 <a href='Lethality'>Lethality</a><br><unique>UNIQUE Passive:</unique> +20 Movement Speed out of Combat.\",\"colloq\":\";lethality\",\"plaintext\":\"Increases Attack Damage and Lethality\",\"stacks\":0,\"from\":[\"1036\",\"1036\"],\"into\":[\"3142\",\"3814\",\"3147\"],\"image\":{\"full\":\"3134.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":240,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":400,\"purchasable\":true,\"total\":1100,\"sell\":770},\"tags\":[\"Damage\",\"NonbootsMovement\",\"ArmorPenetration\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":25},\"effect\":{\"Effect1Amount\":\"0\",\"Effect2Amount\":\"10\",\"Effect3Amount\":\"20\"},\"depth\":2,\"id\":3134},\"3135\":{\"name\":\"Void Staff\",\"description\":\"<stats>+80 Ability Power</stats><br><br><unique>UNIQUE Passive:</unique> +35% <a href='TotalMagicPen'>Magic Penetration</a>.\",\"colloq\":\";\",\"plaintext\":\"Increases magic damage\",\"from\":[\"1026\",\"1052\"],\"image\":{\"full\":\"3135.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":288,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":1365,\"purchasable\":true,\"total\":2650,\"sell\":1855},\"tags\":[\"MagicPenetration\",\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMagicDamageMod\":80},\"effect\":{\"Effect1Amount\":\"0.35\"},\"depth\":2,\"id\":3135},\"3136\":{\"name\":\"Haunting Guise\",\"description\":\"<stats>+25 Ability Power<br>+200 Health</stats><br><br><unique>UNIQUE Passive - Eyes of Pain:</unique> +15 <a href='FlatMagicPen'>Magic Penetration</a>\",\"colloq\":\";mask\",\"plaintext\":\"Increases magic damage\",\"stacks\":0,\"from\":[\"1028\",\"1052\"],\"into\":[\"3151\"],\"image\":{\"full\":\"3136.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":336,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":665,\"purchasable\":true,\"total\":1500,\"sell\":1050},\"tags\":[\"Health\",\"MagicPenetration\",\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":200,\"FlatMagicDamageMod\":25},\"effect\":{\"Effect1Amount\":\"15\"},\"depth\":2,\"id\":3136},\"3137\":{\"name\":\"Dervish Blade\",\"description\":\"<stats>+50% Attack Speed<br>+45 Magic Resist<br>+10% Cooldown Reduction</stats><br><br><active>UNIQUE Active - Quicksilver:</active> Removes all debuffs, and if the champion is melee, also grants +50% bonus Movement Speed for 1 second (90 second cooldown).\",\"colloq\":\";\",\"plaintext\":\"Activate to remove all debuffs and grant massive Movement Speed\",\"from\":[\"3140\",\"3101\"],\"image\":{\"full\":\"3137.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":384,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":300,\"purchasable\":true,\"total\":2700,\"sell\":1890},\"tags\":[\"Active\",\"AttackSpeed\",\"CooldownReduction\",\"NonbootsMovement\",\"SpellBlock\"],\"maps\":{\"8\":false,\"10\":false,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatSpellBlockMod\":45,\"PercentAttackSpeedMod\":0.5},\"effect\":{\"Effect1Amount\":\"0.5\",\"Effect2Amount\":\"1\",\"Effect3Amount\":\"90\"},\"depth\":3,\"id\":3137},\"3139\":{\"name\":\"Mercurial Scimitar\",\"description\":\"<stats>+65 Attack Damage<br>+35 Magic Resist<br>+10% Life Steal</stats><br><br><active>UNIQUE Active - Quicksilver:</active> Removes all crowd control debuffs and also grants +50% bonus Movement Speed for 1 second (90 second cooldown).\",\"colloq\":\";\",\"plaintext\":\"Activate to remove all crowd control debuffs and grant massive Movement Speed\",\"from\":[\"3140\",\"1037\",\"1053\"],\"image\":{\"full\":\"3139.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":432,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":525,\"purchasable\":true,\"total\":3600,\"sell\":2520},\"tags\":[\"SpellBlock\",\"Damage\",\"LifeSteal\",\"Active\",\"NonbootsMovement\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":65,\"FlatSpellBlockMod\":35,\"PercentLifeStealMod\":0.1},\"effect\":{\"Effect1Amount\":\"0.5\",\"Effect2Amount\":\"1\",\"Effect3Amount\":\"90\"},\"depth\":3,\"id\":3139},\"3140\":{\"name\":\"Quicksilver Sash\",\"description\":\"<stats>+30 Magic Resist</stats><br><br><active>UNIQUE Active - Quicksilver:</active> Removes all crowd control debuffs (90 second cooldown).\",\"colloq\":\";qss\",\"plaintext\":\"Activate to remove all crowd control debuffs\",\"from\":[\"1033\"],\"into\":[\"3139\",\"3137\"],\"image\":{\"full\":\"3140.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":0,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":850,\"purchasable\":true,\"total\":1300,\"sell\":910},\"tags\":[\"Active\",\"SpellBlock\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatSpellBlockMod\":30},\"effect\":{\"Effect1Amount\":\"90\"},\"depth\":2,\"id\":3140},\"3142\":{\"name\":\"Youmuu's Ghostblade\",\"description\":\"<stats>+60 Attack Damage<br>+10% Cooldown Reduction</stats><br><br><unique>UNIQUE Passive:</unique> +15 <a href='Lethality'>Lethality</a><br><unique>UNIQUE Passive:</unique> +20 Movement Speed out of Combat<br><active>UNIQUE Active:</active> Grants +20% Movement Speed for 6 seconds (45 second cooldown).\",\"colloq\":\";lethality\",\"plaintext\":\"Activate to greatly increase Movement Speed\",\"from\":[\"3133\",\"3134\"],\"image\":{\"full\":\"3142.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":48,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":700,\"purchasable\":true,\"total\":2900,\"sell\":2030},\"tags\":[\"Damage\",\"Active\",\"CooldownReduction\",\"NonbootsMovement\",\"ArmorPenetration\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":60},\"effect\":{\"Effect1Amount\":\"45\",\"Effect2Amount\":\"15\",\"Effect3Amount\":\"0.2\",\"Effect4Amount\":\"0\",\"Effect5Amount\":\"6\",\"Effect6Amount\":\"20\"},\"depth\":3,\"id\":3142},\"3143\":{\"name\":\"Randuin's Omen\",\"description\":\"<stats>+350 Health<br>+60 Armor</stats><br><br><unique>UNIQUE Passive:</unique> -20% damage taken from basic attack critical strikes.<br><unique>UNIQUE Passive - Cold Steel:</unique> When hit by basic attacks, reduces the attacker's Attack Speed by 15% for 1 second.<br><active>UNIQUE Active:</active> Slows the Movement Speed of nearby enemy units by 55% for 2 seconds (60 second cooldown).\",\"colloq\":\";\",\"plaintext\":\"Greatly increases defenses, activate to slow nearby enemies\",\"from\":[\"1028\",\"3082\",\"1028\"],\"image\":{\"full\":\"3143.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":96,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":1100,\"purchasable\":true,\"total\":2900,\"sell\":2030},\"tags\":[\"Active\",\"Armor\",\"Health\",\"Slow\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":350,\"FlatArmorMod\":60},\"effect\":{\"Effect1Amount\":\"2\",\"Effect2Amount\":\"0.2\",\"Effect3Amount\":\"-0.55\",\"Effect4Amount\":\"1\",\"Effect5Amount\":\"-0.15\",\"Effect6Amount\":\"0.15\",\"Effect7Amount\":\"0.3\"},\"depth\":3,\"id\":3143},\"3144\":{\"name\":\"Bilgewater Cutlass\",\"description\":\"<stats>+25 Attack Damage<br>+10% Life Steal</stats><br><br><active>UNIQUE Active:</active> Deals 100 magic damage and slows the target champion's Movement Speed by 25% for 2 seconds (90 second cooldown).\",\"colloq\":\";\",\"plaintext\":\"Activate to deal magic damage and slow target champion\",\"from\":[\"1053\",\"1036\"],\"into\":[\"3146\",\"3153\"],\"image\":{\"full\":\"3144.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":144,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":250,\"purchasable\":true,\"total\":1500,\"sell\":1050},\"tags\":[\"Active\",\"Damage\",\"LifeSteal\",\"Slow\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":25,\"PercentLifeStealMod\":0.1},\"effect\":{\"Effect1Amount\":\"-0.25\",\"Effect2Amount\":\"2\",\"Effect3Amount\":\"90\",\"Effect4Amount\":\"100\"},\"depth\":3,\"id\":3144},\"3145\":{\"name\":\"Hextech Revolver\",\"description\":\"<stats>+40 Ability Power</stats><br><br><unique>UNIQUE Passive - Magic Bolt:</unique> Damaging an enemy champion with a basic attack shocks them for <scaleLevel>50 - 125</scaleLevel> bonus magic damage (40 second cooldown, shared with other <font color='#9999FF'><a href='itembolt'>Hextech</a></font> items).<br><br>Magic Bolt's cooldown is reduced by Active Item cooldown reduction.<br><br><rules>(Damage scales based on level. Hextech effects can trigger other item spell effects.)</rules>\",\"colloq\":\";\",\"plaintext\":\"Increases Ability Power. Deal bonus magic damage on attack periodically.\",\"from\":[\"1052\",\"1052\"],\"into\":[\"3146\",\"3152\",\"3030\"],\"image\":{\"full\":\"3145.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":192,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":180,\"purchasable\":true,\"total\":1050,\"sell\":735},\"tags\":[\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMagicDamageMod\":40},\"effect\":{\"Effect1Amount\":\"0\",\"Effect2Amount\":\"50\",\"Effect3Amount\":\"0\",\"Effect4Amount\":\"125\",\"Effect5Amount\":\"40\"},\"depth\":2,\"id\":3145},\"3146\":{\"name\":\"Hextech Gunblade\",\"description\":\"<stats>+40 Attack Damage<br>+80 Ability Power</stats><br><br><unique>UNIQUE Passive:</unique> Heal for 15% of damage dealt. This is 33% as effective for Area of Effect damage.<br><active>UNIQUE Active - Lightning Bolt:</active> Deals <scaleLevel>175 - 250</scaleLevel> (+30% of Ability Power) magic damage and slows the target champion's Movement Speed by 40% for 2 seconds (40 second cooldown, shared with other <font color='#9999FF'><a href='itembolt'>Hextech</a></font> items).\",\"colloq\":\";\",\"plaintext\":\"Increases Attack Damage and Ability Power, activate to slow a target\",\"from\":[\"3144\",\"3145\"],\"image\":{\"full\":\"3146.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":240,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":850,\"purchasable\":true,\"total\":3400,\"sell\":2380},\"tags\":[\"Active\",\"Damage\",\"LifeSteal\",\"Slow\",\"SpellDamage\",\"SpellVamp\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":40,\"FlatMagicDamageMod\":80},\"effect\":{\"Effect1Amount\":\"0.15\",\"Effect2Amount\":\"40\",\"Effect3Amount\":\"175\",\"Effect4Amount\":\"250\"},\"depth\":4,\"id\":3146},\"3147\":{\"name\":\"Duskblade of Draktharr\",\"description\":\"<stats>+65 Attack Damage</stats><br><br><unique>UNIQUE Passive:</unique> +15 <a href='Lethality'>Lethality</a><br><unique>UNIQUE Passive:</unique> +20 Movement Speed out of Combat.<br><unique>UNIQUE Passive - Nightstalker:</unique> After being unseen for at least 1 second, your next Basic Attack against an enemy champion will deal 75 (+200% Lethality) true damage on-hit (lasts for 4 seconds after being seen by an enemy champion).<br><unique>UNIQUE Passive - Blackout:</unique> When spotted by an enemy ward, causes a blackout for 8 seconds, revealing invisible traps and revealing / disabling wards (90 second cooldown).\",\"colloq\":\";lethality\",\"plaintext\":\"Deals additional true damage on-hit and provides true sight periodically\",\"from\":[\"3134\",\"1038\"],\"image\":{\"full\":\"3147.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":288,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":850,\"purchasable\":true,\"total\":3250,\"sell\":2275},\"tags\":[\"Damage\",\"OnHit\",\"NonbootsMovement\",\"ArmorPenetration\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":65},\"effect\":{\"Effect1Amount\":\"15\",\"Effect2Amount\":\"20\",\"Effect3Amount\":\"0\",\"Effect4Amount\":\"90\",\"Effect5Amount\":\"75\",\"Effect6Amount\":\"2\"},\"depth\":3,\"id\":3147},\"3151\":{\"name\":\"Liandry's Torment\",\"description\":\"<stats>+80 Ability Power<br>+300 Health</stats><br><br><unique>UNIQUE Passive - Eyes of Pain:</unique> +15 <a href='FlatMagicPen'>Magic Penetration</a><br><unique>UNIQUE Passive:</unique> Spells burn enemies for 3 seconds, dealing bonus magic damage equal to 2% of their current Health per second. Burn damage is doubled against <a href='MovementImpaired'>movement-impaired</a> units.\",\"colloq\":\";mask\",\"plaintext\":\"Spell damage burns enemies for a portion of their Health\",\"stacks\":0,\"from\":[\"3136\",\"1026\"],\"image\":{\"full\":\"3151.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":336,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":750,\"purchasable\":true,\"total\":3100,\"sell\":2170},\"tags\":[\"Health\",\"MagicPenetration\",\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":300,\"FlatMagicDamageMod\":80},\"effect\":{\"Effect1Amount\":\"15\",\"Effect2Amount\":\"0.02\",\"Effect3Amount\":\"3\",\"Effect4Amount\":\"100\",\"Effect5Amount\":\"2\"},\"depth\":3,\"id\":3151},\"3152\":{\"name\":\"Hextech Protobelt-01\",\"description\":\"<stats>+300 Health<br>+60 Ability Power<br>+10% Cooldown Reduction</stats><br><br><unique>UNIQUE Active - Fire Bolt:</unique> Dash forward and unleash a nova of fire bolts that deal <scaleLevel>75 - 150</scaleLevel> (+25% of your Ability Power) as magic damage (40 second cooldown, shared with other <font color='#9999FF'><a href='itembolt'>Hextech</a></font> items).<br><br>Champions and Monsters hit by multiple fire bolts take 10% damage per additional bolt.<br><br><rules>(This dash cannot pass through terrain.)</rules>\",\"colloq\":\"rocket belt;\",\"plaintext\":\"Activate to dash forward and unleash a fiery explosion\",\"from\":[\"3145\",\"3067\"],\"image\":{\"full\":\"3152.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":384,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":650,\"purchasable\":true,\"total\":2500,\"sell\":1750},\"tags\":[\"Health\",\"SpellDamage\",\"Active\",\"CooldownReduction\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":300,\"FlatMagicDamageMod\":60},\"effect\":{\"Effect1Amount\":\"0.12\",\"Effect2Amount\":\"0.04\",\"Effect3Amount\":\"40\",\"Effect4Amount\":\"75\",\"Effect5Amount\":\"150\",\"Effect6Amount\":\"0.1\",\"Effect7Amount\":\"0.25\",\"Effect8Amount\":\"40\",\"Effect9Amount\":\"0.5\"},\"depth\":3,\"id\":3152},\"3153\":{\"name\":\"Blade of the Ruined King\",\"description\":\"<stats>+40 Attack Damage<br>+25% Attack Speed<br>+12% Life Steal</stats><br><br><unique>UNIQUE Passive:</unique> Basic attacks deal 8% of the target's current Health as bonus physical damage on hit.<br><active>UNIQUE Active:</active> Deals 100 magic damage to target champion and steals 25% of their Movement Speed for 3 seconds (90 second cooldown).<br><br><font size='14' color='#8c8c8c'>Minimum bonus physical damage dealt is 15.<br>Maximum bonus physical damage dealt to monsters and minions is 60.<br>User's Life Steal is applied to bonus physical damage dealt.</font>\",\"colloq\":\";brk;bork;bork;bork;botrk\",\"plaintext\":\"Deals damage based on target's Health, can steal Movement Speed\",\"from\":[\"3144\",\"1043\"],\"image\":{\"full\":\"3153.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":432,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":900,\"purchasable\":true,\"total\":3400,\"sell\":2380},\"tags\":[\"Active\",\"AttackSpeed\",\"Damage\",\"LifeSteal\",\"NonbootsMovement\",\"OnHit\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":40,\"PercentAttackSpeedMod\":0.25,\"PercentLifeStealMod\":0.12},\"effect\":{\"Effect1Amount\":\"0.08\"},\"depth\":4,\"id\":3153},\"3155\":{\"name\":\"Hexdrinker\",\"description\":\"<stats>+20 Attack Damage<br>+35 Magic Resist</stats><br><br><unique>UNIQUE Passive - Lifeline:</unique> Upon taking magic damage that would reduce Health below 30%, grants a shield that absorbs 110 to 280 (based on level) magic damage for 5 seconds (90 second cooldown).\",\"colloq\":\";\",\"plaintext\":\"Increases Attack Damage and Magic Resist\",\"stacks\":0,\"from\":[\"1036\",\"1033\"],\"into\":[\"3156\"],\"image\":{\"full\":\"3155.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":0,\"y\":288,\"w\":48,\"h\":48},\"gold\":{\"base\":500,\"purchasable\":true,\"total\":1300,\"sell\":910},\"tags\":[\"Damage\",\"SpellBlock\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":20,\"FlatSpellBlockMod\":35},\"effect\":{\"Effect1Amount\":\"0.3\",\"Effect2Amount\":\"110\",\"Effect3Amount\":\"5\",\"Effect4Amount\":\"90\",\"Effect5Amount\":\"280\",\"Effect6Amount\":\"100\",\"Effect7Amount\":\"10\"},\"depth\":2,\"id\":3155},\"3156\":{\"name\":\"Maw of Malmortius\",\"description\":\"<stats>+50 Attack Damage<br>+45 Magic Resist<br>+10% Cooldown Reduction</stats><br><br><unique>UNIQUE Passive - Lifeline:</unique> Upon taking magic damage that would reduce Health below 30%, grants a shield that absorbs magic damage equal to 300 + 1 per bonus Magic Resistance for 5 seconds (90 second cooldown).<br><unlockedPassive>Lifegrip:</unlockedPassive> When <i>Lifeline</i> triggers, gain +20 Attack Damage, +10% Spell Vamp and +10% Life Steal until out of combat.\",\"colloq\":\";\",\"plaintext\":\"Grants bonus Attack Damage when Health is low\",\"stacks\":0,\"from\":[\"3155\",\"3133\"],\"image\":{\"full\":\"3156.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":48,\"y\":288,\"w\":48,\"h\":48},\"gold\":{\"base\":850,\"purchasable\":true,\"total\":3250,\"sell\":2275},\"tags\":[\"SpellBlock\",\"Damage\",\"LifeSteal\",\"CooldownReduction\",\"SpellVamp\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":50,\"FlatSpellBlockMod\":45},\"effect\":{\"Effect1Amount\":\"1\",\"Effect2Amount\":\"35\",\"Effect3Amount\":\"0.3\",\"Effect4Amount\":\"300\",\"Effect5Amount\":\"5\",\"Effect6Amount\":\"90\",\"Effect7Amount\":\"20\",\"Effect8Amount\":\"0\",\"Effect9Amount\":\"0.1\",\"Effect10Amount\":\"0.1\"},\"depth\":3,\"id\":3156},\"3157\":{\"name\":\"Zhonya's Hourglass\",\"description\":\"<stats>+70 Ability Power<br>+45 Armor<br>+10% Cooldown Reduction</stats><br><br><active>UNIQUE Active - Stasis:</active> Champion becomes invulnerable and untargetable for 2.5 seconds, but is unable to move, attack, cast spells, or use items during this time (120 second cooldown).\",\"colloq\":\";zhg;zonyas\",\"plaintext\":\"Activate to become invincible but unable to take actions\",\"from\":[\"3191\",\"3108\"],\"image\":{\"full\":\"3157.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":96,\"y\":288,\"w\":48,\"h\":48},\"gold\":{\"base\":800,\"purchasable\":true,\"total\":2900,\"sell\":2030},\"tags\":[\"Armor\",\"SpellDamage\",\"Active\",\"CooldownReduction\"],\"maps\":{\"8\":true,\"10\":false,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMagicDamageMod\":70,\"FlatArmorMod\":45},\"effect\":{\"Effect1Amount\":\"0\",\"Effect2Amount\":\"2.5\",\"Effect3Amount\":\"120\"},\"depth\":3,\"id\":3157},\"3158\":{\"name\":\"Ionian Boots of Lucidity\",\"description\":\"<unique>UNIQUE Passive:</unique> +10% Cooldown Reduction<br><unique>UNIQUE Passive - Enhanced Movement:</unique> +45 Movement Speed<br><unique>UNIQUE Passive:</unique> Reduces Summoner Spell cooldowns by 10%<br><br><br><rules><font color='#FDD017'>''This item is dedicated in honor of Ionia's victory over Noxus in the Rematch for the Southern Provinces on 10 December, 20 CLE.''</font></rules>\",\"colloq\":\";\",\"plaintext\":\"Increases Movement Speed and Cooldown Reduction\",\"from\":[\"1001\"],\"image\":{\"full\":\"3158.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":144,\"y\":288,\"w\":48,\"h\":48},\"gold\":{\"base\":600,\"purchasable\":true,\"total\":900,\"sell\":630},\"tags\":[\"Boots\",\"CooldownReduction\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMovementSpeedMod\":45},\"effect\":{\"Effect1Amount\":\"-0.1\",\"Effect2Amount\":\"0.1\"},\"depth\":2,\"id\":3158},\"3165\":{\"name\":\"Morellonomicon\",\"description\":\"<stats>+100 Ability Power<br><mana>+400 Mana</mana></stats><br><br><unique>UNIQUE Passive:</unique> +20% Cooldown Reduction<br><unique>UNIQUE Passive:</unique> Dealing magic damage to champions below 35% Health inflicts <a href='GrievousWounds'>Grievous Wounds</a> for 8 seconds.<br><unique>UNIQUE Passive:</unique> Kills and Assists restore 20% of your maximum Mana.\",\"colloq\":\";nmst;grievous\",\"plaintext\":\"Greatly increases Ability Power and Cooldown Reduction\",\"from\":[\"3108\",\"1052\",\"3802\"],\"image\":{\"full\":\"3165.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":192,\"y\":288,\"w\":48,\"h\":48},\"gold\":{\"base\":665,\"purchasable\":true,\"total\":2900,\"sell\":2030},\"tags\":[\"SpellDamage\",\"Mana\",\"ManaRegen\",\"CooldownReduction\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMPPoolMod\":400,\"FlatMagicDamageMod\":100},\"effect\":{\"Effect1Amount\":\"20\",\"Effect2Amount\":\"35\",\"Effect3Amount\":\"8\",\"Effect4Amount\":\"0.2\"},\"depth\":3,\"id\":3165},\"3170\":{\"name\":\"Moonflair Spellblade\",\"description\":\"<stats>+50 Ability Power<br>+50 Armor<br>+50 Magic Resist</stats><br><br><unique>UNIQUE Passive - Tenacity:</unique> Reduces the duration of stuns, slows, taunts, fears, silences, blinds, polymorphs, and immobilizes by 35%.\",\"colloq\":\";\",\"plaintext\":\"Improves defense and reduces duration of disabling effects\",\"from\":[\"3191\",\"1057\"],\"image\":{\"full\":\"3170.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":240,\"y\":288,\"w\":48,\"h\":48},\"gold\":{\"base\":580,\"purchasable\":true,\"total\":2500,\"sell\":1750},\"tags\":[\"Armor\",\"SpellBlock\",\"SpellDamage\",\"Tenacity\"],\"maps\":{\"8\":true,\"10\":true,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatSpellBlockMod\":50,\"FlatMagicDamageMod\":50,\"FlatArmorMod\":50},\"depth\":3,\"id\":3170},\"3174\":{\"name\":\"Athene's Unholy Grail\",\"description\":\"<stats>+40 Ability Power<br>+30 Magic Resist<br>+20% Cooldown Reduction<br><mana>+75% Base Mana Regen </mana></stats><br><br><unique>UNIQUE Passive:</unique> Gain 20% of the <a href='premitigation'><font color='#6666FF'><u>premitigation</u></font></a> damage dealt to champions as Blood Charges, up to <scaleLevel>100 - 250</scaleLevel>  max. Healing or shielding another ally consumes charges to heal them, up to the original effect amount.<br><unique>UNIQUE Passive - Harmony:</unique> Grants bonus % Base Health Regen equal to your bonus % Base Mana Regen.<br><br><rules>(Maximum amount of Blood Charges stored is based on level. Healing amplification is applied to the total heal value.)</rules>\",\"colloq\":\";\",\"plaintext\":\"Deal damage to empower your heals and shields\",\"from\":[\"3108\",\"3028\"],\"image\":{\"full\":\"3174.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":288,\"y\":288,\"w\":48,\"h\":48},\"gold\":{\"base\":400,\"purchasable\":true,\"total\":2100,\"sell\":1470},\"tags\":[\"SpellBlock\",\"HealthRegen\",\"SpellDamage\",\"ManaRegen\",\"CooldownReduction\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatSpellBlockMod\":30,\"FlatMagicDamageMod\":40},\"effect\":{\"Effect1Amount\":\"20\",\"Effect2Amount\":\"30\",\"Effect3Amount\":\"2\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"100\",\"Effect6Amount\":\"0.2\",\"Effect7Amount\":\"100\",\"Effect8Amount\":\"250\",\"Effect9Amount\":\"1\",\"Effect10Amount\":\"1\",\"Effect11Amount\":\"0.25\",\"Effect12Amount\":\"600\",\"Effect13Amount\":\"8\",\"Effect14Amount\":\"90\"},\"depth\":3,\"id\":3174},\"3175\":{\"name\":\"Head of Kha'Zix\",\"description\":\"<unique>UNIQUE Active - Bonetooth Totem:</unique> Places a Stealth Ward that lasts 180 seconds (90 Second cooldown). Limit 3 Stealth Wards on the map per player.<br><br><unique>UNIQUE Passive - Mementos of the Hunt:</unique> Rengar collects trophies when killing Champions and gains bonus effects based on how many trophies he has. Kills and assists grant 1 trophy.<br><br><passive>3 Trophies:</passive> Rengar gains 25 Movement Speed whilst out of combat or in brush. <br><passive>6 Trophies:</passive> Increases the range of Rengar's Leap by 125.<br><passive>12 Trophies:</passive> Thrill of the Hunt's duration is increased by 5 seconds.<br><passive>20 Trophies:</passive> Rengar gains the movement speed bonus of Thrill of the Hunt while he is stealthed.\",\"colloq\":\"\",\"plaintext\":\"\",\"specialRecipe\":3169,\"inStore\":false,\"requiredChampion\":\"Rengar\",\"hideFromAll\":true,\"image\":{\"full\":\"3175.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":336,\"y\":288,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[\"Active\",\"Trinket\",\"Vision\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"25\",\"Effect2Amount\":\"125\",\"Effect3Amount\":\"5\",\"Effect4Amount\":\"180\",\"Effect5Amount\":\"90\"},\"id\":3175},\"3181\":{\"name\":\"Sanguine Blade\",\"description\":\"<stats>+45 Attack Damage<br>+10% Life Steal</stats><br><br><unique>UNIQUE Passive:</unique> Basic attacks grant +6 Attack Damage and +1% Life Steal for 8 seconds on hit (effect stacks up to 5 times).\",\"colloq\":\";\",\"plaintext\":\"Greatly increases Attack Damage and Life Steal\",\"from\":[\"1037\",\"1053\"],\"image\":{\"full\":\"3181.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":384,\"y\":288,\"w\":48,\"h\":48},\"gold\":{\"base\":625,\"purchasable\":true,\"total\":2400,\"sell\":1680},\"tags\":[\"Damage\",\"LifeSteal\"],\"maps\":{\"8\":false,\"10\":false,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":45,\"PercentLifeStealMod\":0.1},\"effect\":{\"Effect1Amount\":\"6\",\"Effect2Amount\":\"0.01\",\"Effect3Amount\":\"8\",\"Effect4Amount\":\"5\"},\"depth\":3,\"id\":3181},\"3184\":{\"name\":\"Guardian's Hammer\",\"description\":\"<stats>+150 Health<br>+20 Attack Damage<br>+10% Life Steal</stats><br><br><groupLimit>Limited to 1 Guardian's Item.</groupLimit>\",\"colloq\":\";dblade\",\"plaintext\":\"Good starting item for attackers\",\"image\":{\"full\":\"3184.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":432,\"y\":288,\"w\":48,\"h\":48},\"gold\":{\"base\":950,\"purchasable\":true,\"total\":950,\"sell\":380},\"tags\":[\"Health\",\"Damage\",\"LifeSteal\",\"Lane\"],\"maps\":{\"8\":false,\"10\":false,\"11\":false,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":20,\"FlatHPPoolMod\":150,\"PercentLifeStealMod\":0.1},\"id\":3184},\"3185\":{\"name\":\"The Lightbringer\",\"description\":\"<stats>+30 Attack Damage<br>+30% Critical Strike Chance</stats><br><br><unique>UNIQUE Passive:</unique> Critical Strikes cause enemies to bleed for an additional 90% of bonus Attack Damage as magic damage over 3 seconds and reveal them for the duration.<br><unique>UNIQUE Passive - Trap Detection:</unique> Nearby stealthed enemy traps are revealed.<br><active>UNIQUE Active - Hunter's Sight:</active> A stealth-detecting mist grants vision in the target area for 5 seconds, revealing enemy champions that enter for 3 seconds (60 second cooldown).\",\"colloq\":\";lb\",\"plaintext\":\"Critical Strikes cause your target to bleed and be revealed\",\"from\":[\"3122\",\"1018\"],\"image\":{\"full\":\"3185.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":0,\"y\":336,\"w\":48,\"h\":48},\"gold\":{\"base\":350,\"purchasable\":true,\"total\":2350,\"sell\":1645},\"tags\":[\"Active\",\"CriticalStrike\",\"Damage\",\"OnHit\",\"Stealth\",\"Vision\"],\"maps\":{\"8\":true,\"10\":false,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":30,\"FlatCritChanceMod\":0.3},\"effect\":{\"Effect1Amount\":\"0.9\",\"Effect2Amount\":\"3\",\"Effect3Amount\":\"0\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"3\",\"Effect6Amount\":\"60\"},\"depth\":3,\"id\":3185},\"3187\":{\"name\":\"Arcane Sweeper\",\"description\":\"<stats>+225 Health<br>+250 Mana<br>+25 Armor<br>+20% Cooldown Reduction</stats><br><br><unique>UNIQUE Passive - Trap Detection:</unique> Grants <font color='#ee91d7'>True Sight</font>  of nearby enemy traps.<br><active>UNIQUE Active - Hunter's Sight:</active> An arcane mist grants vision in the target area for 5 seconds, revealing enemy champions in the area for 3 seconds (60 second cooldown).\",\"colloq\":\";\",\"plaintext\":\"Activate to reveal a nearby area of the map\",\"from\":[\"3024\",\"3067\"],\"image\":{\"full\":\"3187.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":48,\"y\":336,\"w\":48,\"h\":48},\"gold\":{\"base\":350,\"purchasable\":true,\"total\":2150,\"sell\":1505},\"tags\":[\"Active\",\"Armor\",\"CooldownReduction\",\"Health\",\"Mana\",\"Stealth\",\"Vision\"],\"maps\":{\"8\":true,\"10\":false,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":225,\"FlatMPPoolMod\":250,\"FlatArmorMod\":25},\"effect\":{\"Effect1Amount\":\"-0.2\",\"Effect2Amount\":\"0\",\"Effect3Amount\":\"0\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"3\",\"Effect6Amount\":\"60\"},\"depth\":3,\"id\":3187},\"3190\":{\"name\":\"Locket of the Iron Solari\",\"description\":\"<stats>+30 Armor<br>+60 Magic Resist</stats><br><br><active>UNIQUE Active:</active> Grants a decaying shield to nearby allies for 2.5 seconds that absorbs up to 35 (+35 per level) damage (90 second cooldown).<br><br><rules>(Half effect if the target has been affected by another Locket of the Iron Solari recently.)</rules>\",\"colloq\":\";\",\"plaintext\":\"Activate to shield nearby allies from damage\",\"from\":[\"3105\",\"1033\"],\"image\":{\"full\":\"3190.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":96,\"y\":336,\"w\":48,\"h\":48},\"gold\":{\"base\":650,\"purchasable\":true,\"total\":2200,\"sell\":1540},\"tags\":[\"SpellBlock\",\"Armor\",\"Active\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatSpellBlockMod\":60,\"FlatArmorMod\":30},\"effect\":{\"Effect1Amount\":\"0\",\"Effect2Amount\":\"0.75\",\"Effect3Amount\":\"2.5\",\"Effect4Amount\":\"35\",\"Effect5Amount\":\"35\",\"Effect6Amount\":\"90\"},\"depth\":3,\"id\":3190},\"3191\":{\"name\":\"Seeker's Armguard\",\"description\":\"<stats>+30 Armor<br>+20 Ability Power</stats><br><br><unique>UNIQUE Passive:</unique> Killing a unit grants 0.5 bonus Armor and Ability Power. This bonus stacks up to 30 times.\",\"colloq\":\";\",\"plaintext\":\"Increases Armor and Ability Power\",\"from\":[\"1029\",\"1052\",\"1029\"],\"into\":[\"3090\",\"3170\",\"3157\"],\"image\":{\"full\":\"3191.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":144,\"y\":336,\"w\":48,\"h\":48},\"gold\":{\"base\":165,\"purchasable\":true,\"total\":1200,\"sell\":840},\"tags\":[\"Armor\",\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMagicDamageMod\":20,\"FlatArmorMod\":30},\"effect\":{\"Effect1Amount\":\"0.5\",\"Effect2Amount\":\"30\"},\"depth\":2,\"id\":3191},\"3193\":{\"name\":\"Gargoyle Stoneplate\",\"description\":\"<stats>+40 Armor<br>+40 Magic Resist</stats></stats><br><br><unique>UNIQUE Passive - Stone Skin:</unique> If 3+ enemy champions are nearby, grants 40 bonus Armor and Magic Resist.<br><active>UNIQUE Active - Metallicize:</active> Increases Health by 40% and increases champion size, but reduces damage dealt by 60% for 4 seconds (90 second cooldown). If Stone Skin is active, the Health increase becomes 100% instead.\",\"colloq\":\";\",\"plaintext\":\"Greatly increases defense near multiple enemies.\",\"from\":[\"1031\",\"1057\"],\"image\":{\"full\":\"3193.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":192,\"y\":336,\"w\":48,\"h\":48},\"gold\":{\"base\":980,\"purchasable\":true,\"total\":2500,\"sell\":1750},\"tags\":[\"Health\",\"SpellBlock\",\"Armor\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatSpellBlockMod\":40,\"FlatArmorMod\":40},\"effect\":{\"Effect1Amount\":\"40\",\"Effect2Amount\":\"850\",\"Effect3Amount\":\"3\",\"Effect4Amount\":\"4\",\"Effect5Amount\":\"0.6\",\"Effect6Amount\":\"0.4\",\"Effect7Amount\":\"0.07\",\"Effect8Amount\":\"1\",\"Effect9Amount\":\"0.25\",\"Effect10Amount\":\"90\",\"Effect11Amount\":\"1\"},\"depth\":3,\"id\":3193},\"3194\":{\"name\":\"Adaptive Helm\",\"description\":\"<stats>+300 Health<br>+55 Magic Resist<br>+100% Base Health Regeneration <br>+10% Cooldown Reduction</stats><br><br><unique>UNIQUE Passive:</unique> Taking magic damage from a spell or effect reduces all subsequent magic damage from that same spell or effect by 15% for 4 seconds.\",\"colloq\":\";\",\"plaintext\":\"Reduces damage from repeated spells and effects.\",\"from\":[\"1033\",\"3211\",\"1006\"],\"image\":{\"full\":\"3194.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":240,\"y\":336,\"w\":48,\"h\":48},\"gold\":{\"base\":1000,\"purchasable\":true,\"total\":2800,\"sell\":1960},\"tags\":[\"Health\",\"SpellBlock\",\"HealthRegen\",\"CooldownReduction\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":300,\"FlatSpellBlockMod\":55},\"effect\":{\"Effect1Amount\":\"15\",\"Effect2Amount\":\"4\",\"Effect3Amount\":\"25\"},\"depth\":3,\"id\":3194},\"3196\":{\"name\":\"The Hex Core mk-1\",\"description\":\"<stats>+3 Ability Power per level<br>+15 Mana per level</stats><br><br><unique>UNIQUE Passive - Progress:</unique> Viktor can upgrade one of his basic spells.\",\"colloq\":\";viktor\",\"plaintext\":\"Allows Viktor to improve an ability of his choice\",\"from\":[\"3200\"],\"requiredChampion\":\"Viktor\",\"into\":[\"3197\"],\"image\":{\"full\":\"3196.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":288,\"y\":336,\"w\":48,\"h\":48},\"gold\":{\"base\":1250,\"purchasable\":true,\"total\":1250,\"sell\":875},\"tags\":[\"Mana\",\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"3\",\"Effect2Amount\":\"15\"},\"depth\":2,\"id\":3196},\"3197\":{\"name\":\"The Hex Core mk-2\",\"description\":\"<stats>+6 Ability Power per level<br>+20 Mana per level</stats><br><br><unique>UNIQUE Passive - Progress:</unique> Viktor can upgrade one of his basic spells.\",\"colloq\":\";viktor\",\"plaintext\":\"Allows Viktor to improve an ability of his choice\",\"from\":[\"3196\"],\"requiredChampion\":\"Viktor\",\"into\":[\"3198\"],\"image\":{\"full\":\"3197.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":336,\"y\":336,\"w\":48,\"h\":48},\"gold\":{\"base\":1000,\"purchasable\":true,\"total\":2250,\"sell\":1575},\"tags\":[\"Mana\",\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"6\",\"Effect2Amount\":\"20\"},\"depth\":3,\"id\":3197},\"3198\":{\"name\":\"Perfect Hex Core\",\"description\":\"<stats>+10 Ability Power per level<br>+25 Mana per level</stats><br><br><unique>UNIQUE Passive - Glorious Evolution:</unique> Viktor has reached the pinnacle of his power, upgrading Chaos Storm in addition to his basic spells.\",\"colloq\":\";viktor\",\"plaintext\":\"Allows Viktor to improve an ability of his choice\",\"from\":[\"3197\"],\"requiredChampion\":\"Viktor\",\"image\":{\"full\":\"3198.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":384,\"y\":336,\"w\":48,\"h\":48},\"gold\":{\"base\":750,\"purchasable\":true,\"total\":3000,\"sell\":2100},\"tags\":[\"Mana\",\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"10\",\"Effect2Amount\":\"25\"},\"depth\":4,\"id\":3198},\"3200\":{\"name\":\"Prototype Hex Core\",\"description\":\"<stats>+1 Ability Power per level<br>+10 Mana per level</stats><br><br><unique>UNIQUE Passive - Progress:</unique> This item can be upgraded three times to enhance Viktor's basic abilities.\",\"colloq\":\";viktor\",\"plaintext\":\"Increases Ability Power and can be upgraded to improve Viktor's abilities\",\"inStore\":false,\"requiredChampion\":\"Viktor\",\"into\":[\"3196\"],\"image\":{\"full\":\"3200.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":432,\"y\":336,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"1\",\"Effect2Amount\":\"10\"},\"id\":3200},\"3211\":{\"name\":\"Spectre's Cowl\",\"description\":\"<stats>+250 Health<br>+25 Magic Resist</stats><br><br><unique>UNIQUE Passive:</unique> Grants 150% Base Health Regen for up to 10 seconds after taking damage from an enemy champion.\",\"colloq\":\";hat\",\"plaintext\":\"Improves defense and grants regeneration upon being damaged\",\"from\":[\"1028\",\"1033\"],\"into\":[\"3065\",\"3001\",\"3194\"],\"image\":{\"full\":\"3211.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":0,\"y\":384,\"w\":48,\"h\":48},\"gold\":{\"base\":350,\"purchasable\":true,\"total\":1200,\"sell\":840},\"tags\":[\"Health\",\"HealthRegen\",\"SpellBlock\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":250,\"FlatSpellBlockMod\":25},\"effect\":{\"Effect1Amount\":\"1.5\",\"Effect2Amount\":\"10\"},\"depth\":2,\"id\":3211},\"3222\":{\"name\":\"Mikael's Crucible\",\"description\":\"<stats>+40 Magic Resist<br>+10% Cooldown Reduction<br><mana>+100% Base Mana Regen </mana></stats><br><br><unique>UNIQUE Passive:</unique> +20% Heal and Shield Power<br><unique>UNIQUE Passive - Harmony:</unique> Grants bonus % Base Health Regen equal to your bonus % Base Mana Regen.<br><active>UNIQUE Active:</active> Cleanses all stuns, roots, taunts, fears, silences, and slows on an allied champion and grants them slow immunity for 2 seconds (120 second cooldown). <br><br>Cleansing an effect grants the ally 40% movement speed for 2 seconds.\",\"colloq\":\";\",\"plaintext\":\"Activate to remove all disabling effects from an allied champion\",\"from\":[\"3028\",\"3114\"],\"image\":{\"full\":\"3222.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":48,\"y\":384,\"w\":48,\"h\":48},\"gold\":{\"base\":500,\"purchasable\":true,\"total\":2100,\"sell\":1470},\"tags\":[\"SpellBlock\",\"HealthRegen\",\"ManaRegen\",\"Active\",\"CooldownReduction\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatSpellBlockMod\":40},\"effect\":{\"Effect1Amount\":\"2\",\"Effect2Amount\":\"5\",\"Effect3Amount\":\"0.5\",\"Effect4Amount\":\"0.25\",\"Effect5Amount\":\"600\",\"Effect6Amount\":\"8\",\"Effect7Amount\":\"90\",\"Effect8Amount\":\"1\",\"Effect9Amount\":\"1\",\"Effect10Amount\":\"0.4\",\"Effect11Amount\":\"0.2\",\"Effect12Amount\":\"2\",\"Effect13Amount\":\"120\"},\"depth\":3,\"id\":3222},\"3252\":{\"name\":\"Poacher's Dirk\",\"description\":\"<stats>+10 Attack Damage</stats><br><br><unique>UNIQUE Passive:</unique> +20 Movement Speed out of Combat<br><unique>UNIQUE Passive:</unique> After poaching 3 large monsters from the enemy jungle (50 second cooldown), transforms into a Serrated Dirk.\",\"colloq\":\";serrated dirk;lethality\",\"plaintext\":\"Transforms into a Serrated Dirk after poaching in the enemy jungle.\",\"stacks\":0,\"from\":[\"1036\"],\"image\":{\"full\":\"3252.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":96,\"y\":384,\"w\":48,\"h\":48},\"gold\":{\"base\":250,\"purchasable\":true,\"total\":600,\"sell\":420},\"tags\":[\"Damage\",\"NonbootsMovement\",\"ArmorPenetration\"],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":10},\"effect\":{\"Effect1Amount\":\"100\",\"Effect2Amount\":\"15\",\"Effect3Amount\":\"20\",\"Effect4Amount\":\"50\"},\"depth\":2,\"id\":3252},\"3285\":{\"name\":\"Luden's Echo\",\"description\":\"<stats>+100 Ability Power<br>+10% Movement Speed</stats><br><br><unique>UNIQUE Passive - Echo:</unique> Gain charges upon moving or casting. At 100 charges, the next damaging spell hit expends all charges to deal 100 (+10% of Ability Power) bonus magic damage to up to 4 targets on hit.\",\"colloq\":\";\",\"plaintext\":\"Movement and casting builds charges that release chain lightning on next spell hit\",\"from\":[\"1058\",\"3113\"],\"image\":{\"full\":\"3285.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":144,\"y\":384,\"w\":48,\"h\":48},\"gold\":{\"base\":1100,\"purchasable\":true,\"total\":3200,\"sell\":2240},\"tags\":[\"NonbootsMovement\",\"OnHit\",\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"PercentMovementSpeedMod\":0.1,\"FlatMagicDamageMod\":100},\"effect\":{\"Effect1Amount\":\"100\",\"Effect2Amount\":\"100\",\"Effect3Amount\":\"4\",\"Effect4Amount\":\"0.1\",\"Effect5Amount\":\"35\",\"Effect6Amount\":\"10\"},\"depth\":3,\"id\":3285},\"3301\":{\"name\":\"Ancient Coin\",\"description\":\"<stats>+5% Cooldown Reduction<br>+2 Gold per 10 seconds</stats><br><br><unique>UNIQUE Passive - Favor:</unique> Enemy minions killed by your allies sometimes drop coins that give either <font color='#D4AF37'>20</font> gold or <font color='#44DDFF'>8%</font> missing mana (minimum 15). Cannon minions always drop coins.<hr><passive>QUEST:</passive> Earn 650 gold using this item and upgrade to <font color='#CFBF84'>Nomad's Medallion</font>.<br><passive>REWARD:</passive> <font color='#CFBF84'>Favor</font> is upgraded to <font color='#CFBF84'><a href='coinlinequestreward'>Emperor's Favor</a></font> and you receive an <font color='#29E3D6'><a href='coinlinequestrewardelixir'>Elixir Of Skill</a></font>.<br><br><groupLimit>Limited to 1 Gold Income Item.</groupLimit><br><br><i><font color='#447777'>''Gold dust rises from the desert and clings to the coin.'' - Historian Shurelya, 11 November, 23 CLE</font></i>\",\"colloq\":\";\",\"plaintext\":\"Grants gold and mana when nearby minions die that you didn't kill\",\"into\":[\"3096\"],\"image\":{\"full\":\"3301.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":192,\"y\":384,\"w\":48,\"h\":48},\"gold\":{\"base\":350,\"purchasable\":true,\"total\":350,\"sell\":140},\"tags\":[\"ManaRegen\",\"GoldPer\",\"CooldownReduction\",\"Lane\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"10\",\"Effect2Amount\":\"13\",\"Effect3Amount\":\"2\"},\"id\":3301},\"3302\":{\"name\":\"Relic Shield\",\"description\":\"<stats>+75 Health<br>+2 Gold per 10 seconds </stats><br><br><unique>UNIQUE Passive - Spoils of War:</unique> Melee basic attacks execute minions below 195 (+5 per level) Health. Killing a minion heals the owner and the nearest allied champion for 15 Health and grants them kill Gold. These effects require a nearby ally. Recharges every 40 seconds. Max 2 charges.<hr><passive>QUEST:</passive> Earn 650 gold using this item and upgrade to <font color='#CFBF84'>Targon's Brace</font>.<br><passive>REWARD:</passive> <font color='#CFBF84'>Shield Battery</font>, a permanent shield that regenerates slowly outside of combat.<br><br><groupLimit>Limited to 1 Gold Income Item.</groupLimit>\",\"colloq\":\";\",\"plaintext\":\"Kill minions periodically to heal and grant gold to a nearby ally\",\"into\":[\"3097\"],\"image\":{\"full\":\"3302.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":240,\"y\":384,\"w\":48,\"h\":48},\"gold\":{\"base\":350,\"purchasable\":true,\"total\":350,\"sell\":140},\"tags\":[\"Aura\",\"GoldPer\",\"Health\",\"Lane\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":75},\"effect\":{\"Effect1Amount\":\"195\",\"Effect2Amount\":\"15\",\"Effect3Amount\":\"2\",\"Effect4Amount\":\"5\"},\"id\":3302},\"3303\":{\"name\":\"Spellthief's Edge\",\"description\":\"<stats>+5 Ability Power<br>+2 Gold per 10 seconds<br><mana>+25% Base Mana Regen </mana></stats><br><br><unique>UNIQUE Passive - Tribute:</unique> Damaging spells and attacks against champions or buildings deal 10 additional damage and grant 8 Gold. This can occur up to 3 times every 30 seconds. Killing minions slows Tribute generation.<hr><passive>QUEST:</passive> Earn 650 gold using this item and upgrade to <font color='#CFBF84'>Frostfang</font>.<br><passive>REWARD:</passive> <font color='#CFBF84'>Tribute</font> is upgraded into <font color='#CFBF84'><a href='frostqueenslinequestreward'>Queen's Tribute</a></font>.<br><br><groupLimit>Limited to 1 Gold Income Item.</groupLimit>\",\"colloq\":\";\",\"plaintext\":\"Grants gold when you damage enemies\",\"into\":[\"3098\"],\"image\":{\"full\":\"3303.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":288,\"y\":384,\"w\":48,\"h\":48},\"gold\":{\"base\":350,\"purchasable\":true,\"total\":350,\"sell\":140},\"tags\":[\"GoldPer\",\"Lane\",\"ManaRegen\",\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMagicDamageMod\":5},\"effect\":{\"Effect1Amount\":\"10\",\"Effect2Amount\":\"8\",\"Effect3Amount\":\"12\",\"Effect4Amount\":\"3\",\"Effect5Amount\":\"30\",\"Effect6Amount\":\"2\"},\"id\":3303},\"3340\":{\"name\":\"Warding Totem (Trinket)\",\"description\":\"<groupLimit>Limited to 1 Trinket.</groupLimit><br><br><active>Active:</active> Consume a charge to place an invisible <font color='#BBFFFF'>Stealth Ward</font> which reveals the surrounding area for <scaleLevel>60 - 120</scaleLevel> seconds. <br><br>Stores one charge every <scaleLevel>180 - 90</scaleLevel> seconds, up to 2 maximum charges.<br><br>Ward duration and recharge time gradually improve with level.<br><br><rules>(Limit 3 <font color='#BBFFFF'>Stealth Wards</font> on the map per player. Switching to a <font color='#BBFFFF'>Lens</font> type trinket will disable <font color='#BBFFFF'>Trinket</font> use for 120 seconds.)</rules>\",\"colloq\":\"yellow;\",\"plaintext\":\"Periodically place a Stealth Ward\",\"image\":{\"full\":\"3340.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":336,\"y\":384,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[\"Active\",\"Jungle\",\"Lane\",\"Trinket\",\"Vision\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"60\",\"Effect2Amount\":\"180\",\"Effect3Amount\":\"120\",\"Effect4Amount\":\"90\",\"Effect5Amount\":\"2\",\"Effect6Amount\":\"9\",\"Effect7Amount\":\"30\",\"Effect8Amount\":\"120\"},\"id\":3340},\"3341\":{\"name\":\"Sweeping Lens (Trinket)\",\"description\":\"<groupLimit>Limited to 1 Trinket.</groupLimit><br><br><active>Active:</active> Scans an area for 6 seconds, warning against hidden hostile units and revealing invisible traps and revealing / disabling wards (90 to 60 second cooldown).<br><br>Cast range and sweep radius gradually improve with level.<br><br><rules>(Switching to a <font color='#BBFFFF'>Totem</font>-type trinket will disable <font color='#BBFFFF'>Trinket</font> use for 120 seconds.)</rules>\",\"colloq\":\"red;\",\"plaintext\":\"Detects and disables nearby invisible wards and traps\",\"image\":{\"full\":\"3341.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":384,\"y\":384,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[\"Active\",\"Jungle\",\"Trinket\",\"Vision\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"6\",\"Effect2Amount\":\"90\",\"Effect3Amount\":\"400\",\"Effect4Amount\":\"60\",\"Effect5Amount\":\"1500\",\"Effect6Amount\":\"9\",\"Effect7Amount\":\"30\",\"Effect8Amount\":\"120\",\"Effect9Amount\":\"60\",\"Effect10Amount\":\"450\",\"Effect11Amount\":\"575\"},\"id\":3341},\"3345\":{\"name\":\"Soul Anchor (Trinket)\",\"description\":\"<groupLimit>Limited to 1 Trinket.</groupLimit><br><br><active>Active:</active> Consumes a charge to instantly revive at your Summoner Platform and grants 125% Movement Speed that decays over 12 seconds.<br><br><rules>Additional charges are gained at levels 9 and 14.</rules><br><br><font color='#BBFFFF'>(Max: 2 charges)</font></rules><br><br>\",\"colloq\":\"\",\"plaintext\":\"Consumes charge to revive champion.\",\"inStore\":false,\"image\":{\"full\":\"3345.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":432,\"y\":384,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[\"Active\",\"Trinket\",\"Vision\"],\"maps\":{\"8\":true,\"10\":false,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"id\":3345},\"3348\":{\"name\":\"Arcane Sweeper\",\"description\":\"<active>UNIQUE Active - Hunter's Sight:</active> An arcane mist grants vision in the target area for 5 seconds, revealing enemy champions and granting <font color='#ee91d7'>True Sight</font> of traps in the area for 3 seconds (90 second cooldown).\",\"colloq\":\";\",\"plaintext\":\"Activate to reveal a nearby area of the map\",\"inStore\":false,\"image\":{\"full\":\"3348.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":0,\"y\":432,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[\"Vision\",\"Trinket\",\"Stealth\",\"Active\"],\"maps\":{\"8\":true,\"10\":true,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"0\",\"Effect2Amount\":\"0\",\"Effect3Amount\":\"0\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"3\",\"Effect6Amount\":\"90\"},\"id\":3348},\"3361\":{\"name\":\"Greater Stealth Totem (Trinket)\",\"description\":\"<groupLimit>Limited to 1 Trinket.</groupLimit><levelLimit> *Level 9+ required to upgrade.</levelLimit><stats></stats><br><br><unique>UNIQUE Active:</unique> Consume a charge to place an invisible ward that reveals the surrounding area for 180 seconds.  Stores a charge every 60 seconds, up to 2 total. Limit 3 <font color='#BBFFFF'>Stealth Wards</font> on the map per player.<br><br><rules>(Trinkets cannot be used in the first 30 seconds of a game. Selling a Trinket will disable Trinket use for 120 seconds).</rules>\",\"colloq\":\"yellow;\",\"plaintext\":\"Periodically place a Stealth Ward\",\"inStore\":false,\"image\":{\"full\":\"3361.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":48,\"y\":432,\"w\":48,\"h\":48},\"gold\":{\"base\":250,\"purchasable\":false,\"total\":250,\"sell\":175},\"tags\":[\"Active\",\"Trinket\",\"Vision\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"180\",\"Effect2Amount\":\"60\",\"Effect3Amount\":\"0\",\"Effect4Amount\":\"0\",\"Effect5Amount\":\"0\",\"Effect6Amount\":\"9\",\"Effect7Amount\":\"30\",\"Effect8Amount\":\"120\"},\"id\":3361},\"3362\":{\"name\":\"Greater Vision Totem (Trinket)\",\"description\":\"<groupLimit>Limited to 1 Trinket.</groupLimit><levelLimit> *Level 9+ required to upgrade.</levelLimit><stats></stats><br><br><unique>UNIQUE Active:</unique> Places a visible ward that reveals the surrounding area and invisible units in the area until killed (120 second cooldown). Limit 1 <font color='#BBFFFF'>Vision Ward</font> on the map per player.<br><br><rules>(Trinkets cannot be used in the first 30 seconds of a game. Selling a Trinket will disable Trinket use for 120 seconds).</rules>\",\"colloq\":\"yellow;\",\"plaintext\":\"Periodically place a Vision Ward\",\"inStore\":false,\"image\":{\"full\":\"3362.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":96,\"y\":432,\"w\":48,\"h\":48},\"gold\":{\"base\":250,\"purchasable\":false,\"total\":250,\"sell\":175},\"tags\":[\"Active\",\"Trinket\",\"Vision\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"120\",\"Effect2Amount\":\"0\",\"Effect3Amount\":\"0\",\"Effect4Amount\":\"0\",\"Effect5Amount\":\"0\",\"Effect6Amount\":\"9\",\"Effect7Amount\":\"30\",\"Effect8Amount\":\"120\"},\"id\":3362},\"3363\":{\"name\":\"Farsight Alteration\",\"description\":\"<levelLimit>* Level 9+ required to upgrade.</levelLimit><br><br>Alters the <font color='#FFFFFF'>Warding Totem</font> Trinket:<br><br><stats><font color='#00FF00'>+</font> Massively increased cast range (+650%)<br><font color='#00FF00'>+</font> Infinite duration and does not count towards ward limit<br><font color='#FF0000'>-</font> <font color='#FF6600'>10% increased cooldown</font><br><font color='#FF0000'>-</font> <font color='#FF6600'>Ward is visible, fragile, untargetable by allies</font><br><font color='#FF0000'>-</font> <font color='#FF6600'>45% reduced ward vision radius</font><br><font color='#FF0000'>-</font> <font color='#FF6600'>Cannot store charges</font></stats>\",\"colloq\":\"blue; totem;\",\"plaintext\":\"Grants increased range and reveals the targetted area\",\"image\":{\"full\":\"3363.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":144,\"y\":432,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[\"Active\",\"Trinket\",\"Vision\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"4000\",\"Effect2Amount\":\"2\",\"Effect3Amount\":\"5\",\"Effect4Amount\":\"198\",\"Effect5Amount\":\"60\",\"Effect6Amount\":\"9\",\"Effect7Amount\":\"30\",\"Effect8Amount\":\"120\",\"Effect9Amount\":\"6.5\",\"Effect10Amount\":\"198\",\"Effect11Amount\":\"99\",\"Effect12Amount\":\"60\",\"Effect13Amount\":\"180\",\"Effect14Amount\":\"10\",\"Effect15Amount\":\"45\"},\"id\":3363},\"3364\":{\"name\":\"Oracle Alteration\",\"description\":\"<levelLimit>* Level 9+ required to upgrade.</levelLimit><stats></stats><br><br>Alters the <font color='#FFFFFF'>Sweeping Lens</font> Trinket:<br><br><stats><font color='#00FF00'>+</font> Increased detection radius<br><font color='#00FF00'>+</font> Sweeping effect follows you for 10 seconds<br><font color='#FF0000'>-</font> <font color='#FF6600'>Cast range reduced to zero</font></stats>\",\"colloq\":\"red; lens;\",\"plaintext\":\"Disables nearby invisible wards and traps for a duration\",\"image\":{\"full\":\"3364.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":192,\"y\":432,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[\"Active\",\"Trinket\",\"Vision\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"6\",\"Effect2Amount\":\"10\",\"Effect3Amount\":\"90\",\"Effect4Amount\":\"60\",\"Effect5Amount\":\"0\",\"Effect6Amount\":\"9\",\"Effect7Amount\":\"30\",\"Effect8Amount\":\"120\",\"Effect9Amount\":\"60\"},\"id\":3364},\"3401\":{\"name\":\"Face of the Mountain\",\"description\":\"<stats>+450 Health<br>+100% Base Health Regen <br>+10% Cooldown Reduction<br>+2 Gold per 10 seconds </stats><br><br><unique>UNIQUE Passive - Spoils of War:</unique> Melee basic attacks execute minions below 320 (+20 per level) Health. Killing a minion heals the owner and the nearest allied champion for 50 Health and grants them kill Gold. These effects require a nearby ally. Recharges every 30 seconds. Max 4 charges.<br><unique>UNIQUE Active:</unique> Grant a shield to you and an ally equal to 10% of your maximum Health for 4 seconds. After 4 seconds, the shields explode to slow nearby enemies by 40% for 2 seconds (60 second cooldown).  Automatically targets the most wounded ally if cast upon self.<hr><passive>QUEST:</passive> Earn 650 gold using this item.<br><passive>REWARD:</passive> <font color='#CFBF84'>Shield Battery</font>, a permanent shield that regenerates slowly outside of combat.<br><br><groupLimit>Limited to 1 Gold Income Item.</groupLimit>\",\"colloq\":\";\",\"plaintext\":\"Shield an ally from damage based on your Health\",\"from\":[\"3097\",\"3067\"],\"image\":{\"full\":\"3401.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":240,\"y\":432,\"w\":48,\"h\":48},\"gold\":{\"base\":550,\"purchasable\":true,\"total\":2200,\"sell\":880},\"tags\":[\"Health\",\"HealthRegen\",\"Active\",\"GoldPer\",\"CooldownReduction\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":450},\"effect\":{\"Effect1Amount\":\"320\",\"Effect2Amount\":\"50\",\"Effect3Amount\":\"0.1\",\"Effect4Amount\":\"4\",\"Effect5Amount\":\"-0.4\",\"Effect6Amount\":\"2\",\"Effect7Amount\":\"60\",\"Effect8Amount\":\"120\",\"Effect9Amount\":\"2\",\"Effect10Amount\":\"20\"},\"depth\":3,\"id\":3401},\"3410\":{\"name\":\"Head of Kha'Zix\",\"description\":\"<unique>UNIQUE Active - Sweeping Lens:</unique> Reveals and disables nearby invisible traps and invisible wards for 6 seconds in a medium radius and grants detection of invisible units for 10 seconds (60 second cooldown).<br><br><unique>UNIQUE Passive - Mementos of the Hunt:</unique> Rengar collects trophies when killing Champions and gains bonus effects based on how many trophies he has. Kills and assists grant 1 trophy.<br><br><passive>3 Trophies:</passive> Rengar gains 25 Movement Speed whilst out of combat or in brush. <br><passive>6 Trophies:</passive> Increases the range of Rengar's Leap by 125.<br><passive>12 Trophies:</passive> Thrill of the Hunt's duration is increased by 5 seconds.<br><passive>20 Trophies:</passive> Thrill of the Hunt's Movement Speed while stealthed is doubled.\",\"colloq\":\"\",\"plaintext\":\"\",\"specialRecipe\":3169,\"inStore\":false,\"requiredChampion\":\"Rengar\",\"hideFromAll\":true,\"image\":{\"full\":\"3410.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":288,\"y\":432,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[\"Active\",\"Trinket\",\"Vision\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"25\",\"Effect2Amount\":\"125\",\"Effect3Amount\":\"5\",\"Effect4Amount\":\"6\",\"Effect5Amount\":\"10\",\"Effect6Amount\":\"60\"},\"id\":3410},\"3416\":{\"name\":\"Head of Kha'Zix\",\"description\":\"<unique>UNIQUE Active - Scrying:</unique> Reveals a small location within 4000 range for 2 seconds. Enemy champions found will be revealed for 5 seconds (90 second cooldown).<br><br><unique>UNIQUE Passive - Mementos of the Hunt:</unique> Rengar collects trophies when killing Champions and gains bonus effects based on how many trophies he has. Kills and assists grant 1 trophy.<br><br><passive>3 Trophies:</passive> Rengar gains 25 Movement Speed whilst out of combat or in brush. <br><passive>6 Trophies:</passive> Increases the range of Rengar's Leap by 125.<br><passive>12 Trophies:</passive> Thrill of the Hunt's duration is increased by 5 seconds.<br><passive>20 Trophies:</passive> Thrill of the Hunt's Movement Speed while stealthed is doubled.\",\"colloq\":\"\",\"plaintext\":\"\",\"specialRecipe\":3169,\"inStore\":false,\"requiredChampion\":\"Rengar\",\"hideFromAll\":true,\"image\":{\"full\":\"3416.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":336,\"y\":432,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[\"Active\",\"Trinket\",\"Vision\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"25\",\"Effect2Amount\":\"125\",\"Effect3Amount\":\"5\",\"Effect4Amount\":\"4000\",\"Effect5Amount\":\"2\",\"Effect6Amount\":\"5\",\"Effect7Amount\":\"90\"},\"id\":3416},\"3422\":{\"name\":\"Head of Kha'Zix\",\"description\":\"<unique>UNIQUE Passive - Mementos of the Hunt:</unique> Rengar collects trophies when killing Champions and gains bonus effects based on how many trophies he has. Kills and assists grant 1 trophy.<br><br><passive>3 Trophies:</passive> Rengar gains 25 Movement Speed whilst out of combat or in brush. <br><passive>6 Trophies:</passive> Increases the range of Rengar's Leap by 125.<br><passive>12 Trophies:</passive> Thrill of the Hunt's duration is increased by 5 seconds.<br><passive>20 Trophies:</passive> Thrill of the Hunt's Movement Speed while stealthed is doubled.\",\"colloq\":\"\",\"plaintext\":\"\",\"specialRecipe\":3169,\"inStore\":false,\"requiredChampion\":\"Rengar\",\"hideFromAll\":true,\"image\":{\"full\":\"3422.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":384,\"y\":432,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[\"Active\",\"Trinket\",\"Vision\"],\"maps\":{\"8\":true,\"10\":true,\"11\":false,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"25\",\"Effect2Amount\":\"125\",\"Effect3Amount\":\"5\"},\"id\":3422},\"3455\":{\"name\":\"Head of Kha'Zix\",\"description\":\"<unique>UNIQUE Passive - Mementos of the Hunt:</unique> Rengar collects trophies when killing Champions and gains bonus effects based on how many trophies he has. Kills and assists grant 1 trophy.<br><br><passive>3 Trophies:</passive> Rengar gains 25 Movement Speed whilst out of combat or in brush. <br><passive>6 Trophies:</passive> Increases the range of Rengar's Leap by 125.<br><passive>12 Trophies:</passive> Thrill of the Hunt's duration is increased by 5 seconds.<br><passive>20 Trophies:</passive> Thrill of the Hunt's Movement Speed while stealthed is doubled.\",\"colloq\":\"\",\"plaintext\":\"\",\"specialRecipe\":3169,\"inStore\":false,\"requiredChampion\":\"Rengar\",\"hideFromAll\":true,\"image\":{\"full\":\"3455.png\",\"sprite\":\"item1.png\",\"group\":\"item\",\"x\":432,\"y\":432,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[\"Active\",\"Trinket\",\"Vision\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"25\",\"Effect2Amount\":\"125\",\"Effect3Amount\":\"5\"},\"id\":3455},\"3460\":{\"name\":\"Golden Transcendence\",\"description\":\"<unique>Active:</unique> Use this trinket to teleport to one of the battle platforms. Can only be used from the summoning platform.<br><br><i><font color='#FDD017'>''It is at this magical precipice where a champion is dismantled, reforged, and empowered.''</font></i>\",\"colloq\":\"\",\"plaintext\":\"\",\"inStore\":false,\"image\":{\"full\":\"3460.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":0,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[\"Active\",\"Trinket\"],\"maps\":{\"8\":true,\"10\":false,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"id\":3460},\"3461\":{\"name\":\"Golden Transcendence (Disabled)\",\"description\":\"<unique>Active:</unique> Use this trinket to teleport to one of the battle platforms. Can only be used from the summoning platform.<br><br><i><font color='#FDD017'>''It is at this magical precipice where a champion is dismantled, reforged, and empowered.''</font></i>\",\"colloq\":\"\",\"plaintext\":\"\",\"inStore\":false,\"image\":{\"full\":\"3461.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":48,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[\"Active\",\"Trinket\"],\"maps\":{\"8\":true,\"10\":false,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"id\":3461},\"3462\":{\"name\":\"Seer Stone (Trinket)\",\"description\":\"<groupLimit>Limited to 1 Trinket.</groupLimit><br><br><active>Active:</active> Reveals a small area within <font color='#FFFFF'>2500</font> range for 3 seconds. Enemy champions will be revealed for 5 seconds (60 second cooldown)\",\"colloq\":\"blue;\",\"plaintext\":\"Briefly reveals a nearby targeted area\",\"inStore\":false,\"image\":{\"full\":\"3462.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":96,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[\"Active\",\"Trinket\",\"Vision\"],\"maps\":{\"8\":true,\"10\":false,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"2500\",\"Effect2Amount\":\"60\",\"Effect3Amount\":\"3\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"550\"},\"id\":3462},\"3504\":{\"name\":\"Ardent Censer\",\"description\":\"<stats>+60 Ability Power<br>+10% Cooldown Reduction<br><mana>+50% Base Mana Regen </mana></stats><br><br><unique>UNIQUE Passive:</unique> +10% Heal and Shield Power<br><unique>UNIQUE Passive:</unique> +8% Movement Speed<br><unique>UNIQUE Passive:</unique> Your heals and shields on another allied champion grant them 20% - 35% Attack Speed and their attacks drain 20 - 35 health on-hit for 6 seconds.<br><br><rules>(This does not include regeneration effects or effects on yourself. Bonus effects are based on target's level.</rules>)</rules>\",\"colloq\":\"\",\"plaintext\":\"Shield and heal effects on other units grant them Attack Speed and their attacks drain life\",\"from\":[\"3114\",\"3113\"],\"image\":{\"full\":\"3504.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":144,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":650,\"purchasable\":true,\"total\":2300,\"sell\":1610},\"tags\":[\"CooldownReduction\",\"ManaRegen\",\"NonbootsMovement\",\"SpellDamage\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMagicDamageMod\":60},\"effect\":{\"Effect1Amount\":\"0.08\",\"Effect2Amount\":\"0.2\",\"Effect3Amount\":\"6\",\"Effect4Amount\":\"20\",\"Effect5Amount\":\"0.1\",\"Effect6Amount\":\"0.35\",\"Effect7Amount\":\"35\"},\"depth\":3,\"id\":3504},\"3508\":{\"name\":\"Essence Reaver\",\"description\":\"<stats>+70 Attack Damage<br>+20% Critical Strike Chance</stats><br><br><unique>UNIQUE Passive:</unique> +10% Cooldown Reduction<br><unique>UNIQUE Passive:</unique> Gain increasingly more Cooldown Reduction from Critical Strike Chance provided by other sources (maximum +20% additional Cooldown Reduction at 30% Critical Strike Chance).<br><unique>UNIQUE Passive:</unique> Critical strikes restore 3% of your maximum Mana pool.\",\"colloq\":\";\",\"plaintext\":\"Critical Strike provides Cooldown Reduction and Mana\",\"from\":[\"1038\",\"3133\",\"1018\"],\"image\":{\"full\":\"3508.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":192,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":200,\"purchasable\":true,\"total\":3400,\"sell\":2380},\"tags\":[\"Damage\",\"CriticalStrike\",\"ManaRegen\",\"CooldownReduction\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":70,\"FlatCritChanceMod\":0.2},\"effect\":{\"Effect1Amount\":\"0.1\",\"Effect2Amount\":\"0.03\",\"Effect3Amount\":\"0.2\",\"Effect4Amount\":\"0.3\",\"Effect5Amount\":\"0.0167\",\"Effect6Amount\":\"0.1667\"},\"depth\":3,\"id\":3508},\"3512\":{\"name\":\"Zz'Rot Portal\",\"description\":\"<stats>+55 Armor<br>+55 Magic Resist<br>+125% Base Health Regen <br></stats><br><unique>UNIQUE Passive - Point Runner:</unique> Builds up to +20% Movement Speed over 2 seconds while near turrets, fallen turrets and Void Gates.<br><active>UNIQUE Active:</active> Spawns a <a href='VoidGate'>Void Gate</a> for 120 seconds (120 second cooldown).<br><br>Every 4 seconds the gate makes a <a href='Voidspawn'>Voidspawn</a>. The first and every fourth Voidspawn gains 15% of maximum Health as damage.\",\"colloq\":\";Void Gate\",\"plaintext\":\"Makes a Voidspawn generating Void Gate to push a lane with.\",\"from\":[\"2053\",\"1057\"],\"image\":{\"full\":\"3512.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":240,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":780,\"purchasable\":true,\"total\":2700,\"sell\":1890},\"tags\":[\"SpellBlock\",\"HealthRegen\",\"Armor\",\"Active\",\"NonbootsMovement\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatSpellBlockMod\":55,\"FlatArmorMod\":55},\"effect\":{\"Effect1Amount\":\"20\",\"Effect2Amount\":\"4\",\"Effect3Amount\":\"50\",\"Effect4Amount\":\"0\",\"Effect5Amount\":\"120\",\"Effect6Amount\":\"120\",\"Effect7Amount\":\"0.5\",\"Effect8Amount\":\"0.15\",\"Effect9Amount\":\"3\",\"Effect10Amount\":\"20\",\"Effect11Amount\":\"2\",\"Effect12Amount\":\"100\",\"Effect13Amount\":\"20\",\"Effect14Amount\":\"50\",\"Effect15Amount\":\"2\"},\"depth\":4,\"id\":3512},\"3513\":{\"name\":\"Eye of the Herald\",\"description\":\"<br><unique>UNIQUE Passive - Glimpse of the Void:</unique> The holder of the Eye of the Herald has Empowered Recall.<br><br><active>UNIQUE Active:</active> Channel for 3.5 seconds to crush the Eye of the Herald, summoning the Rift Herald to siege enemy turrets.<br><br>The Eye of the Herald will be lost to the Void if not used within 240 seconds.</font>\",\"colloq\":\";Herald's Eye\",\"plaintext\":\"Eye of the Herald - a Gift of the Void.\",\"consumed\":true,\"inStore\":false,\"image\":{\"full\":\"3513.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":288,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[\"Trinket\",\"Active\"],\"maps\":{\"8\":false,\"10\":false,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"240\",\"Effect2Amount\":\"3.5\",\"Effect3Amount\":\"40\"},\"id\":3513},\"3599\":{\"name\":\"The Black Spear\",\"description\":\"<stats></stats><br><active>Active:</active> Offer to bind with an ally for the remainder of the game, becoming Oathsworn Allies. Oathsworn empowers you both while near one another.\",\"colloq\":\";spear\",\"plaintext\":\"Kalista's spear that binds an Oathsworn Ally.\",\"requiredChampion\":\"Kalista\",\"image\":{\"full\":\"3599.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":336,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[\"Active\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"id\":3599},\"3630\":{\"name\":\"Siege Teleport\",\"description\":\"<unique>Active:</unique> Use this trinket to teleport to one of your team's port pads. Can only be used from the summoning platform.\",\"colloq\":\"\",\"plaintext\":\"\",\"inStore\":false,\"image\":{\"full\":\"3630.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":384,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":10,\"purchasable\":false,\"total\":10,\"sell\":7},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"id\":3630},\"3631\":{\"name\":\"Siege Ballista\",\"description\":\"<br><font color='#FF9900'>Deploys a ballista that shoots the closest turret.</font><br><br>Places a long range ballista if within 2200 range of an enemy turret. After a 5 second delay, it will begin firing at the nearest enemy turret, dealing heavy damage. If the targeted turret expires, the ballista will as well.\",\"colloq\":\"\",\"plaintext\":\"Place a long range anti-turret ballista\",\"consumed\":true,\"consumeOnFull\":true,\"image\":{\"full\":\"3631.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":432,\"y\":0,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"5\",\"Effect2Amount\":\"5\",\"Effect3Amount\":\"20\",\"Effect4Amount\":\"3\",\"Effect5Amount\":\"0\",\"Effect6Amount\":\"2200\",\"Effect7Amount\":\"10\",\"Effect8Amount\":\"0\",\"Effect9Amount\":\"0.5\"},\"id\":3631},\"3632\":{\"name\":\"\",\"description\":\"\",\"colloq\":\"\",\"plaintext\":\"\",\"inStore\":false,\"image\":{\"full\":\"3632.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":0,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"15\",\"Effect2Amount\":\"1\",\"Effect3Amount\":\"310\",\"Effect4Amount\":\"5000\",\"Effect5Amount\":\"20\",\"Effect6Amount\":\"0\",\"Effect7Amount\":\"3\",\"Effect8Amount\":\"45\",\"Effect9Amount\":\"10\",\"Effect10Amount\":\"1\",\"Effect11Amount\":\"15\",\"Effect12Amount\":\"3000\"},\"id\":3632},\"3633\":{\"name\":\"Siege Teleport\",\"description\":\"<unique>Active:</unique> Use this trinket to teleport to one of your team's port pads. Can only be used from the summoning platform.\",\"colloq\":\"\",\"plaintext\":\"\",\"inStore\":false,\"image\":{\"full\":\"3633.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":48,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":10,\"purchasable\":false,\"total\":10,\"sell\":7},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"id\":3633},\"3634\":{\"name\":\"Tower: Beam of Ruination\",\"description\":\"<br><font color='#FF9900'>Attach, then recast to fire a damaging beam from a turret to your cursor.</font><br><br><font color='#FF9900'>First Cast:</font> Attach a Slayer Beam to the target turret that can be fired 3 times.<br></br><font color='#FF9900'>Next Three Casts:</font> Fires the attached beam towards your cursor, dealing 30/level + 30% of the hit target's maximum health (20% damage to minions) in magic damage to all targets in a line.<br></br><br></br>Beam will last 15 seconds, or until it has been fired 3 times.\",\"colloq\":\"\",\"plaintext\":\"Attaches a three shot beam to a turret which can then be aimed and fired\",\"consumed\":true,\"consumeOnFull\":true,\"image\":{\"full\":\"3634.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":96,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"3\",\"Effect2Amount\":\"1.5\",\"Effect3Amount\":\"3000\",\"Effect4Amount\":\"0\",\"Effect5Amount\":\"30\",\"Effect6Amount\":\"0.2\",\"Effect7Amount\":\"15\",\"Effect8Amount\":\"0.3\",\"Effect9Amount\":\"0\",\"Effect10Amount\":\"76\"},\"id\":3634},\"3635\":{\"name\":\"Port Pad\",\"description\":\"<br><font color='#FF9900'>Deploy an additional teleport target.</font><br><br>Places a Port Pad at target location. After a 4 second delay, it activates, allowing you or your allies to teleport to it from base.\",\"colloq\":\"\",\"plaintext\":\"Creates another point for your team to Teleport to\",\"consumed\":true,\"consumeOnFull\":true,\"image\":{\"full\":\"3635.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":144,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"4\",\"Effect2Amount\":\"1000\",\"Effect3Amount\":\"3\",\"Effect4Amount\":\"10\"},\"id\":3635},\"3636\":{\"name\":\"Tower: Storm Bulwark\",\"description\":\"<br><font color='#FF9900'>Makes a turret go invulnerable, then rain fire.</font><br><br>Makes the target turret invulnerable for 6 seconds. Two seconds before expiry, it unleashes a missile volley, dealing 2600 true damage over the remaining time to all nearby enemies.<br><br>Cannot be used on the same turret more than once in 15 seconds.\",\"colloq\":\"\",\"plaintext\":\"Make a turret go invulnerable while charging a powerful barrage\",\"consumed\":true,\"consumeOnFull\":true,\"image\":{\"full\":\"3636.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":192,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatCritChanceMod\":3},\"effect\":{\"Effect1Amount\":\"6\",\"Effect2Amount\":\"15\",\"Effect3Amount\":\"650\",\"Effect4Amount\":\"825\",\"Effect5Amount\":\"1.2\"},\"id\":3636},\"3637\":{\"name\":\"Nexus Siege: Siege Weapon Slot\",\"description\":\"In Nexus Siege, Summoner Spells are replaced with Siege Weapon Slots. Spend Crystal Shards to buy single-use Siege Weapons from the item shop, then use your Summoner Spell keys to activate them!\",\"colloq\":\"\",\"plaintext\":\"\",\"inStore\":false,\"image\":{\"full\":\"3637.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":240,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":10,\"purchasable\":false,\"total\":10,\"sell\":7},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"id\":3637},\"3640\":{\"name\":\"Flash Zone\",\"description\":\"<br><font color='#FF9900'>Allows team to cast Flash repeatedly in a limited zone.</font><br><br>Creates a magic zone for your team for 5 seconds. While in this zone, you and your allies have your summoner spells replaced by an instant cast blink that moves you to any location in the zone (1 second cooldown).\",\"colloq\":\"\",\"plaintext\":\"Allows you and allies to repeatedly flash while in a zone\",\"consumed\":true,\"consumeOnFull\":true,\"image\":{\"full\":\"3640.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":288,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"1\",\"Effect2Amount\":\"5\"},\"id\":3640},\"3641\":{\"name\":\"Vanguard Banner\",\"description\":\"<br><font color='#FF9900'>Place a banner that buffs minions.</font><br><br>Place a Vanguard Banner at target location. After a 2 second delay, any nearby minions will be granted a buff, increasing their damage by 50%, and granting them 50 Armor and 100 Magic Resistance while within range.\",\"colloq\":\"\",\"plaintext\":\"Strengthens nearby minions\",\"consumed\":true,\"image\":{\"full\":\"3641.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":336,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"0\",\"Effect2Amount\":\"0.5\",\"Effect3Amount\":\"50\",\"Effect4Amount\":\"100\",\"Effect5Amount\":\"5\",\"Effect6Amount\":\"0.3\",\"Effect7Amount\":\"2\",\"Effect8Amount\":\"10\",\"Effect9Amount\":\"1400\"},\"id\":3641},\"3642\":{\"name\":\"Siege Refund\",\"description\":\"Refunds all purchased Siege Weapons for their full price.\",\"colloq\":\"\",\"plaintext\":\"Refunds all current Siege Weapons\",\"consumed\":true,\"consumeOnFull\":true,\"image\":{\"full\":\"3642.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":384,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"1\"},\"id\":3642},\"3643\":{\"name\":\"Entropy Field\",\"description\":\"<br><font color='#FF9900'>Stun minions and slow champions in an area.</font><br><br>Places an Entropy Field at target location for 5 seconds.  Enemy minions and Siege Ballistas trapped in the field are unable to move or attack while in the field.  Enemy champions in the field have their Movement Speed reduced by 25%.\",\"colloq\":\"\",\"plaintext\":\"Places a field that stuns enemy minions and slows champions\",\"consumed\":true,\"consumeOnFull\":true,\"image\":{\"full\":\"3643.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":432,\"y\":48,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"5\",\"Effect2Amount\":\"600\",\"Effect3Amount\":\"25\"},\"id\":3643},\"3645\":{\"name\":\"Seer Stone (Trinket)\",\"description\":\"<groupLimit>Limited to 1 Trinket.</groupLimit><br><br><active>Active:</active> Reveals a small area within <font color='#FFFFF'>1400</font> range for 3 seconds. Enemy champions will be revealed for 5 seconds (60 second cooldown)\",\"colloq\":\"blue;\",\"plaintext\":\"Briefly reveals a nearby targeted area\",\"inStore\":false,\"image\":{\"full\":\"3645.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":0,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[\"Active\",\"Trinket\",\"Vision\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"1400\",\"Effect2Amount\":\"60\",\"Effect3Amount\":\"3\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"550\"},\"id\":3645},\"3647\":{\"name\":\"Shield Totem\",\"description\":\"<br><font color='#FF9900'>Place a totem that shields nearby deployables.</font><br><br>Places a Shield Totem at target location. After a 2 second delay, the totem will activate, granting a 2 (+1 per additional Shield Totem) strength shield to all nearby deployables.\",\"colloq\":\"\",\"plaintext\":\"Grants bonus health to nearby Siege Weapons\",\"consumed\":true,\"image\":{\"full\":\"3647.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":48,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"2\",\"Effect2Amount\":\"3\",\"Effect3Amount\":\"2\",\"Effect4Amount\":\"1\",\"Effect5Amount\":\"1000\",\"Effect6Amount\":\"0.25\",\"Effect7Amount\":\"10\",\"Effect8Amount\":\"1\",\"Effect9Amount\":\"4\"},\"id\":3647},\"3648\":{\"name\":\"Siege Teleport (Inactive)\",\"description\":\"\",\"colloq\":\"\",\"plaintext\":\"\",\"inStore\":false,\"image\":{\"full\":\"3648.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":96,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"id\":3648},\"3649\":{\"name\":\"Siege Sight Warder\",\"description\":\"<groupLimit>Limited to 1 Trinket.</groupLimit><br><br><active>Active:</active> Places a <font color='#FFFFFF'>Stealth Ward</font> that lasts <font color='#FFFFFF'>30</font> seconds (30 second cooldown).\",\"colloq\":\"\",\"plaintext\":\"\",\"inStore\":false,\"image\":{\"full\":\"3649.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":144,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":false,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"30\"},\"id\":3649},\"3671\":{\"name\":\"Enchantment: Warrior\",\"description\":\"<stats>+60 Attack Damage<br>+10% Cooldown Reduction</stats>\",\"colloq\":\";\",\"plaintext\":\"\",\"from\":[\"3133\"],\"hideFromAll\":true,\"image\":{\"full\":\"3671.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":192,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":525,\"purchasable\":true,\"total\":1625,\"sell\":1138},\"tags\":[],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":60},\"depth\":3,\"id\":3671},\"3672\":{\"name\":\"Enchantment: Cinderhulk\",\"description\":\"<stats>+400 Health<br>+15% Bonus Health</stats><br><br><unique>UNIQUE Passive - Immolate:</unique> Deals 7 (+2 per champion level) magic damage a second to nearby enemies while in combat. Deals 100% bonus damage to monsters. \",\"colloq\":\";\",\"plaintext\":\"\",\"from\":[\"3751\"],\"hideFromAll\":true,\"image\":{\"full\":\"3672.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":240,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":525,\"purchasable\":true,\"total\":1625,\"sell\":1138},\"tags\":[],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":400},\"depth\":3,\"id\":3672},\"3673\":{\"name\":\"Enchantment: Runic Echoes\",\"description\":\"<stats>+60 Ability Power<br>+7% Movement Speed</stats><br><br><unique>UNIQUE Passive - Echo:</unique> Gain charges upon moving or casting. At 100 charges, the next damaging spell hit expends all charges to deal 60 (+10% of Ability Power) bonus magic damage to up to 4 targets on hit.<br><br>This effect deals 250% damage to Large Monsters. Hitting a Large Monster with this effect will restore 18% of your missing Mana.\",\"colloq\":\";\",\"plaintext\":\"\",\"from\":[\"3113\",\"1052\"],\"hideFromAll\":true,\"image\":{\"full\":\"3673.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":288,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":340,\"purchasable\":true,\"total\":1625,\"sell\":1138},\"tags\":[],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"PercentMovementSpeedMod\":0.07,\"FlatMagicDamageMod\":60},\"depth\":3,\"id\":3673},\"3675\":{\"name\":\"Enchantment: Bloodrazor\",\"description\":\"<stats>+50% Attack Speed</stats><br><br><unique>UNIQUE Passive:</unique> Basic attacks deal 4% of the target's maximum Health in bonus physical damage (max 75 vs. monsters and minions) on hit.\",\"colloq\":\";\",\"plaintext\":\"\",\"from\":[\"1043\"],\"hideFromAll\":true,\"image\":{\"full\":\"3675.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":336,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":625,\"purchasable\":true,\"total\":1625,\"sell\":1138},\"tags\":[],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"PercentAttackSpeedMod\":0.5},\"depth\":3,\"id\":3675},\"3680\":{\"name\":\"Frosted Snax\",\"description\":\"<active>Active - <a href='FeedTheKing'>Feed The King</a>:</active> The King lobs many projectiles at far-away enemies, each dealing <scaleLevel>213-775</scaleLevel> magic damage to targets in the center of the impact, scaling down to <scaleLevel>85-310</scaleLevel> on the edge. (120s cooldown)\",\"colloq\":\"\",\"plaintext\":\"King: Fires a barrage of icy artillery\",\"image\":{\"full\":\"3680.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":384,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[\"Trinket\",\"Lane\"],\"maps\":{\"8\":false,\"10\":false,\"11\":false,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"5\",\"Effect2Amount\":\"120\",\"Effect3Amount\":\"40\",\"Effect4Amount\":\"15\",\"Effect5Amount\":\"250\",\"Effect6Amount\":\"213\",\"Effect7Amount\":\"775\",\"Effect8Amount\":\"85\",\"Effect9Amount\":\"310\"},\"id\":3680},\"3681\":{\"name\":\"Super Spicy Snax\",\"description\":\"<active>Active - <a href='FeedTheKing'>Feed The King</a>:</active> The King breathes fire for 4 seconds, dealing <scaleLevel>705-1479</scaleLevel> true damage over the duration to enemies caught in the cone. Deals up to 560 true damage to Turrets. (120s cooldown)\",\"colloq\":\"\",\"plaintext\":\"King: Shoots flames that burn units and Turrets\",\"image\":{\"full\":\"3681.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":432,\"y\":96,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[\"Trinket\",\"Lane\"],\"maps\":{\"8\":false,\"10\":false,\"11\":false,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"4\",\"Effect2Amount\":\"120\",\"Effect3Amount\":\"700\",\"Effect4Amount\":\"2.35\",\"Effect5Amount\":\"560\",\"Effect6Amount\":\"705\",\"Effect7Amount\":\"1479\"},\"id\":3681},\"3682\":{\"name\":\"Espresso Snax\",\"description\":\"<active>Active - <a href='FeedTheKing'>Feed The King</a>:</active> The King leaps into the air and crashes down twice, knocking enemies away and dealing <scaleLevel>40-190</scaleLevel> physical damage. He also gains a decaying shield for <font color='#FF3300'>20% of his maximum health</font>, lasting 4 seconds. (30s cooldown)\",\"colloq\":\"\",\"plaintext\":\"King: Knocks back and grants a large shield\",\"image\":{\"full\":\"3682.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":0,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[\"Trinket\",\"Lane\"],\"maps\":{\"8\":false,\"10\":false,\"11\":false,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"1.8\",\"Effect2Amount\":\"30\",\"Effect3Amount\":\"10\",\"Effect4Amount\":\"10\",\"Effect5Amount\":\"20\",\"Effect6Amount\":\"4\",\"Effect7Amount\":\"600\",\"Effect8Amount\":\"40\",\"Effect9Amount\":\"190\",\"Effect10Amount\":\"500\"},\"id\":3682},\"3683\":{\"name\":\"Rainbow Snax Party Pack!\",\"description\":\"<active>Active - <a href='FeedTheKing'>Feed The King</a>:</active> The King tosses many Snax behind the enemy, attracting Poros which dash back towards him. Enemy champions hit will be knocked forwards and dealt <scaleLevel>230-680</scaleLevel> physical damage. (120s cooldown)\",\"colloq\":\"\",\"plaintext\":\"King: Poros knock enemies towards him\",\"image\":{\"full\":\"3683.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":48,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[\"Trinket\",\"Lane\"],\"maps\":{\"8\":false,\"10\":false,\"11\":false,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"6\",\"Effect2Amount\":\"120\",\"Effect3Amount\":\"140\",\"Effect4Amount\":\"30\",\"Effect5Amount\":\"1\",\"Effect6Amount\":\"1500\",\"Effect7Amount\":\"1650\",\"Effect8Amount\":\"1000\",\"Effect9Amount\":\"230\",\"Effect10Amount\":\"680\",\"Effect11Amount\":\"2\"},\"id\":3683},\"3690\":{\"name\":\"Cosmic Shackle\",\"description\":\"<passive>Passive - Cosmic Shackle: </passive>Death Sentence pulls much farther (based on the target's Missing Health), and can be ignited by the Dark Star to do more damage.<br><br><flavorText>''A still more glorious dawn awaits.''</flavorText>\",\"colloq\":\";\",\"plaintext\":\"\",\"image\":{\"full\":\"3690.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":96,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"id\":3690},\"3691\":{\"name\":\"Singularity Lantern\",\"description\":\"<passive>Passive - Singularity Lantern: </passive>Dark Passage automatically saves disabled allies. However, it no longer provides a shield.<br><br><flavorText>''The stars call to us.''</flavorText>\",\"colloq\":\";\",\"plaintext\":\"\",\"image\":{\"full\":\"3691.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":144,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"id\":3691},\"3692\":{\"name\":\"Dark Matter Scythe\",\"description\":\"<passive>Passive - Dark Matter Scythe: </passive>Flay's on-hit passive charges damage very quickly. Flay will throw enemies much farther (based on their Missing Health).<br><br><flavorText>''If you want to make a Singularity from scratch, you must first destroy the universe.''</flavorText>\",\"colloq\":\";\",\"plaintext\":\"\",\"image\":{\"full\":\"3692.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":192,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"id\":3692},\"3693\":{\"name\":\"Gravity Boots\",\"description\":\"<passive>Passive - Mass Conversion: </passive>Thresh's Health represents how far enemy pulls and pushes will send him. At lower Health, he will be thrown farther.<br><br><passive>Passive - Terminus Dwellers: </passive>Abyss Scuttlers emerge periodically, and will scurry towards the Dark Star when attacked. Gravitational disturbances will temporarily attract many of them.\",\"colloq\":\";\",\"plaintext\":\"\",\"image\":{\"full\":\"3693.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":240,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatMovementSpeedMod\":50},\"id\":3693},\"3694\":{\"name\":\"Cloak of Stars\",\"description\":\"<passive>Passive - Stellar Spirit: </passive>Upon spawning, Thresh is invulnerable, untargetable, cannot cast, and is able to travel in open space. This is lost when stepping foot on stable ground.<br><br>Being saved by Dark Passage or using Death Sentence on one of the three <font color='#3091ec'>Gravity Anchors</font> will briefly put you into this invulnerable state and break enemy chains on you.\",\"colloq\":\";\",\"plaintext\":\"\",\"image\":{\"full\":\"3694.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":288,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatMovementSpeedMod\":50},\"id\":3694},\"3695\":{\"name\":\"Dark Star Sigil\",\"description\":\"<passive>Passive - Stellar Fealty: </passive>Thresh cannot kill units directly - their souls, experience, and gold belong to the Dark Star.<br><br>Pulling or pushing an enemy into the Dark Star will destroy them instantly, scoring points for your team (+5, or +1 for Abyss Scuttlers).<br><br>Winning a round requires 100 points, and the final points must be from a champion kill.\",\"colloq\":\";\",\"plaintext\":\"\",\"image\":{\"full\":\"3695.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":336,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":false,\"10\":false,\"11\":false,\"12\":false,\"14\":false,\"16\":false},\"stats\":{\"FlatMovementSpeedMod\":50},\"id\":3695},\"3706\":{\"name\":\"Stalker's Blade\",\"description\":\"<groupLimit>Limited to 1 Jungle item</groupLimit><br><br><stats>+10% Life Steal vs. Monsters<br><mana>+180% Base Mana Regen while in Jungle</mana></stats><br><br><unique>UNIQUE Passive - Chilling Smite:</unique> Smite can be cast on enemy champions, dealing reduced true damage and stealing 20% Movement Speed for 2 seconds. <br><unique>UNIQUE Passive - Tooth / Nail:</unique> Basic attacks deal 25 bonus damage vs. monsters. Damaging a monster with a spell or attack steals 30 Health over 5 seconds. Killing monsters grants <font color='#99BBBB'><a href='SpecialJungleExperience'>special bonus experience</a></font>.\",\"colloq\":\";jungle;Jungle;jangle\",\"plaintext\":\"Lets your Smite slow Champions\",\"from\":[\"1039\",\"1041\"],\"into\":[\"1400\",\"1401\",\"1402\",\"1416\"],\"image\":{\"full\":\"3706.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":384,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":300,\"purchasable\":true,\"total\":1000,\"sell\":700},\"tags\":[\"LifeSteal\",\"ManaRegen\",\"Slow\",\"OnHit\",\"NonbootsMovement\",\"Jungle\"],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"30\",\"Effect2Amount\":\"25\",\"Effect3Amount\":\"1.8\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"30\",\"Effect6Amount\":\"-0.2\",\"Effect7Amount\":\"2\",\"Effect8Amount\":\"3\",\"Effect9Amount\":\"0.1\"},\"depth\":2,\"id\":3706},\"3711\":{\"name\":\"Tracker's Knife\",\"description\":\"<groupLimit>Limited to 1 Jungle item</groupLimit><br><br><stats>+10% Life Steal vs. Monsters<br><mana>+180% Base Mana Regen while in Jungle</mana></stats><br><br><unique>UNIQUE Passive - Tooth / Nail:</unique> Basic attacks deal 25 bonus damage vs. monsters. Damaging a monster with a spell or attack steals 30 Health over 5 seconds. Killing monsters grants <font color='#99BBBB'><a href='SpecialJungleExperience'>special bonus experience</a></font>.<br><active>UNIQUE Active - Warding:</active> Consumes a charge to place a <font color='#BBFFFF'>Stealth Ward</font> that reveals the surrounding area for 150 seconds. Holds up to 2 charges which refill upon visiting the shop. <br><br><rules>(A player may only have 3 <font color='#BBFFFF'>Stealth Wards</font> on the map at one time. Unique Passives with the same name don't stack.)</rules>\",\"colloq\":\";jungle;Jungle\",\"plaintext\":\"Provides Stealth Wards over time\",\"from\":[\"1039\",\"1041\"],\"into\":[\"1408\",\"1409\",\"1410\",\"1418\"],\"image\":{\"full\":\"3711.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":432,\"y\":144,\"w\":48,\"h\":48},\"gold\":{\"base\":300,\"purchasable\":true,\"total\":1000,\"sell\":700},\"tags\":[\"LifeSteal\",\"ManaRegen\",\"Vision\",\"Active\",\"OnHit\",\"Jungle\"],\"maps\":{\"8\":false,\"10\":false,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"30\",\"Effect2Amount\":\"25\",\"Effect3Amount\":\"1.8\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"30\",\"Effect6Amount\":\"3\",\"Effect7Amount\":\"20\",\"Effect8Amount\":\"30\",\"Effect9Amount\":\"0.1\",\"Effect10Amount\":\"150\"},\"depth\":2,\"id\":3711},\"3715\":{\"name\":\"Skirmisher's Sabre\",\"description\":\"<groupLimit>Limited to 1 Jungle item</groupLimit><br><br><stats>+10% Life Steal vs. Monsters<br><mana>+180% Base Mana Regen while in Jungle</mana></stats><br><br><passive>Passive - Challenging Smite:</passive> Smite can be cast on enemy champions, marking them for 4 seconds. While marked, the target is revealed, your basic attacks deal bonus true damage over 3 seconds, and their damage to you is reduced by 20%.<br><unique>UNIQUE Passive - Tooth / Nail:</unique> Basic attacks deal 25 bonus damage vs. monsters. Damaging a monster with a spell or attack steals 30 Health over 5 seconds. Killing monsters grants <font color='#99BBBB'><a href='SpecialJungleExperience'>special bonus experience</a></font>.\",\"colloq\":\";jungle;Jungle\",\"plaintext\":\"Lets your Smite mark Champions, giving you combat power against them.\",\"from\":[\"1039\",\"1041\"],\"into\":[\"1412\",\"1413\",\"1414\",\"1419\"],\"image\":{\"full\":\"3715.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":0,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":300,\"purchasable\":true,\"total\":1000,\"sell\":700},\"tags\":[\"LifeSteal\",\"ManaRegen\",\"OnHit\",\"Jungle\"],\"maps\":{\"8\":false,\"10\":true,\"11\":true,\"12\":false,\"14\":false,\"16\":false},\"stats\":{},\"effect\":{\"Effect1Amount\":\"30\",\"Effect2Amount\":\"25\",\"Effect3Amount\":\"1.8\",\"Effect4Amount\":\"5\",\"Effect5Amount\":\"30\",\"Effect6Amount\":\"3\",\"Effect7Amount\":\"20\",\"Effect8Amount\":\"18\",\"Effect9Amount\":\"0.1\",\"Effect10Amount\":\"4\"},\"depth\":2,\"id\":3715},\"3742\":{\"name\":\"Dead Man's Plate\",\"description\":\"<stats>+425 Health<br>+60 Armor</stats><br><br><unique>UNIQUE Passive - Dreadnought:</unique> While moving, build stacks of Momentum, increasing movement speed by up to 60 at 100 stacks. Momentum decays while under the effect of a slow, stun, taunt, fear, polymorph, or immobilize effect, as well as when basic attacking.<br><unique>UNIQUE Passive - Crushing Blow:</unique> Basic attacks at 100 stacks deal 100 bonus damage and discharge the stacks. If the attacker is melee, they also slow the target by 50% for 1 second.<br><br><flavorText>''There's only one way you'll get this armor from me...'' - forgotten namesake</flavorText>\",\"colloq\":\";juggernaut;dreadnought\",\"plaintext\":\"Build momentum as you move around then smash into enemies.\",\"from\":[\"1031\",\"1011\"],\"image\":{\"full\":\"3742.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":48,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":1100,\"purchasable\":true,\"total\":2900,\"sell\":2030},\"tags\":[\"Health\",\"Armor\",\"OnHit\",\"NonbootsMovement\",\"Bilgewater\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":425,\"FlatArmorMod\":60},\"effect\":{\"Effect1Amount\":\"60\",\"Effect2Amount\":\"100\",\"Effect3Amount\":\"2\",\"Effect4Amount\":\"-0.5\",\"Effect5Amount\":\"1\"},\"depth\":3,\"id\":3742},\"3748\":{\"name\":\"Titanic Hydra\",\"description\":\"<stats>+450 Health<br>+35 Attack Damage<br>+100% Base Health Regen </stats><br><br><unique>UNIQUE Passive - Cleave:</unique> Basic attacks deal 5 + 1% of your maximum health as bonus physical damage  to your target and 40 + 2.5% of your maximum health as physical damage  to other enemies in a cone on hit.<br><active>UNIQUE Active - Crescent:</active> Cleave damage to all targets is increased to 40 + 10% of your maximum health as bonus physical damage  in a larger cone for your next basic attack (20 second cooldown).<br><br><rules>(Unique Passives with the same name don't stack.)</rules>\",\"colloq\":\";juggernaut\",\"plaintext\":\"Deals area of effect damage based on owner's health\",\"from\":[\"3077\",\"1028\",\"3052\"],\"image\":{\"full\":\"3748.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":96,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":700,\"purchasable\":true,\"total\":3500,\"sell\":2450},\"tags\":[\"Health\",\"HealthRegen\",\"Damage\",\"Active\",\"OnHit\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":35,\"FlatHPPoolMod\":450},\"effect\":{\"Effect1Amount\":\"0.025\",\"Effect2Amount\":\"40\",\"Effect3Amount\":\"0\",\"Effect4Amount\":\"0\",\"Effect5Amount\":\"0.1\",\"Effect6Amount\":\"0\",\"Effect7Amount\":\"20\",\"Effect8Amount\":\"40\",\"Effect9Amount\":\"0.01\",\"Effect10Amount\":\"5\"},\"depth\":3,\"id\":3748},\"3751\":{\"name\":\"Bami's Cinder\",\"description\":\"<stats>+280 Health  </stats><br><br><unique>UNIQUE Passive - Immolate:</unique> Deals 5 (+1 per champion level) magic damage per second to nearby enemies. Deals 50% bonus damage to minions and monsters.\",\"colloq\":\";\",\"plaintext\":\"Grants Health and Immolate Aura\",\"from\":[\"1028\"],\"into\":[\"3068\",\"1401\",\"1409\",\"1413\",\"3672\"],\"image\":{\"full\":\"3751.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":144,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":700,\"purchasable\":true,\"total\":1100,\"sell\":770},\"tags\":[\"Health\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":280},\"effect\":{\"Effect1Amount\":\"5\",\"Effect2Amount\":\"1\",\"Effect3Amount\":\"50\"},\"depth\":2,\"id\":3751},\"3800\":{\"name\":\"Righteous Glory\",\"description\":\"<stats>+500 Health<br><mana>+300 Mana</mana><br>+100% Base Health Regen </stats><br><br><unique>UNIQUE Passive - Eternity:</unique> 15% of damage taken from champions is gained as Mana. Spending Mana restores 20% of the cost as Health, up to 25 per spell cast.  <br><unique>UNIQUE Active:</unique> Grants +75% Movement Speed when moving towards enemies or enemy turrets for 4 seconds. After 3 seconds, a shockwave is emitted, slowing nearby enemy champion Movement Speed by 75% for 2 second(s) (90 second cooldown).<br><br>This effect may be reactivated early to instantly release the shockwave.\",\"colloq\":\";\",\"plaintext\":\"Grants Health, Mana. Activate to speed towards enemies and slow them.\",\"from\":[\"3010\",\"3801\"],\"image\":{\"full\":\"3800.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":192,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":750,\"purchasable\":true,\"total\":2500,\"sell\":1750},\"tags\":[\"Health\",\"HealthRegen\",\"Mana\",\"ManaRegen\",\"Active\",\"Slow\",\"NonbootsMovement\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":500,\"FlatMPPoolMod\":300},\"effect\":{\"Effect1Amount\":\"0.75\",\"Effect2Amount\":\"4\",\"Effect3Amount\":\"-0.75\",\"Effect4Amount\":\"2\",\"Effect5Amount\":\"90\",\"Effect6Amount\":\"0.2\",\"Effect7Amount\":\"25\",\"Effect8Amount\":\"0.15\",\"Effect9Amount\":\"3\"},\"depth\":3,\"id\":3800},\"3801\":{\"name\":\"Crystalline Bracer\",\"description\":\"<stats>+200 Health<br>+50% Base Health Regen </stats>\",\"colloq\":\";\",\"plaintext\":\"Grants Health and Health Regen\",\"from\":[\"1028\",\"1006\"],\"into\":[\"3109\",\"3800\",\"3083\",\"3084\",\"3107\"],\"image\":{\"full\":\"3801.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":240,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":100,\"purchasable\":true,\"total\":650,\"sell\":455},\"tags\":[\"Health\",\"HealthRegen\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatHPPoolMod\":200},\"depth\":2,\"id\":3801},\"3802\":{\"name\":\"Lost Chapter\",\"description\":\"<stats>+25 Ability Power<br><mana>+300 Mana</mana></stats><br><br><unique>UNIQUE Passive:</unique> Upon levelling up, restores 20% of your maximum Mana over 3 seconds.\",\"colloq\":\";\",\"plaintext\":\"Restores Mana upon levelling up.\",\"from\":[\"1052\",\"1027\"],\"into\":[\"3165\"],\"image\":{\"full\":\"3802.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":288,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":115,\"purchasable\":true,\"total\":900,\"sell\":630},\"tags\":[\"SpellDamage\",\"Mana\",\"ManaRegen\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatMPPoolMod\":300,\"FlatMagicDamageMod\":25},\"effect\":{\"Effect1Amount\":\"25\",\"Effect2Amount\":\"-0.1\",\"Effect3Amount\":\"15\",\"Effect4Amount\":\"10\",\"Effect5Amount\":\"20\",\"Effect6Amount\":\"5\",\"Effect7Amount\":\"0.2\",\"Effect8Amount\":\"3\"},\"depth\":2,\"id\":3802},\"3812\":{\"name\":\"Death's Dance\",\"description\":\"<stats>+80 Attack Damage<br>+10% Cooldown Reduction</stats><br><br><unique>UNIQUE Passive:</unique> Dealing physical damage heals for 15% of the damage dealt. This is 33% as effective for Area of Effect damage.<br><unique>UNIQUE Passive:</unique> 30% of damage taken is dealt as a Bleed effect over 3 seconds instead.\",\"colloq\":\";Bloodbag\",\"plaintext\":\"Trades incoming damage now for incoming damage later\",\"stacks\":0,\"from\":[\"1053\",\"1037\",\"3133\"],\"image\":{\"full\":\"3812.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":336,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":625,\"purchasable\":true,\"total\":3500,\"sell\":2450},\"tags\":[\"Damage\",\"LifeSteal\",\"CooldownReduction\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":80},\"effect\":{\"Effect1Amount\":\"0.15\",\"Effect2Amount\":\"0.3\",\"Effect3Amount\":\"3\"},\"depth\":3,\"id\":3812},\"3814\":{\"name\":\"Edge of Night\",\"description\":\"<stats>+55 Attack Damage<br>+35 Magic Resist</stats><br><br><unique>UNIQUE Passive:</unique> +15 <a href='Lethality'>Lethality</a><br><unique>UNIQUE Passive:</unique> +20 Movement Speed out of Combat<br><unique>UNIQUE Active - Night's Veil:</unique> Channel for 1.5 second to grant a spell shield that blocks the next enemy ability. Lasts for 5 seconds (45 second cooldown).<br><br><rules>(Can move while channeling, but taking damage breaks the channel.)</rules>\",\"colloq\":\";lethality\",\"plaintext\":\"Blocks an incoming enemy spell.\",\"stacks\":0,\"from\":[\"1037\",\"3134\",\"1033\"],\"image\":{\"full\":\"3814.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":384,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":675,\"purchasable\":true,\"total\":3100,\"sell\":2170},\"tags\":[\"SpellBlock\",\"Damage\",\"NonbootsMovement\",\"ArmorPenetration\"],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{\"FlatPhysicalDamageMod\":55,\"FlatSpellBlockMod\":35},\"effect\":{\"Effect1Amount\":\"15\",\"Effect2Amount\":\"5\",\"Effect3Amount\":\"20\",\"Effect4Amount\":\"45\",\"Effect5Amount\":\"5\"},\"depth\":3,\"id\":3814},\"3901\":{\"name\":\"Fire at Will\",\"description\":\"Requires 500 Silver Serpents.<br><br><unique>UNIQUE Passive:</unique> Cannon Barrage fires at an increasing rate over time (additional 6 waves over the duration).\",\"colloq\":\"\",\"plaintext\":\"Cannon Barrage gains extra waves\",\"consumed\":true,\"consumeOnFull\":true,\"requiredChampion\":\"Gangplank\",\"image\":{\"full\":\"3901.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":432,\"y\":192,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"id\":3901},\"3902\":{\"name\":\"Death's Daughter\",\"description\":\"Requires 500 Silver Serpents.<br><br><unique>UNIQUE Passive:</unique> Cannon Barrage additionally fires a mega-cannonball at center of the Barrage, dealing 300% true damage and slowing them by 60% for 1.5 seconds. \",\"colloq\":\"\",\"plaintext\":\"Cannon Barrage fires a mega-cannonball\",\"consumed\":true,\"consumeOnFull\":true,\"requiredChampion\":\"Gangplank\",\"image\":{\"full\":\"3902.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":0,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"id\":3902},\"3903\":{\"name\":\"Raise Morale\",\"description\":\"Requires 500 Silver Serpents.<br><br><unique>UNIQUE Passive:</unique> Allies in the Cannon Barrage gain 30% Movement Speed for 2 seconds.\",\"colloq\":\"\",\"plaintext\":\"Cannon Barrage hastes allies\",\"consumed\":true,\"consumeOnFull\":true,\"requiredChampion\":\"Gangplank\",\"image\":{\"full\":\"3903.png\",\"sprite\":\"item2.png\",\"group\":\"item\",\"x\":48,\"y\":240,\"w\":48,\"h\":48},\"gold\":{\"base\":0,\"purchasable\":true,\"total\":0,\"sell\":0},\"tags\":[],\"maps\":{\"8\":true,\"10\":true,\"11\":true,\"12\":true,\"14\":false,\"16\":false},\"stats\":{},\"id\":3903}}"
  },
  {
    "path": "api_data/masteries.json",
    "content": "{\"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.<br><br>Damage: 8 + 45% Bonus Attack Damage and 25% Ability Power<br><br>Deathfire Touch's duration is reduced for:<br>     - Area of Effect: 2 second duration. <br>     - 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.<br>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.<br><br>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. <br><br>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. <br><br>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\"}}"
  },
  {
    "path": "api_data/runes.js",
    "content": "module.exports = '';"
  },
  {
    "path": "api_data/skills.json",
    "content": "{\"Aatrox\":{\"id\":266,\"key\":\"Aatrox\",\"name\":\"Aatrox\",\"title\":\"the Darkin Blade\",\"spells\":[{\"id\":\"AatroxQ\",\"name\":\"Dark Flight\",\"description\":\"Aatrox takes flight and slams down at a targeted location, dealing damage and knocking up enemies at the center of impact.\",\"tooltip\":\"Aatrox takes flight and slams down at target location, dealing {{ e1 }}<span class=\\\"colorF88017\\\"> (+{{ a1 }})</span> physical damage and knocking up enemies at the center of impact for {{ e4 }} second.<br><br>Aatrox fills up 20% of the Blood Well upon cast.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[13,12.5,12,11.5,11],\"cooldownBurn\":\"13/12.5/12/11.5/11\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[10,35,60,95,120],[310,310,310,310,310],[190,190,190,190,190],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"10/35/60/95/120\",\"310\",\"190\",\"1\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1.1,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[25000,25000,25000,25000,25000],\"rangeBurn\":\"25000\",\"image\":{\"full\":\"AatroxQ.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":336,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"AatroxW\",\"name\":\"Blood Thirst / Blood Price\",\"description\":\"While toggled on Aatrox deals bonus damage and fills a portion of his Blood Well every third subsequent attack. While toggled off Aatrox restores Health every third subsequent attack.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Toggle Off:</span><span class=\\\"colore60000\\\"> Blood Thirst: </span>Every third attack, Aatrox restores {{ e3 }} <span class=\\\"colorCC3300\\\">(+{{ f1*100 }}% Missing Health)</span> Health.<br><br><span class=\\\"colorFF9900\\\">Toggle On:</span><span class=\\\"color9900ff\\\"> Blood Price: </span>Every third attack, Aatrox deals {{ e2 }}<span class=\\\"colorF88017\\\"> (+{{ a2 }})</span> bonus physical damage and fills up 20% of the Blood Well.\",\"leveltip\":{\"label\":[\"Heal\",\"Damage\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[0.5,0.5,0.5,0.5,0.5],\"cooldownBurn\":\"0.5\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[200,200,200,200,200],[45,80,115,150,185],[30,45,60,75,90],[50,50,50,50,50],[50,50,50,50,50],[0.5,0.5,0.5,0.5,0.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"200\",\"45/80/115/150/185\",\"30/45/60/75/90\",\"50\",\"50\",\"0.5\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.75,\"key\":\"a2\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[1,1,1,1,1],\"rangeBurn\":\"1\",\"image\":{\"full\":\"AatroxW.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":384,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"AatroxE\",\"name\":\"Blades of Torment\",\"description\":\"Aatrox unleashes the power of his blade, dealing damage to enemies hit and slowing them.\",\"tooltip\":\"Aatrox unleashes the power of his blade, dealing {{ e1 }} <span class=\\\"colorF88017\\\">(+{{ a2 }})</span> physical damage to enemies hit and slowing them by {{ e2 }}% for {{ e3 }} seconds.<br><br>Aatrox fills up 20% of the Blood Well upon cast.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[12,11,10,9,8],\"cooldownBurn\":\"12/11/10/9/8\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[70,110,150,190,230],[30,35,40,45,50],[2,2,2,2,2],[30,30,30,30,30],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/110/150/190/230\",\"30/35/40/45/50\",\"2\",\"30\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.7,\"key\":\"a2\"}],\"costType\":\" Health\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"AatroxE.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":432,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ e4 }} Health\"},{\"id\":\"AatroxR\",\"name\":\"Massacre\",\"description\":\"Aatrox draws in the blood of his foes, damaging all nearby enemy champions around him and gaining increased Attack Speed and bonus Attack Range for a short duration.\",\"tooltip\":\"Aatrox draws in the blood of his foes, dealing {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to nearby enemy champions, filling up 20% of the Blood Well for each hit.<br><br>Aatrox gains {{ e3 }}% attack speed and {{ e5 }} attack range for {{ e8 }} seconds upon cast.\",\"leveltip\":{\"label\":[\"Damage\",\"Attack Speed\",\"Cooldown\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[100,85,70],\"cooldownBurn\":\"100/85/70\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[12,12,12],[200,300,400],[40,50,60],[10,10,10],[175,175,175],[50,65,80],[20,20,20],[12,12,12],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"12\",\"200/300/400\",\"40/50/60\",\"10\",\"175\",\"50/65/80\",\"20\",\"12\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[550,550,550],\"rangeBurn\":\"550\",\"image\":{\"full\":\"AatroxR.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":0,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Ahri\":{\"id\":103,\"key\":\"Ahri\",\"name\":\"Ahri\",\"title\":\"the Nine-Tailed Fox\",\"spells\":[{\"id\":\"AhriOrbofDeception\",\"name\":\"Orb of Deception\",\"description\":\"Ahri sends out and pulls back her orb, dealing magic damage on the way out and true damage on the way back. Ahri gains movement speed that decays while her orb is traveling.\",\"tooltip\":\"Deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage on the way out, and {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> true damage on the way back.<br><br>Ahri gains movement speed that decays while her orb is traveling.\",\"leveltip\":{\"label\":[\"Damage\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"  {{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[7,7,7,7,7],\"cooldownBurn\":\"7\",\"cost\":[65,70,75,80,85],\"costBurn\":\"65/70/75/80/85\",\"effect\":[null,[40,65,90,115,140],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"40/65/90/115/140\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.35,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.35,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[880,880,880,880,880],\"rangeBurn\":\"880\",\"image\":{\"full\":\"AhriOrbofDeception.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":48,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"AhriFoxFire\",\"name\":\"Fox-Fire\",\"description\":\"Ahri releases three fox-fires, that lock onto and attack nearby enemies.\",\"tooltip\":\"Releases three fox-fires that lock on to nearby enemies (prioritizes Champions) dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.<br><br>Enemies hit with multiple fox-fires take 30% damage from each additional fox-fire beyond the first, for a maximum of {{ f1 }} damage to a single enemy.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[9,8,7,6,5],\"cooldownBurn\":\"9/8/7/6/5\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[40,65,90,115,140],[12,19.5,27,34.5,42],[64,104,144,184,224],[30,30,30,30,30],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"40/65/90/115/140\",\"12/19.5/27/34.5/42\",\"64/104/144/184/224\",\"30\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[700,700,700,700,700],\"rangeBurn\":\"700\",\"image\":{\"full\":\"AhriFoxFire.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":96,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"AhriSeduce\",\"name\":\"Charm\",\"description\":\"Ahri blows a kiss that damages and charms an enemy it encounters, causing them to walk harmlessly towards her.\",\"tooltip\":\"Blows a kiss dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and charms an enemy causing them to walk harmlessly towards Ahri for {{ e2 }} second(s).\",\"leveltip\":{\"label\":[\"Damage\",\"Duration\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[12,12,12,12,12],\"cooldownBurn\":\"12\",\"cost\":[85,85,85,85,85],\"costBurn\":\"85\",\"effect\":[null,[60,95,130,165,200],[1,1.25,1.5,1.75,2],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/95/130/165/200\",\"1/1.25/1.5/1.75/2\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[975,975,975,975,975],\"rangeBurn\":\"975\",\"image\":{\"full\":\"AhriSeduce.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":144,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"AhriTumble\",\"name\":\"Spirit Rush\",\"description\":\"Ahri dashes forward and fires essence bolts, damaging 3 nearby enemies (prioritizes Champions). Spirit Rush can be cast up to three times before going on cooldown.\",\"tooltip\":\"Nimbly dashes forward firing 3 essence bolts at nearby enemies (prioritizes Champions) dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage. Can be cast up to three times within 10 seconds before going on cooldown.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[110,95,80],\"cooldownBurn\":\"110/95/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[70,110,150],[10,20,30],[28,23,18],[2,2,2],[1,1,1],[10,10,10],[10,10,10],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"70/110/150\",\"10/20/30\",\"28/23/18\",\"2\",\"1\",\"10\",\"10\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.25,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[450,450,450],\"rangeBurn\":\"450\",\"image\":{\"full\":\"AhriTumble.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":192,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Akali\":{\"id\":84,\"key\":\"Akali\",\"name\":\"Akali\",\"title\":\"the Fist of Shadow\",\"spells\":[{\"id\":\"AkaliMota\",\"name\":\"Mark of the Assassin\",\"description\":\"Akali spins her kama at a target enemy to deal Magic Damage and mark the target for 6 seconds. Akali's melee attacks against a marked target will trigger and consume the mark to cause additional damage and restore Energy.\",\"tooltip\":\"Akali throws her kama at a target enemy to deal {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and mark the target for {{ e4 }} seconds.<br><br>Akali's melee attacks against a marked target will consume the mark to deal {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage and restore {{ e3 }} energy.\",\"leveltip\":{\"label\":[\"Damage (initial)\",\"Damage (secondary)\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[6,5.5,5,4.5,4],\"cooldownBurn\":\"6/5.5/5/4.5/4\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[35,55,75,95,115],[45,70,95,120,145],[40,40,40,40,40],[6,6,6,6,6],[0,0,0,0,0],[0.4,0.4,0.4,0.4,0.4],[4,4,4,4,4],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"35/55/75/95/115\",\"45/70/95/120/145\",\"40\",\"6\",\"0\",\"0.4\",\"4\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a2\"}],\"costType\":\" Energy\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"AkaliMota.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":240,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Energy\"},{\"id\":\"AkaliSmokeBomb\",\"name\":\"Twilight Shroud\",\"description\":\"Akali teleports to a nearby location, leaving a cover of smoke at her previous location. While inside the shroud, Akali becomes Invisible and gains Movement Speed. Attacking or using abilities will briefly reveal her. Enemies inside the smoke have their Movement Speed reduced.\",\"tooltip\":\"Akali teleports to a nearby location, leaving a cover of smoke at her previous location that lasts for {{ e2 }} seconds and slows enemies within its area by {{ e3 }}%.<br><br>While inside the shroud Akali gains <span class=\\\"color91d7ee\\\">Invisibility</span> and {{ e6 }}% movement speed. Attacking or using abilities briefly removes <span class=\\\"color91d7ee\\\">Invisibility</span>.<br><br><u><span class=\\\"size16 color91d7ee\\\">16\",\"leveltip\":{\"label\":[\"Movement Speed\",\"Slow\",\"Cost\"],\"effect\":[\"{{ e6 }}% -> {{ e6NL }}%\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[20,20,20,20,20],\"cooldownBurn\":\"20\",\"cost\":[60,55,50,45,40],\"costBurn\":\"60/55/50/45/40\",\"effect\":[null,[425,425,425,425,425],[8,8,8,8,8],[14,18,22,26,30],[250,250,250,250,250],[0,0,0,0,0],[20,40,60,80,100],[1,1,1,1,1],[0.5,0.5,0.5,0.5,0.5],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"425\",\"8\",\"14/18/22/26/30\",\"250\",\"0\",\"20/40/60/80/100\",\"1\",\"0.5\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Energy\",\"maxammo\":\"-1\",\"range\":[270,270,270,270,270],\"rangeBurn\":\"270\",\"image\":{\"full\":\"AkaliSmokeBomb.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":288,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Energy\"},{\"id\":\"AkaliShadowSwipe\",\"name\":\"Crescent Slash\",\"description\":\"Akali flourishes her kamas, dealing damage based on her bonus Attack Damage and Ability Power. When Crescent Slash kills a unit, it has a shorter cooldown.\",\"tooltip\":\"Akali flourishes her kamas, slicing enemies for {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a2 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> physical damage.<br><br>If Crescent Slash kills an enemy, its cooldown is refunded by {{ e3 }}%.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[5,4.5,4,3.5,3],\"cooldownBurn\":\"5/4.5/4/3.5/3\",\"cost\":[60,55,50,45,40],\"costBurn\":\"60/55/50/45/40\",\"effect\":[null,[70,100,130,160,190],[1,1,1,1,1],[60,60,60,60,60],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/100/130/160/190\",\"1\",\"60\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.8,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Energy\",\"maxammo\":\"-1\",\"range\":[300,300,300,300,300],\"rangeBurn\":\"300\",\"image\":{\"full\":\"AkaliShadowSwipe.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":336,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Energy\"},{\"id\":\"AkaliShadowDance\",\"name\":\"Shadow Dance\",\"description\":\"Akali moves through shadows to quickly strike through her target, dealing damage and consuming an Essence of Shadow charge. Akali recharges Essence of Shadow charges periodically, max 3 stacks.\",\"tooltip\":\"Akali quickly strikes through her target, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.<br><br>Akali stores an Essence of Shadow on kills and assists as well as every <span class=\\\"colorFFFFFF\\\">{{ f1 }}</span> seconds up to {{ e4 }} total.\",\"leveltip\":{\"label\":[\"Damage\",\"Essence Refresh Timer\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ f1 }} -> {{ f2 }}\"]},\"maxrank\":3,\"cooldown\":[2,2,2],\"cooldownBurn\":\"2\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[50,100,150],[2000,2000,2000],[1,1,1],[3,3,3],[1,1,1],[100,100,100],[200,200,200],[600,600,600],[225,225,225],[1,1,1]],\"effectBurn\":[null,\"50/100/150\",\"2000\",\"1\",\"3\",\"1\",\"100\",\"200\",\"600\",\"225\",\"1\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.35,\"key\":\"a1\"},{\"link\":\"@cooldownchampion\",\"coeff\":[30,22.5,15],\"key\":\"f1\"},{\"link\":\"@cooldownchampion\",\"coeff\":[30,22.5,15],\"key\":\"f1\"},{\"link\":\"@cooldownchampion\",\"coeff\":[22.5,15],\"key\":\"f2\"}],\"costType\":\" Essence of Shadow\",\"maxammo\":\"3\",\"range\":[700,700,700],\"rangeBurn\":\"700\",\"image\":{\"full\":\"AkaliShadowDance.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":384,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ e5 }} Essence of Shadow\"}]},\"Alistar\":{\"id\":12,\"key\":\"Alistar\",\"name\":\"Alistar\",\"title\":\"the Minotaur\",\"spells\":[{\"id\":\"Pulverize\",\"name\":\"Pulverize\",\"description\":\"Alistar smashes the ground, dealing damage to nearby enemies and tossing them into the air.\",\"tooltip\":\"Alistar smashes the ground, dealing {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and tossing nearby enemy units into the air for {{ e3 }} second.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[17,16,15,14,13],\"cooldownBurn\":\"17/16/15/14/13\",\"cost\":[65,70,75,80,85],\"costBurn\":\"65/70/75/80/85\",\"effect\":[null,[375,375,375,375,375],[60,105,150,195,240],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"375\",\"60/105/150/195/240\",\"1\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[365,365,365,365,365],\"rangeBurn\":\"365\",\"image\":{\"full\":\"Pulverize.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":432,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"Headbutt\",\"name\":\"Headbutt\",\"description\":\"Alistar rams a target with his head, dealing damage and knocking the target back.\",\"tooltip\":\"Alistar rams into an enemy, dealing {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and knocking them back.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[14,13,12,11,10],\"cooldownBurn\":\"14/13/12/11/10\",\"cost\":[65,70,75,80,85],\"costBurn\":\"65/70/75/80/85\",\"effect\":[null,[0,0,0,0,0],[55,110,165,220,275],[700,700,700,700,700],[0.75,0.75,0.75,0.75,0.75],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"0\",\"55/110/165/220/275\",\"700\",\"0.75\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"Headbutt.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":0,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"AlistarE\",\"name\":\"Trample\",\"description\":\"Alistar tramples nearby enemy units, ignoring unit colision and gaining stacks if he damages an enemy champion. At full stacks Alistar's next basic attack against an enemy champion deals additional magic damage and stuns them.\",\"tooltip\":\"Alistar tramples the ground, ignoring unit collision and dealing {{ f1 }} <span class=\\\"color99FF99\\\">(+{{ f2 }})</span> magic damage over {{ e3 }} seconds to nearby enemies. Each pulse that damages at least one enemy champion grants Alistar a <span class=\\\"colorFF6E1C\\\">Trample</span> stack.<br><br>At {{ e5 }} <span class=\\\"colorFF6E1C\\\">Trample</span> stacks Alistar empowers his next basic attack against an enemy champion to deal an additional <span class=\\\"colorFFFFFF\\\">{{ f3 }}</span> magic damage and stun for {{ e6 }} second.\",\"leveltip\":{\"label\":[\"Cooldown\",\"Mana Cost\",\"Trample Damage\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\",\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":5,\"cooldown\":[12,11.5,11,10.5,10],\"cooldownBurn\":\"12/11.5/11/10.5/10\",\"cost\":[50,60,70,80,90],\"costBurn\":\"50/60/70/80/90\",\"effect\":[null,[100,125,150,175,200],[50,50,50,50,50],[5,5,5,5,5],[350,350,350,350,350],[5,5,5,5,5],[1,1,1,1,1],[5,5,5,5,5],[40,40,40,40,40],[15,15,15,15,15],[0,0,0,0,0]],\"effectBurn\":[null,\"100/125/150/175/200\",\"50\",\"5\",\"350\",\"5\",\"1\",\"5\",\"40\",\"15\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[350,350,350,350,350],\"rangeBurn\":\"350\",\"image\":{\"full\":\"AlistarE.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":48,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"FerociousHowl\",\"name\":\"Unbreakable Will\",\"description\":\"Alistar lets out a wild roar, removing all crowd control effects on himself, and reducing incoming physical and magical damage for the duration.\",\"tooltip\":\"Removes all disables from Alistar. For {{ e1 }} seconds Alistar takes {{ e2 }}% reduced physical and magical damage.\",\"leveltip\":{\"label\":[\"Damage Reduction\",\"Cooldown\"],\"effect\":[\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,100,80],\"cooldownBurn\":\"120/100/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[7,7,7],[50,60,70],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"7\",\"50/60/70\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1,1,1],\"rangeBurn\":\"1\",\"image\":{\"full\":\"FerociousHowl.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":96,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Amumu\":{\"id\":32,\"key\":\"Amumu\",\"name\":\"Amumu\",\"title\":\"the Sad Mummy\",\"spells\":[{\"id\":\"BandageToss\",\"name\":\"Bandage Toss\",\"description\":\"Amumu tosses a sticky bandage at a target, stunning and damaging the target while he pulls himself to them.\",\"tooltip\":\"Launches a bandage in a direction. If it hits an enemy unit, Amumu pulls himself to them, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and stunning for {{ e2 }} second. \",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[16,14,12,10,8],\"cooldownBurn\":\"16/14/12/10/8\",\"cost\":[80,90,100,110,120],\"costBurn\":\"80/90/100/110/120\",\"effect\":[null,[80,130,180,230,280],[1,1,1,1,1],[1350,1350,1350,1350,1350],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/130/180/230/280\",\"1\",\"1350\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1100,1100,1100,1100,1100],\"rangeBurn\":\"1100\",\"image\":{\"full\":\"BandageToss.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":144,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"AuraofDespair\",\"name\":\"Despair\",\"description\":\"Overcome by anguish, nearby enemies lose a percentage of their maximum Health each second and have their <font color='#9b0f5f'>Curses</font> refreshed.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Toggle: </span>Amumu cries, refreshing <span class=\\\"color9b0f5f\\\">Curses</span> on nearby enemies and dealing magic damage equal to {{ e2 }} plus {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span>% of their maximum health each second.\",\"leveltip\":{\"label\":[\"Percent Health Damaged\",\"Base Damage\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[1,1,1,1,1],\"cooldownBurn\":\"1\",\"cost\":[8,8,8,8,8],\"costBurn\":\"8\",\"effect\":[null,[1,1.25,1.5,1.75,2],[10,15,20,25,30],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"1/1.25/1.5/1.75/2\",\"10/15/20/25/30\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.01,\"key\":\"a1\"}],\"costType\":\" Mana per Second\",\"maxammo\":\"-1\",\"range\":[300,300,300,300,300],\"rangeBurn\":\"300\",\"image\":{\"full\":\"AuraofDespair.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":192,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana per Second\"},{\"id\":\"Tantrum\",\"name\":\"Tantrum\",\"description\":\"Permanently reduces the physical damage Amumu would take. Amumu can unleash his rage, dealing damage to surrounding enemies. Each time Amumu is hit, the cooldown on Tantrum is reduced by 0.5 seconds.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Amumu takes {{ e1 }} reduced damage from physical attacks.<br><br><span class=\\\"colorFF9900\\\">Active: </span>Amumu tantrums, dealing {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to surrounding units.<br><br><span class=\\\"size16 color8C8C8C\\\">16\",\"leveltip\":{\"label\":[\"Damage Reduced\",\"Cooldown\",\"Damage\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ cooldown }} -> {{ cooldownNL }}\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[10,9,8,7,6],\"cooldownBurn\":\"10/9/8/7/6\",\"cost\":[35,35,35,35,35],\"costBurn\":\"35\",\"effect\":[null,[2,4,6,8,10],[75,100,125,150,175],[0.5,0.5,0.5,0.5,0.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"2/4/6/8/10\",\"75/100/125/150/175\",\"0.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[350,350,350,350,350],\"rangeBurn\":\"350\",\"image\":{\"full\":\"Tantrum.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":240,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"CurseoftheSadMummy\",\"name\":\"Curse of the Sad Mummy\",\"description\":\"Amumu entangles surrounding enemy units in bandages, applying his <font color='#9b0f5f'>Curse</font>, damaging them and rendering them unable to attack or move.\",\"tooltip\":\"Amumu entangles nearby enemy units, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and applying <span class=\\\"color9b0f5f\\\">Curse</span>. Entangled enemies are unable to attack or move for 2 seconds.\",\"leveltip\":{\"label\":[\"Damage Dealt\",\"Mana Cost\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cost }} -> {{ costNL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[150,130,110],\"cooldownBurn\":\"150/130/110\",\"cost\":[100,150,200],\"costBurn\":\"100/150/200\",\"effect\":[null,[150,250,350],[2,2,2],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"150/250/350\",\"2\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[550,550,550],\"rangeBurn\":\"550\",\"image\":{\"full\":\"CurseoftheSadMummy.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":288,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Anivia\":{\"id\":34,\"key\":\"Anivia\",\"name\":\"Anivia\",\"title\":\"the Cryophoenix\",\"spells\":[{\"id\":\"FlashFrost\",\"name\":\"Flash Frost\",\"description\":\"Anivia brings her wings together and summons a sphere of ice that flies towards her opponents, chilling and damaging anyone in its path. When the sphere explodes it does moderate damage in a radius, stunning anyone in the area.\",\"tooltip\":\"A massive chunk of ice flies toward target location, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage. <br><br>At the end of its range or if Anivia activates the spell again, the missile detonates, doing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage in a small area and stunning units for {{ e4 }} seconds.<br><br>Enemies damaged by Flash Frost are also slowed by {{ f1 }}% for {{ e5 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Stun Duration\",\"Cooldown\",\"Mana Cost \"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\" {{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[12,11,10,9,8],\"cooldownBurn\":\"12/11/10/9/8\",\"cost\":[80,90,100,110,120],\"costBurn\":\"80/90/100/110/120\",\"effect\":[null,[60,85,110,135,160],[13,12,11,10,9],[0,0,0,0,0],[1.1,1.2,1.3,1.4,1.5],[3,3,3,3,3],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/85/110/135/160\",\"13/12/11/10/9\",\"0\",\"1.1/1.2/1.3/1.4/1.5\",\"3\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1075,1075,1075,1075,1075],\"rangeBurn\":\"1075\",\"image\":{\"full\":\"FlashFrost.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":336,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"Crystallize\",\"name\":\"Crystallize\",\"description\":\"Anivia condenses the moisture in the air into an impassable wall of ice to block all movement. The wall only lasts a short duration before it melts.\",\"tooltip\":\"Anivia summons an impassable wall of ice {{ e2 }} units wide, blocking all movement. The wall lasts for {{ e1 }} seconds before it melts.\",\"leveltip\":{\"label\":[\"Width\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[17,17,17,17,17],\"cooldownBurn\":\"17\",\"cost\":[70,70,70,70,70],\"costBurn\":\"70\",\"effect\":[null,[5,5,5,5,5],[400,500,600,700,800],[4,5,6,7,8],[120,120,120,120,120],[250,250,250,250,250],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"5\",\"400/500/600/700/800\",\"4/5/6/7/8\",\"120\",\"250\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"Crystallize.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":384,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"Frostbite\",\"name\":\"Frostbite\",\"description\":\"With a flap of her wings, Anivia blasts a freezing gust of wind at her target, dealing a low amount of damage. If the target was recently stunned by Flash Frost or damaged by a fully formed Glacial Storm, the damage they take is doubled.\",\"tooltip\":\"Anivia blasts her target with a freezing wind, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage. <br><br>If a target was recently stunned by Anivia or damaged by a fully formed Glacial Storm, they take double damage.\",\"leveltip\":{\"label\":[\"Damage\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[4,4,4,4,4],\"cooldownBurn\":\"4\",\"cost\":[50,60,70,80,90],\"costBurn\":\"50/60/70/80/90\",\"effect\":[null,[50,75,100,125,150],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/75/100/125/150\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"Frostbite.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":432,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"GlacialStorm\",\"name\":\"Glacial Storm\",\"description\":\"Anivia summons a driving rain of ice and hail to damage her enemies and slow their advance.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Toggle: </span>Drains {{ e2 }} Mana per second. <br><br>Anivia calls forth a driving rain of ice and hail that increases in size over {{ e7 }} seconds, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage per second to targets and slowing their Movement Speed by {{ e4 }}%. <br><br>When the Glacial Storm is fully formed, it slows targets' Movement Speed by {{ f1 }}% and does {{ e3 }}% damage instead.\",\"leveltip\":{\"label\":[\"Damage Per Second\",\"Chilled Slow Amount\",\"Mana Cost Per Second\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }}% -> {{ e4NL }}%\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":3,\"cooldown\":[6,6,6],\"cooldownBurn\":\"6\",\"cost\":[75,75,75],\"costBurn\":\"75\",\"effect\":[null,[40,60,80],[40,50,60],[300,300,300],[20,30,40],[0,0,0],[1,1,1],[1.5,1.5,1.5],[50,50,50],[800,800,800],[1000,1000,1000]],\"effectBurn\":[null,\"40/60/80\",\"40/50/60\",\"300\",\"20/30/40\",\"0\",\"1\",\"1.5\",\"50\",\"800\",\"1000\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.125,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[750,750,750],\"rangeBurn\":\"750\",\"image\":{\"full\":\"GlacialStorm.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":0,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Annie\":{\"id\":1,\"key\":\"Annie\",\"name\":\"Annie\",\"title\":\"the Dark Child\",\"spells\":[{\"id\":\"Disintegrate\",\"name\":\"Disintegrate\",\"description\":\"Annie hurls a Mana infused fireball, dealing damage and refunding the Mana cost if it destroys the target.\",\"tooltip\":\"Deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage. Mana cost and half the cooldown are refunded if Disintegrate kills the target.\",\"leveltip\":{\"label\":[\"Damage\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[4,4,4,4,4],\"cooldownBurn\":\"4\",\"cost\":[60,65,70,75,80],\"costBurn\":\"60/65/70/75/80\",\"effect\":[null,[80,115,150,185,220],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/115/150/185/220\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[625,625,625,625,625],\"rangeBurn\":\"625\",\"image\":{\"full\":\"Disintegrate.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":48,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"Incinerate\",\"name\":\"Incinerate\",\"description\":\"Annie casts a blazing cone of fire, dealing damage to all enemies in the area.\",\"tooltip\":\"Casts a cone of fire dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to all enemies in the area.\",\"leveltip\":{\"label\":[\"Damage\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[8,8,8,8,8],\"cooldownBurn\":\"8\",\"cost\":[70,80,90,100,110],\"costBurn\":\"70/80/90/100/110\",\"effect\":[null,[70,115,160,205,250],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/115/160/205/250\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.85,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"Incinerate.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":96,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"MoltenShield\",\"name\":\"Molten Shield\",\"description\":\"Grants Annie and Tibbers increased percentage Damage Resist and damages enemies who attack with basic attacks.\",\"tooltip\":\"Annie grants herself and Tibbers {{ e1 }}% damage reduction for {{ e3 }} seconds.<br><br>While the shield is active, enemies who basic attack it take {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.\",\"leveltip\":{\"label\":[\"Damage Reduction\",\"Damage Return\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[10,10,10,10,10],\"cooldownBurn\":\"10\",\"cost\":[20,20,20,20,20],\"costBurn\":\"20\",\"effect\":[null,[16,22,28,34,40],[20,30,40,50,60],[3,3,3,3,3],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"16/22/28/34/40\",\"20/30/40/50/60\",\"3\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[0,0,0,0,0],\"rangeBurn\":\"0\",\"image\":{\"full\":\"MoltenShield.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":144,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"InfernalGuardian\",\"name\":\"Summon: Tibbers\",\"description\":\"Annie wills her bear Tibbers to life, dealing damage to units in the area. Tibbers can attack and also burns enemies that stand near him.\",\"tooltip\":\"Summons Tibbers, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemies in the target area. For the next 45 seconds, Tibbers burns nearby enemies for {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> per second and attacks for {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ f1 }})</span> as magic damage. Annie can control Tibbers by reactivating this ability.<br><br>Tibbers <span class=\\\"colorFFEB7F\\\">Enrages</span> when: summoned; Annie uses Pyromania on an enemy Champion; and when Annie dies.<br><br><span class=\\\"colorFFEB7F\\\">Enrages</span>: Tibbers gains 275% Attack Speed and 100% Movement Speed, decaying over 3 seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Tibbers Health\",\"Tibbers Armor and Magic Resist\",\"Tibbers Attack Damage\",\"Tibbers Burn Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e0 }} -> {{ e0NL }}\",\"{{ e7 }} -> {{ e7NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,100,80],\"cooldownBurn\":\"120/100/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[150,275,400],[10,15,20],[50,75,100],[-0.1,-0.1,-0.1],[1,1,1],[3,3,3],[30,50,70],[0,900,1800],[0.15,0.15,0.15],[1200,2100,3000]],\"effectBurn\":[null,\"150/275/400\",\"10/15/20\",\"50/75/100\",\"-0.1\",\"1\",\"3\",\"30/50/70\",\"0/900/1800\",\"0.15\",\"1200/2100/3000\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.65,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.1,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"InfernalGuardian.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":192,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Ashe\":{\"id\":22,\"key\":\"Ashe\",\"name\":\"Ashe\",\"title\":\"the Frost Archer\",\"spells\":[{\"id\":\"AsheQ\",\"name\":\"Ranger's Focus\",\"description\":\"Ashe builds up Focus by attacking. At maximum Focus, Ashe can cast Ranger's Focus to consume all stacks of Focus, temporarily increasing her Attack Speed and transforming her basic attack into a powerful flurry attack for the duration.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Basic attacks grant Focus for {{ e1 }} seconds, stacking up to {{ e2 }} times. Stacks fall off one at a time, and at {{ e2 }} stacks, Ranger's Focus can be cast, consuming all Focus.<br><br><span class=\\\"colorFF9900\\\">Active:</span> For {{ e3 }} seconds, Ashe gains {{ e4 }}% Attack Speed, and her basic attacks fire a flurry of arrows dealing <span class=\\\"colorFF8C00\\\">{{ f1 }}</span> physical damage. During this time, she does not stack Focus. Ranger's Focus applies Frost Shot.\",\"leveltip\":{\"label\":[\"Attack Speed Bonus\",\"Flurry Attack Damage ratio\"],\"effect\":[\"{{ e4 }}% -> {{ e4NL }}%\",\"{{ e6 }} -> {{ e6NL }}\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[4,4,4,4,4],[4,4,4,4,4],[4,4,4,4,4],[20,25,30,35,40],[0.21,0.22,0.23,0.24,0.25],[1.05,1.1,1.15,1.2,1.25],[1,1,1,1,1],[1,1,1,1,1],[1,1,1,1,1],[0,0,0,0,0]],\"effectBurn\":[null,\"4\",\"4\",\"4\",\"20/25/30/35/40\",\"0.21/0.22/0.23/0.24/0.25\",\"1.05/1.1/1.15/1.2/1.25\",\"1\",\"1\",\"1\",\"0\"],\"vars\":[],\"costType\":\" Mana, {{ e2 }} Focus\",\"maxammo\":\"-1\",\"range\":[400,400,400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"AsheQ.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":240,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana, {{ e2 }} Focus\"},{\"id\":\"Volley\",\"name\":\"Volley\",\"description\":\"Ashe fires 9 arrows in a cone for increased damage. Also applies Frost Shot.\",\"tooltip\":\"Fires arrows in a cone, each dealing {{ e2 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage. Enemies can block multiple arrows, but only take damage from the first.<br><br>Champion hits count as Critical Strikes for the purposes of Frost Shot.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Cooldown\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[15,12.5,10,7.5,5],\"cooldownBurn\":\"15/12.5/10/7.5/5\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[5,7,9,11,13],[20,35,50,65,80],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"5/7/9/11/13\",\"20/35/50/65/80\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1200,1200,1200,1200,1200],\"rangeBurn\":\"1200\",\"image\":{\"full\":\"Volley.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":288,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"AsheSpiritOfTheHawk\",\"name\":\"Hawkshot\",\"description\":\"Ashe sends her Hawk Spirit on a scouting mission anywhere on the map.\",\"tooltip\":\"Reveals terrain as it flies toward target location anywhere on the map. Grants vision for {{ e4 }} seconds. Ashe can store up to 2 charges of Hawkshot.\",\"leveltip\":{\"label\":[\"Recharge Time\"],\"effect\":[\"{{ ammorechargetime }} -> {{ ammorechargetimeNL }}\"]},\"maxrank\":5,\"cooldown\":[5,5,5,5,5],\"cooldownBurn\":\"5\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[3,3,3,3,3],[50,90,130,170,210],[25000,25000,25000,25000,25000],[5,5,5,5,5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"3\",\"50/90/130/170/210\",\"25000\",\"5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"2\",\"range\":[25000,25000,25000,25000,25000],\"rangeBurn\":\"25000\",\"image\":{\"full\":\"AsheSpiritOfTheHawk.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":336,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"EnchantedCrystalArrow\",\"name\":\"Enchanted Crystal Arrow\",\"description\":\"Ashe fires a missile of ice in a straight line. If the arrow collides with an enemy Champion, it deals damage and stuns the Champion, stunning for longer the farther arrow has traveled. In addition, surrounding enemy units take damage and are slowed.\",\"tooltip\":\"Launches a crystal arrow of ice that stuns the first enemy Champion hit, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage. The farther the arrow flies, the longer the stun, up to {{ e2 }} seconds. Surrounding enemies take half damage.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[100,90,80],\"cooldownBurn\":\"100/90/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[200,400,600],[3.5,3.5,3.5],[50,50,50],[3,3,3],[5,5,5],[1100,1200,1300],[1,1,1],[400,400,400],[0.5,0.5,0.5],[0,0,0]],\"effectBurn\":[null,\"200/400/600\",\"3.5\",\"50\",\"3\",\"5\",\"1100/1200/1300\",\"1\",\"400\",\"0.5\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[25000,25000,25000],\"rangeBurn\":\"25000\",\"image\":{\"full\":\"EnchantedCrystalArrow.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":384,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"AurelionSol\":{\"id\":136,\"key\":\"AurelionSol\",\"name\":\"Aurelion Sol\",\"title\":\"The Star Forger\",\"spells\":[{\"id\":\"AurelionSolQ\",\"name\":\"Starsurge\",\"description\":\"Aurelion Sol creates an expanding disk, which explodes to stun and damage enemies when it moves too far away from him.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">First Press: </span>Aurelion Sol creates a new stellar core, which grows over time and grants him {{ e2 }}% Movement Speed.<br><br>The core will detonate when it reaches his <span class=\\\"color308BDB\\\">Outer Limit</span>, applying {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and a {{ e4 }} second stun.<br><br><span class=\\\"colorFF9900\\\">Second Press: </span>Detonate the core early.<br>\",\"leveltip\":{\"label\":[\"Damage\",\"Stun Duration\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,10,10,10,10],\"cooldownBurn\":\"10\",\"cost\":[60,70,80,90,100],\"costBurn\":\"60/70/80/90/100\",\"effect\":[null,[70,110,150,190,230],[10,10,10,10,10],[20,20,20,20,20],[1.1,1.2,1.3,1.4,1.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/110/150/190/230\",\"10\",\"20\",\"1.1/1.2/1.3/1.4/1.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.65,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1075,1075,1075,1075,1075],\"rangeBurn\":\"1075\",\"image\":{\"full\":\"AurelionSolQ.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":432,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"AurelionSolW\",\"name\":\"Celestial Expansion\",\"description\":\"Aurelion Sol pushes his stars farther out, magnifying their damage.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Increases Star base damage by {{ e2 }}.<br><br><span class=\\\"colorFF9900\\\">Toggle: </span>Aurelion Sol's Stars orbit at his <span class=\\\"color308BDB\\\">Outer Limit</span> and deal {{ e1 }}% damage, for a total of {{ f1 }} <span class=\\\"color99FF99\\\">(+{{ f2 }})</span> magic damage.<br>\",\"leveltip\":{\"label\":[\"Passive Damage Bonus\",\"Mana Cost Per Second\",\"Cooldown\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[6,5.5,5,4.5,4],\"cooldownBurn\":\"6/5.5/5/4.5/4\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[150,150,150,150,150],[5,10,15,20,25],[22,24,26,28,30],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"150\",\"5/10/15/20/25\",\"22/24/26/28/30\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana plus {{ e3 }} Mana Per Second\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"AurelionSolW.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":0,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana plus {{ e3 }} Mana Per Second\"},{\"id\":\"AurelionSolE\",\"name\":\"Comet of Legend\",\"description\":\"Aurelion Sol gains speed while moving in one continuous direction, and can take off flying for a long distance.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Continuously moving in one direction grants increasing Movement Speed up to {{ e2 }}%.<br><br><span class=\\\"colorFF9900\\\">Active: </span>Fly for {{ e3 }} units in the chosen direction. Only castable outside of combat. Aurelion Sol can see and be seen over walls while flying.<br><br>Taking champion or turret damage will force a landing and reset the passive movement speed.<br>\",\"leveltip\":{\"label\":[\"Movement Speed Cap \",\"Flight Range\",\"Cooldown\"],\"effect\":[\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[60,55,50,45,40],\"cooldownBurn\":\"60/55/50/45/40\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[5,6,7,8,9],[25,30,35,40,45],[3000,4000,5000,6000,7000],[600,600,600,600,600],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"5/6/7/8/9\",\"25/30/35/40/45\",\"3000/4000/5000/6000/7000\",\"600\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[3000,4000,5000,6000,7000],\"rangeBurn\":\"3000/4000/5000/6000/7000\",\"image\":{\"full\":\"AurelionSolE.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":48,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"AurelionSolR\",\"name\":\"Voice of Light\",\"description\":\"Aurelion Sol projects a blast of pure starfire, damaging and slowing all enemies caught in it and knocking nearby enemies back to a safer distance.\",\"tooltip\":\"Breathes out a blast of pure starfire, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and slowing by {{ e2 }}% for {{ e3 }} seconds. <br><br>The blast will also knock nearby enemies back to Aurelion Sol's <span class=\\\"color308BDB\\\">Outer Limit</span>.<br>\",\"leveltip\":{\"label\":[\"Damage\",\"Slow \",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,100,80],\"cooldownBurn\":\"120/100/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[150,250,350],[40,50,60],[2,2,2],[0,0,0],[650,650,650],[50,50,50],[1300,1300,1300],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"150/250/350\",\"40/50/60\",\"2\",\"0\",\"650\",\"50\",\"1300\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1500,1500,1500],\"rangeBurn\":\"1500\",\"image\":{\"full\":\"AurelionSolR.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":96,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Azir\":{\"id\":268,\"key\":\"Azir\",\"name\":\"Azir\",\"title\":\"the Emperor of the Sands\",\"spells\":[{\"id\":\"AzirQWrapper\",\"name\":\"Conquering Sands\",\"description\":\"Azir sends all Sand Soldiers towards a location. Sand Soldiers deal magic damage to enemies they pass through and apply a slow for 1 second.\",\"tooltip\":\"Azir sends all <span class=\\\"colorEED6AF\\\">Sand Soldiers</span> towards a location. <span class=\\\"colorEED6AF\\\">Sand Soldiers</span> deal {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemies they pass through and apply a {{ e2 }}% slow for 1 second.<br><br>Enemies hit by multiple <span class=\\\"colorEED6AF\\\">Sand Soldiers</span> will not take additional damage.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ f1 }} -> {{ f2 }}\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[70,70,70,70,70],\"costBurn\":\"70\",\"effect\":[null,[65,85,105,125,145],[25,25,25,25,25],[10,9,8,7,6],[70,70,70,70,70],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"65/85/105/125/145\",\"25\",\"10/9/8/7/6\",\"70\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[875,875,875,875,875],\"rangeBurn\":\"875\",\"image\":{\"full\":\"AzirQWrapper.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":144,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"AzirW\",\"name\":\"Arise!\",\"description\":\"Azir summons a Sand Soldier to attack nearby targets for him, replacing his basic attack against targets within the soldier's range. Their attacks deal magic damage to enemies in a line. Arise! also passively grants attack speed to Azir and his Sand Soldiers.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> Gains {{ e3 }}% attack speed.<br><br><span class=\\\"colorFF9900\\\">Active:</span> Azir summons a <span class=\\\"colorEED6AF\\\">Sand Soldier</span> for {{ e1 }} seconds. When Azir attacks an enemy in a soldier's range, the soldier attacks instead of Azir, dealing {{ f2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemies in a line. If multiple soldiers strike the same target, each soldier after the first deals 25% damage.<br><br>Azir can store up to {{ maxammo }} <span class=\\\"colorEED6AF\\\">Sand Soldiers</span> at a time. A new soldier becomes available every <span class=\\\"colorFFFFFF\\\">{{ f1 }}</span> seconds.<br><br><span class=\\\"color919191\\\"><i>Sand Soldiers can attack targets outside of Azir's basic attack range.<br>Sand Soldiers deactivate if they are too far away from Azir.<br>Sand Soldiers expire twice as fast when near an enemy turret.</i></span>\",\"leveltip\":{\"label\":[\"Recharge\",\"Attack Speed\"],\"effect\":[\"{{ f1 }} -> {{ f3 }}\",\"{{ e3 }}% -> {{ e3NL }}%\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[9,9,9,9,9],[12,11,10,9,8],[20,30,40,50,60],[40,40,40,40,40],[1.5,1.5,1.5,1.5,1.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"9\",\"12/11/10/9/8\",\"20/30/40/50/60\",\"40\",\"1.5\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"2\",\"range\":[450,450,450,450,450],\"rangeBurn\":\"450\",\"image\":{\"full\":\"AzirW.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":192,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"AzirEWrapper\",\"name\":\"Shifting Sands\",\"description\":\"Azir dashes to one of his Sand Soldiers, damaging enemies. If he hits an enemy champion, he gains a shield.\",\"tooltip\":\"Azir dashes to one of his <span class=\\\"colorEED6AF\\\">Sand Soldiers</span>, damaging enemies hit for {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.<br><br>If Azir hits an enemy champion, his dash stops and he gains a shield for {{ e6 }} seconds that absorbs up to {{ e4 }} damage.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Shield\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ f2 }} -> {{ f3 }}\",\"{{ e4 }} -> {{ e4NL }}\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[0.5,0.5,0.5,0.5,0.5],[19,18,17,16,15],[60,90,120,150,180],[80,120,160,200,240],[-0.5,-0.6,-0.7,-0.8,-0.9],[4,4,4,4,4],[1100,1100,1100,1100,1100],[60,60,60,60,60],[0.15,0.15,0.15,0.15,0.15],[0,0,0,0,0]],\"effectBurn\":[null,\"0.5\",\"19/18/17/16/15\",\"60/90/120/150/180\",\"80/120/160/200/240\",\"-0.5/-0.6/-0.7/-0.8/-0.9\",\"4\",\"1100\",\"60\",\"0.15\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1100,1100,1100,1100,1100],\"rangeBurn\":\"1100\",\"image\":{\"full\":\"AzirEWrapper.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":240,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"AzirR\",\"name\":\"Emperor's Divide\",\"description\":\"Azir summons a wall of soldiers which charge forward, knocking back and damaging enemies.\",\"tooltip\":\"Azir summons a wall of armored soldiers that charge forward, knocking back enemies and dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage. The soldiers then remain as a wall for {{ e4 }} seconds.<br><br>Enemies will be stopped by Emperor's Divide, even if they attempt to dash over the wall, but Azir and his allies can pass freely through it.<br><br><span class=\\\"color919191\\\"><i>Emperor's Divide does not interact with Azir's basic attacks or spells.</i></span>\",\"leveltip\":{\"label\":[\"Damage\",\"Number of Soldiers\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[140,120,100],\"cooldownBurn\":\"140/120/100\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[150,225,300],[0,0,0],[4,5,6],[3,3,3],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"150/225/300\",\"0\",\"4/5/6\",\"3\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[250,250,250],\"rangeBurn\":\"250\",\"image\":{\"full\":\"AzirR.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":288,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Bard\":{\"id\":432,\"key\":\"Bard\",\"name\":\"Bard\",\"title\":\"the Wandering Caretaker\",\"spells\":[{\"id\":\"BardQ\",\"name\":\"Cosmic Binding\",\"description\":\"Bard fires a missile which will slow the first enemy struck, and continue onward. If it strikes a wall, it will stun the initial target; if it strikes another enemy, it will stun them both.\",\"tooltip\":\"Bard fires an energy bolt, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to one or two enemies. The first target hit will be slowed by {{ e3 }}% for {{ e4 }} second(s).<br><br>If the bolt hits another enemy or a wall, any enemies hit are stunned for {{ e2 }} second(s).<br>\",\"leveltip\":{\"label\":[\"Damage\",\"Slow Duration\",\"Stun Duration\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[11,10,9,8,7],\"cooldownBurn\":\"11/10/9/8/7\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[80,125,170,215,260],[1,1.2,1.4,1.6,1.8],[60,60,60,60,60],[1,1.2,1.4,1.6,1.8],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/125/170/215/260\",\"1/1.2/1.4/1.6/1.8\",\"60\",\"1/1.2/1.4/1.6/1.8\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.65,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[25000,25000,25000,25000,25000],\"rangeBurn\":\"25000\",\"image\":{\"full\":\"BardQ.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":336,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"BardW\",\"name\":\"Caretaker's Shrine\",\"description\":\"Reveals a healing shrine which powers up over a short time, disappearing after healing the first ally that touches it.\",\"tooltip\":\"Bard raises a health shrine that immediately offers {{ e5 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> health, but restores up to {{ e6 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> health as it gathers power for 10 seconds. The shrine's effect also grants {{ e1 }}% decaying movement speed for {{ e2 }} seconds. <br><br>Bard can have up to {{ e3 }} shrines active at once, which remain until visited by an ally champion or crushed by an enemy champion.<br><br><span class=\\\"colorFFCC00\\\">Active Shrines: </span><span class=\\\"colorFFFFFF\\\">{{ f1 }}</span> / <span class=\\\"colorFFCC00\\\">{{ f2 }}</span></mainText><br>\",\"leveltip\":{\"label\":[\"Base Heal\",\"Max Heal\"],\"effect\":[\"{{ e5 }} -> {{ e5NL }}\",\"{{ e6 }} -> {{ e6NL }}\"]},\"maxrank\":5,\"cooldown\":[12,12,12,12,12],\"cooldownBurn\":\"12\",\"cost\":[90,90,90,90,90],\"costBurn\":\"90\",\"effect\":[null,[50,50,50,50,50],[1.5,1.5,1.5,1.5,1.5],[3,3,3,3,3],[0,0,0,0,0],[30,60,90,120,150],[70,110,150,190,230],[0,0,0,0,0],[10,10,10,10,10],[90,90,90,90,90],[0,0,0,0,0]],\"effectBurn\":[null,\"50\",\"1.5\",\"3\",\"0\",\"30/60/90/120/150\",\"70/110/150/190/230\",\"0\",\"10\",\"90\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[800,800,800,800,800],\"rangeBurn\":\"800\",\"image\":{\"full\":\"BardW.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":384,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"BardE\",\"name\":\"Magical Journey\",\"description\":\"Bard opens a portal in nearby terrain. Allies and enemies alike can take a one-way trip through that terrain by moving into the portal.\",\"tooltip\":\"Bard opens a one-way corridor through nearby terrain. Both allies and enemies can use the corridor by right-clicking on any part of it while near its entrance, with allies travelling {{ e2 }}% faster than enemies.<br><br>The corridor disappears after {{ e1 }} seconds.<br>\",\"leveltip\":{\"label\":[\"Cooldown\",\"Ally Travel Speed Bonus\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[18,17,16,15,14],\"cooldownBurn\":\"18/17/16/15/14\",\"cost\":[30,30,30,30,30],\"costBurn\":\"30\",\"effect\":[null,[10,10,10,10,10],[10,20,30,40,50],[800,800,800,800,800],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"10\",\"10/20/30/40/50\",\"800\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[900,900,900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"BardE.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":432,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"BardR\",\"name\":\"Tempered Fate\",\"description\":\"Bard sends spirit energy arcing to a location, putting all champions, minions, monsters, and turrets hit into stasis for a brief time.\",\"tooltip\":\"Bard sends magical energy arcing to a target location. On impact, all champions, minions, monsters, and turrets in the target area are put in stasis, becoming invincible, untargetable, and unable to act for {{ e1 }} seconds.<br><br>Epic monsters are also put into stasis, despite normally being immune to disables.\",\"leveltip\":{\"label\":[\"Cooldown\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[130,115,90],\"cooldownBurn\":\"130/115/90\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[2.5,2.5,2.5],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"2.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[3400,3400,3400],\"rangeBurn\":\"3400\",\"image\":{\"full\":\"BardR.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":0,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Blitzcrank\":{\"id\":53,\"key\":\"Blitzcrank\",\"name\":\"Blitzcrank\",\"title\":\"the Great Steam Golem\",\"spells\":[{\"id\":\"RocketGrab\",\"name\":\"Rocket Grab\",\"description\":\"Blitzcrank fires his right hand to grab an opponent on its path, dealing damage and dragging it back to him.\",\"tooltip\":\"Blitzcrank fires his right hand. If it encounters an enemy unit it will stun them and deal {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage while he pulls them to himself.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[20,19,18,17,16],\"cooldownBurn\":\"20/19/18/17/16\",\"cost\":[100,100,100,100,100],\"costBurn\":\"100\",\"effect\":[null,[80,135,190,245,300],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/135/190/245/300\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[925,925,925,925,925],\"rangeBurn\":\"925\",\"image\":{\"full\":\"RocketGrab.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":48,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"Overdrive\",\"name\":\"Overdrive\",\"description\":\"Blitzcrank super charges himself to get dramatically increased Movement and Attack Speed. He is temporarily slowed after the effect ends.\",\"tooltip\":\"Blitzcrank supercharges himself, gaining {{ e1 }}% Movement Speed and {{ e2 }}% Attack Speed for {{ e4 }} seconds. The Movement Speed bonus decays over the duration.<br><br>When Overdrive ends, Blitzcrank's Movement Speed is slowed by {{ e3 }}% for {{ e5 }} seconds.\",\"leveltip\":{\"label\":[\"Movement Speed\",\"Attack Speed\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[15,15,15,15,15],\"cooldownBurn\":\"15\",\"cost\":[75,75,75,75,75],\"costBurn\":\"75\",\"effect\":[null,[70,75,80,85,90],[30,38,46,54,62],[30,30,30,30,30],[5,5,5,5,5],[1.5,1.5,1.5,1.5,1.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/75/80/85/90\",\"30/38/46/54/62\",\"30\",\"5\",\"1.5\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1,1,1,1,1],\"rangeBurn\":\"1\",\"image\":{\"full\":\"Overdrive.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":96,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"PowerFist\",\"name\":\"Power Fist\",\"description\":\"Blitzcrank charges up his fist to make his next attack deal double damage and pop his target up in the air.\",\"tooltip\":\"Blitzcrank charges up his fist to make his next attack deal double his total attack damage as physical damage and pop his target up in the air.\",\"leveltip\":{\"label\":[\"Cooldown\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[9,8,7,6,5],\"cooldownBurn\":\"9/8/7/6/5\",\"cost\":[25,25,25,25,25],\"costBurn\":\"25\",\"effect\":[null,[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[300,300,300,300,300],\"rangeBurn\":\"300\",\"image\":{\"full\":\"PowerFist.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":144,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"StaticField\",\"name\":\"Static Field\",\"description\":\"Passively causes lightning bolts to damage a nearby enemy. Additionally, Blitzcrank can activate this ability to damage nearby enemies and silence them for 0.5 seconds, but doing so removes the passive lightning until Static Field becomes available again.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Lightning arcs off of Blitzcrank to hit a random nearby enemy for {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage every 2.5 seconds.<br><br><span class=\\\"colorFF9900\\\">Active: </span>Deals {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage and silences surrounding enemy units for 0.5 seconds. The passive is not in effect during the cooldown.\",\"leveltip\":{\"label\":[\"Lightning Damage\",\"Activation Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[60,40,20],\"cooldownBurn\":\"60/40/20\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[100,200,300],[250,375,500],[2.5,2.5,2.5],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"100/200/300\",\"250/375/500\",\"2.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"StaticField.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":192,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Brand\":{\"id\":63,\"key\":\"Brand\",\"name\":\"Brand\",\"title\":\"the Burning Vengeance\",\"spells\":[{\"id\":\"BrandQ\",\"name\":\"Sear\",\"description\":\"Brand launches a ball of fire forward that deals magic damage. If the target is ablaze, Sear will stun the target for 1.5 seconds.\",\"tooltip\":\"Brand launches a ball of fire forward that deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.<br><br><span class=\\\"colorDDDD77\\\">Blaze:</span> If the target is ablaze, Sear will stun the target for 1.5 seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[8,7.5,7,6.5,6],\"cooldownBurn\":\"8/7.5/7/6.5/6\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[80,110,140,170,200],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/110/140/170/200\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.55,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1050,1050,1050,1050,1050],\"rangeBurn\":\"1050\",\"image\":{\"full\":\"BrandQ.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":240,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"BrandW\",\"name\":\"Pillar of Flame\",\"description\":\"After a short delay, Brand creates a Pillar of Flame at a target area, dealing magic damage to enemy units within the area. Units that are ablaze take an additional 25% damage.\",\"tooltip\":\"After a short delay, Brand creates a Pillar of Flame at a target area, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemy units within the area.<br><br><span class=\\\"colorDDDD77\\\">Blaze:</span> Units that are ablaze take an additional 25% damage from Pillar of Flame.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,9.5,9,8.5,8],\"cooldownBurn\":\"10/9.5/9/8.5/8\",\"cost\":[60,70,80,90,100],\"costBurn\":\"60/70/80/90/100\",\"effect\":[null,[75,120,165,210,255],[20,40,60,80,100],[0.25,0.25,0.25,0.25,0.25],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"75/120/165/210/255\",\"20/40/60/80/100\",\"0.25\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[900,900,900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"BrandW.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":288,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"BrandE\",\"name\":\"Conflagration\",\"description\":\"Brand conjures a powerful blast at his target, dealing magic damage to them. If the target is ablaze, Conflagration spreads to nearby enemies.\",\"tooltip\":\"Brand conjures a powerful blast at his target, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.<br><br><span class=\\\"colorDDDD77\\\">Blaze:</span> If the target is ablaze, Conflagration spreads to nearby enemies. \",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,9,8,7,6],\"cooldownBurn\":\"10/9/8/7/6\",\"cost\":[70,75,80,85,90],\"costBurn\":\"70/75/80/85/90\",\"effect\":[null,[70,90,110,130,150],[375,375,375,375,375],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/90/110/130/150\",\"375\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.35,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[625,625,625,625,625],\"rangeBurn\":\"625\",\"image\":{\"full\":\"BrandE.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":336,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"BrandR\",\"name\":\"Pyroclasm\",\"description\":\"Brand unleashes a devastating torrent of fire, dealing magic damage each time it bounces, up to 5 bounces. Bounces prioritize stacking Blaze to max on Champions. If a target is ablaze, Pyroclasm will briefly slow them.\",\"tooltip\":\"Brand unleashes a devastating torrent of fire, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage each time it bounces up to 5 bounces. Bounces prioritize stacking Blaze to max on Champions.<br><br><span class=\\\"colorDDDD77\\\">Blaze:</span> If the target is ablaze, Pyroclasm will briefly slow the target by {{ e4 }}%.\",\"leveltip\":{\"label\":[\"Damage per Bounce\",\"Slow Amount\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }}% -> {{ e4NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[105,90,75],\"cooldownBurn\":\"105/90/75\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[100,200,300],[5,7,9],[150,225,300],[30,45,60],[0.25,0.25,0.25],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"100/200/300\",\"5/7/9\",\"150/225/300\",\"30/45/60\",\"0.25\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.25,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[750,750,750],\"rangeBurn\":\"750\",\"image\":{\"full\":\"BrandR.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":384,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Braum\":{\"id\":201,\"key\":\"Braum\",\"name\":\"Braum\",\"title\":\"the Heart of the Freljord\",\"spells\":[{\"id\":\"BraumQ\",\"name\":\"Winter's Bite\",\"description\":\"Braum propels freezing ice from his shield, slowing and dealing magic damage.<br><br>Applies a stack of <font color='#FFF673'>Concussive Blows</font>.\",\"tooltip\":\"Braum propels freezing ice from his shield dealing {{ e1 }} <span class=\\\"colorCC3300\\\">(+{{ f1 }}) [2.5% of Braum's Max Health]</span> magic damage to the first enemy hit and slowing them by {{ e2 }}%, decaying over the next {{ e5 }} seconds.<br><br>Applies a stack of <span class=\\\"colorFFF673\\\">Concussive Blows</span>. \",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,9,8,7,6],\"cooldownBurn\":\"10/9/8/7/6\",\"cost\":[55,60,65,70,75],\"costBurn\":\"55/60/65/70/75\",\"effect\":[null,[60,105,150,195,240],[70,70,70,70,70],[30,30,30,30,30],[0.02,0.02,0.02,0.02,0.02],[2,2,2,2,2],[1050,1050,1050,1050,1050],[4,4,4,4,4],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/105/150/195/240\",\"70\",\"30\",\"0.02\",\"2\",\"1050\",\"4\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"BraumQ.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":432,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"BraumW\",\"name\":\"Stand Behind Me\",\"description\":\"Braum leaps to a target allied champion or minion. On arrival, Braum and the ally gain Armor and Magic Resist for a few seconds.\",\"tooltip\":\"Braum leaps to target allied champion or minion.<br><br>On arrival, Braum and the ally gain <span class=\\\"colorFFFF00\\\">{{ f3 }}</span> Armor and <span class=\\\"colorFF00FF\\\">{{ f4 }}</span> Magic Resist ({{ e4 }} plus {{ e3 }}% of Braum's bonus Armor/Magic Resist) for {{ e1 }} seconds.\",\"leveltip\":{\"label\":[\"Base Armor and Magic Resist\",\"Defensive Scaling\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e4 }} -> {{ e4NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[14,13,12,11,10],\"cooldownBurn\":\"14/13/12/11/10\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[3,3,3,3,3],[750,750,750,750,750],[10,11.5,13,14.5,16],[15,17.5,20,22.5,25],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"3\",\"750\",\"10/11.5/13/14.5/16\",\"15/17.5/20/22.5/25\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"@special.BraumWArmor\",\"coeff\":0,\"key\":\"f3\"},{\"link\":\"@special.BraumWMR\",\"coeff\":0,\"key\":\"f4\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"BraumW.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":0,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"BraumE\",\"name\":\"Unbreakable\",\"description\":\"Braum raises his shield in a direction for several seconds, intercepting all projectiles causing them to hit him and be destroyed. He negates the damage of the first attack completely and reduces the damage of all subsequent attacks from this direction.\",\"tooltip\":\"<span class=\\\"colorFFFFFF\\\">Braum reduces incoming damage and blocks for allies behind him.</span><br>Braum raises his shield in a direction for {{ e2 }} seconds negating the damage of the next attack from that direction. Subsequent attacks deal {{ e3 }}% reduced damage.<br><br>Braum <span class=\\\"colorFFF673\\\">intercepts</span> projectiles, causing them to hit him and be destroyed.<br><br>Braum gains {{ e4 }}% Movement Speed for the duration.\",\"leveltip\":{\"label\":[\"Duration\",\"Damage Reduction\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[18,16,14,12,10],\"cooldownBurn\":\"18/16/14/12/10\",\"cost\":[30,35,40,45,50],\"costBurn\":\"30/35/40/45/50\",\"effect\":[null,[0,0,0,0,0],[3,3.25,3.5,3.75,4],[30,32.5,35,37.5,40],[10,10,10,10,10],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"0\",\"3/3.25/3.5/3.75/4\",\"30/32.5/35/37.5/40\",\"10\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[25000,25000,25000,25000,25000],\"rangeBurn\":\"25000\",\"image\":{\"full\":\"BraumE.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":48,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"BraumRWrapper\",\"name\":\"Glacial Fissure\",\"description\":\"Braum slams the ground, knocking up enemies nearby and in a line in front of him. A fissure is left along the line that slows enemies.\",\"tooltip\":\"Braum slams the ground, knocking up enemies nearby and in a line in front of him. A fissure is left along the line for {{ e3 }} seconds, slowing enemies above it by {{ e4 }}%.<br><br>Enemies hit take {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage. The first champion hit is knocked up for {{ e5 }} seconds, subsequent enemies are knocked up briefly.\",\"leveltip\":{\"label\":[\"Damage\",\"Knockup Duration\",\"Slow\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e5 }} -> {{ e5NL }}\",\"{{ e4 }}% -> {{ e4NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[140,120,100],\"cooldownBurn\":\"140/120/100\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[150,250,350],[0.3,0.3,0.3],[4,4,4],[40,50,60],[1,1.25,1.5],[0.25,0.25,0.25],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"150/250/350\",\"0.3\",\"4\",\"40/50/60\",\"1/1.25/1.5\",\"0.25\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1250,1250,1250],\"rangeBurn\":\"1250\",\"image\":{\"full\":\"BraumRWrapper.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":96,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Caitlyn\":{\"id\":51,\"key\":\"Caitlyn\",\"name\":\"Caitlyn\",\"title\":\"the Sheriff of Piltover\",\"spells\":[{\"id\":\"CaitlynPiltoverPeacemaker\",\"name\":\"Piltover Peacemaker\",\"description\":\"Caitlyn revs up her rifle for 1 second to unleash a penetrating shot that deals physical damage (deals less damage to subsequent targets).\",\"tooltip\":\"Revs the rifle for {{ e4 }} second to fire a narrow piercing shot dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage. After hitting any target, it opens into a wider shot that deals {{ e2 }}% less damage. <br><br>Always deals full damage to trap revealed targets.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,9,8,7,6],\"cooldownBurn\":\"10/9/8/7/6\",\"cost\":[50,60,70,80,90],\"costBurn\":\"50/60/70/80/90\",\"effect\":[null,[30,70,110,150,190],[33,33,33,33,33],[50,50,50,50,50],[1,1,1,1,1],[1.3,1.4,1.5,1.6,1.7],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30/70/110/150/190\",\"33\",\"50\",\"1\",\"1.3/1.4/1.5/1.6/1.7\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1250,1250,1250,1250,1250],\"rangeBurn\":\"1250\",\"image\":{\"full\":\"CaitlynPiltoverPeacemaker.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":144,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"CaitlynYordleTrap\",\"name\":\"Yordle Snap Trap\",\"description\":\"Caitlyn sets a trap to find sneaky yordles. When sprung, the trap reveals and immobilizes the enemy champion for 1.5 seconds.\",\"tooltip\":\"Sets traps that an enemy Champion can spring, immobilizing them for {{ e1 }} seconds and granting <span class=\\\"coloree91d7\\\">True Sight</span> for a short duration.<br><br>Traps last for {{ e3 }} seconds. {{ e5 }} traps may be active at once.<br><br>Trapped enemies take an additional {{ e2 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> increased damage from Headshot.\",\"leveltip\":{\"label\":[\"Recharge Rate\",\"Maximum Traps\",\"Bonus Headshot Damage on Trapped Targets\"],\"effect\":[\"{{ ammorechargetime }} -> {{ ammorechargetimeNL }}\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[0.5,0.5,0.5,0.5,0.5],\"cooldownBurn\":\"0.5\",\"cost\":[20,20,20,20,20],\"costBurn\":\"20\",\"effect\":[null,[2,2,2,2,2],[30,70,110,150,190],[90,90,90,90,90],[3,3,4,4,5],[3,3,4,4,5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"2\",\"30/70/110/150/190\",\"90\",\"3/3/4/4/5\",\"3/3/4/4/5\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"2\",\"range\":[800,800,800,800,800],\"rangeBurn\":\"800\",\"image\":{\"full\":\"CaitlynYordleTrap.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":192,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"CaitlynEntrapment\",\"name\":\"90 Caliber Net\",\"description\":\"Caitlyn fires a heavy net to slow her target. The recoil knocks Caitlyn back.\",\"tooltip\":\"Fires a net, knocking Caitlyn backwards. The net deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and slows the first enemy hit by {{ e3 }}% for {{ e2 }} second.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[16,14.5,13,11.5,10],\"cooldownBurn\":\"16/14.5/13/11.5/10\",\"cost\":[75,75,75,75,75],\"costBurn\":\"75\",\"effect\":[null,[70,110,150,190,230],[1,1,1,1,1],[50,50,50,50,50],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/110/150/190/230\",\"1\",\"50\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[750,750,750,750,750],\"rangeBurn\":\"750\",\"image\":{\"full\":\"CaitlynEntrapment.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":240,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"CaitlynAceintheHole\",\"name\":\"Ace in the Hole\",\"description\":\"Caitlyn takes time to line up the perfect shot, dealing massive damage to a single target at a huge range. Enemy champions can intercept the bullet for their ally.\",\"tooltip\":\"Takes a second to line up the perfect shot on an enemy Champion at up to {{ e2 }} range. The shot deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage, but other enemy Champions can intercept it.<br><br>Grants <span class=\\\"coloree91d7\\\">True Sight</span> of the target during the channel.\",\"leveltip\":{\"label\":[\"Damage\",\"Range\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[90,75,60],\"cooldownBurn\":\"90/75/60\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[250,475,700],[2000,2500,3000],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"250/475/700\",\"2000/2500/3000\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":2,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[2000,2500,3000],\"rangeBurn\":\"2000/2500/3000\",\"image\":{\"full\":\"CaitlynAceintheHole.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":288,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Camille\":{\"id\":164,\"key\":\"Camille\",\"name\":\"Camille\",\"title\":\"the Steel Shadow\",\"spells\":[{\"id\":\"CamilleQ\",\"name\":\"Precision Protocol\",\"description\":\"Camille's next attack deals bonus damage and grants bonus movement speed. This spell can be recast for a short period of time, doing significantly increased bonus damage if Camille delays a period of time between the two attacks.\",\"tooltip\":\"Camille's next basic attack deals <span class=\\\"colorFF8C00\\\">{{ f1 }}</span> bonus physical damage and increases her movement speed by {{ e4 }}% for {{ e5 }} second. This ability can be recast in the next {{ e1 }} seconds at no cost.<br><br>If the second Precision Protocol attack hits at least {{ e1 }} seconds after the first, the bonus damage is increased by {{ e8 }}% to <span class=\\\"colorFF8C00\\\">{{ f2 }}</span> and <span class=\\\"colorFFFFFF\\\">{{ f3 }}%</span> of the attack is converted into true damage.<br><br><span class=\\\"size16 colorFFEB7F\\\">16\",\"leveltip\":{\"label\":[\"Total AD Ratio\",\"Cooldown\",\"Movement Speed\"],\"effect\":[\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e4 }}% -> {{ e4NL }}%\"]},\"maxrank\":5,\"cooldown\":[9,8.25,7.5,6.75,6],\"cooldownBurn\":\"9/8.25/7.5/6.75/6\",\"cost\":[25,25,25,25,25],\"costBurn\":\"25\",\"effect\":[null,[3,3,3,3,3],[50,50,50,50,50],[20,25,30,35,40],[20,25,30,35,40],[1,1,1,1,1],[0.4,0.4,0.4,0.4,0.4],[0.04,0.04,0.04,0.04,0.04],[80,80,80,80,80],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"3\",\"50\",\"20/25/30/35/40\",\"20/25/30/35/40\",\"1\",\"0.4\",\"0.04\",\"80\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[325,325,325,325,325],\"rangeBurn\":\"325\",\"image\":{\"full\":\"CamilleQ.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":336,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"CamilleW\",\"name\":\"Tactical Sweep\",\"description\":\"Camille blasts in a cone after a delay, dealing damage. Enemies in the outer half are slowed and take extra damage, while also healing Camille.\",\"tooltip\":\"Camille winds up and then slices in a direction, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage to enemies hit.<br><br>Enemies hit by the outer half are slowed by {{ e4 }}%, decaying over {{ e2 }} seconds. They take extra physical damage equal to {{ e5 }}% (<span class=\\\"colorFF8C00\\\">+{{ f2 }}</span>%) of their maximum health, and Camille heals for {{ e9 }}% of the bonus damage dealt to champions.<br><br><span class=\\\"size16 colorFFEB7F\\\">16\",\"leveltip\":{\"label\":[\"Mana Cost\",\"Damage\",\"Maximum Health Damage\",\"Cooldown\"],\"effect\":[\"{{ cost }} -> {{ costNL }}\",\"{{ e1 }} -> {{ e1NL }}\",\"{{ e5 }}% -> {{ e5NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[15,13.5,12,10.5,9],\"cooldownBurn\":\"15/13.5/12/10.5/9\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[65,95,125,155,185],[2,2,2,2,2],[0.75,0.75,0.75,0.75,0.75],[80,80,80,80,80],[6,6.5,7,7.5,8],[650,650,650,650,650],[30,30,30,30,30],[50,50,50,50,50],[100,100,100,100,100],[0.1,0.1,0.1,0.1,0.1]],\"effectBurn\":[null,\"65/95/125/155/185\",\"2\",\"0.75\",\"80\",\"6/6.5/7/7.5/8\",\"650\",\"30\",\"50\",\"100\",\"0.1\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[610,610,610,610,610],\"rangeBurn\":\"610\",\"image\":{\"full\":\"CamilleW.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":384,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"CamilleE\",\"name\":\"Hookshot\",\"description\":\"Camille pulls herself to a wall, leaping off and knocking up enemies upon landing.\",\"tooltip\":\"<span class=\\\"colorFF8C00\\\">First Cast:</span> Fire a hookshot that attaches to terrain, pulling Camille to it and allowing Hookshot to be recast for 1 second.<br><br><span class=\\\"colorFF8C00\\\">Second Cast:</span> Camille leaps from the wall, colliding with the first enemy champion hit. Upon landing, she deals {{ e3 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage and a {{ e1 }} second stun to nearby enemies. Dashes towards enemy champions travel twice as far and grant {{ e2 }}% attack speed for {{ e5 }} seconds on impact.\",\"leveltip\":{\"label\":[\"Cooldown\",\"Damage\",\"Attack Speed\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[16,14.5,13,11.5,10],\"cooldownBurn\":\"16/14.5/13/11.5/10\",\"cost\":[70,70,70,70,70],\"costBurn\":\"70\",\"effect\":[null,[0.75,0.75,0.75,0.75,0.75],[40,50,60,70,80],[70,115,160,205,250],[1050,1050,1050,1050,1050],[5,5,5,5,5],[1,1,1,1,1],[800,800,800,800,800],[400,400,400,400,400],[130,130,130,130,130],[0,0,0,0,0]],\"effectBurn\":[null,\"0.75\",\"40/50/60/70/80\",\"70/115/160/205/250\",\"1050\",\"5\",\"1\",\"800\",\"400\",\"130\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[800,800,800,800,800],\"rangeBurn\":\"800\",\"image\":{\"full\":\"CamilleE.png\",\"sprite\":\"spell1.png\",\"group\":\"spell\",\"x\":432,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"CamilleR\",\"name\":\"The Hextech Ultimatum\",\"description\":\"Camille dashes to target champion, anchoring them to the area. She also deals bonus magic damage to the target with her basic attacks.\",\"tooltip\":\"Camille briefly becomes untargetable and leaps onto an enemy champion, interrupting channels and locking them into an area they cannot escape by any means for {{ e3 }} seconds. Other nearby enemies are knocked away. Her basic attacks against the trapped enemy deal bonus magic damage equal to {{ e2 }} plus {{ e1 }}% of their current health.<br><br><span class=\\\"size16 colorFFEB7F\\\">16\",\"leveltip\":{\"label\":[\"Duration\",\"Cooldown\",\"Bonus Damage\",\"Current Health Damage\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ e1 }}% -> {{ e1NL }}%>\"]},\"maxrank\":3,\"cooldown\":[140,115,90],\"cooldownBurn\":\"140/115/90\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[4,6,8],[5,10,15],[2.5,3.25,4],[-0.9,-0.9,-0.9],[425,425,425],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"4/6/8\",\"5/10/15\",\"2.5/3.25/4\",\"-0.9\",\"425\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[475,475,475],\"rangeBurn\":\"475\",\"image\":{\"full\":\"CamilleR.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":0,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Cassiopeia\":{\"id\":69,\"key\":\"Cassiopeia\",\"name\":\"Cassiopeia\",\"title\":\"the Serpent's Embrace\",\"spells\":[{\"id\":\"CassiopeiaQ\",\"name\":\"Noxious Blast\",\"description\":\"Cassiopeia blasts an area with Poison after a brief delay, granting her increased Movement Speed if she hits an enemy champion.\",\"tooltip\":\"Blasts enemies in an area with <span class=\\\"color32CD32\\\">Noxious Poison</span>. If a champion is hit, Cassiopeia gains {{ e3 }}% Movement Speed decaying over {{ e4 }} seconds.<br><br><span class=\\\"color32CD32\\\">Noxious Poison</span> deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage over {{ e2 }} seconds.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Movement Speed\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[3.5,3.5,3.5,3.5,3.5],\"cooldownBurn\":\"3.5\",\"cost\":[60,65,70,75,80],\"costBurn\":\"60/65/70/75/80\",\"effect\":[null,[75,120,165,210,255],[3,3,3,3,3],[30,35,40,45,50],[3,3,3,3,3],[7,7,7,7,7],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"75/120/165/210/255\",\"3\",\"30/35/40/45/50\",\"3\",\"7\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[850,850,850,850,850],\"rangeBurn\":\"850\",\"image\":{\"full\":\"CassiopeiaQ.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":48,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"CassiopeiaW\",\"name\":\"Miasma\",\"description\":\"Cassiopeia releases several clouds of poison, slowing, grounding, and lightly damaging enemies that pass through them. Grounded enemies cannot use Movement abilities.\",\"tooltip\":\"Cassiopeia spews venom in an arc, leaving toxic clouds for {{ e4 }} seconds.<br><br>Enemies in the clouds are continually afflicted with <span class=\\\"color32CD32\\\">Debilitating Poison</span>, inflicting a decaying {{ e2 }}% slow and grounding them, prohibiting the use of Movement abilities. They also take {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage per second.<br><br>Miasma has a minimum cast range.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Slow Amount\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[18,17,16,15,14],\"cooldownBurn\":\"18/17/16/15/14\",\"cost\":[70,70,70,70,70],\"costBurn\":\"70\",\"effect\":[null,[20,35,50,65,80],[40,50,60,70,80],[1,1,1,1,1],[5,5,5,5,5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"20/35/50/65/80\",\"40/50/60/70/80\",\"1\",\"5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.15,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[800,800,800,800,800],\"rangeBurn\":\"800\",\"image\":{\"full\":\"CassiopeiaW.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":96,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"CassiopeiaE\",\"name\":\"Twin Fang\",\"description\":\"Cassiopeia lets loose an attack that deals increased damage to Poisoned targets and heals her for a percentage of the damage dealt. If the target dies from this attack, Cassiopeia regains Mana.\",\"tooltip\":\"Deal <span class=\\\"colorFFFFFF\\\">{{ f1 }}</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to a target. If the target is killed by Twin Fang, or is killed during its flight, Cassiopeia gains {{ cost }} Mana.<br><br>If the victim is <span class=\\\"color32CD32\\\">Poisoned</span> when Twin Fang hits, it takes {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> additional magic damage and Cassiopeia heals for <span class=\\\"colorFFFFFF\\\">{{ f4 }}</span> <span class=\\\"color99FF99\\\">(+{{ f2 }})</span>.\",\"leveltip\":{\"label\":[\"Bonus Poison Damage\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[0.75,0.75,0.75,0.75,0.75],\"cooldownBurn\":\"0.75\",\"cost\":[40,50,60,70,80],\"costBurn\":\"40/50/60/70/80\",\"effect\":[null,[10,40,70,100,130],[0,0,0,0,0],[0.1,0.1,0.1,0.1,0.1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"10/40/70/100/130\",\"0\",\"0.1\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.55,\"key\":\"f1\"},{\"link\":\"spelldamage\",\"coeff\":0.1,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.35,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[700,700,700,700,700],\"rangeBurn\":\"700\",\"image\":{\"full\":\"CassiopeiaE.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":144,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"CassiopeiaR\",\"name\":\"Petrifying Gaze\",\"description\":\"Cassiopeia releases a swirl of magical energy from her eyes, stunning any enemies in front of her that are facing her and slowing any others with their back turned.\",\"tooltip\":\"Cassiopeia deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to all enemies in front of her. Enemies facing her turn to stone and are stunned for {{ e3 }} seconds while enemies facing away are slowed by {{ e2 }}%.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,100,80],\"cooldownBurn\":\"120/100/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[150,250,350],[40,40,40],[2,2,2],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"150/250/350\",\"40\",\"2\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[825,825,825],\"rangeBurn\":\"825\",\"image\":{\"full\":\"CassiopeiaR.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":192,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Chogath\":{\"id\":31,\"key\":\"Chogath\",\"name\":\"Cho'Gath\",\"title\":\"the Terror of the Void\",\"spells\":[{\"id\":\"Rupture\",\"name\":\"Rupture\",\"description\":\"Ruptures the ground at target location, popping enemy units into the air, dealing damage and slowing them.\",\"tooltip\":\"Ruptures the ground at target location. Enemies caught in the rupture are launched into the air for {{ e5 }} second, take {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage, and are slowed by {{ e2 }}% for {{ e3 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":5,\"cooldown\":[9,9,9,9,9],\"cooldownBurn\":\"9\",\"cost\":[90,90,90,90,90],\"costBurn\":\"90\",\"effect\":[null,[80,135,190,245,305],[60,60,60,60,60],[1.5,1.5,1.5,1.5,1.5],[0.625,0.625,0.625,0.625,0.625],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/135/190/245/305\",\"60\",\"1.5\",\"0.63\",\"1\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[950,950,950,950,950],\"rangeBurn\":\"950\",\"image\":{\"full\":\"Rupture.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":240,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"FeralScream\",\"name\":\"Feral Scream\",\"description\":\"Cho'Gath unleashes a terrible scream at enemies in a cone, dealing magic damage and Silencing enemies for a few seconds.\",\"tooltip\":\"Silences enemies in a cone for {{ e2 }} seconds and deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Silence Duration\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[13,12,11,10,9],\"cooldownBurn\":\"13/12/11/10/9\",\"cost\":[70,80,90,100,110],\"costBurn\":\"70/80/90/100/110\",\"effect\":[null,[75,125,175,225,275],[1.5,1.625,1.75,1.875,2],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"75/125/175/225/275\",\"1.5/1.625/1.75/1.875/2\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[300,300,300,300,300],\"rangeBurn\":\"300\",\"image\":{\"full\":\"FeralScream.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":288,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"VorpalSpikes\",\"name\":\"Vorpal Spikes\",\"description\":\"Cho'Gath's attacks passively release deadly spikes, dealing damage to all enemy units in front of him.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Toggle: </span>Basic attacks launch spikes that deal {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and apply spell effects. Spikes grow wider as Cho'Gath gains Feast stacks.\",\"leveltip\":{\"label\":[\"Damage\"],\"effect\":[\" {{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":5,\"cooldown\":[0.5,0.5,0.5,0.5,0.5],\"cooldownBurn\":\"0.5\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[20,35,50,65,80],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"20/35/50/65/80\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[40,40,40,40,40],\"rangeBurn\":\"40\",\"image\":{\"full\":\"VorpalSpikes.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":336,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"Feast\",\"name\":\"Feast\",\"description\":\"Devours an enemy unit, dealing a high amount of true damage. If the target is killed, Cho'Gath grows, gaining maximum Health.\",\"tooltip\":\"Ravenously feed on an enemy, dealing {{ e2 }} <span class=\\\"colorFF3300\\\">(+{{ f2 }})</span> <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> true damage to champions or {{ e1 }} <span class=\\\"colorFF3300\\\">(+{{ f2 }}) </span><span class=\\\"color99FF99\\\">(+{{ a2 }})</span> to minions and monsters. If the target is killed, Cho'Gath gains a stack of Feast, which causes him to grow in size and gain {{ e3 }} maximum health.<br><br><li>Only {{ e4 }} total stacks can be gained from minions and non-epic monsters. <span class=\\\"colorFFF673\\\">(Current: {{ f3 }}/{{ e4 }})</span>\",\"leveltip\":{\"label\":[\"Champion Damage\",\"Health per Stack\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ e3 }} -> {{ e3NL }}\"]},\"maxrank\":3,\"cooldown\":[80,80,80],\"cooldownBurn\":\"80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[1000,1000,1000],[300,475,650],[80,120,160],[6,6,6],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"1000\",\"300/475/650\",\"80/120/160\",\"6\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[175,175,175],\"rangeBurn\":\"175\",\"image\":{\"full\":\"Feast.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":384,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Corki\":{\"id\":42,\"key\":\"Corki\",\"name\":\"Corki\",\"title\":\"the Daring Bombardier\",\"spells\":[{\"id\":\"PhosphorusBomb\",\"name\":\"Phosphorus Bomb\",\"description\":\"Corki fires a flash bomb at a target location, dealing magic damage to enemies in the area. This attack additionally reveals units in the area for a duration.\",\"tooltip\":\"Corki lobs a bomb, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemies in the target area. In addition, the blast reveals the area and champions hit by the blast for {{ e2 }} seconds (does not reveal stealth).\",\"leveltip\":{\"label\":[\"Damage\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[8,8,8,8,8],\"cooldownBurn\":\"8\",\"cost\":[60,70,80,90,100],\"costBurn\":\"60/70/80/90/100\",\"effect\":[null,[70,115,160,205,250],[6,6,6,6,6],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/115/160/205/250\",\"6\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[825,825,825,825,825],\"rangeBurn\":\"825\",\"image\":{\"full\":\"PhosphorusBomb.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":432,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"CarpetBomb\",\"name\":\"Valkyrie\",\"description\":\"Corki flies a short distance, dropping bombs that create a trail of fire that damages opponents who remain in it.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Active:</span> Corki flies a short distance, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage per second to enemies in the fire left along his path.<br><br><span class=\\\"colorFF9900\\\">Special Delivery:</span> Corki flies a great distance, dropping bombs that knock aside enemies and leave a burning trail for 5 seconds. The trail slows enemies by {{ e2 }}% and burns them for {{ f1 }} <span class=\\\"colorFF8C00\\\">(+{{ f2 }})</span> <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage per second.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[20,19,18,17,16],\"cooldownBurn\":\"20/19/18/17/16\",\"cost\":[100,100,100,100,100],\"costBurn\":\"100\",\"effect\":[null,[60,90,120,150,180],[90,90,90,90,90],[0,0,0,0,0],[4,4,4,4,4],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/90/120/150/180\",\"90\",\"0\",\"4\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"CarpetBomb.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":0,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"GGun\",\"name\":\"Gatling Gun\",\"description\":\"Corki's gatling gun rapidly fires in a cone in front of him, dealing damage and reducing enemy Armor and Magic Resist.\",\"tooltip\":\"Corki's gatling gun fires continuously at targets in front of him for {{ e2 }} seconds, dealing up to {{ e1 }}<span class=\\\"colorFF8C00\\\"> (+{{ f1 }})</span> damage and reducing up to {{ e4 }} armor and magic resist.<br><br><span class=\\\"color919191\\\"><i>Gatling Gun's damage is {{ e7 }}% physical, {{ e8 }}% magic.<br>Defense reductions last for {{ e6 }} seconds after last being damaged by Gatling Gun.</i></span>\",\"leveltip\":{\"label\":[\"Damage\",\"Defense Reduction\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }} -> {{ e4NL }}\"]},\"maxrank\":5,\"cooldown\":[16,16,16,16,16],\"cooldownBurn\":\"16\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[80,140,200,260,320],[4,4,4,4,4],[4,4,4,4,4],[4,8,12,16,20],[8,8,8,8,8],[2,2,2,2,2],[50,50,50,50,50],[50,50,50,50,50],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/140/200/260/320\",\"4\",\"4\",\"4/8/12/16/20\",\"8\",\"2\",\"50\",\"50\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.4,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"GGun.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":48,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"MissileBarrage\",\"name\":\"Missile Barrage\",\"description\":\"Corki fires a missile toward his target location that explodes on impact, dealing damage to enemies in an area. Corki stores missiles over time, up to a maximum. Every 3rd missile fired will be a Big One, dealing extra damage.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Active:</span> Corki fires a missile that explodes at the first enemy it hits, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f3 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to all nearby enemies.<br><br>Corki can store up to 7 missiles, and every 3rd missile will be a Big One, dealing {{ e8 }}% increased damage.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Attack Damage Scaling\",\"Missile Reload Time\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ f1 }} -> {{ f2 }}\"]},\"maxrank\":3,\"cooldown\":[2,2,2],\"cooldownBurn\":\"2\",\"cost\":[20,20,20],\"costBurn\":\"20\",\"effect\":[null,[75,100,125],[15,45,75],[300,300,300],[2,2,2],[0.15,0.45,0.75],[0,0,0],[50,50,50],[100,100,100],[200,200,200],[0,0,0]],\"effectBurn\":[null,\"75/100/125\",\"15/45/75\",\"300\",\"2\",\"0.15/0.45/0.75\",\"0\",\"50\",\"100\",\"200\",\"0\"],\"vars\":[{\"link\":\"@dynamic.attackdamage\",\"coeff\":0,\"key\":\"f3\"},{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"},{\"link\":\"@cooldownchampion\",\"coeff\":10,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"7\",\"range\":[1225,1225,1225],\"rangeBurn\":\"1225\",\"image\":{\"full\":\"MissileBarrage.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":96,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Darius\":{\"id\":122,\"key\":\"Darius\",\"name\":\"Darius\",\"title\":\"the Hand of Noxus\",\"spells\":[{\"id\":\"DariusCleave\",\"name\":\"Decimate\",\"description\":\"Darius winds up and swings his axe in a wide circle. Enemies struck by the blade take more damage than those struck by the handle. Darius heals based on enemy champions hit by the blade.\",\"tooltip\":\"After a short delay, Darius swings his axe around himself, striking enemies in its path. Enemies hit by the axe's blade take {{ e2 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage. Enemies hit by the handle take {{ e6 }}% damage (does not apply Hemorrhage).<br><br>Darius heals for <span class=\\\"colorFF0000\\\">{{ e5 }}% of his missing Health</span> per enemy champion hit by the blade (max: {{ e7 }}%).\",\"leveltip\":{\"label\":[\"Damage\",\"Attack Damage Scaling\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[9,8,7,6,5],\"cooldownBurn\":\"9/8/7/6/5\",\"cost\":[30,35,40,45,50],\"costBurn\":\"30/35/40/45/50\",\"effect\":[null,[100,110,120,130,140],[40,70,100,130,160],[99,99,99,99,99],[0.1,0.1,0.1,0.1,0.1],[12,12,12,12,12],[35,35,35,35,35],[36,36,36,36,36],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"100/110/120/130/140\",\"40/70/100/130/160\",\"99\",\"0.1\",\"12\",\"35\",\"36\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1.05,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1,1,1,1,1],\"rangeBurn\":\"1\",\"image\":{\"full\":\"DariusCleave.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":144,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"DariusNoxianTacticsONH\",\"name\":\"Crippling Strike\",\"description\":\"Darius's next attack strikes an enemy's crucial artery. As they bleed out, their Movement Speed is slowed.\",\"tooltip\":\" Darius's next basic attack deals <span class=\\\"colorFF8C00\\\">{{ f1 }}</span> physical damage and slows the target by {{ e2 }}% for {{ e5 }} second.<br><br>Crippling Strike refunds its Mana cost and {{ e3 }}% of its cooldown if it kills the target.\",\"leveltip\":{\"label\":[\"Cooldown\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[9,8,7,6,5],\"cooldownBurn\":\"9/8/7/6/5\",\"cost\":[30,30,30,30,30],\"costBurn\":\"30\",\"effect\":[null,[0,0,0,0,0],[90,90,90,90,90],[50,50,50,50,50],[1.4,1.4,1.4,1.4,1.4],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"0\",\"90\",\"50\",\"1.4\",\"1\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[300,300,300,300,300],\"rangeBurn\":\"300\",\"image\":{\"full\":\"DariusNoxianTacticsONH.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":192,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"DariusAxeGrabCone\",\"name\":\"Apprehend\",\"description\":\"Darius hones his axe, passively causing his physical damage to ignore a percentage of his target's Armor. When activated, Darius sweeps up his enemies with his axe's hook and pulls them to him.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Darius gains {{ e1 }}% Armor Penetration.<br><br><span class=\\\"colorFF9900\\\">Active: </span>Pulls in all enemies in front of Darius and slows them by {{ e2 }}% for {{ e3 }} second.\",\"leveltip\":{\"label\":[\"Armor Penetration\",\"Cooldown \"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[24,21,18,15,12],\"cooldownBurn\":\"24/21/18/15/12\",\"cost\":[45,45,45,45,45],\"costBurn\":\"45\",\"effect\":[null,[5,10,15,20,25],[40,40,40,40,40],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"5/10/15/20/25\",\"40\",\"1\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[535,535,535,535,535],\"rangeBurn\":\"535\",\"image\":{\"full\":\"DariusAxeGrabCone.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":240,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"DariusExecute\",\"name\":\"Noxian Guillotine\",\"description\":\"Darius leaps to an enemy champion and strikes a lethal blow, dealing true damage. This damage is increased for each stack of Hemorrhage on the target. If Noxian Guillotine is a killing blow, its cooldown is refreshed for a brief duration.\",\"tooltip\":\"Leaps to target enemy champion and strikes a lethal blow, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> true damage. For each stack of Hemorrhage on the target, Noxian Guillotine deals an additional {{ e3 }}% damage.<span class=\\\"size8 colorFF9900\\\">8\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,100,80],\"cooldownBurn\":\"120/100/80\",\"cost\":[100,100,0],\"costBurn\":\"100/100/0\",\"effect\":[null,[100,200,300],[0.75,0.75,0.75],[20,20,20],[25,50,100],[2,2,2],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"100/200/300\",\"0.75\",\"20\",\"25/50/100\",\"2\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.75,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[460,460,460],\"rangeBurn\":\"460\",\"image\":{\"full\":\"DariusExecute.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":288,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Diana\":{\"id\":131,\"key\":\"Diana\",\"name\":\"Diana\",\"title\":\"Scorn of the Moon\",\"spells\":[{\"id\":\"DianaArc\",\"name\":\"Crescent Strike\",\"description\":\"Diana swings her blade to unleash a bolt of lunar energy that deals damage in an arc before exploding. Afflicts enemies struck with the Moonlight debuff, revealing them if they are not stealthed. \",\"tooltip\":\"Unleashes a bolt of lunar energy in an arc dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.<br><br>Afflicts enemies struck with Moonlight, revealing them if they are not stealthed for {{ e4 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[10,9,8,7,6],\"cooldownBurn\":\"10/9/8/7/6\",\"cost\":[55,55,55,55,55],\"costBurn\":\"55\",\"effect\":[null,[60,95,130,165,200],[15,15,15,15,15],[55,55,55,55,55],[3,3,3,3,3],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/95/130/165/200\",\"15\",\"55\",\"3\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[900,900,900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"DianaArc.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":336,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"DianaOrbs\",\"name\":\"Pale Cascade\",\"description\":\"Diana creates three orbiting spheres that detonate on contact with enemies to deal damage in an area. She also gains a temporary shield that absorbs damage. If her third sphere detonates, the shield gains additional strength.\",\"tooltip\":\"Creates three orbiting spheres that explode on contact with enemies dealing {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage. Lasts {{ e1 }} seconds.<br><br>Grants a temporary shield that absorbs {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> damage. If the third sphere detonates, the shield increases by {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span>.\",\"leveltip\":{\"label\":[\"Damage\",\"Shield\",\"Mana Cost\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,10,10,10,10],\"cooldownBurn\":\"10\",\"cost\":[60,70,80,90,100],\"costBurn\":\"60/70/80/90/100\",\"effect\":[null,[5,5,5,5,5],[22,34,46,58,70],[40,55,70,85,100],[1,1,1,1,1],[5,5,5,5,5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"5\",\"22/34/46/58/70\",\"40/55/70/85/100\",\"1\",\"5\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[800,800,800,800,800],\"rangeBurn\":\"800\",\"image\":{\"full\":\"DianaOrbs.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":384,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"DianaVortex\",\"name\":\"Moonfall\",\"description\":\"Diana draws in and slows all nearby enemies.\",\"tooltip\":\"Reveals and draws in all nearby enemies and then slows them by {{ e1 }}% for {{ e2 }} seconds.\",\"leveltip\":{\"label\":[\"Slow Amount\",\"Cooldown\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[26,24,22,20,18],\"cooldownBurn\":\"26/24/22/20/18\",\"cost\":[70,70,70,70,70],\"costBurn\":\"70\",\"effect\":[null,[35,40,45,50,55],[2,2,2,2,2],[50,50,50,50,50],[4,4,4,4,4],[6,6,6,6,6],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"35/40/45/50/55\",\"2\",\"50\",\"4\",\"6\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[450,450,450,450,450],\"rangeBurn\":\"450\",\"image\":{\"full\":\"DianaVortex.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":432,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"DianaTeleport\",\"name\":\"Lunar Rush\",\"description\":\"Diana dashes to an enemy and deals magic damage. Lunar Rush has no cooldown when used to teleport to a target afflicted with Moonlight.\",\"tooltip\":\"Becomes the living embodiment of the vengeful moon, dashing to an enemy and dealing {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.<br><br>Lunar Rush has no cooldown when used to dash to an enemy afflicted with Moonlight. All other enemies will have the Moonlight debuff removed regardless of whether they were the target of Lunar Rush.\",\"leveltip\":{\"label\":[\"Damage\",\"Mana Cost\",\"Cooldown\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ cost }} -> {{ costNL }}\",\" {{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[25,20,15],\"cooldownBurn\":\"25/20/15\",\"cost\":[50,65,80],\"costBurn\":\"50/65/80\",\"effect\":[null,[4,4,4],[100,160,220],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"4\",\"100/160/220\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[825,825,825],\"rangeBurn\":\"825\",\"image\":{\"full\":\"DianaTeleport.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":0,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Draven\":{\"id\":119,\"key\":\"Draven\",\"name\":\"Draven\",\"title\":\"the Glorious Executioner\",\"spells\":[{\"id\":\"DravenSpinning\",\"name\":\"Spinning Axe\",\"description\":\"Draven's next attack will deal bonus physical damage. This axe will ricochet off the target high up into the air. If Draven catches it, he automatically readies another Spinning Axe. Draven can have two Spinning Axes at once.\",\"tooltip\":\"Draven's next attack will deal <span class=\\\"colorFF8C00\\\">{{ f1 }}</span> bonus physical damage. The bonus is equal to {{ e5 }} plus {{ e2 }}% of his bonus Attack Damage.<br><br>This axe will ricochet off the target high up into the air. If Draven catches it, he automatically readies another Spinning Axe.<br><br><span class=\\\"colorDDDD77\\\">Draven can have two Spinning Axes at once.</span>\",\"leveltip\":{\"label\":[\"Base Damage\",\"Bonus AD Percentage\",\"Cooldown\"],\"effect\":[\"{{ e5 }} -> {{ e5NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[12,11,10,9,8],\"cooldownBurn\":\"12/11/10/9/8\",\"cost\":[45,45,45,45,45],\"costBurn\":\"45\",\"effect\":[null,[100,100,100,100,100],[65,75,85,95,105],[30,35,40,45,50],[5.75,5.75,5.75,5.75,5.75],[30,35,40,45,50],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"100\",\"65/75/85/95/105\",\"30/35/40/45/50\",\"5.75\",\"30/35/40/45/50\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":[0.45,0.55,0.65,0.75,0.85],\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[300,300,300,300,300],\"rangeBurn\":\"300\",\"image\":{\"full\":\"DravenSpinning.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":48,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"DravenFury\",\"name\":\"Blood Rush\",\"description\":\"Draven gains increased Movement Speed and Attack Speed. The Movement Speed bonus decreases rapidly over its duration. Catching a Spinning Axe will refresh the cooldown of Blood Rush.\",\"tooltip\":\"\",\"leveltip\":{\"label\":[\"Attack Speed\",\"Mana Cost\",\"Movement Speed\"],\"effect\":[\"{{ e4 }}% -> {{ e4NL }}%\",\"{{ cost }} -> {{ costNL }}\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[12,12,12,12,12],\"cooldownBurn\":\"12\",\"cost\":[40,35,30,25,20],\"costBurn\":\"40/35/30/25/20\",\"effect\":[null,[4,5,6,7,8],[40,45,50,55,60],[1.5,1.5,1.5,1.5,1.5],[20,25,30,35,40],[3,3,3,3,3],[-0.05,-0.056,-0.062,-0.069,-0.075],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"4/5/6/7/8\",\"40/45/50/55/60\",\"1.5\",\"20/25/30/35/40\",\"3\",\"-0.05/-0.056/-0.062/-0.069/-0.075\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"DravenFury.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":96,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"DravenDoubleShot\",\"name\":\"Stand Aside\",\"description\":\"Draven throws his axes, dealing physical damage to targets hit and knocking them aside. Targets hit are slowed.\",\"tooltip\":\"Draven throws his axes, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage to targets hit and knocking them aside. Targets hit are slowed by {{ e2 }}% for {{ e3 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow Amount\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[18,17,16,15,14],\"cooldownBurn\":\"18/17/16/15/14\",\"cost\":[70,70,70,70,70],\"costBurn\":\"70\",\"effect\":[null,[70,105,140,175,210],[20,25,30,35,40],[2,2,2,2,2],[0.5,0.5,0.5,0.5,0.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/105/140/175/210\",\"20/25/30/35/40\",\"2\",\"0.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.5,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1050,1050,1050,1050,1050],\"rangeBurn\":\"1050\",\"image\":{\"full\":\"DravenDoubleShot.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":144,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"DravenRCast\",\"name\":\"Whirling Death\",\"description\":\"Draven hurls two massive axes to deal physical damage to each unit struck. Whirling Death slowly reverses direction and returns to Draven after striking an enemy champion. Draven may also activate this ability while the axes are in flight to cause it to return early. Deals less damage for each unit hit and resets when the axes reverse direction.\",\"tooltip\":\"Draven hurls two massive axes to deal {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage to each unit struck.<br><br>Whirling Death slowly reverses direction and returns to Draven after striking an enemy champion. Draven may also activate this ability while the axes are in flight to cause it to return early. Deals {{ e4 }}% less damage for each unit hit (Minimum {{ e2 }}%) and resets when the axes reverse direction.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\" {{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[100,90,80],\"cooldownBurn\":\"100/90/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[175,275,375],[40,40,40],[0,0,0],[8,8,8],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"175/275/375\",\"40\",\"0\",\"8\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1.1,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[20000,20000,20000],\"rangeBurn\":\"20000\",\"image\":{\"full\":\"DravenRCast.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":192,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"DrMundo\":{\"id\":36,\"key\":\"DrMundo\",\"name\":\"Dr. Mundo\",\"title\":\"the Madman of Zaun\",\"spells\":[{\"id\":\"InfectedCleaverMissileCast\",\"name\":\"Infected Cleaver\",\"description\":\"Dr. Mundo hurls his cleaver, dealing damage equal to a portion of his target's current Health and slowing them for a short time. Dr. Mundo delights in the suffering of others, so he is returned half of the Health cost when he successfully lands a cleaver (increased to the full Health cost on killing blows).\",\"tooltip\":\"Dr. Mundo hurls his cleaver, dealing magic damage equal to {{ e2 }}% of the target's current Health and slowing them by {{ e4 }}% for {{ e5 }} seconds. {{ e1 }} damage minimum, max {{ e6 }} damage to Monsters.<br><br>Half of the Health cost is refunded if the cleaver hits an enemy. The full Health cost is refunded if the cleaver kills an enemy.\",\"leveltip\":{\"label\":[\"Minimum Damage\",\"Damage Percent\",\"Health Cost\",\"Max Monster Damage\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ e6 }} -> {{ e6NL }}\"]},\"maxrank\":5,\"cooldown\":[4,4,4,4,4],\"cooldownBurn\":\"4\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[80,130,180,230,280],[15,17.5,20,22.5,25],[50,60,70,80,90],[40,40,40,40,40],[2,2,2,2,2],[300,350,400,450,500],[0.5,0.5,0.5,0.5,0.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/130/180/230/280\",\"15/17.5/20/22.5/25\",\"50/60/70/80/90\",\"40\",\"2\",\"300/350/400/450/500\",\"0.5\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Health\",\"maxammo\":\"-1\",\"range\":[975,975,975,975,975],\"rangeBurn\":\"975\",\"image\":{\"full\":\"InfectedCleaverMissileCast.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":240,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ e3 }} Health\"},{\"id\":\"BurningAgony\",\"name\":\"Burning Agony\",\"description\":\"Dr. Mundo drains his Health to reduce the duration of disables and deal continual damage to nearby enemies.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Toggle: </span>Dr. Mundo deals {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ charabilitypower*2 }})</span> magic damage to nearby enemies per second, and reduces the duration of disables on Dr. Mundo by {{ e2 }}%.\",\"leveltip\":{\"label\":[\"Damage Per Second\",\"Disable Reduction\",\"Health Cost\"],\"effect\":[\" {{ e3 }} -> {{ e3NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":5,\"cooldown\":[4,4,4,4,4],\"cooldownBurn\":\"4\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[10,15,20,25,30],[10,15,20,25,30],[40,55,70,85,100],[325,325,325,325,325],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"10/15/20/25/30\",\"10/15/20/25/30\",\"40/55/70/85/100\",\"325\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Health Per Sec\",\"maxammo\":\"-1\",\"range\":[325,325,325,325,325],\"rangeBurn\":\"325\",\"image\":{\"full\":\"BurningAgony.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":288,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ e1 }} Health Per Sec\"},{\"id\":\"Masochism\",\"name\":\"Masochism\",\"description\":\"Masochism deals bonus damage on Dr. Mundo's next basic attack and increases Attack Damage by a flat amount for 5 seconds. In addition, Dr. Mundo also gains an additional amount of Attack Damage for each percentage of Health he is missing.\",\"tooltip\":\"Dr. Mundo deals an additional <span class=\\\"colorFF3300\\\">{{ f1 }}</span> physical damage ({{ e6 }}% of max health) on his next basic attack and gains {{ e1 }} Attack Damage for {{ e4 }} seconds. Dr. Mundo gains an additional +{{ e3 }} Attack Damage (<span class=\\\"colorFF3300\\\">{{ f2 }}</span>) for each percent of his Health he is missing.\",\"leveltip\":{\"label\":[\"Attack Damage Bonus\",\"Bonus Factor\",\"Max Health %\",\"Health Cost\"],\"effect\":[\" {{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ e6 }}% -> {{ e6NL }}%\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[30,50,70,90,110],[25,35,45,55,65],[0.3,0.5,0.7,0.9,1.1],[5,5,5,5,5],[5,20,35,50,65],[3,3.5,4,4.5,5],[25,25,25,25,25],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30/50/70/90/110\",\"25/35/45/55/65\",\"0.3/0.5/0.7/0.9/1.1\",\"5\",\"5/20/35/50/65\",\"3/3.5/4/4.5/5\",\"25\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Health\",\"maxammo\":\"-1\",\"range\":[300,300,300,300,300],\"rangeBurn\":\"300\",\"image\":{\"full\":\"Masochism.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":336,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ e2 }} Health\"},{\"id\":\"Sadism\",\"name\":\"Sadism\",\"description\":\"Dr. Mundo sacrifices a portion of his Health for increased Movement Speed and drastically increased Health Regeneration.\",\"tooltip\":\"Dr. Mundo regenerates <span class=\\\"colorCC3300\\\">{{ f1 }}</span> Health ({{ e1 }}% of max health) over {{ e2 }} seconds. Additionally, he gains {{ e3 }}% movement speed during this time.\",\"leveltip\":{\"label\":[\"Regeneration\",\"Movement Speed\",\"Cooldown\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[100,85,70],\"cooldownBurn\":\"100/85/70\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[40,50,60],[12,12,12],[15,25,35],[20,20,20],[0.015,0.015,0.015],[5,5,5],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"40/50/60\",\"12\",\"15/25/35\",\"20\",\"0.01\",\"5\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\"% of Current Health\",\"maxammo\":\"-1\",\"range\":[20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"Sadism.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":384,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ e4 }}% of Current Health\"}]},\"Ekko\":{\"id\":245,\"key\":\"Ekko\",\"name\":\"Ekko\",\"title\":\"the Boy Who Shattered Time\",\"spells\":[{\"id\":\"EkkoQ\",\"name\":\"Timewinder\",\"description\":\"Ekko throws a temporal grenade that expands into a time-distortion field upon hitting an enemy champion, slowing and damaging anyone caught inside. After a delay, the grenade rewinds back to Ekko, dealing damage on its return.\",\"tooltip\":\"Ekko throws a device that deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemies it passes through. It expands into a slowing field on the first champion hit, slowing everything inside by {{ e2 }}%. It then returns to him after a delay, dealing {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage to all targets hit upon return.\",\"leveltip\":{\"label\":[\"Outgoing Damage\",\"Return Damage\",\"Slow\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[9,8.5,8,7.5,7],\"cooldownBurn\":\"9/8.5/8/7.5/7\",\"cost\":[50,60,70,80,90],\"costBurn\":\"50/60/70/80/90\",\"effect\":[null,[60,75,90,105,120],[32,39,46,53,60],[40,65,90,115,140],[100,100,100,100,100],[0,0,0,0,0],[165,165,165,165,165],[1.75,1.75,1.75,1.75,1.75],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/75/90/105/120\",\"32/39/46/53/60\",\"40/65/90/115/140\",\"100\",\"0\",\"165\",\"1.75\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1075,1075,1075,1075,1075],\"rangeBurn\":\"1075\",\"image\":{\"full\":\"EkkoQ.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":432,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"EkkoW\",\"name\":\"Parallel Convergence\",\"description\":\"Ekko splits the timeline, creating an anomaly after a few seconds that slows enemies caught inside. If Ekko enters the anomaly, he gains shielding and triggers a detonation, stunning enemies by suspending them in time.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> Ekko's basic attacks deal bonus magic damage to enemies under 30% health equal to {{ e3 }}% <span class=\\\"color99FF99\\\">(+{{ a2 }}%)</span> of their missing health. Deals a minimum of {{ e6 }} damage, and a maximum of {{ e5 }} damage vs. minions and monsters.<br><br><span class=\\\"colorFF9900\\\">Active:</span> After a 3 second delay, Ekko creates a short-lived chronosphere at the target location that slows enemies who enter by {{ e0 }}%. If Ekko enters the sphere, he will detonate it, gaining a shield that absorbs up to {{ e4 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> damage for 2 seconds. Enemies caught inside are stunned for {{ e2 }} seconds.\",\"leveltip\":{\"label\":[\"Shield Amount\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e4 }} -> {{ e4NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}<\"]},\"maxrank\":5,\"cooldown\":[22,20,18,16,14],\"cooldownBurn\":\"22/20/18/16/14\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[375,375,375,375,375],[1.75,1.75,1.75,1.75,1.75],[3,3,3,3,3],[80,100,120,140,160],[150,150,150,150,150],[15,15,15,15,15],[1.5,1.5,1.5,1.5,1.5],[3,3,3,3,3],[2,2,2,2,2],[40,40,40,40,40]],\"effectBurn\":[null,\"375\",\"1.75\",\"3\",\"80/100/120/140/160\",\"150\",\"15\",\"1.5\",\"3\",\"2\",\"40\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.03,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":1.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1600,1600,1600,1600,1600],\"rangeBurn\":\"1600\",\"image\":{\"full\":\"EkkoW.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":0,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"EkkoE\",\"name\":\"Phase Dive\",\"description\":\"Ekko rolls evasively while charging up his Z-Drive. His next attack deals bonus damage and warps reality, teleporting him to his target.\",\"tooltip\":\" Ekko dashes a short distance in the targeted direction. His next attack will deal {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> bonus magic damage and teleport him to his target.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[11,10,9,8,7],\"cooldownBurn\":\"11/10/9/8/7\",\"cost\":[40,50,60,70,80],\"costBurn\":\"40/50/60/70/80\",\"effect\":[null,[40,65,90,115,140],[350,350,350,350,350],[3,3,3,3,3],[300,300,300,300,300],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"40/65/90/115/140\",\"350\",\"3\",\"300\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[325,325,325,325,325],\"rangeBurn\":\"325\",\"image\":{\"full\":\"EkkoE.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":48,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"EkkoR\",\"name\":\"Chronobreak\",\"description\":\"Ekko shatters his timeline, becoming untargetable and rewinding to a more favorable point in time. He returns to whenever he was a few seconds ago, and heals for a percentage of the damage received in that duration. Enemies near his arrival zone take massive damage.\",\"tooltip\":\"Ekko turns back time, going briefly untargetable and invulnerable. He teleports to where he was 4 seconds ago and deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to nearby enemies on arrival. Additionally, Ekko heals for {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span>, increased by {{ e5 }}% for each 1% of his health lost over the last 4 seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Flat Heal\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[110,90,70],\"cooldownBurn\":\"110/90/70\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[150,300,450],[20,20,20],[100,150,200],[375,375,375],[3,3,3],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"150/300/450\",\"20\",\"100/150/200\",\"375\",\"3\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1.5,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[850,850,850],\"rangeBurn\":\"850\",\"image\":{\"full\":\"EkkoR.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":96,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Elise\":{\"id\":60,\"key\":\"Elise\",\"name\":\"Elise\",\"title\":\"the Spider Queen\",\"spells\":[{\"id\":\"EliseHumanQ\",\"name\":\"Neurotoxin / Venomous Bite\",\"description\":\"Human Form: Deals damage based upon how high the target's Health is.<br><br>Spider Form: Lunges at an enemy and deals damage based upon how low their Health is.\",\"tooltip\":\"Deals magic damage equal to {{ e1 }} plus {{ e6 }}% <span class=\\\"color99FF99\\\">(+{{ a1 }}%)</span> of the target's current health. Maximum bonus against monsters: {{ e2 }}.\",\"leveltip\":{\"label\":[\"Neurotoxin Damage\",\"Venomous Bite Damage\",\"Max Damage to Monsters\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e5 }} -> {{ e5NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[80,85,90,95,100],\"costBurn\":\"80/85/90/95/100\",\"effect\":[null,[40,75,110,145,180],[75,100,125,150,175],[15,20,25,30,35],[36,42,48,54,60],[60,100,140,180,220],[4,4,4,4,4],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"40/75/110/145/180\",\"75/100/125/150/175\",\"15/20/25/30/35\",\"36/42/48/54/60\",\"60/100/140/180/220\",\"4\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.03,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[625,625,625,625,625],\"rangeBurn\":\"625\",\"image\":{\"full\":\"EliseHumanQ.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":144,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"EliseHumanW\",\"name\":\"Volatile Spiderling / Skittering Frenzy\",\"description\":\"Human Form: Releases a venom-gorged Spiderling that explodes when it nears a target.<br><br>Spider Form: Elise and her Spiderlings gain Attack Speed.\",\"tooltip\":\"Summons a venom-gorged Spiderling that moves to target location and explodes, dealing {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Magic Damage when it nears an enemy or after 3 seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Mana Cost\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[12,12,12,12,12],\"cooldownBurn\":\"12\",\"cost\":[60,70,80,90,100],\"costBurn\":\"60/70/80/90/100\",\"effect\":[null,[4,4,4,4,4],[60,80,100,120,140],[60,110,160,210,260],[3,3,3,3,3],[65,75,85,95,105],[275,275,275,275,275],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"4\",\"60/80/100/120/140\",\"60/110/160/210/260\",\"3\",\"65/75/85/95/105\",\"275\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[950,950,950,950,950],\"rangeBurn\":\"950\",\"image\":{\"full\":\"EliseHumanW.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":192,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"EliseHumanE\",\"name\":\"Cocoon / Rappel\",\"description\":\"Human Form: Stuns the first enemy unit hit and reveals them if they are not stealthed.<br><br>Spider Form: Elise and her Spiderlings ascend into the air and then descend upon target enemy.\",\"tooltip\":\"Stuns the first enemy hit for {{ e5 }} seconds and reveals them if they are not stealthed.\",\"leveltip\":{\"label\":[\"Stun Duration\",\"Cooldown\"],\"effect\":[\"{{ e5 }} -> {{ e5NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[14,13,12,11,10],\"cooldownBurn\":\"14/13/12/11/10\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[14,13,12,11,10],[15,20,25,30,35],[26,23,20,17,14],[2,2,2,2,2],[1.6,1.7,1.8,1.9,2],[40,55,70,85,100],[250,250,250,250,250],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"14/13/12/11/10\",\"15/20/25/30/35\",\"26/23/20/17/14\",\"2\",\"1.6/1.7/1.8/1.9/2\",\"40/55/70/85/100\",\"250\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1075,1075,1075,1075,1075],\"rangeBurn\":\"1075\",\"image\":{\"full\":\"EliseHumanE.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":240,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"EliseR\",\"name\":\"Spider Form\",\"description\":\"Transforms into a menacing spider, reducing her attack range in exchange for movement speed, new abilities, and a Spiderling swarm that will attack her foes.\",\"tooltip\":\"Elise transforms into a menacing spider, sacrificing 425 attack range in exchange for {{ e3 }} movement speed and access to arachnid abilities. All dormant Spiderlings are awakened and will attack nearby foes.<br><br><span class=\\\"colorFF3300\\\">Spiderlings: </span>Spiderlings deal {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> damage and take {{ e4 }}% reduced damage from multi-target abilities.\",\"leveltip\":{\"label\":[\"Spider Form Bite Damage\",\"Spiderling Bonus Damage\",\"Maximum Number of Spiderlings\",\"Spiderling Armor\",\"Spiderling Magic Resist\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ e1 }} -> {{ e1NL }}\",\"{{ e5 }} -> {{ e5NL }}\",\"{{ e6 }} -> {{ e6NL }}\",\"{{ e7 }} -> {{ e7NL }}\"]},\"maxrank\":4,\"cooldown\":[4,4,4,4],\"cooldownBurn\":\"4\",\"cost\":[0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[10,15,20,25],[10,20,30,40],[25,25,25,25],[25,25,25,25],[2,3,4,5],[30,50,70,90],[50,70,90,110],[4,6,8,10],[0.1,0.1,0.1,0.1],[0,0,0,0]],\"effectBurn\":[null,\"10/15/20/25\",\"10/20/30/40\",\"25\",\"25\",\"2/3/4/5\",\"30/50/70/90\",\"50/70/90/110\",\"4/6/8/10\",\"0.1\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.15,\"key\":\"a2\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[20,20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"EliseR.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":288,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Evelynn\":{\"id\":28,\"key\":\"Evelynn\",\"name\":\"Evelynn\",\"title\":\"the Widowmaker\",\"spells\":[{\"id\":\"EvelynnQ\",\"name\":\"Hate Spike\",\"description\":\"Evelynn fires a line of spikes through an enemy, dealing damage to all enemies in its path.\",\"tooltip\":\"Evelynn fires a line of spikes through a nearby enemy dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ f2 }})</span> <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> magic damage to all enemies in its path.<br><br><span class=\\\"color99FF99\\\">Hate Spike prioritizes the target Evelynn is attacking.</span>\",\"leveltip\":{\"label\":[\"Damage\",\"Ability Power Scaling\",\"Attack Damage Scaling\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[1.5,1.5,1.5,1.5,1.5],\"cooldownBurn\":\"1.5\",\"cost\":[12,18,24,30,36],\"costBurn\":\"12/18/24/30/36\",\"effect\":[null,[40,50,60,70,80],[35,40,45,50,55],[50,55,60,65,70],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"40/50/60/70/80\",\"35/40/45/50/55\",\"50/55/60/65/70\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.4,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[500,500,500,500,500],\"rangeBurn\":\"500\",\"image\":{\"full\":\"EvelynnQ.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":336,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"EvelynnW\",\"name\":\"Dark Frenzy\",\"description\":\"Evelynn passively increases her Movement Speed when hitting enemy champions with her spells. Upon activation, Evelynn breaks free from slows affecting her and gains a massive Movement Speed boost for a short duration.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Active:</span> Evelynn removes all slows affecting her and gains {{ e3 }}% Movement Speed for {{ e4 }} seconds.<br><br><span class=\\\"colorFF9900\\\">Passive:</span> Evelynn's spell hits on enemy champions reduce Dark Frenzy's cooldown by 1 second.<br><br><span class=\\\"color99FF99\\\">Champion kills and assists refresh Dark Frenzy's cooldown.</span>\",\"leveltip\":{\"label\":[\"Active Movement Speed\"],\"effect\":[\"{{ e3 }}% -> {{ e3NL }}%\"]},\"maxrank\":5,\"cooldown\":[15,15,15,15,15],\"cooldownBurn\":\"15\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[1,1,1,1,1],[3,3,3,3,3],[30,40,50,60,70],[3,3,3,3,3],[4,4,4,4,4],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"1\",\"3\",\"30/40/50/60/70\",\"3\",\"4\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[10000,10000,10000,10000,10000],\"rangeBurn\":\"10000\",\"image\":{\"full\":\"EvelynnW.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":384,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"EvelynnE\",\"name\":\"Ravage\",\"description\":\"Evelynn slashes her target twice, dealing damage with each hit. She then gains increased Attack Speed for a short duration.\",\"tooltip\":\"Evelynn swiftly attacks a target {{ e3 }} times (applies on-hit effects), dealing a total of {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage.<br><br>Evelynn then gains {{ e4 }}% Attack Speed for {{ e2 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Attack Speed\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }}% -> {{ e4NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[9,9,9,9,9],\"cooldownBurn\":\"9\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[70,110,150,190,230],[3,3,3,3,3],[2,2,2,2,2],[60,75,90,105,120],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/110/150/190/230\",\"3\",\"2\",\"60/75/90/105/120\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"},{\"link\":\"bonusattackdamage\",\"coeff\":0.4,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[225,225,225,225,225],\"rangeBurn\":\"225\",\"image\":{\"full\":\"EvelynnE.png\",\"sprite\":\"spell2.png\",\"group\":\"spell\",\"x\":432,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"EvelynnR\",\"name\":\"Agony's Embrace\",\"description\":\"Evelynn summons spikes from the ground to deal damage and slow enemies in the area. She then gains a shield based on how many enemy champions were hit.\",\"tooltip\":\"Evelynn impales all enemies in the targeted area, dealing <span class=\\\"color99FF99\\\">{{ f1 }}%</span> [{{ e1 }}% + {{ f2 }}% per 100 ability power] of each target's current health as magic damage and slowing their movement speed by {{ e2 }}% for {{ e3 }} seconds.<br><br>Evelynn siphons their pain, gaining a {{ e4 }} health shield for each enemy champion hit which lasts up to {{ e6 }} seconds.\",\"leveltip\":{\"label\":[\"Current Health %\",\"Slow\",\"Shield Amount\",\"Cooldown\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,100,80],\"cooldownBurn\":\"120/100/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[15,20,25],[40,60,80],[2,2,2],[150,225,300],[50,100,150],[6,6,6],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"15/20/25\",\"40/60/80\",\"2\",\"150/225/300\",\"50/100/150\",\"6\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"EvelynnR.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":0,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Ezreal\":{\"id\":81,\"key\":\"Ezreal\",\"name\":\"Ezreal\",\"title\":\"the Prodigal Explorer\",\"spells\":[{\"id\":\"EzrealMysticShot\",\"name\":\"Mystic Shot\",\"description\":\"Ezreal fires a damaging bolt of energy which reduces all of his cooldowns by 1.5 seconds if it strikes an enemy unit.\",\"tooltip\":\"Ezreal fires a bolt of energy, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> physical damage (applies on-hit effects). <br><br>Ezreal's cooldowns are reduced by {{ e2 }} seconds if Mystic Shot hits a target.\",\"leveltip\":{\"label\":[\"Bonus Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[6.5,6,5.5,5,4.5],\"cooldownBurn\":\"6.5/6/5.5/5/4.5\",\"cost\":[28,31,34,37,40],\"costBurn\":\"28/31/34/37/40\",\"effect\":[null,[35,55,75,95,115],[1.5,1.5,1.5,1.5,1.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"35/55/75/95/115\",\"1.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1.1,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1150,1150,1150,1150,1150],\"rangeBurn\":\"1150\",\"image\":{\"full\":\"EzrealMysticShot.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":48,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"EzrealEssenceFlux\",\"name\":\"Essence Flux\",\"description\":\"Ezreal fires a fluctuating wave of energy, dealing magic damage to enemy champions, while increasing the Attack Speed of allied champions.\",\"tooltip\":\"Ezreal fires a wave of energy that damages all enemy champions it passes through for {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage. If Ezreal or his Allied champions are hit by the wave, their Attack Speed is increased by {{ e2 }}% for {{ e3 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Attack Speed\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ e2 }}% -> {{ e2NL }}%\",\" {{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[9,9,9,9,9],\"cooldownBurn\":\"9\",\"cost\":[50,60,70,80,90],\"costBurn\":\"50/60/70/80/90\",\"effect\":[null,[70,115,160,205,250],[20,25,30,35,40],[5,5,5,5,5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/115/160/205/250\",\"20/25/30/35/40\",\"5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"EzrealEssenceFlux.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":96,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"EzrealArcaneShift\",\"name\":\"Arcane Shift\",\"description\":\"Ezreal teleports to a target nearby location and fires a homing bolt which strikes the nearest enemy unit.\",\"tooltip\":\"Ezreal teleports to a nearby location and fires a bolt at the nearest enemy, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a2 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[19,17.5,16,14.5,13],\"cooldownBurn\":\"19/17.5/16/14.5/13\",\"cost\":[90,90,90,90,90],\"costBurn\":\"90\",\"effect\":[null,[75,125,175,225,275],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"75/125/175/225/275\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.5,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.75,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[475,475,475,475,475],\"rangeBurn\":\"475\",\"image\":{\"full\":\"EzrealArcaneShift.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":144,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"EzrealTrueshotBarrage\",\"name\":\"Trueshot Barrage\",\"description\":\"Ezreal winds up for 1 second to fire a powerful barrage of energy missiles which do massive damage to each unit they pass through (deals 10% less damage to each unit it passes through).\",\"tooltip\":\"Ezreal winds up for 1 second to fire a long range missile that deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a2 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to each enemy it passes through. Deals {{ e2 }}% less damage for each enemy hit (minimum {{ e3 }}%).\",\"leveltip\":{\"label\":[\"Damage\"],\"effect\":[\" {{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":3,\"cooldown\":[120,120,120],\"cooldownBurn\":\"120\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[350,500,650],[10,10,10],[30,30,30],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"350/500/650\",\"10\",\"30\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.9,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[20000,20000,20000],\"rangeBurn\":\"20000\",\"image\":{\"full\":\"EzrealTrueshotBarrage.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":192,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Fiddlesticks\":{\"id\":9,\"key\":\"Fiddlesticks\",\"name\":\"Fiddlesticks\",\"title\":\"the Harbinger of Doom\",\"spells\":[{\"id\":\"Terrify\",\"name\":\"Terrify\",\"description\":\"Strikes a target unit with fear, causing it to flee in terror for a duration.\",\"tooltip\":\"Terrifies a target, causing it to flee from Fiddlesticks for {{ e1 }} seconds.\",\"leveltip\":{\"label\":[\"Duration\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[15,14,13,12,11],\"cooldownBurn\":\"15/14/13/12/11\",\"cost\":[65,65,65,65,65],\"costBurn\":\"65\",\"effect\":[null,[1.25,1.5,1.75,2,2.25],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"1.25/1.5/1.75/2/2.25\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[575,575,575,575,575],\"rangeBurn\":\"575\",\"image\":{\"full\":\"Terrify.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":240,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"Drain\",\"name\":\"Drain\",\"description\":\"Fiddlesticks saps the life force of an enemy, dealing damage to a target over time and healing himself.\",\"tooltip\":\"Fiddlesticks creates a tether to the target unit and channels <span class=\\\"colorFFEB7F\\\">Drain</span> on it, granting <span class=\\\"coloree91d7\\\">True Sight</span> and dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage per second to the target. Fiddlesticks heals for {{ e3 }}% of the damage dealt.<br><br><span class=\\\"colorFFEB7F\\\">Drain</span> lasts up to {{ e4 }} seconds, dealing a total of {{ f1 }} <span class=\\\"color99FF99\\\">(+{{ f2 }})</span> damage.<br><br><span class=\\\"size16 color8C8C8C\\\">16\",\"leveltip\":{\"label\":[\"Damage Per Second\",\"Cooldown\",\"Mana Cost\",\"Drain Ratio\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\",\"{{ e3 }}% -> {{ e3NL }}%\"]},\"maxrank\":5,\"cooldown\":[4,3.75,3.5,3.25,3],\"cooldownBurn\":\"4/3.75/3.5/3.25/3\",\"cost\":[80,90,100,110,120],\"costBurn\":\"80/90/100/110/120\",\"effect\":[null,[80,105,130,155,180],[4,4,4,4,4],[60,65,70,75,80],[5,5,5,5,5],[650,650,650,650,650],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/105/130/155/180\",\"4\",\"60/65/70/75/80\",\"5\",\"650\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.45,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[575,575,575,575,575],\"rangeBurn\":\"575\",\"image\":{\"full\":\"Drain.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":288,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"FiddlesticksDarkWind\",\"name\":\"Dark Wind\",\"description\":\"A wisp of wind strikes an enemy unit and then bounces to nearby enemy units, dealing damage and silencing the victims.\",\"tooltip\":\"Unleashes a Crow at the targeted enemy, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage before bouncing to another target. Units struck are also silenced for {{ e2 }} seconds the first time per cast.<br><br>Dark Wind can strike up to {{ e3 }} times and will prioritize enemies being <span class=\\\"colorFFEB7F\\\">Drained</span> or ones it has not yet hit. Deals {{ e4 }}% damage to minions and monsters.\",\"leveltip\":{\"label\":[\"Damage\",\"Mana Cost\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cost }} -> {{ costNL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[12,11.5,11,10.5,10],\"cooldownBurn\":\"12/11.5/11/10.5/10\",\"cost\":[50,60,70,80,90],\"costBurn\":\"50/60/70/80/90\",\"effect\":[null,[65,85,105,125,145],[1.25,1.25,1.25,1.25,1.25],[6,6,6,6,6],[150,150,150,150,150],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"65/85/105/125/145\",\"1.25\",\"6\",\"150\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.45,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[750,750,750,750,750],\"rangeBurn\":\"750\",\"image\":{\"full\":\"FiddlesticksDarkWind.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":336,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"Crowstorm\",\"name\":\"Crowstorm\",\"description\":\"A murder of crows flock wildly around Fiddlesticks, dealing damage per second to all enemy units in the area.\",\"tooltip\":\"Fiddlesticks channels for {{ e3 }} seconds, then blinks to the target point unleashing a murder of crows that flock wildly around him, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage per second to all enemy units in the area.<br><br>The effect lasts for {{ e2 }} seconds, dealing up to {{ f1 }} <span class=\\\"color99FF99\\\">(+{{ f2 }})</span> total magic damage.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[150,140,130],\"cooldownBurn\":\"150/140/130\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[125,225,325],[5,5,5],[1.5,1.5,1.5],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"125/225/325\",\"5\",\"1.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.45,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[800,800,800],\"rangeBurn\":\"800\",\"image\":{\"full\":\"Crowstorm.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":384,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Fiora\":{\"id\":114,\"key\":\"Fiora\",\"name\":\"Fiora\",\"title\":\"the Grand Duelist\",\"spells\":[{\"id\":\"FioraQ\",\"name\":\"Lunge\",\"description\":\"Fiora lunges in a direction and stabs a nearby enemy, dealing physical damage and applying on-hit effects.\",\"tooltip\":\"Fiora lunges in a direction and stabs a nearby enemy, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage and applying on-hit effects. This attack prioritizes <span class=\\\"colorD4BD22\\\">Vitals</span> and enemies it will kill.<br><br>If this ability hits an enemy, {{ e4 }}% of its cooldown is refunded. \",\"leveltip\":{\"label\":[\"Damage\",\"Attack Damage Ratio\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }}  -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[16,14,12,10,8],\"cooldownBurn\":\"16/14/12/10/8\",\"cost\":[20,25,30,35,40],\"costBurn\":\"20/25/30/35/40\",\"effect\":[null,[65,75,85,95,105],[0.95,1,1.05,1.1,1.15],[50,50,50,50,50],[60,60,60,60,60],[2.5,2.5,2.5,2.5,2.5],[2,2,2,2,2],[90,120,150,180,210],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"65/75/85/95/105\",\"0.95/1/1.05/1.1/1.15\",\"50\",\"60\",\"2.5\",\"2\",\"90/120/150/180/210\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.6,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[400,400,400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"FioraQ.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":432,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"FioraW\",\"name\":\"Riposte\",\"description\":\"Fiora parries all incoming damage and disables for a short time, then stabs in a direction. This stab slows the first enemy champion hit, or stuns them if Fiora blocked an immobilizing effect with this ability.\",\"tooltip\":\"Fiora parries all incoming damage, debuffs, and disables for the next {{ e2 }} seconds and then stabs in the target direction.<br><br>The stab deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ f1 }})</span> magic damage to the first enemy champion hit and slows their movement and attack speed by {{ e4 }}% for {{ e3 }} seconds. <br><br>If Fiora parries an immobilizing effect, Riposte stuns instead of slowing.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ f2 }} -> {{ f3 }}\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[90,130,170,210,250],[0.75,0.75,0.75,0.75,0.75],[1.5,1.5,1.5,1.5,1.5],[50,50,50,50,50],[0.03,0.03,0.03,0.03,0.03],[0.15,0.15,0.15,0.15,0.25],[24,22,20,18,16],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"90/130/170/210/250\",\"0.75\",\"1.5\",\"50\",\"0.03\",\"0.15/0.15/0.15/0.15/0.25\",\"24/22/20/18/16\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[750,750,750,750,750],\"rangeBurn\":\"750\",\"image\":{\"full\":\"FioraW.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":0,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"FioraE\",\"name\":\"Bladework\",\"description\":\"Fiora has increased attack speed for the next two attacks. The first attack slows the target, and the second attack will critically strike.\",\"tooltip\":\"Fiora gains {{ e4 }}% attack speed for her next two attacks. The first attack cannot critically strike, but will apply a {{ e3 }}% slow for {{ e2 }} second. The second attack is guaranteed to critically strike for {{ f4 }}% damage <span class=\\\"colorFF8C00\\\">({{ f3 }})</span>.\",\"leveltip\":{\"label\":[\"Critical Strike Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ f4 }}%  -> {{ f5 }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[13,11,9,7,5],\"cooldownBurn\":\"13/11/9/7/5\",\"cost\":[40,45,50,55,60],\"costBurn\":\"40/45/50/55/60\",\"effect\":[null,[3,3,3,3,3],[1,1,1,1,1],[30,30,30,30,30],[50,50,50,50,50],[100,100,100,100,100],[140,155,170,185,200],[5,10,15,20,25],[40,50,60,70,80],[10,12.5,15,17.5,20],[20,20,20,20,20]],\"effectBurn\":[null,\"3\",\"1\",\"30\",\"50\",\"100\",\"140/155/170/185/200\",\"5/10/15/20/25\",\"40/50/60/70/80\",\"10/12.5/15/17.5/20\",\"20\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[425,425,425,425,425],\"rangeBurn\":\"425\",\"image\":{\"full\":\"FioraE.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":48,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"FioraR\",\"name\":\"Grand Challenge\",\"description\":\"Fiora reveals all four Vitals on an enemy champion and gains movement speed while near them. If Fiora hits all 4 Vitals or if the target dies after she has hit at least one, Fiora and her allies in the area are healed over the next few seconds.\",\"tooltip\":\"Fiora reveals all four <span class=\\\"colorD4BD22\\\">Vitals</span> on target champion, for a potential <span class=\\\"colorFFFFFF\\\">{{ f8 }}%</span> max health true damage. In addition, Fiora gains Duelist's Dance's movement speed bonus <span class=\\\"colorFFFFFF\\\">({{ f6 }}%)</span> while near the target.<br><br>If Fiora hits all 4 <span class=\\\"colorD4BD22\\\">Vitals</span> in {{ e1 }} seconds or if the target dies after she has hit at least one, Fiora and her allies in the area are healed for {{ e7 }} <span class=\\\"colorFF8C00\\\">(+{{ f9 }})</span> each second for between {{ e9 }} to {{ e6 }} seconds, scaling with the amount of Vitals hit.\",\"leveltip\":{\"label\":[\"Cooldown\",\"Duelist Movement Speed\",\"Heal Per Second\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ e7 }} -> {{ e7NL }}\"]},\"maxrank\":3,\"cooldown\":[110,90,70],\"cooldownBurn\":\"110/90/70\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[8,8,8],[30,40,50],[0,0,0],[30,45,60],[500,500,500],[5,5,5],[80,110,140],[550,550,550],[2,2,2],[1,1,1]],\"effectBurn\":[null,\"8\",\"30/40/50\",\"0\",\"30/45/60\",\"500\",\"5\",\"80/110/140\",\"550\",\"2\",\"1\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[500,500,500],\"rangeBurn\":\"500\",\"image\":{\"full\":\"FioraR.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":96,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Fizz\":{\"id\":105,\"key\":\"Fizz\",\"name\":\"Fizz\",\"title\":\"the Tidal Trickster\",\"spells\":[{\"id\":\"FizzQ\",\"name\":\"Urchin Strike\",\"description\":\"Fizz dashes through his target, dealing magic damage and applying on hit effects.\",\"tooltip\":\"Fizz dashes through his target, dealing <span class=\\\"colorFF8C00\\\">{{ a2 }}</span> physical damage plus {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage. This spell applies on-hit effects. <br><br><span class=\\\"size16 colorFFEB7F\\\">16\",\"leveltip\":{\"label\":[\"Magic Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[8,7.5,7,6.5,6],\"cooldownBurn\":\"8/7.5/7/6.5/6\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[10,25,40,55,70],[0,0,0,0,0],[650,750,850,950,1050],[1.5,1.5,1.5,1.5,1.5],[600,600,600,600,600],[1,1,1,1,1],[0.35,0.35,0.35,0.35,0.35],[40,40,40,40,40],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"10/25/40/55/70\",\"0\",\"650/750/850/950/1050\",\"1.5\",\"600\",\"1\",\"0.35\",\"40\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.55,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[550,550,550,550,550],\"rangeBurn\":\"550\",\"image\":{\"full\":\"FizzQ.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":144,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"FizzW\",\"name\":\"Seastone Trident\",\"description\":\"Fizz's attacks bleed his enemies, dealing magic damage over several seconds. Fizz can empower his next attack to deal bonus damage, increased by how long the bleed was active on his target.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Fizz's basic attacks bleed his enemies, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage over {{ e8 }} seconds. <br><br><span class=\\\"colorFF9900\\\">Active: </span>Fizz's next basic attack deals {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> bonus magic damage. If the target has been bleeding for at least {{ e7 }} seconds, this bonus increases to {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span>. <br><br><span class=\\\"size16 color8C8C8C\\\">16\",\"leveltip\":{\"label\":[\"Passive / Active Damage\",\"Charged Active Damage\",\"Cooldown\",\"Mana Cost\",\"Mana Refund\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[10,9.5,9,8.5,8],\"cooldownBurn\":\"10/9.5/9/8.5/8\",\"cost\":[30,40,50,60,70],\"costBurn\":\"30/40/50/60/70\",\"effect\":[null,[20,30,40,50,60],[20,28,36,42,50],[60,90,120,150,180],[6,6,6,6,6],[2,2,2,2,2],[4,4,4,4,4],[2,2,2,2,2],[3,3,3,3,3],[750,750,750,750,750],[1,1,1,1,1]],\"effectBurn\":[null,\"20/30/40/50/60\",\"20/28/36/42/50\",\"60/90/120/150/180\",\"6\",\"2\",\"4\",\"2\",\"3\",\"750\",\"1\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":1.2,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"FizzW.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":192,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"FizzE\",\"name\":\"Playful / Trickster\",\"description\":\"Fizz hops into the air, landing gracefully upon his spear and becoming untargetable. From this position, Fizz can either slam the ground or choose to jump again before smashing back down.\",\"tooltip\":\"Fizz hops on his trident towards the cursor, becoming briefly untargetable.<br><br><span class=\\\"colorFF9900\\\">Reactivation: </span>Fizz cancels the spell early, jumping towards the cursor a second time and dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to nearby enemies.<br><br>If Fizz does not reactivate, he deals damage in a larger area and slows all enemies hit by {{ e2 }}% for {{ e4 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow Percentage\",\"Mana Cost\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cost }} -> {{ costNL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[18,16,14,12,10],\"cooldownBurn\":\"18/16/14/12/10\",\"cost\":[90,95,100,105,110],\"costBurn\":\"90/95/100/105/110\",\"effect\":[null,[70,120,170,220,270],[40,45,50,55,60],[225,225,225,225,225],[2,2,2,2,2],[375,375,375,375,375],[80,80,80,80,80],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/120/170/220/270\",\"40/45/50/55/60\",\"225\",\"2\",\"375\",\"80\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.75,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[400,400,400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"FizzE.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":240,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"FizzR\",\"name\":\"Chum the Waters\",\"description\":\"Fizz tosses a fish in a direction that attaches to any champion that touches it, slowing the target. After a short delay, a shark erupts from the ground, knocking up the target and knocking any nearby enemies aside. All enemies hit are dealt magic damage and slowed.\",\"tooltip\":\"Fizz launches a fish that attaches to the first enemy champion it hits, slowing them, granting <span class=\\\"coloree91d7\\\">True Sight</span>, and attracting a shark. After {{ e8 }} seconds, the shark erupts out of the ground, knocking its target up and knocking other enemies aside.<br><br>The farther the fish travels before attaching, the bigger the shark it attracts, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> to {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage and slowing by 40% to 80%.<br><br><span class=\\\"size16 colorFFEB7F\\\">16\",\"leveltip\":{\"label\":[\"Small Shark Damage\",\"Medium Shark Damage\",\"Big Shark Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} ->{{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[100,85,70],\"cooldownBurn\":\"100/85/70\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[150,250,350],[225,325,425],[300,400,500],[200,200,200],[325,325,325],[450,450,450],[1300,1300,1300],[2,2,2],[2,2,2],[60,60,60]],\"effectBurn\":[null,\"150/250/350\",\"225/325/425\",\"300/400/500\",\"200\",\"325\",\"450\",\"1300\",\"2\",\"2\",\"60\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":1.2,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1300,1300,1300],\"rangeBurn\":\"1300\",\"image\":{\"full\":\"FizzR.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":288,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Galio\":{\"id\":3,\"key\":\"Galio\",\"name\":\"Galio\",\"title\":\"the Colossus\",\"spells\":[{\"id\":\"GalioQ\",\"name\":\"Winds of War\",\"description\":\"Galio fires two windblasts that converge into a large tornado.\",\"tooltip\":\"Galio fires two windblasts that deal {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.<br><br>When the windblasts meet, they combine into a giant tornado that deals {{ e5 }} <span class=\\\"color99FF99\\\">(+{{ charabilitypower2*3 }})</span> plus {{ e2 }}% of enemies' maximum Health (max {{ e4 }} vs. monsters) as magic damage over {{ e3 }} seconds.\",\"leveltip\":{\"label\":[\"Windblast Damage\",\"Tornado Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e5 }} -> {{ e5NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,9,8,7,6],\"cooldownBurn\":\"10/9/8/7/6\",\"cost\":[70,75,80,85,90],\"costBurn\":\"70/75/80/85/90\",\"effect\":[null,[60,95,130,165,200],[6,6,6,6,6],[1.5,1.5,1.5,1.5,1.5],[150,150,150,150,150],[45,60,75,90,105],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/95/130/165/200\",\"6\",\"1.5\",\"150\",\"45/60/75/90/105\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.75,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[825,825,825,825,825],\"rangeBurn\":\"825\",\"image\":{\"full\":\"GalioQ.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":336,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"GalioW\",\"name\":\"Shield of Durand\",\"description\":\"Galio charges a defensive stance, moving slowly. Upon releasing the charge, Galio will taunt nearby enemies.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> After not taking damage for {{ e6 }} seconds Galio gains a shield that absorbs <span class=\\\"colorFF0000\\\">{{ f3 }}</span> magic damage.<br><br><span class=\\\"colorFF9900\\\">First Cast:</span> Galio starts to charge, gaining {{ e1 }} <span class=\\\"colorFF00FF\\\">(+{{ f2 }})</span>% Damage Reduction but slowing himself by {{ e3 }}%.<br><br><span class=\\\"colorFF9900\\\">Second Cast:</span> Galio taunts nearby enemy champions for {{ e4 }} to {{ e7 }} seconds and refreshes the Damage Reduction for {{ e8 }} seconds. Taunt duration and radius increase with charge time.<br><br><span class=\\\"colorDDDD77\\\">Shield of Durand's charge cannot be interrupted by crowd control.</span>\",\"leveltip\":{\"label\":[\"Shield Health Ratio\",\"Damage Reduction\",\"Cooldown\"],\"effect\":[\"{{ e5 }}% -> {{ e5NL }}%\",\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[16,15,14,13,12],\"cooldownBurn\":\"16/15/14/13/12\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[20,25,30,35,40],[2,2,2,2,2],[30,30,30,30,30],[0.5,0.5,0.5,0.5,0.5],[8,11,14,17,20],[12,12,12,12,12],[1.5,1.5,1.5,1.5,1.5],[2,2,2,2,2],[1.25,1.25,1.25,1.25,1.25],[0,0,0,0,0]],\"effectBurn\":[null,\"20/25/30/35/40\",\"2\",\"30\",\"0.5\",\"8/11/14/17/20\",\"12\",\"1.5\",\"2\",\"1.25\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[275,275,275,275,275],\"rangeBurn\":\"275\",\"image\":{\"full\":\"GalioW.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":384,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"GalioE\",\"name\":\"Justice Punch\",\"description\":\"Galio will briefly step back and charge, knocking up the first enemy champion he encounters.\",\"tooltip\":\"Galio lunges forward with a mighty blow, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemies and knocking them into the air for {{ e2 }} seconds. Galio will stop upon hitting a champion or terrain.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[14,13,12,11,10],\"cooldownBurn\":\"14/13/12/11/10\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[100,140,180,220,260],[0.75,0.75,0.75,0.75,0.75],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"100/140/180/220/260\",\"0.75\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"GalioE.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":432,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"GalioR\",\"name\":\"Hero's Entrance\",\"description\":\"Galio grants damage reduction to an ally. After a delay Galio smashes down on the ally's original location, knocking up nearby enemies.\",\"tooltip\":\"Galio designates an allied champion's current position as his landing spot and grants {{ e5 }} </span><span class=\\\"colorFF00FF\\\">(+{{ f2 }})</span>% damage reduction to that ally until he lands. <br><br>When Galio lands, enemies in the area take {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and are knocked into the air for {{ e3 }} seconds ({{ e2 }} seconds in the center).\",\"leveltip\":{\"label\":[\"Damage\",\"Damage Reduction to Ally\",\"Range\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e5 }}% -> {{ e5NL }}%\",\"{{ e8 }} -> {{ e8NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[160,140,120],\"cooldownBurn\":\"160/140/120\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[150,250,350],[1.25,1.25,1.25],[0.75,0.75,0.75],[2.5,2.5,2.5],[20,30,40],[0,0,0],[0,0,0],[4000,4750,5500],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"150/250/350\",\"1.25\",\"0.75\",\"2.5\",\"20/30/40\",\"0\",\"0\",\"4000/4750/5500\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[4000,4750,5500],\"rangeBurn\":\"4000/4750/5500\",\"image\":{\"full\":\"GalioR.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":0,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Gangplank\":{\"id\":41,\"key\":\"Gangplank\",\"name\":\"Gangplank\",\"title\":\"the Saltwater Scourge\",\"spells\":[{\"id\":\"GangplankQWrapper\",\"name\":\"Parrrley\",\"description\":\"Shoots target, plundering Gold for each enemy unit killed.\",\"tooltip\":\"Fires a bullet that deals {{ e1 }} (<span class=\\\"colorFF8C00\\\">+{{ a1 }}</span>) physical damage (can crit and applies on-hit effects).<br><br>If Parrrley kills the target, Gangplank plunders <span class=\\\"colorFFDD00\\\">{{ e2 }}</span> bonus Gold and <span class=\\\"colorFFDD00\\\">{{ e5 }}</span> <span class=\\\"colorffd700\\\">Silver Serpents</span>. (Trade <span class=\\\"colorffd700\\\">Silver Serpents</span> at the shop to upgrade Cannon Barrage).<br><br>\",\"leveltip\":{\"label\":[\"Damage\",\"Plunder Gold\",\"Plunder Silver Serpents\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ e5 }} -> {{ e5NL }}\"]},\"maxrank\":5,\"cooldown\":[5,5,5,5,5],\"cooldownBurn\":\"5\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[20,45,70,95,120],[2,3,4,5,6],[0,0,0,0,0],[500,500,500,500,500],[4,5,6,7,8],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"20/45/70/95/120\",\"2/3/4/5/6\",\"0\",\"500\",\"4/5/6/7/8\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[625,625,625,625,625],\"rangeBurn\":\"625\",\"image\":{\"full\":\"GangplankQWrapper.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":48,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"GangplankW\",\"name\":\"Remove Scurvy\",\"description\":\"Eats citrus to cure crowd control effects and restore Health.\",\"tooltip\":\"Gangplank consumes a large quantity of citrus fruit, curing all disabling effects and healing him for {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> + <span class=\\\"colorCC3300\\\">{{ e2 }}% of his missing Health</span>.\",\"leveltip\":{\"label\":[\"Healing\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[22,20,18,16,14],\"cooldownBurn\":\"22/20/18/16/14\",\"cost\":[80,90,100,110,120],\"costBurn\":\"80/90/100/110/120\",\"effect\":[null,[50,75,100,125,150],[15,15,15,15,15],[0.25,0.25,0.25,0.25,0.25],[200,200,200,200,200],[30,40,50,60,70],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/75/100/125/150\",\"15\",\"0.25\",\"200\",\"30/40/50/60/70\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.9,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[400,400,400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"GangplankW.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":96,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"GangplankE\",\"name\":\"Powder Keg\",\"description\":\"Gangplank uncovers a powder keg at target location. If he attacks it, it explodes, spreading the attack's damage to enemies in the area, slowing them.\",\"tooltip\":\"Places a powder keg that can be attacked by Gangplank or his enemies at a location for {{ e5 }} seconds.<br><br>If Gangplank destroys a keg, it explodes dealing <span class=\\\"colorFF8C00\\\">the attack's damage</span> as physical damage (ignores {{ e0 }}% Armor) to enemies and slowing them by {{ e4 }}% for {{ e2 }} seconds. Champions take {{ e3 }} bonus physical damage from the explosion.<br><br>When a keg explodes, other kegs with overlapping blast zones chain explode (damage does not stack).<br><br>Kegs health decays every {{ f5 }} seconds. (Decay rate increases at level 7 and 13)<br><br><span class=\\\"colorffd700\\\">Keg explosions apply Parrrley's plunder effect.</span>\",\"leveltip\":{\"label\":[\"Barrel Ammo Charge\",\"Slow\",\"Bonus Damage Against Champions\"],\"effect\":[\"{{ f2 }} -> {{ f3 }}\",\"{{ e4 }}% -> {{ e4NL }}%\",\"{{ e3 }} -> {{ e3NL }}\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[3,3,3,3,3],[2,2,2,2,2],[60,90,120,150,180],[40,50,60,70,80],[60,60,60,60,60],[2,2,2,2,2],[10,10,10,10,10],[100,100,100,100,100],[0.5,0.5,0.5,0.5,0.5],[40,40,40,40,40]],\"effectBurn\":[null,\"3\",\"2\",\"60/90/120/150/180\",\"40/50/60/70/80\",\"60\",\"2\",\"10\",\"100\",\"0.5\",\"40\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"3\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"GangplankE.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":144,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"GangplankR\",\"name\":\"Cannon Barrage\",\"description\":\"Gangplank signals his ship to bombard an area, slowing and damaging enemies.\",\"tooltip\":\"Signals Gangplank's ship to fire {{ f3 }} waves of cannonballs at an area over {{ e3 }} seconds. Each wave deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and slows enemies by {{ e2 }}% for {{ e6 }} seconds.<br><br><span class=\\\"colorffd700\\\">Collect Silver Serpents with Parrrley to upgrade in the shop.</span>\",\"leveltip\":{\"label\":[\"Damage Per Wave\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[180,160,140],\"cooldownBurn\":\"180/160/140\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[35,60,85],[30,30,30],[8,8,8],[2,2,2],[1.5,0.5,0.5],[0.5,0.5,0.5],[300,300,300],[60,60,60],[1,1,1],[30,30,30]],\"effectBurn\":[null,\"35/60/85\",\"30\",\"8\",\"2\",\"1.5/0.5/0.5\",\"0.5\",\"300\",\"60\",\"1\",\"30\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[30000,30000,30000],\"rangeBurn\":\"30000\",\"image\":{\"full\":\"GangplankR.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":192,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Garen\":{\"id\":86,\"key\":\"Garen\",\"name\":\"Garen\",\"title\":\"The Might of Demacia\",\"spells\":[{\"id\":\"GarenQ\",\"name\":\"Decisive Strike\",\"description\":\"Garen gains a burst of movement speed, breaking free of all slows affecting him. His next attack strikes a vital area of his foe, dealing bonus damage and silencing them.\",\"tooltip\":\"Garen breaks free from all slows affecting him, and gains {{ e2 }}% movement speed for {{ e4 }} seconds.<br><br>His next basic attack within {{ e5 }} seconds deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage and silences his target for {{ e3 }} seconds.\",\"leveltip\":{\"label\":[\"Bonus Damage\",\"Movement Speed Duration\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }} -> {{ e4NL }}\"]},\"maxrank\":5,\"cooldown\":[8,8,8,8,8],\"cooldownBurn\":\"8\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[30,55,80,105,130],[30,30,30,30,30],[1.5,1.5,1.5,1.5,1.5],[1.5,2,2.5,3,3.5],[4.5,4.5,4.5,4.5,4.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30/55/80/105/130\",\"30\",\"1.5\",\"1.5/2/2.5/3/3.5\",\"4.5\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1.4,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[300,300,300,300,300],\"rangeBurn\":\"300\",\"image\":{\"full\":\"GarenQ.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":240,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"GarenW\",\"name\":\"Courage\",\"description\":\"Garen passively increases his armor and magic resist by killing enemies. He may also activate this ability to grant himself a shield that reduces incoming damage for a short time.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> Killing units permanently grants {{ f2 }} armor and magic resist, up to a maximum of {{ e3 }}. Current Bonus: <span class=\\\"colorFFFFFF\\\">{{ f1 }}</span><br><br><span class=\\\"colorFF9900\\\">Active: </span>Garen gains a defensive shield for {{ e4 }} seconds, reducing incoming damage by {{ e2 }}%.\",\"leveltip\":{\"label\":[\"Active Duration\",\"Cooldown\"],\"effect\":[\"{{ e4 }} -> {{ e4NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[24,23,22,21,20],\"cooldownBurn\":\"24/23/22/21/20\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[0.25,0.25,0.25,0.25,0.25],[30,30,30,30,30],[30,30,30,30,30],[2,3,4,5,6],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"0.25\",\"30\",\"30\",\"2/3/4/5/6\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[20,20,20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"GarenW.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":288,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"GarenE\",\"name\":\"Judgment\",\"description\":\"Garen performs a dance of death with his sword, dealing damage around him for the duration and shredding the armor of enemy champions hit.\",\"tooltip\":\"Garen rapidly spins his sword around his body for {{ e2 }} seconds, dealing {{ f3 }} physical damage to nearby enemies -- {{ e1 }} plus {{ e3 }}% of his attack damage <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span>, <span class=\\\"colorFFFFFF\\\">{{ f2 }}</span> times (increased by 1 every 3 champion levels) -- over the duration.<br><br>Enemy champions hit by {{ e6 }} spins lose {{ e5 }}% of their armor for {{ e7 }} seconds.<br><br><span class=\\\"color99FF99\\\">Judgment deals {{ e4 }}% increased damage when striking only one enemy.<br>Cancelling Judgment returns cooldown equal to the remaining duration.<br><span class=\\\"color99FF99\\\">Judgment can critically strike for bonus damage.</span>\",\"leveltip\":{\"label\":[\"Base Damage per hit\",\"Total Attack Damage Ratio per hit\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\"]},\"maxrank\":5,\"cooldown\":[9,9,9,9,9],\"cooldownBurn\":\"9\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[14,18,22,26,30],[3,3,3,3,3],[34,35,36,37,38],[33,33,33,33,33],[25,25,25,25,25],[4,4,4,4,4],[6,6,6,6,6],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"14/18/22/26/30\",\"3\",\"34/35/36/37/38\",\"33\",\"25\",\"4\",\"6\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[325,325,325,325,325],\"rangeBurn\":\"325\",\"image\":{\"full\":\"GarenE.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":336,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"GarenR\",\"name\":\"Demacian Justice\",\"description\":\"The enemy champion with the most recent kills is the Villain. Garen's attacks deal additional true damage to that champion.<br><br>Garen can call upon the might of Demacia to deal a finishing blow to an enemy champion that deals damage based upon how much health his target has missing. This damage is true damage against the Villain.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> The enemy champion with the most recent kills is the <span class=\\\"colorFFF673\\\">Villain</span>. Judgment and basic attacks against it deal an additional {{ e3 }}% of its maximum health as true damage.<br><br><span class=\\\"colorFF9900\\\">Active:</span> Garen calls upon the might of Demacia to attempt to execute an enemy champion, dealing magic damage equal to {{ e1 }} plus {{ e2 }}% of the target's missing health. Deals true damage to the <span class=\\\"colorFFF673\\\">Villain</span>.\",\"leveltip\":{\"label\":[\"Damage\",\"Missing Health Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,100,80],\"cooldownBurn\":\"120/100/80\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[175,350,525],[28,33,40],[1,1,1],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"175/350/525\",\"28/33/40\",\"1\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"GarenR.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":384,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Gnar\":{\"id\":150,\"key\":\"Gnar\",\"name\":\"Gnar\",\"title\":\"the Missing Link\",\"spells\":[{\"id\":\"GnarQ\",\"name\":\"Boomerang Throw / Boulder Toss\",\"description\":\"Gnar throws a boomerang that damages and slows enemies it hits before returning to him. If he catches the boomerang its cooldown is reduced.<br><br>Mega Gnar instead throws a boulder that stops on the first unit hit, damaging and slowing everything nearby. It can then be picked up to reduce the cooldown.\",\"tooltip\":\"<span class=\\\"color33FF33\\\">Mini Gnar: </span>Throws a boomerang that deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage and slows enemies by {{ e3 }}% for {{ e4 }} seconds. The boomerang returns towards Gnar after hitting an enemy, dealing {{ e7 }}% damage to subsequent targets. Each enemy can only be hit once. Catching the boomerang reduces its cooldown by {{ f1 }}%.<br><br><span class=\\\"colorFF3300\\\">Mega Gnar: </span>Throws a boulder that stops when it hits an enemy, slowing all nearby enemies and dealing {{ e2 }} <span class=\\\"colorFF8C00\\\">(+{{ a2 }})</span> physical damage. Picking up the boulder reduces its cooldown by {{ e6 }}%.\",\"leveltip\":{\"label\":[\"Boomerang Damage\",\"Boulder Damage\",\"Slow Amount\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[20,17.5,15,12.5,10],\"cooldownBurn\":\"20/17.5/15/12.5/10\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[5,35,65,95,125],[5,45,85,125,165],[15,20,25,30,35],[2,2,2,2,2],[3000,3000,3000,3000,3000],[60,60,60,60,60],[50,50,50,50,50],[200,200,200,200,200],[6,6,6,6,6],[280,280,280,280,280]],\"effectBurn\":[null,\"5/35/65/95/125\",\"5/45/85/125/165\",\"15/20/25/30/35\",\"2\",\"3000\",\"60\",\"50\",\"200\",\"6\",\"280\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1.15,\"key\":\"a1\"},{\"link\":\"attackdamage\",\"coeff\":1.2,\"key\":\"a2\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[1100,1100,1100,1100,1100],\"rangeBurn\":\"1100\",\"image\":{\"full\":\"GnarQ.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":432,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"GnarW\",\"name\":\"Hyper / Wallop\",\"description\":\"Gnar's attacks and spells hype him up, dealing bonus damage and granting him Movement Speed.<br><br>Mega Gnar is too enraged to be hyper and instead can rear up on his hind legs and smash down on the area in front of him, stunning enemies in an area.\",\"tooltip\":\"<span class=\\\"color33FF33\\\">Mini Gnar: </span><span class=\\\"colorFF9900\\\">Passive:</span> Every 3rd attack or spell on the same target deals an additional {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }}) </span>+ {{ e2 }}% of the target's max Health as magic damage and grants Gnar {{ f1 }}% Movement Speed that decays over 3 seconds (max {{ e4 }} damage vs. monsters). <br><br><span class=\\\"colorFF3300\\\">Mega Gnar: </span>Stuns enemies in an area for {{ e5 }} seconds, dealing {{ e3 }}<span class=\\\"colorFF8C00\\\"> (+{{ a2 }})</span> physical damage.<br><br>Gnar gains Hyper's Movement Speed bonus when he leaves Mega form.\",\"leveltip\":{\"label\":[\"Hyper Damage\",\"Hyper % Health Damage\",\"Hyper Max Damage to monsters\",\"Wallop Damage\",\"Wallop Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[15,13,11,9,7],\"cooldownBurn\":\"15/13/11/9/7\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[10,20,30,40,50],[6,8,10,12,14],[25,45,65,85,105],[100,150,200,250,300],[1.25,1.25,1.25,1.25,1.25],[3.5,3.5,3.5,3.5,3.5],[3,3,3,3,3],[550,550,550,550,550],[200,200,200,200,200],[0,0,0,0,0]],\"effectBurn\":[null,\"10/20/30/40/50\",\"6/8/10/12/14\",\"25/45/65/85/105\",\"100/150/200/250/300\",\"1.25\",\"3.5\",\"3\",\"550\",\"200\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"},{\"link\":\"attackdamage\",\"coeff\":1,\"key\":\"a2\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[0,0,0,0,0],\"rangeBurn\":\"0\",\"image\":{\"full\":\"GnarW.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":0,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"GnarE\",\"name\":\"Hop / Crunch\",\"description\":\"Gnar leaps to a location and bounces off the head of any unit he lands on, traveling further.<br><br>Mega Gnar is too large to bounce and instead lands with earth-shattering force, dealing damage in an area around him.\",\"tooltip\":\"<span class=\\\"color33FF33\\\">Mini Gnar: </span>Leaps to a location, gaining {{ e2 }}% Attack Speed for {{ e3 }} seconds. If Gnar lands on a unit he will bounce off it, traveling further. Deals {{ e1 }} <span class=\\\"colorCC3300\\\">(+{{ f1 }}) [6% of Gnar's Max Health]</span> physical damage and slows briefly if the unit landed on was an enemy.<br><br><span class=\\\"colorFF3300\\\">Mega Gnar: </span>Leaps to a location and deals {{ e1 }} <span class=\\\"colorCC3300\\\">(+{{ f1 }}) [6% of Gnar's Max Health]</span> physical damage to nearby enemies on landing. Enemies Gnar lands directly on top of are slowed briefly.<br><br>If Crunch is used to transform, Gnar will still be able to bounce.\",\"leveltip\":{\"label\":[\"Damage\",\"Attack Speed\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[22,19.5,17,14.5,12],\"cooldownBurn\":\"22/19.5/17/14.5/12\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[20,60,100,140,180],[20,30,40,50,60],[3,3,3,3,3],[-0.8,-0.8,-0.8,-0.8,-0.8],[0.5,0.5,0.5,0.5,0.5],[475,475,475,475,475],[525,525,525,525,525],[200,200,200,200,200],[0.6,0.6,0.6,0.6,0.6],[375,375,375,375,375]],\"effectBurn\":[null,\"20/60/100/140/180\",\"20/30/40/50/60\",\"3\",\"-0.8\",\"0.5\",\"475\",\"525\",\"200\",\"0.6\",\"375\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[475,475,475,475,475],\"rangeBurn\":\"475\",\"image\":{\"full\":\"GnarE.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":48,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"GnarR\",\"name\":\"GNAR!\",\"description\":\"Mega Gnar throws everything around him in a chosen direction, dealing damage and slowing them.  Any enemy that hits a wall is stunned and takes bonus damage.\",\"tooltip\":\"<span class=\\\"color33FF33\\\">Mini Gnar: </span><span class=\\\"colorFF9900\\\">Passive:</span> Increases Boomerang Throw's cooldown reduction on catch to {{ e4 }}% and Hyper's Movement Speed bonus to {{ e2 }}%.<br><br><span class=\\\"colorFF3300\\\">Mega Gnar: </span>Knocks nearby enemies in the specified direction, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> physical damage and slowing them by {{ e8 }}% for {{ e3 }} seconds. Enemies that hit a wall take {{ e7 }}% damage and are stunned instead of slowed.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow/Stun Duration\",\"Boomerang Cooldown Reduction\",\"Hyper Movement Speed\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ e4 }}% -> {{ e4NL }}%\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,100,80],\"cooldownBurn\":\"120/100/80\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[200,300,400],[45,60,75],[1.25,1.5,1.75],[50,55,60],[475,475,475],[500,500,500],[150,150,150],[45,45,45],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"200/300/400\",\"45/60/75\",\"1.25/1.5/1.75\",\"50/55/60\",\"475\",\"500\",\"150\",\"45\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.2,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a2\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[590,590,590],\"rangeBurn\":\"590\",\"image\":{\"full\":\"GnarR.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":96,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Gragas\":{\"id\":79,\"key\":\"Gragas\",\"name\":\"Gragas\",\"title\":\"the Rabble Rouser\",\"spells\":[{\"id\":\"GragasQ\",\"name\":\"Barrel Roll\",\"description\":\"Gragas rolls his cask to a location, which can be activated to explode or will explode on its own after 4 seconds. Enemies struck by the blast have their Movement Speed slowed.\",\"tooltip\":\"Gragas rolls his cask to a location. When reactivated, or after {{ e4 }} seconds, the cask will explode, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to nearby enemies and slowing their movement speed by {{ e2 }}% for {{ e3 }} seconds.<br><br>The damage and slow amount increase as the cask ferments, up to {{ e6 }}% after {{ e5 }} seconds. Deals {{ e7 }}% damage to minions.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow %\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[11,10,9,8,7],\"cooldownBurn\":\"11/10/9/8/7\",\"cost\":[60,65,70,75,80],\"costBurn\":\"60/65/70/75/80\",\"effect\":[null,[80,120,160,200,240],[40,45,50,55,60],[2,2,2,2,2],[4,4,4,4,4],[2,2,2,2,2],[150,150,150,150,150],[70,70,70,70,70],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/120/160/200/240\",\"40/45/50/55/60\",\"2\",\"4\",\"2\",\"150\",\"70\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[850,850,850,850,850],\"rangeBurn\":\"850\",\"image\":{\"full\":\"GragasQ.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":144,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"GragasW\",\"name\":\"Drunken Rage\",\"description\":\"Gragas guzzles down brew from his cask for 1 second. After finishing, he becomes drunkenly empowered, dealing magic damage to all nearby enemies on his next basic attack and reducing damage received.\",\"tooltip\":\"Gragas guzzles down his brew, taking {{ e1 }}% reduced damage for {{ e5 }} seconds.<br><br>After finishing his drink, his next basic attack deals magic damage to nearby enemies equal to {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> plus {{ e2 }}% of their max Health (max {{ e4 }} vs monsters).\",\"leveltip\":{\"label\":[\"Damage Reduction\",\"Base Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[8,7,6,5,4],\"cooldownBurn\":\"8/7/6/5/4\",\"cost\":[30,30,30,30,30],\"costBurn\":\"30\",\"effect\":[null,[10,12,14,16,18],[8,8,8,8,8],[20,50,80,110,140],[300,300,300,300,300],[2.5,2.5,2.5,2.5,2.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"10/12/14/16/18\",\"8\",\"20/50/80/110/140\",\"300\",\"2.5\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[20,20,20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"GragasW.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":192,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"GragasE\",\"name\":\"Body Slam\",\"description\":\"Gragas charges to a location and collides with the first enemy unit he comes across, dealing damage to all nearby enemy units and stunning them.\",\"tooltip\":\"Gragas charges forward, colliding with the first enemy unit. He deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemies in the area and bumps them back, stunning them for {{ e2 }} second.<br><br>Body Slam's Cooldown is reduced by {{ f1 }} seconds if Gragas collides with a unit.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[16,15,14,13,12],\"cooldownBurn\":\"16/15/14/13/12\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[80,130,180,230,280],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/130/180/230/280\",\"1\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"GragasE.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":240,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"GragasR\",\"name\":\"Explosive Cask\",\"description\":\"Gragas hurls his cask to a location, dealing damage and knocking back enemies caught in the blast radius.\",\"tooltip\":\"Gragas hurls his cask, causing it to explode when it lands. Enemies hit take {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and are knocked away from the explosion.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[100,90,80],\"cooldownBurn\":\"100/90/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[200,300,400],[900,900,900],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"200/300/400\",\"900\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"GragasR.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":288,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Graves\":{\"id\":104,\"key\":\"Graves\",\"name\":\"Graves\",\"title\":\"the Outlaw\",\"spells\":[{\"id\":\"GravesQLineSpell\",\"name\":\"End of the Line\",\"description\":\"Graves fires an explosive shell that detonates after 2 seconds, or 0.2 seconds if it strikes terrain.\",\"tooltip\":\"Fires a powder round that deals {{ e1 }}<span class=\\\"colorFF8C00\\\"> (+{{ f1 }})</span> physical damage to enemies in a line.<br><br>After 2 seconds or {{ e6 }} seconds on collision with terrain, the round detonates, dealing {{ e2 }}<span class=\\\"colorFF8C00\\\"> (+{{ f2 }})</span> physical damage to all nearby enemies.\",\"leveltip\":{\"label\":[\"Damage\",\"Detonation AD Ratio\",\"Mana Cost\",\"Cooldown \"],\"effect\":[\" {{ e1 }} -> {{ e1NL }} / {{ e2 }} -> {{ e2NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cost }} -> {{ costNL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[14,13,12,11,10],\"cooldownBurn\":\"14/13/12/11/10\",\"cost\":[60,70,80,90,100],\"costBurn\":\"60/70/80/90/100\",\"effect\":[null,[40,55,70,85,100],[80,110,140,170,200],[0.4,0.7,1,1.3,1.6],[0,0,0,0,0],[1,1,1,1,1],[0.2,0.2,0.2,0.2,0.2],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"40/55/70/85/100\",\"80/110/140/170/200\",\"0.4/0.7/1/1.3/1.6\",\"0\",\"1\",\"0.2\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[925,925,925,925,925],\"rangeBurn\":\"925\",\"image\":{\"full\":\"GravesQLineSpell.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":336,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"GravesSmokeGrenade\",\"name\":\"Smoke Screen\",\"description\":\"Graves fires a smoke canister at the target area creating a cloud of smoke. Enemies inside the smoke cloud have reduced sight range and Movement Speed. \",\"tooltip\":\"Creates a cloud of Black Smoke lasting 4 seconds. Enemies inside Black Smoke cannot see out.<br><br>Deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and slows by {{ e2 }}% on impact.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow\",\"Mana Cost\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cost }} -> {{ costNL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[26,24,22,20,18],\"cooldownBurn\":\"26/24/22/20/18\",\"cost\":[70,75,80,85,90],\"costBurn\":\"70/75/80/85/90\",\"effect\":[null,[60,110,160,210,260],[15,20,25,30,35],[200,200,200,200,200],[4,4,4,4,4],[1.5,1.5,1.5,1.5,1.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/110/160/210/260\",\"15/20/25/30/35\",\"200\",\"4\",\"1.5\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[950,950,950,950,950],\"rangeBurn\":\"950\",\"image\":{\"full\":\"GravesSmokeGrenade.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":384,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"GravesMove\",\"name\":\"Quickdraw\",\"description\":\"Graves dashes forward gaining an Armor boost for several seconds. If Graves dashes towards an enemy champion, gain two stacks of True Grit instead. Hitting enemies with basic attacks lowers the cooldown of this skill and refreshes the resistance boost. \",\"tooltip\":\"Dashes in a direction, reloading one shell. Graves gains <span class=\\\"colorFFF673\\\">True Grit</span> for 4 seconds. If Graves dashes towards an enemy champion, gain two stacks of <span class=\\\"colorFFF673\\\">True Grit</span> instead. <br><br>Basic attack hits lower the cooldown of Quickdraw by {{ e4 }} seconds. Damage against non-minions also refreshes <span class=\\\"colorFFF673\\\">True Grit</span>.<br><br><span class=\\\"colorFFF673\\\">True Grit</span> grants {{ e5 }} Armor (stacks up to {{ e0 }} times).\",\"leveltip\":{\"label\":[\"True Grit Armor\",\"Cooldown\"],\"effect\":[\"{{ e5 }} -> {{ e5NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[18,17,16,15,14],\"cooldownBurn\":\"18/17/16/15/14\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[30,40,50,60,70],[4,4,4,4,4],[20,25,30,35,40],[0.5,0.5,0.5,0.5,0.5],[5,7.5,10,12.5,15],[750,750,750,750,750],[375,375,375,375,375],[275,275,275,275,275],[60,60,60,60,60],[8,8,8,8,8]],\"effectBurn\":[null,\"30/40/50/60/70\",\"4\",\"20/25/30/35/40\",\"0.5\",\"5/7.5/10/12.5/15\",\"750\",\"375\",\"275\",\"60\",\"8\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[425,425,425,425,425],\"rangeBurn\":\"425\",\"image\":{\"full\":\"GravesMove.png\",\"sprite\":\"spell3.png\",\"group\":\"spell\",\"x\":432,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"GravesChargeShot\",\"name\":\"Collateral Damage\",\"description\":\"Graves fires an explosive shell dealing heavy damage to the first champion it hits. After hitting a champion or reaching the end of its range, the shell explodes dealing damage in a cone. \",\"tooltip\":\"Fires an explosive shell with such force that it knocks Graves back. The shell deals {{ e1 }}<span class=\\\"colorFF8C00\\\"> (+{{ f1 }})</span> physical damage to the first enemy hit. After hitting an enemy Champion or reaching the end of its range, the shell explodes, dealing {{ e2 }}<span class=\\\"colorFF8C00\\\"> (+{{ f2 }})</span> physical damage in a cone.\",\"leveltip\":{\"label\":[\"Primary Damage\",\"Cone Damage\",\"Cooldown \"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,100,80],\"cooldownBurn\":\"120/100/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[250,400,550],[200,320,440],[400,400,400],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"250/400/550\",\"200/320/440\",\"400\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"GravesChargeShot.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":0,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Hecarim\":{\"id\":120,\"key\":\"Hecarim\",\"name\":\"Hecarim\",\"title\":\"the Shadow of War\",\"spells\":[{\"id\":\"HecarimRapidSlash\",\"name\":\"Rampage\",\"description\":\"Hecarim cleaves nearby enemies dealing physical damage. \",\"tooltip\":\"Hecarim cleaves nearby enemies for {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage. ({{ e5 }}% damage to minions)<br><br>If Hecarim damages at least one enemy with this attack he gains a stack of Rampage, reducing the base cooldown of this skill by {{ e3 }} second for a short duration. This effect can stack up to {{ e2 }} times.\",\"leveltip\":{\"label\":[\"Damage\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[4,4,4,4,4],\"cooldownBurn\":\"4\",\"cost\":[32,34,36,38,40],\"costBurn\":\"32/34/36/38/40\",\"effect\":[null,[50,85,120,155,190],[2,2,2,2,2],[1,1,1,1,1],[3,3,3,3,3],[66,66,66,66,66],[6,6,6,6,6],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/85/120/155/190\",\"2\",\"1\",\"3\",\"66\",\"6\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.6,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[350,350,350,350,350],\"rangeBurn\":\"350\",\"image\":{\"full\":\"HecarimRapidSlash.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":48,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"HecarimW\",\"name\":\"Spirit of Dread\",\"description\":\"Hecarim deals magic damage to nearby enemies for a short duration. Hecarim gains Health equal to a percentage of any damage those enemies suffer.\",\"tooltip\":\"Hecarim deals {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage over {{ e3 }} seconds to all nearby enemies. Hecarim is healed for {{ e1 }}% of the damage these enemies take from any source.<br><br>Hecarim cannot heal more than {{ e4 }} Health from minions or monsters.\",\"leveltip\":{\"label\":[\"Damage\",\"Healing Cap\",\"Cooldown\",\"Mana Cost \"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[22,21,20,19,18],\"cooldownBurn\":\"22/21/20/19/18\",\"cost\":[50,60,70,80,90],\"costBurn\":\"50/60/70/80/90\",\"effect\":[null,[20,20,20,20,20],[80,120,160,200,240],[4,4,4,4,4],[90,120,150,180,210],[8,8,8,8,8],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"20\",\"80/120/160/200/240\",\"4\",\"90/120/150/180/210\",\"8\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[575,575,575,575,575],\"rangeBurn\":\"575\",\"image\":{\"full\":\"HecarimW.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":96,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"HecarimRamp\",\"name\":\"Devastating Charge\",\"description\":\"Hecarim gains increasing Movement Speed and can move through units for a short duration. His next attack knocks the target back and deals additional physical damage based on the distance he has traveled since activating the ability. \",\"tooltip\":\"Hecarim gains increasing Movement Speed and can move through units for {{ e5 }} seconds. His next attack knocks the target back dealing {{ e4 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> to {{ e3 }}<span class=\\\"colorFF8C00\\\"> (+{{ a2 }})</span> physical damage based on how far Hecarim has traveled during Devastating Charge (knockback distance also scales).\",\"leveltip\":{\"label\":[\"Minimum Damage\",\"Maximum Damage\",\"Cooldown\"],\"effect\":[\" {{ e4 }} -> {{ e4NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[24,22,20,18,16],\"cooldownBurn\":\"24/22/20/18/16\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[250,250,250,250,250],[450,450,450,450,450],[80,150,220,290,360],[40,75,110,145,180],[4,4,4,4,4],[0.75,0.75,0.75,0.75,0.75],[1200,1200,1200,1200,1200],[0.25,0.25,0.25,0.25,0.25],[2.5,2.5,2.5,2.5,2.5],[0,0,0,0,0]],\"effectBurn\":[null,\"250\",\"450\",\"80/150/220/290/360\",\"40/75/110/145/180\",\"4\",\"0.75\",\"1200\",\"0.25\",\"2.5\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.5,\"key\":\"a1\"},{\"link\":\"bonusattackdamage\",\"coeff\":1,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[300,300,300,300,300],\"rangeBurn\":\"300\",\"image\":{\"full\":\"HecarimRamp.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":144,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"HecarimUlt\",\"name\":\"Onslaught of Shadows\",\"description\":\"Hecarim summons spectral riders and charges forward, dealing magic damage in a line. Hecarim creates a shockwave when he finishes his charge, causing nearby enemies to flee in terror.\",\"tooltip\":\"Hecarim summons spectral riders and charges forward, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemies hit. <br><br>Hecarim releases a shockwave when he finishes his charge, causing nearby enemies to flee from him for {{ e2 }} second.<br><br><span class=\\\"color99FF99\\\">Hecarim himself will only move to the targeted location. The riders will always move the full distance.</span>\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[140,120,100],\"cooldownBurn\":\"140/120/100\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[150,250,350],[1,1,1],[50,125,200],[1100,1100,1100],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"150/250/350\",\"1\",\"50/125/200\",\"1100\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[50000,50000,50000],\"rangeBurn\":\"50000\",\"image\":{\"full\":\"HecarimUlt.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":192,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Heimerdinger\":{\"id\":74,\"key\":\"Heimerdinger\",\"name\":\"Heimerdinger\",\"title\":\"the Revered Inventor\",\"spells\":[{\"id\":\"HeimerdingerQ\",\"name\":\"H-28G Evolution Turret\",\"description\":\"Heimerdinger lays down a rapid-fire cannon turret equipped with a secondary pass-through beam attack (turrets deal half damage to towers).\",\"tooltip\":\"Places a Turret. Turret attacks prioritize Heimerdinger's targets and enemies attacking Heimerdinger. Turrets shut down if Heimerdinger moves far away. Heimerdinger generates a Turret Kit every <span class=\\\"colorFFFFFF\\\">{{ f1 }}</span> seconds and can hold {{ e3 }} Kits at once. <br><br><u><span class=\\\"colorFFFFFF\\\">H-28G Evolution Turret Stats</span></u><br><span class=\\\"colorFFDD77\\\">Health:</span> <span class=\\\"colorFFFFFF\\\">{{ f4 }}</span> <span class=\\\"color99FF99\\\">(+{{ f2 }})</span><br><span class=\\\"colorFFDD77\\\">Attack - Cannon:</span> {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Magic Damage.<br><span class=\\\"colorFFDD77\\\">Attack - Beam:</span> {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> Magic Damage every {{ e6 }} seconds.<br><span class=\\\"colorFFDD77\\\">Maximum Turrets Placed:</span> 3\",\"leveltip\":{\"label\":[\"Cannon Damage\",\"Beam Damage\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[1,1,1,1,1],\"cooldownBurn\":\"1\",\"cost\":[20,20,20,20,20],\"costBurn\":\"20\",\"effect\":[null,[6,9,12,15,18],[40,60,80,100,120],[3,3,3,3,3],[26,22,18,14,10],[900,900,900,900,900],[90,90,90,90,90],[0,0,0,0,0],[0.25,0.25,0.25,0.25,0.25],[0,0,0,0,0],[20,20,20,20,20]],\"effectBurn\":[null,\"6/9/12/15/18\",\"40/60/80/100/120\",\"3\",\"26/22/18/14/10\",\"900\",\"90\",\"0\",\"0.25\",\"0\",\"20\"],\"vars\":[{\"link\":\"@cooldownchampion\",\"coeff\":0,\"key\":\"f1\"},{\"link\":\"@text\",\"coeff\":[150,175,200,225,250,275,300,325,350,375,400,425,450,475,500,525,550,575],\"key\":\"f4\"},{\"link\":\"@cooldownchampion\",\"coeff\":0,\"key\":\"f2\"},{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.55,\"key\":\"a2\"}],\"costType\":\" Mana and  Turret Kit\",\"maxammo\":\"-1\",\"range\":[350,350,350,350,350],\"rangeBurn\":\"350\",\"image\":{\"full\":\"HeimerdingerQ.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":240,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana and  Turret Kit\"},{\"id\":\"HeimerdingerW\",\"name\":\"Hextech Micro-Rockets\",\"description\":\"Heimerdinger fires long-range rockets that converge on his cursor. \",\"tooltip\":\"Unleashes a barrage of 5 rockets that converge towards the cursor and fan out past it. Rockets deal {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Magic Damage. Additional rocket hits deal reduced damage:<br><br><span class=\\\"colorFFDD77\\\">Champions and Monsters:</span> {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> Magic Damage, max {{ e6 }} <span class=\\\"color99FF99\\\">(+{{ f2 }})</span> total damage<br><span class=\\\"colorFFDD77\\\">Minions:</span> 60% of base damage<br><br>Rocket hits against champions charge nearby turret beam attacks.\",\"leveltip\":{\"label\":[\"Cooldown\",\"Damage\",\"Maximum Damage\",\"Mana Cost\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e1 }} -> {{ e1NL }}\",\"{{ e6 }} -> {{ e6NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[11,10,9,8,7],\"cooldownBurn\":\"11/10/9/8/7\",\"cost\":[50,60,70,80,90],\"costBurn\":\"50/60/70/80/90\",\"effect\":[null,[60,90,120,150,180],[12,18,24,30,36],[25,25,25,25,25],[20,20,20,20,20],[30,30,30,30,30],[108,162,216,270,324],[5,5,5,5,5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/90/120/150/180\",\"12/18/24/30/36\",\"25\",\"20\",\"30\",\"108/162/216/270/324\",\"5\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.45,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.12,\"key\":\"a2\"},{\"link\":\"@dynamic.abilitypower\",\"coeff\":0.93,\"key\":\"f2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1325,1325,1325,1325,1325],\"rangeBurn\":\"1325\",\"image\":{\"full\":\"HeimerdingerW.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":288,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"HeimerdingerE\",\"name\":\"CH-2 Electron Storm Grenade\",\"description\":\"Heimerdinger lobs a grenade at a location, dealing damage to enemy units, as well as stunning anyone directly hit and slowing surrounding units.\",\"tooltip\":\"Hurls a grenade that deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Magic Damage to enemies and slows their Movement Speed by {{ e3 }}% for {{ e2 }} seconds. Enemies in the center of the blast are also stunned for {{ e4 }} seconds.<br><br>Hitting a champion fully charges nearby turret beams.\",\"leveltip\":{\"label\":[\"Damage\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":5,\"cooldown\":[12,12,12,12,12],\"cooldownBurn\":\"12\",\"cost\":[85,85,85,85,85],\"costBurn\":\"85\",\"effect\":[null,[60,100,140,180,220],[2,2,2,2,2],[35,35,35,35,35],[1.25,1.25,1.25,1.25,1.25],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/100/140/180/220\",\"2\",\"35\",\"1.25\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[970,970,970,970,970],\"rangeBurn\":\"970\",\"image\":{\"full\":\"HeimerdingerE.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":336,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"HeimerdingerR\",\"name\":\"UPGRADE!!!\",\"description\":\"Heimerdinger invents an upgrade, causing his next spell to have increased effects. \",\"tooltip\":\"Makes Heimerdinger's next ability free and gives it bonus effects. Reactivate to cancel. <br><br><span class=\\\"colorFFFF99\\\">H-28Q Apex Turret:</span> Places a Turret for 8 seconds that deals {{ e7 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> Magic Damage with its cannon and {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Magic Damage with its beam. It has splash damage, immunity to disables, its attacks slow by 25% for 2 seconds and it doesn't count toward the Turret limit. <br><br><span class=\\\"colorFFFF99\\\">Hextech Rocket Swarm:</span> Fires 4 waves of rockets that each deal {{ e8 }} <span class=\\\"color99FF99\\\">(+{{ f1 }})</span> Magic Damage. Champions and Monsters hit by multiple rockets take reduced damage, max {{ e5 }} <span class=\\\"color99FF99\\\">(+{{ f2 }})</span>. <br><br><span class=\\\"colorFFFF99\\\">CH-3X Lightning Grenade:</span> Throws a bouncing grenade that discharges three times, dealing {{ e0 }} <span class=\\\"color99FF99\\\">(+{{ f4 }})</span> Magic Damage. Both the stun and slow areas are larger and the slow is improved to 80%. \",\"leveltip\":{\"label\":[\"Cooldown\",\"Apex Turret Cannon Damage\",\"Turret Beam Damage\",\"Rocket Swarm Damage\",\"Rocket Swarm Max Damage\",\"Lightning Grenade Damage\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e7 }} -> {{ e7NL }}\",\"{{ e1 }} -> {{ e1NL }}\",\"{{ e8 }} -> {{ e8NL }}\",\"{{ e5 }} -> {{ e5NL }}\",\"{{ e0 }} -> {{ e0NL }}\"]},\"maxrank\":3,\"cooldown\":[100,85,70],\"cooldownBurn\":\"100/85/70\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[200,270,340],[80,80,80],[1.5,1.5,1.5],[0.12,0.12,0.12],[500,690,865],[0.45,0.45,0.45],[80,100,120],[135,180,225],[28,39,49],[150,250,350]],\"effectBurn\":[null,\"200/270/340\",\"80\",\"1.5\",\"0.12\",\"500/690/865\",\"0.45\",\"80/100/120\",\"135/180/225\",\"28/39/49\",\"150/250/350\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"},{\"link\":\"@dynamic.abilitypower\",\"coeff\":0,\"key\":\"f1\"},{\"link\":\"@dynamic.abilitypower\",\"coeff\":1.83,\"key\":\"f2\"},{\"link\":\"@dynamic.abilitypower\",\"coeff\":0.6,\"key\":\"f4\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1,1,1],\"rangeBurn\":\"1\",\"image\":{\"full\":\"HeimerdingerR.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":384,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Illaoi\":{\"id\":420,\"key\":\"Illaoi\",\"name\":\"Illaoi\",\"title\":\"the Kraken Priestess\",\"spells\":[{\"id\":\"IllaoiQ\",\"name\":\"Tentacle Smash\",\"description\":\"Increases the damage dealt by Tentacles. When activated, Illaoi smashes down a Tentacle that deals physical damage.\",\"tooltip\":\"<span class=\\\"colorFF8C00\\\">Passive:</span> <span class=\\\"color85FFAD\\\">Slam</span> damage is increased by {{ e6 }}% (<span class=\\\"colorFF8C00\\\">{{ f1 }}</span>).<br><br><span class=\\\"colorFF8C00\\\">Active:</span> Illaoi swings her idol, causing a Tentacle to <span class=\\\"color85FFAD\\\">Slam</span> forward.\",\"leveltip\":{\"label\":[\"Slam Bonus\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e6 }}% -> {{ e6NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,9,8,7,6],\"cooldownBurn\":\"10/9/8/7/6\",\"cost\":[40,45,50,55,60],\"costBurn\":\"40/45/50/55/60\",\"effect\":[null,[10,10,10,10,10],[200,200,200,200,200],[800,800,800,800,800],[10,8,6,4,2],[5,5,5,5,5],[10,15,20,25,30],[1.2,1.2,1.2,1.2,1.2],[-0.3,-0.35,-0.4,-0.45,-0.5],[1.5,1.5,1.5,1.5,1.5],[0,0,0,0,0]],\"effectBurn\":[null,\"10\",\"200\",\"800\",\"10/8/6/4/2\",\"5\",\"10/15/20/25/30\",\"1.2\",\"-0.3/-0.35/-0.4/-0.45/-0.5\",\"1.5\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[850,850,850,850,850],\"rangeBurn\":\"850\",\"image\":{\"full\":\"IllaoiQ.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":432,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"IllaoiW\",\"name\":\"Harsh Lesson\",\"description\":\"Illaoi leaps to her target, dealing physical damage and causing nearby Tentacles to also swing at the target.\",\"tooltip\":\"Illaoi leaps at her target on her next basic attack, dealing bonus physical damage equal to <span class=\\\"colorFF8C00\\\">{{ f1*100 }}%</span> of their maximum health [{{ e1 }}% + {{ f2*100 }}% per 100 attack damage].<br><br>When she strikes, nearby Tentacles will <span class=\\\"color85FFAD\\\">Slam</span> at the target.<br><br><span class=\\\"color919191\\\"><i>Harsh Lesson's bonus damage against monsters is capped at {{ e3 }} damage per hit.</i></span>\",\"leveltip\":{\"label\":[\"Base Damage\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\"]},\"maxrank\":5,\"cooldown\":[4,4,4,4,4],\"cooldownBurn\":\"4\",\"cost\":[30,30,30,30,30],\"costBurn\":\"30\",\"effect\":[null,[3,3.5,4,4.5,5],[2,2,2,2,2],[300,300,300,300,300],[6,6,6,6,6],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"3/3.5/4/4.5/5\",\"2\",\"300\",\"6\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[400,400,400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"IllaoiW.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":0,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"IllaoiE\",\"name\":\"Test of Spirit\",\"description\":\"Illaoi rips the spirit from a foe's body, forcing it to stand before her. Spirits echo a percentage of the damage they take to the original target. If killed, or if the target gets too far from the spirit, the target will become a <font color='#669900'>Vessel</font> and begin spawning Tentacles.\",\"tooltip\":\"Illaoi pulls the spirit from an enemy champion for {{ e3 }} seconds (reduced when Illaoi is attacked by the target). The spirit can be attacked, with {{ e1 }}% <span class=\\\"colorFF8C00\\\">(+{{ f5 }}%)</span> of the damage taken echoing to its owner.<br><br>If the spirit dies or the target leaves its range, the target becomes a <span class=\\\"color669900\\\">Vessel</span> for {{ e2 }} seconds and is slowed by {{ e8 }}% for {{ e5 }} seconds. <span class=\\\"color669900\\\">Vessels</span> spawn Tentacles every <span class=\\\"colorFFFFFF\\\">{{ f1 }}</span> seconds if no other Tentacles are nearby.<br><br>Tentacles will automatically <span class=\\\"color85FFAD\\\">Slam</span> at spirits and <span class=\\\"color669900\\\">Vessels</span> once every 10 seconds.\",\"leveltip\":{\"label\":[\"Damage Echo\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ f4 }} -> {{ f6 }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[35,40,45,50,55],\"costBurn\":\"35/40/45/50/55\",\"effect\":[null,[25,30,35,40,45],[12,12,12,12,12],[10,10,10,10,10],[1500,1500,1500,1500,1500],[1.5,1.5,1.5,1.5,1.5],[3,3,3,3,3],[20,18,16,14,12],[80,80,80,80,80],[8,8,8,8,8],[4,3,0,0,0]],\"effectBurn\":[null,\"25/30/35/40/45\",\"12\",\"10\",\"1500\",\"1.5\",\"3\",\"20/18/16/14/12\",\"80\",\"8\",\"4/3/0/0/0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[900,900,900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"IllaoiE.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":48,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"IllaoiR\",\"name\":\"Leap of Faith\",\"description\":\"Illaoi smashes her idol into the ground, dealing physical damage to nearby enemies. A Tentacle spawns for each enemy champion hit.\",\"tooltip\":\"Illaoi smashes her idol into the ground, dealing {{ e2 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage to nearby enemies and spawning a Tentacle for each enemy champion hit.<br><br>For the next {{ e1 }} seconds Tentacles are untargetable and will <span class=\\\"color85FFAD\\\">Slam</span> 50% faster, and Harsh Lesson has a 2 second cooldown.<br><br><span class=\\\"colorcccccc\\\"><i>'There are kind and gentle gods. Mine isn't one of those.'</i></span>\",\"leveltip\":{\"label\":[\"Base Damage\",\"Cooldown<\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,105,90],\"cooldownBurn\":\"120/105/90\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[8,8,8],[150,250,350],[1500,1500,1500],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"8\",\"150/250/350\",\"1500\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[450,450,450],\"rangeBurn\":\"450\",\"image\":{\"full\":\"IllaoiR.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":96,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Irelia\":{\"id\":39,\"key\":\"Irelia\",\"name\":\"Irelia\",\"title\":\"the Will of the Blades\",\"spells\":[{\"id\":\"IreliaGatotsu\",\"name\":\"Bladesurge\",\"description\":\"Irelia dashes forward to strike her target. If it kills the target, Bladesurge's cooldown refreshes and refunds a portion of the Mana Cost.\",\"tooltip\":\"Irelia dashes forward to strike her target, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage and applying on-hit effects.<br><br>If it kills the target, Bladesurge's cooldown is refreshed and Irelia gains {{ e2 }} Mana.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[14,12,10,8,6],\"cooldownBurn\":\"14/12/10/8/6\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[20,50,80,110,140],[35,35,35,35,35],[1400,1400,1400,1400,1400],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"20/50/80/110/140\",\"35\",\"1400\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"IreliaGatotsu.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":144,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"IreliaHitenStyle\",\"name\":\"Hiten Style\",\"description\":\"Irelia is skilled in the art of Hiten, passively giving her physical attacks Health restoration. Activating Hiten Style doubles her Health restoration and gives her basic attacks true damage.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> Basic attacks restore {{ e1 }} Health.<br><br><span class=\\\"colorFF9900\\\">Active:</span> Irelia's basic attacks deal {{ e2 }} true damage and Hiten Style restores twice as much health for {{ e3 }} seconds.\",\"leveltip\":{\"label\":[\"Health Restoration\",\"True Damage\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[15,15,15,15,15],\"cooldownBurn\":\"15\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[5,7,9,11,13],[15,30,45,60,75],[6,6,6,6,6],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"5/7/9/11/13\",\"15/30/45/60/75\",\"6\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[20,20,20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"IreliaHitenStyle.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":192,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"IreliaEquilibriumStrike\",\"name\":\"Equilibrium Strike\",\"description\":\"Irelia's attack balances the scales, dealing damage and slowing the target. However, if the target has a higher Health % than Irelia, then the blow stuns the target instead of slowing.\",\"tooltip\":\"Irelia pierces her target, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and slowing them by {{ e3 }}% for {{ e2 }} second(s).<br><br>If the target has a higher Health % than Irelia, she stuns them for the duration instead.\",\"leveltip\":{\"label\":[\"Damage\",\"Disable Duration\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ e2 }} -> {{ e2NL }}\",\" {{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[8,8,8,8,8],\"cooldownBurn\":\"8\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[80,120,160,200,240],[1,1.25,1.5,1.75,2],[60,60,60,60,60],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/120/160/200/240\",\"1/1.25/1.5/1.75/2\",\"60\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[325,325,325,325,325],\"rangeBurn\":\"325\",\"image\":{\"full\":\"IreliaEquilibriumStrike.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":240,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"IreliaTranscendentBlades\",\"name\":\"Transcendent Blades\",\"description\":\"Irelia summons 4 spirit blades that she can fling to deal physical damage and siphon life from enemies they pass through.\",\"tooltip\":\"Irelia summons 4 spirit blades that she can fling to deal {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a2 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> physical damage to enemies they pass through. She heals for {{ e3 }}% of the damage dealt against champions. ({{ e4 }}% vs. minions and monsters)\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[110,85,60],\"cooldownBurn\":\"110/85/60\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[80,120,160],[10,10,10],[25,25,25],[10,10,10],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"80/120/160\",\"10\",\"25\",\"10\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.7,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"IreliaTranscendentBlades.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":288,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Ivern\":{\"id\":427,\"key\":\"Ivern\",\"name\":\"Ivern\",\"title\":\"the Green Father\",\"spells\":[{\"id\":\"IvernQ\",\"name\":\"Rootcaller\",\"description\":\"Ivern conjures a vine, dealing damage and rooting enemy targets hit. Ivern's allies can dash to the rooted target.\",\"tooltip\":\"Ivern conjures a vine dealing {{ e5 }} <span class=\\\"color99FF99\\\">(+{{ f1 }})</span> magic damage and rooting the first enemy hit for {{ e1 }} second(s). Allies can click on rooted enemies to dash into attack range.\",\"leveltip\":{\"label\":[\"Root Duration\",\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e5 }} -> {{ e5NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[14,13,12,11,10],\"cooldownBurn\":\"14/13/12/11/10\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[1.2,1.4,1.6,1.8,2],[15,20,25,30,35],[26,23,20,17,14],[2,2,2,2,2],[80,125,170,215,260],[40,55,70,85,100],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"1.2/1.4/1.6/1.8/2\",\"15/20/25/30/35\",\"26/23/20/17/14\",\"2\",\"80/125/170/215/260\",\"40/55/70/85/100\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1075,1075,1075,1075,1075],\"rangeBurn\":\"1075\",\"image\":{\"full\":\"IvernQ.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":336,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"IvernW\",\"name\":\"Brushmaker\",\"description\":\"In brush, Ivern's attacks are ranged and deal bonus magic damage. Ivern can activate this ability to create a patch of brush.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> In brush, Ivern's attacks are ranged and deal {{ e5 }} <span class=\\\"color99FF99\\\">(+{{ f2 }})</span> bonus magic damage.<br><br><span class=\\\"colorFF9900\\\">Active:</span> Ivern grows a patch of brush for {{ e1 }} seconds. For 3 seconds the area in and around the brush is revealed.\",\"leveltip\":{\"label\":[\"Bonus Magic Damage\",\"Brush Recharge\"],\"effect\":[\"{{ e5 }} -> {{ e5NL }}\",\"{{ e4 }} -> {{ e4NL }}\"]},\"maxrank\":5,\"cooldown\":[0.5,0.5,0.5,0.5,0.5],\"cooldownBurn\":\"0.5\",\"cost\":[30,30,30,30,30],\"costBurn\":\"30\",\"effect\":[null,[30,30,30,30,30],[90,90,90,90,90],[190,190,190,190,190],[40,36,32,28,24],[20,30,40,50,60],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30\",\"90\",\"190\",\"40/36/32/28/24\",\"20/30/40/50/60\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"3\",\"range\":[1650,1650,1650,1650,1650],\"rangeBurn\":\"1650\",\"image\":{\"full\":\"IvernW.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":384,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"IvernE\",\"name\":\"Triggerseed\",\"description\":\"Ivern places a shield on an ally which explodes after a short duration slowing and damaging enemies.\",\"tooltip\":\"Ivern shields an ally, absorbing up to {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ f3 }})</span> damage. After 2 seconds, the shield bursts dealing {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ f4 }})</span> magic damage and slowing enemies by {{ e3 }}% for {{ e4 }} seconds.\",\"leveltip\":{\"label\":[\"Shield\",\"Damage\",\"Slow\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[12,11,10,9,8],\"cooldownBurn\":\"12/11/10/9/8\",\"cost\":[70,70,70,70,70],\"costBurn\":\"70\",\"effect\":[null,[70,100,130,160,190],[60,90,120,150,180],[40,45,50,55,60],[2,2,2,2,2],[1,1,1,1,1],[80,120,160,200,240],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/100/130/160/190\",\"60/90/120/150/180\",\"40/45/50/55/60\",\"2\",\"1\",\"80/120/160/200/240\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[750,750,750,750,750],\"rangeBurn\":\"750\",\"image\":{\"full\":\"IvernE.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":432,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"IvernR\",\"name\":\"Daisy!\",\"description\":\"Ivern summons his Sentinel friend Daisy to fight with him. Daisy will send out a shockwave if she attacks the same champion three times in a row.\",\"tooltip\":\"Ivern summons his sentinel friend Daisy. If Daisy attacks the same champion three times in a row, she will create a shockwave knocking enemies up for 1 second (3s cooldown). Daisy gains additional damage and defenses based on Ivern's Ability Power.<br><br>Recasting this ability will direct Daisy to a new target or position.\",\"leveltip\":{\"label\":[\"Cooldown\",\"Daisy Health\",\"Daisy Resistances\",\"Daisy Bonus Attack Speed\"],\"effect\":[\"{{ e6 }} -> {{ e6NL }}\",\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\"]},\"maxrank\":3,\"cooldown\":[160,140,120],\"cooldownBurn\":\"160/140/120\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[1250,2500,3750],[15,40,90],[30,50,70],[20,25,30],[150,250,350],[160,140,120],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"1250/2500/3750\",\"15/40/90\",\"30/50/70\",\"20/25/30\",\"150/250/350\",\"160/140/120\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[25000,25000,25000],\"rangeBurn\":\"25000\",\"image\":{\"full\":\"IvernR.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":0,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Janna\":{\"id\":40,\"key\":\"Janna\",\"name\":\"Janna\",\"title\":\"the Storm's Fury\",\"spells\":[{\"id\":\"HowlingGale\",\"name\":\"Howling Gale\",\"description\":\"By creating a localized change in pressure and temperature, Janna is able to create a small storm that grows in size with time. She can activate the spell again to release the storm. On release this storm will fly towards the direction it was cast in, dealing damage and knocking away any enemies in its path.\",\"tooltip\":\"Summons a whirlwind, which deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemies in its path and knocks them into the air for {{ e4 }} seconds.<br><br>The whirlwind can be charged for up to {{ e6 }} seconds. For each second it charges, it deals {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> bonus damage, knocks up for an additional {{ e5 }} seconds, and travels {{ e3 }}% further.<br><br>Activate again to release the whirlwind early.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Damage Per Second Charged\",\"Mana Cost\",\"Cooldown \"],\"effect\":[\"{{ e1 }} -> {{ e1NL }} \",\"{{ e2 }} -> {{ e2NL }} \",\"{{ cost }} -> {{ costNL }}\",\" {{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[14,13,12,11,10],\"cooldownBurn\":\"14/13/12/11/10\",\"cost\":[90,105,120,135,150],\"costBurn\":\"90/105/120/135/150\",\"effect\":[null,[60,85,110,135,160],[15,20,25,30,35],[35,35,35,35,35],[0.5,0.5,0.5,0.5,0.5],[0.25,0.25,0.25,0.25,0.25],[3,3,3,3,3],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/85/110/135/160\",\"15/20/25/30/35\",\"35\",\"0.5\",\"0.25\",\"3\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.35,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.1,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1700,1700,1700,1700,1700],\"rangeBurn\":\"1700\",\"image\":{\"full\":\"HowlingGale.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":48,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SowTheWind\",\"name\":\"Zephyr\",\"description\":\"Janna summons an air elemental that passively increases her Movement Speed and enables her to pass through units. She may also activate this ability to deal damage and slow an enemy's Movement Speed. The passive is lost while this ability is on cooldown.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Increases Movement Speed by {{ e1 }}% <span class=\\\"color99FF99\\\">(+{{ f1 }}%)</span> and allows movement through units.<br><br><span class=\\\"colorFF9900\\\">Active: </span>Deals {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to an enemy and slows their Movement Speed by {{ e3 }}% <span class=\\\"color99FF99\\\">(+{{ f2 }}%)</span> for {{ e4 }} seconds. Passive benefit is lost while Zephyr is on cooldown.\",\"leveltip\":{\"label\":[\"Passive Movement Speed\",\"Damage\",\"Slow %\",\"Mana Cost\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}% \",\"{{ e2 }} -> {{ e2NL }} \",\"{{ e3 }}% -> {{ e3NL }}% \",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[12,12,12,12,12],\"cooldownBurn\":\"12\",\"cost\":[40,50,60,70,80],\"costBurn\":\"40/50/60/70/80\",\"effect\":[null,[9,11,13,15,17],[60,115,170,225,280],[24,28,32,36,40],[3,3,3,3,3],[0.0002,0.0002,0.0002,0.0002,0.0002],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"9/11/13/15/17\",\"60/115/170/225/280\",\"24/28/32/36/40\",\"3\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.02,\"key\":\"f1\"},{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.06,\"key\":\"f2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"SowTheWind.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":96,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"EyeOfTheStorm\",\"name\":\"Eye Of The Storm\",\"description\":\"Janna conjures a defensive gale that shields an ally champion or turret from incoming damage and increases their Attack Damage.\",\"tooltip\":\"Shields an allied Champion or turret for {{ e3 }} seconds. The shield absorbs up to {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> damage and grants {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> Attack Damage until it breaks.\",\"leveltip\":{\"label\":[\"Shield Health\",\"Attack Damage Bonus\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }} \",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,10,10,10,10],\"cooldownBurn\":\"10\",\"cost\":[70,80,90,100,110],\"costBurn\":\"70/80/90/100/110\",\"effect\":[null,[80,120,160,200,240],[10,17.5,25,32.5,40],[5,5,5,5,5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/120/160/200/240\",\"10/17.5/25/32.5/40\",\"5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.1,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[800,800,800,800,800],\"rangeBurn\":\"800\",\"image\":{\"full\":\"EyeOfTheStorm.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":144,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"ReapTheWhirlwind\",\"name\":\"Monsoon\",\"description\":\"Janna surrounds herself in a magical storm, throwing enemies back. After the storm has settled, soothing winds heal nearby allies while the ability is active.\",\"tooltip\":\"Summons forth the might of the wind to knock surrounding enemies back and restores {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Health to nearby allies each second for {{ e3 }} seconds.\",\"leveltip\":{\"label\":[\"Heal Per Second\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[150,135,120],\"cooldownBurn\":\"150/135/120\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[100,150,200],[300,450,600],[3,3,3],[700,700,700],[875,875,875],[875,875,875],[1200,1200,1200],[10,10,10],[0.5,0.5,0.5],[0,0,0]],\"effectBurn\":[null,\"100/150/200\",\"300/450/600\",\"3\",\"700\",\"875\",\"875\",\"1200\",\"10\",\"0.5\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[725,725,725],\"rangeBurn\":\"725\",\"image\":{\"full\":\"ReapTheWhirlwind.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":192,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"JarvanIV\":{\"id\":59,\"key\":\"JarvanIV\",\"name\":\"Jarvan IV\",\"title\":\"the Exemplar of Demacia\",\"spells\":[{\"id\":\"JarvanIVDragonStrike\",\"name\":\"Dragon Strike\",\"description\":\"Jarvan IV extends his lance, dealing physical damage and lowering the Armor of enemies in its path. Additionally, this will pull Jarvan to his Demacian Standard, knocking up enemies in his path.\",\"tooltip\":\"Extends Jarvan IV's lance dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage and lowering the Armor of enemies hit by {{ e2 }}% for {{ e3 }} seconds.<br><br>If the lance contacts Demacian Standard it will pull Jarvan IV to its location, knocking up enemies in his path. This effect can be triggered even while immobilized.\",\"leveltip\":{\"label\":[\"Damage\",\"Armor Reduction\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,9,8,7,6],\"cooldownBurn\":\"10/9/8/7/6\",\"cost\":[45,50,55,60,65],\"costBurn\":\"45/50/55/60/65\",\"effect\":[null,[70,115,160,205,250],[10,14,18,22,26],[3,3,3,3,3],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/115/160/205/250\",\"10/14/18/22/26\",\"3\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1.2,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[770,770,770,770,770],\"rangeBurn\":\"770\",\"image\":{\"full\":\"JarvanIVDragonStrike.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":240,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"JarvanIVGoldenAegis\",\"name\":\"Golden Aegis\",\"description\":\"Jarvan IV calls upon the ancient kings of Demacia to shield him from harm and slow surrounding enemies.\",\"tooltip\":\"Grants a shield that absorbs {{ e1 }} (<span class=\\\"colorCC3300\\\">+{{ f1 }}</span> [{{ e6 }}% max Health] for each nearby enemy champion) damage for {{ e4 }} seconds, and slows surrounding enemies by {{ e2 }}% for {{ e5 }} seconds.\",\"leveltip\":{\"label\":[\"Shield\",\"Bonus Shield Percent Health\",\"Slow Amount\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e6 }}% -> {{ e6NL }}%\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[12,12,12,12,12],\"cooldownBurn\":\"12\",\"cost\":[30,30,30,30,30],\"costBurn\":\"30\",\"effect\":[null,[65,90,115,140,165],[15,20,25,30,35],[10,20,30,40,50],[5,5,5,5,5],[2,2,2,2,2],[2,2.5,3,3.5,4],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"65/90/115/140/165\",\"15/20/25/30/35\",\"10/20/30/40/50\",\"5\",\"2\",\"2/2.5/3/3.5/4\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[625,625,625,625,625],\"rangeBurn\":\"625\",\"image\":{\"full\":\"JarvanIVGoldenAegis.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":288,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"JarvanIVDemacianStandard\",\"name\":\"Demacian Standard\",\"description\":\"Jarvan IV carries the pride of Demacia, passively granting him bonus Attack Speed. Activating Demacian Standard allows Jarvan IV to place a Demacian flag that deals magic damage on impact and grants Attack Speed to nearby allied champions.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> Gains {{ e3 }}% Attack Speed.<br><br><span class=\\\"colorFF9900\\\">Active:</span> Throws a Demacian Standard to a nearby area dealing {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemies. The Standard lasts for {{ e4 }} seconds and grants surrounding allied champions {{ e3 }}% Attack Speed.\",\"leveltip\":{\"label\":[\"Damage\",\"Passive Attack Speed\",\"Active Attack Speed\",\"Cooldown\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\" {{ e3 }}% -> {{ e3NL }}%\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[13,12.5,12,11.5,11],\"cooldownBurn\":\"13/12.5/12/11.5/11\",\"cost\":[55,55,55,55,55],\"costBurn\":\"55\",\"effect\":[null,[10,13,16,19,22],[60,105,150,195,240],[15,17.5,20,22.5,25],[8,8,8,8,8],[0.15,0.025,0.025,0.025,0.025],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"10/13/16/19/22\",\"60/105/150/195/240\",\"15/17.5/20/22.5/25\",\"8\",\"0.15/0.025/0.025/0.025/0.025\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[860,860,860,860,860],\"rangeBurn\":\"860\",\"image\":{\"full\":\"JarvanIVDemacianStandard.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":336,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"JarvanIVCataclysm\",\"name\":\"Cataclysm\",\"description\":\"Jarvan IV heroically leaps into battle at a target with such force that he terraforms the surrounding area to create an arena around them. Nearby enemies are damaged at the moment of impact.\",\"tooltip\":\"Heroically leaps to an enemy Champion dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage to nearby enemies and creating an arena of impassable terrain around them for {{ e2 }} seconds.<br><br>Activate again to collapse the terrain.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,105,90],\"cooldownBurn\":\"120/105/90\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[200,325,450],[3.5,3.5,3.5],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"200/325/450\",\"3.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"JarvanIVCataclysm.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":384,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Jax\":{\"id\":24,\"key\":\"Jax\",\"name\":\"Jax\",\"title\":\"Grandmaster at Arms\",\"spells\":[{\"id\":\"JaxLeapStrike\",\"name\":\"Leap Strike\",\"description\":\"Jax leaps toward a unit. If they are an enemy, he strikes them with his weapon.\",\"tooltip\":\"Jax leaps to target unit, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> physical damage if it is an enemy.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[10,9,8,7,6],\"cooldownBurn\":\"10/9/8/7/6\",\"cost\":[65,65,65,65,65],\"costBurn\":\"65\",\"effect\":[null,[70,110,150,190,230],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/110/150/190/230\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1,\"key\":\"f1\"},{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[700,700,700,700,700],\"rangeBurn\":\"700\",\"image\":{\"full\":\"JaxLeapStrike.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":432,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"JaxEmpowerTwo\",\"name\":\"Empower\",\"description\":\"Jax charges his weapon with energy, causing his next attack to deal additional damage.\",\"tooltip\":\"Jax charges his weapon with energy, causing his next basic attack or Leap Strike to deal an additional {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }}) </span>Magic Damage.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\" {{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[7,6,5,4,3],\"cooldownBurn\":\"7/6/5/4/3\",\"cost\":[30,30,30,30,30],\"costBurn\":\"30\",\"effect\":[null,[40,75,110,145,180],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"40/75/110/145/180\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[300,300,300,300,300],\"rangeBurn\":\"300\",\"image\":{\"full\":\"JaxEmpowerTwo.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":0,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"JaxCounterStrike\",\"name\":\"Counter Strike\",\"description\":\"Jax's combat prowess allows him to dodge all incoming attacks for a short duration and then quickly counterattack, stunning all surrounding enemies.\",\"tooltip\":\"Jax enters a defensive stance for up to {{ e6 }} seconds, dodging all incoming basic attacks and taking {{ e3 }}% less damage from area of effect abilities.<br><br>After 2 seconds or if activated again, Jax stuns surrounding enemies for {{ e2 }} second and deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f2 }})</span> physical damage to them.<br><br><span class=\\\"color99FF99\\\">Counter Strike deals {{ e5 }}% more damage for each attack Jax dodged (max: {{ e4 }}% increased damage).</span>\",\"leveltip\":{\"label\":[\"Cooldown\",\"Damage\",\"Mana Cost\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e1 }} -> {{ e1NL }}\",\"{{ cost }} -> {{ costNL }} \"]},\"maxrank\":5,\"cooldown\":[16,14,12,10,8],\"cooldownBurn\":\"16/14/12/10/8\",\"cost\":[50,60,70,80,90],\"costBurn\":\"50/60/70/80/90\",\"effect\":[null,[50,75,100,125,150],[1,1,1,1,1],[25,25,25,25,25],[100,100,100,100,100],[20,20,20,20,20],[2,2,2,2,2],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/75/100/125/150\",\"1\",\"25\",\"100\",\"20\",\"2\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.5,\"key\":\"f2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[300,300,300,300,300],\"rangeBurn\":\"300\",\"image\":{\"full\":\"JaxCounterStrike.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":48,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"JaxRelentlessAssault\",\"name\":\"Grandmaster's Might\",\"description\":\"Every third consecutive attack deals additional Magic Damage. Additionally, Jax can activate this ability to strengthen his resolve, increasing his Armor and Magic Resist for a short duration.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> Every third consecutive strike Jax deals {{ e1 }}<span class=\\\"color99FF99\\\"> (+{{ a1 }}) </span>additional Magic Damage.<br><br><span class=\\\"colorFF9900\\\">Active:</span> Jax strengthens his resolve, granting him <span class=\\\"colorFF8C00\\\">{{ f2 }}</span> Armor and <span class=\\\"color99FF99\\\">{{ f1 }}</span> Magic Resist for {{ e5 }} seconds.<span class=\\\"colorFF8C00\\\"><br><br>Armor bonus is equal to {{ e3 }} + {{ e6 }}% bonus Attack Damage.</span><span class=\\\"color99FF99\\\"><br>Magic Resist bonus is equal to {{ e3 }} + {{ e7 }}% Ability Power.</span>\",\"leveltip\":{\"label\":[\"Passive Damage\",\"Armor Bonus\",\"Magic Resist Bonus\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ e3 }} -> {{ e3NL }}\"]},\"maxrank\":3,\"cooldown\":[80,80,80],\"cooldownBurn\":\"80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[100,160,220],[3,3,3],[30,50,70],[6,8,10],[8,8,8],[50,50,50],[20,20,20],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"100/160/220\",\"3\",\"30/50/70\",\"6/8/10\",\"8\",\"50\",\"20\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"},{\"link\":\"@special.jaxrarmor\",\"coeff\":0.3,\"key\":\"f2\"},{\"link\":\"@special.jaxrmr\",\"coeff\":0.2,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[100,100,100],\"rangeBurn\":\"100\",\"image\":{\"full\":\"JaxRelentlessAssault.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":96,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Jayce\":{\"id\":126,\"key\":\"Jayce\",\"name\":\"Jayce\",\"title\":\"the Defender of Tomorrow\",\"spells\":[{\"id\":\"JayceToTheSkies\",\"name\":\"To the Skies! / Shock Blast\",\"description\":\"Hammer Stance: Leaps to an enemy dealing physical damage and slowing enemies.<br><br>Cannon Stance: Fires an orb of electricity that detonates upon hitting an enemy (or reaching the end of its path) dealing physical damage to all enemies hit.\",\"tooltip\":\"Leaps to an enemy dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage to nearby enemies and slowing them by {{ e3 }}% for {{ e5 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Slow\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e3 }}% -> {{ e3NL }}%\"]},\"maxrank\":6,\"cooldown\":[16,14,12,10,8,6],\"cooldownBurn\":\"16/14/12/10/8/6\",\"cost\":[40,40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[35,70,105,140,175,210],[1.5,1.5,1.5,1.5,1.5,2],[30,35,40,45,50,55],[20,40,60,80,0,0],[2,2,2,2,2,2],[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0]],\"effectBurn\":[null,\"35/70/105/140/175/210\",\"1.5/1.5/1.5/1.5/1.5/2\",\"30/35/40/45/50/55\",\"20/40/60/80/0/0\",\"2\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1.2,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"JayceToTheSkies.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":144,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"JayceStaticField\",\"name\":\"Lightning Field / Hyper Charge\",\"description\":\"Hammer Stance: Passive: Restores Mana per strike. Active: Creates a field of lightning damaging nearby enemies for several seconds.<br><br>Cannon Stance: Gains a burst of energy, increasing Attack Speed to maximum for several attacks.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> Gains {{ e5 }} Mana per strike while in Hammer Stance.<br><br><span class=\\\"colorFF9900\\\">Active:</span> Creates an electric aura dealing {{ e4 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage over {{ e2 }} seconds to nearby enemies.\",\"leveltip\":{\"label\":[\"Damage\",\"Mana Return\"],\"effect\":[\"{{ e4 }} -> {{ e4NL }}\",\"{{ e5 }} -> {{ e5NL }}\"]},\"maxrank\":6,\"cooldown\":[10,10,10,10,10,10],\"cooldownBurn\":\"10\",\"cost\":[40,40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[25,35,45,55,65,75],[4,4,4,4,4,4],[25,30,35,40,45,50],[100,160,220,280,340,400],[6,8,10,12,14,16],[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0]],\"effectBurn\":[null,\"25/35/45/55/65/75\",\"4\",\"25/30/35/40/45/50\",\"100/160/220/280/340/400\",\"6/8/10/12/14/16\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[285,285,285,285,285,285],\"rangeBurn\":\"285\",\"image\":{\"full\":\"JayceStaticField.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":192,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"JayceThunderingBlow\",\"name\":\"Thundering Blow / Acceleration Gate\",\"description\":\"Hammer Stance: Deals magic damage to an enemy and knocks them back a short distance.<br><br>Cannon Stance: Deploys an Acceleration Gate increasing the Movement Speed of all allied champions who pass through it. If Shock Blast is fired through the gate the missile speed, range, and damage will increase.\",\"tooltip\":\"Deals {{ e4 }}% of the target's maximum health <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> as magic damage and knocks them back a short distance. ({{ e1 }} Maximum Damage against monsters)\",\"leveltip\":{\"label\":[\"Maximum Health %\",\"Cooldown\",\"Maximum Damage against monsters\"],\"effect\":[\"{{ e4 }}% -> {{ e4NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":6,\"cooldown\":[15,14,13,12,11,10],\"cooldownBurn\":\"15/14/13/12/11/10\",\"cost\":[55,55,55,55,55,55],\"costBurn\":\"55\",\"effect\":[null,[200,300,400,500,600,700],[0.25,0.25,0.25,0.25,0.25,0.25],[10,20,30,40,50,60],[8,10.4,12.8,15.2,17.6,20],[40,70,100,130,160,190],[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0]],\"effectBurn\":[null,\"200/300/400/500/600/700\",\"0.25\",\"10/20/30/40/50/60\",\"8/10.4/12.8/15.2/17.6/20\",\"40/70/100/130/160/190\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[240,240,240,240,240,240],\"rangeBurn\":\"240\",\"image\":{\"full\":\"JayceThunderingBlow.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":240,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"JayceStanceHtG\",\"name\":\"Mercury Cannon / Mercury Hammer\",\"description\":\"Hammer Stance: Transforms the Mercury Hammer into the Mercury Cannon gaining new abilities and increased range. The first attack in this form reduces the target's Armor and Magic Resist.<br><br>Cannon Stance: Transforms the Mercury Cannon into the Mercury Hammer gaining new abilities and increasing Armor and Magic Resist. The first attack in this form deals additional magic damage.\",\"tooltip\":\"<span class=\\\"colorFF8C00\\\">Active: </span>Transforms the Mercury Hammer into the Mercury Cannon gaining new abilities and ranged attacks.<br><br>The next attack in Cannon Stance reduces the target's Armor and Magic Resist by {{ f3 }}% for {{ e3 }} seconds.\",\"maxrank\":1,\"cooldown\":[6],\"cooldownBurn\":\"6\",\"cost\":[0],\"costBurn\":\"0\",\"effect\":[null,[5],[10],[5],[5],[20],[0],[0],[0],[0],[0]],\"effectBurn\":[null,\"5\",\"10\",\"5\",\"5\",\"20\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"JayceStanceHtG.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":288,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Jhin\":{\"id\":202,\"key\":\"Jhin\",\"name\":\"Jhin\",\"title\":\"the Virtuoso\",\"spells\":[{\"id\":\"JhinQ\",\"name\":\"Dancing Grenade\",\"description\":\"Jhin launches a magical cartridge at an enemy. It can hit up to four targets and gains damage each time it kills.\",\"tooltip\":\"Jhin launches a cartridge at the targeted enemy that deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }}) </span>physical damage before bouncing to a nearby target that has not yet been hit.<br><br>The cartridge can hit a maximum of 4 times. Each kill by the cartridge increases the damage of subsequent hits by {{ e2 }}%.\",\"leveltip\":{\"label\":[\"Damage\",\"Total AD Ratio\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[7,6.5,6,5.5,5],\"cooldownBurn\":\"7/6.5/6/5.5/5\",\"cost\":[40,45,50,55,60],\"costBurn\":\"40/45/50/55/60\",\"effect\":[null,[50,75,100,125,150],[35,35,35,35,35],[0.3,0.35,0.4,0.45,0.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/75/100/125/150\",\"35\",\"0.3/0.35/0.4/0.45/0.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[550,550,550,550,550],\"rangeBurn\":\"550\",\"image\":{\"full\":\"JhinQ.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":336,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"JhinW\",\"name\":\"Deadly Flourish\",\"description\":\"Jhin brandishes his cane, firing a single shot with incredible range. It pierces through minions and monsters, but stops on the first champion hit. If the target was recently struck by Jhin's allies, lotus traps, or basic attacks, they are rooted.\",\"tooltip\":\"Jhin fires a long range shot that stops on the first champion hit, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage to it, and {{ e4 }}% of that damage to minions and monsters hit along the way.<br><br>If Deadly Flourish strikes a champion that has been hit by one of Jhin's basic attacks, Lotus Traps, or allies within the last 4 seconds it will root them for {{ e2 }} seconds and grants Jhin movement speed as though he had <span class=\\\"colorFF5802\\\">crit</span> them.\",\"leveltip\":{\"label\":[\"Damage\",\"Root Duration\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[14,14,14,14,14],\"cooldownBurn\":\"14\",\"cost\":[50,60,70,80,90],\"costBurn\":\"50/60/70/80/90\",\"effect\":[null,[50,85,120,155,190],[0.75,1,1.25,1.5,1.75],[0,0,0,0,0],[75,75,75,75,75],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/85/120/155/190\",\"0.75/1/1.25/1.5/1.75\",\"0\",\"75\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[3000,3000,3000,3000,3000],\"rangeBurn\":\"3000\",\"image\":{\"full\":\"JhinW.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":384,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"JhinE\",\"name\":\"Captive Audience\",\"description\":\"Jhin places an invisible lotus trap that blooms when walked over. It slows nearby enemies before dealing damage with an explosion of serrated petals. <br><br><font color='#FFFFFF'>Beauty in Death -</font> When Jhin kills an enemy champion, a lotus trap will bloom near their corpse.\",\"tooltip\":\"Jhin places an invisible Lotus Trap that lasts {{ e5 }} minutes and activates when stepped on, revealing nearby enemies for 4 seconds.<br><br>The trap leaves behind a zone that slows enemies inside by {{ e7 }}% and detonates after 2 seconds, dealing {{ e2 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage ({{ e1 }}% damage on subsequent hits and vs. minions and monsters).<br><br>Jhin prepares a new trap every <span class=\\\"colorFFFFFF\\\">{{ f1 }}</span> seconds and can store 2 at once. <br><br><span class=\\\"colorFFFFFF\\\">Beauty in Death -</span> When Jhin kills an enemy champion a Lotus Trap will spawn and detonate where they were killed.\",\"leveltip\":{\"label\":[\"Damage\",\"Recharge Time\",\"Mana Cost\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ f1 }} -> {{ f2 }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[2,2,2,2,2],\"cooldownBurn\":\"2\",\"cost\":[30,35,40,45,50],\"costBurn\":\"30/35/40/45/50\",\"effect\":[null,[65,65,65,65,65],[20,80,140,200,260],[0,0,0,0,0],[0.75,0.75,0.75,0.75,0.75],[2,2,2,2,2],[0,0,0,0,0],[35,35,35,35,35],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"65\",\"20/80/140/200/260\",\"0\",\"0.75\",\"2\",\"0\",\"35\",\"1\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1.2,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"2\",\"range\":[750,750,750,750,750],\"rangeBurn\":\"750\",\"image\":{\"full\":\"JhinE.png\",\"sprite\":\"spell4.png\",\"group\":\"spell\",\"x\":432,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"JhinR\",\"name\":\"Curtain Call\",\"description\":\"Jhin channels, transforming Whisper into a shoulder-mounted mega-cannon. It is able to fire 4 super shots at extreme range that pierce through minions and monsters, but stop on the first champion impacted. Whisper cripples enemies hit, which slows them and deals execute damage. The 4th shot is perfectly crafted, epically powerful, and guaranteed to critically strike.\",\"tooltip\":\"Jhin sets up and channels, enabling him to fire 4 super shots at extreme range in a cone in front of him. The shots stop on the first champion hit, slowing it by {{ e3 }}% for {{ e4 }} seconds and dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage, increased by {{ e5 }}% for each 1% health the target is missing (up to {{ f2 }} <span class=\\\"colorFF8C00\\\">(+{{ f3 }})</span>). The 4th shot <span class=\\\"colorFF5802\\\">crits</span> for <span class=\\\"colorFFFFFF\\\">{{ f1 }}%</span> damage.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,105,90],\"cooldownBurn\":\"120/105/90\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[40,100,160],[200,200,200],[80,80,80],[0.5,0.5,0.5],[2.5,2.5,2.5],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"40/100/160\",\"200\",\"80\",\"0.5\",\"2.5\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":0.2,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[25000,25000,25000],\"rangeBurn\":\"25000\",\"image\":{\"full\":\"JhinR.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":0,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Jinx\":{\"id\":222,\"key\":\"Jinx\",\"name\":\"Jinx\",\"title\":\"the Loose Cannon\",\"spells\":[{\"id\":\"JinxQ\",\"name\":\"Switcheroo!\",\"description\":\"Jinx modifies her basic attacks by swapping between Pow-Pow, her minigun and Fishbones, her rocket launcher. Attacks with Pow-Pow grant Attack Speed, while attacks with Fishbones deal area of effect damage, gain increased range, and drain Mana.\",\"tooltip\":\"Jinx swaps weapons.<br><br><span class=\\\"colorFF9900\\\">Fishbones, the Rocket Launcher:</span> Basic attacks deal 110% Damage to the target and nearby enemies, gain {{ e3 }} range, drain Mana, and scale {{ e5 }}% less with bonus Attack Speed.<br><br><span class=\\\"colorFF9900\\\">Pow-Pow, the Minigun:</span> Basic attacks grant bonus Attack Speed for 2.5 seconds. This effect stacks up to 3 times for a total bonus of <span class=\\\"colorFFFFFF\\\">{{ f4 }}%</span> (bonus scales with spell rank and Jinx's level). Stacks fall off one at a time and only benefit Jinx's first attack after switching to Rocket Launcher.\",\"leveltip\":{\"label\":[\"Rocket Bonus Range\",\"Minigun Total Attack Speed\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ f4 }}% -> {{ f5 }}%\"]},\"maxrank\":5,\"cooldown\":[1,1,1,1,1],\"cooldownBurn\":\"1\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[30,40,50,60,70],[100,100,100,100,100],[75,100,125,150,175],[20,20,20,20,20],[25,25,25,25,25],[33.25,33.25,33.25,33.25,33.25],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30/40/50/60/70\",\"100\",\"75/100/125/150/175\",\"20\",\"25\",\"33.25\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana per Rocket\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"JinxQ.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":48,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ e4 }} Mana per Rocket\"},{\"id\":\"JinxW\",\"name\":\"Zap!\",\"description\":\"Jinx uses Zapper, her shock pistol, to fire a blast that deals damage to the first enemy hit, slowing and revealing it.\",\"tooltip\":\"Jinx fires a shock blast that deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> Physical Damage to the first enemy hit, revealing it and slowing it by {{ e2 }}% for 2 seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,9,8,7,6],\"cooldownBurn\":\"10/9/8/7/6\",\"cost\":[50,60,70,80,90],\"costBurn\":\"50/60/70/80/90\",\"effect\":[null,[10,60,110,160,210],[30,40,50,60,70],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"10/60/110/160/210\",\"30/40/50/60/70\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1.4,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1450,1450,1450,1450,1450],\"rangeBurn\":\"1450\",\"image\":{\"full\":\"JinxW.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":96,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"JinxE\",\"name\":\"Flame Chompers!\",\"description\":\"Jinx throws out a line of snare grenades that explode after 5 seconds, lighting enemies on fire. Flame Chompers will bite enemy champions who walk over them, rooting them in place.\",\"tooltip\":\"Jinx tosses out 3 chompers that, once armed, explode on contact with enemy champions, rooting the target for 1.5 seconds and dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Magic Damage over 1.5 seconds to nearby enemies. Chompers last for 5 seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[24,20.5,17,13.5,10],\"cooldownBurn\":\"24/20.5/17/13.5/10\",\"cost\":[70,70,70,70,70],\"costBurn\":\"70\",\"effect\":[null,[70,120,170,220,270],[100,140,180,220,260],[30,35,40,45,50],[35,50,65,80,95],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/120/170/220/270\",\"100/140/180/220/260\",\"30/35/40/45/50\",\"35/50/65/80/95\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[900,900,900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"JinxE.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":144,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"JinxR\",\"name\":\"Super Mega Death Rocket!\",\"description\":\"Jinx fires a super rocket across the map that gains damage as it travels. The rocket will explode upon colliding with an enemy champion, dealing damage to it and surrounding enemies based on their missing Health. \",\"tooltip\":\"Jinx fires a rocket that gains damage over the first second it travels. It explodes on the first enemy champion hit, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> to {{ e2 }} <span class=\\\"colorFF8C00\\\">(+{{ a2 }})</span> Physical Damage plus {{ e3 }}% of the target's missing Health. Nearby enemies take 80% Damage.\",\"leveltip\":{\"label\":[\"Minimum Damage\",\"Maximum Damage\",\"Percent Missing Health Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[90,75,60],\"cooldownBurn\":\"90/75/60\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[25,35,45],[250,350,450],[25,30,35],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"25/35/45\",\"250/350/450\",\"25/30/35\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.15,\"key\":\"a1\"},{\"link\":\"bonusattackdamage\",\"coeff\":1.5,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[25000,25000,25000],\"rangeBurn\":\"25000\",\"image\":{\"full\":\"JinxR.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":192,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Kalista\":{\"id\":429,\"key\":\"Kalista\",\"name\":\"Kalista\",\"title\":\"the Spear of Vengeance\",\"spells\":[{\"id\":\"KalistaMysticShot\",\"name\":\"Pierce\",\"description\":\"Throw a fast moving spear that passes through enemies it kills.\",\"tooltip\":\"Hurl a fast but narrow spear that deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage. Triggers <span class=\\\"colorFFFFFF\\\"><i>Martial Poise</i> (P)</span>, <span class=\\\"colorFFFFFF\\\"><i>Sentinel</i> (W)</span>, and <span class=\\\"colorFFFFFF\\\"><i>Rend</i> (E)</span>.<br><br><span class=\\\"colorFFFF00\\\">If it kills a target, </span><span class=\\\"colorFFFFFF\\\"><i>Pierce</i></span><span class=\\\"colorFFFF00\\\"> continues onward, passing all stacks of <span class=\\\"colorFFFFFF\\\"><i>Rend</i></span> to the next target.</span>\",\"leveltip\":{\"label\":[\"Mana Cost\",\"Damage\"],\"effect\":[\"{{ cost }} -> {{ costNL }}\",\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":5,\"cooldown\":[8,8,8,8,8],\"cooldownBurn\":\"8\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[10,70,130,190,250],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"10/70/130/190/250\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1150,1150,1150,1150,1150],\"rangeBurn\":\"1150\",\"image\":{\"full\":\"KalistaMysticShot.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":240,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"KalistaW\",\"name\":\"Sentinel\",\"description\":\"Passively gain Attack Speed when near her Oathsworn ally, and deal bonus damage when striking the same target. <br><br>Activate to send a soul to scout out the path, revealing the area in front of it.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>When Kalista is near her <i>Oathsworn</i>, she gains <span class=\\\"colorFFFFFF\\\">+{{ f1 }}%</span> Attack Speed. If they both basic attack the same target, she deals {{ e2 }}% of their max health as magic damage. {{ e7 }} second cooldown per target.<br><br><span class=\\\"colorFF9900\\\">Active: </span>Send a soul Sentinel to patrol an unseen area. Champions spotted are revealed for 4 seconds. Sentinels last 3 laps.<br><br>Kalista gains a charge of Sentinel every {{ e4 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Ammo Recharge\"],\"effect\":[\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ e4 }} -> {{ e4NL }}\"]},\"maxrank\":5,\"cooldown\":[30,30,30,30,30],\"cooldownBurn\":\"30\",\"cost\":[20,20,20,20,20],\"costBurn\":\"20\",\"effect\":[null,[80,80,80,80,80],[5,7.5,10,12.5,15],[125,150,175,200,225],[90,80,70,60,50],[0,0,0,0,0],[75,100,125,150,175],[10,10,10,10,10],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80\",\"5/7.5/10/12.5/15\",\"125/150/175/200/225\",\"90/80/70/60/50\",\"0\",\"75/100/125/150/175\",\"10\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"2\",\"range\":[5000,5000,5000,5000,5000],\"rangeBurn\":\"5000\",\"image\":{\"full\":\"KalistaW.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":288,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"KalistaExpungeWrapper\",\"name\":\"Rend\",\"description\":\"Attacks impale their targets with spears. Activate to rip the spears out, slowing and dealing escalating damage.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Kalista's spears pierce their target, lingering for 4 seconds.<br><br><span class=\\\"colorFF9900\\\">Active: </span>Rip the spears from nearby targets, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage and slowing their Movement Speed by {{ e2 }}% for {{ e6 }} seconds.</span><br><br><span class=\\\"colorFFFF00\\\">If </span><span class=\\\"colorFFFFFF\\\"><i>Rend</i></span><span class=\\\"colorFFFF00\\\"> kills at least one target, its cooldown is reset and its mana cost is refunded.</span>\",\"leveltip\":{\"label\":[\"Damage\",\"Slow\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }}% -> {{ e4NL }}%\",\"{{ e5 }} -> {{ e5NL }}\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[30,30,30,30,30],\"costBurn\":\"30\",\"effect\":[null,[20,30,40,50,60],[25,30,35,40,45],[30,30,30,30,30],[33,37.5,41.667,45.833,50],[14,12.5,11,9.5,8],[2,2,2,2,2],[254,254,254,254,254],[10,10,10,10,10],[10,14,19,25,32],[0,0,0,0,0]],\"effectBurn\":[null,\"20/30/40/50/60\",\"25/30/35/40/45\",\"30\",\"33/37.5/41.667/45.833/50\",\"14/12.5/11/9.5/8\",\"2\",\"254\",\"10\",\"10/14/19/25/32\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"KalistaExpungeWrapper.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":336,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"KalistaRx\",\"name\":\"Fate's Call\",\"description\":\"Kalista teleports the Oathsworn ally to herself. They gain the ability to dash toward a position, knocking enemy champions back.\",\"tooltip\":\"Draw Kalista's <i>Oathsworn</i> to her. For up to 4 seconds, the Oathsworn is untargetable and pacified.<br><br>The Oathsworn may mouse click to fly toward target position, stopping at the first enemy champion hit and knocking all enemies in a small radius back.<br><br>Kalista's Oathsworn must be within 1100 units for her to cast this ability.\",\"leveltip\":{\"label\":[\"Cooldown\",\"Knock Up Duration\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":3,\"cooldown\":[120,90,60],\"cooldownBurn\":\"120/90/60\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[0,0,0],[1.5,1.75,2],[40,60,80],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"0\",\"1.5/1.75/2\",\"40/60/80\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"KalistaRx.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":384,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Karma\":{\"id\":43,\"key\":\"Karma\",\"name\":\"Karma\",\"title\":\"the Enlightened One\",\"spells\":[{\"id\":\"KarmaQ\",\"name\":\"Inner Flame\",\"description\":\"Karma sends forth a ball of spirit energy that explodes and deals damage upon hitting an enemy unit.<br><br>Mantra Bonus: In addition to the explosion, Mantra increases the destructive power of her Inner Flame, creating a cataclysm which deals damage after a short delay.\",\"tooltip\":\"Fires a blast of energy that explodes upon enemy contact, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and slowing Movement Speed by {{ e2 }}% for {{ e3 }} seconds. <br><br><span class=\\\"colorFF9900\\\">Mantra Bonus - Soulflare:</span> Deals {{ f1 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> additional magic damage and leaves a circle of flame that slows enemies by {{ f3 }}%. After 1.5 seconds the circle erupts, dealing {{ f2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemies in the area.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[7,6.5,6,5.5,5],\"cooldownBurn\":\"7/6.5/6/5.5/5\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[80,125,170,215,260],[25,25,25,25,25],[1.5,1.5,1.5,1.5,1.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/125/170/215/260\",\"25\",\"1.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"},{\"link\":\"@text\",\"coeff\":[25,75,125,175],\"key\":\"f1\"},{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a2\"},{\"link\":\"@text\",\"coeff\":[50,150,250,350],\"key\":\"f2\"},{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[950,950,950,950,950],\"rangeBurn\":\"950\",\"image\":{\"full\":\"KarmaQ.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":432,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"KarmaSpiritBind\",\"name\":\"Focused Resolve\",\"description\":\"Karma creates a tether between herself and a targeted enemy, dealing damage and revealing them. If the tether is not broken, the enemy will be rooted and damaged again.<br><br>Mantra Bonus: Karma strengthens the link, healing herself and extending the root duration.\",\"tooltip\":\"Links Karma to an enemy champion or monster, granting <span class=\\\"coloree91d7\\\">True Sight</span> and dealing {{ effect1amount*0.5 }} <span class=\\\"color99FF99\\\">(+{{ f4 }}) </span>magic damage. If the link is unbroken for {{ e3 }} seconds, the target is rooted for {{ e2 }} second(s) and takes an additional {{ effect1amount*0.5 }} <span class=\\\"color99FF99\\\">(+{{ f4 }}) </span>magic damage.<br><br><span class=\\\"colorFF9900\\\">Mantra Bonus - Renewal:</span> Karma is healed for {{ f1 }}% <span class=\\\"color99FF99\\\">(+{{ f3 }}%)</span> of her missing health. If the link is not broken or the target dies, the root duration is increased by {{ f2 }} second(s) and Karma is healed for {{ f1 }}% <span class=\\\"color99FF99\\\">(+{{ f3 }}%)</span> of her missing health.\",\"leveltip\":{\"label\":[\"Damage\",\"Root Duration\",\"Mana Cost\"],\"effect\":[\"{{ effect1amount*0.5 }} -> {{ effect1amountnl*0.5 }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[12,12,12,12,12],\"cooldownBurn\":\"12\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[60,110,160,210,260],[1,1.25,1.5,1.75,2],[2,2,2,2,2],[2,2,2,2,2],[825,825,825,825,825],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/110/160/210/260\",\"1/1.25/1.5/1.75/2\",\"2\",\"2\",\"825\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"@text\",\"coeff\":[75,150,225,300],\"key\":\"f1\"},{\"link\":\"@text\",\"coeff\":[75,150,225,300],\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[675,675,675,675,675],\"rangeBurn\":\"675\",\"image\":{\"full\":\"KarmaSpiritBind.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":0,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"KarmaSolKimShield\",\"name\":\"Inspire\",\"description\":\"Karma summons a protective shield that absorbs incoming damage and increases the Movement Speed of the protected ally.<br><br>Mantra Bonus: Energy radiates out from her target, strengthening the initial shield and applying Inspire to nearby allied champions.\",\"tooltip\":\"Target ally gains a shield, granting {{ e3 }}% Movement Speed for {{ e2 }} seconds and absorbing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> damage for {{ e4 }} seconds.<br><br><span class=\\\"colorFF9900\\\">Mantra Bonus - Defiance:</span> The shield overflows with energy, absorbing an additional {{ f1 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> damage. Nearby allied champions gain shields that absorb {{ f2 }}% as much as the initial target's shield. All champions shielded gain {{ f3 }}% movement speed for {{ e2 }} seconds.\",\"leveltip\":{\"label\":[\"Damage Absorption\",\"Movement Speed\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,9.5,9,8.5,8],\"cooldownBurn\":\"10/9.5/9/8.5/8\",\"cost\":[60,65,70,75,80],\"costBurn\":\"60/65/70/75/80\",\"effect\":[null,[70,100,130,160,190],[1.5,1.5,1.5,1.5,1.5],[40,45,50,55,60],[4,4,4,4,4],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/100/130/160/190\",\"1.5\",\"40/45/50/55/60\",\"4\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"},{\"link\":\"@text\",\"coeff\":[60,140,220,300],\"key\":\"f1\"},{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[800,800,800,800,800],\"rangeBurn\":\"800\",\"image\":{\"full\":\"KarmaSolKimShield.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":48,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"KarmaMantra\",\"name\":\"Mantra\",\"description\":\"Karma empowers her next ability to do an additional effect. Mantra is available at level 1 and does not require a skill point.\",\"tooltip\":\"Karma empowers her next ability within 8 seconds for an additional effect. <br><br><span class=\\\"colorFF9900\\\">Soulflare: </span>Deals bonus damage and leaves behind a circle of flame, slowing enemies and dealing additional damage. <br><br><span class=\\\"colorFF9900\\\">Renewal: </span>Karma heals for a portion of her missing health. If the link is unbroken the root is longer and Karma is healed again.<br><br><span class=\\\"colorFF9900\\\">Defiance: </span>The shield is stronger and allied champions around the target also gain a shield and movement speed.\",\"leveltip\":{\"label\":[\"Soulflare Impact\",\"Soulflare Circle Damage\",\"Renewal Root Extension\",\"Defiance Shield\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ e5 }} -> {{ e5NL }}\",\"{{ e7 }} -> {{ e7NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":4,\"cooldown\":[45,42,39,36],\"cooldownBurn\":\"45/42/39/36\",\"cost\":[0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[25,75,125,175],[50,150,250,350],[0.5,0.5,0.5,0.5],[1.5,1.5,1.5,1.5],[0.5,0.75,1,1.25],[0.2,0.2,0.2,0.2],[30,90,150,210],[0.6,0.6,0.6,0.6],[0.5,0.5,0.5,0.5],[0.01,0.01,0.01,0.01]],\"effectBurn\":[null,\"25/75/125/175\",\"50/150/250/350\",\"0.5\",\"1.5\",\"0.5/0.75/1/1.25\",\"0.2\",\"30/90/150/210\",\"0.6\",\"0.5\",\"0.01\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[1100,1100,1100,1100],\"rangeBurn\":\"1100\",\"image\":{\"full\":\"KarmaMantra.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":96,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Karthus\":{\"id\":30,\"key\":\"Karthus\",\"name\":\"Karthus\",\"title\":\"the Deathsinger\",\"spells\":[{\"id\":\"KarthusLayWasteA1\",\"name\":\"Lay Waste\",\"description\":\"Karthus unleashes a delayed blast at a location, dealing damage to nearby enemies.\",\"tooltip\":\"Creates a delayed blast at Karthus' cursor position dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to each nearby enemy.<br><br>If the blast hits only a single unit it deals double damage.\",\"leveltip\":{\"label\":[\"Damage\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[20,26,32,38,44],\"costBurn\":\"20/26/32/38/44\",\"effect\":[null,[40,60,80,100,120],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"40/60/80/100/120\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"2\",\"range\":[875,875,875,875,875],\"rangeBurn\":\"875\",\"image\":{\"full\":\"KarthusLayWasteA1.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":144,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"KarthusWallOfPain\",\"name\":\"Wall of Pain\",\"description\":\"Karthus creates a passable screen of leeching energy. Any enemy units that walk through the screen have their Movement Speed and Magic Resist reduced for a period.\",\"tooltip\":\"Creates a wall that lasts for {{ e4 }} seconds. Enemies that pass through the wall have their Magic Resist reduced by {{ e1 }}% and Movement Speed reduced by {{ e3 }}% for {{ e5 }} seconds (their Movement Speed gradually recovers over the duration).\",\"leveltip\":{\"label\":[\"Wall Width\",\"Movement Speed Slow\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\" {{ e3 }}% -> {{ e3NL }}%\"]},\"maxrank\":5,\"cooldown\":[18,18,18,18,18],\"cooldownBurn\":\"18\",\"cost\":[100,100,100,100,100],\"costBurn\":\"100\",\"effect\":[null,[15,15,15,15,15],[800,900,1000,1100,1200],[40,50,60,70,80],[5,5,5,5,5],[5,5,5,5,5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"15\",\"800/900/1000/1100/1200\",\"40/50/60/70/80\",\"5\",\"5\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"KarthusWallOfPain.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":192,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"KarthusDefile\",\"name\":\"Defile\",\"description\":\"Karthus passively steals energy from his victims, gaining Mana on each kill. Alternatively, Karthus can surround himself in the souls of his prey, dealing damage to nearby enemies, but quickly draining his own Mana.  \",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Toggle Off: </span>When Karthus kills a unit, he restores {{ e2 }} Mana.<br><br><span class=\\\"colorFF9900\\\">Toggle On: </span>Drains {{ cost }} Mana to deal {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to nearby enemies each second. \",\"leveltip\":{\"label\":[\"Damage per second\",\"Mana Restore\",\"Mana Cost per second\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ e2 }} -> {{ e2NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[0.5,0.5,0.5,0.5,0.5],\"cooldownBurn\":\"0.5\",\"cost\":[30,42,54,66,78],\"costBurn\":\"30/42/54/66/78\",\"effect\":[null,[30,50,70,90,110],[20,27,34,41,48],[0.5,0.5,0.5,0.5,0.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30/50/70/90/110\",\"20/27/34/41/48\",\"0.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"}],\"costType\":\" Mana Per Second\",\"maxammo\":\"-1\",\"range\":[550,550,550,550,550],\"rangeBurn\":\"550\",\"image\":{\"full\":\"KarthusDefile.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":240,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana Per Second\"},{\"id\":\"KarthusFallenOne\",\"name\":\"Requiem\",\"description\":\"After channeling for 3 seconds, Karthus deals damage to all enemy champions.\",\"tooltip\":\"After channeling for 3 seconds, Karthus deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to all enemy champions (regardless of distance). \",\"leveltip\":{\"label\":[\"Damage\",\"Mana cost\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ cost }} -> {{ costNL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[200,180,160],\"cooldownBurn\":\"200/180/160\",\"cost\":[150,175,200],\"costBurn\":\"150/175/200\",\"effect\":[null,[250,400,550],[200,300,400],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"250/400/550\",\"200/300/400\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[10000,10000,10000],\"rangeBurn\":\"10000\",\"image\":{\"full\":\"KarthusFallenOne.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":288,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Kassadin\":{\"id\":38,\"key\":\"Kassadin\",\"name\":\"Kassadin\",\"title\":\"the Void Walker\",\"spells\":[{\"id\":\"NullLance\",\"name\":\"Null Sphere\",\"description\":\"Kassadin fires an orb of void energy at a target, dealing damage and interrupting channels. The excess energy forms around himself, granting a temporary shield that absorbs magic damage.\",\"tooltip\":\"Kassadin fires an orb of void energy that deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and interrupts channels.<br><br>The excess energy forms around himself, granting a shield that absorbs {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage for {{ e4 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Shield\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[9,9,9,9,9],\"cooldownBurn\":\"9\",\"cost\":[70,75,80,85,90],\"costBurn\":\"70/75/80/85/90\",\"effect\":[null,[65,95,125,155,185],[0,0,0,0,0],[40,70,100,130,160],[1.5,1.5,1.5,1.5,1.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"65/95/125/155/185\",\"0\",\"40/70/100/130/160\",\"1.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"NullLance.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":336,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"NetherBlade\",\"name\":\"Nether Blade\",\"description\":\"Passive: Kassadin's basic attacks deal bonus magic damage. Active: Kassadin's next basic attack deals significant bonus magic damage and restores Mana.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Kassadin's basic attacks draw energy from the void, dealing {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> bonus magic damage.<br><br><span class=\\\"colorFF9900\\\">Active: </span>Kassadin charges his Nether Blade, causing his next basic attack to deal {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> bonus magic damage and restore <span class=\\\"color44DDFF\\\">{{ e1 }}% of his missing Mana</span> (increases to <span class=\\\"color44DDFF\\\">{{ e4 }}%</span> against champions).\",\"leveltip\":{\"label\":[\"Active Damage\",\"Base Mana Restore\",\"Champion Mana Restore\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ e4 }}% -> {{ e4NL }}%\"]},\"maxrank\":5,\"cooldown\":[7,7,7,7,7],\"cooldownBurn\":\"7\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[4,5,6,7,8],[20,20,20,20,20],[40,65,90,115,140],[20,25,30,35,40],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"4/5/6/7/8\",\"20\",\"40/65/90/115/140\",\"20/25/30/35/40\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.1,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a2\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[1,1,1,1,1],\"rangeBurn\":\"1\",\"image\":{\"full\":\"NetherBlade.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":384,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"ForcePulse\",\"name\":\"Force Pulse\",\"description\":\"Kassadin draws energy from spells cast in his vicinity. Upon charging up, Kassadin can use Force Pulse to damage and slow enemies in a cone in front of him.\",\"tooltip\":\"Kassadin draws energy from spells cast in his vicinity, gaining a charge whenever a spell is cast near him.<br><br>Upon reaching 6 charges, Kassadin can use Force Pulse to deal {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and slow enemies by {{ e2 }}% for {{ e3 }} second in a cone in front of him.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow %\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[5,5,5,5,5],\"cooldownBurn\":\"5\",\"cost\":[60,65,70,75,80],\"costBurn\":\"60/65/70/75/80\",\"effect\":[null,[80,105,130,155,180],[50,60,70,80,90],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/105/130/155/180\",\"50/60/70/80/90\",\"1\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[400,400,400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"ForcePulse.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":432,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"RiftWalk\",\"name\":\"Riftwalk\",\"description\":\"Kassadin teleports to a nearby location dealing damage to nearby enemy units. Multiple Riftwalks in a short period of time cost additional Mana but also deal additional damage.\",\"tooltip\":\"Kassadin teleports to a nearby location dealing {{ e1 }} <span class=\\\"color44DDFF\\\">(+{{ f2 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to surrounding enemy units.<br><br>Each subsequent Riftwalk within the next {{ e2 }} seconds doubles the Mana cost and deals an additional {{ e3 }} <span class=\\\"color44DDFF\\\">(+{{ f1 }})</span> <span class=\\\"color99FF99\\\">(+{{ f3 }})</span> magic damage per stack, stacking up to {{ e6 }} times.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Damage Per Stack\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[6,4,2],\"cooldownBurn\":\"6/4/2\",\"cost\":[50,50,50],\"costBurn\":\"50\",\"effect\":[null,[80,100,120],[15,15,15],[40,50,60],[1,1,1],[0,0,0],[4,4,4],[2,2,2],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"80/100/120\",\"15\",\"40/50/60\",\"1\",\"0\",\"4\",\"2\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[500,500,500],\"rangeBurn\":\"500\",\"image\":{\"full\":\"RiftWalk.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":0,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Katarina\":{\"id\":55,\"key\":\"Katarina\",\"name\":\"Katarina\",\"title\":\"the Sinister Blade\",\"spells\":[{\"id\":\"KatarinaQ\",\"name\":\"Bouncing Blade\",\"description\":\"Katarina throws a <font color='#FFF673'>Dagger</font> at the target that then bounces to nearby enemies before ricocheting onto the ground.\",\"tooltip\":\"Katarina throws a <span class=\\\"colorFFF673\\\">Dagger</span>, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to the target and {{ e4 }} nearby enemies. The dagger then ricochets onto the ground behind the primary target.<br><br><span class=\\\"size16 color8C8C8C\\\">16\",\"leveltip\":{\"label\":[\"Initial Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[11,10,9,8,7],\"cooldownBurn\":\"11/10/9/8/7\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[75,105,135,165,195],[350,350,350,350,350],[450,450,450,450,450],[2,2,2,2,2],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"75/105/135/165/195\",\"350\",\"450\",\"2\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[625,625,625,625,625],\"rangeBurn\":\"625\",\"image\":{\"full\":\"KatarinaQ.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":48,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"KatarinaW\",\"name\":\"Preparation\",\"description\":\"Katarina gains a burst of movement speed, tossing a <font color='#FFF673'>Dagger</font> into the air directly above herself.\",\"tooltip\":\"Katarina tosses a <span class=\\\"colorFFF673\\\">Dagger</span> into the air and gains {{ e4 }}% decaying movement speed over {{ e2 }} seconds.</span>\",\"leveltip\":{\"label\":[\"Movement Speed Buff\",\"Cooldown\"],\"effect\":[\"{{ e4 }}% -> {{ e4NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[15,14,13,12,11],\"cooldownBurn\":\"15/14/13/12/11\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[0,0,0,0,0],[1.25,1.25,1.25,1.25,1.25],[0,0,0,0,0],[50,60,70,80,90],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"0\",\"1.25\",\"0\",\"50/60/70/80/90\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[25000,25000,25000,25000,25000],\"rangeBurn\":\"25000\",\"image\":{\"full\":\"KatarinaW.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":96,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"KatarinaEWrapper\",\"name\":\"Shunpo\",\"description\":\"Katarina blinks to the target, striking it if its an enemy, or striking the nearest enemy otherwise.\",\"tooltip\":\"Katarina dashes in the blink of an eye to the target ally, enemy, or <span class=\\\"colorFFF673\\\">Dagger</span>. If it is an enemy, Katarina strikes for {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage - otherwise she strikes the nearest enemy in range.<br><br>Picking up a <span class=\\\"colorFFF673\\\">Dagger</span> will reduce Shunpo's cooldown by <span class=\\\"colorFFFFFF\\\">{{ f1 }}</span> seconds (<span class=\\\"colorFFFFFF\\\">{{ f3*100 }}%</span>).<br><br><span class=\\\"size16 color8C8C8C\\\">16\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}<Br>{{ e3 }} -> {{ e3NL }}\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[30,45,60,75,90],[0,0,0,0,0],[14,12.5,11,9.5,8],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30/45/60/75/90\",\"0\",\"14/12.5/11/9.5/8\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":0.5,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.25,\"key\":\"a2\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[725,725,725,725,725],\"rangeBurn\":\"725\",\"image\":{\"full\":\"KatarinaEWrapper.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":144,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"KatarinaR\",\"name\":\"Death Lotus\",\"description\":\"Katarina becomes a flurry of blades, dealing massive magic damage while she channels to the 3 nearest enemy champions.\",\"tooltip\":\"Katarina becomes a flurry of blades, rapidly throwing knives at the three nearest enemy champions, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a2 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage per knife. Total over {{ e3 }} seconds to each enemy: {{ f1 }} <span class=\\\"colorFF8C00\\\">(+{{ f2 }})</span> <span class=\\\"color99FF99\\\">(+{{ f3 }})</span> magic damage.<br><br>Applies Grievous Wounds to all enemies struck, reducing their healing and regeneration by 40%.\",\"leveltip\":{\"label\":[\"Damage Per Knife\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[90,60,45],\"cooldownBurn\":\"90/60/45\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[25,37.5,50],[6,6,6],[2.5,2.5,2.5],[3,3,3],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"25/37.5/50\",\"6\",\"2.5\",\"3\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.22,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.19,\"key\":\"a1\"},{\"link\":\"bonusattackdamage\",\"coeff\":3,\"key\":\"f1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[550,550,550],\"rangeBurn\":\"550\",\"image\":{\"full\":\"KatarinaR.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":192,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Kayle\":{\"id\":10,\"key\":\"Kayle\",\"name\":\"Kayle\",\"title\":\"The Judicator\",\"spells\":[{\"id\":\"JudicatorReckoning\",\"name\":\"Reckoning\",\"description\":\"Blasts an enemy unit with angelic force, dealing damage, slowing Movement Speed, and applying Holy Fervor.\",\"tooltip\":\"Blasts an enemy, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a2 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage, slowing their Movement Speed by {{ e2 }}% for {{ e3 }} seconds, and applying a stack of Holy Fervor.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cost }}  ->  {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[8,8,8,8,8],\"cooldownBurn\":\"8\",\"cost\":[70,75,80,85,90],\"costBurn\":\"70/75/80/85/90\",\"effect\":[null,[60,110,160,210,260],[35,40,45,50,55],[3,3,3,3,3],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/110/160/210/260\",\"35/40/45/50/55\",\"3\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"JudicatorReckoning.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":240,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"JudicatorDivineBlessing\",\"name\":\"Divine Blessing\",\"description\":\"Blesses a target friendly champion, granting them increased Movement Speed and healing them.\",\"tooltip\":\"Blesses an allied champion, increasing their Movement Speed by {{ e2 }}% <span class=\\\"color99FF99\\\">(+{{ a2 }}%)</span> for {{ e3 }} seconds and healing them for {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Health.\",\"leveltip\":{\"label\":[\"Healing\",\"Movement Speed\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[15,15,15,15,15],\"cooldownBurn\":\"15\",\"cost\":[60,70,80,90,100],\"costBurn\":\"60/70/80/90/100\",\"effect\":[null,[60,105,150,195,240],[18,21,24,27,30],[3,3,3,3,3],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/105/150/195/240\",\"18/21/24/27/30\",\"3\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.07,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.45,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[900,900,900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"JudicatorDivineBlessing.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":288,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"JudicatorRighteousFury\",\"name\":\"Righteous Fury\",\"description\":\"Passive: Grants Kayle on Hit magic Damage.  Activate: Ignite Kayle's sword with a holy flame, granting Kayle a ranged attack that damages multiple enemies and deals bonus magic damage.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Kayle's Basic Attacks deal an additional {{ e4 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage on hit.<br><br><span class=\\\"colorFF9900\\\">Active: </span>Kayle increases her Attack Range by 400 for 10 seconds and the passive bonus is increased to {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and apply spell effects on hit.<br><br>In addition, deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f2 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and applies spell effects on attack to enemies near her target.\",\"leveltip\":{\"label\":[\"Passive Damage\",\"Active Damage\",\"Damage to Nearby Enemies Ratio \"],\"effect\":[\"{{ e4 }} -> {{ e4NL }}\",\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\"]},\"maxrank\":5,\"cooldown\":[16,16,16,16,16],\"cooldownBurn\":\"16\",\"cost\":[45,45,45,45,45],\"costBurn\":\"45\",\"effect\":[null,[20,30,40,50,60],[20,25,30,35,40],[0.2,0.25,0.3,0.35,0.4],[10,15,20,25,30],[10,10,10,10,10],[400,400,400,400,400],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"20/30/40/50/60\",\"20/25/30/35/40\",\"0.2/0.25/0.3/0.35/0.4\",\"10/15/20/25/30\",\"10\",\"400\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.15,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"},{\"link\":\"attackdamage\",\"coeff\":[0.2,0.25,0.3,0.35,0.4],\"key\":\"f2\"},{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[20,20,20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"JudicatorRighteousFury.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":336,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"JudicatorIntervention\",\"name\":\"Intervention\",\"description\":\"Shields Kayle or an ally for a short time, causing them to be immune to damage.\",\"tooltip\":\"Bathes Kayle's target in a holy light, rendering them immune to all damage for {{ e1 }} seconds.\",\"leveltip\":{\"label\":[\"Duration\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[100,90,80],\"cooldownBurn\":\"100/90/80\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[2,2.5,3],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"2/2.5/3\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"JudicatorIntervention.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":384,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Kennen\":{\"id\":85,\"key\":\"Kennen\",\"name\":\"Kennen\",\"title\":\"the Heart of the Tempest\",\"spells\":[{\"id\":\"KennenShurikenHurlMissile1\",\"name\":\"Thundering Shuriken\",\"description\":\"Kennen throws a fast moving shuriken towards a location, causing damage and adding a Mark of the Storm to any opponent that it hits.\",\"tooltip\":\"Throws a shuriken that deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to the first enemy it hits.\",\"leveltip\":{\"label\":[\"Damage\",\"Energy Cost\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cost }} -> {{ costNL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[8,7,6,5,4],\"cooldownBurn\":\"8/7/6/5/4\",\"cost\":[60,55,50,45,40],\"costBurn\":\"60/55/50/45/40\",\"effect\":[null,[75,115,155,195,235],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"75/115/155/195/235\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.75,\"key\":\"a1\"}],\"costType\":\" Energy\",\"maxammo\":\"-1\",\"range\":[950,950,950,950,950],\"rangeBurn\":\"950\",\"image\":{\"full\":\"KennenShurikenHurlMissile1.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":432,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Energy\"},{\"id\":\"KennenBringTheLight\",\"name\":\"Electrical Surge\",\"description\":\"Kennen passively deals extra damage and adds a Mark of the Storm to his target every few attacks, and he can activate this ability to damage and add another Mark of the Storm to targets who are already marked.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> Every 5 attacks, Kennen deals bonus magic damage equal to {{ e2 }}% of his attack damage and adds a Mark of the Storm to his target.<br><br><span class=\\\"colorFF9900\\\">Active:</span> Sends a surge of electricity through all nearby enemies affected by Mark of the Storm or Slicing Maelstrom, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.\",\"leveltip\":{\"label\":[\"Passive %\",\"Cooldown\",\"Active Damage\"],\"effect\":[\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":5,\"cooldown\":[14,12,10,8,6],\"cooldownBurn\":\"14/12/10/8/6\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[65,95,125,155,185],[40,50,60,70,80],[0.4,0.5,0.6,0.7,0.8],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"65/95/125/155/185\",\"40/50/60/70/80\",\"0.4/0.5/0.6/0.7/0.8\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.55,\"key\":\"a1\"}],\"costType\":\" Energy\",\"maxammo\":\"-1\",\"range\":[725,725,725,725,725],\"rangeBurn\":\"725\",\"image\":{\"full\":\"KennenBringTheLight.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":0,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Energy\"},{\"id\":\"KennenLightningRush\",\"name\":\"Lightning Rush\",\"description\":\"Kennen morphs into a lightning form, enabling him to pass through units. Each enemy unit he touches takes damage and receives a Mark of the Storm.\",\"tooltip\":\"Kennen doubles his base movement speed and ignores unit collision for 2 seconds, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to any enemy he passes through. Additionally, Kennen will gain {{ e2 }} Armor and Magic Resist for 4 seconds.<br><br>Kennen gains 40 Energy the first time he passes through an enemy.<br>Lightning Rush deals half damage to minions.\",\"leveltip\":{\"label\":[\"Damage\",\"Bonus Armor and Magic Resist\",\"Cooldown\",\"Energy Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,9,8,7,6],\"cooldownBurn\":\"10/9/8/7/6\",\"cost\":[100,95,90,85,80],\"costBurn\":\"100/95/90/85/80\",\"effect\":[null,[85,125,165,205,245],[10,20,30,40,50],[40,40,40,40,40],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"85/125/165/205/245\",\"10/20/30/40/50\",\"40\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Energy\",\"maxammo\":\"-1\",\"range\":[200,200,200,200,200],\"rangeBurn\":\"200\",\"image\":{\"full\":\"KennenLightningRush.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":48,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Energy\"},{\"id\":\"KennenShurikenStorm\",\"name\":\"Slicing Maelstrom\",\"description\":\"Kennen summons a storm that strikes at nearby enemy champions for magical damage.\",\"tooltip\":\"Summons a magical storm for {{ e3 }} seconds that deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to all enemies near Kennen every {{ e2 }} seconds. Each maelstrom hit applies Mark of the Storm, up to a maximum of 3.<br><br>Successive maelstrom hits against the same enemy deal an additional {{ e4 }}% damage for each hit they've already suffered from this storm.\",\"leveltip\":{\"label\":[\"Damage\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":3,\"cooldown\":[120,120,120],\"cooldownBurn\":\"120\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[40,75,110],[0.5,0.5,0.5],[3,3,3],[10,10,10],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"40/75/110\",\"0.5\",\"3\",\"10\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[550,550,550],\"rangeBurn\":\"550\",\"image\":{\"full\":\"KennenShurikenStorm.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":96,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Khazix\":{\"id\":121,\"key\":\"Khazix\",\"name\":\"Kha'Zix\",\"title\":\"the Voidreaver\",\"spells\":[{\"id\":\"KhazixQ\",\"name\":\"Taste Their Fear\",\"description\":\"Deals physical damage to the target. Damage increased on <font color='#FFF673'>Isolated</font> targets. If he chooses to <font color='#00DD33'>Evolve Reaper Claws</font>, this refunds a percent of it's cooldown against <font color='#FFF673'>Isolated</font> targets. Kha'Zix also gains increased range on his basic attacks and Taste Their Fear.\",\"tooltip\":\"Deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage. If the target is <span class=\\\"colorFFF673\\\">Isolated</span>, the damage is increased by {{ e2 }}% (<span class=\\\"colorFF8C00\\\">{{ f1 }}</span>).<br><br><span class=\\\"color00DD33\\\">Evolved Reaper Claws:</span> Increases the range of Taste Their Fear and Kha'Zix's basic attacks by {{ e3 }}. If target is <span class=\\\"colorFFF673\\\">Isolated</span>, refunds {{ e4 }}% of Taste Their Fear's cooldown.\",\"leveltip\":{\"label\":[\"Base Damage\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":5,\"cooldown\":[4,4,4,4,4],\"cooldownBurn\":\"4\",\"cost\":[20,20,20,20,20],\"costBurn\":\"20\",\"effect\":[null,[70,95,120,145,170],[50,50,50,50,50],[50,50,50,50,50],[45,45,45,45,45],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/95/120/145/170\",\"50\",\"50\",\"45\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1.2,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[325,325,325,325,325],\"rangeBurn\":\"325\",\"image\":{\"full\":\"KhazixQ.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":144,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"KhazixW\",\"name\":\"Void Spike\",\"description\":\"Kha'Zix fires exploding spikes that deal physical damage to enemies hit. Kha'Zix is healed if he is also within the explosion radius. If he chooses to <font color='#00DD33'>Evolve Spike Racks</font>, Void Spike now fires three spikes in a cone, slow enemies hit, and reveals enemy champions hit for 2 seconds. <font color='#FFF673'>Isolated</font> targets are slowed for extra.\",\"tooltip\":\"Fires exploding spikes, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage to enemies hit ({{ e5 }}% bonus damage to monsters). Heals Kha'Zix for {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> if he is within the explosion radius.<br><br><span class=\\\"color00DD33\\\">Evolved Spike Racks:</span> Void Spike fires three spikes in a cone and slows targets hit by {{ e3 }}% for {{ e4 }} seconds. Reveals enemy champions hit for 2 seconds. <span class=\\\"colorFFF673\\\">Isolated</span> targets are slowed for {{ e7 }}% instead.\",\"leveltip\":{\"label\":[\"Damage\",\"Healing\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[9,9,9,9,9],\"cooldownBurn\":\"9\",\"cost\":[55,60,65,70,75],\"costBurn\":\"55/60/65/70/75\",\"effect\":[null,[80,110,140,170,200],[60,85,110,135,160],[40,40,40,40,40],[2,2,2,2,2],[20,20,20,20,20],[2,2,2,2,2],[80,80,80,80,80],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/110/140/170/200\",\"60/85/110/135/160\",\"40\",\"2\",\"20\",\"2\",\"80\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"KhazixW.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":192,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"KhazixE\",\"name\":\"Leap\",\"description\":\"Kha'Zix leaps to an area, dealing physical damage upon landing. If he chooses to <font color='#00DD33'>Evolve Wings</font>, Leap's range increases by 200 and the cooldown resets on champion kill or assist.\",\"tooltip\":\"Leaps to target area, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage.<br><br><span class=\\\"color00DD33\\\">Evolved Wings:</span> Increases Leap's range by 200, and the cooldown resets on champion kill or assist.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[20,18,16,14,12],\"cooldownBurn\":\"20/18/16/14/12\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[65,100,135,170,205],[30,35,40,45,50],[10,10,10,10,10],[2.5,2.5,2.5,2.5,2.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"65/100/135/170/205\",\"30/35/40/45/50\",\"10\",\"2.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.2,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[700,700,700,700,700],\"rangeBurn\":\"700\",\"image\":{\"full\":\"KhazixE.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":240,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"KhazixR\",\"name\":\"Void Assault\",\"description\":\"Each rank allows Kha'Zix to evolve one of his abilities, giving it a unique additional effect. When activated, Kha'Zix becomes <font color='#91d7ee'>Invisible</font>, triggering Unseen Threat and increasing Movement Speed. If he chooses to <font color='#00DD33'>Evolve Adaptive Cloaking</font>, while out of combat Kha'Zix also passively gains Void Assault upon entering a new brush.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> Ranking up Void Assault allows Kha'Zix to evolve one of his abilities.<br><br><span class=\\\"colorFF9900\\\">Active:</span> Kha'Zix becomes <span class=\\\"color91d7ee\\\">Invisible</span> for {{ e1 }} seconds and activates Unseen Threat. While <span class=\\\"color91d7ee\\\">Invisible</span>, he gains {{ e3 }}% movement speed and ignores unit collision. Void Assault can be cast a second time within {{ e2 }} seconds.<br><br><span class=\\\"color00DD33\\\">Evolved Adaptive Cloaking:</span> While out of combat, Kha'Zix also passively gains Void Assault for {{ e5 }} seconds in brush. This persists for up to {{ e1 }} seconds after exiting the brush ({{ f1 }} second per brush cooldown).<br><br><u><span class=\\\"size16 color91d7ee\\\">16\",\"leveltip\":{\"label\":[\"Evolutions Available\",\"Cooldown\"],\"effect\":[\"{{ e4 }} -> {{ e4NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[100,90,80],\"cooldownBurn\":\"100/90/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[1.25,1.25,1.25],[10,10,10],[40,40,40],[1,2,3],[2.5,2.5,2.5],[2,2,2],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"1.25\",\"10\",\"40\",\"1/2/3\",\"2.5\",\"2\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[25000,25000,25000],\"rangeBurn\":\"25000\",\"image\":{\"full\":\"KhazixR.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":288,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Kindred\":{\"id\":203,\"key\":\"Kindred\",\"name\":\"Kindred\",\"title\":\"The Eternal Hunters\",\"spells\":[{\"id\":\"KindredQ\",\"name\":\"Dance of Arrows\",\"description\":\"Kindred tumbles and shoots up to three arrows at nearby targets.\",\"tooltip\":\"Lamb vaults, firing up to 3 arrows at nearby enemies, dealing {{ e1 }}<span class=\\\"colorFF8C00\\\"> (+{{ a1 }})</span> physical damage and gains <scaleLevel>{{ f2 }}%</scaleLevel> bonus attack speed for {{ e8 }} seconds.<br><br>Casting <span class=\\\"colorFFFFFF\\\">Wolf's Frenzy</span> or vaulting inside of its effect reduces the cooldown of this spell to {{ e4 }} seconds.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Cooldown in Wolf's Frenzy\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }} -> {{ e4NL }}\"]},\"maxrank\":5,\"cooldown\":[9,9,9,9,9],\"cooldownBurn\":\"9\",\"cost\":[35,35,35,35,35],\"costBurn\":\"35\",\"effect\":[null,[55,75,95,115,135],[0,0,0,0,0],[500,500,500,500,500],[4,3.5,3,2.5,2],[100,100,100,100,100],[12,12,12,12,12],[0.1,0.1,0.1,0.1,0.1],[4,4,4,4,4],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"55/75/95/115/135\",\"0\",\"500\",\"4/3.5/3/2.5/2\",\"100\",\"12\",\"0.1\",\"4\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.65,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[340,340,340,340,340],\"rangeBurn\":\"340\",\"image\":{\"full\":\"KindredQ.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":336,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"KindredW\",\"name\":\"Wolf's Frenzy\",\"description\":\"Wolf enrages and attacks enemies around him.\",\"tooltip\":\"Wolf claims a territory, attacking nearby enemies inside it for {{ e4 }} seconds. Lamb can redirect Wolf to new targets by attacking them. If Lamb leaves Wolf's territory he will cease attacking and join her.<br><br>Wolf's attacks deal magic damage equal to {{ e5 }}<span class=\\\"colorFF8C00\\\"> (+{{ a1 }})</span> plus <scaleLevel>{{ f3 }}%</scaleLevel> of the target's current health.<br><br><rules>Wolf attacks faster based on Kindred's attack speed. His attacks maim monsters for {{ e7 }}% increased damage and reduce their attack and movement speed by {{ e8 }}% for 2 seconds.</rules>\",\"leveltip\":{\"label\":[\"Base Damage\",\"Cooldown\"],\"effect\":[\"{{ e5 }} -> {{ e5NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[18,17,16,15,14],\"cooldownBurn\":\"18/17/16/15/14\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[40,45,50,55,60],[1,1,1,1,1],[1.5,1.5,1.5,1.5,1.5],[8.5,8.5,8.5,8.5,8.5],[25,30,35,40,45],[800,800,800,800,800],[50,50,50,50,50],[50,50,50,50,50],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"40/45/50/55/60\",\"1\",\"1.5\",\"8.5\",\"25/30/35/40/45\",\"800\",\"50\",\"50\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.2,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[560,560,560,560,560],\"rangeBurn\":\"560\",\"image\":{\"full\":\"KindredW.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":384,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"KindredEWrapper\",\"name\":\"Mounting Dread\",\"description\":\"Lamb fires a carefully placed shot, slowing the target. If Lamb attacks the target two more times, her third attack instead directs Wolf to pounce on the enemy, savaging them for massive damage.\",\"tooltip\":\"Cripple an enemy, slowing their movement speed by {{ e2 }}% for {{ e8 }} second.<br><br>After Lamb attacks the target twice, her third attack instead directs Wolf to pounce on the enemy, dealing {{ e1 }}<span class=\\\"colorFF8C00\\\"> (+{{ a1 }})</span> plus <scaleLevel>{{ f2 }}%</scaleLevel> of the target's missing health as bonus physical damage.<br><br>Wolf's attack critically strikes targets for {{ e6 }}% increased damage if they are below {{ e5 }}% <span class=\\\"colorFF5802\\\">(+{{ f3 }}%)</span> health (Increased by <span class=\\\"colorFF5802\\\">Critical Strike Chance</span>).<br><br><rules>[Maximum {{ e0 }} vs. monsters]</rules>\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[16,15,14,13,12],\"cooldownBurn\":\"16/15/14/13/12\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[40,60,80,100,120],[50,50,50,50,50],[0.5,0.5,0.5,0.5,0.5],[8,8,8,8,8],[15,15,15,15,15],[50,50,50,50,50],[4,4,4,4,4],[1,1,1,1,1],[4,4,4,4,4],[300,300,300,300,300]],\"effectBurn\":[null,\"40/60/80/100/120\",\"50\",\"0.5\",\"8\",\"15\",\"50\",\"4\",\"1\",\"4\",\"300\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[500,500,500,500,500],\"rangeBurn\":\"500\",\"image\":{\"full\":\"KindredEWrapper.png\",\"sprite\":\"spell5.png\",\"group\":\"spell\",\"x\":432,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"KindredR\",\"name\":\"Lamb's Respite\",\"description\":\"Lamb grants all living things inside a zone a respite from death. Until the effect ends, nothing can die. At the end, units are healed.\",\"tooltip\":\"Lamb blesses the ground underneath her for {{ e2 }} seconds, creating an area in which no living things, <span class=\\\"color99FF99\\\">ally</span> or <span class=\\\"colorFF9999\\\">enemy</span>, can die. Upon reaching 10% Health, units become immune to further damage or healing.<br><br>When the blessing ends all living things inside heal for {{ e1 }}.\",\"leveltip\":{\"label\":[\"Heal\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[160,130,100],\"cooldownBurn\":\"160/130/100\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[200,250,300],[4,4,4],[530,530,530],[400,600,800],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"200/250/300\",\"4\",\"530\",\"400/600/800\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[500,500,500],\"rangeBurn\":\"500\",\"image\":{\"full\":\"KindredR.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":0,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Kled\":{\"id\":240,\"key\":\"Kled\",\"name\":\"Kled\",\"title\":\"the Cantankerous Cavalier\",\"spells\":[{\"id\":\"KledQ\",\"name\":\"Bear Trap on a Rope\",\"description\":\"Kled throws a bear trap that damages and hooks an enemy champion. If shackled for a short duration, the target takes additional physical damage and is yanked toward Kled.<br><br>When dismounted, this ability is replaced by Pocket Pistol, a ranged gun blast that knocks back Kled and restores courage.\",\"tooltip\":\"Kled throws a bear trap that deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }}) </span>physical damage and hooks onto the first enemy champion or large monster hit, granting <span class=\\\"coloree91d7\\\">True Sight</span>. Deals 150% damage to minions passed through.<br><br>If Kled stays near a hooked enemy for {{ e3 }} seconds, he deals {{ e1 }}<span class=\\\"colorFF8C00\\\"> (+{{ charbonusphysical*2 }})</span> physical damage, yanks the enemy toward him, and slows the enemy by {{ e5 }}% for 1.5 seconds.<br><br><span class=\\\"colorFF8C00\\\">Dismounted:</span> Becomes Pocket Pistol, a ranged ability that restores <span class=\\\"coloreae57e\\\">courage</span>.\",\"leveltip\":{\"label\":[\"Throw Damage\",\"Rip Damage\",\"Slow\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e1 }} -> {{ e1NL }}\",\"{{ e5 }}% -> {{ e5NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[9,8.5,8,7.5,7],\"cooldownBurn\":\"9/8.5/8/7.5/7\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[50,100,150,200,250],[0.6,0.6,0.6,0.6,0.6],[1.75,1.75,1.75,1.75,1.75],[2,2,2,2,2],[40,45,50,55,60],[9,8.5,8,7.5,7],[1.5,1.5,1.5,1.5,1.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/100/150/200/250\",\"0.6\",\"1.75\",\"2\",\"40/45/50/55/60\",\"9/8.5/8/7.5/7\",\"1.5\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[800,800,800,800,800],\"rangeBurn\":\"800\",\"image\":{\"full\":\"KledQ.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":48,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"KledW\",\"name\":\"Violent Tendencies\",\"description\":\"Kled gains massive attack speed for four attacks. The fourth attack deals more damage.\",\"tooltip\":\"<span class=\\\"colorFF8C00\\\">Passive:</span> When Kled basic attacks, he frenzies, gaining {{ e2 }}% attack speed for four attacks or {{ e6 }} seconds. Violent Tendencies then goes on cooldown.<br><br>The fourth hit deals bonus physical damage equal to {{ e4 }} plus {{ e1 }}% <span class=\\\"colorFF8C00\\\">(+{{ f1 }}%)</span> of the target's maximum health (maximum {{ e9 }} vs monsters).\",\"leveltip\":{\"label\":[\"Flat Damage\",\"Percentage Damage\",\"Cooldown\"],\"effect\":[\"{{ e4 }} -> {{ e4NL }}\",\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[14,12.5,11,9.5,8],\"cooldownBurn\":\"14/12.5/11/9.5/8\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[4,4.5,5,5.5,6],[150,150,150,150,150],[20,20,20,20,20],[20,30,40,50,60],[15,30,45,60,75],[4,4,4,4,4],[200,200,200,200,200],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"4/4.5/5/5.5/6\",\"150\",\"20\",\"20/30/40/50/60\",\"15/30/45/60/75\",\"4\",\"200\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[0,0,0,0,0],\"rangeBurn\":\"0\",\"image\":{\"full\":\"KledW.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":96,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"KledE\",\"name\":\"Jousting\",\"description\":\"Kled dashes, dealing physical damage and gaining a short burst of speed. Kled can then reactivate this ability to dash back through his initial target, dealing the same damage.\",\"tooltip\":\"Kled dashes, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }}) </span>physical damage to enemies in his path. Cannot cross walls.<br><br>If Jousting hits an enemy champion or large monster, Kled gains {{ e5 }}% movement speed for {{ e4 }} second and <span class=\\\"coloree91d7\\\">True Sight</span> of the target. He can reactivate this ability within {{ e6 }} seconds to dash back through the same target, dealing the same damage.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[14,13,12,11,10],\"cooldownBurn\":\"14/13/12/11/10\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[20,45,70,95,120],[0,0,0,0,0],[350,350,350,350,350],[1,1,1,1,1],[50,50,50,50,50],[3,3,3,3,3],[600,600,600,600,600],[700,700,700,700,700],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"20/45/70/95/120\",\"0\",\"350\",\"1\",\"50\",\"3\",\"600\",\"700\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[550,550,550,550,550],\"rangeBurn\":\"550\",\"image\":{\"full\":\"KledE.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":144,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"KledR\",\"name\":\"Chaaaaaaaarge!!!\",\"description\":\"Kled and Skaarl charge to a location, leaving a speed-granting trail behind them and gaining a shield. Skaarl locks onto and rams the first enemy champion encountered.\",\"tooltip\":\"Kled charges toward a location, leaving a trail that grants allies increasing movement speed. While charging, Kled gains a shield that increases over time to a maximum of {{ e4 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> and lasts for 2 seconds after the charge ends.<br><br>Skaarl rams the first enemy champion encountered, dealing up to {{ e1 }}% <span class=\\\"colorFF8C00\\\">(+{{ f1*3 }}%)</span> of the target's maximum health as physical damage based on distance traveled.\",\"leveltip\":{\"label\":[\"Maximum Damage\",\"Maximum Shield\",\"Range\",\"Cooldown\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ e0 }} -> {{ e0NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[160,140,120],\"cooldownBurn\":\"160/140/120\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[12,15,18],[4,4,4],[0,0,0],[200,300,400],[3,3,3],[100,150,0],[0,0,0],[150,0,0],[3,3,3],[3500,4000,4500]],\"effectBurn\":[null,\"12/15/18\",\"4\",\"0\",\"200/300/400\",\"3\",\"100/150/0\",\"0\",\"150/0/0\",\"3\",\"3500/4000/4500\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":3,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[3500,4000,4500],\"rangeBurn\":\"3500/4000/4500\",\"image\":{\"full\":\"KledR.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":192,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"KogMaw\":{\"id\":96,\"key\":\"KogMaw\",\"name\":\"Kog'Maw\",\"title\":\"the Mouth of the Abyss\",\"spells\":[{\"id\":\"KogMawQ\",\"name\":\"Caustic Spittle\",\"description\":\"Kog'Maw launches a corrosive projectile which deals magic damage and corrodes the target's armor and magic resist for a short time. Kog'Maw also gains additional attack speed.\",\"tooltip\":\"<span class=\\\"colorFF8C00\\\">Passive: </span> Kog'Maw gains {{ e2 }}% bonus attack speed.<br><br><span class=\\\"colorFF8C00\\\">Active: </span>Launches a corrosive projectile that deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Magic Damage to the first enemy hit and shreds its Armor and Magic Resist by {{ e3 }}% for {{ e4 }} seconds.\",\"leveltip\":{\"label\":[\"Bonus Attack Speed\",\"Damage\",\"Armor and Magic Resist Shred\"],\"effect\":[\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\"]},\"maxrank\":5,\"cooldown\":[8,8,8,8,8],\"cooldownBurn\":\"8\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[80,130,180,230,280],[15,20,25,30,35],[20,22,24,26,28],[4,4,4,4,4],[1,1,1,1,1],[1,1,1,1,1],[100,100,100,100,100],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/130/180/230/280\",\"15/20/25/30/35\",\"20/22/24/26/28\",\"4\",\"1\",\"1\",\"100\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1175,1175,1175,1175,1175],\"rangeBurn\":\"1175\",\"image\":{\"full\":\"KogMawQ.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":240,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"KogMawBioArcaneBarrage\",\"name\":\"Bio-Arcane Barrage\",\"description\":\"Kog'Maw's attacks gain range and deal a percent of the target's maximum health as magic damage.\",\"tooltip\":\"For {{ e3 }} seconds, Kog'Maw's basic attacks gain {{ e1 }} range and deal {{ e2 }}% <span class=\\\"color99FF99\\\">(+{{ f1 }})%</span> of the target's maximum health as bonus magic damage.\",\"leveltip\":{\"label\":[\"Range\",\"Bonus Max Health Damage\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[17,17,17,17,17],\"cooldownBurn\":\"17\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[130,150,170,190,210],[3,4,5,6,7],[8,8,8,8,8],[100,100,100,100,100],[100,100,100,100,100],[0,0,0,0,0],[100,100,100,100,100],[15,15,15,15,15],[0.15,0.2,0.25,0.3,0.35],[15,20,25,30,35]],\"effectBurn\":[null,\"130/150/170/190/210\",\"3/4/5/6/7\",\"8\",\"100\",\"100\",\"0\",\"100\",\"15\",\"0.15/0.2/0.25/0.3/0.35\",\"15/20/25/30/35\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[530,530,530,530,530],\"rangeBurn\":\"530\",\"image\":{\"full\":\"KogMawBioArcaneBarrage.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":288,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"KogMawVoidOoze\",\"name\":\"Void Ooze\",\"description\":\"Kog'Maw launches a peculiar ooze which damages all enemies it passes through and leaves a trail which slows enemies who stand on it.\",\"tooltip\":\"Deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Magic Damage to enemies hit and leaves a trail on the ground for {{ e3 }} seconds, slowing enemies in it by {{ e2 }}%.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[12,12,12,12,12],\"cooldownBurn\":\"12\",\"cost\":[80,90,100,110,120],\"costBurn\":\"80/90/100/110/120\",\"effect\":[null,[60,105,150,195,240],[20,28,36,44,52],[4,4,4,4,4],[0.25,0.25,0.25,0.25,0.25],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/105/150/195/240\",\"20/28/36/44/52\",\"4\",\"0.25\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1200,1200,1200,1200,1200],\"rangeBurn\":\"1200\",\"image\":{\"full\":\"KogMawVoidOoze.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":336,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"KogMawLivingArtillery\",\"name\":\"Living Artillery\",\"description\":\"Kog'Maw fires an artillery shell at a great distance dealing magic damage (increased significantly on low health enemies) and revealing non-stealthed targets. Additionally, multiple Living Artilleries in a short period of time cause them to cost additional Mana.\",\"tooltip\":\"Fires an artillery shot, damaging enemies above 40% health for {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a2 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> to {{ effect1amount*1.5 }} <span class=\\\"colorFF8C00\\\">(+{{ f3 }})</span> <span class=\\\"color99FF99\\\">(+{{ f2 }})</span> magic damage based on their missing health.<br><br>If enemies are below 40% health, they take {{ f4 }} <span class=\\\"colorFF8C00\\\">(+{{ f6 }})</span> <span class=\\\"color99FF99\\\">(+{{ f5 }})</span> magic damage instead.<br><br>Each subsequent shot within {{ e4 }} seconds costs {{ e3 }} more Mana (max {{ e5 }}).\",\"leveltip\":{\"label\":[\"Damage\",\"Range\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[2,1.5,1],\"cooldownBurn\":\"2/1.5/1\",\"cost\":[40,40,40],\"costBurn\":\"40\",\"effect\":[null,[100,140,180],[1200,1500,1800],[40,40,40],[8,8,8],[400,400,400],[2,2,2],[1.5,1.5,1.5],[2,2,2],[360,360,360],[400,400,400]],\"effectBurn\":[null,\"100/140/180\",\"1200/1500/1800\",\"40\",\"8\",\"400\",\"2\",\"1.5\",\"2\",\"360\",\"400\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.65,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.25,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1200,1500,1800],\"rangeBurn\":\"1200/1500/1800\",\"image\":{\"full\":\"KogMawLivingArtillery.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":384,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Leblanc\":{\"id\":7,\"key\":\"Leblanc\",\"name\":\"LeBlanc\",\"title\":\"the Deceiver\",\"spells\":[{\"id\":\"LeblancQ\",\"name\":\"Shatter Orb\",\"description\":\"LeBlanc projects an orb towards her target, dealing magic damage. When Shatter Orb is empowered by Sigil of Malice, the spell bounces to the nearest ready Sigil of Malice (bounces deal reduced damage to minions).\",\"tooltip\":\"Deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage. When Shatter Orb is empowered by Sigil of Malice, the spell bounces to the nearest ready Sigil of Malice (bounces deal {{ e7 }}% damage to minions). \",\"leveltip\":{\"label\":[\"Damage\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[40,45,50,55,60],\"costBurn\":\"40/45/50/55/60\",\"effect\":[null,[55,90,125,160,195],[25,25,25,25,25],[3.5,3.5,3.5,3.5,3.5],[6,6,6,6,6],[60,90,120,150,180],[0,0,0,0,0],[80,80,80,80,80],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"55/90/125/160/195\",\"25\",\"3.5\",\"6\",\"60/90/120/150/180\",\"0\",\"80\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[700,700,700,700,700],\"rangeBurn\":\"700\",\"image\":{\"full\":\"LeblancQ.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":432,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"LeblancW\",\"name\":\"Distortion\",\"description\":\"LeBlanc rapidly dashes to a target location, dealing magic damage to nearby units. In the following 4 seconds, she can activate Distortion again to return to her starting location.\",\"tooltip\":\"Dashes to target location, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemies in the target area.<br><br>For the next 4 seconds, she can activate Distortion again to return to her starting location.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[18,16,14,12,10],\"cooldownBurn\":\"18/16/14/12/10\",\"cost\":[70,80,90,100,110],\"costBurn\":\"70/80/90/100/110\",\"effect\":[null,[40,55,70,85,100],[2,2,2,2,2],[4,4,4,4,4],[600,600,600,600,600],[0.85,0.85,0.85,0.85,0.85],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"40/55/70/85/100\",\"2\",\"4\",\"600\",\"0.85\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"LeblancW.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":0,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"LeblancE\",\"name\":\"Ethereal Chains\",\"description\":\"LeBlanc flings illusionary chains towards a target location. If it hits an enemy unit, it will deal initial magic damage and shackle them. If the target remains shackled for 1.5 seconds, the target takes additional magic damage and is unable to move.\",\"tooltip\":\"Launches a chain that shackles the first unit hit to LeBlanc, granting <span class=\\\"coloree91d7\\\">True Sight</span> of the unit and dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.<br><br>If the target remains shackled for {{ e2 }} seconds they are rooted for {{ e2 }} seconds and take an additional {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage. \",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ f1 }} -> {{ f2 }}\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[40,60,80,100,120],[1.5,1.5,1.5,1.5,1.5],[1.5,1.5,1.5,1.5,1.5],[25,25,25,25,25],[885,885,885,885,885],[14,13.25,12.5,11.75,11],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"40/60/80/100/120\",\"1.5\",\"1.5\",\"25\",\"885\",\"14/13.25/12.5/11.75/11\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[925,925,925,925,925],\"rangeBurn\":\"925\",\"image\":{\"full\":\"LeblancE.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":48,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"LeblancR\",\"name\":\"Mimic\",\"description\":\"LeBlanc creates a clone and casts a mimicked version of one of her basic spells. The clone casts a fake copy of that spell. LeBlanc can instead send the clone far away, where it casts a fake copy of her most recently cast spell.\",\"tooltip\":\"LeBlanc copies one of her spells and creates a <span class=\\\"colorCC00FF\\\">Mimic</span> decoy to cast it with her. The <span class=\\\"colorCC00FF\\\">Mimic</span> lasts {{ e7 }} seconds. <br><br><li>Shatter Orb deals {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage.<br>Distortion deals {{ e5 }} <span class=\\\"color99FF99\\\">(+{{ f1 }})</span> magic damage.<br>Ethereal Chains deals {{ e4 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage each time.<br>Using this spell on itself invokes <span class=\\\"colorDDDD77\\\">Shadow of the Rose</span>.</li><br><span class=\\\"colorDDDD77\\\">Shadow of the Rose</span><br>The <span class=\\\"colorCC00FF\\\">Mimic</span> appears at target location and approaches the closest visible enemy Champion, then casts a fake version of LeBlanc's most recent basic ability. <br><br>Has a separate <span class=\\\"colorFFFFFF\\\">{{ f2 }} second</span> cooldown. This <span class=\\\"colorCC00FF\\\">Mimic</span> lasts much longer but cannot be controlled.\",\"leveltip\":{\"label\":[\"Mimicked Shatter Orb Damage\",\"Mimicked Distortion Damage\",\"Mimicked Ethereal Chains Damage\",\"Shadow of the Rose Cooldown\",\"Mimic Cooldown\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ e5 }} -> {{ e5NL }}\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ f2 }} -> {{ f3 }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[54,42,30],\"cooldownBurn\":\"54/42/30\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[20,40,60],[30,37.5,45],[150,275,400],[100,160,220],[60,120,180],[160,140,120],[2.5,2.5,2.5],[150,150,150],[0.3,0.3,0.3],[0.075,0.075,0.075]],\"effectBurn\":[null,\"20/40/60\",\"30/37.5/45\",\"150/275/400\",\"100/160/220\",\"60/120/180\",\"160/140/120\",\"2.5\",\"150\",\"0.3\",\"0.08\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\"Toggle\",\"maxammo\":\"2\",\"range\":[25000,25000,25000],\"rangeBurn\":\"25000\",\"image\":{\"full\":\"LeblancR.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":96,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"Toggle\"}]},\"LeeSin\":{\"id\":64,\"key\":\"LeeSin\",\"name\":\"Lee Sin\",\"title\":\"the Blind Monk\",\"spells\":[{\"id\":\"BlindMonkQOne\",\"name\":\"Sonic Wave / Resonating Strike\",\"description\":\"Sonic Wave: Lee Sin projects a discordant wave of sound to locate his enemies, dealing physical damage to the first enemy it encounters. If Sonic Wave hits, Lee Sin can cast Resonating Strike for the next 3 seconds.<br>Resonating Strike: Lee Sin dashes to the enemy hit by Sonic Wave, dealing physical damage plus 8% of their missing Health.\",\"tooltip\":\"<spellActive>Sonic Wave: </spellActive>Lee Sin projects a discordant wave of sound to locate his enemies, dealing {{ e1 }} <scaleAD>(+{{ a1 }})</scaleAD> physical damage to the first enemy it encounters, granting <keyword>True Sight</keyword> of the target. <specialRules>If Sonic Wave hits, Lee Sin can cast Resonating Strike for the next {{ e7 }} seconds.</specialRules><br><br><spellActive>Resonating Strike: </spellActive>Lee Sin dashes to the enemy hit by Sonic Wave, dealing {{ e2 }} <scaleAD>(+{{ a1 }})</scaleAD> physical damage plus {{ e3 }}% of their missing Health (Max: {{ e6 }} Damage vs. Monsters).\",\"leveltip\":{\"label\":[\"Sonic Wave Damage\",\"Resonating Strike Base Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[11,10,9,8,7],\"cooldownBurn\":\"11/10/9/8/7\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[50,80,110,140,170],[50,80,110,140,170],[8,8,8,8,8],[30,30,30,30,30],[1350,1350,1350,1350,1350],[400,400,400,400,400],[3,3,3,3,3],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/80/110/140/170\",\"50/80/110/140/170\",\"8\",\"30\",\"1350\",\"400\",\"3\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.9,\"key\":\"a1\"},{\"link\":\"bonusattackdamage\",\"coeff\":0.9,\"key\":\"a1\"}],\"costType\":\" Energy / {{ e4 }} Energy\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"BlindMonkQOne.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":144,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Energy / {{ e4 }} Energy\"},{\"id\":\"BlindMonkWOne\",\"name\":\"Safeguard / Iron Will\",\"description\":\"Safeguard: Lee Sin rushes to target ally, shielding himself from damage. If the ally is a champion, they are also shielded. After using Safeguard, Lee Sin can cast Iron Will for the next 3 seconds.<br>Iron Will: Lee Sin's intense training allows him to thrive in battle. For 4 seconds, Lee Sin gains Life Steal and Spell Vamp.\",\"tooltip\":\"<spellActive>Safeguard: </spellActive>Lee Sin rushes to target ally. If the ally is a champion, Lee Sin shields the ally and himself for {{ e1 }} <scaleAP>(+{{ a1 }})</scaleAP> damage for {{ e3 }} seconds and Safeguard's cooldown is reduced by {{ e6 }}%. <specialRules>After using Safeguard, Lee Sin can cast Iron Will for the next {{ e9 }} seconds.</specialRules><br><br><spellActive>Iron Will: </spellActive>Lee Sin gains {{ e2 }}% Life Steal and Spell Vamp for {{ e8 }} seconds.\",\"leveltip\":{\"label\":[\"Safeguard Shield Absorption\",\"Iron Will Lifesteal / Spell Vamp %\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[14,14,14,14,14],\"cooldownBurn\":\"14\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[40,100,160,220,280],[10,15,20,25,30],[2,2,2,2,2],[30,30,30,30,30],[5,5,5,5,5],[50,50,50,50,50],[0,0,0,0,0],[4,4,4,4,4],[3,3,3,3,3],[1350,1350,1350,1350,1350]],\"effectBurn\":[null,\"40/100/160/220/280\",\"10/15/20/25/30\",\"2\",\"30\",\"5\",\"50\",\"0\",\"4\",\"3\",\"1350\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\" Energy / {{ e4 }} Energy\",\"maxammo\":\"-1\",\"range\":[700,700,700,700,700],\"rangeBurn\":\"700\",\"image\":{\"full\":\"BlindMonkWOne.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":192,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Energy / {{ e4 }} Energy\"},{\"id\":\"BlindMonkEOne\",\"name\":\"Tempest / Cripple\",\"description\":\"Tempest: Lee Sin smashes the ground, sending out a shockwave that deals magic damage and reveals enemy units hit. If Tempest hits an enemy, Lee Sin can cast cripple for the next 3 seconds.<br>Cripple: Lee Sin cripples nearby enemies damaged by Tempest, reducing their Movement Speed for 4 seconds. Movement Speed recovers gradually over the duration.\",\"tooltip\":\"<spellActive>Tempest: </spellActive>Lee Sin smashes the ground, sending out a shockwave that deals {{ e1 }} <scaleAD>(+{{ a1 }})</scaleAD> magic damage. <specialRules>If Tempest hits an enemy, Lee Sin can cast Cripple for the next {{ e6 }} seconds.</specialRules><br><br><spellActive>Cripple: </spellActive>Lee Sin cripples nearby enemies struck by Tempest for {{ e5 }} seconds, slowing their Movement Speed by {{ e2 }}%. Movement Speed recovers gradually over the duration.\",\"leveltip\":{\"label\":[\"Tempest Damage\",\"Cripple Slow\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[10,10,10,10,10],\"cooldownBurn\":\"10\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[60,95,130,165,200],[20,30,40,50,60],[60,95,130,165,200],[30,30,30,30,30],[4,4,4,4,4],[3,3,3,3,3],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/95/130/165/200\",\"20/30/40/50/60\",\"60/95/130/165/200\",\"30\",\"4\",\"3\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Energy / {{ e4 }} Energy\",\"maxammo\":\"-1\",\"range\":[425,425,425,425,425],\"rangeBurn\":\"425\",\"image\":{\"full\":\"BlindMonkEOne.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":240,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Energy / {{ e4 }} Energy\"},{\"id\":\"BlindMonkRKick\",\"name\":\"Dragon's Rage\",\"description\":\"Lee Sin performs a powerful roundhouse kick launching his target back, dealing physical damage to the target and any enemies they collide with. Enemies the target collides with are knocked into the air for a short duration. This technique was taught to him by Jesse Perring, although Lee Sin does not kick players off the map.\",\"tooltip\":\"Lee Sin performs a powerful roundhouse kick knocking an enemy champion back and dealing {{ e1 }} <scaleAD>(+{{ a1 }})</scaleAD> physical damage.<br><br>Enemies the target collides with are knocked into the air briefly and take physical damage equal to {{ e1 }} <scaleAD>(+{{ a1 }})</scaleAD> plus {{ e3 }}% of the initial target's bonus health.</span>\",\"leveltip\":{\"label\":[\"Damage\",\"Bonus Health Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[110,85,60],\"cooldownBurn\":\"110/85/60\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[150,300,450],[75,125,175],[12,15,18],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"150/300/450\",\"75/125/175\",\"12/15/18\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":2,\"key\":\"a1\"},{\"link\":\"bonusattackdamage\",\"coeff\":2,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[375,375,375],\"rangeBurn\":\"375\",\"image\":{\"full\":\"BlindMonkRKick.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":288,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Leona\":{\"id\":89,\"key\":\"Leona\",\"name\":\"Leona\",\"title\":\"the Radiant Dawn\",\"spells\":[{\"id\":\"LeonaShieldOfDaybreak\",\"name\":\"Shield of Daybreak\",\"description\":\"Leona uses her shield to perform her next basic attack, dealing bonus magic damage and stunning the target.\",\"tooltip\":\"Next basic attack deals {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> bonus magic damage and stuns for {{ e1 }} second.\",\"leveltip\":{\"label\":[\"Damage\",\"Mana Cost\",\"Cooldown\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ cost }} -> {{ costNL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[9,8,7,6,5],\"cooldownBurn\":\"9/8/7/6/5\",\"cost\":[45,50,55,60,65],\"costBurn\":\"45/50/55/60/65\",\"effect\":[null,[1,1,1,1,1],[30,55,80,105,130],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"1\",\"30/55/80/105/130\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[100,100,100,100,100],\"rangeBurn\":\"100\",\"image\":{\"full\":\"LeonaShieldOfDaybreak.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":336,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"LeonaSolarBarrier\",\"name\":\"Eclipse\",\"description\":\"Leona raises her shield to gain Armor and Magic Resist. When the duration first ends, if there are nearby enemies, she will deal magic damage to them and prolong the duration of the effect.\",\"tooltip\":\"Grants {{ e2 }} <span class=\\\"colorFFFF00\\\">(+{{ f1 }})</span> bonus Armor and {{ e2 }} <span class=\\\"colorFF00FF\\\">(+{{ f2 }})</span> Magic Resist for {{ e3 }} seconds. When the effect ends, nearby enemies struck take {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and Leona retains her bonus Armor and Magic Resist for {{ e3 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Armor and Magic Resist\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[14,14,14,14,14],\"cooldownBurn\":\"14\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[60,100,140,180,220],[20,30,40,50,60],[3,3,3,3,3],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/100/140/180/220\",\"20/30/40/50/60\",\"3\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusarmor\",\"coeff\":0.2,\"key\":\"f1\"},{\"link\":\"bonusspellblock\",\"coeff\":0.2,\"key\":\"f2\"},{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[450,450,450,450,450],\"rangeBurn\":\"450\",\"image\":{\"full\":\"LeonaSolarBarrier.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":384,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"LeonaZenithBlade\",\"name\":\"Zenith Blade\",\"description\":\"Leona projects a solar image of her sword, dealing magic damage to all enemies in a line. When the image fades, the last enemy champion struck will be briefly immobilized and Leona will dash to them.\",\"tooltip\":\"Strikes all enemies in a line dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage. The last enemy Champion struck will be rooted for {{ e2 }} seconds and Leona will dash to them.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[13,12,11,10,9],\"cooldownBurn\":\"13/12/11/10/9\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[60,100,140,180,220],[0.5,0.5,0.5,0.5,0.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/100/140/180/220\",\"0.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[875,875,875,875,875],\"rangeBurn\":\"875\",\"image\":{\"full\":\"LeonaZenithBlade.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":432,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"LeonaSolarFlare\",\"name\":\"Solar Flare\",\"description\":\"Leona calls down a beam of solar energy, dealing damage to enemies in an area. Enemies in the center of the area are stunned, while enemies on the outside are slowed. Afterward, Leona's sword is charged with the power of the sun and deals bonus magic damage for a few attacks.\",\"tooltip\":\"Calls down a radiant beam of solar energy dealing {{ e4 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and slowing enemies by {{ e1 }}% for {{ e2 }} seconds. Enemies in the center of the flare are stunned instead of slowed.<br><br>Leona's sword remains charged with <span class=\\\"colorFFBB00\\\">Incandescence</span>, causing her next {{ e5 }} basic attacks to gain 100 range and deal {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> bonus magic damage on hit.\",\"leveltip\":{\"label\":[\"Damage\",\"Incandescence Damage\",\"Incandescent Attacks\",\"Cooldown\"],\"effect\":[\"{{ e4 }} -> {{ e4NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ e5 }} -> {{ e5NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[90,75,60],\"cooldownBurn\":\"90/75/60\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[80,80,80],[1.5,1.5,1.5],[30,40,50],[100,175,250],[3,4,5],[5,5,5],[100,100,100],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"80\",\"1.5\",\"30/40/50\",\"100/175/250\",\"3/4/5\",\"5\",\"100\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.15,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1200,1200,1200],\"rangeBurn\":\"1200\",\"image\":{\"full\":\"LeonaSolarFlare.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":0,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Lissandra\":{\"id\":127,\"key\":\"Lissandra\",\"name\":\"Lissandra\",\"title\":\"the Ice Witch\",\"spells\":[{\"id\":\"LissandraQ\",\"name\":\"Ice Shard\",\"description\":\"Throws a spear of ice that shatters when it hits an enemy, dealing magic damage and slowing Movement Speed. Shards pass through the target, dealing the same damage to other enemies hit.\",\"tooltip\":\"Throws a spear of ice that shatters when it hits an enemy, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and slowing Movement Speed by {{ e3 }}% for {{ e2 }} seconds. Shards then pass through the target, dealing the same damage to other enemies hit.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[6,5.25,4.5,3.75,3],\"cooldownBurn\":\"6/5.25/4.5/3.75/3\",\"cost\":[85,85,85,85,85],\"costBurn\":\"85\",\"effect\":[null,[70,100,130,160,190],[1.5,1.5,1.5,1.5,1.5],[16,19,22,25,28],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/100/130/160/190\",\"1.5\",\"16/19/22/25/28\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[725,725,725,725,725],\"rangeBurn\":\"725\",\"image\":{\"full\":\"LissandraQ.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":48,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"LissandraW\",\"name\":\"Ring of Frost\",\"description\":\"Freezes nearby enemies in ice, dealing magic damage and rooting them. \",\"tooltip\":\"Deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to nearby enemies and roots them for {{ e2 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Root Duration\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[14,13,12,11,10],\"cooldownBurn\":\"14/13/12/11/10\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[70,110,150,190,230],[1.1,1.2,1.3,1.4,1.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/110/150/190/230\",\"1.1/1.2/1.3/1.4/1.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[450,450,450,450,450],\"rangeBurn\":\"450\",\"image\":{\"full\":\"LissandraW.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":96,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"LissandraE\",\"name\":\"Glacial Path\",\"description\":\"Lissandra creates an ice claw that deals magic damage. Reactivating this ability transports Lissandra to the claw's current location.\",\"tooltip\":\"Casts an ice claw that deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemies hit.<br><br>Reactivating this ability transports Lissandra to the claw's current location.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[24,21,18,15,12],\"cooldownBurn\":\"24/21/18/15/12\",\"cost\":[80,85,90,95,100],\"costBurn\":\"80/85/90/95/100\",\"effect\":[null,[70,115,160,205,250],[14,13,12,11,10],[20,20,20,20,20],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/115/160/205/250\",\"14/13/12/11/10\",\"20\",\"1\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1050,1050,1050,1050,1050],\"rangeBurn\":\"1050\",\"image\":{\"full\":\"LissandraE.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":144,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"LissandraR\",\"name\":\"Frozen Tomb\",\"description\":\"If cast on an enemy champion, the target is frozen solid, stunning it. If cast on Lissandra, she encases herself in dark ice, healing herself while becoming untargetable and invulnerable. Dark ice then emanates from the target dealing magic damage to enemies and slowing Movement Speed.\",\"tooltip\":\"<span class=\\\"colorFF9999\\\">On Enemy Cast: </span>Freezes target champion solid, stunning it for {{ e4 }} seconds.<br><br><span class=\\\"colorCCFF99\\\">On Self Cast: </span>Lissandra encases herself in dark ice for {{ e5 }} seconds, healing for {{ e6 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span>, increased by {{ e7 }}% for each {{ e8 }}% Health she is missing. During this time Lissandra is untargetable and invulnerable but is unable to take any actions.<br><br>Dark ice then emanates from the target dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemies. The ice lasts for {{ e3 }} seconds and slows enemy Movement Speed by {{ e2 }}%.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow\",\"Heal\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ e6 }} -> {{ e6NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[130,105,80],\"cooldownBurn\":\"130/105/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[150,250,350],[30,45,75],[3,3,3],[1.5,1.5,1.5],[2.5,2.5,2.5],[100,150,200],[1,1,1],[1,1,1],[292,292,292],[0,0,0]],\"effectBurn\":[null,\"150/250/350\",\"30/45/75\",\"3\",\"1.5\",\"2.5\",\"100/150/200\",\"1\",\"1\",\"292\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[550,550,550],\"rangeBurn\":\"550\",\"image\":{\"full\":\"LissandraR.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":192,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Lucian\":{\"id\":236,\"key\":\"Lucian\",\"name\":\"Lucian\",\"title\":\"the Purifier\",\"spells\":[{\"id\":\"LucianQ\",\"name\":\"Piercing Light\",\"description\":\"Lucian shoots a bolt of piercing light through a target.\",\"tooltip\":\"Shoots a bolt of piercing light through an enemy unit, damaging enemies in a line for {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> ({{ e2 }}% of bonus Attack Damage) physical damage.<br><br><rules><span class=\\\"color8c8c8c\\\">Piercing Light's cast time decreases slightly as Lucian gains levels.</span></rules>\",\"leveltip\":{\"label\":[\"Damage\",\"Bonus Attack Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[9,8,7,6,5],\"cooldownBurn\":\"9/8/7/6/5\",\"cost\":[50,60,70,80,90],\"costBurn\":\"50/60/70/80/90\",\"effect\":[null,[80,115,150,185,220],[60,70,80,90,100],[900,900,900,900,900],[0.41,0.41,0.41,0.41,0.41],[100,100,100,100,100],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/115/150/185/220\",\"60/70/80/90/100\",\"900\",\"0.41\",\"100\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":[0.6,0.75,0.9,1.05,1.2],\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[500,500,500,500,500],\"rangeBurn\":\"500\",\"image\":{\"full\":\"LucianQ.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":240,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"LucianW\",\"name\":\"Ardent Blaze\",\"description\":\"Lucian shoots a missile that explodes in a star shape, marking enemies. Lucian gains Movement Speed for attacking marked enemies.\",\"tooltip\":\"Fires a shot that explodes upon enemy contact or reaching the end of its path. The explosion deals {{ e1 }} <span class=\\\"color88FF88\\\">(+{{ a1 }})</span> magic damage and marks enemies for 6 seconds.<br><br>When Lucian or his allies damage a marked target, Lucian gains {{ e2 }} Movement Speed for 1 second.\",\"leveltip\":{\"label\":[\"Damage\",\"Movement Speed\",\"Cooldown</br>\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[14,13,12,11,10],\"cooldownBurn\":\"14/13/12/11/10\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[60,100,140,180,220],[60,65,70,75,80],[900,900,900,900,900],[0,0,0,0,0],[1,1,1,1,1],[200,200,200,200,200],[1,1,1,1,1],[6,6,6,6,6],[1,1,1,1,1],[700,700,700,700,700]],\"effectBurn\":[null,\"60/100/140/180/220\",\"60/65/70/75/80\",\"900\",\"0\",\"1\",\"200\",\"1\",\"6\",\"1\",\"700\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.9,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[900,900,900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"LucianW.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":288,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"LucianE\",\"name\":\"Relentless Pursuit\",\"description\":\"Lucian quickly dashes a short distance. Lightslinger attacks reduce Relentless Pursuit's cooldown.\",\"tooltip\":\"Quickly dashes a short distance.<br><br>Whenever Lightslinger hits an enemy, Relentless Pursuit's cooldown is reduced by {{ e1 }} second (doubles to {{ e2 }} seconds against champions).\",\"leveltip\":{\"label\":[\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[18,17,16,15,14],\"cooldownBurn\":\"18/17/16/15/14\",\"cost\":[40,30,20,10,0],\"costBurn\":\"40/30/20/10/0\",\"effect\":[null,[1,1,1,1,1],[2,2,2,2,2],[425,425,425,425,425],[200,200,200,200,200],[1350,1350,1350,1350,1350],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"1\",\"2\",\"425\",\"200\",\"1350\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[445,445,445,445,445],\"rangeBurn\":\"445\",\"image\":{\"full\":\"LucianE.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":336,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"LucianR\",\"name\":\"The Culling\",\"description\":\"Lucian unleashes a torrent of shots from his weapons.\",\"tooltip\":\"Lucian moves freely while firing rapidly in a single direction for {{ e1 }} seconds. His shots collide with the first enemy they hit and each do {{ e2 }} <span class=\\\"color88FF88\\\">(+{{ a1 }})</span> <span class=\\\"colorFF8C00\\\">(+{{ a2 }})</span> physical damage.  The Culling does {{ e8 }}% damage to minions.<br><br>Lucian may use Relentless Pursuit during The Culling.<br><br>Reactivate The Culling to cancel early.\",\"leveltip\":{\"label\":[\"Damage\",\"Number of Shots\",\"Cooldown\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ e5 }} -> {{ e5NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[110,100,90],\"cooldownBurn\":\"110/100/90\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[3,3,3],[20,35,50],[2.5,3,3.5],[2.5,2.5,2.5],[20,25,30],[0.75,0.75,0.75],[1050,1050,1050],[400,400,400],[125,125,125],[0.25,0.25,0.25]],\"effectBurn\":[null,\"3\",\"20/35/50\",\"2.5/3/3.5\",\"2.5\",\"20/25/30\",\"0.75\",\"1050\",\"400\",\"125\",\"0.25\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.1,\"key\":\"a1\"},{\"link\":\"attackdamage\",\"coeff\":0.2,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1400,1400,1400],\"rangeBurn\":\"1400\",\"image\":{\"full\":\"LucianR.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":384,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Lulu\":{\"id\":117,\"key\":\"Lulu\",\"name\":\"Lulu\",\"title\":\"the Fae Sorceress\",\"spells\":[{\"id\":\"LuluQ\",\"name\":\"Glitterlance\",\"description\":\"Pix and Lulu each fire a bolt of magical energy that heavily slows all enemies it hits. An enemy can only be damaged by one bolt.\",\"tooltip\":\"Lulu and Pix each fire a piercing bolt dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to the first enemy hit and {{ f4 }}<span class=\\\"color99FF99\\\"> (+{{ f5 }})</span> to all additional enemies. Enemies hit are slowed by {{ e2 }}%, decaying over the next {{ e3 }} second(s).<br><br>An enemy can only be damaged for up to a total of <span class=\\\"color99FF99\\\">{{ f6 }}</span> damage per cast.\",\"leveltip\":{\"label\":[\"Damage\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[7,7,7,7,7],\"cooldownBurn\":\"7\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[80,125,170,215,260],[80,80,80,80,80],[2,2,2,2,2],[0,0,0,0,0],[0.7,0.7,0.7,0.7,0.7],[8,8,8,8,8],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/125/170/215/260\",\"80\",\"2\",\"0\",\"0.7\",\"8\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[925,925,925,925,925],\"rangeBurn\":\"925\",\"image\":{\"full\":\"LuluQ.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":432,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"LuluW\",\"name\":\"Whimsy\",\"description\":\"If cast on an ally, grants them Attack Speed and Movement Speed for a short time. If cast on an enemy, turns them into an adorable critter that can't attack or cast spells.\",\"tooltip\":\"<span class=\\\"colorCCFF99\\\">On Ally Cast: </span>Target ally gains {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span>% Movement Speed and {{ e7 }}% Attack Speed for {{ e5 }} seconds.<br><br><span class=\\\"colorFF9999\\\">On Enemy Cast: </span>Polymorphs an enemy champion for {{ e3 }} seconds, disabling their ability to attack or cast spells and reducing their base Movement Speed by {{ e4 }}.\",\"leveltip\":{\"label\":[\"Movement and Attack Speed Duration\",\"Attack Speed Bonus\",\"Polymorph Duration\",\"Cooldown\"],\"effect\":[\"{{ e5 }} -> {{ e5NL }}\",\"{{ e7 }}% -> {{ e7NL }}%\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[16,15,14,13,12],\"cooldownBurn\":\"16/15/14/13/12\",\"cost\":[65,65,65,65,65],\"costBurn\":\"65\",\"effect\":[null,[30,30,30,30,30],[0,0,0,0,0],[1.25,1.5,1.75,2,2.25],[60,60,60,60,60],[3,3.25,3.5,3.75,4],[0.01,0.01,0.01,0.01,0.01],[25,30,35,40,45],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30\",\"0\",\"1.25/1.5/1.75/2/2.25\",\"60\",\"3/3.25/3.5/3.75/4\",\"0.01\",\"25/30/35/40/45\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.05,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"LuluW.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":0,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"LuluE\",\"name\":\"Help, Pix!\",\"description\":\"If cast on an ally, commands Pix to jump to an ally and shield them. He then follows them and aids their attacks. If cast on an enemy, commands Pix to jump to an enemy and damage them. He then follows them and grants you vision of that enemy.\",\"tooltip\":\"<span class=\\\"colorCCFF99\\\">On Ally Cast: </span>Commands Pix to jump to an ally and then follow and aid their attacks instead of Lulu's for {{ e1 }} seconds. If the ally is a champion, Pix shields them from {{ e2 }}<span class=\\\"color99FF99\\\"> (+{{ a1 }})</span> damage for {{ e1 }} seconds.<br><br><span class=\\\"colorFF9999\\\">On Enemy Cast: </span>Pix deals {{ e4 }}<span class=\\\"color99FF99\\\"> (+{{ a2 }})</span> magic damage to target enemy unit. Pix then follows and grants <span class=\\\"coloree91d7\\\">True Sight</span> of them for {{ e6 }} seconds.</span> \",\"leveltip\":{\"label\":[\"Shield Amount\",\"Damage\",\"Mana Cost\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,10,10,10,10],\"cooldownBurn\":\"10\",\"cost\":[60,70,80,90,100],\"costBurn\":\"60/70/80/90/100\",\"effect\":[null,[6,6,6,6,6],[70,105,140,175,210],[50,50,50,50,50],[80,110,140,170,200],[25,25,25,25,25],[4,4,4,4,4],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"6\",\"70/105/140/175/210\",\"50\",\"80/110/140/170/200\",\"25\",\"4\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"LuluE.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":48,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"LuluR\",\"name\":\"Wild Growth\",\"description\":\"Lulu enlarges an ally, knocking nearby enemies into the air and granting the ally a large amount of bonus health. For the next few seconds, that ally gains an aura that slows nearby enemies.\",\"tooltip\":\"Lulu enlarges her ally, knocking nearby enemies into the air. For {{ e4 }} seconds, her ally gains {{ e1 }}<span class=\\\"color99FF99\\\"> (+{{ a1 }})</span> bonus health and slows nearby enemies by {{ e2 }}%.\",\"leveltip\":{\"label\":[\"Bonus Health\",\"Slow Percent\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[110,95,80],\"cooldownBurn\":\"110/95/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[300,450,600],[30,45,60],[1,1,1],[7,7,7],[50,50,50],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"300/450/600\",\"30/45/60\",\"1\",\"7\",\"50\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"LuluR.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":96,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Lux\":{\"id\":99,\"key\":\"Lux\",\"name\":\"Lux\",\"title\":\"the Lady of Luminosity\",\"spells\":[{\"id\":\"LuxLightBinding\",\"name\":\"Light Binding\",\"description\":\"Lux releases a sphere of light that binds and deals damage to up to two enemy units.\",\"tooltip\":\"Fires a ball of light, rooting up to two enemies for {{ e3 }} seconds and dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to each.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\" {{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[15,14,13,12,11],\"cooldownBurn\":\"15/14/13/12/11\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[50,100,150,200,250],[50,50,50,50,50],[2,2,2,2,2],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/100/150/200/250\",\"50\",\"2\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1175,1175,1175,1175,1175],\"rangeBurn\":\"1175\",\"image\":{\"full\":\"LuxLightBinding.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":144,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"LuxPrismaticWave\",\"name\":\"Prismatic Barrier\",\"description\":\"Lux throws her wand and bends the light around any friendly target it touches, protecting them from enemy damage.\",\"tooltip\":\"Throws Lux's wand in target direction protecting her and all allied Champions it touches from {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> damage for 3 seconds.<br><br>Upon reaching its destination it returns to Lux, protecting her and other allied Champions it touches from another {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> damage for 3 seconds.\",\"leveltip\":{\"label\":[\"Shield\",\"Cooldown\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[14,13,12,11,10],\"cooldownBurn\":\"14/13/12/11/10\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[2,4,6,8,10],[50,65,80,95,110],[3,3,3,3,3],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"2/4/6/8/10\",\"50/65/80/95/110\",\"3\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1075,1075,1075,1075,1075],\"rangeBurn\":\"1075\",\"image\":{\"full\":\"LuxPrismaticWave.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":192,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"LuxLightStrikeKugel\",\"name\":\"Lucent Singularity\",\"description\":\"Fires an anomaly of twisted light to an area, which slows nearby enemies. Lux can detonate it to damage enemies in the area of effect.\",\"tooltip\":\"Creates a zone that slows enemies by {{ e1 }}%. After {{ e3 }} seconds the zone detonates dealing {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.<br><br>Activate again to detonate early.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow Amount\",\"Mana Cost\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,10,10,10,10],\"cooldownBurn\":\"10\",\"cost\":[70,85,100,115,130],\"costBurn\":\"70/85/100/115/130\",\"effect\":[null,[25,30,35,40,45],[60,105,150,195,240],[5,5,5,5,5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"25/30/35/40/45\",\"60/105/150/195/240\",\"5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1100,1100,1100,1100,1100],\"rangeBurn\":\"1100\",\"image\":{\"full\":\"LuxLightStrikeKugel.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":240,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"LuxMaliceCannon\",\"name\":\"Final Spark\",\"description\":\"After gathering energy, Lux fires a beam of light that deals damage to all targets in the area. If Final Spark kills a champion, part of its cooldown is refunded. In addition, triggers Lux's passive ability and refreshes the Illumination debuff duration.\",\"tooltip\":\"Fires a dazzling ray of light dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to all enemies in a line. If Final Spark kills an enemy champion, {{ e3 }}% of its cooldown is refunded.<br><br>Final Spark ignites and refreshes the Illumination debuff. \",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown Refund\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[80,65,50],\"cooldownBurn\":\"80/65/50\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[300,400,500],[1,1,1],[10,30,50],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"300/400/500\",\"1\",\"10/30/50\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.75,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[3340,3340,3340],\"rangeBurn\":\"3340\",\"image\":{\"full\":\"LuxMaliceCannon.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":288,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Malphite\":{\"id\":54,\"key\":\"Malphite\",\"name\":\"Malphite\",\"title\":\"Shard of the Monolith\",\"spells\":[{\"id\":\"SeismicShard\",\"name\":\"Seismic Shard\",\"description\":\"Using his primal elemental magic, Malphite sends a shard of the earth through the ground at his foe, dealing damage upon impact and stealing Movement Speed for 4 seconds.\",\"tooltip\":\"Deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and steals {{ e2 }}% Movement Speed from the target for {{ e3 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Movement Speed\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[8,8,8,8,8],\"cooldownBurn\":\"8\",\"cost\":[70,75,80,85,90],\"costBurn\":\"70/75/80/85/90\",\"effect\":[null,[70,120,170,220,270],[14,17,20,23,26],[4,4,4,4,4],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/120/170/220/270\",\"14/17/20/23/26\",\"4\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[625,625,625,625,625],\"rangeBurn\":\"625\",\"image\":{\"full\":\"SeismicShard.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":336,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"Obduracy\",\"name\":\"Brutal Strikes\",\"description\":\"Malphite starts to hit with such force that his attacks deal damage to all units in front of him. Passively increases his Armor.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Malphite's armor is increased by {{ e1 }}% (<span class=\\\"colorFFFF00\\\">{{ f1 }}</span>).<br><br><span class=\\\"colorFF9900\\\">Active: </span>Basic attacks deal an additional {{ e2 }} <span class=\\\"colorFFFF00\\\">(+{{ f2 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> physical damage to the target and nearby enemies. Lasts {{ e3 }} seconds.\",\"leveltip\":{\"label\":[\"Armor\",\"Damage\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[12,12,12,12,12],\"cooldownBurn\":\"12\",\"cost\":[25,25,25,25,25],\"costBurn\":\"25\",\"effect\":[null,[15,20,25,30,35],[15,30,45,60,75],[6,6,6,6,6],[225,225,225,225,225],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"15/20/25/30/35\",\"15/30/45/60/75\",\"6\",\"225\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[400,400,400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"Obduracy.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":384,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"Landslide\",\"name\":\"Ground Slam\",\"description\":\"Malphite slams the ground, sending out a shockwave that deals magic damage based on his Armor as damage and reduces the Attack Speed of enemies for a short duration.\",\"tooltip\":\"Malphite slams the ground dealing {{ e2 }} <span class=\\\"colorFFFF00\\\">(+{{ f1 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to surrounding enemies, reducing their Attack Speed by {{ e1 }}% for {{ e4 }} seconds.<br><br>This ability gains damage equal to {{ e3 }}% of Malphite's Armor.\",\"leveltip\":{\"label\":[\"Damage\",\"Attack Speed Reduction\",\"Mana Cost\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[7,7,7,7,7],\"cooldownBurn\":\"7\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[30,35,40,45,50],[60,95,130,165,200],[40,40,40,40,40],[3,3,3,3,3],[6,6,6,6,6],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30/35/40/45/50\",\"60/95/130/165/200\",\"40\",\"3\",\"6\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"armor\",\"coeff\":0.3,\"key\":\"f1\"},{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[400,400,400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"Landslide.png\",\"sprite\":\"spell6.png\",\"group\":\"spell\",\"x\":432,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"UFSlash\",\"name\":\"Unstoppable Force\",\"description\":\"Malphite ferociously charges to a location, damaging enemies and knocking them into the air.\",\"tooltip\":\"Malphite charges to target area. Upon his arrival he deals {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to nearby enemies and knocks them into the air for {{ e3 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[130,115,100],\"cooldownBurn\":\"130/115/100\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[1.5,1.75,2],[200,300,400],[1.5,1.5,1.5],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"1.5/1.75/2\",\"200/300/400\",\"1.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"UFSlash.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":0,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Malzahar\":{\"id\":90,\"key\":\"Malzahar\",\"name\":\"Malzahar\",\"title\":\"the Prophet of the Void\",\"spells\":[{\"id\":\"MalzaharQ\",\"name\":\"Call of the Void\",\"description\":\"Malzahar opens up two portals to the Void. After a short delay, they fire projectiles that deal Magic Damage and silence enemy champions.\",\"tooltip\":\"Malzahar opens two portals to the Void that fire projectiles inward, dealing {{ e1 }} <scaleAP>(+{{ a1 }})</scaleAP> magic damage and silencing enemies hit for {{ e2 }} second(s).\",\"leveltip\":{\"label\":[\"Damage\",\"Silence Duration\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[80,80,80,80,80],\"costBurn\":\"80\",\"effect\":[null,[70,110,150,190,230],[1,1.25,1.5,1.75,2],[0.4,0.4,0.4,0.4,0.4],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/110/150/190/230\",\"1/1.25/1.5/1.75/2\",\"0.4\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[900,900,900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"MalzaharQ.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":48,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"MalzaharW\",\"name\":\"Void Swarm\",\"description\":\"Malzahar summons Voidlings to attack nearby enemies.\",\"tooltip\":\"<span class=\\\"colorFF8C00\\\">Passive:</span> Casting Malzahar's other spells gives him Gathering Swarm, increasing the number of Voidlings summoned by Void Swarm (max {{ e6 }}).<br><br><span class=\\\"colorFF8C00\\\">Active:</span> Summons one or more Voidlings. Voidlings last {{ e1 }} seconds and deal an additional {{ e2 }} <scaleAP>(+{{ a1 }})</scaleAP> <scaleAD>(+{{ a2 }})</scaleAD> magic damage each hit.<br><br><span class=\\\"color919191\\\"><i>Voidlings deal {{ e3 }}% damage to lane minions affected by Malefic Visions.<br>Voidlings deal {{ e4 }}% damage to epic monsters.</i></span>\",\"leveltip\":{\"label\":[\"Voidling Bonus Damage\",\"Voidling Duration\",\"Cost\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ e1 }} -> {{ e1NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[8,8,8,8,8],\"cooldownBurn\":\"8\",\"cost\":[40,45,50,55,60],\"costBurn\":\"40/45/50/55/60\",\"effect\":[null,[8,8,9,9,10],[12,14,16,18,20],[300,300,300,300,300],[50,50,50,50,50],[25000,25000,25000,25000,25000],[2,2,2,2,2],[0.5,0.5,0.5,0.5,0.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"8/8/9/9/10\",\"12/14/16/18/20\",\"300\",\"50\",\"25000\",\"2\",\"0.5\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"},{\"link\":\"bonusattackdamage\",\"coeff\":0.4,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"0\",\"range\":[150,150,150,150,150],\"rangeBurn\":\"150\",\"image\":{\"full\":\"MalzaharW.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":96,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"MalzaharE\",\"name\":\"Malefic Visions\",\"description\":\"Malzahar infects his target's mind with cruel visions of their demise, dealing damage over time. Using Malzahar's other spells on the target will refresh the visions.<br><br>If the target dies while afflicted by the visions, they pass on to a nearby enemy unit and Malzahar gains Mana. Malzahar's Voidlings are attracted to affected units.\",\"tooltip\":\"Deal {{ e1 }} <scaleAP>(+{{ a1 }})</scaleAP> magic damage to an enemy target over {{ e3 }} seconds. Applying Call of the Void or Nether Grasp to the victim during this time refreshes the visions.<br><br>If the victim is killed, Malzahar gains <scaleMana>{{ f1 }}</scaleMana> Mana ({{ e5 }}% of max mana) and the visions spread to the nearest enemy.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Cooldown\",\"Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[15,13,11,9,7],\"cooldownBurn\":\"15/13/11/9/7\",\"cost\":[60,70,80,90,100],\"costBurn\":\"60/70/80/90/100\",\"effect\":[null,[80,115,150,185,220],[8,8,8,8,8],[4,4,4,4,4],[8,8,8,8,8],[2,2,2,2,2],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/115/150/185/220\",\"8\",\"4\",\"8\",\"2\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"MalzaharE.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":144,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"MalzaharR\",\"name\":\"Nether Grasp\",\"description\":\"Malzahar channels the essence of the Void to suppress an enemy champion over a zone of damaging negative energy.\",\"tooltip\":\"Malzahar suppresses a target champion for {{ e4 }} seconds, dealing {{ e7 }} <scaleAP>(+{{ a2 }})</scaleAP> magic damage over the duration. A zone of negative energy is created around his target for {{ e3 }} seconds, dealing {{ e1 }}% <scaleAP>(+{{ a1 }}%)</scaleAP> of nearby enemies max health as magic damage per second.\",\"leveltip\":{\"label\":[\"Nether Grasp Damage\",\"Null Zone Damage\",\"Cooldown\"],\"effect\":[\"{{ e7 }} -> {{ e7NL }}\",\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,100,80],\"cooldownBurn\":\"120/100/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[2,3,4],[120,100,80],[5,5,5],[2.5,2.5,2.5],[120,120,120],[10,10,10],[125,250,375],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"2/3/4\",\"120/100/80\",\"5\",\"2.5\",\"120\",\"10\",\"125/250/375\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1.15,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.005,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[700,700,700],\"rangeBurn\":\"700\",\"image\":{\"full\":\"MalzaharR.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":192,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Maokai\":{\"id\":57,\"key\":\"Maokai\",\"name\":\"Maokai\",\"title\":\"the Twisted Treant\",\"spells\":[{\"id\":\"MaokaiQ\",\"name\":\"Bramble Smash\",\"description\":\"Maokai knocks back nearby enemies with a shockwave, dealing magic damage and slowing them.\",\"tooltip\":\"Maokai smashes his fist into the ground, releasing a shockwave. Nearby enemies are knocked away from him and all affected enemies take {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and are slowed briefly.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[8,7.25,6.5,5.75,5],\"cooldownBurn\":\"8/7.25/6.5/5.75/5\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[70,115,160,205,250],[325,325,325,325,325],[99,99,99,99,99],[0.25,0.25,0.25,0.25,0.25],[750,750,750,750,750],[300,300,300,300,300],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/115/160/205/250\",\"325\",\"99\",\"0.25\",\"750\",\"300\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"MaokaiQ.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":240,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"MaokaiW\",\"name\":\"Twisted Advance\",\"description\":\"Maokai contorts into a mass of moving roots, becoming untargetable and dashing to the target. Upon arrival, he roots the target.\",\"tooltip\":\"Maokai transforms into a moving mass of roots, becoming untargetable and dashing to the target.<br><br>Upon arrival, he deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and roots the target for {{ e2 }} second(s).\",\"leveltip\":{\"label\":[\"Damage\",\"Root Duration\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[13,12,11,10,9],\"cooldownBurn\":\"13/12/11/10/9\",\"cost\":[60,65,70,75,80],\"costBurn\":\"60/65/70/75/80\",\"effect\":[null,[50,75,100,125,150],[1,1.1,1.2,1.3,1.4],[0,0,0,0,0],[0,0,0,0,0],[1300,1300,1300,1300,1300],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/75/100/125/150\",\"1/1.1/1.2/1.3/1.4\",\"0\",\"0\",\"1300\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[525,525,525,525,525],\"rangeBurn\":\"525\",\"image\":{\"full\":\"MaokaiW.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":288,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"MaokaiE\",\"name\":\"Sapling Toss\",\"description\":\"Maokai flings a sapling to the target area to stand guard. More effective in brush.\",\"tooltip\":\"Maokai flings a sapling, which stands watch for {{ f1 }} seconds. Saplings will chase nearby enemies, detonating on proximity, dealing {{ e1 }} <span class=\\\"colorCC3300\\\">+{{ e8 }}% <span class=\\\"color99FF99\\\">[+{{ a1 }}%]</span> Target Max Health</span> magic damage and slowing enemies struck by {{ e5 }}% for {{ e6 }} seconds.<br><br>Saplings placed in brush last for <span class=\\\"colorCC3300\\\">{{ f2 }}</span> seconds and cause a larger explosion, dealing double damage over {{ e6 }} seconds to all enemies hit.<br><br><rules><span class=\\\"color8c8c8c\\\">Maximum {{ e4 }} Damage to non-Champions, doubled for brush.</span></rules>\",\"leveltip\":{\"label\":[\"Base Damage\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[11,11,11,11,11],\"cooldownBurn\":\"11\",\"cost\":[60,65,70,75,80],\"costBurn\":\"60/65/70/75/80\",\"effect\":[null,[25,50,75,100,125],[0.5,0.5,0.5,0.5,0.5],[2,2,2,2,2],[300,300,300,300,300],[35,35,35,35,35],[2,2,2,2,2],[2,2,2,2,2],[8,8,8,8,8],[0,0,0,0,0],[550,550,550,550,550]],\"effectBurn\":[null,\"25/50/75/100/125\",\"0.5\",\"2\",\"300\",\"35\",\"2\",\"2\",\"8\",\"0\",\"550\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.01,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1100,1100,1100,1100,1100],\"rangeBurn\":\"1100\",\"image\":{\"full\":\"MaokaiE.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":336,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"MaokaiR\",\"name\":\"Nature's Grasp\",\"description\":\"Maokai summons a colossal wall of brambles and thorns that slowly advances forwards, damaging and rooting any enemies in the path.\",\"tooltip\":\"Maokai summons a colossal wall of brambles and thorns that slowly advances forwards, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and rooting any enemies struck for ({{ e2 }} to {{ e2 }}) seconds, increasing with distance travelled.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,110,100],\"cooldownBurn\":\"120/110/100\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[150,225,300],[2.4,2.4,2.4],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"150/225/300\",\"2.4\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.75,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[2500,2500,2500],\"rangeBurn\":\"2500\",\"image\":{\"full\":\"MaokaiR.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":384,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"MasterYi\":{\"id\":11,\"key\":\"MasterYi\",\"name\":\"Master Yi\",\"title\":\"the Wuju Bladesman\",\"spells\":[{\"id\":\"AlphaStrike\",\"name\":\"Alpha Strike\",\"description\":\"Master Yi teleports across the battlefield with blinding speed, dealing physical damage to multiple units in his path, while simultaneously becoming untargetable. Alpha Strike can critically strike and deals bonus physical damage to minions and monsters. Basic attacks reduce Alpha Strike's cooldown.\",\"tooltip\":\"Master Yi teleports to strike up to {{ e8 }} enemies, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage, with an additional {{ e3 }} damage to minions and monsters. During Alpha Strike Master Yi is untargetable.<br><br>Alpha Strike can critically strike, dealing an additional <span class=\\\"colorFF8C00\\\">{{ f1 }}</span> physical damage. Basic attacks lower the cooldown of Alpha Strike by {{ e7 }} second.\",\"leveltip\":{\"label\":[\"Damage\",\"Bonus Minion/Monster Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[18,17,16,15,14],\"cooldownBurn\":\"18/17/16/15/14\",\"cost\":[70,80,90,100,110],\"costBurn\":\"70/80/90/100/110\",\"effect\":[null,[25,60,95,130,165],[50,50,50,50,50],[75,100,125,150,175],[1,1,1,1,1],[0.25,0.25,0.25,0.25,0.25],[0.6,0.6,0.6,0.6,0.6],[1,1,1,1,1],[4,4,4,4,4],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"25/60/95/130/165\",\"50\",\"75/100/125/150/175\",\"1\",\"0.25\",\"0.6\",\"1\",\"4\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1,\"key\":\"a1\"},{\"link\":\"attackdamage\",\"coeff\":0.6,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"AlphaStrike.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":432,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"Meditate\",\"name\":\"Meditate\",\"description\":\"Master Yi rejuvenates his body by focus of mind, restoring Health and taking reduced damage for a short time. In addition, Master Yi will gain stacks of Double Strike and pause the remaining duration on Wuju Style and Highlander for each second he channels.\",\"tooltip\":\"Master Yi channels, restoring {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Health per second for 4 seconds. This healing is increased by {{ e4 }}% for every {{ e5 }}% of Master Yi's missing Health.<br><br>While channeling, Master Yi reduces incoming damage by {{ e3 }}%. This damage reduction is halved against turrets. <br><br>In addition, Master Yi will gain stacks of Double Strike and pause the remaining duration on Wuju Style and Highlander for each second he channels.\",\"leveltip\":{\"label\":[\"Damage Reduction\",\"Health Restored\"],\"effect\":[\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":5,\"cooldown\":[35,35,35,35,35],\"cooldownBurn\":\"35\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[30,50,70,90,110],[100,150,200,250,300],[50,55,60,65,70],[1,1,1,1,1],[1,1,1,1,1],[50,50,50,50,50],[4,4,4,4,4],[0.5,0.5,0.5,0.5,0.5],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30/50/70/90/110\",\"100/150/200/250/300\",\"50/55/60/65/70\",\"1\",\"1\",\"50\",\"4\",\"0.5\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.25,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[20,20,20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"Meditate.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":0,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"WujuStyle\",\"name\":\"Wuju Style\",\"description\":\"Master Yi becomes skilled in the art of Wuju, passively increasing his Attack Damage. Activating Wuju Style grants bonus true damage on basic attacks, but the passive bonus is then lost while on cooldown.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> Grants {{ e1 }}% <span class=\\\"colorFF8C00\\\">({{ f1 }})</span> Attack Damage.<br><br><span class=\\\"colorFF9900\\\">Active:</span> Basic attacks deal {{ e3 }} <span class=\\\"colorFF8C00\\\">(+{{ f2 }})</span> bonus true damage for {{ e5 }} seconds. Afterwards the passive bonus is lost while Wuju Style is on cooldown.\",\"leveltip\":{\"label\":[\"Active Damage\",\"Cooldown\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[18,17,16,15,14],\"cooldownBurn\":\"18/17/16/15/14\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[10,10,10,10,10],[25,25,25,25,25],[14,23,32,41,50],[20,30,40,50,60],[5,5,5,5,5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"10\",\"25\",\"14/23/32/41/50\",\"20/30/40/50/60\",\"5\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":[0.1,0.125,0.15,0.175,0.2],\"key\":\"f2\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[20,20,20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"WujuStyle.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":48,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"Highlander\",\"name\":\"Highlander\",\"description\":\"Master Yi moves with unparalleled agility, temporarily increasing his Movement and Attack Speeds as well as making him immune to all slowing effects. While active, Champion kills or assists extend Highlander's duration. Passively reduces cooldown for his other abilities on a kill or assist.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> Champion kills and assists reduce the remaining cooldown of Master Yi's basic abilities by {{ e5 }}%.<br><br><span class=\\\"colorFF9900\\\">Active:</span> Increases Movement Speed by {{ e3 }}%, Attack Speed by {{ e2 }}%, and grants immunity to slows for {{ e1 }} seconds. While active, champion kills and assists extend the duration of Highlander by {{ e4 }} seconds.\",\"leveltip\":{\"label\":[\"Attack Speed\",\"Movement Speed\"],\"effect\":[\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ e3 }}% -> {{ e3NL }}%\"]},\"maxrank\":3,\"cooldown\":[85,85,85],\"cooldownBurn\":\"85\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[7,7,7],[30,55,80],[25,35,45],[7,7,7],[70,70,70],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"7\",\"30/55/80\",\"25/35/45\",\"7\",\"70\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1,1,1],\"rangeBurn\":\"1\",\"image\":{\"full\":\"Highlander.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":96,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"MissFortune\":{\"id\":21,\"key\":\"MissFortune\",\"name\":\"Miss Fortune\",\"title\":\"the Bounty Hunter\",\"spells\":[{\"id\":\"MissFortuneRicochetShot\",\"name\":\"Double Up\",\"description\":\"Miss Fortune fires a bullet at an enemy, damaging them and a target behind them. Both strikes can also apply Love Tap.\",\"tooltip\":\"Miss Fortune fires a bouncing shot through an enemy, dealing {{ e2 }} <span class=\\\"colorFF8C00\\\">(+{{ a2 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> physical damage to each target hit. Both apply on-hit effects.<br><br>The second shot can critically strike for {{ f1 }}% damage, and it always critically strikes if the first shot kills its target.\",\"leveltip\":{\"label\":[\"Mana Cost\",\"Cooldown\",\"Damage\"],\"effect\":[\"{{ cost }} -> {{ costNL }} \",\"{{ cooldown }} -> {{ cooldownNL }} \",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[7,6,5,4,3],\"cooldownBurn\":\"7/6/5/4/3\",\"cost\":[43,46,49,52,55],\"costBurn\":\"43/46/49/52/55\",\"effect\":[null,[1,1,1,1,1],[20,40,60,80,100],[1,1,1,1,1],[40,70,100,130,160],[40,40,40,40,40],[20,20,20,20,20],[40,40,40,40,40],[110,110,110,110,110],[160,160,160,160,160],[0,0,0,0,0]],\"effectBurn\":[null,\"1\",\"20/40/60/80/100\",\"1\",\"40/70/100/130/160\",\"40\",\"20\",\"40\",\"110\",\"160\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.35,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"MissFortuneRicochetShot.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":144,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"MissFortuneViciousStrikes\",\"name\":\"Strut\",\"description\":\"Miss Fortune passively gains Movement Speed when not attacked. This ability can be activated to grant bonus Attack Speed for a short duration. While it's on cooldown, Love Taps reduce the remaining cooldown of Strut.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> After 5 seconds of not taking direct damage, Miss Fortune gains {{ e5 }} Movement Speed. After another 5 seconds, this bonus increases to {{ e2 }}.<br><br><span class=\\\"colorFF9900\\\">Active:</span> Fully activates Strut's Movement Speed and grants {{ e1 }}% Attack Speed for {{ e3 }} seconds.<br><br>Love Taps reduce the cooldown of Strut by {{ f2 }} seconds.\",\"leveltip\":{\"label\":[\"Max Movement Speed\",\"Attack Speed\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ e1 }}% -> {{ e1NL }}%\"]},\"maxrank\":5,\"cooldown\":[12,12,12,12,12],\"cooldownBurn\":\"12\",\"cost\":[30,30,30,30,30],\"costBurn\":\"30\",\"effect\":[null,[40,55,70,85,100],[60,70,80,90,100],[4,4,4,4,4],[2,2,2,2,2],[25,25,25,25,25],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"40/55/70/85/100\",\"60/70/80/90/100\",\"4\",\"2\",\"25\",\"1\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"MissFortuneViciousStrikes.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":192,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"MissFortuneScattershot\",\"name\":\"Make It Rain\",\"description\":\"Miss Fortune reveals an area with a flurry of bullets, dealing waves of damage to opponents and slowing them.\",\"tooltip\":\"Miss Fortune reveals an area, raining down bullets that deal {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage over 2 seconds and slow enemies hit by {{ e2 }}%.\",\"leveltip\":{\"label\":[\"Cooldown\",\"Damage\",\"Slow\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }} \",\"{{ e1 }} -> {{ e1NL }} \",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[14,13,12,11,10],\"cooldownBurn\":\"14/13/12/11/10\",\"cost\":[80,80,80,80,80],\"costBurn\":\"80\",\"effect\":[null,[80,115,150,185,220],[28,36,44,52,60],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/115/150/185/220\",\"28/36/44/52/60\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"MissFortuneScattershot.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":240,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"MissFortuneBulletTime\",\"name\":\"Bullet Time\",\"description\":\"Miss Fortune channels a flurry of bullets into a cone in front of her, dealing large amounts of damage to enemies. Each wave of Bullet Time can critically strike.\",\"tooltip\":\"Miss Fortune channels a barrage of bullets for {{ e3 }} seconds, dealing <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> physical damage per wave ({{ e2 }} waves total).<br><br>Each wave of Bullet Time can critically strike for {{ f3 }}% damage.<br><br><span class=\\\"colorF50F00\\\">Total Damage: {{ f2 }}</span>\",\"leveltip\":{\"label\":[\"Cooldown\",\"Waves Fired\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }} \",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":3,\"cooldown\":[120,110,100],\"cooldownBurn\":\"120/110/100\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[0,0,0],[12,14,16],[3,3,3],[75,75,75],[120,120,120],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"0\",\"12/14/16\",\"3\",\"75\",\"120\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.35,\"key\":\"f1\"},{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"MissFortuneBulletTime.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":288,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"MonkeyKing\":{\"id\":62,\"key\":\"MonkeyKing\",\"name\":\"Wukong\",\"title\":\"the Monkey King\",\"spells\":[{\"id\":\"MonkeyKingDoubleAttack\",\"name\":\"Crushing Blow\",\"description\":\"Wukong's next attack deals additional physical damage, gains range, and reduces the enemy's Armor for a short duration.\",\"tooltip\":\"Wukong's next attack gains {{ e4 }} range, deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> bonus physical damage and reduces the enemy's Armor by {{ e2 }}% for {{ e3 }} seconds.\",\"leveltip\":{\"label\":[\"Bonus Damage\",\"Armor Reduction\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} ->{{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[9,8,7,6,5],\"cooldownBurn\":\"9/8/7/6/5\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[30,60,90,120,150],[10,15,20,25,30],[3,3,3,3,3],[125,125,125,125,125],[6,6,6,6,6],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30/60/90/120/150\",\"10/15/20/25/30\",\"3\",\"125\",\"6\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":0.1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"MonkeyKingDoubleAttack.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":336,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"MonkeyKingDecoy\",\"name\":\"Decoy\",\"description\":\"Wukong becomes Invisible for a short duration, leaving behind a decoy that will deal Magic Damage to enemies near it when Wukong's stealth expires.\",\"tooltip\":\"Wukong becomes <span class=\\\"color91d7ee\\\">Invisible</span> for {{ e2 }} seconds, leaving behind a decoy that will deal {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Magic Damage to enemies near it after {{ e2 }} seconds. <br><br><u><span class=\\\"size16 color91d7ee\\\">16\",\"leveltip\":{\"label\":[\"Base Damage\",\"Cooldown\",\"Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} ->{{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[18,16,14,12,10],\"cooldownBurn\":\"18/16/14/12/10\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[70,115,160,205,250],[1.5,1.5,1.5,1.5,1.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/115/160/205/250\",\"1.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[325,325,325,325,325],\"rangeBurn\":\"325\",\"image\":{\"full\":\"MonkeyKingDecoy.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":384,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"MonkeyKingNimbus\",\"name\":\"Nimbus Strike\",\"description\":\"Wukong dashes to target enemy and sends out images to attack up to 2 additional enemies near his target, dealing physical damage to each enemy struck.\",\"tooltip\":\"Wukong dashes to target enemy and sends out images to attack up to 2 additional enemies nearby. Each enemy struck takes {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage. Wukong then gains {{ e2 }}% Attack Speed for {{ e3 }} seconds.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Attack Speed\",\"Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cost }} ->{{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[8,8,8,8,8],\"cooldownBurn\":\"8\",\"cost\":[45,50,55,60,65],\"costBurn\":\"45/50/55/60/65\",\"effect\":[null,[60,105,150,195,240],[30,35,40,45,50],[4,4,4,4,4],[1050,1050,1050,1050,1050],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/105/150/195/240\",\"30/35/40/45/50\",\"4\",\"1050\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"MonkeyKingNimbus.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":432,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"MonkeyKingSpinToWin\",\"name\":\"Cyclone\",\"description\":\"Wukong's staff grows outward and he spins it around, dealing damage and knocking up enemies. Wukong gains Movement Speed over the duration of the spell.\",\"tooltip\":\"Wukong's staff grows outward and he spins it around for 4 seconds, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage per second to nearby enemies, knocking them up for {{ e4 }} second the first time they get hit.<br><br>At the start, Wukong gains {{ e2 }}% Movement Speed, and he gains an additional {{ e3 }}% per second over the duration.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} ->{{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,105,90],\"cooldownBurn\":\"120/105/90\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[20,110,200],[15,15,15],[10,10,10],[1,1,1],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"20/110/200\",\"15\",\"10\",\"1\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1.1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[315,315,315],\"rangeBurn\":\"315\",\"image\":{\"full\":\"MonkeyKingSpinToWin.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":0,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Mordekaiser\":{\"id\":82,\"key\":\"Mordekaiser\",\"name\":\"Mordekaiser\",\"title\":\"the Iron Revenant\",\"spells\":[{\"id\":\"MordekaiserMaceOfSpades\",\"name\":\"Mace of Spades\",\"description\":\"Mordekaiser's next three attacks deal escalating bonus damage.\",\"tooltip\":\"Mordekaiser's next three hits are empowered. The first two strikes deal {{ e3 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> <span class=\\\"color99FF99\\\">(+{{ f2 }})</span> bonus magic damage. The final strike deals {{ e9 }} times the bonus damage of the previous strikes, up to {{ f5 }} <span class=\\\"colorFF8C00\\\">(+{{ f3 }})</span> <span class=\\\"color99FF99\\\">(+{{ f4 }})</span>.\",\"leveltip\":{\"label\":[\"Bonus Damage\",\"Attack Damage Ratio\",\"Cooldown\",\"Health Cost\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ e7 }} -> {{ e7NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e0 }} -> {{ e0NL }}\"]},\"maxrank\":5,\"cooldown\":[10,8.5,7,5.5,4],\"cooldownBurn\":\"10/8.5/7/5.5/4\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[132,181.5,231,280.5,330],[10,15,20,25,30],[10,20,30,40,50],[4,4,4,4,4],[30,60,90,120,150],[20,225,250,275,300],[0.5,0.6,0.7,0.8,0.9],[0.6,0.6,0.6,0.6,0.6],[2,2,2,2,2],[20,23,26,29,32]],\"effectBurn\":[null,\"132/181.5/231/280.5/330\",\"10/15/20/25/30\",\"10/20/30/40/50\",\"4\",\"30/60/90/120/150\",\"20/225/250/275/300\",\"0.5/0.6/0.7/0.8/0.9\",\"0.6\",\"2\",\"20/23/26/29/32\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1.65,\"key\":\"f1\"},{\"link\":\"bonusattackdamage\",\"coeff\":1,\"key\":\"f2\"}],\"costType\":\" Health\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"MordekaiserMaceOfSpades.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":48,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ e0 }} Health\"},{\"id\":\"MordekaiserCreepingDeathCast\",\"name\":\"Harvesters of Sorrow\",\"description\":\"Coats an ally in magnetic metal, increasing each unit's movement speed toward one another. While near one another, the metal swirls violently dealing damage per second to enemies.\",\"tooltip\":\"<span class=\\\"size18 colorFF9900\\\">18\",\"leveltip\":{\"label\":[\"Damage\",\"Reactivation Damage\",\"Cooldown\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ e6 }} -> {{ e6NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[12,11,10,9,8],\"cooldownBurn\":\"12/11/10/9/8\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[20,30,40,50,60],[140,180,220,260,300],[25,35,45,55,65],[1,1,1,1,1],[75,75,75,75,75],[50,85,120,155,190],[25,25,25,25,25],[2,2,2,2,2],[50,50,50,50,50],[0,0,0,0,0]],\"effectBurn\":[null,\"20/30/40/50/60\",\"140/180/220/260/300\",\"25/35/45/55/65\",\"1\",\"75\",\"50/85/120/155/190\",\"25\",\"2\",\"50\",\"0\"],\"vars\":[],\"costType\":\" Health\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"MordekaiserCreepingDeathCast.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":96,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ e3 }} Health\"},{\"id\":\"MordekaiserSyphonOfDestruction\",\"name\":\"Siphon of Destruction\",\"description\":\"Mordekaiser deals damage to enemies in a cone in front of him. For each unit hit, Mordekaiser's shield absorbs energy.\",\"tooltip\":\"Mordekaiser deals {{ e3 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span><span class=\\\"color99FF99\\\"> (+{{ a1 }})</span> magic damage in a cone.<span class=\\\"size8\\\"><br><br></span>For each Champion hit, he gains {{ e5 }}% maximum shield.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"% Max Shield From Champions\",\"Health Cost\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e5 }}% -> {{ e5NL }}%\",\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":5,\"cooldown\":[6,5.75,5.5,5.25,5],\"cooldownBurn\":\"6/5.75/5.5/5.25/5\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[24,36,48,60,72],[1,2,3,4,5],[35,65,95,125,155],[4,4,4,4,4],[15,17.5,20,22.5,25],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"24/36/48/60/72\",\"1/2/3/4/5\",\"35/65/95/125/155\",\"4\",\"15/17.5/20/22.5/25\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":0.6,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Health\",\"maxammo\":\"-1\",\"range\":[700,700,700,700,700],\"rangeBurn\":\"700\",\"image\":{\"full\":\"MordekaiserSyphonOfDestruction.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":144,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ e1 }} Health\"},{\"id\":\"MordekaiserChildrenOfTheGrave\",\"name\":\"Children of the Grave\",\"description\":\"Mordekaiser curses an enemy champion or the Dragon, stealing a percent of their life initially and each second. If the target dies while the spell is active, their soul is enslaved and will follow Mordekaiser as a ghost.\",\"tooltip\":\"<span class=\\\"size18 colorFF9900\\\">18\",\"leveltip\":{\"label\":[\"Total Health Stolen\",\"Cooldown\",\"Ghost Lifetime\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e9 }} -> {{ e9NL }}\"]},\"maxrank\":3,\"cooldown\":[120,105,90],\"cooldownBurn\":\"120/105/90\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[25,30,35],[55,65,75],[75,150,225],[2,2,2],[3,3,3],[10,25,50],[30,30,30],[25,25,25],[45,60,75],[2000,2000,2000]],\"effectBurn\":[null,\"25/30/35\",\"55/65/75\",\"75/150/225\",\"2\",\"3\",\"10/25/50\",\"30\",\"25\",\"45/60/75\",\"2000\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"MordekaiserChildrenOfTheGrave.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":192,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Morgana\":{\"id\":25,\"key\":\"Morgana\",\"name\":\"Morgana\",\"title\":\"Fallen Angel\",\"spells\":[{\"id\":\"DarkBindingMissile\",\"name\":\"Dark Binding\",\"description\":\"Morgana releases a sphere of dark magic. Upon contact with an enemy unit, the sphere will deal magic damage and force the unit to the ground for a period of time.\",\"tooltip\":\"Fires a bolt of dark energy, rooting the first enemy hit for {{ e2 }} seconds and dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.\",\"leveltip\":{\"label\":[\"Damage\",\"Root Duration\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\" {{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[11,11,11,11,11],\"cooldownBurn\":\"11\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[80,135,190,245,300],[2,2.25,2.5,2.75,3],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/135/190/245/300\",\"2/2.25/2.5/2.75/3\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.9,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1175,1175,1175,1175,1175],\"rangeBurn\":\"1175\",\"image\":{\"full\":\"DarkBindingMissile.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":240,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"TormentedSoil\",\"name\":\"Tormented Soil\",\"description\":\"Infects an area with desecrated soil, causing enemy units who stand on the location to take continual damage.\",\"tooltip\":\"Curses an area for {{ e7 }} seconds. Enemies on the cursed ground are dealt<br>from {{ e5 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> to {{ e6 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage every second based on the enemy's missing Health.\",\"leveltip\":{\"label\":[\"Damage \",\"Mana Cost\"],\"effect\":[\"{{ e5 }} -> {{ e5NL }} \",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,10,10,10,10],\"cooldownBurn\":\"10\",\"cost\":[70,85,100,115,130],\"costBurn\":\"70/85/100/115/130\",\"effect\":[null,[8,16,24,32,40],[0.5,0.5,0.5,0.5,0.5],[280,280,280,280,280],[0,0,0,0,0],[16,32,48,64,80],[24,48,72,96,120],[5,5,5,5,5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"8/16/24/32/40\",\"0.5\",\"280\",\"0\",\"16/32/48/64/80\",\"24/48/72/96/120\",\"5\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.22,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.33,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[900,900,900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"TormentedSoil.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":288,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"BlackShield\",\"name\":\"Black Shield\",\"description\":\"Places a protective barrier around an allied champion, absorbing magical damage and disables until penetrated or the shield dissipates.\",\"tooltip\":\"Shields an allied Champion for {{ e2 }} seconds. The shield absorbs {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and prevents disables until it breaks.\",\"leveltip\":{\"label\":[\"Shield Health\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[23,21,19,17,15],\"cooldownBurn\":\"23/21/19/17/15\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[70,140,210,280,350],[5,5,5,5,5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/140/210/280/350\",\"5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[800,800,800,800,800],\"rangeBurn\":\"800\",\"image\":{\"full\":\"BlackShield.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":336,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SoulShackles\",\"name\":\"Soul Shackles\",\"description\":\"Latches chains of energy onto nearby enemy champions, dealing initial damage to them and slowing their Movement Speed, and then echoing the pain a few seconds later and stunning them if they remain close to Morgana.\",\"tooltip\":\"Dark chains latch onto nearby enemy Champions dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and slowing them by {{ e4 }}% for {{ e3 }} seconds, granting <span class=\\\"coloree91d7\\\">True Sight</span>. After {{ e3 }} seconds, they are dealt an additional {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and are stunned for {{ e2 }} seconds.<br><br>Enemy Champions can break the chains by moving away from Morgana.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,110,100],\"cooldownBurn\":\"120/110/100\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[150,225,300],[1.5,1.5,1.5],[3,3,3],[20,20,20],[575,575,575],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"150/225/300\",\"1.5\",\"3\",\"20\",\"575\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[625,625,625],\"rangeBurn\":\"625\",\"image\":{\"full\":\"SoulShackles.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":384,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Nami\":{\"id\":267,\"key\":\"Nami\",\"name\":\"Nami\",\"title\":\"the Tidecaller\",\"spells\":[{\"id\":\"NamiQ\",\"name\":\"Aqua Prison\",\"description\":\"Sends a bubble to a target area, dealing damage and stunning all enemies on impact.\",\"tooltip\":\"Sends a bubble to target area, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemies, and stunning them for {{ e2 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }} \",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[14,13,12,11,10],\"cooldownBurn\":\"14/13/12/11/10\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[75,130,185,240,295],[1.5,1.5,1.5,1.5,1.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"75/130/185/240/295\",\"1.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[875,875,875,875,875],\"rangeBurn\":\"875\",\"image\":{\"full\":\"NamiQ.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":432,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"NamiW\",\"name\":\"Ebb and Flow\",\"description\":\"Unleashes a stream of water that bounces back and forth between allied and enemy champions, healing allies and damaging enemies.\",\"tooltip\":\"Unleashes a stream of water that bounces back and forth between allied and enemy champions.<br><br><span class=\\\"colorCCFF99\\\">On Ally Hit: </span>Heals {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> Health and will bounce to a nearby enemy champion.<br><br><span class=\\\"colorFF9999\\\">On Enemy Hit: </span>Deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and bounces to a nearby allied champion.<br><br>Bounces to each target only once, and hits up to {{ e2 }} targets. The damage and healing value is modified by <span class=\\\"color99FF99\\\">({{ f1 }}%)</span> each bounce. \",\"leveltip\":{\"label\":[\"Heal\",\"Damage\",\"Mana Cost\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ e1 }} -> {{ e1NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,10,10,10,10],\"cooldownBurn\":\"10\",\"cost\":[70,85,100,115,130],\"costBurn\":\"70/85/100/115/130\",\"effect\":[null,[70,110,150,190,230],[3,3,3,3,3],[65,95,125,155,185],[15,15,15,15,15],[0.075,0.075,0.075,0.075,0.075],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/110/150/190/230\",\"3\",\"65/95/125/155/185\",\"15\",\"0.08\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[725,725,725,725,725],\"rangeBurn\":\"725\",\"image\":{\"full\":\"NamiW.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":0,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"NamiE\",\"name\":\"Tidecaller's Blessing\",\"description\":\"Empowers an allied champion for a short duration. The ally's basic attacks deal bonus magic damage and slow the target.\",\"tooltip\":\"Empowers an allied champion's next {{ e4 }} basic attacks, causing them to slow the target by {{ e2 }}% <span class=\\\"color99FF99\\\">(+{{ a2 }}%)</span> for {{ e3 }} second and deal {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> bonus magic damage. Lasts for {{ e5 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[11,11,11,11,11],\"cooldownBurn\":\"11\",\"cost\":[55,60,65,70,75],\"costBurn\":\"55/60/65/70/75\",\"effect\":[null,[25,40,55,70,85],[15,20,25,30,35],[1,1,1,1,1],[3,3,3,3,3],[6,6,6,6,6],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"25/40/55/70/85\",\"15/20/25/30/35\",\"1\",\"3\",\"6\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.05,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[800,800,800,800,800],\"rangeBurn\":\"800\",\"image\":{\"full\":\"NamiE.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":48,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"NamiR\",\"name\":\"Tidal Wave\",\"description\":\"Summons a massive Tidal Wave that knocks up, slows, and damages enemies. Allies hit gain double the effect of Surging Tides.\",\"tooltip\":\"Summons a Tidal Wave from Nami's position. The wave knocks up enemies and slows them by {{ e4 }}%, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage. The duration of the slow increases based on how far the Tidal Wave has traveled, with a minimum duration of {{ e3 }} seconds and a maximum of {{ e5 }} seconds.<br><br>Allies hit by the wave gain double the effect of Surging Tides.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Slow\"],\"effect\":[\" \",\" {{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e4 }}% -> {{ e4NL }}%\"]},\"maxrank\":3,\"cooldown\":[120,110,100],\"cooldownBurn\":\"120/110/100\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[150,250,350],[0.5,0.5,0.5],[2,2,2],[50,60,70],[4,4,4],[0.002,0.002,0.002],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"150/250/350\",\"0.5\",\"2\",\"50/60/70\",\"4\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[2550,2550,2550],\"rangeBurn\":\"2550\",\"image\":{\"full\":\"NamiR.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":96,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Nasus\":{\"id\":75,\"key\":\"Nasus\",\"name\":\"Nasus\",\"title\":\"the Curator of the Sands\",\"spells\":[{\"id\":\"NasusQ\",\"name\":\"Siphoning Strike\",\"description\":\"Nasus strikes his foe, dealing damage and increasing the power of his future Siphoning Strikes if he slays his target.\",\"tooltip\":\"Nasus's next basic attack will deal {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a2 }})</span> <span class=\\\"color5555FF\\\">(+{{ f1 }})</span> physical damage.<br><br>Siphoning Strike permanently gains <span class=\\\"color5555FF\\\">{{ e2 }}</span> damage if it kills an enemy. This bonus is doubled against Champions, large minions and large monsters.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[8,7,6,5,4],\"cooldownBurn\":\"8/7/6/5/4\",\"cost\":[20,20,20,20,20],\"costBurn\":\"20\",\"effect\":[null,[30,50,70,90,110],[3,3,3,3,3],[25,25,25,25,25],[6,6,6,6,6],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30/50/70/90/110\",\"3\",\"25\",\"6\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1,\"key\":\"a2\"},{\"link\":\"@stacks\",\"coeff\":3,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[300,300,300,300,300],\"rangeBurn\":\"300\",\"image\":{\"full\":\"NasusQ.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":144,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"NasusW\",\"name\":\"Wither\",\"description\":\"Nasus ages an enemy champion, decelerating their Movement and Attack Speeds over time.\",\"tooltip\":\"Nasus ages target champion over {{ e3 }} seconds, slowing their Movement Speed by {{ e2 }}%, increasing to {{ e1 }}% over the duration. The target's Attack Speed is reduced by half the amount.\",\"leveltip\":{\"label\":[\"Max Slow\",\"Cooldown\"],\"effect\":[\" {{ e1 }}% -> {{ e1NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[15,14,13,12,11],\"cooldownBurn\":\"15/14/13/12/11\",\"cost\":[80,80,80,80,80],\"costBurn\":\"80\",\"effect\":[null,[47,59,71,83,95],[35,35,35,35,35],[5,5,5,5,5],[17.5,17.5,17.5,17.5,17.5],[1.5,3,4.5,6,7.5],[3,6,9,12,15],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"47/59/71/83/95\",\"35\",\"5\",\"17.5\",\"1.5/3/4.5/6/7.5\",\"3/6/9/12/15\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"NasusW.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":192,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"NasusE\",\"name\":\"Spirit Fire\",\"description\":\"Nasus unleashes a spirit flame at a location, dealing damage and reducing the Armor of enemies who stand on it.\",\"tooltip\":\"Nasus unleashes a spirit flame at target location, dealing {{ e4 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage.<br><br>For the next {{ e3 }} seconds, enemies in the area have their Armor reduced by {{ e2 }} and are dealt an additional {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage each second.\",\"leveltip\":{\"label\":[\"Initial Damage\",\"Damage Per Second\",\"Armor Reduction\",\"Mana Cost\"],\"effect\":[\"{{ e4 }} -> {{ e4NL }}\",\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[12,12,12,12,12],\"cooldownBurn\":\"12\",\"cost\":[70,85,100,115,130],\"costBurn\":\"70/85/100/115/130\",\"effect\":[null,[11,19,27,35,43],[20,25,30,35,40],[5,5,5,5,5],[55,95,135,175,215],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"11/19/27/35/43\",\"20/25/30/35/40\",\"5\",\"55/95/135/175/215\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.12,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"NasusE.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":240,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"NasusR\",\"name\":\"Fury of the Sands\",\"description\":\"Nasus unleashes a mighty sandstorm that batters nearby enemies. While the storm rages, he gains increased Health, Attack Range, damages nearby enemies and gains bonus Armor and Magic Resistance for the duration.\",\"tooltip\":\"Nasus becomes empowered in the sandstorm for 15 seconds, increasing his maximum Health by {{ e1 }} and Armor and Magic Resistance by {{ e4 }}.<br><br>While the storm rages, he deals {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span>% of nearby enemies' maximum Health each second as magic damage ({{ e5 }} damage max per second) and gains an additional {{ e8 }} Armor and Magic Resistance.\",\"leveltip\":{\"label\":[\"Bonus Health\",\"Max Health %\",\"Initial Armor and Magic Resistance\",\"Incremental Armor and Magic Resistance\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ e8 }} -> {{ e8NL }}\"]},\"maxrank\":3,\"cooldown\":[120,120,120],\"cooldownBurn\":\"120\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[300,450,600],[50,50,50],[3,4,5],[15,35,55],[240,240,240],[15,15,15],[0,0,0],[1,2,3],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"300/450/600\",\"50\",\"3/4/5\",\"15/35/55\",\"240\",\"15\",\"0\",\"1/2/3\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.01,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"NasusR.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":288,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Nautilus\":{\"id\":111,\"key\":\"Nautilus\",\"name\":\"Nautilus\",\"title\":\"the Titan of the Depths\",\"spells\":[{\"id\":\"NautilusAnchorDrag\",\"name\":\"Dredge Line\",\"description\":\"Nautilus hurls his anchor forward. If it hits a champion, he drags both himself and the opponent close together. If it hits terrain, Nautilus instead pulls himself to the anchor and the cooldown of Dredge Line is reduced by half.\",\"tooltip\":\"Nautilus hurls his anchor forward. If it hits an enemy unit, Nautilus drags himself and the target together dealing {{ e1 }} <scaleAP>(+{{ a1 }})</scaleAP> magic damage and stunning them briefly.<br><br>If the anchor hits terrain, Nautilus will drag himself forward and the cooldown is reduced by {{ e3 }}% (<totalValue>{{ f1 }}</totalValue>).\",\"leveltip\":{\"label\":[\"Damage\",\"Mana Cost\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cost }} -> {{ costNL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[18,16,14,12,10],\"cooldownBurn\":\"18/16/14/12/10\",\"cost\":[60,70,80,90,100],\"costBurn\":\"60/70/80/90/100\",\"effect\":[null,[60,105,150,195,240],[0,0,0,0,0],[50,50,50,50,50],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/105/150/195/240\",\"0\",\"50\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.75,\"key\":\"a1\"},{\"link\":\"@special.nautilusq\",\"coeff\":0.5,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[950,950,950,950,950],\"rangeBurn\":\"950\",\"image\":{\"full\":\"NautilusAnchorDrag.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":336,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"NautilusPiercingGaze\",\"name\":\"Titan's Wrath\",\"description\":\"Nautilus surrounds himself with dark energies, gaining a shield that blocks incoming damage. While the shield persists, his attacks apply a damage over time effect to enemies around his target.\",\"tooltip\":\"Nautilus surrounds himself with dark energies for {{ e3 }} seconds, shielding him from the next {{ e1 }} <scaleHealth>(+{{ f1 }})</scaleHealth> ({{ e2 }}% of his max Health) damage.<br><br>While the shield persists, Nautilus' basic attacks deal {{ e4 }} <scaleAP>(+{{ a1 }})</scaleAP> bonus magic damage over 2 seconds to all enemies around his target.\",\"leveltip\":{\"label\":[\"Shield Amount\",\"Bonus Magic Damage\",\"Percent Max Health\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[18,18,18,18,18],\"cooldownBurn\":\"18\",\"cost\":[80,80,80,80,80],\"costBurn\":\"80\",\"effect\":[null,[65,70,75,80,85],[9,11,13,15,17],[10,10,10,10,10],[30,40,50,60,70],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"65/70/75/80/85\",\"9/11/13/15/17\",\"10\",\"30/40/50/60/70\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonushealth\",\"coeff\":0.1,\"key\":\"f1\"},{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[350,350,350,350,350],\"rangeBurn\":\"350\",\"image\":{\"full\":\"NautilusPiercingGaze.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":384,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"NautilusSplashZone\",\"name\":\"Riptide\",\"description\":\"Nautilus slams the ground, causing the earth to explode around him in a set of three explosions. Each explosion damages and slows enemies.\",\"tooltip\":\"Nautilus slams the ground, causing the earth to explode around him. Each explosion deals {{ e1 }} <scaleAP>(+{{ a1 }})</scaleAP> magic damage to enemies in the area and slows them by {{ e2 }}% for {{ e4 }} seconds. This slow diminishes over time.<br><br>A unit can be hit by more than one explosion, but they take {{ e3 }}% less damage from additional explosions.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow\",\"Cooldown\",\"Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[7,6.5,6,5.5,5],\"cooldownBurn\":\"7/6.5/6/5.5/5\",\"cost\":[50,60,70,80,90],\"costBurn\":\"50/60/70/80/90\",\"effect\":[null,[55,85,115,145,175],[30,35,40,45,50],[50,50,50,50,50],[1.25,1.25,1.25,1.25,1.25],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"55/85/115/145/175\",\"30/35/40/45/50\",\"50\",\"1.25\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"NautilusSplashZone.png\",\"sprite\":\"spell7.png\",\"group\":\"spell\",\"x\":432,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"NautilusGrandLine\",\"name\":\"Depth Charge\",\"description\":\"Nautilus fires a shockwave into the earth that chases an opponent. This shockwave rips up the earth above it, knocking enemies into the air. When it reaches the opponent, the shockwave erupts, knocking his target into the air and stunning them.\",\"tooltip\":\"Nautilus fires a shockwave that chases an enemy champion, dealing {{ e1 }} <scaleAP>(+{{ a1 }})</scaleAP> magic damage to other enemies it passes through, knocking them into the air, and stunning them for {{ e5 }} seconds.<br><br>The shockwave explodes upon hitting its target dealing {{ e2 }} <scaleAP>(+{{ a2 }})</scaleAP> magic damage, launching them into the air and stunning them for {{ e5 }} seconds. \",\"leveltip\":{\"label\":[\"Pass Through Damage\",\"Explosion Damage\",\"Stun Duration\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ e5 }} -> {{ e5NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[140,110,80],\"cooldownBurn\":\"140/110/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[125,175,225],[200,325,450],[0,0,0],[0,0,0],[1,1.5,2],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"125/175/225\",\"200/325/450\",\"0\",\"0\",\"1/1.5/2\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[825,825,825],\"rangeBurn\":\"825\",\"image\":{\"full\":\"NautilusGrandLine.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":0,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Nidalee\":{\"id\":76,\"key\":\"Nidalee\",\"name\":\"Nidalee\",\"title\":\"the Bestial Huntress\",\"spells\":[{\"id\":\"JavelinToss\",\"name\":\"Javelin Toss / Takedown\",\"description\":\"In human form, Nidalee throws a spiked javelin at her target that gains damage as it flies.  As a cougar, her next attack will attempt to fatally wound her target, dealing more damage the less life they have.\",\"tooltip\":\"<span class=\\\"size18 colorFF9900\\\">18\",\"leveltip\":{\"label\":[\"Javelin Minimum Damage\",\"Javelin Maximum Damage\",\"Javelin Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[50,60,70,80,90],\"costBurn\":\"50/60/70/80/90\",\"effect\":[null,[70,85,100,115,130],[210,255,300,345,390],[0,0,0,0,0],[0,0,0,0,0],[0.4,0.4,0.4,0.4,0.4],[1.2,1.2,1.2,1.2,1.2],[525,525,525,525,525],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/85/100/115/130\",\"210/255/300/345/390\",\"0\",\"0\",\"0.4\",\"1.2\",\"525\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1500,1500,1500,1500,1500],\"rangeBurn\":\"1500\",\"image\":{\"full\":\"JavelinToss.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":48,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"Bushwhack\",\"name\":\"Bushwhack / Pounce\",\"description\":\"In human form, Nidalee lays a trap for unwary opponents that, when sprung, damages and reveals its target. As a cougar, she jumps in a direction, dealing damage in an area where she lands.\",\"tooltip\":\"<span class=\\\"size18 colorFF9900\\\">18\",\"leveltip\":{\"label\":[\"Trap Damage\",\"Trap Cooldown\",\"Trap Mana Cost\"],\"effect\":[\"{{ e7 }} -> {{ e7NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[13,12,11,10,9],\"cooldownBurn\":\"13/12/11/10/9\",\"cost\":[40,45,50,55,60],\"costBurn\":\"40/45/50/55/60\",\"effect\":[null,[0,0,0,0,0],[0,0,0,0,0],[4,4,4,4,4],[13,12,11,10,9],[10,20,30,40,50],[120,120,120,120,120],[40,80,120,160,200],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"0\",\"0\",\"4\",\"13/12/11/10/9\",\"10/20/30/40/50\",\"120\",\"40/80/120/160/200\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"0\",\"range\":[900,900,900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"Bushwhack.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":96,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"PrimalSurge\",\"name\":\"Primal Surge / Swipe\",\"description\":\"In human form, Nidalee channels the spirit of the cougar to heal her allies and imbue them with Attack Speed for a short duration. As a cougar, she claws in a direction, dealing damage to enemies in front of her.\",\"tooltip\":\"<span class=\\\"size18 colorFF9900\\\">18\",\"leveltip\":{\"label\":[\"Primal Surge Heal\",\"Primal Surge Attack Speed\",\"Primal Surge Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }}% -> {{ e4NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[12,12,12,12,12],\"cooldownBurn\":\"12\",\"cost\":[60,75,90,105,120],\"costBurn\":\"60/75/90/105/120\",\"effect\":[null,[35,55,75,95,115],[70,110,150,190,230],[0,0,0,0,0],[20,30,40,50,60],[0,0,0,0,0],[0.05,0.05,0.05,0.05,0.05],[2,2,2,2,2],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"35/55/75/95/115\",\"70/110/150/190/230\",\"0\",\"20/30/40/50/60\",\"0\",\"0.05\",\"2\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"PrimalSurge.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":144,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"AspectOfTheCougar\",\"name\":\"Aspect Of The Cougar\",\"description\":\"Nidalee transforms into a cougar, gaining new abilities.\",\"tooltip\":\"<span class=\\\"size18 colorFF9900\\\">18\",\"leveltip\":{\"label\":[\"Takedown Damage\",\"Takedown Damage Amp\",\"Pounce Damage\",\"Swipe Damage\",\"Enhanced Pounce Cooldown\"],\"effect\":[\"{{ f1 }} -> {{ f6 }}\",\"{{ f4 }}% -> {{ f5 }}%\",\"{{ f7 }} -> {{ f8 }}\",\"{{ f9 }} -> {{ f10 }}\",\"{{ f2 }} -> {{ f3 }}\"]},\"maxrank\":4,\"cooldown\":[3,3,3,3],\"cooldownBurn\":\"3\",\"cost\":[0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[6,6,6,6],[0,0,0,0],[10,10,10,10],[0,0,0,0],[0,0,0,0]],\"effectBurn\":[null,\"0\",\"0\",\"0\",\"0\",\"0\",\"6\",\"0\",\"10\",\"0\",\"0\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[20,20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"AspectOfTheCougar.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":192,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Nocturne\":{\"id\":56,\"key\":\"Nocturne\",\"name\":\"Nocturne\",\"title\":\"the Eternal Nightmare\",\"spells\":[{\"id\":\"NocturneDuskbringer\",\"name\":\"Duskbringer\",\"description\":\"Nocturne throws a shadow blade that deals damage, leaves a Dusk Trail, and causes champions to leave a Dusk Trail. While on the trail, Nocturne can move through units and has increased Movement Speed and Attack Damage.\",\"tooltip\":\"Nocturne throws a shadow blade that deals {{ e2 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage and leaves a Dusk Trail for {{ e3 }} seconds. Enemy champions hit also leave a Dusk Trail.<br><br>While on the trail, Nocturne can move through units and gains {{ e1 }}% Movement Speed and {{ e4 }} Attack Damage.\",\"leveltip\":{\"label\":[\"Damage\",\"Movement Speed Bonus\",\"Bonus Attack Damage\",\"Mana Cost\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\" {{ e1 }}% -> {{ e1NL }}%\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,10,10,10,10],\"cooldownBurn\":\"10\",\"cost\":[60,65,70,75,80],\"costBurn\":\"60/65/70/75/80\",\"effect\":[null,[15,20,25,30,35],[60,105,150,195,240],[5,5,5,5,5],[15,25,35,45,55],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"15/20/25/30/35\",\"60/105/150/195/240\",\"5\",\"15/25/35/45/55\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.75,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1125,1125,1125,1125,1125],\"rangeBurn\":\"1125\",\"image\":{\"full\":\"NocturneDuskbringer.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":240,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"NocturneShroudofDarkness\",\"name\":\"Shroud of Darkness\",\"description\":\"Nocturne empowers his blades, passively gaining Attack Speed. Activating Shroud of Darkness allows Nocturne to fade into the shadows, creating a magical barrier which blocks a single enemy ability and doubles his passive Attack Speed if successful.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> Nocturne gains {{ e1 }}% Attack Speed.<br><br><span class=\\\"colorFF9900\\\">Active:</span> Nocturne creates a magical barrier for 1.5 seconds, which blocks the next enemy ability.<br><br>If an ability is blocked by the shield, Nocturne's passive Attack Speed bonus doubles for 5 seconds.\",\"leveltip\":{\"label\":[\"Attack Speed Bonus\",\"Cooldown\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\" {{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[20,18,16,14,12],\"cooldownBurn\":\"20/18/16/14/12\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[20,25,30,35,40],[0.2,0.05,0.05,0.05,0.05],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"20/25/30/35/40\",\"0.2/0.05/0.05/0.05/0.05\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[20,20,20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"NocturneShroudofDarkness.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":288,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"NocturneUnspeakableHorror\",\"name\":\"Unspeakable Horror\",\"description\":\"Nocturne plants a nightmare into his target's mind, dealing damage each second and terrifying the target if they do not get out of range by the end of the duration.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> Nocturne gains massively increased Movement Speed toward terrified enemies.<br><br><span class=\\\"colorFF9900\\\">Active:</span> Nocturne plants a nightmare into his target's mind, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage over {{ e3 }} seconds. If Nocturne stays within range of the target for the full duration, the target becomes terrified for {{ e2 }} second(s).\",\"leveltip\":{\"label\":[\"Damage\",\"Terrify Duration\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[15,14,13,12,11],\"cooldownBurn\":\"15/14/13/12/11\",\"cost\":[60,65,70,75,80],\"costBurn\":\"60/65/70/75/80\",\"effect\":[null,[80,125,170,215,260],[1.25,1.5,1.75,2,2.25],[2,2,2,2,2],[465,465,465,465,465],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/125/170/215/260\",\"1.25/1.5/1.75/2/2.25\",\"2\",\"465\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[425,425,425,425,425],\"rangeBurn\":\"425\",\"image\":{\"full\":\"NocturneUnspeakableHorror.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":336,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"NocturneParanoia\",\"name\":\"Paranoia\",\"description\":\"Nocturne reduces the sight radius of all enemy champions and removes their ally vision in the process. He can then launch himself at a nearby enemy champion.\",\"tooltip\":\"Nocturne reduces the sight radius of all enemy champions and removes their ally vision for 4 seconds.<br><br>While Paranoia is active, Nocturne can launch himself at an enemy champion, dealing {{ e3 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage.\",\"leveltip\":{\"label\":[\"Damage\",\"Range\",\"Cooldown\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[150,125,100],\"cooldownBurn\":\"150/125/100\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[250,250,250],[2500,3250,4000],[150,250,350],[3500,4250,5000],[1800,1800,1800],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"250\",\"2500/3250/4000\",\"150/250/350\",\"3500/4250/5000\",\"1800\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1.2,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[2500,3250,4000],\"rangeBurn\":\"2500/3250/4000\",\"image\":{\"full\":\"NocturneParanoia.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":384,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Nunu\":{\"id\":20,\"key\":\"Nunu\",\"name\":\"Nunu\",\"title\":\"the Yeti Rider\",\"spells\":[{\"id\":\"Consume\",\"name\":\"Consume\",\"description\":\"Nunu commands the yeti to take a bite out of a target minion or monster, dealing heavy damage to it and healing himself.\",\"tooltip\":\"Nunu commands the yeti to take a bite out of a minion or monster, dealing {{ e1 }} true damage and healing himself for {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span>.<br><br><span class=\\\"colorFF9900\\\">Passive -</span><span class=\\\"color33FF33\\\"> Feed The Yeti</span>: For every Large or Epic Monster Consumed, Nunu gains increased Size, {{ e0 }}% Maximum Health, and {{ e9 }} out of combat Movement Speed for the next <span class=\\\"colorFFFFFF\\\">{{ f5 }}</span> seconds (max {{ e6 }} stacks).<br><br><span class=\\\"colorDDDD77\\\">Recently Consumed monsters will not grant additional bonuses ({{ e5 }} second cooldown).</span>\",\"leveltip\":{\"label\":[\"Damage\",\"Health Restore\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[12,11,10,9,8],\"cooldownBurn\":\"12/11/10/9/8\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[340,500,660,820,980],[50,100,150,200,250],[120,150,180,210,240],[0,0,0,0,0],[60,60,60,60,60],[5,5,5,5,5],[5,5,5,5,5],[50,50,50,50,50],[10,10,10,10,10],[0.02,0.02,0.02,0.02,0.02]],\"effectBurn\":[null,\"340/500/660/820/980\",\"50/100/150/200/250\",\"120/150/180/210/240\",\"0\",\"60\",\"5\",\"5\",\"50\",\"10\",\"0.02\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.75,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[125,125,125,125,125],\"rangeBurn\":\"125\",\"image\":{\"full\":\"Consume.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":432,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"BloodBoil\",\"name\":\"Blood Boil\",\"description\":\"Nunu invigorates himself and an allied unit by heating their blood, increasing their Movement and Attack Speeds.\",\"tooltip\":\"The heat of Nunu and a target ally's blood rises, increasing Movement Speed by {{ e2 }}% and Attack Speed by {{ e1 }}% for {{ e3 }} seconds.<br><br>If Nunu targets himself, Blood Boil will try to target the nearest Ally champion.\",\"leveltip\":{\"label\":[\"Attack Speed\",\"Movement Speed\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\" {{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[15,15,15,15,15],\"cooldownBurn\":\"15\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[25,30,35,40,45],[8,9,10,11,12],[12,12,12,12,12],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"25/30/35/40/45\",\"8/9/10/11/12\",\"12\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[700,700,700,700,700],\"rangeBurn\":\"700\",\"image\":{\"full\":\"BloodBoil.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":0,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"IceBlast\",\"name\":\"Ice Blast\",\"description\":\"Nunu launches a ball of ice at an enemy unit, dealing damage and temporarily slowing their Movement and Attack Speeds.\",\"tooltip\":\"Nunu launches a ball of ice at an enemy unit, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and slowing their Movement Speed by {{ e2 }}% and Attack Speed by {{ e3 }}% for {{ e4 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Movement Speed Slow\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[6,5.5,5,4.5,4],\"cooldownBurn\":\"6/5.5/5/4.5/4\",\"cost\":[70,75,80,85,90],\"costBurn\":\"70/75/80/85/90\",\"effect\":[null,[80,120,160,200,240],[40,45,50,55,60],[25,25,25,25,25],[2,2,2,2,2],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/120/160/200/240\",\"40/45/50/55/60\",\"25\",\"2\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.9,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[550,550,550,550,550],\"rangeBurn\":\"550\",\"image\":{\"full\":\"IceBlast.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":48,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"AbsoluteZero\",\"name\":\"Absolute Zero\",\"description\":\"Nunu begins to sap the area of heat, slowing all nearby enemies. When Absolute Zero ends, he deals massive damage to all enemies caught in the area.\",\"tooltip\":\"Nunu channels for {{ e4 }} seconds, sapping the area of heat. Nearby enemies have their Movement Speed slowed by {{ e2 }}% and their Attack Speed slowed by {{ e3 }}%. The Movement Speed slow increases to {{ e6 }}% over the duration of the channel.<br><br>Enemies caught in the area when the channel ends take up to {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage, depending on how long Absolute Zero was channeled.<br>(Minimum Damage: <span class=\\\"color99FF99\\\">{{ f2 }}</span>)\",\"leveltip\":{\"label\":[\"Maximum Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[110,100,90],\"cooldownBurn\":\"110/100/90\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[625,875,1125],[50,50,50],[25,25,25],[3,3,3],[3,3,3],[95,95,95],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"625/875/1125\",\"50\",\"25\",\"3\",\"3\",\"95\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":2.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"AbsoluteZero.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":96,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Olaf\":{\"id\":2,\"key\":\"Olaf\",\"name\":\"Olaf\",\"title\":\"the Berserker\",\"spells\":[{\"id\":\"OlafAxeThrowCast\",\"name\":\"Undertow\",\"description\":\"Olaf throws an axe into the ground at a target location, dealing damage to enemies it passes through and slowing their Movement Speed. If Olaf picks up the axe, the ability's cooldown is reduced by 4.5 seconds.\",\"tooltip\":\"Olaf throws an axe to target location, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage to units it passes through and slowing them by {{ e2 }}% for up to {{ e3 }} seconds. The further the Axe flies, the longer the slow lasts, but it is never less than {{ e4 }} seconds.<br><br>If Olaf picks up the axe, the ability's cooldown is reduced by 4.5 seconds.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Slow %\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[7,7,7,7,7],\"cooldownBurn\":\"7\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[70,115,160,205,250],[29,33,37,41,45],[2.5,2.5,2.5,2.5,2.5],[1.5,1.5,1.5,1.5,1.5],[800,800,800,800,800],[400,400,400,400,400],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/115/160/205/250\",\"29/33/37/41/45\",\"2.5\",\"1.5\",\"800\",\"400\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"OlafAxeThrowCast.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":144,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"OlafFrenziedStrikes\",\"name\":\"Vicious Strikes\",\"description\":\"Olaf's Attack Speed is increased, he gains Life Steal and has increased healing from all sources based on how much Health he is missing.\",\"tooltip\":\"For 6 seconds, Olaf gains {{ e2 }}% Life Steal and his Attack Speed is increased by {{ e1 }}%.<br><br>During this time, Olaf also receives 1% increased healing from all sources for every {{ e3 }}% of Health he is missing.\",\"leveltip\":{\"label\":[\"Attack Speed\",\"Life Steal\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[16,16,16,16,16],\"cooldownBurn\":\"16\",\"cost\":[30,30,30,30,30],\"costBurn\":\"30\",\"effect\":[null,[40,50,60,70,80],[14,16,18,20,22],[2,2,2,2,2],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"40/50/60/70/80\",\"14/16/18/20/22\",\"2\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[700,700,700,700,700],\"rangeBurn\":\"700\",\"image\":{\"full\":\"OlafFrenziedStrikes.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":192,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"OlafRecklessStrike\",\"name\":\"Reckless Swing\",\"description\":\"Olaf attacks with such force that it deals true damage to his target and himself, refunding the Health cost if he destroys the target.\",\"tooltip\":\"Olaf ferociously swings his axes, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> true damage to his target. This ability's cost is equal to {{ e2 }}% of the total damage dealt, but the cost is refunded if it kills the target.<br><br>Basic attacks lower the cooldown of Reckless Swing by 1 second.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[12,11,10,9,8],\"cooldownBurn\":\"12/11/10/9/8\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[70,115,160,205,250],[30,30,30,30,30],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/115/160/205/250\",\"30\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\"Health\",\"maxammo\":\"-1\",\"range\":[325,325,325,325,325],\"rangeBurn\":\"325\",\"image\":{\"full\":\"OlafRecklessStrike.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":240,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"Health\"},{\"id\":\"OlafRagnarok\",\"name\":\"Ragnarok\",\"description\":\"Olaf temporarily becomes immune to disables.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Olaf gains {{ e1 }} Armor and {{ e1 }} Magic Resist.<br><br><span class=\\\"colorFF9900\\\">Active: </span>Olaf removes all disables from himself and becomes immune to them for the next {{ e2 }} seconds. Olaf also receives a {{ e5 }}% Movement Speed bonus towards enemy champions for {{ e4 }} second. During this time, Olaf loses the passive portion of Ragnarok and gains {{ e3 }} Attack Damage.\",\"leveltip\":{\"label\":[\"Armor and Magic Resist\",\"Attack Damage\",\"Movement Speed Bonus\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ e5 }} -> {{ e5NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[100,90,80],\"cooldownBurn\":\"100/90/80\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[20,30,40],[6,6,6],[40,60,80],[1,1,1],[50,60,70],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"20/30/40\",\"6\",\"40/60/80\",\"1\",\"50/60/70\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"OlafRagnarok.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":288,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Orianna\":{\"id\":61,\"key\":\"Orianna\",\"name\":\"Orianna\",\"title\":\"the Lady of Clockwork\",\"spells\":[{\"id\":\"OrianaIzunaCommand\",\"name\":\"Command: Attack\",\"description\":\"Orianna commands her Ball to fire toward a target location, dealing magic damage to targets along the way (deals less damage to subsequent targets). Her Ball remains at the target location after.\",\"tooltip\":\"Orianna commands her Ball to shoot toward a target location, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to targets along the way. However, it deals {{ e2 }}% less damage for each unit it hits (Minimum {{ e3 }}%).<br><br>Her Ball remains behind at the target location afterwards.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[6,5.25,4.5,3.75,3],\"cooldownBurn\":\"6/5.25/4.5/3.75/3\",\"cost\":[30,35,40,45,50],\"costBurn\":\"30/35/40/45/50\",\"effect\":[null,[60,90,120,150,180],[10,10,10,10,10],[40,40,40,40,40],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/90/120/150/180\",\"10\",\"40\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[815,815,815,815,815],\"rangeBurn\":\"815\",\"image\":{\"full\":\"OrianaIzunaCommand.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":336,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"OrianaDissonanceCommand\",\"name\":\"Command: Dissonance\",\"description\":\"Orianna commands the Ball to release a pulse of energy, dealing magic damage around it. This leaves a field behind that speeds up allies and slows enemies.\",\"tooltip\":\"Orianna commands her Ball to release an electric pulse, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to nearby enemies.<br><br>The pulse leaves behind an energy field for {{ e4 }} seconds, lowering enemy Movement Speed by {{ e2 }}% and increasing ally Movement Speed by {{ e3 }}% for {{ e5 }} seconds. This effect diminishes over time.\",\"leveltip\":{\"label\":[\"Damage\",\"Movement Speed Bonus\",\"Slow Amount\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[9,9,9,9,9],\"cooldownBurn\":\"9\",\"cost\":[70,80,90,100,110],\"costBurn\":\"70/80/90/100/110\",\"effect\":[null,[70,115,160,205,250],[20,25,30,35,40],[20,25,30,35,40],[3,3,3,3,3],[2,2,2,2,2],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/115/160/205/250\",\"20/25/30/35/40\",\"20/25/30/35/40\",\"3\",\"2\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[255,255,255,255,255],\"rangeBurn\":\"255\",\"image\":{\"full\":\"OrianaDissonanceCommand.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":384,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"OrianaRedactCommand\",\"name\":\"Command: Protect\",\"description\":\"Orianna commands her Ball to attach to an allied champion, shielding them and dealing magic damage to any enemies it passes through on the way. Additionally, the Ball grants additional Armor and Magic Resist to the champion it is attached to.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>The Ball adds {{ e2 }} Armor and Magic Resist to the allied champion it is attached to.<br><br><span class=\\\"colorFF9900\\\">Active: </span>Orianna commands her Ball to travel to and attach onto an allied champion, shielding them for {{ e5 }} seconds from the next {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> damage. Enemies the Ball passes through along the way are damaged for {{ e4 }}% of the shield value: {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span>.</span>\",\"leveltip\":{\"label\":[\"Armor Bonus\",\"Magic Resist Bonus\",\"Damage Absorption\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":5,\"cooldown\":[9,9,9,9,9],\"cooldownBurn\":\"9\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[80,120,160,200,240],[10,15,20,25,30],[60,90,120,150,180],[75,75,75,75,75],[4,4,4,4,4],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/120/160/200/240\",\"10/15/20/25/30\",\"60/90/120/150/180\",\"75\",\"4\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1095,1095,1095,1095,1095],\"rangeBurn\":\"1095\",\"image\":{\"full\":\"OrianaRedactCommand.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":432,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"OrianaDetonateCommand\",\"name\":\"Command: Shockwave\",\"description\":\"Orianna commands her Ball to unleash a shockwave, dealing magic damage and launching nearby enemies towards the Ball after a short delay.\",\"tooltip\":\"Orianna commands her Ball to unleash a shockwave after a brief delay, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to nearby enemies and flinging them into the air a set distance in the direction of the Ball.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[110,95,80],\"cooldownBurn\":\"110/95/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[150,225,300],[40,50,60],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"150/225/300\",\"40/50/60\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[410,410,410],\"rangeBurn\":\"410\",\"image\":{\"full\":\"OrianaDetonateCommand.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":0,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Pantheon\":{\"id\":80,\"key\":\"Pantheon\",\"name\":\"Pantheon\",\"title\":\"the Artisan of War\",\"spells\":[{\"id\":\"PantheonQ\",\"name\":\"Spear Shot\",\"description\":\"Pantheon hurls his spear at an opponent, dealing damage.\",\"tooltip\":\"Pantheon hurls his spear at an opponent, dealing {{ e2 }} <scaleAD>(+{{ f1 }})</scaleAD> physical damage. \",\"leveltip\":{\"label\":[\"Base Damage\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[4,4,4,4,4],\"cooldownBurn\":\"4\",\"cost\":[45,45,45,45,45],\"costBurn\":\"45\",\"effect\":[null,[100,115,130,145,160],[65,105,145,185,225],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"100/115/130/145/160\",\"65/105/145/185/225\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1.4,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"PantheonQ.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":48,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"PantheonW\",\"name\":\"Aegis of Zeonia\",\"description\":\"Pantheon leaps at an enemy and bashes them with his shield, stunning them. After finishing the attack, Pantheon readies himself to block the next attack.\",\"tooltip\":\"Pantheon leaps at target enemy, dealing {{ e1 }} <scaleAP>(+{{ a1 }})</scaleAP> magic damage and stunning them for {{ e2 }} second. Pantheon also instantly refreshes his Aegis Protection.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[13,12,11,10,9],\"cooldownBurn\":\"13/12/11/10/9\",\"cost\":[55,55,55,55,55],\"costBurn\":\"55\",\"effect\":[null,[50,75,100,125,150],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/75/100/125/150\",\"1\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"PantheonW.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":96,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"PantheonE\",\"name\":\"Heartseeker Strike\",\"description\":\"Pantheon focuses and unleashes 3 swift strikes to the area in front of him dealing damage to all enemies. Pantheon also becomes more aware of his enemy's vital spots, allowing him to always crit enemies below 15% Health.\",\"tooltip\":\"<spellPassive>Passive: </spellPassive>Pantheon's basic attacks and Spear Shot gain 100% critical strike chance against targets below {{ e3 }}% Health.<br><br><spellActive>Active: </spellActive>Pantheon focuses and delivers {{ e4 }} swift strikes in front of him for a total of {{ e1 }} <scaleAD>(+{{ a1 }})</scaleAD> physical damage (deals {{ e2 }}% damage to minions and monsters).<br><br>Damage per Strike: <totalValue>{{ f1 }}</totalValue>\",\"leveltip\":{\"label\":[\"Base Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,9,8,7,6],\"cooldownBurn\":\"10/9/8/7/6\",\"cost\":[45,50,55,60,65],\"costBurn\":\"45/50/55/60/65\",\"effect\":[null,[80,130,180,230,280],[60,60,60,60,60],[15,15,15,15,15],[3,3,3,3,3],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/130/180/230/280\",\"60\",\"15\",\"3\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":3,\"key\":\"a1\"},{\"link\":\"bonusattackdamage\",\"coeff\":0.6,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[400,400,400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"PantheonE.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":144,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"PantheonRJump\",\"name\":\"Grand Skyfall\",\"description\":\"Pantheon composes himself then leaps into the air to a target, striking all enemy units in an area. Enemies closer to the impact point take more damage.\",\"tooltip\":\"Pantheon gathers his strength and then leaps high into the air, crashing down at target area a few seconds later. Deals up to {{ e1 }} <scaleAP>(+{{ a1 }})</scaleAP> magic damage to enemies at the center (down to {{ e6 }}% at the edge) and slows their Movement Speed by {{ e4 }}% for 1 second.<br><br>If Pantheon cancels this channel, Grand Skyfall is put on a {{ e5 }} second cooldown.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[150,135,120],\"cooldownBurn\":\"150/135/120\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[400,700,1000],[0,0,0],[0,0,0],[35,35,35],[30,30,30],[50,50,50],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"400/700/1000\",\"0\",\"0\",\"35\",\"30\",\"50\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[5500,5500,5500],\"rangeBurn\":\"5500\",\"image\":{\"full\":\"PantheonRJump.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":192,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Poppy\":{\"id\":78,\"key\":\"Poppy\",\"name\":\"Poppy\",\"title\":\"Keeper of the Hammer\",\"spells\":[{\"id\":\"PoppyQ\",\"name\":\"Hammer Shock\",\"description\":\"Poppy swings her hammer, dealing damage and creating a zone that will slow enemies and explode after a delay.\",\"tooltip\":\"Poppy smashes the ground, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> plus {{ e5 }}% of the enemies' maximum Health as physical damage, and leaving an unstable area.<br><br>The area slows enemies inside it by {{ e3 }}% and erupts after {{ e4 }} second, dealing the initial damage again.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[9,8,7,6,5],\"cooldownBurn\":\"9/8/7/6/5\",\"cost\":[35,40,45,50,55],\"costBurn\":\"35/40/45/50/55\",\"effect\":[null,[35,55,75,95,115],[0.5,0.5,0.5,0.5,0.5],[20,25,30,35,40],[1,1,1,1,1],[7,7,7,7,7],[40,60,80,100,120],[100,100,100,100,100],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"35/55/75/95/115\",\"0.5\",\"20/25/30/35/40\",\"1\",\"7\",\"40/60/80/100/120\",\"100\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[430,430,430,430,430],\"rangeBurn\":\"430\",\"image\":{\"full\":\"PoppyQ.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":240,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"PoppyW\",\"name\":\"Steadfast Presence\",\"description\":\"Poppy passively gains Armor and Magic Resist. This bonus increases when she is low on Health. Poppy can activate Steadfast Presence to stop enemy dashes around her and gain movement speed.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> Poppy gains <span class=\\\"colorFFFF00\\\">{{ f1 }}</span> Armor and <span class=\\\"colorFF00FF\\\">{{ f2 }}</span> Magic Resist ({{ e3 }}% of Armor and Magic Resist). This bonus is doubled if Poppy is below 40% Health.<br><br><span class=\\\"colorFF9900\\\">Active:</span> For the next {{ e1 }} seconds, Poppy gains {{ e2 }}% Movement Speed. While Steadfast Presence is active, she stops enemy dashes in an area around her, dealing {{ e5 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.\",\"leveltip\":{\"label\":[\"Movement Speed\",\"Cooldown\",\"Damage\"],\"effect\":[\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e5 }} -> {{ e5NL }}\"]},\"maxrank\":5,\"cooldown\":[24,22,20,18,16],\"cooldownBurn\":\"24/22/20/18/16\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[2.5,2.5,2.5,2.5,2.5],[32,34,36,38,40],[15,15,15,15,15],[0.5,0.5,0.5,0.5,0.5],[70,110,150,190,230],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"2.5\",\"32/34/36/38/40\",\"15\",\"0.5\",\"70/110/150/190/230\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[400,400,400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"PoppyW.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":288,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"PoppyE\",\"name\":\"Heroic Charge\",\"description\":\"Poppy dashes to the target and pushes it back. If the target is pushed into a wall, it is stunned.\",\"tooltip\":\"Poppy tackles an enemy, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage and carrying them forward. If Poppy carries the target into terrain, the enemy takes {{ e2 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> additional physical damage and is stunned for {{ e3 }} seconds.\",\"leveltip\":{\"label\":[\"Initial Damage\",\"Wall Damage\",\"Stun Duration\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[14,13,12,11,10],\"cooldownBurn\":\"14/13/12/11/10\",\"cost\":[70,70,70,70,70],\"costBurn\":\"70\",\"effect\":[null,[50,70,90,110,130],[50,70,90,110,130],[1.6,1.7,1.8,1.9,2],[1800,1800,1800,1800,1800],[400,400,400,400,400],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/70/90/110/130\",\"50/70/90/110/130\",\"1.6/1.7/1.8/1.9/2\",\"1800\",\"400\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.5,\"key\":\"a1\"},{\"link\":\"bonusattackdamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[425,425,425,425,425],\"rangeBurn\":\"425\",\"image\":{\"full\":\"PoppyE.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":336,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"PoppyR\",\"name\":\"Keeper's Verdict\",\"description\":\"Poppy channels a hammer strike that knocks enemies very far away.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">First Cast:</span> Poppy channels for up to {{ e3 }} seconds, slowing herself by {{ e7 }}%.<br><br><span class=\\\"colorFF9900\\\">Second Cast:</span> Poppy smashes the ground, emanating a shockwave that deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage to enemies around the first champion hit and knocking them a large distance toward their Summoning Platform. The shockwave length and knockback distance increases with channel duration.<br><br>Enemies hit with an uncharged Keeper's Verdict will take half the damage and be knocked straight into the air for half the duration. Enemies are untargetable while they are being knocked away.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[140,120,100],\"cooldownBurn\":\"140/120/100\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[200,300,400],[0.25,0.25,0.25],[4,4,4],[2400,2400,2400],[2,2,2],[1,1,1],[15,15,15],[30,30,30],[1,1,1],[0.5,0.5,0.5]],\"effectBurn\":[null,\"200/300/400\",\"0.25\",\"4\",\"2400\",\"2\",\"1\",\"15\",\"30\",\"1\",\"0.5\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.9,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[500,500,500],\"rangeBurn\":\"500\",\"image\":{\"full\":\"PoppyR.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":384,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Quinn\":{\"id\":133,\"key\":\"Quinn\",\"name\":\"Quinn\",\"title\":\"Demacia's Wings\",\"spells\":[{\"id\":\"QuinnQ\",\"name\":\"Blinding Assault\",\"description\":\"Quinn calls Valor to mark an enemy and hinder its vision before damaging all enemies in the immediate area.\",\"tooltip\":\"Valor flies in a line, marking the first enemy he hits as <span class=\\\"colorFFF673\\\">Vulnerable</span> and reducing its vision radius dramatically for {{ e3 }} seconds. He then deals {{ e1 }} <span class=\\\"colorFF9900\\\">(+{{ f2 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> physical damage to all nearby enemies.<br><br>If the primary target is not a champion, it cannot attack for {{ e3 }} seconds.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Total Attack Damage Ratio\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[11,10.5,10,9.5,9],\"cooldownBurn\":\"11/10.5/10/9.5/9\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[20,45,70,95,120],[-1000,-1000,-1000,-1000,-1000],[1.5,1.5,1.5,1.5,1.5],[0.8,0.9,1,1.1,1.2],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"20/45/70/95/120\",\"-1000\",\"1.5\",\"0.8/0.9/1/1.1/1.2\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1025,1025,1025,1025,1025],\"rangeBurn\":\"1025\",\"image\":{\"full\":\"QuinnQ.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":432,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"QuinnW\",\"name\":\"Heightened Senses\",\"description\":\"Passively grants Quinn Attack Speed and Movement Speed after she attacks a <font color='#FFF673'>Vulnerable</font> target. Activate to have Valor reveal a large area nearby.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> Attacking a <span class=\\\"colorFFF673\\\">Vulnerable</span> target increases Quinn's Attack Speed {{ e2 }}% and Movement Speed by {{ e3 }}% for {{ e1 }} seconds.<br><br><span class=\\\"colorFF9900\\\">Active:</span> Valor reveals a large area nearby for {{ e5 }} seconds.\",\"leveltip\":{\"label\":[\"Attack Speed Bonus\",\"Movement Speed Bonus\",\"Cooldown\"],\"effect\":[\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[50,45,40,35,30],\"cooldownBurn\":\"50/45/40/35/30\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[2,2,2,2,2],[20,35,50,65,80],[20,25,30,35,40],[25,45,65,85,105],[2,2,2,2,2],[20,30,40,50,60],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"2\",\"20/35/50/65/80\",\"20/25/30/35/40\",\"25/45/65/85/105\",\"2\",\"20/30/40/50/60\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[2100,2100,2100,2100,2100],\"rangeBurn\":\"2100\",\"image\":{\"full\":\"QuinnW.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":0,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"QuinnE\",\"name\":\"Vault\",\"description\":\"Quinn dashes to an enemy, dealing physical damage and slowing the target's Movement Speed. Upon reaching the target, she leaps off the target, briefly interrupting it, and lands near her maximum Attack Range away from the target.\",\"tooltip\":\"Quinn dashes to an enemy, dealing {{ e2 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage while Valor marks it as <span class=\\\"colorFFF673\\\">Vulnerable</span>.<br><br>Upon reaching the target, Quinn leaps off, briefly displacing and slowing it by {{ e1 }}% (diminishing over {{ e3 }} seconds).\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[12,11,10,9,8],\"cooldownBurn\":\"12/11/10/9/8\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[50,50,50,50,50],[40,70,100,130,160],[1.5,1.5,1.5,1.5,1.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50\",\"40/70/100/130/160\",\"1.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.2,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[675,675,675,675,675],\"rangeBurn\":\"675\",\"image\":{\"full\":\"QuinnE.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":48,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"QuinnR\",\"name\":\"Behind Enemy Lines\",\"description\":\"Quinn and Valor team up to fly around at great speed.\",\"tooltip\":\"Quinn calls down Valor to assist her. After a 2 second channel, they unite, gaining {{ e3 }}% Total Movement Speed and the ability to cast Skystrike by recasting this ability or taking offensive action. Skystrike damages nearby enemies.<br><br>Taking champion or turret damage puts Behind Enemy Lines on a 3 second cooldown.\",\"leveltip\":{\"label\":[\"Movement Speed Bonus\",\"Mana Cost\"],\"effect\":[\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ e7 }} -> {{ e7NL }}\"]},\"maxrank\":3,\"cooldown\":[0,0,0],\"cooldownBurn\":\"0\",\"cost\":[100,50,0],\"costBurn\":\"100/50/0\",\"effect\":[null,[15,20,25],[0,0,0],[70,100,130],[25000,25000,25000],[7,7,7],[8,8,8],[100,50,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"15/20/25\",\"0\",\"70/100/130\",\"25000\",\"7\",\"8\",\"100/50/0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[700,700,700],\"rangeBurn\":\"700\",\"image\":{\"full\":\"QuinnR.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":96,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Rakan\":{\"id\":497,\"key\":\"Rakan\",\"name\":\"Rakan\",\"title\":\"The Charmer\",\"spells\":[{\"id\":\"RakanQ\",\"name\":\"Gleaming Quill\",\"description\":\"Flings a magical feather that deals magic damage. Striking a champion or epic monster enables Rakan to heal his allies.\",\"tooltip\":\"Flings a magical feather that deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to the first enemy hit.<br><br>If the feather hits a champion or epic monster, Rakan restores <span class=\\\"colorFFFFFF\\\">{{ f1 }}</span> <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> health to himself and nearby allies after {{ e4 }} seconds or when he touches an ally.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ f3 }} -> {{ f2 }}\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[70,115,160,205,250],[15,15,15,15,15],[7.5,7.5,7.5,7.5,7.5],[3,3,3,3,3],[12,10.5,9,7.5,6],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/115/160/205/250\",\"15\",\"7.5\",\"3\",\"12/10.5/9/7.5/6\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[850,850,850,850,850],\"rangeBurn\":\"850\",\"image\":{\"full\":\"RakanQ.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":144,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"RakanW\",\"name\":\"Grand Entrance\",\"description\":\"Dashes to a location, knocking up nearby enemies on arrival.\",\"tooltip\":\"Dashes to a location. Upon arrival Rakan spirals into the air, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and knocking up enemies for {{ e3 }} second.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[18,16.5,15,13.5,12],\"cooldownBurn\":\"18/16.5/15/13.5/12\",\"cost\":[50,60,70,80,90],\"costBurn\":\"50/60/70/80/90\",\"effect\":[null,[70,115,160,205,250],[1800,1800,1800,1800,1800],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/115/160/205/250\",\"1800\",\"1\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"RakanW.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":192,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"RakanE\",\"name\":\"Battle Dance\",\"description\":\"Flies to an allied champion granting them a shield. Can be re-cast for free for a short duration.\",\"tooltip\":\"Flies to an allied champion, granting them a {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> health shield for {{ e3 }} seconds.<br><br>Can be re-cast at no cost once within {{ e2 }} seconds.<br><br><span class=\\\"colore5c100\\\">Battle Dance's cast range is increased when cast on Xayah.</span></i>\",\"leveltip\":{\"label\":[\"Shield Health\",\"Cooldown\",\"Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ f2 }} -> {{ f6 }}\",\"{{ e5 }} -> {{ e5NL }}\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[60,70,80,90,100],\"costBurn\":\"60/70/80/90/100\",\"effect\":[null,[50,75,100,125,150],[5,5,5,5,5],[3,3,3,3,3],[20,18,16,14,12],[60,70,80,90,100],[600,600,600,600,600],[1000,1000,1000,1000,1000],[1150,1150,1150,1150,1150],[1250,1250,1250,1250,1250],[0,0,0,0,0]],\"effectBurn\":[null,\"50/75/100/125/150\",\"5\",\"3\",\"20/18/16/14/12\",\"60/70/80/90/100\",\"600\",\"1000\",\"1150\",\"1250\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[550,550,550,550,550],\"rangeBurn\":\"550\",\"image\":{\"full\":\"RakanE.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":240,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"RakanR\",\"name\":\"The Quickness\",\"description\":\"Gains movement speed, charming and dealing magic damage to enemies touched.\",\"tooltip\":\"Gain {{ e5 }}% movement speed for {{ e2 }} seconds.<br><br>Touching enemies deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and charms them for {{ e3 }} second(s) the first time they're hit. The first champion charmed grants Rakan {{ e6 }}% decaying movement speed.\",\"leveltip\":{\"label\":[\"Charm Duration\",\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,110,100],\"cooldownBurn\":\"120/110/100\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[100,200,300],[4,4,4],[1,1.25,1.5],[0.25,0.25,0.25],[50,50,50],[150,150,150],[200,200,200],[120,110,100],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"100/200/300\",\"4\",\"1/1.25/1.5\",\"0.25\",\"50\",\"150\",\"200\",\"120/110/100\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[200,200,200],\"rangeBurn\":\"200\",\"image\":{\"full\":\"RakanR.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":288,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Rammus\":{\"id\":33,\"key\":\"Rammus\",\"name\":\"Rammus\",\"title\":\"the Armordillo\",\"spells\":[{\"id\":\"PowerBall\",\"name\":\"Powerball\",\"description\":\"Rammus accelerates in a ball towards his enemies, dealing damage and slowing targets affected by the impact.\",\"tooltip\":\"Rammus accelerates in a ball, gaining up to <span class=\\\"colorFFFFFF\\\">{{ f2 }}%</span> Movement Speed over {{ e4 }} seconds. On enemy collision, he stops, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to nearby enemies, knocking them back, and slowing them by {{ e2 }}% for {{ e3 }} second.<br><br><rules><span class=\\\"color8c8c8c\\\">Can be reactivated to end the effect early and put Powerball on cooldown.<br>Powerball is a channeled spell and is subject to interruption by spells that prevent casting.</span></rules>\",\"leveltip\":{\"label\":[\"Damage \",\"Slow\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[16,13.5,11,8.5,6],\"cooldownBurn\":\"16/13.5/11/8.5/6\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[100,135,170,205,240],[40,50,60,70,80],[1,1,1,1,1],[6,6,6,6,6],[125,125,125,125,125],[0.35,0.35,0.35,0.35,0.35],[25,25,25,25,25],[0.25,0.25,0.25,0.25,0.25],[50,50,50,50,50],[1000,1000,1000,1000,1000]],\"effectBurn\":[null,\"100/135/170/205/240\",\"40/50/60/70/80\",\"1\",\"6\",\"125\",\"0.35\",\"25\",\"0.25\",\"50\",\"1000\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[300,300,300,300,300],\"rangeBurn\":\"300\",\"image\":{\"full\":\"PowerBall.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":336,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"DefensiveBallCurl\",\"name\":\"Defensive Ball Curl\",\"description\":\"Rammus goes into a defensive formation, vastly increasing his Armor and Magic Resistance, amplifying Spiked Shells' damage, and returning damage to enemies that basic attack him, but he is also slowed during this time.\",\"tooltip\":\"Rammus enters a defensive formation for up to {{ e3 }} seconds, increasing Armor and Magic Resist by {{ e1 }} plus {{ e6 }}% (<span class=\\\"colorFFFF00\\\">{{ f1 }}</span>/<span class=\\\"colorFF00FF\\\">{{ f2 }}</span>) but becoming slowed by {{ e5 }}%.<br><br>During this time, Spiked Shell deals {{ e4 }}% damage and also deals its damage to enemies that basic attack Rammus.<br><br><rules><span class=\\\"color8c8c8c\\\">Can be reactivated to end the effect early and put Defensive Ball Curl on cooldown.</span></rules>\",\"leveltip\":{\"label\":[\"Percent Armor Bonus\",\"Percent Magic Resist Bonus\"],\"effect\":[\"{{ e6 }}% -> {{ e6NL }}%\",\"{{ e6 }}% -> {{ e6NL }}%\"]},\"maxrank\":5,\"cooldown\":[6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[20,20,20,20,20],[25,35,45,55,65],[6,6,6,6,6],[150,150,150,150,150],[60,60,60,60,60],[50,55,60,65,70],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"20\",\"25/35/45/55/65\",\"6\",\"150\",\"60\",\"50/55/60/65/70\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"armor\",\"coeff\":0.1,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[300,300,300,300,300],\"rangeBurn\":\"300\",\"image\":{\"full\":\"DefensiveBallCurl.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":384,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"PuncturingTaunt\",\"name\":\"Frenzying Taunt\",\"description\":\"Rammus taunts an enemy champion or monster into a reckless assault against him. Additionally, he gains increased Attack Speed for a short time, but this bonus is extended by having any of his other spells active.\",\"tooltip\":\"Taunt an enemy champion or monster for {{ e1 }} seconds and gain {{ e2 }}% Attack Speed for the same duration.<br><br>While any of Rammus' other spells are active, Frenzying Taunt's Attack Speed bonus is refreshed.\",\"leveltip\":{\"label\":[\"Duration\",\"Attack Speed Bonus\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[12,12,12,12,12],\"cooldownBurn\":\"12\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[1.25,1.5,1.75,2,2.25],[20,25,30,35,40],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"1.25/1.5/1.75/2/2.25\",\"20/25/30/35/40\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[325,325,325,325,325],\"rangeBurn\":\"325\",\"image\":{\"full\":\"PuncturingTaunt.png\",\"sprite\":\"spell8.png\",\"group\":\"spell\",\"x\":432,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"Tremors2\",\"name\":\"Tremors\",\"description\":\"Rammus creates waves of destruction pulsing through the ground, causing damage to nearby enemies and slowing them. Turrets take double damage from Tremors.\",\"tooltip\":\"Shake the earth for {{ e2 }} seconds, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to nearby enemies and slowing them by {{ e4 }}% for {{ e3 }} seconds, stacking up to {{ e6 }} times.<br><br>Tremors deals {{ e5 }}% damage to turrets.\",\"leveltip\":{\"label\":[\"Damage per Tremor\",\"Slow per Tremor\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }}% -> {{ e4NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[100,80,60],\"cooldownBurn\":\"100/80/60\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[40,80,120],[7,7,7],[1.5,1.5,1.5],[8,10,12],[200,200,200],[8,8,8],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"40/80/120\",\"7\",\"1.5\",\"8/10/12\",\"200\",\"8\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[375,375,375],\"rangeBurn\":\"375\",\"image\":{\"full\":\"Tremors2.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":0,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"RekSai\":{\"id\":421,\"key\":\"RekSai\",\"name\":\"Rek'Sai\",\"title\":\"the Void Burrower\",\"spells\":[{\"id\":\"RekSaiQ\",\"name\":\"Queen's Wrath / Prey Seeker\",\"description\":\"Rek'Sai's next 3 basic attacks deal bonus Physical Damage to nearby enemies.<br><br>While Burrowed, Rek'Sai launches a burst of void-charged earth that deals Physical Damage and reveals enemies hit.\",\"tooltip\":\"<span class=\\\"colorFF8C00\\\">Un-Burrowed:</span> Rek'Sai's next 3 basic attacks within 5 seconds deal {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> bonus Physical Damage to nearby enemies.<br><br><span class=\\\"colorFF8C00\\\">Burrowed:</span> Rek'Sai launches a burst of void-charged earth that explodes on first enemy hit, dealing {{ e4 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> Physical Damage and revealing non-stealthed enemies for {{ e6 }} seconds.\",\"leveltip\":{\"label\":[\"Queen's Wrath Damage\",\"Prey Seeker Damage\",\"Prey Seeker Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ e5 }} -> {{ e5NL }}\"]},\"maxrank\":5,\"cooldown\":[4,4,4,4,4],\"cooldownBurn\":\"4\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[15,20,25,30,35],[0,0,0,0,0],[0,0,0,0,0],[60,90,120,150,180],[11,10,9,8,7],[2.5,2.5,2.5,2.5,2.5],[5,5,5,5,5],[300,300,300,300,300],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"15/20/25/30/35\",\"0\",\"0\",\"60/90/120/150/180\",\"11/10/9/8/7\",\"2.5\",\"5\",\"300\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.4,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a2\"},{\"link\":\"bonusattackdamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[325,325,325,325,325],\"rangeBurn\":\"325\",\"image\":{\"full\":\"RekSaiQ.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":48,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"RekSaiW\",\"name\":\"Burrow / Un-burrow\",\"description\":\"Rek'Sai burrows into the ground, gaining new abilities and increased Movement Speed. Her vision range is reduced and she cannot use basic attacks.<br><br>While Burrowed, Rek'Sai may cast Un-burrow to knock up and damage nearby enemies.\",\"tooltip\":\"<span class=\\\"colorFF8C00\\\">Un-Burrowed:</span> Burrow into the ground. <br><br><span class=\\\"colorFF8C00\\\">Burrowed:</span> Un-burrow, dealing {{ e2 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> Physical Damage and knocking up the closest enemy for {{ e5 }} second. All other nearby enemies will be knocked back. An enemy who has been knocked up will be immune from further Un-burrow effects for {{ e6 }} seconds.<br><br><span class=\\\"colorFF8C00\\\">Burrowed Effects:</span> New abilities, <span class=\\\"colorFFFFFF\\\">+{{ f1 }}</span> Movement Speed, reduced vision range, disabled basic attacks, and gains <span class=\\\"color993299\\\">Tremor Sense:</span> Nearby enemies that move in Fog of War have their position revealed to Rek'Sai and her allies.\",\"leveltip\":{\"label\":[\"Un-burrow Damage\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[4,4,4,4,4],\"cooldownBurn\":\"4\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[1500,1500,1500,1500,1500],[50,65,80,95,110],[0,0,0,0,0],[0,0,0,0,0],[1,1,1,1,1],[10,10,10,10,10],[250,250,250,250,250],[175,175,175,175,175],[3,3,3,3,3],[0,0,0,0,0]],\"effectBurn\":[null,\"1500\",\"50/65/80/95/110\",\"0\",\"0\",\"1\",\"10\",\"250\",\"175\",\"3\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[1650,1650,1650,1650,1650],\"rangeBurn\":\"1650\",\"image\":{\"full\":\"RekSaiW.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":96,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"RekSaiE\",\"name\":\"Furious Bite / Tunnel\",\"description\":\"Rek'Sai bites her target, dealing double and True Damage if she has max Fury.<br><br>While Burrowed, Rek'Sai creates a re-usable, long lasting tunnel. Enemies can destroy it by standing on top of either entrance.\",\"tooltip\":\"<span class=\\\"colorFF8C00\\\">Un-Burrowed:</span> Rek'Sai bites a target dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> Physical Damage. At max Fury, the bite does 100% more and deals True Damage.<br><br>Maximum Damage: <span class=\\\"colorFFFFFF\\\">{{ f2 }}</span><br><br><span class=\\\"colorFF8C00\\\">Burrowed:</span> Rek'Sai tunnels forward leaving two connected <span class=\\\"color993299\\\">Tunnel Entrances</span>. Clicking a <span class=\\\"color993299\\\">Tunnel Entrance</span> will make Rek'Sai dive to the other entrance.<br><br><span class=\\\"color993299\\\">Tunnel Entrances</span> last for {{ e5 }} minutes and can be destroyed by enemies. Rek'Sai may have {{ e6 }} tunnels at one time. Tunnels have a {{ e8 }} second cooldown on use.\",\"leveltip\":{\"label\":[\"Furious Bite Damage\",\"Tunnel Cast Cooldown\",\"Tunnel Use Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ f4 }} -> {{ f5 }}\",\"{{ e8 }} -> {{ e8NL }}\"]},\"maxrank\":5,\"cooldown\":[12,12,12,12,12],\"cooldownBurn\":\"12\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[50,60,70,80,90],[0,0,0,0,0],[12,12,12,12,12],[0,0,0,0,0],[10,10,10,10,10],[8,8,8,8,8],[0,0,0,0,0],[10,8,6,4,2],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/60/70/80/90\",\"0\",\"12\",\"0\",\"10\",\"8\",\"0\",\"10/8/6/4/2\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.85,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[250,250,250,250,250],\"rangeBurn\":\"250\",\"image\":{\"full\":\"RekSaiE.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":144,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"RekSaiRWrapper\",\"name\":\"Void Rush\",\"description\":\"Rek'Sai dives underground and leaps towards her target, dealing massive damage.\",\"tooltip\":\"<span class=\\\"colorFF8C00\\\">Passive:</span> Dealing damage to enemy Champions marks them for the next {{ e6 }} seconds.<br><br><span class=\\\"colorFF8C00\\\">Active:</span> Rek'Sai lets out a harrowing scream before burrowing underground seeking out a marked target.  Moments later she leaps from underground, dealing {{ e8 }} <span class=\\\"colorFF8C00\\\">(+{{ a2 }})</span> physical damage plus {{ e9 }}% of their missing Health.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Missing Health Damage\"],\"effect\":[\"{{ e8 }} -> {{ e8NL }}\",\"{{ f7 }} -> {{ f8 }}\",\"{{ e9 }}% -> {{ e9NL }}%\"]},\"maxrank\":3,\"cooldown\":[0,0,0],\"cooldownBurn\":\"0\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[5,5,5],[0,0,0],[100,250,400],[20,25,30],[0,0,0]],\"effectBurn\":[null,\"0\",\"0\",\"0\",\"0\",\"0\",\"5\",\"0\",\"100/250/400\",\"20/25/30\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1.6,\"key\":\"a2\"}],\"costType\":\"No cost\",\"maxammo\":\"-1\",\"range\":[1500,1500,1500],\"rangeBurn\":\"1500\",\"image\":{\"full\":\"RekSaiRWrapper.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":192,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No cost\"}]},\"Renekton\":{\"id\":58,\"key\":\"Renekton\",\"name\":\"Renekton\",\"title\":\"the Butcher of the Sands\",\"spells\":[{\"id\":\"RenektonCleave\",\"name\":\"Cull the Meek\",\"description\":\"Renekton swings his blade, dealing moderate physical damage to all targets around him, and heals for a small portion of the damage dealt. If he has more than 50 Fury, his damage and heal are increased.\",\"tooltip\":\"Renekton swings his blade, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage to nearby enemies and healing himself for {{ e2 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> Health for each non-champion hit and {{ f2 }} <span class=\\\"colorFF8C00\\\">(+{{ f3 }})</span> Health for each champion hit, up to {{ e4 }} Health.<br><br>He generates <span class=\\\"colorFF6633\\\">2.5 Fury</span> for each non-champion hit and <span class=\\\"colorFF6633\\\">10 Fury</span> for each champion hit, up to a maximum of <span class=\\\"colorFF6633\\\">30 Fury</span>.<br><br><span class=\\\"colorFF6633\\\">50 Fury Bonus:</span> Damage increased to {{ e6 }} <span class=\\\"colorFF8C00\\\">(+{{ a2 }})</span>. Heal increased to {{ e7 }} <span class=\\\"colorFF8C00\\\">(+{{ f4 }})</span> per non-champion and {{ f5 }} <span class=\\\"colorFF8C00\\\">(+{{ f6 }})</span> per champion, up to {{ e9 }}. No longer generates <span class=\\\"colorFF6633\\\">Fury</span>.\",\"leveltip\":{\"label\":[\"Damage\",\"Max Health Gain\",\"Fury Damage\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }} / {{ e9 }} -> {{ e4NL }} / {{ e9NL }}\",\"{{ e6 }} -> {{ e6NL }}\"]},\"maxrank\":5,\"cooldown\":[8,8,8,8,8],\"cooldownBurn\":\"8\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[60,90,120,150,180],[3,4.5,6,7.5,9],[0.04,0.04,0.04,0.04,0.04],[50,75,100,125,150],[300,300,300,300,300],[90,135,180,225,270],[9,13.5,18,22.5,27],[0.12,0.12,0.12,0.12,0.12],[150,225,300,375,450],[0,0,0,0,0]],\"effectBurn\":[null,\"60/90/120/150/180\",\"3/4.5/6/7.5/9\",\"0.04\",\"50/75/100/125/150\",\"300\",\"90/135/180/225/270\",\"9/13.5/18/22.5/27\",\"0.12\",\"150/225/300/375/450\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.8,\"key\":\"a1\"},{\"link\":\"bonusattackdamage\",\"coeff\":0.8,\"key\":\"f1\"},{\"link\":\"bonusattackdamage\",\"coeff\":1.2,\"key\":\"f2\"},{\"link\":\"bonusattackdamage\",\"coeff\":1.2,\"key\":\"a2\"}],\"costType\":\"No Cost or 50 Fury\",\"maxammo\":\"-1\",\"range\":[325,325,325,325,325],\"rangeBurn\":\"325\",\"image\":{\"full\":\"RenektonCleave.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":240,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost or 50 Fury\"},{\"id\":\"RenektonPreExecute\",\"name\":\"Ruthless Predator\",\"description\":\"Renekton slashes his target twice, dealing moderate physical damage and stuns them for 0.75 seconds. If Renekton has more than 50 Fury, he slashes his target three times, dealing high physical damage and stuns them for 1.5 seconds.\",\"tooltip\":\"Renekton's next attack strikes twice, stunning his target for {{ e3 }} seconds and dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage per hit ({{ e5 }} <span class=\\\"colorFF8C00\\\">(+{{ f2 }})</span> total).<br><br>Each strike applies on-hit effects and generates <span class=\\\"colorFF6633\\\">Fury</span>. Hitting a champion generates an additional <span class=\\\"colorFF6633\\\">{{ e7 }} Fury</span>.<br><br><span class=\\\"colorFF6633\\\">50 Fury Bonus:</span> Renekton attacks three times, stunning his target for {{ e4 }} seconds and dealing a total of {{ e6 }} <span class=\\\"colorFF8C00\\\">(+{{ f3 }})</span> damage.  No longer generates <span class=\\\"colorFF6633\\\">Fury</span>.\",\"leveltip\":{\"label\":[\"Bonus Damage\",\"Cooldown\"],\"effect\":[\"{{ e5 }} / {{ e6 }} -> {{ e5NL }} / {{ e6NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[13,12,11,10,9],\"cooldownBurn\":\"13/12/11/10/9\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[5,15,25,35,45],[0.75,0.75,0.75,0.75,0.75],[0.75,0.75,0.75,0.75,0.75],[1.5,1.5,1.5,1.5,1.5],[10,30,50,70,90],[15,45,75,105,135],[10,10,10,10,10],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"5/15/25/35/45\",\"0.75\",\"0.75\",\"1.5\",\"10/30/50/70/90\",\"15/45/75/105/135\",\"10\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1.5,\"key\":\"f1\"},{\"link\":\"attackdamage\",\"coeff\":2.25,\"key\":\"f2\"}],\"costType\":\"No Cost or 50 Fury\",\"maxammo\":\"-1\",\"range\":[300,300,300,300,300],\"rangeBurn\":\"300\",\"image\":{\"full\":\"RenektonPreExecute.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":288,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost or 50 Fury\"},{\"id\":\"RenektonSliceAndDice\",\"name\":\"Slice and Dice\",\"description\":\"Renekton dashes, dealing damage to units along the way. Empowered, Renekton deals bonus damage and reduces the Armor of units hit.\",\"tooltip\":\"<span class=\\\"colorFF6633\\\">Slice:</span> Renekton dashes, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage to enemies he passes through. Hitting an enemy grants the ability to use Dice for {{ e8 }} seconds.<br><br><span class=\\\"colorFF6633\\\">Dice:</span> Renekton dashes, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage to enemies he passes through.<br><br>He generates <span class=\\\"colorFF6633\\\">{{ e4 }} Fury</span> for each non-champion hit and <span class=\\\"colorFF6633\\\">{{ e5 }} Fury</span> for each champion hit, up to a maximum of <span class=\\\"colorFF6633\\\">{{ e0 }} Fury</span>.<br><br><span class=\\\"colorFF6633\\\">Dice - 50 Fury Bonus:</span> Damage increased to {{ e3 }} <span class=\\\"colorFF8C00\\\">(+{{ f2 }})</span>. Enemies hit have {{ e2 }}% reduced Armor for {{ e9 }} seconds. No longer generates <span class=\\\"colorFF6633\\\">Fury</span>.\",\"leveltip\":{\"label\":[\"Bonus Damage\",\"Armor Reduction %\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[18,17,16,15,14],\"cooldownBurn\":\"18/17/16/15/14\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[30,60,90,120,150],[15,20,25,30,35],[45,90,135,180,225],[2,2,2,2,2],[10,10,10,10,10],[0.9,0.9,0.9,0.9,0.9],[1.35,1.35,1.35,1.35,1.35],[4,4,4,4,4],[4,4,4,4,4],[30,30,30,30,30]],\"effectBurn\":[null,\"30/60/90/120/150\",\"15/20/25/30/35\",\"45/90/135/180/225\",\"2\",\"10\",\"0.9\",\"1.35\",\"4\",\"4\",\"30\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.9,\"key\":\"f1\"},{\"link\":\"bonusattackdamage\",\"coeff\":0.9,\"key\":\"f1\"},{\"link\":\"bonusattackdamage\",\"coeff\":1.35,\"key\":\"f2\"}],\"costType\":\"No Cost or 50 Fury\",\"maxammo\":\"-1\",\"range\":[450,450,450,450,450],\"rangeBurn\":\"450\",\"image\":{\"full\":\"RenektonSliceAndDice.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":336,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost or 50 Fury\"},{\"id\":\"RenektonReignOfTheTyrant\",\"name\":\"Dominus\",\"description\":\"Renekton transforms into the Tyrant form, gaining bonus Health and dealing damage to enemies around him. While in this form, Renekton gains Fury periodically.\",\"tooltip\":\"Renekton surrounds himself with dark energies for {{ e5 }} seconds, gaining {{ e1 }} Health and <span class=\\\"colorFF6633\\\">{{ e6 }} Fury</span>. While active, he deals {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to nearby enemies and gains <span class=\\\"colorFF6633\\\">{{ e4 }} Fury</span> per second.\",\"leveltip\":{\"label\":[\"Bonus Health\",\"Periodic Damage\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":3,\"cooldown\":[120,120,120],\"cooldownBurn\":\"120\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[250,500,750],[40,80,120],[7.5,7.5,7.5],[5,5,5],[15,15,15],[20,20,20],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"250/500/750\",\"40/80/120\",\"7.5\",\"5\",\"15\",\"20\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.1,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"RenektonReignOfTheTyrant.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":384,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Rengar\":{\"id\":107,\"key\":\"Rengar\",\"name\":\"Rengar\",\"title\":\"the Pridestalker\",\"spells\":[{\"id\":\"RengarQ\",\"name\":\"Savagery\",\"description\":\"Rengar slashes all enemies in an arc before piercing all enemies in a line. Casting this outside the max range moves Rengar a short distance in that direction.<br><br>Ferocity effect: deals increased damage.\",\"tooltip\":\"Slash all enemies in an arc before piercing all enemies in a line for {{ e1 }} <span class=\\\"colorF88017\\\">(+{{ f2 }})</span> physical damage per strike.<br><br><span class=\\\"colorEDDA74\\\">Ferocity effect:</span><br>Each hit deals <span class=\\\"colorFFFFFF\\\">{{ f1 }}</span> <span class=\\\"colorFF8C00\\\">(+{{ a2 }})</span> damage.<br><br><rules><span class=\\\"color8c8c8c\\\">If cast outside max range, Rengar lunges forward to extend the range of the piercing attack.<br>If used during leap, Savagery is cast immediately following the leap.</span></rules>\",\"leveltip\":{\"label\":[\"Base Damage\",\"Bonus Attack Damage Ratio\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e7 }} -> {{ e7NL }}\"]},\"maxrank\":5,\"cooldown\":[0.25,0.25,0.25,0.25,0.25],\"cooldownBurn\":\"0.25\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[25,45,65,85,105],[0.3,0.3,0.3,0.3,0.3],[350,350,350,350,350],[90,90,90,90,90],[150,150,150,150,150],[0.15,0.15,0.15,0.15,0.15],[0.2,0.3,0.4,0.5,0.6],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"25/45/65/85/105\",\"0.3\",\"350\",\"90\",\"150\",\"0.15\",\"0.2/0.3/0.4/0.5/0.6\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"@dynamic.attackdamage\",\"coeff\":1,\"key\":\"f1\"},{\"link\":\"bonusattackdamage\",\"coeff\":1.1,\"key\":\"a2\"}],\"costType\":\"Generates 1 Ferocity\",\"maxammo\":\"1\",\"range\":[450,450,450,450,450],\"rangeBurn\":\"450\",\"image\":{\"full\":\"RengarQ.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":432,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"Generates 1 Ferocity\"},{\"id\":\"RengarW\",\"name\":\"Battle Roar\",\"description\":\"Rengar lets out a battle roar, damaging enemies and healing for some of the recent damage he has taken.<br><br>Ferocity effect: additionally breaks crowd control effects.\",\"tooltip\":\"Deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to nearby enemies and heals Rengar for {{ e2 }}% of the damage he took in the last {{ e3 }} seconds.<br><br><span class=\\\"colorEDDA74\\\">Ferocity effect: </span><br>Deals <span class=\\\"colorFFFFFF\\\">{{ f1 }}</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and removes existing crowd control effects, in addition to its normal effect.<br><br><rules><span class=\\\"color8c8c8c\\\">Damage healed from monster attacks is increased by {{ e5 }}%.</span></rules>\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ ammorechargetime }} -> {{ ammorechargetimeNL }}\"]},\"maxrank\":5,\"cooldown\":[0.25,0.25,0.25,0.25,0.25],\"cooldownBurn\":\"0.25\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[50,80,110,140,170],[50,50,50,50,50],[1.5,1.5,1.5,1.5,1.5],[1.5,1.5,1.5,1.5,1.5],[50,50,50,50,50],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/80/110/140/170\",\"50\",\"1.5\",\"1.5\",\"50\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\"Generates 1 Ferocity\",\"maxammo\":\"1\",\"range\":[450,450,450,450,450],\"rangeBurn\":\"450\",\"image\":{\"full\":\"RengarW.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":0,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"Generates 1 Ferocity\"},{\"id\":\"RengarE\",\"name\":\"Bola Strike\",\"description\":\"Rengar throws a bola, slowing the first target hit for a short duration.<br><br>Ferocity effect: roots the target.\",\"tooltip\":\"Throw a bola, dealing {{ e1 }}<span class=\\\"colorF88017\\\"> (+{{ a1 }})</span> physical damage and slowing the first enemy hit by {{ e2 }}% for {{ e3 }} seconds.<br><br><span class=\\\"colorEDDA74\\\">Ferocity effect:</span><br>Deals <span class=\\\"colorFFFFFF\\\">{{ f1 }}</span> <span class=\\\"colorF88017\\\">(+{{ a1 }})</span> physical damage and roots the target for {{ e4 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow Amount\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[0.25,0.25,0.25,0.25,0.25],\"cooldownBurn\":\"0.25\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[50,95,140,185,230],[30,45,60,75,90],[1.75,1.75,1.75,1.75,1.75],[1.75,1.75,1.75,1.75,1.75],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/95/140/185/230\",\"30/45/60/75/90\",\"1.75\",\"1.75\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.7,\"key\":\"a1\"},{\"link\":\"bonusattackdamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\"Generates 1 Ferocity\",\"maxammo\":\"1\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"RengarE.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":48,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"Generates 1 Ferocity\"},{\"id\":\"RengarR\",\"name\":\"Thrill of the Hunt\",\"description\":\"Rengar's predatory instincts take over, <font color='#cd90ee'>Camouflaging</font> him and revealing the nearest enemy champion in a large radius around him. During Thrill of the Hunt, Rengar gains Movement Speed and he can leap to the tracked enemy for a guaranteed critical strike, even without being in brush.\",\"tooltip\":\"Rengar gains {{ e1 }}% Movement Speed and <span class=\\\"coloree91d7\\\">True Sight</span> of the nearest enemy champion (within {{ e3 }} range) for {{ e2 }} seconds.<br><br>After the first {{ e6 }} seconds, Rengar becomes <span class=\\\"colorcd90ee\\\">Camouflaged</span> and can leap to an enemy without being in brush. Leaping to the nearest champion results in a critical strike.<br><br><rules><span class=\\\"color8c8c8c\\\">Attacking or casting most spells ends Thrill of the Hunt.</span><br><span class=\\\"colorcd90ee\\\"><u>Stealth - Camouflaged:</u></span><span class=\\\"color8c8c8c\\\"> This character is hidden from view. It is revealed by nearby enemy champions and turrets.</span></rules>\",\"leveltip\":{\"label\":[\"Duration\",\"Detection Range\",\"Cooldown\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[130,100,70],\"cooldownBurn\":\"130/100/70\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[40,40,40],[12,16,20],[2000,3000,4000],[750,750,750],[725,725,725],[2,2,2],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"40\",\"12/16/20\",\"2000/3000/4000\",\"750\",\"725\",\"2\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[2000,3000,4000],\"rangeBurn\":\"2000/3000/4000\",\"image\":{\"full\":\"RengarR.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":96,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Riven\":{\"id\":92,\"key\":\"Riven\",\"name\":\"Riven\",\"title\":\"the Exile\",\"spells\":[{\"id\":\"RivenTriCleave\",\"name\":\"Broken Wings\",\"description\":\"Riven lashes out in a series of strikes. This ability can be reactivated three times in a short time frame with the third hit knocking back nearby enemies.\",\"tooltip\":\"Riven strikes out in front of her in a short line. This ability may be re-cast {{ e4 }} additional times.<br><br><span class=\\\"colorDDDD77\\\">1st and 2nd Use:</span> Slashes forward, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage to all units she comes in contact with.<br><span class=\\\"colorDDDD77\\\">3rd Use:</span> Leaps forward and slams the ground dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage and knocking nearby enemies up. This leap can cross unpathable terrain.<br><br><span class=\\\"colorDDDD77\\\">Riven will target the unit your cursor is hovering over, or if no targets are present, Riven will simply strike the direction she is currently facing.</span>\",\"leveltip\":{\"label\":[\"Base Damage\",\"Attack Damage Scaling\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e5 }}% -> {{ e5NL }}%\"]},\"maxrank\":5,\"cooldown\":[13,13,13,13,13],\"cooldownBurn\":\"13\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[10,30,50,70,90],[30,55,80,105,130],[150,225,300,375,450],[2,2,2,2,2],[40,45,50,55,60],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"10/30/50/70/90\",\"30/55/80/105/130\",\"150/225/300/375/450\",\"2\",\"40/45/50/55/60\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":[0.4,0.45,0.5,0.55,0.6],\"key\":\"f1\"},{\"link\":\"attackdamage\",\"coeff\":[0.4,0.45,0.5,0.55,0.6],\"key\":\"f1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[275,275,275,275,275],\"rangeBurn\":\"275\",\"image\":{\"full\":\"RivenTriCleave.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":144,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"RivenMartyr\",\"name\":\"Ki Burst\",\"description\":\"Riven emits a Ki Burst, damaging and stunning nearby enemies.\",\"tooltip\":\"Riven's sword emits a burst of runic energy that shocks nearby enemies, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage and stunning them for {{ e5 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[11,10,9,8,7],\"cooldownBurn\":\"11/10/9/8/7\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[50,80,110,140,170],[40,70,100,130,160],[3,3,3,3,3],[0.75,0.75,0.75,0.75,0.75],[0.75,0.75,0.75,0.75,0.75],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/80/110/140/170\",\"40/70/100/130/160\",\"3\",\"0.75\",\"0.75\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1,\"key\":\"f1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[260,260,260,260,260],\"rangeBurn\":\"260\",\"image\":{\"full\":\"RivenMartyr.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":192,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"RivenFeint\",\"name\":\"Valor\",\"description\":\"Riven steps forward a short distance and blocks incoming damage.\",\"tooltip\":\"Riven does a quick dash in the direction of your cursor and becomes shielded, blocking up to {{ e2 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> incoming damage for 1.5 seconds.\",\"leveltip\":{\"label\":[\"Damage Blocked\",\"Cooldown\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[10,9,8,7,6],\"cooldownBurn\":\"10/9/8/7/6\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[90,120,150,180,210],[90,120,150,180,210],[4,4,4,4,4],[800,800,800,800,800],[1.5,1.5,1.5,1.5,1.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"90/120/150/180/210\",\"90/120/150/180/210\",\"4\",\"800\",\"1.5\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1,\"key\":\"f1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[250,250,250,250,250],\"rangeBurn\":\"250\",\"image\":{\"full\":\"RivenFeint.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":240,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"RivenFengShuiEngine\",\"name\":\"Blade of the Exile\",\"description\":\"Riven empowers her keepsake weapon with energy, and gains Attack Damage and Range. During this time, she also gains the ability to use Wind Slash, a powerful ranged attack, once. \",\"tooltip\":\"Riven's weapon surges with spiritual energy for {{ e4 }} seconds, granting her {{ e5 }}% extra Attack Damage (<span class=\\\"colorFF8C00\\\">+{{ f3 }}</span>), increased Range on her damaging spells and attacks, and the ability to use Wind Slash once.<br><br><span class=\\\"colorDDDD77\\\">Wind Slash:</span> Riven fires a shockwave that deals from {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> to {{ e2 }} <span class=\\\"colorFF8C00\\\">(+{{ f2 }})</span> physical damage to all enemies hit, increasing based on how much Health they are missing.\",\"leveltip\":{\"label\":[\"Shockwave Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} / {{ e2 }} -> {{ e1NL }} / {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[130,95,60],\"cooldownBurn\":\"130/95/60\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[100,150,200],[300,450,600],[20,20,20],[15,15,15],[20,20,20],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"100/150/200\",\"300/450/600\",\"20\",\"15\",\"20\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.2,\"key\":\"f3\"},{\"link\":\"bonusattackdamage\",\"coeff\":0.6,\"key\":\"f1\"},{\"link\":\"bonusattackdamage\",\"coeff\":1.8,\"key\":\"f2\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[200,200,200],\"rangeBurn\":\"200\",\"image\":{\"full\":\"RivenFengShuiEngine.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":288,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Rumble\":{\"id\":68,\"key\":\"Rumble\",\"name\":\"Rumble\",\"title\":\"the Mechanized Menace\",\"spells\":[{\"id\":\"RumbleFlameThrower\",\"name\":\"Flamespitter\",\"description\":\"Rumble torches opponents in front of him, dealing magic damage in a cone for 3 seconds. While in Danger Zone this damage is increased. \",\"tooltip\":\"Rumble torches his opponents, dealing {{ e1 }} <scaleAP>(+{{ a1 }})</scaleAP> magic damage in a cone over {{ e9 }} seconds.<br><br><caution>Danger Zone Bonus:</caution> Deals {{ e5 }}% damage.\",\"leveltip\":{\"label\":[\"Damage\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":5,\"cooldown\":[6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[75,135,195,255,315],[20,20,20,20,20],[0,0,0,0,0],[1,1,1,1,1],[150,150,150,150,150],[12,12,12,12,12],[0.25,0.25,0.25,0.25,0.25],[3,3,3,3,3],[3,3,3,3,3],[0,0,0,0,0]],\"effectBurn\":[null,\"75/135/195/255/315\",\"20\",\"0\",\"1\",\"150\",\"12\",\"0.25\",\"3\",\"3\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Heat\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"RumbleFlameThrower.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":336,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ e2 }} Heat\"},{\"id\":\"RumbleShield\",\"name\":\"Scrap Shield\",\"description\":\"Rumble pulls up a shield, protecting him from damage and granting him a quick burst of speed. While in Danger Zone, the shield strength and speed bonus is increased. \",\"tooltip\":\"Rumble tosses up a shield for {{ e5 }} seconds that absorbs {{ e1 }} <scaleAP>(+{{ a1 }})</scaleAP> damage. Rumble also gains an additional {{ e2 }}% Movement Speed for {{ e6 }} second. </span><br><br><caution>Danger Zone Bonus:</caution> 50% increase in shield health and Movement Speed.\",\"leveltip\":{\"label\":[\"Damage Absorption\",\"Movement Speed Bonus\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[50,80,110,140,170],[10,15,20,25,30],[20,20,20,20,20],[0,0,0,0,0],[2,2,2,2,2],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/80/110/140/170\",\"10/15/20/25/30\",\"20\",\"0\",\"2\",\"1\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\" Heat\",\"maxammo\":\"-1\",\"range\":[20,20,20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"RumbleShield.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":384,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ e3 }} Heat\"},{\"id\":\"RumbleGrenade\",\"name\":\"Electro Harpoon\",\"description\":\"Rumble launches a taser, electrocuting his target with magic damage and slowing their Movement Speed. Rumble can carry 2 harpoons at a time. While in Danger Zone the damage and slow percentage is increased.  \",\"tooltip\":\"Rumble shoots his opponent with up to 2 tasers, dealing {{ e1 }}<scaleAP> (+{{ a1 }})</scaleAP> magic damage and applying a stacking slow of {{ e2 }}% for {{ e7 }} seconds.<br><br><caution>Danger Zone Bonus:</caution> Damage and slow percentage increased by 50%.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow Amount\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[0.5,0.5,0.5,0.5,0.5],\"cooldownBurn\":\"0.5\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[45,70,95,120,145],[15,20,25,30,35],[10,10,10,10,10],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[2,2,2,2,2],[30,40,50,60,66],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"45/70/95/120/145\",\"15/20/25/30/35\",\"10\",\"0\",\"0\",\"0\",\"2\",\"30/40/50/60/66\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\" Heat\",\"maxammo\":\"2\",\"range\":[850,850,850,850,850],\"rangeBurn\":\"850\",\"image\":{\"full\":\"RumbleGrenade.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":432,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ e3 }} Heat\"},{\"id\":\"RumbleCarpetBomb\",\"name\":\"The Equalizer\",\"description\":\"Rumble fires off a group of rockets, creating a wall of flames that damages and slows enemies. \",\"tooltip\":\"Rumble launches a line of rockets that creates a burning trail for {{ e5 }} seconds. Enemies in the area have their Movement Speed slowed by {{ e3 }}% and take {{ e1 }}<scaleAP> (+{{ a2 }})</scaleAP> magic damage each second.<br><br><specialRules>You can control the placement of this attack by clicking and dragging your mouse in a line.</specialRules>\",\"leveltip\":{\"label\":[\"Damage Per Second\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[110,100,90],\"cooldownBurn\":\"110/100/90\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[130,185,240],[0,0,0],[35,35,35],[0.5,0.5,0.5],[5,5,5],[6,6,6],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"130/185/240\",\"0\",\"35\",\"0.5\",\"5\",\"6\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a2\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[1750,1750,1750],\"rangeBurn\":\"1750\",\"image\":{\"full\":\"RumbleCarpetBomb.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":0,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Ryze\":{\"id\":13,\"key\":\"Ryze\",\"name\":\"Ryze\",\"title\":\"the Rune Mage\",\"spells\":[{\"id\":\"RyzeQWrapper\",\"name\":\"Overload\",\"description\":\"Passively, Ryze's other damaging spells reset Overload and begin to charge a Rune that can be used to empower Overload.<br><br>On cast, Ryze throws a charge of pure energy in a line, dealing damage to the first enemy struck. If a Rune is fully charged, Ryze also gains a shield and Movement Speed.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> Rune Prison and Spell Flux reset Overload's cooldown and charge a <span class=\\\"colorFFF673\\\">Rune</span> for {{ e2 }} seconds, up to {{ e9 }} <span class=\\\"colorFFF673\\\">Runes</span>.<br><br><span class=\\\"colorFF9900\\\">Active:</span> Unleash a runic blast, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> <span class=\\\"color44DDFF\\\">(+{{ f1 }})</span> magic damage to the first enemy struck. Any active <span class=\\\"colorFFF673\\\">Runes</span> are discharged.<br><br>If {{ e9 }} <span class=\\\"colorFFF673\\\">Runes</span> are discharged, they Overload, shielding Ryze from <span class=\\\"colorFFFFFF\\\">{{ f3 }}</span> <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> <span class=\\\"color44DDFF\\\">(+{{ f2 }})</span> damage and increasing his Movement Speed by {{ e4 }}% for {{ e5 }} seconds. \",\"leveltip\":{\"label\":[\"Base Damage\",\"Movement Speed\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }}% -> {{ e4NL }}%\"]},\"maxrank\":6,\"cooldown\":[6,6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[0,0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[60,85,110,135,160,185],[4,4,4,4,4,4],[55,55,55,55,55,55],[25,28,31,34,37,40],[2,2,2,2,2,2],[2,2,2,2,2,2],[3,3,3,3,3,3],[3,3,3,3,3,3],[2,2,2,2,2,2],[40,40,40,40,40,40]],\"effectBurn\":[null,\"60/85/110/135/160/185\",\"4\",\"55\",\"25/28/31/34/37/40\",\"2\",\"2\",\"3\",\"3\",\"2\",\"40\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.45,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"RyzeQWrapper.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":48,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ e0 }} Mana\"},{\"id\":\"RyzeW\",\"name\":\"Rune Prison\",\"description\":\"Ryze traps a target enemy unit in a cage of runes, damaging them and preventing them from moving.\",\"tooltip\":\"Instantly root an enemy for {{ e1 }} second and deal {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> <span class=\\\"color44DDFF\\\">(+{{ f1 }})</span> magic damage.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":6,\"cooldown\":[13,12,11,10,9,9],\"cooldownBurn\":\"13/12/11/10/9/9\",\"cost\":[50,60,70,80,90,100],\"costBurn\":\"50/60/70/80/90/100\",\"effect\":[null,[0.75,0.75,0.75,0.75,0.75,0.75],[80,100,120,140,160,180],[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0]],\"effectBurn\":[null,\"0.75\",\"80/100/120/140/160/180\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[615,615,615,615,615,615],\"rangeBurn\":\"615\",\"image\":{\"full\":\"RyzeW.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":96,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"RyzeE\",\"name\":\"Spell Flux\",\"description\":\"Ryze releases an orb of pure magical power that damages an enemy and debuffs them. Ryze's spells have additional effects against the debuffed enemy.\",\"tooltip\":\"Apply Flux to an enemy, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> <span class=\\\"color44DDFF\\\">(+{{ f1 }})</span> magic damage. Spells consume Flux for bonus effects:<br><br><span class=\\\"colorFFF673\\\">Overload:</span> Deals {{ e2 }}% more damage and spreads to nearby enemies with Flux.<br><span class=\\\"colorFFF673\\\">Rune Prison:</span> Root duration is increased to {{ e6 }} seconds.<br><span class=\\\"colorFFF673\\\">Spell Flux:</span> Spreads Spell Flux to nearby enemies.<br><br><i>Spells that kill enemies affected by Flux spread Spell Flux to nearby enemies.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Overload Damage Increase\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":6,\"cooldown\":[3.25,3,2.75,2.5,2.25,2.25],\"cooldownBurn\":\"3.25/3/2.75/2.5/2.25/2.25\",\"cost\":[40,55,70,85,100,115],\"costBurn\":\"40/55/70/85/100/115\",\"effect\":[null,[50,75,100,125,150,175],[40,50,60,70,80,90],[167,167,167,167,167,167],[0.1,0.1,0.1,0.1,0.1,0.1],[1.5,1.5,1.5,1.5,1.5,1.5],[2,2,2,2,2,2],[4,4,4,4,4,4],[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0]],\"effectBurn\":[null,\"50/75/100/125/150/175\",\"40/50/60/70/80/90\",\"167\",\"0.1\",\"1.5\",\"2\",\"4\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[615,615,615,615,615,615],\"rangeBurn\":\"615\",\"image\":{\"full\":\"RyzeE.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":144,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"RyzeR\",\"name\":\"Realm Warp\",\"description\":\"Ryze creates a portal to a nearby location. After a few seconds, allies standing near the portal are teleported to the target location.\",\"tooltip\":\"Ryze opens a portal to a location up to {{ e7 }} range away. After {{ e4 }} seconds, all allies near the portal are teleported to that location.<br><br><i>If Ryze becomes unable to cast or move, Realm Warp is cancelled.</i>\",\"leveltip\":{\"label\":[\"Range\"],\"effect\":[\"{{ e7 }} -> {{ e7NL }}\"]},\"maxrank\":2,\"cooldown\":[120,120],\"cooldownBurn\":\"120\",\"cost\":[100,100],\"costBurn\":\"100\",\"effect\":[null,[2.1,2.1],[0.65,0.65],[0.75,0.75],[2,2],[150,250],[6,6],[1750,3000],[0,0],[0,0],[0,0]],\"effectBurn\":[null,\"2.1\",\"0.65\",\"0.75\",\"2\",\"150/250\",\"6\",\"1750/3000\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1750,3000],\"rangeBurn\":\"1750/3000\",\"image\":{\"full\":\"RyzeR.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":192,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Sejuani\":{\"id\":113,\"key\":\"Sejuani\",\"name\":\"Sejuani\",\"title\":\"Fury of the North\",\"spells\":[{\"id\":\"SejuaniQ\",\"name\":\"Arctic Assault\",\"description\":\"Sejuani charges forward, knocking enemies into the air. The charge stops after hitting an enemy champion.\",\"tooltip\":\"Sejuani charges, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemies and knocking them up. The charge ends after hitting an enemy champion.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[13,12.5,12,11.5,11],\"cooldownBurn\":\"13/12.5/12/11.5/11\",\"cost\":[70,75,80,85,90],\"costBurn\":\"70/75/80/85/90\",\"effect\":[null,[60,90,120,150,180],[625,625,625,625,625],[1000,1000,1000,1000,1000],[0.5,0.5,0.5,0.5,0.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/90/120/150/180\",\"625\",\"1000\",\"0.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"SejuaniQ.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":240,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SejuaniW\",\"name\":\"Winter's Wrath\",\"description\":\"Sejuani swings her mace twice, dealing damage and applying Frost stacks.\",\"tooltip\":\"Sejuani swings her flail, dealing {{ e1 }} <span class=\\\"colorFF3300\\\">(+{{ f1 }})</span> physical damage and applying <span class=\\\"color07F2F1\\\">Frost</span> to enemies hit and knocking minions and monsters back.<br><br>She then lashes out with her flail, dealing {{ e3 }} <span class=\\\"colorFF3300\\\">(+{{ f2 }})</span> physical damage, applying <span class=\\\"color07F2F1\\\">Frost</span> and slowing enemies hit briefly.\",\"leveltip\":{\"label\":[\"First Hit Damage\",\"Second Hit Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[9,8,7,6,5],\"cooldownBurn\":\"9/8/7/6/5\",\"cost\":[80,80,80,80,80],\"costBurn\":\"80\",\"effect\":[null,[20,25,30,35,40],[0.75,0.75,0.75,0.75,0.75],[30,65,100,135,170],[0,0,0,0,0],[75,75,75,75,75],[0.25,0.25,0.25,0.25,0.25],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"20/25/30/35/40\",\"0.75\",\"30/65/100/135/170\",\"0\",\"75\",\"0.25\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"SejuaniW.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":288,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SejuaniE\",\"name\":\"Permafrost\",\"description\":\"Sejuani freezes and stuns an enemy champion that has maximum Frost stacks.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> Nearby melee allied champions' basic attacks apply <span class=\\\"color07F2F1\\\">Frost</span> to enemy champions and large monsters.<br><br><span class=\\\"colorFF9900\\\">Active:</span> Target enemy with 4 stacks of <span class=\\\"color07F2F1\\\">Frost</span> takes {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and is stunned for {{ e1 }} second(s).<br><br><span class=\\\"size16 color8C8C8C\\\">16\",\"leveltip\":{\"label\":[\"Damage\",\"Stun Duration\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":5,\"cooldown\":[1.5,1.5,1.5,1.5,1.5],\"cooldownBurn\":\"1.5\",\"cost\":[20,20,20,20,20],\"costBurn\":\"20\",\"effect\":[null,[1,1.25,1.5,1.75,2],[0,0,0,0,0],[40,60,80,100,120],[4,4,4,4,4],[5,5,5,5,5],[1100,1100,1100,1100,1100],[0,0,0,0,0],[0,0,0,0,0],[250,250,250,250,250],[0,0,0,0,0]],\"effectBurn\":[null,\"1/1.25/1.5/1.75/2\",\"0\",\"40/60/80/100/120\",\"4\",\"5\",\"1100\",\"0\",\"0\",\"250\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[750,750,750,750,750],\"rangeBurn\":\"750\",\"image\":{\"full\":\"SejuaniE.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":336,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SejuaniR\",\"name\":\"Glacial Prison\",\"description\":\"Sejuani throws her bola that freezes and stuns the first champion hit and creates an ice storm that slows other enemies.\",\"tooltip\":\"Sejuani throws her True Ice bola that deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to the first enemy champion hit and stuns them for {{ e2 }} second.<br><br>The bola becomes more powerful as it travels, dealing {{ e7 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage, stunning for {{ e6 }} seconds and creating a storm that slows other enemies by {{ e8 }}%. After {{ e3 }} seconds, the storm deals {{ e7 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage and slows by {{ e4 }}% for {{ e5 }} seconds.\",\"leveltip\":{\"label\":[\"Minimum Damage\",\"Maximum Damage\",\"Cooldown\"],\"effect\":[\" {{ e1 }} -> {{ e1NL }}\",\"{{ e7 }} -> {{ e7NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,100,80],\"cooldownBurn\":\"120/100/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[100,125,150],[1,1,1],[2,2,2],[80,80,80],[3,3,3],[2,2,2],[150,250,350],[30,30,30],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"100/125/150\",\"1\",\"2\",\"80\",\"3\",\"2\",\"150/250/350\",\"30\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1300,1300,1300],\"rangeBurn\":\"1300\",\"image\":{\"full\":\"SejuaniR.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":384,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Shaco\":{\"id\":35,\"key\":\"Shaco\",\"name\":\"Shaco\",\"title\":\"the Demon Jester\",\"spells\":[{\"id\":\"Deceive\",\"name\":\"Deceive\",\"description\":\"Shaco becomes Invisible and teleports to target location.\",\"tooltip\":\"Shaco teleports nearby and becomes <span class=\\\"color91d7ee\\\">Invisible</span> for {{ e3 }} seconds. Shaco remains <span class=\\\"color91d7ee\\\">Invisible</span> even if he uses Jack in the Box or Hallucinate. <br><br>His next basic attack during or shortly after <span class=\\\"color91d7ee\\\">Invisibility</span> will reduce Deceive's cooldown by {{ f3 }} seconds. <br><br><u><span class=\\\"size16 color91d7ee\\\">16\",\"leveltip\":{\"label\":[\"Stealth Duration\",\"Cooldown\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[16,15.5,15,14.5,14],\"cooldownBurn\":\"16/15.5/15/14.5/14\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[0,0,0,0,0],[200,200,200,200,200],[1.5,2.25,3,3.75,4.5],[6,6,6,6,6],[400,400,400,400,400],[2.5,2.5,2.5,2.5,2.5],[0,0,0,0,0],[2.5,2.5,2.5,2.5,2.5],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"0\",\"200\",\"1.5/2.25/3/3.75/4.5\",\"6\",\"400\",\"2.5\",\"0\",\"2.5\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[400,400,400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"Deceive.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":432,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"JackInTheBox\",\"name\":\"Jack In The Box\",\"description\":\"Shaco creates a hidden animated Jack-in-the-Box. When triggered, it will fear and attack nearby enemies.\",\"tooltip\":\"Shaco creates a Jack in the Box that hides from view after {{ e5 }} seconds. It pops out when an enemy comes near or when uncovered by a ward or trinket, making nearby enemies flee very slowly for {{ e3 }} seconds.<br><br>Its attacks deal {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage. It lasts for {{ e4 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> seconds while hidden or 5 seconds while firing. \",\"leveltip\":{\"label\":[\"Damage\",\"Fear Duration\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[16,16,16,16,16],\"cooldownBurn\":\"16\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[35,50,65,80,95],[200,300,400,500,600],[0.5,0.75,1,1.25,1.5],[60,60,60,60,60],[2,2,2,2,2],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"35/50/65/80/95\",\"200/300/400/500/600\",\"0.5/0.75/1/1.25/1.5\",\"60\",\"2\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.05,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[500,500,500,500,500],\"rangeBurn\":\"500\",\"image\":{\"full\":\"JackInTheBox.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":0,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"TwoShivPoison\",\"name\":\"Two-Shiv Poison\",\"description\":\"Shaco's Shivs passively poison targets on hit, slowing their movement speed. He can throw his Shivs to deal damage and poison the target. The thrown Shiv deals bonus damage based on the target's missing Health.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>While Two-Shiv Poison is ready to cast, Shaco's basic attacks apply Two-Shiv Poison for {{ e4 }} seconds. It reduces Movement Speed by {{ e2 }}%.<br><br><span class=\\\"colorFF9900\\\">Active: </span>Shaco throws a shiv that applies Two-Shiv Poison for {{ e5 }} seconds and deals magic damage equal to {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> + <span class=\\\"colorFFFFFF\\\">{{ f2 }}%</span> of the target's missing Health.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Movement Speed Slow\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ e2 }}% -> {{ e2NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[8,8,8,8,8],\"cooldownBurn\":\"8\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[5,35,65,95,125],[20,22.5,25,27.5,30],[20,22.5,25,27.5,30],[2,2,2,2,2],[3,3,3,3,3],[30,35,40,45,50],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"5/35/65/95/125\",\"20/22.5/25/27.5/30\",\"20/22.5/25/27.5/30\",\"2\",\"3\",\"30/35/40/45/50\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1,\"key\":\"f1\"},{\"link\":\"spelldamage\",\"coeff\":0.75,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[625,625,625,625,625],\"rangeBurn\":\"625\",\"image\":{\"full\":\"TwoShivPoison.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":48,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"HallucinateFull\",\"name\":\"Hallucinate\",\"description\":\"Shaco creates an illusion of himself near him, which can attack nearby enemies (Deals reduced damage to turrets).  Upon death, it explodes, spawning three mini Jack in the Boxes and dealing damage to nearby enemies. \",\"tooltip\":\"Shaco vanishes briefly and reappears with a clone. The clone lasts up to 18 seconds and detonates when it dies, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to nearby enemies and spawning three mini Jack in the Boxes. <br><br><li>Mini boxes deal {{ e6 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> damage, make enemies flee for {{ e7 }} second(s), and trigger together. <br><br>Clone deals {{ e8 }}% of Shaco's damage and receives {{ e2 }}% increased damage.</li><br><span class=\\\"color99FF99\\\">The clone can be controlled by holding the alt key and using the right mouse button or by reactivating this ability.</span>\",\"leveltip\":{\"label\":[\"On Death Damage\",\"Mini Box Damage\",\"Mini Box Flee Duration\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e6 }} -> {{ e6NL }}\",\"{{ e7 }} -> {{ e7NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[100,90,80],\"cooldownBurn\":\"100/90/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[200,300,400],[50,50,50],[15,15,15],[2,2,2],[6,6,6],[25,50,75],[0.75,1,1.25],[75,75,75],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"200/300/400\",\"50\",\"15\",\"2\",\"6\",\"25/50/75\",\"0.75/1/1.25\",\"75\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.15,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[25000,25000,25000],\"rangeBurn\":\"25000\",\"image\":{\"full\":\"HallucinateFull.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":96,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Shen\":{\"id\":98,\"key\":\"Shen\",\"name\":\"Shen\",\"title\":\"the Eye of Twilight\",\"spells\":[{\"id\":\"ShenQ\",\"name\":\"Twilight Assault\",\"description\":\"Shen recalls his spirit blade to attack with it, dealing damage based on the target's max health. The attacks are greatly empowered if it collides with an enemy champion, and all collided enemies are slowed while running away from Shen.\",\"tooltip\":\"Shen recalls his <span class=\\\"colorBB77FF\\\">spirit blade</span> and draws it. Enemies it collides with are slowed by {{ e4 }}% when moving away from Shen for the next {{ e5 }} seconds.<br><br>Shen's next {{ e3 }} attacks deal <scaleLevel>{{ f1 }}</scaleLevel> plus {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ charabilitypower*.01 }})</span>% of their target's max health as bonus magic damage. If the <span class=\\\"colorBB77FF\\\">spirit blade</span> collided with an enemy champion, those attacks deal <scaleLevel>{{ f2 }}</scaleLevel> plus {{ e6 }} <span class=\\\"color99FF99\\\">(+{{ charabilitypower2*.01 }})</span>% instead and have +{{ e9 }}% Attack Speed.<br><br><rules>Each attack deals {{ e1 }}% increased damage to monsters (capped at {{ e7 }}).</rules>\",\"leveltip\":{\"label\":[\"Base Percent Damage\",\"Enhanced Percent Damage\",\"Energy Cost\",\"Monster Damage Cap\",\"Cooldown\"],\"effect\":[\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ e6 }}% -> {{ e6NL }}%\",\"{{ cost }} -> {{ costNL }}\",\"{{ e7 }} -> {{ e7NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[8,7.25,6.5,5.75,5],\"cooldownBurn\":\"8/7.25/6.5/5.75/5\",\"cost\":[140,130,120,110,100],\"costBurn\":\"140/130/120/110/100\",\"effect\":[null,[100,100,100,100,100],[2,2.5,3,3.5,4],[3,3,3,3,3],[35,35,35,35,35],[2,2,2,2,2],[4,4.5,5,5.5,6],[75,100,125,150,175],[8,8,8,8,8],[50,50,50,50,50],[75,75,75,75,75]],\"effectBurn\":[null,\"100\",\"2/2.5/3/3.5/4\",\"3\",\"35\",\"2\",\"4/4.5/5/5.5/6\",\"75/100/125/150/175\",\"8\",\"50\",\"75\"],\"vars\":[],\"costType\":\" Energy\",\"maxammo\":\"-1\",\"range\":[400,400,400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"ShenQ.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":144,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Energy\"},{\"id\":\"ShenW\",\"name\":\"Spirit's Refuge\",\"description\":\"Attacks that would hit Shen or his allies near his spirit blade are blocked.\",\"tooltip\":\"Shen's <span class=\\\"colorBB77FF\\\">spirit blade</span> creates a defensive zone for {{ e1 }} seconds. Basic attacks that would hit Shen or an allied champion in the zone are blocked.<br><br>If there are no champions to protect in the zone when it starts, the <span class=\\\"colorBB77FF\\\">spirit blade</span> will not activate until one enters or {{ e2 }} seconds pass.\",\"leveltip\":{\"label\":[\"Cooldown\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[18,16.5,15,13.5,11],\"cooldownBurn\":\"18/16.5/15/13.5/11\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[1.75,1.75,1.75,1.75,1.75],[2,2,2,2,2],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"1.75\",\"2\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Energy\",\"maxammo\":\"-1\",\"range\":[400,400,400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"ShenW.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":192,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Energy\"},{\"id\":\"ShenE\",\"name\":\"Shadow Dash\",\"description\":\"Shen dashes in a direction, taunting enemies in his path.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> Dealing damage with Shadow Dash or Twilight Assault recovers <span class=\\\"colorFFFFFF\\\">{{ f1 }}</span> Energy.<br><br><span class=\\\"colorFF9900\\\">Active:</span> Shen dashes in a direction, dealing {{ e2 }} <span class=\\\"colorCC3300\\\">[+{{ f2 }}]</span> physical damage to enemy champions and neutral monsters in his path and taunting them for {{ e1 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[18,16,14,12,10],\"cooldownBurn\":\"18/16/14/12/10\",\"cost\":[150,150,150,150,150],\"costBurn\":\"150\",\"effect\":[null,[1.5,1.5,1.5,1.5,1.5],[50,75,100,125,150],[25,25,25,25,25],[300,300,300,300,300],[125,125,125,125,125],[150,150,150,150,150],[800,800,800,800,800],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"1.5\",\"50/75/100/125/150\",\"25\",\"300\",\"125\",\"150\",\"800\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Energy\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"ShenE.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":240,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Energy\"},{\"id\":\"ShenR\",\"name\":\"Stand United\",\"description\":\"Shen shields target allied champion from incoming damage, and soon after teleports to their location.\",\"tooltip\":\"Shen grants a shield to a target allied champion that lasts {{ e2 }} seconds and absorbs up to {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> to {{ e8 }} <span class=\\\"color99FF99\\\">(+{{ f2 }})</span> damage based on their missing health. After channeling for {{ f1 }} seconds, Shen and his <span class=\\\"colorBB77FF\\\">spirit blade</span> teleport to the ally's location.\",\"leveltip\":{\"label\":[\"Minimum Shield Health\",\"Maximum Shield Health\",\"Cooldown \"],\"effect\":[\" {{ e1 }} -> {{ e1NL }}\",\"{{ e8 }} -> {{ e8NL }} \",\" {{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[200,180,160],\"cooldownBurn\":\"200/180/160\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[175,350,525],[5,5,5],[175,175,175],[2000,2000,2000],[600,600,600],[0.4,0.4,0.4],[1.6,1.6,1.6],[280,560,840],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"175/350/525\",\"5\",\"175\",\"2000\",\"600\",\"0.4\",\"1.6\",\"280/560/840\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1.35,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[35000,35000,35000],\"rangeBurn\":\"35000\",\"image\":{\"full\":\"ShenR.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":288,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Shyvana\":{\"id\":102,\"key\":\"Shyvana\",\"name\":\"Shyvana\",\"title\":\"the Half-Dragon\",\"spells\":[{\"id\":\"ShyvanaDoubleAttack\",\"name\":\"Twin Bite\",\"description\":\"Shyvana strikes twice on her next attack. Basic attacks reduce the cooldown of Twin Bite by 0.5 seconds.<br><br><font color='#FF3300'>Dragon Form: </font>Twin Bite cleaves all units in front Shyvana. \",\"tooltip\":\"Shyvana strikes twice on her next attack, dealing <span class=\\\"colorFF8C00\\\">{{ f2 }}</span> and <span class=\\\"colorFF8C00\\\">{{ f1 }}</span> physical damage on the first and second hits respectively.<br><br>While Twin Bite is on cooldown, basic attacks reduce the remaining cooldown duration by {{ e3 }} seconds.<br><br><span class=\\\"colorFF3300\\\">Dragon Form: </span>Twin Bite cleaves all units in front Shyvana.<br><br><span class=\\\"color919191\\\"><i>Twin Bite's second hit damage is equal to {{ e1 }}% of Shyvana's attack damage.</i></span>\",\"leveltip\":{\"label\":[\"Bonus Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ cooldown }} ->{{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[9,8,7,6,5],\"cooldownBurn\":\"9/8/7/6/5\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[40,55,70,85,100],[250,250,250,250,250],[0.5,0.5,0.5,0.5,0.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"40/55/70/85/100\",\"250\",\"0.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":[0.8,0.85,0.9,0.95,1],\"key\":\"f1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"ShyvanaDoubleAttack.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":336,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"ShyvanaImmolationAura\",\"name\":\"Burnout\",\"description\":\"Shyvana surrounds herself in fire, dealing magic damage per second to nearby enemies and moving faster for 3 seconds, part of this damage is applied again when Shyvana basic attacks an enemy with Burnout active. The Movement Speed reduces over the duration of the spell. Basic attacks extend the duration of Burnout. <br><br><font color='#FF3300'>Dragon Form: </font>Burnout grows in size.\",\"tooltip\":\"Shyvana deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage per second to nearby enemies and gains a bonus {{ e2 }}% movement speed that decays over {{ e7 }} seconds.<br><br>While Burnout is active, basic attacks deal {{ effect1amount*0.25 }} <span class=\\\"colorFF8C00\\\">(+{{ charbonusphysical*0.25 }})</span> <span class=\\\"color99FF99\\\">(+{{ charabilitypower2*.25 }})</span> magic damage to nearby enemies and extend its duration by {{ e8 }} second.<br><br><span class=\\\"colorFF3300\\\">Dragon Form: </span>Burnout grows in size.<br><br><span class=\\\"color919191\\\"><i>Burnout has a maximum duration of {{ e9 }} seconds.</i></span>\",\"leveltip\":{\"label\":[\"Damage (aura)\",\"Damage (on hit)\",\"Movement Speed Bonus\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ effect1amount*0.25 }} -> {{ effect1amountnl*0.25 }}\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[12,12,12,12,12],\"cooldownBurn\":\"12\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[20,32,45,57,70],[30,35,40,45,50],[0.85,0.85,0.85,0.85,0.85],[0,0,0,0,0],[25,25,25,25,25],[325,325,325,325,325],[3,3,3,3,3],[1,1,1,1,1],[7,7,7,7,7],[350,365,380,380,380]],\"effectBurn\":[null,\"20/32/45/57/70\",\"30/35/40/45/50\",\"0.85\",\"0\",\"25\",\"325\",\"3\",\"1\",\"7\",\"350/365/380/380/380\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.2,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.1,\"key\":\"a2\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[325,325,325,325,325],\"rangeBurn\":\"325\",\"image\":{\"full\":\"ShyvanaImmolationAura.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":384,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"ShyvanaFireball\",\"name\":\"Flame Breath\",\"description\":\"Shyvana unleashes a fireball that deals damage to all enemies it encounters and leaves cinders on the target, marking them for 5 seconds. Shyvana's basic attacks on marked targets deal a percentage of their maximum Health as damage on-hit.<br><br><font color='#FF3300'>Dragon Form: </font>Flame Breath explodes on impact or at target location, dealing bonus damage and scorching the earth for a short duration.\",\"tooltip\":\"Shyvana unleashes a fireball that stops after hitting a champion. All enemies hit take {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and are marked for {{ e2 }} seconds.<br><br>Shyvana's basic attacks against marked targets deal {{ e4 }}% of their maximum health as magic damage on hit.<br><br><span class=\\\"colorFF3300\\\">Dragon Form: </span>Flame Breath explodes on impact or at target location, dealing <span class=\\\"colorFFFFFF\\\">{{ f1 }}</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> bonus magic damage and scorching the earth for 4 seconds. Enemies on scorched earth take <span class=\\\"colorFFFFFF\\\">{{ f3 }}</span> <span class=\\\"color99FF99\\\">(+{{ f2 }})</span> magic damage per second.<br><br><span class=\\\"color919191\\\"><i>Flame Breath's on-hit damage against monsters is capped at {{ e3 }} damage per hit.</i></span>\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[12,11,10,9,8],\"cooldownBurn\":\"12/11/10/9/8\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[60,100,140,180,220],[5,5,5,5,5],[100,100,100,100,100],[2.5,2.5,2.5,2.5,2.5],[0,0,0,0,0],[60,100,140,180,220],[4.5,4.5,4.5,4.5,4.5],[220,240,260,260,260],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/100/140/180/220\",\"5\",\"100\",\"2.5\",\"0\",\"60/100/140/180/220\",\"4.5\",\"220/240/260/260/260\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[925,925,925,925,925],\"rangeBurn\":\"925\",\"image\":{\"full\":\"ShyvanaFireball.png\",\"sprite\":\"spell9.png\",\"group\":\"spell\",\"x\":432,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"ShyvanaTransformCast\",\"name\":\"Dragon's Descent\",\"description\":\"Shyvana transforms into a dragon and takes flight to a target location. Enemies along her path take damage and are knocked toward her target location.<br><br>Shyvana passively gains Fury per second and gains 2 Fury on basic attack.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Active: </span>Shyvana transforms into a dragon, gaining {{ e0 }} Health and flying to target location. Enemies along her path take {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and are knocked toward her target location.<br><br><span class=\\\"colorFF9900\\\">Passive: </span>Shyvana generates {{ e3 }} Fury every second. Basic Attacks generate {{ e2 }} Fury.\",\"leveltip\":{\"label\":[\"Passive Fury Gain\",\"Dragon Size\",\"Bonus Health\",\"Flight Damage\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"Big -> Bigger\",\"{{ e0 }} -> {{ e0NL }}\",\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":3,\"cooldown\":[0,0,0],\"cooldownBurn\":\"0\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[150,250,350],[2,2,2],[1,1.5,2],[5,5,5],[1100,1100,1100],[275,275,275],[100,100,100],[200,230,260],[0,0.085,0.16],[150,250,350]],\"effectBurn\":[null,\"150/250/350\",\"2\",\"1/1.5/2\",\"5\",\"1100\",\"275\",\"100\",\"200/230/260\",\"0/0.085/0.16\",\"150/250/350\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Fury a Second\",\"maxammo\":\"-1\",\"range\":[850,850,850],\"rangeBurn\":\"850\",\"image\":{\"full\":\"ShyvanaTransformCast.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":0,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ e4 }} Fury a Second\"}]},\"Singed\":{\"id\":27,\"key\":\"Singed\",\"name\":\"Singed\",\"title\":\"the Mad Chemist\",\"spells\":[{\"id\":\"PoisonTrail\",\"name\":\"Poison Trail\",\"description\":\"Leaves a trail of poison behind Singed, dealing damage to enemies caught in the path.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Toggle: </span>Singed lays a poisonous trail that deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ charabilitypower*4 }})</span> magic damage per second.\",\"leveltip\":{\"label\":[\"Damage\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[13,13,13,13,13],\"costBurn\":\"13\",\"effect\":[null,[22,34,46,58,70],[1,1,1,1,1],[3.25,3.25,3.25,3.25,3.25],[2.1,2.1,2.1,2.1,2.1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"22/34/46/58/70\",\"1\",\"3.25\",\"2.1\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana per Second\",\"maxammo\":\"-1\",\"range\":[20,20,20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"PoisonTrail.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":48,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana per Second\"},{\"id\":\"MegaAdhesive\",\"name\":\"Mega Adhesive\",\"description\":\"Throws a vial of mega adhesive on the ground, slowing enemies who walk on it.\",\"tooltip\":\"Singed leaves a sticky area on the ground for {{ e2 }} seconds, slowing enemies in the area by {{ e1 }}%.<br><br>If Singed flings a target into the zone, it will root them.\",\"leveltip\":{\"label\":[\"Slow Amount\",\"Mana Cost \"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\" {{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[14,14,14,14,14],\"cooldownBurn\":\"14\",\"cost\":[70,80,90,100,110],\"costBurn\":\"70/80/90/100/110\",\"effect\":[null,[35,45,55,65,75],[5,5,5,5,5],[0.25,0.25,0.25,0.25,0.25],[265,265,265,265,265],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"35/45/55/65/75\",\"5\",\"0.25\",\"265\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"MegaAdhesive.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":96,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"Fling\",\"name\":\"Fling\",\"description\":\"Damages target enemy unit and flings them into the air behind Singed. If the target Singed flings lands in his Mega Adhesive, they are also rooted.\",\"tooltip\":\"Singed flings an enemy over his shoulder, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> plus {{ e3 }}% of the target's maximum Health as magic damage. Max 300 bonus damage against minions and monsters.<br><br>If the target Singed flings lands in his Mega Adhesive, they are also rooted for {{ e2 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Max Health Damage\",\"Root Duration\",\"Mana Cost \"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,10,10,10,10],\"cooldownBurn\":\"10\",\"cost\":[100,110,120,130,140],\"costBurn\":\"100/110/120/130/140\",\"effect\":[null,[50,65,80,95,110],[1,1.25,1.5,1.75,2],[6,6.5,7,7.5,8],[420,420,420,420,420],[300,300,300,300,300],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/65/80/95/110\",\"1/1.25/1.5/1.75/2\",\"6/6.5/7/7.5/8\",\"420\",\"300\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.75,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[125,125,125,125,125],\"rangeBurn\":\"125\",\"image\":{\"full\":\"Fling.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":144,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"InsanityPotion\",\"name\":\"Insanity Potion\",\"description\":\"Singed drinks a potent brew of chemicals, granting him increased combat stats.\",\"tooltip\":\"Singed drinks a potent brew of chemicals, granting him {{ e1 }} Ability Power, Armor, Magic Resist, Movement Speed, Health Regen, and Mana Regen for {{ e2 }} seconds.\",\"leveltip\":{\"label\":[\"Bonus Stats\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":3,\"cooldown\":[100,100,100],\"cooldownBurn\":\"100\",\"cost\":[150,150,150],\"costBurn\":\"150\",\"effect\":[null,[35,50,80],[25,25,25],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"35/50/80\",\"25\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"InsanityPotion.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":192,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Sion\":{\"id\":14,\"key\":\"Sion\",\"name\":\"Sion\",\"title\":\"The Undead Juggernaut\",\"spells\":[{\"id\":\"SionQ\",\"name\":\"Decimating Smash\",\"description\":\"Sion charges a powerful swing in an area in front of himself that will deal damage to enemies when released. If he charges for enough time, enemies hit by the swing will also be knocked up and stunned.</font>\",\"tooltip\":\"Sion charges up a heavy blow for up to 2 seconds. When released, he deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> to {{ e4 }} <span class=\\\"colorFF8C00\\\">(+{{ f2 }})</span> physical damage to enemies hit ({{ e8 }}% damage to minions) and briefly slows them.<br><br>If Sion charges for at least 1 second, enemies are knocked up and stunned for 1.25 to 2.25 seconds.\",\"leveltip\":{\"label\":[\"Minimum Damage\",\"Maximum Damage\",\"Cooldown \"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[10,9,8,7,6],\"cooldownBurn\":\"10/9/8/7/6\",\"cost\":[45,45,45,45,45],\"costBurn\":\"45\",\"effect\":[null,[20,40,60,80,100],[1.25,1.25,1.25,1.25,1.25],[0.65,0.65,0.65,0.65,0.65],[60,120,180,240,300],[2.5,2.5,2.5,2.5,2.5],[1.95,1.95,1.95,1.95,1.95],[7.5,7.5,7.5,7.5,7.5],[60,60,60,60,60],[80,80,80,80,80],[-0.8,-0.8,-0.8,-0.8,-0.8]],\"effectBurn\":[null,\"20/40/60/80/100\",\"1.25\",\"0.65\",\"60/120/180/240/300\",\"2.5\",\"1.95\",\"7.5\",\"60\",\"80\",\"-0.8\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":0.6,\"key\":\"f1\"},{\"link\":\"attackdamage\",\"coeff\":1.8,\"key\":\"f2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[10000,10000,10000,10000,10000],\"rangeBurn\":\"10000\",\"image\":{\"full\":\"SionQ.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":240,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SionW\",\"name\":\"Soul Furnace\",\"description\":\"Sion shields himself and can reactivate after 3 seconds to deal Magic Damage to enemies nearby. When Sion kills enemies, he passively gains maximum Health.\",\"tooltip\":\"<span class=\\\"colorFFE076\\\">Passive:</span> Sion gains {{ e5 }} maximum Health when he kills a unit ({{ e6 }} for large monsters and champion kills or assists). <span class=\\\"colorFF3300\\\">Current Bonus: {{ f1 }} </span><br><br><span class=\\\"colorFFE076\\\">Active:</span> Sion shields himself for {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> <span class=\\\"colorFF3300\\\">(+{{ f2 }})</span> ({{ e3 }}% of maximum Health) for 6 seconds. After {{ e7 }} seconds, while the shield holds, Sion can reactivate to deal {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> plus {{ e4 }}% of the target's maximum Health as magic damage to nearby enemies. Max 400 bonus damage to minions and monsters.\",\"leveltip\":{\"label\":[\"Shield\",\"Damage\",\"Max Health Damage\",\"Max Health Shield Ratio\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ e4 }}% -> {{ e4NL }}%\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[11,11,11,11,11],\"cooldownBurn\":\"11\",\"cost\":[65,70,75,80,85],\"costBurn\":\"65/70/75/80/85\",\"effect\":[null,[30,55,80,105,130],[40,65,90,115,140],[8,9,10,11,12],[10,11,12,13,14],[2,2,2,2,2],[10,10,10,10,10],[3,3,3,3,3],[10,10,10,10,10],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30/55/80/105/130\",\"40/65/90/115/140\",\"8/9/10/11/12\",\"10/11/12/13/14\",\"2\",\"10\",\"3\",\"10\",\"0\",\"0\"],\"vars\":[{\"link\":\"health\",\"coeff\":0.1,\"key\":\"f1\"},{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[500,500,500,500,500],\"rangeBurn\":\"500\",\"image\":{\"full\":\"SionW.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":288,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SionE\",\"name\":\"Roar of the Slayer\",\"description\":\"Sion fires a short range shockwave that damages and slows and reduces the Armor of the first enemy hit. If the shockwave hits a minion or monster, it will be knocked back, damaging and slowing all enemies that it passes through.\",\"tooltip\":\"Sion fires a shockwave, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to the first enemy hit, slowing it by {{ e2 }}%, and reducing its Armor by {{ e5 }}% for 2.5 seconds.<br><br>If the target is not a champion, it will be knocked back. Enemies that the knocked back unit collides with take {{ e6 }}% bonus damage and are slowed by {{ e2 }}%.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow\",\"Cooldown\",\"Mana Cost \"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[12,11,10,9,8],\"cooldownBurn\":\"12/11/10/9/8\",\"cost\":[35,40,45,50,55],\"costBurn\":\"35/40/45/50/55\",\"effect\":[null,[70,105,140,175,210],[40,45,50,55,60],[5,5,5,5,5],[40,45,50,55,60],[20,20,20,20,20],[30,30,30,30,30],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/105/140/175/210\",\"40/45/50/55/60\",\"5\",\"40/45/50/55/60\",\"20\",\"30\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[800,800,800,800,800],\"rangeBurn\":\"800\",\"image\":{\"full\":\"SionE.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":336,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SionR\",\"name\":\"Unstoppable Onslaught\",\"description\":\"Sion charges in a direction, ramping up speed over time. He can steer his charge slightly with the mouse cursor location. When he collides with an enemy he deals damage and knocks them up based on the distance he has charged.\",\"tooltip\":\"Sion charges in a direction for 8 seconds and can steer slowly towards the mouse cursor. While charging, Sion is immune to all Crowd Control. Reactivating will cancel Sion's charge early.<br><br>When Sion collides with an enemy champion or wall, he deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage and knocks up enemies in a small area for {{ e5 }} seconds. Enemies in a larger area take the same damage and are slowed by {{ e3 }}% for 3 seconds.<br><br>The damage increases to {{ e2 }} <span class=\\\"colorFF8C00\\\">(+{{ f2 }})</span> and the stun increases to {{ e6 }} seconds as Sion charges farther.\",\"leveltip\":{\"label\":[\"Minimum Damage\",\"Maximum Damage\",\"Slow\",\"Cooldown \"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[140,100,60],\"cooldownBurn\":\"140/100/60\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[150,300,450],[300,600,900],[40,45,50],[500,500,500],[0.75,0.75,0.75],[1.75,1.75,1.75],[950,950,950],[0,0,0],[2,2,2],[0,0,0]],\"effectBurn\":[null,\"150/300/450\",\"300/600/900\",\"40/45/50\",\"500\",\"0.75\",\"1.75\",\"950\",\"0\",\"2\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.4,\"key\":\"f1\"},{\"link\":\"bonusattackdamage\",\"coeff\":0.8,\"key\":\"f2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[7500,7500,7500],\"rangeBurn\":\"7500\",\"image\":{\"full\":\"SionR.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":384,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Sivir\":{\"id\":15,\"key\":\"Sivir\",\"name\":\"Sivir\",\"title\":\"the Battle Mistress\",\"spells\":[{\"id\":\"SivirQ\",\"name\":\"Boomerang Blade\",\"description\":\"Sivir hurls her crossblade like a boomerang, dealing damage each way.\",\"tooltip\":\"Sivir hurls her crossblade like a boomerang, which deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> physical damage to the first target hit and {{ e2 }}% reduced damage to each subsequent target down to a minimum of {{ e3 }}%.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Attack Damage Scaling\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e5 }}% -> {{ e5NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[9,9,9,9,9],\"cooldownBurn\":\"9\",\"cost\":[70,80,90,100,110],\"costBurn\":\"70/80/90/100/110\",\"effect\":[null,[25,45,65,85,105],[15,15,15,15,15],[40,40,40,40,40],[0.7,0.8,0.9,1,1.1],[70,80,90,100,110],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"25/45/65/85/105\",\"15\",\"40\",\"0.7/0.8/0.9/1/1.1\",\"70/80/90/100/110\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":0,\"key\":\"f1\"},{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1200,1200,1200,1200,1200],\"rangeBurn\":\"1200\",\"image\":{\"full\":\"SivirQ.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":432,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SivirW\",\"name\":\"Ricochet\",\"description\":\"Sivir's next few basic attacks will bounce to nearby targets, dealing reduced damage to secondary targets.\",\"tooltip\":\"Sivir's next {{ e4 }} basic attacks bounce to nearby targets, dealing <span class=\\\"colorFF8C00\\\">{{ f1 }}</span> physical damage to the first target and <span class=\\\"colorFF8C00\\\">{{ f2 }}</span> physical damage to each subsequent target.<br><br>If a given Ricochet attack critically strikes its first target, all subsequent bounces from that attack will also critically strike.<br><br><span class=\\\"color919191\\\"><i>Targets cannot be hit more than once by a given Ricochet attack and on-hit effects are applied only to the first target of each attack.</i></span>\",\"leveltip\":{\"label\":[\"Cooldown\",\"Bounce Damage (total attack damage)\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e3 }}% -> {{ e3NL }}%\"]},\"maxrank\":5,\"cooldown\":[12,10.5,9,7.5,6],\"cooldownBurn\":\"12/10.5/9/7.5/6\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[5,6,7,8,9],[10,20,30,40,50],[50,55,60,65,70],[3,3,3,3,3],[60,70,80,90,100],[1,1,1,1,1],[0,0,0,0,0],[4.5,4.25,4,3.75,3.5],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"5/6/7/8/9\",\"10/20/30/40/50\",\"50/55/60/65/70\",\"3\",\"60/70/80/90/100\",\"1\",\"0\",\"4.5/4.25/4/3.75/3.5\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":0,\"key\":\"f1\"},{\"link\":\"attackdamage\",\"coeff\":0,\"key\":\"f2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[20,20,20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"SivirW.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":0,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SivirE\",\"name\":\"Spell Shield\",\"description\":\"Creates a magical barrier that blocks a single enemy ability cast on Sivir. She receives Mana back if a spell is blocked.\",\"tooltip\":\"Sivir creates a magical barrier for {{ e1 }} seconds that blocks the next incoming enemy ability.<br><br>If an ability is blocked by the shield, Sivir regains {{ e2 }} Mana.\",\"leveltip\":{\"label\":[\"Cooldown\",\"Mana Gain\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[22,19,16,13,10],\"cooldownBurn\":\"22/19/16/13/10\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[1.5,1.5,1.5,1.5,1.5],[80,95,110,125,140],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"1.5\",\"80/95/110/125/140\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[20,20,20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"SivirE.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":48,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"SivirR\",\"name\":\"On The Hunt\",\"description\":\"Sivir leads her allies in battle, granting them a surge Movement Speed for a period of time. Additionally passively grants Sivir bonus Attack Speed while Ricochet is active.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Sivir gains {{ e1 }}% Attack Speed while Ricochet is active.<br><br><span class=\\\"colorFF9900\\\">Active: </span>Sivir rallies her allies for {{ e3 }} seconds, granting all nearby allies an initial {{ e4 }}% Movement Speed bonus that reduces to {{ e2 }}% after the first {{ e5 }} seconds of On The Hunt elapse.\",\"leveltip\":{\"label\":[\"Attack Speed\",\"Enhanced Movement Speed Bonus\",\"Enhanced Movement Speed Duration\",\"Cooldown\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ e4 }}% -> {{ e4NL }}%\",\"{{ e5 }} -> {{ e5NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,100,80],\"cooldownBurn\":\"120/100/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[30,45,60],[20,20,20],[8,8,8],[40,50,60],[2,3,4],[1000,1000,1000],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"30/45/60\",\"20\",\"8\",\"40/50/60\",\"2/3/4\",\"1000\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"SivirR.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":96,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Skarner\":{\"id\":72,\"key\":\"Skarner\",\"name\":\"Skarner\",\"title\":\"the Crystal Vanguard\",\"spells\":[{\"id\":\"SkarnerVirulentSlash\",\"name\":\"Crystal Slash\",\"description\":\"Skarner lashes out with his claws, dealing physical damage to all nearby enemies and charging himself with Crystal Energy for several seconds if a unit is struck. If he casts Crystal Slash again while powered by Crystal Energy, he deals bonus magic damage.\",\"tooltip\":\"Skarner deals <span class=\\\"colorFF8C00\\\">{{ f1 }}</span> physical damage to all nearby enemies. If a unit is struck, he charges himself with Crystal Energy for {{ e2 }} seconds.<br><br>While Skarner is charged, Crystal Slash deals <span class=\\\"colorFF8C00\\\">{{ f1 }}</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> bonus magic damage.<br><br>Basic attacks against non-structures lower Crystal Slash's cooldown by 0.25 seconds (quadrupled against champions).\",\"leveltip\":{\"label\":[\"Total Attack Damage Ratio\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[3.5,3.25,3,2.75,2.5],\"cooldownBurn\":\"3.5/3.25/3/2.75/2.5\",\"cost\":[10,11,12,13,14],\"costBurn\":\"10/11/12/13/14\",\"effect\":[null,[33,36,39,42,45],[4,4,4,4,4],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"33/36/39/42/45\",\"4\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.8,\"key\":\"f1\"},{\"link\":\"bonusattackdamage\",\"coeff\":0.8,\"key\":\"f1\"},{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[350,350,350,350,350],\"rangeBurn\":\"350\",\"image\":{\"full\":\"SkarnerVirulentSlash.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":144,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SkarnerExoskeleton\",\"name\":\"Crystalline Exoskeleton\",\"description\":\"Skarner gains a shield and has increased Movement Speed while the shield persists.\",\"tooltip\":\"Skarner is shielded for <span class=\\\"colorCC3300\\\">{{ f1 }}</span> ({{ e1 }}% of his maximum health) <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> damage for {{ e4 }} seconds. While the shield persists, Skarner gains movement speed that ramps up to {{ e5 }}% over 3 seconds.\",\"leveltip\":{\"label\":[\"Max Health Damage Absorption\",\"Movement Speed Bonus\",\"Cooldown\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ e5 }}% -> {{ e5NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[13,12.5,12,11.5,11],\"cooldownBurn\":\"13/12.5/12/11.5/11\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[10,11,12,13,14],[30,35,40,45,50],[8,10,12,14,16],[6,6,6,6,6],[16,20,24,28,32],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"10/11/12/13/14\",\"30/35/40/45/50\",\"8/10/12/14/16\",\"6\",\"16/20/24/28/32\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1,1,1,1,1],\"rangeBurn\":\"1\",\"image\":{\"full\":\"SkarnerExoskeleton.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":192,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SkarnerFracture\",\"name\":\"Fracture\",\"description\":\"Skarner summons a blast of crystalline energy which deals damage to enemies struck and slows them. Basic attacking these enemies within a short window will stun them.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> Crystallizing enemies with Fracture and Impale grants <span class=\\\"colorFFF673\\\">Crystal Charge</span> for the disable duration and reduces the cooldown of Fracture by the same amount.<br><br><span class=\\\"colorFF9900\\\">Active:</span> Skarner summons a blast of crystalline energy, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage, slowing targets hit by {{ e8 }}% for {{ e7 }} seconds and reducing the blast's speed.<br><br>Enemies hit by Fracture are afflicted with Crystal Venom for {{ e6 }} seconds, causing Skarner's next basic attack against them to deal {{ e2 }} additional physical damage and stun the target for {{ e3 }} second.\",\"leveltip\":{\"label\":[\"Blast Damage / Attack Damage\",\"Slow\",\"Cooldown\"],\"effect\":[\"{{ e1 }} / {{ e2 }} -> {{ e1NL }} / {{ e2NL }}\",\"{{ e8 }}% -> {{ e8NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[14,13.5,13,12.5,12],\"cooldownBurn\":\"14/13.5/13/12.5/12\",\"cost\":[55,55,55,55,55],\"costBurn\":\"55\",\"effect\":[null,[40,65,90,115,140],[25,35,45,55,65],[1,1,1,1,1],[50,50,50,50,50],[6,6,6,6,6],[5,5,5,5,5],[2.5,2.5,2.5,2.5,2.5],[30,35,40,45,50],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"40/65/90/115/140\",\"25/35/45/55/65\",\"1\",\"50\",\"6\",\"5\",\"2.5\",\"30/35/40/45/50\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[980,980,980,980,980],\"rangeBurn\":\"980\",\"image\":{\"full\":\"SkarnerFracture.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":240,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SkarnerImpale\",\"name\":\"Impale\",\"description\":\"Skarner suppresses an enemy champion and deals damage to it. During this time, Skarner can move freely and will drag his helpless victim around with him. When the effect ends, the target will be dealt additional damage.\",\"tooltip\":\"Skarner suppresses an enemy champion for {{ e1 }} seconds, dealing <span class=\\\"colorFF8C00\\\">{{ a2 }}</span> physical damage plus {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage. Skarner can move freely during this time, and will drag his helpless victim around with him. When the effect ends, Skarner's target will be dealt the same damage again.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,100,80],\"cooldownBurn\":\"120/100/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[1.75,1.75,1.75],[20,60,100],[50,75,100],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"1.75\",\"20/60/100\",\"50/75/100\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":0.6,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[350,350,350],\"rangeBurn\":\"350\",\"image\":{\"full\":\"SkarnerImpale.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":288,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Sona\":{\"id\":37,\"key\":\"Sona\",\"name\":\"Sona\",\"title\":\"Maven of the Strings\",\"spells\":[{\"id\":\"SonaQ\",\"name\":\"Hymn of Valor\",\"description\":\"Sona plays the Hymn of Valor, sends out bolts of sound, dealing magic damage to two nearby enemies, prioritizing champions and monsters. Sona gains a temporary aura that grants allies tagged by the zone bonus damage on their next attack against enemies.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Active:</span> Deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to the nearest two enemies (prioritizes champions) and changes her <span class=\\\"colorEEEEEE\\\">Power Chord</span> bonus to <span class=\\\"color4477FF\\\">Staccato</span>.<br><br><span class=\\\"colorFF9900\\\">Melody:</span> Sona gains an aura for {{ e3 }} seconds. Allied champions that enter the aura will gain an additional {{ e4 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage on their next attack.\",\"leveltip\":{\"label\":[\"Damage (active)\",\"Damage (melody)\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[8,8,8,8,8],\"cooldownBurn\":\"8\",\"cost\":[45,50,55,60,65],\"costBurn\":\"45/50/55/60/65\",\"effect\":[null,[40,70,100,130,160],[400,400,400,400,400],[3,3,3,3,3],[20,30,40,50,60],[5,5,5,5,5],[0.4,0.4,0.4,0.4,0.4],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"40/70/100/130/160\",\"400\",\"3\",\"20/30/40/50/60\",\"5\",\"0.4\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[825,825,825,825,825],\"rangeBurn\":\"825\",\"image\":{\"full\":\"SonaQ.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":336,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SonaW\",\"name\":\"Aria of Perseverance\",\"description\":\"Sona plays the Aria of Perseverance, sending out protective melodies, healing Sona and a nearby wounded ally. Sona gains a temporary aura that grants allies tagged by the zone a temporary shield.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Active:</span> Restores {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> health to Sona and a nearby allied champion (prioritizes most wounded) and changes her <span class=\\\"colorEEEEEE\\\">Power Chord</span> bonus to <span class=\\\"color44FF77\\\">Diminuendo</span>.<br><br><span class=\\\"colorFF9900\\\">Melody:</span> Sona gains an aura for {{ e3 }} seconds. Allied champions that enter the aura will gain a shield that prevents up to {{ e4 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> damage within the next {{ e5 }} seconds.\",\"leveltip\":{\"label\":[\"Heal (active)\",\"Shield (melody)\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,10,10,10,10],\"cooldownBurn\":\"10\",\"cost\":[80,85,90,95,100],\"costBurn\":\"80/85/90/95/100\",\"effect\":[null,[35,55,75,95,115],[400,400,400,400,400],[3,3,3,3,3],[30,55,80,105,130],[1.5,1.5,1.5,1.5,1.5],[0.25,0.25,0.25,0.25,0.25],[0.04,0.04,0.04,0.04,0.04],[3,3,3,3,3],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"35/55/75/95/115\",\"400\",\"3\",\"30/55/80/105/130\",\"1.5\",\"0.25\",\"0.04\",\"3\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.25,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"SonaW.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":384,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SonaE\",\"name\":\"Song of Celerity\",\"description\":\"Sona plays the Song of Celerity, granting nearby allies bonus Movement Speed. Sona gains a temporary aura that grants allied champions tagged by the zone bonus Movement Speed on their next attack.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Active:</span> Grants Sona <span class=\\\"color99FF99\\\">{{ f1*100 }}%</span> movement speed [{{ e1 }}% + {{ f2*100 }}% per 100 ability power] for {{ e9 }} seconds (or until damaged) and changes her <span class=\\\"colorEEEEEE\\\">Power Chord</span> bonus to <span class=\\\"colorDD5599\\\">Tempo</span>.<br><br><span class=\\\"colorFF9900\\\">Melody:</span> Sona gains an aura for {{ e3 }} seconds. Allied champions that enter the aura will gain <span class=\\\"color99FF99\\\">{{ f3*100 }}%</span> movement speed for {{ e5 }} seconds.<br><br><span class=\\\"color919191\\\"><i>Sona's personal movement speed increase will always last at least {{ e5 }} seconds.</i></span>\",\"leveltip\":{\"label\":[\"Movement Speed\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\"]},\"maxrank\":5,\"cooldown\":[12,12,12,12,12],\"cooldownBurn\":\"12\",\"cost\":[65,65,65,65,65],\"costBurn\":\"65\",\"effect\":[null,[10,11,12,13,14],[400,400,400,400,400],[3,3,3,3,3],[0.1,0.11,0.12,0.13,0.14],[3,3,3,3,3],[0.4,0.4,0.4,0.4,0.4],[0.04,0.04,0.04,0.04,0.04],[2,2,2,2,2],[7,7,7,7,7],[0,0,0,0,0]],\"effectBurn\":[null,\"10/11/12/13/14\",\"400\",\"3\",\"0.1/0.11/0.12/0.13/0.14\",\"3\",\"0.4\",\"0.04\",\"2\",\"7\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[430,430,430,430,430],\"rangeBurn\":\"430\",\"image\":{\"full\":\"SonaE.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":432,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SonaR\",\"name\":\"Crescendo\",\"description\":\"Sona plays her ultimate chord, stunning enemy champions and forcing them to dance and dealing magic damage to them. Each rank reduces the base cooldown of Sona's basic abilities.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Active: </span>Strikes an irresistible chord, stunning enemy Champions and forcing them to dance for {{ e2 }} seconds and take {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.<br><br><span class=\\\"colorFF9900\\\">Passive: </span>Reduces the base cooldown of Sona's basic abilities by {{ e3 }}%.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Hymn of Valor Cooldown\",\"Aria of Perseverance Cooldown\",\"Song of Celerity Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ f1 }} -> {{ f2 }}\",\"{{ f3 }} -> {{ f4 }}\",\"{{ f5 }} -> {{ f6 }}\"]},\"maxrank\":3,\"cooldown\":[140,120,100],\"cooldownBurn\":\"140/120/100\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[150,250,350],[1.5,1.5,1.5],[10,25,40],[-0.1,-0.25,-0.4],[-0.1,-0.25,-0.4],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"150/250/350\",\"1.5\",\"10/25/40\",\"-0.1/-0.25/-0.4\",\"-0.1/-0.25/-0.4\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"SonaR.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":0,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Soraka\":{\"id\":16,\"key\":\"Soraka\",\"name\":\"Soraka\",\"title\":\"the Starchild\",\"spells\":[{\"id\":\"SorakaQ\",\"name\":\"Starcall\",\"description\":\"A star falls from the sky at the target location dealing magic damage and slowing enemies. If an enemy champion is hit by Starcall, Soraka recovers health and gains movement speed when moving away from enemy champions.\",\"tooltip\":\"Calls down a star from Soraka to a target location. Enemies standing in the explosion radius take {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and are slowed by {{ e2 }}% for 2 seconds.<br><br>If Starcall hits a champion Soraka gains <span class=\\\"colorDDDDDD\\\">Rejuvenation</span> for {{ e5 }} seconds, which restores {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ f1 }})</span> health per second and grants {{ e9 }}% movement speed when not moving toward enemy champions.\",\"leveltip\":{\"label\":[\"Damage\",\"Health Restore (Rejuvenation)\",\"Rejuvenation Duration (for allies)\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ e6 }} -> {{ e6NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[5,5,5,5,5],\"cooldownBurn\":\"5\",\"cost\":[40,45,50,55,60],\"costBurn\":\"40/45/50/55/60\",\"effect\":[null,[70,110,150,190,230],[30,30,30,30,30],[14,16,18,20,22],[7,9,11,13,15],[4,4,4,4,4],[3,3.5,4,4.5,5],[0.1,0.1,0.1,0.1,0.1],[0.05,0.05,0.05,0.05,0.05],[10,10,10,10,10],[0,0,0,0,0]],\"effectBurn\":[null,\"70/110/150/190/230\",\"30\",\"14/16/18/20/22\",\"7/9/11/13/15\",\"4\",\"3/3.5/4/4.5/5\",\"0.1\",\"0.05\",\"10\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.35,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[810,810,810,810,810],\"rangeBurn\":\"810\",\"image\":{\"full\":\"SorakaQ.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":48,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SorakaW\",\"name\":\"Astral Infusion\",\"description\":\"Soraka sacrifices a portion of her own health to heal another friendly champion.\",\"tooltip\":\"Restores {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> health to another champion ally.<br><br>If cast while affected by <span class=\\\"colorDDDDDD\\\">Rejuvenation</span>, Soraka will grant her target its benefits for {{ f1 }} seconds.<br><br><span class=\\\"color919191\\\"><i>Cannot be cast if Soraka is below 5% Health.</i></span>\",\"leveltip\":{\"label\":[\"Heal Amount\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[8,6.5,5,3.5,2],\"cooldownBurn\":\"8/6.5/5/3.5/2\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[80,110,140,170,200],[0.1,0.1,0.1,0.1,0.1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/110/140/170/200\",\"0.1\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[550,550,550,550,550],\"rangeBurn\":\"550\",\"image\":{\"full\":\"SorakaW.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":96,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"10% Max Health, {{ cost }} Mana\"},{\"id\":\"SorakaE\",\"name\":\"Equinox\",\"description\":\"Creates a zone at a location that silences all enemies inside. When the zone expires, all enemies still inside are rooted.\",\"tooltip\":\"Creates a zone at target location for 1.5 seconds, dealing {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemy Champions in the cast radius. Enemy Champions standing in the zone are silenced until they leave. <br><br>When the zone disappears, all enemy Champions still standing in the zone are rooted for {{ e1 }} second(s) and are dealt {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }}) </span>magic damage.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Root Duration\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":5,\"cooldown\":[24,22,20,18,16],\"cooldownBurn\":\"24/22/20/18/16\",\"cost\":[70,70,70,70,70],\"costBurn\":\"70\",\"effect\":[null,[1,1.25,1.5,1.75,2],[70,110,150,190,230],[40,70,100,130,160],[5,5,5,5,5],[20,40,60,80,100],[60,90,120,150,180],[1.5,1.5,1.5,1.5,1.5],[260,260,260,260,260],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"1/1.25/1.5/1.75/2\",\"70/110/150/190/230\",\"40/70/100/130/160\",\"5\",\"20/40/60/80/100\",\"60/90/120/150/180\",\"1.5\",\"260\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[925,925,925,925,925],\"rangeBurn\":\"925\",\"image\":{\"full\":\"SorakaE.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":144,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SorakaR\",\"name\":\"Wish\",\"description\":\"Soraka fills her allies with hope, instantly restoring health to herself and all friendly champions.\",\"tooltip\":\"Calls upon divine powers to restore {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Health to all allied Champions. Wish's power is increased by 50% on each Champion below 40% Health.\",\"leveltip\":{\"label\":[\"Health Restored\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[160,145,130],\"cooldownBurn\":\"160/145/130\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[150,250,350],[120,90,60],[250,350,450],[1.5,1.5,1.5],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"150/250/350\",\"120/90/60\",\"250/350/450\",\"1.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.55,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[25000,25000,25000],\"rangeBurn\":\"25000\",\"image\":{\"full\":\"SorakaR.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":192,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Swain\":{\"id\":50,\"key\":\"Swain\",\"name\":\"Swain\",\"title\":\"the Master Tactician\",\"spells\":[{\"id\":\"SwainDecrepify\",\"name\":\"Decrepify\",\"description\":\"Swain sends Beatrice to cripple enemies within the target area. Crippled targets are slowed and damaged over time.\",\"tooltip\":\"Beatrice lands on the target area for {{ e3 }} seconds and attacks the closest enemy unit prioritizing champions, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage per second and slowing it down by <span class=\\\"colorFFFFFF\\\">{{ e2 }}%</span>. Damage is doubled to {{ f1 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage against minions.<br><br>Beatrice will look for a new target if the current one moves away or dies. If any nearby enemy unit is afflicted by <span class=\\\"colorFFFF00\\\">Torment</span> then it will become Beatrice's target.<br><br>Max damage: {{ f2 }} <span class=\\\"color99FF99\\\">(+{{ f3 }})</span> to non-minions and {{ f4 }} <span class=\\\"color99FF99\\\">(+{{ f5 }})</span> to enemy minions.\",\"leveltip\":{\"label\":[\"Damage Per Second\",\"Slow\",\"Mana Cost\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cost }} -> {{ costNL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[14,13,12,11,10],\"cooldownBurn\":\"14/13/12/11/10\",\"cost\":[60,65,70,75,80],\"costBurn\":\"60/65/70/75/80\",\"effect\":[null,[30,48,65,83,100],[20,25,30,35,40],[4,4,4,4,4],[350,350,350,350,350],[0.5,0.5,0.5,0.5,0.5],[0.25,0.25,0.25,0.25,0.25],[2,2,2,2,2],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30/48/65/83/100\",\"20/25/30/35/40\",\"4\",\"350\",\"0.5\",\"0.25\",\"2\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[700,700,700,700,700],\"rangeBurn\":\"700\",\"image\":{\"full\":\"SwainDecrepify.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":240,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SwainShadowGrasp\",\"name\":\"Nevermove\",\"description\":\"Swain marks a target area. After a short delay, mighty talons grab hold of enemy units, dealing damage and rooting them.\",\"tooltip\":\"Swain marks the target area that triggers after a brief delay dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemies within range and rooting them for <span class=\\\"colorFFFFFF\\\">{{ e2 }}</span> second(s).\",\"leveltip\":{\"label\":[\"Damage\",\"Root Duration\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[18,16,14,12,10],\"cooldownBurn\":\"18/16/14/12/10\",\"cost\":[80,85,90,95,100],\"costBurn\":\"80/85/90/95/100\",\"effect\":[null,[80,120,160,200,240],[1,1.25,1.5,1.75,2],[0.875,0.875,0.875,0.875,0.875],[0,0,0,0,0],[0.875,0.875,0.875,0.875,0.875],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/120/160/200/240\",\"1/1.25/1.5/1.75/2\",\"0.88\",\"0\",\"0.88\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[900,900,900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"SwainShadowGrasp.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":288,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SwainTorment\",\"name\":\"Torment\",\"description\":\"Swain afflicts his target, dealing damage to them over time and causing them to take increased damage from Swain. Enemies afflicted by Torment are focused by Beatrice while Decrepify is active.\",\"tooltip\":\"Swain afflicts his target with a curse that deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage over 4 seconds and amplifies Swain's damage against it by {{ e2 }}%.\",\"leveltip\":{\"label\":[\"Base Damage Over Time\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,10,10,10,10],\"cooldownBurn\":\"10\",\"cost\":[65,70,75,80,85],\"costBurn\":\"65/70/75/80/85\",\"effect\":[null,[50,80,110,140,170],[20,20,20,20,20],[6,7,8,9,10],[4,4,4,4,4],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/80/110/140/170\",\"20\",\"6/7/8/9/10\",\"4\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[625,625,625,625,625],\"rangeBurn\":\"625\",\"image\":{\"full\":\"SwainTorment.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":336,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SwainMetamorphism\",\"name\":\"Ravenous Flock\",\"description\":\"Swain inspires dread in his enemies by temporarily taking the form of a raven. During this time ravens strike out at up to 5 nearby enemies. Each raven deals damage and heals Swain by a flat amount.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Toggle:</span> Swain transforms into a monstrous raven, spawning up to {{ e7 }} lesser ravens per second that strike out at nearby enemies, prioritizing champions. Each raven deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and heals Swain for {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ f9 }})</span> against champions and {{ e9 }} <span class=\\\"color99FF99\\\">(+{{ f10 }})</span> against minions and monsters.<br><br>Ravens can't attack the same target for 1 second and the mana cost to sustain Ravenous Flock increases by {{ e8 }} Mana each second.\",\"leveltip\":{\"label\":[\"Damage Amount\",\"Heal From Champions\",\"Heal From Minions and Monsters\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ e9 }} -> {{ e9NL }}\"]},\"maxrank\":3,\"cooldown\":[20,20,20],\"cooldownBurn\":\"20\",\"cost\":[25,25,25],\"costBurn\":\"25\",\"effect\":[null,[50,70,90],[20,30,40],[0.12,0.12,0.12],[25,25,25],[2,2,2],[25000,25000,25000],[5,5,5],[5,5,5],[8,11,14],[0.03,0.03,0.03]],\"effectBurn\":[null,\"50/70/90\",\"20/30/40\",\"0.12\",\"25\",\"2\",\"25000\",\"5\",\"5\",\"8/11/14\",\"0.03\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"}],\"costType\":\" Initial Mana Cost per Second\",\"maxammo\":\"-1\",\"range\":[625,625,625],\"rangeBurn\":\"625\",\"image\":{\"full\":\"SwainMetamorphism.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":384,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Initial Mana Cost per Second\"}]},\"Syndra\":{\"id\":134,\"key\":\"Syndra\",\"name\":\"Syndra\",\"title\":\"the Dark Sovereign\",\"spells\":[{\"id\":\"SyndraQ\",\"name\":\"Dark Sphere\",\"description\":\"Syndra conjures a Dark Sphere dealing magic damage. The sphere remains and can be manipulated by her other powers.\",\"tooltip\":\"Conjures a Dark Sphere dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage. The sphere remains for <span class=\\\"colorFFFFFF\\\">{{ f1 }}</span> seconds and can be manipulated by Syndra's other abilities.<br><br><span class=\\\"color99FF99\\\">This spell can be cast while moving.</span>\",\"leveltip\":{\"label\":[\"Damage\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }} \",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[4,4,4,4,4],\"cooldownBurn\":\"4\",\"cost\":[40,50,60,70,80],\"costBurn\":\"40/50/60/70/80\",\"effect\":[null,[50,95,140,185,230],[6,6,6,6,6],[2,2.2,2.4,2.6,2.8],[8,8,8,8,8],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/95/140/185/230\",\"6\",\"2/2.2/2.4/2.6/2.8\",\"8\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.75,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[800,800,800,800,800],\"rangeBurn\":\"800\",\"image\":{\"full\":\"SyndraQ.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":432,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SyndraW\",\"name\":\"Force of Will\",\"description\":\"Syndra picks up and throws a Dark Sphere or enemy minion dealing magic damage and slowing the Movement Speed of enemies. \",\"tooltip\":\"<span class=\\\"colorFF9900\\\">First Cast:</span> Grabs target Dark Sphere or enemy minion. If no target is selected, grabs nearest Dark Sphere.<br><br><span class=\\\"colorFF9900\\\">Second Cast:</span> Throws the grabbed unit. Enemies hit take {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and are slowed by <span class=\\\"colorFFFFFF\\\">{{ e1 }}%</span> for {{ f2 }} seconds.<br><br><span class=\\\"color99FF99\\\">This spell can be cast while moving.</span>\",\"leveltip\":{\"label\":[\"Damage\",\"Mana Cost\",\"Slow\",\"Cooldown\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }} \",\"{{ cost }} -> {{ costNL }} \",\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ e3 }} -> {{ e3NL }}\"]},\"maxrank\":5,\"cooldown\":[12,11,10,9,8],\"cooldownBurn\":\"12/11/10/9/8\",\"cost\":[60,70,80,90,100],\"costBurn\":\"60/70/80/90/100\",\"effect\":[null,[25,30,35,40,45],[70,110,150,190,230],[12,11,10,9,8],[1.5,1.5,1.5,1.5,1.5],[400,400,400,400,400],[0.2,0.2,0.2,0.2,0.2],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"25/30/35/40/45\",\"70/110/150/190/230\",\"12/11/10/9/8\",\"1.5\",\"400\",\"0.2\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"},{\"link\":\"@text\",\"coeff\":1.5,\"key\":\"f2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[925,925,925,925,925],\"rangeBurn\":\"925\",\"image\":{\"full\":\"SyndraW.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":0,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SyndraE\",\"name\":\"Scatter the Weak\",\"description\":\"Syndra knocks enemies and Dark Spheres back dealing magic damage. Enemies hit by Dark Spheres become stunned.\",\"tooltip\":\"Knocks enemies and Dark Spheres back dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to them and any enemies they collide with.<br><br>Dark Spheres that are knocked back stun all enemies in their path for {{ e2 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }} \",\"{{ cooldown }} -> {{ cooldownNL }} \"]},\"maxrank\":5,\"cooldown\":[16,15,14,13,12],\"cooldownBurn\":\"16/15/14/13/12\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[70,115,160,205,250],[1.5,1.5,1.5,1.5,1.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/115/160/205/250\",\"1.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"SyndraE.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":48,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"SyndraR\",\"name\":\"Unleashed Power\",\"description\":\"Syndra bombards an enemy Champion with all of her Dark Spheres.\",\"tooltip\":\"Draws upon Syndra's full cataclysmic power, harnessing Dark Spheres to deal magic damage to target enemy Champion. Unleashed Power manipulates the three Dark Spheres orbiting Syndra, in addition to up to 4 Dark Spheres previously created.<br><br>Damage per sphere: {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span>.<br>Minimum total damage: {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span>.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }} \",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,100,80],\"cooldownBurn\":\"120/100/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[90,135,180],[270,405,540],[7,7,7],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"90/135/180\",\"270/405/540\",\"7\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[675,675,675],\"rangeBurn\":\"675\",\"image\":{\"full\":\"SyndraR.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":96,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"TahmKench\":{\"id\":223,\"key\":\"TahmKench\",\"name\":\"Tahm Kench\",\"title\":\"the River King\",\"spells\":[{\"id\":\"TahmKenchQ\",\"name\":\"Tongue Lash\",\"description\":\"Tahm Kench lashes out with his tongue, damaging and slowing the first unit hit. This ability gains a stun after three stacks of An Acquired Taste.\",\"tooltip\":\"Damage the first enemy hit for {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and slow them by {{ e4 }}% for {{ e5 }}s. Champions with 3 stacks of An Acquired Taste will additionally be stunned for {{ e6 }}s.<br><br>Activate <span class=\\\"color0bf7de\\\">Devour</span> while your tongue is in midair to devour monsters/minions from a distance.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Slow Amount\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ e4 }}% -> {{ e4NL }}%\"]},\"maxrank\":5,\"cooldown\":[6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[875,875,875,875,875],[1000,1000,1000,1000,1000],[80,130,180,230,280],[30,40,50,60,70],[2,2,2,2,2],[1.5,1.5,1.5,1.5,1.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"875\",\"1000\",\"80/130/180/230/280\",\"30/40/50/60/70\",\"2\",\"1.5\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[800,800,800,800,800],\"rangeBurn\":\"800\",\"image\":{\"full\":\"TahmKenchQ.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":144,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"TahmKenchW\",\"name\":\"Devour\",\"description\":\"Tahm Kench devours a target, dealing a percentage of their maximum health as magic damage. He can spit devoured minions and monsters out as a skillshot that deals magic damage in an area upon impact.\",\"tooltip\":\"Devour a target for {{ e1 }}s (half that for enemy champions.) Enemies are dealt {{ e3 }}% <span class=\\\"color99FF99\\\">(+{{ a1 }}%)</span> of their maximum health as magic damage (maximum of {{ e2 }} against neutral monsters).<br><br><span class=\\\"colorFF9900\\\">Enemy Champions:</span> Requires 3 stacks of An Acquired Taste to be devoured. While holding an enemy champion, Tahm Kench is slowed by 95%. <br><br><span class=\\\"colorFF9900\\\">Allied Champions:</span> While holding an allied champion, Tahm Kench gains {{ e4 }}% movement speed toward enemy champions.<br><br><span class=\\\"colorFF9900\\\">Minions and Monsters:</span> Reactivate to spit them, dealing {{ e7 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage to targets hit.\",\"leveltip\":{\"label\":[\"Devour Duration\",\"Max Health Damage\",\"Max Damage to Monsters\",\"Minion Spit Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }}s -> {{ e1NL }}s\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ e7 }} -> {{ e7NL }}\",\"{{ cooldown }}s -> {{ cooldownNL }}s\"]},\"maxrank\":5,\"cooldown\":[14,13,12,11,10],\"cooldownBurn\":\"14/13/12/11/10\",\"cost\":[90,90,90,90,90],\"costBurn\":\"90\",\"effect\":[null,[4,4.5,5,5.5,6],[400,450,500,550,600],[20,23,26,29,32],[30,35,40,45,50],[0,0,0,0,0],[0,0,0,0,0],[100,150,200,250,300],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"4/4.5/5/5.5/6\",\"400/450/500/550/600\",\"20/23/26/29/32\",\"30/35/40/45/50\",\"0\",\"0\",\"100/150/200/250/300\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.02,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[250,250,250,250,250],\"rangeBurn\":\"250\",\"image\":{\"full\":\"TahmKenchW.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":192,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"TahmKenchE\",\"name\":\"Thick Skin\",\"description\":\"Tahm Kench turns incoming damage into gray health. As gray health decays, Tahm Kench is healed for a percentage of the gray health amount. When activated, this ability turns all gray health into a shield.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive:</span> {{ e3 }}% of the Damage taken while this ability is not cooling down is converted to gray health. If allowed to decay, {{ e2 }}% of gray health will turn back into health.<br><br><span class=\\\"colorFF9900\\\">Active:</span> Convert all of your gray health into a shield that lasts {{ e1 }} seconds.\",\"leveltip\":{\"label\":[\"Damage to Gray Health Conversion\",\"Gray Health to Healing Conversion\"],\"effect\":[\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[3,3,3,3,3],[25,30,35,40,45],[70,75,80,85,90],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"3\",\"25/30/35/40/45\",\"70/75/80/85/90\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1,1,1,1,1],\"rangeBurn\":\"1\",\"image\":{\"full\":\"TahmKenchE.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":240,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"TahmKenchNewR\",\"name\":\"Abyssal Voyage\",\"description\":\"Tahm Kench teleports across the map, and he may bring one ally champion with him.\",\"tooltip\":\"Begin to channel for up to 6 seconds. During this time, one ally champion can right click Tahm Kench to join in. Alternatively, reactivate this ability to travel alone. On reactivation or once an ally has opted in, Tahm travels to the target location. Incoming champion damage breaks the channel.<br><br><span class=\\\"colorcccccc\\\"><i>'Boy, the world's one river, and I'm its king. Ain't no place I ain't been; ain't no place I can't go again.'</i></span>\",\"leveltip\":{\"label\":[\"Cooldown\",\"Range\"],\"effect\":[\"{{ cooldown }}s -> {{ cooldownNL }}s\",\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":3,\"cooldown\":[120,110,100],\"cooldownBurn\":\"120/110/100\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[4500,5500,6500],[0.02,0.04,0.06],[20,20,20],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"4500/5500/6500\",\"0.02/0.04/0.06\",\"20\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[4500,5500,6500],\"rangeBurn\":\"4500/5500/6500\",\"image\":{\"full\":\"TahmKenchNewR.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":288,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Taliyah\":{\"id\":163,\"key\":\"Taliyah\",\"name\":\"Taliyah\",\"title\":\"the Stoneweaver\",\"spells\":[{\"id\":\"TaliyahQ\",\"name\":\"Threaded Volley\",\"description\":\"Taliyah throws a volley of missiles in a target direction, moving freely as she does. This works the ground below her. If Taliyah casts Threaded Volley on worked ground, she only throws one missile.\",\"tooltip\":\"Hurls 5 rocks in a direction, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage in a small area around the first enemy hit. Subsequent hits on the same unit deal {{ e2 }}% reduced damage. Creates <span class=\\\"colora56705\\\">Worked Ground</span> for <span class=\\\"colorFFFFFF\\\">{{ f1 }}</span> seconds.<br><br>Taliyah gains <span class=\\\"colorffffff\\\">{{ f2 }}%</span> movement speed on <span class=\\\"colora56705\\\">Worked Ground</span>. Casting Threaded Volley on <span class=\\\"colora56705\\\">Worked Ground</span> only hurls one rock but refunds {{ e7 }}% of its mana cost.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }}s -> {{ cooldownNL }}s\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[9,7.5,6,4.5,3],\"cooldownBurn\":\"9/7.5/6/4.5/3\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[70,95,120,145,170],[60,60,60,60,60],[0,0,0,0,0],[0,0,0,0,0],[160,160,160,160,160],[120,120,120,120,120],[50,50,50,50,50],[450,450,450,450,450],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/95/120/145/170\",\"60\",\"0\",\"0\",\"160\",\"120\",\"50\",\"450\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.45,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"TaliyahQ.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":336,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"TaliyahWVC\",\"name\":\"Seismic Shove\",\"description\":\"Taliyah causes an area of ground to erupt and throws enemies within in a direction of her choosing.\",\"tooltip\":\"Taliyah targets an area. After a short delay, enemies caught within the area will be <span class=\\\"colorffffff\\\">pushed</span> and dealt {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.<br><br>You can control the direction enemies will be <span class=\\\"colorffffff\\\">pushed</span> by clicking and dragging your mouse in a line.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }}s -> {{ cooldownNL }}s\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[16,15,14,13,12],\"cooldownBurn\":\"16/15/14/13/12\",\"cost\":[70,80,90,100,110],\"costBurn\":\"70/80/90/100/110\",\"effect\":[null,[60,80,100,120,140],[0.55,0.55,0.55,0.55,0.55],[400,400,400,400,400],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/80/100/120/140\",\"0.55\",\"400\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[900,900,900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"TaliyahWVC.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":384,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"TaliyahE\",\"name\":\"Unraveled Earth\",\"description\":\"Taliyah creates a slowing minefield that explodes if enemies dash through it or are pushed/pulled through it.\",\"tooltip\":\"Places a field of dash-sensitive traps that deal {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and slow enemies in the area by <span class=\\\"color99FF99\\\">{{ f2 }}%</span>. After {{ e3 }} seconds, the traps explode, dealing damage again.<br><br>Enemies <span class=\\\"colorFFFFFF\\\">dashing</span>, being <span class=\\\"colorFFFFFF\\\">pushed</span>, or being <span class=\\\"colorFFFFFF\\\">pulled</span> through Unraveled Earth will trigger traps, taking {{ f3 }} <span class=\\\"color99FF99\\\">(+{{ f1 }})</span> magic damage from each trap (maximum 4, each hit after the first deals {{ f4 }}% less damage).\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }}s -> {{ cooldownNL }}s\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[16,14,12,10,8],\"cooldownBurn\":\"16/14/12/10/8\",\"cost\":[90,95,100,105,110],\"costBurn\":\"90/95/100/105/110\",\"effect\":[null,[70,90,110,130,150],[-0.2,-0.2,-0.2,-0.2,-0.2],[4,4,4,4,4],[85,85,85,85,85],[0.5,0.5,0.5,0.5,0.5],[4,4,4,4,4],[0.15,0.15,0.15,0.15,0.15],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/90/110/130/150\",\"-0.2\",\"4\",\"85\",\"0.5\",\"4\",\"0.15\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[800,800,800,800,800],\"rangeBurn\":\"800\",\"image\":{\"full\":\"TaliyahE.png\",\"sprite\":\"spell10.png\",\"group\":\"spell\",\"x\":432,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"TaliyahR\",\"name\":\"Weaver's Wall\",\"description\":\"Stone Weaver creates a very long wall and then surfs it.\",\"tooltip\":\"Cast once to create a wall. Recast immediately to ride ahead of the wall. Moving or receiving damage will cause Taliyah to stop.<br><br>Weaver's Wall lasts for {{ e1 }} seconds. Deactivate this spell to break the wall early.\",\"leveltip\":{\"label\":[\"Duration\",\"Wall length\",\"Cooldown\"],\"effect\":[\"{{ e1 }}s -> {{ e1NL }}s\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }}s -> {{ cooldownNL }}s\"]},\"maxrank\":3,\"cooldown\":[180,150,120],\"cooldownBurn\":\"180/150/120\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[6,7,8],[3000,4500,6000],[0.1,0.1,0.1],[2500,2500,2500],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"6/7/8\",\"3000/4500/6000\",\"0.1\",\"2500\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[3000,4500,6000],\"rangeBurn\":\"3000/4500/6000\",\"image\":{\"full\":\"TaliyahR.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":0,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Talon\":{\"id\":91,\"key\":\"Talon\",\"name\":\"Talon\",\"title\":\"the Blade's Shadow\",\"spells\":[{\"id\":\"TalonQ\",\"name\":\"Noxian Diplomacy\",\"description\":\"Talon stabs the target unit. If they are within melee range, this attack deals critical damage. If they are outside melee range, Talon will leap at his target before stabbing them. Talon refunds some health and cooldown if this ability kills the target.\",\"tooltip\":\"Talon leaps to target and deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage. If cast from melee range, Talon does not leap but instead critically strikes, dealing {{ f2 }}% damage <span class=\\\"colorFF8C00\\\">({{ f3 }})</span>.<br><br>When Noxian Diplomacy kills a unit, Talon regains <span class=\\\"colorFFFFFF\\\">{{ f4 }}</span> health and refunds {{ e5 }}% of its cooldown.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ f5 }} -> {{ f6 }}\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[60,85,110,135,160],[50,50,50,50,50],[17,17,17,17,17],[3,3,3,3,3],[50,50,50,50,50],[8,7.5,7,6.5,6],[0.8,0.8,0.8,0.8,0.8],[1.5,1.5,1.5,1.5,1.5],[30,30,30,30,30],[0,0,0,0,0]],\"effectBurn\":[null,\"60/85/110/135/160\",\"50\",\"17\",\"3\",\"50\",\"8/7.5/7/6.5/6\",\"0.8\",\"1.5\",\"30\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1.1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[10000,10000,10000,10000,10000],\"rangeBurn\":\"10000\",\"image\":{\"full\":\"TalonQ.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":48,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ e9 }} Mana\"},{\"id\":\"TalonW\",\"name\":\"Rake\",\"description\":\"Talon sends out a volley of daggers that then return back to him, dealing physical damage every time it passes through an enemy. The returning blades deal bonus damage and slow units hit.\",\"tooltip\":\"Talon tosses a volley of blades, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage to units hit and returning to him after a delay.<br><br>On their way back to Talon, the blades deal {{ e5 }} <span class=\\\"colorFF8C00\\\">(+{{ a2 }})</span> additional damage and slow the enemy by {{ e2 }}% for 1 second.\",\"leveltip\":{\"label\":[\"Initial Damage\",\"Return Damage\",\"Slow Amount\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e5 }} -> {{ e5NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[9,9,9,9,9],\"cooldownBurn\":\"9\",\"cost\":[55,60,65,70,75],\"costBurn\":\"55/60/65/70/75\",\"effect\":[null,[50,65,80,95,110],[40,45,50,55,60],[14,13,12,11,10],[2,2,2,2,2],[60,85,110,135,160],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/65/80/95/110\",\"40/45/50/55/60\",\"14/13/12/11/10\",\"2\",\"60/85/110/135/160\",\"1\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.6,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"TalonW.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":96,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"TalonE\",\"name\":\"Assassin's Path\",\"description\":\"Talon vaults over any terrain or structure, up to a max distance. This ability has a low cooldown, but puts the used terrain on a long cooldown.\",\"tooltip\":\"Talon vaults up to {{ f2 }} units over the nearest structure or terrain in the target direction. The vault's speed is affected by Talon's movement speed.<br><br>Talon cannot dash over the same section of terrain more than once every <span class=\\\"colorFFFFFF\\\">{{ f3 }}</span> seconds.\",\"leveltip\":{\"label\":[\"Terrain Cooldown\"],\"effect\":[\"{{ f3 }} -> {{ f4 }}\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[0,0,0,0,0],[0,0,0,0,0],[625,625,625,625,625],[1250,1250,1250,1250,1250],[2,2,2,2,2],[160,135,110,85,60],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"0\",\"0\",\"625\",\"1250\",\"2\",\"160/135/110/85/60\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\"No cost\",\"maxammo\":\"-1\",\"range\":[725,725,725,725,725],\"rangeBurn\":\"725\",\"image\":{\"full\":\"TalonE.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":144,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No cost\"},{\"id\":\"TalonR\",\"name\":\"Shadow Assault\",\"description\":\"Talon disperses a ring of blades and becomes Invisible while gaining additional Movement Speed. When Talon emerges from Invisibility, the blades converge on his location. Each time the blades move, Shadow Assault deals physical damage to enemies hit by at least one blade.  \",\"tooltip\":\"Talon disperses a ring of blades that deal {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage to all units they hit, gains {{ e3 }}% increased movement speed, and becomes <span class=\\\"color91d7ee\\\">Invisible</span> for up to {{ e5 }} seconds. When the <span class=\\\"color91d7ee\\\">Invisibility</span> ends, the blades converge, dealing the same damage again to enemies they pass through.<br><br>If Talon cancels <span class=\\\"color91d7ee\\\">Invisibility</span> with an attack or Noxian Diplomacy, the blades converge on his target's location instead.<br><br><u><span class=\\\"size16 color91d7ee\\\">16\",\"leveltip\":{\"label\":[\"Damage\",\"Movement Speed\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[100,80,60],\"cooldownBurn\":\"100/80/60\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[90,135,180],[200,300,400],[40,55,70],[70,85,100],[2.5,2.5,2.5],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"90/135/180\",\"200/300/400\",\"40/55/70\",\"70/85/100\",\"2.5\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[550,550,550],\"rangeBurn\":\"550\",\"image\":{\"full\":\"TalonR.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":192,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Taric\":{\"id\":44,\"key\":\"Taric\",\"name\":\"Taric\",\"title\":\"the Shield of Valoran\",\"spells\":[{\"id\":\"TaricQ\",\"name\":\"Starlight's Touch\",\"description\":\"Heals nearby allied champions based on charges stored.<br><br>Bravado-empowered attacks reduce this recharge time more than usual.\",\"tooltip\":\"Spends all charges to heal nearby allied champions for {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> <span class=\\\"colorFF3300\\\">(+{{ f1 }})</span> per charge, up to {{ e5 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> <span class=\\\"colorFF3300\\\">(+{{ f2 }})</span> at 3 charges.<br><br>Bravado-empowered attacks reduce this recharge time by <span class=\\\"colorFFFFFF\\\">{{ f3 }}</span> <i>additional</i> seconds.\",\"leveltip\":{\"label\":[\"Base Healing\",\"Max Healing\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e5 }} -> {{ e5NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[60,80,100,120,140],\"costBurn\":\"60/80/100/120/140\",\"effect\":[null,[20,30,40,50,60],[1.5,1.5,1.5,1.5,1.5],[5,5,5,5,5],[6,6,6,6,6],[60,90,120,150,180],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"20/30/40/50/60\",\"1.5\",\"5\",\"6\",\"60/90/120/150/180\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a2\"}],\"costType\":\" Mana, 1-3 Charges\",\"maxammo\":\"-1\",\"range\":[325,325,325,325,325],\"rangeBurn\":\"325\",\"image\":{\"full\":\"TaricQ.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":240,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana, 1-3 Charges\"},{\"id\":\"TaricW\",\"name\":\"Bastion\",\"description\":\"Passively increase the Armor of Taric and any allied champion with Bastion.<br><br>Actively shields an ally and grants them Bastion for as long as they remain near Taric. Taric's spells also cast off the ally with Bastion.\",\"tooltip\":\"Taric's spells will also cast from a nearby ally protected by Bastion.<br><br><span class=\\\"colorFF9900\\\">Passive: </span>Bastion increases Armor by <span class=\\\"colorFFFF00\\\">{{ f1 }}</span> ({{ e1 }}% of Taric's Armor).<br><br><span class=\\\"colorFF9900\\\">Active: </span>Blesses an ally with Bastion and shields them for {{ e2 }}% of their maximum Health for {{ e3 }} seconds. Bastion lasts on the target until a new one is chosen.\",\"leveltip\":{\"label\":[\"Passive Armor\",\"Shield Ratio\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[15,15,15,15,15],\"cooldownBurn\":\"15\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[10,12.5,15,17.5,20],[8,9,10,11,12],[2.5,2.5,2.5,2.5,2.5],[1000,1000,1000,1000,1000],[1300,1300,1300,1300,1300],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"10/12.5/15/17.5/20\",\"8/9/10/11/12\",\"2.5\",\"1000\",\"1300\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[800,800,800,800,800],\"rangeBurn\":\"800\",\"image\":{\"full\":\"TaricW.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":288,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"TaricE\",\"name\":\"Dazzle\",\"description\":\"Taric readies a beam of starlight that, after a brief delay, deals magic damage and stuns enemies.\",\"tooltip\":\"Readies a beam of starlight that, after {{ e3 }} second, deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> <span class=\\\"colorFFFF00\\\">(+{{ f1 }})</span> magic damage and stuns enemies for {{ e2 }} second(s).\",\"leveltip\":{\"label\":[\"Damage\",\"Stun Duration\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[15,15,15,15,15],\"cooldownBurn\":\"15\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[60,105,150,195,240],[1,1.125,1.25,1.375,1.5],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/105/150/195/240\",\"1/1.125/1.25/1.375/1.5\",\"1\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[610,610,610,610,610],\"rangeBurn\":\"610\",\"image\":{\"full\":\"TaricE.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":336,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"TaricR\",\"name\":\"Cosmic Radiance\",\"description\":\"Pulses cosmic energy onto nearby allied champions after a delay, making them invulnerable for a short duration.\",\"tooltip\":\"After a {{ e4 }} second delay, pulses cosmic energy onto nearby allied champions, making them invulnerable for {{ e5 }} seconds.\",\"leveltip\":{\"label\":[\"Cooldown\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[160,130,100],\"cooldownBurn\":\"160/130/100\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[150,275,400],[25,35,45],[2.5,2.5,2.5],[2.5,2.5,2.5],[2.5,2.5,2.5],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"150/275/400\",\"25/35/45\",\"2.5\",\"2.5\",\"2.5\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"TaricR.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":384,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Teemo\":{\"id\":17,\"key\":\"Teemo\",\"name\":\"Teemo\",\"title\":\"the Swift Scout\",\"spells\":[{\"id\":\"BlindingDart\",\"name\":\"Blinding Dart\",\"description\":\"Obscures an enemy's vision with a powerful venom, dealing damage to the target unit and blinding it for the duration.\",\"tooltip\":\"Deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and blinds the target for {{ e2 }} seconds. \",\"leveltip\":{\"label\":[\"Damage\",\"Duration\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ e2 }} -> {{ e2NL }}\",\" {{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[8,8,8,8,8],\"cooldownBurn\":\"8\",\"cost\":[70,75,80,85,90],\"costBurn\":\"70/75/80/85/90\",\"effect\":[null,[80,125,170,215,260],[1.5,1.75,2,2.25,2.5],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/125/170/215/260\",\"1.5/1.75/2/2.25/2.5\",\"1\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[680,680,680,680,680],\"rangeBurn\":\"680\",\"image\":{\"full\":\"BlindingDart.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":432,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"MoveQuick\",\"name\":\"Move Quick\",\"description\":\"Teemo scampers around, passively increasing his Movement Speed until he is struck by an enemy champion or turret. Teemo can sprint to gain bonus Movement Speed that isn't stopped by being struck for a short time.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Teemo's Movement Speed is increased by {{ e1 }}% unless he has been damaged by an enemy champion or turret in the last 5 seconds.<br><br><span class=\\\"colorFF9900\\\">Active: </span>Teemo sprints, gaining twice his normal bonus for {{ e2 }} seconds. This bonus is not lost when struck.\",\"leveltip\":{\"label\":[\"Base Movement Speed Bonus\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\"]},\"maxrank\":5,\"cooldown\":[17,17,17,17,17],\"cooldownBurn\":\"17\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[10,14,18,22,26],[3,3,3,3,3],[0.2,0.28,0.36,0.44,0.52],[5,5,5,5,5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"10/14/18/22/26\",\"3\",\"0.2/0.28/0.36/0.44/0.52\",\"5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[20,20,20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"MoveQuick.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":0,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"ToxicShot\",\"name\":\"Toxic Shot\",\"description\":\"Each of Teemo's attacks will poison the target, dealing damage on impact and each second after for 4 seconds.\",\"tooltip\":\"Teemo's basic attacks poison their target, dealing {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magical damage upon impact and {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magical damage each second for {{ e3 }} seconds.\",\"leveltip\":{\"label\":[\"Impact Damage\",\"Damage per Second \"],\"effect\":[\" {{ e2 }} -> {{ e2NL }}\",\" {{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":5,\"cooldown\":[8,8,8,8,8],\"cooldownBurn\":\"8\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[6,12,18,24,30],[10,20,30,40,50],[4,4,4,4,4],[4.5,4.5,4.5,4.5,4.5],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"6/12/18/24/30\",\"10/20/30/40/50\",\"4\",\"4.5\",\"1\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.1,\"key\":\"a2\"}],\"costType\":\"Passive\",\"maxammo\":\"-1\",\"range\":[680,680,680,680,680],\"rangeBurn\":\"680\",\"image\":{\"full\":\"ToxicShot.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":48,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"Passive\"},{\"id\":\"TeemoRCast\",\"name\":\"Noxious Trap\",\"description\":\"Teemo throws an explosive poisonous trap using one of the mushrooms stored in his pack. If an enemy steps on the trap, it will release a poisonous cloud, slowing enemies and damaging them over time. If Teemo throws a mushroom onto another mushroom it will bounce, gaining additional range.\",\"tooltip\":\"Tosses a stored mushroom as a trap that detonates if an enemy steps on it, spreading poison to nearby enemies that slows Movement Speed by {{ e2 }}%, reveals them and deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage over {{ e9 }} seconds. <br><br>Traps last {{ e5 }} minutes and take {{ e4 }} second to arm and stealth. If a thrown trap lands on another trap, it will bounce up to <span class=\\\"colorFFFF99\\\">{{ e6 }}</span> Teemos further before planting.<br><br>Teemo forages for a mushroom every <span class=\\\"colorFFFFFF\\\">{{ f1 }}</span> seconds, but he is only big enough to carry 3 at once.\",\"leveltip\":{\"label\":[\"Damage \",\"Slow Percent\",\"Max Bounce Range\",\"Forage Timer\",\"Cast Range\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ e2 }}% -> {{ e2NL }}%\",\"{{ e6 }} Teemos -> {{ e6NL }} Teemos\",\" {{ f1 }} -> {{ f2 }}\",\" {{ e8 }} -> {{ e8NL }}\"]},\"maxrank\":3,\"cooldown\":[0.25,0.25,0.25],\"cooldownBurn\":\"0.25\",\"cost\":[75,75,75],\"costBurn\":\"75\",\"effect\":[null,[200,325,450],[30,40,50],[75,75,75],[1,1,1],[5,5,5],[3,4,5],[30,25,20],[400,650,900],[4,4,4],[450,450,450]],\"effectBurn\":[null,\"200/325/450\",\"30/40/50\",\"75\",\"1\",\"5\",\"3/4/5\",\"30/25/20\",\"400/650/900\",\"4\",\"450\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"3\",\"range\":[400,650,900],\"rangeBurn\":\"400/650/900\",\"image\":{\"full\":\"TeemoRCast.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":96,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Thresh\":{\"id\":412,\"key\":\"Thresh\",\"name\":\"Thresh\",\"title\":\"the Chain Warden\",\"spells\":[{\"id\":\"ThreshQ\",\"name\":\"Death Sentence\",\"description\":\"Thresh binds an enemy in chains and pulls them toward him. Activating this ability a second time pulls Thresh to the enemy.\",\"tooltip\":\"Thresh throws out his scythe, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage, granting <span class=\\\"coloree91d7\\\">True Sight</span> and stunning the first unit hit, pulling them in for {{ e2 }} seconds.<br><br>Reactivating this ability will pull Thresh to the bound enemy.<br><br>Death Sentence's cooldown is reduced by {{ e4 }} seconds if it hits an enemy.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[20,18,16,14,12],\"cooldownBurn\":\"20/18/16/14/12\",\"cost\":[80,80,80,80,80],\"costBurn\":\"80\",\"effect\":[null,[80,120,160,200,240],[1.5,1.5,1.5,1.5,1.5],[75,75,75,75,75],[3,3,3,3,3],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/120/160/200/240\",\"1.5\",\"75\",\"3\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1075,1075,1075,1075,1075],\"rangeBurn\":\"1075\",\"image\":{\"full\":\"ThreshQ.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":144,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"ThreshW\",\"name\":\"Dark Passage\",\"description\":\"Thresh throws out a lantern that shields nearby allied Champions from damage. Allies can click the lantern to dash to Thresh.\",\"tooltip\":\"Thresh throws the Lantern to a target location. If an ally clicks on it Thresh will pull both the Lantern and his ally to him.<br><br>The Lantern grants a shield lasting {{ e5 }} seconds that absorbs up to {{ e1 }} <span class=\\\"color0bf7de\\\">(+{{ f6 }})</span> damage to Thresh and up to one ally if they come near it. The shield amount scales with the number of <span class=\\\"color0bf7de\\\">souls</span> Thresh has collected.\",\"leveltip\":{\"label\":[\"Shield Absorption\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[22,20.5,19,17.5,16],\"cooldownBurn\":\"22/20.5/19/17.5/16\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[60,100,140,180,220],[1,1,1,1,1],[0,0,0,0,0],[6,6,6,6,6],[4,4,4,4,4],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/100/140/180/220\",\"1\",\"0\",\"6\",\"4\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[950,950,950,950,950],\"rangeBurn\":\"950\",\"image\":{\"full\":\"ThreshW.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":192,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"ThreshE\",\"name\":\"Flay\",\"description\":\"Thresh's attacks wind up, dealing more damage the longer he waits between attacks. When activated, Thresh sweeps his chain, knocking all enemies hit in the direction of the blow.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Thresh's basic attacks deal from <span class=\\\"color0bf7de\\\">{{ f3 }}</span> to <span class=\\\"color0bf7de\\\">{{ f3 }}</span> <span class=\\\"colorFF8C00\\\">(+{{ f2 }})</span> additional magic damage, which builds up while not attacking (total <span class=\\\"color0bf7de\\\">souls</span> collected plus up to {{ e3 }}% total Attack Damage).<br><br><span class=\\\"colorFF9900\\\">Active: </span>Deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage in a line beginning behind Thresh. Enemies hit are pushed in the direction of the swing, then slowed by {{ e2 }}% for {{ e4 }} second.<br><br>Cast forward to push; cast backward to pull.\",\"leveltip\":{\"label\":[\"Passive Damage\",\"Active Damage\",\"Slow\",\"Mana Cost\"],\"effect\":[\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[9,9,9,9,9],\"cooldownBurn\":\"9\",\"cost\":[60,65,70,75,80],\"costBurn\":\"60/65/70/75/80\",\"effect\":[null,[65,95,125,155,185],[20,25,30,35,40],[80,110,140,170,200],[1,1,1,1,1],[1,1,1,1,1],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"65/95/125/155/185\",\"20/25/30/35/40\",\"80/110/140/170/200\",\"1\",\"1\",\"1\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[500,500,500,500,500],\"rangeBurn\":\"500\",\"image\":{\"full\":\"ThreshE.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":240,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"ThreshRPenta\",\"name\":\"The Box\",\"description\":\"A prison of walls that slow and deal damage if broken.\",\"tooltip\":\"Thresh creates a prison of spectral walls around himself. Enemy champions who walk through a wall suffer {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and are slowed by {{ e3 }}% for {{ e2 }} seconds, but break that wall.  <br><br>Once one wall is broken, the remaining walls deal no damage and apply half slow duration. An enemy cannot be affected by multiple walls simultaneously.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[140,120,100],\"cooldownBurn\":\"140/120/100\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[250,400,550],[2,2,2],[99,99,99],[4,4,4],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"250/400/550\",\"2\",\"99\",\"4\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[450,450,450],\"rangeBurn\":\"450\",\"image\":{\"full\":\"ThreshRPenta.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":288,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Tristana\":{\"id\":18,\"key\":\"Tristana\",\"name\":\"Tristana\",\"title\":\"the Yordle Gunner\",\"spells\":[{\"id\":\"TristanaQ\",\"name\":\"Rapid Fire\",\"description\":\"Tristana fires her weapon rapidly, increasing her Attack Speed for a short time.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Active: </span>Increases Tristana's Attack Speed by {{ e1 }}% for {{ e2 }} seconds.\",\"leveltip\":{\"label\":[\"Attack Speed %\",\"Cooldown\"],\"effect\":[\" {{ e1 }}% -> {{ e1NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}</postScriptRight>\"]},\"maxrank\":5,\"cooldown\":[20,19,18,17,16],\"cooldownBurn\":\"20/19/18/17/16\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[30,50,70,90,110],[7,7,7,7,7],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30/50/70/90/110\",\"7\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[20,20,20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"TristanaQ.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":336,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"TristanaW\",\"name\":\"Rocket Jump\",\"description\":\"Tristana fires at the ground to propel her to a distant location, dealing damage and slowing surrounding units for a brief period where she lands.\",\"tooltip\":\"Tristana launches herself to target location, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Magic Damage and slowing surrounding enemies by {{ e3 }}% for {{ e2 }} seconds.<br><br>Kills, Assists, and max stack Explosive Charge detonations on Champions reset Rocket Jump's cooldown.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow Duration\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[22,20,18,16,14],\"cooldownBurn\":\"22/20/18/16/14\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[60,110,160,210,260],[1,1.5,2,2.5,3],[60,60,60,60,60],[350,350,350,350,350],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/110/160/210/260\",\"1/1.5/2/2.5/3\",\"60\",\"350\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[900,900,900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"TristanaW.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":384,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"TristanaE\",\"name\":\"Explosive Charge\",\"description\":\"When Tristana kills a unit, her cannonballs burst into shrapnel, dealing damage to surrounding enemies. Can be activated to place a bomb on a target enemy that explodes after a short duration dealing damage to surrounding units.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Enemies explode when slain by Tristana's basic attacks, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> Magic Damage to nearby enemies.<br><br><span class=\\\"colorFF9900\\\">Active: </span>Places a bomb on an enemy or turret that explodes after 4 seconds, dealing {{ e4 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Physical Damage. Each attack and ability charges the bomb's damage by +{{ e6 }}%.<br><br>At 4 charges, the bomb explodes immediately. The detonation radius is twice as large if used on a turret.\",\"leveltip\":{\"label\":[\"Passive Explosion Damage\",\"Base Charge Damage\",\"Mana Cost\",\"Bonus Attack Damage\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ cost }} -> {{ costNL }}\",\"{{ e5 }}% -> {{ e5NL }}%\"]},\"maxrank\":5,\"cooldown\":[16,15.5,15,14.5,14],\"cooldownBurn\":\"16/15.5/15/14.5/14\",\"cost\":[70,75,80,85,90],\"costBurn\":\"70/75/80/85/90\",\"effect\":[null,[50,75,100,125,150],[4,4,4,4,4],[300,300,300,300,300],[60,70,80,90,100],[50,65,80,95,110],[30,30,30,30,30],[500,500,500,500,500],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/75/100/125/150\",\"4\",\"300\",\"60/70/80/90/100\",\"50/65/80/95/110\",\"30\",\"500\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.25,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[550,550,550,550,550],\"rangeBurn\":\"550\",\"image\":{\"full\":\"TristanaE.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":432,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"TristanaR\",\"name\":\"Buster Shot\",\"description\":\"Tristana loads a massive cannonball into her weapon and fires it at an enemy unit. This deals Magic Damage and knocks the target back. If the target is carrying the Explosive Charge bomb, the bomb detonation radius is doubled.\",\"tooltip\":\"Tristana fires a massive cannonball at an enemy unit. This deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Magic Damage and knocks surrounding units back {{ e2 }} distance.\",\"leveltip\":{\"label\":[\"Damage\",\"Knockback Distance\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\" {{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[100,85,70],\"cooldownBurn\":\"100/85/70\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[300,400,500],[600,800,1000],[200,200,200],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"300/400/500\",\"600/800/1000\",\"200\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[550,550,550],\"rangeBurn\":\"550\",\"image\":{\"full\":\"TristanaR.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":0,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Trundle\":{\"id\":48,\"key\":\"Trundle\",\"name\":\"Trundle\",\"title\":\"the Troll King\",\"spells\":[{\"id\":\"TrundleTrollSmash\",\"name\":\"Chomp\",\"description\":\"Trundle bites his opponent, dealing damage, briefly slowing and sapping some of their Attack Damage.\",\"tooltip\":\"Trundle lunges at his opponent with his next basic attack, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage and briefly slowing his target.<br><br>This attack increases Trundle's Attack Damage by {{ e3 }} for {{ e6 }} seconds, with his opponent losing half of this amount.\",\"leveltip\":{\"label\":[\"Damage\",\"Attack Damage\",\"Attack Damage Scaling\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[4,4,4,4,4],\"cooldownBurn\":\"4\",\"cost\":[30,30,30,30,30],\"costBurn\":\"30\",\"effect\":[null,[20,40,60,80,100],[100,105,110,115,120],[20,25,30,35,40],[-10,-12.5,-15,-17.5,-20],[7,7,7,7,7],[8,8,8,8,8],[0.75,0.75,0.75,0.75,0.75],[0.1,0.1,0.1,0.1,0.1],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"20/40/60/80/100\",\"100/105/110/115/120\",\"20/25/30/35/40\",\"-10/-12.5/-15/-17.5/-20\",\"7\",\"8\",\"0.75\",\"0.1\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":[0.8,0.9,1,1.1,1.2],\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[300,300,300,300,300],\"rangeBurn\":\"300\",\"image\":{\"full\":\"TrundleTrollSmash.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":48,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"trundledesecrate\",\"name\":\"Frozen Domain\",\"description\":\"Trundle turns target location into his domain, gaining Attack Speed, Movement Speed, and increased healing from all sources while on it.\",\"tooltip\":\"Trundle coats target location with ice for {{ e4 }} seconds, gaining {{ e1 }}% Movement Speed, {{ e2 }}% Attack Speed and {{ e3 }}% increased healing and regeneration from all sources.\",\"leveltip\":{\"label\":[\"Movement Speed\",\"Attack Speed\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[15,15,15,15,15],\"cooldownBurn\":\"15\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[20,25,30,35,40],[20,35,50,65,80],[20,20,20,20,20],[8,8,8,8,8],[775,775,775,775,775],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"20/25/30/35/40\",\"20/35/50/65/80\",\"20\",\"8\",\"775\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[750,750,750,750,750],\"rangeBurn\":\"750\",\"image\":{\"full\":\"trundledesecrate.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":96,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"TrundleCircle\",\"name\":\"Pillar of Ice\",\"description\":\"Trundle creates an ice pillar at target location, becoming impassable terrain and slowing all nearby enemy units.\",\"tooltip\":\"Trundle creates an icy pillar at target location for {{ e1 }} seconds, becoming impassable terrain and slowing all nearby enemy units by {{ e2 }}%.\",\"leveltip\":{\"label\":[\"Cooldown\",\"Slow\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[22,20,18,16,14],\"cooldownBurn\":\"22/20/18/16/14\",\"cost\":[75,75,75,75,75],\"costBurn\":\"75\",\"effect\":[null,[6,6,6,6,6],[30,35,40,45,50],[360,360,360,360,360],[225,225,225,225,225],[150,150,150,150,150],[225,225,225,225,225],[400,400,400,400,400],[60,60,60,60,60],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"6\",\"30/35/40/45/50\",\"360\",\"225\",\"150\",\"225\",\"400\",\"60\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"TrundleCircle.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":144,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"TrundlePain\",\"name\":\"Subjugate\",\"description\":\"Trundle immediately steals a percent of his target's Health, Armor and Magic Resistance. Over the next 4 seconds the amount of Health, Armor, and Magic Resistance stolen is doubled.\",\"tooltip\":\"Trundle drains {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span>% of an enemy champion's maximum Health as Magic Damage and {{ e2 }}% of their Armor and Magic Resist, half immediately and half over {{ e3 }} seconds. The Armor and Magic Resist is returned {{ e3 }} seconds after the drain ends.\",\"leveltip\":{\"label\":[\"Health Drain\",\"Cooldown\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[110,90,70],\"cooldownBurn\":\"110/90/70\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[20,27.5,35],[40,40,40],[4,4,4],[4,4,4],[8,8,8],[0.5,0.5,0.5],[5,5,5],[0.02,0.02,0.02],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"20/27.5/35\",\"40\",\"4\",\"4\",\"8\",\"0.5\",\"5\",\"0.02\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.02,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"TrundlePain.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":192,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Tryndamere\":{\"id\":23,\"key\":\"Tryndamere\",\"name\":\"Tryndamere\",\"title\":\"the Barbarian King\",\"spells\":[{\"id\":\"TryndamereQ\",\"name\":\"Bloodlust\",\"description\":\"Tryndamere thrives on the thrills of combat, increasing his Attack Damage as he is more and more wounded. He can cast Bloodlust to consume his Fury and heal himself.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Tryndamere thirsts for blood, gaining {{ e1 }} Attack Damage plus {{ e2 }} per 1% Health missing.<br><br><span class=\\\"colorFF9900\\\">Active: </span>Tryndamere consumes his Fury, restoring {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Health, plus {{ e4 }} <span class=\\\"color99FF99\\\"> (+{{ f2 }}) </span>Health per Fury consumed.\",\"leveltip\":{\"label\":[\"Attack Damage\",\"Attack Damage per Health % Missing\",\"Heal\",\"Heal Per Fury\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ e4 }} -> {{ e4NL }}\"]},\"maxrank\":5,\"cooldown\":[12,12,12,12,12],\"cooldownBurn\":\"12\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[5,10,15,20,25],[0.15,0.2,0.25,0.3,0.35],[30,40,50,60,70],[0.5,0.95,1.4,1.85,2.3],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"5/10/15/20/25\",\"0.15/0.2/0.25/0.3/0.35\",\"30/40/50/60/70\",\"0.5/0.95/1.4/1.85/2.3\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[400,400,400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"TryndamereQ.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":240,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"TryndamereW\",\"name\":\"Mocking Shout\",\"description\":\"Tryndamere lets out an insulting cry, decreasing surrounding champions' Attack Damage. Enemies with their backs turned to Tryndamere also have their Movement Speed reduced.\",\"tooltip\":\"Decreases surrounding champions' Attack Damage by {{ e1 }} for {{ e3 }} seconds, and enemies with their backs turned also have their Movement Speed reduced by {{ e2 }}% for {{ e4 }} seconds.\",\"leveltip\":{\"label\":[\"Attack Damage Reduction\",\"Movement Speed Reduction\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[14,14,14,14,14],\"cooldownBurn\":\"14\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[20,35,50,65,80],[30,37.5,45,52.5,60],[4,4,4,4,4],[4,4,4,4,4],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"20/35/50/65/80\",\"30/37.5/45/52.5/60\",\"4\",\"4\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[850,850,850,850,850],\"rangeBurn\":\"850\",\"image\":{\"full\":\"TryndamereW.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":288,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"TryndamereE\",\"name\":\"Spinning Slash\",\"description\":\"Tryndamere slices toward a target unit, dealing damage to enemies in his path.\",\"tooltip\":\"Tryndamere spins through his enemies, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> physical damage to enemies in his path and generating Fury.<br><br>Spinning Slash's cooldown is reduced by {{ e2 }} second whenever Tryndamere critically strikes. This reduction is increased to {{ e3 }} seconds against champions.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[13,12,11,10,9],\"cooldownBurn\":\"13/12/11/10/9\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[70,100,130,160,190],[1,1,1,1,1],[2,2,2,2,2],[2,2,2,2,2],[5,5,5,5,5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/100/130/160/190\",\"1\",\"2\",\"2\",\"5\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"TryndamereE.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":336,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"UndyingRage\",\"name\":\"Undying Rage\",\"description\":\"Tryndamere's lust for battle becomes so strong that he is unable to die, no matter how wounded he becomes.\",\"tooltip\":\"Tryndamere becomes completely immune to death for {{ e3 }} seconds, refusing to be reduced below {{ e2 }} Health and instantly gaining {{ e1 }} Fury.\",\"leveltip\":{\"label\":[\"Fury Gained\",\"Minimum Health\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[110,100,90],\"cooldownBurn\":\"110/100/90\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[50,75,100],[30,50,70],[5,5,5],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"50/75/100\",\"30/50/70\",\"5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"UndyingRage.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":384,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"TwistedFate\":{\"id\":4,\"key\":\"TwistedFate\",\"name\":\"Twisted Fate\",\"title\":\"the Card Master\",\"spells\":[{\"id\":\"WildCards\",\"name\":\"Wild Cards\",\"description\":\"Twisted Fate throws three cards, dealing damage to each enemy unit they pass through.\",\"tooltip\":\"Throws three cards that deal {{ e1 }} <scaleAP>(+{{ a1 }})</scaleAP> Magic Damage to each enemy unit they pass through.\",\"leveltip\":{\"label\":[\"Damage\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[60,70,80,90,100],\"costBurn\":\"60/70/80/90/100\",\"effect\":[null,[60,105,150,195,240],[40,55,70,85,100],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/105/150/195/240\",\"40/55/70/85/100\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.65,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[10000,10000,10000,10000,10000],\"rangeBurn\":\"10000\",\"image\":{\"full\":\"WildCards.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":432,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"PickACard\",\"name\":\"Pick A Card\",\"description\":\"Twisted Fate chooses a magic card from his deck, and uses that for his next attack, causing bonus effects.\",\"tooltip\":\"Cast once to shuffle the deck and again to choose your card, enhancing your next attack.<br><br><span class=\\\"color4455FF\\\">Blue Card</span> deals {{ e1 }} <scaleAD>(+{{ a2 }})</scaleAD> <scaleAP>(+{{ a1 }})</scaleAP> Magic Damage and restores {{ e6 }} Mana.<br><br><span class=\\\"colorFF2222\\\">Red Card</span> deals {{ e4 }} <scaleAD>(+{{ a2 }})</scaleAD> <scaleAP>(+{{ a1 }})</scaleAP> Magic Damage to units around the target and slows their Movement Speed by {{ e2 }}% for 2.5 seconds.<br><br><span class=\\\"colorFFD700\\\">Gold Card</span> deals {{ e5 }}<scaleAD> (+{{ a2 }})</scaleAD> <scaleAP>(+{{ a1 }})</scaleAP> Magic Damage and stuns for {{ e3 }} seconds.\",\"leveltip\":{\"label\":[\"Blue Card Damage\",\"Blue Card Mana Restore\",\"Red Card Damage\",\"Red Card Slow %\",\"Gold Card Damage\",\"Gold Card Stun Duration\",\"Mana Cost\"],\"effect\":[\" {{ e1 }} -> {{ e1NL }}\",\"{{ e6 }} -> {{ e6NL }}\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ e5 }} -> {{ e5NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[40,55,70,85,100],\"costBurn\":\"40/55/70/85/100\",\"effect\":[null,[40,60,80,100,120],[30,35,40,45,50],[1,1.25,1.5,1.75,2],[30,45,60,75,90],[15,22.5,30,37.5,45],[50,75,100,125,150],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"40/60/80/100/120\",\"30/35/40/45/50\",\"1/1.25/1.5/1.75/2\",\"30/45/60/75/90\",\"15/22.5/30/37.5/45\",\"50/75/100/125/150\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"},{\"link\":\"attackdamage\",\"coeff\":1,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"},{\"link\":\"attackdamage\",\"coeff\":1,\"key\":\"a2\"},{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[200,200,200,200,200],\"rangeBurn\":\"200\",\"image\":{\"full\":\"PickACard.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":0,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"CardmasterStack\",\"name\":\"Stacked Deck\",\"description\":\"Every 4 attacks, Twisted Fate deals bonus damage. In addition, his Attack Speed is increased.\",\"tooltip\":\"<spellPassive>Passive:</spellPassive> Every 4 attacks, Twisted Fate deals an additional {{ e1 }} <scaleAP>(+{{ a1 }})</scaleAP> Magic Damage.<br><br>In addition, his Attack Speed is increased by {{ e3 }}%.\",\"leveltip\":{\"label\":[\"Bonus Damage\",\"Attack Speed Increase\"],\"effect\":[\" {{ e1 }} -> {{ e1NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[55,80,105,130,155],[0,0,0,0,0],[10,15,20,25,30],[4,4,4,4,4],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"55/80/105/130/155\",\"0\",\"10/15/20/25/30\",\"4\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\"\",\"maxammo\":\"-1\",\"range\":[0,0,0,0,0],\"rangeBurn\":\"0\",\"image\":{\"full\":\"CardmasterStack.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":48,\"y\":144,\"w\":48,\"h\":48}},{\"id\":\"Destiny\",\"name\":\"Destiny\",\"description\":\"Twisted Fate predicts the fortunes of his foes, revealing all enemy champions and enabling the use of Gate, which teleports Twisted Fate to any target location in 1.5 seconds.\",\"tooltip\":\"Grants <span class=\\\"coloree91d7\\\">True Sight</span> all enemy champions on the map for {{ e1 }} seconds.<br><br>While Destiny is active, Twisted Fate can teleport up to {{ e4 }} units away in 1.5 seconds.\",\"leveltip\":{\"label\":[\"Duration\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":3,\"cooldown\":[180,150,120],\"cooldownBurn\":\"180/150/120\",\"cost\":[150,125,100],\"costBurn\":\"150/125/100\",\"effect\":[null,[6,8,10],[180,150,120],[150,125,100],[5500,5500,5500],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"6/8/10\",\"180/150/120\",\"150/125/100\",\"5500\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[5500,5500,5500],\"rangeBurn\":\"5500\",\"image\":{\"full\":\"Destiny.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":96,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Twitch\":{\"id\":29,\"key\":\"Twitch\",\"name\":\"Twitch\",\"title\":\"the Plague Rat\",\"spells\":[{\"id\":\"TwitchHideInShadows\",\"name\":\"Ambush\",\"description\":\"Twitch becomes Camouflaged for a short duration and gains Movement Speed. When leaving Camouflage, Twitch gains Attack Speed for a short duration.<br><br>When an enemy champion with Deadly Venom dies, Ambush's cooldown is reset.\",\"tooltip\":\"Twitch becomes <span class=\\\"colorcd90ee\\\">Camouflaged</span> and gains {{ e3 }}% Movement Speed for {{ e2 }} seconds. This bonus triples when he is nearby an enemy champion that cannot see him. <br><br>Twitch gains {{ e1 }}% Attack Speed for {{ e6 }} seconds after exiting Ambush. Ambush resets when a champion dies while affected by <span class=\\\"color3ca126\\\">Deadly Venom.</span><br><br><font color='#cd90ee'size='16'><u>Stealth - Camouflage:</u></span> <font color='#8C8C8C'size='16'>Twitch is hidden from view while enemy champions remain outside his detection radius. Attacking or casting spells ends Camouflage.</span>\",\"leveltip\":{\"label\":[\"Camouflage Duration\",\"Attack Speed\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ e1 }}% -> {{ e1NL }}%\"]},\"maxrank\":5,\"cooldown\":[16,16,16,16,16],\"cooldownBurn\":\"16\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[30,35,40,45,50],[10,11,12,13,14],[10,10,10,10,10],[1,1,1,1,1],[1,1,1,1,1],[5,5,5,5,5],[3,3,3,3,3],[500,500,500,500,500],[1000,1000,1000,1000,1000],[30,30,30,30,30]],\"effectBurn\":[null,\"30/35/40/45/50\",\"10/11/12/13/14\",\"10\",\"1\",\"1\",\"5\",\"3\",\"500\",\"1000\",\"30\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[20,20,20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"TwitchHideInShadows.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":144,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"TwitchVenomCask\",\"name\":\"Venom Cask\",\"description\":\"Twitch hurls a cask of venom that explodes in an area, slowing targets and applying deadly venom to the target.\",\"tooltip\":\"Twitch hurls a cask that adds a stack of <span class=\\\"color3ca126\\\">Deadly Venom</span> to all enemies struck and leaves behind a toxic cloud that persists for {{ e3 }} seconds.<br><br>Enemies that remain within the cloud have {{ e2 }}% reduced Movement Speed and receive an additional stack of <span class=\\\"color3ca126\\\">Deadly Venom</span> each second.\",\"leveltip\":{\"label\":[\"Slow Amount\",\"Cooldown\"],\"effect\":[\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[13,12,11,10,9],\"cooldownBurn\":\"13/12/11/10/9\",\"cost\":[70,70,70,70,70],\"costBurn\":\"70\",\"effect\":[null,[2,2,2,2,2],[25,30,35,40,45],[3,3,3,3,3],[300,300,300,300,300],[3.5,3.5,3.5,3.5,3.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"2\",\"25/30/35/40/45\",\"3\",\"300\",\"3.5\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[950,950,950,950,950],\"rangeBurn\":\"950\",\"image\":{\"full\":\"TwitchVenomCask.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":192,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"TwitchExpunge\",\"name\":\"Contaminate\",\"description\":\"Twitch wreaks further havoc on poisoned enemies with a blast of his vile diseases.\",\"tooltip\":\"Deals {{ e2 }} physical damage plus {{ e1 }}<span class=\\\"color99FF99\\\"> (+{{ a1 }})</span> <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> per stack of <span class=\\\"color3ca126\\\">Deadly Venom</span> to all nearby enemies affected by <span class=\\\"color3ca126\\\">Deadly Venom.</span> <span class=\\\"colora3554e\\\">(Max Stack Damage: {{ f2 }})</span>\",\"leveltip\":{\"label\":[\"Base Damage\",\"Damage per Stack\",\"Mana Cost\",\"Cooldown\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ e1 }} -> {{ e1NL }}\",\"{{ cost }} -> {{ costNL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[12,11,10,9,8],\"cooldownBurn\":\"12/11/10/9/8\",\"cost\":[50,60,70,80,90],\"costBurn\":\"50/60/70/80/90\",\"effect\":[null,[15,20,25,30,35],[20,35,50,65,80],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"15/20/25/30/35\",\"20/35/50/65/80\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.2,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1200,1200,1200,1200,1200],\"rangeBurn\":\"1200\",\"image\":{\"full\":\"TwitchExpunge.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":240,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"TwitchFullAutomatic\",\"name\":\"Spray and Pray\",\"description\":\"Twitch unleashes the full power of his crossbow, shooting bolts over a great distance that pierce all enemies caught in their path.\",\"tooltip\":\"For {{ e2 }} seconds Twitch gains {{ e5 }} Attack Range and {{ e1 }} Bonus Attack Damage.<br><br>For the duration, his basic attacks become piercing bolts that deal {{ e3 }}% less damage to subsequent targets, down to a minimum of {{ e4 }}% damage.<br><br><font color='#cd90ee'size='16'><u>Stealth - Camouflage:</u></span> <font color='#8C8C8C'size='16'>Activating Spray and Pray does not end Camouflage.\",\"leveltip\":{\"label\":[\"Attack Damage Bonus\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":3,\"cooldown\":[90,90,90],\"cooldownBurn\":\"90\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[20,30,40],[5,5,5],[20,20,20],[40,40,40],[300,300,300],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"20/30/40\",\"5\",\"20\",\"40\",\"300\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1200,1200,1200],\"rangeBurn\":\"1200\",\"image\":{\"full\":\"TwitchFullAutomatic.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":288,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Udyr\":{\"id\":77,\"key\":\"Udyr\",\"name\":\"Udyr\",\"title\":\"the Spirit Walker\",\"spells\":[{\"id\":\"UdyrTigerStance\",\"name\":\"Tiger Stance\",\"description\":\"Tiger Stance: Activation - Udyr's Attack Speed is increased for a few seconds. Persistent Effect - Udyr's first attack and every third attack after will deal a high amount of damage over 2 seconds.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Persistent Effect: </span>Udyr's first attack and every third attack after will perform a Tiger Strike, dealing bonus {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f2 }})</span> physical damage over {{ e5 }} seconds. <br><br>If a target is affected by Tiger Strike, a new application of Tiger Strike will deal the remaining damage instantly.<br><br><span class=\\\"colorFF9900\\\">Activation:</span> Increases Attack Speed by {{ e2 }}% for {{ e6 }} seconds.<br><br><rules><span class=\\\"color8c8c8c\\\">Udyr's stance mana costs decrease by 1 each time he levels up.</span></rules>\",\"leveltip\":{\"label\":[\"Tiger Strike Damage\",\"Attack Speed\",\"Tiger Strike Scaling\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ e4 }}% -> {{ e4NL }}%\"]},\"maxrank\":5,\"cooldown\":[6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[45,45,45,45,45],\"costBurn\":\"45\",\"effect\":[null,[30,60,90,120,150],[30,40,50,60,70],[0.15,0.15,0.15,0.15,0.15],[120,130,140,150,160],[2,2,2,2,2],[5,5,5,5,5],[0.5,0.5,0.5,0.5,0.5],[0.25,0.25,0.25,0.25,0.25],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30/60/90/120/150\",\"30/40/50/60/70\",\"0.15\",\"120/130/140/150/160\",\"2\",\"5\",\"0.5\",\"0.25\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":[1.2,1.3,1.4,1.5,1.6],\"key\":\"f2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"UdyrTigerStance.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":336,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"UdyrTurtleStance\",\"name\":\"Turtle Stance\",\"description\":\"Turtle Stance: Activation - Udyr gains a temporary shield that absorbs damage. Persistent Effect - Udyr's first attack and every third attack after heals him for 2.5% of his maximum health.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Persistent Effect: </span>Udyr's first attack and every third attack after heals him for <span class=\\\"colorFF3300\\\">(+{{ f1 }})</span> ({{ e4 }}% of his maximum health), increasing by 1% for every percent of Udyr's missing health.<br><br><span class=\\\"colorFF9900\\\">Activation: </span>Gains a shield that absorbs {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> damage for {{ e3 }} seconds.<br><br><rules><span class=\\\"color8c8c8c\\\">Udyr's stance mana costs decrease by 1 each time he levels up.</span></rules>\",\"leveltip\":{\"label\":[\"Shield Amount\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":5,\"cooldown\":[6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[45,45,45,45,45],\"costBurn\":\"45\",\"effect\":[null,[60,95,130,165,200],[0,0,0,0,0],[5,5,5,5,5],[2.5,2.5,2.5,2.5,2.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/95/130/165/200\",\"0\",\"5\",\"2.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"UdyrTurtleStance.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":384,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"UdyrBearStance\",\"name\":\"Bear Stance\",\"description\":\"Bear Stance: Activation - Udyr increases Movement Speed for a short duration. Persistent Effect - Udyr's basic attacks stun his target for 1 second. This effect cannot occur on the same target for several seconds.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Persistent Effect: </span>Basic attacks stun the target for {{ e3 }} second. This effect cannot occur on the same target again for {{ e4 }} seconds.<br><br><span class=\\\"colorFF9900\\\">Activation: </span>Increases Movement Speed by {{ e1 }}% and ignores unit collision for {{ e2 }} seconds.<br><br><rules><span class=\\\"color8c8c8c\\\">Udyr's stance mana costs decrease by 1 each time he levels up.</span></rules>\",\"leveltip\":{\"label\":[\"Movement Speed Increase\",\"Movement Speed Duration\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[45,45,45,45,45],\"costBurn\":\"45\",\"effect\":[null,[15,20,25,30,35],[2,2.25,2.5,2.75,3],[1,1,1,1,1],[5,5,5,5,5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"15/20/25/30/35\",\"2/2.25/2.5/2.75/3\",\"1\",\"5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"UdyrBearStance.png\",\"sprite\":\"spell11.png\",\"group\":\"spell\",\"x\":432,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"UdyrPhoenixStance\",\"name\":\"Phoenix Stance\",\"description\":\"Phoenix Stance: Activation - Udyr unleashes pulsing waves of fire, dealing damage to nearby enemies. Persistent Effect - Udyr's first attack and every third attack after engulfs enemies in front of him with flames.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Persistent Effect: </span>Udyr's first attack and every third attack after burns enemies in front of him, dealing {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.<br><br><span class=\\\"colorFF9900\\\">Activation: </span>Unleashes pulsing waves of fire, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage each second to nearby enemies for {{ e5 }} seconds.<br><br><rules><span class=\\\"color8c8c8c\\\">Udyr's stance mana costs decrease by 1 each time he levels up.</span></rules>\",\"leveltip\":{\"label\":[\"Pulse Damage\",\"Flame Damage\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[45,45,45,45,45],\"costBurn\":\"45\",\"effect\":[null,[10,20,30,40,50],[40,80,120,160,200],[16,24,32,40,48],[1,1,1,1,1],[4,4,4,4,4],[325,325,325,325,325],[400,400,400,400,400],[3,3,3,3,3],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"10/20/30/40/50\",\"40/80/120/160/200\",\"16/24/32/40/48\",\"1\",\"4\",\"325\",\"400\",\"3\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.45,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.25,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[325,325,325,325,325],\"rangeBurn\":\"325\",\"image\":{\"full\":\"UdyrPhoenixStance.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":0,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Urgot\":{\"id\":6,\"key\":\"Urgot\",\"name\":\"Urgot\",\"title\":\"the Headsman's Pride\",\"spells\":[{\"id\":\"UrgotHeatseekingMissile\",\"name\":\"Acid Hunter\",\"description\":\"Urgot fires an Acid Hunter missile that collides with the first enemy it hits, slowing the target if he has his Terror Capacitor up. Acid Hunter missile-locks on enemies affected by Noxian Corrosive Charge.\",\"tooltip\":\"Urgot fires a missile toward the cursor that deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage. If Acid Hunter kills a unit half its mana cost is refunded.<br><br>Missile-lock can be achieved by holding the cursor over a target afflicted by Noxian Corrosive Charge.\",\"leveltip\":{\"label\":[\"Damage\"],\"effect\":[\" {{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":5,\"cooldown\":[2,2,2,2,2],\"cooldownBurn\":\"2\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[10,40,70,100,130],[0.5,0.5,0.5,0.5,0.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"10/40/70/100/130\",\"0.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":0.85,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"UrgotHeatseekingMissile.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":48,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"UrgotTerrorCapacitorActive2\",\"name\":\"Terror Capacitor\",\"description\":\"Urgot charges up his capacitor to gain a shield. While the shield is active, Urgot gains slowing attacks.\",\"tooltip\":\"Urgot charges up his terror capacitor to gain a shield that absorbs {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> plus {{ e4 }}% of his maximum mana <span class=\\\"color44DDFF\\\">(+{{ f1 }})</span> damage for 5 seconds. While the shield is active, Urgot's attacks and missiles slow targets by {{ e2 }}%. \",\"leveltip\":{\"label\":[\"Damage Absorption \",\"Slow % \",\"Cooldown\",\"Mana Cost\"],\"effect\":[\" {{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}% \",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[16,15,14,13,12],\"cooldownBurn\":\"16/15/14/13/12\",\"cost\":[55,60,65,70,75],\"costBurn\":\"55/60/65/70/75\",\"effect\":[null,[60,100,140,180,220],[20,25,30,35,40],[4,6,8,10,12],[8,8,8,8,8],[1.5,1.5,1.5,1.5,1.5],[5,5,5,5,5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/100/140/180/220\",\"20/25/30/35/40\",\"4/6/8/10/12\",\"8\",\"1.5\",\"5\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"UrgotTerrorCapacitorActive2.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":96,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"UrgotPlasmaGrenade\",\"name\":\"Noxian Corrosive Charge\",\"description\":\"Urgot launches a corrosive charge that damages enemies in an area and reduces their Armor.\",\"tooltip\":\"Urgot launches a Corrosive Charge at a target location. Enemies afflicted by the charge have their Armor reduced by {{ e2 }}% and take {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage over {{ e3 }} seconds.<br><br>Acid Hunter is able to missile-lock onto targets affected by Noxian Corrosive Charge.\",\"leveltip\":{\"label\":[\"Damage\",\"Armor Reduction\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\" {{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[15,14,13,12,11],\"cooldownBurn\":\"15/14/13/12/11\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[75,130,185,240,295],[12,14,16,18,20],[5,5,5,5,5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"75/130/185/240/295\",\"12/14/16/18/20\",\"5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.6,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[900,900,900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"UrgotPlasmaGrenade.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":144,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"UrgotSwap2\",\"name\":\"Hyper-Kinetic Position Reverser\",\"description\":\"Urgot charges up his Hyper-Kinetic Position Reverser, swapping positions with the target and terrifying nearby enemies for 1.5 seconds. His target is suppressed for the duration of the channel. He gains Damage Reduction during and after the swap.\",\"tooltip\":\"Urgot targets an enemy champion and channels his Hyper-Kinetic Position Reverser for 1 second, swapping locations with his target afterward and terrifying nearby enemies for 1.5 seconds. His target is suppressed for the duration of the channel.<br><br>Urgot takes {{ e2 }}% reduced damage for {{ e4 }} seconds beginning at the start of the channel, and his target is slowed by 40% for 3 seconds after being swapped.\",\"leveltip\":{\"label\":[\"Swap Range\",\"Damage Reduction\",\"Cooldown \"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,100,80],\"cooldownBurn\":\"120/100/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[550,700,850],[30,40,50],[200,250,300],[5,5,5],[1.5,1.5,1.5],[40,40,40],[3,3,3],[1,1,1],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"550/700/850\",\"30/40/50\",\"200/250/300\",\"5\",\"1.5\",\"40\",\"3\",\"1\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[550,700,850],\"rangeBurn\":\"550/700/850\",\"image\":{\"full\":\"UrgotSwap2.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":192,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Varus\":{\"id\":110,\"key\":\"Varus\",\"name\":\"Varus\",\"title\":\"the Arrow of Retribution\",\"spells\":[{\"id\":\"VarusQ\",\"name\":\"Piercing Arrow\",\"description\":\"Varus readies and then fires a powerful shot that gains extra range and damage the longer he spends preparing to fire.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">First Cast:</span> Varus starts drawing back his next shot, gradually increasing its range and damage. While preparing to shoot Varus' Movement Speed is slowed by {{ e7 }}%. After {{ e5 }} seconds, Piercing Arrow fails but refunds {{ e4 }}% of its Mana cost.<br><br><span class=\\\"colorFF9900\\\">Second Cast:</span> Varus fires, dealing {{ e1 }}<span class=\\\"colorFF8C00\\\"> (+{{ a1 }})</span> to {{ e2 }}<span class=\\\"colorFF8C00\\\"> (+{{ a2 }})</span> physical damage, reduced by {{ e3 }}% per enemy hit (minimum {{ e9 }}%).<br><br>Piercing Arrow's cooldown is reduced by {{ f1 }} seconds if the arrow detonates <span class=\\\"colorB829FF\\\">Blight</span> stacks from at least one enemy champion.\",\"leveltip\":{\"label\":[\"Max Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[20,18,16,14,12],\"cooldownBurn\":\"20/18/16/14/12\",\"cost\":[70,75,80,85,90],\"costBurn\":\"70/75/80/85/90\",\"effect\":[null,[10,47,84,120,157],[15,70,125,180,235],[15,15,15,15,15],[50,50,50,50,50],[4,4,4,4,4],[4,4,4,4,4],[20,20,20,20,20],[0,0,0,0,0],[33,33,33,33,33],[0,0,0,0,0]],\"effectBurn\":[null,\"10/47/84/120/157\",\"15/70/125/180/235\",\"15\",\"50\",\"4\",\"4\",\"20\",\"0\",\"33\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1,\"key\":\"a1\"},{\"link\":\"attackdamage\",\"coeff\":1.5,\"key\":\"a2\"},{\"link\":\"attackdamage\",\"coeff\":1,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[925,925,925,925,925],\"rangeBurn\":\"925\",\"image\":{\"full\":\"VarusQ.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":240,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"VarusW\",\"name\":\"Blighted Quiver\",\"description\":\"Varus' basic attacks deal bonus magic damage and apply Blight. Varus' other abilities detonate Blight, dealing magic damage based on the target's maximum Health.\",\"tooltip\":\"Varus' basic attacks deal {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> bonus magic damage and apply <span class=\\\"colorB829FF\\\">Blight</span> for {{ e3 }} seconds (stacks {{ e4 }} times).<br><br>Varus' other abilities detonate <span class=\\\"colorB829FF\\\">Blight</span>, dealing magic damage equal to {{ e2 }}% <span class=\\\"color99FF99\\\">(+{{ a2 }}%)</span> of the target's maximum Health per stack. Max damage vs. monsters: {{ e5 }} per stack.\",\"leveltip\":{\"label\":[\"Initial Damage\",\"Max Health Damage\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[10,14,18,22,26],[2,2.75,3.5,4.25,5],[6,6,6,6,6],[3,3,3,3,3],[120,120,120,120,120],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"10/14/18/22/26\",\"2/2.75/3.5/4.25/5\",\"6\",\"3\",\"120\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.25,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.02,\"key\":\"a2\"}],\"costType\":\"Passive\",\"maxammo\":\"-1\",\"range\":[750,750,750,750,750],\"rangeBurn\":\"750\",\"image\":{\"full\":\"VarusW.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":288,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"Passive\"},{\"id\":\"VarusE\",\"name\":\"Hail of Arrows\",\"description\":\"Varus fires a hail of arrows that deal physical damage and desecrate the ground. Desecrated ground slows enemies' Movement Speed and reduces their self healing and regeneration. \",\"tooltip\":\"Varus fires a hail of arrows that deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage and desecrates the ground for {{ e3 }} seconds.<br><br>Desecrated Ground slows enemy Movement Speed by {{ e2 }}% and reduces healing effects by {{ e4 }}%.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[18,16,14,12,10],\"cooldownBurn\":\"18/16/14/12/10\",\"cost\":[80,80,80,80,80],\"costBurn\":\"80\",\"effect\":[null,[65,100,135,170,205],[25,30,35,40,45],[4,4,4,4,4],[40,40,40,40,40],[300,300,300,300,300],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"65/100/135/170/205\",\"25/30/35/40/45\",\"4\",\"40\",\"300\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[925,925,925,925,925],\"rangeBurn\":\"925\",\"image\":{\"full\":\"VarusE.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":336,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"VarusR\",\"name\":\"Chain of Corruption\",\"description\":\"Varus flings out a damaging tendril of corruption that immobilizes the first enemy champion hit and then spreads towards nearby uninfected champions, immobilizing them too on contact. \",\"tooltip\":\"Varus flings out a tendril of corruption that deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and immobilizes the first enemy champion hit for {{ e2 }} seconds.<br><br>The corruption then spreads towards nearby uninfected enemy champions. If it reaches them, they take the same damage and are also immobilized. Immobilized units gain {{ e4 }} <span class=\\\"colorB829FF\\\">Blight</span> stacks over the duration.\",\"leveltip\":{\"label\":[\"Damage \",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[110,90,70],\"cooldownBurn\":\"110/90/70\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[100,175,250],[2,2,2],[650,650,650],[3,3,3],[0.5,0.5,0.5],[600,600,600],[1.75,1.75,1.75],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"100/175/250\",\"2\",\"650\",\"3\",\"0.5\",\"600\",\"1.75\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1200,1200,1200],\"rangeBurn\":\"1200\",\"image\":{\"full\":\"VarusR.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":384,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Vayne\":{\"id\":67,\"key\":\"Vayne\",\"name\":\"Vayne\",\"title\":\"the Night Hunter\",\"spells\":[{\"id\":\"VayneTumble\",\"name\":\"Tumble\",\"description\":\"Vayne tumbles, maneuvering to carefully place her next shot. Her next attack deals bonus damage.\",\"tooltip\":\"Rolls a short distance. The next basic attack within {{ e1 }} seconds deals <span class=\\\"colorFF8C00\\\">{{ f1 }}</span> bonus physical damage, equal to {{ e2 }}% of total Attack Damage.\",\"leveltip\":{\"label\":[\"Bonus Damage\",\"Cooldown\"],\"effect\":[\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[6,5,4,3,2],\"cooldownBurn\":\"6/5/4/3/2\",\"cost\":[30,30,30,30,30],\"costBurn\":\"30\",\"effect\":[null,[7,7,7,7,7],[30,35,40,45,50],[4,4,4,4,4],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"7\",\"30/35/40/45/50\",\"4\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":[0.3,0.35,0.4,0.45,0.5],\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[300,300,300,300,300],\"rangeBurn\":\"300\",\"image\":{\"full\":\"VayneTumble.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":432,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"VayneSilveredBolts\",\"name\":\"Silver Bolts\",\"description\":\"Vayne tips her bolts with a rare metal, toxic to evil things. The third consecutive attack or ability against the same target deals a percentage of the target's maximum Health as bonus true damage. (Max: 200 damage vs. Monsters)\",\"tooltip\":\"Every third consecutive attack or ability against an enemy deals an additional {{ e1 }}% of the enemy's maximum Health as true damage.<br><br>Deals a maximum of 200 damage vs. Monsters and a minimum of {{ e2 }} damage vs. all targets.\",\"leveltip\":{\"label\":[\"Max Health Damage\",\"Minimum Damage\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[6,7.5,9,10.5,12],[40,60,80,100,120],[200,200,200,200,200],[3.5,3.5,3.5,3.5,3.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"6/7.5/9/10.5/12\",\"40/60/80/100/120\",\"200\",\"3.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\"Passive\",\"maxammo\":\"-1\",\"range\":[750,750,750,750,750],\"rangeBurn\":\"750\",\"image\":{\"full\":\"VayneSilveredBolts.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":0,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"Passive\"},{\"id\":\"VayneCondemn\",\"name\":\"Condemn\",\"description\":\"Vayne draws a heavy crossbow from her back, and fires a huge bolt at her target, knocking them back and dealing damage. If they collide with terrain, they are impaled, dealing bonus damage and stunning them.\",\"tooltip\":\"Fires a bolt that knocks back target enemy and deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage. Enemies that collide with terrain take {{ e2 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> additional physical damage and are stunned for {{ e3 }} seconds.\",\"leveltip\":{\"label\":[\"Initial Damage\",\"Collision Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[20,18,16,14,12],\"cooldownBurn\":\"20/18/16/14/12\",\"cost\":[90,90,90,90,90],\"costBurn\":\"90\",\"effect\":[null,[45,80,115,150,185],[45,80,115,150,185],[1.5,1.5,1.5,1.5,1.5],[475,475,475,475,475],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"45/80/115/150/185\",\"45/80/115/150/185\",\"1.5\",\"475\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.5,\"key\":\"f1\"},{\"link\":\"bonusattackdamage\",\"coeff\":0.5,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[710,710,710,710,710],\"rangeBurn\":\"710\",\"image\":{\"full\":\"VayneCondemn.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":48,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"VayneInquisition\",\"name\":\"Final Hour\",\"description\":\"Readying herself for an epic confrontation, Vayne gains increased Attack Damage, Invisibility during Tumble, and triple the bonus Movement Speed from Night Hunter.\",\"tooltip\":\"Gains {{ e1 }} Bonus Attack Damage for {{ e2 }} seconds. While active, Tumble grants <span class=\\\"color91d7ee\\\">Invisibility</span> for {{ e3 }} second, and Night Hunter's bonus Movement Speed is increased to {{ e4 }}.<br><br><u><span class=\\\"size16 color91d7ee\\\">16\",\"leveltip\":{\"label\":[\"Bonus Attack Damage\",\"Duration\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":3,\"cooldown\":[100,85,70],\"cooldownBurn\":\"100/85/70\",\"cost\":[80,80,80],\"costBurn\":\"80\",\"effect\":[null,[30,50,70],[8,10,12],[1,1,1],[90,90,90],[100,85,70],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"30/50/70\",\"8/10/12\",\"1\",\"90\",\"100/85/70\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1,1,1],\"rangeBurn\":\"1\",\"image\":{\"full\":\"VayneInquisition.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":96,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Veigar\":{\"id\":45,\"key\":\"Veigar\",\"name\":\"Veigar\",\"title\":\"the Tiny Master of Evil\",\"spells\":[{\"id\":\"VeigarBalefulStrike\",\"name\":\"Baleful Strike\",\"description\":\"Veigar unleashes a bolt of dark energy that deals magic damage to the first two enemies hit. Units killed by this bolt grant Veigar some ability power permanently.\",\"tooltip\":\"Unleashes a bolt of dark energy, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to the first two enemies hit.<br><br>Killing a unit with this also grants Veigar a stack of <span class=\\\"colorFFF673\\\">Phenomenal Evil</span>. Large minions and large monsters grant two.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[7,6.5,6,5.5,5],\"cooldownBurn\":\"7/6.5/6/5.5/5\",\"cost\":[40,45,50,55,60],\"costBurn\":\"40/45/50/55/60\",\"effect\":[null,[70,110,150,190,230],[1,1,1,1,1],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/110/150/190/230\",\"1\",\"1\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[900,900,900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"VeigarBalefulStrike.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":144,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"VeigarDarkMatter\",\"name\":\"Dark Matter\",\"description\":\"Veigar calls a great mass of dark matter to fall from the sky to the target location, dealing magic damage when it lands.\",\"tooltip\":\"After 1.2 seconds, dark matter falls from the sky to the target location, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,9.5,9,8.5,8],\"cooldownBurn\":\"10/9.5/9/8.5/8\",\"cost\":[60,65,70,75,80],\"costBurn\":\"60/65/70/75/80\",\"effect\":[null,[100,150,200,250,300],[1.2,1.2,1.2,1.2,1.2],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"100/150/200/250/300\",\"1.2\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[900,900,900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"VeigarDarkMatter.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":192,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"VeigarEventHorizon\",\"name\":\"Event Horizon\",\"description\":\"After a brief delay Veigar twists the edges of space around the target location for 3 seconds, stunning enemies who pass through the perimeter.\",\"tooltip\":\"After a {{ e2 }} second delay, Veigar twists the edges of space around the target location for 3 seconds, forming a pentagon of walls. Enemies who attempt to pass through the perimeter are stopped and stunned for {{ e1 }} seconds.\",\"leveltip\":{\"label\":[\"Stun Duration\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[18,17,16,15,14],\"cooldownBurn\":\"18/17/16/15/14\",\"cost\":[70,75,80,85,90],\"costBurn\":\"70/75/80/85/90\",\"effect\":[null,[1.5,1.75,2,2.25,2.5],[0.5,0.5,0.5,0.5,0.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"1.5/1.75/2/2.25/2.5\",\"0.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[725,725,725,725,725],\"rangeBurn\":\"725\",\"image\":{\"full\":\"VeigarEventHorizon.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":240,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"VeigarR\",\"name\":\"Primordial Burst\",\"description\":\"Blasts target enemy champion, dealing a large amount of magic damage, increasing based on the target's missing health.\",\"tooltip\":\"Blasts the target with primal magic to deal {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> to {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage, increasing based on the <span class=\\\"colorCC3300\\\">target's missing health</span>.<br><br>Damage is maximized against enemies below 33% health.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,100,80],\"cooldownBurn\":\"120/100/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[175,250,325],[350,500,650],[1,1,1],[2,2,2],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"175/250/325\",\"350/500/650\",\"1\",\"2\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.75,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":1.5,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"VeigarR.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":288,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Velkoz\":{\"id\":161,\"key\":\"Velkoz\",\"name\":\"Vel'Koz\",\"title\":\"the Eye of the Void\",\"spells\":[{\"id\":\"VelkozQ\",\"name\":\"Plasma Fission\",\"description\":\"Vel'Koz shoots a bolt of plasma that splits in two on reactivation or upon hitting an enemy. The bolt slows and damages on hit.\",\"tooltip\":\"Vel'Koz shoots a plasma bolt that deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Magic Damage and applies a slow of {{ e5 }}% that decays over {{ e4 }} second(s).<br><br>Upon reactivation or upon hitting an enemy, the bolt splits at a 90 degree angle.<br><br>Killing a unit with this refunds {{ e2 }}% of the <span class=\\\"color44DDFF\\\">Mana Cost</span>.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow Duration\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[7,7,7,7,7],\"cooldownBurn\":\"7\",\"cost\":[40,45,50,55,60],\"costBurn\":\"40/45/50/55/60\",\"effect\":[null,[80,120,160,200,240],[50,50,50,50,50],[0.25,0.25,0.25,0.25,0.25],[1,1.4,1.8,2.2,2.6],[70,70,70,70,70],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/120/160/200/240\",\"50\",\"0.25\",\"1/1.4/1.8/2.2/2.6\",\"70\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1050,1050,1050,1050,1050],\"rangeBurn\":\"1050\",\"image\":{\"full\":\"VelkozQ.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":336,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"VelkozW\",\"name\":\"Void Rift\",\"description\":\"Vel'Koz opens a rift to the void that deals an initial burst of damage, then explodes for a second burst of damage after a delay.\",\"tooltip\":\"Vel'Koz opens a rift to the void that deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Magic Damage. After a delay, it deals an additional {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> Magic Damage.<br><br>Void Rift has a 2 second cooldown between casts.\",\"leveltip\":{\"label\":[\"Initial Damage\",\"Secondary Damage\",\"Ammo Recharge Time\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ ammorechargetime }} -> {{ ammorechargetimeNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[1.5,1.5,1.5,1.5,1.5],\"cooldownBurn\":\"1.5\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[30,50,70,90,110],[45,75,105,135,165],[100,100,100,100,100],[0,0,0,0,0],[0.25,0.25,0.25,0.25,0.25],[0.5,0.5,0.5,0.5,0.5],[88,88,88,88,88],[500,500,500,500,500],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30/50/70/90/110\",\"45/75/105/135/165\",\"100\",\"0\",\"0.25\",\"0.5\",\"88\",\"500\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.15,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.25,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"2\",\"range\":[1050,1050,1050,1050,1050],\"rangeBurn\":\"1050\",\"image\":{\"full\":\"VelkozW.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":384,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"VelkozE\",\"name\":\"Tectonic Disruption\",\"description\":\"Vel'Koz causes an area to explode, knocking up enemies, and knocking close enemies slightly away.\",\"tooltip\":\"Vel'Koz disrupts a nearby area which, after a delay, deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Magic Damage and knocks up enemies hit for {{ e2 }} seconds.<br><br>Any enemies hit that are close to Vel'Koz will be pushed slightly in the direction the ability was cast.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[16,15,14,13,12],\"cooldownBurn\":\"16/15/14/13/12\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[70,100,130,160,190],[0.75,0.75,0.75,0.75,0.75],[0,0,0,0,0],[225,225,225,225,225],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/100/130/160/190\",\"0.75\",\"0\",\"225\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[810,810,810,810,810],\"rangeBurn\":\"810\",\"image\":{\"full\":\"VelkozE.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":432,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"VelkozR\",\"name\":\"Life Form Disintegration Ray\",\"description\":\"Vel'Koz unleashes a channelled beam that follows the cursor for 2.5 seconds that deals magic damage. Organic Deconstruction Researches enemy champions causing them to take true damage instead.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Deconstructing enemy Champions <span class=\\\"colorFFFFFF\\\">Researches</span> them for {{ e2 }} seconds. Basic attacks and all abilities refresh Research.<br><br><span class=\\\"colorFF9900\\\">Active: </span>Vel'Koz channels a ray of energy that follows the cursor for 2.5 seconds, dealing Magic Damage up to a total of {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> and slowing units hit by {{ e3 }}%.<br><br>Enemies that stay in the beam will periodically gain <span class=\\\"colorFFFFFF\\\">Organic Deconstruction</span> stacks. Deals True Damage instead of Magic against <span class=\\\"colorFFFFFF\\\">Researched</span> units.\",\"leveltip\":{\"label\":[\"Total Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,100,80],\"cooldownBurn\":\"120/100/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[450,625,800],[7,7,7],[20,20,20],[40,40,40],[175,175,175],[7,7,7],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"450/625/800\",\"7\",\"20\",\"40\",\"175\",\"7\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1.25,\"key\":\"a1\"}],\"costType\":\"Mana\",\"maxammo\":\"-1\",\"range\":[1575,1575,1575],\"rangeBurn\":\"1575\",\"image\":{\"full\":\"VelkozR.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":0,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"Mana\"}]},\"Vi\":{\"id\":254,\"key\":\"Vi\",\"name\":\"Vi\",\"title\":\"the Piltover Enforcer\",\"spells\":[{\"id\":\"ViQ\",\"name\":\"Vault Breaker\",\"description\":\"Vi charges her gauntlets and unleashes a vault shattering punch, carrying her forward. Enemies she hits are knocked back and receive a stack of Denting Blows.\",\"tooltip\":\"Charges a powerful punch that carries Vi forward.<br><br><span class=\\\"colorFF9900\\\">First Cast:</span> Slows Movement Speed by {{ e4 }}% while increasing damage and dash range over 1.25 seconds.<br><br><span class=\\\"colorFF9900\\\">Second Cast:</span> Dashes forward dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> to {{ f2 }} <span class=\\\"colorFF8C00\\\">(+{{ f1 }})</span> physical damage and applying Denting Blows to all enemies hit (deals {{ e3 }}% damage to minions and monsters). Stops upon colliding with an enemy champion, knocking it back.\",\"leveltip\":{\"label\":[\"Min Damage\",\"Max Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\" {{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[16,14,12,10,8],\"cooldownBurn\":\"16/14/12/10/8\",\"cost\":[50,60,70,80,90],\"costBurn\":\"50/60/70/80/90\",\"effect\":[null,[100,150,200,250,300],[2,2,2,2,2],[75,75,75,75,75],[15,15,15,15,15],[6,6,6,6,6],[5,5,5,5,5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"100/150/200/250/300\",\"2\",\"75\",\"15\",\"6\",\"5\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.8,\"key\":\"a1\"},{\"link\":\"bonusattackdamage\",\"coeff\":1.4,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[250,250,250,250,250],\"rangeBurn\":\"250\",\"image\":{\"full\":\"ViQ.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":48,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"ViW\",\"name\":\"Denting Blows\",\"description\":\"Vi's punches break her opponent's Armor, dealing bonus damage and granting her Attack Speed.\",\"tooltip\":\"Every 3rd attack on the same target deals an additional {{ e1 }}% <span class=\\\"colorFF8C00\\\">(+{{ f1 }}%)</span> of the target's maximum Health as physical damage, reduces its Armor by {{ e3 }}% and grants Vi {{ e2 }}% Attack Speed for {{ e4 }} seconds (max {{ e5 }} damage vs. monsters).\",\"leveltip\":{\"label\":[\"Max Health Damage\",\"Attack Speed\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[4,5.5,7,8.5,10],[30,35,40,45,50],[20,20,20,20,20],[4,4,4,4,4],[300,300,300,300,300],[4,4,4,4,4],[0.0286,0.0286,0.0286,0.0286,0.0286],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"4/5.5/7/8.5/10\",\"30/35/40/45/50\",\"20\",\"4\",\"300\",\"4\",\"0.03\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"@special.viw\",\"coeff\":35,\"key\":\"f1\"}],\"costType\":\"Passive\",\"maxammo\":\"-1\",\"range\":[750,750,750,750,750],\"rangeBurn\":\"750\",\"image\":{\"full\":\"ViW.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":96,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"Passive\"},{\"id\":\"ViE\",\"name\":\"Excessive Force\",\"description\":\"Vi's next attack blasts through her target, dealing damage to enemies behind it.\",\"tooltip\":\"Causes next basic attack to deal {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ f3 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> physical damage to the target and enemies behind it.<br><br></span>Vi charges a new punch every <span class=\\\"colorFFFFFF\\\">{{ f1 }}</span> seconds and can hold 2 charges at once.\",\"leveltip\":{\"label\":[\"Damage\",\"Charge Time\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ f1 }} -> {{ f2 }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[1,1,1,1,1],\"cooldownBurn\":\"1\",\"cost\":[40,45,50,55,60],\"costBurn\":\"40/45/50/55/60\",\"effect\":[null,[10,30,50,70,90],[1.15,1.15,1.15,1.15,1.15],[50,50,50,50,50],[1.5,1.5,1.5,1.5,1.5],[6,6,6,6,6],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"10/30/50/70/90\",\"1.15\",\"50\",\"1.5\",\"6\",\"1\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1.15,\"key\":\"f3\"},{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"},{\"link\":\"@text\",\"coeff\":[14,12.5,11,9.5,8],\"key\":\"f1\"},{\"link\":\"@text\",\"coeff\":[14,12.5,11,9.5,8],\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"2\",\"range\":[400,400,400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"ViE.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":144,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"ViR\",\"name\":\"Assault and Battery\",\"description\":\"Vi runs down an enemy, knocking aside anyone in the way. When she reaches her target she knocks it into the air, jumps after it, and slams it back into the ground.\",\"tooltip\":\"Targets an enemy champion and chases it down, knocking it up for 1.25 seconds, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage.<br><br>While charging, Vi cannot be stopped and has <span class=\\\"coloree91d7\\\">True Sight</span> of the target. Any enemies in the way are knocked aside and dealt 75% of the damage.\",\"leveltip\":{\"label\":[\"Total Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\" {{ cost }} -> {{ costNL }}\"]},\"maxrank\":3,\"cooldown\":[150,115,80],\"cooldownBurn\":\"150/115/80\",\"cost\":[100,125,150],\"costBurn\":\"100/125/150\",\"effect\":[null,[150,300,450],[0.75,0.75,0.75],[1.25,1.25,1.25],[4,4,4],[1,1.5,2],[800,800,800],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"150/300/450\",\"0.75\",\"1.25\",\"4\",\"1/1.5/2\",\"800\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1.4,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[800,800,800],\"rangeBurn\":\"800\",\"image\":{\"full\":\"ViR.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":192,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Viktor\":{\"id\":112,\"key\":\"Viktor\",\"name\":\"Viktor\",\"title\":\"the Machine Herald\",\"spells\":[{\"id\":\"ViktorPowerTransfer\",\"name\":\"Siphon Power\",\"description\":\"Viktor blasts an enemy unit dealing magic damage, gaining a shield and empowering his next basic attack.<br><br>Augment: Viktor gains bonus Movement Speed after casting.\",\"tooltip\":\"Viktor blasts an enemy unit, dealing {{ e1 }}<span class=\\\"color99FF99\\\"> (+{{ a1 }})</span> magic damage while granting Viktor a shield that absorbs up to <span class=\\\"color44DDFF\\\">{{ f1 }}</span> <span class=\\\"color99FF99\\\">(+{{ f2 }})</span> damage over the next {{ e2 }} seconds.<br><br>Viktor's next basic attack deals {{ e5 }}<span class=\\\"color99FF99\\\"> (+{{ a2 }})</span> bonus magic damage.<br><br><span class=\\\"colorFFCC00\\\">Augment - Turbocharge:</span> Viktor gains {{ e3 }}% Movement Speed for {{ e2 }} seconds.\",\"leveltip\":{\"label\":[\"Damage (missile)\",\"Damage (attack)\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e5 }} -> {{ e5NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[8,7,6,5,4],\"cooldownBurn\":\"8/7/6/5/4\",\"cost\":[45,50,55,60,65],\"costBurn\":\"45/50/55/60/65\",\"effect\":[null,[60,80,100,120,140],[2.5,2.5,2.5,2.5,2.5],[30,30,30,30,30],[0,0,0,0,0],[20,40,60,80,100],[0.08,0.08,0.08,0.08,0.08],[0.15,0.15,0.15,0.15,0.15],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/80/100/120/140\",\"2.5\",\"30\",\"0\",\"20/40/60/80/100\",\"0.08\",\"0.15\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"ViktorPowerTransfer.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":240,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"ViktorGravitonField\",\"name\":\"Gravity Field\",\"description\":\"Viktor conjures a heavy gravitational field that slows enemies in its radius. Enemies who stay within the device for too long are stunned.<br><br>Augment: Enemies stunned by Gravity Field are dragged to the center.\",\"tooltip\":\"Viktor deploys a gravitational imprisonment device for {{ e3 }} seconds, slowing enemy units by {{ e1 }}% and adding a stack every {{ e4 }} seconds. At {{ e5 }} stacks the target is stunned for {{ e2 }} seconds.<br><br><span class=\\\"colorFFCC00\\\">Augment - Implosion:</span> Enemies stunned by Gravity Field are dragged to the center.\",\"leveltip\":{\"label\":[\"Slow\",\"Cooldown\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[17,16,15,14,13],\"cooldownBurn\":\"17/16/15/14/13\",\"cost\":[65,65,65,65,65],\"costBurn\":\"65\",\"effect\":[null,[28,32,36,40,44],[1.5,1.5,1.5,1.5,1.5],[4,4,4,4,4],[0.5,0.5,0.5,0.5,0.5],[3,3,3,3,3],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"28/32/36/40/44\",\"1.5\",\"4\",\"0.5\",\"3\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[700,700,700,700,700],\"rangeBurn\":\"700\",\"image\":{\"full\":\"ViktorGravitonField.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":288,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"ViktorDeathRay\",\"name\":\"Death Ray\",\"description\":\"Viktor uses his robotic arm to fire a chaos beam that cuts across the field in a line, dealing damage to all enemies in its path.<br><br>Augment: An explosion follows the Death Ray's wake, dealing magic damage.\",\"tooltip\":\"Viktor uses his robotic arm to fire a chaos beam that cuts across the field in a line, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to every enemy in its path.<br><br><span class=\\\"colorFFCC00\\\">Augment - Aftershock:</span> An explosion follows the Death Ray's wake, dealing {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage.\",\"leveltip\":{\"label\":[\"Damage (Death Ray)\",\"Damage (Aftershock)\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[13,12,11,10,9],\"cooldownBurn\":\"13/12/11/10/9\",\"cost\":[70,80,90,100,110],\"costBurn\":\"70/80/90/100/110\",\"effect\":[null,[70,110,150,190,230],[20,60,100,140,180],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/110/150/190/230\",\"20/60/100/140/180\",\"1\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[525,525,525,525,525],\"rangeBurn\":\"525\",\"image\":{\"full\":\"ViktorDeathRay.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":336,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"ViktorChaosStorm\",\"name\":\"Chaos Storm\",\"description\":\"Viktor conjures a singularity on the field which deals magic damage and interrupts enemy channels. The singularity then periodically does magic damage to all nearby enemies. Viktor can redirect the singularity.<br><br>Augment: The Chaos Storm moves faster.\",\"tooltip\":\"Viktor conjures a singularity at target location, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and interrupting enemy channels.<br><br>Viktor can redirect the singularity for {{ e3 }} seconds, which will discharge {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage every {{ e4 }} seconds to nearby enemies. The singularity will move slower as it attempts to move farther away from Viktor.<br><br><span class=\\\"colorFFCC00\\\">Augment - Velocity:</span> Chaos Storm moves {{ e0 }}% faster.\",\"leveltip\":{\"label\":[\"Damage (impact)\",\"Damage (discharge)\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,110,100],\"cooldownBurn\":\"120/110/100\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[100,175,250],[150,250,350],[6.5,6.5,6.5],[2,2,2],[3,3,3],[400,400,400],[200,200,200],[300,300,300],[900,900,900],[0.2,0.2,0.2]],\"effectBurn\":[null,\"100/175/250\",\"150/250/350\",\"6.5\",\"2\",\"3\",\"400\",\"200\",\"300\",\"900\",\"0.2\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[700,700,700],\"rangeBurn\":\"700\",\"image\":{\"full\":\"ViktorChaosStorm.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":384,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Vladimir\":{\"id\":8,\"key\":\"Vladimir\",\"name\":\"Vladimir\",\"title\":\"the Crimson Reaper\",\"spells\":[{\"id\":\"VladimirQ\",\"name\":\"Transfusion\",\"description\":\"Vladimir steals life from the target enemy. When Vladimir's resource is full, Transfusion will benefit from massively increased damage and healing for a brief time.\",\"tooltip\":\"Vladimir drains the lifeforce of his target, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and healing himself for {{ e2 }}<span class=\\\"color99ff99\\\"> (+{{ a2 }})</span> health. After casting Transfusion twice, Vladimir gains <span class=\\\"colorFFB3B3\\\">Crimson Rush</span> the next time it becomes available to cast.<br><br><span class=\\\"colorFFB3B3\\\">Crimson Rush:</span> Vladimir is briefly hasted and for the next {{ e8 }} seconds Transfusion deals {{ e7 }}% increased damage and heals for an additional <span class=\\\"colorFFFFFF\\\">{{ f7 }}</span> plus {{ e5 }}% <span class=\\\"color99FF99\\\">(+{{ f6 }}%)</span> of his missing health (empowered heal has {{ e9 }}% effectiveness against minions). \",\"leveltip\":{\"label\":[\"Damage\",\"Heal\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[9,8,7,6,5],\"cooldownBurn\":\"9/8/7/6/5\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[80,100,120,140,160],[20,25,30,35,40],[0,0,0,0,0],[0,0,0,0,0],[5,5,5,5,5],[0.04,0.04,0.04,0.04,0.04],[85,85,85,85,85],[2.5,2.5,2.5,2.5,2.5],[35,35,35,35,35],[0,0,0,0,0]],\"effectBurn\":[null,\"80/100/120/140/160\",\"20/25/30/35/40\",\"0\",\"0\",\"5\",\"0.04\",\"85\",\"2.5\",\"35\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.35,\"key\":\"a2\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"VladimirQ.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":432,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"VladimirSanguinePool\",\"name\":\"Sanguine Pool\",\"description\":\"Vladimir sinks into a pool of blood, becoming untargetable for 2 seconds. Additionally, enemies on the pool are slowed and Vladimir siphons life from them.\",\"tooltip\":\"Vladimir sinks into a pool of blood for 2 seconds, gaining a brief haste and becoming untargetable while slowing enemies above him by {{ e3 }}%.<br><br>Vladimir deals {{ e1 }} <span class=\\\"colorCC3300\\\">(+{{ f1 }}) [{{ e4 }}% of bonus Health]</span> magic damage over the duration and heals himself for {{ e5 }}% of that amount.<br><br>Sanguine Pool can be cast while charging Tides of Blood.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[28,25,22,19,16],\"cooldownBurn\":\"28/25/22/19/16\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[80,135,190,245,300],[20,20,20,20,20],[40,40,40,40,40],[10,10,10,10,10],[15,15,15,15,15],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/135/190/245/300\",\"20\",\"40\",\"10\",\"15\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonushealth\",\"coeff\":0.15,\"key\":\"f1\"}],\"costType\":\"% of current Health\",\"maxammo\":\"-1\",\"range\":[350,350,350,350,350],\"rangeBurn\":\"350\",\"image\":{\"full\":\"VladimirSanguinePool.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":0,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ e2 }}% of current Health\"},{\"id\":\"VladimirE\",\"name\":\"Tides of Blood\",\"description\":\"Vladimir pays his own health to charge up a reservoir of blood which, when released, deals damage in the area around him but can be blocked by enemy units.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">First Cast: </span>Vladimir charges up a reservoir of blood, spending up to <span class=\\\"colorCC3300\\\">{{ e2 }}% of his max Health ({{ f3 }})</span> to increase this spell's damage. While at full charge, Vladimir is slowed.<br><br><span class=\\\"colorFF9900\\\">Second Cast: </span>Vladimir unleashes a nova of blood at surrounding enemies which deals between {{ e3 }}<span class=\\\"colorCC3300\\\"> (+{{ f2 }})</span><span class=\\\"color99FF99\\\"> (+{{ a1 }})</span> and {{ e0 }}<span class=\\\"colorCC3300\\\"> (+{{ f4 }})</span><span class=\\\"color99FF99\\\"> (+{{ a2 }})</span> magic damage and, at full charge, briefly slows by {{ e9 }}%. Targets hit block a portion of the nova.<br><br>Tides of Blood will release automatically if held for more than {{ e7 }} seconds.\",\"leveltip\":{\"label\":[\"Min Damage\",\"Max Damage\",\"Slow Amount\",\"Cooldown\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ e0 }} -> {{ e0NL }}\",\"{{ e9 }}% -> {{ e9NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[13,11,9,7,5],\"cooldownBurn\":\"13/11/9/7/5\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[0,0,0,0,0],[8,8,8,8,8],[30,45,60,75,90],[6,6,6,6,6],[150,150,150,150,150],[0,0,0,0,0],[1.5,1.5,1.5,1.5,1.5],[1,1,1,1,1],[40,45,50,55,60],[60,90,120,150,180]],\"effectBurn\":[null,\"0\",\"8\",\"30/45/60/75/90\",\"6\",\"150\",\"0\",\"1.5\",\"1\",\"40/45/50/55/60\",\"60/90/120/150/180\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.35,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":1,\"key\":\"a2\"}],\"costType\":\"% of max Health\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"VladimirE.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":48,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"Channeling Cost {{ e2 }}% of max Health\"},{\"id\":\"VladimirHemoplague\",\"name\":\"Hemoplague\",\"description\":\"Vladimir infects an area with a virulent plague. Affected enemies take increased damage for the duration. After a few seconds, Hemoplague deals magic damage to infected enemies and heals Vladimir for each enemy Champion hit.\",\"tooltip\":\"Vladimir infects enemies in a target area with a virulent plague, causing them to take {{ e2 }}% increased damage from all sources for {{ e4 }} seconds.<br><br>After {{ e4 }} seconds, Vladimir deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to all infected targets. If Hemoplague damages an enemy Champion, Vladimir heals himself for {{ f4 }} <span class=\\\"color99FF99\\\">(+{{ f3 }})</span>, plus {{ e5 }}% for each Champion beyond the first.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[150,135,120],\"cooldownBurn\":\"150/135/120\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[150,250,350],[10,10,10],[100,100,100],[4,4,4],[50,50,50],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"150/250/350\",\"10\",\"100\",\"4\",\"50\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[625,625,625],\"rangeBurn\":\"625\",\"image\":{\"full\":\"VladimirHemoplague.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":96,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Volibear\":{\"id\":106,\"key\":\"Volibear\",\"name\":\"Volibear\",\"title\":\"the Thunder's Roar\",\"spells\":[{\"id\":\"VolibearQ\",\"name\":\"Rolling Thunder\",\"description\":\"Volibear drops to all fours and runs faster. This bonus speed increases when chasing enemy champions. The first enemy he attacks is thrown backwards over Volibear.\",\"tooltip\":\"Volibear drops to all fours to hunt his enemies, gaining {{ e4 }}% Movement Speed for {{ e3 }} seconds. This bonus is enhanced to {{ e2 }}% Movement Speed while moving toward enemy champions.<br><br>Volibear's next attack during this time deals an additional {{ e1 }} physical damage and flings the target behind him.\",\"leveltip\":{\"label\":[\"Bonus Damage\",\"Enhanced Movement Speed\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[12,11,10,9,8],\"cooldownBurn\":\"12/11/10/9/8\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[30,60,90,120,150],[30,35,40,45,50],[4,4,4,4,4],[15,15,15,15,15],[400,400,400,400,400],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30/60/90/120/150\",\"30/35/40/45/50\",\"4\",\"15\",\"400\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[300,300,300,300,300],\"rangeBurn\":\"300\",\"image\":{\"full\":\"VolibearQ.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":144,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"VolibearW\",\"name\":\"Frenzy\",\"description\":\"Volibear's repeated attacks grant him additional Attack Speed. Once Volibear has repeatedly attacked three times, he can perform a vicious bite on his target which deals increased damage based on the target's missing Health.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Volibear gains {{ e1 }}% Attack Speed with each attack. Stacks up to {{ e2 }} times.<br><br><span class=\\\"colorFF9900\\\">Active: </span>When Volibear has {{ e2 }} stacks of Frenzy, he can bite an enemy to deal {{ e3 }} <span class=\\\"colorCC3300\\\">(+{{ f1 }} [{{ e4 }}% of bonus Health])</span> physical damage, increased by {{ e5 }}% for each {{ e7 }}% Health the target is missing. If Volibear bites a monster, Frenzy's cooldown is reduced by {{ e6 }}%.\",\"leveltip\":{\"label\":[\"Attack Speed Bonus\",\"Bite Damage\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ e3 }} -> {{ e3NL }}\"]},\"maxrank\":5,\"cooldown\":[18,18,18,18,18],\"cooldownBurn\":\"18\",\"cost\":[35,35,35,35,35],\"costBurn\":\"35\",\"effect\":[null,[4,8,12,16,20],[3,3,3,3,3],[60,110,160,210,260],[15,15,15,15,15],[1,1,1,1,1],[50,50,50,50,50],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"4/8/12/16/20\",\"3\",\"60/110/160/210/260\",\"15\",\"1\",\"50\",\"1\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonushealth\",\"coeff\":0.15,\"key\":\"f1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[350,350,350,350,350],\"rangeBurn\":\"350\",\"image\":{\"full\":\"VolibearW.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":192,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"VolibearE\",\"name\":\"Majestic Roar\",\"description\":\"Volibear lets out a powerful roar that damages and slows enemies. Minions and monsters are feared as well.\",\"tooltip\":\"Volibear deals {{ e2 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to nearby enemies and slows them by {{ e1 }}% for {{ e3 }} seconds.<br><br>Minions and monsters are feared as well.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow Amount \",\"Mana Cost \"],\"effect\":[\"{{ e2 }} -> {{ e2NL }}\",\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[11,11,11,11,11],\"cooldownBurn\":\"11\",\"cost\":[60,65,70,75,80],\"costBurn\":\"60/65/70/75/80\",\"effect\":[null,[30,35,40,45,50],[60,105,150,195,240],[3,3,3,3,3],[49,49,49,49,49],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30/35/40/45/50\",\"60/105/150/195/240\",\"3\",\"49\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[425,425,425,425,425],\"rangeBurn\":\"425\",\"image\":{\"full\":\"VolibearE.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":240,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"VolibearR\",\"name\":\"Thunder Claws\",\"description\":\"Volibear erupts with chain lightning, damaging a number nearby enemies. The power of the storm causes Volibear's attacks to blast his targets with lightning that bounces to other nearby enemies.\",\"tooltip\":\"Volibear erupts with chain lightning, striking up to {{ e4 }} nearby enemies with {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.<br><br>For the next {{ e3 }} seconds, the storm empowers Volibear, causing his basic attacks to deal {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> bonus magic damage that chains to {{ e4 }} nearby enemies.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[100,90,80],\"cooldownBurn\":\"100/90/80\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[75,115,155],[9,9,9],[12,12,12],[8,8,8],[0.08,0.08,0.08],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"75/115/155\",\"9\",\"12\",\"8\",\"0.08\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[500,500,500],\"rangeBurn\":\"500\",\"image\":{\"full\":\"VolibearR.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":288,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Warwick\":{\"id\":19,\"key\":\"Warwick\",\"name\":\"Warwick\",\"title\":\"the Uncaged Wrath of Zaun\",\"spells\":[{\"id\":\"WarwickQ\",\"name\":\"Jaws of the Beast\",\"description\":\"Warwick lunges forward and bites his target, dealing damage based on their maximum health and healing for damage dealt.\",\"tooltip\":\"Warwick lunges forward and bites his target. While the key is held down, he will <span class=\\\"colorFFEB7F\\\">attach</span> to the target then leap behind them.<br><br>On release, deals <span class=\\\"colorFF8C00\\\">{{ a1 }}</span><span class=\\\"color99FF99\\\">+{{ a2 }}</span> plus {{ e1 }}% of your target's maximum health as magic damage (applies On Hit effects). Heal {{ e3 }}% of damage dealt.<br><br><span class=\\\"size16 color8C8C8C\\\">16\",\"leveltip\":{\"label\":[\"Heal\",\"Percent Health Damage\",\"Cost\"],\"effect\":[\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[50,60,70,80,90],\"costBurn\":\"50/60/70/80/90\",\"effect\":[null,[6,7,8,9,10],[100,150,200,250,300],[30,40,50,60,70],[100,125,150,175,200],[450,450,450,450,450],[200,200,200,200,200],[300,300,300,300,300],[425,425,425,425,425],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"6/7/8/9/10\",\"100/150/200/250/300\",\"30/40/50/60/70\",\"100/125/150/175/200\",\"450\",\"200\",\"300\",\"425\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1.2,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.9,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[350,350,350,350,350],\"rangeBurn\":\"350\",\"image\":{\"full\":\"WarwickQ.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":336,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"WarwickW\",\"name\":\"Blood Hunt\",\"description\":\"Warwick senses enemies below 50% health, gaining movement speed toward and attack speed against them. When they fall below 20% health, he frenzies and these bonuses triple.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\"></span><span class=\\\"colorFF9900\\\">Passive: </span>Warwick gains {{ e2 }}% attack speed against enemies below 50% health. He also senses low health champions globally, moving {{ e1 }}% faster toward them when out of combat. These bonuses are tripled against enemies below 20% health.<br><br><span class=\\\"colorFF9900\\\">Active: </span>Warwick briefly senses all enemies. The nearest sensed champion is Blood Hunted for 8 seconds. Cast only while not in combat with a champion.<br><br>While no enemies are being hunted, Blood Hunt cools down twice as fast.\",\"leveltip\":{\"label\":[\"Movement Speed\",\"Attack Speed\",\"Cooldown\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[120,105,90,75,60],\"cooldownBurn\":\"120/105/90/75/60\",\"cost\":[70,70,70,70,70],\"costBurn\":\"70\",\"effect\":[null,[35,40,45,50,55],[50,65,80,95,110],[10,15,20,25,30],[80,90,100,110,120],[30,30,30,30,30],[8,8,8,8,8],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"35/40/45/50/55\",\"50/65/80/95/110\",\"10/15/20/25/30\",\"80/90/100/110/120\",\"30\",\"8\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"2\",\"range\":[4000,4000,4000,4000,4000],\"rangeBurn\":\"4000\",\"image\":{\"full\":\"WarwickW.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":384,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"WarwickE\",\"name\":\"Primal Howl\",\"description\":\"Warwick gains damage reduction for 2.5 seconds. At the end, or if re-activated, he howls, causing nearby enemies to flee for 1 second.\",\"tooltip\":\"Warwick gains {{ e1 }}% damage reduction for 2.5 seconds. At the end of the duration, or if activated again, Warwick howls, causing all nearby enemies to flee for {{ e3 }} second.\",\"leveltip\":{\"label\":[\"Damage Reduction\",\"Cooldown\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[15,14,13,12,11],\"cooldownBurn\":\"15/14/13/12/11\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[35,40,45,50,55],[2.75,2.75,2.75,2.75,2.75],[1,1,1,1,1],[1,1,1,1,1],[800,800,800,800,800],[400,400,400,400,400],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"35/40/45/50/55\",\"2.75\",\"1\",\"1\",\"800\",\"400\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[375,375,375,375,375],\"rangeBurn\":\"375\",\"image\":{\"full\":\"WarwickE.png\",\"sprite\":\"spell12.png\",\"group\":\"spell\",\"x\":432,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"WarwickR\",\"name\":\"Infinite Duress\",\"description\":\"Warwick leaps in a direction (scaling with his bonus movement speed), suppressing the first champion he collides with for 1.5 seconds.\",\"tooltip\":\"Warwick leaps 2.5 seconds worth of movement speed in a direction, suppressing the first champion he collides with for {{ e2 }} seconds. Deals {{ e7 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> magic damage and applies on-hit effects 3 times. Warwick heals for 100% of all damage he deals during Infinite Duress.\",\"leveltip\":{\"label\":[\"Base Damage\",\"Cooldown\"],\"effect\":[\"{{ e7 }} -> {{ e7NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[110,90,70],\"cooldownBurn\":\"110/90/70\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[150,250,350],[1.5,1.5,1.5],[30,30,30],[5,5,5],[20,20,20],[0,0,0],[175,350,525],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"150/250/350\",\"1.5\",\"30\",\"5\",\"20\",\"0\",\"175/350/525\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1.67,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[25000,25000,25000],\"rangeBurn\":\"25000\",\"image\":{\"full\":\"WarwickR.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":0,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Xayah\":{\"id\":498,\"key\":\"Xayah\",\"name\":\"Xayah\",\"title\":\"the Rebel\",\"spells\":[{\"id\":\"XayahQ\",\"name\":\"Double Daggers\",\"description\":\"Xayah throws two damaging daggers that also drop Feathers she can recall.\",\"tooltip\":\"Xayah throws two knives dealing {{ f1 }} <span class=\\\"colorFF9900\\\">(+{{ f2 }})</span> physical damage and leaving two <span class=\\\"colorC200E1\\\">Feathers</span>. Targets hit after the first take {{ e2 }}% damage.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[10,9,8,7,6],\"cooldownBurn\":\"10/9/8/7/6\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[80,120,160,200,240],[50,50,50,50,50],[0.334,0.334,0.334,0.334,0.334],[0.584,0.584,0.584,0.584,0.584],[3500,3500,3500,3500,3500],[3500,3500,3500,3500,3500],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/120/160/200/240\",\"50\",\"0.33\",\"0.58\",\"3500\",\"3500\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[400,400,400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"XayahQ.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":48,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"XayahW\",\"name\":\"Deadly Plumage\",\"description\":\"Xayah creates a storm of blades that increase her basic attack speed and damage while granting her movement speed if she attacks a champion.\",\"tooltip\":\"Xayah creates a storm of blades for {{ e2 }} seconds that grant her {{ e1 }}% attack speed and cause her basic attacks to strike an additional time for {{ e5 }}% damage. <br><br>If Deadly Plumage strikes an enemy champion, Xayah gains {{ e3 }}% movement speed for {{ e4 }} seconds.<br><br><span class=\\\"colore5c100\\\">If Rakan is nearby he will also gain the effects of Deadly Plumage.</span>\",\"leveltip\":{\"label\":[\"Attack Speed\",\"Cooldown\",\"Cost\"],\"effect\":[\"{{ e1 }}% -> {{ e1NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[16,15,14,13,12],\"cooldownBurn\":\"16/15/14/13/12\",\"cost\":[60,55,50,45,40],\"costBurn\":\"60/55/50/45/40\",\"effect\":[null,[30,35,40,45,50],[4,4,4,4,4],[30,30,30,30,30],[1.5,1.5,1.5,1.5,1.5],[20,20,20,20,20],[1000,1000,1000,1000,1000],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30/35/40/45/50\",\"4\",\"30\",\"1.5\",\"20\",\"1000\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"XayahW.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":96,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"XayahE\",\"name\":\"Bladecaller\",\"description\":\"Xayah calls back all her dropped Feathers dealing damage and rooting enemies.\",\"tooltip\":\"Xayah calls all <span class=\\\"colorC200E1\\\">Feathers</span> back to her, dealing {{ e1 }} <span class=\\\"colorFF9900\\\">(+{{ f3 }})</span> <span class=\\\"colorFF5802\\\">(+{{ f4 }})</span> physical damage to enemies they pass through (Increased by <span class=\\\"colorFF5802\\\">Critical Strike Chance</span>).<br><br>Hitting an enemy with {{ e5 }} <span class=\\\"colorC200E1\\\">Feathers</span> roots them for {{ e4 }} second.<br><br>Minions take {{ e2 }}% damage from Bladecaller.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[12,11,10,9,8],\"cooldownBurn\":\"12/11/10/9/8\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[50,60,70,80,90],[50,50,50,50,50],[0.1,0.1,0.1,0.1,0.1],[1,1,1,1,1],[3,3,3,3,3],[0.25,0.25,0.25,0.25,0.25],[0,0,0,0,0],[0.5,0.5,0.5,0.5,0.5],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"50/60/70/80/90\",\"50\",\"0.1\",\"1\",\"3\",\"0.25\",\"0\",\"0.5\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[2000,2000,2000,2000,2000],\"rangeBurn\":\"2000\",\"image\":{\"full\":\"XayahE.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":144,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"XayahR\",\"name\":\"Featherstorm\",\"description\":\"Xayah leaps into the air becoming untargetable and throwing out a fan of daggers, dropping Feathers she can recall.\",\"tooltip\":\"Xayah leaps into the air becoming untargetable. She then rains down daggers which deal {{ e1 }}<span class=\\\"colorFF9900\\\"> (+{{ a1 }})</span> physical damage and leave behind a line of <span class=\\\"colorC200E1\\\">Feathers</span>.<br><br>Xayah can move while in the air.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[140,125,110],\"cooldownBurn\":\"140/125/110\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[100,150,200],[1,1,1],[1.25,1.25,1.25],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"100/150/200\",\"1\",\"1.25\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[450,450,450],\"rangeBurn\":\"450\",\"image\":{\"full\":\"XayahR.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":192,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Xerath\":{\"id\":101,\"key\":\"Xerath\",\"name\":\"Xerath\",\"title\":\"the Magus Ascendant\",\"spells\":[{\"id\":\"XerathArcanopulseChargeUp\",\"name\":\"Arcanopulse\",\"description\":\"Fires a long-range beam of energy, dealing magic damage to all targets hit.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">First cast:</span> Xerath charges Arcanopulse, gradually decreasing his Movement Speed while increasing the spell's range.<br><br><span class=\\\"colorFF9900\\\">Second cast:</span> Xerath fires Arcanopulse, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to all enemies in a line.<br><br>While charging Arcanopulse, Xerath cannot attack or cast other spells. If Xerath does not fire the spell, half the Mana cost is refunded.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ ammorechargetime }} -> {{ ammorechargetimeNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[9,8,7,6,5],\"cooldownBurn\":\"9/8/7/6/5\",\"cost\":[80,90,100,110,120],\"costBurn\":\"80/90/100/110/120\",\"effect\":[null,[80,120,160,200,240],[4,4,4,4,4],[0.5,0.5,0.5,0.5,0.5],[145,145,145,145,145],[0.5,0.5,0.5,0.5,0.5],[-0.2,-0.2,-0.2,-0.2,-0.2],[0.1,0.1,0.1,0.1,0.1],[0.5,0.5,0.5,0.5,0.5],[1.5,1.5,1.5,1.5,1.5],[0,0,0,0,0]],\"effectBurn\":[null,\"80/120/160/200/240\",\"4\",\"0.5\",\"145\",\"0.5\",\"-0.2\",\"0.1\",\"0.5\",\"1.5\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.75,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"1\",\"range\":[750,750,750,750,750],\"rangeBurn\":\"750\",\"image\":{\"full\":\"XerathArcanopulseChargeUp.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":240,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"XerathArcaneBarrage2\",\"name\":\"Eye of Destruction\",\"description\":\"Calls down a barrage of arcane energy, slowing and dealing magic damage to all enemies in an area. Targets in the middle receive additional damage and a stronger slow.\",\"tooltip\":\"Xerath calls down a blast of arcane energy, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to all enemies within the target area, slowing them by {{ e8 }}% for {{ e4 }} seconds. Enemies in the center of the blast take {{ f1 }} <span class=\\\"color99FF99\\\">(+{{ f2 }})</span> magic damage and are slowed by {{ e3 }}%. This slow decays rapidly.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Slow Amount\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ ammorechargetime }} -> {{ ammorechargetimeNL }}\",\"{{ e3 }}% -> {{ e3NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[1,1,1,1,1],\"cooldownBurn\":\"1\",\"cost\":[70,80,90,100,110],\"costBurn\":\"70/80/90/100/110\",\"effect\":[null,[60,90,120,150,180],[2,2,2,2,2],[60,65,70,75,80],[2.5,2.5,2.5,2.5,2.5],[0.5,0.5,0.5,0.5,0.5],[50,50,50,50,50],[50,50,50,50,50],[10,10,10,10,10],[0.8,0.8,0.8,0.8,0.8],[-0.15,-0.15,-0.15,-0.15,-0.15]],\"effectBurn\":[null,\"60/90/120/150/180\",\"2\",\"60/65/70/75/80\",\"2.5\",\"0.5\",\"50\",\"50\",\"10\",\"0.8\",\"-0.15\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"1\",\"range\":[1100,1100,1100,1100,1100],\"rangeBurn\":\"1100\",\"image\":{\"full\":\"XerathArcaneBarrage2.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":288,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"XerathMageSpear\",\"name\":\"Shocking Orb\",\"description\":\"Deals magic damage to an enemy and stuns them.\",\"tooltip\":\"Xerath fires an orb of raw magic. The first enemy hit takes {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and is stunned for between {{ e8 }} and {{ e2 }} seconds. The stun duration lengthens based on how far the orb travels.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ ammorechargetime }} -> {{ ammorechargetimeNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[13,12.5,12,11.5,11],\"cooldownBurn\":\"13/12.5/12/11.5/11\",\"cost\":[60,65,70,75,80],\"costBurn\":\"60/65/70/75/80\",\"effect\":[null,[80,110,140,170,200],[2,2,2,2,2],[0.17,0.17,0.17,0.17,0.17],[1125,1125,1125,1125,1125],[0.5,0.5,0.5,0.5,0.5],[0,0,0,0,0],[0,0,0,0,0],[0.5,0.5,0.5,0.5,0.5],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"80/110/140/170/200\",\"2\",\"0.17\",\"1125\",\"0.5\",\"0\",\"0\",\"0.5\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.45,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"1\",\"range\":[1050,1050,1050,1050,1050],\"rangeBurn\":\"1050\",\"image\":{\"full\":\"XerathMageSpear.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":336,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"XerathLocusOfPower2\",\"name\":\"Rite of the Arcane\",\"description\":\"Xerath immobilizes himself and gains numerous long-range barrages.\",\"tooltip\":\"Xerath ascends to his true form, becoming rooted in place and gaining {{ e2 }} Arcane Barrages. This magic artillery deals {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to all enemies hit.<br><br>The root ends after {{ e1 }} seconds, when all shots have been fired or when manually deactivated by issuing a move command. If no barrages are fired, {{ e7 }}% of the cooldown is refunded.\",\"leveltip\":{\"label\":[\"Damage\",\"Number of Shots\",\"Range\",\"Cooldown\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ e5 }} -> {{ e5NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[130,115,100],\"cooldownBurn\":\"130/115/100\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[10,10,10],[3,4,5],[200,230,260],[200,200,200],[3200,4400,5600],[0.8,0.8,0.8],[50,50,50],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"10\",\"3/4/5\",\"200/230/260\",\"200\",\"3200/4400/5600\",\"0.8\",\"50\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.43,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[3200,4400,5600],\"rangeBurn\":\"3200/4400/5600\",\"image\":{\"full\":\"XerathLocusOfPower2.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":384,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"XinZhao\":{\"id\":5,\"key\":\"XinZhao\",\"name\":\"Xin Zhao\",\"title\":\"the Seneschal of Demacia\",\"spells\":[{\"id\":\"XenZhaoComboTarget\",\"name\":\"Three Talon Strike\",\"description\":\"Xin Zhao's next 3 standard attacks deal increased damage that reduce his other ability cooldowns, with the third attack knocking an opponent into the air.\",\"tooltip\":\"Xin Zhao's next 3 basic attacks deal {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a2 }})</span> physical damage and reduce his other abilities' cooldowns by 1 second. The final strike also knocks the target into the air for {{ e2 }} seconds.\",\"leveltip\":{\"label\":[\"Bonus Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[9,8,7,6,5],\"cooldownBurn\":\"9/8/7/6/5\",\"cost\":[30,30,30,30,30],\"costBurn\":\"30\",\"effect\":[null,[15,30,45,60,75],[0.75,0.75,0.75,0.75,0.75],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"15/30/45/60/75\",\"0.75\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1.2,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[375,375,375,375,375],\"rangeBurn\":\"375\",\"image\":{\"full\":\"XenZhaoComboTarget.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":432,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"XenZhaoBattleCry\",\"name\":\"Battle Cry\",\"description\":\"Xin Zhao passively will critically strike every third attack, healing on every third hit. This ability can be activated to attack faster.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Every third basic attack Xin Zhao critically strikes his target for {{ e7 }}% additional damage. On every third hit, he heals himself for {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> <span class=\\\"color99FF99\\\">(+{{ a1 }})</span>.<br><br><span class=\\\"colorFF9900\\\">Active: </span>Xin Zhao unleashes a battle cry, increasing his Attack Speed by {{ e2 }}% for {{ e3 }} seconds.\",\"leveltip\":{\"label\":[\"Heal Amount\",\"Bonus Damage\",\"Attack Speed\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e7 }}% -> {{ e7NL }}%\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[16,15,14,13,12],\"cooldownBurn\":\"16/15/14/13/12\",\"cost\":[40,40,40,40,40],\"costBurn\":\"40\",\"effect\":[null,[30,35,40,45,50],[40,45,50,55,60],[5,5,5,5,5],[1,1,1,1,1],[-0.75,-0.625,-0.5,-0.375,-0.25],[0.3,0.3,0.3,0.3,0.3],[25,37.5,50,62.5,75],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30/35/40/45/50\",\"40/45/50/55/60\",\"5\",\"1\",\"-0.75/-0.625/-0.5/-0.375/-0.25\",\"0.3\",\"25/37.5/50/62.5/75\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.4,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.4,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[20,20,20,20,20],\"rangeBurn\":\"20\",\"image\":{\"full\":\"XenZhaoBattleCry.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":0,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"XenZhaoSweep\",\"name\":\"Audacious Charge\",\"description\":\"Xin Zhao charges an enemy, dealing damage and slowing all enemies in the area.\",\"tooltip\":\"Xin Zhao charges and challenges an enemy. The charge deals {{ e1 }}<span class=\\\"color99FF99\\\"> (+{{ a1 }})</span> magic damage to all nearby enemies and slows them by {{ e2 }}% for {{ e4 }} seconds. \",\"leveltip\":{\"label\":[\"Damage\",\"Slow %\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\"]},\"maxrank\":5,\"cooldown\":[12,12,12,12,12],\"cooldownBurn\":\"12\",\"cost\":[60,60,60,60,60],\"costBurn\":\"60\",\"effect\":[null,[70,110,150,190,230],[25,30,35,40,45],[0,0,0,0,0],[2,2,2,2,2],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/110/150/190/230\",\"25/30/35/40/45\",\"0\",\"2\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"XenZhaoSweep.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":48,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"XenZhaoParry\",\"name\":\"Crescent Sweep\",\"description\":\"Xin Zhao deals damage to nearby enemies based on their current Health and knocks non-challenged targets back. Xin Zhao gains bonus Armor and Magic Resist based on number of champions hit.\",\"tooltip\":\"Xin Zhao unleashes a sweep around him that deals {{ e1 }}<span class=\\\"colorFF8C00\\\"> (+{{ a1 }})</span> plus {{ e2 }}% of target's current Health in physical damage and stuns enemies while knocking them back (max 600 vs minions and monsters). Xin Zhao gains {{ e3 }} Armor and Magic Resist for 6 seconds for each champion hit.<br><br><span class=\\\"colorDDDD77\\\">Challenge:</span> If a challenged target is hit by the sweep, it is unaffected by the knockback.\",\"leveltip\":{\"label\":[\"Bonus Damage\",\"Resistance Per Target\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,110,100],\"cooldownBurn\":\"120/110/100\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[75,175,275],[15,15,15],[15,20,25],[7,10,13],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"75/175/275\",\"15\",\"15/20/25\",\"7/10/13\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[500,500,500],\"rangeBurn\":\"500\",\"image\":{\"full\":\"XenZhaoParry.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":96,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Yasuo\":{\"id\":157,\"key\":\"Yasuo\",\"name\":\"Yasuo\",\"title\":\"the Unforgiven\",\"spells\":[{\"id\":\"YasuoQW\",\"name\":\"Steel Tempest\",\"description\":\"A skillshot basic attack. After two successful Steel Tempests, the next fires a tornado that knocks enemies Airborne.\",\"tooltip\":\"Thrusts forward, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage.<br><br>On hit, Steel Tempest grants a stack of Gathering Storm for 6 seconds. At 2 stacks, Steel Tempest fires a whirlwind that knocks <span class=\\\"color6655CC\\\">Airborne</span>.<br><br>Steel Tempest is treated as a basic attack: It can critically strike, applies on-hit effects, is interruptible by crowd control and its cooldown and cast time are reduced by Attack Speed.<br><br><span class=\\\"color99FF99\\\">If cast while dashing, Steel Tempest will strike as a circle.</span>\",\"leveltip\":{\"label\":[\"Damage\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\"]},\"maxrank\":5,\"cooldown\":[6,5.5,5,4.5,4],\"cooldownBurn\":\"6/5.5/5/4.5/4\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[20,40,60,80,100],[100,100,100,100,100],[85,95,105,115,125],[4,4,4,4,4],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"20/40/60/80/100\",\"100\",\"85/95/105/115/125\",\"4\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[475,475,475,475,475],\"rangeBurn\":\"475\",\"image\":{\"full\":\"YasuoQW.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":144,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"YasuoWMovingWall\",\"name\":\"Wind Wall\",\"description\":\"Creates a moving wall that blocks enemy projectiles.\",\"tooltip\":\"Creates a moving wall that blocks all enemy projectiles for 4 seconds.\",\"leveltip\":{\"label\":[\"Wall Width\",\"Cooldown\"],\"effect\":[\"{{ e4 }} -> {{ e4NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[26,24,22,20,18],\"cooldownBurn\":\"26/24/22/20/18\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[15,20,25,30,35],[60,90,120,150,180],[3,6,9,12,15],[300,350,400,450,500],[320,390,460,530,600],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"15/20/25/30/35\",\"60/90/120/150/180\",\"3/6/9/12/15\",\"300/350/400/450/500\",\"320/390/460/530/600\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[400,400,400,400,400],\"rangeBurn\":\"400\",\"image\":{\"full\":\"YasuoWMovingWall.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":192,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},{\"id\":\"YasuoDashWrapper\",\"name\":\"Sweeping Blade\",\"description\":\"Dashes through a unit, dealing escalating Magic Damage with each cast.\",\"tooltip\":\"Dashes through target enemy, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> <span class=\\\"colorFF8C00\\\">(+{{ f3 }})</span> Magic Damage. Each cast increases your next dash's base Damage by 25%, up to {{ e6 }}%.<br><br>Cannot be re-cast on the same enemy for {{ e2 }} seconds.<br><br><span class=\\\"color99FF99\\\">If cast while dashing, Steel Tempest will strike as a circle.</span>\",\"leveltip\":{\"label\":[\"Damage\",\"Recast Block\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }} -> {{ e2NL }}\",\"{{ e3 }} -> {{ e3NL }}\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[60,70,80,90,100],[10,9,8,7,6],[0.5,0.4,0.3,0.2,0.1],[1.5,1.5,1.5,1.5,1.5],[15,17.5,20,22.5,25],[50,50,50,50,50],[20,20,20,20,20],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/70/80/90/100\",\"10/9/8/7/6\",\"0.5/0.4/0.3/0.2/0.1\",\"1.5\",\"15/17.5/20/22.5/25\",\"50\",\"20\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"}],\"costType\":\"\",\"maxammo\":\"-1\",\"range\":[475,475,475,475,475],\"rangeBurn\":\"475\",\"image\":{\"full\":\"YasuoDashWrapper.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":240,\"y\":48,\"w\":48,\"h\":48}},{\"id\":\"YasuoRKnockUpComboW\",\"name\":\"Last Breath\",\"description\":\"Moves to a unit and strikes them repeatedly for heavy damage. Can only be cast on Airborne targets.\",\"tooltip\":\"Blinks to an <span class=\\\"color6655CC\\\">Airborne</span> enemy champion, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage and holding all <span class=\\\"color6655CC\\\">Airborne</span> enemies in the area in the air for an additional 1 second. Grants maximum Flow but resets all stacks of Gathering Storm.<br><br>For 15 seconds, Yasuo's critical strikes gain {{ e5 }}% Bonus Armor Penetration - this affects Armor from items, buffs, runes and masteries.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ f1 }} -> {{ f2 }}\"]},\"maxrank\":3,\"cooldown\":[0,0,0],\"cooldownBurn\":\"0\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[200,300,400],[33,66,100],[650,750,850],[1.5,1.5,1.5],[50,50,50],[33,66,100],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"200/300/400\",\"33/66/100\",\"650/750/850\",\"1.5\",\"50\",\"33/66/100\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":1.5,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[1200,1200,1200],\"rangeBurn\":\"1200\",\"image\":{\"full\":\"YasuoRKnockUpComboW.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":288,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Yorick\":{\"id\":83,\"key\":\"Yorick\",\"name\":\"Yorick\",\"title\":\"Shepherd of Souls\",\"spells\":[{\"id\":\"YorickQ\",\"name\":\"Last Rites\",\"description\":\"Yorick deals bonus damage on his next attack and heals himself. If the target dies a grave will be dug.\",\"tooltip\":\"Yorick's next basic attack deals {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage and restores {{ f1 }} Health (doubled when below half Health). Last Rites will leave a grave if it kills the target.<br><br>When there are three or more graves nearby and Last Rites has been used, Yorick can cast Awakening to raise Mist Walkers from graves.</span>\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[7,6.5,6,5.5,5],\"cooldownBurn\":\"7/6.5/6/5.5/5\",\"cost\":[25,25,25,25,25],\"costBurn\":\"25\",\"effect\":[null,[30,55,80,105,130],[5,5,5,5,5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"30/55/80/105/130\",\"5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1.4,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[0,0,0,0,0],\"rangeBurn\":\"0\",\"image\":{\"full\":\"YorickQ.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":336,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"YorickW\",\"name\":\"Dark Procession\",\"description\":\"Yorick summons a destructible wall at target location that will block enemy movement.\",\"tooltip\":\"Yorick summons a destructible wall of spirits with {{ e5 }} Health around target area for {{ e1 }} seconds. Allies can walk through the wall freely.\",\"leveltip\":{\"label\":[\"Health\",\"Cooldown\"],\"effect\":[\"{{ e5 }} -> {{ e5NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[20,18,16,14,12],\"cooldownBurn\":\"20/18/16/14/12\",\"cost\":[70,70,70,70,70],\"costBurn\":\"70\",\"effect\":[null,[4,4,4,4,4],[18,18,18,18,18],[0.75,0.75,0.75,0.75,0.75],[0,0,1,1,2],[2,2,3,3,4],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"4\",\"18\",\"0.75\",\"0/0/1/1/2\",\"2/2/3/3/4\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"YorickW.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":384,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"YorickE\",\"name\":\"Mourning Mist\",\"description\":\"Yorick throws a globule of Black Mist that damages, slows and marks enemies.\",\"tooltip\":\"Yorick throws a globule of Black Mist that deals {{ e7 }}% of enemy's current Health as magic damage (minimum {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span>), slows by {{ e3 }}% for {{ e6 }} seconds, and marks champions and monsters for {{ e2 }} seconds.<br><br>Yorick and his minions are hasted by {{ e5 }}% while moving toward the mark.\",\"leveltip\":{\"label\":[\"Minimum Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[12,11,10,9,8],\"cooldownBurn\":\"12/11/10/9/8\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[70,105,140,175,210],[4,4,4,4,4],[30,30,30,30,30],[1000,1000,1000,1000,1000],[20,20,20,20,20],[2,2,2,2,2],[15,15,15,15,15],[300,300,300,300,300],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/105/140/175/210\",\"4\",\"30\",\"1000\",\"20\",\"2\",\"15\",\"300\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[700,700,700,700,700],\"rangeBurn\":\"700\",\"image\":{\"full\":\"YorickE.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":432,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"YorickR\",\"name\":\"Eulogy of the Isles\",\"description\":\"Yorick summons the Maiden of the Mist that causes Yorick's attacks against the Maiden's target to deal bonus damage. The Maiden will also automatically raise Walkers from dead enemies.\",\"tooltip\":\"Yorick summons The Maiden of the Mist and {{ e1 }} Mist Walkers. The Maiden has {{ e3 }} <span class=\\\"colorFF3300\\\">(+{{ f1 }})</span> Health, deals {{ e4 }} <span class=\\\"colorFF8C00\\\">(+{{ f2 }})</span> magic damage, and raises Mist Walkers from nearby enemy deaths.<br><br>When Yorick damages the Maiden's target he will deal {{ e5 }}% of their max Health as bonus magic damage ({{ e7 }} second cooldown).<br><br><span class=\\\"colorDDDD77\\\">If the Maiden is summoned inside a lane, she will start pushing the lane.</span>\",\"leveltip\":{\"label\":[\"Health\",\"Damage\",\"Mist Walkers\",\"Mark Damage\",\"Cooldown\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ e1 }} -> {{ e1NL }}\",\"{{ e5 }}% ->{{ e5NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[160,150,140],\"cooldownBurn\":\"160/150/140\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[2,3,4],[700,700,700],[700,1500,3000],[10,20,40],[5,7.5,10],[1,1,1],[2,2,2],[200,300,400],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"2/3/4\",\"700\",\"700/1500/3000\",\"10/20/40\",\"5/7.5/10\",\"1\",\"2\",\"200/300/400\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"YorickR.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":0,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Zac\":{\"id\":154,\"key\":\"Zac\",\"name\":\"Zac\",\"title\":\"the Secret Weapon\",\"spells\":[{\"id\":\"ZacQ\",\"name\":\"Stretching Strikes\",\"description\":\"Zac stretches an arm, grabbing an enemy. Attacking a different enemy will cause him to throw both targets towards each other.\",\"tooltip\":\"Zac's arm stretches and grabs the first enemy it hits, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> <span class=\\\"colorCC3300\\\">(+{{ f2 }})</span> magic damage and briefly slowing them. Zac's next basic attack is replaced with a long range smack that repeats the initial magic damage and slow effect.<br><br>If Zac grabs a different enemy with each attack he'll throw them towards each other, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> <span class=\\\"colorCC3300\\\">(+{{ f2 }})</span> magic damage in an area if they collide.</span>\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }}-> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[12,11,10,9,8],\"cooldownBurn\":\"12/11/10/9/8\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[30,40,50,60,70],[4,4,4,4,4],[800,800,800,800,800],[-0.6,-0.6,-0.6,-0.6,-0.6],[0.5,0.5,0.5,0.5,0.5],[2.5,2.5,2.5,2.5,2.5],[1000,1000,1000,1000,1000],[700,700,700,700,700],[300,300,300,300,300],[300,300,300,300,300]],\"effectBurn\":[null,\"30/40/50/60/70\",\"4\",\"800\",\"-0.6\",\"0.5\",\"2.5\",\"1000\",\"700\",\"300\",\"300\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"}],\"costType\":\"% of current Health\",\"maxammo\":\"-1\",\"range\":[800,800,800,800,800],\"rangeBurn\":\"800\",\"image\":{\"full\":\"ZacQ.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":48,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ e2 }}% of current Health\"},{\"id\":\"ZacW\",\"name\":\"Unstable Matter\",\"description\":\"Zac's body erupts, damaging nearby enemies.\",\"tooltip\":\"Zac's body erupts, dealing {{ e1 }} magic damage +{{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span>% of the enemy's max health as magic damage to nearby enemies.<br><br>Absorbing <span class=\\\"color3ca126\\\">Goo</span> reduces Unstable Matter's cooldown by {{ e4 }} second.<br><br><span class=\\\"size16 color8C8C8C\\\">16\",\"leveltip\":{\"label\":[\"Flat Damage\",\"Percent Max Health Damage\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }}% -> {{ e3NL }}%\"]},\"maxrank\":5,\"cooldown\":[5,5,5,5,5],\"cooldownBurn\":\"5\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[40,55,70,85,100],[4,4,4,4,4],[4,5,6,7,8],[1,1,1,1,1],[200,200,200,200,200],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"40/55/70/85/100\",\"4\",\"4/5/6/7/8\",\"1\",\"200\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.02,\"key\":\"a1\"}],\"costType\":\"% of current Health\",\"maxammo\":\"-1\",\"range\":[350,350,350,350,350],\"rangeBurn\":\"350\",\"image\":{\"full\":\"ZacW.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":96,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ e2 }}% of current Health\"},{\"id\":\"ZacE\",\"name\":\"Elastic Slingshot\",\"description\":\"Zac attaches his arms to the ground and stretches back, launching himself forward.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">First cast:</span> Zac faces the cursor and charges up over {{ e4 }} seconds.<br><br><span class=\\\"colorFF9900\\\">Second cast:</span> Launches Zac towards a location, knocking up nearby enemies for {{ f2 }} to {{ f3 }} seconds (based on charge time) and dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage. Zac spawns extra chunks of <span class=\\\"color3ca126\\\">Goo</span> for each enemy champion hit.<br><br><span class=\\\"size16 color8C8C8C\\\">16\",\"leveltip\":{\"label\":[\"Damage\",\"Range\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[24,21,18,15,12],\"cooldownBurn\":\"24/21/18/15/12\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[80,130,180,230,280],[4,4,4,4,4],[1200,1350,1500,1650,1800],[0.9,1,1.1,1.2,1.3],[0.5,0.5,0.5,0.5,0.5],[500,500,500,500,500],[1350,1350,1350,1350,1350],[0.6,0.6,0.6,0.6,0.6],[265,265,265,265,265],[1,1,1,1,1]],\"effectBurn\":[null,\"80/130/180/230/280\",\"4\",\"1200/1350/1500/1650/1800\",\"0.9/1/1.1/1.2/1.3\",\"0.5\",\"500\",\"1350\",\"0.6\",\"265\",\"1\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\"% of current Health\",\"maxammo\":\"-1\",\"range\":[300,300,300,300,300],\"rangeBurn\":\"300\",\"image\":{\"full\":\"ZacE.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":144,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ e2 }}% of current Health\"},{\"id\":\"ZacR\",\"name\":\"Let's Bounce!\",\"description\":\"Zac flattens himself, making him immune to crowd control while slowing enemies standing on top of him. When he decides to bounce away, he will suck up any enemies standing on top of him, taking them for a ride.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">First cast: </span>Zac squishes himself down, making him immune to Crowd Control for up to {{ e5 }} seconds. Enemies standing on top of him are slowed by {{ e4 }}%.<br><br><span class=\\\"colorFF9900\\\">Second cast: </span>Charging for at least {{ f1 }} seconds before recasting causes Zac to scoop up enemies on top of him, carrying them towards a location. Upon landing nearby enemies take {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and are briefly slowed.<br><br>Reactivating before Zac charges up knocks back nearby enemies instead.</span></span>\",\"leveltip\":{\"label\":[\"Damage\",\"Range\",\"Slow Percent\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e3 }} -> {{ e3NL }}\",\"{{ e4 }}% -> {{ e4NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[130,115,100],\"cooldownBurn\":\"130/115/100\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[150,250,350],[1.1,1.1,1.1],[700,850,1000],[30,40,50],[2.5,2.5,2.5],[275,275,275],[300,300,300],[1,1,1],[0.25,0.25,0.25],[0.1,0.1,0.1]],\"effectBurn\":[null,\"150/250/350\",\"1.1\",\"700/850/1000\",\"30/40/50\",\"2.5\",\"275\",\"300\",\"1\",\"0.25\",\"0.1\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[300,300,300],\"rangeBurn\":\"300\",\"image\":{\"full\":\"ZacR.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":192,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Zed\":{\"id\":238,\"key\":\"Zed\",\"name\":\"Zed\",\"title\":\"the Master of Shadows\",\"spells\":[{\"id\":\"ZedQ\",\"name\":\"Razor Shuriken\",\"description\":\"Zed and his shadow both throw their spinning blades forward, dealing damage to any targets they pass through.\",\"tooltip\":\"Zed and his shadows throw their shurikens, each dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage to the first enemy they pass through, and {{ e3 }} <span class=\\\"colorFF8C00\\\">(+{{ a2 }})</span> physical damage to each additional enemy.<br><br>Additional shurikens that hit the same enemy deal 25% less damage than previous shurikens.\",\"leveltip\":{\"label\":[\"Damage\",\"Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[6,6,6,6,6],\"cooldownBurn\":\"6\",\"cost\":[75,70,65,60,55],\"costBurn\":\"75/70/65/60/55\",\"effect\":[null,[70,105,140,175,210],[0.6,0.6,0.6,0.6,0.6],[42,63,84,105,126],[0.75,0.75,0.75,0.75,0.75],[925,925,925,925,925],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"70/105/140/175/210\",\"0.6\",\"42/63/84/105/126\",\"0.75\",\"925\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.9,\"key\":\"a1\"},{\"link\":\"bonusattackdamage\",\"coeff\":0.54,\"key\":\"a2\"}],\"costType\":\" Energy\",\"maxammo\":\"-1\",\"range\":[900,900,900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"ZedQ.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":240,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Energy\"},{\"id\":\"ZedW\",\"name\":\"Living Shadow\",\"description\":\"Zed's shadow dashes forward, remaining in place for 4 seconds, and mimicking his spell casts. Zed can reactivate to swap places with the shadow.\",\"tooltip\":\"<span class=\\\"colorFF9900\\\">Passive: </span>Whenever Zed and his shadows strike an enemy with the same ability, Zed gains {{ e3 }} energy. Energy can only be gained once per cast ability.<br><br><span class=\\\"colorFF9900\\\">Active: </span>Zed's shadow dashes forward, remaining in place for {{ e1 }} seconds. <br>Reactivating Living Shadow will cause Zed to switch positions with this shadow.\",\"leveltip\":{\"label\":[\"Energy Return\",\"Cost\",\"Cooldown\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ cost }} -> {{ costNL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[22,20,18,16,14],\"cooldownBurn\":\"22/20/18/16/14\",\"cost\":[40,35,30,25,20],\"costBurn\":\"40/35/30/25/20\",\"effect\":[null,[4.5,4.5,4.5,4.5,4.5],[0.2,0.2,0.2,0.2,0.2],[30,35,40,45,50],[4.5,4.5,4.5,4.5,4.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"4.5\",\"0.2\",\"30/35/40/45/50\",\"4.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Energy\",\"maxammo\":\"-1\",\"range\":[650,650,650,650,650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"ZedW.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":288,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Energy\"},{\"id\":\"ZedE\",\"name\":\"Shadow Slash\",\"description\":\"Zed and his shadow spin their blades, creating a burst of shadow energy.  The shadow's spin slows.\",\"tooltip\":\"Zed and his shadows slash, dealing {{ e1 }} <span class=\\\"colorFF8C00\\\">(+{{ a1 }})</span> physical damage to nearby enemies.<br><br>Each enemy champion hit by Zed's slash reduces Living Shadow's cooldown by {{ e4 }} seconds.<br><br>Enemies hit by a Shadow's slash are slowed by {{ e2 }}% for 1.5 seconds. Enemies hit by multiple slashes take no additional damage but are slowed by {{ e3 }}% instead.\",\"leveltip\":{\"label\":[\"Damage\",\"Slow\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[5,4.5,4,3.5,3],\"cooldownBurn\":\"5/4.5/4/3.5/3\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[65,90,115,140,165],[20,25,30,35,40],[30,37.5,45,52.5,60],[2,2,2,2,2],[1.5,1.5,1.5,1.5,1.5],[315,315,315,315,315],[290,290,290,290,290],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"65/90/115/140/165\",\"20/25/30/35/40\",\"30/37.5/45/52.5/60\",\"2\",\"1.5\",\"315\",\"290\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"bonusattackdamage\",\"coeff\":0.8,\"key\":\"a1\"}],\"costType\":\" Energy\",\"maxammo\":\"-1\",\"range\":[290,290,290,290,290],\"rangeBurn\":\"290\",\"image\":{\"full\":\"ZedE.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":336,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Energy\"},{\"id\":\"ZedR\",\"name\":\"Death Mark\",\"description\":\"Zed leaves a shadow behind and dashes to target Champion, marking them for death. After 3 seconds, the mark will trigger, dealing a percentage of the damage Zed has dealt while the mark was active. If the Champion dies under Death Mark, Zed can gain a portion of their attack damage.\",\"tooltip\":\"Zed becomes untargetable and dashes to an enemy champion, marking them. After 3 seconds, the mark triggers, dealing physical damage equal to <span class=\\\"colorFF8C00\\\">{{ a1 }}</span> + {{ e2 }}% of all damage dealt to the target by Zed while the mark was active.<br><br>The dash leaves a shadow behind for {{ e4 }} seconds. Reactivating Death Mark causes Zed to switch positions with this shadow.<br><br><span class=\\\"colorFFFFFF\\\">Reaper of Shadows: </span>Zed reaps the shadow of the strongest foe slain under Death Mark, gaining <span class=\\\"colorFF8C00\\\">{{ f2 }}</span> attack damage. ({{ e0 }} + {{ e9 }}% of the victim's attack damage.)\",\"leveltip\":{\"label\":[\"Mark Detonation Damage\",\"Percent Attack Damage Stolen\",\"Cooldown\"],\"effect\":[\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ e9 }}% -> {{ e9NL }}%\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,100,80],\"cooldownBurn\":\"120/100/80\",\"cost\":[0,0,0],\"costBurn\":\"0\",\"effect\":[null,[1,1,1],[25,35,45],[0.5,0.5,0.5],[6,6,6],[6.5,6.5,6.5],[3,3,3],[7.5,7.5,7.5],[4,4,4],[5,10,15],[5,5,5]],\"effectBurn\":[null,\"1\",\"25/35/45\",\"0.5\",\"6\",\"6.5\",\"3\",\"7.5\",\"4\",\"5/10/15\",\"5\"],\"vars\":[{\"link\":\"attackdamage\",\"coeff\":1,\"key\":\"a1\"}],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[625,625,625],\"rangeBurn\":\"625\",\"image\":{\"full\":\"ZedR.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":384,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}]},\"Ziggs\":{\"id\":115,\"key\":\"Ziggs\",\"name\":\"Ziggs\",\"title\":\"the Hexplosives Expert\",\"spells\":[{\"id\":\"ZiggsQ\",\"name\":\"Bouncing Bomb\",\"description\":\"Ziggs throws a bouncing bomb that deals magic damage.\",\"tooltip\":\"Ziggs throws a bouncing bomb that deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[6,5.5,5,4.5,4],\"cooldownBurn\":\"6/5.5/5/4.5/4\",\"cost\":[50,55,60,65,70],\"costBurn\":\"50/55/60/65/70\",\"effect\":[null,[75,120,165,210,255],[850,850,850,850,850],[325,325,325,325,325],[225,225,225,225,225],[150,150,150,150,150],[240,240,240,240,240],[70,70,70,70,70],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"75/120/165/210/255\",\"850\",\"325\",\"225\",\"150\",\"240\",\"70\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.65,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[850,850,850,850,850],\"rangeBurn\":\"850\",\"image\":{\"full\":\"ZiggsQ.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":432,\"y\":96,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"ZiggsW\",\"name\":\"Satchel Charge\",\"description\":\"Ziggs flings an explosive charge that detonates after 4 seconds, or when this ability is activated again. The explosion deals magic damage to enemies, knocking them away. Ziggs is also knocked away, but takes no damage. Ziggs can use the Satchel to hexplode vulnerable enemy turrets.\",\"tooltip\":\"Ziggs flings an explosive charge that detonates after {{ e2 }} seconds, or when this ability is activated again. The explosion deals {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemies, knocking them away. Ziggs is also knocked away, but takes no damage.<br><br>Satchel Charge will <span class=\\\"colorD41919\\\">hexplode</span> structurally unsound towers below {{ f1*100 }}% of their Health.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Tower Destruction Threshold\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ f1*100 }}% -> {{ f2*100 }}%\"]},\"maxrank\":5,\"cooldown\":[26,24,22,20,18],\"cooldownBurn\":\"26/24/22/20/18\",\"cost\":[65,65,65,65,65],\"costBurn\":\"65\",\"effect\":[null,[70,105,140,175,210],[4,4,4,4,4],[0.25,0.275,0.3,0.325,0.35],[600,600,600,600,600],[50,50,50,50,50],[400,400,400,400,400],[325,325,325,325,325],[775,775,775,775,775],[400,400,400,400,400],[6,6,6,6,6]],\"effectBurn\":[null,\"70/105/140/175/210\",\"4\",\"0.25/0.275/0.3/0.325/0.35\",\"600\",\"50\",\"400\",\"325\",\"775\",\"400\",\"6\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.35,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1000,1000,1000,1000,1000],\"rangeBurn\":\"1000\",\"image\":{\"full\":\"ZiggsW.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":0,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"ZiggsE\",\"name\":\"Hexplosive Minefield\",\"description\":\"Ziggs scatters proximity mines that detonate on enemy contact, dealing magic damage and slowing.\",\"tooltip\":\"Ziggs scatters proximity mines that detonate on enemy contact, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage. Enemies hit are slowed by {{ e2 }}% for {{ e4 }} seconds.<br><br>Enemies triggering a mine take {{ e5 }}% damage from additional mines. Mines disarm automatically after {{ e3 }} seconds.\",\"leveltip\":{\"label\":[\"Mine Damage\",\"Slow\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e2 }}% -> {{ e2NL }}%\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[16,16,16,16,16],\"cooldownBurn\":\"16\",\"cost\":[70,80,90,100,110],\"costBurn\":\"70/80/90/100/110\",\"effect\":[null,[40,65,90,115,140],[20,25,30,35,40],[10,10,10,10,10],[1.5,1.5,1.5,1.5,1.5],[40,40,40,40,40],[-0.2,-0.25,-0.3,-0.35,-0.4],[135,135,135,135,135],[150,150,150,150,150],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"40/65/90/115/140\",\"20/25/30/35/40\",\"10\",\"1.5\",\"40\",\"-0.2/-0.25/-0.3/-0.35/-0.4\",\"135\",\"150\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.3,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[900,900,900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"ZiggsE.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":48,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"ZiggsR\",\"name\":\"Mega Inferno Bomb\",\"description\":\"Ziggs deploys his ultimate creation, the Mega Inferno Bomb, hurling it an enormous distance. Enemies in the primary blast zone take more damage than those farther away. \",\"tooltip\":\"Ziggs deploys his ultimate creation, the Mega Inferno Bomb, hurling it an enormous distance. Enemies in the primary blast zone take {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage. Enemies farther away take two-thirds damage.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":3,\"cooldown\":[120,105,90],\"cooldownBurn\":\"120/105/90\",\"cost\":[100,100,100],\"costBurn\":\"100\",\"effect\":[null,[300,450,600],[66.6667,66.6667,66.6667],[525,525,525],[250,250,250],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"300/450/600\",\"66.67\",\"525\",\"250\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":1.1,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[5000,5000,5000],\"rangeBurn\":\"5000\",\"image\":{\"full\":\"ZiggsR.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":96,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Zilean\":{\"id\":26,\"key\":\"Zilean\",\"name\":\"Zilean\",\"title\":\"the Chronokeeper\",\"spells\":[{\"id\":\"ZileanQ\",\"name\":\"Time Bomb\",\"description\":\"Tosses a bomb to target area that sticks to units that come near it (prioritizes Champions). It detonates after 3 seconds, dealing area of effect damage. If a Time Bomb is detonated early by another Time Bomb, it also stuns enemies.\",\"tooltip\":\"Zilean tosses a time-delayed bomb to a location. The bomb sticks to the first unit which comes within a small area around it (prioritizes Champions). After {{ e2 }} seconds it detonates, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to all surrounding enemies. <br><br>Placing two bombs on a unit detonates the first bomb early, stunning all enemies in the blast for {{ e4 }} seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Stun Duration\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e4 }} -> {{ e4NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[10,9.5,9,8.5,8],\"cooldownBurn\":\"10/9.5/9/8.5/8\",\"cost\":[60,65,70,75,80],\"costBurn\":\"60/65/70/75/80\",\"effect\":[null,[75,115,165,230,300],[3,3,3,3,3],[7,7,7,7,7],[1.1,1.2,1.3,1.4,1.5],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"75/115/165/230/300\",\"3\",\"7\",\"1.1/1.2/1.3/1.4/1.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.9,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[900,900,900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"ZileanQ.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":144,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"ZileanW\",\"name\":\"Rewind\",\"description\":\"Zilean can prepare himself for future confrontations, reducing the cooldowns of his other basic abilities. \",\"tooltip\":\"Reduces Zilean's other basic spell cooldowns by {{ e2 }} seconds.\",\"leveltip\":{\"label\":[\"Cooldown\"],\"effect\":[\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[14,12,10,8,6],\"cooldownBurn\":\"14/12/10/8/6\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[20,30,40,50,60],[10,10,10,10,10],[35,35,35,35,35],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"20/30/40/50/60\",\"10\",\"35\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[600,600,600,600,600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"ZileanW.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":192,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ e3 }} Mana\"},{\"id\":\"TimeWarp\",\"name\":\"Time Warp\",\"description\":\"Zilean bends time around any unit, decreasing an enemy's Movement Speed or increasing an ally's Movement Speed for a short time.\",\"tooltip\":\"Zilean increases an allied champion's Movement Speed, or slows an enemy champion, by {{ e2 }}% for {{ e1 }} seconds.\",\"leveltip\":{\"label\":[\"Speed\"],\"effect\":[\"{{ e2 }}% -> {{ e2NL }}%\"]},\"maxrank\":5,\"cooldown\":[15,15,15,15,15],\"cooldownBurn\":\"15\",\"cost\":[50,50,50,50,50],\"costBurn\":\"50\",\"effect\":[null,[2.5,2.5,2.5,2.5,2.5],[40,55,70,85,99],[1.5,1.5,1.5,1.5,1.5],[8,8,8,8,8],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"2.5\",\"40/55/70/85/99\",\"1.5\",\"8\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[700,700,700,700,700],\"rangeBurn\":\"700\",\"image\":{\"full\":\"TimeWarp.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":240,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"ChronoShift\",\"name\":\"Chronoshift\",\"description\":\"Zilean places a protective time rune on an allied champion, teleporting the champion back in time if they take lethal damage.\",\"tooltip\":\"Zilean marks himself or an allied champion with a protective time rune for {{ e2 }} seconds. If the target would take lethal damage, they are instead transported back in time, arriving with {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> Health.\",\"leveltip\":{\"label\":[\"Health Restored\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":3,\"cooldown\":[120,90,60],\"cooldownBurn\":\"120/90/60\",\"cost\":[125,150,175],\"costBurn\":\"125/150/175\",\"effect\":[null,[600,850,1100],[5,5,5],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"600/850/1100\",\"5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":2,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[900,900,900],\"rangeBurn\":\"900\",\"image\":{\"full\":\"ChronoShift.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":288,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]},\"Zyra\":{\"id\":143,\"key\":\"Zyra\",\"name\":\"Zyra\",\"title\":\"Rise of the Thorns\",\"spells\":[{\"id\":\"ZyraQ\",\"name\":\"Deadly Spines\",\"description\":\"Thick vines spread through the ground and explode into spines, dealing magic damage to enemies within the area. If cast near a seed, Deadly Spines grows a Thorn Spitter plant, which fires at enemies from afar.\",\"tooltip\":\"Thick vines spread through the ground and explode into spines, dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to enemies within the area. <br><br><span class=\\\"colorDDDD77\\\">Garden of Thorns: </span>If Deadly Spines is cast near a seed, a Thorn Spitter grows, dealing {{ f1 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage. A Thorn Spitter has 750 range and lasts <span class=\\\"colorFFFFFF\\\">{{ f2 }}</span> seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\"]},\"maxrank\":5,\"cooldown\":[7,6.5,6,5.5,5],\"cooldownBurn\":\"7/6.5/6/5.5/5\",\"cost\":[70,70,70,70,70],\"costBurn\":\"70\",\"effect\":[null,[60,95,130,165,200],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/95/130/165/200\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.6,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.15,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[800,800,800,800,800],\"rangeBurn\":\"800\",\"image\":{\"full\":\"ZyraQ.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":336,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"ZyraW\",\"name\":\"Rampant Growth\",\"description\":\"Zyra plants a seed, lasting up to 60 seconds. Deadly Spines and Grasping Roots cast near seeds will turn them into plants who fight for Zyra. Additionally, Rampant Growth passively grants her plants improved maximum Health.\",\"tooltip\":\"Plants a seed, lasting {{ e6 }} seconds. If an enemy Champion steps on a seed, it dies. Seeds spawned by Rampant Growth grant vision in a small area, and if stepped on they grant <span class=\\\"coloree91d7\\\">True Sight</span> of the enemy champion for {{ e3 }} seconds.<br><br>Zyra stores a seed every <span class=\\\"colorFFFFFF\\\">{{ ammorechargetime }}</span> seconds (Max: 8 seeds planted). <br><br><span class=\\\"colorDDDD77\\\">Passive: </span>Zyra's plants gain {{ e8 }}% extra maximum Health.\",\"leveltip\":{\"label\":[\"Plant Health Bonus\",\"Seed Recharge Rate\"],\"effect\":[\"{{ e8 }}% -> {{ e8NL }}%\",\"{{ ammorechargetime }} -> {{ ammorechargetimeNL }}\"]},\"maxrank\":5,\"cooldown\":[0,0,0,0,0],\"cooldownBurn\":\"0\",\"cost\":[0,0,0,0,0],\"costBurn\":\"0\",\"effect\":[null,[0,0,0,0,0],[0,0,0,0,0],[2,2,2,2,2],[0,0,0,0,0],[0,0,0,0,0],[60,60,60,60,60],[0,0,0,0,0],[10,20,30,40,50],[0.4,0.8,1.2,1.6,2],[0,0,0,0,0]],\"effectBurn\":[null,\"0\",\"0\",\"2\",\"0\",\"0\",\"60\",\"0\",\"10/20/30/40/50\",\"0.4/0.8/1.2/1.6/2\",\"0\"],\"vars\":[],\"costType\":\"1 Seed\",\"maxammo\":\"2\",\"range\":[850,850,850,850,850],\"rangeBurn\":\"850\",\"image\":{\"full\":\"ZyraW.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":384,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"1 Seed\"},{\"id\":\"ZyraE\",\"name\":\"Grasping Roots\",\"description\":\"Zyra sends forth vines through the ground to ensnare her target, dealing damage and rooting enemies they come across. If cast near a seed, Grasping Roots grows a Vine Lasher, whose short range attacks reduce enemy Movement Speed.\",\"tooltip\":\"Sends forward vines dealing {{ e1 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage and rooting enemies for {{ e5 }} second(s).<br><br><span class=\\\"colorDDDD77\\\">Garden of Thorns: </span>If Grasping Roots passes near a seed, a Vine Lasher grows dealing {{ f1 }} <span class=\\\"color99FF99\\\">(+{{ a2 }})</span> magic damage and slowing enemies by {{ e2 }}% for {{ e3 }} seconds. A Vine Lasher has 400 range and lasts <span class=\\\"colorFFFFFF\\\">{{ f2 }}</span> seconds.\",\"leveltip\":{\"label\":[\"Damage\",\"Root Duration\",\"Mana Cost\"],\"effect\":[\"{{ e1 }} -> {{ e1NL }}\",\"{{ e5 }} -> {{ e5NL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":5,\"cooldown\":[12,12,12,12,12],\"cooldownBurn\":\"12\",\"cost\":[70,75,80,85,90],\"costBurn\":\"70/75/80/85/90\",\"effect\":[null,[60,95,130,165,200],[30,30,30,30,30],[2,2,2,2,2],[4,4,4,0,0],[0.75,1,1.25,1.5,1.75],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]],\"effectBurn\":[null,\"60/95/130/165/200\",\"30\",\"2\",\"4/4/4/0/0\",\"0.75/1/1.25/1.5/1.75\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.5,\"key\":\"a1\"},{\"link\":\"spelldamage\",\"coeff\":0.15,\"key\":\"a2\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[1100,1100,1100,1100,1100],\"rangeBurn\":\"1100\",\"image\":{\"full\":\"ZyraE.png\",\"sprite\":\"spell13.png\",\"group\":\"spell\",\"x\":432,\"y\":144,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"},{\"id\":\"ZyraR\",\"name\":\"Stranglethorns\",\"description\":\"Zyra summons a twisted thicket at her target location, dealing damage to enemies as it expands and knocking them airborne as it contracts.\",\"tooltip\":\"Summons the fury of nature, growing a twisted thicket at target location which deals {{ e3 }} <span class=\\\"color99FF99\\\">(+{{ a1 }})</span> magic damage to all enemies in the area as it expands. After 2 seconds, the vines snap upward knocking enemies into the air for {{ e1 }} second.<br><br><span class=\\\"colorDDDD77\\\">Garden of Thorns: </span>Plants within the thicket are enraged, attacking in a flurry for 150% total damage.\",\"leveltip\":{\"label\":[\"Damage\",\"Cooldown\",\"Mana Cost\"],\"effect\":[\"{{ e3 }} -> {{ e3NL }}\",\"{{ cooldown }} -> {{ cooldownNL }}\",\"{{ cost }} -> {{ costNL }}\"]},\"maxrank\":3,\"cooldown\":[130,120,110],\"cooldownBurn\":\"130/120/110\",\"cost\":[100,120,140],\"costBurn\":\"100/120/140\",\"effect\":[null,[1,1,1],[0,0,0],[180,265,350],[0,0,0],[0,0,0],[0,0,0],[150,150,150],[0,0,0],[0,0,0],[0,0,0]],\"effectBurn\":[null,\"1\",\"0\",\"180/265/350\",\"0\",\"0\",\"0\",\"150\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"spelldamage\",\"coeff\":0.7,\"key\":\"a1\"}],\"costType\":\" Mana\",\"maxammo\":\"-1\",\"range\":[700,700,700],\"rangeBurn\":\"700\",\"image\":{\"full\":\"ZyraR.png\",\"sprite\":\"spell14.png\",\"group\":\"spell\",\"x\":0,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"{{ cost }} Mana\"}]}}"
  },
  {
    "path": "api_data/summoners.json",
    "content": "{\"SummonerBarrier\":{\"id\":21,\"name\":\"Barrier\",\"description\":\"Shields your champion from 115-455 damage (depending on champion level) for 2 seconds.\",\"tooltip\":\"Temporarily shields {{ f1 }} damage from your champion for 2 seconds.\",\"maxrank\":1,\"cooldown\":[180],\"cooldownBurn\":\"180\",\"cost\":[0],\"costBurn\":\"0\",\"effect\":[null,[95],[20],[0],[0],[0],[0],[0],[0],[0],[0]],\"effectBurn\":[null,\"95\",\"20\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"key\":\"SummonerBarrier\",\"summonerLevel\":4,\"modes\":[\"ARAM\",\"CLASSIC\",\"TUTORIAL\",\"ODIN\",\"ASCENSION\",\"FIRSTBLOOD\",\"ASSASSINATE\",\"URF\",\"ARSR\",\"DOOMBOTSTEEMO\"],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[1200],\"rangeBurn\":\"1200\",\"image\":{\"full\":\"SummonerBarrier.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":0,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},\"SummonerBoost\":{\"id\":1,\"name\":\"Cleanse\",\"description\":\"Removes all disables (excluding suppression) and summoner spell debuffs affecting your champion and lowers the duration of incoming disables by 65% for 3 seconds.\",\"tooltip\":\"Removes all disables (excluding suppression) and summoner spell debuffs affecting your champion and reduces the duration of disables by 65% for the next {{ f1 }} seconds.\",\"maxrank\":1,\"cooldown\":[210],\"cooldownBurn\":\"210\",\"cost\":[0],\"costBurn\":\"0\",\"effect\":[null,[0.65],[3],[0],[0],[0],[0],[0],[0],[0],[0]],\"effectBurn\":[null,\"0.65\",\"3\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"@text\",\"coeff\":3,\"key\":\"f1\"}],\"key\":\"SummonerBoost\",\"summonerLevel\":6,\"modes\":[\"CLASSIC\",\"ODIN\",\"TUTORIAL\",\"ARAM\",\"ASCENSION\",\"FIRSTBLOOD\",\"URF\",\"ARSR\",\"DOOMBOTSTEEMO\"],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[200],\"rangeBurn\":\"200\",\"image\":{\"full\":\"SummonerBoost.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":48,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},\"SummonerDarkStarChampSelect1\":{\"id\":35,\"name\":\"Disabled Summoner Spells\",\"description\":\"Summoner spells are disabled in this mode.\",\"tooltip\":\"\",\"maxrank\":1,\"cooldown\":[0],\"cooldownBurn\":\"0\",\"cost\":[0],\"costBurn\":\"0\",\"effect\":[null,[0],[0],[0],[0],[0],[0],[0],[0],[0],[0]],\"effectBurn\":[null,\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"key\":\"SummonerDarkStarChampSelect1\",\"summonerLevel\":1,\"modes\":[\"DARKSTAR\"],\"costType\":\"\",\"maxammo\":\"-1\",\"range\":[2500],\"rangeBurn\":\"2500\",\"image\":{\"full\":\"SummonerDarkStarChampSelect1.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":96,\"y\":0,\"w\":48,\"h\":48}},\"SummonerDarkStarChampSelect2\":{\"id\":36,\"name\":\"Disabled Summoner Spells\",\"description\":\"Summoner spells are disabled in this mode.\",\"tooltip\":\"\",\"maxrank\":1,\"cooldown\":[0],\"cooldownBurn\":\"0\",\"cost\":[0],\"costBurn\":\"0\",\"effect\":[null,[0],[0],[0],[0],[0],[0],[0],[0],[0],[0]],\"effectBurn\":[null,\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"key\":\"SummonerDarkStarChampSelect2\",\"summonerLevel\":1,\"modes\":[\"DARKSTAR\"],\"costType\":\"\",\"maxammo\":\"-1\",\"range\":[2500],\"rangeBurn\":\"2500\",\"image\":{\"full\":\"SummonerDarkStarChampSelect2.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":144,\"y\":0,\"w\":48,\"h\":48}},\"SummonerDot\":{\"id\":14,\"name\":\"Ignite\",\"description\":\"Ignites target enemy champion, dealing 70-410 true damage (depending on champion level) over 5 seconds, grants you vision of the target, and reduces healing effects on them for the duration.\",\"tooltip\":\"Ignite deals <span class=\\\"colorFEFCFF\\\">{{ f1 }}</span> true damage to target enemy champion over 5 seconds, grants you vision of the target and applies Grievous Wounds for the duration.<br><br><rules>(Grievous Wounds reduces healing effects by 40%. This vision does not reveal stealthed enemies.)</rules>\",\"maxrank\":1,\"cooldown\":[210],\"cooldownBurn\":\"210\",\"cost\":[0],\"costBurn\":\"0\",\"effect\":[null,[5],[10],[4],[100],[0],[0],[0],[0],[0],[0]],\"effectBurn\":[null,\"5\",\"10\",\"4\",\"100\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"@player.level\",\"coeff\":[70,90,110,130,150,170,190,210,230,250,270,290,310,330,350,370,390,410],\"key\":\"f1\"}],\"key\":\"SummonerDot\",\"summonerLevel\":10,\"modes\":[\"CLASSIC\",\"ODIN\",\"TUTORIAL\",\"ARAM\",\"ASCENSION\",\"FIRSTBLOOD\",\"ASSASSINATE\",\"URF\",\"ARSR\",\"DOOMBOTSTEEMO\"],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"SummonerDot.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":192,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},\"SummonerExhaust\":{\"id\":3,\"name\":\"Exhaust\",\"description\":\"Exhausts target enemy champion, reducing their Movement Speed by 30%, and their damage dealt by 40% for 2.5 seconds.\",\"tooltip\":\"Exhausts target enemy champion, reducing their Movement Speed by {{ f3 }}%, and their damage dealt by {{ f2 }}% for 2.5 seconds.\",\"maxrank\":1,\"cooldown\":[210],\"cooldownBurn\":\"210\",\"cost\":[0],\"costBurn\":\"0\",\"effect\":[null,[2.5],[40],[0],[0],[30],[0],[0],[0],[0],[0]],\"effectBurn\":[null,\"2.5\",\"40\",\"0\",\"0\",\"30\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"key\":\"SummonerExhaust\",\"summonerLevel\":4,\"modes\":[\"CLASSIC\",\"ODIN\",\"TUTORIAL\",\"ARAM\",\"ASCENSION\",\"FIRSTBLOOD\",\"URF\",\"ARSR\",\"DOOMBOTSTEEMO\"],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[650],\"rangeBurn\":\"650\",\"image\":{\"full\":\"SummonerExhaust.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":240,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},\"SummonerFlash\":{\"id\":4,\"name\":\"Flash\",\"description\":\"Teleports your champion a short distance toward your cursor's location.\",\"tooltip\":\"Teleports your champion a short distance toward your cursor's location.\",\"maxrank\":1,\"cooldown\":[300],\"cooldownBurn\":\"300\",\"cost\":[0],\"costBurn\":\"0\",\"effect\":[null,[400],[0],[0],[0],[0],[0],[0],[0],[0],[0]],\"effectBurn\":[null,\"400\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"key\":\"SummonerFlash\",\"summonerLevel\":8,\"modes\":[\"CLASSIC\",\"ODIN\",\"TUTORIAL\",\"ARAM\",\"ASCENSION\",\"FIRSTBLOOD\",\"ASSASSINATE\",\"URF\",\"ARSR\",\"DOOMBOTSTEEMO\"],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[425],\"rangeBurn\":\"425\",\"image\":{\"full\":\"SummonerFlash.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":288,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},\"SummonerHaste\":{\"id\":6,\"name\":\"Ghost\",\"description\":\"Your champion gains increased Movement Speed and can move through units for 10 seconds. Grants a maximum of 28-45% (depending on champion level) Movement Speed after accelerating for 2 seconds.\",\"tooltip\":\"Your champion gains increased Movement Speed and can move through units for 10 seconds. Grants a maximum of {{ f1 }}% Movement Speed after accelerating for 2 seconds.\",\"maxrank\":1,\"cooldown\":[180],\"cooldownBurn\":\"180\",\"cost\":[0],\"costBurn\":\"0\",\"effect\":[null,[27],[0],[2],[0],[0],[0],[0],[0],[0],[0]],\"effectBurn\":[null,\"27\",\"0\",\"2\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"@text\",\"coeff\":27,\"key\":\"f1\"}],\"key\":\"SummonerHaste\",\"summonerLevel\":1,\"modes\":[\"CLASSIC\",\"ODIN\",\"TUTORIAL\",\"ARAM\",\"ASCENSION\",\"FIRSTBLOOD\",\"ASSASSINATE\",\"URF\",\"ARSR\",\"DOOMBOTSTEEMO\"],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[200],\"rangeBurn\":\"200\",\"image\":{\"full\":\"SummonerHaste.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":336,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},\"SummonerHeal\":{\"id\":7,\"name\":\"Heal\",\"description\":\"Restores 90-345 Health (depending on champion level) and grants 30% Movement Speed for 1 second to you and target allied champion. This healing is halved for units recently affected by Summoner Heal.\",\"tooltip\":\"Restores {{ f1 }} Health and grants 30% Movement Speed for 1 second to your champion and target allied champion. This healing is halved for units recently affected by Summoner Heal.<br><br><span class=\\\"colorFFFF00\\\">If this spell cannot find a target, it will cast on the most wounded allied champion in range.</span>\",\"maxrank\":1,\"cooldown\":[240],\"cooldownBurn\":\"240\",\"cost\":[0],\"costBurn\":\"0\",\"effect\":[null,[0.3],[75],[15],[0.5],[826],[0],[0],[0],[0],[0]],\"effectBurn\":[null,\"0.3\",\"75\",\"15\",\"0.5\",\"826\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"@player.level\",\"coeff\":[90,105,120,135,150,165,180,195,210,225,240,255,270,285,300,315,330,345],\"key\":\"f1\"}],\"key\":\"SummonerHeal\",\"summonerLevel\":1,\"modes\":[\"CLASSIC\",\"ODIN\",\"TUTORIAL\",\"ARAM\",\"ASCENSION\",\"FIRSTBLOOD\",\"ASSASSINATE\",\"URF\",\"ARSR\",\"DOOMBOTSTEEMO\"],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[850],\"rangeBurn\":\"850\",\"image\":{\"full\":\"SummonerHeal.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":384,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},\"SummonerMana\":{\"id\":13,\"name\":\"Clarity\",\"description\":\"Restores 50% of your champion's maximum Mana. Also restores allies for 25% of their maximum Mana.\",\"tooltip\":\"Restores {{ f1 }}% maximum Mana to your Champion and {{ f2 }}% to nearby allies.\",\"maxrank\":1,\"cooldown\":[240],\"cooldownBurn\":\"240\",\"cost\":[0],\"costBurn\":\"0\",\"effect\":[null,[50],[25],[0],[0],[0],[0],[0],[0],[0],[0]],\"effectBurn\":[null,\"50\",\"25\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"@player.level\",\"coeff\":[190,220,250,280,310,340,370,400,430,460,490,520,550,580,610,640,670,700],\"key\":\"f1\"},{\"link\":\"@player.level\",\"coeff\":[95,110,125,140,155,170,185,200,215,230,245,260,275,290,305,320,335,350],\"key\":\"f2\"}],\"key\":\"SummonerMana\",\"summonerLevel\":1,\"modes\":[\"ODIN\",\"ARAM\",\"ASCENSION\"],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[600],\"rangeBurn\":\"600\",\"image\":{\"full\":\"SummonerMana.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":432,\"y\":0,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},\"SummonerPoroRecall\":{\"id\":30,\"name\":\"To the King!\",\"description\":\"Quickly travel to the Poro King's side.\",\"tooltip\":\"<span class=\\\"colorFFE076\\\">Passive:</span> Hitting an enemy champion with a Poro gives your team a Poro Mark. Upon reaching 10 Poro Marks, your team summons the Poro King to fight alongside them. While the Poro King is active, no Poro Marks can be scored by either team.<br><br><span class=\\\"colorFFE076\\\">Active:</span> Quickly dash to King Poro's side. Can only be cast while the Poro King is summoned for your team. <br><br><i><span class=\\\"colorFDD017\\\">''Poros tug the heartstrings. The rest of you just comes along for the ride.''</span></i>\",\"maxrank\":1,\"cooldown\":[10],\"cooldownBurn\":\"10\",\"cost\":[0],\"costBurn\":\"0\",\"effect\":[null,[3000],[0],[0],[0],[0],[0],[0],[0],[0],[0]],\"effectBurn\":[null,\"3000\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"key\":\"SummonerPoroRecall\",\"summonerLevel\":1,\"modes\":[\"KINGPORO\"],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[200],\"rangeBurn\":\"200\",\"image\":{\"full\":\"SummonerPoroRecall.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":0,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},\"SummonerPoroThrow\":{\"id\":31,\"name\":\"Poro Toss\",\"description\":\"Toss a Poro at your enemies. If it hits, you can quickly travel to your target as a follow up.\",\"tooltip\":\"Toss a Poro a long distance, dealing {{ f2 }} true damage to the first enemy unit hit, granting <span class=\\\"coloree91d7\\\">True Sight</span> of the target. This ability can be recast for 3 seconds if it hits an enemy to dash to the target hit. Dashing to the target will reduce the cooldown of Poro Toss by 5 seconds.<br><br>Poros are not blocked by spell shields or wind walls because they are animals, not spells!<br><br><i><span class=\\\"colorFDD017\\\">''Poros are a model for Runeterran aerodynamics.''</span></i>\",\"maxrank\":1,\"cooldown\":[20],\"cooldownBurn\":\"20\",\"cost\":[0],\"costBurn\":\"0\",\"effect\":[null,[20],[10],[0],[0],[0],[0],[0],[0],[0],[0]],\"effectBurn\":[null,\"20\",\"10\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"key\":\"SummonerPoroThrow\",\"summonerLevel\":1,\"modes\":[\"KINGPORO\"],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[2500],\"rangeBurn\":\"2500\",\"image\":{\"full\":\"SummonerPoroThrow.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":48,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},\"SummonerSiegeChampSelect1\":{\"id\":33,\"name\":\"Nexus Siege: Siege Weapon Slot\",\"description\":\"In Nexus Siege, Summoner Spells are replaced with Siege Weapon Slots. Spend Crystal Shards to buy single-use Siege Weapons from the item shop, then use your Summoner Spell keys to activate them!\",\"tooltip\":\"\",\"maxrank\":1,\"cooldown\":[0],\"cooldownBurn\":\"0\",\"cost\":[0],\"costBurn\":\"0\",\"effect\":[null,[0],[0],[0],[0],[0],[0],[0],[0],[0],[0]],\"effectBurn\":[null,\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"key\":\"SummonerSiegeChampSelect1\",\"summonerLevel\":1,\"modes\":[\"SIEGE\"],\"costType\":\"\",\"maxammo\":\"-1\",\"range\":[2500],\"rangeBurn\":\"2500\",\"image\":{\"full\":\"SummonerSiegeChampSelect1.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":96,\"y\":48,\"w\":48,\"h\":48}},\"SummonerSiegeChampSelect2\":{\"id\":34,\"name\":\"Nexus Siege: Siege Weapon Slot\",\"description\":\"In Nexus Siege, Summoner Spells are replaced with Siege Weapon Slots. Spend Crystal Shards to buy single-use Siege Weapons from the item shop, then use your Summoner Spell keys to activate them!\",\"tooltip\":\"\",\"maxrank\":1,\"cooldown\":[0],\"cooldownBurn\":\"0\",\"cost\":[0],\"costBurn\":\"0\",\"effect\":[null,[0],[0],[0],[0],[0],[0],[0],[0],[0],[0]],\"effectBurn\":[null,\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"key\":\"SummonerSiegeChampSelect2\",\"summonerLevel\":1,\"modes\":[\"SIEGE\"],\"costType\":\"\",\"maxammo\":\"-1\",\"range\":[2500],\"rangeBurn\":\"2500\",\"image\":{\"full\":\"SummonerSiegeChampSelect2.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":144,\"y\":48,\"w\":48,\"h\":48}},\"SummonerSmite\":{\"id\":11,\"name\":\"Smite\",\"description\":\"Deals 390-1000 true damage (depending on champion level) to target epic or large monster or enemy minion. Restores Health based on your maximum life when used against monsters.\",\"tooltip\":\"Deals <span class=\\\"colorFEFCFF\\\">{{ f1 }}</span> true damage to target epic or large monster or enemy minion.  Against monsters, additionally restores <span class=\\\"colorFFFFFF\\\">{{ f6 }}</span> <span class=\\\"colorFF6666\\\">(+{{ f7 }})</span> Health.<br><br>Smite regains a charge every {{ f3 }} seconds, up to a maximum of 2 charges.\",\"maxrank\":1,\"cooldown\":[75],\"cooldownBurn\":\"75\",\"cost\":[0],\"costBurn\":\"0\",\"effect\":[null,[15],[0],[0],[0],[0],[0],[0],[0],[0],[0]],\"effectBurn\":[null,\"15\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"@player.level\",\"coeff\":[390,410,430,450,480,510,540,570,600,640,680,720,760,800,850,900,950,1000],\"key\":\"f1\"}],\"key\":\"SummonerSmite\",\"summonerLevel\":10,\"modes\":[\"CLASSIC\",\"TUTORIAL\",\"FIRSTBLOOD\",\"URF\",\"ARSR\",\"DOOMBOTSTEEMO\"],\"costType\":\"No Cost\",\"maxammo\":\"2\",\"range\":[500],\"rangeBurn\":\"500\",\"image\":{\"full\":\"SummonerSmite.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":192,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},\"SummonerSnowball\":{\"id\":32,\"name\":\"Mark\",\"description\":\"Throw a snowball in a straight line at your enemies. If it hits an enemy, they become marked, granting True Sight, and your champion can quickly travel to the marked target as a follow up.\",\"tooltip\":\"Throw a snowball a long distance, dealing {{ f1 }} true damage to the first enemy unit hit and granting <span class=\\\"coloree91d7\\\">True Sight</span> of the target. If it hits an enemy, this ability can be recast for {{ f2 }} seconds to Dash to the tagged unit, dealing an additional {{ f5 }} true damage. Dashing to the target will reduce the cooldown of Mark by {{ f3 }}%.<br><br><span class=\\\"colorFFFF00\\\">Mark projectiles are not stopped by spell shields or projectile mitigation.</span>\",\"maxrank\":1,\"cooldown\":[80],\"cooldownBurn\":\"80\",\"cost\":[0],\"costBurn\":\"0\",\"effect\":[null,[1200],[20],[10],[0.5],[0],[0],[0],[0],[0],[0]],\"effectBurn\":[null,\"1200\",\"20\",\"10\",\"0.5\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[],\"key\":\"SummonerSnowball\",\"summonerLevel\":1,\"modes\":[\"ARAM\",\"FIRSTBLOOD\"],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[1600],\"rangeBurn\":\"1600\",\"image\":{\"full\":\"SummonerSnowball.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":240,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"},\"SummonerTeleport\":{\"id\":12,\"name\":\"Teleport\",\"description\":\"After channeling for 4.5 seconds, teleports your champion to target allied structure, minion, or ward.\",\"tooltip\":\"After channeling for {{ f1 }} seconds, your champion teleports to target allied structure, minion, or ward.<br><br>You may reactivate Teleport to cancel it, placing it on a {{ f3 }} second cooldown.\",\"maxrank\":1,\"cooldown\":[0],\"cooldownBurn\":\"0\",\"cost\":[0],\"costBurn\":\"0\",\"effect\":[null,[4.5],[200],[300],[0],[0],[0],[0],[0],[0],[0]],\"effectBurn\":[null,\"4.5\",\"200\",\"300\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"],\"vars\":[{\"link\":\"@text\",\"coeff\":4,\"key\":\"f1\"}],\"key\":\"SummonerTeleport\",\"summonerLevel\":6,\"modes\":[\"CLASSIC\",\"TUTORIAL\",\"ASSASSINATE\",\"URF\",\"ARSR\",\"DOOMBOTSTEEMO\"],\"costType\":\"No Cost\",\"maxammo\":\"-1\",\"range\":[25000],\"rangeBurn\":\"25000\",\"image\":{\"full\":\"SummonerTeleport.png\",\"sprite\":\"spell0.png\",\"group\":\"spell\",\"x\":288,\"y\":48,\"w\":48,\"h\":48},\"resource\":\"No Cost\"}}"
  },
  {
    "path": "app.js",
    "content": "\"use strict\";\nvar express = require('express');\nvar http = require('http');\nvar path = require('path');\nvar favicon = require('serve-favicon');\nvar logger = require('morgan');\nvar bodyParser = require('body-parser');\nvar compress = require('compression');\n\n//middle ware\nvar overallData = require('./middleware/overall_data.js');\n\n//routes\nvar champion = require('./routes/champion');\nvar matchup = require('./routes/matchup');\nvar matchupJson = require('./routes/matchup_json');\nvar apiStatic = require('./routes/api_static');\nvar statistics = require('./routes/statistics');\nvar faq = require('./routes/faq');\nvar index = require('./routes/index');\n\nvar app = express();\n\napp.get('/*', function(req, res, next) { // redirect to http instead of www\n  if (req.headers.host.match(/^www/) !== null ) {\n    res.redirect('http://' + req.headers.host.replace(/^www\\./, '') + req.url);\n  } else {\n    next();     \n  }\n});\n\n// view engine setup\napp.set('views', path.join(__dirname, 'views'));\napp.set('view engine', 'ejs');\n\napp.use(compress());\napp.use(favicon(__dirname + '/public/favicon.ico'));\napp.use(logger('dev'));\napp.use(bodyParser.json({limit: '2kb', extended: true}));\napp.use(bodyParser.urlencoded({limit: '2kb', extended: true}));\n\napp.use(express.static(path.join(__dirname, 'public'), {maxAge:86400000})); //one day\n\n//pages\n//set cache headers for page now that we are utilizing cloudflare\napp.use(function(req, res, next){\n  res.setHeader('Cache-Control', 'public, max-age=86400'); //cache pages for 1 minute, if needed I can purge cache from cloud flare\n  next();\n});\n\napp.use(overallData);\n\napp.use('/champion', champion);\napp.use('/matchup', matchup);\napp.use('/matchupJson', matchupJson);\napp.use('/static', apiStatic);\n\napp.use('/statistics', statistics);\napp.use('/faq', faq);\napp.use('/', index);\n\n/// catch 404 and forwarding to error handler\napp.use(function(req, res, next) {\n    var err = new Error('Not Found');\n    err.status = 404;\n    next(err);\n});\n\n/// error handlers\n\n// development error handler\n// will print stacktrace\n\nif (app.get('env') === 'development') {\n  app.use(function(err, req, res, next) {\n      res.statusCode = err.status;\n      res.render('error', {\n          pageData:{\n            appName: 'core',\n            name:'error',\n            title: 'We got ourselves a problem...'\n          },\n          message: err.message,\n          error: err\n      });\n  });\n} else {\n  // production error handler\n  // no stacktraces leaked to user\n  app.use(function(err, req, res, next) {\n    res.statusCode = err.status;\n    res.render('error', {\n        pageData:{\n          appName: 'core',\n          name:'error',\n          title: 'We got ourselves a wild teemo problem...'\n        },\n        message: err.message,\n        error: {}\n    });\n  });\n}\n\nmodule.exports = app;\n"
  },
  {
    "path": "bash.txt",
    "content": "Mongodump file\nmongodump --db championgg --collection webchampionpages --out ./db\nmongodump --db championgg --collection webchampionroles --out ./db\nmongodump --db championgg --collection webmatchuppages --out ./db\nmongodump --db championgg --collection weboverallroledatas --out ./db\nmongodump --db championgg --collection webhomepagesummaries --out ./db\nmongodump --db championgg --collection webstatisticspages --out ./db\nmongodump --db championgg --collection weboverallrolestats --out ./db\ngit add -A && git commit -m \"updated data\" && git push\n\n\ncd /code/championweb\ngit pull\nmongorestore --db championgg --collection webchampionpages --drop db/championgg/webchampionpages.bson\nmongorestore --db championgg --collection webchampionroles --drop db/championgg/webchampionroles.bson\nmongorestore --db championgg --collection webmatchuppages --drop db/championgg/webmatchuppages.bson\nmongorestore --db championgg --collection weboverallroledatas --drop db/championgg/weboverallroledatas.bson\nmongorestore --db championgg --collection webhomepagesummaries --drop db/championgg/webhomepagesummaries.bson\nmongorestore --db championgg --collection webstatisticspages --drop db/championgg/webstatisticspages.bson\nmongorestore --db championgg --collection weboverallroledatas --drop db/championgg/weboverallstats.bson\ncd bin && NODE_ENV=production pm2 restart www.js\n\n\nNODE_ENV=production pm2 restart www.js\nNODE_ENV=updating pm2 restart www.js\nNODE_ENV=serverUpdate pm2 restart www.js\n\nNODE_ENV=production pm2 start www.js -i max\nNODE_ENV=updating pm2 start www.js -i max\nNODE_ENV=serverUpdate pm2 start www.js -i max\n\n\nUPDATES=aggregation node performUpdate\n\nmongodump --db leaguetimes --collection votes\n"
  },
  {
    "path": "bin/update_server.sh",
    "content": "sudo npm install\nmongorestore --db championgg --collection webchampionpages --drop db/championgg/webchampionpages.bson\nmongorestore --db championgg --collection webchampionroles --drop db/championgg/webchampionroles.bson\nmongorestore --db championgg --collection webmatchuppages --drop db/championgg/webmatchuppages.bson\nmongorestore --db championgg --collection weboverallroledatas --drop db/championgg/weboverallroledatas.bson\nmongorestore --db championgg --collection weboverallstats --drop db/championgg/weboverallstats.bson\nmongorestore --db championgg --collection webhomepagesummaries --drop db/championgg/webhomepagesummaries.bson\nmongorestore --db championgg --collection webstatisticspages --drop db/championgg/webstatisticspages.bson\ncd bin && NODE_ENV=production pm2 restart www.js\n"
  },
  {
    "path": "bin/www.js",
    "content": "#!/usr/bin/env node\n\"use strict\";\nvar debug = require('debug')('my-application');\nvar app = require('../app');\nvar db = require('../db');\n\napp.set('port', process.env.PORT || 80);\n\napp.listen(app.get('port'), function() {\n  console.log('Express server listening on port ' + app.get('port'));\n});\n"
  },
  {
    "path": "config/config.js",
    "content": "module.exports.config = {\n    worker: {\n        local: {\n            githubRoot: 'https://raw.githubusercontent.com/joel1st/championweb/master/',\n            githubRoute : 'https://raw.githubusercontent.com/joel1st/championweb/master/db/championgg/',\n            files: [\n                'webchampionpages.bson',\n                'webchampionpages.metadata.json',\n                'webchampionroles.bson',\n                'webchampionroles.metadata.json',\n                'webhomepagesummaries.bson',\n                'webhomepagesummaries.metadata.json',\n                'webmatchuppages.bson',\n                'webmatchuppages.metadata.json',\n                'weboverallroledatas.bson',\n                'weboverallroledatas.metadata.json',\n                'weboverallstats.bson',\n                'weboverallstats.metadata.json',\n                'webstatisticspages.bson',\n                'webstatisticspages.metadata.json'\n            ],\n            headline: 'headline.js',\n            tmpFolder: 'tmp/',\n            mongo: {\n                host: 'localhost',\n                port: '27017',\n                db: 'championgg',\n                user: '',\n                password: ''\n            },\n            queue: {\n                prefix: 'championggQ_',\n                host: 'localhost',\n                pass: '',\n                port: 6379\n            },\n            fastly: {\n                api_key: process.env.FASTLY_API_KEY,\n                purgeall: 'https://api.fastly.com/service/' + process.env.FASTLY_SERVICE_ID + '/purge_all'\n            }\n        },\n        production: {\n            githubRoot: 'https://raw.githubusercontent.com/joel1st/championweb/master/',\n            githubRoute : 'https://raw.githubusercontent.com/joel1st/championweb/master/db/championgg/',\n            files: [\n                'webchampionpages.bson',\n                'webchampionpages.metadata.json',\n                'webchampionroles.bson',\n                'webchampionroles.metadata.json',\n                'webhomepagesummaries.bson',\n                'webhomepagesummaries.metadata.json',\n                'webmatchuppages.bson',\n                'webmatchuppages.metadata.json',\n                'weboverallroledatas.bson',\n                'weboverallroledatas.metadata.json',\n                'weboverallstats.bson',\n                'weboverallstats.metadata.json',\n                'webstatisticspages.bson',\n                'webstatisticspages.metadata.json'\n            ],\n            headline: 'headline.js',\n            tmpFolder: 'tmp/',\n            mongo: {\n                host: process.env.WORKER_MONGO_HOST,\n                port: process.env.WORKER_MONGO_PORT,\n                db: process.env.WORKER_MONGO_DB,\n                user: process.env.WORKER_MONGO_USER,\n                password: process.env.WORKER_MONGO_PASSWORD\n            },\n            queue: {\n                prefix: process.env.QUEUE_PREFIX || 'championggQ_',\n                host: process.env.REDIS_HOST || 'localhost',\n                pass: process.env.REDIS_PASS || '',\n                port: process.env.REDIS_PORT || 6379\n            },\n            fastly: {\n                api_key: process.env.FASTLY_API_KEY,\n                purgeall: 'https://api.fastly.com/service/' + process.env.FASTLY_SERVICE_ID + '/purge_all'\n            }\n        }\n    }\n};\n"
  },
  {
    "path": "db/championgg/webchampionpages.metadata.json",
    "content": "{ \"indexes\" : [ { \"v\" : 1, \"key\" : { \"_id\" : 1 }, \"name\" : \"_id_\", \"ns\" : \"championgg.webchampionpages\" } ] }"
  },
  {
    "path": "db/championgg/webchampionroles.metadata.json",
    "content": "{ \"indexes\" : [ { \"v\" : 1, \"key\" : { \"_id\" : 1 }, \"name\" : \"_id_\", \"ns\" : \"championgg.webchampionroles\" } ] }"
  },
  {
    "path": "db/championgg/webhomepagesummaries.metadata.json",
    "content": "{ \"indexes\" : [ { \"v\" : 1, \"key\" : { \"_id\" : 1 }, \"name\" : \"_id_\", \"ns\" : \"championgg.webhomepagesummaries\" } ] }"
  },
  {
    "path": "db/championgg/webmatchuppages.metadata.json",
    "content": "{ \"indexes\" : [ { \"v\" : 1, \"key\" : { \"_id\" : 1 }, \"name\" : \"_id_\", \"ns\" : \"championgg.webmatchuppages\" } ] }"
  },
  {
    "path": "db/championgg/weboverallroledatas.metadata.json",
    "content": "{ \"indexes\" : [ { \"v\" : 1, \"key\" : { \"_id\" : 1 }, \"name\" : \"_id_\", \"ns\" : \"championgg.weboverallroledatas\" } ] }"
  },
  {
    "path": "db/championgg/weboverallstats.metadata.json",
    "content": "{ \"indexes\" : [ { \"v\" : 1, \"key\" : { \"_id\" : 1 }, \"name\" : \"_id_\", \"ns\" : \"championgg.weboverallstats\" } ] }"
  },
  {
    "path": "db/championgg/webstatisticspages.metadata.json",
    "content": "{ \"indexes\" : [ { \"v\" : 1, \"key\" : { \"_id\" : 1 }, \"name\" : \"_id_\", \"ns\" : \"championgg.webstatisticspages\" } ] }"
  },
  {
    "path": "db.js",
    "content": "\"use strict\";\nvar mongoose = require('mongoose');\nmongoose.connect(process.env.MONGO_DB || 'mongodb://localhost/championgg');\nvar db = mongoose.connection;\n\ndb.on('error', console.error.bind(console, 'connection error:'));\n\ndb.once('open', function () {\n\tconsole.log('Connection Made!');\n});\n"
  },
  {
    "path": "gruntfile.js",
    "content": "module.exports = function(grunt){\n\t\"use strict\";\n\trequire(\"matchdep\").filterDev(\"grunt-*\").forEach(grunt.loadNpmTasks);\n\n\tgrunt.initConfig({\n\t\twatch: {\n\t\t\tjs: {\n\t\t\t\tfiles: [\n\t\t\t\t\t'routes/*.js',\n\t\t\t\t\t'bin/*.js',\n\t\t\t\t\t'logic/*.js',\n\t\t\t\t\t'models/*.js',\n\t\t\t\t\t'public/js/*.js',\n\t\t\t\t\t'*.js'   \n\t\t\t\t],\n\t\t\t\ttasks: ['jshint']\n\t\t\t}\n\t\t},\n\t\tpkg: grunt.file.readJSON('package.json'),\n\t\tjshint: {\n\t\t\tfiles: [\n\t\t\t\t\t'routes/*.js',\n\t\t\t\t\t'bin/*.js',\n\t\t\t\t\t'logic/*.js',\n\t\t\t\t\t'models/*.js',\n\t\t\t\t\t'public/js/*.js',\n\t\t\t\t\t'!public/js/master.min.js',\n\t\t\t\t\t'*.js' \n\t\t\t\t],\n\t\t\toptions: {\n\t\t\t// options here to override JSHint defaults\n\t\t\t\tnode: true,\n\t\t\t\tloopfunc: true,\n\t\t\t\tglobals: {\n\t\t\t\t\tjQuery: false,\n\t\t\t\t\tconsole: true,\n\t\t\t\t\tmodule: true,\n\t\t\t\t\trequire: true\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tconcat: {\n\t\t    js: {\n\t\t      \tsrc: ['public/dist/js/angular.js', 'public/dist/js/angular-bootstrap.js', \n\t\t      \t\t'public/dist/js/dirDisqus.js', 'public/dist/js/chart.js',\n\t\t      \t\t'public/dist/js/tc-angular-chartjs.js', 'public/js/champion_data.js', 'public/js/chart_options.js', 'public/js/championgg_tooltip.js',\n\t\t      \t\t 'public/js/app.js', 'public/js/champion_page.js', 'public/js/matchup_page.js', 'public/js/statistics_page.js'],\n\t\t      \tdest: 'public/js/master.min.js'\n\t\t    },\n\t\t},\n\t    uglify: {\n\t\t\tbuild:  {\n\t\t\t    files: {\n\t\t\t      \t'public/js/master.min.js': 'public/js/master.min.js'\n\t\t\t    }\n\t\t\t}\n\t\t},\n\t\tcssmin: {\n\t\t    build: {\n\t\t        src: ['public/css/master.css', 'public/css/sprite.css'],\n\t\t        dest: 'public/css/master.min.css'\n\t\t    }\n    \t}\n    });\n\t\n\tgrunt.registerTask('production', ['concat', 'uglify','cssmin']);\n\tgrunt.registerTask('default', []);\n};"
  },
  {
    "path": "headline.js",
    "content": "// Message to display at the top of the site.\n// Standard message: We are currently aggregating patch 6.14 data - check back in 3 days!\nmodule.exports = \"\";\n"
  },
  {
    "path": "logic/lower_case_champ.js",
    "content": "\"use strict\";\nvar champList = require('../api_data/champions.json');\n\n/**\n * Converts the champion list keys to lower case. It then compares\n * the input to see if there is a match. (this is useful for checking,\n * if a url has entered a champion key - but doesn't match the correct casing).\n * @param  {string} champName - the champ name to compare against the champList.\n * @return {string|undefined} - if a match is found, the champ key is returned, otherwise undefined.\n */\nvar lowerCaseChamp = function(champName) {\n    for (var prop in champList) {\n        if (prop.toLowerCase() === champName.toLowerCase()) {\n            return prop;\n        }\n    }\n};\n\n\nmodule.exports = lowerCaseChamp;"
  },
  {
    "path": "logic/produce_error.js",
    "content": "\"use strict\";\nvar errors = {\n    champNotFound: 'That champ or role doesn\\'t appear to exist!',\n    pageNotFound: 'We couldn\\'t find the page you are looking for - sorry!',\n    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!',\n    invalidMatchup: 'That appears to be an invalid or old matchup!'\n};\n\n/**\n * A function for generating errors. \n * @param  {string} errorType  - the key for the error type (which corresponds with the keys \n *                             in the error object above).\n * @param  {number} errorNumber - the response number of the error (defaults to 404).\n * @return {object} - the error object.\n */\nvar produceError = function(errorType, errorNumber) {\n    var err = new Error(errors[errorType]);\n    err.status = errorNumber || 404;\n    return err;\n};\n\nmodule.exports = produceError;"
  },
  {
    "path": "logic/role_hash_table.js",
    "content": "\"use strict\";\n/**\n * The varius role keys and values.\n * Used for determining legitimate roles for champion page routes.\n */\nvar roleList = {\n\t'Top': 'TOP',\n\t'Middle': 'MIDDLE',\n\t'Support': 'DUO_SUPPORT',\n\t'ADC': 'DUO_CARRY',\n\t'Jungle': 'JUNGLE',\n\t'top': 'TOP',\n\t'middle': 'MIDDLE',\n\t'support': 'DUO_SUPPORT',\n\t'adc': 'DUO_CARRY',\n\t'jungle': 'JUNGLE',\n\t'adcsupport': 'ADCSUPPORT',\n\t'synergy': 'SYNERGY'\n};\n\nvar roleKey = {\n\t'TOP': 'Top',\n\t'MIDDLE': 'Middle',\n\t'DUO_SUPPORT': 'Support',\n\t'DUO_CARRY': 'ADC',\n\t'JUNGLE': 'Jungle',\n\t'ADCSUPPORT': 'adcsupport',\n\t'SYNERGY': 'synergy'\n};\n\nexports.roleList = roleList;\nexports.roleKey = roleKey;"
  },
  {
    "path": "middleware/overall_data.js",
    "content": "var WebOverallStats = require('../models/web_overall_stats.js');\nvar produceError = require('../logic/produce_error.js');\n\n/**\n * The core object is used in all views template for overall data.\n * Values from database are loaded from the webOverallStats\n * collection and added to core object.\n * @type {Object}\n */ \nvar ddPatch = require('../api_data/dd_patch.json').ddPatch\nvar core = {\n\tddPatch: ddPatch,\n\tresetCache: ddPatch + Math.random().toFixed(6),\n\tmasteryOrder: ['Offense','Defense','Utility'],\n\theadline: require('../headline.js')\n};\n// Data retrieved from DB:\n// gamesAnalyzed:\"3,549,640\",\n// patch:\"5.10\",\n// patchHistory: [\"5.6\",\"5.7\",\"5.8\",\"5.9\",\"5.10\"]\n\nmodule.exports = function(req, res, next){\n\t/**\n\t * Set the core object as a local for the view\n\t */\n    res.locals.core = core;\n\n    /**\n     * If no data has been retrieved from data base yet,\n     * request overall stats data from collection and add it\n     * to the core object. \n     */\n\tif (!core.championsAnalyzed){\n\t\tWebOverallStats.findOne({}, function(err, data) {\n\t\t    if (err) {\n\t\t        return next(produceError('serverMaintenance', 503));\n\t\t    } else if (!data) {\n\t\t        return next(produceError('serverMaintenance', 503));\n\t\t    } else {\n\t\t        core.championsAnalyzed = data.championsAnalyzed;\n\t\t\t\tcore.patch = data.patch;\n\t\t\t\tcore.patchHistory = data.patchHistory;\n\t\t\t\tnext();\n\t\t    }\n\t\t});\n\t} else {\t\n\t\tnext();\n\t}\n};\n"
  },
  {
    "path": "models/web_champion_page.js",
    "content": "var mongoose = require('mongoose');\n\nvar webChampionPage = new mongoose.Schema({\n\trole: String,\n\tkey: String,\n\tgeneral: [{\n\t\ttitle:String,\n\t\ttitleLink: String,\n\t\tval:String,\n\t\tposition:Number,\n\t\tchange:Number\n\t}],\n\toverallPosition: {\n\t\tposition: Number, \n\t\tchange:Number\n\t},\n\tchampionMatrix:[Number],\n\tpatchPlay:[Number],\n\tgameLength:[Number],\n\texperienceRate:[Number],\n\texperienceSample:[Number],\n\tpatchWin:[Number],\n\tdmgComposition:{\n\t\tphysicalDmg: Number,\n\t\tmagicDmg: Number,\n\t\ttrueDmg: Number\n\t},\n\titems:{\n\t\tmostGames: {\n\t\t\titems: [{\n\t\t\t\tid:Number,\n\t\t\t\tname:String,\n\t\t\t}],\n\t\t\tgames: Number,\n\t\t\twinPercent: Number\n\t\t},\n\t\thighestWinPercent: {\n\t\t\titems: [{\n\t\t\t\tid:Number,\n\t\t\t\tname:String,\n\t\t\t}],\n\t\t\tgames: Number,\n\t\t\twinPercent: Number\n\t\t}\n\t},\n\tfirstItems:{\n\t\tmostGames: {\n\t\t\titems: [{\n\t\t\t\tid:Number,\n\t\t\t\tname:String,\n\t\t\t}],\n\t\t\tgames: Number,\n\t\t\twinPercent: Number\n\t\t},\n\t\thighestWinPercent: {\n\t\t\titems: [{\n\t\t\t\tid:Number,\n\t\t\t\tname:String,\n\t\t\t}],\n\t\t\tgames: Number,\n\t\t\twinPercent: Number\n\t\t}\n\t},\n\ttrinkets: [{\n\t\titem: {\n\t\t\tid:Number,\n\t\t\tname:String,\n\t\t},\n\t\tgames: Number,\n\t\twinPercent: Number\n\t}],\n\tsummoners:{\n\t\tmostGames: {\n\t\t\tsummoner1: {\n\t\t\t\tname:String,\n\t\t\t\turl: String\n\t\t\t},\n\t\t\tsummoner2: {\n\t\t\t\tname:String,\n\t\t\t\turl: String\n\t\t\t},\n\t\t\tgames: Number,\n\t\t\twinPercent: Number\n\t\t},\n\t\thighestWinPercent: {\n\t\t\tsummoner1: {\n\t\t\t\tname:String,\n\t\t\t\turl: String\n\t\t\t},\n\t\t\tsummoner2: {\n\t\t\t\tname:String,\n\t\t\t\turl: String\n\t\t\t},\n\t\t\tgames: Number,\n\t\t\twinPercent: Number\n\t\t}\n\t},\n\tskills:{\n\t\tskillInfo: [{\n\t\t\tname: String,\n\t\t\timg: String,\n\t\t\tkey: String\n\t\t}],\n\t\tmostGames: {\n\t\t\torder: [String],\n\t\t\tgames: Number,\n\t\t\twinPercent: Number\n\t\t},\n\t\thighestWinPercent: {\n\t\t\torder: [String],\n\t\t\tgames: Number,\n\t\t\twinPercent: Number\n\t\t}\n\t},\n\tmasteries:{\n\t\tmostGames: {\n\t\t\tmasteries: [{\n\t\t\t\ttree: String,\n\t\t\t\ttotal: Number,\n\t\t\t\tdata: {row1: [{mastery:Number, points:Number}],\n\t\t\t\t\t\trow2: [{mastery:Number, points:Number}],\n\t\t\t\t\t\trow3: [{mastery:Number, points:Number}],\n\t\t\t\t\t\trow4: [{mastery:Number, points:Number}],\n\t\t\t\t\t\trow5: [{mastery:Number, points:Number}],\n\t\t\t\t\t\trow6: [{mastery:Number, points:Number}]}\n\t\t\t}],\n\t\t\tgames: Number,\n\t\t\twinPercent: Number\n\t\t},\n\t\thighestWinPercent: {\n\t\t\tmasteries: [{\n\t\t\t\ttree: String,\n\t\t\t\ttotal: Number,\n\t\t\t\tdata: {row1: [{mastery:Number, points:Number}],\n\t\t\t\t\t\trow2: [{mastery:Number, points:Number}],\n\t\t\t\t\t\trow3: [{mastery:Number, points:Number}],\n\t\t\t\t\t\trow4: [{mastery:Number, points:Number}],\n\t\t\t\t\t\trow5: [{mastery:Number, points:Number}],\n\t\t\t\t\t\trow6: [{mastery:Number, points:Number}]}\n\t\t\t}],\n\t\t\tgames: Number,\n\t\t\twinPercent: Number\n\t\t}\n\t},\n\trunes:{\n\t\tmostGames: {\n\t\t\trunes: [{\n\t\t\t\tid: Number,\n\t\t\t\tnumber: Number,\n\t\t\t\tname: String,\n\t\t\t\timg: String,\n\t\t\t\tdescription: String,\n\t\t\t}],\n\t\t\tgames: Number,\n\t\t\twinPercent: Number\n\t\t},\n\t\thighestWinPercent: {\n\t\t\trunes: [{\n\t\t\t\tid: Number,\n\t\t\t\tnumber: Number,\n\t\t\t\tname: String,\n\t\t\t\timg: String,\n\t\t\t\tdescription: String,\n\t\t\t}],\n\t\t\tgames: Number,\n\t\t\twinPercent: Number\n\t\t}\n\t},\n\tunique:{ //champions like viktor\n\t\tmostGames: {\n\t\t\torder: [Number],\n\t\t\tgames: Number,\n\t\t\twinPercent: Number\n\t\t},\n\t\thighestWinPercent: {\n\t\t\torder: [Number],\n\t\t\tgames: Number,\n\t\t\twinPercent: Number\n\t\t}\n\t},\n\tmatchups:[{ // For all champions\n\t\tkey:String,\n\t\tstatScore:Number,\n\t\tgames:Number,\n\t\twinRate:Number,\n\t\twinRateChange:Number\n\t}],\n\tadcsupport:[{ // only support/ad\n\t\tkey:String,\n\t\tstatScore:Number,\n\t\tgames:Number,\n\t\twinRate:Number,\n\t\twinRateChange:Number\n\t}],\n\tsynergy:[{ // only support/ad\n\t\tkey:String,\n\t\tstatScore:Number,\n\t\tgames:Number,\n\t\twinRate:Number,\n\t\twinRateChange:Number\n\t}]\n});\n\nmodule.exports = mongoose.model('WebChampionPage', webChampionPage);"
  },
  {
    "path": "models/web_champion_roles.js",
    "content": "var mongoose = require('mongoose');\n/**\n * Used to build up the long champ list on the homepage and the \n * individual champion roles on the left hand side of the champion pages.\n */\nvar webChampionRoles = new mongoose.Schema({\n\tkey: String,\n\tname: String,\n\tlastUpdated: Number,\n\troles:[{\n\t\trole:String,\n\t\ttitle:String,\n\t\tgames:Number,\n\t\tpercentPlayed:Number\n\t}]\n});\n\nmodule.exports = mongoose.model('WebChampionRoles', webChampionRoles);"
  },
  {
    "path": "models/web_home_page_summaries.js",
    "content": "var mongoose = require('mongoose');\n\nvar webHomePageSummaries = new mongoose.Schema({\n\tid: Number,\n\tdata: [{\n\t\ttitle: String,\n\t\ttotal: Number,\n\t\tmostImproved:{\n\t\t\tkey: String,\n\t\t\tname: String,\n\t\t\tdifference: Number,\n\t\t\toverall: Number\n\t\t},\n\t\tleastImproved:{\n\t\t\tkey: String,\n\t\t\tname: String,\n\t\t\tdifference: Number,\n\t\t\toverall: Number\n\t\t},\n\t\thighestWinRate:{\n\t\t\tkey: String,\n\t\t\tname: String,\n\t\t\tvalue: Number\n\t\t},\n\t\tlowestWinRate:{\n\t\t\tkey: String,\n\t\t\tname: String,\n\t\t\tvalue: Number\n\t\t},\n\t\tbestOverall:{\n\t\t\tkey: String,\n\t\t\tname: String,\n\t\t\tvalue: Number\n\t\t},\n\t\tworstOverall:{\n\t\t\tkey: String,\n\t\t\tname: String,\n\t\t\tvalue: Number\n\t\t}\n\t}]\n});\n\nmodule.exports = mongoose.model('WebHomePageSummaries', webHomePageSummaries);"
  },
  {
    "path": "models/web_matchup_page.js",
    "content": "var mongoose = require('mongoose');\n\nvar webMatchupPage = new mongoose.Schema({\n\tchamp1: {\n\t\tid: Number,\n\t\tkey: String,\n\t\tname: String,\n\t\trole: String,\n\t\troleTitle: String,\n\t\tperformance: Number\n\t},\n\tchamp2: {\n\t\tid: Number,\n\t\tkey: String,\n\t\tname: String,\n\t\trole: String,\n\t\troleTitle: String,\n\t\tperformance: Number\n\t},\n\trole: String,\n\tdateAdded: Number,\n\ttotalGames:Number,\n\tgeneral: [{\n\t\ttitle: String,\n\t\tchamp1:{\n\t\t\tval: Number,\n\t\t\tchange: Number,\n\t\t\tscore: Number //score to use is champ 1 is quering page\n\t\t},\n\t\tchamp2:{\n\t\t\tval: Number,\n\t\t\tchange: Number,\n\t\t\tscore: Number //score to use if champ 2 is quering page\n\t\t},\n\t}],\n\tchampionMatrix:{\n\t\tlabels:[String],\n\t\tchamp1:[Number],\n\t\tchamp2:[Number]\n\t},\n\tgoldLength:{\n\t\tchamp1:[Number],\n\t\tchamp2:[Number]\n\t}\n});\n\nmodule.exports = mongoose.model('WebMatchupPage', webMatchupPage);"
  },
  {
    "path": "models/web_overall_role_data.js",
    "content": "var mongoose = require('mongoose');\n\nvar webOverallRoleData = new mongoose.Schema({\n\trole:String,\n    totalNumber: Number,\n    matrixLabels:[String],\n    patchPlay: [Number]\n});\n\nmodule.exports = mongoose.model('WebOverallRoleData', webOverallRoleData);"
  },
  {
    "path": "models/web_overall_stats.js",
    "content": "var mongoose = require('mongoose');\n\nvar webOverallStats = new mongoose.Schema({\n\tpatchHistory: [String],\n\tpatch: String,\n\tchampionsAnalyzed: String,\n});\n\nmodule.exports = mongoose.model('WebOverallStats', webOverallStats);"
  },
  {
    "path": "models/web_statistics_page.js",
    "content": "var mongoose = require('mongoose');\n\nvar webStatisticsPage = new mongoose.Schema({\n\tkey: String,\n\ttitle: String,\n\trole: String,\n\tgeneral: {\n\t\twinPercent: Number,\n\t\tplayPercent: Number,\n\t\tbanRate: Number,\n\t\texperience: Number,\n\t\tkills: Number,\n\t\tdeaths: Number,\n\t\tassists: Number,\n\t\ttotalDamageDealtToChampions: Number,\n\t\ttotalDamageTaken: Number,\n\t\ttotalHeal: Number,\n\t\tlargestKillingSpree: Number,\n\t\tminionsKilled: Number,\n\t\tneutralMinionsKilledTeamJungle: Number,\n\t\tneutralMinionsKilledEnemyJungle: Number,\n\t\tgoldEarned: Number,\n\t\toverallPosition: Number,\n\t\toverallPositionChange: Number\n\t}\n});\n\nmodule.exports = mongoose.model('WebStatisticsPage', webStatisticsPage);"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"application-name\",\n  \"version\": \"0.0.1\",\n  \"private\": true,\n  \"scripts\": {\n    \"start\": \"node ./bin/www\"\n  },\n  \"dependencies\": {\n    \"body-parser\": \"1.9.2\",\n    \"compression\": \"1.2.0\",\n    \"debug\": \"2.1.0 \",\n    \"ejs\": \"1.0.0\",\n    \"express\": \"4.10.2\",\n    \"mongoose\": \"3.8.19\",\n    \"morgan\": \"1.5.0 \",\n    \"q\": \"1.4.1\",\n    \"serve-favicon\": \"2.1.7\"\n  },\n  \"devDependencies\": {\n    \"grunt\": \"~0.4.0\",\n    \"grunt-contrib-cssmin\": \"*\",\n    \"grunt-contrib-uglify\": \"*\",\n    \"grunt-contrib-watch\": \"*\",\n    \"grunt-contrib-concat\": \"*\",\n    \"grunt-contrib-jshint\": \"*\",\n    \"grunt-htmlhint\": \"*\",\n    \"matchdep\": \"*\"\n  }\n}\n"
  },
  {
    "path": "public/cpmstar/cpmstar_siteskin_iframebuster.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n    <title></title>\n</head>\n<body>\n<script type=\"text/javascript\">\n    if (!window.JSON) { //for older browser support: see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON\n        window.JSON = {\n            parse: function (sJSON) { return eval(\"(\" + sJSON + \")\"); },\n            stringify: function (vContent) {\n                if (vContent instanceof Object) {\n                    var sOutput = \"\";\n                    if (vContent.constructor === Array) {\n                        for (var nId = 0; nId < vContent.length; sOutput += this.stringify(vContent[nId]) + \",\", nId++);\n                        return \"[\" + sOutput.substr(0, sOutput.length - 1) + \"]\";\n                    }\n                    if (vContent.toString !== Object.prototype.toString) { return \"\\\"\" + vContent.toString().replace(/\"/g, \"\\\\$&\") + \"\\\"\"; }\n                    for (var sProp in vContent) { sOutput += \"\\\"\" + sProp.replace(/\"/g, \"\\\\$&\") + \"\\\":\" + this.stringify(vContent[sProp]) + \",\"; }\n                    return \"{\" + sOutput.substr(0, sOutput.length - 1) + \"}\";\n                }\n                return typeof vContent === \"string\" ? \"\\\"\" + vContent.replace(/\"/g, \"\\\\$&\") + \"\\\"\" : String(vContent);\n            }\n        };\n    }\n    var getParameterByName = function (name) {\n        name = name.replace(/[\\[]/, \"\\\\\\[\").replace(/[\\]]/, \"\\\\\\]\");\n        var regex = new RegExp(\"[\\\\?&]\" + name + \"=([^&#]*)\"),\n                results = regex.exec(location.search);\n        return results == null ? \"\" : decodeURIComponent(results[1].replace(/\\+/g, \" \"));\n    }\n    var cpmstar_siteskin_settings = JSON.parse(getParameterByName(\"cpmstar_siteskin_settings\"));    //this can't be namespaced because the site skin script needs to access it\n    cpmstar_siteskin_settings.d = parent.document;\n    (function () {\n        var t = document.createElement('script'); t.type = 'text/javascript'; t.async = 1; t.src = \"//server.cpmstar.com/cached/js/siteskin_v100.pack.js\"; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(t, s);\n    })();\n</script>\n</body>"
  },
  {
    "path": "public/css/master.css",
    "content": "body{\n  position:relative;\n}\n\n.tooltip-hover{\n      z-index:88888888888;\n      position:absolute;\n      opacity:0;\n      left:0;\n      right:0;\n      display:block;\n      text-align:center;\n      width:80%;\n      max-width:300px;\n      min-width:120px;\n      transition:opacity 0.2s;\n    }\n\n    .tooltip-hover > .primary-content{\n      padding:10px;\n      background-color:rgba(1,1,1,0.85);\n      color:white;\n      border-radius: 3px;\n      z-index:99999999;\n      }\n\n    .arrow-down {\n      width: 0; \n      height: 0; \n      margin:auto;\n      border-left: 5px solid transparent;\n      border-right: 5px solid transparent;\n      border-top: 10px solid rgba(1,1,1,0.85);\n    }\n\n.center-google{\n  text-align:center;\n  position:relative;\n}\n\n.mastery-row{\n  display: table;\n  margin-left: auto;\n  margin-right: auto;\n}\na {\ncolor: #89f5a2;\n}\n.nav>li {\nposition: relative;\n/* display: block; */\ndisplay: inline-block;\n}\na:hover, a:focus {\ncolor: #FCFF90;\n/* text-decoration: underline; */\n}\n.primary-hue{\n\n    /* background-color: rgba(205, 255, 240, 0.04); */\n\n    /* max-width: 1400px; */\n\n    margin: auto;\n\n    /* background: url('../img/bg.jpg'); */;\n\n}\n[ng\\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {\ndisplay: none !important;\n}\n\n.table {\n  table-layout: fixed;\n}\n\n::-moz-selection { /* Code for Firefox */\n    color: white;\n    background: #A5FFBA;\n}\n\n::selection {\n    color: white; \n    background: #A5FFBA;\n}\n\n.table-striped>tbody>tr:nth-child(odd)>td, .table-striped>tbody>tr:nth-child(odd)>th {\nbackground-color: #222425;\n}\n.visible-xxs{\n      display:none;\n    }\n.col-xxs-1, .col-xxs-2, .col-xxs-3, .col-xxs-4,\n.col-xxs-5, .col-xxs-6, .col-xxs-7, .col-xxs-8,\n.col-xxs-9, .col-xxs-10, .col-xxs-11, .col-xxs-12 {\n    min-height: 1px;\n    padding-left: 15px;\n    padding-right: 15px;\n    position: relative;\n}\n\n@media (max-width: 600px) {\n    .visible-xxs{\n      display:block;\n    }\n    .col-xxs-1,\n    .col-xxs-2,\n    .col-xxs-3,\n    .col-xxs-4,\n    .col-xxs-5,\n    .col-xxs-6,\n    .col-xxs-7,\n    .col-xxs-8,\n    .col-xxs-9,\n    .col-xxs-10,\n    .col-xxs-11 {\n        float: left;\n    }\n\n    .col-xxs-1 {\n        width: 8.333333333333332%;\n    }\n\n    .col-xxs-2 {\n        width: 16.666666666666664%;\n    }\n\n    .col-xxs-3 {\n        width: 25%;\n    }\n\n    .col-xxs-4 {\n        width: 33.33333333333333%;\n    }\n\n    .col-xxs-5 {\n        width: 41.66666666666667%;\n    }\n\n    .col-xxs-6 {\n        width: 50%;\n    }\n\n    .col-xxs-7 {\n        width: 58.333333333333336%;\n    }\n\n    .col-xxs-8 {\n        width: 66.66666666666666%;\n    }\n\n    .col-xxs-9 {\n        width: 75%;\n    }\n\n    .col-xxs-10 {\n        width: 83.33333333333334%;\n    }\n\n    .col-xxs-11 {\n        width: 91.66666666666666%;\n    }\n\n    .col-xxs-12 {\n        width: 100%;\n    }\n\n    .col-xxs-push-1 {\n        left: 8.333333333333332%;\n    }\n\n    .col-xxs-push-2 {\n        left: 16.666666666666664%;\n    }\n\n    .col-xxs-push-3 {\n        left: 25%;\n    }\n\n    .col-xss-push-4 {\n        left: 33.33333333333333%;\n    }\n\n    .col-xxs-push-5 {\n        left: 41.66666666666667%;\n    }\n\n    .col-xxs-push-6 {\n        left: 50%;\n    }\n\n    .col-xxs-push-7 {\n        left: 58.333333333333336%;\n    }\n\n    .col-xxs-push-8 {\n        left: 66.66666666666666%;\n    }\n\n    .col-xxs-push-9 {\n        left: 75%;\n    }\n\n    .col-xxs-push-10 {\n       left: 83.33333333333334%;\n    }\n\n    .col-xxs-push-11 {\n       left: 91.66666666666666%;\n    }\n\n    .col-xxs-pull-1 {\n        right: 8.333333333333332%;\n    }\n\n    .col-xxs-pull-2 {\n        right: 16.666666666666664%;\n    }\n\n    .col-xxs-pull-3 {\n        right: 25%;\n    }\n\n    .col-xxs-pull-4 {\n        right: 33.33333333333333%;\n    }\n\n    .col-xxs-pull-5 {\n        right: 41.66666666666667%;\n    }\n\n    .col-xxs-pull-6 {\n        right: 50%;\n    }\n\n    .col-xxs-pull-7 {\n        right: 58.333333333333336%;\n    }\n\n    .col-xxs-pull-8 {\n        right: 66.66666666666666%;\n    }\n\n    .col-xxs-pull-9 {\n        right: 75%;\n    }\n\n    .col-xxs-pull-10 {\n        right: 83.33333333333334%;\n    }\n\n    .col-xxs-pull-11 {\n        right: 91.66666666666666%;\n    }\n\n    .col-xxs-offset-1 {\n        margin-left: 8.333333333333332%;\n    }\n\n    .col-xxs-offset-2 {\n        margin-left: 16.666666666666664%;\n    }\n\n    .col-xxs-offset-3 {\n        margin-left: 25%;\n    }\n\n    .col-xxs-offset-4 {\n        margin-left: 33.33333333333333%;\n    }\n\n    .col-xxs-offset-5 {\n        margin-left: 41.66666666666667%;\n    }\n\n    .col-xxs-offset-6 {\n        margin-left: 50%;\n    }\n\n    .col-xxs-offset-7 {\n        margin-left: 58.333333333333336%;\n    }\n\n    .col-xxs-offset-8 {\n        margin-left: 66.66666666666666%;\n    }\n\n    .col-xxs-offset-9 {\n        margin-left: 75%;\n    }\n\n    .col-xxs-offset-10 {\n        margin-left: 83.33333333333334%;\n    }\n\n    .col-xxs-offset-11 {\n        margin-left: 91.66666666666666%;\n    }\n}    \n      /* cyrillic-ext */\n@font-face {\n  font-family: 'Roboto';\n  font-style: normal;\n  font-weight: 400;\n  src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/ek4gzZ-GeXAPcSbHtCeQI_esZW2xOQ-xsNqO47m55DA.woff2) format('woff2');\n  unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;\n}\n/* cyrillic */\n@font-face {\n  font-family: 'Roboto';\n  font-style: normal;\n  font-weight: 400;\n  src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/mErvLBYg_cXG3rLvUsKT_fesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');\n  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;\n}\n/* greek-ext */\n@font-face {\n  font-family: 'Roboto';\n  font-style: normal;\n  font-weight: 400;\n  src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/-2n2p-_Y08sg57CNWQfKNvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');\n  unicode-range: U+1F00-1FFF;\n}\n/* greek */\n@font-face {\n  font-family: 'Roboto';\n  font-style: normal;\n  font-weight: 400;\n  src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/u0TOpm082MNkS5K0Q4rhqvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');\n  unicode-range: U+0370-03FF;\n}\n/* vietnamese */\n@font-face {\n  font-family: 'Roboto';\n  font-style: normal;\n  font-weight: 400;\n  src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/NdF9MtnOpLzo-noMoG0miPesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');\n  unicode-range: U+0102-0103, U+1EA0-1EF1, U+20AB;\n}\n/* latin-ext */\n@font-face {\n  font-family: 'Roboto';\n  font-style: normal;\n  font-weight: 400;\n  src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/Fcx7Wwv8OzT71A3E1XOAjvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');\n  unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;\n}\n/* latin */\n@font-face {\n  font-family: 'Roboto';\n  font-style: normal;\n  font-weight: 400;\n  src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/fg2nPs59wPnJ0blURyMU3PesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');\n  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;\n}\n/* cyrillic-ext */\n@font-face {\n  font-family: 'Roboto';\n  font-style: normal;\n  font-weight: 700;\n  src: local('Roboto Bold'), local('Roboto-Bold'), url(http://fonts.gstatic.com/s/roboto/v14/77FXFjRbGzN4aCrSFhlh3hJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');\n  unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;\n}\n/* cyrillic */\n@font-face {\n  font-family: 'Roboto';\n  font-style: normal;\n  font-weight: 700;\n  src: local('Roboto Bold'), local('Roboto-Bold'), url(http://fonts.gstatic.com/s/roboto/v14/isZ-wbCXNKAbnjo6_TwHThJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');\n  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;\n}\n/* greek-ext */\n@font-face {\n  font-family: 'Roboto';\n  font-style: normal;\n  font-weight: 700;\n  src: local('Roboto Bold'), local('Roboto-Bold'), url(http://fonts.gstatic.com/s/roboto/v14/UX6i4JxQDm3fVTc1CPuwqhJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');\n  unicode-range: U+1F00-1FFF;\n}\n/* greek */\n@font-face {\n  font-family: 'Roboto';\n  font-style: normal;\n  font-weight: 700;\n  src: local('Roboto Bold'), local('Roboto-Bold'), url(http://fonts.gstatic.com/s/roboto/v14/jSN2CGVDbcVyCnfJfjSdfBJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');\n  unicode-range: U+0370-03FF;\n}\n/* vietnamese */\n@font-face {\n  font-family: 'Roboto';\n  font-style: normal;\n  font-weight: 700;\n  src: local('Roboto Bold'), local('Roboto-Bold'), url(http://fonts.gstatic.com/s/roboto/v14/PwZc-YbIL414wB9rB1IAPRJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');\n  unicode-range: U+0102-0103, U+1EA0-1EF1, U+20AB;\n}\n/* latin-ext */\n@font-face {\n  font-family: 'Roboto';\n  font-style: normal;\n  font-weight: 700;\n  src: local('Roboto Bold'), local('Roboto-Bold'), url(http://fonts.gstatic.com/s/roboto/v14/97uahxiqZRoncBaCEI3aWxJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');\n  unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;\n}\n/* latin */\n@font-face {\n  font-family: 'Roboto';\n  font-style: normal;\n  font-weight: 700;\n  src: local('Roboto Bold'), local('Roboto-Bold'), url(http://fonts.gstatic.com/s/roboto/v14/d-6IYplOFocCacKzxwXSOBJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');\n  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;\n}\n\nbody{font-family:\"Helvetica Neue\",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#E0E0E0;}\n\nh1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:\"Roboto\",\"Helvetica Neue\",Helvetica,Arial,sans-serif;font-weight:500;line-height:1.1;color:#ffffff}\n\nbody{\n    background: url('../img/bg.jpg'); \n    background-color: #181c1c;\n}\n\n.logo-holder{\n  float:left;\n  /* padding:10px; */\n  width:33%;\n  margin-top: 2px;\n}\n.search-holder{\n  float:left;\n  /* margin-top: 10px; */\n  padding:10px;\n  width: 25%;\n  position: relative;\n  top: -14px;\n  left: 58px;\n}\n.navigation-holder{\n  float:right;\n  padding:10px;\n  padding-right:0px;\n  width: 38%;\n}\n.search-fb-holder{\n  margin:auto;\n  width: 230px;\n}\n/*\n.fb-like{\n  display:block!important;\n  height: 20px !important;\n  width:60px;\n  position: absolute !important;\n}*/\n.analysis-holder{\n  margin: auto;\n\n  margin-top: 18px;\n  text-transform: capitalize;\n\n  /* width: 300px; */\n\n  color: #717777;\n\n  text-align: right;\n  padding-right: 20px;\n}\n.analysis-holder strong{\n  font-size:1.2em;\n  color:#EDEDEF;\n  margin-left:3px;\n  display: inline-block;\n}\n.analysis-holder .spacer{\n  margin-left: 7px;\n  margin-right:7px;\n}\n.input-group {\n  display: block;\n  position: relative;\n\n  top: 20px;\n  margin: auto;\n}\n.dropdown-search{\n  position: relative;\ntop: -11px;\n}\n\n\nh1{\n    color:#89f5a2;\n    text-align: center;\n    margin-bottom: 18px;\n}\n\nh2 {\ncolor: #FFED68;\nfont-size: 1.42em;\nmargin-top: 5px;\ntext-align: center;\n}\n.first-column{\n  width:37%\n}\n\n.second-column{\n  width:21%\n}\n\n.third-column{\n  width:21%\n}\n\n.fourth-column{\n  width:21%\n}\n.navbar-brand img{\n  margin-top: -6px;\n\n  margin-left: 34px;\nwidth: 75%;\nmax-width: 330px;\n}\n.navbar-nav {\n  float: left;\n  margin-top: 0px;\n  margin-right:15px;\n  /* margin-bottom: -4px; */\n  margin-left: 20px;\n  display:inline-block;\n}\n.nav>li>a:hover, .nav>li>a:focus {\ntext-decoration: none;\nbackground-color: #15181A;\n/* border-left: 1px solid rgb(43, 49, 52); */\n/* border-right: 1px solid rgb(43,49,52); */\n}\n.champion-area h2{\n    margin-bottom:20px;\n    margin-top: 20px\n}\n.champion-area h2.line-chart-header{\n    margin-bottom:10px;\n}\n.navbar-inverse {\n/* background-color: #222222; */\n    border:none;\n}\n.social-media{\n  display: block;\n  /* position: absolute; */\n\n  /* top: -12px; */\n\n  /* float: right; */\n  /* top: 12px; */\n  /* right: 10px; */\n  /* left: 13px; */\n  float: right;\n  margin-top: 10px;\n}\n.champion-area div h2:first-child {\nmargin-top: 12px;\n}\n.navbar {\n/* min-height: 85px; */\nmargin-bottom: 0px;\ntext-transform: uppercase;\nfont-weight: bold;\nbackground-color: transparent;\n}\n.champion-home-list{\n  width: 97%;\nmargin: auto;\npadding-top: 30px;\n}\n.main-container{\n  max-width:1400px;\n  width: 95%;\n  margin:auto;\n  background-color: rgba(176, 219, 255, 0.03);\n  /* margin-top: 15px; */\n\n    \n  box-shadow: 0px 0px 15px rgba(0, 0, 20, 0.21);\n\n  border: 1px solid #121617;\n  /* border-left: 1px solid rgb(35, 42, 44); */\n  /* border-right: 1px solid rgb(35,42,44); */\n}\n\n.matchup-chart-holder{\n  width:70%;\n  margin:auto;\n}\n.navbar-inverse .navbar-nav>.active>a, .navbar-inverse .navbar-nav>.active>a:hover, .navbar-inverse .navbar-nav>.active>a:focus {\ncolor: #F7F7F7;\nbackground: none;\n}\n\n.nav li {\nmargin-left: 40px;\n}\n\n.navbar-inverse .navbar-nav>li>a:hover, .navbar-inverse .navbar-nav>li>a:focus {\ncolor: #89f5a2;\nbackground-color: transparent;\ntransition:color 0.15s;\n}\n\n.matchupSearch{\nbackground-color: #181C1D;\npadding: 6px;\npadding-top: 9px;\npadding-left: 12px;\nfont-size: 13px;\nborder: 1px solid rgb(51, 51, 51);\nwidth: 90%;\nmargin-bottom: 2px;\nmargin-right: 16px;\nposition: relative;\ntop: 1px;\ncolor:white;\n}\n.matchups .row{\n    margin-left:0px;\n    margin-right:0px;\n    z-index: 99999999;\nposition: relative;\n    /* border-bottom: 1px solid #2C2C2C; */\n}\n.matchupSort{\n    background-color: #333;\nborder: 1px solid grey;\ncolor: white;\npadding: 5px;\nmargin-left:3px;\n}\n.nav li.first-button{\n  margin-left:0px;\n}\n.navbar-inverse .navbar-nav>li>a {\ncolor: #F7F7F7;\n}\n.update-happening{\n/* margin-top: 15px; */\n/* text-align: center; */\n/* width: 100%; */\n/* margin-bottom: 15px; */\n/* display: inline-block; */\n/* width: 50%; */\ntext-align: center;\ntop: 10px;\nposition: relative;\nz-index:-9999999;\n/* float: left; */\n/* width: 90%; */\n/* width: 100%; */\n/* margin-left: 100px; */\n}\n.champion-area{\n    border-color: #0F0F0F;\n    /*background-color: rgba(32, 32, 34, 0.76)*/;\n    width:100%;\n    /* margin-top:15px; */\n}\n\n.navigation-elem{\n\n    /* width: 95%; */\n    \n\n    max-width: 1400px;\n\n    /* background-color: rgb(28, 33, 35); */\n    \n\n    border-bottom: 1px solid rgb(38, 43, 47);\n      \n\n    border-top: 1px solid rgb(27, 34, 34);\n    margin: auto;\n\n}\n.fb_iframe_widget {\ndisplay: inline-block;\nposition: relative;\ntop: -5px;\n}\n.inner-nav{\n\n    max-width: 1395px;\n    margin: auto;\n\n    width: 97%;\n\n    position: relative;\n\n}\n\n.champion-area .row{\n  margin-top: 25px;\n}\n\n.btn-success {\n    color: #fff;\n    background-color: #89f5a2;\n    border-color: #4cae4c;\n}\n\n.btn-success:hover {\n    color: #fff;\n    background-color: #89f5a2;\n    border-color: #4cae4c;\n}\n\n\n\n#query{\n    background-color: #1E2325;\n    border: 1px solid rgb(43, 49, 52);\n    font-weight: 400;\n    color: rgb(255, 255, 255);\n}\n\ndiv.champion-area .container-fluid .row > div {\n    border-right: 1px solid rgb(50, 55, 60);\n}\n\ndiv.champion-area .container-fluid .row > div:last-child, div.matchups .container-fluid .row > div:last-child {\n    border-right: none;\n}\n\ndiv.champion-area .container-fluid .row{\n    padding-bottom: 18px;\n}\n\n.matchups {\n    /* background-color: #1D1D1F; */\n}\n\n.champ-img{\n    display:block;\n    margin:auto;\n    margin-top:5px;\n}\n.champ-index-img{\n  border-right: 1px solid rgb(43, 49, 52);\n}\n\n.champion-list-header{\nmargin-left: 50px;\nmargin-bottom: 10px;\ndisplay: block;\nfont-size: 1.18em !important;\ntext-transform: uppercase;\n}\n.summary .champion-list-header{\n  /* margin-left: 25px; */\n  text-align:center;\n  margin-left:0px;\n  margin-bottom:6px;\n}\n.large-header{\n  /* margin-top: 25px; */\n\n  /* background-color: rgb(24, 28, 29); */\n\n  padding: 20px 12%;\n\n  background-image: url('/img/header-bg.jpg');\n  /* background-repeat: no-repeat; */\n  background-position-y: -80px;\n  /* border-bottom:1px solid rgb(23, 27, 31); */\n  /* max-width: 1340px; */\n  margin: auto;\n  /* display: none; */\n}\n.large-header h2{\n  font-size:1.75em;\n  line-height:1.6em\n}\n.glyphicon-search{\n    color: rgb(43, 43, 43);\n}\na.twitter-share-button{\n  color:rgba(0,0,0,0.1);\n}\n\n.champion-profile ul, .champion-statistics ul{\n    padding:0px;\n    margin-left:0px;\n}\n\nh2.champion-stats{\n  margin-top: 30px;margin-bottom: 14px;\n}\n\n.champion-statistics ul li{\n    list-style:none;\n}\n\n.champion-profile ul li{\n    list-style: none;\n    text-align: center;\n}\n\n.champion-profile h3{\n    margin-bottom:0px;\n\n}\n\n.selected-role{\n  border: 1px solid rgb(50, 57, 60);\npadding: 10px;\n\n  background-color: rgba(31, 36, 39, 1);\nmargin-top:13px;\n}\n.selected-role h3{\n    margin-bottom:0px;\n    margin-top:0px;\n    color:#89f5a2;\n}\n\n.champion-profile ul li a:hover{\n    text-decoration: none;\n}\n\n.champion-profile ul li a:hover h3{\n    color:#89f5a2;\n}\n.summary{\n/* margin-top: 45px; */\n/* width: 98%; */\n/* margin-bottom: 55px; */\npadding-top: 15px;\n/* background-color: rgba(24, 28, 29, 1); */\n/* padding-bottom: 18px; */\n/* padding-left: 1%; */\npadding-right: 1%;\nmax-width: 1350px;\nmargin: auto;\n}\n.champion-name{\n  font-weight:bold;\n  font-size:1.2em;\n  color:#E0E0E0;\n}\n.stats-role-title{\n  color:#FFE08C;\n}\n.glyphicon-arrow-up, .top-half, .green-death-arrow{\n    color:#89f5a2;\n}\n\n.glyphicon-arrow-up.Deaths-title{\n  color: #F17D59;\n}\n\n.glyphicon-arrow-down, .bottom-half, .red-death-arrow{\n    color: #F17D59;\n}\n\n.glyphicon-arrow-down.Deaths-title{\n  color: #89f5a2;\n}\n\n.above-average{\n    background-color:#89f5a2;\n}\n\n.below-average{\n    background-color:#F17D59;\n}\n.average{\n  background-color:#bdb97e;\n}\n\n.same-position::after { \n    content: \"-\";\n    color: #555;\n    font-size:1.2em;\n    margin-right:3px;\n    margin-left:1px;\n    font-weight: bold;\n}\n\n\n\n.table>thead>tr>th, .table>tbody>tr>th, .table>tfoot>tr>th, .table>thead>tr>td, .table>tbody>tr>td, .table>tfoot>tr>td {\n    padding: 8px;\n    line-height: 1.42857143;\n    vertical-align: middle;\n    border-top: none;\n}\n\n.champion-statistics .table-striped>tbody>tr:nth-child(odd)>td, .table-striped>tbody>tr:nth-child(odd)>th {\n    background-color: rgba(31, 36, 39, 1);\n}\n\n.champion-statistics .table-striped>tbody>tr.highlighted>td {\n    background-color: rgb(41, 48, 52);\n}\n\n.matchups .table-striped>tbody>tr:nth-child(odd)>td, .table-striped>tbody>tr:nth-child(odd)>th {\nbackground-color: #212325;\n}\n\n.champion-statistics tr>td:first-child, .summary-side tr>td:first-child, .matchups tr>td:first-child, #matchups tr>td:first-child{\n    color: #FFE08C;\n    \n    padding-left: 15px;\n}\n\n.champion-statistics tr>td:nth-child(3), .champion-statistics tr>td:nth-child(4), .champion-statistics tr>th:nth-child(3), .champion-statistics tr>th:nth-child(4){\n  text-align:center;\n}\n\n.champion-statistics tr:last-child>td:first-child {\n    font-weight:bold;\n    text-transform:uppercase;\n    color:#89f5a2;\n}\n.champion-statistics tbody tr:last-child {\n    border: 1px solid #2E3638;\n}\n\n.champion-statistics tr>td:nth-child(2) {\nfont-weight: bold;\n}\n\n.table>thead>tr>th {\nborder-bottom: 1px solid rgb(43, 49, 52);\n}\n\n.btn{\n    border-radius:0px;\n}\n.form-control, .input-group-btn button{\n  height: 42px;\n\n  /* margin-bottom: 4px; */\n}\n.form-control:focus {\n/* border-color: #66afe9; */\noutline: 0;\n-webkit-box-shadow: 0 0 10px rgba(5, 5, 5, 0.6);\nbox-shadow: 0 0 10px rgba(5, 5, 5, 0.6);\n}\n\n.background-colourisation{\n  background-color:rgba(126, 211, 255, 0.015);\n}\n\n.navbar, .matchups, .champion-area{\n    padding-left: 10px;\n\n    padding-right: 10px;\n    border-radius: 0px;\n    \n    \n}\n.navbar {\n  min-height:75px;\n  \n  /* width: 95%; */\n  /* background-color: rgb(27, 32, 34); */\n\n  max-width: 1400px;  \n  /* width: 95%; */  /* height: 100%; */  \n  background-color: rgba(25, 30, 31, 1);  padding-bottom: 15px;\n  margin: auto;\n  border-bottom: 1px solid rgb(38, 43, 47);\n}\n\n\n.donate{\n  position:absolute;\n  right: 30px;\n  top: 5px;\n  text-transform:none;\n  font-size:0.8em;\n}\n\n.counter-row{\n  margin-top:25px;\n}\n.counter-column{\n  border-right: 1px solid rgb(55, 59, 66);\n}\n.matchup-settings{\n  border: 1px solid rgb(55, 59, 66);\n  padding:10px;\n  display:inline-block;\n}\n.matchups h2{\n  margin-bottom:20px;\n  display:inline-block;\n}\n\n.counter-matchups{\n    /* background-color: #242427; */\n    padding: 6px 4px 7px;\n    margin-bottom: 0px;\n    /* position:relative; */\n    transition:background-color 0.05s;\n}\n.counter-matchups:hover{\n    background-color: #151A1A;\n}\n\n.counter-matchups > div{\n    padding-left: 8px;\n    \n}\n.counter-matchups h3{\ndisplay: inline-block;\nposition: absolute;\nfont-size: 1.25em;\nleft: 48px;\ntop: -21px;\n}\n\n.matchups .col-sm-3{\n    padding-right:0px;\n}\n.view-more-comment{\n    position:absolute;\n    left: 74px;\n    top: 21px;\n    cursor:pointer;\n    transition:color 0.15s;\n}\n\n.view-more-comment, .view-more-stats, .view-linked-page{\n    color:#FAFAFA;\n}\n.view-more-comment:hover, .view-more-stats:hover, .view-linked-page:hover{\n    color:rgb(255, 83, 83);\n}\n\n.chart-holder{\n  width:90%;margin:auto;\n}\n\n.middle-graphic-holder{\n  width:95%;margin:auto;\n}\n.damage-dealt{\n  height:25px;\n  position:relative;\n}\n.damage-dealt > div{\n    position:absolute;\n    height:100%;\n}\n\n.matchup-values-width{\n  width:19%;\n}\n.matchup-title-width{\n  width:24%;\n}\n.matchup-champ-img-width{\n  width:34%;\n  text-align:center;\n}\n.matchup-div-header{\n  display:inline-block;\n}\n.view-more-stats{\n    position:absolute;\n    left: 50px;\n    top: 21px;\n    cursor:pointer;\n    transition:color 0.15s;\n}\n.view-linked-page {\nposition: absolute;\nleft: 96px;\ntop: 21px;\ncursor: pointer;\ntransition: color 0.15s;\n}\n\n.matchups{\n    padding-bottom:20px;\n}\n.matchup-champion-info{\n  width:27.5%;display:inline-block;position:relative;top: 3px;\n}\n.matchup-champion-info h3:hover{\n  color:#89f5a2;\n}\n\n.progress{\n    margin-bottom:0px;\n    width: 80%;\n    margin:auto;\n    background-color: #404146;\n    height:18px;\n    position:relative;\n}\n\n.statistic-rating{\n    overflow: visible;\n    display: inline;\n    position: absolute;\n    text-shadow: -1px 1px 1px black, 1px 0px 1px rgba(1,1,1,0.8);\n    color: white;\n    margin: auto;\n    left: 20%;\n}\n.left+.show-more{\n  color:#F17D59;\n}\n.right+.show-more{\n  color:#89f5a2;\n}\n\n.show-more{\nheight: 40px;\ntext-align: center;\npadding: 8px;\ncursor: pointer;\nwidth: 100%;\nbackground-color: rgb(32, 37, 40);\nz-index: 99999999;\nposition: relative;\nborder: 1px solid rgb(24, 27, 31);\nborder-top:none;\n}\n\n.show-more:hover{\n    background-color: rgb(25, 29, 29);\n    color: white;\n    border: 1px solid rgb(27, 31, 37);\nborder-top: none;\n}\n\n.show-more:active{\n  background-color: rgb(37, 42, 45);\n}\n\n.important-message{\n  color: #FD6F6F;\n  /* margin-right:15px; */\n  margin-right: 160px;\n}\n.matchup-stats{\n    display:inline-block;\n    margin-right:1px;\n    width:42%;\n    padding-right:8px;\n    /* text-align:center; */\n}\n.matchup-wrapper{\n  width:100%;\n  text-align:center;\n  margin-top: 12px;\n}\n.matchup-stats small{\n  margin-left: 10%;\n}\n.matchup-stat-area{\n    background-color: rgba(22, 22, 24, 0.36);\n    margin-right:5px;\n    margin-top: 15px;\n}\n.total-rating{\n    float: left;\npadding: 2px;\n\n    width: 30%;\nheight: 19px;\nposition:relative;\n\n    /* margin-left: 2px; */\n\n    /* margin-right: 5px; */\ncolor: white;\nborder-radius: 3px;\n}\n\n.winrating-area{\nposition: relative;\nwidth:28%;\ndisplay:inline-block;\ntext-align:center;\n}\n.winrating-area strong{\n  display: block;\n  position:relative;\n  top: -8px;\n  font-size:1.3em;\n}\n\n#matchups h2 {\n  margin-bottom:20px;margin-top:25px\n}\n.champ-user-rating small{\n  position: relative;\n  top: -4px;\n}\n.overall-score-rating{\n  width:23%;display:inline-block;position:relative;top:-15px;\n}\nul.user-rating li{\n    display:inline-block;\n    width: 18%;height:19px;background-color: #404146;margin-left:1px;cursor:pointer;\n    transition:background-color 0.15s, border 0.35s;\n    border:2px solid transparent;\n    border-radius:1px;\n}\n\n\n.physical-dmg{\n    background-color:#FF5353;\n}\n.magic-dmg{\n    background-color:rgba(101, 228, 245, 1);\n}\n.true-dmg{\n    background-color:rgba(107, 107, 107, 1);\n}\n.stat-sorter{\nfloat:left;\nwidth: 42%;text-align:center;\n}\n.winrate-sorter{\nfloat:left;\nwidth: 28%;text-align:center;\n}\n.search-sorter{\nfloat:left;\nwidth: 29%;text-align:center;\n}\n.winrate-sorter, .stat-sorter{\n    cursor:pointer;\n    padding: 8px 0px;\n}\n.user-sorter:not(.selected-sorter):hover, .overall-sorter:not(.selected-sorter):hover, .stat-sorter:not(.selected-sorter):hover{\n    color:white;\n    background-color: rgb(26, 29, 30);\n}\n\n.selected-sorter{\n    font-weight:bold;\n    color:white;\n    background-color: rgb(37, 43, 47);}\n\n.radar-legend, .line-legend, .pie-legend {\n    text-align:center;\n  list-style-type: none;\n  padding-left: 0px;\n}\n.pie-legend {\n  text-align:left;\n}\n.radar-legend li, .line-legend li{\n    display:inline-block;\n  padding:10px;\n}\n.pie-legend li {\n  display: block;\n}\n.radar-legend li span, .line-legend li span, .pie-legend li span {\nwidth: 12px;\nheight: 12px;\nposition: relative;\ntop: 4px;\ndisplay: block;\nfloat: left;\nmargin-right: 6px;\n}\n\n.disqus{\n    padding-right:6px;\n    padding-left:14px;\n}\n\n.first-summary{\n  width:16%;\n}\n.second-summary{\n  width:42%;\n}\n.third-summary{\n  width:42%;\n}\n.summary-side .matchup-champion {\n  transform: scale(0.7);\n  margin-left: -4px\n}\n.summary-side .table .champion-name{\n  position: absolute;\n  left: 42px;\n  top: 11px;\n  font-size: 1em;\n}\n.summary-side .table .champion-name:hover{\ncolor: #FCFF90;\n}\n.summary-value{\n  background-color: rgb(27, 32, 33);\n  right: 10px;\nposition: absolute;\ntop: 15px;\nfont-size:0.9em;\n}\n.summary-side .table-striped>tbody>tr:nth-child(odd)>td, .summary-side .table-striped>tbody>tr:nth-child(odd)>td .summary-value {\n  background-color: #191E1F;\n}\n.summary-side tr>td:nth-child(2), .summary-side tr>td:nth-child(3){\n  position:relative;\n}\n.summary-side tr>th:nth-child(2), .summary-side tr>th:nth-child(3){\n  text-align:center;\n}\n.summary-side tr>td:nth-child(1), .summary-side tr>td:nth-child(2){\n  border-right: 1px solid rgb(43, 49, 52);\n}\n.win-summary{\n  width:36.75%;\n  padding: 1.25%;\n  float:left;\n}\n.overall-summary{\n  width:30.5%;\n  padding: 1.25%;\n  float:left;\n}\n.change-summary{\n  width:32.75%;\n  padding: 1.25%;\n  float:left;\n}\n\n\n.viktor-skills, .trinket-stats{\n  margin-top: 35px;\nposition: relative;\nleft: 6px;\n}\n.viktor-skills small, .trinket-stats small{\n  position: relative;\nleft: -7px;\n}\n.viktor-skill, .trinket-single{\n  width:25%;\n  position:relative;\n  display: inline-block\n}\n\n.viktor-skill img, .trinket-single img{\n  width:36px;\n  height:36px;\n}\n.viktor-key, .trinket-key{\n  position:absolute;\n  top:4px;\n}\n/*\nul.user-rating li:hover {\n    background-color:white;\n}*/\n.build-wrapper, .summoner-wrapper{\n  margin:auto;\n  text-align:center;\n}\n.build-wrapper small{\n  font-weight:bold;\n  margin-left:2px;\n  margin-right:2px;\n}\n.possible-build, .possible-summoner {\n    display:inline-block;\n    max-width:36px;\n    max-height:36px;\n}\n.build-text, .summoner-text{\n  margin-top: 10px;\n  display: block;\n  text-align: center;\n  margin:bottom:5px;\n  white-space: nowrap;\n}\n.build-text strong, .summoner-text strong{\n  color: rgb(136, 244, 161);\n}\n.skill-order .build-text{\n  margin-top:20px;\n}\n.rune-img img{\n  width:42px;\n  height:42px;\n  margin-left: 10px;\nmargin-right: 10px;\n}\nspan.rune-title {\ncolor: rgb(101, 228, 245)\n}\n\n.rune-type-area{\n  background-color: #202528;\n}\n.rune-img strong {\nmargin-right: 12px;\n}\nh4{\n    color:#89f5a2;\n    margin-bottom:25px;\n    text-align:center;\n}\n.description{\n  font-size:0.85em;\n}\n.description>.highlight{\n  color: #89f5a2 !important;\nfont-size: 1.15em;\nfont-weight: bold;\nborder: 1px solid;\npadding: 2px;\nmargin: 5px;\n}\n\n\ndiv.matchup-table{\n  margin: 0 auto;\ndisplay: block;\nmargin-top: 10px;\nmargin-bottom: -8px;\n}\n\n.dropdown-menu {\nposition: absolute;\nbackground-color:transparent;\ntop: 100%;\nleft: 0;\nz-index: 1000;\ndisplay: none;\nfloat: left;\nmin-width: 160px;\npadding: 3px 0;\nmargin: 0 0;\nfont-size: 14px;\ntext-align: left;\nlist-style: none;\nborder: 1px solid #2F2F2F;\nborder-radius: 0px;\n-webkit-box-shadow: 0 6px 20px rgba(0,0,0,.875);\nbox-shadow: 0 6px 20px rgba(0,0,0,.875);\n}\n\n.dropdown-menu>li>a {\ndisplay: block;\npadding: 3px 20px;\nclear: both;\nfont-weight: 400;\nline-height: 1.42857143;\ncolor: #CBCBCB;\ntext-transform:none;\nwhite-space: nowrap;\nbackground-color: rgb(33, 34, 36);\n}\n\n\n.dropdown-menu>.active>a, .dropdown-menu>.active>a:hover, .dropdown-menu>.active>a:focus {\n  color: #fff;\n  text-decoration: none;\n  background-color: #3FBB83;\n  outline: 0;\n}\n\n.minimum-games{\n  background-color: #242527;\nborder: 1px solid rgb(71, 76, 81);\nfont-weight: 400;\ncolor: rgb(236, 236, 236);\n}\n\n/* Possibly Use */\n.counter-matchups {\n  background-color:none;\n  border-bottom: 1px solid #292D34;\n}\nmatchups div:nth-child(odd) > .counter-matchups {\nbackground-color: rgba(26, 29, 29, 0.44);\n/* background-color: rgba(31, 42, 41, 1); */\n}\n\nmatchups div:nth-child(odd) > .counter-matchups:hover {\nbackground-color: #16191A;\n}\n\n.left:first-child{\n  border-top: 1px solid #F17D59;\n}\n\n\n.right:first-child{\n  border-top: 1px solid #89f5a2;\n}\n\n#stats{\n  width: 98.5%;\nmargin: auto;\nmargin-top: 20px;\n}\n\n#stats thead{\n    background-color: rgb(13, 144, 114);\n    font-weight: bold;\n}\n#stats thead tr td{\n    cursor:pointer;\n    color:white;\n}\n#stats thead tr td:hover{\n  background-color:rgb(10, 118, 93);\n}\n\n#stats thead .selected-column {\n  background-color:rgb(10, 118, 93);\n  color: #FFE08C;\n}\n\n#stats tbody .selected-column {\nbackground-color: rgba(37, 44, 45, 0.92);\ncolor: #FFE08C;\n\n}\n\n#stats .down .selected-column::after {\n  content: '\\25B2';\n}\n\n#stats .up .selected-column::after {\n  content: '\\25BC';\n}\n\n#stats td{\n  text-align:center;\n  box-sizing: content-box;\n}\n\n#stats tbody td{\n  padding-top: 12px;\npadding-bottom: 12px;\nfont-weight:bold;\n}\n\n#stats tr>td:nth-child(2){\n  text-align:left;\n  padding-left:11px;\n}\n\n\n#stats .table-striped>tbody>tr:nth-child(odd)>td, #stats .table-striped>tbody>tr:nth-child(odd)>th {\nbackground-color: #191D20;\n}\n\n#stats .table-striped>tbody>tr:nth-child(even)>td, #stats .table-striped>tbody>tr:nth-child(even)>th {\nbackground-color: #1F2325;\n}\n\n#stats .table>thead>tr>th, #stats .table>tbody>tr>th, #stats .table>tfoot>tr>th, #stats .table>thead>tr>td, #stats .table>tbody>tr>td, #stats .table>tfoot>tr>td {\nborder-left: 1px solid rgba(0, 0, 0, 0.21);\nborder-right: 1px solid rgba(0,0,0,0.21);\n}\n\n#stats div.matchup-champion{\n  transform: scale(0.75);\n} \n\nspan.stat-champ-title{\nposition: relative;\n/* top: -6px; */\nfont-weight:bold;\n}\n\n#header-fixed { \n    position: fixed; \n    top: 0px; \n    display:none;\n    z-index: 9999999;\n}\n\n\n\n#matchups .progress-bar{\n  background-color:rgba(101, 228, 245, 1);\n}\n#matchups .synergy .progress-bar{\n  background-color: rgba(107, 219, 147, 1);\n}\n\n#matchups .progress{\n  margin-bottom:20px;\n  background-color:#FF5353;\n}\n#matchups .synergy .progress{\n  background-color: #383838;\n}\n\n.champ-height {\nmin-height: 175px;\ntext-align: center;\nwidth: 12%;\nfloat: left;\n}\n\niframe.center{\n  display: block;\nmargin: auto;\nposition:relative;\n}\n\n.matchup-progress-bars {\nmargin-bottom: 30px;\nmargin-top: 25px;\nbackground-color: rgba(20, 26, 29, 0.27);\npadding-top: 10px;\npadding-bottom: 10px;\n}\n\n.matchup-progress-bars h4{\n  margin-bottom:7px;\n  margin-top:14px;\n}\n\n.matchup-progress-bars h2{\n  margin-bottom:18px;\n  margin-top:7px;\n}\n\nh1.champ1, .victor1{\n  color:rgb(101, 228, 245);\n}\nh1.champ2, .victor2{\n  color:#FF5353;\n}\n\n.matchup-header{\n  text-align:center;\n}\n.reddit-titles {\n  padding:4px 4px 4px 12px\n}\n.container-reddit{\n  margin-bottom:8px;\n  /* background-color: #1B2021; */\n}\n.container-reddit h4{\n  text-align:left;margin-bottom:5px;margin-top:2px\n}\n.reddit-summary{\n  cursor:pointer;margin-right:15px;font-weight:bold;color:rgb(215, 215, 215);\n}\n.reddit-summary:hover{\n  color:white;\n}\n.reddit-comments{\n  margin-right:15px;\n  color:#428bca;\n}\n.reddit-date{\n  color:#f7f7f7\n}\n.reddit-summary-area{\n  background-color: rgba(1, 1, 1, 0.12);padding:12px;margin-top:4px;border:1px solid #333;color:#C7C7C7;\n}\n\n.footer-attr{\n  margin-top: 15px;\n  padding: 15px 15%;\n  font-size: 0.84em;\n  color: #A3A3A3;\n  text-align: center;\n}\n\n.navbar .container-fluid{\n  /* max-width: 1400px; */\n  /* width:95%; */\n  /* height: 100%; */\n  /* background-color: rgba(176, 219, 255, 0.03); */\n  /* padding-bottom: 15px; */\n}\n.matchup-champion{\n    width: 36px;\n    height: 36px;\n    margin-top: -5px;\n    display: inline-block;\n    /* float: left; */\n    margin-left: -7px;\n    margin-bottom: -12px;\n    padding: 0px;\n    /* margin: 0px; */\n    /* position: initial; */\n}\n.home-champion{\n    width: 48px;\n    height: 48px;\n    margin:auto;\n    margin-bottom: 10px;\n    display: block;\n    position: relative;\n    transform: scale(1.25);\n}\n\n.summary h3{\n  text-align:center;\n  color: rgba(101, 245, 179, 1);\n  font-size:1em;\n  font-size: 1.4em;\n}\n.bottom-champ{\nmargin-top: 14px;\n}\n.stat-summary-box{\n  margin-bottom:25px;\n  width:19%;display:inline-block;\n}\n.stat-summary-box h3{\n  text-align:center;\n  padding-bottom: 8px;\n  padding-top: 6px;\n  /* background-color: rgba(25, 29, 32, 1); */\n  border: 1px solid #3F986F;\n  font-weight: bold;\n  font-size: 1.1em;\n  text-transform: uppercase;\n}\n.stat-summary-container{\n  text-align:center;\n}\n.stat-summary-container>div{\n  border-left: 1px solid rgb(39, 46, 48);\n}\n\n.stat-summary-container.first-summary>div{\n  border-left: 0px;\n}\n\n.summary-highlight{\n  color: #FFED68;\n}\n\n\n\n/*Skill Order*/\n.skill-order{\n  /*background-color:black;*/\n  width:100%;\n}\n\n\n.skill-order > div:nth-of-type(odd){\n\n  background-color: rgb(32, 37, 40);\n}\n\n.skill-order > div:first-child{\n  background-color: rgb(22, 27, 27);\n  text-align: center;\n  font-weight: bold;\n  font-size:1.1em;\n}\n\n.skill-order > div:first-child div span{\n  position:relative;\n  top:6px;\n}\n\n.selected span{\n  color:black;\n  font-size:0.8em;\n  font-weight: bold;\n}\n\n\n.skill{\n  width:100%;\n  float:left;\n  overflow:hidden;\n}\n.skill-order img, .img-placeholder{\n  width:38px;\n  height:38px;\n  float:left;\n}\n\n.skill-selections{\noverflow:hidden;\n}\n\n.skill-selections > div {\n  width:5.555%;\n  padding-left:2px;\n  padding-right:2px;\n  height:38px;\n  border-left: 1px solid rgba(50, 57, 60, 0.66);\n  border-bottom: 1px solid rgba(50, 57, 60, 0.66);\n  border-collapse: collapse;\n  float:left;\n}\n\n.skill-selections > div.selected {\n\nbackground-color:rgb(101, 228, 245);\nborder: 1px solid black;\n}\n\n\n/* masteries */\n\n.mastery0, .mastery1, .mastery2{\n  display: inline-block;\n  cursor:default;\n  position:relative;\n  padding:10px;\n  background-size:120%;\n  border:1px solid rgb(55, 59, 66);\n  margin-left:10px;\n  \n}\n\n\n.mastery-header{\n  font-weight:bold;\n  font-size:1.1em;\n  text-align:center;\n}\n.mastery0{\n  background-image:url('/img/mastery0.jpg?v=5.22.3');\n}\n.mastery1{\n  background-image:url('/img/mastery1.jpg?v=5.22.3');\n}\n.mastery2{\n  background-image:url('/img/mastery2.jpg?v=5.22.3');\n}\n.mastery-container{\n  border-right:none !important;\n  position:relative;\n  text-align: center;\n}\n.mastery-icon{\n  margin: 3px;\n  border: 1px solid grey;\n  width: 40px;\n  height: 40px;\n  display: block;\n  background-size: initial;\n  position:relative;\n  opacity:0.35;\n  float: left;\n  z-index:88;\n}\n.mastery-icon .points {display: block;width:100%;height: 8px;padding: 0px;margin: 0px;background-color:rgba(0, 0, 0, 0.83);position:absolute;bottom:0px;}\n.mastery-icon .point {display: block;position: relative;background-color:yellow;width: 4px;height: 4px;margin-left: 3px;top: 3px;float: left;}\n.mastery-spacer{\n  width: 40px;\n  height: 40px;\n  margin: 3px;\n  position:relative;\n  display: block;\n  float: left;\n}\n\n.mastery-active{\n  border: 2px solid black;\n  opacity:1;\n}\n\n.mastery-icon.double-mastery-left-indent{\n  margin-left: 124px;\n}\n\n.mastery-icon.mastery-left-indent{\n  margin-left: 62px;\n}\n\n.mastery-icon.tripple-mastery-left-indent{\n  margin-left: 186px;\n}\n\n.mastery-points{\n  display:none;\n}\n\n.mastery-selected-3 .mastery-points{\n  width:100%;\n  height:10px;\n  background-color:black;\n  position: absolute;\n  display:block;\n}\n.faq-column{\n  padding: 30px;\npadding-top: 5px;\n}\n.faq{\n  padding:10px;\n  padding-top:0px;\n}\n.faq h2{\n  margin-top:40px;\n  text-align:left;\n}\n.faq p{\n  line-height:1.1em;\n  line-height: 1.9em;\ncolor: rgb(187, 187, 187);\n}\n\n.navbar-nav>li>a {\nfont-weight: bold;\ntext-transform: uppercase;\n}\n.matchup-champion.dropdown-img{\n    margin-left: -9px;\n    margin-right: 8px;\n    margin-bottom: -4px;\n    margin-top: 0px;\n}\n.counter-matchups .matchup-champion {\n  margin-top: 0px;\n  margin-left: 0px;\n  margin-bottom: 0px;\n  margin-left: -4px;\n}\n/* Responsive */\n\n @media (min-width: 992px) and (max-width: 1267px) {\n    .progress{\n      width:95%;\n    }\n }\n\n @media (min-width: 1060px) and (max-width: 1240px) {\n    .navigation-holder .nav li{\n      margin-left:5px;\n    }\n    .navigation-holder{\n      width:44%;\n    }\n    .search-holder{\n      width:24%;\n      left:0px;\n    }\n    .logo-holder{\n      width:30%;\n    }\n\n }\n\n @media (min-width: 775px) and (max-width: 1060px) {\n\n    .logo-holder{\n      width:50%;\n    }\n    .navigation-holder{\n      width:100%;\n    }\n    .navbar .nav{\n      text-align: center;\n      margin-top:8px;\n      float:none;\n      margin-top: 4px;\nmargin-bottom: 15px;\n    }\n    .navbar-nav>li {\n      float: none;\n    }\n    .large-header{\n      margin-top:0px;\n      padding:8px 10%;\n    }\n    .champion-area{\n      margin-top: -25px;\n    }\n    \n }\n\n @media (max-width: 1267px) {\n     .win-summary{\n      width:58%;\n    }\n    .overall-summary{\n      width:42%;\n    }\n    .change-summary{\n      width:100%;\n    }\n  }\n\n   @media (max-width: 1300px) {\n    .important-message{\n      margin-right:0px;\n    }\n  }\n\n\n  @media(max-width:1060px){\n    .analysis-holder{\n      text-align:center;\n      margin-top: 5px;\nmargin-bottom: -7px;\n    }\n    .navbar{\n      margin-bottom:12px;\n    }\n    .navbar-nav {\n      margin-bottom:0px;\n    }\n    .update-happening{\n      float:left;\n      width:100%;\n      top:0px;\n      margin-bottom:7px;\n    }\n  }\n\n  @media (max-width: 900px) {\n     .win-summary{\n      width:100%;\n    }\n    .overall-summary{\n      width:100%;\n    }\n    .change-summary{\n      width:100%;\n    }\n  }\n  @media (min-width: 768px) {\n  .navbar-nav>li>a {\n    padding-top: 12px;\n    padding-bottom: 12px;\n  }\n}\n  @media (max-width: 775px) {\n    .logo-holder{\n      width:100%;\n      text-align:center;\n    }\n    .search-holder{\n      width:100%;\n      margin-top: -12px;\n      left:0px;\n    }\n\n    .navigation-holder{\n      width:100%;\n      padding:0;\n    }\n    .navbar-brand {\n      float:none;\n    }\n    .navbar-nav>li {\n      float: none;\n    \n      margin-left: 15px;\n}\n    .navbar .nav{\n      text-align: center;\n      float:none;\n      margin-top: 4px;\n      margin-bottom: 15px;\n    }\n    .large-header{\n      margin-top:0px;\n      padding:4px 10%;\n    }\n    .champion-area .row{\n      margin-top:0px;\n    }\n  .champ-height{\n    width:18%;\n  }\n  .champion-list-header{\n    margin-left:25px;\n  }\n }\n @media (max-width: 992px) {\n  .counter-column {\n      border-right: 0;\n    }\n      .matchup-area{\n    padding-right:0px;\n    padding-left:0px;\n  }\n  .matchup-area.row, .matchup-area .row{\n    margin-right:0px;\n    margin-left:0px;\n  }\n  .matchup-block{\n    padding-right:0px;\n    padding-left:0px;\n  }\n  #matchups h2 {\n  margin-bottom:5px;margin-top:5px\n  }\n }\n\n  @media (max-width: 600px) {\n    .social-media{\n      float:none;\n      margin-left:20px;\n    }\n    .stat-sorter{\n      width:40%;\n    }\n    .winrate-sorter{\n      width:20%;\n    }\n    .search-sorter{\n      width:35%;\n    }\n\n    .matchup-champion-info{\n      width:35%;\n    }\n    .matchup-stats{\n      width:40%;\n    }\n    .winrating-area{\n      width:20%;\n    }\n\n    .nav li.first-button{\n      margin-left:5px;\n    }\n    .matchup-wrapper{\n      margin-bottom:-30px;\n    }\n    .matchups h2{\n      margin-top:20px;\n      margin-bottom:12px;\n    }\n    .logo-holder {\n      padding: 10px 0px 0px 0px;\n    }\n    div.champion-area .container-fluid .row > div {\n      border-right: 0;\n    }\n     .counter-column {\n      padding:0;\n    }\n    .navbar, .matchups, .champion-area {\n      padding-left: 0; \n      padding-right: 0; \n    }\n\n    .progress{\n      width:95%;\n    }\n\n    div.champion-area .container-fluid .row > div:last-child, div.matchups .container-fluid .row > div:last-child {\n      padding: 0px;\n    }\n\n    .counter-matchups{\n      padding-top: 14px;\n      padding-bottom: 15px;\n    }\n    .matchup-stats small{\n      margin-left:0;\n    }\n    .large-header{\n      margin-top:0px;\n      padding:6px 4%;\n    \n      background-position-y: 0px;\n}\n    .large-header h2{\n    font-size:1.4em;\n    }\n    .champ-height{\n     width:24%;\n    }\n    .stat-summary-box {\n    width: 32%;\n    margin-bottom:15px;\n    }\n    .first-column, .second-column, .third-column, .fourth-column{\n      width:25%\n    }\n    .champion-area div h2:first-child {\n    margin-top: 30px;\n    }\n  }\n  @media (max-width: 495px) {\n    .champ-height{\n     width:32%;\n    }\n  }\n\n  @media (max-width: 410px) {\n    .navbar-nav{\n      margin:0px;\n    }\n    .nav li {\n    margin-left: 2px;\n    }\n    .nav li.first-button{\n      margin-left:7px;\n    }\n    .overall-score-rating{\n      padding-left:0px;\n      width:22%;\n    }\n    .logo-holder img{\n      width:80%;\n    }\n  }\n\n  @media (max-width: 360px) {\n    .nav li.first-button{\n      margin-left:7px;\n    }\n    .overall-score-rating{\n      padding-left:0px;\n      width:15%;\n    }\n    .matchup-champion-info{\n      width:42%;\n    }\n    .matchup-stats{\n      width:30%;\n    }\n    .winrating-area{\n      width:20%;\n    }\n    \n  }\n\n"
  },
  {
    "path": "public/css/sprite.css",
    "content": "/* This CSS was generated by champion.gg's asset_generator */ \n.matchup-champion{    background-image:url('../img/small_champion.jpg?v=7.11.1');} .home-champion{ background-image:url('../img/champion.jpg?v=7.11.1');}.mastery-icon{ background-image:url('http://ddragon.leagueoflegends.com/cdn/7.11.1/img/sprite/gray_mastery0.png'); } .mastery-active{ background-image:url('http://ddragon.leagueoflegends.com/cdn/7.11.1/img/sprite/mastery0.png');}#home .Aatrox {\n\tbackground-position:-0px -0px;\n}#home .Ahri {\n\tbackground-position:-48px -0px;\n}#home .Akali {\n\tbackground-position:-96px -0px;\n}#home .Alistar {\n\tbackground-position:-144px -0px;\n}#home .Amumu {\n\tbackground-position:-192px -0px;\n}#home .Anivia {\n\tbackground-position:-240px -0px;\n}#home .Annie {\n\tbackground-position:-288px -0px;\n}#home .Ashe {\n\tbackground-position:-336px -0px;\n}#home .AurelionSol {\n\tbackground-position:-384px -0px;\n}#home .Azir {\n\tbackground-position:-432px -0px;\n}#home .Bard {\n\tbackground-position:-0px -48px;\n}#home .Blitzcrank {\n\tbackground-position:-48px -48px;\n}#home .Brand {\n\tbackground-position:-96px -48px;\n}#home .Braum {\n\tbackground-position:-144px -48px;\n}#home .Caitlyn {\n\tbackground-position:-192px -48px;\n}#home .Camille {\n\tbackground-position:-240px -48px;\n}#home .Cassiopeia {\n\tbackground-position:-288px -48px;\n}#home .Chogath {\n\tbackground-position:-336px -48px;\n}#home .Corki {\n\tbackground-position:-384px -48px;\n}#home .Darius {\n\tbackground-position:-432px -48px;\n}#home .Diana {\n\tbackground-position:-0px -96px;\n}#home .Draven {\n\tbackground-position:-48px -96px;\n}#home .DrMundo {\n\tbackground-position:-96px -96px;\n}#home .Ekko {\n\tbackground-position:-144px -96px;\n}#home .Elise {\n\tbackground-position:-192px -96px;\n}#home .Evelynn {\n\tbackground-position:-240px -96px;\n}#home .Ezreal {\n\tbackground-position:-288px -96px;\n}#home .Fiddlesticks {\n\tbackground-position:-336px -96px;\n}#home .Fiora {\n\tbackground-position:-384px -96px;\n}#home .Fizz {\n\tbackground-position:-432px -96px;\n}#home .Galio {\n\tbackground-position:-0px -144px;\n}#home .Gangplank {\n\tbackground-position:-48px -144px;\n}#home .Garen {\n\tbackground-position:-96px -144px;\n}#home .Gnar {\n\tbackground-position:-144px -144px;\n}#home .Gragas {\n\tbackground-position:-192px -144px;\n}#home .Graves {\n\tbackground-position:-240px -144px;\n}#home .Hecarim {\n\tbackground-position:-288px -144px;\n}#home .Heimerdinger {\n\tbackground-position:-336px -144px;\n}#home .Illaoi {\n\tbackground-position:-384px -144px;\n}#home .Irelia {\n\tbackground-position:-432px -144px;\n}#home .Ivern {\n\tbackground-position:-0px -192px;\n}#home .Janna {\n\tbackground-position:-48px -192px;\n}#home .JarvanIV {\n\tbackground-position:-96px -192px;\n}#home .Jax {\n\tbackground-position:-144px -192px;\n}#home .Jayce {\n\tbackground-position:-192px -192px;\n}#home .Jhin {\n\tbackground-position:-240px -192px;\n}#home .Jinx {\n\tbackground-position:-288px -192px;\n}#home .Kalista {\n\tbackground-position:-336px -192px;\n}#home .Karma {\n\tbackground-position:-384px -192px;\n}#home .Karthus {\n\tbackground-position:-432px -192px;\n}#home .Kassadin {\n\tbackground-position:-0px -240px;\n}#home .Katarina {\n\tbackground-position:-48px -240px;\n}#home .Kayle {\n\tbackground-position:-96px -240px;\n}#home .Kennen {\n\tbackground-position:-144px -240px;\n}#home .Khazix {\n\tbackground-position:-192px -240px;\n}#home .Kindred {\n\tbackground-position:-240px -240px;\n}#home .Kled {\n\tbackground-position:-288px -240px;\n}#home .KogMaw {\n\tbackground-position:-336px -240px;\n}#home .Leblanc {\n\tbackground-position:-384px -240px;\n}#home .LeeSin {\n\tbackground-position:-432px -240px;\n}#home .Leona {\n\tbackground-position:-0px -288px;\n}#home .Lissandra {\n\tbackground-position:-48px -288px;\n}#home .Lucian {\n\tbackground-position:-96px -288px;\n}#home .Lulu {\n\tbackground-position:-144px -288px;\n}#home .Lux {\n\tbackground-position:-192px -288px;\n}#home .Malphite {\n\tbackground-position:-240px -288px;\n}#home .Malzahar {\n\tbackground-position:-288px -288px;\n}#home .Maokai {\n\tbackground-position:-336px -288px;\n}#home .MasterYi {\n\tbackground-position:-384px -288px;\n}#home .MissFortune {\n\tbackground-position:-432px -288px;\n}#home .MonkeyKing {\n\tbackground-position:-0px -336px;\n}#home .Mordekaiser {\n\tbackground-position:-48px -336px;\n}#home .Morgana {\n\tbackground-position:-96px -336px;\n}#home .Nami {\n\tbackground-position:-144px -336px;\n}#home .Nasus {\n\tbackground-position:-192px -336px;\n}#home .Nautilus {\n\tbackground-position:-240px -336px;\n}#home .Nidalee {\n\tbackground-position:-288px -336px;\n}#home .Nocturne {\n\tbackground-position:-336px -336px;\n}#home .Nunu {\n\tbackground-position:-384px -336px;\n}#home .Olaf {\n\tbackground-position:-432px -336px;\n}#home .Orianna {\n\tbackground-position:-0px -384px;\n}#home .Pantheon {\n\tbackground-position:-48px -384px;\n}#home .Poppy {\n\tbackground-position:-96px -384px;\n}#home .Quinn {\n\tbackground-position:-144px -384px;\n}#home .Rakan {\n\tbackground-position:-192px -384px;\n}#home .Rammus {\n\tbackground-position:-240px -384px;\n}#home .RekSai {\n\tbackground-position:-288px -384px;\n}#home .Renekton {\n\tbackground-position:-336px -384px;\n}#home .Rengar {\n\tbackground-position:-384px -384px;\n}#home .Riven {\n\tbackground-position:-432px -384px;\n}#home .Rumble {\n\tbackground-position:-0px -432px;\n}#home .Ryze {\n\tbackground-position:-48px -432px;\n}#home .Sejuani {\n\tbackground-position:-96px -432px;\n}#home .Shaco {\n\tbackground-position:-144px -432px;\n}#home .Shen {\n\tbackground-position:-192px -432px;\n}#home .Shyvana {\n\tbackground-position:-240px -432px;\n}#home .Singed {\n\tbackground-position:-288px -432px;\n}#home .Sion {\n\tbackground-position:-336px -432px;\n}#home .Sivir {\n\tbackground-position:-384px -432px;\n}#home .Skarner {\n\tbackground-position:-432px -432px;\n}#home .Sona {\n\tbackground-position:-0px -480px;\n}#home .Soraka {\n\tbackground-position:-48px -480px;\n}#home .Swain {\n\tbackground-position:-96px -480px;\n}#home .Syndra {\n\tbackground-position:-144px -480px;\n}#home .TahmKench {\n\tbackground-position:-192px -480px;\n}#home .Taliyah {\n\tbackground-position:-240px -480px;\n}#home .Talon {\n\tbackground-position:-288px -480px;\n}#home .Taric {\n\tbackground-position:-336px -480px;\n}#home .Teemo {\n\tbackground-position:-384px -480px;\n}#home .Thresh {\n\tbackground-position:-432px -480px;\n}#home .Tristana {\n\tbackground-position:-0px -528px;\n}#home .Trundle {\n\tbackground-position:-48px -528px;\n}#home .Tryndamere {\n\tbackground-position:-96px -528px;\n}#home .TwistedFate {\n\tbackground-position:-144px -528px;\n}#home .Twitch {\n\tbackground-position:-192px -528px;\n}#home .Udyr {\n\tbackground-position:-240px -528px;\n}#home .Urgot {\n\tbackground-position:-288px -528px;\n}#home .Varus {\n\tbackground-position:-336px -528px;\n}#home .Vayne {\n\tbackground-position:-384px -528px;\n}#home .Veigar {\n\tbackground-position:-432px -528px;\n}#home .Velkoz {\n\tbackground-position:-0px -576px;\n}#home .Vi {\n\tbackground-position:-48px -576px;\n}#home .Viktor {\n\tbackground-position:-96px -576px;\n}#home .Vladimir {\n\tbackground-position:-144px -576px;\n}#home .Volibear {\n\tbackground-position:-192px -576px;\n}#home .Warwick {\n\tbackground-position:-240px -576px;\n}#home .Xayah {\n\tbackground-position:-288px -576px;\n}#home .Xerath {\n\tbackground-position:-336px -576px;\n}#home .XinZhao {\n\tbackground-position:-384px -576px;\n}#home .Yasuo {\n\tbackground-position:-432px -576px;\n}#home .Yorick {\n\tbackground-position:-0px -624px;\n}#home .Zac {\n\tbackground-position:-48px -624px;\n}#home .Zed {\n\tbackground-position:-96px -624px;\n}#home .Ziggs {\n\tbackground-position:-144px -624px;\n}#home .Zilean {\n\tbackground-position:-192px -624px;\n}#home .Zyra {\n\tbackground-position:-240px -624px;\n}.Aatrox {\n\tbackground-position:-0px -0px;\n}.Ahri {\n\tbackground-position:-36px -0px;\n}.Akali {\n\tbackground-position:-72px -0px;\n}.Alistar {\n\tbackground-position:-108px -0px;\n}.Amumu {\n\tbackground-position:-144px -0px;\n}.Anivia {\n\tbackground-position:-180px -0px;\n}.Annie {\n\tbackground-position:-216px -0px;\n}.Ashe {\n\tbackground-position:-252px -0px;\n}.AurelionSol {\n\tbackground-position:-288px -0px;\n}.Azir {\n\tbackground-position:-324px -0px;\n}.Bard {\n\tbackground-position:-0px -36px;\n}.Blitzcrank {\n\tbackground-position:-36px -36px;\n}.Brand {\n\tbackground-position:-72px -36px;\n}.Braum {\n\tbackground-position:-108px -36px;\n}.Caitlyn {\n\tbackground-position:-144px -36px;\n}.Camille {\n\tbackground-position:-180px -36px;\n}.Cassiopeia {\n\tbackground-position:-216px -36px;\n}.Chogath {\n\tbackground-position:-252px -36px;\n}.Corki {\n\tbackground-position:-288px -36px;\n}.Darius {\n\tbackground-position:-324px -36px;\n}.Diana {\n\tbackground-position:-0px -72px;\n}.Draven {\n\tbackground-position:-36px -72px;\n}.DrMundo {\n\tbackground-position:-72px -72px;\n}.Ekko {\n\tbackground-position:-108px -72px;\n}.Elise {\n\tbackground-position:-144px -72px;\n}.Evelynn {\n\tbackground-position:-180px -72px;\n}.Ezreal {\n\tbackground-position:-216px -72px;\n}.Fiddlesticks {\n\tbackground-position:-252px -72px;\n}.Fiora {\n\tbackground-position:-288px -72px;\n}.Fizz {\n\tbackground-position:-324px -72px;\n}.Galio {\n\tbackground-position:-0px -108px;\n}.Gangplank {\n\tbackground-position:-36px -108px;\n}.Garen {\n\tbackground-position:-72px -108px;\n}.Gnar {\n\tbackground-position:-108px -108px;\n}.Gragas {\n\tbackground-position:-144px -108px;\n}.Graves {\n\tbackground-position:-180px -108px;\n}.Hecarim {\n\tbackground-position:-216px -108px;\n}.Heimerdinger {\n\tbackground-position:-252px -108px;\n}.Illaoi {\n\tbackground-position:-288px -108px;\n}.Irelia {\n\tbackground-position:-324px -108px;\n}.Ivern {\n\tbackground-position:-0px -144px;\n}.Janna {\n\tbackground-position:-36px -144px;\n}.JarvanIV {\n\tbackground-position:-72px -144px;\n}.Jax {\n\tbackground-position:-108px -144px;\n}.Jayce {\n\tbackground-position:-144px -144px;\n}.Jhin {\n\tbackground-position:-180px -144px;\n}.Jinx {\n\tbackground-position:-216px -144px;\n}.Kalista {\n\tbackground-position:-252px -144px;\n}.Karma {\n\tbackground-position:-288px -144px;\n}.Karthus {\n\tbackground-position:-324px -144px;\n}.Kassadin {\n\tbackground-position:-0px -180px;\n}.Katarina {\n\tbackground-position:-36px -180px;\n}.Kayle {\n\tbackground-position:-72px -180px;\n}.Kennen {\n\tbackground-position:-108px -180px;\n}.Khazix {\n\tbackground-position:-144px -180px;\n}.Kindred {\n\tbackground-position:-180px -180px;\n}.Kled {\n\tbackground-position:-216px -180px;\n}.KogMaw {\n\tbackground-position:-252px -180px;\n}.Leblanc {\n\tbackground-position:-288px -180px;\n}.LeeSin {\n\tbackground-position:-324px -180px;\n}.Leona {\n\tbackground-position:-0px -216px;\n}.Lissandra {\n\tbackground-position:-36px -216px;\n}.Lucian {\n\tbackground-position:-72px -216px;\n}.Lulu {\n\tbackground-position:-108px -216px;\n}.Lux {\n\tbackground-position:-144px -216px;\n}.Malphite {\n\tbackground-position:-180px -216px;\n}.Malzahar {\n\tbackground-position:-216px -216px;\n}.Maokai {\n\tbackground-position:-252px -216px;\n}.MasterYi {\n\tbackground-position:-288px -216px;\n}.MissFortune {\n\tbackground-position:-324px -216px;\n}.MonkeyKing {\n\tbackground-position:-0px -252px;\n}.Mordekaiser {\n\tbackground-position:-36px -252px;\n}.Morgana {\n\tbackground-position:-72px -252px;\n}.Nami {\n\tbackground-position:-108px -252px;\n}.Nasus {\n\tbackground-position:-144px -252px;\n}.Nautilus {\n\tbackground-position:-180px -252px;\n}.Nidalee {\n\tbackground-position:-216px -252px;\n}.Nocturne {\n\tbackground-position:-252px -252px;\n}.Nunu {\n\tbackground-position:-288px -252px;\n}.Olaf {\n\tbackground-position:-324px -252px;\n}.Orianna {\n\tbackground-position:-0px -288px;\n}.Pantheon {\n\tbackground-position:-36px -288px;\n}.Poppy {\n\tbackground-position:-72px -288px;\n}.Quinn {\n\tbackground-position:-108px -288px;\n}.Rakan {\n\tbackground-position:-144px -288px;\n}.Rammus {\n\tbackground-position:-180px -288px;\n}.RekSai {\n\tbackground-position:-216px -288px;\n}.Renekton {\n\tbackground-position:-252px -288px;\n}.Rengar {\n\tbackground-position:-288px -288px;\n}.Riven {\n\tbackground-position:-324px -288px;\n}.Rumble {\n\tbackground-position:-0px -324px;\n}.Ryze {\n\tbackground-position:-36px -324px;\n}.Sejuani {\n\tbackground-position:-72px -324px;\n}.Shaco {\n\tbackground-position:-108px -324px;\n}.Shen {\n\tbackground-position:-144px -324px;\n}.Shyvana {\n\tbackground-position:-180px -324px;\n}.Singed {\n\tbackground-position:-216px -324px;\n}.Sion {\n\tbackground-position:-252px -324px;\n}.Sivir {\n\tbackground-position:-288px -324px;\n}.Skarner {\n\tbackground-position:-324px -324px;\n}.Sona {\n\tbackground-position:-0px -360px;\n}.Soraka {\n\tbackground-position:-36px -360px;\n}.Swain {\n\tbackground-position:-72px -360px;\n}.Syndra {\n\tbackground-position:-108px -360px;\n}.TahmKench {\n\tbackground-position:-144px -360px;\n}.Taliyah {\n\tbackground-position:-180px -360px;\n}.Talon {\n\tbackground-position:-216px -360px;\n}.Taric {\n\tbackground-position:-252px -360px;\n}.Teemo {\n\tbackground-position:-288px -360px;\n}.Thresh {\n\tbackground-position:-324px -360px;\n}.Tristana {\n\tbackground-position:-0px -396px;\n}.Trundle {\n\tbackground-position:-36px -396px;\n}.Tryndamere {\n\tbackground-position:-72px -396px;\n}.TwistedFate {\n\tbackground-position:-108px -396px;\n}.Twitch {\n\tbackground-position:-144px -396px;\n}.Udyr {\n\tbackground-position:-180px -396px;\n}.Urgot {\n\tbackground-position:-216px -396px;\n}.Varus {\n\tbackground-position:-252px -396px;\n}.Vayne {\n\tbackground-position:-288px -396px;\n}.Veigar {\n\tbackground-position:-324px -396px;\n}.Velkoz {\n\tbackground-position:-0px -432px;\n}.Vi {\n\tbackground-position:-36px -432px;\n}.Viktor {\n\tbackground-position:-72px -432px;\n}.Vladimir {\n\tbackground-position:-108px -432px;\n}.Volibear {\n\tbackground-position:-144px -432px;\n}.Warwick {\n\tbackground-position:-180px -432px;\n}.Xayah {\n\tbackground-position:-216px -432px;\n}.Xerath {\n\tbackground-position:-252px -432px;\n}.XinZhao {\n\tbackground-position:-288px -432px;\n}.Yasuo {\n\tbackground-position:-324px -432px;\n}.Yorick {\n\tbackground-position:-0px -468px;\n}.Zac {\n\tbackground-position:-36px -468px;\n}.Zed {\n\tbackground-position:-72px -468px;\n}.Ziggs {\n\tbackground-position:-108px -468px;\n}.Zilean {\n\tbackground-position:-144px -468px;\n}.Zyra {\n\tbackground-position:-180px -468px;\n}.mastery-6111 {\n\tbackground-position:-0px -0px;\n}.mastery-6114 {\n\tbackground-position:-48px -0px;\n}.mastery-6121 {\n\tbackground-position:-96px -0px;\n}.mastery-6122 {\n\tbackground-position:-144px -0px;\n}.mastery-6123 {\n\tbackground-position:-192px -0px;\n}.mastery-6131 {\n\tbackground-position:-240px -0px;\n}.mastery-6134 {\n\tbackground-position:-288px -0px;\n}.mastery-6141 {\n\tbackground-position:-336px -0px;\n}.mastery-6142 {\n\tbackground-position:-384px -0px;\n}.mastery-6143 {\n\tbackground-position:-432px -0px;\n}.mastery-6151 {\n\tbackground-position:-0px -48px;\n}.mastery-6154 {\n\tbackground-position:-48px -48px;\n}.mastery-6161 {\n\tbackground-position:-96px -48px;\n}.mastery-6162 {\n\tbackground-position:-144px -48px;\n}.mastery-6164 {\n\tbackground-position:-192px -48px;\n}.mastery-6211 {\n\tbackground-position:-0px -144px;\n}.mastery-6212 {\n\tbackground-position:-48px -144px;\n}.mastery-6221 {\n\tbackground-position:-96px -144px;\n}.mastery-6222 {\n\tbackground-position:-192px -144px;\n}.mastery-6223 {\n\tbackground-position:-144px -144px;\n}.mastery-6231 {\n\tbackground-position:-240px -144px;\n}.mastery-6232 {\n\tbackground-position:-288px -144px;\n}.mastery-6241 {\n\tbackground-position:-336px -144px;\n}.mastery-6242 {\n\tbackground-position:-384px -144px;\n}.mastery-6243 {\n\tbackground-position:-432px -144px;\n}.mastery-6251 {\n\tbackground-position:-0px -192px;\n}.mastery-6252 {\n\tbackground-position:-48px -192px;\n}.mastery-6261 {\n\tbackground-position:-96px -192px;\n}.mastery-6262 {\n\tbackground-position:-144px -192px;\n}.mastery-6263 {\n\tbackground-position:-192px -192px;\n}.mastery-6311 {\n\tbackground-position:-240px -48px;\n}.mastery-6312 {\n\tbackground-position:-288px -48px;\n}.mastery-6321 {\n\tbackground-position:-336px -48px;\n}.mastery-6322 {\n\tbackground-position:-384px -48px;\n}.mastery-6323 {\n\tbackground-position:-432px -48px;\n}.mastery-6331 {\n\tbackground-position:-0px -96px;\n}.mastery-6332 {\n\tbackground-position:-48px -96px;\n}.mastery-6341 {\n\tbackground-position:-96px -96px;\n}.mastery-6342 {\n\tbackground-position:-144px -96px;\n}.mastery-6343 {\n\tbackground-position:-192px -96px;\n}.mastery-6351 {\n\tbackground-position:-240px -96px;\n}.mastery-6352 {\n\tbackground-position:-288px -96px;\n}.mastery-6361 {\n\tbackground-position:-336px -96px;\n}.mastery-6362 {\n\tbackground-position:-384px -96px;\n}.mastery-6363 {\n\tbackground-position:-432px -96px;\n}"
  },
  {
    "path": "public/dist/js/angular-bootstrap.js",
    "content": "/*\n * angular-ui-bootstrap\n * http://angular-ui.github.io/bootstrap/\n\n * Version: 0.12.1 - 2015-02-20\n * License: MIT\n */\nangular.module(\"ui.bootstrap\", [\"ui.bootstrap.transition\",\"ui.bootstrap.collapse\",\"ui.bootstrap.accordion\",\"ui.bootstrap.alert\",\"ui.bootstrap.bindHtml\",\"ui.bootstrap.buttons\",\"ui.bootstrap.carousel\",\"ui.bootstrap.dateparser\",\"ui.bootstrap.position\",\"ui.bootstrap.datepicker\",\"ui.bootstrap.dropdown\",\"ui.bootstrap.modal\",\"ui.bootstrap.pagination\",\"ui.bootstrap.tooltip\",\"ui.bootstrap.popover\",\"ui.bootstrap.progressbar\",\"ui.bootstrap.rating\",\"ui.bootstrap.tabs\",\"ui.bootstrap.timepicker\",\"ui.bootstrap.typeahead\"]);\nangular.module('ui.bootstrap.transition', [])\n\n/**\n * $transition service provides a consistent interface to trigger CSS 3 transitions and to be informed when they complete.\n * @param  {DOMElement} element  The DOMElement that will be animated.\n * @param  {string|object|function} trigger  The thing that will cause the transition to start:\n *   - As a string, it represents the css class to be added to the element.\n *   - As an object, it represents a hash of style attributes to be applied to the element.\n *   - As a function, it represents a function to be called that will cause the transition to occur.\n * @return {Promise}  A promise that is resolved when the transition finishes.\n */\n.factory('$transition', ['$q', '$timeout', '$rootScope', function($q, $timeout, $rootScope) {\n\n  var $transition = function(element, trigger, options) {\n    options = options || {};\n    var deferred = $q.defer();\n    var endEventName = $transition[options.animation ? 'animationEndEventName' : 'transitionEndEventName'];\n\n    var transitionEndHandler = function(event) {\n      $rootScope.$apply(function() {\n        element.unbind(endEventName, transitionEndHandler);\n        deferred.resolve(element);\n      });\n    };\n\n    if (endEventName) {\n      element.bind(endEventName, transitionEndHandler);\n    }\n\n    // Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur\n    $timeout(function() {\n      if ( angular.isString(trigger) ) {\n        element.addClass(trigger);\n      } else if ( angular.isFunction(trigger) ) {\n        trigger(element);\n      } else if ( angular.isObject(trigger) ) {\n        element.css(trigger);\n      }\n      //If browser does not support transitions, instantly resolve\n      if ( !endEventName ) {\n        deferred.resolve(element);\n      }\n    });\n\n    // Add our custom cancel function to the promise that is returned\n    // We can call this if we are about to run a new transition, which we know will prevent this transition from ending,\n    // i.e. it will therefore never raise a transitionEnd event for that transition\n    deferred.promise.cancel = function() {\n      if ( endEventName ) {\n        element.unbind(endEventName, transitionEndHandler);\n      }\n      deferred.reject('Transition cancelled');\n    };\n\n    return deferred.promise;\n  };\n\n  // Work out the name of the transitionEnd event\n  var transElement = document.createElement('trans');\n  var transitionEndEventNames = {\n    'WebkitTransition': 'webkitTransitionEnd',\n    'MozTransition': 'transitionend',\n    'OTransition': 'oTransitionEnd',\n    'transition': 'transitionend'\n  };\n  var animationEndEventNames = {\n    'WebkitTransition': 'webkitAnimationEnd',\n    'MozTransition': 'animationend',\n    'OTransition': 'oAnimationEnd',\n    'transition': 'animationend'\n  };\n  function findEndEventName(endEventNames) {\n    for (var name in endEventNames){\n      if (transElement.style[name] !== undefined) {\n        return endEventNames[name];\n      }\n    }\n  }\n  $transition.transitionEndEventName = findEndEventName(transitionEndEventNames);\n  $transition.animationEndEventName = findEndEventName(animationEndEventNames);\n  return $transition;\n}]);\n\nangular.module('ui.bootstrap.collapse', ['ui.bootstrap.transition'])\n\n  .directive('collapse', ['$transition', function ($transition) {\n\n    return {\n      link: function (scope, element, attrs) {\n\n        var initialAnimSkip = true;\n        var currentTransition;\n\n        function doTransition(change) {\n          var newTransition = $transition(element, change);\n          if (currentTransition) {\n            currentTransition.cancel();\n          }\n          currentTransition = newTransition;\n          newTransition.then(newTransitionDone, newTransitionDone);\n          return newTransition;\n\n          function newTransitionDone() {\n            // Make sure it's this transition, otherwise, leave it alone.\n            if (currentTransition === newTransition) {\n              currentTransition = undefined;\n            }\n          }\n        }\n\n        function expand() {\n          if (initialAnimSkip) {\n            initialAnimSkip = false;\n            expandDone();\n          } else {\n            element.removeClass('collapse').addClass('collapsing');\n            doTransition({ height: element[0].scrollHeight + 'px' }).then(expandDone);\n          }\n        }\n\n        function expandDone() {\n          element.removeClass('collapsing');\n          element.addClass('collapse in');\n          element.css({height: 'auto'});\n        }\n\n        function collapse() {\n          if (initialAnimSkip) {\n            initialAnimSkip = false;\n            collapseDone();\n            element.css({height: 0});\n          } else {\n            // CSS transitions don't work with height: auto, so we have to manually change the height to a specific value\n            element.css({ height: element[0].scrollHeight + 'px' });\n            //trigger reflow so a browser realizes that height was updated from auto to a specific value\n            var x = element[0].offsetWidth;\n\n            element.removeClass('collapse in').addClass('collapsing');\n\n            doTransition({ height: 0 }).then(collapseDone);\n          }\n        }\n\n        function collapseDone() {\n          element.removeClass('collapsing');\n          element.addClass('collapse');\n        }\n\n        scope.$watch(attrs.collapse, function (shouldCollapse) {\n          if (shouldCollapse) {\n            collapse();\n          } else {\n            expand();\n          }\n        });\n      }\n    };\n  }]);\n\nangular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])\n\n.constant('accordionConfig', {\n  closeOthers: true\n})\n\n.controller('AccordionController', ['$scope', '$attrs', 'accordionConfig', function ($scope, $attrs, accordionConfig) {\n\n  // This array keeps track of the accordion groups\n  this.groups = [];\n\n  // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to\n  this.closeOthers = function(openGroup) {\n    var closeOthers = angular.isDefined($attrs.closeOthers) ? $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;\n    if ( closeOthers ) {\n      angular.forEach(this.groups, function (group) {\n        if ( group !== openGroup ) {\n          group.isOpen = false;\n        }\n      });\n    }\n  };\n\n  // This is called from the accordion-group directive to add itself to the accordion\n  this.addGroup = function(groupScope) {\n    var that = this;\n    this.groups.push(groupScope);\n\n    groupScope.$on('$destroy', function (event) {\n      that.removeGroup(groupScope);\n    });\n  };\n\n  // This is called from the accordion-group directive when to remove itself\n  this.removeGroup = function(group) {\n    var index = this.groups.indexOf(group);\n    if ( index !== -1 ) {\n      this.groups.splice(index, 1);\n    }\n  };\n\n}])\n\n// The accordion directive simply sets up the directive controller\n// and adds an accordion CSS class to itself element.\n.directive('accordion', function () {\n  return {\n    restrict:'EA',\n    controller:'AccordionController',\n    transclude: true,\n    replace: false,\n    templateUrl: 'template/accordion/accordion.html'\n  };\n})\n\n// The accordion-group directive indicates a block of html that will expand and collapse in an accordion\n.directive('accordionGroup', function() {\n  return {\n    require:'^accordion',         // We need this directive to be inside an accordion\n    restrict:'EA',\n    transclude:true,              // It transcludes the contents of the directive into the template\n    replace: true,                // The element containing the directive will be replaced with the template\n    templateUrl:'template/accordion/accordion-group.html',\n    scope: {\n      heading: '@',               // Interpolate the heading attribute onto this scope\n      isOpen: '=?',\n      isDisabled: '=?'\n    },\n    controller: function() {\n      this.setHeading = function(element) {\n        this.heading = element;\n      };\n    },\n    link: function(scope, element, attrs, accordionCtrl) {\n      accordionCtrl.addGroup(scope);\n\n      scope.$watch('isOpen', function(value) {\n        if ( value ) {\n          accordionCtrl.closeOthers(scope);\n        }\n      });\n\n      scope.toggleOpen = function() {\n        if ( !scope.isDisabled ) {\n          scope.isOpen = !scope.isOpen;\n        }\n      };\n    }\n  };\n})\n\n// Use accordion-heading below an accordion-group to provide a heading containing HTML\n// <accordion-group>\n//   <accordion-heading>Heading containing HTML - <img src=\"...\"></accordion-heading>\n// </accordion-group>\n.directive('accordionHeading', function() {\n  return {\n    restrict: 'EA',\n    transclude: true,   // Grab the contents to be used as the heading\n    template: '',       // In effect remove this element!\n    replace: true,\n    require: '^accordionGroup',\n    link: function(scope, element, attr, accordionGroupCtrl, transclude) {\n      // Pass the heading to the accordion-group controller\n      // so that it can be transcluded into the right place in the template\n      // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]\n      accordionGroupCtrl.setHeading(transclude(scope, function() {}));\n    }\n  };\n})\n\n// Use in the accordion-group template to indicate where you want the heading to be transcluded\n// You must provide the property on the accordion-group controller that will hold the transcluded element\n// <div class=\"accordion-group\">\n//   <div class=\"accordion-heading\" ><a ... accordion-transclude=\"heading\">...</a></div>\n//   ...\n// </div>\n.directive('accordionTransclude', function() {\n  return {\n    require: '^accordionGroup',\n    link: function(scope, element, attr, controller) {\n      scope.$watch(function() { return controller[attr.accordionTransclude]; }, function(heading) {\n        if ( heading ) {\n          element.html('');\n          element.append(heading);\n        }\n      });\n    }\n  };\n});\n\nangular.module('ui.bootstrap.alert', [])\n\n.controller('AlertController', ['$scope', '$attrs', function ($scope, $attrs) {\n  $scope.closeable = 'close' in $attrs;\n  this.close = $scope.close;\n}])\n\n.directive('alert', function () {\n  return {\n    restrict:'EA',\n    controller:'AlertController',\n    templateUrl:'template/alert/alert.html',\n    transclude:true,\n    replace:true,\n    scope: {\n      type: '@',\n      close: '&'\n    }\n  };\n})\n\n.directive('dismissOnTimeout', ['$timeout', function($timeout) {\n  return {\n    require: 'alert',\n    link: function(scope, element, attrs, alertCtrl) {\n      $timeout(function(){\n        alertCtrl.close();\n      }, parseInt(attrs.dismissOnTimeout, 10));\n    }\n  };\n}]);\n\nangular.module('ui.bootstrap.bindHtml', [])\n\n  .directive('bindHtmlUnsafe', function () {\n    return function (scope, element, attr) {\n      element.addClass('ng-binding').data('$binding', attr.bindHtmlUnsafe);\n      scope.$watch(attr.bindHtmlUnsafe, function bindHtmlUnsafeWatchAction(value) {\n        element.html(value || '');\n      });\n    };\n  });\nangular.module('ui.bootstrap.buttons', [])\n\n.constant('buttonConfig', {\n  activeClass: 'active',\n  toggleEvent: 'click'\n})\n\n.controller('ButtonsController', ['buttonConfig', function(buttonConfig) {\n  this.activeClass = buttonConfig.activeClass || 'active';\n  this.toggleEvent = buttonConfig.toggleEvent || 'click';\n}])\n\n.directive('btnRadio', function () {\n  return {\n    require: ['btnRadio', 'ngModel'],\n    controller: 'ButtonsController',\n    link: function (scope, element, attrs, ctrls) {\n      var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];\n\n      //model -> UI\n      ngModelCtrl.$render = function () {\n        element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.btnRadio)));\n      };\n\n      //ui->model\n      element.bind(buttonsCtrl.toggleEvent, function () {\n        var isActive = element.hasClass(buttonsCtrl.activeClass);\n\n        if (!isActive || angular.isDefined(attrs.uncheckable)) {\n          scope.$apply(function () {\n            ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.btnRadio));\n            ngModelCtrl.$render();\n          });\n        }\n      });\n    }\n  };\n})\n\n.directive('btnCheckbox', function () {\n  return {\n    require: ['btnCheckbox', 'ngModel'],\n    controller: 'ButtonsController',\n    link: function (scope, element, attrs, ctrls) {\n      var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];\n\n      function getTrueValue() {\n        return getCheckboxValue(attrs.btnCheckboxTrue, true);\n      }\n\n      function getFalseValue() {\n        return getCheckboxValue(attrs.btnCheckboxFalse, false);\n      }\n\n      function getCheckboxValue(attributeValue, defaultValue) {\n        var val = scope.$eval(attributeValue);\n        return angular.isDefined(val) ? val : defaultValue;\n      }\n\n      //model -> UI\n      ngModelCtrl.$render = function () {\n        element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));\n      };\n\n      //ui->model\n      element.bind(buttonsCtrl.toggleEvent, function () {\n        scope.$apply(function () {\n          ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue());\n          ngModelCtrl.$render();\n        });\n      });\n    }\n  };\n});\n\n/**\n* @ngdoc overview\n* @name ui.bootstrap.carousel\n*\n* @description\n* AngularJS version of an image carousel.\n*\n*/\nangular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])\n.controller('CarouselController', ['$scope', '$timeout', '$interval', '$transition', function ($scope, $timeout, $interval, $transition) {\n  var self = this,\n    slides = self.slides = $scope.slides = [],\n    currentIndex = -1,\n    currentInterval, isPlaying;\n  self.currentSlide = null;\n\n  var destroyed = false;\n  /* direction: \"prev\" or \"next\" */\n  self.select = $scope.select = function(nextSlide, direction) {\n    var nextIndex = slides.indexOf(nextSlide);\n    //Decide direction if it's not given\n    if (direction === undefined) {\n      direction = nextIndex > currentIndex ? 'next' : 'prev';\n    }\n    if (nextSlide && nextSlide !== self.currentSlide) {\n      if ($scope.$currentTransition) {\n        $scope.$currentTransition.cancel();\n        //Timeout so ng-class in template has time to fix classes for finished slide\n        $timeout(goNext);\n      } else {\n        goNext();\n      }\n    }\n    function goNext() {\n      // Scope has been destroyed, stop here.\n      if (destroyed) { return; }\n      //If we have a slide to transition from and we have a transition type and we're allowed, go\n      if (self.currentSlide && angular.isString(direction) && !$scope.noTransition && nextSlide.$element) {\n        //We shouldn't do class manip in here, but it's the same weird thing bootstrap does. need to fix sometime\n        nextSlide.$element.addClass(direction);\n        var reflow = nextSlide.$element[0].offsetWidth; //force reflow\n\n        //Set all other slides to stop doing their stuff for the new transition\n        angular.forEach(slides, function(slide) {\n          angular.extend(slide, {direction: '', entering: false, leaving: false, active: false});\n        });\n        angular.extend(nextSlide, {direction: direction, active: true, entering: true});\n        angular.extend(self.currentSlide||{}, {direction: direction, leaving: true});\n\n        $scope.$currentTransition = $transition(nextSlide.$element, {});\n        //We have to create new pointers inside a closure since next & current will change\n        (function(next,current) {\n          $scope.$currentTransition.then(\n            function(){ transitionDone(next, current); },\n            function(){ transitionDone(next, current); }\n          );\n        }(nextSlide, self.currentSlide));\n      } else {\n        transitionDone(nextSlide, self.currentSlide);\n      }\n      self.currentSlide = nextSlide;\n      currentIndex = nextIndex;\n      //every time you change slides, reset the timer\n      restartTimer();\n    }\n    function transitionDone(next, current) {\n      angular.extend(next, {direction: '', active: true, leaving: false, entering: false});\n      angular.extend(current||{}, {direction: '', active: false, leaving: false, entering: false});\n      $scope.$currentTransition = null;\n    }\n  };\n  $scope.$on('$destroy', function () {\n    destroyed = true;\n  });\n\n  /* Allow outside people to call indexOf on slides array */\n  self.indexOfSlide = function(slide) {\n    return slides.indexOf(slide);\n  };\n\n  $scope.next = function() {\n    var newIndex = (currentIndex + 1) % slides.length;\n\n    //Prevent this user-triggered transition from occurring if there is already one in progress\n    if (!$scope.$currentTransition) {\n      return self.select(slides[newIndex], 'next');\n    }\n  };\n\n  $scope.prev = function() {\n    var newIndex = currentIndex - 1 < 0 ? slides.length - 1 : currentIndex - 1;\n\n    //Prevent this user-triggered transition from occurring if there is already one in progress\n    if (!$scope.$currentTransition) {\n      return self.select(slides[newIndex], 'prev');\n    }\n  };\n\n  $scope.isActive = function(slide) {\n     return self.currentSlide === slide;\n  };\n\n  $scope.$watch('interval', restartTimer);\n  $scope.$on('$destroy', resetTimer);\n\n  function restartTimer() {\n    resetTimer();\n    var interval = +$scope.interval;\n    if (!isNaN(interval) && interval > 0) {\n      currentInterval = $interval(timerFn, interval);\n    }\n  }\n\n  function resetTimer() {\n    if (currentInterval) {\n      $interval.cancel(currentInterval);\n      currentInterval = null;\n    }\n  }\n\n  function timerFn() {\n    var interval = +$scope.interval;\n    if (isPlaying && !isNaN(interval) && interval > 0) {\n      $scope.next();\n    } else {\n      $scope.pause();\n    }\n  }\n\n  $scope.play = function() {\n    if (!isPlaying) {\n      isPlaying = true;\n      restartTimer();\n    }\n  };\n  $scope.pause = function() {\n    if (!$scope.noPause) {\n      isPlaying = false;\n      resetTimer();\n    }\n  };\n\n  self.addSlide = function(slide, element) {\n    slide.$element = element;\n    slides.push(slide);\n    //if this is the first slide or the slide is set to active, select it\n    if(slides.length === 1 || slide.active) {\n      self.select(slides[slides.length-1]);\n      if (slides.length == 1) {\n        $scope.play();\n      }\n    } else {\n      slide.active = false;\n    }\n  };\n\n  self.removeSlide = function(slide) {\n    //get the index of the slide inside the carousel\n    var index = slides.indexOf(slide);\n    slides.splice(index, 1);\n    if (slides.length > 0 && slide.active) {\n      if (index >= slides.length) {\n        self.select(slides[index-1]);\n      } else {\n        self.select(slides[index]);\n      }\n    } else if (currentIndex > index) {\n      currentIndex--;\n    }\n  };\n\n}])\n\n/**\n * @ngdoc directive\n * @name ui.bootstrap.carousel.directive:carousel\n * @restrict EA\n *\n * @description\n * Carousel is the outer container for a set of image 'slides' to showcase.\n *\n * @param {number=} interval The time, in milliseconds, that it will take the carousel to go to the next slide.\n * @param {boolean=} noTransition Whether to disable transitions on the carousel.\n * @param {boolean=} noPause Whether to disable pausing on the carousel (by default, the carousel interval pauses on hover).\n *\n * @example\n<example module=\"ui.bootstrap\">\n  <file name=\"index.html\">\n    <carousel>\n      <slide>\n        <img src=\"http://placekitten.com/150/150\" style=\"margin:auto;\">\n        <div class=\"carousel-caption\">\n          <p>Beautiful!</p>\n        </div>\n      </slide>\n      <slide>\n        <img src=\"http://placekitten.com/100/150\" style=\"margin:auto;\">\n        <div class=\"carousel-caption\">\n          <p>D'aww!</p>\n        </div>\n      </slide>\n    </carousel>\n  </file>\n  <file name=\"demo.css\">\n    .carousel-indicators {\n      top: auto;\n      bottom: 15px;\n    }\n  </file>\n</example>\n */\n.directive('carousel', [function() {\n  return {\n    restrict: 'EA',\n    transclude: true,\n    replace: true,\n    controller: 'CarouselController',\n    require: 'carousel',\n    templateUrl: 'template/carousel/carousel.html',\n    scope: {\n      interval: '=',\n      noTransition: '=',\n      noPause: '='\n    }\n  };\n}])\n\n/**\n * @ngdoc directive\n * @name ui.bootstrap.carousel.directive:slide\n * @restrict EA\n *\n * @description\n * Creates a slide inside a {@link ui.bootstrap.carousel.directive:carousel carousel}.  Must be placed as a child of a carousel element.\n *\n * @param {boolean=} active Model binding, whether or not this slide is currently active.\n *\n * @example\n<example module=\"ui.bootstrap\">\n  <file name=\"index.html\">\n<div ng-controller=\"CarouselDemoCtrl\">\n  <carousel>\n    <slide ng-repeat=\"slide in slides\" active=\"slide.active\">\n      <img ng-src=\"{{slide.image}}\" style=\"margin:auto;\">\n      <div class=\"carousel-caption\">\n        <h4>Slide {{$index}}</h4>\n        <p>{{slide.text}}</p>\n      </div>\n    </slide>\n  </carousel>\n  Interval, in milliseconds: <input type=\"number\" ng-model=\"myInterval\">\n  <br />Enter a negative number to stop the interval.\n</div>\n  </file>\n  <file name=\"script.js\">\nfunction CarouselDemoCtrl($scope) {\n  $scope.myInterval = 5000;\n}\n  </file>\n  <file name=\"demo.css\">\n    .carousel-indicators {\n      top: auto;\n      bottom: 15px;\n    }\n  </file>\n</example>\n*/\n\n.directive('slide', function() {\n  return {\n    require: '^carousel',\n    restrict: 'EA',\n    transclude: true,\n    replace: true,\n    templateUrl: 'template/carousel/slide.html',\n    scope: {\n      active: '=?'\n    },\n    link: function (scope, element, attrs, carouselCtrl) {\n      carouselCtrl.addSlide(scope, element);\n      //when the scope is destroyed then remove the slide from the current slides array\n      scope.$on('$destroy', function() {\n        carouselCtrl.removeSlide(scope);\n      });\n\n      scope.$watch('active', function(active) {\n        if (active) {\n          carouselCtrl.select(scope);\n        }\n      });\n    }\n  };\n});\n\nangular.module('ui.bootstrap.dateparser', [])\n\n.service('dateParser', ['$locale', 'orderByFilter', function($locale, orderByFilter) {\n\n  this.parsers = {};\n\n  var formatCodeToRegex = {\n    'yyyy': {\n      regex: '\\\\d{4}',\n      apply: function(value) { this.year = +value; }\n    },\n    'yy': {\n      regex: '\\\\d{2}',\n      apply: function(value) { this.year = +value + 2000; }\n    },\n    'y': {\n      regex: '\\\\d{1,4}',\n      apply: function(value) { this.year = +value; }\n    },\n    'MMMM': {\n      regex: $locale.DATETIME_FORMATS.MONTH.join('|'),\n      apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); }\n    },\n    'MMM': {\n      regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),\n      apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); }\n    },\n    'MM': {\n      regex: '0[1-9]|1[0-2]',\n      apply: function(value) { this.month = value - 1; }\n    },\n    'M': {\n      regex: '[1-9]|1[0-2]',\n      apply: function(value) { this.month = value - 1; }\n    },\n    'dd': {\n      regex: '[0-2][0-9]{1}|3[0-1]{1}',\n      apply: function(value) { this.date = +value; }\n    },\n    'd': {\n      regex: '[1-2]?[0-9]{1}|3[0-1]{1}',\n      apply: function(value) { this.date = +value; }\n    },\n    'EEEE': {\n      regex: $locale.DATETIME_FORMATS.DAY.join('|')\n    },\n    'EEE': {\n      regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|')\n    }\n  };\n\n  function createParser(format) {\n    var map = [], regex = format.split('');\n\n    angular.forEach(formatCodeToRegex, function(data, code) {\n      var index = format.indexOf(code);\n\n      if (index > -1) {\n        format = format.split('');\n\n        regex[index] = '(' + data.regex + ')';\n        format[index] = '$'; // Custom symbol to define consumed part of format\n        for (var i = index + 1, n = index + code.length; i < n; i++) {\n          regex[i] = '';\n          format[i] = '$';\n        }\n        format = format.join('');\n\n        map.push({ index: index, apply: data.apply });\n      }\n    });\n\n    return {\n      regex: new RegExp('^' + regex.join('') + '$'),\n      map: orderByFilter(map, 'index')\n    };\n  }\n\n  this.parse = function(input, format) {\n    if ( !angular.isString(input) || !format ) {\n      return input;\n    }\n\n    format = $locale.DATETIME_FORMATS[format] || format;\n\n    if ( !this.parsers[format] ) {\n      this.parsers[format] = createParser(format);\n    }\n\n    var parser = this.parsers[format],\n        regex = parser.regex,\n        map = parser.map,\n        results = input.match(regex);\n\n    if ( results && results.length ) {\n      var fields = { year: 1900, month: 0, date: 1, hours: 0 }, dt;\n\n      for( var i = 1, n = results.length; i < n; i++ ) {\n        var mapper = map[i-1];\n        if ( mapper.apply ) {\n          mapper.apply.call(fields, results[i]);\n        }\n      }\n\n      if ( isValid(fields.year, fields.month, fields.date) ) {\n        dt = new Date( fields.year, fields.month, fields.date, fields.hours);\n      }\n\n      return dt;\n    }\n  };\n\n  // Check if date is valid for specific month (and year for February).\n  // Month: 0 = Jan, 1 = Feb, etc\n  function isValid(year, month, date) {\n    if ( month === 1 && date > 28) {\n        return date === 29 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0);\n    }\n\n    if ( month === 3 || month === 5 || month === 8 || month === 10) {\n        return date < 31;\n    }\n\n    return true;\n  }\n}]);\n\nangular.module('ui.bootstrap.position', [])\n\n/**\n * A set of utility methods that can be use to retrieve position of DOM elements.\n * It is meant to be used where we need to absolute-position DOM elements in\n * relation to other, existing elements (this is the case for tooltips, popovers,\n * typeahead suggestions etc.).\n */\n  .factory('$position', ['$document', '$window', function ($document, $window) {\n\n    function getStyle(el, cssprop) {\n      if (el.currentStyle) { //IE\n        return el.currentStyle[cssprop];\n      } else if ($window.getComputedStyle) {\n        return $window.getComputedStyle(el)[cssprop];\n      }\n      // finally try and get inline style\n      return el.style[cssprop];\n    }\n\n    /**\n     * Checks if a given element is statically positioned\n     * @param element - raw DOM element\n     */\n    function isStaticPositioned(element) {\n      return (getStyle(element, 'position') || 'static' ) === 'static';\n    }\n\n    /**\n     * returns the closest, non-statically positioned parentOffset of a given element\n     * @param element\n     */\n    var parentOffsetEl = function (element) {\n      var docDomEl = $document[0];\n      var offsetParent = element.offsetParent || docDomEl;\n      while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {\n        offsetParent = offsetParent.offsetParent;\n      }\n      return offsetParent || docDomEl;\n    };\n\n    return {\n      /**\n       * Provides read-only equivalent of jQuery's position function:\n       * http://api.jquery.com/position/\n       */\n      position: function (element) {\n        var elBCR = this.offset(element);\n        var offsetParentBCR = { top: 0, left: 0 };\n        var offsetParentEl = parentOffsetEl(element[0]);\n        if (offsetParentEl != $document[0]) {\n          offsetParentBCR = this.offset(angular.element(offsetParentEl));\n          offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;\n          offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;\n        }\n\n        var boundingClientRect = element[0].getBoundingClientRect();\n        return {\n          width: boundingClientRect.width || element.prop('offsetWidth'),\n          height: boundingClientRect.height || element.prop('offsetHeight'),\n          top: elBCR.top - offsetParentBCR.top,\n          left: elBCR.left - offsetParentBCR.left\n        };\n      },\n\n      /**\n       * Provides read-only equivalent of jQuery's offset function:\n       * http://api.jquery.com/offset/\n       */\n      offset: function (element) {\n        var boundingClientRect = element[0].getBoundingClientRect();\n        return {\n          width: boundingClientRect.width || element.prop('offsetWidth'),\n          height: boundingClientRect.height || element.prop('offsetHeight'),\n          top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop),\n          left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)\n        };\n      },\n\n      /**\n       * Provides coordinates for the targetEl in relation to hostEl\n       */\n      positionElements: function (hostEl, targetEl, positionStr, appendToBody) {\n\n        var positionStrParts = positionStr.split('-');\n        var pos0 = positionStrParts[0], pos1 = positionStrParts[1] || 'center';\n\n        var hostElPos,\n          targetElWidth,\n          targetElHeight,\n          targetElPos;\n\n        hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl);\n\n        targetElWidth = targetEl.prop('offsetWidth');\n        targetElHeight = targetEl.prop('offsetHeight');\n\n        var shiftWidth = {\n          center: function () {\n            return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;\n          },\n          left: function () {\n            return hostElPos.left;\n          },\n          right: function () {\n            return hostElPos.left + hostElPos.width;\n          }\n        };\n\n        var shiftHeight = {\n          center: function () {\n            return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;\n          },\n          top: function () {\n            return hostElPos.top;\n          },\n          bottom: function () {\n            return hostElPos.top + hostElPos.height;\n          }\n        };\n\n        switch (pos0) {\n          case 'right':\n            targetElPos = {\n              top: shiftHeight[pos1](),\n              left: shiftWidth[pos0]()\n            };\n            break;\n          case 'left':\n            targetElPos = {\n              top: shiftHeight[pos1](),\n              left: hostElPos.left - targetElWidth\n            };\n            break;\n          case 'bottom':\n            targetElPos = {\n              top: shiftHeight[pos0](),\n              left: shiftWidth[pos1]()\n            };\n            break;\n          default:\n            targetElPos = {\n              top: hostElPos.top - targetElHeight,\n              left: shiftWidth[pos1]()\n            };\n            break;\n        }\n\n        return targetElPos;\n      }\n    };\n  }]);\n\nangular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.position'])\n\n.constant('datepickerConfig', {\n  formatDay: 'dd',\n  formatMonth: 'MMMM',\n  formatYear: 'yyyy',\n  formatDayHeader: 'EEE',\n  formatDayTitle: 'MMMM yyyy',\n  formatMonthTitle: 'yyyy',\n  datepickerMode: 'day',\n  minMode: 'day',\n  maxMode: 'year',\n  showWeeks: true,\n  startingDay: 0,\n  yearRange: 20,\n  minDate: null,\n  maxDate: null\n})\n\n.controller('DatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$timeout', '$log', 'dateFilter', 'datepickerConfig', function($scope, $attrs, $parse, $interpolate, $timeout, $log, dateFilter, datepickerConfig) {\n  var self = this,\n      ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl;\n\n  // Modes chain\n  this.modes = ['day', 'month', 'year'];\n\n  // Configuration attributes\n  angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle',\n                   'minMode', 'maxMode', 'showWeeks', 'startingDay', 'yearRange'], function( key, index ) {\n    self[key] = angular.isDefined($attrs[key]) ? (index < 8 ? $interpolate($attrs[key])($scope.$parent) : $scope.$parent.$eval($attrs[key])) : datepickerConfig[key];\n  });\n\n  // Watchable date attributes\n  angular.forEach(['minDate', 'maxDate'], function( key ) {\n    if ( $attrs[key] ) {\n      $scope.$parent.$watch($parse($attrs[key]), function(value) {\n        self[key] = value ? new Date(value) : null;\n        self.refreshView();\n      });\n    } else {\n      self[key] = datepickerConfig[key] ? new Date(datepickerConfig[key]) : null;\n    }\n  });\n\n  $scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode;\n  $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);\n  this.activeDate = angular.isDefined($attrs.initDate) ? $scope.$parent.$eval($attrs.initDate) : new Date();\n\n  $scope.isActive = function(dateObject) {\n    if (self.compare(dateObject.date, self.activeDate) === 0) {\n      $scope.activeDateId = dateObject.uid;\n      return true;\n    }\n    return false;\n  };\n\n  this.init = function( ngModelCtrl_ ) {\n    ngModelCtrl = ngModelCtrl_;\n\n    ngModelCtrl.$render = function() {\n      self.render();\n    };\n  };\n\n  this.render = function() {\n    if ( ngModelCtrl.$modelValue ) {\n      var date = new Date( ngModelCtrl.$modelValue ),\n          isValid = !isNaN(date);\n\n      if ( isValid ) {\n        this.activeDate = date;\n      } else {\n        $log.error('Datepicker directive: \"ng-model\" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');\n      }\n      ngModelCtrl.$setValidity('date', isValid);\n    }\n    this.refreshView();\n  };\n\n  this.refreshView = function() {\n    if ( this.element ) {\n      this._refreshView();\n\n      var date = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : null;\n      ngModelCtrl.$setValidity('date-disabled', !date || (this.element && !this.isDisabled(date)));\n    }\n  };\n\n  this.createDateObject = function(date, format) {\n    var model = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : null;\n    return {\n      date: date,\n      label: dateFilter(date, format),\n      selected: model && this.compare(date, model) === 0,\n      disabled: this.isDisabled(date),\n      current: this.compare(date, new Date()) === 0\n    };\n  };\n\n  this.isDisabled = function( date ) {\n    return ((this.minDate && this.compare(date, this.minDate) < 0) || (this.maxDate && this.compare(date, this.maxDate) > 0) || ($attrs.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode})));\n  };\n\n  // Split array into smaller arrays\n  this.split = function(arr, size) {\n    var arrays = [];\n    while (arr.length > 0) {\n      arrays.push(arr.splice(0, size));\n    }\n    return arrays;\n  };\n\n  $scope.select = function( date ) {\n    if ( $scope.datepickerMode === self.minMode ) {\n      var dt = ngModelCtrl.$modelValue ? new Date( ngModelCtrl.$modelValue ) : new Date(0, 0, 0, 0, 0, 0, 0);\n      dt.setFullYear( date.getFullYear(), date.getMonth(), date.getDate() );\n      ngModelCtrl.$setViewValue( dt );\n      ngModelCtrl.$render();\n    } else {\n      self.activeDate = date;\n      $scope.datepickerMode = self.modes[ self.modes.indexOf( $scope.datepickerMode ) - 1 ];\n    }\n  };\n\n  $scope.move = function( direction ) {\n    var year = self.activeDate.getFullYear() + direction * (self.step.years || 0),\n        month = self.activeDate.getMonth() + direction * (self.step.months || 0);\n    self.activeDate.setFullYear(year, month, 1);\n    self.refreshView();\n  };\n\n  $scope.toggleMode = function( direction ) {\n    direction = direction || 1;\n\n    if (($scope.datepickerMode === self.maxMode && direction === 1) || ($scope.datepickerMode === self.minMode && direction === -1)) {\n      return;\n    }\n\n    $scope.datepickerMode = self.modes[ self.modes.indexOf( $scope.datepickerMode ) + direction ];\n  };\n\n  // Key event mapper\n  $scope.keys = { 13:'enter', 32:'space', 33:'pageup', 34:'pagedown', 35:'end', 36:'home', 37:'left', 38:'up', 39:'right', 40:'down' };\n\n  var focusElement = function() {\n    $timeout(function() {\n      self.element[0].focus();\n    }, 0 , false);\n  };\n\n  // Listen for focus requests from popup directive\n  $scope.$on('datepicker.focus', focusElement);\n\n  $scope.keydown = function( evt ) {\n    var key = $scope.keys[evt.which];\n\n    if ( !key || evt.shiftKey || evt.altKey ) {\n      return;\n    }\n\n    evt.preventDefault();\n    evt.stopPropagation();\n\n    if (key === 'enter' || key === 'space') {\n      if ( self.isDisabled(self.activeDate)) {\n        return; // do nothing\n      }\n      $scope.select(self.activeDate);\n      focusElement();\n    } else if (evt.ctrlKey && (key === 'up' || key === 'down')) {\n      $scope.toggleMode(key === 'up' ? 1 : -1);\n      focusElement();\n    } else {\n      self.handleKeyDown(key, evt);\n      self.refreshView();\n    }\n  };\n}])\n\n.directive( 'datepicker', function () {\n  return {\n    restrict: 'EA',\n    replace: true,\n    templateUrl: 'template/datepicker/datepicker.html',\n    scope: {\n      datepickerMode: '=?',\n      dateDisabled: '&'\n    },\n    require: ['datepicker', '?^ngModel'],\n    controller: 'DatepickerController',\n    link: function(scope, element, attrs, ctrls) {\n      var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];\n\n      if ( ngModelCtrl ) {\n        datepickerCtrl.init( ngModelCtrl );\n      }\n    }\n  };\n})\n\n.directive('daypicker', ['dateFilter', function (dateFilter) {\n  return {\n    restrict: 'EA',\n    replace: true,\n    templateUrl: 'template/datepicker/day.html',\n    require: '^datepicker',\n    link: function(scope, element, attrs, ctrl) {\n      scope.showWeeks = ctrl.showWeeks;\n\n      ctrl.step = { months: 1 };\n      ctrl.element = element;\n\n      var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n      function getDaysInMonth( year, month ) {\n        return ((month === 1) && (year % 4 === 0) && ((year % 100 !== 0) || (year % 400 === 0))) ? 29 : DAYS_IN_MONTH[month];\n      }\n\n      function getDates(startDate, n) {\n        var dates = new Array(n), current = new Date(startDate), i = 0;\n        current.setHours(12); // Prevent repeated dates because of timezone bug\n        while ( i < n ) {\n          dates[i++] = new Date(current);\n          current.setDate( current.getDate() + 1 );\n        }\n        return dates;\n      }\n\n      ctrl._refreshView = function() {\n        var year = ctrl.activeDate.getFullYear(),\n          month = ctrl.activeDate.getMonth(),\n          firstDayOfMonth = new Date(year, month, 1),\n          difference = ctrl.startingDay - firstDayOfMonth.getDay(),\n          numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference,\n          firstDate = new Date(firstDayOfMonth);\n\n        if ( numDisplayedFromPreviousMonth > 0 ) {\n          firstDate.setDate( - numDisplayedFromPreviousMonth + 1 );\n        }\n\n        // 42 is the number of days on a six-month calendar\n        var days = getDates(firstDate, 42);\n        for (var i = 0; i < 42; i ++) {\n          days[i] = angular.extend(ctrl.createDateObject(days[i], ctrl.formatDay), {\n            secondary: days[i].getMonth() !== month,\n            uid: scope.uniqueId + '-' + i\n          });\n        }\n\n        scope.labels = new Array(7);\n        for (var j = 0; j < 7; j++) {\n          scope.labels[j] = {\n            abbr: dateFilter(days[j].date, ctrl.formatDayHeader),\n            full: dateFilter(days[j].date, 'EEEE')\n          };\n        }\n\n        scope.title = dateFilter(ctrl.activeDate, ctrl.formatDayTitle);\n        scope.rows = ctrl.split(days, 7);\n\n        if ( scope.showWeeks ) {\n          scope.weekNumbers = [];\n          var weekNumber = getISO8601WeekNumber( scope.rows[0][0].date ),\n              numWeeks = scope.rows.length;\n          while( scope.weekNumbers.push(weekNumber++) < numWeeks ) {}\n        }\n      };\n\n      ctrl.compare = function(date1, date2) {\n        return (new Date( date1.getFullYear(), date1.getMonth(), date1.getDate() ) - new Date( date2.getFullYear(), date2.getMonth(), date2.getDate() ) );\n      };\n\n      function getISO8601WeekNumber(date) {\n        var checkDate = new Date(date);\n        checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday\n        var time = checkDate.getTime();\n        checkDate.setMonth(0); // Compare with Jan 1\n        checkDate.setDate(1);\n        return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;\n      }\n\n      ctrl.handleKeyDown = function( key, evt ) {\n        var date = ctrl.activeDate.getDate();\n\n        if (key === 'left') {\n          date = date - 1;   // up\n        } else if (key === 'up') {\n          date = date - 7;   // down\n        } else if (key === 'right') {\n          date = date + 1;   // down\n        } else if (key === 'down') {\n          date = date + 7;\n        } else if (key === 'pageup' || key === 'pagedown') {\n          var month = ctrl.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1);\n          ctrl.activeDate.setMonth(month, 1);\n          date = Math.min(getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth()), date);\n        } else if (key === 'home') {\n          date = 1;\n        } else if (key === 'end') {\n          date = getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth());\n        }\n        ctrl.activeDate.setDate(date);\n      };\n\n      ctrl.refreshView();\n    }\n  };\n}])\n\n.directive('monthpicker', ['dateFilter', function (dateFilter) {\n  return {\n    restrict: 'EA',\n    replace: true,\n    templateUrl: 'template/datepicker/month.html',\n    require: '^datepicker',\n    link: function(scope, element, attrs, ctrl) {\n      ctrl.step = { years: 1 };\n      ctrl.element = element;\n\n      ctrl._refreshView = function() {\n        var months = new Array(12),\n            year = ctrl.activeDate.getFullYear();\n\n        for ( var i = 0; i < 12; i++ ) {\n          months[i] = angular.extend(ctrl.createDateObject(new Date(year, i, 1), ctrl.formatMonth), {\n            uid: scope.uniqueId + '-' + i\n          });\n        }\n\n        scope.title = dateFilter(ctrl.activeDate, ctrl.formatMonthTitle);\n        scope.rows = ctrl.split(months, 3);\n      };\n\n      ctrl.compare = function(date1, date2) {\n        return new Date( date1.getFullYear(), date1.getMonth() ) - new Date( date2.getFullYear(), date2.getMonth() );\n      };\n\n      ctrl.handleKeyDown = function( key, evt ) {\n        var date = ctrl.activeDate.getMonth();\n\n        if (key === 'left') {\n          date = date - 1;   // up\n        } else if (key === 'up') {\n          date = date - 3;   // down\n        } else if (key === 'right') {\n          date = date + 1;   // down\n        } else if (key === 'down') {\n          date = date + 3;\n        } else if (key === 'pageup' || key === 'pagedown') {\n          var year = ctrl.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1);\n          ctrl.activeDate.setFullYear(year);\n        } else if (key === 'home') {\n          date = 0;\n        } else if (key === 'end') {\n          date = 11;\n        }\n        ctrl.activeDate.setMonth(date);\n      };\n\n      ctrl.refreshView();\n    }\n  };\n}])\n\n.directive('yearpicker', ['dateFilter', function (dateFilter) {\n  return {\n    restrict: 'EA',\n    replace: true,\n    templateUrl: 'template/datepicker/year.html',\n    require: '^datepicker',\n    link: function(scope, element, attrs, ctrl) {\n      var range = ctrl.yearRange;\n\n      ctrl.step = { years: range };\n      ctrl.element = element;\n\n      function getStartingYear( year ) {\n        return parseInt((year - 1) / range, 10) * range + 1;\n      }\n\n      ctrl._refreshView = function() {\n        var years = new Array(range);\n\n        for ( var i = 0, start = getStartingYear(ctrl.activeDate.getFullYear()); i < range; i++ ) {\n          years[i] = angular.extend(ctrl.createDateObject(new Date(start + i, 0, 1), ctrl.formatYear), {\n            uid: scope.uniqueId + '-' + i\n          });\n        }\n\n        scope.title = [years[0].label, years[range - 1].label].join(' - ');\n        scope.rows = ctrl.split(years, 5);\n      };\n\n      ctrl.compare = function(date1, date2) {\n        return date1.getFullYear() - date2.getFullYear();\n      };\n\n      ctrl.handleKeyDown = function( key, evt ) {\n        var date = ctrl.activeDate.getFullYear();\n\n        if (key === 'left') {\n          date = date - 1;   // up\n        } else if (key === 'up') {\n          date = date - 5;   // down\n        } else if (key === 'right') {\n          date = date + 1;   // down\n        } else if (key === 'down') {\n          date = date + 5;\n        } else if (key === 'pageup' || key === 'pagedown') {\n          date += (key === 'pageup' ? - 1 : 1) * ctrl.step.years;\n        } else if (key === 'home') {\n          date = getStartingYear( ctrl.activeDate.getFullYear() );\n        } else if (key === 'end') {\n          date = getStartingYear( ctrl.activeDate.getFullYear() ) + range - 1;\n        }\n        ctrl.activeDate.setFullYear(date);\n      };\n\n      ctrl.refreshView();\n    }\n  };\n}])\n\n.constant('datepickerPopupConfig', {\n  datepickerPopup: 'yyyy-MM-dd',\n  currentText: 'Today',\n  clearText: 'Clear',\n  closeText: 'Done',\n  closeOnDateSelection: true,\n  appendToBody: false,\n  showButtonBar: true\n})\n\n.directive('datepickerPopup', ['$compile', '$parse', '$document', '$position', 'dateFilter', 'dateParser', 'datepickerPopupConfig',\nfunction ($compile, $parse, $document, $position, dateFilter, dateParser, datepickerPopupConfig) {\n  return {\n    restrict: 'EA',\n    require: 'ngModel',\n    scope: {\n      isOpen: '=?',\n      currentText: '@',\n      clearText: '@',\n      closeText: '@',\n      dateDisabled: '&'\n    },\n    link: function(scope, element, attrs, ngModel) {\n      var dateFormat,\n          closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection,\n          appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody;\n\n      scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;\n\n      scope.getText = function( key ) {\n        return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];\n      };\n\n      attrs.$observe('datepickerPopup', function(value) {\n          dateFormat = value || datepickerPopupConfig.datepickerPopup;\n          ngModel.$render();\n      });\n\n      // popup element used to display calendar\n      var popupEl = angular.element('<div datepicker-popup-wrap><div datepicker></div></div>');\n      popupEl.attr({\n        'ng-model': 'date',\n        'ng-change': 'dateSelection()'\n      });\n\n      function cameltoDash( string ){\n        return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); });\n      }\n\n      // datepicker element\n      var datepickerEl = angular.element(popupEl.children()[0]);\n      if ( attrs.datepickerOptions ) {\n        angular.forEach(scope.$parent.$eval(attrs.datepickerOptions), function( value, option ) {\n          datepickerEl.attr( cameltoDash(option), value );\n        });\n      }\n\n      scope.watchData = {};\n      angular.forEach(['minDate', 'maxDate', 'datepickerMode'], function( key ) {\n        if ( attrs[key] ) {\n          var getAttribute = $parse(attrs[key]);\n          scope.$parent.$watch(getAttribute, function(value){\n            scope.watchData[key] = value;\n          });\n          datepickerEl.attr(cameltoDash(key), 'watchData.' + key);\n\n          // Propagate changes from datepicker to outside\n          if ( key === 'datepickerMode' ) {\n            var setAttribute = getAttribute.assign;\n            scope.$watch('watchData.' + key, function(value, oldvalue) {\n              if ( value !== oldvalue ) {\n                setAttribute(scope.$parent, value);\n              }\n            });\n          }\n        }\n      });\n      if (attrs.dateDisabled) {\n        datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })');\n      }\n\n      function parseDate(viewValue) {\n        if (!viewValue) {\n          ngModel.$setValidity('date', true);\n          return null;\n        } else if (angular.isDate(viewValue) && !isNaN(viewValue)) {\n          ngModel.$setValidity('date', true);\n          return viewValue;\n        } else if (angular.isString(viewValue)) {\n          var date = dateParser.parse(viewValue, dateFormat) || new Date(viewValue);\n          if (isNaN(date)) {\n            ngModel.$setValidity('date', false);\n            return undefined;\n          } else {\n            ngModel.$setValidity('date', true);\n            return date;\n          }\n        } else {\n          ngModel.$setValidity('date', false);\n          return undefined;\n        }\n      }\n      ngModel.$parsers.unshift(parseDate);\n\n      // Inner change\n      scope.dateSelection = function(dt) {\n        if (angular.isDefined(dt)) {\n          scope.date = dt;\n        }\n        ngModel.$setViewValue(scope.date);\n        ngModel.$render();\n\n        if ( closeOnDateSelection ) {\n          scope.isOpen = false;\n          element[0].focus();\n        }\n      };\n\n      element.bind('input change keyup', function() {\n        scope.$apply(function() {\n          scope.date = ngModel.$modelValue;\n        });\n      });\n\n      // Outter change\n      ngModel.$render = function() {\n        var date = ngModel.$viewValue ? dateFilter(ngModel.$viewValue, dateFormat) : '';\n        element.val(date);\n        scope.date = parseDate( ngModel.$modelValue );\n      };\n\n      var documentClickBind = function(event) {\n        if (scope.isOpen && event.target !== element[0]) {\n          scope.$apply(function() {\n            scope.isOpen = false;\n          });\n        }\n      };\n\n      var keydown = function(evt, noApply) {\n        scope.keydown(evt);\n      };\n      element.bind('keydown', keydown);\n\n      scope.keydown = function(evt) {\n        if (evt.which === 27) {\n          evt.preventDefault();\n          evt.stopPropagation();\n          scope.close();\n        } else if (evt.which === 40 && !scope.isOpen) {\n          scope.isOpen = true;\n        }\n      };\n\n      scope.$watch('isOpen', function(value) {\n        if (value) {\n          scope.$broadcast('datepicker.focus');\n          scope.position = appendToBody ? $position.offset(element) : $position.position(element);\n          scope.position.top = scope.position.top + element.prop('offsetHeight');\n\n          $document.bind('click', documentClickBind);\n        } else {\n          $document.unbind('click', documentClickBind);\n        }\n      });\n\n      scope.select = function( date ) {\n        if (date === 'today') {\n          var today = new Date();\n          if (angular.isDate(ngModel.$modelValue)) {\n            date = new Date(ngModel.$modelValue);\n            date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());\n          } else {\n            date = new Date(today.setHours(0, 0, 0, 0));\n          }\n        }\n        scope.dateSelection( date );\n      };\n\n      scope.close = function() {\n        scope.isOpen = false;\n        element[0].focus();\n      };\n\n      var $popup = $compile(popupEl)(scope);\n      // Prevent jQuery cache memory leak (template is now redundant after linking)\n      popupEl.remove();\n\n      if ( appendToBody ) {\n        $document.find('body').append($popup);\n      } else {\n        element.after($popup);\n      }\n\n      scope.$on('$destroy', function() {\n        $popup.remove();\n        element.unbind('keydown', keydown);\n        $document.unbind('click', documentClickBind);\n      });\n    }\n  };\n}])\n\n.directive('datepickerPopupWrap', function() {\n  return {\n    restrict:'EA',\n    replace: true,\n    transclude: true,\n    templateUrl: 'template/datepicker/popup.html',\n    link:function (scope, element, attrs) {\n      element.bind('click', function(event) {\n        event.preventDefault();\n        event.stopPropagation();\n      });\n    }\n  };\n});\n\nangular.module('ui.bootstrap.dropdown', [])\n\n.constant('dropdownConfig', {\n  openClass: 'open'\n})\n\n.service('dropdownService', ['$document', function($document) {\n  var openScope = null;\n\n  this.open = function( dropdownScope ) {\n    if ( !openScope ) {\n      $document.bind('click', closeDropdown);\n      $document.bind('keydown', escapeKeyBind);\n    }\n\n    if ( openScope && openScope !== dropdownScope ) {\n        openScope.isOpen = false;\n    }\n\n    openScope = dropdownScope;\n  };\n\n  this.close = function( dropdownScope ) {\n    if ( openScope === dropdownScope ) {\n      openScope = null;\n      $document.unbind('click', closeDropdown);\n      $document.unbind('keydown', escapeKeyBind);\n    }\n  };\n\n  var closeDropdown = function( evt ) {\n    // This method may still be called during the same mouse event that\n    // unbound this event handler. So check openScope before proceeding.\n    if (!openScope) { return; }\n\n    var toggleElement = openScope.getToggleElement();\n    if ( evt && toggleElement && toggleElement[0].contains(evt.target) ) {\n        return;\n    }\n\n    openScope.$apply(function() {\n      openScope.isOpen = false;\n    });\n  };\n\n  var escapeKeyBind = function( evt ) {\n    if ( evt.which === 27 ) {\n      openScope.focusToggleElement();\n      closeDropdown();\n    }\n  };\n}])\n\n.controller('DropdownController', ['$scope', '$attrs', '$parse', 'dropdownConfig', 'dropdownService', '$animate', function($scope, $attrs, $parse, dropdownConfig, dropdownService, $animate) {\n  var self = this,\n      scope = $scope.$new(), // create a child scope so we are not polluting original one\n      openClass = dropdownConfig.openClass,\n      getIsOpen,\n      setIsOpen = angular.noop,\n      toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop;\n\n  this.init = function( element ) {\n    self.$element = element;\n\n    if ( $attrs.isOpen ) {\n      getIsOpen = $parse($attrs.isOpen);\n      setIsOpen = getIsOpen.assign;\n\n      $scope.$watch(getIsOpen, function(value) {\n        scope.isOpen = !!value;\n      });\n    }\n  };\n\n  this.toggle = function( open ) {\n    return scope.isOpen = arguments.length ? !!open : !scope.isOpen;\n  };\n\n  // Allow other directives to watch status\n  this.isOpen = function() {\n    return scope.isOpen;\n  };\n\n  scope.getToggleElement = function() {\n    return self.toggleElement;\n  };\n\n  scope.focusToggleElement = function() {\n    if ( self.toggleElement ) {\n      self.toggleElement[0].focus();\n    }\n  };\n\n  scope.$watch('isOpen', function( isOpen, wasOpen ) {\n    $animate[isOpen ? 'addClass' : 'removeClass'](self.$element, openClass);\n\n    if ( isOpen ) {\n      scope.focusToggleElement();\n      dropdownService.open( scope );\n    } else {\n      dropdownService.close( scope );\n    }\n\n    setIsOpen($scope, isOpen);\n    if (angular.isDefined(isOpen) && isOpen !== wasOpen) {\n      toggleInvoker($scope, { open: !!isOpen });\n    }\n  });\n\n  $scope.$on('$locationChangeSuccess', function() {\n    scope.isOpen = false;\n  });\n\n  $scope.$on('$destroy', function() {\n    scope.$destroy();\n  });\n}])\n\n.directive('dropdown', function() {\n  return {\n    controller: 'DropdownController',\n    link: function(scope, element, attrs, dropdownCtrl) {\n      dropdownCtrl.init( element );\n    }\n  };\n})\n\n.directive('dropdownToggle', function() {\n  return {\n    require: '?^dropdown',\n    link: function(scope, element, attrs, dropdownCtrl) {\n      if ( !dropdownCtrl ) {\n        return;\n      }\n\n      dropdownCtrl.toggleElement = element;\n\n      var toggleDropdown = function(event) {\n        event.preventDefault();\n\n        if ( !element.hasClass('disabled') && !attrs.disabled ) {\n          scope.$apply(function() {\n            dropdownCtrl.toggle();\n          });\n        }\n      };\n\n      element.bind('click', toggleDropdown);\n\n      // WAI-ARIA\n      element.attr({ 'aria-haspopup': true, 'aria-expanded': false });\n      scope.$watch(dropdownCtrl.isOpen, function( isOpen ) {\n        element.attr('aria-expanded', !!isOpen);\n      });\n\n      scope.$on('$destroy', function() {\n        element.unbind('click', toggleDropdown);\n      });\n    }\n  };\n});\n\nangular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])\n\n/**\n * A helper, internal data structure that acts as a map but also allows getting / removing\n * elements in the LIFO order\n */\n  .factory('$$stackedMap', function () {\n    return {\n      createNew: function () {\n        var stack = [];\n\n        return {\n          add: function (key, value) {\n            stack.push({\n              key: key,\n              value: value\n            });\n          },\n          get: function (key) {\n            for (var i = 0; i < stack.length; i++) {\n              if (key == stack[i].key) {\n                return stack[i];\n              }\n            }\n          },\n          keys: function() {\n            var keys = [];\n            for (var i = 0; i < stack.length; i++) {\n              keys.push(stack[i].key);\n            }\n            return keys;\n          },\n          top: function () {\n            return stack[stack.length - 1];\n          },\n          remove: function (key) {\n            var idx = -1;\n            for (var i = 0; i < stack.length; i++) {\n              if (key == stack[i].key) {\n                idx = i;\n                break;\n              }\n            }\n            return stack.splice(idx, 1)[0];\n          },\n          removeTop: function () {\n            return stack.splice(stack.length - 1, 1)[0];\n          },\n          length: function () {\n            return stack.length;\n          }\n        };\n      }\n    };\n  })\n\n/**\n * A helper directive for the $modal service. It creates a backdrop element.\n */\n  .directive('modalBackdrop', ['$timeout', function ($timeout) {\n    return {\n      restrict: 'EA',\n      replace: true,\n      templateUrl: 'template/modal/backdrop.html',\n      link: function (scope, element, attrs) {\n        scope.backdropClass = attrs.backdropClass || '';\n\n        scope.animate = false;\n\n        //trigger CSS transitions\n        $timeout(function () {\n          scope.animate = true;\n        });\n      }\n    };\n  }])\n\n  .directive('modalWindow', ['$modalStack', '$timeout', function ($modalStack, $timeout) {\n    return {\n      restrict: 'EA',\n      scope: {\n        index: '@',\n        animate: '='\n      },\n      replace: true,\n      transclude: true,\n      templateUrl: function(tElement, tAttrs) {\n        return tAttrs.templateUrl || 'template/modal/window.html';\n      },\n      link: function (scope, element, attrs) {\n        element.addClass(attrs.windowClass || '');\n        scope.size = attrs.size;\n\n        $timeout(function () {\n          // trigger CSS transitions\n          scope.animate = true;\n\n          /**\n           * Auto-focusing of a freshly-opened modal element causes any child elements\n           * with the autofocus attribute to lose focus. This is an issue on touch\n           * based devices which will show and then hide the onscreen keyboard.\n           * Attempts to refocus the autofocus element via JavaScript will not reopen\n           * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus\n           * the modal element if the modal does not contain an autofocus element.\n           */\n          if (!element[0].querySelectorAll('[autofocus]').length) {\n            element[0].focus();\n          }\n        });\n\n        scope.close = function (evt) {\n          var modal = $modalStack.getTop();\n          if (modal && modal.value.backdrop && modal.value.backdrop != 'static' && (evt.target === evt.currentTarget)) {\n            evt.preventDefault();\n            evt.stopPropagation();\n            $modalStack.dismiss(modal.key, 'backdrop click');\n          }\n        };\n      }\n    };\n  }])\n\n  .directive('modalTransclude', function () {\n    return {\n      link: function($scope, $element, $attrs, controller, $transclude) {\n        $transclude($scope.$parent, function(clone) {\n          $element.empty();\n          $element.append(clone);\n        });\n      }\n    };\n  })\n\n  .factory('$modalStack', ['$transition', '$timeout', '$document', '$compile', '$rootScope', '$$stackedMap',\n    function ($transition, $timeout, $document, $compile, $rootScope, $$stackedMap) {\n\n      var OPENED_MODAL_CLASS = 'modal-open';\n\n      var backdropDomEl, backdropScope;\n      var openedWindows = $$stackedMap.createNew();\n      var $modalStack = {};\n\n      function backdropIndex() {\n        var topBackdropIndex = -1;\n        var opened = openedWindows.keys();\n        for (var i = 0; i < opened.length; i++) {\n          if (openedWindows.get(opened[i]).value.backdrop) {\n            topBackdropIndex = i;\n          }\n        }\n        return topBackdropIndex;\n      }\n\n      $rootScope.$watch(backdropIndex, function(newBackdropIndex){\n        if (backdropScope) {\n          backdropScope.index = newBackdropIndex;\n        }\n      });\n\n      function removeModalWindow(modalInstance) {\n\n        var body = $document.find('body').eq(0);\n        var modalWindow = openedWindows.get(modalInstance).value;\n\n        //clean up the stack\n        openedWindows.remove(modalInstance);\n\n        //remove window DOM element\n        removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, 300, function() {\n          modalWindow.modalScope.$destroy();\n          body.toggleClass(OPENED_MODAL_CLASS, openedWindows.length() > 0);\n          checkRemoveBackdrop();\n        });\n      }\n\n      function checkRemoveBackdrop() {\n          //remove backdrop if no longer needed\n          if (backdropDomEl && backdropIndex() == -1) {\n            var backdropScopeRef = backdropScope;\n            removeAfterAnimate(backdropDomEl, backdropScope, 150, function () {\n              backdropScopeRef.$destroy();\n              backdropScopeRef = null;\n            });\n            backdropDomEl = undefined;\n            backdropScope = undefined;\n          }\n      }\n\n      function removeAfterAnimate(domEl, scope, emulateTime, done) {\n        // Closing animation\n        scope.animate = false;\n\n        var transitionEndEventName = $transition.transitionEndEventName;\n        if (transitionEndEventName) {\n          // transition out\n          var timeout = $timeout(afterAnimating, emulateTime);\n\n          domEl.bind(transitionEndEventName, function () {\n            $timeout.cancel(timeout);\n            afterAnimating();\n            scope.$apply();\n          });\n        } else {\n          // Ensure this call is async\n          $timeout(afterAnimating);\n        }\n\n        function afterAnimating() {\n          if (afterAnimating.done) {\n            return;\n          }\n          afterAnimating.done = true;\n\n          domEl.remove();\n          if (done) {\n            done();\n          }\n        }\n      }\n\n      $document.bind('keydown', function (evt) {\n        var modal;\n\n        if (evt.which === 27) {\n          modal = openedWindows.top();\n          if (modal && modal.value.keyboard) {\n            evt.preventDefault();\n            $rootScope.$apply(function () {\n              $modalStack.dismiss(modal.key, 'escape key press');\n            });\n          }\n        }\n      });\n\n      $modalStack.open = function (modalInstance, modal) {\n\n        openedWindows.add(modalInstance, {\n          deferred: modal.deferred,\n          modalScope: modal.scope,\n          backdrop: modal.backdrop,\n          keyboard: modal.keyboard\n        });\n\n        var body = $document.find('body').eq(0),\n            currBackdropIndex = backdropIndex();\n\n        if (currBackdropIndex >= 0 && !backdropDomEl) {\n          backdropScope = $rootScope.$new(true);\n          backdropScope.index = currBackdropIndex;\n          var angularBackgroundDomEl = angular.element('<div modal-backdrop></div>');\n          angularBackgroundDomEl.attr('backdrop-class', modal.backdropClass);\n          backdropDomEl = $compile(angularBackgroundDomEl)(backdropScope);\n          body.append(backdropDomEl);\n        }\n\n        var angularDomEl = angular.element('<div modal-window></div>');\n        angularDomEl.attr({\n          'template-url': modal.windowTemplateUrl,\n          'window-class': modal.windowClass,\n          'size': modal.size,\n          'index': openedWindows.length() - 1,\n          'animate': 'animate'\n        }).html(modal.content);\n\n        var modalDomEl = $compile(angularDomEl)(modal.scope);\n        openedWindows.top().value.modalDomEl = modalDomEl;\n        body.append(modalDomEl);\n        body.addClass(OPENED_MODAL_CLASS);\n      };\n\n      $modalStack.close = function (modalInstance, result) {\n        var modalWindow = openedWindows.get(modalInstance);\n        if (modalWindow) {\n          modalWindow.value.deferred.resolve(result);\n          removeModalWindow(modalInstance);\n        }\n      };\n\n      $modalStack.dismiss = function (modalInstance, reason) {\n        var modalWindow = openedWindows.get(modalInstance);\n        if (modalWindow) {\n          modalWindow.value.deferred.reject(reason);\n          removeModalWindow(modalInstance);\n        }\n      };\n\n      $modalStack.dismissAll = function (reason) {\n        var topModal = this.getTop();\n        while (topModal) {\n          this.dismiss(topModal.key, reason);\n          topModal = this.getTop();\n        }\n      };\n\n      $modalStack.getTop = function () {\n        return openedWindows.top();\n      };\n\n      return $modalStack;\n    }])\n\n  .provider('$modal', function () {\n\n    var $modalProvider = {\n      options: {\n        backdrop: true, //can be also false or 'static'\n        keyboard: true\n      },\n      $get: ['$injector', '$rootScope', '$q', '$http', '$templateCache', '$controller', '$modalStack',\n        function ($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) {\n\n          var $modal = {};\n\n          function getTemplatePromise(options) {\n            return options.template ? $q.when(options.template) :\n              $http.get(angular.isFunction(options.templateUrl) ? (options.templateUrl)() : options.templateUrl,\n                {cache: $templateCache}).then(function (result) {\n                  return result.data;\n              });\n          }\n\n          function getResolvePromises(resolves) {\n            var promisesArr = [];\n            angular.forEach(resolves, function (value) {\n              if (angular.isFunction(value) || angular.isArray(value)) {\n                promisesArr.push($q.when($injector.invoke(value)));\n              }\n            });\n            return promisesArr;\n          }\n\n          $modal.open = function (modalOptions) {\n\n            var modalResultDeferred = $q.defer();\n            var modalOpenedDeferred = $q.defer();\n\n            //prepare an instance of a modal to be injected into controllers and returned to a caller\n            var modalInstance = {\n              result: modalResultDeferred.promise,\n              opened: modalOpenedDeferred.promise,\n              close: function (result) {\n                $modalStack.close(modalInstance, result);\n              },\n              dismiss: function (reason) {\n                $modalStack.dismiss(modalInstance, reason);\n              }\n            };\n\n            //merge and clean up options\n            modalOptions = angular.extend({}, $modalProvider.options, modalOptions);\n            modalOptions.resolve = modalOptions.resolve || {};\n\n            //verify options\n            if (!modalOptions.template && !modalOptions.templateUrl) {\n              throw new Error('One of template or templateUrl options is required.');\n            }\n\n            var templateAndResolvePromise =\n              $q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)));\n\n\n            templateAndResolvePromise.then(function resolveSuccess(tplAndVars) {\n\n              var modalScope = (modalOptions.scope || $rootScope).$new();\n              modalScope.$close = modalInstance.close;\n              modalScope.$dismiss = modalInstance.dismiss;\n\n              var ctrlInstance, ctrlLocals = {};\n              var resolveIter = 1;\n\n              //controllers\n              if (modalOptions.controller) {\n                ctrlLocals.$scope = modalScope;\n                ctrlLocals.$modalInstance = modalInstance;\n                angular.forEach(modalOptions.resolve, function (value, key) {\n                  ctrlLocals[key] = tplAndVars[resolveIter++];\n                });\n\n                ctrlInstance = $controller(modalOptions.controller, ctrlLocals);\n                if (modalOptions.controllerAs) {\n                  modalScope[modalOptions.controllerAs] = ctrlInstance;\n                }\n              }\n\n              $modalStack.open(modalInstance, {\n                scope: modalScope,\n                deferred: modalResultDeferred,\n                content: tplAndVars[0],\n                backdrop: modalOptions.backdrop,\n                keyboard: modalOptions.keyboard,\n                backdropClass: modalOptions.backdropClass,\n                windowClass: modalOptions.windowClass,\n                windowTemplateUrl: modalOptions.windowTemplateUrl,\n                size: modalOptions.size\n              });\n\n            }, function resolveError(reason) {\n              modalResultDeferred.reject(reason);\n            });\n\n            templateAndResolvePromise.then(function () {\n              modalOpenedDeferred.resolve(true);\n            }, function () {\n              modalOpenedDeferred.reject(false);\n            });\n\n            return modalInstance;\n          };\n\n          return $modal;\n        }]\n    };\n\n    return $modalProvider;\n  });\n\nangular.module('ui.bootstrap.pagination', [])\n\n.controller('PaginationController', ['$scope', '$attrs', '$parse', function ($scope, $attrs, $parse) {\n  var self = this,\n      ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl\n      setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop;\n\n  this.init = function(ngModelCtrl_, config) {\n    ngModelCtrl = ngModelCtrl_;\n    this.config = config;\n\n    ngModelCtrl.$render = function() {\n      self.render();\n    };\n\n    if ($attrs.itemsPerPage) {\n      $scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) {\n        self.itemsPerPage = parseInt(value, 10);\n        $scope.totalPages = self.calculateTotalPages();\n      });\n    } else {\n      this.itemsPerPage = config.itemsPerPage;\n    }\n  };\n\n  this.calculateTotalPages = function() {\n    var totalPages = this.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / this.itemsPerPage);\n    return Math.max(totalPages || 0, 1);\n  };\n\n  this.render = function() {\n    $scope.page = parseInt(ngModelCtrl.$viewValue, 10) || 1;\n  };\n\n  $scope.selectPage = function(page) {\n    if ( $scope.page !== page && page > 0 && page <= $scope.totalPages) {\n      ngModelCtrl.$setViewValue(page);\n      ngModelCtrl.$render();\n    }\n  };\n\n  $scope.getText = function( key ) {\n    return $scope[key + 'Text'] || self.config[key + 'Text'];\n  };\n  $scope.noPrevious = function() {\n    return $scope.page === 1;\n  };\n  $scope.noNext = function() {\n    return $scope.page === $scope.totalPages;\n  };\n\n  $scope.$watch('totalItems', function() {\n    $scope.totalPages = self.calculateTotalPages();\n  });\n\n  $scope.$watch('totalPages', function(value) {\n    setNumPages($scope.$parent, value); // Readonly variable\n\n    if ( $scope.page > value ) {\n      $scope.selectPage(value);\n    } else {\n      ngModelCtrl.$render();\n    }\n  });\n}])\n\n.constant('paginationConfig', {\n  itemsPerPage: 10,\n  boundaryLinks: false,\n  directionLinks: true,\n  firstText: 'First',\n  previousText: 'Previous',\n  nextText: 'Next',\n  lastText: 'Last',\n  rotate: true\n})\n\n.directive('pagination', ['$parse', 'paginationConfig', function($parse, paginationConfig) {\n  return {\n    restrict: 'EA',\n    scope: {\n      totalItems: '=',\n      firstText: '@',\n      previousText: '@',\n      nextText: '@',\n      lastText: '@'\n    },\n    require: ['pagination', '?ngModel'],\n    controller: 'PaginationController',\n    templateUrl: 'template/pagination/pagination.html',\n    replace: true,\n    link: function(scope, element, attrs, ctrls) {\n      var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];\n\n      if (!ngModelCtrl) {\n         return; // do nothing if no ng-model\n      }\n\n      // Setup configuration parameters\n      var maxSize = angular.isDefined(attrs.maxSize) ? scope.$parent.$eval(attrs.maxSize) : paginationConfig.maxSize,\n          rotate = angular.isDefined(attrs.rotate) ? scope.$parent.$eval(attrs.rotate) : paginationConfig.rotate;\n      scope.boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$parent.$eval(attrs.boundaryLinks) : paginationConfig.boundaryLinks;\n      scope.directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$parent.$eval(attrs.directionLinks) : paginationConfig.directionLinks;\n\n      paginationCtrl.init(ngModelCtrl, paginationConfig);\n\n      if (attrs.maxSize) {\n        scope.$parent.$watch($parse(attrs.maxSize), function(value) {\n          maxSize = parseInt(value, 10);\n          paginationCtrl.render();\n        });\n      }\n\n      // Create page object used in template\n      function makePage(number, text, isActive) {\n        return {\n          number: number,\n          text: text,\n          active: isActive\n        };\n      }\n\n      function getPages(currentPage, totalPages) {\n        var pages = [];\n\n        // Default page limits\n        var startPage = 1, endPage = totalPages;\n        var isMaxSized = ( angular.isDefined(maxSize) && maxSize < totalPages );\n\n        // recompute if maxSize\n        if ( isMaxSized ) {\n          if ( rotate ) {\n            // Current page is displayed in the middle of the visible ones\n            startPage = Math.max(currentPage - Math.floor(maxSize/2), 1);\n            endPage   = startPage + maxSize - 1;\n\n            // Adjust if limit is exceeded\n            if (endPage > totalPages) {\n              endPage   = totalPages;\n              startPage = endPage - maxSize + 1;\n            }\n          } else {\n            // Visible pages are paginated with maxSize\n            startPage = ((Math.ceil(currentPage / maxSize) - 1) * maxSize) + 1;\n\n            // Adjust last page if limit is exceeded\n            endPage = Math.min(startPage + maxSize - 1, totalPages);\n          }\n        }\n\n        // Add page number links\n        for (var number = startPage; number <= endPage; number++) {\n          var page = makePage(number, number, number === currentPage);\n          pages.push(page);\n        }\n\n        // Add links to move between page sets\n        if ( isMaxSized && ! rotate ) {\n          if ( startPage > 1 ) {\n            var previousPageSet = makePage(startPage - 1, '...', false);\n            pages.unshift(previousPageSet);\n          }\n\n          if ( endPage < totalPages ) {\n            var nextPageSet = makePage(endPage + 1, '...', false);\n            pages.push(nextPageSet);\n          }\n        }\n\n        return pages;\n      }\n\n      var originalRender = paginationCtrl.render;\n      paginationCtrl.render = function() {\n        originalRender();\n        if (scope.page > 0 && scope.page <= scope.totalPages) {\n          scope.pages = getPages(scope.page, scope.totalPages);\n        }\n      };\n    }\n  };\n}])\n\n.constant('pagerConfig', {\n  itemsPerPage: 10,\n  previousText: 'Â« Previous',\n  nextText: 'Next Â»',\n  align: true\n})\n\n.directive('pager', ['pagerConfig', function(pagerConfig) {\n  return {\n    restrict: 'EA',\n    scope: {\n      totalItems: '=',\n      previousText: '@',\n      nextText: '@'\n    },\n    require: ['pager', '?ngModel'],\n    controller: 'PaginationController',\n    templateUrl: 'template/pagination/pager.html',\n    replace: true,\n    link: function(scope, element, attrs, ctrls) {\n      var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];\n\n      if (!ngModelCtrl) {\n         return; // do nothing if no ng-model\n      }\n\n      scope.align = angular.isDefined(attrs.align) ? scope.$parent.$eval(attrs.align) : pagerConfig.align;\n      paginationCtrl.init(ngModelCtrl, pagerConfig);\n    }\n  };\n}]);\n\n/**\n * The following features are still outstanding: animation as a\n * function, placement as a function, inside, support for more triggers than\n * just mouse enter/leave, html tooltips, and selector delegation.\n */\nangular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap.bindHtml' ] )\n\n/**\n * The $tooltip service creates tooltip- and popover-like directives as well as\n * houses global options for them.\n */\n.provider( '$tooltip', function () {\n  // The default options tooltip and popover.\n  var defaultOptions = {\n    placement: 'top',\n    animation: true,\n    popupDelay: 0\n  };\n\n  // Default hide triggers for each show trigger\n  var triggerMap = {\n    'mouseenter': 'mouseleave',\n    'click': 'click',\n    'focus': 'blur'\n  };\n\n  // The options specified to the provider globally.\n  var globalOptions = {};\n\n  /**\n   * `options({})` allows global configuration of all tooltips in the\n   * application.\n   *\n   *   var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {\n   *     // place tooltips left instead of top by default\n   *     $tooltipProvider.options( { placement: 'left' } );\n   *   });\n   */\n\tthis.options = function( value ) {\n\t\tangular.extend( globalOptions, value );\n\t};\n\n  /**\n   * This allows you to extend the set of trigger mappings available. E.g.:\n   *\n   *   $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' );\n   */\n  this.setTriggers = function setTriggers ( triggers ) {\n    angular.extend( triggerMap, triggers );\n  };\n\n  /**\n   * This is a helper function for translating camel-case to snake-case.\n   */\n  function snake_case(name){\n    var regexp = /[A-Z]/g;\n    var separator = '-';\n    return name.replace(regexp, function(letter, pos) {\n      return (pos ? separator : '') + letter.toLowerCase();\n    });\n  }\n\n  /**\n   * Returns the actual instance of the $tooltip service.\n   * TODO support multiple triggers\n   */\n  this.$get = [ '$window', '$compile', '$timeout', '$document', '$position', '$interpolate', function ( $window, $compile, $timeout, $document, $position, $interpolate ) {\n    return function $tooltip ( type, prefix, defaultTriggerShow ) {\n      var options = angular.extend( {}, defaultOptions, globalOptions );\n\n      /**\n       * Returns an object of show and hide triggers.\n       *\n       * If a trigger is supplied,\n       * it is used to show the tooltip; otherwise, it will use the `trigger`\n       * option passed to the `$tooltipProvider.options` method; else it will\n       * default to the trigger supplied to this directive factory.\n       *\n       * The hide trigger is based on the show trigger. If the `trigger` option\n       * was passed to the `$tooltipProvider.options` method, it will use the\n       * mapped trigger from `triggerMap` or the passed trigger if the map is\n       * undefined; otherwise, it uses the `triggerMap` value of the show\n       * trigger; else it will just use the show trigger.\n       */\n      function getTriggers ( trigger ) {\n        var show = trigger || options.trigger || defaultTriggerShow;\n        var hide = triggerMap[show] || show;\n        return {\n          show: show,\n          hide: hide\n        };\n      }\n\n      var directiveName = snake_case( type );\n\n      var startSym = $interpolate.startSymbol();\n      var endSym = $interpolate.endSymbol();\n      var template =\n        '<div '+ directiveName +'-popup '+\n          'title=\"'+startSym+'title'+endSym+'\" '+\n          'content=\"'+startSym+'content'+endSym+'\" '+\n          'placement=\"'+startSym+'placement'+endSym+'\" '+\n          'animation=\"animation\" '+\n          'is-open=\"isOpen\"'+\n          '>'+\n        '</div>';\n\n      return {\n        restrict: 'EA',\n        compile: function (tElem, tAttrs) {\n          var tooltipLinker = $compile( template );\n\n          return function link ( scope, element, attrs ) {\n            var tooltip;\n            var tooltipLinkedScope;\n            var transitionTimeout;\n            var popupTimeout;\n            var appendToBody = angular.isDefined( options.appendToBody ) ? options.appendToBody : false;\n            var triggers = getTriggers( undefined );\n            var hasEnableExp = angular.isDefined(attrs[prefix+'Enable']);\n            var ttScope = scope.$new(true);\n\n            var positionTooltip = function () {\n\n              var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody);\n              ttPosition.top += 'px';\n              ttPosition.left += 'px';\n\n              // Now set the calculated positioning.\n              tooltip.css( ttPosition );\n            };\n\n            // By default, the tooltip is not open.\n            // TODO add ability to start tooltip opened\n            ttScope.isOpen = false;\n\n            function toggleTooltipBind () {\n              if ( ! ttScope.isOpen ) {\n                showTooltipBind();\n              } else {\n                hideTooltipBind();\n              }\n            }\n\n            // Show the tooltip with delay if specified, otherwise show it immediately\n            function showTooltipBind() {\n              if(hasEnableExp && !scope.$eval(attrs[prefix+'Enable'])) {\n                return;\n              }\n\n              prepareTooltip();\n\n              if ( ttScope.popupDelay ) {\n                // Do nothing if the tooltip was already scheduled to pop-up.\n                // This happens if show is triggered multiple times before any hide is triggered.\n                if (!popupTimeout) {\n                  popupTimeout = $timeout( show, ttScope.popupDelay, false );\n                  popupTimeout.then(function(reposition){reposition();});\n                }\n              } else {\n                show()();\n              }\n            }\n\n            function hideTooltipBind () {\n              scope.$apply(function () {\n                hide();\n              });\n            }\n\n            // Show the tooltip popup element.\n            function show() {\n\n              popupTimeout = null;\n\n              // If there is a pending remove transition, we must cancel it, lest the\n              // tooltip be mysteriously removed.\n              if ( transitionTimeout ) {\n                $timeout.cancel( transitionTimeout );\n                transitionTimeout = null;\n              }\n\n              // Don't show empty tooltips.\n              if ( ! ttScope.content ) {\n                return angular.noop;\n              }\n\n              createTooltip();\n\n              // Set the initial positioning.\n              tooltip.css({ top: 0, left: 0, display: 'block' });\n              ttScope.$digest();\n\n              positionTooltip();\n\n              // And show the tooltip.\n              ttScope.isOpen = true;\n              ttScope.$digest(); // digest required as $apply is not called\n\n              // Return positioning function as promise callback for correct\n              // positioning after draw.\n              return positionTooltip;\n            }\n\n            // Hide the tooltip popup element.\n            function hide() {\n              // First things first: we don't show it anymore.\n              ttScope.isOpen = false;\n\n              //if tooltip is going to be shown after delay, we must cancel this\n              $timeout.cancel( popupTimeout );\n              popupTimeout = null;\n\n              // And now we remove it from the DOM. However, if we have animation, we\n              // need to wait for it to expire beforehand.\n              // FIXME: this is a placeholder for a port of the transitions library.\n              if ( ttScope.animation ) {\n                if (!transitionTimeout) {\n                  transitionTimeout = $timeout(removeTooltip, 500);\n                }\n              } else {\n                removeTooltip();\n              }\n            }\n\n            function createTooltip() {\n              // There can only be one tooltip element per directive shown at once.\n              if (tooltip) {\n                removeTooltip();\n              }\n              tooltipLinkedScope = ttScope.$new();\n              tooltip = tooltipLinker(tooltipLinkedScope, function (tooltip) {\n                if ( appendToBody ) {\n                  $document.find( 'body' ).append( tooltip );\n                } else {\n                  element.after( tooltip );\n                }\n              });\n            }\n\n            function removeTooltip() {\n              transitionTimeout = null;\n              if (tooltip) {\n                tooltip.remove();\n                tooltip = null;\n              }\n              if (tooltipLinkedScope) {\n                tooltipLinkedScope.$destroy();\n                tooltipLinkedScope = null;\n              }\n            }\n\n            function prepareTooltip() {\n              prepPlacement();\n              prepPopupDelay();\n            }\n\n            /**\n             * Observe the relevant attributes.\n             */\n            attrs.$observe( type, function ( val ) {\n              ttScope.content = val;\n\n              if (!val && ttScope.isOpen ) {\n                hide();\n              }\n            });\n\n            attrs.$observe( prefix+'Title', function ( val ) {\n              ttScope.title = val;\n            });\n\n            function prepPlacement() {\n              var val = attrs[ prefix + 'Placement' ];\n              ttScope.placement = angular.isDefined( val ) ? val : options.placement;\n            }\n\n            function prepPopupDelay() {\n              var val = attrs[ prefix + 'PopupDelay' ];\n              var delay = parseInt( val, 10 );\n              ttScope.popupDelay = ! isNaN(delay) ? delay : options.popupDelay;\n            }\n\n            var unregisterTriggers = function () {\n              element.unbind(triggers.show, showTooltipBind);\n              element.unbind(triggers.hide, hideTooltipBind);\n            };\n\n            function prepTriggers() {\n              var val = attrs[ prefix + 'Trigger' ];\n              unregisterTriggers();\n\n              triggers = getTriggers( val );\n\n              if ( triggers.show === triggers.hide ) {\n                element.bind( triggers.show, toggleTooltipBind );\n              } else {\n                element.bind( triggers.show, showTooltipBind );\n                element.bind( triggers.hide, hideTooltipBind );\n              }\n            }\n            prepTriggers();\n\n            var animation = scope.$eval(attrs[prefix + 'Animation']);\n            ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation;\n\n            var appendToBodyVal = scope.$eval(attrs[prefix + 'AppendToBody']);\n            appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody;\n\n            // if a tooltip is attached to <body> we need to remove it on\n            // location change as its parent scope will probably not be destroyed\n            // by the change.\n            if ( appendToBody ) {\n              scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess () {\n              if ( ttScope.isOpen ) {\n                hide();\n              }\n            });\n            }\n\n            // Make sure tooltip is destroyed and removed.\n            scope.$on('$destroy', function onDestroyTooltip() {\n              $timeout.cancel( transitionTimeout );\n              $timeout.cancel( popupTimeout );\n              unregisterTriggers();\n              removeTooltip();\n              ttScope = null;\n            });\n          };\n        }\n      };\n    };\n  }];\n})\n\n.directive( 'tooltipPopup', function () {\n  return {\n    restrict: 'EA',\n    replace: true,\n    scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },\n    templateUrl: 'template/tooltip/tooltip-popup.html'\n  };\n})\n\n.directive( 'tooltip', [ '$tooltip', function ( $tooltip ) {\n  return $tooltip( 'tooltip', 'tooltip', 'mouseenter' );\n}])\n\n.directive( 'tooltipHtmlUnsafePopup', function () {\n  return {\n    restrict: 'EA',\n    replace: true,\n    scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },\n    templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html'\n  };\n})\n\n.directive( 'tooltipHtmlUnsafe', [ '$tooltip', function ( $tooltip ) {\n  return $tooltip( 'tooltipHtmlUnsafe', 'tooltip', 'mouseenter' );\n}]);\n\n/**\n * The following features are still outstanding: popup delay, animation as a\n * function, placement as a function, inside, support for more triggers than\n * just mouse enter/leave, html popovers, and selector delegatation.\n */\nangular.module( 'ui.bootstrap.popover', [ 'ui.bootstrap.tooltip' ] )\n\n.directive( 'popoverPopup', function () {\n  return {\n    restrict: 'EA',\n    replace: true,\n    scope: { title: '@', content: '@', placement: '@', animation: '&', isOpen: '&' },\n    templateUrl: 'template/popover/popover.html'\n  };\n})\n\n.directive( 'popover', [ '$tooltip', function ( $tooltip ) {\n  return $tooltip( 'popover', 'popover', 'click' );\n}]);\n\nangular.module('ui.bootstrap.progressbar', [])\n\n.constant('progressConfig', {\n  animate: true,\n  max: 100\n})\n\n.controller('ProgressController', ['$scope', '$attrs', 'progressConfig', function($scope, $attrs, progressConfig) {\n    var self = this,\n        animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate;\n\n    this.bars = [];\n    $scope.max = angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : progressConfig.max;\n\n    this.addBar = function(bar, element) {\n        if ( !animate ) {\n            element.css({'transition': 'none'});\n        }\n\n        this.bars.push(bar);\n\n        bar.$watch('value', function( value ) {\n            bar.percent = +(100 * value / $scope.max).toFixed(2);\n        });\n\n        bar.$on('$destroy', function() {\n            element = null;\n            self.removeBar(bar);\n        });\n    };\n\n    this.removeBar = function(bar) {\n        this.bars.splice(this.bars.indexOf(bar), 1);\n    };\n}])\n\n.directive('progress', function() {\n    return {\n        restrict: 'EA',\n        replace: true,\n        transclude: true,\n        controller: 'ProgressController',\n        require: 'progress',\n        scope: {},\n        templateUrl: 'template/progressbar/progress.html'\n    };\n})\n\n.directive('bar', function() {\n    return {\n        restrict: 'EA',\n        replace: true,\n        transclude: true,\n        require: '^progress',\n        scope: {\n            value: '=',\n            type: '@'\n        },\n        templateUrl: 'template/progressbar/bar.html',\n        link: function(scope, element, attrs, progressCtrl) {\n            progressCtrl.addBar(scope, element);\n        }\n    };\n})\n\n.directive('progressbar', function() {\n    return {\n        restrict: 'EA',\n        replace: true,\n        transclude: true,\n        controller: 'ProgressController',\n        scope: {\n            value: '=',\n            type: '@'\n        },\n        templateUrl: 'template/progressbar/progressbar.html',\n        link: function(scope, element, attrs, progressCtrl) {\n            progressCtrl.addBar(scope, angular.element(element.children()[0]));\n        }\n    };\n});\nangular.module('ui.bootstrap.rating', [])\n\n.constant('ratingConfig', {\n  max: 5,\n  stateOn: null,\n  stateOff: null\n})\n\n.controller('RatingController', ['$scope', '$attrs', 'ratingConfig', function($scope, $attrs, ratingConfig) {\n  var ngModelCtrl  = { $setViewValue: angular.noop };\n\n  this.init = function(ngModelCtrl_) {\n    ngModelCtrl = ngModelCtrl_;\n    ngModelCtrl.$render = this.render;\n\n    this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn;\n    this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff;\n\n    var ratingStates = angular.isDefined($attrs.ratingStates) ? $scope.$parent.$eval($attrs.ratingStates) :\n                        new Array( angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max );\n    $scope.range = this.buildTemplateObjects(ratingStates);\n  };\n\n  this.buildTemplateObjects = function(states) {\n    for (var i = 0, n = states.length; i < n; i++) {\n      states[i] = angular.extend({ index: i }, { stateOn: this.stateOn, stateOff: this.stateOff }, states[i]);\n    }\n    return states;\n  };\n\n  $scope.rate = function(value) {\n    if ( !$scope.readonly && value >= 0 && value <= $scope.range.length ) {\n      ngModelCtrl.$setViewValue(value);\n      ngModelCtrl.$render();\n    }\n  };\n\n  $scope.enter = function(value) {\n    if ( !$scope.readonly ) {\n      $scope.value = value;\n    }\n    $scope.onHover({value: value});\n  };\n\n  $scope.reset = function() {\n    $scope.value = ngModelCtrl.$viewValue;\n    $scope.onLeave();\n  };\n\n  $scope.onKeydown = function(evt) {\n    if (/(37|38|39|40)/.test(evt.which)) {\n      evt.preventDefault();\n      evt.stopPropagation();\n      $scope.rate( $scope.value + (evt.which === 38 || evt.which === 39 ? 1 : -1) );\n    }\n  };\n\n  this.render = function() {\n    $scope.value = ngModelCtrl.$viewValue;\n  };\n}])\n\n.directive('rating', function() {\n  return {\n    restrict: 'EA',\n    require: ['rating', 'ngModel'],\n    scope: {\n      readonly: '=?',\n      onHover: '&',\n      onLeave: '&'\n    },\n    controller: 'RatingController',\n    templateUrl: 'template/rating/rating.html',\n    replace: true,\n    link: function(scope, element, attrs, ctrls) {\n      var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1];\n\n      if ( ngModelCtrl ) {\n        ratingCtrl.init( ngModelCtrl );\n      }\n    }\n  };\n});\n\n/**\n * @ngdoc overview\n * @name ui.bootstrap.tabs\n *\n * @description\n * AngularJS version of the tabs directive.\n */\n\nangular.module('ui.bootstrap.tabs', [])\n\n.controller('TabsetController', ['$scope', function TabsetCtrl($scope) {\n  var ctrl = this,\n      tabs = ctrl.tabs = $scope.tabs = [];\n\n  ctrl.select = function(selectedTab) {\n    angular.forEach(tabs, function(tab) {\n      if (tab.active && tab !== selectedTab) {\n        tab.active = false;\n        tab.onDeselect();\n      }\n    });\n    selectedTab.active = true;\n    selectedTab.onSelect();\n  };\n\n  ctrl.addTab = function addTab(tab) {\n    tabs.push(tab);\n    // we can't run the select function on the first tab\n    // since that would select it twice\n    if (tabs.length === 1) {\n      tab.active = true;\n    } else if (tab.active) {\n      ctrl.select(tab);\n    }\n  };\n\n  ctrl.removeTab = function removeTab(tab) {\n    var index = tabs.indexOf(tab);\n    //Select a new tab if the tab to be removed is selected and not destroyed\n    if (tab.active && tabs.length > 1 && !destroyed) {\n      //If this is the last tab, select the previous tab. else, the next tab.\n      var newActiveIndex = index == tabs.length - 1 ? index - 1 : index + 1;\n      ctrl.select(tabs[newActiveIndex]);\n    }\n    tabs.splice(index, 1);\n  };\n\n  var destroyed;\n  $scope.$on('$destroy', function() {\n    destroyed = true;\n  });\n}])\n\n/**\n * @ngdoc directive\n * @name ui.bootstrap.tabs.directive:tabset\n * @restrict EA\n *\n * @description\n * Tabset is the outer container for the tabs directive\n *\n * @param {boolean=} vertical Whether or not to use vertical styling for the tabs.\n * @param {boolean=} justified Whether or not to use justified styling for the tabs.\n *\n * @example\n<example module=\"ui.bootstrap\">\n  <file name=\"index.html\">\n    <tabset>\n      <tab heading=\"Tab 1\"><b>First</b> Content!</tab>\n      <tab heading=\"Tab 2\"><i>Second</i> Content!</tab>\n    </tabset>\n    <hr />\n    <tabset vertical=\"true\">\n      <tab heading=\"Vertical Tab 1\"><b>First</b> Vertical Content!</tab>\n      <tab heading=\"Vertical Tab 2\"><i>Second</i> Vertical Content!</tab>\n    </tabset>\n    <tabset justified=\"true\">\n      <tab heading=\"Justified Tab 1\"><b>First</b> Justified Content!</tab>\n      <tab heading=\"Justified Tab 2\"><i>Second</i> Justified Content!</tab>\n    </tabset>\n  </file>\n</example>\n */\n.directive('tabset', function() {\n  return {\n    restrict: 'EA',\n    transclude: true,\n    replace: true,\n    scope: {\n      type: '@'\n    },\n    controller: 'TabsetController',\n    templateUrl: 'template/tabs/tabset.html',\n    link: function(scope, element, attrs) {\n      scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false;\n      scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false;\n    }\n  };\n})\n\n/**\n * @ngdoc directive\n * @name ui.bootstrap.tabs.directive:tab\n * @restrict EA\n *\n * @param {string=} heading The visible heading, or title, of the tab. Set HTML headings with {@link ui.bootstrap.tabs.directive:tabHeading tabHeading}.\n * @param {string=} select An expression to evaluate when the tab is selected.\n * @param {boolean=} active A binding, telling whether or not this tab is selected.\n * @param {boolean=} disabled A binding, telling whether or not this tab is disabled.\n *\n * @description\n * Creates a tab with a heading and content. Must be placed within a {@link ui.bootstrap.tabs.directive:tabset tabset}.\n *\n * @example\n<example module=\"ui.bootstrap\">\n  <file name=\"index.html\">\n    <div ng-controller=\"TabsDemoCtrl\">\n      <button class=\"btn btn-small\" ng-click=\"items[0].active = true\">\n        Select item 1, using active binding\n      </button>\n      <button class=\"btn btn-small\" ng-click=\"items[1].disabled = !items[1].disabled\">\n        Enable/disable item 2, using disabled binding\n      </button>\n      <br />\n      <tabset>\n        <tab heading=\"Tab 1\">First Tab</tab>\n        <tab select=\"alertMe()\">\n          <tab-heading><i class=\"icon-bell\"></i> Alert me!</tab-heading>\n          Second Tab, with alert callback and html heading!\n        </tab>\n        <tab ng-repeat=\"item in items\"\n          heading=\"{{item.title}}\"\n          disabled=\"item.disabled\"\n          active=\"item.active\">\n          {{item.content}}\n        </tab>\n      </tabset>\n    </div>\n  </file>\n  <file name=\"script.js\">\n    function TabsDemoCtrl($scope) {\n      $scope.items = [\n        { title:\"Dynamic Title 1\", content:\"Dynamic Item 0\" },\n        { title:\"Dynamic Title 2\", content:\"Dynamic Item 1\", disabled: true }\n      ];\n\n      $scope.alertMe = function() {\n        setTimeout(function() {\n          alert(\"You've selected the alert tab!\");\n        });\n      };\n    };\n  </file>\n</example>\n */\n\n/**\n * @ngdoc directive\n * @name ui.bootstrap.tabs.directive:tabHeading\n * @restrict EA\n *\n * @description\n * Creates an HTML heading for a {@link ui.bootstrap.tabs.directive:tab tab}. Must be placed as a child of a tab element.\n *\n * @example\n<example module=\"ui.bootstrap\">\n  <file name=\"index.html\">\n    <tabset>\n      <tab>\n        <tab-heading><b>HTML</b> in my titles?!</tab-heading>\n        And some content, too!\n      </tab>\n      <tab>\n        <tab-heading><i class=\"icon-heart\"></i> Icon heading?!?</tab-heading>\n        That's right.\n      </tab>\n    </tabset>\n  </file>\n</example>\n */\n.directive('tab', ['$parse', function($parse) {\n  return {\n    require: '^tabset',\n    restrict: 'EA',\n    replace: true,\n    templateUrl: 'template/tabs/tab.html',\n    transclude: true,\n    scope: {\n      active: '=?',\n      heading: '@',\n      onSelect: '&select', //This callback is called in contentHeadingTransclude\n                          //once it inserts the tab's content into the dom\n      onDeselect: '&deselect'\n    },\n    controller: function() {\n      //Empty controller so other directives can require being 'under' a tab\n    },\n    compile: function(elm, attrs, transclude) {\n      return function postLink(scope, elm, attrs, tabsetCtrl) {\n        scope.$watch('active', function(active) {\n          if (active) {\n            tabsetCtrl.select(scope);\n          }\n        });\n\n        scope.disabled = false;\n        if ( attrs.disabled ) {\n          scope.$parent.$watch($parse(attrs.disabled), function(value) {\n            scope.disabled = !! value;\n          });\n        }\n\n        scope.select = function() {\n          if ( !scope.disabled ) {\n            scope.active = true;\n          }\n        };\n\n        tabsetCtrl.addTab(scope);\n        scope.$on('$destroy', function() {\n          tabsetCtrl.removeTab(scope);\n        });\n\n        //We need to transclude later, once the content container is ready.\n        //when this link happens, we're inside a tab heading.\n        scope.$transcludeFn = transclude;\n      };\n    }\n  };\n}])\n\n.directive('tabHeadingTransclude', [function() {\n  return {\n    restrict: 'A',\n    require: '^tab',\n    link: function(scope, elm, attrs, tabCtrl) {\n      scope.$watch('headingElement', function updateHeadingElement(heading) {\n        if (heading) {\n          elm.html('');\n          elm.append(heading);\n        }\n      });\n    }\n  };\n}])\n\n.directive('tabContentTransclude', function() {\n  return {\n    restrict: 'A',\n    require: '^tabset',\n    link: function(scope, elm, attrs) {\n      var tab = scope.$eval(attrs.tabContentTransclude);\n\n      //Now our tab is ready to be transcluded: both the tab heading area\n      //and the tab content area are loaded.  Transclude 'em both.\n      tab.$transcludeFn(tab.$parent, function(contents) {\n        angular.forEach(contents, function(node) {\n          if (isTabHeading(node)) {\n            //Let tabHeadingTransclude know.\n            tab.headingElement = node;\n          } else {\n            elm.append(node);\n          }\n        });\n      });\n    }\n  };\n  function isTabHeading(node) {\n    return node.tagName &&  (\n      node.hasAttribute('tab-heading') ||\n      node.hasAttribute('data-tab-heading') ||\n      node.tagName.toLowerCase() === 'tab-heading' ||\n      node.tagName.toLowerCase() === 'data-tab-heading'\n    );\n  }\n})\n\n;\n\nangular.module('ui.bootstrap.timepicker', [])\n\n.constant('timepickerConfig', {\n  hourStep: 1,\n  minuteStep: 1,\n  showMeridian: true,\n  meridians: null,\n  readonlyInput: false,\n  mousewheel: true\n})\n\n.controller('TimepickerController', ['$scope', '$attrs', '$parse', '$log', '$locale', 'timepickerConfig', function($scope, $attrs, $parse, $log, $locale, timepickerConfig) {\n  var selected = new Date(),\n      ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl\n      meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS;\n\n  this.init = function( ngModelCtrl_, inputs ) {\n    ngModelCtrl = ngModelCtrl_;\n    ngModelCtrl.$render = this.render;\n\n    var hoursInputEl = inputs.eq(0),\n        minutesInputEl = inputs.eq(1);\n\n    var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel;\n    if ( mousewheel ) {\n      this.setupMousewheelEvents( hoursInputEl, minutesInputEl );\n    }\n\n    $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput;\n    this.setupInputEvents( hoursInputEl, minutesInputEl );\n  };\n\n  var hourStep = timepickerConfig.hourStep;\n  if ($attrs.hourStep) {\n    $scope.$parent.$watch($parse($attrs.hourStep), function(value) {\n      hourStep = parseInt(value, 10);\n    });\n  }\n\n  var minuteStep = timepickerConfig.minuteStep;\n  if ($attrs.minuteStep) {\n    $scope.$parent.$watch($parse($attrs.minuteStep), function(value) {\n      minuteStep = parseInt(value, 10);\n    });\n  }\n\n  // 12H / 24H mode\n  $scope.showMeridian = timepickerConfig.showMeridian;\n  if ($attrs.showMeridian) {\n    $scope.$parent.$watch($parse($attrs.showMeridian), function(value) {\n      $scope.showMeridian = !!value;\n\n      if ( ngModelCtrl.$error.time ) {\n        // Evaluate from template\n        var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate();\n        if (angular.isDefined( hours ) && angular.isDefined( minutes )) {\n          selected.setHours( hours );\n          refresh();\n        }\n      } else {\n        updateTemplate();\n      }\n    });\n  }\n\n  // Get $scope.hours in 24H mode if valid\n  function getHoursFromTemplate ( ) {\n    var hours = parseInt( $scope.hours, 10 );\n    var valid = ( $scope.showMeridian ) ? (hours > 0 && hours < 13) : (hours >= 0 && hours < 24);\n    if ( !valid ) {\n      return undefined;\n    }\n\n    if ( $scope.showMeridian ) {\n      if ( hours === 12 ) {\n        hours = 0;\n      }\n      if ( $scope.meridian === meridians[1] ) {\n        hours = hours + 12;\n      }\n    }\n    return hours;\n  }\n\n  function getMinutesFromTemplate() {\n    var minutes = parseInt($scope.minutes, 10);\n    return ( minutes >= 0 && minutes < 60 ) ? minutes : undefined;\n  }\n\n  function pad( value ) {\n    return ( angular.isDefined(value) && value.toString().length < 2 ) ? '0' + value : value;\n  }\n\n  // Respond on mousewheel spin\n  this.setupMousewheelEvents = function( hoursInputEl, minutesInputEl ) {\n    var isScrollingUp = function(e) {\n      if (e.originalEvent) {\n        e = e.originalEvent;\n      }\n      //pick correct delta variable depending on event\n      var delta = (e.wheelDelta) ? e.wheelDelta : -e.deltaY;\n      return (e.detail || delta > 0);\n    };\n\n    hoursInputEl.bind('mousewheel wheel', function(e) {\n      $scope.$apply( (isScrollingUp(e)) ? $scope.incrementHours() : $scope.decrementHours() );\n      e.preventDefault();\n    });\n\n    minutesInputEl.bind('mousewheel wheel', function(e) {\n      $scope.$apply( (isScrollingUp(e)) ? $scope.incrementMinutes() : $scope.decrementMinutes() );\n      e.preventDefault();\n    });\n\n  };\n\n  this.setupInputEvents = function( hoursInputEl, minutesInputEl ) {\n    if ( $scope.readonlyInput ) {\n      $scope.updateHours = angular.noop;\n      $scope.updateMinutes = angular.noop;\n      return;\n    }\n\n    var invalidate = function(invalidHours, invalidMinutes) {\n      ngModelCtrl.$setViewValue( null );\n      ngModelCtrl.$setValidity('time', false);\n      if (angular.isDefined(invalidHours)) {\n        $scope.invalidHours = invalidHours;\n      }\n      if (angular.isDefined(invalidMinutes)) {\n        $scope.invalidMinutes = invalidMinutes;\n      }\n    };\n\n    $scope.updateHours = function() {\n      var hours = getHoursFromTemplate();\n\n      if ( angular.isDefined(hours) ) {\n        selected.setHours( hours );\n        refresh( 'h' );\n      } else {\n        invalidate(true);\n      }\n    };\n\n    hoursInputEl.bind('blur', function(e) {\n      if ( !$scope.invalidHours && $scope.hours < 10) {\n        $scope.$apply( function() {\n          $scope.hours = pad( $scope.hours );\n        });\n      }\n    });\n\n    $scope.updateMinutes = function() {\n      var minutes = getMinutesFromTemplate();\n\n      if ( angular.isDefined(minutes) ) {\n        selected.setMinutes( minutes );\n        refresh( 'm' );\n      } else {\n        invalidate(undefined, true);\n      }\n    };\n\n    minutesInputEl.bind('blur', function(e) {\n      if ( !$scope.invalidMinutes && $scope.minutes < 10 ) {\n        $scope.$apply( function() {\n          $scope.minutes = pad( $scope.minutes );\n        });\n      }\n    });\n\n  };\n\n  this.render = function() {\n    var date = ngModelCtrl.$modelValue ? new Date( ngModelCtrl.$modelValue ) : null;\n\n    if ( isNaN(date) ) {\n      ngModelCtrl.$setValidity('time', false);\n      $log.error('Timepicker directive: \"ng-model\" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');\n    } else {\n      if ( date ) {\n        selected = date;\n      }\n      makeValid();\n      updateTemplate();\n    }\n  };\n\n  // Call internally when we know that model is valid.\n  function refresh( keyboardChange ) {\n    makeValid();\n    ngModelCtrl.$setViewValue( new Date(selected) );\n    updateTemplate( keyboardChange );\n  }\n\n  function makeValid() {\n    ngModelCtrl.$setValidity('time', true);\n    $scope.invalidHours = false;\n    $scope.invalidMinutes = false;\n  }\n\n  function updateTemplate( keyboardChange ) {\n    var hours = selected.getHours(), minutes = selected.getMinutes();\n\n    if ( $scope.showMeridian ) {\n      hours = ( hours === 0 || hours === 12 ) ? 12 : hours % 12; // Convert 24 to 12 hour system\n    }\n\n    $scope.hours = keyboardChange === 'h' ? hours : pad(hours);\n    $scope.minutes = keyboardChange === 'm' ? minutes : pad(minutes);\n    $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];\n  }\n\n  function addMinutes( minutes ) {\n    var dt = new Date( selected.getTime() + minutes * 60000 );\n    selected.setHours( dt.getHours(), dt.getMinutes() );\n    refresh();\n  }\n\n  $scope.incrementHours = function() {\n    addMinutes( hourStep * 60 );\n  };\n  $scope.decrementHours = function() {\n    addMinutes( - hourStep * 60 );\n  };\n  $scope.incrementMinutes = function() {\n    addMinutes( minuteStep );\n  };\n  $scope.decrementMinutes = function() {\n    addMinutes( - minuteStep );\n  };\n  $scope.toggleMeridian = function() {\n    addMinutes( 12 * 60 * (( selected.getHours() < 12 ) ? 1 : -1) );\n  };\n}])\n\n.directive('timepicker', function () {\n  return {\n    restrict: 'EA',\n    require: ['timepicker', '?^ngModel'],\n    controller:'TimepickerController',\n    replace: true,\n    scope: {},\n    templateUrl: 'template/timepicker/timepicker.html',\n    link: function(scope, element, attrs, ctrls) {\n      var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];\n\n      if ( ngModelCtrl ) {\n        timepickerCtrl.init( ngModelCtrl, element.find('input') );\n      }\n    }\n  };\n});\n\nangular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap.bindHtml'])\n\n/**\n * A helper service that can parse typeahead's syntax (string provided by users)\n * Extracted to a separate service for ease of unit testing\n */\n  .factory('typeaheadParser', ['$parse', function ($parse) {\n\n  //                      00000111000000000000022200000000000000003333333333333330000000000044000\n  var TYPEAHEAD_REGEXP = /^\\s*([\\s\\S]+?)(?:\\s+as\\s+([\\s\\S]+?))?\\s+for\\s+(?:([\\$\\w][\\$\\w\\d]*))\\s+in\\s+([\\s\\S]+?)$/;\n\n  return {\n    parse:function (input) {\n\n      var match = input.match(TYPEAHEAD_REGEXP);\n      if (!match) {\n        throw new Error(\n          'Expected typeahead specification in form of \"_modelValue_ (as _label_)? for _item_ in _collection_\"' +\n            ' but got \"' + input + '\".');\n      }\n\n      return {\n        itemName:match[3],\n        source:$parse(match[4]),\n        viewMapper:$parse(match[2] || match[1]),\n        modelMapper:$parse(match[1])\n      };\n    }\n  };\n}])\n\n  .directive('typeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$position', 'typeaheadParser',\n    function ($compile, $parse, $q, $timeout, $document, $position, typeaheadParser) {\n\n  var HOT_KEYS = [9, 13, 27, 38, 40];\n\n  return {\n    require:'ngModel',\n    link:function (originalScope, element, attrs, modelCtrl) {\n\n      //SUPPORTED ATTRIBUTES (OPTIONS)\n\n      //minimal no of characters that needs to be entered before typeahead kicks-in\n      var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1;\n\n      //minimal wait time after last character typed before typehead kicks-in\n      var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;\n\n      //should it restrict model values to the ones selected from the popup only?\n      var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;\n\n      //binding to a variable that indicates if matches are being retrieved asynchronously\n      var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;\n\n      //a callback executed when a match is selected\n      var onSelectCallback = $parse(attrs.typeaheadOnSelect);\n\n      var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;\n\n      var appendToBody =  attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false;\n\n      var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false;\n\n      //INTERNAL VARIABLES\n\n      //model setter executed upon match selection\n      var $setModelValue = $parse(attrs.ngModel).assign;\n\n      //expressions used by typeahead\n      var parserResult = typeaheadParser.parse(attrs.typeahead);\n\n      var hasFocus;\n\n      //create a child scope for the typeahead directive so we are not polluting original scope\n      //with typeahead-specific data (matches, query etc.)\n      var scope = originalScope.$new();\n      originalScope.$on('$destroy', function(){\n        scope.$destroy();\n      });\n\n      // WAI-ARIA\n      var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000);\n      element.attr({\n        'aria-autocomplete': 'list',\n        'aria-expanded': false,\n        'aria-owns': popupId\n      });\n\n      //pop-up element used to display matches\n      var popUpEl = angular.element('<div typeahead-popup></div>');\n      popUpEl.attr({\n        id: popupId,\n        matches: 'matches',\n        active: 'activeIdx',\n        select: 'select(activeIdx)',\n        query: 'query',\n        position: 'position'\n      });\n      //custom item template\n      if (angular.isDefined(attrs.typeaheadTemplateUrl)) {\n        popUpEl.attr('template-url', attrs.typeaheadTemplateUrl);\n      }\n\n      var resetMatches = function() {\n        scope.matches = [];\n        scope.activeIdx = -1;\n        element.attr('aria-expanded', false);\n      };\n\n      var getMatchId = function(index) {\n        return popupId + '-option-' + index;\n      };\n\n      // Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead.\n      // This attribute is added or removed automatically when the `activeIdx` changes.\n      scope.$watch('activeIdx', function(index) {\n        if (index < 0) {\n          element.removeAttr('aria-activedescendant');\n        } else {\n          element.attr('aria-activedescendant', getMatchId(index));\n        }\n      });\n\n      var getMatchesAsync = function(inputValue) {\n\n        var locals = {$viewValue: inputValue};\n        isLoadingSetter(originalScope, true);\n        $q.when(parserResult.source(originalScope, locals)).then(function(matches) {\n\n          //it might happen that several async queries were in progress if a user were typing fast\n          //but we are interested only in responses that correspond to the current view value\n          var onCurrentRequest = (inputValue === modelCtrl.$viewValue);\n          if (onCurrentRequest && hasFocus) {\n            if (matches.length > 0) {\n\n              scope.activeIdx = focusFirst ? 0 : -1;\n              scope.matches.length = 0;\n\n              //transform labels\n              for(var i=0; i<matches.length; i++) {\n                locals[parserResult.itemName] = matches[i];\n                scope.matches.push({\n                  id: getMatchId(i),\n                  label: parserResult.viewMapper(scope, locals),\n                  model: matches[i]\n                });\n              }\n\n              scope.query = inputValue;\n              //position pop-up with matches - we need to re-calculate its position each time we are opening a window\n              //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page\n              //due to other elements being rendered\n              scope.position = appendToBody ? $position.offset(element) : $position.position(element);\n              scope.position.top = scope.position.top + element.prop('offsetHeight');\n\n              element.attr('aria-expanded', true);\n            } else {\n              resetMatches();\n            }\n          }\n          if (onCurrentRequest) {\n            isLoadingSetter(originalScope, false);\n          }\n        }, function(){\n          resetMatches();\n          isLoadingSetter(originalScope, false);\n        });\n      };\n\n      resetMatches();\n\n      //we need to propagate user's query so we can higlight matches\n      scope.query = undefined;\n\n      //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later \n      var timeoutPromise;\n\n      var scheduleSearchWithTimeout = function(inputValue) {\n        timeoutPromise = $timeout(function () {\n          getMatchesAsync(inputValue);\n        }, waitTime);\n      };\n\n      var cancelPreviousTimeout = function() {\n        if (timeoutPromise) {\n          $timeout.cancel(timeoutPromise);\n        }\n      };\n\n      //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM\n      //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue\n      modelCtrl.$parsers.unshift(function (inputValue) {\n\n        hasFocus = true;\n\n        if (inputValue && inputValue.length >= minSearch) {\n          if (waitTime > 0) {\n            cancelPreviousTimeout();\n            scheduleSearchWithTimeout(inputValue);\n          } else {\n            getMatchesAsync(inputValue);\n          }\n        } else {\n          isLoadingSetter(originalScope, false);\n          cancelPreviousTimeout();\n          resetMatches();\n        }\n\n        if (isEditable) {\n          return inputValue;\n        } else {\n          if (!inputValue) {\n            // Reset in case user had typed something previously.\n            modelCtrl.$setValidity('editable', true);\n            return inputValue;\n          } else {\n            modelCtrl.$setValidity('editable', false);\n            return undefined;\n          }\n        }\n      });\n\n      modelCtrl.$formatters.push(function (modelValue) {\n\n        var candidateViewValue, emptyViewValue;\n        var locals = {};\n\n        if (inputFormatter) {\n\n          locals.$model = modelValue;\n          return inputFormatter(originalScope, locals);\n\n        } else {\n\n          //it might happen that we don't have enough info to properly render input value\n          //we need to check for this situation and simply return model value if we can't apply custom formatting\n          locals[parserResult.itemName] = modelValue;\n          candidateViewValue = parserResult.viewMapper(originalScope, locals);\n          locals[parserResult.itemName] = undefined;\n          emptyViewValue = parserResult.viewMapper(originalScope, locals);\n\n          return candidateViewValue!== emptyViewValue ? candidateViewValue : modelValue;\n        }\n      });\n\n      scope.select = function (activeIdx) {\n        //called from within the $digest() cycle\n        var locals = {};\n        var model, item;\n\n        locals[parserResult.itemName] = item = scope.matches[activeIdx].model;\n        model = parserResult.modelMapper(originalScope, locals);\n        $setModelValue(originalScope, model);\n        modelCtrl.$setValidity('editable', true);\n\n        onSelectCallback(originalScope, {\n          $item: item,\n          $model: model,\n          $label: parserResult.viewMapper(originalScope, locals)\n        });\n\n        resetMatches();\n\n        //return focus to the input element if a match was selected via a mouse click event\n        // use timeout to avoid $rootScope:inprog error\n        $timeout(function() { element[0].focus(); }, 0, false);\n      };\n\n      //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)\n      element.bind('keydown', function (evt) {\n\n        //typeahead is open and an \"interesting\" key was pressed\n        if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {\n          return;\n        }\n\n        // if there's nothing selected (i.e. focusFirst) and enter is hit, don't do anything\n        if (scope.activeIdx == -1 && (evt.which === 13 || evt.which === 9)) {\n          return;\n        }\n\n        evt.preventDefault();\n\n        if (evt.which === 40) {\n          scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;\n          scope.$digest();\n\n        } else if (evt.which === 38) {\n          scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1;\n          scope.$digest();\n\n        } else if (evt.which === 13 || evt.which === 9) {\n          scope.$apply(function () {\n            scope.select(scope.activeIdx);\n          });\n\n        } else if (evt.which === 27) {\n          evt.stopPropagation();\n\n          resetMatches();\n          scope.$digest();\n        }\n      });\n\n      element.bind('blur', function (evt) {\n        hasFocus = false;\n      });\n\n      // Keep reference to click handler to unbind it.\n      var dismissClickHandler = function (evt) {\n        if (element[0] !== evt.target) {\n          resetMatches();\n          scope.$digest();\n        }\n      };\n\n      $document.bind('click', dismissClickHandler);\n\n      originalScope.$on('$destroy', function(){\n        $document.unbind('click', dismissClickHandler);\n        if (appendToBody) {\n          $popup.remove();\n        }\n      });\n\n      var $popup = $compile(popUpEl)(scope);\n      if (appendToBody) {\n        $document.find('body').append($popup);\n      } else {\n        element.after($popup);\n      }\n    }\n  };\n\n}])\n\n  .directive('typeaheadPopup', function () {\n    return {\n      restrict:'EA',\n      scope:{\n        matches:'=',\n        query:'=',\n        active:'=',\n        position:'=',\n        select:'&'\n      },\n      replace:true,\n      templateUrl:'template/typeahead/typeahead-popup.html',\n      link:function (scope, element, attrs) {\n\n        scope.templateUrl = attrs.templateUrl;\n\n        scope.isOpen = function () {\n          return scope.matches.length > 0;\n        };\n\n        scope.isActive = function (matchIdx) {\n          return scope.active == matchIdx;\n        };\n\n        scope.selectActive = function (matchIdx) {\n          scope.active = matchIdx;\n        };\n\n        scope.selectMatch = function (activeIdx) {\n          scope.select({activeIdx:activeIdx});\n        };\n      }\n    };\n  })\n\n  .directive('typeaheadMatch', ['$http', '$templateCache', '$compile', '$parse', function ($http, $templateCache, $compile, $parse) {\n    return {\n      restrict:'EA',\n      scope:{\n        index:'=',\n        match:'=',\n        query:'='\n      },\n      link:function (scope, element, attrs) {\n        var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'template/typeahead/typeahead-match.html';\n        $http.get(tplUrl, {cache: $templateCache}).success(function(tplContent){\n           element.replaceWith($compile(tplContent.trim())(scope));\n        });\n      }\n    };\n  }])\n\n  .filter('typeaheadHighlight', function() {\n\n    function escapeRegexp(queryToEscape) {\n      return queryToEscape.replace(/([.?*+^$[\\]\\\\(){}|-])/g, '\\\\$1');\n    }\n\n    return function(matchItem, query) {\n      return query ? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem;\n    };\n  });"
  },
  {
    "path": "public/dist/js/angular.js",
    "content": "/**\n * @license AngularJS v1.3.15\n * (c) 2010-2014 Google, Inc. http://angularjs.org\n * License: MIT\n */\n(function(window, document, undefined) {'use strict';\n\n/**\n * @description\n *\n * This object provides a utility for producing rich Error messages within\n * Angular. It can be called as follows:\n *\n * var exampleMinErr = minErr('example');\n * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);\n *\n * The above creates an instance of minErr in the example namespace. The\n * resulting error will have a namespaced error code of example.one.  The\n * resulting error will replace {0} with the value of foo, and {1} with the\n * value of bar. The object is not restricted in the number of arguments it can\n * take.\n *\n * If fewer arguments are specified than necessary for interpolation, the extra\n * interpolation markers will be preserved in the final string.\n *\n * Since data will be parsed statically during a build step, some restrictions\n * are applied with respect to how minErr instances are created and called.\n * Instances should have names of the form namespaceMinErr for a minErr created\n * using minErr('namespace') . Error codes, namespaces and template strings\n * should all be static strings, not variables or general expressions.\n *\n * @param {string} module The namespace to use for the new minErr instance.\n * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning\n *   error from returned function, for cases when a particular type of error is useful.\n * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance\n */\n\nfunction minErr(module, ErrorConstructor) {\n  ErrorConstructor = ErrorConstructor || Error;\n  return function() {\n    var code = arguments[0],\n      prefix = '[' + (module ? module + ':' : '') + code + '] ',\n      template = arguments[1],\n      templateArgs = arguments,\n\n      message, i;\n\n    message = prefix + template.replace(/\\{\\d+\\}/g, function(match) {\n      var index = +match.slice(1, -1), arg;\n\n      if (index + 2 < templateArgs.length) {\n        return toDebugString(templateArgs[index + 2]);\n      }\n      return match;\n    });\n\n    message = message + '\\nhttp://errors.angularjs.org/1.3.15/' +\n      (module ? module + '/' : '') + code;\n    for (i = 2; i < arguments.length; i++) {\n      message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' +\n        encodeURIComponent(toDebugString(arguments[i]));\n    }\n    return new ErrorConstructor(message);\n  };\n}\n\n/* We need to tell jshint what variables are being exported */\n/* global angular: true,\n  msie: true,\n  jqLite: true,\n  jQuery: true,\n  slice: true,\n  splice: true,\n  push: true,\n  toString: true,\n  ngMinErr: true,\n  angularModule: true,\n  uid: true,\n  REGEX_STRING_REGEXP: true,\n  VALIDITY_STATE_PROPERTY: true,\n\n  lowercase: true,\n  uppercase: true,\n  manualLowercase: true,\n  manualUppercase: true,\n  nodeName_: true,\n  isArrayLike: true,\n  forEach: true,\n  sortedKeys: true,\n  forEachSorted: true,\n  reverseParams: true,\n  nextUid: true,\n  setHashKey: true,\n  extend: true,\n  int: true,\n  inherit: true,\n  noop: true,\n  identity: true,\n  valueFn: true,\n  isUndefined: true,\n  isDefined: true,\n  isObject: true,\n  isString: true,\n  isNumber: true,\n  isDate: true,\n  isArray: true,\n  isFunction: true,\n  isRegExp: true,\n  isWindow: true,\n  isScope: true,\n  isFile: true,\n  isFormData: true,\n  isBlob: true,\n  isBoolean: true,\n  isPromiseLike: true,\n  trim: true,\n  escapeForRegexp: true,\n  isElement: true,\n  makeMap: true,\n  includes: true,\n  arrayRemove: true,\n  copy: true,\n  shallowCopy: true,\n  equals: true,\n  csp: true,\n  concat: true,\n  sliceArgs: true,\n  bind: true,\n  toJsonReplacer: true,\n  toJson: true,\n  fromJson: true,\n  startingTag: true,\n  tryDecodeURIComponent: true,\n  parseKeyValue: true,\n  toKeyValue: true,\n  encodeUriSegment: true,\n  encodeUriQuery: true,\n  angularInit: true,\n  bootstrap: true,\n  getTestability: true,\n  snake_case: true,\n  bindJQuery: true,\n  assertArg: true,\n  assertArgFn: true,\n  assertNotHasOwnProperty: true,\n  getter: true,\n  getBlockNodes: true,\n  hasOwnProperty: true,\n  createMap: true,\n\n  NODE_TYPE_ELEMENT: true,\n  NODE_TYPE_TEXT: true,\n  NODE_TYPE_COMMENT: true,\n  NODE_TYPE_DOCUMENT: true,\n  NODE_TYPE_DOCUMENT_FRAGMENT: true,\n*/\n\n////////////////////////////////////\n\n/**\n * @ngdoc module\n * @name ng\n * @module ng\n * @description\n *\n * # ng (core module)\n * The ng module is loaded by default when an AngularJS application is started. The module itself\n * contains the essential components for an AngularJS application to function. The table below\n * lists a high level breakdown of each of the services/factories, filters, directives and testing\n * components available within this core module.\n *\n * <div doc-module-components=\"ng\"></div>\n */\n\nvar REGEX_STRING_REGEXP = /^\\/(.+)\\/([a-z]*)$/;\n\n// The name of a form control's ValidityState property.\n// This is used so that it's possible for internal tests to create mock ValidityStates.\nvar VALIDITY_STATE_PROPERTY = 'validity';\n\n/**\n * @ngdoc function\n * @name angular.lowercase\n * @module ng\n * @kind function\n *\n * @description Converts the specified string to lowercase.\n * @param {string} string String to be converted to lowercase.\n * @returns {string} Lowercased string.\n */\nvar lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};\nvar hasOwnProperty = Object.prototype.hasOwnProperty;\n\n/**\n * @ngdoc function\n * @name angular.uppercase\n * @module ng\n * @kind function\n *\n * @description Converts the specified string to uppercase.\n * @param {string} string String to be converted to uppercase.\n * @returns {string} Uppercased string.\n */\nvar uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};\n\n\nvar manualLowercase = function(s) {\n  /* jshint bitwise: false */\n  return isString(s)\n      ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})\n      : s;\n};\nvar manualUppercase = function(s) {\n  /* jshint bitwise: false */\n  return isString(s)\n      ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})\n      : s;\n};\n\n\n// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish\n// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods\n// with correct but slower alternatives.\nif ('i' !== 'I'.toLowerCase()) {\n  lowercase = manualLowercase;\n  uppercase = manualUppercase;\n}\n\n\nvar\n    msie,             // holds major version number for IE, or NaN if UA is not IE.\n    jqLite,           // delay binding since jQuery could be loaded after us.\n    jQuery,           // delay binding\n    slice             = [].slice,\n    splice            = [].splice,\n    push              = [].push,\n    toString          = Object.prototype.toString,\n    ngMinErr          = minErr('ng'),\n\n    /** @name angular */\n    angular           = window.angular || (window.angular = {}),\n    angularModule,\n    uid               = 0;\n\n/**\n * documentMode is an IE-only property\n * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx\n */\nmsie = document.documentMode;\n\n\n/**\n * @private\n * @param {*} obj\n * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,\n *                   String ...)\n */\nfunction isArrayLike(obj) {\n  if (obj == null || isWindow(obj)) {\n    return false;\n  }\n\n  var length = obj.length;\n\n  if (obj.nodeType === NODE_TYPE_ELEMENT && length) {\n    return true;\n  }\n\n  return isString(obj) || isArray(obj) || length === 0 ||\n         typeof length === 'number' && length > 0 && (length - 1) in obj;\n}\n\n/**\n * @ngdoc function\n * @name angular.forEach\n * @module ng\n * @kind function\n *\n * @description\n * Invokes the `iterator` function once for each item in `obj` collection, which can be either an\n * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`\n * is the value of an object property or an array element, `key` is the object property key or\n * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.\n *\n * It is worth noting that `.forEach` does not iterate over inherited properties because it filters\n * using the `hasOwnProperty` method.\n *\n * Unlike ES262's\n * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),\n * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just\n * return the value provided.\n *\n   ```js\n     var values = {name: 'misko', gender: 'male'};\n     var log = [];\n     angular.forEach(values, function(value, key) {\n       this.push(key + ': ' + value);\n     }, log);\n     expect(log).toEqual(['name: misko', 'gender: male']);\n   ```\n *\n * @param {Object|Array} obj Object to iterate over.\n * @param {Function} iterator Iterator function.\n * @param {Object=} context Object to become context (`this`) for the iterator function.\n * @returns {Object|Array} Reference to `obj`.\n */\n\nfunction forEach(obj, iterator, context) {\n  var key, length;\n  if (obj) {\n    if (isFunction(obj)) {\n      for (key in obj) {\n        // Need to check if hasOwnProperty exists,\n        // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function\n        if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {\n          iterator.call(context, obj[key], key, obj);\n        }\n      }\n    } else if (isArray(obj) || isArrayLike(obj)) {\n      var isPrimitive = typeof obj !== 'object';\n      for (key = 0, length = obj.length; key < length; key++) {\n        if (isPrimitive || key in obj) {\n          iterator.call(context, obj[key], key, obj);\n        }\n      }\n    } else if (obj.forEach && obj.forEach !== forEach) {\n        obj.forEach(iterator, context, obj);\n    } else {\n      for (key in obj) {\n        if (obj.hasOwnProperty(key)) {\n          iterator.call(context, obj[key], key, obj);\n        }\n      }\n    }\n  }\n  return obj;\n}\n\nfunction sortedKeys(obj) {\n  return Object.keys(obj).sort();\n}\n\nfunction forEachSorted(obj, iterator, context) {\n  var keys = sortedKeys(obj);\n  for (var i = 0; i < keys.length; i++) {\n    iterator.call(context, obj[keys[i]], keys[i]);\n  }\n  return keys;\n}\n\n\n/**\n * when using forEach the params are value, key, but it is often useful to have key, value.\n * @param {function(string, *)} iteratorFn\n * @returns {function(*, string)}\n */\nfunction reverseParams(iteratorFn) {\n  return function(value, key) { iteratorFn(key, value); };\n}\n\n/**\n * A consistent way of creating unique IDs in angular.\n *\n * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before\n * we hit number precision issues in JavaScript.\n *\n * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M\n *\n * @returns {number} an unique alpha-numeric string\n */\nfunction nextUid() {\n  return ++uid;\n}\n\n\n/**\n * Set or clear the hashkey for an object.\n * @param obj object\n * @param h the hashkey (!truthy to delete the hashkey)\n */\nfunction setHashKey(obj, h) {\n  if (h) {\n    obj.$$hashKey = h;\n  } else {\n    delete obj.$$hashKey;\n  }\n}\n\n/**\n * @ngdoc function\n * @name angular.extend\n * @module ng\n * @kind function\n *\n * @description\n * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)\n * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so\n * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.\n * Note: Keep in mind that `angular.extend` does not support recursive merge (deep copy).\n *\n * @param {Object} dst Destination object.\n * @param {...Object} src Source object(s).\n * @returns {Object} Reference to `dst`.\n */\nfunction extend(dst) {\n  var h = dst.$$hashKey;\n\n  for (var i = 1, ii = arguments.length; i < ii; i++) {\n    var obj = arguments[i];\n    if (obj) {\n      var keys = Object.keys(obj);\n      for (var j = 0, jj = keys.length; j < jj; j++) {\n        var key = keys[j];\n        dst[key] = obj[key];\n      }\n    }\n  }\n\n  setHashKey(dst, h);\n  return dst;\n}\n\nfunction int(str) {\n  return parseInt(str, 10);\n}\n\n\nfunction inherit(parent, extra) {\n  return extend(Object.create(parent), extra);\n}\n\n/**\n * @ngdoc function\n * @name angular.noop\n * @module ng\n * @kind function\n *\n * @description\n * A function that performs no operations. This function can be useful when writing code in the\n * functional style.\n   ```js\n     function foo(callback) {\n       var result = calculateResult();\n       (callback || angular.noop)(result);\n     }\n   ```\n */\nfunction noop() {}\nnoop.$inject = [];\n\n\n/**\n * @ngdoc function\n * @name angular.identity\n * @module ng\n * @kind function\n *\n * @description\n * A function that returns its first argument. This function is useful when writing code in the\n * functional style.\n *\n   ```js\n     function transformer(transformationFn, value) {\n       return (transformationFn || angular.identity)(value);\n     };\n   ```\n  * @param {*} value to be returned.\n  * @returns {*} the value passed in.\n */\nfunction identity($) {return $;}\nidentity.$inject = [];\n\n\nfunction valueFn(value) {return function() {return value;};}\n\n/**\n * @ngdoc function\n * @name angular.isUndefined\n * @module ng\n * @kind function\n *\n * @description\n * Determines if a reference is undefined.\n *\n * @param {*} value Reference to check.\n * @returns {boolean} True if `value` is undefined.\n */\nfunction isUndefined(value) {return typeof value === 'undefined';}\n\n\n/**\n * @ngdoc function\n * @name angular.isDefined\n * @module ng\n * @kind function\n *\n * @description\n * Determines if a reference is defined.\n *\n * @param {*} value Reference to check.\n * @returns {boolean} True if `value` is defined.\n */\nfunction isDefined(value) {return typeof value !== 'undefined';}\n\n\n/**\n * @ngdoc function\n * @name angular.isObject\n * @module ng\n * @kind function\n *\n * @description\n * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not\n * considered to be objects. Note that JavaScript arrays are objects.\n *\n * @param {*} value Reference to check.\n * @returns {boolean} True if `value` is an `Object` but not `null`.\n */\nfunction isObject(value) {\n  // http://jsperf.com/isobject4\n  return value !== null && typeof value === 'object';\n}\n\n\n/**\n * @ngdoc function\n * @name angular.isString\n * @module ng\n * @kind function\n *\n * @description\n * Determines if a reference is a `String`.\n *\n * @param {*} value Reference to check.\n * @returns {boolean} True if `value` is a `String`.\n */\nfunction isString(value) {return typeof value === 'string';}\n\n\n/**\n * @ngdoc function\n * @name angular.isNumber\n * @module ng\n * @kind function\n *\n * @description\n * Determines if a reference is a `Number`.\n *\n * This includes the \"special\" numbers `NaN`, `+Infinity` and `-Infinity`.\n *\n * If you wish to exclude these then you can use the native\n * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)\n * method.\n *\n * @param {*} value Reference to check.\n * @returns {boolean} True if `value` is a `Number`.\n */\nfunction isNumber(value) {return typeof value === 'number';}\n\n\n/**\n * @ngdoc function\n * @name angular.isDate\n * @module ng\n * @kind function\n *\n * @description\n * Determines if a value is a date.\n *\n * @param {*} value Reference to check.\n * @returns {boolean} True if `value` is a `Date`.\n */\nfunction isDate(value) {\n  return toString.call(value) === '[object Date]';\n}\n\n\n/**\n * @ngdoc function\n * @name angular.isArray\n * @module ng\n * @kind function\n *\n * @description\n * Determines if a reference is an `Array`.\n *\n * @param {*} value Reference to check.\n * @returns {boolean} True if `value` is an `Array`.\n */\nvar isArray = Array.isArray;\n\n/**\n * @ngdoc function\n * @name angular.isFunction\n * @module ng\n * @kind function\n *\n * @description\n * Determines if a reference is a `Function`.\n *\n * @param {*} value Reference to check.\n * @returns {boolean} True if `value` is a `Function`.\n */\nfunction isFunction(value) {return typeof value === 'function';}\n\n\n/**\n * Determines if a value is a regular expression object.\n *\n * @private\n * @param {*} value Reference to check.\n * @returns {boolean} True if `value` is a `RegExp`.\n */\nfunction isRegExp(value) {\n  return toString.call(value) === '[object RegExp]';\n}\n\n\n/**\n * Checks if `obj` is a window object.\n *\n * @private\n * @param {*} obj Object to check\n * @returns {boolean} True if `obj` is a window obj.\n */\nfunction isWindow(obj) {\n  return obj && obj.window === obj;\n}\n\n\nfunction isScope(obj) {\n  return obj && obj.$evalAsync && obj.$watch;\n}\n\n\nfunction isFile(obj) {\n  return toString.call(obj) === '[object File]';\n}\n\n\nfunction isFormData(obj) {\n  return toString.call(obj) === '[object FormData]';\n}\n\n\nfunction isBlob(obj) {\n  return toString.call(obj) === '[object Blob]';\n}\n\n\nfunction isBoolean(value) {\n  return typeof value === 'boolean';\n}\n\n\nfunction isPromiseLike(obj) {\n  return obj && isFunction(obj.then);\n}\n\n\nvar trim = function(value) {\n  return isString(value) ? value.trim() : value;\n};\n\n// Copied from:\n// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021\n// Prereq: s is a string.\nvar escapeForRegexp = function(s) {\n  return s.replace(/([-()\\[\\]{}+?*.$\\^|,:#<!\\\\])/g, '\\\\$1').\n           replace(/\\x08/g, '\\\\x08');\n};\n\n\n/**\n * @ngdoc function\n * @name angular.isElement\n * @module ng\n * @kind function\n *\n * @description\n * Determines if a reference is a DOM element (or wrapped jQuery element).\n *\n * @param {*} value Reference to check.\n * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).\n */\nfunction isElement(node) {\n  return !!(node &&\n    (node.nodeName  // we are a direct element\n    || (node.prop && node.attr && node.find)));  // we have an on and find method part of jQuery API\n}\n\n/**\n * @param str 'key1,key2,...'\n * @returns {object} in the form of {key1:true, key2:true, ...}\n */\nfunction makeMap(str) {\n  var obj = {}, items = str.split(\",\"), i;\n  for (i = 0; i < items.length; i++)\n    obj[items[i]] = true;\n  return obj;\n}\n\n\nfunction nodeName_(element) {\n  return lowercase(element.nodeName || (element[0] && element[0].nodeName));\n}\n\nfunction includes(array, obj) {\n  return Array.prototype.indexOf.call(array, obj) != -1;\n}\n\nfunction arrayRemove(array, value) {\n  var index = array.indexOf(value);\n  if (index >= 0)\n    array.splice(index, 1);\n  return value;\n}\n\n/**\n * @ngdoc function\n * @name angular.copy\n * @module ng\n * @kind function\n *\n * @description\n * Creates a deep copy of `source`, which should be an object or an array.\n *\n * * If no destination is supplied, a copy of the object or array is created.\n * * If a destination is provided, all of its elements (for arrays) or properties (for objects)\n *   are deleted and then all elements/properties from the source are copied to it.\n * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.\n * * If `source` is identical to 'destination' an exception will be thrown.\n *\n * @param {*} source The source that will be used to make a copy.\n *                   Can be any type, including primitives, `null`, and `undefined`.\n * @param {(Object|Array)=} destination Destination into which the source is copied. If\n *     provided, must be of the same type as `source`.\n * @returns {*} The copy or updated `destination`, if `destination` was specified.\n *\n * @example\n <example module=\"copyExample\">\n <file name=\"index.html\">\n <div ng-controller=\"ExampleController\">\n <form novalidate class=\"simple-form\">\n Name: <input type=\"text\" ng-model=\"user.name\" /><br />\n E-mail: <input type=\"email\" ng-model=\"user.email\" /><br />\n Gender: <input type=\"radio\" ng-model=\"user.gender\" value=\"male\" />male\n <input type=\"radio\" ng-model=\"user.gender\" value=\"female\" />female<br />\n <button ng-click=\"reset()\">RESET</button>\n <button ng-click=\"update(user)\">SAVE</button>\n </form>\n <pre>form = {{user | json}}</pre>\n <pre>master = {{master | json}}</pre>\n </div>\n\n <script>\n  angular.module('copyExample', [])\n    .controller('ExampleController', ['$scope', function($scope) {\n      $scope.master= {};\n\n      $scope.update = function(user) {\n        // Example with 1 argument\n        $scope.master= angular.copy(user);\n      };\n\n      $scope.reset = function() {\n        // Example with 2 arguments\n        angular.copy($scope.master, $scope.user);\n      };\n\n      $scope.reset();\n    }]);\n </script>\n </file>\n </example>\n */\nfunction copy(source, destination, stackSource, stackDest) {\n  if (isWindow(source) || isScope(source)) {\n    throw ngMinErr('cpws',\n      \"Can't copy! Making copies of Window or Scope instances is not supported.\");\n  }\n\n  if (!destination) {\n    destination = source;\n    if (source) {\n      if (isArray(source)) {\n        destination = copy(source, [], stackSource, stackDest);\n      } else if (isDate(source)) {\n        destination = new Date(source.getTime());\n      } else if (isRegExp(source)) {\n        destination = new RegExp(source.source, source.toString().match(/[^\\/]*$/)[0]);\n        destination.lastIndex = source.lastIndex;\n      } else if (isObject(source)) {\n        var emptyObject = Object.create(Object.getPrototypeOf(source));\n        destination = copy(source, emptyObject, stackSource, stackDest);\n      }\n    }\n  } else {\n    if (source === destination) throw ngMinErr('cpi',\n      \"Can't copy! Source and destination are identical.\");\n\n    stackSource = stackSource || [];\n    stackDest = stackDest || [];\n\n    if (isObject(source)) {\n      var index = stackSource.indexOf(source);\n      if (index !== -1) return stackDest[index];\n\n      stackSource.push(source);\n      stackDest.push(destination);\n    }\n\n    var result;\n    if (isArray(source)) {\n      destination.length = 0;\n      for (var i = 0; i < source.length; i++) {\n        result = copy(source[i], null, stackSource, stackDest);\n        if (isObject(source[i])) {\n          stackSource.push(source[i]);\n          stackDest.push(result);\n        }\n        destination.push(result);\n      }\n    } else {\n      var h = destination.$$hashKey;\n      if (isArray(destination)) {\n        destination.length = 0;\n      } else {\n        forEach(destination, function(value, key) {\n          delete destination[key];\n        });\n      }\n      for (var key in source) {\n        if (source.hasOwnProperty(key)) {\n          result = copy(source[key], null, stackSource, stackDest);\n          if (isObject(source[key])) {\n            stackSource.push(source[key]);\n            stackDest.push(result);\n          }\n          destination[key] = result;\n        }\n      }\n      setHashKey(destination,h);\n    }\n\n  }\n  return destination;\n}\n\n/**\n * Creates a shallow copy of an object, an array or a primitive.\n *\n * Assumes that there are no proto properties for objects.\n */\nfunction shallowCopy(src, dst) {\n  if (isArray(src)) {\n    dst = dst || [];\n\n    for (var i = 0, ii = src.length; i < ii; i++) {\n      dst[i] = src[i];\n    }\n  } else if (isObject(src)) {\n    dst = dst || {};\n\n    for (var key in src) {\n      if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {\n        dst[key] = src[key];\n      }\n    }\n  }\n\n  return dst || src;\n}\n\n\n/**\n * @ngdoc function\n * @name angular.equals\n * @module ng\n * @kind function\n *\n * @description\n * Determines if two objects or two values are equivalent. Supports value types, regular\n * expressions, arrays and objects.\n *\n * Two objects or values are considered equivalent if at least one of the following is true:\n *\n * * Both objects or values pass `===` comparison.\n * * Both objects or values are of the same type and all of their properties are equal by\n *   comparing them with `angular.equals`.\n * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)\n * * Both values represent the same regular expression (In JavaScript,\n *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual\n *   representation matches).\n *\n * During a property comparison, properties of `function` type and properties with names\n * that begin with `$` are ignored.\n *\n * Scope and DOMWindow objects are being compared only by identify (`===`).\n *\n * @param {*} o1 Object or value to compare.\n * @param {*} o2 Object or value to compare.\n * @returns {boolean} True if arguments are equal.\n */\nfunction equals(o1, o2) {\n  if (o1 === o2) return true;\n  if (o1 === null || o2 === null) return false;\n  if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN\n  var t1 = typeof o1, t2 = typeof o2, length, key, keySet;\n  if (t1 == t2) {\n    if (t1 == 'object') {\n      if (isArray(o1)) {\n        if (!isArray(o2)) return false;\n        if ((length = o1.length) == o2.length) {\n          for (key = 0; key < length; key++) {\n            if (!equals(o1[key], o2[key])) return false;\n          }\n          return true;\n        }\n      } else if (isDate(o1)) {\n        if (!isDate(o2)) return false;\n        return equals(o1.getTime(), o2.getTime());\n      } else if (isRegExp(o1)) {\n        return isRegExp(o2) ? o1.toString() == o2.toString() : false;\n      } else {\n        if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||\n          isArray(o2) || isDate(o2) || isRegExp(o2)) return false;\n        keySet = {};\n        for (key in o1) {\n          if (key.charAt(0) === '$' || isFunction(o1[key])) continue;\n          if (!equals(o1[key], o2[key])) return false;\n          keySet[key] = true;\n        }\n        for (key in o2) {\n          if (!keySet.hasOwnProperty(key) &&\n              key.charAt(0) !== '$' &&\n              o2[key] !== undefined &&\n              !isFunction(o2[key])) return false;\n        }\n        return true;\n      }\n    }\n  }\n  return false;\n}\n\nvar csp = function() {\n  if (isDefined(csp.isActive_)) return csp.isActive_;\n\n  var active = !!(document.querySelector('[ng-csp]') ||\n                  document.querySelector('[data-ng-csp]'));\n\n  if (!active) {\n    try {\n      /* jshint -W031, -W054 */\n      new Function('');\n      /* jshint +W031, +W054 */\n    } catch (e) {\n      active = true;\n    }\n  }\n\n  return (csp.isActive_ = active);\n};\n\n\n\nfunction concat(array1, array2, index) {\n  return array1.concat(slice.call(array2, index));\n}\n\nfunction sliceArgs(args, startIndex) {\n  return slice.call(args, startIndex || 0);\n}\n\n\n/* jshint -W101 */\n/**\n * @ngdoc function\n * @name angular.bind\n * @module ng\n * @kind function\n *\n * @description\n * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for\n * `fn`). You can supply optional `args` that are prebound to the function. This feature is also\n * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as\n * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).\n *\n * @param {Object} self Context which `fn` should be evaluated in.\n * @param {function()} fn Function to be bound.\n * @param {...*} args Optional arguments to be prebound to the `fn` function call.\n * @returns {function()} Function that wraps the `fn` with all the specified bindings.\n */\n/* jshint +W101 */\nfunction bind(self, fn) {\n  var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];\n  if (isFunction(fn) && !(fn instanceof RegExp)) {\n    return curryArgs.length\n      ? function() {\n          return arguments.length\n            ? fn.apply(self, concat(curryArgs, arguments, 0))\n            : fn.apply(self, curryArgs);\n        }\n      : function() {\n          return arguments.length\n            ? fn.apply(self, arguments)\n            : fn.call(self);\n        };\n  } else {\n    // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)\n    return fn;\n  }\n}\n\n\nfunction toJsonReplacer(key, value) {\n  var val = value;\n\n  if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {\n    val = undefined;\n  } else if (isWindow(value)) {\n    val = '$WINDOW';\n  } else if (value &&  document === value) {\n    val = '$DOCUMENT';\n  } else if (isScope(value)) {\n    val = '$SCOPE';\n  }\n\n  return val;\n}\n\n\n/**\n * @ngdoc function\n * @name angular.toJson\n * @module ng\n * @kind function\n *\n * @description\n * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be\n * stripped since angular uses this notation internally.\n *\n * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.\n * @param {boolean|number=} pretty If set to true, the JSON output will contain newlines and whitespace.\n *    If set to an integer, the JSON output will contain that many spaces per indentation (the default is 2).\n * @returns {string|undefined} JSON-ified string representing `obj`.\n */\nfunction toJson(obj, pretty) {\n  if (typeof obj === 'undefined') return undefined;\n  if (!isNumber(pretty)) {\n    pretty = pretty ? 2 : null;\n  }\n  return JSON.stringify(obj, toJsonReplacer, pretty);\n}\n\n\n/**\n * @ngdoc function\n * @name angular.fromJson\n * @module ng\n * @kind function\n *\n * @description\n * Deserializes a JSON string.\n *\n * @param {string} json JSON string to deserialize.\n * @returns {Object|Array|string|number} Deserialized JSON string.\n */\nfunction fromJson(json) {\n  return isString(json)\n      ? JSON.parse(json)\n      : json;\n}\n\n\n/**\n * @returns {string} Returns the string representation of the element.\n */\nfunction startingTag(element) {\n  element = jqLite(element).clone();\n  try {\n    // turns out IE does not let you set .html() on elements which\n    // are not allowed to have children. So we just ignore it.\n    element.empty();\n  } catch (e) {}\n  var elemHtml = jqLite('<div>').append(element).html();\n  try {\n    return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :\n        elemHtml.\n          match(/^(<[^>]+>)/)[1].\n          replace(/^<([\\w\\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });\n  } catch (e) {\n    return lowercase(elemHtml);\n  }\n\n}\n\n\n/////////////////////////////////////////////////\n\n/**\n * Tries to decode the URI component without throwing an exception.\n *\n * @private\n * @param str value potential URI component to check.\n * @returns {boolean} True if `value` can be decoded\n * with the decodeURIComponent function.\n */\nfunction tryDecodeURIComponent(value) {\n  try {\n    return decodeURIComponent(value);\n  } catch (e) {\n    // Ignore any invalid uri component\n  }\n}\n\n\n/**\n * Parses an escaped url query string into key-value pairs.\n * @returns {Object.<string,boolean|Array>}\n */\nfunction parseKeyValue(/**string*/keyValue) {\n  var obj = {}, key_value, key;\n  forEach((keyValue || \"\").split('&'), function(keyValue) {\n    if (keyValue) {\n      key_value = keyValue.replace(/\\+/g,'%20').split('=');\n      key = tryDecodeURIComponent(key_value[0]);\n      if (isDefined(key)) {\n        var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;\n        if (!hasOwnProperty.call(obj, key)) {\n          obj[key] = val;\n        } else if (isArray(obj[key])) {\n          obj[key].push(val);\n        } else {\n          obj[key] = [obj[key],val];\n        }\n      }\n    }\n  });\n  return obj;\n}\n\nfunction toKeyValue(obj) {\n  var parts = [];\n  forEach(obj, function(value, key) {\n    if (isArray(value)) {\n      forEach(value, function(arrayValue) {\n        parts.push(encodeUriQuery(key, true) +\n                   (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));\n      });\n    } else {\n    parts.push(encodeUriQuery(key, true) +\n               (value === true ? '' : '=' + encodeUriQuery(value, true)));\n    }\n  });\n  return parts.length ? parts.join('&') : '';\n}\n\n\n/**\n * We need our custom method because encodeURIComponent is too aggressive and doesn't follow\n * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path\n * segments:\n *    segment       = *pchar\n *    pchar         = unreserved / pct-encoded / sub-delims / \":\" / \"@\"\n *    pct-encoded   = \"%\" HEXDIG HEXDIG\n *    unreserved    = ALPHA / DIGIT / \"-\" / \".\" / \"_\" / \"~\"\n *    sub-delims    = \"!\" / \"$\" / \"&\" / \"'\" / \"(\" / \")\"\n *                     / \"*\" / \"+\" / \",\" / \";\" / \"=\"\n */\nfunction encodeUriSegment(val) {\n  return encodeUriQuery(val, true).\n             replace(/%26/gi, '&').\n             replace(/%3D/gi, '=').\n             replace(/%2B/gi, '+');\n}\n\n\n/**\n * This method is intended for encoding *key* or *value* parts of query component. We need a custom\n * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be\n * encoded per http://tools.ietf.org/html/rfc3986:\n *    query       = *( pchar / \"/\" / \"?\" )\n *    pchar         = unreserved / pct-encoded / sub-delims / \":\" / \"@\"\n *    unreserved    = ALPHA / DIGIT / \"-\" / \".\" / \"_\" / \"~\"\n *    pct-encoded   = \"%\" HEXDIG HEXDIG\n *    sub-delims    = \"!\" / \"$\" / \"&\" / \"'\" / \"(\" / \")\"\n *                     / \"*\" / \"+\" / \",\" / \";\" / \"=\"\n */\nfunction encodeUriQuery(val, pctEncodeSpaces) {\n  return encodeURIComponent(val).\n             replace(/%40/gi, '@').\n             replace(/%3A/gi, ':').\n             replace(/%24/g, '$').\n             replace(/%2C/gi, ',').\n             replace(/%3B/gi, ';').\n             replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));\n}\n\nvar ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];\n\nfunction getNgAttribute(element, ngAttr) {\n  var attr, i, ii = ngAttrPrefixes.length;\n  element = jqLite(element);\n  for (i = 0; i < ii; ++i) {\n    attr = ngAttrPrefixes[i] + ngAttr;\n    if (isString(attr = element.attr(attr))) {\n      return attr;\n    }\n  }\n  return null;\n}\n\n/**\n * @ngdoc directive\n * @name ngApp\n * @module ng\n *\n * @element ANY\n * @param {angular.Module} ngApp an optional application\n *   {@link angular.module module} name to load.\n * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be\n *   created in \"strict-di\" mode. This means that the application will fail to invoke functions which\n *   do not use explicit function annotation (and are thus unsuitable for minification), as described\n *   in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in\n *   tracking down the root of these bugs.\n *\n * @description\n *\n * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive\n * designates the **root element** of the application and is typically placed near the root element\n * of the page - e.g. on the `<body>` or `<html>` tags.\n *\n * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`\n * found in the document will be used to define the root element to auto-bootstrap as an\n * application. To run multiple applications in an HTML document you must manually bootstrap them using\n * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.\n *\n * You can specify an **AngularJS module** to be used as the root module for the application.  This\n * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It\n * should contain the application code needed or have dependencies on other modules that will\n * contain the code. See {@link angular.module} for more information.\n *\n * In the example below if the `ngApp` directive were not placed on the `html` element then the\n * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`\n * would not be resolved to `3`.\n *\n * `ngApp` is the easiest, and most common way to bootstrap an application.\n *\n <example module=\"ngAppDemo\">\n   <file name=\"index.html\">\n   <div ng-controller=\"ngAppDemoController\">\n     I can add: {{a}} + {{b}} =  {{ a+b }}\n   </div>\n   </file>\n   <file name=\"script.js\">\n   angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {\n     $scope.a = 1;\n     $scope.b = 2;\n   });\n   </file>\n </example>\n *\n * Using `ngStrictDi`, you would see something like this:\n *\n <example ng-app-included=\"true\">\n   <file name=\"index.html\">\n   <div ng-app=\"ngAppStrictDemo\" ng-strict-di>\n       <div ng-controller=\"GoodController1\">\n           I can add: {{a}} + {{b}} =  {{ a+b }}\n\n           <p>This renders because the controller does not fail to\n              instantiate, by using explicit annotation style (see\n              script.js for details)\n           </p>\n       </div>\n\n       <div ng-controller=\"GoodController2\">\n           Name: <input ng-model=\"name\"><br />\n           Hello, {{name}}!\n\n           <p>This renders because the controller does not fail to\n              instantiate, by using explicit annotation style\n              (see script.js for details)\n           </p>\n       </div>\n\n       <div ng-controller=\"BadController\">\n           I can add: {{a}} + {{b}} =  {{ a+b }}\n\n           <p>The controller could not be instantiated, due to relying\n              on automatic function annotations (which are disabled in\n              strict mode). As such, the content of this section is not\n              interpolated, and there should be an error in your web console.\n           </p>\n       </div>\n   </div>\n   </file>\n   <file name=\"script.js\">\n   angular.module('ngAppStrictDemo', [])\n     // BadController will fail to instantiate, due to relying on automatic function annotation,\n     // rather than an explicit annotation\n     .controller('BadController', function($scope) {\n       $scope.a = 1;\n       $scope.b = 2;\n     })\n     // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,\n     // due to using explicit annotations using the array style and $inject property, respectively.\n     .controller('GoodController1', ['$scope', function($scope) {\n       $scope.a = 1;\n       $scope.b = 2;\n     }])\n     .controller('GoodController2', GoodController2);\n     function GoodController2($scope) {\n       $scope.name = \"World\";\n     }\n     GoodController2.$inject = ['$scope'];\n   </file>\n   <file name=\"style.css\">\n   div[ng-controller] {\n       margin-bottom: 1em;\n       -webkit-border-radius: 4px;\n       border-radius: 4px;\n       border: 1px solid;\n       padding: .5em;\n   }\n   div[ng-controller^=Good] {\n       border-color: #d6e9c6;\n       background-color: #dff0d8;\n       color: #3c763d;\n   }\n   div[ng-controller^=Bad] {\n       border-color: #ebccd1;\n       background-color: #f2dede;\n       color: #a94442;\n       margin-bottom: 0;\n   }\n   </file>\n </example>\n */\nfunction angularInit(element, bootstrap) {\n  var appElement,\n      module,\n      config = {};\n\n  // The element `element` has priority over any other element\n  forEach(ngAttrPrefixes, function(prefix) {\n    var name = prefix + 'app';\n\n    if (!appElement && element.hasAttribute && element.hasAttribute(name)) {\n      appElement = element;\n      module = element.getAttribute(name);\n    }\n  });\n  forEach(ngAttrPrefixes, function(prefix) {\n    var name = prefix + 'app';\n    var candidate;\n\n    if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\\\:') + ']'))) {\n      appElement = candidate;\n      module = candidate.getAttribute(name);\n    }\n  });\n  if (appElement) {\n    config.strictDi = getNgAttribute(appElement, \"strict-di\") !== null;\n    bootstrap(appElement, module ? [module] : [], config);\n  }\n}\n\n/**\n * @ngdoc function\n * @name angular.bootstrap\n * @module ng\n * @description\n * Use this function to manually start up angular application.\n *\n * See: {@link guide/bootstrap Bootstrap}\n *\n * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.\n * They must use {@link ng.directive:ngApp ngApp}.\n *\n * Angular will detect if it has been loaded into the browser more than once and only allow the\n * first loaded script to be bootstrapped and will report a warning to the browser console for\n * each of the subsequent scripts. This prevents strange results in applications, where otherwise\n * multiple instances of Angular try to work on the DOM.\n *\n * ```html\n * <!doctype html>\n * <html>\n * <body>\n * <div ng-controller=\"WelcomeController\">\n *   {{greeting}}\n * </div>\n *\n * <script src=\"angular.js\"></script>\n * <script>\n *   var app = angular.module('demo', [])\n *   .controller('WelcomeController', function($scope) {\n *       $scope.greeting = 'Welcome!';\n *   });\n *   angular.bootstrap(document, ['demo']);\n * </script>\n * </body>\n * </html>\n * ```\n *\n * @param {DOMElement} element DOM element which is the root of angular application.\n * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.\n *     Each item in the array should be the name of a predefined module or a (DI annotated)\n *     function that will be invoked by the injector as a `config` block.\n *     See: {@link angular.module modules}\n * @param {Object=} config an object for defining configuration options for the application. The\n *     following keys are supported:\n *\n * * `strictDi` - disable automatic function annotation for the application. This is meant to\n *   assist in finding bugs which break minified code. Defaults to `false`.\n *\n * @returns {auto.$injector} Returns the newly created injector for this app.\n */\nfunction bootstrap(element, modules, config) {\n  if (!isObject(config)) config = {};\n  var defaultConfig = {\n    strictDi: false\n  };\n  config = extend(defaultConfig, config);\n  var doBootstrap = function() {\n    element = jqLite(element);\n\n    if (element.injector()) {\n      var tag = (element[0] === document) ? 'document' : startingTag(element);\n      //Encode angle brackets to prevent input from being sanitized to empty string #8683\n      throw ngMinErr(\n          'btstrpd',\n          \"App Already Bootstrapped with this Element '{0}'\",\n          tag.replace(/</,'&lt;').replace(/>/,'&gt;'));\n    }\n\n    modules = modules || [];\n    modules.unshift(['$provide', function($provide) {\n      $provide.value('$rootElement', element);\n    }]);\n\n    if (config.debugInfoEnabled) {\n      // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.\n      modules.push(['$compileProvider', function($compileProvider) {\n        $compileProvider.debugInfoEnabled(true);\n      }]);\n    }\n\n    modules.unshift('ng');\n    var injector = createInjector(modules, config.strictDi);\n    injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',\n       function bootstrapApply(scope, element, compile, injector) {\n        scope.$apply(function() {\n          element.data('$injector', injector);\n          compile(element)(scope);\n        });\n      }]\n    );\n    return injector;\n  };\n\n  var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;\n  var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;\n\n  if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {\n    config.debugInfoEnabled = true;\n    window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');\n  }\n\n  if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {\n    return doBootstrap();\n  }\n\n  window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');\n  angular.resumeBootstrap = function(extraModules) {\n    forEach(extraModules, function(module) {\n      modules.push(module);\n    });\n    return doBootstrap();\n  };\n\n  if (isFunction(angular.resumeDeferredBootstrap)) {\n    angular.resumeDeferredBootstrap();\n  }\n}\n\n/**\n * @ngdoc function\n * @name angular.reloadWithDebugInfo\n * @module ng\n * @description\n * Use this function to reload the current application with debug information turned on.\n * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.\n *\n * See {@link ng.$compileProvider#debugInfoEnabled} for more.\n */\nfunction reloadWithDebugInfo() {\n  window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;\n  window.location.reload();\n}\n\n/**\n * @name angular.getTestability\n * @module ng\n * @description\n * Get the testability service for the instance of Angular on the given\n * element.\n * @param {DOMElement} element DOM element which is the root of angular application.\n */\nfunction getTestability(rootElement) {\n  var injector = angular.element(rootElement).injector();\n  if (!injector) {\n    throw ngMinErr('test',\n      'no injector found for element argument to getTestability');\n  }\n  return injector.get('$$testability');\n}\n\nvar SNAKE_CASE_REGEXP = /[A-Z]/g;\nfunction snake_case(name, separator) {\n  separator = separator || '_';\n  return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {\n    return (pos ? separator : '') + letter.toLowerCase();\n  });\n}\n\nvar bindJQueryFired = false;\nvar skipDestroyOnNextJQueryCleanData;\nfunction bindJQuery() {\n  var originalCleanData;\n\n  if (bindJQueryFired) {\n    return;\n  }\n\n  // bind to jQuery if present;\n  jQuery = window.jQuery;\n  // Use jQuery if it exists with proper functionality, otherwise default to us.\n  // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.\n  // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older\n  // versions. It will not work for sure with jQuery <1.7, though.\n  if (jQuery && jQuery.fn.on) {\n    jqLite = jQuery;\n    extend(jQuery.fn, {\n      scope: JQLitePrototype.scope,\n      isolateScope: JQLitePrototype.isolateScope,\n      controller: JQLitePrototype.controller,\n      injector: JQLitePrototype.injector,\n      inheritedData: JQLitePrototype.inheritedData\n    });\n\n    // All nodes removed from the DOM via various jQuery APIs like .remove()\n    // are passed through jQuery.cleanData. Monkey-patch this method to fire\n    // the $destroy event on all removed nodes.\n    originalCleanData = jQuery.cleanData;\n    jQuery.cleanData = function(elems) {\n      var events;\n      if (!skipDestroyOnNextJQueryCleanData) {\n        for (var i = 0, elem; (elem = elems[i]) != null; i++) {\n          events = jQuery._data(elem, \"events\");\n          if (events && events.$destroy) {\n            jQuery(elem).triggerHandler('$destroy');\n          }\n        }\n      } else {\n        skipDestroyOnNextJQueryCleanData = false;\n      }\n      originalCleanData(elems);\n    };\n  } else {\n    jqLite = JQLite;\n  }\n\n  angular.element = jqLite;\n\n  // Prevent double-proxying.\n  bindJQueryFired = true;\n}\n\n/**\n * throw error if the argument is falsy.\n */\nfunction assertArg(arg, name, reason) {\n  if (!arg) {\n    throw ngMinErr('areq', \"Argument '{0}' is {1}\", (name || '?'), (reason || \"required\"));\n  }\n  return arg;\n}\n\nfunction assertArgFn(arg, name, acceptArrayAnnotation) {\n  if (acceptArrayAnnotation && isArray(arg)) {\n      arg = arg[arg.length - 1];\n  }\n\n  assertArg(isFunction(arg), name, 'not a function, got ' +\n      (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));\n  return arg;\n}\n\n/**\n * throw error if the name given is hasOwnProperty\n * @param  {String} name    the name to test\n * @param  {String} context the context in which the name is used, such as module or directive\n */\nfunction assertNotHasOwnProperty(name, context) {\n  if (name === 'hasOwnProperty') {\n    throw ngMinErr('badname', \"hasOwnProperty is not a valid {0} name\", context);\n  }\n}\n\n/**\n * Return the value accessible from the object by path. Any undefined traversals are ignored\n * @param {Object} obj starting object\n * @param {String} path path to traverse\n * @param {boolean} [bindFnToScope=true]\n * @returns {Object} value as accessible by path\n */\n//TODO(misko): this function needs to be removed\nfunction getter(obj, path, bindFnToScope) {\n  if (!path) return obj;\n  var keys = path.split('.');\n  var key;\n  var lastInstance = obj;\n  var len = keys.length;\n\n  for (var i = 0; i < len; i++) {\n    key = keys[i];\n    if (obj) {\n      obj = (lastInstance = obj)[key];\n    }\n  }\n  if (!bindFnToScope && isFunction(obj)) {\n    return bind(lastInstance, obj);\n  }\n  return obj;\n}\n\n/**\n * Return the DOM siblings between the first and last node in the given array.\n * @param {Array} array like object\n * @returns {jqLite} jqLite collection containing the nodes\n */\nfunction getBlockNodes(nodes) {\n  // TODO(perf): just check if all items in `nodes` are siblings and if they are return the original\n  //             collection, otherwise update the original collection.\n  var node = nodes[0];\n  var endNode = nodes[nodes.length - 1];\n  var blockNodes = [node];\n\n  do {\n    node = node.nextSibling;\n    if (!node) break;\n    blockNodes.push(node);\n  } while (node !== endNode);\n\n  return jqLite(blockNodes);\n}\n\n\n/**\n * Creates a new object without a prototype. This object is useful for lookup without having to\n * guard against prototypically inherited properties via hasOwnProperty.\n *\n * Related micro-benchmarks:\n * - http://jsperf.com/object-create2\n * - http://jsperf.com/proto-map-lookup/2\n * - http://jsperf.com/for-in-vs-object-keys2\n *\n * @returns {Object}\n */\nfunction createMap() {\n  return Object.create(null);\n}\n\nvar NODE_TYPE_ELEMENT = 1;\nvar NODE_TYPE_TEXT = 3;\nvar NODE_TYPE_COMMENT = 8;\nvar NODE_TYPE_DOCUMENT = 9;\nvar NODE_TYPE_DOCUMENT_FRAGMENT = 11;\n\n/**\n * @ngdoc type\n * @name angular.Module\n * @module ng\n * @description\n *\n * Interface for configuring angular {@link angular.module modules}.\n */\n\nfunction setupModuleLoader(window) {\n\n  var $injectorMinErr = minErr('$injector');\n  var ngMinErr = minErr('ng');\n\n  function ensure(obj, name, factory) {\n    return obj[name] || (obj[name] = factory());\n  }\n\n  var angular = ensure(window, 'angular', Object);\n\n  // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap\n  angular.$$minErr = angular.$$minErr || minErr;\n\n  return ensure(angular, 'module', function() {\n    /** @type {Object.<string, angular.Module>} */\n    var modules = {};\n\n    /**\n     * @ngdoc function\n     * @name angular.module\n     * @module ng\n     * @description\n     *\n     * The `angular.module` is a global place for creating, registering and retrieving Angular\n     * modules.\n     * All modules (angular core or 3rd party) that should be available to an application must be\n     * registered using this mechanism.\n     *\n     * When passed two or more arguments, a new module is created.  If passed only one argument, an\n     * existing module (the name passed as the first argument to `module`) is retrieved.\n     *\n     *\n     * # Module\n     *\n     * A module is a collection of services, directives, controllers, filters, and configuration information.\n     * `angular.module` is used to configure the {@link auto.$injector $injector}.\n     *\n     * ```js\n     * // Create a new module\n     * var myModule = angular.module('myModule', []);\n     *\n     * // register a new service\n     * myModule.value('appName', 'MyCoolApp');\n     *\n     * // configure existing services inside initialization blocks.\n     * myModule.config(['$locationProvider', function($locationProvider) {\n     *   // Configure existing providers\n     *   $locationProvider.hashPrefix('!');\n     * }]);\n     * ```\n     *\n     * Then you can create an injector and load your modules like this:\n     *\n     * ```js\n     * var injector = angular.injector(['ng', 'myModule'])\n     * ```\n     *\n     * However it's more likely that you'll just use\n     * {@link ng.directive:ngApp ngApp} or\n     * {@link angular.bootstrap} to simplify this process for you.\n     *\n     * @param {!string} name The name of the module to create or retrieve.\n     * @param {!Array.<string>=} requires If specified then new module is being created. If\n     *        unspecified then the module is being retrieved for further configuration.\n     * @param {Function=} configFn Optional configuration function for the module. Same as\n     *        {@link angular.Module#config Module#config()}.\n     * @returns {module} new module with the {@link angular.Module} api.\n     */\n    return function module(name, requires, configFn) {\n      var assertNotHasOwnProperty = function(name, context) {\n        if (name === 'hasOwnProperty') {\n          throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);\n        }\n      };\n\n      assertNotHasOwnProperty(name, 'module');\n      if (requires && modules.hasOwnProperty(name)) {\n        modules[name] = null;\n      }\n      return ensure(modules, name, function() {\n        if (!requires) {\n          throw $injectorMinErr('nomod', \"Module '{0}' is not available! You either misspelled \" +\n             \"the module name or forgot to load it. If registering a module ensure that you \" +\n             \"specify the dependencies as the second argument.\", name);\n        }\n\n        /** @type {!Array.<Array.<*>>} */\n        var invokeQueue = [];\n\n        /** @type {!Array.<Function>} */\n        var configBlocks = [];\n\n        /** @type {!Array.<Function>} */\n        var runBlocks = [];\n\n        var config = invokeLater('$injector', 'invoke', 'push', configBlocks);\n\n        /** @type {angular.Module} */\n        var moduleInstance = {\n          // Private state\n          _invokeQueue: invokeQueue,\n          _configBlocks: configBlocks,\n          _runBlocks: runBlocks,\n\n          /**\n           * @ngdoc property\n           * @name angular.Module#requires\n           * @module ng\n           *\n           * @description\n           * Holds the list of modules which the injector will load before the current module is\n           * loaded.\n           */\n          requires: requires,\n\n          /**\n           * @ngdoc property\n           * @name angular.Module#name\n           * @module ng\n           *\n           * @description\n           * Name of the module.\n           */\n          name: name,\n\n\n          /**\n           * @ngdoc method\n           * @name angular.Module#provider\n           * @module ng\n           * @param {string} name service name\n           * @param {Function} providerType Construction function for creating new instance of the\n           *                                service.\n           * @description\n           * See {@link auto.$provide#provider $provide.provider()}.\n           */\n          provider: invokeLater('$provide', 'provider'),\n\n          /**\n           * @ngdoc method\n           * @name angular.Module#factory\n           * @module ng\n           * @param {string} name service name\n           * @param {Function} providerFunction Function for creating new instance of the service.\n           * @description\n           * See {@link auto.$provide#factory $provide.factory()}.\n           */\n          factory: invokeLater('$provide', 'factory'),\n\n          /**\n           * @ngdoc method\n           * @name angular.Module#service\n           * @module ng\n           * @param {string} name service name\n           * @param {Function} constructor A constructor function that will be instantiated.\n           * @description\n           * See {@link auto.$provide#service $provide.service()}.\n           */\n          service: invokeLater('$provide', 'service'),\n\n          /**\n           * @ngdoc method\n           * @name angular.Module#value\n           * @module ng\n           * @param {string} name service name\n           * @param {*} object Service instance object.\n           * @description\n           * See {@link auto.$provide#value $provide.value()}.\n           */\n          value: invokeLater('$provide', 'value'),\n\n          /**\n           * @ngdoc method\n           * @name angular.Module#constant\n           * @module ng\n           * @param {string} name constant name\n           * @param {*} object Constant value.\n           * @description\n           * Because the constant are fixed, they get applied before other provide methods.\n           * See {@link auto.$provide#constant $provide.constant()}.\n           */\n          constant: invokeLater('$provide', 'constant', 'unshift'),\n\n          /**\n           * @ngdoc method\n           * @name angular.Module#animation\n           * @module ng\n           * @param {string} name animation name\n           * @param {Function} animationFactory Factory function for creating new instance of an\n           *                                    animation.\n           * @description\n           *\n           * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.\n           *\n           *\n           * Defines an animation hook that can be later used with\n           * {@link ngAnimate.$animate $animate} service and directives that use this service.\n           *\n           * ```js\n           * module.animation('.animation-name', function($inject1, $inject2) {\n           *   return {\n           *     eventName : function(element, done) {\n           *       //code to run the animation\n           *       //once complete, then run done()\n           *       return function cancellationFunction(element) {\n           *         //code to cancel the animation\n           *       }\n           *     }\n           *   }\n           * })\n           * ```\n           *\n           * See {@link ng.$animateProvider#register $animateProvider.register()} and\n           * {@link ngAnimate ngAnimate module} for more information.\n           */\n          animation: invokeLater('$animateProvider', 'register'),\n\n          /**\n           * @ngdoc method\n           * @name angular.Module#filter\n           * @module ng\n           * @param {string} name Filter name.\n           * @param {Function} filterFactory Factory function for creating new instance of filter.\n           * @description\n           * See {@link ng.$filterProvider#register $filterProvider.register()}.\n           */\n          filter: invokeLater('$filterProvider', 'register'),\n\n          /**\n           * @ngdoc method\n           * @name angular.Module#controller\n           * @module ng\n           * @param {string|Object} name Controller name, or an object map of controllers where the\n           *    keys are the names and the values are the constructors.\n           * @param {Function} constructor Controller constructor function.\n           * @description\n           * See {@link ng.$controllerProvider#register $controllerProvider.register()}.\n           */\n          controller: invokeLater('$controllerProvider', 'register'),\n\n          /**\n           * @ngdoc method\n           * @name angular.Module#directive\n           * @module ng\n           * @param {string|Object} name Directive name, or an object map of directives where the\n           *    keys are the names and the values are the factories.\n           * @param {Function} directiveFactory Factory function for creating new instance of\n           * directives.\n           * @description\n           * See {@link ng.$compileProvider#directive $compileProvider.directive()}.\n           */\n          directive: invokeLater('$compileProvider', 'directive'),\n\n          /**\n           * @ngdoc method\n           * @name angular.Module#config\n           * @module ng\n           * @param {Function} configFn Execute this function on module load. Useful for service\n           *    configuration.\n           * @description\n           * Use this method to register work which needs to be performed on module loading.\n           * For more about how to configure services, see\n           * {@link providers#provider-recipe Provider Recipe}.\n           */\n          config: config,\n\n          /**\n           * @ngdoc method\n           * @name angular.Module#run\n           * @module ng\n           * @param {Function} initializationFn Execute this function after injector creation.\n           *    Useful for application initialization.\n           * @description\n           * Use this method to register work which should be performed when the injector is done\n           * loading all modules.\n           */\n          run: function(block) {\n            runBlocks.push(block);\n            return this;\n          }\n        };\n\n        if (configFn) {\n          config(configFn);\n        }\n\n        return moduleInstance;\n\n        /**\n         * @param {string} provider\n         * @param {string} method\n         * @param {String=} insertMethod\n         * @returns {angular.Module}\n         */\n        function invokeLater(provider, method, insertMethod, queue) {\n          if (!queue) queue = invokeQueue;\n          return function() {\n            queue[insertMethod || 'push']([provider, method, arguments]);\n            return moduleInstance;\n          };\n        }\n      });\n    };\n  });\n\n}\n\n/* global: toDebugString: true */\n\nfunction serializeObject(obj) {\n  var seen = [];\n\n  return JSON.stringify(obj, function(key, val) {\n    val = toJsonReplacer(key, val);\n    if (isObject(val)) {\n\n      if (seen.indexOf(val) >= 0) return '<<already seen>>';\n\n      seen.push(val);\n    }\n    return val;\n  });\n}\n\nfunction toDebugString(obj) {\n  if (typeof obj === 'function') {\n    return obj.toString().replace(/ \\{[\\s\\S]*$/, '');\n  } else if (typeof obj === 'undefined') {\n    return 'undefined';\n  } else if (typeof obj !== 'string') {\n    return serializeObject(obj);\n  }\n  return obj;\n}\n\n/* global angularModule: true,\n  version: true,\n\n  $LocaleProvider,\n  $CompileProvider,\n\n  htmlAnchorDirective,\n  inputDirective,\n  inputDirective,\n  formDirective,\n  scriptDirective,\n  selectDirective,\n  styleDirective,\n  optionDirective,\n  ngBindDirective,\n  ngBindHtmlDirective,\n  ngBindTemplateDirective,\n  ngClassDirective,\n  ngClassEvenDirective,\n  ngClassOddDirective,\n  ngCspDirective,\n  ngCloakDirective,\n  ngControllerDirective,\n  ngFormDirective,\n  ngHideDirective,\n  ngIfDirective,\n  ngIncludeDirective,\n  ngIncludeFillContentDirective,\n  ngInitDirective,\n  ngNonBindableDirective,\n  ngPluralizeDirective,\n  ngRepeatDirective,\n  ngShowDirective,\n  ngStyleDirective,\n  ngSwitchDirective,\n  ngSwitchWhenDirective,\n  ngSwitchDefaultDirective,\n  ngOptionsDirective,\n  ngTranscludeDirective,\n  ngModelDirective,\n  ngListDirective,\n  ngChangeDirective,\n  patternDirective,\n  patternDirective,\n  requiredDirective,\n  requiredDirective,\n  minlengthDirective,\n  minlengthDirective,\n  maxlengthDirective,\n  maxlengthDirective,\n  ngValueDirective,\n  ngModelOptionsDirective,\n  ngAttributeAliasDirectives,\n  ngEventDirectives,\n\n  $AnchorScrollProvider,\n  $AnimateProvider,\n  $BrowserProvider,\n  $CacheFactoryProvider,\n  $ControllerProvider,\n  $DocumentProvider,\n  $ExceptionHandlerProvider,\n  $FilterProvider,\n  $InterpolateProvider,\n  $IntervalProvider,\n  $HttpProvider,\n  $HttpBackendProvider,\n  $LocationProvider,\n  $LogProvider,\n  $ParseProvider,\n  $RootScopeProvider,\n  $QProvider,\n  $$QProvider,\n  $$SanitizeUriProvider,\n  $SceProvider,\n  $SceDelegateProvider,\n  $SnifferProvider,\n  $TemplateCacheProvider,\n  $TemplateRequestProvider,\n  $$TestabilityProvider,\n  $TimeoutProvider,\n  $$RAFProvider,\n  $$AsyncCallbackProvider,\n  $WindowProvider,\n  $$jqLiteProvider\n*/\n\n\n/**\n * @ngdoc object\n * @name angular.version\n * @module ng\n * @description\n * An object that contains information about the current AngularJS version. This object has the\n * following properties:\n *\n * - `full` â€“ `{string}` â€“ Full version string, such as \"0.9.18\".\n * - `major` â€“ `{number}` â€“ Major version number, such as \"0\".\n * - `minor` â€“ `{number}` â€“ Minor version number, such as \"9\".\n * - `dot` â€“ `{number}` â€“ Dot version number, such as \"18\".\n * - `codeName` â€“ `{string}` â€“ Code name of the release, such as \"jiggling-armfat\".\n */\nvar version = {\n  full: '1.3.15',    // all of these placeholder strings will be replaced by grunt's\n  major: 1,    // package task\n  minor: 3,\n  dot: 15,\n  codeName: 'locality-filtration'\n};\n\n\nfunction publishExternalAPI(angular) {\n  extend(angular, {\n    'bootstrap': bootstrap,\n    'copy': copy,\n    'extend': extend,\n    'equals': equals,\n    'element': jqLite,\n    'forEach': forEach,\n    'injector': createInjector,\n    'noop': noop,\n    'bind': bind,\n    'toJson': toJson,\n    'fromJson': fromJson,\n    'identity': identity,\n    'isUndefined': isUndefined,\n    'isDefined': isDefined,\n    'isString': isString,\n    'isFunction': isFunction,\n    'isObject': isObject,\n    'isNumber': isNumber,\n    'isElement': isElement,\n    'isArray': isArray,\n    'version': version,\n    'isDate': isDate,\n    'lowercase': lowercase,\n    'uppercase': uppercase,\n    'callbacks': {counter: 0},\n    'getTestability': getTestability,\n    '$$minErr': minErr,\n    '$$csp': csp,\n    'reloadWithDebugInfo': reloadWithDebugInfo\n  });\n\n  angularModule = setupModuleLoader(window);\n  try {\n    angularModule('ngLocale');\n  } catch (e) {\n    angularModule('ngLocale', []).provider('$locale', $LocaleProvider);\n  }\n\n  angularModule('ng', ['ngLocale'], ['$provide',\n    function ngModule($provide) {\n      // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.\n      $provide.provider({\n        $$sanitizeUri: $$SanitizeUriProvider\n      });\n      $provide.provider('$compile', $CompileProvider).\n        directive({\n            a: htmlAnchorDirective,\n            input: inputDirective,\n            textarea: inputDirective,\n            form: formDirective,\n            script: scriptDirective,\n            select: selectDirective,\n            style: styleDirective,\n            option: optionDirective,\n            ngBind: ngBindDirective,\n            ngBindHtml: ngBindHtmlDirective,\n            ngBindTemplate: ngBindTemplateDirective,\n            ngClass: ngClassDirective,\n            ngClassEven: ngClassEvenDirective,\n            ngClassOdd: ngClassOddDirective,\n            ngCloak: ngCloakDirective,\n            ngController: ngControllerDirective,\n            ngForm: ngFormDirective,\n            ngHide: ngHideDirective,\n            ngIf: ngIfDirective,\n            ngInclude: ngIncludeDirective,\n            ngInit: ngInitDirective,\n            ngNonBindable: ngNonBindableDirective,\n            ngPluralize: ngPluralizeDirective,\n            ngRepeat: ngRepeatDirective,\n            ngShow: ngShowDirective,\n            ngStyle: ngStyleDirective,\n            ngSwitch: ngSwitchDirective,\n            ngSwitchWhen: ngSwitchWhenDirective,\n            ngSwitchDefault: ngSwitchDefaultDirective,\n            ngOptions: ngOptionsDirective,\n            ngTransclude: ngTranscludeDirective,\n            ngModel: ngModelDirective,\n            ngList: ngListDirective,\n            ngChange: ngChangeDirective,\n            pattern: patternDirective,\n            ngPattern: patternDirective,\n            required: requiredDirective,\n            ngRequired: requiredDirective,\n            minlength: minlengthDirective,\n            ngMinlength: minlengthDirective,\n            maxlength: maxlengthDirective,\n            ngMaxlength: maxlengthDirective,\n            ngValue: ngValueDirective,\n            ngModelOptions: ngModelOptionsDirective\n        }).\n        directive({\n          ngInclude: ngIncludeFillContentDirective\n        }).\n        directive(ngAttributeAliasDirectives).\n        directive(ngEventDirectives);\n      $provide.provider({\n        $anchorScroll: $AnchorScrollProvider,\n        $animate: $AnimateProvider,\n        $browser: $BrowserProvider,\n        $cacheFactory: $CacheFactoryProvider,\n        $controller: $ControllerProvider,\n        $document: $DocumentProvider,\n        $exceptionHandler: $ExceptionHandlerProvider,\n        $filter: $FilterProvider,\n        $interpolate: $InterpolateProvider,\n        $interval: $IntervalProvider,\n        $http: $HttpProvider,\n        $httpBackend: $HttpBackendProvider,\n        $location: $LocationProvider,\n        $log: $LogProvider,\n        $parse: $ParseProvider,\n        $rootScope: $RootScopeProvider,\n        $q: $QProvider,\n        $$q: $$QProvider,\n        $sce: $SceProvider,\n        $sceDelegate: $SceDelegateProvider,\n        $sniffer: $SnifferProvider,\n        $templateCache: $TemplateCacheProvider,\n        $templateRequest: $TemplateRequestProvider,\n        $$testability: $$TestabilityProvider,\n        $timeout: $TimeoutProvider,\n        $window: $WindowProvider,\n        $$rAF: $$RAFProvider,\n        $$asyncCallback: $$AsyncCallbackProvider,\n        $$jqLite: $$jqLiteProvider\n      });\n    }\n  ]);\n}\n\n/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n *     Any commits to this file should be reviewed with security in mind.  *\n *   Changes to this file can potentially create security vulnerabilities. *\n *          An approval from 2 Core members with history of modifying      *\n *                         this file is required.                          *\n *                                                                         *\n *  Does the change somehow allow for arbitrary javascript to be executed? *\n *    Or allows for someone to change the prototype of built-in objects?   *\n *     Or gives undesired access to variables likes document or window?    *\n * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */\n\n/* global JQLitePrototype: true,\n  addEventListenerFn: true,\n  removeEventListenerFn: true,\n  BOOLEAN_ATTR: true,\n  ALIASED_ATTR: true,\n*/\n\n//////////////////////////////////\n//JQLite\n//////////////////////////////////\n\n/**\n * @ngdoc function\n * @name angular.element\n * @module ng\n * @kind function\n *\n * @description\n * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.\n *\n * If jQuery is available, `angular.element` is an alias for the\n * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`\n * delegates to Angular's built-in subset of jQuery, called \"jQuery lite\" or \"jqLite.\"\n *\n * <div class=\"alert alert-success\">jqLite is a tiny, API-compatible subset of jQuery that allows\n * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most\n * commonly needed functionality with the goal of having a very small footprint.</div>\n *\n * To use jQuery, simply load it before `DOMContentLoaded` event fired.\n *\n * <div class=\"alert\">**Note:** all element references in Angular are always wrapped with jQuery or\n * jqLite; they are never raw DOM references.</div>\n *\n * ## Angular's jqLite\n * jqLite provides only the following jQuery methods:\n *\n * - [`addClass()`](http://api.jquery.com/addClass/)\n * - [`after()`](http://api.jquery.com/after/)\n * - [`append()`](http://api.jquery.com/append/)\n * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters\n * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData\n * - [`children()`](http://api.jquery.com/children/) - Does not support selectors\n * - [`clone()`](http://api.jquery.com/clone/)\n * - [`contents()`](http://api.jquery.com/contents/)\n * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`\n * - [`data()`](http://api.jquery.com/data/)\n * - [`detach()`](http://api.jquery.com/detach/)\n * - [`empty()`](http://api.jquery.com/empty/)\n * - [`eq()`](http://api.jquery.com/eq/)\n * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name\n * - [`hasClass()`](http://api.jquery.com/hasClass/)\n * - [`html()`](http://api.jquery.com/html/)\n * - [`next()`](http://api.jquery.com/next/) - Does not support selectors\n * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData\n * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors\n * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors\n * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors\n * - [`prepend()`](http://api.jquery.com/prepend/)\n * - [`prop()`](http://api.jquery.com/prop/)\n * - [`ready()`](http://api.jquery.com/ready/)\n * - [`remove()`](http://api.jquery.com/remove/)\n * - [`removeAttr()`](http://api.jquery.com/removeAttr/)\n * - [`removeClass()`](http://api.jquery.com/removeClass/)\n * - [`removeData()`](http://api.jquery.com/removeData/)\n * - [`replaceWith()`](http://api.jquery.com/replaceWith/)\n * - [`text()`](http://api.jquery.com/text/)\n * - [`toggleClass()`](http://api.jquery.com/toggleClass/)\n * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.\n * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces\n * - [`val()`](http://api.jquery.com/val/)\n * - [`wrap()`](http://api.jquery.com/wrap/)\n *\n * ## jQuery/jqLite Extras\n * Angular also provides the following additional methods and events to both jQuery and jqLite:\n *\n * ### Events\n * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event\n *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM\n *    element before it is removed.\n *\n * ### Methods\n * - `controller(name)` - retrieves the controller of the current element or its parent. By default\n *   retrieves controller associated with the `ngController` directive. If `name` is provided as\n *   camelCase directive name, then the controller for this directive will be retrieved (e.g.\n *   `'ngModel'`).\n * - `injector()` - retrieves the injector of the current element or its parent.\n * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current\n *   element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to\n *   be enabled.\n * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the\n *   current element. This getter should be used only on elements that contain a directive which starts a new isolate\n *   scope. Calling `scope()` on this element always returns the original non-isolate scope.\n *   Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.\n * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top\n *   parent element is reached.\n *\n * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.\n * @returns {Object} jQuery object.\n */\n\nJQLite.expando = 'ng339';\n\nvar jqCache = JQLite.cache = {},\n    jqId = 1,\n    addEventListenerFn = function(element, type, fn) {\n      element.addEventListener(type, fn, false);\n    },\n    removeEventListenerFn = function(element, type, fn) {\n      element.removeEventListener(type, fn, false);\n    };\n\n/*\n * !!! This is an undocumented \"private\" function !!!\n */\nJQLite._data = function(node) {\n  //jQuery always returns an object on cache miss\n  return this.cache[node[this.expando]] || {};\n};\n\nfunction jqNextId() { return ++jqId; }\n\n\nvar SPECIAL_CHARS_REGEXP = /([\\:\\-\\_]+(.))/g;\nvar MOZ_HACK_REGEXP = /^moz([A-Z])/;\nvar MOUSE_EVENT_MAP= { mouseleave: \"mouseout\", mouseenter: \"mouseover\"};\nvar jqLiteMinErr = minErr('jqLite');\n\n/**\n * Converts snake_case to camelCase.\n * Also there is special case for Moz prefix starting with upper case letter.\n * @param name Name to normalize\n */\nfunction camelCase(name) {\n  return name.\n    replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {\n      return offset ? letter.toUpperCase() : letter;\n    }).\n    replace(MOZ_HACK_REGEXP, 'Moz$1');\n}\n\nvar SINGLE_TAG_REGEXP = /^<(\\w+)\\s*\\/?>(?:<\\/\\1>|)$/;\nvar HTML_REGEXP = /<|&#?\\w+;/;\nvar TAG_NAME_REGEXP = /<([\\w:]+)/;\nvar XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\\w:]+)[^>]*)\\/>/gi;\n\nvar wrapMap = {\n  'option': [1, '<select multiple=\"multiple\">', '</select>'],\n\n  'thead': [1, '<table>', '</table>'],\n  'col': [2, '<table><colgroup>', '</colgroup></table>'],\n  'tr': [2, '<table><tbody>', '</tbody></table>'],\n  'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],\n  '_default': [0, \"\", \"\"]\n};\n\nwrapMap.optgroup = wrapMap.option;\nwrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;\nwrapMap.th = wrapMap.td;\n\n\nfunction jqLiteIsTextNode(html) {\n  return !HTML_REGEXP.test(html);\n}\n\nfunction jqLiteAcceptsData(node) {\n  // The window object can accept data but has no nodeType\n  // Otherwise we are only interested in elements (1) and documents (9)\n  var nodeType = node.nodeType;\n  return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;\n}\n\nfunction jqLiteBuildFragment(html, context) {\n  var tmp, tag, wrap,\n      fragment = context.createDocumentFragment(),\n      nodes = [], i;\n\n  if (jqLiteIsTextNode(html)) {\n    // Convert non-html into a text node\n    nodes.push(context.createTextNode(html));\n  } else {\n    // Convert html into DOM nodes\n    tmp = tmp || fragment.appendChild(context.createElement(\"div\"));\n    tag = (TAG_NAME_REGEXP.exec(html) || [\"\", \"\"])[1].toLowerCase();\n    wrap = wrapMap[tag] || wrapMap._default;\n    tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, \"<$1></$2>\") + wrap[2];\n\n    // Descend through wrappers to the right content\n    i = wrap[0];\n    while (i--) {\n      tmp = tmp.lastChild;\n    }\n\n    nodes = concat(nodes, tmp.childNodes);\n\n    tmp = fragment.firstChild;\n    tmp.textContent = \"\";\n  }\n\n  // Remove wrapper from fragment\n  fragment.textContent = \"\";\n  fragment.innerHTML = \"\"; // Clear inner HTML\n  forEach(nodes, function(node) {\n    fragment.appendChild(node);\n  });\n\n  return fragment;\n}\n\nfunction jqLiteParseHTML(html, context) {\n  context = context || document;\n  var parsed;\n\n  if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {\n    return [context.createElement(parsed[1])];\n  }\n\n  if ((parsed = jqLiteBuildFragment(html, context))) {\n    return parsed.childNodes;\n  }\n\n  return [];\n}\n\n/////////////////////////////////////////////\nfunction JQLite(element) {\n  if (element instanceof JQLite) {\n    return element;\n  }\n\n  var argIsString;\n\n  if (isString(element)) {\n    element = trim(element);\n    argIsString = true;\n  }\n  if (!(this instanceof JQLite)) {\n    if (argIsString && element.charAt(0) != '<') {\n      throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');\n    }\n    return new JQLite(element);\n  }\n\n  if (argIsString) {\n    jqLiteAddNodes(this, jqLiteParseHTML(element));\n  } else {\n    jqLiteAddNodes(this, element);\n  }\n}\n\nfunction jqLiteClone(element) {\n  return element.cloneNode(true);\n}\n\nfunction jqLiteDealoc(element, onlyDescendants) {\n  if (!onlyDescendants) jqLiteRemoveData(element);\n\n  if (element.querySelectorAll) {\n    var descendants = element.querySelectorAll('*');\n    for (var i = 0, l = descendants.length; i < l; i++) {\n      jqLiteRemoveData(descendants[i]);\n    }\n  }\n}\n\nfunction jqLiteOff(element, type, fn, unsupported) {\n  if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');\n\n  var expandoStore = jqLiteExpandoStore(element);\n  var events = expandoStore && expandoStore.events;\n  var handle = expandoStore && expandoStore.handle;\n\n  if (!handle) return; //no listeners registered\n\n  if (!type) {\n    for (type in events) {\n      if (type !== '$destroy') {\n        removeEventListenerFn(element, type, handle);\n      }\n      delete events[type];\n    }\n  } else {\n    forEach(type.split(' '), function(type) {\n      if (isDefined(fn)) {\n        var listenerFns = events[type];\n        arrayRemove(listenerFns || [], fn);\n        if (listenerFns && listenerFns.length > 0) {\n          return;\n        }\n      }\n\n      removeEventListenerFn(element, type, handle);\n      delete events[type];\n    });\n  }\n}\n\nfunction jqLiteRemoveData(element, name) {\n  var expandoId = element.ng339;\n  var expandoStore = expandoId && jqCache[expandoId];\n\n  if (expandoStore) {\n    if (name) {\n      delete expandoStore.data[name];\n      return;\n    }\n\n    if (expandoStore.handle) {\n      if (expandoStore.events.$destroy) {\n        expandoStore.handle({}, '$destroy');\n      }\n      jqLiteOff(element);\n    }\n    delete jqCache[expandoId];\n    element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it\n  }\n}\n\n\nfunction jqLiteExpandoStore(element, createIfNecessary) {\n  var expandoId = element.ng339,\n      expandoStore = expandoId && jqCache[expandoId];\n\n  if (createIfNecessary && !expandoStore) {\n    element.ng339 = expandoId = jqNextId();\n    expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};\n  }\n\n  return expandoStore;\n}\n\n\nfunction jqLiteData(element, key, value) {\n  if (jqLiteAcceptsData(element)) {\n\n    var isSimpleSetter = isDefined(value);\n    var isSimpleGetter = !isSimpleSetter && key && !isObject(key);\n    var massGetter = !key;\n    var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);\n    var data = expandoStore && expandoStore.data;\n\n    if (isSimpleSetter) { // data('key', value)\n      data[key] = value;\n    } else {\n      if (massGetter) {  // data()\n        return data;\n      } else {\n        if (isSimpleGetter) { // data('key')\n          // don't force creation of expandoStore if it doesn't exist yet\n          return data && data[key];\n        } else { // mass-setter: data({key1: val1, key2: val2})\n          extend(data, key);\n        }\n      }\n    }\n  }\n}\n\nfunction jqLiteHasClass(element, selector) {\n  if (!element.getAttribute) return false;\n  return ((\" \" + (element.getAttribute('class') || '') + \" \").replace(/[\\n\\t]/g, \" \").\n      indexOf(\" \" + selector + \" \") > -1);\n}\n\nfunction jqLiteRemoveClass(element, cssClasses) {\n  if (cssClasses && element.setAttribute) {\n    forEach(cssClasses.split(' '), function(cssClass) {\n      element.setAttribute('class', trim(\n          (\" \" + (element.getAttribute('class') || '') + \" \")\n          .replace(/[\\n\\t]/g, \" \")\n          .replace(\" \" + trim(cssClass) + \" \", \" \"))\n      );\n    });\n  }\n}\n\nfunction jqLiteAddClass(element, cssClasses) {\n  if (cssClasses && element.setAttribute) {\n    var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')\n                            .replace(/[\\n\\t]/g, \" \");\n\n    forEach(cssClasses.split(' '), function(cssClass) {\n      cssClass = trim(cssClass);\n      if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {\n        existingClasses += cssClass + ' ';\n      }\n    });\n\n    element.setAttribute('class', trim(existingClasses));\n  }\n}\n\n\nfunction jqLiteAddNodes(root, elements) {\n  // THIS CODE IS VERY HOT. Don't make changes without benchmarking.\n\n  if (elements) {\n\n    // if a Node (the most common case)\n    if (elements.nodeType) {\n      root[root.length++] = elements;\n    } else {\n      var length = elements.length;\n\n      // if an Array or NodeList and not a Window\n      if (typeof length === 'number' && elements.window !== elements) {\n        if (length) {\n          for (var i = 0; i < length; i++) {\n            root[root.length++] = elements[i];\n          }\n        }\n      } else {\n        root[root.length++] = elements;\n      }\n    }\n  }\n}\n\n\nfunction jqLiteController(element, name) {\n  return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');\n}\n\nfunction jqLiteInheritedData(element, name, value) {\n  // if element is the document object work with the html element instead\n  // this makes $(document).scope() possible\n  if (element.nodeType == NODE_TYPE_DOCUMENT) {\n    element = element.documentElement;\n  }\n  var names = isArray(name) ? name : [name];\n\n  while (element) {\n    for (var i = 0, ii = names.length; i < ii; i++) {\n      if ((value = jqLite.data(element, names[i])) !== undefined) return value;\n    }\n\n    // If dealing with a document fragment node with a host element, and no parent, use the host\n    // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM\n    // to lookup parent controllers.\n    element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);\n  }\n}\n\nfunction jqLiteEmpty(element) {\n  jqLiteDealoc(element, true);\n  while (element.firstChild) {\n    element.removeChild(element.firstChild);\n  }\n}\n\nfunction jqLiteRemove(element, keepData) {\n  if (!keepData) jqLiteDealoc(element);\n  var parent = element.parentNode;\n  if (parent) parent.removeChild(element);\n}\n\n\nfunction jqLiteDocumentLoaded(action, win) {\n  win = win || window;\n  if (win.document.readyState === 'complete') {\n    // Force the action to be run async for consistent behaviour\n    // from the action's point of view\n    // i.e. it will definitely not be in a $apply\n    win.setTimeout(action);\n  } else {\n    // No need to unbind this handler as load is only ever called once\n    jqLite(win).on('load', action);\n  }\n}\n\n//////////////////////////////////////////\n// Functions which are declared directly.\n//////////////////////////////////////////\nvar JQLitePrototype = JQLite.prototype = {\n  ready: function(fn) {\n    var fired = false;\n\n    function trigger() {\n      if (fired) return;\n      fired = true;\n      fn();\n    }\n\n    // check if document is already loaded\n    if (document.readyState === 'complete') {\n      setTimeout(trigger);\n    } else {\n      this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9\n      // we can not use jqLite since we are not done loading and jQuery could be loaded later.\n      // jshint -W064\n      JQLite(window).on('load', trigger); // fallback to window.onload for others\n      // jshint +W064\n    }\n  },\n  toString: function() {\n    var value = [];\n    forEach(this, function(e) { value.push('' + e);});\n    return '[' + value.join(', ') + ']';\n  },\n\n  eq: function(index) {\n      return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);\n  },\n\n  length: 0,\n  push: push,\n  sort: [].sort,\n  splice: [].splice\n};\n\n//////////////////////////////////////////\n// Functions iterating getter/setters.\n// these functions return self on setter and\n// value on get.\n//////////////////////////////////////////\nvar BOOLEAN_ATTR = {};\nforEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {\n  BOOLEAN_ATTR[lowercase(value)] = value;\n});\nvar BOOLEAN_ELEMENTS = {};\nforEach('input,select,option,textarea,button,form,details'.split(','), function(value) {\n  BOOLEAN_ELEMENTS[value] = true;\n});\nvar ALIASED_ATTR = {\n  'ngMinlength': 'minlength',\n  'ngMaxlength': 'maxlength',\n  'ngMin': 'min',\n  'ngMax': 'max',\n  'ngPattern': 'pattern'\n};\n\nfunction getBooleanAttrName(element, name) {\n  // check dom last since we will most likely fail on name\n  var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];\n\n  // booleanAttr is here twice to minimize DOM access\n  return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;\n}\n\nfunction getAliasedAttrName(element, name) {\n  var nodeName = element.nodeName;\n  return (nodeName === 'INPUT' || nodeName === 'TEXTAREA') && ALIASED_ATTR[name];\n}\n\nforEach({\n  data: jqLiteData,\n  removeData: jqLiteRemoveData\n}, function(fn, name) {\n  JQLite[name] = fn;\n});\n\nforEach({\n  data: jqLiteData,\n  inheritedData: jqLiteInheritedData,\n\n  scope: function(element) {\n    // Can't use jqLiteData here directly so we stay compatible with jQuery!\n    return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);\n  },\n\n  isolateScope: function(element) {\n    // Can't use jqLiteData here directly so we stay compatible with jQuery!\n    return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');\n  },\n\n  controller: jqLiteController,\n\n  injector: function(element) {\n    return jqLiteInheritedData(element, '$injector');\n  },\n\n  removeAttr: function(element, name) {\n    element.removeAttribute(name);\n  },\n\n  hasClass: jqLiteHasClass,\n\n  css: function(element, name, value) {\n    name = camelCase(name);\n\n    if (isDefined(value)) {\n      element.style[name] = value;\n    } else {\n      return element.style[name];\n    }\n  },\n\n  attr: function(element, name, value) {\n    var lowercasedName = lowercase(name);\n    if (BOOLEAN_ATTR[lowercasedName]) {\n      if (isDefined(value)) {\n        if (!!value) {\n          element[name] = true;\n          element.setAttribute(name, lowercasedName);\n        } else {\n          element[name] = false;\n          element.removeAttribute(lowercasedName);\n        }\n      } else {\n        return (element[name] ||\n                 (element.attributes.getNamedItem(name) || noop).specified)\n               ? lowercasedName\n               : undefined;\n      }\n    } else if (isDefined(value)) {\n      element.setAttribute(name, value);\n    } else if (element.getAttribute) {\n      // the extra argument \"2\" is to get the right thing for a.href in IE, see jQuery code\n      // some elements (e.g. Document) don't have get attribute, so return undefined\n      var ret = element.getAttribute(name, 2);\n      // normalize non-existing attributes to undefined (as jQuery)\n      return ret === null ? undefined : ret;\n    }\n  },\n\n  prop: function(element, name, value) {\n    if (isDefined(value)) {\n      element[name] = value;\n    } else {\n      return element[name];\n    }\n  },\n\n  text: (function() {\n    getText.$dv = '';\n    return getText;\n\n    function getText(element, value) {\n      if (isUndefined(value)) {\n        var nodeType = element.nodeType;\n        return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';\n      }\n      element.textContent = value;\n    }\n  })(),\n\n  val: function(element, value) {\n    if (isUndefined(value)) {\n      if (element.multiple && nodeName_(element) === 'select') {\n        var result = [];\n        forEach(element.options, function(option) {\n          if (option.selected) {\n            result.push(option.value || option.text);\n          }\n        });\n        return result.length === 0 ? null : result;\n      }\n      return element.value;\n    }\n    element.value = value;\n  },\n\n  html: function(element, value) {\n    if (isUndefined(value)) {\n      return element.innerHTML;\n    }\n    jqLiteDealoc(element, true);\n    element.innerHTML = value;\n  },\n\n  empty: jqLiteEmpty\n}, function(fn, name) {\n  /**\n   * Properties: writes return selection, reads return first value\n   */\n  JQLite.prototype[name] = function(arg1, arg2) {\n    var i, key;\n    var nodeCount = this.length;\n\n    // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it\n    // in a way that survives minification.\n    // jqLiteEmpty takes no arguments but is a setter.\n    if (fn !== jqLiteEmpty &&\n        (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) {\n      if (isObject(arg1)) {\n\n        // we are a write, but the object properties are the key/values\n        for (i = 0; i < nodeCount; i++) {\n          if (fn === jqLiteData) {\n            // data() takes the whole object in jQuery\n            fn(this[i], arg1);\n          } else {\n            for (key in arg1) {\n              fn(this[i], key, arg1[key]);\n            }\n          }\n        }\n        // return self for chaining\n        return this;\n      } else {\n        // we are a read, so read the first child.\n        // TODO: do we still need this?\n        var value = fn.$dv;\n        // Only if we have $dv do we iterate over all, otherwise it is just the first element.\n        var jj = (value === undefined) ? Math.min(nodeCount, 1) : nodeCount;\n        for (var j = 0; j < jj; j++) {\n          var nodeValue = fn(this[j], arg1, arg2);\n          value = value ? value + nodeValue : nodeValue;\n        }\n        return value;\n      }\n    } else {\n      // we are a write, so apply to all children\n      for (i = 0; i < nodeCount; i++) {\n        fn(this[i], arg1, arg2);\n      }\n      // return self for chaining\n      return this;\n    }\n  };\n});\n\nfunction createEventHandler(element, events) {\n  var eventHandler = function(event, type) {\n    // jQuery specific api\n    event.isDefaultPrevented = function() {\n      return event.defaultPrevented;\n    };\n\n    var eventFns = events[type || event.type];\n    var eventFnsLength = eventFns ? eventFns.length : 0;\n\n    if (!eventFnsLength) return;\n\n    if (isUndefined(event.immediatePropagationStopped)) {\n      var originalStopImmediatePropagation = event.stopImmediatePropagation;\n      event.stopImmediatePropagation = function() {\n        event.immediatePropagationStopped = true;\n\n        if (event.stopPropagation) {\n          event.stopPropagation();\n        }\n\n        if (originalStopImmediatePropagation) {\n          originalStopImmediatePropagation.call(event);\n        }\n      };\n    }\n\n    event.isImmediatePropagationStopped = function() {\n      return event.immediatePropagationStopped === true;\n    };\n\n    // Copy event handlers in case event handlers array is modified during execution.\n    if ((eventFnsLength > 1)) {\n      eventFns = shallowCopy(eventFns);\n    }\n\n    for (var i = 0; i < eventFnsLength; i++) {\n      if (!event.isImmediatePropagationStopped()) {\n        eventFns[i].call(element, event);\n      }\n    }\n  };\n\n  // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all\n  //       events on `element`\n  eventHandler.elem = element;\n  return eventHandler;\n}\n\n//////////////////////////////////////////\n// Functions iterating traversal.\n// These functions chain results into a single\n// selector.\n//////////////////////////////////////////\nforEach({\n  removeData: jqLiteRemoveData,\n\n  on: function jqLiteOn(element, type, fn, unsupported) {\n    if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');\n\n    // Do not add event handlers to non-elements because they will not be cleaned up.\n    if (!jqLiteAcceptsData(element)) {\n      return;\n    }\n\n    var expandoStore = jqLiteExpandoStore(element, true);\n    var events = expandoStore.events;\n    var handle = expandoStore.handle;\n\n    if (!handle) {\n      handle = expandoStore.handle = createEventHandler(element, events);\n    }\n\n    // http://jsperf.com/string-indexof-vs-split\n    var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];\n    var i = types.length;\n\n    while (i--) {\n      type = types[i];\n      var eventFns = events[type];\n\n      if (!eventFns) {\n        events[type] = [];\n\n        if (type === 'mouseenter' || type === 'mouseleave') {\n          // Refer to jQuery's implementation of mouseenter & mouseleave\n          // Read about mouseenter and mouseleave:\n          // http://www.quirksmode.org/js/events_mouse.html#link8\n\n          jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) {\n            var target = this, related = event.relatedTarget;\n            // For mousenter/leave call the handler if related is outside the target.\n            // NB: No relatedTarget if the mouse left/entered the browser window\n            if (!related || (related !== target && !target.contains(related))) {\n              handle(event, type);\n            }\n          });\n\n        } else {\n          if (type !== '$destroy') {\n            addEventListenerFn(element, type, handle);\n          }\n        }\n        eventFns = events[type];\n      }\n      eventFns.push(fn);\n    }\n  },\n\n  off: jqLiteOff,\n\n  one: function(element, type, fn) {\n    element = jqLite(element);\n\n    //add the listener twice so that when it is called\n    //you can remove the original function and still be\n    //able to call element.off(ev, fn) normally\n    element.on(type, function onFn() {\n      element.off(type, fn);\n      element.off(type, onFn);\n    });\n    element.on(type, fn);\n  },\n\n  replaceWith: function(element, replaceNode) {\n    var index, parent = element.parentNode;\n    jqLiteDealoc(element);\n    forEach(new JQLite(replaceNode), function(node) {\n      if (index) {\n        parent.insertBefore(node, index.nextSibling);\n      } else {\n        parent.replaceChild(node, element);\n      }\n      index = node;\n    });\n  },\n\n  children: function(element) {\n    var children = [];\n    forEach(element.childNodes, function(element) {\n      if (element.nodeType === NODE_TYPE_ELEMENT)\n        children.push(element);\n    });\n    return children;\n  },\n\n  contents: function(element) {\n    return element.contentDocument || element.childNodes || [];\n  },\n\n  append: function(element, node) {\n    var nodeType = element.nodeType;\n    if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;\n\n    node = new JQLite(node);\n\n    for (var i = 0, ii = node.length; i < ii; i++) {\n      var child = node[i];\n      element.appendChild(child);\n    }\n  },\n\n  prepend: function(element, node) {\n    if (element.nodeType === NODE_TYPE_ELEMENT) {\n      var index = element.firstChild;\n      forEach(new JQLite(node), function(child) {\n        element.insertBefore(child, index);\n      });\n    }\n  },\n\n  wrap: function(element, wrapNode) {\n    wrapNode = jqLite(wrapNode).eq(0).clone()[0];\n    var parent = element.parentNode;\n    if (parent) {\n      parent.replaceChild(wrapNode, element);\n    }\n    wrapNode.appendChild(element);\n  },\n\n  remove: jqLiteRemove,\n\n  detach: function(element) {\n    jqLiteRemove(element, true);\n  },\n\n  after: function(element, newElement) {\n    var index = element, parent = element.parentNode;\n    newElement = new JQLite(newElement);\n\n    for (var i = 0, ii = newElement.length; i < ii; i++) {\n      var node = newElement[i];\n      parent.insertBefore(node, index.nextSibling);\n      index = node;\n    }\n  },\n\n  addClass: jqLiteAddClass,\n  removeClass: jqLiteRemoveClass,\n\n  toggleClass: function(element, selector, condition) {\n    if (selector) {\n      forEach(selector.split(' '), function(className) {\n        var classCondition = condition;\n        if (isUndefined(classCondition)) {\n          classCondition = !jqLiteHasClass(element, className);\n        }\n        (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);\n      });\n    }\n  },\n\n  parent: function(element) {\n    var parent = element.parentNode;\n    return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;\n  },\n\n  next: function(element) {\n    return element.nextElementSibling;\n  },\n\n  find: function(element, selector) {\n    if (element.getElementsByTagName) {\n      return element.getElementsByTagName(selector);\n    } else {\n      return [];\n    }\n  },\n\n  clone: jqLiteClone,\n\n  triggerHandler: function(element, event, extraParameters) {\n\n    var dummyEvent, eventFnsCopy, handlerArgs;\n    var eventName = event.type || event;\n    var expandoStore = jqLiteExpandoStore(element);\n    var events = expandoStore && expandoStore.events;\n    var eventFns = events && events[eventName];\n\n    if (eventFns) {\n      // Create a dummy event to pass to the handlers\n      dummyEvent = {\n        preventDefault: function() { this.defaultPrevented = true; },\n        isDefaultPrevented: function() { return this.defaultPrevented === true; },\n        stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },\n        isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },\n        stopPropagation: noop,\n        type: eventName,\n        target: element\n      };\n\n      // If a custom event was provided then extend our dummy event with it\n      if (event.type) {\n        dummyEvent = extend(dummyEvent, event);\n      }\n\n      // Copy event handlers in case event handlers array is modified during execution.\n      eventFnsCopy = shallowCopy(eventFns);\n      handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];\n\n      forEach(eventFnsCopy, function(fn) {\n        if (!dummyEvent.isImmediatePropagationStopped()) {\n          fn.apply(element, handlerArgs);\n        }\n      });\n    }\n  }\n}, function(fn, name) {\n  /**\n   * chaining functions\n   */\n  JQLite.prototype[name] = function(arg1, arg2, arg3) {\n    var value;\n\n    for (var i = 0, ii = this.length; i < ii; i++) {\n      if (isUndefined(value)) {\n        value = fn(this[i], arg1, arg2, arg3);\n        if (isDefined(value)) {\n          // any function which returns a value needs to be wrapped\n          value = jqLite(value);\n        }\n      } else {\n        jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));\n      }\n    }\n    return isDefined(value) ? value : this;\n  };\n\n  // bind legacy bind/unbind to on/off\n  JQLite.prototype.bind = JQLite.prototype.on;\n  JQLite.prototype.unbind = JQLite.prototype.off;\n});\n\n\n// Provider for private $$jqLite service\nfunction $$jqLiteProvider() {\n  this.$get = function $$jqLite() {\n    return extend(JQLite, {\n      hasClass: function(node, classes) {\n        if (node.attr) node = node[0];\n        return jqLiteHasClass(node, classes);\n      },\n      addClass: function(node, classes) {\n        if (node.attr) node = node[0];\n        return jqLiteAddClass(node, classes);\n      },\n      removeClass: function(node, classes) {\n        if (node.attr) node = node[0];\n        return jqLiteRemoveClass(node, classes);\n      }\n    });\n  };\n}\n\n/**\n * Computes a hash of an 'obj'.\n * Hash of a:\n *  string is string\n *  number is number as string\n *  object is either result of calling $$hashKey function on the object or uniquely generated id,\n *         that is also assigned to the $$hashKey property of the object.\n *\n * @param obj\n * @returns {string} hash string such that the same input will have the same hash string.\n *         The resulting string key is in 'type:hashKey' format.\n */\nfunction hashKey(obj, nextUidFn) {\n  var key = obj && obj.$$hashKey;\n\n  if (key) {\n    if (typeof key === 'function') {\n      key = obj.$$hashKey();\n    }\n    return key;\n  }\n\n  var objType = typeof obj;\n  if (objType == 'function' || (objType == 'object' && obj !== null)) {\n    key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();\n  } else {\n    key = objType + ':' + obj;\n  }\n\n  return key;\n}\n\n/**\n * HashMap which can use objects as keys\n */\nfunction HashMap(array, isolatedUid) {\n  if (isolatedUid) {\n    var uid = 0;\n    this.nextUid = function() {\n      return ++uid;\n    };\n  }\n  forEach(array, this.put, this);\n}\nHashMap.prototype = {\n  /**\n   * Store key value pair\n   * @param key key to store can be any type\n   * @param value value to store can be any type\n   */\n  put: function(key, value) {\n    this[hashKey(key, this.nextUid)] = value;\n  },\n\n  /**\n   * @param key\n   * @returns {Object} the value for the key\n   */\n  get: function(key) {\n    return this[hashKey(key, this.nextUid)];\n  },\n\n  /**\n   * Remove the key/value pair\n   * @param key\n   */\n  remove: function(key) {\n    var value = this[key = hashKey(key, this.nextUid)];\n    delete this[key];\n    return value;\n  }\n};\n\n/**\n * @ngdoc function\n * @module ng\n * @name angular.injector\n * @kind function\n *\n * @description\n * Creates an injector object that can be used for retrieving services as well as for\n * dependency injection (see {@link guide/di dependency injection}).\n *\n * @param {Array.<string|Function>} modules A list of module functions or their aliases. See\n *     {@link angular.module}. The `ng` module must be explicitly added.\n * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which\n *     disallows argument name annotation inference.\n * @returns {injector} Injector object. See {@link auto.$injector $injector}.\n *\n * @example\n * Typical usage\n * ```js\n *   // create an injector\n *   var $injector = angular.injector(['ng']);\n *\n *   // use the injector to kick off your application\n *   // use the type inference to auto inject arguments, or use implicit injection\n *   $injector.invoke(function($rootScope, $compile, $document) {\n *     $compile($document)($rootScope);\n *     $rootScope.$digest();\n *   });\n * ```\n *\n * Sometimes you want to get access to the injector of a currently running Angular app\n * from outside Angular. Perhaps, you want to inject and compile some markup after the\n * application has been bootstrapped. You can do this using the extra `injector()` added\n * to JQuery/jqLite elements. See {@link angular.element}.\n *\n * *This is fairly rare but could be the case if a third party library is injecting the\n * markup.*\n *\n * In the following example a new block of HTML containing a `ng-controller`\n * directive is added to the end of the document body by JQuery. We then compile and link\n * it into the current AngularJS scope.\n *\n * ```js\n * var $div = $('<div ng-controller=\"MyCtrl\">{{content.label}}</div>');\n * $(document.body).append($div);\n *\n * angular.element(document).injector().invoke(function($compile) {\n *   var scope = angular.element($div).scope();\n *   $compile($div)(scope);\n * });\n * ```\n */\n\n\n/**\n * @ngdoc module\n * @name auto\n * @description\n *\n * Implicit module which gets automatically added to each {@link auto.$injector $injector}.\n */\n\nvar FN_ARGS = /^function\\s*[^\\(]*\\(\\s*([^\\)]*)\\)/m;\nvar FN_ARG_SPLIT = /,/;\nvar FN_ARG = /^\\s*(_?)(\\S+?)\\1\\s*$/;\nvar STRIP_COMMENTS = /((\\/\\/.*$)|(\\/\\*[\\s\\S]*?\\*\\/))/mg;\nvar $injectorMinErr = minErr('$injector');\n\nfunction anonFn(fn) {\n  // For anonymous functions, showing at the very least the function signature can help in\n  // debugging.\n  var fnText = fn.toString().replace(STRIP_COMMENTS, ''),\n      args = fnText.match(FN_ARGS);\n  if (args) {\n    return 'function(' + (args[1] || '').replace(/[\\s\\r\\n]+/, ' ') + ')';\n  }\n  return 'fn';\n}\n\nfunction annotate(fn, strictDi, name) {\n  var $inject,\n      fnText,\n      argDecl,\n      last;\n\n  if (typeof fn === 'function') {\n    if (!($inject = fn.$inject)) {\n      $inject = [];\n      if (fn.length) {\n        if (strictDi) {\n          if (!isString(name) || !name) {\n            name = fn.name || anonFn(fn);\n          }\n          throw $injectorMinErr('strictdi',\n            '{0} is not using explicit annotation and cannot be invoked in strict mode', name);\n        }\n        fnText = fn.toString().replace(STRIP_COMMENTS, '');\n        argDecl = fnText.match(FN_ARGS);\n        forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {\n          arg.replace(FN_ARG, function(all, underscore, name) {\n            $inject.push(name);\n          });\n        });\n      }\n      fn.$inject = $inject;\n    }\n  } else if (isArray(fn)) {\n    last = fn.length - 1;\n    assertArgFn(fn[last], 'fn');\n    $inject = fn.slice(0, last);\n  } else {\n    assertArgFn(fn, 'fn', true);\n  }\n  return $inject;\n}\n\n///////////////////////////////////////\n\n/**\n * @ngdoc service\n * @name $injector\n *\n * @description\n *\n * `$injector` is used to retrieve object instances as defined by\n * {@link auto.$provide provider}, instantiate types, invoke methods,\n * and load modules.\n *\n * The following always holds true:\n *\n * ```js\n *   var $injector = angular.injector();\n *   expect($injector.get('$injector')).toBe($injector);\n *   expect($injector.invoke(function($injector) {\n *     return $injector;\n *   })).toBe($injector);\n * ```\n *\n * # Injection Function Annotation\n *\n * JavaScript does not have annotations, and annotations are needed for dependency injection. The\n * following are all valid ways of annotating function with injection arguments and are equivalent.\n *\n * ```js\n *   // inferred (only works if code not minified/obfuscated)\n *   $injector.invoke(function(serviceA){});\n *\n *   // annotated\n *   function explicit(serviceA) {};\n *   explicit.$inject = ['serviceA'];\n *   $injector.invoke(explicit);\n *\n *   // inline\n *   $injector.invoke(['serviceA', function(serviceA){}]);\n * ```\n *\n * ## Inference\n *\n * In JavaScript calling `toString()` on a function returns the function definition. The definition\n * can then be parsed and the function arguments can be extracted. This method of discovering\n * annotations is disallowed when the injector is in strict mode.\n * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the\n * argument names.\n *\n * ## `$inject` Annotation\n * By adding an `$inject` property onto a function the injection parameters can be specified.\n *\n * ## Inline\n * As an array of injection names, where the last item in the array is the function to call.\n */\n\n/**\n * @ngdoc method\n * @name $injector#get\n *\n * @description\n * Return an instance of the service.\n *\n * @param {string} name The name of the instance to retrieve.\n * @param {string} caller An optional string to provide the origin of the function call for error messages.\n * @return {*} The instance.\n */\n\n/**\n * @ngdoc method\n * @name $injector#invoke\n *\n * @description\n * Invoke the method and supply the method arguments from the `$injector`.\n *\n * @param {!Function} fn The function to invoke. Function parameters are injected according to the\n *   {@link guide/di $inject Annotation} rules.\n * @param {Object=} self The `this` for the invoked method.\n * @param {Object=} locals Optional object. If preset then any argument names are read from this\n *                         object first, before the `$injector` is consulted.\n * @returns {*} the value returned by the invoked `fn` function.\n */\n\n/**\n * @ngdoc method\n * @name $injector#has\n *\n * @description\n * Allows the user to query if the particular service exists.\n *\n * @param {string} name Name of the service to query.\n * @returns {boolean} `true` if injector has given service.\n */\n\n/**\n * @ngdoc method\n * @name $injector#instantiate\n * @description\n * Create a new instance of JS type. The method takes a constructor function, invokes the new\n * operator, and supplies all of the arguments to the constructor function as specified by the\n * constructor annotation.\n *\n * @param {Function} Type Annotated constructor function.\n * @param {Object=} locals Optional object. If preset then any argument names are read from this\n * object first, before the `$injector` is consulted.\n * @returns {Object} new instance of `Type`.\n */\n\n/**\n * @ngdoc method\n * @name $injector#annotate\n *\n * @description\n * Returns an array of service names which the function is requesting for injection. This API is\n * used by the injector to determine which services need to be injected into the function when the\n * function is invoked. There are three ways in which the function can be annotated with the needed\n * dependencies.\n *\n * # Argument names\n *\n * The simplest form is to extract the dependencies from the arguments of the function. This is done\n * by converting the function into a string using `toString()` method and extracting the argument\n * names.\n * ```js\n *   // Given\n *   function MyController($scope, $route) {\n *     // ...\n *   }\n *\n *   // Then\n *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);\n * ```\n *\n * You can disallow this method by using strict injection mode.\n *\n * This method does not work with code minification / obfuscation. For this reason the following\n * annotation strategies are supported.\n *\n * # The `$inject` property\n *\n * If a function has an `$inject` property and its value is an array of strings, then the strings\n * represent names of services to be injected into the function.\n * ```js\n *   // Given\n *   var MyController = function(obfuscatedScope, obfuscatedRoute) {\n *     // ...\n *   }\n *   // Define function dependencies\n *   MyController['$inject'] = ['$scope', '$route'];\n *\n *   // Then\n *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);\n * ```\n *\n * # The array notation\n *\n * It is often desirable to inline Injected functions and that's when setting the `$inject` property\n * is very inconvenient. In these situations using the array notation to specify the dependencies in\n * a way that survives minification is a better choice:\n *\n * ```js\n *   // We wish to write this (not minification / obfuscation safe)\n *   injector.invoke(function($compile, $rootScope) {\n *     // ...\n *   });\n *\n *   // We are forced to write break inlining\n *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {\n *     // ...\n *   };\n *   tmpFn.$inject = ['$compile', '$rootScope'];\n *   injector.invoke(tmpFn);\n *\n *   // To better support inline function the inline annotation is supported\n *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {\n *     // ...\n *   }]);\n *\n *   // Therefore\n *   expect(injector.annotate(\n *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])\n *    ).toEqual(['$compile', '$rootScope']);\n * ```\n *\n * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to\n * be retrieved as described above.\n *\n * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.\n *\n * @returns {Array.<string>} The names of the services which the function requires.\n */\n\n\n\n\n/**\n * @ngdoc service\n * @name $provide\n *\n * @description\n *\n * The {@link auto.$provide $provide} service has a number of methods for registering components\n * with the {@link auto.$injector $injector}. Many of these functions are also exposed on\n * {@link angular.Module}.\n *\n * An Angular **service** is a singleton object created by a **service factory**.  These **service\n * factories** are functions which, in turn, are created by a **service provider**.\n * The **service providers** are constructor functions. When instantiated they must contain a\n * property called `$get`, which holds the **service factory** function.\n *\n * When you request a service, the {@link auto.$injector $injector} is responsible for finding the\n * correct **service provider**, instantiating it and then calling its `$get` **service factory**\n * function to get the instance of the **service**.\n *\n * Often services have no configuration options and there is no need to add methods to the service\n * provider.  The provider will be no more than a constructor function with a `$get` property. For\n * these cases the {@link auto.$provide $provide} service has additional helper methods to register\n * services without specifying a provider.\n *\n * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the\n *     {@link auto.$injector $injector}\n * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by\n *     providers and services.\n * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by\n *     services, not providers.\n * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,\n *     that will be wrapped in a **service provider** object, whose `$get` property will contain the\n *     given factory function.\n * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`\n *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate\n *      a new object using the given constructor function.\n *\n * See the individual methods for more information and examples.\n */\n\n/**\n * @ngdoc method\n * @name $provide#provider\n * @description\n *\n * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions\n * are constructor functions, whose instances are responsible for \"providing\" a factory for a\n * service.\n *\n * Service provider names start with the name of the service they provide followed by `Provider`.\n * For example, the {@link ng.$log $log} service has a provider called\n * {@link ng.$logProvider $logProvider}.\n *\n * Service provider objects can have additional methods which allow configuration of the provider\n * and its service. Importantly, you can configure what kind of service is created by the `$get`\n * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a\n * method {@link ng.$logProvider#debugEnabled debugEnabled}\n * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the\n * console or not.\n *\n * @param {string} name The name of the instance. NOTE: the provider will be available under `name +\n                        'Provider'` key.\n * @param {(Object|function())} provider If the provider is:\n *\n *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using\n *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.\n *   - `Constructor`: a new instance of the provider will be created using\n *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.\n *\n * @returns {Object} registered provider instance\n\n * @example\n *\n * The following example shows how to create a simple event tracking service and register it using\n * {@link auto.$provide#provider $provide.provider()}.\n *\n * ```js\n *  // Define the eventTracker provider\n *  function EventTrackerProvider() {\n *    var trackingUrl = '/track';\n *\n *    // A provider method for configuring where the tracked events should been saved\n *    this.setTrackingUrl = function(url) {\n *      trackingUrl = url;\n *    };\n *\n *    // The service factory function\n *    this.$get = ['$http', function($http) {\n *      var trackedEvents = {};\n *      return {\n *        // Call this to track an event\n *        event: function(event) {\n *          var count = trackedEvents[event] || 0;\n *          count += 1;\n *          trackedEvents[event] = count;\n *          return count;\n *        },\n *        // Call this to save the tracked events to the trackingUrl\n *        save: function() {\n *          $http.post(trackingUrl, trackedEvents);\n *        }\n *      };\n *    }];\n *  }\n *\n *  describe('eventTracker', function() {\n *    var postSpy;\n *\n *    beforeEach(module(function($provide) {\n *      // Register the eventTracker provider\n *      $provide.provider('eventTracker', EventTrackerProvider);\n *    }));\n *\n *    beforeEach(module(function(eventTrackerProvider) {\n *      // Configure eventTracker provider\n *      eventTrackerProvider.setTrackingUrl('/custom-track');\n *    }));\n *\n *    it('tracks events', inject(function(eventTracker) {\n *      expect(eventTracker.event('login')).toEqual(1);\n *      expect(eventTracker.event('login')).toEqual(2);\n *    }));\n *\n *    it('saves to the tracking url', inject(function(eventTracker, $http) {\n *      postSpy = spyOn($http, 'post');\n *      eventTracker.event('login');\n *      eventTracker.save();\n *      expect(postSpy).toHaveBeenCalled();\n *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');\n *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');\n *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });\n *    }));\n *  });\n * ```\n */\n\n/**\n * @ngdoc method\n * @name $provide#factory\n * @description\n *\n * Register a **service factory**, which will be called to return the service instance.\n * This is short for registering a service where its provider consists of only a `$get` property,\n * which is the given service factory function.\n * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to\n * configure your service in a provider.\n *\n * @param {string} name The name of the instance.\n * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand\n *                            for `$provide.provider(name, {$get: $getFn})`.\n * @returns {Object} registered provider instance\n *\n * @example\n * Here is an example of registering a service\n * ```js\n *   $provide.factory('ping', ['$http', function($http) {\n *     return function ping() {\n *       return $http.send('/ping');\n *     };\n *   }]);\n * ```\n * You would then inject and use this service like this:\n * ```js\n *   someModule.controller('Ctrl', ['ping', function(ping) {\n *     ping();\n *   }]);\n * ```\n */\n\n\n/**\n * @ngdoc method\n * @name $provide#service\n * @description\n *\n * Register a **service constructor**, which will be invoked with `new` to create the service\n * instance.\n * This is short for registering a service where its provider's `$get` property is the service\n * constructor function that will be used to instantiate the service instance.\n *\n * You should use {@link auto.$provide#service $provide.service(class)} if you define your service\n * as a type/class.\n *\n * @param {string} name The name of the instance.\n * @param {Function} constructor A class (constructor function) that will be instantiated.\n * @returns {Object} registered provider instance\n *\n * @example\n * Here is an example of registering a service using\n * {@link auto.$provide#service $provide.service(class)}.\n * ```js\n *   var Ping = function($http) {\n *     this.$http = $http;\n *   };\n *\n *   Ping.$inject = ['$http'];\n *\n *   Ping.prototype.send = function() {\n *     return this.$http.get('/ping');\n *   };\n *   $provide.service('ping', Ping);\n * ```\n * You would then inject and use this service like this:\n * ```js\n *   someModule.controller('Ctrl', ['ping', function(ping) {\n *     ping.send();\n *   }]);\n * ```\n */\n\n\n/**\n * @ngdoc method\n * @name $provide#value\n * @description\n *\n * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a\n * number, an array, an object or a function.  This is short for registering a service where its\n * provider's `$get` property is a factory function that takes no arguments and returns the **value\n * service**.\n *\n * Value services are similar to constant services, except that they cannot be injected into a\n * module configuration function (see {@link angular.Module#config}) but they can be overridden by\n * an Angular\n * {@link auto.$provide#decorator decorator}.\n *\n * @param {string} name The name of the instance.\n * @param {*} value The value.\n * @returns {Object} registered provider instance\n *\n * @example\n * Here are some examples of creating value services.\n * ```js\n *   $provide.value('ADMIN_USER', 'admin');\n *\n *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });\n *\n *   $provide.value('halfOf', function(value) {\n *     return value / 2;\n *   });\n * ```\n */\n\n\n/**\n * @ngdoc method\n * @name $provide#constant\n * @description\n *\n * Register a **constant service**, such as a string, a number, an array, an object or a function,\n * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be\n * injected into a module configuration function (see {@link angular.Module#config}) and it cannot\n * be overridden by an Angular {@link auto.$provide#decorator decorator}.\n *\n * @param {string} name The name of the constant.\n * @param {*} value The constant value.\n * @returns {Object} registered instance\n *\n * @example\n * Here a some examples of creating constants:\n * ```js\n *   $provide.constant('SHARD_HEIGHT', 306);\n *\n *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);\n *\n *   $provide.constant('double', function(value) {\n *     return value * 2;\n *   });\n * ```\n */\n\n\n/**\n * @ngdoc method\n * @name $provide#decorator\n * @description\n *\n * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator\n * intercepts the creation of a service, allowing it to override or modify the behaviour of the\n * service. The object returned by the decorator may be the original service, or a new service\n * object which replaces or wraps and delegates to the original service.\n *\n * @param {string} name The name of the service to decorate.\n * @param {function()} decorator This function will be invoked when the service needs to be\n *    instantiated and should return the decorated service instance. The function is called using\n *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.\n *    Local injection arguments:\n *\n *    * `$delegate` - The original service instance, which can be monkey patched, configured,\n *      decorated or delegated to.\n *\n * @example\n * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting\n * calls to {@link ng.$log#error $log.warn()}.\n * ```js\n *   $provide.decorator('$log', ['$delegate', function($delegate) {\n *     $delegate.warn = $delegate.error;\n *     return $delegate;\n *   }]);\n * ```\n */\n\n\nfunction createInjector(modulesToLoad, strictDi) {\n  strictDi = (strictDi === true);\n  var INSTANTIATING = {},\n      providerSuffix = 'Provider',\n      path = [],\n      loadedModules = new HashMap([], true),\n      providerCache = {\n        $provide: {\n            provider: supportObject(provider),\n            factory: supportObject(factory),\n            service: supportObject(service),\n            value: supportObject(value),\n            constant: supportObject(constant),\n            decorator: decorator\n          }\n      },\n      providerInjector = (providerCache.$injector =\n          createInternalInjector(providerCache, function(serviceName, caller) {\n            if (angular.isString(caller)) {\n              path.push(caller);\n            }\n            throw $injectorMinErr('unpr', \"Unknown provider: {0}\", path.join(' <- '));\n          })),\n      instanceCache = {},\n      instanceInjector = (instanceCache.$injector =\n          createInternalInjector(instanceCache, function(serviceName, caller) {\n            var provider = providerInjector.get(serviceName + providerSuffix, caller);\n            return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);\n          }));\n\n\n  forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });\n\n  return instanceInjector;\n\n  ////////////////////////////////////\n  // $provider\n  ////////////////////////////////////\n\n  function supportObject(delegate) {\n    return function(key, value) {\n      if (isObject(key)) {\n        forEach(key, reverseParams(delegate));\n      } else {\n        return delegate(key, value);\n      }\n    };\n  }\n\n  function provider(name, provider_) {\n    assertNotHasOwnProperty(name, 'service');\n    if (isFunction(provider_) || isArray(provider_)) {\n      provider_ = providerInjector.instantiate(provider_);\n    }\n    if (!provider_.$get) {\n      throw $injectorMinErr('pget', \"Provider '{0}' must define $get factory method.\", name);\n    }\n    return providerCache[name + providerSuffix] = provider_;\n  }\n\n  function enforceReturnValue(name, factory) {\n    return function enforcedReturnValue() {\n      var result = instanceInjector.invoke(factory, this);\n      if (isUndefined(result)) {\n        throw $injectorMinErr('undef', \"Provider '{0}' must return a value from $get factory method.\", name);\n      }\n      return result;\n    };\n  }\n\n  function factory(name, factoryFn, enforce) {\n    return provider(name, {\n      $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn\n    });\n  }\n\n  function service(name, constructor) {\n    return factory(name, ['$injector', function($injector) {\n      return $injector.instantiate(constructor);\n    }]);\n  }\n\n  function value(name, val) { return factory(name, valueFn(val), false); }\n\n  function constant(name, value) {\n    assertNotHasOwnProperty(name, 'constant');\n    providerCache[name] = value;\n    instanceCache[name] = value;\n  }\n\n  function decorator(serviceName, decorFn) {\n    var origProvider = providerInjector.get(serviceName + providerSuffix),\n        orig$get = origProvider.$get;\n\n    origProvider.$get = function() {\n      var origInstance = instanceInjector.invoke(orig$get, origProvider);\n      return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});\n    };\n  }\n\n  ////////////////////////////////////\n  // Module Loading\n  ////////////////////////////////////\n  function loadModules(modulesToLoad) {\n    var runBlocks = [], moduleFn;\n    forEach(modulesToLoad, function(module) {\n      if (loadedModules.get(module)) return;\n      loadedModules.put(module, true);\n\n      function runInvokeQueue(queue) {\n        var i, ii;\n        for (i = 0, ii = queue.length; i < ii; i++) {\n          var invokeArgs = queue[i],\n              provider = providerInjector.get(invokeArgs[0]);\n\n          provider[invokeArgs[1]].apply(provider, invokeArgs[2]);\n        }\n      }\n\n      try {\n        if (isString(module)) {\n          moduleFn = angularModule(module);\n          runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);\n          runInvokeQueue(moduleFn._invokeQueue);\n          runInvokeQueue(moduleFn._configBlocks);\n        } else if (isFunction(module)) {\n            runBlocks.push(providerInjector.invoke(module));\n        } else if (isArray(module)) {\n            runBlocks.push(providerInjector.invoke(module));\n        } else {\n          assertArgFn(module, 'module');\n        }\n      } catch (e) {\n        if (isArray(module)) {\n          module = module[module.length - 1];\n        }\n        if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {\n          // Safari & FF's stack traces don't contain error.message content\n          // unlike those of Chrome and IE\n          // So if stack doesn't contain message, we create a new string that contains both.\n          // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.\n          /* jshint -W022 */\n          e = e.message + '\\n' + e.stack;\n        }\n        throw $injectorMinErr('modulerr', \"Failed to instantiate module {0} due to:\\n{1}\",\n                  module, e.stack || e.message || e);\n      }\n    });\n    return runBlocks;\n  }\n\n  ////////////////////////////////////\n  // internal Injector\n  ////////////////////////////////////\n\n  function createInternalInjector(cache, factory) {\n\n    function getService(serviceName, caller) {\n      if (cache.hasOwnProperty(serviceName)) {\n        if (cache[serviceName] === INSTANTIATING) {\n          throw $injectorMinErr('cdep', 'Circular dependency found: {0}',\n                    serviceName + ' <- ' + path.join(' <- '));\n        }\n        return cache[serviceName];\n      } else {\n        try {\n          path.unshift(serviceName);\n          cache[serviceName] = INSTANTIATING;\n          return cache[serviceName] = factory(serviceName, caller);\n        } catch (err) {\n          if (cache[serviceName] === INSTANTIATING) {\n            delete cache[serviceName];\n          }\n          throw err;\n        } finally {\n          path.shift();\n        }\n      }\n    }\n\n    function invoke(fn, self, locals, serviceName) {\n      if (typeof locals === 'string') {\n        serviceName = locals;\n        locals = null;\n      }\n\n      var args = [],\n          $inject = createInjector.$$annotate(fn, strictDi, serviceName),\n          length, i,\n          key;\n\n      for (i = 0, length = $inject.length; i < length; i++) {\n        key = $inject[i];\n        if (typeof key !== 'string') {\n          throw $injectorMinErr('itkn',\n                  'Incorrect injection token! Expected service name as string, got {0}', key);\n        }\n        args.push(\n          locals && locals.hasOwnProperty(key)\n          ? locals[key]\n          : getService(key, serviceName)\n        );\n      }\n      if (isArray(fn)) {\n        fn = fn[length];\n      }\n\n      // http://jsperf.com/angularjs-invoke-apply-vs-switch\n      // #5388\n      return fn.apply(self, args);\n    }\n\n    function instantiate(Type, locals, serviceName) {\n      // Check if Type is annotated and use just the given function at n-1 as parameter\n      // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);\n      // Object creation: http://jsperf.com/create-constructor/2\n      var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);\n      var returnedValue = invoke(Type, instance, locals, serviceName);\n\n      return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;\n    }\n\n    return {\n      invoke: invoke,\n      instantiate: instantiate,\n      get: getService,\n      annotate: createInjector.$$annotate,\n      has: function(name) {\n        return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);\n      }\n    };\n  }\n}\n\ncreateInjector.$$annotate = annotate;\n\n/**\n * @ngdoc provider\n * @name $anchorScrollProvider\n *\n * @description\n * Use `$anchorScrollProvider` to disable automatic scrolling whenever\n * {@link ng.$location#hash $location.hash()} changes.\n */\nfunction $AnchorScrollProvider() {\n\n  var autoScrollingEnabled = true;\n\n  /**\n   * @ngdoc method\n   * @name $anchorScrollProvider#disableAutoScrolling\n   *\n   * @description\n   * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to\n   * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />\n   * Use this method to disable automatic scrolling.\n   *\n   * If automatic scrolling is disabled, one must explicitly call\n   * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the\n   * current hash.\n   */\n  this.disableAutoScrolling = function() {\n    autoScrollingEnabled = false;\n  };\n\n  /**\n   * @ngdoc service\n   * @name $anchorScroll\n   * @kind function\n   * @requires $window\n   * @requires $location\n   * @requires $rootScope\n   *\n   * @description\n   * When called, it checks the current value of {@link ng.$location#hash $location.hash()} and\n   * scrolls to the related element, according to the rules specified in the\n   * [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).\n   *\n   * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to\n   * match any anchor whenever it changes. This can be disabled by calling\n   * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.\n   *\n   * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a\n   * vertical scroll-offset (either fixed or dynamic).\n   *\n   * @property {(number|function|jqLite)} yOffset\n   * If set, specifies a vertical scroll-offset. This is often useful when there are fixed\n   * positioned elements at the top of the page, such as navbars, headers etc.\n   *\n   * `yOffset` can be specified in various ways:\n   * - **number**: A fixed number of pixels to be used as offset.<br /><br />\n   * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return\n   *   a number representing the offset (in pixels).<br /><br />\n   * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from\n   *   the top of the page to the element's bottom will be used as offset.<br />\n   *   **Note**: The element will be taken into account only as long as its `position` is set to\n   *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust\n   *   their height and/or positioning according to the viewport's size.\n   *\n   * <br />\n   * <div class=\"alert alert-warning\">\n   * In order for `yOffset` to work properly, scrolling should take place on the document's root and\n   * not some child element.\n   * </div>\n   *\n   * @example\n     <example module=\"anchorScrollExample\">\n       <file name=\"index.html\">\n         <div id=\"scrollArea\" ng-controller=\"ScrollController\">\n           <a ng-click=\"gotoBottom()\">Go to bottom</a>\n           <a id=\"bottom\"></a> You're at the bottom!\n         </div>\n       </file>\n       <file name=\"script.js\">\n         angular.module('anchorScrollExample', [])\n           .controller('ScrollController', ['$scope', '$location', '$anchorScroll',\n             function ($scope, $location, $anchorScroll) {\n               $scope.gotoBottom = function() {\n                 // set the location.hash to the id of\n                 // the element you wish to scroll to.\n                 $location.hash('bottom');\n\n                 // call $anchorScroll()\n                 $anchorScroll();\n               };\n             }]);\n       </file>\n       <file name=\"style.css\">\n         #scrollArea {\n           height: 280px;\n           overflow: auto;\n         }\n\n         #bottom {\n           display: block;\n           margin-top: 2000px;\n         }\n       </file>\n     </example>\n   *\n   * <hr />\n   * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).\n   * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.\n   *\n   * @example\n     <example module=\"anchorScrollOffsetExample\">\n       <file name=\"index.html\">\n         <div class=\"fixed-header\" ng-controller=\"headerCtrl\">\n           <a href=\"\" ng-click=\"gotoAnchor(x)\" ng-repeat=\"x in [1,2,3,4,5]\">\n             Go to anchor {{x}}\n           </a>\n         </div>\n         <div id=\"anchor{{x}}\" class=\"anchor\" ng-repeat=\"x in [1,2,3,4,5]\">\n           Anchor {{x}} of 5\n         </div>\n       </file>\n       <file name=\"script.js\">\n         angular.module('anchorScrollOffsetExample', [])\n           .run(['$anchorScroll', function($anchorScroll) {\n             $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels\n           }])\n           .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',\n             function ($anchorScroll, $location, $scope) {\n               $scope.gotoAnchor = function(x) {\n                 var newHash = 'anchor' + x;\n                 if ($location.hash() !== newHash) {\n                   // set the $location.hash to `newHash` and\n                   // $anchorScroll will automatically scroll to it\n                   $location.hash('anchor' + x);\n                 } else {\n                   // call $anchorScroll() explicitly,\n                   // since $location.hash hasn't changed\n                   $anchorScroll();\n                 }\n               };\n             }\n           ]);\n       </file>\n       <file name=\"style.css\">\n         body {\n           padding-top: 50px;\n         }\n\n         .anchor {\n           border: 2px dashed DarkOrchid;\n           padding: 10px 10px 200px 10px;\n         }\n\n         .fixed-header {\n           background-color: rgba(0, 0, 0, 0.2);\n           height: 50px;\n           position: fixed;\n           top: 0; left: 0; right: 0;\n         }\n\n         .fixed-header > a {\n           display: inline-block;\n           margin: 5px 15px;\n         }\n       </file>\n     </example>\n   */\n  this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {\n    var document = $window.document;\n\n    // Helper function to get first anchor from a NodeList\n    // (using `Array#some()` instead of `angular#forEach()` since it's more performant\n    //  and working in all supported browsers.)\n    function getFirstAnchor(list) {\n      var result = null;\n      Array.prototype.some.call(list, function(element) {\n        if (nodeName_(element) === 'a') {\n          result = element;\n          return true;\n        }\n      });\n      return result;\n    }\n\n    function getYOffset() {\n\n      var offset = scroll.yOffset;\n\n      if (isFunction(offset)) {\n        offset = offset();\n      } else if (isElement(offset)) {\n        var elem = offset[0];\n        var style = $window.getComputedStyle(elem);\n        if (style.position !== 'fixed') {\n          offset = 0;\n        } else {\n          offset = elem.getBoundingClientRect().bottom;\n        }\n      } else if (!isNumber(offset)) {\n        offset = 0;\n      }\n\n      return offset;\n    }\n\n    function scrollTo(elem) {\n      if (elem) {\n        elem.scrollIntoView();\n\n        var offset = getYOffset();\n\n        if (offset) {\n          // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.\n          // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the\n          // top of the viewport.\n          //\n          // IF the number of pixels from the top of `elem` to the end of the page's content is less\n          // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some\n          // way down the page.\n          //\n          // This is often the case for elements near the bottom of the page.\n          //\n          // In such cases we do not need to scroll the whole `offset` up, just the difference between\n          // the top of the element and the offset, which is enough to align the top of `elem` at the\n          // desired position.\n          var elemTop = elem.getBoundingClientRect().top;\n          $window.scrollBy(0, elemTop - offset);\n        }\n      } else {\n        $window.scrollTo(0, 0);\n      }\n    }\n\n    function scroll() {\n      var hash = $location.hash(), elm;\n\n      // empty hash, scroll to the top of the page\n      if (!hash) scrollTo(null);\n\n      // element with given id\n      else if ((elm = document.getElementById(hash))) scrollTo(elm);\n\n      // first anchor with given name :-D\n      else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);\n\n      // no element and hash == 'top', scroll to the top of the page\n      else if (hash === 'top') scrollTo(null);\n    }\n\n    // does not scroll when user clicks on anchor link that is currently on\n    // (no url change, no $location.hash() change), browser native does scroll\n    if (autoScrollingEnabled) {\n      $rootScope.$watch(function autoScrollWatch() {return $location.hash();},\n        function autoScrollWatchAction(newVal, oldVal) {\n          // skip the initial scroll if $location.hash is empty\n          if (newVal === oldVal && newVal === '') return;\n\n          jqLiteDocumentLoaded(function() {\n            $rootScope.$evalAsync(scroll);\n          });\n        });\n    }\n\n    return scroll;\n  }];\n}\n\nvar $animateMinErr = minErr('$animate');\n\n/**\n * @ngdoc provider\n * @name $animateProvider\n *\n * @description\n * Default implementation of $animate that doesn't perform any animations, instead just\n * synchronously performs DOM\n * updates and calls done() callbacks.\n *\n * In order to enable animations the ngAnimate module has to be loaded.\n *\n * To see the functional implementation check out src/ngAnimate/animate.js\n */\nvar $AnimateProvider = ['$provide', function($provide) {\n\n\n  this.$$selectors = {};\n\n\n  /**\n   * @ngdoc method\n   * @name $animateProvider#register\n   *\n   * @description\n   * Registers a new injectable animation factory function. The factory function produces the\n   * animation object which contains callback functions for each event that is expected to be\n   * animated.\n   *\n   *   * `eventFn`: `function(Element, doneFunction)` The element to animate, the `doneFunction`\n   *   must be called once the element animation is complete. If a function is returned then the\n   *   animation service will use this function to cancel the animation whenever a cancel event is\n   *   triggered.\n   *\n   *\n   * ```js\n   *   return {\n     *     eventFn : function(element, done) {\n     *       //code to run the animation\n     *       //once complete, then run done()\n     *       return function cancellationFunction() {\n     *         //code to cancel the animation\n     *       }\n     *     }\n     *   }\n   * ```\n   *\n   * @param {string} name The name of the animation.\n   * @param {Function} factory The factory function that will be executed to return the animation\n   *                           object.\n   */\n  this.register = function(name, factory) {\n    var key = name + '-animation';\n    if (name && name.charAt(0) != '.') throw $animateMinErr('notcsel',\n        \"Expecting class selector starting with '.' got '{0}'.\", name);\n    this.$$selectors[name.substr(1)] = key;\n    $provide.factory(key, factory);\n  };\n\n  /**\n   * @ngdoc method\n   * @name $animateProvider#classNameFilter\n   *\n   * @description\n   * Sets and/or returns the CSS class regular expression that is checked when performing\n   * an animation. Upon bootstrap the classNameFilter value is not set at all and will\n   * therefore enable $animate to attempt to perform an animation on any element.\n   * When setting the classNameFilter value, animations will only be performed on elements\n   * that successfully match the filter expression. This in turn can boost performance\n   * for low-powered devices as well as applications containing a lot of structural operations.\n   * @param {RegExp=} expression The className expression which will be checked against all animations\n   * @return {RegExp} The current CSS className expression value. If null then there is no expression value\n   */\n  this.classNameFilter = function(expression) {\n    if (arguments.length === 1) {\n      this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;\n    }\n    return this.$$classNameFilter;\n  };\n\n  this.$get = ['$$q', '$$asyncCallback', '$rootScope', function($$q, $$asyncCallback, $rootScope) {\n\n    var currentDefer;\n\n    function runAnimationPostDigest(fn) {\n      var cancelFn, defer = $$q.defer();\n      defer.promise.$$cancelFn = function ngAnimateMaybeCancel() {\n        cancelFn && cancelFn();\n      };\n\n      $rootScope.$$postDigest(function ngAnimatePostDigest() {\n        cancelFn = fn(function ngAnimateNotifyComplete() {\n          defer.resolve();\n        });\n      });\n\n      return defer.promise;\n    }\n\n    function resolveElementClasses(element, classes) {\n      var toAdd = [], toRemove = [];\n\n      var hasClasses = createMap();\n      forEach((element.attr('class') || '').split(/\\s+/), function(className) {\n        hasClasses[className] = true;\n      });\n\n      forEach(classes, function(status, className) {\n        var hasClass = hasClasses[className];\n\n        // If the most recent class manipulation (via $animate) was to remove the class, and the\n        // element currently has the class, the class is scheduled for removal. Otherwise, if\n        // the most recent class manipulation (via $animate) was to add the class, and the\n        // element does not currently have the class, the class is scheduled to be added.\n        if (status === false && hasClass) {\n          toRemove.push(className);\n        } else if (status === true && !hasClass) {\n          toAdd.push(className);\n        }\n      });\n\n      return (toAdd.length + toRemove.length) > 0 &&\n        [toAdd.length ? toAdd : null, toRemove.length ? toRemove : null];\n    }\n\n    function cachedClassManipulation(cache, classes, op) {\n      for (var i=0, ii = classes.length; i < ii; ++i) {\n        var className = classes[i];\n        cache[className] = op;\n      }\n    }\n\n    function asyncPromise() {\n      // only serve one instance of a promise in order to save CPU cycles\n      if (!currentDefer) {\n        currentDefer = $$q.defer();\n        $$asyncCallback(function() {\n          currentDefer.resolve();\n          currentDefer = null;\n        });\n      }\n      return currentDefer.promise;\n    }\n\n    function applyStyles(element, options) {\n      if (angular.isObject(options)) {\n        var styles = extend(options.from || {}, options.to || {});\n        element.css(styles);\n      }\n    }\n\n    /**\n     *\n     * @ngdoc service\n     * @name $animate\n     * @description The $animate service provides rudimentary DOM manipulation functions to\n     * insert, remove and move elements within the DOM, as well as adding and removing classes.\n     * This service is the core service used by the ngAnimate $animator service which provides\n     * high-level animation hooks for CSS and JavaScript.\n     *\n     * $animate is available in the AngularJS core, however, the ngAnimate module must be included\n     * to enable full out animation support. Otherwise, $animate will only perform simple DOM\n     * manipulation operations.\n     *\n     * To learn more about enabling animation support, click here to visit the {@link ngAnimate\n     * ngAnimate module page} as well as the {@link ngAnimate.$animate ngAnimate $animate service\n     * page}.\n     */\n    return {\n      animate: function(element, from, to) {\n        applyStyles(element, { from: from, to: to });\n        return asyncPromise();\n      },\n\n      /**\n       *\n       * @ngdoc method\n       * @name $animate#enter\n       * @kind function\n       * @description Inserts the element into the DOM either after the `after` element or\n       * as the first child within the `parent` element. When the function is called a promise\n       * is returned that will be resolved at a later time.\n       * @param {DOMElement} element the element which will be inserted into the DOM\n       * @param {DOMElement} parent the parent element which will append the element as\n       *   a child (if the after element is not present)\n       * @param {DOMElement} after the sibling element which will append the element\n       *   after itself\n       * @param {object=} options an optional collection of styles that will be applied to the element.\n       * @return {Promise} the animation callback promise\n       */\n      enter: function(element, parent, after, options) {\n        applyStyles(element, options);\n        after ? after.after(element)\n              : parent.prepend(element);\n        return asyncPromise();\n      },\n\n      /**\n       *\n       * @ngdoc method\n       * @name $animate#leave\n       * @kind function\n       * @description Removes the element from the DOM. When the function is called a promise\n       * is returned that will be resolved at a later time.\n       * @param {DOMElement} element the element which will be removed from the DOM\n       * @param {object=} options an optional collection of options that will be applied to the element.\n       * @return {Promise} the animation callback promise\n       */\n      leave: function(element, options) {\n        applyStyles(element, options);\n        element.remove();\n        return asyncPromise();\n      },\n\n      /**\n       *\n       * @ngdoc method\n       * @name $animate#move\n       * @kind function\n       * @description Moves the position of the provided element within the DOM to be placed\n       * either after the `after` element or inside of the `parent` element. When the function\n       * is called a promise is returned that will be resolved at a later time.\n       *\n       * @param {DOMElement} element the element which will be moved around within the\n       *   DOM\n       * @param {DOMElement} parent the parent element where the element will be\n       *   inserted into (if the after element is not present)\n       * @param {DOMElement} after the sibling element where the element will be\n       *   positioned next to\n       * @param {object=} options an optional collection of options that will be applied to the element.\n       * @return {Promise} the animation callback promise\n       */\n      move: function(element, parent, after, options) {\n        // Do not remove element before insert. Removing will cause data associated with the\n        // element to be dropped. Insert will implicitly do the remove.\n        return this.enter(element, parent, after, options);\n      },\n\n      /**\n       *\n       * @ngdoc method\n       * @name $animate#addClass\n       * @kind function\n       * @description Adds the provided className CSS class value to the provided element.\n       * When the function is called a promise is returned that will be resolved at a later time.\n       * @param {DOMElement} element the element which will have the className value\n       *   added to it\n       * @param {string} className the CSS class which will be added to the element\n       * @param {object=} options an optional collection of options that will be applied to the element.\n       * @return {Promise} the animation callback promise\n       */\n      addClass: function(element, className, options) {\n        return this.setClass(element, className, [], options);\n      },\n\n      $$addClassImmediately: function(element, className, options) {\n        element = jqLite(element);\n        className = !isString(className)\n                        ? (isArray(className) ? className.join(' ') : '')\n                        : className;\n        forEach(element, function(element) {\n          jqLiteAddClass(element, className);\n        });\n        applyStyles(element, options);\n        return asyncPromise();\n      },\n\n      /**\n       *\n       * @ngdoc method\n       * @name $animate#removeClass\n       * @kind function\n       * @description Removes the provided className CSS class value from the provided element.\n       * When the function is called a promise is returned that will be resolved at a later time.\n       * @param {DOMElement} element the element which will have the className value\n       *   removed from it\n       * @param {string} className the CSS class which will be removed from the element\n       * @param {object=} options an optional collection of options that will be applied to the element.\n       * @return {Promise} the animation callback promise\n       */\n      removeClass: function(element, className, options) {\n        return this.setClass(element, [], className, options);\n      },\n\n      $$removeClassImmediately: function(element, className, options) {\n        element = jqLite(element);\n        className = !isString(className)\n                        ? (isArray(className) ? className.join(' ') : '')\n                        : className;\n        forEach(element, function(element) {\n          jqLiteRemoveClass(element, className);\n        });\n        applyStyles(element, options);\n        return asyncPromise();\n      },\n\n      /**\n       *\n       * @ngdoc method\n       * @name $animate#setClass\n       * @kind function\n       * @description Adds and/or removes the given CSS classes to and from the element.\n       * When the function is called a promise is returned that will be resolved at a later time.\n       * @param {DOMElement} element the element which will have its CSS classes changed\n       *   removed from it\n       * @param {string} add the CSS classes which will be added to the element\n       * @param {string} remove the CSS class which will be removed from the element\n       * @param {object=} options an optional collection of options that will be applied to the element.\n       * @return {Promise} the animation callback promise\n       */\n      setClass: function(element, add, remove, options) {\n        var self = this;\n        var STORAGE_KEY = '$$animateClasses';\n        var createdCache = false;\n        element = jqLite(element);\n\n        var cache = element.data(STORAGE_KEY);\n        if (!cache) {\n          cache = {\n            classes: {},\n            options: options\n          };\n          createdCache = true;\n        } else if (options && cache.options) {\n          cache.options = angular.extend(cache.options || {}, options);\n        }\n\n        var classes = cache.classes;\n\n        add = isArray(add) ? add : add.split(' ');\n        remove = isArray(remove) ? remove : remove.split(' ');\n        cachedClassManipulation(classes, add, true);\n        cachedClassManipulation(classes, remove, false);\n\n        if (createdCache) {\n          cache.promise = runAnimationPostDigest(function(done) {\n            var cache = element.data(STORAGE_KEY);\n            element.removeData(STORAGE_KEY);\n\n            // in the event that the element is removed before postDigest\n            // is run then the cache will be undefined and there will be\n            // no need anymore to add or remove and of the element classes\n            if (cache) {\n              var classes = resolveElementClasses(element, cache.classes);\n              if (classes) {\n                self.$$setClassImmediately(element, classes[0], classes[1], cache.options);\n              }\n            }\n\n            done();\n          });\n          element.data(STORAGE_KEY, cache);\n        }\n\n        return cache.promise;\n      },\n\n      $$setClassImmediately: function(element, add, remove, options) {\n        add && this.$$addClassImmediately(element, add);\n        remove && this.$$removeClassImmediately(element, remove);\n        applyStyles(element, options);\n        return asyncPromise();\n      },\n\n      enabled: noop,\n      cancel: noop\n    };\n  }];\n}];\n\nfunction $$AsyncCallbackProvider() {\n  this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) {\n    return $$rAF.supported\n      ? function(fn) { return $$rAF(fn); }\n      : function(fn) {\n        return $timeout(fn, 0, false);\n      };\n  }];\n}\n\n/* global stripHash: true */\n\n/**\n * ! This is a private undocumented service !\n *\n * @name $browser\n * @requires $log\n * @description\n * This object has two goals:\n *\n * - hide all the global state in the browser caused by the window object\n * - abstract away all the browser specific features and inconsistencies\n *\n * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`\n * service, which can be used for convenient testing of the application without the interaction with\n * the real browser apis.\n */\n/**\n * @param {object} window The global window object.\n * @param {object} document jQuery wrapped document.\n * @param {object} $log window.console or an object with the same interface.\n * @param {object} $sniffer $sniffer service\n */\nfunction Browser(window, document, $log, $sniffer) {\n  var self = this,\n      rawDocument = document[0],\n      location = window.location,\n      history = window.history,\n      setTimeout = window.setTimeout,\n      clearTimeout = window.clearTimeout,\n      pendingDeferIds = {};\n\n  self.isMock = false;\n\n  var outstandingRequestCount = 0;\n  var outstandingRequestCallbacks = [];\n\n  // TODO(vojta): remove this temporary api\n  self.$$completeOutstandingRequest = completeOutstandingRequest;\n  self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };\n\n  /**\n   * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`\n   * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.\n   */\n  function completeOutstandingRequest(fn) {\n    try {\n      fn.apply(null, sliceArgs(arguments, 1));\n    } finally {\n      outstandingRequestCount--;\n      if (outstandingRequestCount === 0) {\n        while (outstandingRequestCallbacks.length) {\n          try {\n            outstandingRequestCallbacks.pop()();\n          } catch (e) {\n            $log.error(e);\n          }\n        }\n      }\n    }\n  }\n\n  function getHash(url) {\n    var index = url.indexOf('#');\n    return index === -1 ? '' : url.substr(index + 1);\n  }\n\n  /**\n   * @private\n   * Note: this method is used only by scenario runner\n   * TODO(vojta): prefix this method with $$ ?\n   * @param {function()} callback Function that will be called when no outstanding request\n   */\n  self.notifyWhenNoOutstandingRequests = function(callback) {\n    // force browser to execute all pollFns - this is needed so that cookies and other pollers fire\n    // at some deterministic time in respect to the test runner's actions. Leaving things up to the\n    // regular poller would result in flaky tests.\n    forEach(pollFns, function(pollFn) { pollFn(); });\n\n    if (outstandingRequestCount === 0) {\n      callback();\n    } else {\n      outstandingRequestCallbacks.push(callback);\n    }\n  };\n\n  //////////////////////////////////////////////////////////////\n  // Poll Watcher API\n  //////////////////////////////////////////////////////////////\n  var pollFns = [],\n      pollTimeout;\n\n  /**\n   * @name $browser#addPollFn\n   *\n   * @param {function()} fn Poll function to add\n   *\n   * @description\n   * Adds a function to the list of functions that poller periodically executes,\n   * and starts polling if not started yet.\n   *\n   * @returns {function()} the added function\n   */\n  self.addPollFn = function(fn) {\n    if (isUndefined(pollTimeout)) startPoller(100, setTimeout);\n    pollFns.push(fn);\n    return fn;\n  };\n\n  /**\n   * @param {number} interval How often should browser call poll functions (ms)\n   * @param {function()} setTimeout Reference to a real or fake `setTimeout` function.\n   *\n   * @description\n   * Configures the poller to run in the specified intervals, using the specified\n   * setTimeout fn and kicks it off.\n   */\n  function startPoller(interval, setTimeout) {\n    (function check() {\n      forEach(pollFns, function(pollFn) { pollFn(); });\n      pollTimeout = setTimeout(check, interval);\n    })();\n  }\n\n  //////////////////////////////////////////////////////////////\n  // URL API\n  //////////////////////////////////////////////////////////////\n\n  var cachedState, lastHistoryState,\n      lastBrowserUrl = location.href,\n      baseElement = document.find('base'),\n      reloadLocation = null;\n\n  cacheState();\n  lastHistoryState = cachedState;\n\n  /**\n   * @name $browser#url\n   *\n   * @description\n   * GETTER:\n   * Without any argument, this method just returns current value of location.href.\n   *\n   * SETTER:\n   * With at least one argument, this method sets url to new value.\n   * If html5 history api supported, pushState/replaceState is used, otherwise\n   * location.href/location.replace is used.\n   * Returns its own instance to allow chaining\n   *\n   * NOTE: this api is intended for use only by the $location service. Please use the\n   * {@link ng.$location $location service} to change url.\n   *\n   * @param {string} url New url (when used as setter)\n   * @param {boolean=} replace Should new url replace current history record?\n   * @param {object=} state object to use with pushState/replaceState\n   */\n  self.url = function(url, replace, state) {\n    // In modern browsers `history.state` is `null` by default; treating it separately\n    // from `undefined` would cause `$browser.url('/foo')` to change `history.state`\n    // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.\n    if (isUndefined(state)) {\n      state = null;\n    }\n\n    // Android Browser BFCache causes location, history reference to become stale.\n    if (location !== window.location) location = window.location;\n    if (history !== window.history) history = window.history;\n\n    // setter\n    if (url) {\n      var sameState = lastHistoryState === state;\n\n      // Don't change anything if previous and current URLs and states match. This also prevents\n      // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.\n      // See https://github.com/angular/angular.js/commit/ffb2701\n      if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {\n        return self;\n      }\n      var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);\n      lastBrowserUrl = url;\n      lastHistoryState = state;\n      // Don't use history API if only the hash changed\n      // due to a bug in IE10/IE11 which leads\n      // to not firing a `hashchange` nor `popstate` event\n      // in some cases (see #9143).\n      if ($sniffer.history && (!sameBase || !sameState)) {\n        history[replace ? 'replaceState' : 'pushState'](state, '', url);\n        cacheState();\n        // Do the assignment again so that those two variables are referentially identical.\n        lastHistoryState = cachedState;\n      } else {\n        if (!sameBase) {\n          reloadLocation = url;\n        }\n        if (replace) {\n          location.replace(url);\n        } else if (!sameBase) {\n          location.href = url;\n        } else {\n          location.hash = getHash(url);\n        }\n      }\n      return self;\n    // getter\n    } else {\n      // - reloadLocation is needed as browsers don't allow to read out\n      //   the new location.href if a reload happened.\n      // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172\n      return reloadLocation || location.href.replace(/%27/g,\"'\");\n    }\n  };\n\n  /**\n   * @name $browser#state\n   *\n   * @description\n   * This method is a getter.\n   *\n   * Return history.state or null if history.state is undefined.\n   *\n   * @returns {object} state\n   */\n  self.state = function() {\n    return cachedState;\n  };\n\n  var urlChangeListeners = [],\n      urlChangeInit = false;\n\n  function cacheStateAndFireUrlChange() {\n    cacheState();\n    fireUrlChange();\n  }\n\n  function getCurrentState() {\n    try {\n      return history.state;\n    } catch (e) {\n      // MSIE can reportedly throw when there is no state (UNCONFIRMED).\n    }\n  }\n\n  // This variable should be used *only* inside the cacheState function.\n  var lastCachedState = null;\n  function cacheState() {\n    // This should be the only place in $browser where `history.state` is read.\n    cachedState = getCurrentState();\n    cachedState = isUndefined(cachedState) ? null : cachedState;\n\n    // Prevent callbacks fo fire twice if both hashchange & popstate were fired.\n    if (equals(cachedState, lastCachedState)) {\n      cachedState = lastCachedState;\n    }\n    lastCachedState = cachedState;\n  }\n\n  function fireUrlChange() {\n    if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {\n      return;\n    }\n\n    lastBrowserUrl = self.url();\n    lastHistoryState = cachedState;\n    forEach(urlChangeListeners, function(listener) {\n      listener(self.url(), cachedState);\n    });\n  }\n\n  /**\n   * @name $browser#onUrlChange\n   *\n   * @description\n   * Register callback function that will be called, when url changes.\n   *\n   * It's only called when the url is changed from outside of angular:\n   * - user types different url into address bar\n   * - user clicks on history (forward/back) button\n   * - user clicks on a link\n   *\n   * It's not called when url is changed by $browser.url() method\n   *\n   * The listener gets called with new url as parameter.\n   *\n   * NOTE: this api is intended for use only by the $location service. Please use the\n   * {@link ng.$location $location service} to monitor url changes in angular apps.\n   *\n   * @param {function(string)} listener Listener function to be called when url changes.\n   * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.\n   */\n  self.onUrlChange = function(callback) {\n    // TODO(vojta): refactor to use node's syntax for events\n    if (!urlChangeInit) {\n      // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)\n      // don't fire popstate when user change the address bar and don't fire hashchange when url\n      // changed by push/replaceState\n\n      // html5 history api - popstate event\n      if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);\n      // hashchange event\n      jqLite(window).on('hashchange', cacheStateAndFireUrlChange);\n\n      urlChangeInit = true;\n    }\n\n    urlChangeListeners.push(callback);\n    return callback;\n  };\n\n  /**\n   * Checks whether the url has changed outside of Angular.\n   * Needs to be exported to be able to check for changes that have been done in sync,\n   * as hashchange/popstate events fire in async.\n   */\n  self.$$checkUrlChange = fireUrlChange;\n\n  //////////////////////////////////////////////////////////////\n  // Misc API\n  //////////////////////////////////////////////////////////////\n\n  /**\n   * @name $browser#baseHref\n   *\n   * @description\n   * Returns current <base href>\n   * (always relative - without domain)\n   *\n   * @returns {string} The current base href\n   */\n  self.baseHref = function() {\n    var href = baseElement.attr('href');\n    return href ? href.replace(/^(https?\\:)?\\/\\/[^\\/]*/, '') : '';\n  };\n\n  //////////////////////////////////////////////////////////////\n  // Cookies API\n  //////////////////////////////////////////////////////////////\n  var lastCookies = {};\n  var lastCookieString = '';\n  var cookiePath = self.baseHref();\n\n  function safeDecodeURIComponent(str) {\n    try {\n      return decodeURIComponent(str);\n    } catch (e) {\n      return str;\n    }\n  }\n\n  /**\n   * @name $browser#cookies\n   *\n   * @param {string=} name Cookie name\n   * @param {string=} value Cookie value\n   *\n   * @description\n   * The cookies method provides a 'private' low level access to browser cookies.\n   * It is not meant to be used directly, use the $cookie service instead.\n   *\n   * The return values vary depending on the arguments that the method was called with as follows:\n   *\n   * - cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify\n   *   it\n   * - cookies(name, value) -> set name to value, if value is undefined delete the cookie\n   * - cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that\n   *   way)\n   *\n   * @returns {Object} Hash of all cookies (if called without any parameter)\n   */\n  self.cookies = function(name, value) {\n    var cookieLength, cookieArray, cookie, i, index;\n\n    if (name) {\n      if (value === undefined) {\n        rawDocument.cookie = encodeURIComponent(name) + \"=;path=\" + cookiePath +\n                                \";expires=Thu, 01 Jan 1970 00:00:00 GMT\";\n      } else {\n        if (isString(value)) {\n          cookieLength = (rawDocument.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value) +\n                                ';path=' + cookiePath).length + 1;\n\n          // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:\n          // - 300 cookies\n          // - 20 cookies per unique domain\n          // - 4096 bytes per cookie\n          if (cookieLength > 4096) {\n            $log.warn(\"Cookie '\" + name +\n              \"' possibly not set or overflowed because it was too large (\" +\n              cookieLength + \" > 4096 bytes)!\");\n          }\n        }\n      }\n    } else {\n      if (rawDocument.cookie !== lastCookieString) {\n        lastCookieString = rawDocument.cookie;\n        cookieArray = lastCookieString.split(\"; \");\n        lastCookies = {};\n\n        for (i = 0; i < cookieArray.length; i++) {\n          cookie = cookieArray[i];\n          index = cookie.indexOf('=');\n          if (index > 0) { //ignore nameless cookies\n            name = safeDecodeURIComponent(cookie.substring(0, index));\n            // the first value that is seen for a cookie is the most\n            // specific one.  values for the same cookie name that\n            // follow are for less specific paths.\n            if (lastCookies[name] === undefined) {\n              lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));\n            }\n          }\n        }\n      }\n      return lastCookies;\n    }\n  };\n\n\n  /**\n   * @name $browser#defer\n   * @param {function()} fn A function, who's execution should be deferred.\n   * @param {number=} [delay=0] of milliseconds to defer the function execution.\n   * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.\n   *\n   * @description\n   * Executes a fn asynchronously via `setTimeout(fn, delay)`.\n   *\n   * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using\n   * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed\n   * via `$browser.defer.flush()`.\n   *\n   */\n  self.defer = function(fn, delay) {\n    var timeoutId;\n    outstandingRequestCount++;\n    timeoutId = setTimeout(function() {\n      delete pendingDeferIds[timeoutId];\n      completeOutstandingRequest(fn);\n    }, delay || 0);\n    pendingDeferIds[timeoutId] = true;\n    return timeoutId;\n  };\n\n\n  /**\n   * @name $browser#defer.cancel\n   *\n   * @description\n   * Cancels a deferred task identified with `deferId`.\n   *\n   * @param {*} deferId Token returned by the `$browser.defer` function.\n   * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully\n   *                    canceled.\n   */\n  self.defer.cancel = function(deferId) {\n    if (pendingDeferIds[deferId]) {\n      delete pendingDeferIds[deferId];\n      clearTimeout(deferId);\n      completeOutstandingRequest(noop);\n      return true;\n    }\n    return false;\n  };\n\n}\n\nfunction $BrowserProvider() {\n  this.$get = ['$window', '$log', '$sniffer', '$document',\n      function($window, $log, $sniffer, $document) {\n        return new Browser($window, $document, $log, $sniffer);\n      }];\n}\n\n/**\n * @ngdoc service\n * @name $cacheFactory\n *\n * @description\n * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to\n * them.\n *\n * ```js\n *\n *  var cache = $cacheFactory('cacheId');\n *  expect($cacheFactory.get('cacheId')).toBe(cache);\n *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();\n *\n *  cache.put(\"key\", \"value\");\n *  cache.put(\"another key\", \"another value\");\n *\n *  // We've specified no options on creation\n *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});\n *\n * ```\n *\n *\n * @param {string} cacheId Name or id of the newly created cache.\n * @param {object=} options Options object that specifies the cache behavior. Properties:\n *\n *   - `{number=}` `capacity` â€” turns the cache into LRU cache.\n *\n * @returns {object} Newly created cache object with the following set of methods:\n *\n * - `{object}` `info()` â€” Returns id, size, and options of cache.\n * - `{{*}}` `put({string} key, {*} value)` â€” Puts a new key-value pair into the cache and returns\n *   it.\n * - `{{*}}` `get({string} key)` â€” Returns cached value for `key` or undefined for cache miss.\n * - `{void}` `remove({string} key)` â€” Removes a key-value pair from the cache.\n * - `{void}` `removeAll()` â€” Removes all cached values.\n * - `{void}` `destroy()` â€” Removes references to this cache from $cacheFactory.\n *\n * @example\n   <example module=\"cacheExampleApp\">\n     <file name=\"index.html\">\n       <div ng-controller=\"CacheController\">\n         <input ng-model=\"newCacheKey\" placeholder=\"Key\">\n         <input ng-model=\"newCacheValue\" placeholder=\"Value\">\n         <button ng-click=\"put(newCacheKey, newCacheValue)\">Cache</button>\n\n         <p ng-if=\"keys.length\">Cached Values</p>\n         <div ng-repeat=\"key in keys\">\n           <span ng-bind=\"key\"></span>\n           <span>: </span>\n           <b ng-bind=\"cache.get(key)\"></b>\n         </div>\n\n         <p>Cache Info</p>\n         <div ng-repeat=\"(key, value) in cache.info()\">\n           <span ng-bind=\"key\"></span>\n           <span>: </span>\n           <b ng-bind=\"value\"></b>\n         </div>\n       </div>\n     </file>\n     <file name=\"script.js\">\n       angular.module('cacheExampleApp', []).\n         controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {\n           $scope.keys = [];\n           $scope.cache = $cacheFactory('cacheId');\n           $scope.put = function(key, value) {\n             if ($scope.cache.get(key) === undefined) {\n               $scope.keys.push(key);\n             }\n             $scope.cache.put(key, value === undefined ? null : value);\n           };\n         }]);\n     </file>\n     <file name=\"style.css\">\n       p {\n         margin: 10px 0 3px;\n       }\n     </file>\n   </example>\n */\nfunction $CacheFactoryProvider() {\n\n  this.$get = function() {\n    var caches = {};\n\n    function cacheFactory(cacheId, options) {\n      if (cacheId in caches) {\n        throw minErr('$cacheFactory')('iid', \"CacheId '{0}' is already taken!\", cacheId);\n      }\n\n      var size = 0,\n          stats = extend({}, options, {id: cacheId}),\n          data = {},\n          capacity = (options && options.capacity) || Number.MAX_VALUE,\n          lruHash = {},\n          freshEnd = null,\n          staleEnd = null;\n\n      /**\n       * @ngdoc type\n       * @name $cacheFactory.Cache\n       *\n       * @description\n       * A cache object used to store and retrieve data, primarily used by\n       * {@link $http $http} and the {@link ng.directive:script script} directive to cache\n       * templates and other data.\n       *\n       * ```js\n       *  angular.module('superCache')\n       *    .factory('superCache', ['$cacheFactory', function($cacheFactory) {\n       *      return $cacheFactory('super-cache');\n       *    }]);\n       * ```\n       *\n       * Example test:\n       *\n       * ```js\n       *  it('should behave like a cache', inject(function(superCache) {\n       *    superCache.put('key', 'value');\n       *    superCache.put('another key', 'another value');\n       *\n       *    expect(superCache.info()).toEqual({\n       *      id: 'super-cache',\n       *      size: 2\n       *    });\n       *\n       *    superCache.remove('another key');\n       *    expect(superCache.get('another key')).toBeUndefined();\n       *\n       *    superCache.removeAll();\n       *    expect(superCache.info()).toEqual({\n       *      id: 'super-cache',\n       *      size: 0\n       *    });\n       *  }));\n       * ```\n       */\n      return caches[cacheId] = {\n\n        /**\n         * @ngdoc method\n         * @name $cacheFactory.Cache#put\n         * @kind function\n         *\n         * @description\n         * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be\n         * retrieved later, and incrementing the size of the cache if the key was not already\n         * present in the cache. If behaving like an LRU cache, it will also remove stale\n         * entries from the set.\n         *\n         * It will not insert undefined values into the cache.\n         *\n         * @param {string} key the key under which the cached data is stored.\n         * @param {*} value the value to store alongside the key. If it is undefined, the key\n         *    will not be stored.\n         * @returns {*} the value stored.\n         */\n        put: function(key, value) {\n          if (capacity < Number.MAX_VALUE) {\n            var lruEntry = lruHash[key] || (lruHash[key] = {key: key});\n\n            refresh(lruEntry);\n          }\n\n          if (isUndefined(value)) return;\n          if (!(key in data)) size++;\n          data[key] = value;\n\n          if (size > capacity) {\n            this.remove(staleEnd.key);\n          }\n\n          return value;\n        },\n\n        /**\n         * @ngdoc method\n         * @name $cacheFactory.Cache#get\n         * @kind function\n         *\n         * @description\n         * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.\n         *\n         * @param {string} key the key of the data to be retrieved\n         * @returns {*} the value stored.\n         */\n        get: function(key) {\n          if (capacity < Number.MAX_VALUE) {\n            var lruEntry = lruHash[key];\n\n            if (!lruEntry) return;\n\n            refresh(lruEntry);\n          }\n\n          return data[key];\n        },\n\n\n        /**\n         * @ngdoc method\n         * @name $cacheFactory.Cache#remove\n         * @kind function\n         *\n         * @description\n         * Removes an entry from the {@link $cacheFactory.Cache Cache} object.\n         *\n         * @param {string} key the key of the entry to be removed\n         */\n        remove: function(key) {\n          if (capacity < Number.MAX_VALUE) {\n            var lruEntry = lruHash[key];\n\n            if (!lruEntry) return;\n\n            if (lruEntry == freshEnd) freshEnd = lruEntry.p;\n            if (lruEntry == staleEnd) staleEnd = lruEntry.n;\n            link(lruEntry.n,lruEntry.p);\n\n            delete lruHash[key];\n          }\n\n          delete data[key];\n          size--;\n        },\n\n\n        /**\n         * @ngdoc method\n         * @name $cacheFactory.Cache#removeAll\n         * @kind function\n         *\n         * @description\n         * Clears the cache object of any entries.\n         */\n        removeAll: function() {\n          data = {};\n          size = 0;\n          lruHash = {};\n          freshEnd = staleEnd = null;\n        },\n\n\n        /**\n         * @ngdoc method\n         * @name $cacheFactory.Cache#destroy\n         * @kind function\n         *\n         * @description\n         * Destroys the {@link $cacheFactory.Cache Cache} object entirely,\n         * removing it from the {@link $cacheFactory $cacheFactory} set.\n         */\n        destroy: function() {\n          data = null;\n          stats = null;\n          lruHash = null;\n          delete caches[cacheId];\n        },\n\n\n        /**\n         * @ngdoc method\n         * @name $cacheFactory.Cache#info\n         * @kind function\n         *\n         * @description\n         * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.\n         *\n         * @returns {object} an object with the following properties:\n         *   <ul>\n         *     <li>**id**: the id of the cache instance</li>\n         *     <li>**size**: the number of entries kept in the cache instance</li>\n         *     <li>**...**: any additional properties from the options object when creating the\n         *       cache.</li>\n         *   </ul>\n         */\n        info: function() {\n          return extend({}, stats, {size: size});\n        }\n      };\n\n\n      /**\n       * makes the `entry` the freshEnd of the LRU linked list\n       */\n      function refresh(entry) {\n        if (entry != freshEnd) {\n          if (!staleEnd) {\n            staleEnd = entry;\n          } else if (staleEnd == entry) {\n            staleEnd = entry.n;\n          }\n\n          link(entry.n, entry.p);\n          link(entry, freshEnd);\n          freshEnd = entry;\n          freshEnd.n = null;\n        }\n      }\n\n\n      /**\n       * bidirectionally links two entries of the LRU linked list\n       */\n      function link(nextEntry, prevEntry) {\n        if (nextEntry != prevEntry) {\n          if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify\n          if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify\n        }\n      }\n    }\n\n\n  /**\n   * @ngdoc method\n   * @name $cacheFactory#info\n   *\n   * @description\n   * Get information about all the caches that have been created\n   *\n   * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`\n   */\n    cacheFactory.info = function() {\n      var info = {};\n      forEach(caches, function(cache, cacheId) {\n        info[cacheId] = cache.info();\n      });\n      return info;\n    };\n\n\n  /**\n   * @ngdoc method\n   * @name $cacheFactory#get\n   *\n   * @description\n   * Get access to a cache object by the `cacheId` used when it was created.\n   *\n   * @param {string} cacheId Name or id of a cache to access.\n   * @returns {object} Cache object identified by the cacheId or undefined if no such cache.\n   */\n    cacheFactory.get = function(cacheId) {\n      return caches[cacheId];\n    };\n\n\n    return cacheFactory;\n  };\n}\n\n/**\n * @ngdoc service\n * @name $templateCache\n *\n * @description\n * The first time a template is used, it is loaded in the template cache for quick retrieval. You\n * can load templates directly into the cache in a `script` tag, or by consuming the\n * `$templateCache` service directly.\n *\n * Adding via the `script` tag:\n *\n * ```html\n *   <script type=\"text/ng-template\" id=\"templateId.html\">\n *     <p>This is the content of the template</p>\n *   </script>\n * ```\n *\n * **Note:** the `script` tag containing the template does not need to be included in the `head` of\n * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,\n * element with ng-app attribute), otherwise the template will be ignored.\n *\n * Adding via the `$templateCache` service:\n *\n * ```js\n * var myApp = angular.module('myApp', []);\n * myApp.run(function($templateCache) {\n *   $templateCache.put('templateId.html', 'This is the content of the template');\n * });\n * ```\n *\n * To retrieve the template later, simply use it in your HTML:\n * ```html\n * <div ng-include=\" 'templateId.html' \"></div>\n * ```\n *\n * or get it via Javascript:\n * ```js\n * $templateCache.get('templateId.html')\n * ```\n *\n * See {@link ng.$cacheFactory $cacheFactory}.\n *\n */\nfunction $TemplateCacheProvider() {\n  this.$get = ['$cacheFactory', function($cacheFactory) {\n    return $cacheFactory('templates');\n  }];\n}\n\n/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n *     Any commits to this file should be reviewed with security in mind.  *\n *   Changes to this file can potentially create security vulnerabilities. *\n *          An approval from 2 Core members with history of modifying      *\n *                         this file is required.                          *\n *                                                                         *\n *  Does the change somehow allow for arbitrary javascript to be executed? *\n *    Or allows for someone to change the prototype of built-in objects?   *\n *     Or gives undesired access to variables likes document or window?    *\n * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */\n\n/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!\n *\n * DOM-related variables:\n *\n * - \"node\" - DOM Node\n * - \"element\" - DOM Element or Node\n * - \"$node\" or \"$element\" - jqLite-wrapped node or element\n *\n *\n * Compiler related stuff:\n *\n * - \"linkFn\" - linking fn of a single directive\n * - \"nodeLinkFn\" - function that aggregates all linking fns for a particular node\n * - \"childLinkFn\" -  function that aggregates all linking fns for child nodes of a particular node\n * - \"compositeLinkFn\" - function that aggregates all linking fns for a compilation root (nodeList)\n */\n\n\n/**\n * @ngdoc service\n * @name $compile\n * @kind function\n *\n * @description\n * Compiles an HTML string or DOM into a template and produces a template function, which\n * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.\n *\n * The compilation is a process of walking the DOM tree and matching DOM elements to\n * {@link ng.$compileProvider#directive directives}.\n *\n * <div class=\"alert alert-warning\">\n * **Note:** This document is an in-depth reference of all directive options.\n * For a gentle introduction to directives with examples of common use cases,\n * see the {@link guide/directive directive guide}.\n * </div>\n *\n * ## Comprehensive Directive API\n *\n * There are many different options for a directive.\n *\n * The difference resides in the return value of the factory function.\n * You can either return a \"Directive Definition Object\" (see below) that defines the directive properties,\n * or just the `postLink` function (all other properties will have the default values).\n *\n * <div class=\"alert alert-success\">\n * **Best Practice:** It's recommended to use the \"directive definition object\" form.\n * </div>\n *\n * Here's an example directive declared with a Directive Definition Object:\n *\n * ```js\n *   var myModule = angular.module(...);\n *\n *   myModule.directive('directiveName', function factory(injectables) {\n *     var directiveDefinitionObject = {\n *       priority: 0,\n *       template: '<div></div>', // or // function(tElement, tAttrs) { ... },\n *       // or\n *       // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },\n *       transclude: false,\n *       restrict: 'A',\n *       templateNamespace: 'html',\n *       scope: false,\n *       controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },\n *       controllerAs: 'stringAlias',\n *       require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],\n *       compile: function compile(tElement, tAttrs, transclude) {\n *         return {\n *           pre: function preLink(scope, iElement, iAttrs, controller) { ... },\n *           post: function postLink(scope, iElement, iAttrs, controller) { ... }\n *         }\n *         // or\n *         // return function postLink( ... ) { ... }\n *       },\n *       // or\n *       // link: {\n *       //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },\n *       //  post: function postLink(scope, iElement, iAttrs, controller) { ... }\n *       // }\n *       // or\n *       // link: function postLink( ... ) { ... }\n *     };\n *     return directiveDefinitionObject;\n *   });\n * ```\n *\n * <div class=\"alert alert-warning\">\n * **Note:** Any unspecified options will use the default value. You can see the default values below.\n * </div>\n *\n * Therefore the above can be simplified as:\n *\n * ```js\n *   var myModule = angular.module(...);\n *\n *   myModule.directive('directiveName', function factory(injectables) {\n *     var directiveDefinitionObject = {\n *       link: function postLink(scope, iElement, iAttrs) { ... }\n *     };\n *     return directiveDefinitionObject;\n *     // or\n *     // return function postLink(scope, iElement, iAttrs) { ... }\n *   });\n * ```\n *\n *\n *\n * ### Directive Definition Object\n *\n * The directive definition object provides instructions to the {@link ng.$compile\n * compiler}. The attributes are:\n *\n * #### `multiElement`\n * When this property is set to true, the HTML compiler will collect DOM nodes between\n * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them\n * together as the directive elements. It is recommended that this feature be used on directives\n * which are not strictly behavioural (such as {@link ngClick}), and which\n * do not manipulate or replace child nodes (such as {@link ngInclude}).\n *\n * #### `priority`\n * When there are multiple directives defined on a single DOM element, sometimes it\n * is necessary to specify the order in which the directives are applied. The `priority` is used\n * to sort the directives before their `compile` functions get called. Priority is defined as a\n * number. Directives with greater numerical `priority` are compiled first. Pre-link functions\n * are also run in priority order, but post-link functions are run in reverse order. The order\n * of directives with the same priority is undefined. The default priority is `0`.\n *\n * #### `terminal`\n * If set to true then the current `priority` will be the last set of directives\n * which will execute (any directives at the current priority will still execute\n * as the order of execution on same `priority` is undefined). Note that expressions\n * and other directives used in the directive's template will also be excluded from execution.\n *\n * #### `scope`\n * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the\n * same element request a new scope, only one new scope is created. The new scope rule does not\n * apply for the root of the template since the root of the template always gets a new scope.\n *\n * **If set to `{}` (object hash),** then a new \"isolate\" scope is created. The 'isolate' scope differs from\n * normal scope in that it does not prototypically inherit from the parent scope. This is useful\n * when creating reusable components, which should not accidentally read or modify data in the\n * parent scope.\n *\n * The 'isolate' scope takes an object hash which defines a set of local scope properties\n * derived from the parent scope. These local properties are useful for aliasing values for\n * templates. Locals definition is a hash of local scope property to its source:\n *\n * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is\n *   always a string since DOM attributes are strings. If no `attr` name is specified  then the\n *   attribute name is assumed to be the same as the local name.\n *   Given `<widget my-attr=\"hello {{name}}\">` and widget definition\n *   of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect\n *   the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the\n *   `localName` property on the widget scope. The `name` is read from the parent scope (not\n *   component scope).\n *\n * * `=` or `=attr` - set up bi-directional binding between a local scope property and the\n *   parent scope property of name defined via the value of the `attr` attribute. If no `attr`\n *   name is specified then the attribute name is assumed to be the same as the local name.\n *   Given `<widget my-attr=\"parentModel\">` and widget definition of\n *   `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the\n *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected\n *   in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent\n *   scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You\n *   can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If\n *   you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use\n *   `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).\n *\n * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.\n *   If no `attr` name is specified then the attribute name is assumed to be the same as the\n *   local name. Given `<widget my-attr=\"count = count + value\">` and widget definition of\n *   `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to\n *   a function wrapper for the `count = count + value` expression. Often it's desirable to\n *   pass data from the isolated scope via an expression to the parent scope, this can be\n *   done by passing a map of local variable names and values into the expression wrapper fn.\n *   For example, if the expression is `increment(amount)` then we can specify the amount value\n *   by calling the `localFn` as `localFn({amount: 22})`.\n *\n *\n * #### `bindToController`\n * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will\n * allow a component to have its properties bound to the controller, rather than to scope. When the controller\n * is instantiated, the initial values of the isolate scope bindings are already available.\n *\n * #### `controller`\n * Controller constructor function. The controller is instantiated before the\n * pre-linking phase and it is shared with other directives (see\n * `require` attribute). This allows the directives to communicate with each other and augment\n * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:\n *\n * * `$scope` - Current scope associated with the element\n * * `$element` - Current element\n * * `$attrs` - Current attributes object for the element\n * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:\n *   `function([scope], cloneLinkingFn, futureParentElement)`.\n *    * `scope`: optional argument to override the scope.\n *    * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.\n *    * `futureParentElement`:\n *        * defines the parent to which the `cloneLinkingFn` will add the cloned elements.\n *        * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.\n *        * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)\n *          and when the `cloneLinkinFn` is passed,\n *          as those elements need to created and cloned in a special way when they are defined outside their\n *          usual containers (e.g. like `<svg>`).\n *        * See also the `directive.templateNamespace` property.\n *\n *\n * #### `require`\n * Require another directive and inject its controller as the fourth argument to the linking function. The\n * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the\n * injected argument will be an array in corresponding order. If no such directive can be\n * found, or if the directive does not have a controller, then an error is raised (unless no link function\n * is specified, in which case error checking is skipped). The name can be prefixed with:\n *\n * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.\n * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.\n * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.\n * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.\n * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass\n *   `null` to the `link` fn if not found.\n * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass\n *   `null` to the `link` fn if not found.\n *\n *\n * #### `controllerAs`\n * Controller alias at the directive scope. An alias for the controller so it\n * can be referenced at the directive template. The directive needs to define a scope for this\n * configuration to be used. Useful in the case when directive is used as component.\n *\n *\n * #### `restrict`\n * String of subset of `EACM` which restricts the directive to a specific directive\n * declaration style. If omitted, the defaults (elements and attributes) are used.\n *\n * * `E` - Element name (default): `<my-directive></my-directive>`\n * * `A` - Attribute (default): `<div my-directive=\"exp\"></div>`\n * * `C` - Class: `<div class=\"my-directive: exp;\"></div>`\n * * `M` - Comment: `<!-- directive: my-directive exp -->`\n *\n *\n * #### `templateNamespace`\n * String representing the document type used by the markup in the template.\n * AngularJS needs this information as those elements need to be created and cloned\n * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.\n *\n * * `html` - All root nodes in the template are HTML. Root nodes may also be\n *   top-level elements such as `<svg>` or `<math>`.\n * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).\n * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).\n *\n * If no `templateNamespace` is specified, then the namespace is considered to be `html`.\n *\n * #### `template`\n * HTML markup that may:\n * * Replace the contents of the directive's element (default).\n * * Replace the directive's element itself (if `replace` is true - DEPRECATED).\n * * Wrap the contents of the directive's element (if `transclude` is true).\n *\n * Value may be:\n *\n * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.\n * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`\n *   function api below) and returns a string value.\n *\n *\n * #### `templateUrl`\n * This is similar to `template` but the template is loaded from the specified URL, asynchronously.\n *\n * Because template loading is asynchronous the compiler will suspend compilation of directives on that element\n * for later when the template has been resolved.  In the meantime it will continue to compile and link\n * sibling and parent elements as though this element had not contained any directives.\n *\n * The compiler does not suspend the entire compilation to wait for templates to be loaded because this\n * would result in the whole app \"stalling\" until all templates are loaded asynchronously - even in the\n * case when only one deeply nested directive has `templateUrl`.\n *\n * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}\n *\n * You can specify `templateUrl` as a string representing the URL or as a function which takes two\n * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns\n * a string value representing the url.  In either case, the template URL is passed through {@link\n * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.\n *\n *\n * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)\n * specify what the template should replace. Defaults to `false`.\n *\n * * `true` - the template will replace the directive's element.\n * * `false` - the template will replace the contents of the directive's element.\n *\n * The replacement process migrates all of the attributes / classes from the old element to the new\n * one. See the {@link guide/directive#template-expanding-directive\n * Directives Guide} for an example.\n *\n * There are very few scenarios where element replacement is required for the application function,\n * the main one being reusable custom components that are used within SVG contexts\n * (because SVG doesn't work with custom elements in the DOM tree).\n *\n * #### `transclude`\n * Extract the contents of the element where the directive appears and make it available to the directive.\n * The contents are compiled and provided to the directive as a **transclusion function**. See the\n * {@link $compile#transclusion Transclusion} section below.\n *\n * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the\n * directive's element or the entire element:\n *\n * * `true` - transclude the content (i.e. the child nodes) of the directive's element.\n * * `'element'` - transclude the whole of the directive's element including any directives on this\n *   element that defined at a lower priority than this directive. When used, the `template`\n *   property is ignored.\n *\n *\n * #### `compile`\n *\n * ```js\n *   function compile(tElement, tAttrs, transclude) { ... }\n * ```\n *\n * The compile function deals with transforming the template DOM. Since most directives do not do\n * template transformation, it is not used often. The compile function takes the following arguments:\n *\n *   * `tElement` - template element - The element where the directive has been declared. It is\n *     safe to do template transformation on the element and child elements only.\n *\n *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared\n *     between all directive compile functions.\n *\n *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`\n *\n * <div class=\"alert alert-warning\">\n * **Note:** The template instance and the link instance may be different objects if the template has\n * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that\n * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration\n * should be done in a linking function rather than in a compile function.\n * </div>\n\n * <div class=\"alert alert-warning\">\n * **Note:** The compile function cannot handle directives that recursively use themselves in their\n * own templates or compile functions. Compiling these directives results in an infinite loop and a\n * stack overflow errors.\n *\n * This can be avoided by manually using $compile in the postLink function to imperatively compile\n * a directive's template instead of relying on automatic template compilation via `template` or\n * `templateUrl` declaration or manual compilation inside the compile function.\n * </div>\n *\n * <div class=\"alert alert-error\">\n * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it\n *   e.g. does not know about the right outer scope. Please use the transclude function that is passed\n *   to the link function instead.\n * </div>\n\n * A compile function can have a return value which can be either a function or an object.\n *\n * * returning a (post-link) function - is equivalent to registering the linking function via the\n *   `link` property of the config object when the compile function is empty.\n *\n * * returning an object with function(s) registered via `pre` and `post` properties - allows you to\n *   control when a linking function should be called during the linking phase. See info about\n *   pre-linking and post-linking functions below.\n *\n *\n * #### `link`\n * This property is used only if the `compile` property is not defined.\n *\n * ```js\n *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }\n * ```\n *\n * The link function is responsible for registering DOM listeners as well as updating the DOM. It is\n * executed after the template has been cloned. This is where most of the directive logic will be\n * put.\n *\n *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the\n *     directive for registering {@link ng.$rootScope.Scope#$watch watches}.\n *\n *   * `iElement` - instance element - The element where the directive is to be used. It is safe to\n *     manipulate the children of the element only in `postLink` function since the children have\n *     already been linked.\n *\n *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared\n *     between all directive linking functions.\n *\n *   * `controller` - a controller instance - A controller instance if at least one directive on the\n *     element defines a controller. The controller is shared among all the directives, which allows\n *     the directives to use the controllers as a communication channel.\n *\n *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.\n *     This is the same as the `$transclude`\n *     parameter of directive controllers, see there for details.\n *     `function([scope], cloneLinkingFn, futureParentElement)`.\n *\n * #### Pre-linking function\n *\n * Executed before the child elements are linked. Not safe to do DOM transformation since the\n * compiler linking function will fail to locate the correct elements for linking.\n *\n * #### Post-linking function\n *\n * Executed after the child elements are linked.\n *\n * Note that child elements that contain `templateUrl` directives will not have been compiled\n * and linked since they are waiting for their template to load asynchronously and their own\n * compilation and linking has been suspended until that occurs.\n *\n * It is safe to do DOM transformation in the post-linking function on elements that are not waiting\n * for their async templates to be resolved.\n *\n *\n * ### Transclusion\n *\n * Transclusion is the process of extracting a collection of DOM element from one part of the DOM and\n * copying them to another part of the DOM, while maintaining their connection to the original AngularJS\n * scope from where they were taken.\n *\n * Transclusion is used (often with {@link ngTransclude}) to insert the\n * original contents of a directive's element into a specified place in the template of the directive.\n * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded\n * content has access to the properties on the scope from which it was taken, even if the directive\n * has isolated scope.\n * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.\n *\n * This makes it possible for the widget to have private state for its template, while the transcluded\n * content has access to its originating scope.\n *\n * <div class=\"alert alert-warning\">\n * **Note:** When testing an element transclude directive you must not place the directive at the root of the\n * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives\n * Testing Transclusion Directives}.\n * </div>\n *\n * #### Transclusion Functions\n *\n * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion\n * function** to the directive's `link` function and `controller`. This transclusion function is a special\n * **linking function** that will return the compiled contents linked to a new transclusion scope.\n *\n * <div class=\"alert alert-info\">\n * If you are just using {@link ngTransclude} then you don't need to worry about this function, since\n * ngTransclude will deal with it for us.\n * </div>\n *\n * If you want to manually control the insertion and removal of the transcluded content in your directive\n * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery\n * object that contains the compiled DOM, which is linked to the correct transclusion scope.\n *\n * When you call a transclusion function you can pass in a **clone attach function**. This function accepts\n * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded\n * content and the `scope` is the newly created transclusion scope, to which the clone is bound.\n *\n * <div class=\"alert alert-info\">\n * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function\n * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.\n * </div>\n *\n * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone\n * attach function**:\n *\n * ```js\n * var transcludedContent, transclusionScope;\n *\n * $transclude(function(clone, scope) {\n *   element.append(clone);\n *   transcludedContent = clone;\n *   transclusionScope = scope;\n * });\n * ```\n *\n * Later, if you want to remove the transcluded content from your DOM then you should also destroy the\n * associated transclusion scope:\n *\n * ```js\n * transcludedContent.remove();\n * transclusionScope.$destroy();\n * ```\n *\n * <div class=\"alert alert-info\">\n * **Best Practice**: if you intend to add and remove transcluded content manually in your directive\n * (by calling the transclude function to get the DOM and and calling `element.remove()` to remove it),\n * then you are also responsible for calling `$destroy` on the transclusion scope.\n * </div>\n *\n * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}\n * automatically destroy their transluded clones as necessary so you do not need to worry about this if\n * you are simply using {@link ngTransclude} to inject the transclusion into your directive.\n *\n *\n * #### Transclusion Scopes\n *\n * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion\n * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed\n * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it\n * was taken.\n *\n * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look\n * like this:\n *\n * ```html\n * <div ng-app>\n *   <div isolate>\n *     <div transclusion>\n *     </div>\n *   </div>\n * </div>\n * ```\n *\n * The `$parent` scope hierarchy will look like this:\n *\n * ```\n * - $rootScope\n *   - isolate\n *     - transclusion\n * ```\n *\n * but the scopes will inherit prototypically from different scopes to their `$parent`.\n *\n * ```\n * - $rootScope\n *   - transclusion\n * - isolate\n * ```\n *\n *\n * ### Attributes\n *\n * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the\n * `link()` or `compile()` functions. It has a variety of uses.\n *\n * accessing *Normalized attribute names:*\n * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.\n * the attributes object allows for normalized access to\n *   the attributes.\n *\n * * *Directive inter-communication:* All directives share the same instance of the attributes\n *   object which allows the directives to use the attributes object as inter directive\n *   communication.\n *\n * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object\n *   allowing other directives to read the interpolated value.\n *\n * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes\n *   that contain interpolation (e.g. `src=\"{{bar}}\"`). Not only is this very efficient but it's also\n *   the only way to easily get the actual value because during the linking phase the interpolation\n *   hasn't been evaluated yet and so the value is at this time set to `undefined`.\n *\n * ```js\n * function linkingFn(scope, elm, attrs, ctrl) {\n *   // get the attribute value\n *   console.log(attrs.ngModel);\n *\n *   // change the attribute\n *   attrs.$set('ngModel', 'new value');\n *\n *   // observe changes to interpolated attribute\n *   attrs.$observe('ngModel', function(value) {\n *     console.log('ngModel has changed value to ' + value);\n *   });\n * }\n * ```\n *\n * ## Example\n *\n * <div class=\"alert alert-warning\">\n * **Note**: Typically directives are registered with `module.directive`. The example below is\n * to illustrate how `$compile` works.\n * </div>\n *\n <example module=\"compileExample\">\n   <file name=\"index.html\">\n    <script>\n      angular.module('compileExample', [], function($compileProvider) {\n        // configure new 'compile' directive by passing a directive\n        // factory function. The factory function injects the '$compile'\n        $compileProvider.directive('compile', function($compile) {\n          // directive factory creates a link function\n          return function(scope, element, attrs) {\n            scope.$watch(\n              function(scope) {\n                 // watch the 'compile' expression for changes\n                return scope.$eval(attrs.compile);\n              },\n              function(value) {\n                // when the 'compile' expression changes\n                // assign it into the current DOM\n                element.html(value);\n\n                // compile the new DOM and link it to the current\n                // scope.\n                // NOTE: we only compile .childNodes so that\n                // we don't get into infinite loop compiling ourselves\n                $compile(element.contents())(scope);\n              }\n            );\n          };\n        });\n      })\n      .controller('GreeterController', ['$scope', function($scope) {\n        $scope.name = 'Angular';\n        $scope.html = 'Hello {{name}}';\n      }]);\n    </script>\n    <div ng-controller=\"GreeterController\">\n      <input ng-model=\"name\"> <br>\n      <textarea ng-model=\"html\"></textarea> <br>\n      <div compile=\"html\"></div>\n    </div>\n   </file>\n   <file name=\"protractor.js\" type=\"protractor\">\n     it('should auto compile', function() {\n       var textarea = $('textarea');\n       var output = $('div[compile]');\n       // The initial state reads 'Hello Angular'.\n       expect(output.getText()).toBe('Hello Angular');\n       textarea.clear();\n       textarea.sendKeys('{{name}}!');\n       expect(output.getText()).toBe('Angular!');\n     });\n   </file>\n </example>\n\n *\n *\n * @param {string|DOMElement} element Element or HTML string to compile into a template function.\n * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.\n *\n * <div class=\"alert alert-error\">\n * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it\n *   e.g. will not use the right outer scope. Please pass the transclude function as a\n *   `parentBoundTranscludeFn` to the link function instead.\n * </div>\n *\n * @param {number} maxPriority only apply directives lower than given priority (Only effects the\n *                 root element(s), not their children)\n * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template\n * (a DOM element/tree) to a scope. Where:\n *\n *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.\n *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the\n *  `template` and call the `cloneAttachFn` function allowing the caller to attach the\n *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is\n *  called as: <br> `cloneAttachFn(clonedElement, scope)` where:\n *\n *      * `clonedElement` - is a clone of the original `element` passed into the compiler.\n *      * `scope` - is the current scope with which the linking function is working with.\n *\n *  * `options` - An optional object hash with linking options. If `options` is provided, then the following\n *  keys may be used to control linking behavior:\n *\n *      * `parentBoundTranscludeFn` - the transclude function made available to\n *        directives; if given, it will be passed through to the link functions of\n *        directives found in `element` during compilation.\n *      * `transcludeControllers` - an object hash with keys that map controller names\n *        to controller instances; if given, it will make the controllers\n *        available to directives.\n *      * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add\n *        the cloned elements; only needed for transcludes that are allowed to contain non html\n *        elements (e.g. SVG elements). See also the directive.controller property.\n *\n * Calling the linking function returns the element of the template. It is either the original\n * element passed in, or the clone of the element if the `cloneAttachFn` is provided.\n *\n * After linking the view is not updated until after a call to $digest which typically is done by\n * Angular automatically.\n *\n * If you need access to the bound view, there are two ways to do it:\n *\n * - If you are not asking the linking function to clone the template, create the DOM element(s)\n *   before you send them to the compiler and keep this reference around.\n *   ```js\n *     var element = $compile('<p>{{total}}</p>')(scope);\n *   ```\n *\n * - if on the other hand, you need the element to be cloned, the view reference from the original\n *   example would not point to the clone, but rather to the original template that was cloned. In\n *   this case, you can access the clone via the cloneAttachFn:\n *   ```js\n *     var templateElement = angular.element('<p>{{total}}</p>'),\n *         scope = ....;\n *\n *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {\n *       //attach the clone to DOM document at the right place\n *     });\n *\n *     //now we have reference to the cloned DOM via `clonedElement`\n *   ```\n *\n *\n * For information on how the compiler works, see the\n * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.\n */\n\nvar $compileMinErr = minErr('$compile');\n\n/**\n * @ngdoc provider\n * @name $compileProvider\n *\n * @description\n */\n$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];\nfunction $CompileProvider($provide, $$sanitizeUriProvider) {\n  var hasDirectives = {},\n      Suffix = 'Directive',\n      COMMENT_DIRECTIVE_REGEXP = /^\\s*directive\\:\\s*([\\w\\-]+)\\s+(.*)$/,\n      CLASS_DIRECTIVE_REGEXP = /(([\\w\\-]+)(?:\\:([^;]+))?;?)/,\n      ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),\n      REQUIRE_PREFIX_REGEXP = /^(?:(\\^\\^?)?(\\?)?(\\^\\^?)?)?/;\n\n  // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes\n  // The assumption is that future DOM event attribute names will begin with\n  // 'on' and be composed of only English letters.\n  var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;\n\n  function parseIsolateBindings(scope, directiveName) {\n    var LOCAL_REGEXP = /^\\s*([@&]|=(\\*?))(\\??)\\s*(\\w*)\\s*$/;\n\n    var bindings = {};\n\n    forEach(scope, function(definition, scopeName) {\n      var match = definition.match(LOCAL_REGEXP);\n\n      if (!match) {\n        throw $compileMinErr('iscp',\n            \"Invalid isolate scope definition for directive '{0}'.\" +\n            \" Definition: {... {1}: '{2}' ...}\",\n            directiveName, scopeName, definition);\n      }\n\n      bindings[scopeName] = {\n        mode: match[1][0],\n        collection: match[2] === '*',\n        optional: match[3] === '?',\n        attrName: match[4] || scopeName\n      };\n    });\n\n    return bindings;\n  }\n\n  /**\n   * @ngdoc method\n   * @name $compileProvider#directive\n   * @kind function\n   *\n   * @description\n   * Register a new directive with the compiler.\n   *\n   * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which\n   *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the\n   *    names and the values are the factories.\n   * @param {Function|Array} directiveFactory An injectable directive factory function. See\n   *    {@link guide/directive} for more info.\n   * @returns {ng.$compileProvider} Self for chaining.\n   */\n   this.directive = function registerDirective(name, directiveFactory) {\n    assertNotHasOwnProperty(name, 'directive');\n    if (isString(name)) {\n      assertArg(directiveFactory, 'directiveFactory');\n      if (!hasDirectives.hasOwnProperty(name)) {\n        hasDirectives[name] = [];\n        $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',\n          function($injector, $exceptionHandler) {\n            var directives = [];\n            forEach(hasDirectives[name], function(directiveFactory, index) {\n              try {\n                var directive = $injector.invoke(directiveFactory);\n                if (isFunction(directive)) {\n                  directive = { compile: valueFn(directive) };\n                } else if (!directive.compile && directive.link) {\n                  directive.compile = valueFn(directive.link);\n                }\n                directive.priority = directive.priority || 0;\n                directive.index = index;\n                directive.name = directive.name || name;\n                directive.require = directive.require || (directive.controller && directive.name);\n                directive.restrict = directive.restrict || 'EA';\n                if (isObject(directive.scope)) {\n                  directive.$$isolateBindings = parseIsolateBindings(directive.scope, directive.name);\n                }\n                directives.push(directive);\n              } catch (e) {\n                $exceptionHandler(e);\n              }\n            });\n            return directives;\n          }]);\n      }\n      hasDirectives[name].push(directiveFactory);\n    } else {\n      forEach(name, reverseParams(registerDirective));\n    }\n    return this;\n  };\n\n\n  /**\n   * @ngdoc method\n   * @name $compileProvider#aHrefSanitizationWhitelist\n   * @kind function\n   *\n   * @description\n   * Retrieves or overrides the default regular expression that is used for whitelisting of safe\n   * urls during a[href] sanitization.\n   *\n   * The sanitization is a security measure aimed at preventing XSS attacks via html links.\n   *\n   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into\n   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`\n   * regular expression. If a match is found, the original url is written into the dom. Otherwise,\n   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.\n   *\n   * @param {RegExp=} regexp New regexp to whitelist urls with.\n   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for\n   *    chaining otherwise.\n   */\n  this.aHrefSanitizationWhitelist = function(regexp) {\n    if (isDefined(regexp)) {\n      $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);\n      return this;\n    } else {\n      return $$sanitizeUriProvider.aHrefSanitizationWhitelist();\n    }\n  };\n\n\n  /**\n   * @ngdoc method\n   * @name $compileProvider#imgSrcSanitizationWhitelist\n   * @kind function\n   *\n   * @description\n   * Retrieves or overrides the default regular expression that is used for whitelisting of safe\n   * urls during img[src] sanitization.\n   *\n   * The sanitization is a security measure aimed at prevent XSS attacks via html links.\n   *\n   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into\n   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`\n   * regular expression. If a match is found, the original url is written into the dom. Otherwise,\n   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.\n   *\n   * @param {RegExp=} regexp New regexp to whitelist urls with.\n   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for\n   *    chaining otherwise.\n   */\n  this.imgSrcSanitizationWhitelist = function(regexp) {\n    if (isDefined(regexp)) {\n      $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);\n      return this;\n    } else {\n      return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();\n    }\n  };\n\n  /**\n   * @ngdoc method\n   * @name  $compileProvider#debugInfoEnabled\n   *\n   * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the\n   * current debugInfoEnabled state\n   * @returns {*} current value if used as getter or itself (chaining) if used as setter\n   *\n   * @kind function\n   *\n   * @description\n   * Call this method to enable/disable various debug runtime information in the compiler such as adding\n   * binding information and a reference to the current scope on to DOM elements.\n   * If enabled, the compiler will add the following to DOM elements that have been bound to the scope\n   * * `ng-binding` CSS class\n   * * `$binding` data property containing an array of the binding expressions\n   *\n   * You may want to disable this in production for a significant performance boost. See\n   * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.\n   *\n   * The default value is true.\n   */\n  var debugInfoEnabled = true;\n  this.debugInfoEnabled = function(enabled) {\n    if (isDefined(enabled)) {\n      debugInfoEnabled = enabled;\n      return this;\n    }\n    return debugInfoEnabled;\n  };\n\n  this.$get = [\n            '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',\n            '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',\n    function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,\n             $controller,   $rootScope,   $document,   $sce,   $animate,   $$sanitizeUri) {\n\n    var Attributes = function(element, attributesToCopy) {\n      if (attributesToCopy) {\n        var keys = Object.keys(attributesToCopy);\n        var i, l, key;\n\n        for (i = 0, l = keys.length; i < l; i++) {\n          key = keys[i];\n          this[key] = attributesToCopy[key];\n        }\n      } else {\n        this.$attr = {};\n      }\n\n      this.$$element = element;\n    };\n\n    Attributes.prototype = {\n      /**\n       * @ngdoc method\n       * @name $compile.directive.Attributes#$normalize\n       * @kind function\n       *\n       * @description\n       * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or\n       * `data-`) to its normalized, camelCase form.\n       *\n       * Also there is special case for Moz prefix starting with upper case letter.\n       *\n       * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}\n       *\n       * @param {string} name Name to normalize\n       */\n      $normalize: directiveNormalize,\n\n\n      /**\n       * @ngdoc method\n       * @name $compile.directive.Attributes#$addClass\n       * @kind function\n       *\n       * @description\n       * Adds the CSS class value specified by the classVal parameter to the element. If animations\n       * are enabled then an animation will be triggered for the class addition.\n       *\n       * @param {string} classVal The className value that will be added to the element\n       */\n      $addClass: function(classVal) {\n        if (classVal && classVal.length > 0) {\n          $animate.addClass(this.$$element, classVal);\n        }\n      },\n\n      /**\n       * @ngdoc method\n       * @name $compile.directive.Attributes#$removeClass\n       * @kind function\n       *\n       * @description\n       * Removes the CSS class value specified by the classVal parameter from the element. If\n       * animations are enabled then an animation will be triggered for the class removal.\n       *\n       * @param {string} classVal The className value that will be removed from the element\n       */\n      $removeClass: function(classVal) {\n        if (classVal && classVal.length > 0) {\n          $animate.removeClass(this.$$element, classVal);\n        }\n      },\n\n      /**\n       * @ngdoc method\n       * @name $compile.directive.Attributes#$updateClass\n       * @kind function\n       *\n       * @description\n       * Adds and removes the appropriate CSS class values to the element based on the difference\n       * between the new and old CSS class values (specified as newClasses and oldClasses).\n       *\n       * @param {string} newClasses The current CSS className value\n       * @param {string} oldClasses The former CSS className value\n       */\n      $updateClass: function(newClasses, oldClasses) {\n        var toAdd = tokenDifference(newClasses, oldClasses);\n        if (toAdd && toAdd.length) {\n          $animate.addClass(this.$$element, toAdd);\n        }\n\n        var toRemove = tokenDifference(oldClasses, newClasses);\n        if (toRemove && toRemove.length) {\n          $animate.removeClass(this.$$element, toRemove);\n        }\n      },\n\n      /**\n       * Set a normalized attribute on the element in a way such that all directives\n       * can share the attribute. This function properly handles boolean attributes.\n       * @param {string} key Normalized key. (ie ngAttribute)\n       * @param {string|boolean} value The value to set. If `null` attribute will be deleted.\n       * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.\n       *     Defaults to true.\n       * @param {string=} attrName Optional none normalized name. Defaults to key.\n       */\n      $set: function(key, value, writeAttr, attrName) {\n        // TODO: decide whether or not to throw an error if \"class\"\n        //is set through this function since it may cause $updateClass to\n        //become unstable.\n\n        var node = this.$$element[0],\n            booleanKey = getBooleanAttrName(node, key),\n            aliasedKey = getAliasedAttrName(node, key),\n            observer = key,\n            nodeName;\n\n        if (booleanKey) {\n          this.$$element.prop(key, value);\n          attrName = booleanKey;\n        } else if (aliasedKey) {\n          this[aliasedKey] = value;\n          observer = aliasedKey;\n        }\n\n        this[key] = value;\n\n        // translate normalized key to actual key\n        if (attrName) {\n          this.$attr[key] = attrName;\n        } else {\n          attrName = this.$attr[key];\n          if (!attrName) {\n            this.$attr[key] = attrName = snake_case(key, '-');\n          }\n        }\n\n        nodeName = nodeName_(this.$$element);\n\n        if ((nodeName === 'a' && key === 'href') ||\n            (nodeName === 'img' && key === 'src')) {\n          // sanitize a[href] and img[src] values\n          this[key] = value = $$sanitizeUri(value, key === 'src');\n        } else if (nodeName === 'img' && key === 'srcset') {\n          // sanitize img[srcset] values\n          var result = \"\";\n\n          // first check if there are spaces because it's not the same pattern\n          var trimmedSrcset = trim(value);\n          //                (   999x   ,|   999w   ,|   ,|,   )\n          var srcPattern = /(\\s+\\d+x\\s*,|\\s+\\d+w\\s*,|\\s+,|,\\s+)/;\n          var pattern = /\\s/.test(trimmedSrcset) ? srcPattern : /(,)/;\n\n          // split srcset into tuple of uri and descriptor except for the last item\n          var rawUris = trimmedSrcset.split(pattern);\n\n          // for each tuples\n          var nbrUrisWith2parts = Math.floor(rawUris.length / 2);\n          for (var i = 0; i < nbrUrisWith2parts; i++) {\n            var innerIdx = i * 2;\n            // sanitize the uri\n            result += $$sanitizeUri(trim(rawUris[innerIdx]), true);\n            // add the descriptor\n            result += (\" \" + trim(rawUris[innerIdx + 1]));\n          }\n\n          // split the last item into uri and descriptor\n          var lastTuple = trim(rawUris[i * 2]).split(/\\s/);\n\n          // sanitize the last uri\n          result += $$sanitizeUri(trim(lastTuple[0]), true);\n\n          // and add the last descriptor if any\n          if (lastTuple.length === 2) {\n            result += (\" \" + trim(lastTuple[1]));\n          }\n          this[key] = value = result;\n        }\n\n        if (writeAttr !== false) {\n          if (value === null || value === undefined) {\n            this.$$element.removeAttr(attrName);\n          } else {\n            this.$$element.attr(attrName, value);\n          }\n        }\n\n        // fire observers\n        var $$observers = this.$$observers;\n        $$observers && forEach($$observers[observer], function(fn) {\n          try {\n            fn(value);\n          } catch (e) {\n            $exceptionHandler(e);\n          }\n        });\n      },\n\n\n      /**\n       * @ngdoc method\n       * @name $compile.directive.Attributes#$observe\n       * @kind function\n       *\n       * @description\n       * Observes an interpolated attribute.\n       *\n       * The observer function will be invoked once during the next `$digest` following\n       * compilation. The observer is then invoked whenever the interpolated value\n       * changes.\n       *\n       * @param {string} key Normalized key. (ie ngAttribute) .\n       * @param {function(interpolatedValue)} fn Function that will be called whenever\n                the interpolated value of the attribute changes.\n       *        See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.\n       * @returns {function()} Returns a deregistration function for this observer.\n       */\n      $observe: function(key, fn) {\n        var attrs = this,\n            $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),\n            listeners = ($$observers[key] || ($$observers[key] = []));\n\n        listeners.push(fn);\n        $rootScope.$evalAsync(function() {\n          if (!listeners.$$inter && attrs.hasOwnProperty(key)) {\n            // no one registered attribute interpolation function, so lets call it manually\n            fn(attrs[key]);\n          }\n        });\n\n        return function() {\n          arrayRemove(listeners, fn);\n        };\n      }\n    };\n\n\n    function safeAddClass($element, className) {\n      try {\n        $element.addClass(className);\n      } catch (e) {\n        // ignore, since it means that we are trying to set class on\n        // SVG element, where class name is read-only.\n      }\n    }\n\n\n    var startSymbol = $interpolate.startSymbol(),\n        endSymbol = $interpolate.endSymbol(),\n        denormalizeTemplate = (startSymbol == '{{' || endSymbol  == '}}')\n            ? identity\n            : function denormalizeTemplate(template) {\n              return template.replace(/\\{\\{/g, startSymbol).replace(/}}/g, endSymbol);\n        },\n        NG_ATTR_BINDING = /^ngAttr[A-Z]/;\n\n    compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {\n      var bindings = $element.data('$binding') || [];\n\n      if (isArray(binding)) {\n        bindings = bindings.concat(binding);\n      } else {\n        bindings.push(binding);\n      }\n\n      $element.data('$binding', bindings);\n    } : noop;\n\n    compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {\n      safeAddClass($element, 'ng-binding');\n    } : noop;\n\n    compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {\n      var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';\n      $element.data(dataName, scope);\n    } : noop;\n\n    compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {\n      safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');\n    } : noop;\n\n    return compile;\n\n    //================================\n\n    function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,\n                        previousCompileContext) {\n      if (!($compileNodes instanceof jqLite)) {\n        // jquery always rewraps, whereas we need to preserve the original selector so that we can\n        // modify it.\n        $compileNodes = jqLite($compileNodes);\n      }\n      // We can not compile top level text elements since text nodes can be merged and we will\n      // not be able to attach scope data to them, so we will wrap them in <span>\n      forEach($compileNodes, function(node, index) {\n        if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\\S+/) /* non-empty */ ) {\n          $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];\n        }\n      });\n      var compositeLinkFn =\n              compileNodes($compileNodes, transcludeFn, $compileNodes,\n                           maxPriority, ignoreDirective, previousCompileContext);\n      compile.$$addScopeClass($compileNodes);\n      var namespace = null;\n      return function publicLinkFn(scope, cloneConnectFn, options) {\n        assertArg(scope, 'scope');\n\n        options = options || {};\n        var parentBoundTranscludeFn = options.parentBoundTranscludeFn,\n          transcludeControllers = options.transcludeControllers,\n          futureParentElement = options.futureParentElement;\n\n        // When `parentBoundTranscludeFn` is passed, it is a\n        // `controllersBoundTransclude` function (it was previously passed\n        // as `transclude` to directive.link) so we must unwrap it to get\n        // its `boundTranscludeFn`\n        if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {\n          parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;\n        }\n\n        if (!namespace) {\n          namespace = detectNamespaceForChildElements(futureParentElement);\n        }\n        var $linkNode;\n        if (namespace !== 'html') {\n          // When using a directive with replace:true and templateUrl the $compileNodes\n          // (or a child element inside of them)\n          // might change, so we need to recreate the namespace adapted compileNodes\n          // for call to the link function.\n          // Note: This will already clone the nodes...\n          $linkNode = jqLite(\n            wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())\n          );\n        } else if (cloneConnectFn) {\n          // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart\n          // and sometimes changes the structure of the DOM.\n          $linkNode = JQLitePrototype.clone.call($compileNodes);\n        } else {\n          $linkNode = $compileNodes;\n        }\n\n        if (transcludeControllers) {\n          for (var controllerName in transcludeControllers) {\n            $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);\n          }\n        }\n\n        compile.$$addScopeInfo($linkNode, scope);\n\n        if (cloneConnectFn) cloneConnectFn($linkNode, scope);\n        if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);\n        return $linkNode;\n      };\n    }\n\n    function detectNamespaceForChildElements(parentElement) {\n      // TODO: Make this detect MathML as well...\n      var node = parentElement && parentElement[0];\n      if (!node) {\n        return 'html';\n      } else {\n        return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';\n      }\n    }\n\n    /**\n     * Compile function matches each node in nodeList against the directives. Once all directives\n     * for a particular node are collected their compile functions are executed. The compile\n     * functions return values - the linking functions - are combined into a composite linking\n     * function, which is the a linking function for the node.\n     *\n     * @param {NodeList} nodeList an array of nodes or NodeList to compile\n     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the\n     *        scope argument is auto-generated to the new child of the transcluded parent scope.\n     * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then\n     *        the rootElement must be set the jqLite collection of the compile root. This is\n     *        needed so that the jqLite collection items can be replaced with widgets.\n     * @param {number=} maxPriority Max directive priority.\n     * @returns {Function} A composite linking function of all of the matched directives or null.\n     */\n    function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,\n                            previousCompileContext) {\n      var linkFns = [],\n          attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;\n\n      for (var i = 0; i < nodeList.length; i++) {\n        attrs = new Attributes();\n\n        // we must always refer to nodeList[i] since the nodes can be replaced underneath us.\n        directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,\n                                        ignoreDirective);\n\n        nodeLinkFn = (directives.length)\n            ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,\n                                      null, [], [], previousCompileContext)\n            : null;\n\n        if (nodeLinkFn && nodeLinkFn.scope) {\n          compile.$$addScopeClass(attrs.$$element);\n        }\n\n        childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||\n                      !(childNodes = nodeList[i].childNodes) ||\n                      !childNodes.length)\n            ? null\n            : compileNodes(childNodes,\n                 nodeLinkFn ? (\n                  (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)\n                     && nodeLinkFn.transclude) : transcludeFn);\n\n        if (nodeLinkFn || childLinkFn) {\n          linkFns.push(i, nodeLinkFn, childLinkFn);\n          linkFnFound = true;\n          nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;\n        }\n\n        //use the previous context only for the first element in the virtual group\n        previousCompileContext = null;\n      }\n\n      // return a linking function if we have found anything, null otherwise\n      return linkFnFound ? compositeLinkFn : null;\n\n      function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {\n        var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;\n        var stableNodeList;\n\n\n        if (nodeLinkFnFound) {\n          // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our\n          // offsets don't get screwed up\n          var nodeListLength = nodeList.length;\n          stableNodeList = new Array(nodeListLength);\n\n          // create a sparse array by only copying the elements which have a linkFn\n          for (i = 0; i < linkFns.length; i+=3) {\n            idx = linkFns[i];\n            stableNodeList[idx] = nodeList[idx];\n          }\n        } else {\n          stableNodeList = nodeList;\n        }\n\n        for (i = 0, ii = linkFns.length; i < ii;) {\n          node = stableNodeList[linkFns[i++]];\n          nodeLinkFn = linkFns[i++];\n          childLinkFn = linkFns[i++];\n\n          if (nodeLinkFn) {\n            if (nodeLinkFn.scope) {\n              childScope = scope.$new();\n              compile.$$addScopeInfo(jqLite(node), childScope);\n            } else {\n              childScope = scope;\n            }\n\n            if (nodeLinkFn.transcludeOnThisElement) {\n              childBoundTranscludeFn = createBoundTranscludeFn(\n                  scope, nodeLinkFn.transclude, parentBoundTranscludeFn,\n                  nodeLinkFn.elementTranscludeOnThisElement);\n\n            } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {\n              childBoundTranscludeFn = parentBoundTranscludeFn;\n\n            } else if (!parentBoundTranscludeFn && transcludeFn) {\n              childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);\n\n            } else {\n              childBoundTranscludeFn = null;\n            }\n\n            nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);\n\n          } else if (childLinkFn) {\n            childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);\n          }\n        }\n      }\n    }\n\n    function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn, elementTransclusion) {\n\n      var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {\n\n        if (!transcludedScope) {\n          transcludedScope = scope.$new(false, containingScope);\n          transcludedScope.$$transcluded = true;\n        }\n\n        return transcludeFn(transcludedScope, cloneFn, {\n          parentBoundTranscludeFn: previousBoundTranscludeFn,\n          transcludeControllers: controllers,\n          futureParentElement: futureParentElement\n        });\n      };\n\n      return boundTranscludeFn;\n    }\n\n    /**\n     * Looks for directives on the given node and adds them to the directive collection which is\n     * sorted.\n     *\n     * @param node Node to search.\n     * @param directives An array to which the directives are added to. This array is sorted before\n     *        the function returns.\n     * @param attrs The shared attrs object which is used to populate the normalized attributes.\n     * @param {number=} maxPriority Max directive priority.\n     */\n    function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {\n      var nodeType = node.nodeType,\n          attrsMap = attrs.$attr,\n          match,\n          className;\n\n      switch (nodeType) {\n        case NODE_TYPE_ELEMENT: /* Element */\n          // use the node name: <directive>\n          addDirective(directives,\n              directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);\n\n          // iterate over the attributes\n          for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,\n                   j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {\n            var attrStartName = false;\n            var attrEndName = false;\n\n            attr = nAttrs[j];\n            name = attr.name;\n            value = trim(attr.value);\n\n            // support ngAttr attribute binding\n            ngAttrName = directiveNormalize(name);\n            if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {\n              name = name.replace(PREFIX_REGEXP, '')\n                .substr(8).replace(/_(.)/g, function(match, letter) {\n                  return letter.toUpperCase();\n                });\n            }\n\n            var directiveNName = ngAttrName.replace(/(Start|End)$/, '');\n            if (directiveIsMultiElement(directiveNName)) {\n              if (ngAttrName === directiveNName + 'Start') {\n                attrStartName = name;\n                attrEndName = name.substr(0, name.length - 5) + 'end';\n                name = name.substr(0, name.length - 6);\n              }\n            }\n\n            nName = directiveNormalize(name.toLowerCase());\n            attrsMap[nName] = name;\n            if (isNgAttr || !attrs.hasOwnProperty(nName)) {\n                attrs[nName] = value;\n                if (getBooleanAttrName(node, nName)) {\n                  attrs[nName] = true; // presence means true\n                }\n            }\n            addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);\n            addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,\n                          attrEndName);\n          }\n\n          // use class as directive\n          className = node.className;\n          if (isObject(className)) {\n              // Maybe SVGAnimatedString\n              className = className.animVal;\n          }\n          if (isString(className) && className !== '') {\n            while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {\n              nName = directiveNormalize(match[2]);\n              if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {\n                attrs[nName] = trim(match[3]);\n              }\n              className = className.substr(match.index + match[0].length);\n            }\n          }\n          break;\n        case NODE_TYPE_TEXT: /* Text Node */\n          addTextInterpolateDirective(directives, node.nodeValue);\n          break;\n        case NODE_TYPE_COMMENT: /* Comment */\n          try {\n            match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);\n            if (match) {\n              nName = directiveNormalize(match[1]);\n              if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {\n                attrs[nName] = trim(match[2]);\n              }\n            }\n          } catch (e) {\n            // turns out that under some circumstances IE9 throws errors when one attempts to read\n            // comment's node value.\n            // Just ignore it and continue. (Can't seem to reproduce in test case.)\n          }\n          break;\n      }\n\n      directives.sort(byPriority);\n      return directives;\n    }\n\n    /**\n     * Given a node with an directive-start it collects all of the siblings until it finds\n     * directive-end.\n     * @param node\n     * @param attrStart\n     * @param attrEnd\n     * @returns {*}\n     */\n    function groupScan(node, attrStart, attrEnd) {\n      var nodes = [];\n      var depth = 0;\n      if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {\n        do {\n          if (!node) {\n            throw $compileMinErr('uterdir',\n                      \"Unterminated attribute, found '{0}' but no matching '{1}' found.\",\n                      attrStart, attrEnd);\n          }\n          if (node.nodeType == NODE_TYPE_ELEMENT) {\n            if (node.hasAttribute(attrStart)) depth++;\n            if (node.hasAttribute(attrEnd)) depth--;\n          }\n          nodes.push(node);\n          node = node.nextSibling;\n        } while (depth > 0);\n      } else {\n        nodes.push(node);\n      }\n\n      return jqLite(nodes);\n    }\n\n    /**\n     * Wrapper for linking function which converts normal linking function into a grouped\n     * linking function.\n     * @param linkFn\n     * @param attrStart\n     * @param attrEnd\n     * @returns {Function}\n     */\n    function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {\n      return function(scope, element, attrs, controllers, transcludeFn) {\n        element = groupScan(element[0], attrStart, attrEnd);\n        return linkFn(scope, element, attrs, controllers, transcludeFn);\n      };\n    }\n\n    /**\n     * Once the directives have been collected, their compile functions are executed. This method\n     * is responsible for inlining directive templates as well as terminating the application\n     * of the directives if the terminal directive has been reached.\n     *\n     * @param {Array} directives Array of collected directives to execute their compile function.\n     *        this needs to be pre-sorted by priority order.\n     * @param {Node} compileNode The raw DOM node to apply the compile functions to\n     * @param {Object} templateAttrs The shared attribute function\n     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the\n     *                                                  scope argument is auto-generated to the new\n     *                                                  child of the transcluded parent scope.\n     * @param {JQLite} jqCollection If we are working on the root of the compile tree then this\n     *                              argument has the root jqLite array so that we can replace nodes\n     *                              on it.\n     * @param {Object=} originalReplaceDirective An optional directive that will be ignored when\n     *                                           compiling the transclusion.\n     * @param {Array.<Function>} preLinkFns\n     * @param {Array.<Function>} postLinkFns\n     * @param {Object} previousCompileContext Context used for previous compilation of the current\n     *                                        node\n     * @returns {Function} linkFn\n     */\n    function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,\n                                   jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,\n                                   previousCompileContext) {\n      previousCompileContext = previousCompileContext || {};\n\n      var terminalPriority = -Number.MAX_VALUE,\n          newScopeDirective,\n          controllerDirectives = previousCompileContext.controllerDirectives,\n          controllers,\n          newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,\n          templateDirective = previousCompileContext.templateDirective,\n          nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,\n          hasTranscludeDirective = false,\n          hasTemplate = false,\n          hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,\n          $compileNode = templateAttrs.$$element = jqLite(compileNode),\n          directive,\n          directiveName,\n          $template,\n          replaceDirective = originalReplaceDirective,\n          childTranscludeFn = transcludeFn,\n          linkFn,\n          directiveValue;\n\n      // executes all directives on the current element\n      for (var i = 0, ii = directives.length; i < ii; i++) {\n        directive = directives[i];\n        var attrStart = directive.$$start;\n        var attrEnd = directive.$$end;\n\n        // collect multiblock sections\n        if (attrStart) {\n          $compileNode = groupScan(compileNode, attrStart, attrEnd);\n        }\n        $template = undefined;\n\n        if (terminalPriority > directive.priority) {\n          break; // prevent further processing of directives\n        }\n\n        if (directiveValue = directive.scope) {\n\n          // skip the check for directives with async templates, we'll check the derived sync\n          // directive when the template arrives\n          if (!directive.templateUrl) {\n            if (isObject(directiveValue)) {\n              // This directive is trying to add an isolated scope.\n              // Check that there is no scope of any kind already\n              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,\n                                directive, $compileNode);\n              newIsolateScopeDirective = directive;\n            } else {\n              // This directive is trying to add a child scope.\n              // Check that there is no isolated scope already\n              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,\n                                $compileNode);\n            }\n          }\n\n          newScopeDirective = newScopeDirective || directive;\n        }\n\n        directiveName = directive.name;\n\n        if (!directive.templateUrl && directive.controller) {\n          directiveValue = directive.controller;\n          controllerDirectives = controllerDirectives || {};\n          assertNoDuplicate(\"'\" + directiveName + \"' controller\",\n              controllerDirectives[directiveName], directive, $compileNode);\n          controllerDirectives[directiveName] = directive;\n        }\n\n        if (directiveValue = directive.transclude) {\n          hasTranscludeDirective = true;\n\n          // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.\n          // This option should only be used by directives that know how to safely handle element transclusion,\n          // where the transcluded nodes are added or replaced after linking.\n          if (!directive.$$tlb) {\n            assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);\n            nonTlbTranscludeDirective = directive;\n          }\n\n          if (directiveValue == 'element') {\n            hasElementTranscludeDirective = true;\n            terminalPriority = directive.priority;\n            $template = $compileNode;\n            $compileNode = templateAttrs.$$element =\n                jqLite(document.createComment(' ' + directiveName + ': ' +\n                                              templateAttrs[directiveName] + ' '));\n            compileNode = $compileNode[0];\n            replaceWith(jqCollection, sliceArgs($template), compileNode);\n\n            childTranscludeFn = compile($template, transcludeFn, terminalPriority,\n                                        replaceDirective && replaceDirective.name, {\n                                          // Don't pass in:\n                                          // - controllerDirectives - otherwise we'll create duplicates controllers\n                                          // - newIsolateScopeDirective or templateDirective - combining templates with\n                                          //   element transclusion doesn't make sense.\n                                          //\n                                          // We need only nonTlbTranscludeDirective so that we prevent putting transclusion\n                                          // on the same element more than once.\n                                          nonTlbTranscludeDirective: nonTlbTranscludeDirective\n                                        });\n          } else {\n            $template = jqLite(jqLiteClone(compileNode)).contents();\n            $compileNode.empty(); // clear contents\n            childTranscludeFn = compile($template, transcludeFn);\n          }\n        }\n\n        if (directive.template) {\n          hasTemplate = true;\n          assertNoDuplicate('template', templateDirective, directive, $compileNode);\n          templateDirective = directive;\n\n          directiveValue = (isFunction(directive.template))\n              ? directive.template($compileNode, templateAttrs)\n              : directive.template;\n\n          directiveValue = denormalizeTemplate(directiveValue);\n\n          if (directive.replace) {\n            replaceDirective = directive;\n            if (jqLiteIsTextNode(directiveValue)) {\n              $template = [];\n            } else {\n              $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));\n            }\n            compileNode = $template[0];\n\n            if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {\n              throw $compileMinErr('tplrt',\n                  \"Template for directive '{0}' must have exactly one root element. {1}\",\n                  directiveName, '');\n            }\n\n            replaceWith(jqCollection, $compileNode, compileNode);\n\n            var newTemplateAttrs = {$attr: {}};\n\n            // combine directives from the original node and from the template:\n            // - take the array of directives for this element\n            // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)\n            // - collect directives from the template and sort them by priority\n            // - combine directives as: processed + template + unprocessed\n            var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);\n            var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));\n\n            if (newIsolateScopeDirective) {\n              markDirectivesAsIsolate(templateDirectives);\n            }\n            directives = directives.concat(templateDirectives).concat(unprocessedDirectives);\n            mergeTemplateAttributes(templateAttrs, newTemplateAttrs);\n\n            ii = directives.length;\n          } else {\n            $compileNode.html(directiveValue);\n          }\n        }\n\n        if (directive.templateUrl) {\n          hasTemplate = true;\n          assertNoDuplicate('template', templateDirective, directive, $compileNode);\n          templateDirective = directive;\n\n          if (directive.replace) {\n            replaceDirective = directive;\n          }\n\n          nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,\n              templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {\n                controllerDirectives: controllerDirectives,\n                newIsolateScopeDirective: newIsolateScopeDirective,\n                templateDirective: templateDirective,\n                nonTlbTranscludeDirective: nonTlbTranscludeDirective\n              });\n          ii = directives.length;\n        } else if (directive.compile) {\n          try {\n            linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);\n            if (isFunction(linkFn)) {\n              addLinkFns(null, linkFn, attrStart, attrEnd);\n            } else if (linkFn) {\n              addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);\n            }\n          } catch (e) {\n            $exceptionHandler(e, startingTag($compileNode));\n          }\n        }\n\n        if (directive.terminal) {\n          nodeLinkFn.terminal = true;\n          terminalPriority = Math.max(terminalPriority, directive.priority);\n        }\n\n      }\n\n      nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;\n      nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;\n      nodeLinkFn.elementTranscludeOnThisElement = hasElementTranscludeDirective;\n      nodeLinkFn.templateOnThisElement = hasTemplate;\n      nodeLinkFn.transclude = childTranscludeFn;\n\n      previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;\n\n      // might be normal or delayed nodeLinkFn depending on if templateUrl is present\n      return nodeLinkFn;\n\n      ////////////////////\n\n      function addLinkFns(pre, post, attrStart, attrEnd) {\n        if (pre) {\n          if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);\n          pre.require = directive.require;\n          pre.directiveName = directiveName;\n          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {\n            pre = cloneAndAnnotateFn(pre, {isolateScope: true});\n          }\n          preLinkFns.push(pre);\n        }\n        if (post) {\n          if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);\n          post.require = directive.require;\n          post.directiveName = directiveName;\n          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {\n            post = cloneAndAnnotateFn(post, {isolateScope: true});\n          }\n          postLinkFns.push(post);\n        }\n      }\n\n\n      function getControllers(directiveName, require, $element, elementControllers) {\n        var value, retrievalMethod = 'data', optional = false;\n        var $searchElement = $element;\n        var match;\n        if (isString(require)) {\n          match = require.match(REQUIRE_PREFIX_REGEXP);\n          require = require.substring(match[0].length);\n\n          if (match[3]) {\n            if (match[1]) match[3] = null;\n            else match[1] = match[3];\n          }\n          if (match[1] === '^') {\n            retrievalMethod = 'inheritedData';\n          } else if (match[1] === '^^') {\n            retrievalMethod = 'inheritedData';\n            $searchElement = $element.parent();\n          }\n          if (match[2] === '?') {\n            optional = true;\n          }\n\n          value = null;\n\n          if (elementControllers && retrievalMethod === 'data') {\n            if (value = elementControllers[require]) {\n              value = value.instance;\n            }\n          }\n          value = value || $searchElement[retrievalMethod]('$' + require + 'Controller');\n\n          if (!value && !optional) {\n            throw $compileMinErr('ctreq',\n                \"Controller '{0}', required by directive '{1}', can't be found!\",\n                require, directiveName);\n          }\n          return value || null;\n        } else if (isArray(require)) {\n          value = [];\n          forEach(require, function(require) {\n            value.push(getControllers(directiveName, require, $element, elementControllers));\n          });\n        }\n        return value;\n      }\n\n\n      function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {\n        var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element,\n            attrs;\n\n        if (compileNode === linkNode) {\n          attrs = templateAttrs;\n          $element = templateAttrs.$$element;\n        } else {\n          $element = jqLite(linkNode);\n          attrs = new Attributes($element, templateAttrs);\n        }\n\n        if (newIsolateScopeDirective) {\n          isolateScope = scope.$new(true);\n        }\n\n        if (boundTranscludeFn) {\n          // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`\n          // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`\n          transcludeFn = controllersBoundTransclude;\n          transcludeFn.$$boundTransclude = boundTranscludeFn;\n        }\n\n        if (controllerDirectives) {\n          // TODO: merge `controllers` and `elementControllers` into single object.\n          controllers = {};\n          elementControllers = {};\n          forEach(controllerDirectives, function(directive) {\n            var locals = {\n              $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,\n              $element: $element,\n              $attrs: attrs,\n              $transclude: transcludeFn\n            }, controllerInstance;\n\n            controller = directive.controller;\n            if (controller == '@') {\n              controller = attrs[directive.name];\n            }\n\n            controllerInstance = $controller(controller, locals, true, directive.controllerAs);\n\n            // For directives with element transclusion the element is a comment,\n            // but jQuery .data doesn't support attaching data to comment nodes as it's hard to\n            // clean up (http://bugs.jquery.com/ticket/8335).\n            // Instead, we save the controllers for the element in a local hash and attach to .data\n            // later, once we have the actual element.\n            elementControllers[directive.name] = controllerInstance;\n            if (!hasElementTranscludeDirective) {\n              $element.data('$' + directive.name + 'Controller', controllerInstance.instance);\n            }\n\n            controllers[directive.name] = controllerInstance;\n          });\n        }\n\n        if (newIsolateScopeDirective) {\n          compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||\n              templateDirective === newIsolateScopeDirective.$$originalDirective)));\n          compile.$$addScopeClass($element, true);\n\n          var isolateScopeController = controllers && controllers[newIsolateScopeDirective.name];\n          var isolateBindingContext = isolateScope;\n          if (isolateScopeController && isolateScopeController.identifier &&\n              newIsolateScopeDirective.bindToController === true) {\n            isolateBindingContext = isolateScopeController.instance;\n          }\n\n          forEach(isolateScope.$$isolateBindings = newIsolateScopeDirective.$$isolateBindings, function(definition, scopeName) {\n            var attrName = definition.attrName,\n                optional = definition.optional,\n                mode = definition.mode, // @, =, or &\n                lastValue,\n                parentGet, parentSet, compare;\n\n            switch (mode) {\n\n              case '@':\n                attrs.$observe(attrName, function(value) {\n                  isolateBindingContext[scopeName] = value;\n                });\n                attrs.$$observers[attrName].$$scope = scope;\n                if (attrs[attrName]) {\n                  // If the attribute has been provided then we trigger an interpolation to ensure\n                  // the value is there for use in the link fn\n                  isolateBindingContext[scopeName] = $interpolate(attrs[attrName])(scope);\n                }\n                break;\n\n              case '=':\n                if (optional && !attrs[attrName]) {\n                  return;\n                }\n                parentGet = $parse(attrs[attrName]);\n                if (parentGet.literal) {\n                  compare = equals;\n                } else {\n                  compare = function(a, b) { return a === b || (a !== a && b !== b); };\n                }\n                parentSet = parentGet.assign || function() {\n                  // reset the change, or we will throw this exception on every $digest\n                  lastValue = isolateBindingContext[scopeName] = parentGet(scope);\n                  throw $compileMinErr('nonassign',\n                      \"Expression '{0}' used with directive '{1}' is non-assignable!\",\n                      attrs[attrName], newIsolateScopeDirective.name);\n                };\n                lastValue = isolateBindingContext[scopeName] = parentGet(scope);\n                var parentValueWatch = function parentValueWatch(parentValue) {\n                  if (!compare(parentValue, isolateBindingContext[scopeName])) {\n                    // we are out of sync and need to copy\n                    if (!compare(parentValue, lastValue)) {\n                      // parent changed and it has precedence\n                      isolateBindingContext[scopeName] = parentValue;\n                    } else {\n                      // if the parent can be assigned then do so\n                      parentSet(scope, parentValue = isolateBindingContext[scopeName]);\n                    }\n                  }\n                  return lastValue = parentValue;\n                };\n                parentValueWatch.$stateful = true;\n                var unwatch;\n                if (definition.collection) {\n                  unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch);\n                } else {\n                  unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);\n                }\n                isolateScope.$on('$destroy', unwatch);\n                break;\n\n              case '&':\n                parentGet = $parse(attrs[attrName]);\n                isolateBindingContext[scopeName] = function(locals) {\n                  return parentGet(scope, locals);\n                };\n                break;\n            }\n          });\n        }\n        if (controllers) {\n          forEach(controllers, function(controller) {\n            controller();\n          });\n          controllers = null;\n        }\n\n        // PRELINKING\n        for (i = 0, ii = preLinkFns.length; i < ii; i++) {\n          linkFn = preLinkFns[i];\n          invokeLinkFn(linkFn,\n              linkFn.isolateScope ? isolateScope : scope,\n              $element,\n              attrs,\n              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),\n              transcludeFn\n          );\n        }\n\n        // RECURSION\n        // We only pass the isolate scope, if the isolate directive has a template,\n        // otherwise the child elements do not belong to the isolate directive.\n        var scopeToChild = scope;\n        if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {\n          scopeToChild = isolateScope;\n        }\n        childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);\n\n        // POSTLINKING\n        for (i = postLinkFns.length - 1; i >= 0; i--) {\n          linkFn = postLinkFns[i];\n          invokeLinkFn(linkFn,\n              linkFn.isolateScope ? isolateScope : scope,\n              $element,\n              attrs,\n              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),\n              transcludeFn\n          );\n        }\n\n        // This is the function that is injected as `$transclude`.\n        // Note: all arguments are optional!\n        function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {\n          var transcludeControllers;\n\n          // No scope passed in:\n          if (!isScope(scope)) {\n            futureParentElement = cloneAttachFn;\n            cloneAttachFn = scope;\n            scope = undefined;\n          }\n\n          if (hasElementTranscludeDirective) {\n            transcludeControllers = elementControllers;\n          }\n          if (!futureParentElement) {\n            futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;\n          }\n          return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);\n        }\n      }\n    }\n\n    function markDirectivesAsIsolate(directives) {\n      // mark all directives as needing isolate scope.\n      for (var j = 0, jj = directives.length; j < jj; j++) {\n        directives[j] = inherit(directives[j], {$$isolateScope: true});\n      }\n    }\n\n    /**\n     * looks up the directive and decorates it with exception handling and proper parameters. We\n     * call this the boundDirective.\n     *\n     * @param {string} name name of the directive to look up.\n     * @param {string} location The directive must be found in specific format.\n     *   String containing any of theses characters:\n     *\n     *   * `E`: element name\n     *   * `A': attribute\n     *   * `C`: class\n     *   * `M`: comment\n     * @returns {boolean} true if directive was added.\n     */\n    function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,\n                          endAttrName) {\n      if (name === ignoreDirective) return null;\n      var match = null;\n      if (hasDirectives.hasOwnProperty(name)) {\n        for (var directive, directives = $injector.get(name + Suffix),\n            i = 0, ii = directives.length; i < ii; i++) {\n          try {\n            directive = directives[i];\n            if ((maxPriority === undefined || maxPriority > directive.priority) &&\n                 directive.restrict.indexOf(location) != -1) {\n              if (startAttrName) {\n                directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});\n              }\n              tDirectives.push(directive);\n              match = directive;\n            }\n          } catch (e) { $exceptionHandler(e); }\n        }\n      }\n      return match;\n    }\n\n\n    /**\n     * looks up the directive and returns true if it is a multi-element directive,\n     * and therefore requires DOM nodes between -start and -end markers to be grouped\n     * together.\n     *\n     * @param {string} name name of the directive to look up.\n     * @returns true if directive was registered as multi-element.\n     */\n    function directiveIsMultiElement(name) {\n      if (hasDirectives.hasOwnProperty(name)) {\n        for (var directive, directives = $injector.get(name + Suffix),\n            i = 0, ii = directives.length; i < ii; i++) {\n          directive = directives[i];\n          if (directive.multiElement) {\n            return true;\n          }\n        }\n      }\n      return false;\n    }\n\n    /**\n     * When the element is replaced with HTML template then the new attributes\n     * on the template need to be merged with the existing attributes in the DOM.\n     * The desired effect is to have both of the attributes present.\n     *\n     * @param {object} dst destination attributes (original DOM)\n     * @param {object} src source attributes (from the directive template)\n     */\n    function mergeTemplateAttributes(dst, src) {\n      var srcAttr = src.$attr,\n          dstAttr = dst.$attr,\n          $element = dst.$$element;\n\n      // reapply the old attributes to the new element\n      forEach(dst, function(value, key) {\n        if (key.charAt(0) != '$') {\n          if (src[key] && src[key] !== value) {\n            value += (key === 'style' ? ';' : ' ') + src[key];\n          }\n          dst.$set(key, value, true, srcAttr[key]);\n        }\n      });\n\n      // copy the new attributes on the old attrs object\n      forEach(src, function(value, key) {\n        if (key == 'class') {\n          safeAddClass($element, value);\n          dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;\n        } else if (key == 'style') {\n          $element.attr('style', $element.attr('style') + ';' + value);\n          dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;\n          // `dst` will never contain hasOwnProperty as DOM parser won't let it.\n          // You will get an \"InvalidCharacterError: DOM Exception 5\" error if you\n          // have an attribute like \"has-own-property\" or \"data-has-own-property\", etc.\n        } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {\n          dst[key] = value;\n          dstAttr[key] = srcAttr[key];\n        }\n      });\n    }\n\n\n    function compileTemplateUrl(directives, $compileNode, tAttrs,\n        $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {\n      var linkQueue = [],\n          afterTemplateNodeLinkFn,\n          afterTemplateChildLinkFn,\n          beforeTemplateCompileNode = $compileNode[0],\n          origAsyncDirective = directives.shift(),\n          derivedSyncDirective = inherit(origAsyncDirective, {\n            templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective\n          }),\n          templateUrl = (isFunction(origAsyncDirective.templateUrl))\n              ? origAsyncDirective.templateUrl($compileNode, tAttrs)\n              : origAsyncDirective.templateUrl,\n          templateNamespace = origAsyncDirective.templateNamespace;\n\n      $compileNode.empty();\n\n      $templateRequest($sce.getTrustedResourceUrl(templateUrl))\n        .then(function(content) {\n          var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;\n\n          content = denormalizeTemplate(content);\n\n          if (origAsyncDirective.replace) {\n            if (jqLiteIsTextNode(content)) {\n              $template = [];\n            } else {\n              $template = removeComments(wrapTemplate(templateNamespace, trim(content)));\n            }\n            compileNode = $template[0];\n\n            if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {\n              throw $compileMinErr('tplrt',\n                  \"Template for directive '{0}' must have exactly one root element. {1}\",\n                  origAsyncDirective.name, templateUrl);\n            }\n\n            tempTemplateAttrs = {$attr: {}};\n            replaceWith($rootElement, $compileNode, compileNode);\n            var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);\n\n            if (isObject(origAsyncDirective.scope)) {\n              markDirectivesAsIsolate(templateDirectives);\n            }\n            directives = templateDirectives.concat(directives);\n            mergeTemplateAttributes(tAttrs, tempTemplateAttrs);\n          } else {\n            compileNode = beforeTemplateCompileNode;\n            $compileNode.html(content);\n          }\n\n          directives.unshift(derivedSyncDirective);\n\n          afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,\n              childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,\n              previousCompileContext);\n          forEach($rootElement, function(node, i) {\n            if (node == compileNode) {\n              $rootElement[i] = $compileNode[0];\n            }\n          });\n          afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);\n\n          while (linkQueue.length) {\n            var scope = linkQueue.shift(),\n                beforeTemplateLinkNode = linkQueue.shift(),\n                linkRootElement = linkQueue.shift(),\n                boundTranscludeFn = linkQueue.shift(),\n                linkNode = $compileNode[0];\n\n            if (scope.$$destroyed) continue;\n\n            if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {\n              var oldClasses = beforeTemplateLinkNode.className;\n\n              if (!(previousCompileContext.hasElementTranscludeDirective &&\n                  origAsyncDirective.replace)) {\n                // it was cloned therefore we have to clone as well.\n                linkNode = jqLiteClone(compileNode);\n              }\n              replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);\n\n              // Copy in CSS classes from original node\n              safeAddClass(jqLite(linkNode), oldClasses);\n            }\n            if (afterTemplateNodeLinkFn.transcludeOnThisElement) {\n              childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);\n            } else {\n              childBoundTranscludeFn = boundTranscludeFn;\n            }\n            afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,\n              childBoundTranscludeFn);\n          }\n          linkQueue = null;\n        });\n\n      return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {\n        var childBoundTranscludeFn = boundTranscludeFn;\n        if (scope.$$destroyed) return;\n        if (linkQueue) {\n          linkQueue.push(scope,\n                         node,\n                         rootElement,\n                         childBoundTranscludeFn);\n        } else {\n          if (afterTemplateNodeLinkFn.transcludeOnThisElement) {\n            childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);\n          }\n          afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);\n        }\n      };\n    }\n\n\n    /**\n     * Sorting function for bound directives.\n     */\n    function byPriority(a, b) {\n      var diff = b.priority - a.priority;\n      if (diff !== 0) return diff;\n      if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;\n      return a.index - b.index;\n    }\n\n\n    function assertNoDuplicate(what, previousDirective, directive, element) {\n      if (previousDirective) {\n        throw $compileMinErr('multidir', 'Multiple directives [{0}, {1}] asking for {2} on: {3}',\n            previousDirective.name, directive.name, what, startingTag(element));\n      }\n    }\n\n\n    function addTextInterpolateDirective(directives, text) {\n      var interpolateFn = $interpolate(text, true);\n      if (interpolateFn) {\n        directives.push({\n          priority: 0,\n          compile: function textInterpolateCompileFn(templateNode) {\n            var templateNodeParent = templateNode.parent(),\n                hasCompileParent = !!templateNodeParent.length;\n\n            // When transcluding a template that has bindings in the root\n            // we don't have a parent and thus need to add the class during linking fn.\n            if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);\n\n            return function textInterpolateLinkFn(scope, node) {\n              var parent = node.parent();\n              if (!hasCompileParent) compile.$$addBindingClass(parent);\n              compile.$$addBindingInfo(parent, interpolateFn.expressions);\n              scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {\n                node[0].nodeValue = value;\n              });\n            };\n          }\n        });\n      }\n    }\n\n\n    function wrapTemplate(type, template) {\n      type = lowercase(type || 'html');\n      switch (type) {\n      case 'svg':\n      case 'math':\n        var wrapper = document.createElement('div');\n        wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';\n        return wrapper.childNodes[0].childNodes;\n      default:\n        return template;\n      }\n    }\n\n\n    function getTrustedContext(node, attrNormalizedName) {\n      if (attrNormalizedName == \"srcdoc\") {\n        return $sce.HTML;\n      }\n      var tag = nodeName_(node);\n      // maction[xlink:href] can source SVG.  It's not limited to <maction>.\n      if (attrNormalizedName == \"xlinkHref\" ||\n          (tag == \"form\" && attrNormalizedName == \"action\") ||\n          (tag != \"img\" && (attrNormalizedName == \"src\" ||\n                            attrNormalizedName == \"ngSrc\"))) {\n        return $sce.RESOURCE_URL;\n      }\n    }\n\n\n    function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {\n      var trustedContext = getTrustedContext(node, name);\n      allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;\n\n      var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);\n\n      // no interpolation found -> ignore\n      if (!interpolateFn) return;\n\n\n      if (name === \"multiple\" && nodeName_(node) === \"select\") {\n        throw $compileMinErr(\"selmulti\",\n            \"Binding to the 'multiple' attribute is not supported. Element: {0}\",\n            startingTag(node));\n      }\n\n      directives.push({\n        priority: 100,\n        compile: function() {\n            return {\n              pre: function attrInterpolatePreLinkFn(scope, element, attr) {\n                var $$observers = (attr.$$observers || (attr.$$observers = {}));\n\n                if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {\n                  throw $compileMinErr('nodomevents',\n                      \"Interpolations for HTML DOM event attributes are disallowed.  Please use the \" +\n                          \"ng- versions (such as ng-click instead of onclick) instead.\");\n                }\n\n                // If the attribute has changed since last $interpolate()ed\n                var newValue = attr[name];\n                if (newValue !== value) {\n                  // we need to interpolate again since the attribute value has been updated\n                  // (e.g. by another directive's compile function)\n                  // ensure unset/empty values make interpolateFn falsy\n                  interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);\n                  value = newValue;\n                }\n\n                // if attribute was updated so that there is no interpolation going on we don't want to\n                // register any observers\n                if (!interpolateFn) return;\n\n                // initialize attr object so that it's ready in case we need the value for isolate\n                // scope initialization, otherwise the value would not be available from isolate\n                // directive's linking fn during linking phase\n                attr[name] = interpolateFn(scope);\n\n                ($$observers[name] || ($$observers[name] = [])).$$inter = true;\n                (attr.$$observers && attr.$$observers[name].$$scope || scope).\n                  $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {\n                    //special case for class attribute addition + removal\n                    //so that class changes can tap into the animation\n                    //hooks provided by the $animate service. Be sure to\n                    //skip animations when the first digest occurs (when\n                    //both the new and the old values are the same) since\n                    //the CSS classes are the non-interpolated values\n                    if (name === 'class' && newValue != oldValue) {\n                      attr.$updateClass(newValue, oldValue);\n                    } else {\n                      attr.$set(name, newValue);\n                    }\n                  });\n              }\n            };\n          }\n      });\n    }\n\n\n    /**\n     * This is a special jqLite.replaceWith, which can replace items which\n     * have no parents, provided that the containing jqLite collection is provided.\n     *\n     * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes\n     *                               in the root of the tree.\n     * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep\n     *                                  the shell, but replace its DOM node reference.\n     * @param {Node} newNode The new DOM node.\n     */\n    function replaceWith($rootElement, elementsToRemove, newNode) {\n      var firstElementToRemove = elementsToRemove[0],\n          removeCount = elementsToRemove.length,\n          parent = firstElementToRemove.parentNode,\n          i, ii;\n\n      if ($rootElement) {\n        for (i = 0, ii = $rootElement.length; i < ii; i++) {\n          if ($rootElement[i] == firstElementToRemove) {\n            $rootElement[i++] = newNode;\n            for (var j = i, j2 = j + removeCount - 1,\n                     jj = $rootElement.length;\n                 j < jj; j++, j2++) {\n              if (j2 < jj) {\n                $rootElement[j] = $rootElement[j2];\n              } else {\n                delete $rootElement[j];\n              }\n            }\n            $rootElement.length -= removeCount - 1;\n\n            // If the replaced element is also the jQuery .context then replace it\n            // .context is a deprecated jQuery api, so we should set it only when jQuery set it\n            // http://api.jquery.com/context/\n            if ($rootElement.context === firstElementToRemove) {\n              $rootElement.context = newNode;\n            }\n            break;\n          }\n        }\n      }\n\n      if (parent) {\n        parent.replaceChild(newNode, firstElementToRemove);\n      }\n\n      // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?\n      var fragment = document.createDocumentFragment();\n      fragment.appendChild(firstElementToRemove);\n\n      // Copy over user data (that includes Angular's $scope etc.). Don't copy private\n      // data here because there's no public interface in jQuery to do that and copying over\n      // event listeners (which is the main use of private data) wouldn't work anyway.\n      jqLite(newNode).data(jqLite(firstElementToRemove).data());\n\n      // Remove data of the replaced element. We cannot just call .remove()\n      // on the element it since that would deallocate scope that is needed\n      // for the new node. Instead, remove the data \"manually\".\n      if (!jQuery) {\n        delete jqLite.cache[firstElementToRemove[jqLite.expando]];\n      } else {\n        // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after\n        // the replaced element. The cleanData version monkey-patched by Angular would cause\n        // the scope to be trashed and we do need the very same scope to work with the new\n        // element. However, we cannot just cache the non-patched version and use it here as\n        // that would break if another library patches the method after Angular does (one\n        // example is jQuery UI). Instead, set a flag indicating scope destroying should be\n        // skipped this one time.\n        skipDestroyOnNextJQueryCleanData = true;\n        jQuery.cleanData([firstElementToRemove]);\n      }\n\n      for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {\n        var element = elementsToRemove[k];\n        jqLite(element).remove(); // must do this way to clean up expando\n        fragment.appendChild(element);\n        delete elementsToRemove[k];\n      }\n\n      elementsToRemove[0] = newNode;\n      elementsToRemove.length = 1;\n    }\n\n\n    function cloneAndAnnotateFn(fn, annotation) {\n      return extend(function() { return fn.apply(null, arguments); }, fn, annotation);\n    }\n\n\n    function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {\n      try {\n        linkFn(scope, $element, attrs, controllers, transcludeFn);\n      } catch (e) {\n        $exceptionHandler(e, startingTag($element));\n      }\n    }\n  }];\n}\n\nvar PREFIX_REGEXP = /^((?:x|data)[\\:\\-_])/i;\n/**\n * Converts all accepted directives format into proper directive name.\n * @param name Name to normalize\n */\nfunction directiveNormalize(name) {\n  return camelCase(name.replace(PREFIX_REGEXP, ''));\n}\n\n/**\n * @ngdoc type\n * @name $compile.directive.Attributes\n *\n * @description\n * A shared object between directive compile / linking functions which contains normalized DOM\n * element attributes. The values reflect current binding state `{{ }}`. The normalization is\n * needed since all of these are treated as equivalent in Angular:\n *\n * ```\n *    <span ng:bind=\"a\" ng-bind=\"a\" data-ng-bind=\"a\" x-ng-bind=\"a\">\n * ```\n */\n\n/**\n * @ngdoc property\n * @name $compile.directive.Attributes#$attr\n *\n * @description\n * A map of DOM element attribute names to the normalized name. This is\n * needed to do reverse lookup from normalized name back to actual name.\n */\n\n\n/**\n * @ngdoc method\n * @name $compile.directive.Attributes#$set\n * @kind function\n *\n * @description\n * Set DOM element attribute value.\n *\n *\n * @param {string} name Normalized element attribute name of the property to modify. The name is\n *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}\n *          property to the original name.\n * @param {string} value Value to set the attribute to. The value can be an interpolated string.\n */\n\n\n\n/**\n * Closure compiler type information\n */\n\nfunction nodesetLinkingFn(\n  /* angular.Scope */ scope,\n  /* NodeList */ nodeList,\n  /* Element */ rootElement,\n  /* function(Function) */ boundTranscludeFn\n) {}\n\nfunction directiveLinkingFn(\n  /* nodesetLinkingFn */ nodesetLinkingFn,\n  /* angular.Scope */ scope,\n  /* Node */ node,\n  /* Element */ rootElement,\n  /* function(Function) */ boundTranscludeFn\n) {}\n\nfunction tokenDifference(str1, str2) {\n  var values = '',\n      tokens1 = str1.split(/\\s+/),\n      tokens2 = str2.split(/\\s+/);\n\n  outer:\n  for (var i = 0; i < tokens1.length; i++) {\n    var token = tokens1[i];\n    for (var j = 0; j < tokens2.length; j++) {\n      if (token == tokens2[j]) continue outer;\n    }\n    values += (values.length > 0 ? ' ' : '') + token;\n  }\n  return values;\n}\n\nfunction removeComments(jqNodes) {\n  jqNodes = jqLite(jqNodes);\n  var i = jqNodes.length;\n\n  if (i <= 1) {\n    return jqNodes;\n  }\n\n  while (i--) {\n    var node = jqNodes[i];\n    if (node.nodeType === NODE_TYPE_COMMENT) {\n      splice.call(jqNodes, i, 1);\n    }\n  }\n  return jqNodes;\n}\n\nvar $controllerMinErr = minErr('$controller');\n\n/**\n * @ngdoc provider\n * @name $controllerProvider\n * @description\n * The {@link ng.$controller $controller service} is used by Angular to create new\n * controllers.\n *\n * This provider allows controller registration via the\n * {@link ng.$controllerProvider#register register} method.\n */\nfunction $ControllerProvider() {\n  var controllers = {},\n      globals = false,\n      CNTRL_REG = /^(\\S+)(\\s+as\\s+(\\w+))?$/;\n\n\n  /**\n   * @ngdoc method\n   * @name $controllerProvider#register\n   * @param {string|Object} name Controller name, or an object map of controllers where the keys are\n   *    the names and the values are the constructors.\n   * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI\n   *    annotations in the array notation).\n   */\n  this.register = function(name, constructor) {\n    assertNotHasOwnProperty(name, 'controller');\n    if (isObject(name)) {\n      extend(controllers, name);\n    } else {\n      controllers[name] = constructor;\n    }\n  };\n\n  /**\n   * @ngdoc method\n   * @name $controllerProvider#allowGlobals\n   * @description If called, allows `$controller` to find controller constructors on `window`\n   */\n  this.allowGlobals = function() {\n    globals = true;\n  };\n\n\n  this.$get = ['$injector', '$window', function($injector, $window) {\n\n    /**\n     * @ngdoc service\n     * @name $controller\n     * @requires $injector\n     *\n     * @param {Function|string} constructor If called with a function then it's considered to be the\n     *    controller constructor function. Otherwise it's considered to be a string which is used\n     *    to retrieve the controller constructor using the following steps:\n     *\n     *    * check if a controller with given name is registered via `$controllerProvider`\n     *    * check if evaluating the string on the current scope returns a constructor\n     *    * if $controllerProvider#allowGlobals, check `window[constructor]` on the global\n     *      `window` object (not recommended)\n     *\n     *    The string can use the `controller as property` syntax, where the controller instance is published\n     *    as the specified property on the `scope`; the `scope` must be injected into `locals` param for this\n     *    to work correctly.\n     *\n     * @param {Object} locals Injection locals for Controller.\n     * @return {Object} Instance of given controller.\n     *\n     * @description\n     * `$controller` service is responsible for instantiating controllers.\n     *\n     * It's just a simple call to {@link auto.$injector $injector}, but extracted into\n     * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).\n     */\n    return function(expression, locals, later, ident) {\n      // PRIVATE API:\n      //   param `later` --- indicates that the controller's constructor is invoked at a later time.\n      //                     If true, $controller will allocate the object with the correct\n      //                     prototype chain, but will not invoke the controller until a returned\n      //                     callback is invoked.\n      //   param `ident` --- An optional label which overrides the label parsed from the controller\n      //                     expression, if any.\n      var instance, match, constructor, identifier;\n      later = later === true;\n      if (ident && isString(ident)) {\n        identifier = ident;\n      }\n\n      if (isString(expression)) {\n        match = expression.match(CNTRL_REG);\n        if (!match) {\n          throw $controllerMinErr('ctrlfmt',\n            \"Badly formed controller string '{0}'. \" +\n            \"Must match `__name__ as __id__` or `__name__`.\", expression);\n        }\n        constructor = match[1],\n        identifier = identifier || match[3];\n        expression = controllers.hasOwnProperty(constructor)\n            ? controllers[constructor]\n            : getter(locals.$scope, constructor, true) ||\n                (globals ? getter($window, constructor, true) : undefined);\n\n        assertArgFn(expression, constructor, true);\n      }\n\n      if (later) {\n        // Instantiate controller later:\n        // This machinery is used to create an instance of the object before calling the\n        // controller's constructor itself.\n        //\n        // This allows properties to be added to the controller before the constructor is\n        // invoked. Primarily, this is used for isolate scope bindings in $compile.\n        //\n        // This feature is not intended for use by applications, and is thus not documented\n        // publicly.\n        // Object creation: http://jsperf.com/create-constructor/2\n        var controllerPrototype = (isArray(expression) ?\n          expression[expression.length - 1] : expression).prototype;\n        instance = Object.create(controllerPrototype || null);\n\n        if (identifier) {\n          addIdentifier(locals, identifier, instance, constructor || expression.name);\n        }\n\n        return extend(function() {\n          $injector.invoke(expression, instance, locals, constructor);\n          return instance;\n        }, {\n          instance: instance,\n          identifier: identifier\n        });\n      }\n\n      instance = $injector.instantiate(expression, locals, constructor);\n\n      if (identifier) {\n        addIdentifier(locals, identifier, instance, constructor || expression.name);\n      }\n\n      return instance;\n    };\n\n    function addIdentifier(locals, identifier, instance, name) {\n      if (!(locals && isObject(locals.$scope))) {\n        throw minErr('$controller')('noscp',\n          \"Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.\",\n          name, identifier);\n      }\n\n      locals.$scope[identifier] = instance;\n    }\n  }];\n}\n\n/**\n * @ngdoc service\n * @name $document\n * @requires $window\n *\n * @description\n * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.\n *\n * @example\n   <example module=\"documentExample\">\n     <file name=\"index.html\">\n       <div ng-controller=\"ExampleController\">\n         <p>$document title: <b ng-bind=\"title\"></b></p>\n         <p>window.document title: <b ng-bind=\"windowTitle\"></b></p>\n       </div>\n     </file>\n     <file name=\"script.js\">\n       angular.module('documentExample', [])\n         .controller('ExampleController', ['$scope', '$document', function($scope, $document) {\n           $scope.title = $document[0].title;\n           $scope.windowTitle = angular.element(window.document)[0].title;\n         }]);\n     </file>\n   </example>\n */\nfunction $DocumentProvider() {\n  this.$get = ['$window', function(window) {\n    return jqLite(window.document);\n  }];\n}\n\n/**\n * @ngdoc service\n * @name $exceptionHandler\n * @requires ng.$log\n *\n * @description\n * Any uncaught exception in angular expressions is delegated to this service.\n * The default implementation simply delegates to `$log.error` which logs it into\n * the browser console.\n *\n * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by\n * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.\n *\n * ## Example:\n *\n * ```js\n *   angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {\n *     return function(exception, cause) {\n *       exception.message += ' (caused by \"' + cause + '\")';\n *       throw exception;\n *     };\n *   });\n * ```\n *\n * This example will override the normal action of `$exceptionHandler`, to make angular\n * exceptions fail hard when they happen, instead of just logging to the console.\n *\n * <hr />\n * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`\n * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}\n * (unless executed during a digest).\n *\n * If you wish, you can manually delegate exceptions, e.g.\n * `try { ... } catch(e) { $exceptionHandler(e); }`\n *\n * @param {Error} exception Exception associated with the error.\n * @param {string=} cause optional information about the context in which\n *       the error was thrown.\n *\n */\nfunction $ExceptionHandlerProvider() {\n  this.$get = ['$log', function($log) {\n    return function(exception, cause) {\n      $log.error.apply($log, arguments);\n    };\n  }];\n}\n\nvar APPLICATION_JSON = 'application/json';\nvar CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};\nvar JSON_START = /^\\[|^\\{(?!\\{)/;\nvar JSON_ENDS = {\n  '[': /]$/,\n  '{': /}$/\n};\nvar JSON_PROTECTION_PREFIX = /^\\)\\]\\}',?\\n/;\n\nfunction defaultHttpResponseTransform(data, headers) {\n  if (isString(data)) {\n    // Strip json vulnerability protection prefix and trim whitespace\n    var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();\n\n    if (tempData) {\n      var contentType = headers('Content-Type');\n      if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {\n        data = fromJson(tempData);\n      }\n    }\n  }\n\n  return data;\n}\n\nfunction isJsonLike(str) {\n    var jsonStart = str.match(JSON_START);\n    return jsonStart && JSON_ENDS[jsonStart[0]].test(str);\n}\n\n/**\n * Parse headers into key value object\n *\n * @param {string} headers Raw headers as a string\n * @returns {Object} Parsed headers as key value object\n */\nfunction parseHeaders(headers) {\n  var parsed = createMap(), key, val, i;\n\n  if (!headers) return parsed;\n\n  forEach(headers.split('\\n'), function(line) {\n    i = line.indexOf(':');\n    key = lowercase(trim(line.substr(0, i)));\n    val = trim(line.substr(i + 1));\n\n    if (key) {\n      parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;\n    }\n  });\n\n  return parsed;\n}\n\n\n/**\n * Returns a function that provides access to parsed headers.\n *\n * Headers are lazy parsed when first requested.\n * @see parseHeaders\n *\n * @param {(string|Object)} headers Headers to provide access to.\n * @returns {function(string=)} Returns a getter function which if called with:\n *\n *   - if called with single an argument returns a single header value or null\n *   - if called with no arguments returns an object containing all headers.\n */\nfunction headersGetter(headers) {\n  var headersObj = isObject(headers) ? headers : undefined;\n\n  return function(name) {\n    if (!headersObj) headersObj =  parseHeaders(headers);\n\n    if (name) {\n      var value = headersObj[lowercase(name)];\n      if (value === void 0) {\n        value = null;\n      }\n      return value;\n    }\n\n    return headersObj;\n  };\n}\n\n\n/**\n * Chain all given functions\n *\n * This function is used for both request and response transforming\n *\n * @param {*} data Data to transform.\n * @param {function(string=)} headers HTTP headers getter fn.\n * @param {number} status HTTP status code of the response.\n * @param {(Function|Array.<Function>)} fns Function or an array of functions.\n * @returns {*} Transformed data.\n */\nfunction transformData(data, headers, status, fns) {\n  if (isFunction(fns))\n    return fns(data, headers, status);\n\n  forEach(fns, function(fn) {\n    data = fn(data, headers, status);\n  });\n\n  return data;\n}\n\n\nfunction isSuccess(status) {\n  return 200 <= status && status < 300;\n}\n\n\n/**\n * @ngdoc provider\n * @name $httpProvider\n * @description\n * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.\n * */\nfunction $HttpProvider() {\n  /**\n   * @ngdoc property\n   * @name $httpProvider#defaults\n   * @description\n   *\n   * Object containing default values for all {@link ng.$http $http} requests.\n   *\n   * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}\n   * that will provide the cache for all requests who set their `cache` property to `true`.\n   * If you set the `default.cache = false` then only requests that specify their own custom\n   * cache object will be cached. See {@link $http#caching $http Caching} for more information.\n   *\n   * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.\n   * Defaults value is `'XSRF-TOKEN'`.\n   *\n   * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the\n   * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.\n   *\n   * - **`defaults.headers`** - {Object} - Default headers for all $http requests.\n   * Refer to {@link ng.$http#setting-http-headers $http} for documentation on\n   * setting default headers.\n   *     - **`defaults.headers.common`**\n   *     - **`defaults.headers.post`**\n   *     - **`defaults.headers.put`**\n   *     - **`defaults.headers.patch`**\n   *\n   **/\n  var defaults = this.defaults = {\n    // transform incoming response data\n    transformResponse: [defaultHttpResponseTransform],\n\n    // transform outgoing request data\n    transformRequest: [function(d) {\n      return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;\n    }],\n\n    // default headers\n    headers: {\n      common: {\n        'Accept': 'application/json, text/plain, */*'\n      },\n      post:   shallowCopy(CONTENT_TYPE_APPLICATION_JSON),\n      put:    shallowCopy(CONTENT_TYPE_APPLICATION_JSON),\n      patch:  shallowCopy(CONTENT_TYPE_APPLICATION_JSON)\n    },\n\n    xsrfCookieName: 'XSRF-TOKEN',\n    xsrfHeaderName: 'X-XSRF-TOKEN'\n  };\n\n  var useApplyAsync = false;\n  /**\n   * @ngdoc method\n   * @name $httpProvider#useApplyAsync\n   * @description\n   *\n   * Configure $http service to combine processing of multiple http responses received at around\n   * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in\n   * significant performance improvement for bigger applications that make many HTTP requests\n   * concurrently (common during application bootstrap).\n   *\n   * Defaults to false. If no value is specifed, returns the current configured value.\n   *\n   * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred\n   *    \"apply\" on the next tick, giving time for subsequent requests in a roughly ~10ms window\n   *    to load and share the same digest cycle.\n   *\n   * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.\n   *    otherwise, returns the current configured value.\n   **/\n  this.useApplyAsync = function(value) {\n    if (isDefined(value)) {\n      useApplyAsync = !!value;\n      return this;\n    }\n    return useApplyAsync;\n  };\n\n  /**\n   * @ngdoc property\n   * @name $httpProvider#interceptors\n   * @description\n   *\n   * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}\n   * pre-processing of request or postprocessing of responses.\n   *\n   * These service factories are ordered by request, i.e. they are applied in the same order as the\n   * array, on request, but reverse order, on response.\n   *\n   * {@link ng.$http#interceptors Interceptors detailed info}\n   **/\n  var interceptorFactories = this.interceptors = [];\n\n  this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector',\n      function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) {\n\n    var defaultCache = $cacheFactory('$http');\n\n    /**\n     * Interceptors stored in reverse order. Inner interceptors before outer interceptors.\n     * The reversal is needed so that we can build up the interception chain around the\n     * server request.\n     */\n    var reversedInterceptors = [];\n\n    forEach(interceptorFactories, function(interceptorFactory) {\n      reversedInterceptors.unshift(isString(interceptorFactory)\n          ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));\n    });\n\n    /**\n     * @ngdoc service\n     * @kind function\n     * @name $http\n     * @requires ng.$httpBackend\n     * @requires $cacheFactory\n     * @requires $rootScope\n     * @requires $q\n     * @requires $injector\n     *\n     * @description\n     * The `$http` service is a core Angular service that facilitates communication with the remote\n     * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)\n     * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).\n     *\n     * For unit testing applications that use `$http` service, see\n     * {@link ngMock.$httpBackend $httpBackend mock}.\n     *\n     * For a higher level of abstraction, please check out the {@link ngResource.$resource\n     * $resource} service.\n     *\n     * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by\n     * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage\n     * it is important to familiarize yourself with these APIs and the guarantees they provide.\n     *\n     *\n     * ## General usage\n     * The `$http` service is a function which takes a single argument â€” a configuration object â€”\n     * that is used to generate an HTTP request and returns  a {@link ng.$q promise}\n     * with two $http specific methods: `success` and `error`.\n     *\n     * ```js\n     *   // Simple GET request example :\n     *   $http.get('/someUrl').\n     *     success(function(data, status, headers, config) {\n     *       // this callback will be called asynchronously\n     *       // when the response is available\n     *     }).\n     *     error(function(data, status, headers, config) {\n     *       // called asynchronously if an error occurs\n     *       // or server returns response with an error status.\n     *     });\n     * ```\n     *\n     * ```js\n     *   // Simple POST request example (passing data) :\n     *   $http.post('/someUrl', {msg:'hello word!'}).\n     *     success(function(data, status, headers, config) {\n     *       // this callback will be called asynchronously\n     *       // when the response is available\n     *     }).\n     *     error(function(data, status, headers, config) {\n     *       // called asynchronously if an error occurs\n     *       // or server returns response with an error status.\n     *     });\n     * ```\n     *\n     *\n     * Since the returned value of calling the $http function is a `promise`, you can also use\n     * the `then` method to register callbacks, and these callbacks will receive a single argument â€“\n     * an object representing the response. See the API signature and type info below for more\n     * details.\n     *\n     * A response status code between 200 and 299 is considered a success status and\n     * will result in the success callback being called. Note that if the response is a redirect,\n     * XMLHttpRequest will transparently follow it, meaning that the error callback will not be\n     * called for such responses.\n     *\n     * ## Writing Unit Tests that use $http\n     * When unit testing (using {@link ngMock ngMock}), it is necessary to call\n     * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending\n     * request using trained responses.\n     *\n     * ```\n     * $httpBackend.expectGET(...);\n     * $http.get(...);\n     * $httpBackend.flush();\n     * ```\n     *\n     * ## Shortcut methods\n     *\n     * Shortcut methods are also available. All shortcut methods require passing in the URL, and\n     * request data must be passed in for POST/PUT requests.\n     *\n     * ```js\n     *   $http.get('/someUrl').success(successCallback);\n     *   $http.post('/someUrl', data).success(successCallback);\n     * ```\n     *\n     * Complete list of shortcut methods:\n     *\n     * - {@link ng.$http#get $http.get}\n     * - {@link ng.$http#head $http.head}\n     * - {@link ng.$http#post $http.post}\n     * - {@link ng.$http#put $http.put}\n     * - {@link ng.$http#delete $http.delete}\n     * - {@link ng.$http#jsonp $http.jsonp}\n     * - {@link ng.$http#patch $http.patch}\n     *\n     *\n     * ## Setting HTTP Headers\n     *\n     * The $http service will automatically add certain HTTP headers to all requests. These defaults\n     * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration\n     * object, which currently contains this default configuration:\n     *\n     * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):\n     *   - `Accept: application/json, text/plain, * / *`\n     * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)\n     *   - `Content-Type: application/json`\n     * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)\n     *   - `Content-Type: application/json`\n     *\n     * To add or overwrite these defaults, simply add or remove a property from these configuration\n     * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object\n     * with the lowercased HTTP method name as the key, e.g.\n     * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }.\n     *\n     * The defaults can also be set at runtime via the `$http.defaults` object in the same\n     * fashion. For example:\n     *\n     * ```\n     * module.run(function($http) {\n     *   $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'\n     * });\n     * ```\n     *\n     * In addition, you can supply a `headers` property in the config object passed when\n     * calling `$http(config)`, which overrides the defaults without changing them globally.\n     *\n     * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,\n     * Use the `headers` property, setting the desired header to `undefined`. For example:\n     *\n     * ```js\n     * var req = {\n     *  method: 'POST',\n     *  url: 'http://example.com',\n     *  headers: {\n     *    'Content-Type': undefined\n     *  },\n     *  data: { test: 'test' },\n     * }\n     *\n     * $http(req).success(function(){...}).error(function(){...});\n     * ```\n     *\n     * ## Transforming Requests and Responses\n     *\n     * Both requests and responses can be transformed using transformation functions: `transformRequest`\n     * and `transformResponse`. These properties can be a single function that returns\n     * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,\n     * which allows you to `push` or `unshift` a new transformation function into the transformation chain.\n     *\n     * ### Default Transformations\n     *\n     * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and\n     * `defaults.transformResponse` properties. If a request does not provide its own transformations\n     * then these will be applied.\n     *\n     * You can augment or replace the default transformations by modifying these properties by adding to or\n     * replacing the array.\n     *\n     * Angular provides the following default transformations:\n     *\n     * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):\n     *\n     * - If the `data` property of the request configuration object contains an object, serialize it\n     *   into JSON format.\n     *\n     * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):\n     *\n     *  - If XSRF prefix is detected, strip it (see Security Considerations section below).\n     *  - If JSON response is detected, deserialize it using a JSON parser.\n     *\n     *\n     * ### Overriding the Default Transformations Per Request\n     *\n     * If you wish override the request/response transformations only for a single request then provide\n     * `transformRequest` and/or `transformResponse` properties on the configuration object passed\n     * into `$http`.\n     *\n     * Note that if you provide these properties on the config object the default transformations will be\n     * overwritten. If you wish to augment the default transformations then you must include them in your\n     * local transformation array.\n     *\n     * The following code demonstrates adding a new response transformation to be run after the default response\n     * transformations have been run.\n     *\n     * ```js\n     * function appendTransform(defaults, transform) {\n     *\n     *   // We can't guarantee that the default transformation is an array\n     *   defaults = angular.isArray(defaults) ? defaults : [defaults];\n     *\n     *   // Append the new transformation to the defaults\n     *   return defaults.concat(transform);\n     * }\n     *\n     * $http({\n     *   url: '...',\n     *   method: 'GET',\n     *   transformResponse: appendTransform($http.defaults.transformResponse, function(value) {\n     *     return doTransform(value);\n     *   })\n     * });\n     * ```\n     *\n     *\n     * ## Caching\n     *\n     * To enable caching, set the request configuration `cache` property to `true` (to use default\n     * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).\n     * When the cache is enabled, `$http` stores the response from the server in the specified\n     * cache. The next time the same request is made, the response is served from the cache without\n     * sending a request to the server.\n     *\n     * Note that even if the response is served from cache, delivery of the data is asynchronous in\n     * the same way that real requests are.\n     *\n     * If there are multiple GET requests for the same URL that should be cached using the same\n     * cache, but the cache is not populated yet, only one request to the server will be made and\n     * the remaining requests will be fulfilled using the response from the first request.\n     *\n     * You can change the default cache to a new object (built with\n     * {@link ng.$cacheFactory `$cacheFactory`}) by updating the\n     * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set\n     * their `cache` property to `true` will now use this cache object.\n     *\n     * If you set the default cache to `false` then only requests that specify their own custom\n     * cache object will be cached.\n     *\n     * ## Interceptors\n     *\n     * Before you start creating interceptors, be sure to understand the\n     * {@link ng.$q $q and deferred/promise APIs}.\n     *\n     * For purposes of global error handling, authentication, or any kind of synchronous or\n     * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be\n     * able to intercept requests before they are handed to the server and\n     * responses before they are handed over to the application code that\n     * initiated these requests. The interceptors leverage the {@link ng.$q\n     * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.\n     *\n     * The interceptors are service factories that are registered with the `$httpProvider` by\n     * adding them to the `$httpProvider.interceptors` array. The factory is called and\n     * injected with dependencies (if specified) and returns the interceptor.\n     *\n     * There are two kinds of interceptors (and two kinds of rejection interceptors):\n     *\n     *   * `request`: interceptors get called with a http `config` object. The function is free to\n     *     modify the `config` object or create a new one. The function needs to return the `config`\n     *     object directly, or a promise containing the `config` or a new `config` object.\n     *   * `requestError`: interceptor gets called when a previous interceptor threw an error or\n     *     resolved with a rejection.\n     *   * `response`: interceptors get called with http `response` object. The function is free to\n     *     modify the `response` object or create a new one. The function needs to return the `response`\n     *     object directly, or as a promise containing the `response` or a new `response` object.\n     *   * `responseError`: interceptor gets called when a previous interceptor threw an error or\n     *     resolved with a rejection.\n     *\n     *\n     * ```js\n     *   // register the interceptor as a service\n     *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {\n     *     return {\n     *       // optional method\n     *       'request': function(config) {\n     *         // do something on success\n     *         return config;\n     *       },\n     *\n     *       // optional method\n     *      'requestError': function(rejection) {\n     *         // do something on error\n     *         if (canRecover(rejection)) {\n     *           return responseOrNewPromise\n     *         }\n     *         return $q.reject(rejection);\n     *       },\n     *\n     *\n     *\n     *       // optional method\n     *       'response': function(response) {\n     *         // do something on success\n     *         return response;\n     *       },\n     *\n     *       // optional method\n     *      'responseError': function(rejection) {\n     *         // do something on error\n     *         if (canRecover(rejection)) {\n     *           return responseOrNewPromise\n     *         }\n     *         return $q.reject(rejection);\n     *       }\n     *     };\n     *   });\n     *\n     *   $httpProvider.interceptors.push('myHttpInterceptor');\n     *\n     *\n     *   // alternatively, register the interceptor via an anonymous factory\n     *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {\n     *     return {\n     *      'request': function(config) {\n     *          // same as above\n     *       },\n     *\n     *       'response': function(response) {\n     *          // same as above\n     *       }\n     *     };\n     *   });\n     * ```\n     *\n     * ## Security Considerations\n     *\n     * When designing web applications, consider security threats from:\n     *\n     * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)\n     * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)\n     *\n     * Both server and the client must cooperate in order to eliminate these threats. Angular comes\n     * pre-configured with strategies that address these issues, but for this to work backend server\n     * cooperation is required.\n     *\n     * ### JSON Vulnerability Protection\n     *\n     * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)\n     * allows third party website to turn your JSON resource URL into\n     * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To\n     * counter this your server can prefix all JSON requests with following string `\")]}',\\n\"`.\n     * Angular will automatically strip the prefix before processing it as JSON.\n     *\n     * For example if your server needs to return:\n     * ```js\n     * ['one','two']\n     * ```\n     *\n     * which is vulnerable to attack, your server can return:\n     * ```js\n     * )]}',\n     * ['one','two']\n     * ```\n     *\n     * Angular will strip the prefix, before processing the JSON.\n     *\n     *\n     * ### Cross Site Request Forgery (XSRF) Protection\n     *\n     * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which\n     * an unauthorized site can gain your user's private data. Angular provides a mechanism\n     * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie\n     * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only\n     * JavaScript that runs on your domain could read the cookie, your server can be assured that\n     * the XHR came from JavaScript running on your domain. The header will not be set for\n     * cross-domain requests.\n     *\n     * To take advantage of this, your server needs to set a token in a JavaScript readable session\n     * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the\n     * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure\n     * that only JavaScript running on your domain could have sent the request. The token must be\n     * unique for each user and must be verifiable by the server (to prevent the JavaScript from\n     * making up its own tokens). We recommend that the token is a digest of your site's\n     * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)\n     * for added security.\n     *\n     * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName\n     * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,\n     * or the per-request config object.\n     *\n     *\n     * @param {object} config Object describing the request to be made and how it should be\n     *    processed. The object has following properties:\n     *\n     *    - **method** â€“ `{string}` â€“ HTTP method (e.g. 'GET', 'POST', etc)\n     *    - **url** â€“ `{string}` â€“ Absolute or relative URL of the resource that is being requested.\n     *    - **params** â€“ `{Object.<string|Object>}` â€“ Map of strings or objects which will be turned\n     *      to `?key1=value1&key2=value2` after the url. If the value is not a string, it will be\n     *      JSONified.\n     *    - **data** â€“ `{string|Object}` â€“ Data to be sent as the request message data.\n     *    - **headers** â€“ `{Object}` â€“ Map of strings or functions which return strings representing\n     *      HTTP headers to send to the server. If the return value of a function is null, the\n     *      header will not be sent.\n     *    - **xsrfHeaderName** â€“ `{string}` â€“ Name of HTTP header to populate with the XSRF token.\n     *    - **xsrfCookieName** â€“ `{string}` â€“ Name of cookie containing the XSRF token.\n     *    - **transformRequest** â€“\n     *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` â€“\n     *      transform function or an array of such functions. The transform function takes the http\n     *      request body and headers and returns its transformed (typically serialized) version.\n     *      See {@link ng.$http#overriding-the-default-transformations-per-request\n     *      Overriding the Default Transformations}\n     *    - **transformResponse** â€“\n     *      `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` â€“\n     *      transform function or an array of such functions. The transform function takes the http\n     *      response body, headers and status and returns its transformed (typically deserialized) version.\n     *      See {@link ng.$http#overriding-the-default-transformations-per-request\n     *      Overriding the Default Transformations}\n     *    - **cache** â€“ `{boolean|Cache}` â€“ If true, a default $http cache will be used to cache the\n     *      GET request, otherwise if a cache instance built with\n     *      {@link ng.$cacheFactory $cacheFactory}, this cache will be used for\n     *      caching.\n     *    - **timeout** â€“ `{number|Promise}` â€“ timeout in milliseconds, or {@link ng.$q promise}\n     *      that should abort the request when resolved.\n     *    - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the\n     *      XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)\n     *      for more information.\n     *    - **responseType** - `{string}` - see\n     *      [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).\n     *\n     * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the\n     *   standard `then` method and two http specific methods: `success` and `error`. The `then`\n     *   method takes two arguments a success and an error callback which will be called with a\n     *   response object. The `success` and `error` methods take a single argument - a function that\n     *   will be called when the request succeeds or fails respectively. The arguments passed into\n     *   these functions are destructured representation of the response object passed into the\n     *   `then` method. The response object has these properties:\n     *\n     *   - **data** â€“ `{string|Object}` â€“ The response body transformed with the transform\n     *     functions.\n     *   - **status** â€“ `{number}` â€“ HTTP status code of the response.\n     *   - **headers** â€“ `{function([headerName])}` â€“ Header getter function.\n     *   - **config** â€“ `{Object}` â€“ The configuration object that was used to generate the request.\n     *   - **statusText** â€“ `{string}` â€“ HTTP status text of the response.\n     *\n     * @property {Array.<Object>} pendingRequests Array of config objects for currently pending\n     *   requests. This is primarily meant to be used for debugging purposes.\n     *\n     *\n     * @example\n<example module=\"httpExample\">\n<file name=\"index.html\">\n  <div ng-controller=\"FetchController\">\n    <select ng-model=\"method\">\n      <option>GET</option>\n      <option>JSONP</option>\n    </select>\n    <input type=\"text\" ng-model=\"url\" size=\"80\"/>\n    <button id=\"fetchbtn\" ng-click=\"fetch()\">fetch</button><br>\n    <button id=\"samplegetbtn\" ng-click=\"updateModel('GET', 'http-hello.html')\">Sample GET</button>\n    <button id=\"samplejsonpbtn\"\n      ng-click=\"updateModel('JSONP',\n                    'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')\">\n      Sample JSONP\n    </button>\n    <button id=\"invalidjsonpbtn\"\n      ng-click=\"updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')\">\n        Invalid JSONP\n      </button>\n    <pre>http status code: {{status}}</pre>\n    <pre>http response data: {{data}}</pre>\n  </div>\n</file>\n<file name=\"script.js\">\n  angular.module('httpExample', [])\n    .controller('FetchController', ['$scope', '$http', '$templateCache',\n      function($scope, $http, $templateCache) {\n        $scope.method = 'GET';\n        $scope.url = 'http-hello.html';\n\n        $scope.fetch = function() {\n          $scope.code = null;\n          $scope.response = null;\n\n          $http({method: $scope.method, url: $scope.url, cache: $templateCache}).\n            success(function(data, status) {\n              $scope.status = status;\n              $scope.data = data;\n            }).\n            error(function(data, status) {\n              $scope.data = data || \"Request failed\";\n              $scope.status = status;\n          });\n        };\n\n        $scope.updateModel = function(method, url) {\n          $scope.method = method;\n          $scope.url = url;\n        };\n      }]);\n</file>\n<file name=\"http-hello.html\">\n  Hello, $http!\n</file>\n<file name=\"protractor.js\" type=\"protractor\">\n  var status = element(by.binding('status'));\n  var data = element(by.binding('data'));\n  var fetchBtn = element(by.id('fetchbtn'));\n  var sampleGetBtn = element(by.id('samplegetbtn'));\n  var sampleJsonpBtn = element(by.id('samplejsonpbtn'));\n  var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));\n\n  it('should make an xhr GET request', function() {\n    sampleGetBtn.click();\n    fetchBtn.click();\n    expect(status.getText()).toMatch('200');\n    expect(data.getText()).toMatch(/Hello, \\$http!/);\n  });\n\n// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185\n// it('should make a JSONP request to angularjs.org', function() {\n//   sampleJsonpBtn.click();\n//   fetchBtn.click();\n//   expect(status.getText()).toMatch('200');\n//   expect(data.getText()).toMatch(/Super Hero!/);\n// });\n\n  it('should make JSONP request to invalid URL and invoke the error handler',\n      function() {\n    invalidJsonpBtn.click();\n    fetchBtn.click();\n    expect(status.getText()).toMatch('0');\n    expect(data.getText()).toMatch('Request failed');\n  });\n</file>\n</example>\n     */\n    function $http(requestConfig) {\n\n      if (!angular.isObject(requestConfig)) {\n        throw minErr('$http')('badreq', 'Http request configuration must be an object.  Received: {0}', requestConfig);\n      }\n\n      var config = extend({\n        method: 'get',\n        transformRequest: defaults.transformRequest,\n        transformResponse: defaults.transformResponse\n      }, requestConfig);\n\n      config.headers = mergeHeaders(requestConfig);\n      config.method = uppercase(config.method);\n\n      var serverRequest = function(config) {\n        var headers = config.headers;\n        var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);\n\n        // strip content-type if data is undefined\n        if (isUndefined(reqData)) {\n          forEach(headers, function(value, header) {\n            if (lowercase(header) === 'content-type') {\n                delete headers[header];\n            }\n          });\n        }\n\n        if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {\n          config.withCredentials = defaults.withCredentials;\n        }\n\n        // send request\n        return sendReq(config, reqData).then(transformResponse, transformResponse);\n      };\n\n      var chain = [serverRequest, undefined];\n      var promise = $q.when(config);\n\n      // apply interceptors\n      forEach(reversedInterceptors, function(interceptor) {\n        if (interceptor.request || interceptor.requestError) {\n          chain.unshift(interceptor.request, interceptor.requestError);\n        }\n        if (interceptor.response || interceptor.responseError) {\n          chain.push(interceptor.response, interceptor.responseError);\n        }\n      });\n\n      while (chain.length) {\n        var thenFn = chain.shift();\n        var rejectFn = chain.shift();\n\n        promise = promise.then(thenFn, rejectFn);\n      }\n\n      promise.success = function(fn) {\n        promise.then(function(response) {\n          fn(response.data, response.status, response.headers, config);\n        });\n        return promise;\n      };\n\n      promise.error = function(fn) {\n        promise.then(null, function(response) {\n          fn(response.data, response.status, response.headers, config);\n        });\n        return promise;\n      };\n\n      return promise;\n\n      function transformResponse(response) {\n        // make a copy since the response must be cacheable\n        var resp = extend({}, response);\n        if (!response.data) {\n          resp.data = response.data;\n        } else {\n          resp.data = transformData(response.data, response.headers, response.status, config.transformResponse);\n        }\n        return (isSuccess(response.status))\n          ? resp\n          : $q.reject(resp);\n      }\n\n      function executeHeaderFns(headers) {\n        var headerContent, processedHeaders = {};\n\n        forEach(headers, function(headerFn, header) {\n          if (isFunction(headerFn)) {\n            headerContent = headerFn();\n            if (headerContent != null) {\n              processedHeaders[header] = headerContent;\n            }\n          } else {\n            processedHeaders[header] = headerFn;\n          }\n        });\n\n        return processedHeaders;\n      }\n\n      function mergeHeaders(config) {\n        var defHeaders = defaults.headers,\n            reqHeaders = extend({}, config.headers),\n            defHeaderName, lowercaseDefHeaderName, reqHeaderName;\n\n        defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);\n\n        // using for-in instead of forEach to avoid unecessary iteration after header has been found\n        defaultHeadersIteration:\n        for (defHeaderName in defHeaders) {\n          lowercaseDefHeaderName = lowercase(defHeaderName);\n\n          for (reqHeaderName in reqHeaders) {\n            if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {\n              continue defaultHeadersIteration;\n            }\n          }\n\n          reqHeaders[defHeaderName] = defHeaders[defHeaderName];\n        }\n\n        // execute if header value is a function for merged headers\n        return executeHeaderFns(reqHeaders);\n      }\n    }\n\n    $http.pendingRequests = [];\n\n    /**\n     * @ngdoc method\n     * @name $http#get\n     *\n     * @description\n     * Shortcut method to perform `GET` request.\n     *\n     * @param {string} url Relative or absolute URL specifying the destination of the request\n     * @param {Object=} config Optional configuration object\n     * @returns {HttpPromise} Future object\n     */\n\n    /**\n     * @ngdoc method\n     * @name $http#delete\n     *\n     * @description\n     * Shortcut method to perform `DELETE` request.\n     *\n     * @param {string} url Relative or absolute URL specifying the destination of the request\n     * @param {Object=} config Optional configuration object\n     * @returns {HttpPromise} Future object\n     */\n\n    /**\n     * @ngdoc method\n     * @name $http#head\n     *\n     * @description\n     * Shortcut method to perform `HEAD` request.\n     *\n     * @param {string} url Relative or absolute URL specifying the destination of the request\n     * @param {Object=} config Optional configuration object\n     * @returns {HttpPromise} Future object\n     */\n\n    /**\n     * @ngdoc method\n     * @name $http#jsonp\n     *\n     * @description\n     * Shortcut method to perform `JSONP` request.\n     *\n     * @param {string} url Relative or absolute URL specifying the destination of the request.\n     *                     The name of the callback should be the string `JSON_CALLBACK`.\n     * @param {Object=} config Optional configuration object\n     * @returns {HttpPromise} Future object\n     */\n    createShortMethods('get', 'delete', 'head', 'jsonp');\n\n    /**\n     * @ngdoc method\n     * @name $http#post\n     *\n     * @description\n     * Shortcut method to perform `POST` request.\n     *\n     * @param {string} url Relative or absolute URL specifying the destination of the request\n     * @param {*} data Request content\n     * @param {Object=} config Optional configuration object\n     * @returns {HttpPromise} Future object\n     */\n\n    /**\n     * @ngdoc method\n     * @name $http#put\n     *\n     * @description\n     * Shortcut method to perform `PUT` request.\n     *\n     * @param {string} url Relative or absolute URL specifying the destination of the request\n     * @param {*} data Request content\n     * @param {Object=} config Optional configuration object\n     * @returns {HttpPromise} Future object\n     */\n\n     /**\n      * @ngdoc method\n      * @name $http#patch\n      *\n      * @description\n      * Shortcut method to perform `PATCH` request.\n      *\n      * @param {string} url Relative or absolute URL specifying the destination of the request\n      * @param {*} data Request content\n      * @param {Object=} config Optional configuration object\n      * @returns {HttpPromise} Future object\n      */\n    createShortMethodsWithData('post', 'put', 'patch');\n\n        /**\n         * @ngdoc property\n         * @name $http#defaults\n         *\n         * @description\n         * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of\n         * default headers, withCredentials as well as request and response transformations.\n         *\n         * See \"Setting HTTP Headers\" and \"Transforming Requests and Responses\" sections above.\n         */\n    $http.defaults = defaults;\n\n\n    return $http;\n\n\n    function createShortMethods(names) {\n      forEach(arguments, function(name) {\n        $http[name] = function(url, config) {\n          return $http(extend(config || {}, {\n            method: name,\n            url: url\n          }));\n        };\n      });\n    }\n\n\n    function createShortMethodsWithData(name) {\n      forEach(arguments, function(name) {\n        $http[name] = function(url, data, config) {\n          return $http(extend(config || {}, {\n            method: name,\n            url: url,\n            data: data\n          }));\n        };\n      });\n    }\n\n\n    /**\n     * Makes the request.\n     *\n     * !!! ACCESSES CLOSURE VARS:\n     * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests\n     */\n    function sendReq(config, reqData) {\n      var deferred = $q.defer(),\n          promise = deferred.promise,\n          cache,\n          cachedResp,\n          reqHeaders = config.headers,\n          url = buildUrl(config.url, config.params);\n\n      $http.pendingRequests.push(config);\n      promise.then(removePendingReq, removePendingReq);\n\n\n      if ((config.cache || defaults.cache) && config.cache !== false &&\n          (config.method === 'GET' || config.method === 'JSONP')) {\n        cache = isObject(config.cache) ? config.cache\n              : isObject(defaults.cache) ? defaults.cache\n              : defaultCache;\n      }\n\n      if (cache) {\n        cachedResp = cache.get(url);\n        if (isDefined(cachedResp)) {\n          if (isPromiseLike(cachedResp)) {\n            // cached request has already been sent, but there is no response yet\n            cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);\n          } else {\n            // serving from cache\n            if (isArray(cachedResp)) {\n              resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);\n            } else {\n              resolvePromise(cachedResp, 200, {}, 'OK');\n            }\n          }\n        } else {\n          // put the promise for the non-transformed response into cache as a placeholder\n          cache.put(url, promise);\n        }\n      }\n\n\n      // if we won't have the response in cache, set the xsrf headers and\n      // send the request to the backend\n      if (isUndefined(cachedResp)) {\n        var xsrfValue = urlIsSameOrigin(config.url)\n            ? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName]\n            : undefined;\n        if (xsrfValue) {\n          reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;\n        }\n\n        $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,\n            config.withCredentials, config.responseType);\n      }\n\n      return promise;\n\n\n      /**\n       * Callback registered to $httpBackend():\n       *  - caches the response if desired\n       *  - resolves the raw $http promise\n       *  - calls $apply\n       */\n      function done(status, response, headersString, statusText) {\n        if (cache) {\n          if (isSuccess(status)) {\n            cache.put(url, [status, response, parseHeaders(headersString), statusText]);\n          } else {\n            // remove promise from the cache\n            cache.remove(url);\n          }\n        }\n\n        function resolveHttpPromise() {\n          resolvePromise(response, status, headersString, statusText);\n        }\n\n        if (useApplyAsync) {\n          $rootScope.$applyAsync(resolveHttpPromise);\n        } else {\n          resolveHttpPromise();\n          if (!$rootScope.$$phase) $rootScope.$apply();\n        }\n      }\n\n\n      /**\n       * Resolves the raw $http promise.\n       */\n      function resolvePromise(response, status, headers, statusText) {\n        // normalize internal statuses to 0\n        status = Math.max(status, 0);\n\n        (isSuccess(status) ? deferred.resolve : deferred.reject)({\n          data: response,\n          status: status,\n          headers: headersGetter(headers),\n          config: config,\n          statusText: statusText\n        });\n      }\n\n      function resolvePromiseWithResult(result) {\n        resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);\n      }\n\n      function removePendingReq() {\n        var idx = $http.pendingRequests.indexOf(config);\n        if (idx !== -1) $http.pendingRequests.splice(idx, 1);\n      }\n    }\n\n\n    function buildUrl(url, params) {\n      if (!params) return url;\n      var parts = [];\n      forEachSorted(params, function(value, key) {\n        if (value === null || isUndefined(value)) return;\n        if (!isArray(value)) value = [value];\n\n        forEach(value, function(v) {\n          if (isObject(v)) {\n            if (isDate(v)) {\n              v = v.toISOString();\n            } else {\n              v = toJson(v);\n            }\n          }\n          parts.push(encodeUriQuery(key) + '=' +\n                     encodeUriQuery(v));\n        });\n      });\n      if (parts.length > 0) {\n        url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');\n      }\n      return url;\n    }\n  }];\n}\n\nfunction createXhr() {\n    return new window.XMLHttpRequest();\n}\n\n/**\n * @ngdoc service\n * @name $httpBackend\n * @requires $window\n * @requires $document\n *\n * @description\n * HTTP backend used by the {@link ng.$http service} that delegates to\n * XMLHttpRequest object or JSONP and deals with browser incompatibilities.\n *\n * You should never need to use this service directly, instead use the higher-level abstractions:\n * {@link ng.$http $http} or {@link ngResource.$resource $resource}.\n *\n * During testing this implementation is swapped with {@link ngMock.$httpBackend mock\n * $httpBackend} which can be trained with responses.\n */\nfunction $HttpBackendProvider() {\n  this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {\n    return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]);\n  }];\n}\n\nfunction createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {\n  // TODO(vojta): fix the signature\n  return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {\n    $browser.$$incOutstandingRequestCount();\n    url = url || $browser.url();\n\n    if (lowercase(method) == 'jsonp') {\n      var callbackId = '_' + (callbacks.counter++).toString(36);\n      callbacks[callbackId] = function(data) {\n        callbacks[callbackId].data = data;\n        callbacks[callbackId].called = true;\n      };\n\n      var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),\n          callbackId, function(status, text) {\n        completeRequest(callback, status, callbacks[callbackId].data, \"\", text);\n        callbacks[callbackId] = noop;\n      });\n    } else {\n\n      var xhr = createXhr();\n\n      xhr.open(method, url, true);\n      forEach(headers, function(value, key) {\n        if (isDefined(value)) {\n            xhr.setRequestHeader(key, value);\n        }\n      });\n\n      xhr.onload = function requestLoaded() {\n        var statusText = xhr.statusText || '';\n\n        // responseText is the old-school way of retrieving response (supported by IE8 & 9)\n        // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)\n        var response = ('response' in xhr) ? xhr.response : xhr.responseText;\n\n        // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)\n        var status = xhr.status === 1223 ? 204 : xhr.status;\n\n        // fix status code when it is 0 (0 status is undocumented).\n        // Occurs when accessing file resources or on Android 4.1 stock browser\n        // while retrieving files from application cache.\n        if (status === 0) {\n          status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;\n        }\n\n        completeRequest(callback,\n            status,\n            response,\n            xhr.getAllResponseHeaders(),\n            statusText);\n      };\n\n      var requestError = function() {\n        // The response is always empty\n        // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error\n        completeRequest(callback, -1, null, null, '');\n      };\n\n      xhr.onerror = requestError;\n      xhr.onabort = requestError;\n\n      if (withCredentials) {\n        xhr.withCredentials = true;\n      }\n\n      if (responseType) {\n        try {\n          xhr.responseType = responseType;\n        } catch (e) {\n          // WebKit added support for the json responseType value on 09/03/2013\n          // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are\n          // known to throw when setting the value \"json\" as the response type. Other older\n          // browsers implementing the responseType\n          //\n          // The json response type can be ignored if not supported, because JSON payloads are\n          // parsed on the client-side regardless.\n          if (responseType !== 'json') {\n            throw e;\n          }\n        }\n      }\n\n      xhr.send(post || null);\n    }\n\n    if (timeout > 0) {\n      var timeoutId = $browserDefer(timeoutRequest, timeout);\n    } else if (isPromiseLike(timeout)) {\n      timeout.then(timeoutRequest);\n    }\n\n\n    function timeoutRequest() {\n      jsonpDone && jsonpDone();\n      xhr && xhr.abort();\n    }\n\n    function completeRequest(callback, status, response, headersString, statusText) {\n      // cancel timeout and subsequent timeout promise resolution\n      if (timeoutId !== undefined) {\n        $browserDefer.cancel(timeoutId);\n      }\n      jsonpDone = xhr = null;\n\n      callback(status, response, headersString, statusText);\n      $browser.$$completeOutstandingRequest(noop);\n    }\n  };\n\n  function jsonpReq(url, callbackId, done) {\n    // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.:\n    // - fetches local scripts via XHR and evals them\n    // - adds and immediately removes script elements from the document\n    var script = rawDocument.createElement('script'), callback = null;\n    script.type = \"text/javascript\";\n    script.src = url;\n    script.async = true;\n\n    callback = function(event) {\n      removeEventListenerFn(script, \"load\", callback);\n      removeEventListenerFn(script, \"error\", callback);\n      rawDocument.body.removeChild(script);\n      script = null;\n      var status = -1;\n      var text = \"unknown\";\n\n      if (event) {\n        if (event.type === \"load\" && !callbacks[callbackId].called) {\n          event = { type: \"error\" };\n        }\n        text = event.type;\n        status = event.type === \"error\" ? 404 : 200;\n      }\n\n      if (done) {\n        done(status, text);\n      }\n    };\n\n    addEventListenerFn(script, \"load\", callback);\n    addEventListenerFn(script, \"error\", callback);\n    rawDocument.body.appendChild(script);\n    return callback;\n  }\n}\n\nvar $interpolateMinErr = minErr('$interpolate');\n\n/**\n * @ngdoc provider\n * @name $interpolateProvider\n *\n * @description\n *\n * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.\n *\n * @example\n<example module=\"customInterpolationApp\">\n<file name=\"index.html\">\n<script>\n  var customInterpolationApp = angular.module('customInterpolationApp', []);\n\n  customInterpolationApp.config(function($interpolateProvider) {\n    $interpolateProvider.startSymbol('//');\n    $interpolateProvider.endSymbol('//');\n  });\n\n\n  customInterpolationApp.controller('DemoController', function() {\n      this.label = \"This binding is brought you by // interpolation symbols.\";\n  });\n</script>\n<div ng-app=\"App\" ng-controller=\"DemoController as demo\">\n    //demo.label//\n</div>\n</file>\n<file name=\"protractor.js\" type=\"protractor\">\n  it('should interpolate binding with custom symbols', function() {\n    expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');\n  });\n</file>\n</example>\n */\nfunction $InterpolateProvider() {\n  var startSymbol = '{{';\n  var endSymbol = '}}';\n\n  /**\n   * @ngdoc method\n   * @name $interpolateProvider#startSymbol\n   * @description\n   * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.\n   *\n   * @param {string=} value new value to set the starting symbol to.\n   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.\n   */\n  this.startSymbol = function(value) {\n    if (value) {\n      startSymbol = value;\n      return this;\n    } else {\n      return startSymbol;\n    }\n  };\n\n  /**\n   * @ngdoc method\n   * @name $interpolateProvider#endSymbol\n   * @description\n   * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.\n   *\n   * @param {string=} value new value to set the ending symbol to.\n   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.\n   */\n  this.endSymbol = function(value) {\n    if (value) {\n      endSymbol = value;\n      return this;\n    } else {\n      return endSymbol;\n    }\n  };\n\n\n  this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {\n    var startSymbolLength = startSymbol.length,\n        endSymbolLength = endSymbol.length,\n        escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),\n        escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');\n\n    function escape(ch) {\n      return '\\\\\\\\\\\\' + ch;\n    }\n\n    /**\n     * @ngdoc service\n     * @name $interpolate\n     * @kind function\n     *\n     * @requires $parse\n     * @requires $sce\n     *\n     * @description\n     *\n     * Compiles a string with markup into an interpolation function. This service is used by the\n     * HTML {@link ng.$compile $compile} service for data binding. See\n     * {@link ng.$interpolateProvider $interpolateProvider} for configuring the\n     * interpolation markup.\n     *\n     *\n     * ```js\n     *   var $interpolate = ...; // injected\n     *   var exp = $interpolate('Hello {{name | uppercase}}!');\n     *   expect(exp({name:'Angular'}).toEqual('Hello ANGULAR!');\n     * ```\n     *\n     * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is\n     * `true`, the interpolation function will return `undefined` unless all embedded expressions\n     * evaluate to a value other than `undefined`.\n     *\n     * ```js\n     *   var $interpolate = ...; // injected\n     *   var context = {greeting: 'Hello', name: undefined };\n     *\n     *   // default \"forgiving\" mode\n     *   var exp = $interpolate('{{greeting}} {{name}}!');\n     *   expect(exp(context)).toEqual('Hello !');\n     *\n     *   // \"allOrNothing\" mode\n     *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);\n     *   expect(exp(context)).toBeUndefined();\n     *   context.name = 'Angular';\n     *   expect(exp(context)).toEqual('Hello Angular!');\n     * ```\n     *\n     * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.\n     *\n     * ####Escaped Interpolation\n     * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers\n     * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).\n     * It will be rendered as a regular start/end marker, and will not be interpreted as an expression\n     * or binding.\n     *\n     * This enables web-servers to prevent script injection attacks and defacing attacks, to some\n     * degree, while also enabling code examples to work without relying on the\n     * {@link ng.directive:ngNonBindable ngNonBindable} directive.\n     *\n     * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,\n     * replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all\n     * interpolation start/end markers with their escaped counterparts.**\n     *\n     * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered\n     * output when the $interpolate service processes the text. So, for HTML elements interpolated\n     * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter\n     * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,\n     * this is typically useful only when user-data is used in rendering a template from the server, or\n     * when otherwise untrusted data is used by a directive.\n     *\n     * <example>\n     *  <file name=\"index.html\">\n     *    <div ng-init=\"username='A user'\">\n     *      <p ng-init=\"apptitle='Escaping demo'\">{{apptitle}}: \\{\\{ username = \"defaced value\"; \\}\\}\n     *        </p>\n     *      <p><strong>{{username}}</strong> attempts to inject code which will deface the\n     *        application, but fails to accomplish their task, because the server has correctly\n     *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)\n     *        characters.</p>\n     *      <p>Instead, the result of the attempted script injection is visible, and can be removed\n     *        from the database by an administrator.</p>\n     *    </div>\n     *  </file>\n     * </example>\n     *\n     * @param {string} text The text with markup to interpolate.\n     * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have\n     *    embedded expression in order to return an interpolation function. Strings with no\n     *    embedded expression will return null for the interpolation function.\n     * @param {string=} trustedContext when provided, the returned function passes the interpolated\n     *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,\n     *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that\n     *    provides Strict Contextual Escaping for details.\n     * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined\n     *    unless all embedded expressions evaluate to a value other than `undefined`.\n     * @returns {function(context)} an interpolation function which is used to compute the\n     *    interpolated string. The function has these parameters:\n     *\n     * - `context`: evaluation context for all expressions embedded in the interpolated text\n     */\n    function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {\n      allOrNothing = !!allOrNothing;\n      var startIndex,\n          endIndex,\n          index = 0,\n          expressions = [],\n          parseFns = [],\n          textLength = text.length,\n          exp,\n          concat = [],\n          expressionPositions = [];\n\n      while (index < textLength) {\n        if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&\n             ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {\n          if (index !== startIndex) {\n            concat.push(unescapeText(text.substring(index, startIndex)));\n          }\n          exp = text.substring(startIndex + startSymbolLength, endIndex);\n          expressions.push(exp);\n          parseFns.push($parse(exp, parseStringifyInterceptor));\n          index = endIndex + endSymbolLength;\n          expressionPositions.push(concat.length);\n          concat.push('');\n        } else {\n          // we did not find an interpolation, so we have to add the remainder to the separators array\n          if (index !== textLength) {\n            concat.push(unescapeText(text.substring(index)));\n          }\n          break;\n        }\n      }\n\n      // Concatenating expressions makes it hard to reason about whether some combination of\n      // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a\n      // single expression be used for iframe[src], object[src], etc., we ensure that the value\n      // that's used is assigned or constructed by some JS code somewhere that is more testable or\n      // make it obvious that you bound the value to some user controlled value.  This helps reduce\n      // the load when auditing for XSS issues.\n      if (trustedContext && concat.length > 1) {\n          throw $interpolateMinErr('noconcat',\n              \"Error while interpolating: {0}\\nStrict Contextual Escaping disallows \" +\n              \"interpolations that concatenate multiple expressions when a trusted value is \" +\n              \"required.  See http://docs.angularjs.org/api/ng.$sce\", text);\n      }\n\n      if (!mustHaveExpression || expressions.length) {\n        var compute = function(values) {\n          for (var i = 0, ii = expressions.length; i < ii; i++) {\n            if (allOrNothing && isUndefined(values[i])) return;\n            concat[expressionPositions[i]] = values[i];\n          }\n          return concat.join('');\n        };\n\n        var getValue = function(value) {\n          return trustedContext ?\n            $sce.getTrusted(trustedContext, value) :\n            $sce.valueOf(value);\n        };\n\n        var stringify = function(value) {\n          if (value == null) { // null || undefined\n            return '';\n          }\n          switch (typeof value) {\n            case 'string':\n              break;\n            case 'number':\n              value = '' + value;\n              break;\n            default:\n              value = toJson(value);\n          }\n\n          return value;\n        };\n\n        return extend(function interpolationFn(context) {\n            var i = 0;\n            var ii = expressions.length;\n            var values = new Array(ii);\n\n            try {\n              for (; i < ii; i++) {\n                values[i] = parseFns[i](context);\n              }\n\n              return compute(values);\n            } catch (err) {\n              var newErr = $interpolateMinErr('interr', \"Can't interpolate: {0}\\n{1}\", text,\n                  err.toString());\n              $exceptionHandler(newErr);\n            }\n\n          }, {\n          // all of these properties are undocumented for now\n          exp: text, //just for compatibility with regular watchers created via $watch\n          expressions: expressions,\n          $$watchDelegate: function(scope, listener, objectEquality) {\n            var lastValue;\n            return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {\n              var currValue = compute(values);\n              if (isFunction(listener)) {\n                listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);\n              }\n              lastValue = currValue;\n            }, objectEquality);\n          }\n        });\n      }\n\n      function unescapeText(text) {\n        return text.replace(escapedStartRegexp, startSymbol).\n          replace(escapedEndRegexp, endSymbol);\n      }\n\n      function parseStringifyInterceptor(value) {\n        try {\n          value = getValue(value);\n          return allOrNothing && !isDefined(value) ? value : stringify(value);\n        } catch (err) {\n          var newErr = $interpolateMinErr('interr', \"Can't interpolate: {0}\\n{1}\", text,\n            err.toString());\n          $exceptionHandler(newErr);\n        }\n      }\n    }\n\n\n    /**\n     * @ngdoc method\n     * @name $interpolate#startSymbol\n     * @description\n     * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.\n     *\n     * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change\n     * the symbol.\n     *\n     * @returns {string} start symbol.\n     */\n    $interpolate.startSymbol = function() {\n      return startSymbol;\n    };\n\n\n    /**\n     * @ngdoc method\n     * @name $interpolate#endSymbol\n     * @description\n     * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.\n     *\n     * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change\n     * the symbol.\n     *\n     * @returns {string} end symbol.\n     */\n    $interpolate.endSymbol = function() {\n      return endSymbol;\n    };\n\n    return $interpolate;\n  }];\n}\n\nfunction $IntervalProvider() {\n  this.$get = ['$rootScope', '$window', '$q', '$$q',\n       function($rootScope,   $window,   $q,   $$q) {\n    var intervals = {};\n\n\n     /**\n      * @ngdoc service\n      * @name $interval\n      *\n      * @description\n      * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`\n      * milliseconds.\n      *\n      * The return value of registering an interval function is a promise. This promise will be\n      * notified upon each tick of the interval, and will be resolved after `count` iterations, or\n      * run indefinitely if `count` is not defined. The value of the notification will be the\n      * number of iterations that have run.\n      * To cancel an interval, call `$interval.cancel(promise)`.\n      *\n      * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to\n      * move forward by `millis` milliseconds and trigger any functions scheduled to run in that\n      * time.\n      *\n      * <div class=\"alert alert-warning\">\n      * **Note**: Intervals created by this service must be explicitly destroyed when you are finished\n      * with them.  In particular they are not automatically destroyed when a controller's scope or a\n      * directive's element are destroyed.\n      * You should take this into consideration and make sure to always cancel the interval at the\n      * appropriate moment.  See the example below for more details on how and when to do this.\n      * </div>\n      *\n      * @param {function()} fn A function that should be called repeatedly.\n      * @param {number} delay Number of milliseconds between each function call.\n      * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat\n      *   indefinitely.\n      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise\n      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.\n      * @returns {promise} A promise which will be notified on each iteration.\n      *\n      * @example\n      * <example module=\"intervalExample\">\n      * <file name=\"index.html\">\n      *   <script>\n      *     angular.module('intervalExample', [])\n      *       .controller('ExampleController', ['$scope', '$interval',\n      *         function($scope, $interval) {\n      *           $scope.format = 'M/d/yy h:mm:ss a';\n      *           $scope.blood_1 = 100;\n      *           $scope.blood_2 = 120;\n      *\n      *           var stop;\n      *           $scope.fight = function() {\n      *             // Don't start a new fight if we are already fighting\n      *             if ( angular.isDefined(stop) ) return;\n      *\n      *             stop = $interval(function() {\n      *               if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {\n      *                 $scope.blood_1 = $scope.blood_1 - 3;\n      *                 $scope.blood_2 = $scope.blood_2 - 4;\n      *               } else {\n      *                 $scope.stopFight();\n      *               }\n      *             }, 100);\n      *           };\n      *\n      *           $scope.stopFight = function() {\n      *             if (angular.isDefined(stop)) {\n      *               $interval.cancel(stop);\n      *               stop = undefined;\n      *             }\n      *           };\n      *\n      *           $scope.resetFight = function() {\n      *             $scope.blood_1 = 100;\n      *             $scope.blood_2 = 120;\n      *           };\n      *\n      *           $scope.$on('$destroy', function() {\n      *             // Make sure that the interval is destroyed too\n      *             $scope.stopFight();\n      *           });\n      *         }])\n      *       // Register the 'myCurrentTime' directive factory method.\n      *       // We inject $interval and dateFilter service since the factory method is DI.\n      *       .directive('myCurrentTime', ['$interval', 'dateFilter',\n      *         function($interval, dateFilter) {\n      *           // return the directive link function. (compile function not needed)\n      *           return function(scope, element, attrs) {\n      *             var format,  // date format\n      *                 stopTime; // so that we can cancel the time updates\n      *\n      *             // used to update the UI\n      *             function updateTime() {\n      *               element.text(dateFilter(new Date(), format));\n      *             }\n      *\n      *             // watch the expression, and update the UI on change.\n      *             scope.$watch(attrs.myCurrentTime, function(value) {\n      *               format = value;\n      *               updateTime();\n      *             });\n      *\n      *             stopTime = $interval(updateTime, 1000);\n      *\n      *             // listen on DOM destroy (removal) event, and cancel the next UI update\n      *             // to prevent updating time after the DOM element was removed.\n      *             element.on('$destroy', function() {\n      *               $interval.cancel(stopTime);\n      *             });\n      *           }\n      *         }]);\n      *   </script>\n      *\n      *   <div>\n      *     <div ng-controller=\"ExampleController\">\n      *       Date format: <input ng-model=\"format\"> <hr/>\n      *       Current time is: <span my-current-time=\"format\"></span>\n      *       <hr/>\n      *       Blood 1 : <font color='red'>{{blood_1}}</font>\n      *       Blood 2 : <font color='red'>{{blood_2}}</font>\n      *       <button type=\"button\" data-ng-click=\"fight()\">Fight</button>\n      *       <button type=\"button\" data-ng-click=\"stopFight()\">StopFight</button>\n      *       <button type=\"button\" data-ng-click=\"resetFight()\">resetFight</button>\n      *     </div>\n      *   </div>\n      *\n      * </file>\n      * </example>\n      */\n    function interval(fn, delay, count, invokeApply) {\n      var setInterval = $window.setInterval,\n          clearInterval = $window.clearInterval,\n          iteration = 0,\n          skipApply = (isDefined(invokeApply) && !invokeApply),\n          deferred = (skipApply ? $$q : $q).defer(),\n          promise = deferred.promise;\n\n      count = isDefined(count) ? count : 0;\n\n      promise.then(null, null, fn);\n\n      promise.$$intervalId = setInterval(function tick() {\n        deferred.notify(iteration++);\n\n        if (count > 0 && iteration >= count) {\n          deferred.resolve(iteration);\n          clearInterval(promise.$$intervalId);\n          delete intervals[promise.$$intervalId];\n        }\n\n        if (!skipApply) $rootScope.$apply();\n\n      }, delay);\n\n      intervals[promise.$$intervalId] = deferred;\n\n      return promise;\n    }\n\n\n     /**\n      * @ngdoc method\n      * @name $interval#cancel\n      *\n      * @description\n      * Cancels a task associated with the `promise`.\n      *\n      * @param {promise} promise returned by the `$interval` function.\n      * @returns {boolean} Returns `true` if the task was successfully canceled.\n      */\n    interval.cancel = function(promise) {\n      if (promise && promise.$$intervalId in intervals) {\n        intervals[promise.$$intervalId].reject('canceled');\n        $window.clearInterval(promise.$$intervalId);\n        delete intervals[promise.$$intervalId];\n        return true;\n      }\n      return false;\n    };\n\n    return interval;\n  }];\n}\n\n/**\n * @ngdoc service\n * @name $locale\n *\n * @description\n * $locale service provides localization rules for various Angular components. As of right now the\n * only public api is:\n *\n * * `id` â€“ `{string}` â€“ locale id formatted as `languageId-countryId` (e.g. `en-us`)\n */\nfunction $LocaleProvider() {\n  this.$get = function() {\n    return {\n      id: 'en-us',\n\n      NUMBER_FORMATS: {\n        DECIMAL_SEP: '.',\n        GROUP_SEP: ',',\n        PATTERNS: [\n          { // Decimal Pattern\n            minInt: 1,\n            minFrac: 0,\n            maxFrac: 3,\n            posPre: '',\n            posSuf: '',\n            negPre: '-',\n            negSuf: '',\n            gSize: 3,\n            lgSize: 3\n          },{ //Currency Pattern\n            minInt: 1,\n            minFrac: 2,\n            maxFrac: 2,\n            posPre: '\\u00A4',\n            posSuf: '',\n            negPre: '(\\u00A4',\n            negSuf: ')',\n            gSize: 3,\n            lgSize: 3\n          }\n        ],\n        CURRENCY_SYM: '$'\n      },\n\n      DATETIME_FORMATS: {\n        MONTH:\n            'January,February,March,April,May,June,July,August,September,October,November,December'\n            .split(','),\n        SHORTMONTH:  'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','),\n        DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','),\n        SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','),\n        AMPMS: ['AM','PM'],\n        medium: 'MMM d, y h:mm:ss a',\n        'short': 'M/d/yy h:mm a',\n        fullDate: 'EEEE, MMMM d, y',\n        longDate: 'MMMM d, y',\n        mediumDate: 'MMM d, y',\n        shortDate: 'M/d/yy',\n        mediumTime: 'h:mm:ss a',\n        shortTime: 'h:mm a',\n        ERANAMES: [\n          \"Before Christ\",\n          \"Anno Domini\"\n        ],\n        ERAS: [\n          \"BC\",\n          \"AD\"\n        ]\n      },\n\n      pluralCat: function(num) {\n        if (num === 1) {\n          return 'one';\n        }\n        return 'other';\n      }\n    };\n  };\n}\n\nvar PATH_MATCH = /^([^\\?#]*)(\\?([^#]*))?(#(.*))?$/,\n    DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};\nvar $locationMinErr = minErr('$location');\n\n\n/**\n * Encode path using encodeUriSegment, ignoring forward slashes\n *\n * @param {string} path Path to encode\n * @returns {string}\n */\nfunction encodePath(path) {\n  var segments = path.split('/'),\n      i = segments.length;\n\n  while (i--) {\n    segments[i] = encodeUriSegment(segments[i]);\n  }\n\n  return segments.join('/');\n}\n\nfunction parseAbsoluteUrl(absoluteUrl, locationObj) {\n  var parsedUrl = urlResolve(absoluteUrl);\n\n  locationObj.$$protocol = parsedUrl.protocol;\n  locationObj.$$host = parsedUrl.hostname;\n  locationObj.$$port = int(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;\n}\n\n\nfunction parseAppUrl(relativeUrl, locationObj) {\n  var prefixed = (relativeUrl.charAt(0) !== '/');\n  if (prefixed) {\n    relativeUrl = '/' + relativeUrl;\n  }\n  var match = urlResolve(relativeUrl);\n  locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?\n      match.pathname.substring(1) : match.pathname);\n  locationObj.$$search = parseKeyValue(match.search);\n  locationObj.$$hash = decodeURIComponent(match.hash);\n\n  // make sure path starts with '/';\n  if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {\n    locationObj.$$path = '/' + locationObj.$$path;\n  }\n}\n\n\n/**\n *\n * @param {string} begin\n * @param {string} whole\n * @returns {string} returns text from whole after begin or undefined if it does not begin with\n *                   expected string.\n */\nfunction beginsWith(begin, whole) {\n  if (whole.indexOf(begin) === 0) {\n    return whole.substr(begin.length);\n  }\n}\n\n\nfunction stripHash(url) {\n  var index = url.indexOf('#');\n  return index == -1 ? url : url.substr(0, index);\n}\n\nfunction trimEmptyHash(url) {\n  return url.replace(/(#.+)|#$/, '$1');\n}\n\n\nfunction stripFile(url) {\n  return url.substr(0, stripHash(url).lastIndexOf('/') + 1);\n}\n\n/* return the server only (scheme://host:port) */\nfunction serverBase(url) {\n  return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));\n}\n\n\n/**\n * LocationHtml5Url represents an url\n * This object is exposed as $location service when HTML5 mode is enabled and supported\n *\n * @constructor\n * @param {string} appBase application base URL\n * @param {string} basePrefix url path prefix\n */\nfunction LocationHtml5Url(appBase, basePrefix) {\n  this.$$html5 = true;\n  basePrefix = basePrefix || '';\n  var appBaseNoFile = stripFile(appBase);\n  parseAbsoluteUrl(appBase, this);\n\n\n  /**\n   * Parse given html5 (regular) url string into properties\n   * @param {string} url HTML5 url\n   * @private\n   */\n  this.$$parse = function(url) {\n    var pathUrl = beginsWith(appBaseNoFile, url);\n    if (!isString(pathUrl)) {\n      throw $locationMinErr('ipthprfx', 'Invalid url \"{0}\", missing path prefix \"{1}\".', url,\n          appBaseNoFile);\n    }\n\n    parseAppUrl(pathUrl, this);\n\n    if (!this.$$path) {\n      this.$$path = '/';\n    }\n\n    this.$$compose();\n  };\n\n  /**\n   * Compose url and update `absUrl` property\n   * @private\n   */\n  this.$$compose = function() {\n    var search = toKeyValue(this.$$search),\n        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';\n\n    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;\n    this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'\n  };\n\n  this.$$parseLinkUrl = function(url, relHref) {\n    if (relHref && relHref[0] === '#') {\n      // special case for links to hash fragments:\n      // keep the old url and only replace the hash fragment\n      this.hash(relHref.slice(1));\n      return true;\n    }\n    var appUrl, prevAppUrl;\n    var rewrittenUrl;\n\n    if ((appUrl = beginsWith(appBase, url)) !== undefined) {\n      prevAppUrl = appUrl;\n      if ((appUrl = beginsWith(basePrefix, appUrl)) !== undefined) {\n        rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);\n      } else {\n        rewrittenUrl = appBase + prevAppUrl;\n      }\n    } else if ((appUrl = beginsWith(appBaseNoFile, url)) !== undefined) {\n      rewrittenUrl = appBaseNoFile + appUrl;\n    } else if (appBaseNoFile == url + '/') {\n      rewrittenUrl = appBaseNoFile;\n    }\n    if (rewrittenUrl) {\n      this.$$parse(rewrittenUrl);\n    }\n    return !!rewrittenUrl;\n  };\n}\n\n\n/**\n * LocationHashbangUrl represents url\n * This object is exposed as $location service when developer doesn't opt into html5 mode.\n * It also serves as the base class for html5 mode fallback on legacy browsers.\n *\n * @constructor\n * @param {string} appBase application base URL\n * @param {string} hashPrefix hashbang prefix\n */\nfunction LocationHashbangUrl(appBase, hashPrefix) {\n  var appBaseNoFile = stripFile(appBase);\n\n  parseAbsoluteUrl(appBase, this);\n\n\n  /**\n   * Parse given hashbang url into properties\n   * @param {string} url Hashbang url\n   * @private\n   */\n  this.$$parse = function(url) {\n    var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);\n    var withoutHashUrl;\n\n    if (withoutBaseUrl.charAt(0) === '#') {\n\n      // The rest of the url starts with a hash so we have\n      // got either a hashbang path or a plain hash fragment\n      withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);\n      if (isUndefined(withoutHashUrl)) {\n        // There was no hashbang prefix so we just have a hash fragment\n        withoutHashUrl = withoutBaseUrl;\n      }\n\n    } else {\n      // There was no hashbang path nor hash fragment:\n      // If we are in HTML5 mode we use what is left as the path;\n      // Otherwise we ignore what is left\n      withoutHashUrl = this.$$html5 ? withoutBaseUrl : '';\n    }\n\n    parseAppUrl(withoutHashUrl, this);\n\n    this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);\n\n    this.$$compose();\n\n    /*\n     * In Windows, on an anchor node on documents loaded from\n     * the filesystem, the browser will return a pathname\n     * prefixed with the drive name ('/C:/path') when a\n     * pathname without a drive is set:\n     *  * a.setAttribute('href', '/foo')\n     *   * a.pathname === '/C:/foo' //true\n     *\n     * Inside of Angular, we're always using pathnames that\n     * do not include drive names for routing.\n     */\n    function removeWindowsDriveName(path, url, base) {\n      /*\n      Matches paths for file protocol on windows,\n      such as /C:/foo/bar, and captures only /foo/bar.\n      */\n      var windowsFilePathExp = /^\\/[A-Z]:(\\/.*)/;\n\n      var firstPathSegmentMatch;\n\n      //Get the relative path from the input URL.\n      if (url.indexOf(base) === 0) {\n        url = url.replace(base, '');\n      }\n\n      // The input URL intentionally contains a first path segment that ends with a colon.\n      if (windowsFilePathExp.exec(url)) {\n        return path;\n      }\n\n      firstPathSegmentMatch = windowsFilePathExp.exec(path);\n      return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;\n    }\n  };\n\n  /**\n   * Compose hashbang url and update `absUrl` property\n   * @private\n   */\n  this.$$compose = function() {\n    var search = toKeyValue(this.$$search),\n        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';\n\n    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;\n    this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');\n  };\n\n  this.$$parseLinkUrl = function(url, relHref) {\n    if (stripHash(appBase) == stripHash(url)) {\n      this.$$parse(url);\n      return true;\n    }\n    return false;\n  };\n}\n\n\n/**\n * LocationHashbangUrl represents url\n * This object is exposed as $location service when html5 history api is enabled but the browser\n * does not support it.\n *\n * @constructor\n * @param {string} appBase application base URL\n * @param {string} hashPrefix hashbang prefix\n */\nfunction LocationHashbangInHtml5Url(appBase, hashPrefix) {\n  this.$$html5 = true;\n  LocationHashbangUrl.apply(this, arguments);\n\n  var appBaseNoFile = stripFile(appBase);\n\n  this.$$parseLinkUrl = function(url, relHref) {\n    if (relHref && relHref[0] === '#') {\n      // special case for links to hash fragments:\n      // keep the old url and only replace the hash fragment\n      this.hash(relHref.slice(1));\n      return true;\n    }\n\n    var rewrittenUrl;\n    var appUrl;\n\n    if (appBase == stripHash(url)) {\n      rewrittenUrl = url;\n    } else if ((appUrl = beginsWith(appBaseNoFile, url))) {\n      rewrittenUrl = appBase + hashPrefix + appUrl;\n    } else if (appBaseNoFile === url + '/') {\n      rewrittenUrl = appBaseNoFile;\n    }\n    if (rewrittenUrl) {\n      this.$$parse(rewrittenUrl);\n    }\n    return !!rewrittenUrl;\n  };\n\n  this.$$compose = function() {\n    var search = toKeyValue(this.$$search),\n        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';\n\n    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;\n    // include hashPrefix in $$absUrl when $$url is empty so IE8 & 9 do not reload page because of removal of '#'\n    this.$$absUrl = appBase + hashPrefix + this.$$url;\n  };\n\n}\n\n\nvar locationPrototype = {\n\n  /**\n   * Are we in html5 mode?\n   * @private\n   */\n  $$html5: false,\n\n  /**\n   * Has any change been replacing?\n   * @private\n   */\n  $$replace: false,\n\n  /**\n   * @ngdoc method\n   * @name $location#absUrl\n   *\n   * @description\n   * This method is getter only.\n   *\n   * Return full url representation with all segments encoded according to rules specified in\n   * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).\n   *\n   *\n   * ```js\n   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo\n   * var absUrl = $location.absUrl();\n   * // => \"http://example.com/#/some/path?foo=bar&baz=xoxo\"\n   * ```\n   *\n   * @return {string} full url\n   */\n  absUrl: locationGetter('$$absUrl'),\n\n  /**\n   * @ngdoc method\n   * @name $location#url\n   *\n   * @description\n   * This method is getter / setter.\n   *\n   * Return url (e.g. `/path?a=b#hash`) when called without any parameter.\n   *\n   * Change path, search and hash, when called with parameter and return `$location`.\n   *\n   *\n   * ```js\n   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo\n   * var url = $location.url();\n   * // => \"/some/path?foo=bar&baz=xoxo\"\n   * ```\n   *\n   * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)\n   * @return {string} url\n   */\n  url: function(url) {\n    if (isUndefined(url))\n      return this.$$url;\n\n    var match = PATH_MATCH.exec(url);\n    if (match[1] || url === '') this.path(decodeURIComponent(match[1]));\n    if (match[2] || match[1] || url === '') this.search(match[3] || '');\n    this.hash(match[5] || '');\n\n    return this;\n  },\n\n  /**\n   * @ngdoc method\n   * @name $location#protocol\n   *\n   * @description\n   * This method is getter only.\n   *\n   * Return protocol of current url.\n   *\n   *\n   * ```js\n   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo\n   * var protocol = $location.protocol();\n   * // => \"http\"\n   * ```\n   *\n   * @return {string} protocol of current url\n   */\n  protocol: locationGetter('$$protocol'),\n\n  /**\n   * @ngdoc method\n   * @name $location#host\n   *\n   * @description\n   * This method is getter only.\n   *\n   * Return host of current url.\n   *\n   *\n   * ```js\n   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo\n   * var host = $location.host();\n   * // => \"example.com\"\n   * ```\n   *\n   * @return {string} host of current url.\n   */\n  host: locationGetter('$$host'),\n\n  /**\n   * @ngdoc method\n   * @name $location#port\n   *\n   * @description\n   * This method is getter only.\n   *\n   * Return port of current url.\n   *\n   *\n   * ```js\n   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo\n   * var port = $location.port();\n   * // => 80\n   * ```\n   *\n   * @return {Number} port\n   */\n  port: locationGetter('$$port'),\n\n  /**\n   * @ngdoc method\n   * @name $location#path\n   *\n   * @description\n   * This method is getter / setter.\n   *\n   * Return path of current url when called without any parameter.\n   *\n   * Change path when called with parameter and return `$location`.\n   *\n   * Note: Path should always begin with forward slash (/), this method will add the forward slash\n   * if it is missing.\n   *\n   *\n   * ```js\n   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo\n   * var path = $location.path();\n   * // => \"/some/path\"\n   * ```\n   *\n   * @param {(string|number)=} path New path\n   * @return {string} path\n   */\n  path: locationGetterSetter('$$path', function(path) {\n    path = path !== null ? path.toString() : '';\n    return path.charAt(0) == '/' ? path : '/' + path;\n  }),\n\n  /**\n   * @ngdoc method\n   * @name $location#search\n   *\n   * @description\n   * This method is getter / setter.\n   *\n   * Return search part (as object) of current url when called without any parameter.\n   *\n   * Change search part when called with parameter and return `$location`.\n   *\n   *\n   * ```js\n   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo\n   * var searchObject = $location.search();\n   * // => {foo: 'bar', baz: 'xoxo'}\n   *\n   * // set foo to 'yipee'\n   * $location.search('foo', 'yipee');\n   * // $location.search() => {foo: 'yipee', baz: 'xoxo'}\n   * ```\n   *\n   * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or\n   * hash object.\n   *\n   * When called with a single argument the method acts as a setter, setting the `search` component\n   * of `$location` to the specified value.\n   *\n   * If the argument is a hash object containing an array of values, these values will be encoded\n   * as duplicate search parameters in the url.\n   *\n   * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`\n   * will override only a single search property.\n   *\n   * If `paramValue` is an array, it will override the property of the `search` component of\n   * `$location` specified via the first argument.\n   *\n   * If `paramValue` is `null`, the property specified via the first argument will be deleted.\n   *\n   * If `paramValue` is `true`, the property specified via the first argument will be added with no\n   * value nor trailing equal sign.\n   *\n   * @return {Object} If called with no arguments returns the parsed `search` object. If called with\n   * one or more arguments returns `$location` object itself.\n   */\n  search: function(search, paramValue) {\n    switch (arguments.length) {\n      case 0:\n        return this.$$search;\n      case 1:\n        if (isString(search) || isNumber(search)) {\n          search = search.toString();\n          this.$$search = parseKeyValue(search);\n        } else if (isObject(search)) {\n          search = copy(search, {});\n          // remove object undefined or null properties\n          forEach(search, function(value, key) {\n            if (value == null) delete search[key];\n          });\n\n          this.$$search = search;\n        } else {\n          throw $locationMinErr('isrcharg',\n              'The first argument of the `$location#search()` call must be a string or an object.');\n        }\n        break;\n      default:\n        if (isUndefined(paramValue) || paramValue === null) {\n          delete this.$$search[search];\n        } else {\n          this.$$search[search] = paramValue;\n        }\n    }\n\n    this.$$compose();\n    return this;\n  },\n\n  /**\n   * @ngdoc method\n   * @name $location#hash\n   *\n   * @description\n   * This method is getter / setter.\n   *\n   * Return hash fragment when called without any parameter.\n   *\n   * Change hash fragment when called with parameter and return `$location`.\n   *\n   *\n   * ```js\n   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue\n   * var hash = $location.hash();\n   * // => \"hashValue\"\n   * ```\n   *\n   * @param {(string|number)=} hash New hash fragment\n   * @return {string} hash\n   */\n  hash: locationGetterSetter('$$hash', function(hash) {\n    return hash !== null ? hash.toString() : '';\n  }),\n\n  /**\n   * @ngdoc method\n   * @name $location#replace\n   *\n   * @description\n   * If called, all changes to $location during current `$digest` will be replacing current history\n   * record, instead of adding new one.\n   */\n  replace: function() {\n    this.$$replace = true;\n    return this;\n  }\n};\n\nforEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {\n  Location.prototype = Object.create(locationPrototype);\n\n  /**\n   * @ngdoc method\n   * @name $location#state\n   *\n   * @description\n   * This method is getter / setter.\n   *\n   * Return the history state object when called without any parameter.\n   *\n   * Change the history state object when called with one parameter and return `$location`.\n   * The state object is later passed to `pushState` or `replaceState`.\n   *\n   * NOTE: This method is supported only in HTML5 mode and only in browsers supporting\n   * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support\n   * older browsers (like IE9 or Android < 4.0), don't use this method.\n   *\n   * @param {object=} state State object for pushState or replaceState\n   * @return {object} state\n   */\n  Location.prototype.state = function(state) {\n    if (!arguments.length)\n      return this.$$state;\n\n    if (Location !== LocationHtml5Url || !this.$$html5) {\n      throw $locationMinErr('nostate', 'History API state support is available only ' +\n        'in HTML5 mode and only in browsers supporting HTML5 History API');\n    }\n    // The user might modify `stateObject` after invoking `$location.state(stateObject)`\n    // but we're changing the $$state reference to $browser.state() during the $digest\n    // so the modification window is narrow.\n    this.$$state = isUndefined(state) ? null : state;\n\n    return this;\n  };\n});\n\n\nfunction locationGetter(property) {\n  return function() {\n    return this[property];\n  };\n}\n\n\nfunction locationGetterSetter(property, preprocess) {\n  return function(value) {\n    if (isUndefined(value))\n      return this[property];\n\n    this[property] = preprocess(value);\n    this.$$compose();\n\n    return this;\n  };\n}\n\n\n/**\n * @ngdoc service\n * @name $location\n *\n * @requires $rootElement\n *\n * @description\n * The $location service parses the URL in the browser address bar (based on the\n * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL\n * available to your application. Changes to the URL in the address bar are reflected into\n * $location service and changes to $location are reflected into the browser address bar.\n *\n * **The $location service:**\n *\n * - Exposes the current URL in the browser address bar, so you can\n *   - Watch and observe the URL.\n *   - Change the URL.\n * - Synchronizes the URL with the browser when the user\n *   - Changes the address bar.\n *   - Clicks the back or forward button (or clicks a History link).\n *   - Clicks on a link.\n * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).\n *\n * For more information see {@link guide/$location Developer Guide: Using $location}\n */\n\n/**\n * @ngdoc provider\n * @name $locationProvider\n * @description\n * Use the `$locationProvider` to configure how the application deep linking paths are stored.\n */\nfunction $LocationProvider() {\n  var hashPrefix = '',\n      html5Mode = {\n        enabled: false,\n        requireBase: true,\n        rewriteLinks: true\n      };\n\n  /**\n   * @ngdoc method\n   * @name $locationProvider#hashPrefix\n   * @description\n   * @param {string=} prefix Prefix for hash part (containing path and search)\n   * @returns {*} current value if used as getter or itself (chaining) if used as setter\n   */\n  this.hashPrefix = function(prefix) {\n    if (isDefined(prefix)) {\n      hashPrefix = prefix;\n      return this;\n    } else {\n      return hashPrefix;\n    }\n  };\n\n  /**\n   * @ngdoc method\n   * @name $locationProvider#html5Mode\n   * @description\n   * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.\n   *   If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported\n   *   properties:\n   *   - **enabled** â€“ `{boolean}` â€“ (default: false) If true, will rely on `history.pushState` to\n   *     change urls where supported. Will fall back to hash-prefixed paths in browsers that do not\n   *     support `pushState`.\n   *   - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies\n   *     whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are\n   *     true, and a base tag is not present, an error will be thrown when `$location` is injected.\n   *     See the {@link guide/$location $location guide for more information}\n   *   - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,\n   *     enables/disables url rewriting for relative links.\n   *\n   * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter\n   */\n  this.html5Mode = function(mode) {\n    if (isBoolean(mode)) {\n      html5Mode.enabled = mode;\n      return this;\n    } else if (isObject(mode)) {\n\n      if (isBoolean(mode.enabled)) {\n        html5Mode.enabled = mode.enabled;\n      }\n\n      if (isBoolean(mode.requireBase)) {\n        html5Mode.requireBase = mode.requireBase;\n      }\n\n      if (isBoolean(mode.rewriteLinks)) {\n        html5Mode.rewriteLinks = mode.rewriteLinks;\n      }\n\n      return this;\n    } else {\n      return html5Mode;\n    }\n  };\n\n  /**\n   * @ngdoc event\n   * @name $location#$locationChangeStart\n   * @eventType broadcast on root scope\n   * @description\n   * Broadcasted before a URL will change.\n   *\n   * This change can be prevented by calling\n   * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more\n   * details about event object. Upon successful change\n   * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.\n   *\n   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when\n   * the browser supports the HTML5 History API.\n   *\n   * @param {Object} angularEvent Synthetic event object.\n   * @param {string} newUrl New URL\n   * @param {string=} oldUrl URL that was before it was changed.\n   * @param {string=} newState New history state object\n   * @param {string=} oldState History state object that was before it was changed.\n   */\n\n  /**\n   * @ngdoc event\n   * @name $location#$locationChangeSuccess\n   * @eventType broadcast on root scope\n   * @description\n   * Broadcasted after a URL was changed.\n   *\n   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when\n   * the browser supports the HTML5 History API.\n   *\n   * @param {Object} angularEvent Synthetic event object.\n   * @param {string} newUrl New URL\n   * @param {string=} oldUrl URL that was before it was changed.\n   * @param {string=} newState New history state object\n   * @param {string=} oldState History state object that was before it was changed.\n   */\n\n  this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',\n      function($rootScope, $browser, $sniffer, $rootElement, $window) {\n    var $location,\n        LocationMode,\n        baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''\n        initialUrl = $browser.url(),\n        appBase;\n\n    if (html5Mode.enabled) {\n      if (!baseHref && html5Mode.requireBase) {\n        throw $locationMinErr('nobase',\n          \"$location in HTML5 mode requires a <base> tag to be present!\");\n      }\n      appBase = serverBase(initialUrl) + (baseHref || '/');\n      LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;\n    } else {\n      appBase = stripHash(initialUrl);\n      LocationMode = LocationHashbangUrl;\n    }\n    $location = new LocationMode(appBase, '#' + hashPrefix);\n    $location.$$parseLinkUrl(initialUrl, initialUrl);\n\n    $location.$$state = $browser.state();\n\n    var IGNORE_URI_REGEXP = /^\\s*(javascript|mailto):/i;\n\n    function setBrowserUrlWithFallback(url, replace, state) {\n      var oldUrl = $location.url();\n      var oldState = $location.$$state;\n      try {\n        $browser.url(url, replace, state);\n\n        // Make sure $location.state() returns referentially identical (not just deeply equal)\n        // state object; this makes possible quick checking if the state changed in the digest\n        // loop. Checking deep equality would be too expensive.\n        $location.$$state = $browser.state();\n      } catch (e) {\n        // Restore old values if pushState fails\n        $location.url(oldUrl);\n        $location.$$state = oldState;\n\n        throw e;\n      }\n    }\n\n    $rootElement.on('click', function(event) {\n      // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)\n      // currently we open nice url link and redirect then\n\n      if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;\n\n      var elm = jqLite(event.target);\n\n      // traverse the DOM up to find first A tag\n      while (nodeName_(elm[0]) !== 'a') {\n        // ignore rewriting if no A tag (reached root element, or no parent - removed from document)\n        if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;\n      }\n\n      var absHref = elm.prop('href');\n      // get the actual href attribute - see\n      // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx\n      var relHref = elm.attr('href') || elm.attr('xlink:href');\n\n      if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {\n        // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during\n        // an animation.\n        absHref = urlResolve(absHref.animVal).href;\n      }\n\n      // Ignore when url is started with javascript: or mailto:\n      if (IGNORE_URI_REGEXP.test(absHref)) return;\n\n      if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {\n        if ($location.$$parseLinkUrl(absHref, relHref)) {\n          // We do a preventDefault for all urls that are part of the angular application,\n          // in html5mode and also without, so that we are able to abort navigation without\n          // getting double entries in the location history.\n          event.preventDefault();\n          // update location manually\n          if ($location.absUrl() != $browser.url()) {\n            $rootScope.$apply();\n            // hack to work around FF6 bug 684208 when scenario runner clicks on links\n            $window.angular['ff-684208-preventDefault'] = true;\n          }\n        }\n      }\n    });\n\n\n    // rewrite hashbang url <> html5 url\n    if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {\n      $browser.url($location.absUrl(), true);\n    }\n\n    var initializing = true;\n\n    // update $location when $browser url changes\n    $browser.onUrlChange(function(newUrl, newState) {\n      $rootScope.$evalAsync(function() {\n        var oldUrl = $location.absUrl();\n        var oldState = $location.$$state;\n        var defaultPrevented;\n\n        $location.$$parse(newUrl);\n        $location.$$state = newState;\n\n        defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,\n            newState, oldState).defaultPrevented;\n\n        // if the location was changed by a `$locationChangeStart` handler then stop\n        // processing this location change\n        if ($location.absUrl() !== newUrl) return;\n\n        if (defaultPrevented) {\n          $location.$$parse(oldUrl);\n          $location.$$state = oldState;\n          setBrowserUrlWithFallback(oldUrl, false, oldState);\n        } else {\n          initializing = false;\n          afterLocationChange(oldUrl, oldState);\n        }\n      });\n      if (!$rootScope.$$phase) $rootScope.$digest();\n    });\n\n    // update browser\n    $rootScope.$watch(function $locationWatch() {\n      var oldUrl = trimEmptyHash($browser.url());\n      var newUrl = trimEmptyHash($location.absUrl());\n      var oldState = $browser.state();\n      var currentReplace = $location.$$replace;\n      var urlOrStateChanged = oldUrl !== newUrl ||\n        ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);\n\n      if (initializing || urlOrStateChanged) {\n        initializing = false;\n\n        $rootScope.$evalAsync(function() {\n          var newUrl = $location.absUrl();\n          var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,\n              $location.$$state, oldState).defaultPrevented;\n\n          // if the location was changed by a `$locationChangeStart` handler then stop\n          // processing this location change\n          if ($location.absUrl() !== newUrl) return;\n\n          if (defaultPrevented) {\n            $location.$$parse(oldUrl);\n            $location.$$state = oldState;\n          } else {\n            if (urlOrStateChanged) {\n              setBrowserUrlWithFallback(newUrl, currentReplace,\n                                        oldState === $location.$$state ? null : $location.$$state);\n            }\n            afterLocationChange(oldUrl, oldState);\n          }\n        });\n      }\n\n      $location.$$replace = false;\n\n      // we don't need to return anything because $evalAsync will make the digest loop dirty when\n      // there is a change\n    });\n\n    return $location;\n\n    function afterLocationChange(oldUrl, oldState) {\n      $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,\n        $location.$$state, oldState);\n    }\n}];\n}\n\n/**\n * @ngdoc service\n * @name $log\n * @requires $window\n *\n * @description\n * Simple service for logging. Default implementation safely writes the message\n * into the browser's console (if present).\n *\n * The main purpose of this service is to simplify debugging and troubleshooting.\n *\n * The default is to log `debug` messages. You can use\n * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.\n *\n * @example\n   <example module=\"logExample\">\n     <file name=\"script.js\">\n       angular.module('logExample', [])\n         .controller('LogController', ['$scope', '$log', function($scope, $log) {\n           $scope.$log = $log;\n           $scope.message = 'Hello World!';\n         }]);\n     </file>\n     <file name=\"index.html\">\n       <div ng-controller=\"LogController\">\n         <p>Reload this page with open console, enter text and hit the log button...</p>\n         Message:\n         <input type=\"text\" ng-model=\"message\"/>\n         <button ng-click=\"$log.log(message)\">log</button>\n         <button ng-click=\"$log.warn(message)\">warn</button>\n         <button ng-click=\"$log.info(message)\">info</button>\n         <button ng-click=\"$log.error(message)\">error</button>\n         <button ng-click=\"$log.debug(message)\">debug</button>\n       </div>\n     </file>\n   </example>\n */\n\n/**\n * @ngdoc provider\n * @name $logProvider\n * @description\n * Use the `$logProvider` to configure how the application logs messages\n */\nfunction $LogProvider() {\n  var debug = true,\n      self = this;\n\n  /**\n   * @ngdoc method\n   * @name $logProvider#debugEnabled\n   * @description\n   * @param {boolean=} flag enable or disable debug level messages\n   * @returns {*} current value if used as getter or itself (chaining) if used as setter\n   */\n  this.debugEnabled = function(flag) {\n    if (isDefined(flag)) {\n      debug = flag;\n    return this;\n    } else {\n      return debug;\n    }\n  };\n\n  this.$get = ['$window', function($window) {\n    return {\n      /**\n       * @ngdoc method\n       * @name $log#log\n       *\n       * @description\n       * Write a log message\n       */\n      log: consoleLog('log'),\n\n      /**\n       * @ngdoc method\n       * @name $log#info\n       *\n       * @description\n       * Write an information message\n       */\n      info: consoleLog('info'),\n\n      /**\n       * @ngdoc method\n       * @name $log#warn\n       *\n       * @description\n       * Write a warning message\n       */\n      warn: consoleLog('warn'),\n\n      /**\n       * @ngdoc method\n       * @name $log#error\n       *\n       * @description\n       * Write an error message\n       */\n      error: consoleLog('error'),\n\n      /**\n       * @ngdoc method\n       * @name $log#debug\n       *\n       * @description\n       * Write a debug message\n       */\n      debug: (function() {\n        var fn = consoleLog('debug');\n\n        return function() {\n          if (debug) {\n            fn.apply(self, arguments);\n          }\n        };\n      }())\n    };\n\n    function formatError(arg) {\n      if (arg instanceof Error) {\n        if (arg.stack) {\n          arg = (arg.message && arg.stack.indexOf(arg.message) === -1)\n              ? 'Error: ' + arg.message + '\\n' + arg.stack\n              : arg.stack;\n        } else if (arg.sourceURL) {\n          arg = arg.message + '\\n' + arg.sourceURL + ':' + arg.line;\n        }\n      }\n      return arg;\n    }\n\n    function consoleLog(type) {\n      var console = $window.console || {},\n          logFn = console[type] || console.log || noop,\n          hasApply = false;\n\n      // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.\n      // The reason behind this is that console.log has type \"object\" in IE8...\n      try {\n        hasApply = !!logFn.apply;\n      } catch (e) {}\n\n      if (hasApply) {\n        return function() {\n          var args = [];\n          forEach(arguments, function(arg) {\n            args.push(formatError(arg));\n          });\n          return logFn.apply(console, args);\n        };\n      }\n\n      // we are IE which either doesn't have window.console => this is noop and we do nothing,\n      // or we are IE where console.log doesn't have apply so we log at least first 2 args\n      return function(arg1, arg2) {\n        logFn(arg1, arg2 == null ? '' : arg2);\n      };\n    }\n  }];\n}\n\n/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n *     Any commits to this file should be reviewed with security in mind.  *\n *   Changes to this file can potentially create security vulnerabilities. *\n *          An approval from 2 Core members with history of modifying      *\n *                         this file is required.                          *\n *                                                                         *\n *  Does the change somehow allow for arbitrary javascript to be executed? *\n *    Or allows for someone to change the prototype of built-in objects?   *\n *     Or gives undesired access to variables likes document or window?    *\n * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */\n\nvar $parseMinErr = minErr('$parse');\n\n// Sandboxing Angular Expressions\n// ------------------------------\n// Angular expressions are generally considered safe because these expressions only have direct\n// access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by\n// obtaining a reference to native JS functions such as the Function constructor.\n//\n// As an example, consider the following Angular expression:\n//\n//   {}.toString.constructor('alert(\"evil JS code\")')\n//\n// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits\n// against the expression language, but not to prevent exploits that were enabled by exposing\n// sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good\n// practice and therefore we are not even trying to protect against interaction with an object\n// explicitly exposed in this way.\n//\n// In general, it is not possible to access a Window object from an angular expression unless a\n// window or some DOM object that has a reference to window is published onto a Scope.\n// Similarly we prevent invocations of function known to be dangerous, as well as assignments to\n// native objects.\n//\n// See https://docs.angularjs.org/guide/security\n\n\nfunction ensureSafeMemberName(name, fullExpression) {\n  if (name === \"__defineGetter__\" || name === \"__defineSetter__\"\n      || name === \"__lookupGetter__\" || name === \"__lookupSetter__\"\n      || name === \"__proto__\") {\n    throw $parseMinErr('isecfld',\n        'Attempting to access a disallowed field in Angular expressions! '\n        + 'Expression: {0}', fullExpression);\n  }\n  return name;\n}\n\nfunction ensureSafeObject(obj, fullExpression) {\n  // nifty check if obj is Function that is fast and works across iframes and other contexts\n  if (obj) {\n    if (obj.constructor === obj) {\n      throw $parseMinErr('isecfn',\n          'Referencing Function in Angular expressions is disallowed! Expression: {0}',\n          fullExpression);\n    } else if (// isWindow(obj)\n        obj.window === obj) {\n      throw $parseMinErr('isecwindow',\n          'Referencing the Window in Angular expressions is disallowed! Expression: {0}',\n          fullExpression);\n    } else if (// isElement(obj)\n        obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {\n      throw $parseMinErr('isecdom',\n          'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',\n          fullExpression);\n    } else if (// block Object so that we can't get hold of dangerous Object.* methods\n        obj === Object) {\n      throw $parseMinErr('isecobj',\n          'Referencing Object in Angular expressions is disallowed! Expression: {0}',\n          fullExpression);\n    }\n  }\n  return obj;\n}\n\nvar CALL = Function.prototype.call;\nvar APPLY = Function.prototype.apply;\nvar BIND = Function.prototype.bind;\n\nfunction ensureSafeFunction(obj, fullExpression) {\n  if (obj) {\n    if (obj.constructor === obj) {\n      throw $parseMinErr('isecfn',\n        'Referencing Function in Angular expressions is disallowed! Expression: {0}',\n        fullExpression);\n    } else if (obj === CALL || obj === APPLY || obj === BIND) {\n      throw $parseMinErr('isecff',\n        'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',\n        fullExpression);\n    }\n  }\n}\n\n//Keyword constants\nvar CONSTANTS = createMap();\nforEach({\n  'null': function() { return null; },\n  'true': function() { return true; },\n  'false': function() { return false; },\n  'undefined': function() {}\n}, function(constantGetter, name) {\n  constantGetter.constant = constantGetter.literal = constantGetter.sharedGetter = true;\n  CONSTANTS[name] = constantGetter;\n});\n\n//Not quite a constant, but can be lex/parsed the same\nCONSTANTS['this'] = function(self) { return self; };\nCONSTANTS['this'].sharedGetter = true;\n\n\n//Operators - will be wrapped by binaryFn/unaryFn/assignment/filter\nvar OPERATORS = extend(createMap(), {\n    '+':function(self, locals, a, b) {\n      a=a(self, locals); b=b(self, locals);\n      if (isDefined(a)) {\n        if (isDefined(b)) {\n          return a + b;\n        }\n        return a;\n      }\n      return isDefined(b) ? b : undefined;},\n    '-':function(self, locals, a, b) {\n          a=a(self, locals); b=b(self, locals);\n          return (isDefined(a) ? a : 0) - (isDefined(b) ? b : 0);\n        },\n    '*':function(self, locals, a, b) {return a(self, locals) * b(self, locals);},\n    '/':function(self, locals, a, b) {return a(self, locals) / b(self, locals);},\n    '%':function(self, locals, a, b) {return a(self, locals) % b(self, locals);},\n    '===':function(self, locals, a, b) {return a(self, locals) === b(self, locals);},\n    '!==':function(self, locals, a, b) {return a(self, locals) !== b(self, locals);},\n    '==':function(self, locals, a, b) {return a(self, locals) == b(self, locals);},\n    '!=':function(self, locals, a, b) {return a(self, locals) != b(self, locals);},\n    '<':function(self, locals, a, b) {return a(self, locals) < b(self, locals);},\n    '>':function(self, locals, a, b) {return a(self, locals) > b(self, locals);},\n    '<=':function(self, locals, a, b) {return a(self, locals) <= b(self, locals);},\n    '>=':function(self, locals, a, b) {return a(self, locals) >= b(self, locals);},\n    '&&':function(self, locals, a, b) {return a(self, locals) && b(self, locals);},\n    '||':function(self, locals, a, b) {return a(self, locals) || b(self, locals);},\n    '!':function(self, locals, a) {return !a(self, locals);},\n\n    //Tokenized as operators but parsed as assignment/filters\n    '=':true,\n    '|':true\n});\nvar ESCAPE = {\"n\":\"\\n\", \"f\":\"\\f\", \"r\":\"\\r\", \"t\":\"\\t\", \"v\":\"\\v\", \"'\":\"'\", '\"':'\"'};\n\n\n/////////////////////////////////////////\n\n\n/**\n * @constructor\n */\nvar Lexer = function(options) {\n  this.options = options;\n};\n\nLexer.prototype = {\n  constructor: Lexer,\n\n  lex: function(text) {\n    this.text = text;\n    this.index = 0;\n    this.tokens = [];\n\n    while (this.index < this.text.length) {\n      var ch = this.text.charAt(this.index);\n      if (ch === '\"' || ch === \"'\") {\n        this.readString(ch);\n      } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {\n        this.readNumber();\n      } else if (this.isIdent(ch)) {\n        this.readIdent();\n      } else if (this.is(ch, '(){}[].,;:?')) {\n        this.tokens.push({index: this.index, text: ch});\n        this.index++;\n      } else if (this.isWhitespace(ch)) {\n        this.index++;\n      } else {\n        var ch2 = ch + this.peek();\n        var ch3 = ch2 + this.peek(2);\n        var op1 = OPERATORS[ch];\n        var op2 = OPERATORS[ch2];\n        var op3 = OPERATORS[ch3];\n        if (op1 || op2 || op3) {\n          var token = op3 ? ch3 : (op2 ? ch2 : ch);\n          this.tokens.push({index: this.index, text: token, operator: true});\n          this.index += token.length;\n        } else {\n          this.throwError('Unexpected next character ', this.index, this.index + 1);\n        }\n      }\n    }\n    return this.tokens;\n  },\n\n  is: function(ch, chars) {\n    return chars.indexOf(ch) !== -1;\n  },\n\n  peek: function(i) {\n    var num = i || 1;\n    return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;\n  },\n\n  isNumber: function(ch) {\n    return ('0' <= ch && ch <= '9') && typeof ch === \"string\";\n  },\n\n  isWhitespace: function(ch) {\n    // IE treats non-breaking space as \\u00A0\n    return (ch === ' ' || ch === '\\r' || ch === '\\t' ||\n            ch === '\\n' || ch === '\\v' || ch === '\\u00A0');\n  },\n\n  isIdent: function(ch) {\n    return ('a' <= ch && ch <= 'z' ||\n            'A' <= ch && ch <= 'Z' ||\n            '_' === ch || ch === '$');\n  },\n\n  isExpOperator: function(ch) {\n    return (ch === '-' || ch === '+' || this.isNumber(ch));\n  },\n\n  throwError: function(error, start, end) {\n    end = end || this.index;\n    var colStr = (isDefined(start)\n            ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'\n            : ' ' + end);\n    throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',\n        error, colStr, this.text);\n  },\n\n  readNumber: function() {\n    var number = '';\n    var start = this.index;\n    while (this.index < this.text.length) {\n      var ch = lowercase(this.text.charAt(this.index));\n      if (ch == '.' || this.isNumber(ch)) {\n        number += ch;\n      } else {\n        var peekCh = this.peek();\n        if (ch == 'e' && this.isExpOperator(peekCh)) {\n          number += ch;\n        } else if (this.isExpOperator(ch) &&\n            peekCh && this.isNumber(peekCh) &&\n            number.charAt(number.length - 1) == 'e') {\n          number += ch;\n        } else if (this.isExpOperator(ch) &&\n            (!peekCh || !this.isNumber(peekCh)) &&\n            number.charAt(number.length - 1) == 'e') {\n          this.throwError('Invalid exponent');\n        } else {\n          break;\n        }\n      }\n      this.index++;\n    }\n    this.tokens.push({\n      index: start,\n      text: number,\n      constant: true,\n      value: Number(number)\n    });\n  },\n\n  readIdent: function() {\n    var start = this.index;\n    while (this.index < this.text.length) {\n      var ch = this.text.charAt(this.index);\n      if (!(this.isIdent(ch) || this.isNumber(ch))) {\n        break;\n      }\n      this.index++;\n    }\n    this.tokens.push({\n      index: start,\n      text: this.text.slice(start, this.index),\n      identifier: true\n    });\n  },\n\n  readString: function(quote) {\n    var start = this.index;\n    this.index++;\n    var string = '';\n    var rawString = quote;\n    var escape = false;\n    while (this.index < this.text.length) {\n      var ch = this.text.charAt(this.index);\n      rawString += ch;\n      if (escape) {\n        if (ch === 'u') {\n          var hex = this.text.substring(this.index + 1, this.index + 5);\n          if (!hex.match(/[\\da-f]{4}/i))\n            this.throwError('Invalid unicode escape [\\\\u' + hex + ']');\n          this.index += 4;\n          string += String.fromCharCode(parseInt(hex, 16));\n        } else {\n          var rep = ESCAPE[ch];\n          string = string + (rep || ch);\n        }\n        escape = false;\n      } else if (ch === '\\\\') {\n        escape = true;\n      } else if (ch === quote) {\n        this.index++;\n        this.tokens.push({\n          index: start,\n          text: rawString,\n          constant: true,\n          value: string\n        });\n        return;\n      } else {\n        string += ch;\n      }\n      this.index++;\n    }\n    this.throwError('Unterminated quote', start);\n  }\n};\n\n\nfunction isConstant(exp) {\n  return exp.constant;\n}\n\n/**\n * @constructor\n */\nvar Parser = function(lexer, $filter, options) {\n  this.lexer = lexer;\n  this.$filter = $filter;\n  this.options = options;\n};\n\nParser.ZERO = extend(function() {\n  return 0;\n}, {\n  sharedGetter: true,\n  constant: true\n});\n\nParser.prototype = {\n  constructor: Parser,\n\n  parse: function(text) {\n    this.text = text;\n    this.tokens = this.lexer.lex(text);\n\n    var value = this.statements();\n\n    if (this.tokens.length !== 0) {\n      this.throwError('is an unexpected token', this.tokens[0]);\n    }\n\n    value.literal = !!value.literal;\n    value.constant = !!value.constant;\n\n    return value;\n  },\n\n  primary: function() {\n    var primary;\n    if (this.expect('(')) {\n      primary = this.filterChain();\n      this.consume(')');\n    } else if (this.expect('[')) {\n      primary = this.arrayDeclaration();\n    } else if (this.expect('{')) {\n      primary = this.object();\n    } else if (this.peek().identifier && this.peek().text in CONSTANTS) {\n      primary = CONSTANTS[this.consume().text];\n    } else if (this.peek().identifier) {\n      primary = this.identifier();\n    } else if (this.peek().constant) {\n      primary = this.constant();\n    } else {\n      this.throwError('not a primary expression', this.peek());\n    }\n\n    var next, context;\n    while ((next = this.expect('(', '[', '.'))) {\n      if (next.text === '(') {\n        primary = this.functionCall(primary, context);\n        context = null;\n      } else if (next.text === '[') {\n        context = primary;\n        primary = this.objectIndex(primary);\n      } else if (next.text === '.') {\n        context = primary;\n        primary = this.fieldAccess(primary);\n      } else {\n        this.throwError('IMPOSSIBLE');\n      }\n    }\n    return primary;\n  },\n\n  throwError: function(msg, token) {\n    throw $parseMinErr('syntax',\n        'Syntax Error: Token \\'{0}\\' {1} at column {2} of the expression [{3}] starting at [{4}].',\n          token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));\n  },\n\n  peekToken: function() {\n    if (this.tokens.length === 0)\n      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);\n    return this.tokens[0];\n  },\n\n  peek: function(e1, e2, e3, e4) {\n    return this.peekAhead(0, e1, e2, e3, e4);\n  },\n  peekAhead: function(i, e1, e2, e3, e4) {\n    if (this.tokens.length > i) {\n      var token = this.tokens[i];\n      var t = token.text;\n      if (t === e1 || t === e2 || t === e3 || t === e4 ||\n          (!e1 && !e2 && !e3 && !e4)) {\n        return token;\n      }\n    }\n    return false;\n  },\n\n  expect: function(e1, e2, e3, e4) {\n    var token = this.peek(e1, e2, e3, e4);\n    if (token) {\n      this.tokens.shift();\n      return token;\n    }\n    return false;\n  },\n\n  consume: function(e1) {\n    if (this.tokens.length === 0) {\n      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);\n    }\n\n    var token = this.expect(e1);\n    if (!token) {\n      this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());\n    }\n    return token;\n  },\n\n  unaryFn: function(op, right) {\n    var fn = OPERATORS[op];\n    return extend(function $parseUnaryFn(self, locals) {\n      return fn(self, locals, right);\n    }, {\n      constant:right.constant,\n      inputs: [right]\n    });\n  },\n\n  binaryFn: function(left, op, right, isBranching) {\n    var fn = OPERATORS[op];\n    return extend(function $parseBinaryFn(self, locals) {\n      return fn(self, locals, left, right);\n    }, {\n      constant: left.constant && right.constant,\n      inputs: !isBranching && [left, right]\n    });\n  },\n\n  identifier: function() {\n    var id = this.consume().text;\n\n    //Continue reading each `.identifier` unless it is a method invocation\n    while (this.peek('.') && this.peekAhead(1).identifier && !this.peekAhead(2, '(')) {\n      id += this.consume().text + this.consume().text;\n    }\n\n    return getterFn(id, this.options, this.text);\n  },\n\n  constant: function() {\n    var value = this.consume().value;\n\n    return extend(function $parseConstant() {\n      return value;\n    }, {\n      constant: true,\n      literal: true\n    });\n  },\n\n  statements: function() {\n    var statements = [];\n    while (true) {\n      if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))\n        statements.push(this.filterChain());\n      if (!this.expect(';')) {\n        // optimize for the common case where there is only one statement.\n        // TODO(size): maybe we should not support multiple statements?\n        return (statements.length === 1)\n            ? statements[0]\n            : function $parseStatements(self, locals) {\n                var value;\n                for (var i = 0, ii = statements.length; i < ii; i++) {\n                  value = statements[i](self, locals);\n                }\n                return value;\n              };\n      }\n    }\n  },\n\n  filterChain: function() {\n    var left = this.expression();\n    var token;\n    while ((token = this.expect('|'))) {\n      left = this.filter(left);\n    }\n    return left;\n  },\n\n  filter: function(inputFn) {\n    var fn = this.$filter(this.consume().text);\n    var argsFn;\n    var args;\n\n    if (this.peek(':')) {\n      argsFn = [];\n      args = []; // we can safely reuse the array\n      while (this.expect(':')) {\n        argsFn.push(this.expression());\n      }\n    }\n\n    var inputs = [inputFn].concat(argsFn || []);\n\n    return extend(function $parseFilter(self, locals) {\n      var input = inputFn(self, locals);\n      if (args) {\n        args[0] = input;\n\n        var i = argsFn.length;\n        while (i--) {\n          args[i + 1] = argsFn[i](self, locals);\n        }\n\n        return fn.apply(undefined, args);\n      }\n\n      return fn(input);\n    }, {\n      constant: !fn.$stateful && inputs.every(isConstant),\n      inputs: !fn.$stateful && inputs\n    });\n  },\n\n  expression: function() {\n    return this.assignment();\n  },\n\n  assignment: function() {\n    var left = this.ternary();\n    var right;\n    var token;\n    if ((token = this.expect('='))) {\n      if (!left.assign) {\n        this.throwError('implies assignment but [' +\n            this.text.substring(0, token.index) + '] can not be assigned to', token);\n      }\n      right = this.ternary();\n      return extend(function $parseAssignment(scope, locals) {\n        return left.assign(scope, right(scope, locals), locals);\n      }, {\n        inputs: [left, right]\n      });\n    }\n    return left;\n  },\n\n  ternary: function() {\n    var left = this.logicalOR();\n    var middle;\n    var token;\n    if ((token = this.expect('?'))) {\n      middle = this.assignment();\n      if (this.consume(':')) {\n        var right = this.assignment();\n\n        return extend(function $parseTernary(self, locals) {\n          return left(self, locals) ? middle(self, locals) : right(self, locals);\n        }, {\n          constant: left.constant && middle.constant && right.constant\n        });\n      }\n    }\n\n    return left;\n  },\n\n  logicalOR: function() {\n    var left = this.logicalAND();\n    var token;\n    while ((token = this.expect('||'))) {\n      left = this.binaryFn(left, token.text, this.logicalAND(), true);\n    }\n    return left;\n  },\n\n  logicalAND: function() {\n    var left = this.equality();\n    var token;\n    while ((token = this.expect('&&'))) {\n      left = this.binaryFn(left, token.text, this.equality(), true);\n    }\n    return left;\n  },\n\n  equality: function() {\n    var left = this.relational();\n    var token;\n    while ((token = this.expect('==','!=','===','!=='))) {\n      left = this.binaryFn(left, token.text, this.relational());\n    }\n    return left;\n  },\n\n  relational: function() {\n    var left = this.additive();\n    var token;\n    while ((token = this.expect('<', '>', '<=', '>='))) {\n      left = this.binaryFn(left, token.text, this.additive());\n    }\n    return left;\n  },\n\n  additive: function() {\n    var left = this.multiplicative();\n    var token;\n    while ((token = this.expect('+','-'))) {\n      left = this.binaryFn(left, token.text, this.multiplicative());\n    }\n    return left;\n  },\n\n  multiplicative: function() {\n    var left = this.unary();\n    var token;\n    while ((token = this.expect('*','/','%'))) {\n      left = this.binaryFn(left, token.text, this.unary());\n    }\n    return left;\n  },\n\n  unary: function() {\n    var token;\n    if (this.expect('+')) {\n      return this.primary();\n    } else if ((token = this.expect('-'))) {\n      return this.binaryFn(Parser.ZERO, token.text, this.unary());\n    } else if ((token = this.expect('!'))) {\n      return this.unaryFn(token.text, this.unary());\n    } else {\n      return this.primary();\n    }\n  },\n\n  fieldAccess: function(object) {\n    var getter = this.identifier();\n\n    return extend(function $parseFieldAccess(scope, locals, self) {\n      var o = self || object(scope, locals);\n      return (o == null) ? undefined : getter(o);\n    }, {\n      assign: function(scope, value, locals) {\n        var o = object(scope, locals);\n        if (!o) object.assign(scope, o = {}, locals);\n        return getter.assign(o, value);\n      }\n    });\n  },\n\n  objectIndex: function(obj) {\n    var expression = this.text;\n\n    var indexFn = this.expression();\n    this.consume(']');\n\n    return extend(function $parseObjectIndex(self, locals) {\n      var o = obj(self, locals),\n          i = indexFn(self, locals),\n          v;\n\n      ensureSafeMemberName(i, expression);\n      if (!o) return undefined;\n      v = ensureSafeObject(o[i], expression);\n      return v;\n    }, {\n      assign: function(self, value, locals) {\n        var key = ensureSafeMemberName(indexFn(self, locals), expression);\n        // prevent overwriting of Function.constructor which would break ensureSafeObject check\n        var o = ensureSafeObject(obj(self, locals), expression);\n        if (!o) obj.assign(self, o = {}, locals);\n        return o[key] = value;\n      }\n    });\n  },\n\n  functionCall: function(fnGetter, contextGetter) {\n    var argsFn = [];\n    if (this.peekToken().text !== ')') {\n      do {\n        argsFn.push(this.expression());\n      } while (this.expect(','));\n    }\n    this.consume(')');\n\n    var expressionText = this.text;\n    // we can safely reuse the array across invocations\n    var args = argsFn.length ? [] : null;\n\n    return function $parseFunctionCall(scope, locals) {\n      var context = contextGetter ? contextGetter(scope, locals) : isDefined(contextGetter) ? undefined : scope;\n      var fn = fnGetter(scope, locals, context) || noop;\n\n      if (args) {\n        var i = argsFn.length;\n        while (i--) {\n          args[i] = ensureSafeObject(argsFn[i](scope, locals), expressionText);\n        }\n      }\n\n      ensureSafeObject(context, expressionText);\n      ensureSafeFunction(fn, expressionText);\n\n      // IE doesn't have apply for some native functions\n      var v = fn.apply\n            ? fn.apply(context, args)\n            : fn(args[0], args[1], args[2], args[3], args[4]);\n\n      if (args) {\n        // Free-up the memory (arguments of the last function call).\n        args.length = 0;\n      }\n\n      return ensureSafeObject(v, expressionText);\n      };\n  },\n\n  // This is used with json array declaration\n  arrayDeclaration: function() {\n    var elementFns = [];\n    if (this.peekToken().text !== ']') {\n      do {\n        if (this.peek(']')) {\n          // Support trailing commas per ES5.1.\n          break;\n        }\n        elementFns.push(this.expression());\n      } while (this.expect(','));\n    }\n    this.consume(']');\n\n    return extend(function $parseArrayLiteral(self, locals) {\n      var array = [];\n      for (var i = 0, ii = elementFns.length; i < ii; i++) {\n        array.push(elementFns[i](self, locals));\n      }\n      return array;\n    }, {\n      literal: true,\n      constant: elementFns.every(isConstant),\n      inputs: elementFns\n    });\n  },\n\n  object: function() {\n    var keys = [], valueFns = [];\n    if (this.peekToken().text !== '}') {\n      do {\n        if (this.peek('}')) {\n          // Support trailing commas per ES5.1.\n          break;\n        }\n        var token = this.consume();\n        if (token.constant) {\n          keys.push(token.value);\n        } else if (token.identifier) {\n          keys.push(token.text);\n        } else {\n          this.throwError(\"invalid key\", token);\n        }\n        this.consume(':');\n        valueFns.push(this.expression());\n      } while (this.expect(','));\n    }\n    this.consume('}');\n\n    return extend(function $parseObjectLiteral(self, locals) {\n      var object = {};\n      for (var i = 0, ii = valueFns.length; i < ii; i++) {\n        object[keys[i]] = valueFns[i](self, locals);\n      }\n      return object;\n    }, {\n      literal: true,\n      constant: valueFns.every(isConstant),\n      inputs: valueFns\n    });\n  }\n};\n\n\n//////////////////////////////////////////////////\n// Parser helper functions\n//////////////////////////////////////////////////\n\nfunction setter(obj, locals, path, setValue, fullExp) {\n  ensureSafeObject(obj, fullExp);\n  ensureSafeObject(locals, fullExp);\n\n  var element = path.split('.'), key;\n  for (var i = 0; element.length > 1; i++) {\n    key = ensureSafeMemberName(element.shift(), fullExp);\n    var propertyObj = (i === 0 && locals && locals[key]) || obj[key];\n    if (!propertyObj) {\n      propertyObj = {};\n      obj[key] = propertyObj;\n    }\n    obj = ensureSafeObject(propertyObj, fullExp);\n  }\n  key = ensureSafeMemberName(element.shift(), fullExp);\n  ensureSafeObject(obj[key], fullExp);\n  obj[key] = setValue;\n  return setValue;\n}\n\nvar getterFnCacheDefault = createMap();\nvar getterFnCacheExpensive = createMap();\n\nfunction isPossiblyDangerousMemberName(name) {\n  return name == 'constructor';\n}\n\n/**\n * Implementation of the \"Black Hole\" variant from:\n * - http://jsperf.com/angularjs-parse-getter/4\n * - http://jsperf.com/path-evaluation-simplified/7\n */\nfunction cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, expensiveChecks) {\n  ensureSafeMemberName(key0, fullExp);\n  ensureSafeMemberName(key1, fullExp);\n  ensureSafeMemberName(key2, fullExp);\n  ensureSafeMemberName(key3, fullExp);\n  ensureSafeMemberName(key4, fullExp);\n  var eso = function(o) {\n    return ensureSafeObject(o, fullExp);\n  };\n  var eso0 = (expensiveChecks || isPossiblyDangerousMemberName(key0)) ? eso : identity;\n  var eso1 = (expensiveChecks || isPossiblyDangerousMemberName(key1)) ? eso : identity;\n  var eso2 = (expensiveChecks || isPossiblyDangerousMemberName(key2)) ? eso : identity;\n  var eso3 = (expensiveChecks || isPossiblyDangerousMemberName(key3)) ? eso : identity;\n  var eso4 = (expensiveChecks || isPossiblyDangerousMemberName(key4)) ? eso : identity;\n\n  return function cspSafeGetter(scope, locals) {\n    var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope;\n\n    if (pathVal == null) return pathVal;\n    pathVal = eso0(pathVal[key0]);\n\n    if (!key1) return pathVal;\n    if (pathVal == null) return undefined;\n    pathVal = eso1(pathVal[key1]);\n\n    if (!key2) return pathVal;\n    if (pathVal == null) return undefined;\n    pathVal = eso2(pathVal[key2]);\n\n    if (!key3) return pathVal;\n    if (pathVal == null) return undefined;\n    pathVal = eso3(pathVal[key3]);\n\n    if (!key4) return pathVal;\n    if (pathVal == null) return undefined;\n    pathVal = eso4(pathVal[key4]);\n\n    return pathVal;\n  };\n}\n\nfunction getterFnWithEnsureSafeObject(fn, fullExpression) {\n  return function(s, l) {\n    return fn(s, l, ensureSafeObject, fullExpression);\n  };\n}\n\nfunction getterFn(path, options, fullExp) {\n  var expensiveChecks = options.expensiveChecks;\n  var getterFnCache = (expensiveChecks ? getterFnCacheExpensive : getterFnCacheDefault);\n  var fn = getterFnCache[path];\n  if (fn) return fn;\n\n\n  var pathKeys = path.split('.'),\n      pathKeysLength = pathKeys.length;\n\n  // http://jsperf.com/angularjs-parse-getter/6\n  if (options.csp) {\n    if (pathKeysLength < 6) {\n      fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp, expensiveChecks);\n    } else {\n      fn = function cspSafeGetter(scope, locals) {\n        var i = 0, val;\n        do {\n          val = cspSafeGetterFn(pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++],\n                                pathKeys[i++], fullExp, expensiveChecks)(scope, locals);\n\n          locals = undefined; // clear after first iteration\n          scope = val;\n        } while (i < pathKeysLength);\n        return val;\n      };\n    }\n  } else {\n    var code = '';\n    if (expensiveChecks) {\n      code += 's = eso(s, fe);\\nl = eso(l, fe);\\n';\n    }\n    var needsEnsureSafeObject = expensiveChecks;\n    forEach(pathKeys, function(key, index) {\n      ensureSafeMemberName(key, fullExp);\n      var lookupJs = (index\n                      // we simply dereference 's' on any .dot notation\n                      ? 's'\n                      // but if we are first then we check locals first, and if so read it first\n                      : '((l&&l.hasOwnProperty(\"' + key + '\"))?l:s)') + '.' + key;\n      if (expensiveChecks || isPossiblyDangerousMemberName(key)) {\n        lookupJs = 'eso(' + lookupJs + ', fe)';\n        needsEnsureSafeObject = true;\n      }\n      code += 'if(s == null) return undefined;\\n' +\n              's=' + lookupJs + ';\\n';\n    });\n    code += 'return s;';\n\n    /* jshint -W054 */\n    var evaledFnGetter = new Function('s', 'l', 'eso', 'fe', code); // s=scope, l=locals, eso=ensureSafeObject\n    /* jshint +W054 */\n    evaledFnGetter.toString = valueFn(code);\n    if (needsEnsureSafeObject) {\n      evaledFnGetter = getterFnWithEnsureSafeObject(evaledFnGetter, fullExp);\n    }\n    fn = evaledFnGetter;\n  }\n\n  fn.sharedGetter = true;\n  fn.assign = function(self, value, locals) {\n    return setter(self, locals, path, value, path);\n  };\n  getterFnCache[path] = fn;\n  return fn;\n}\n\nvar objectValueOf = Object.prototype.valueOf;\n\nfunction getValueOf(value) {\n  return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);\n}\n\n///////////////////////////////////\n\n/**\n * @ngdoc service\n * @name $parse\n * @kind function\n *\n * @description\n *\n * Converts Angular {@link guide/expression expression} into a function.\n *\n * ```js\n *   var getter = $parse('user.name');\n *   var setter = getter.assign;\n *   var context = {user:{name:'angular'}};\n *   var locals = {user:{name:'local'}};\n *\n *   expect(getter(context)).toEqual('angular');\n *   setter(context, 'newValue');\n *   expect(context.user.name).toEqual('newValue');\n *   expect(getter(context, locals)).toEqual('local');\n * ```\n *\n *\n * @param {string} expression String expression to compile.\n * @returns {function(context, locals)} a function which represents the compiled expression:\n *\n *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings\n *      are evaluated against (typically a scope object).\n *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in\n *      `context`.\n *\n *    The returned function also has the following properties:\n *      * `literal` â€“ `{boolean}` â€“ whether the expression's top-level node is a JavaScript\n *        literal.\n *      * `constant` â€“ `{boolean}` â€“ whether the expression is made entirely of JavaScript\n *        constant literals.\n *      * `assign` â€“ `{?function(context, value)}` â€“ if the expression is assignable, this will be\n *        set to a function to change its value on the given context.\n *\n */\n\n\n/**\n * @ngdoc provider\n * @name $parseProvider\n *\n * @description\n * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}\n *  service.\n */\nfunction $ParseProvider() {\n  var cacheDefault = createMap();\n  var cacheExpensive = createMap();\n\n\n\n  this.$get = ['$filter', '$sniffer', function($filter, $sniffer) {\n    var $parseOptions = {\n          csp: $sniffer.csp,\n          expensiveChecks: false\n        },\n        $parseOptionsExpensive = {\n          csp: $sniffer.csp,\n          expensiveChecks: true\n        };\n\n    function wrapSharedExpression(exp) {\n      var wrapped = exp;\n\n      if (exp.sharedGetter) {\n        wrapped = function $parseWrapper(self, locals) {\n          return exp(self, locals);\n        };\n        wrapped.literal = exp.literal;\n        wrapped.constant = exp.constant;\n        wrapped.assign = exp.assign;\n      }\n\n      return wrapped;\n    }\n\n    return function $parse(exp, interceptorFn, expensiveChecks) {\n      var parsedExpression, oneTime, cacheKey;\n\n      switch (typeof exp) {\n        case 'string':\n          cacheKey = exp = exp.trim();\n\n          var cache = (expensiveChecks ? cacheExpensive : cacheDefault);\n          parsedExpression = cache[cacheKey];\n\n          if (!parsedExpression) {\n            if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {\n              oneTime = true;\n              exp = exp.substring(2);\n            }\n\n            var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;\n            var lexer = new Lexer(parseOptions);\n            var parser = new Parser(lexer, $filter, parseOptions);\n            parsedExpression = parser.parse(exp);\n\n            if (parsedExpression.constant) {\n              parsedExpression.$$watchDelegate = constantWatchDelegate;\n            } else if (oneTime) {\n              //oneTime is not part of the exp passed to the Parser so we may have to\n              //wrap the parsedExpression before adding a $$watchDelegate\n              parsedExpression = wrapSharedExpression(parsedExpression);\n              parsedExpression.$$watchDelegate = parsedExpression.literal ?\n                oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;\n            } else if (parsedExpression.inputs) {\n              parsedExpression.$$watchDelegate = inputsWatchDelegate;\n            }\n\n            cache[cacheKey] = parsedExpression;\n          }\n          return addInterceptor(parsedExpression, interceptorFn);\n\n        case 'function':\n          return addInterceptor(exp, interceptorFn);\n\n        default:\n          return addInterceptor(noop, interceptorFn);\n      }\n    };\n\n    function collectExpressionInputs(inputs, list) {\n      for (var i = 0, ii = inputs.length; i < ii; i++) {\n        var input = inputs[i];\n        if (!input.constant) {\n          if (input.inputs) {\n            collectExpressionInputs(input.inputs, list);\n          } else if (list.indexOf(input) === -1) { // TODO(perf) can we do better?\n            list.push(input);\n          }\n        }\n      }\n\n      return list;\n    }\n\n    function expressionInputDirtyCheck(newValue, oldValueOfValue) {\n\n      if (newValue == null || oldValueOfValue == null) { // null/undefined\n        return newValue === oldValueOfValue;\n      }\n\n      if (typeof newValue === 'object') {\n\n        // attempt to convert the value to a primitive type\n        // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can\n        //             be cheaply dirty-checked\n        newValue = getValueOf(newValue);\n\n        if (typeof newValue === 'object') {\n          // objects/arrays are not supported - deep-watching them would be too expensive\n          return false;\n        }\n\n        // fall-through to the primitive equality check\n      }\n\n      //Primitive or NaN\n      return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);\n    }\n\n    function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression) {\n      var inputExpressions = parsedExpression.$$inputs ||\n                    (parsedExpression.$$inputs = collectExpressionInputs(parsedExpression.inputs, []));\n\n      var lastResult;\n\n      if (inputExpressions.length === 1) {\n        var oldInputValue = expressionInputDirtyCheck; // init to something unique so that equals check fails\n        inputExpressions = inputExpressions[0];\n        return scope.$watch(function expressionInputWatch(scope) {\n          var newInputValue = inputExpressions(scope);\n          if (!expressionInputDirtyCheck(newInputValue, oldInputValue)) {\n            lastResult = parsedExpression(scope);\n            oldInputValue = newInputValue && getValueOf(newInputValue);\n          }\n          return lastResult;\n        }, listener, objectEquality);\n      }\n\n      var oldInputValueOfValues = [];\n      for (var i = 0, ii = inputExpressions.length; i < ii; i++) {\n        oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails\n      }\n\n      return scope.$watch(function expressionInputsWatch(scope) {\n        var changed = false;\n\n        for (var i = 0, ii = inputExpressions.length; i < ii; i++) {\n          var newInputValue = inputExpressions[i](scope);\n          if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {\n            oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);\n          }\n        }\n\n        if (changed) {\n          lastResult = parsedExpression(scope);\n        }\n\n        return lastResult;\n      }, listener, objectEquality);\n    }\n\n    function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {\n      var unwatch, lastValue;\n      return unwatch = scope.$watch(function oneTimeWatch(scope) {\n        return parsedExpression(scope);\n      }, function oneTimeListener(value, old, scope) {\n        lastValue = value;\n        if (isFunction(listener)) {\n          listener.apply(this, arguments);\n        }\n        if (isDefined(value)) {\n          scope.$$postDigest(function() {\n            if (isDefined(lastValue)) {\n              unwatch();\n            }\n          });\n        }\n      }, objectEquality);\n    }\n\n    function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {\n      var unwatch, lastValue;\n      return unwatch = scope.$watch(function oneTimeWatch(scope) {\n        return parsedExpression(scope);\n      }, function oneTimeListener(value, old, scope) {\n        lastValue = value;\n        if (isFunction(listener)) {\n          listener.call(this, value, old, scope);\n        }\n        if (isAllDefined(value)) {\n          scope.$$postDigest(function() {\n            if (isAllDefined(lastValue)) unwatch();\n          });\n        }\n      }, objectEquality);\n\n      function isAllDefined(value) {\n        var allDefined = true;\n        forEach(value, function(val) {\n          if (!isDefined(val)) allDefined = false;\n        });\n        return allDefined;\n      }\n    }\n\n    function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {\n      var unwatch;\n      return unwatch = scope.$watch(function constantWatch(scope) {\n        return parsedExpression(scope);\n      }, function constantListener(value, old, scope) {\n        if (isFunction(listener)) {\n          listener.apply(this, arguments);\n        }\n        unwatch();\n      }, objectEquality);\n    }\n\n    function addInterceptor(parsedExpression, interceptorFn) {\n      if (!interceptorFn) return parsedExpression;\n      var watchDelegate = parsedExpression.$$watchDelegate;\n\n      var regularWatch =\n          watchDelegate !== oneTimeLiteralWatchDelegate &&\n          watchDelegate !== oneTimeWatchDelegate;\n\n      var fn = regularWatch ? function regularInterceptedExpression(scope, locals) {\n        var value = parsedExpression(scope, locals);\n        return interceptorFn(value, scope, locals);\n      } : function oneTimeInterceptedExpression(scope, locals) {\n        var value = parsedExpression(scope, locals);\n        var result = interceptorFn(value, scope, locals);\n        // we only return the interceptor's result if the\n        // initial value is defined (for bind-once)\n        return isDefined(value) ? result : value;\n      };\n\n      // Propagate $$watchDelegates other then inputsWatchDelegate\n      if (parsedExpression.$$watchDelegate &&\n          parsedExpression.$$watchDelegate !== inputsWatchDelegate) {\n        fn.$$watchDelegate = parsedExpression.$$watchDelegate;\n      } else if (!interceptorFn.$stateful) {\n        // If there is an interceptor, but no watchDelegate then treat the interceptor like\n        // we treat filters - it is assumed to be a pure function unless flagged with $stateful\n        fn.$$watchDelegate = inputsWatchDelegate;\n        fn.inputs = [parsedExpression];\n      }\n\n      return fn;\n    }\n  }];\n}\n\n/**\n * @ngdoc service\n * @name $q\n * @requires $rootScope\n *\n * @description\n * A service that helps you run functions asynchronously, and use their return values (or exceptions)\n * when they are done processing.\n *\n * This is an implementation of promises/deferred objects inspired by\n * [Kris Kowal's Q](https://github.com/kriskowal/q).\n *\n * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred\n * implementations, and the other which resembles ES6 promises to some degree.\n *\n * # $q constructor\n *\n * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`\n * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,\n * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).\n *\n * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are\n * available yet.\n *\n * It can be used like so:\n *\n * ```js\n *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`\n *   // are available in the current lexical scope (they could have been injected or passed in).\n *\n *   function asyncGreet(name) {\n *     // perform some asynchronous operation, resolve or reject the promise when appropriate.\n *     return $q(function(resolve, reject) {\n *       setTimeout(function() {\n *         if (okToGreet(name)) {\n *           resolve('Hello, ' + name + '!');\n *         } else {\n *           reject('Greeting ' + name + ' is not allowed.');\n *         }\n *       }, 1000);\n *     });\n *   }\n *\n *   var promise = asyncGreet('Robin Hood');\n *   promise.then(function(greeting) {\n *     alert('Success: ' + greeting);\n *   }, function(reason) {\n *     alert('Failed: ' + reason);\n *   });\n * ```\n *\n * Note: progress/notify callbacks are not currently supported via the ES6-style interface.\n *\n * However, the more traditional CommonJS-style usage is still available, and documented below.\n *\n * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an\n * interface for interacting with an object that represents the result of an action that is\n * performed asynchronously, and may or may not be finished at any given point in time.\n *\n * From the perspective of dealing with error handling, deferred and promise APIs are to\n * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.\n *\n * ```js\n *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`\n *   // are available in the current lexical scope (they could have been injected or passed in).\n *\n *   function asyncGreet(name) {\n *     var deferred = $q.defer();\n *\n *     setTimeout(function() {\n *       deferred.notify('About to greet ' + name + '.');\n *\n *       if (okToGreet(name)) {\n *         deferred.resolve('Hello, ' + name + '!');\n *       } else {\n *         deferred.reject('Greeting ' + name + ' is not allowed.');\n *       }\n *     }, 1000);\n *\n *     return deferred.promise;\n *   }\n *\n *   var promise = asyncGreet('Robin Hood');\n *   promise.then(function(greeting) {\n *     alert('Success: ' + greeting);\n *   }, function(reason) {\n *     alert('Failed: ' + reason);\n *   }, function(update) {\n *     alert('Got notification: ' + update);\n *   });\n * ```\n *\n * At first it might not be obvious why this extra complexity is worth the trouble. The payoff\n * comes in the way of guarantees that promise and deferred APIs make, see\n * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.\n *\n * Additionally the promise api allows for composition that is very hard to do with the\n * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.\n * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the\n * section on serial or parallel joining of promises.\n *\n * # The Deferred API\n *\n * A new instance of deferred is constructed by calling `$q.defer()`.\n *\n * The purpose of the deferred object is to expose the associated Promise instance as well as APIs\n * that can be used for signaling the successful or unsuccessful completion, as well as the status\n * of the task.\n *\n * **Methods**\n *\n * - `resolve(value)` â€“ resolves the derived promise with the `value`. If the value is a rejection\n *   constructed via `$q.reject`, the promise will be rejected instead.\n * - `reject(reason)` â€“ rejects the derived promise with the `reason`. This is equivalent to\n *   resolving it with a rejection constructed via `$q.reject`.\n * - `notify(value)` - provides updates on the status of the promise's execution. This may be called\n *   multiple times before the promise is either resolved or rejected.\n *\n * **Properties**\n *\n * - promise â€“ `{Promise}` â€“ promise object associated with this deferred.\n *\n *\n * # The Promise API\n *\n * A new promise instance is created when a deferred instance is created and can be retrieved by\n * calling `deferred.promise`.\n *\n * The purpose of the promise object is to allow for interested parties to get access to the result\n * of the deferred task when it completes.\n *\n * **Methods**\n *\n * - `then(successCallback, errorCallback, notifyCallback)` â€“ regardless of when the promise was or\n *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously\n *   as soon as the result is available. The callbacks are called with a single argument: the result\n *   or rejection reason. Additionally, the notify callback may be called zero or more times to\n *   provide a progress indication, before the promise is resolved or rejected.\n *\n *   This method *returns a new promise* which is resolved or rejected via the return value of the\n *   `successCallback`, `errorCallback`. It also notifies via the return value of the\n *   `notifyCallback` method. The promise cannot be resolved or rejected from the notifyCallback\n *   method.\n *\n * - `catch(errorCallback)` â€“ shorthand for `promise.then(null, errorCallback)`\n *\n * - `finally(callback, notifyCallback)` â€“ allows you to observe either the fulfillment or rejection of a promise,\n *   but to do so without modifying the final value. This is useful to release resources or do some\n *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full\n *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for\n *   more information.\n *\n * # Chaining promises\n *\n * Because calling the `then` method of a promise returns a new derived promise, it is easily\n * possible to create a chain of promises:\n *\n * ```js\n *   promiseB = promiseA.then(function(result) {\n *     return result + 1;\n *   });\n *\n *   // promiseB will be resolved immediately after promiseA is resolved and its value\n *   // will be the result of promiseA incremented by 1\n * ```\n *\n * It is possible to create chains of any length and since a promise can be resolved with another\n * promise (which will defer its resolution further), it is possible to pause/defer resolution of\n * the promises at any point in the chain. This makes it possible to implement powerful APIs like\n * $http's response interceptors.\n *\n *\n * # Differences between Kris Kowal's Q and $q\n *\n *  There are two main differences:\n *\n * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation\n *   mechanism in angular, which means faster propagation of resolution or rejection into your\n *   models and avoiding unnecessary browser repaints, which would result in flickering UI.\n * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains\n *   all the important functionality needed for common async tasks.\n *\n *  # Testing\n *\n *  ```js\n *    it('should simulate promise', inject(function($q, $rootScope) {\n *      var deferred = $q.defer();\n *      var promise = deferred.promise;\n *      var resolvedValue;\n *\n *      promise.then(function(value) { resolvedValue = value; });\n *      expect(resolvedValue).toBeUndefined();\n *\n *      // Simulate resolving of promise\n *      deferred.resolve(123);\n *      // Note that the 'then' function does not get called synchronously.\n *      // This is because we want the promise API to always be async, whether or not\n *      // it got called synchronously or asynchronously.\n *      expect(resolvedValue).toBeUndefined();\n *\n *      // Propagate promise resolution to 'then' functions using $apply().\n *      $rootScope.$apply();\n *      expect(resolvedValue).toEqual(123);\n *    }));\n *  ```\n *\n * @param {function(function, function)} resolver Function which is responsible for resolving or\n *   rejecting the newly created promise. The first parameter is a function which resolves the\n *   promise, the second parameter is a function which rejects the promise.\n *\n * @returns {Promise} The newly created promise.\n */\nfunction $QProvider() {\n\n  this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {\n    return qFactory(function(callback) {\n      $rootScope.$evalAsync(callback);\n    }, $exceptionHandler);\n  }];\n}\n\nfunction $$QProvider() {\n  this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {\n    return qFactory(function(callback) {\n      $browser.defer(callback);\n    }, $exceptionHandler);\n  }];\n}\n\n/**\n * Constructs a promise manager.\n *\n * @param {function(function)} nextTick Function for executing functions in the next turn.\n * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for\n *     debugging purposes.\n * @returns {object} Promise manager.\n */\nfunction qFactory(nextTick, exceptionHandler) {\n  var $qMinErr = minErr('$q', TypeError);\n  function callOnce(self, resolveFn, rejectFn) {\n    var called = false;\n    function wrap(fn) {\n      return function(value) {\n        if (called) return;\n        called = true;\n        fn.call(self, value);\n      };\n    }\n\n    return [wrap(resolveFn), wrap(rejectFn)];\n  }\n\n  /**\n   * @ngdoc method\n   * @name ng.$q#defer\n   * @kind function\n   *\n   * @description\n   * Creates a `Deferred` object which represents a task which will finish in the future.\n   *\n   * @returns {Deferred} Returns a new instance of deferred.\n   */\n  var defer = function() {\n    return new Deferred();\n  };\n\n  function Promise() {\n    this.$$state = { status: 0 };\n  }\n\n  Promise.prototype = {\n    then: function(onFulfilled, onRejected, progressBack) {\n      var result = new Deferred();\n\n      this.$$state.pending = this.$$state.pending || [];\n      this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);\n      if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);\n\n      return result.promise;\n    },\n\n    \"catch\": function(callback) {\n      return this.then(null, callback);\n    },\n\n    \"finally\": function(callback, progressBack) {\n      return this.then(function(value) {\n        return handleCallback(value, true, callback);\n      }, function(error) {\n        return handleCallback(error, false, callback);\n      }, progressBack);\n    }\n  };\n\n  //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native\n  function simpleBind(context, fn) {\n    return function(value) {\n      fn.call(context, value);\n    };\n  }\n\n  function processQueue(state) {\n    var fn, promise, pending;\n\n    pending = state.pending;\n    state.processScheduled = false;\n    state.pending = undefined;\n    for (var i = 0, ii = pending.length; i < ii; ++i) {\n      promise = pending[i][0];\n      fn = pending[i][state.status];\n      try {\n        if (isFunction(fn)) {\n          promise.resolve(fn(state.value));\n        } else if (state.status === 1) {\n          promise.resolve(state.value);\n        } else {\n          promise.reject(state.value);\n        }\n      } catch (e) {\n        promise.reject(e);\n        exceptionHandler(e);\n      }\n    }\n  }\n\n  function scheduleProcessQueue(state) {\n    if (state.processScheduled || !state.pending) return;\n    state.processScheduled = true;\n    nextTick(function() { processQueue(state); });\n  }\n\n  function Deferred() {\n    this.promise = new Promise();\n    //Necessary to support unbound execution :/\n    this.resolve = simpleBind(this, this.resolve);\n    this.reject = simpleBind(this, this.reject);\n    this.notify = simpleBind(this, this.notify);\n  }\n\n  Deferred.prototype = {\n    resolve: function(val) {\n      if (this.promise.$$state.status) return;\n      if (val === this.promise) {\n        this.$$reject($qMinErr(\n          'qcycle',\n          \"Expected promise to be resolved with value other than itself '{0}'\",\n          val));\n      } else {\n        this.$$resolve(val);\n      }\n\n    },\n\n    $$resolve: function(val) {\n      var then, fns;\n\n      fns = callOnce(this, this.$$resolve, this.$$reject);\n      try {\n        if ((isObject(val) || isFunction(val))) then = val && val.then;\n        if (isFunction(then)) {\n          this.promise.$$state.status = -1;\n          then.call(val, fns[0], fns[1], this.notify);\n        } else {\n          this.promise.$$state.value = val;\n          this.promise.$$state.status = 1;\n          scheduleProcessQueue(this.promise.$$state);\n        }\n      } catch (e) {\n        fns[1](e);\n        exceptionHandler(e);\n      }\n    },\n\n    reject: function(reason) {\n      if (this.promise.$$state.status) return;\n      this.$$reject(reason);\n    },\n\n    $$reject: function(reason) {\n      this.promise.$$state.value = reason;\n      this.promise.$$state.status = 2;\n      scheduleProcessQueue(this.promise.$$state);\n    },\n\n    notify: function(progress) {\n      var callbacks = this.promise.$$state.pending;\n\n      if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {\n        nextTick(function() {\n          var callback, result;\n          for (var i = 0, ii = callbacks.length; i < ii; i++) {\n            result = callbacks[i][0];\n            callback = callbacks[i][3];\n            try {\n              result.notify(isFunction(callback) ? callback(progress) : progress);\n            } catch (e) {\n              exceptionHandler(e);\n            }\n          }\n        });\n      }\n    }\n  };\n\n  /**\n   * @ngdoc method\n   * @name $q#reject\n   * @kind function\n   *\n   * @description\n   * Creates a promise that is resolved as rejected with the specified `reason`. This api should be\n   * used to forward rejection in a chain of promises. If you are dealing with the last promise in\n   * a promise chain, you don't need to worry about it.\n   *\n   * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of\n   * `reject` as the `throw` keyword in JavaScript. This also means that if you \"catch\" an error via\n   * a promise error callback and you want to forward the error to the promise derived from the\n   * current promise, you have to \"rethrow\" the error by returning a rejection constructed via\n   * `reject`.\n   *\n   * ```js\n   *   promiseB = promiseA.then(function(result) {\n   *     // success: do something and resolve promiseB\n   *     //          with the old or a new result\n   *     return result;\n   *   }, function(reason) {\n   *     // error: handle the error if possible and\n   *     //        resolve promiseB with newPromiseOrValue,\n   *     //        otherwise forward the rejection to promiseB\n   *     if (canHandle(reason)) {\n   *      // handle the error and recover\n   *      return newPromiseOrValue;\n   *     }\n   *     return $q.reject(reason);\n   *   });\n   * ```\n   *\n   * @param {*} reason Constant, message, exception or an object representing the rejection reason.\n   * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.\n   */\n  var reject = function(reason) {\n    var result = new Deferred();\n    result.reject(reason);\n    return result.promise;\n  };\n\n  var makePromise = function makePromise(value, resolved) {\n    var result = new Deferred();\n    if (resolved) {\n      result.resolve(value);\n    } else {\n      result.reject(value);\n    }\n    return result.promise;\n  };\n\n  var handleCallback = function handleCallback(value, isResolved, callback) {\n    var callbackOutput = null;\n    try {\n      if (isFunction(callback)) callbackOutput = callback();\n    } catch (e) {\n      return makePromise(e, false);\n    }\n    if (isPromiseLike(callbackOutput)) {\n      return callbackOutput.then(function() {\n        return makePromise(value, isResolved);\n      }, function(error) {\n        return makePromise(error, false);\n      });\n    } else {\n      return makePromise(value, isResolved);\n    }\n  };\n\n  /**\n   * @ngdoc method\n   * @name $q#when\n   * @kind function\n   *\n   * @description\n   * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.\n   * This is useful when you are dealing with an object that might or might not be a promise, or if\n   * the promise comes from a source that can't be trusted.\n   *\n   * @param {*} value Value or a promise\n   * @returns {Promise} Returns a promise of the passed value or promise\n   */\n\n\n  var when = function(value, callback, errback, progressBack) {\n    var result = new Deferred();\n    result.resolve(value);\n    return result.promise.then(callback, errback, progressBack);\n  };\n\n  /**\n   * @ngdoc method\n   * @name $q#all\n   * @kind function\n   *\n   * @description\n   * Combines multiple promises into a single promise that is resolved when all of the input\n   * promises are resolved.\n   *\n   * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.\n   * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,\n   *   each value corresponding to the promise at the same index/key in the `promises` array/hash.\n   *   If any of the promises is resolved with a rejection, this resulting promise will be rejected\n   *   with the same rejection value.\n   */\n\n  function all(promises) {\n    var deferred = new Deferred(),\n        counter = 0,\n        results = isArray(promises) ? [] : {};\n\n    forEach(promises, function(promise, key) {\n      counter++;\n      when(promise).then(function(value) {\n        if (results.hasOwnProperty(key)) return;\n        results[key] = value;\n        if (!(--counter)) deferred.resolve(results);\n      }, function(reason) {\n        if (results.hasOwnProperty(key)) return;\n        deferred.reject(reason);\n      });\n    });\n\n    if (counter === 0) {\n      deferred.resolve(results);\n    }\n\n    return deferred.promise;\n  }\n\n  var $Q = function Q(resolver) {\n    if (!isFunction(resolver)) {\n      throw $qMinErr('norslvr', \"Expected resolverFn, got '{0}'\", resolver);\n    }\n\n    if (!(this instanceof Q)) {\n      // More useful when $Q is the Promise itself.\n      return new Q(resolver);\n    }\n\n    var deferred = new Deferred();\n\n    function resolveFn(value) {\n      deferred.resolve(value);\n    }\n\n    function rejectFn(reason) {\n      deferred.reject(reason);\n    }\n\n    resolver(resolveFn, rejectFn);\n\n    return deferred.promise;\n  };\n\n  $Q.defer = defer;\n  $Q.reject = reject;\n  $Q.when = when;\n  $Q.all = all;\n\n  return $Q;\n}\n\nfunction $$RAFProvider() { //rAF\n  this.$get = ['$window', '$timeout', function($window, $timeout) {\n    var requestAnimationFrame = $window.requestAnimationFrame ||\n                                $window.webkitRequestAnimationFrame;\n\n    var cancelAnimationFrame = $window.cancelAnimationFrame ||\n                               $window.webkitCancelAnimationFrame ||\n                               $window.webkitCancelRequestAnimationFrame;\n\n    var rafSupported = !!requestAnimationFrame;\n    var raf = rafSupported\n      ? function(fn) {\n          var id = requestAnimationFrame(fn);\n          return function() {\n            cancelAnimationFrame(id);\n          };\n        }\n      : function(fn) {\n          var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666\n          return function() {\n            $timeout.cancel(timer);\n          };\n        };\n\n    raf.supported = rafSupported;\n\n    return raf;\n  }];\n}\n\n/**\n * DESIGN NOTES\n *\n * The design decisions behind the scope are heavily favored for speed and memory consumption.\n *\n * The typical use of scope is to watch the expressions, which most of the time return the same\n * value as last time so we optimize the operation.\n *\n * Closures construction is expensive in terms of speed as well as memory:\n *   - No closures, instead use prototypical inheritance for API\n *   - Internal state needs to be stored on scope directly, which means that private state is\n *     exposed as $$____ properties\n *\n * Loop operations are optimized by using while(count--) { ... }\n *   - this means that in order to keep the same order of execution as addition we have to add\n *     items to the array at the beginning (unshift) instead of at the end (push)\n *\n * Child scopes are created and removed often\n *   - Using an array would be slow since inserts in middle are expensive so we use linked list\n *\n * There are few watches then a lot of observers. This is why you don't want the observer to be\n * implemented in the same way as watch. Watch requires return of initialization function which\n * are expensive to construct.\n */\n\n\n/**\n * @ngdoc provider\n * @name $rootScopeProvider\n * @description\n *\n * Provider for the $rootScope service.\n */\n\n/**\n * @ngdoc method\n * @name $rootScopeProvider#digestTtl\n * @description\n *\n * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and\n * assuming that the model is unstable.\n *\n * The current default is 10 iterations.\n *\n * In complex applications it's possible that the dependencies between `$watch`s will result in\n * several digest iterations. However if an application needs more than the default 10 digest\n * iterations for its model to stabilize then you should investigate what is causing the model to\n * continuously change during the digest.\n *\n * Increasing the TTL could have performance implications, so you should not change it without\n * proper justification.\n *\n * @param {number} limit The number of digest iterations.\n */\n\n\n/**\n * @ngdoc service\n * @name $rootScope\n * @description\n *\n * Every application has a single root {@link ng.$rootScope.Scope scope}.\n * All other scopes are descendant scopes of the root scope. Scopes provide separation\n * between the model and the view, via a mechanism for watching the model for changes.\n * They also provide an event emission/broadcast and subscription facility. See the\n * {@link guide/scope developer guide on scopes}.\n */\nfunction $RootScopeProvider() {\n  var TTL = 10;\n  var $rootScopeMinErr = minErr('$rootScope');\n  var lastDirtyWatch = null;\n  var applyAsyncId = null;\n\n  this.digestTtl = function(value) {\n    if (arguments.length) {\n      TTL = value;\n    }\n    return TTL;\n  };\n\n  function createChildScopeClass(parent) {\n    function ChildScope() {\n      this.$$watchers = this.$$nextSibling =\n          this.$$childHead = this.$$childTail = null;\n      this.$$listeners = {};\n      this.$$listenerCount = {};\n      this.$$watchersCount = 0;\n      this.$id = nextUid();\n      this.$$ChildScope = null;\n    }\n    ChildScope.prototype = parent;\n    return ChildScope;\n  }\n\n  this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',\n      function($injector, $exceptionHandler, $parse, $browser) {\n\n    function destroyChildScope($event) {\n        $event.currentScope.$$destroyed = true;\n    }\n\n    /**\n     * @ngdoc type\n     * @name $rootScope.Scope\n     *\n     * @description\n     * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the\n     * {@link auto.$injector $injector}. Child scopes are created using the\n     * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when\n     * compiled HTML template is executed.)\n     *\n     * Here is a simple scope snippet to show how you can interact with the scope.\n     * ```html\n     * <file src=\"./test/ng/rootScopeSpec.js\" tag=\"docs1\" />\n     * ```\n     *\n     * # Inheritance\n     * A scope can inherit from a parent scope, as in this example:\n     * ```js\n         var parent = $rootScope;\n         var child = parent.$new();\n\n         parent.salutation = \"Hello\";\n         expect(child.salutation).toEqual('Hello');\n\n         child.salutation = \"Welcome\";\n         expect(child.salutation).toEqual('Welcome');\n         expect(parent.salutation).toEqual('Hello');\n     * ```\n     *\n     * When interacting with `Scope` in tests, additional helper methods are available on the\n     * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional\n     * details.\n     *\n     *\n     * @param {Object.<string, function()>=} providers Map of service factory which need to be\n     *                                       provided for the current scope. Defaults to {@link ng}.\n     * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should\n     *                              append/override services provided by `providers`. This is handy\n     *                              when unit-testing and having the need to override a default\n     *                              service.\n     * @returns {Object} Newly created scope.\n     *\n     */\n    function Scope() {\n      this.$id = nextUid();\n      this.$$phase = this.$parent = this.$$watchers =\n                     this.$$nextSibling = this.$$prevSibling =\n                     this.$$childHead = this.$$childTail = null;\n      this.$root = this;\n      this.$$destroyed = false;\n      this.$$listeners = {};\n      this.$$listenerCount = {};\n      this.$$isolateBindings = null;\n    }\n\n    /**\n     * @ngdoc property\n     * @name $rootScope.Scope#$id\n     *\n     * @description\n     * Unique scope ID (monotonically increasing) useful for debugging.\n     */\n\n     /**\n      * @ngdoc property\n      * @name $rootScope.Scope#$parent\n      *\n      * @description\n      * Reference to the parent scope.\n      */\n\n      /**\n       * @ngdoc property\n       * @name $rootScope.Scope#$root\n       *\n       * @description\n       * Reference to the root scope.\n       */\n\n    Scope.prototype = {\n      constructor: Scope,\n      /**\n       * @ngdoc method\n       * @name $rootScope.Scope#$new\n       * @kind function\n       *\n       * @description\n       * Creates a new child {@link ng.$rootScope.Scope scope}.\n       *\n       * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.\n       * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.\n       *\n       * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is\n       * desired for the scope and its child scopes to be permanently detached from the parent and\n       * thus stop participating in model change detection and listener notification by invoking.\n       *\n       * @param {boolean} isolate If true, then the scope does not prototypically inherit from the\n       *         parent scope. The scope is isolated, as it can not see parent scope properties.\n       *         When creating widgets, it is useful for the widget to not accidentally read parent\n       *         state.\n       *\n       * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`\n       *                              of the newly created scope. Defaults to `this` scope if not provided.\n       *                              This is used when creating a transclude scope to correctly place it\n       *                              in the scope hierarchy while maintaining the correct prototypical\n       *                              inheritance.\n       *\n       * @returns {Object} The newly created child scope.\n       *\n       */\n      $new: function(isolate, parent) {\n        var child;\n\n        parent = parent || this;\n\n        if (isolate) {\n          child = new Scope();\n          child.$root = this.$root;\n        } else {\n          // Only create a child scope class if somebody asks for one,\n          // but cache it to allow the VM to optimize lookups.\n          if (!this.$$ChildScope) {\n            this.$$ChildScope = createChildScopeClass(this);\n          }\n          child = new this.$$ChildScope();\n        }\n        child.$parent = parent;\n        child.$$prevSibling = parent.$$childTail;\n        if (parent.$$childHead) {\n          parent.$$childTail.$$nextSibling = child;\n          parent.$$childTail = child;\n        } else {\n          parent.$$childHead = parent.$$childTail = child;\n        }\n\n        // When the new scope is not isolated or we inherit from `this`, and\n        // the parent scope is destroyed, the property `$$destroyed` is inherited\n        // prototypically. In all other cases, this property needs to be set\n        // when the parent scope is destroyed.\n        // The listener needs to be added after the parent is set\n        if (isolate || parent != this) child.$on('$destroy', destroyChildScope);\n\n        return child;\n      },\n\n      /**\n       * @ngdoc method\n       * @name $rootScope.Scope#$watch\n       * @kind function\n       *\n       * @description\n       * Registers a `listener` callback to be executed whenever the `watchExpression` changes.\n       *\n       * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest\n       *   $digest()} and should return the value that will be watched. (Since\n       *   {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the\n       *   `watchExpression` can execute multiple times per\n       *   {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.)\n       * - The `listener` is called only when the value from the current `watchExpression` and the\n       *   previous call to `watchExpression` are not equal (with the exception of the initial run,\n       *   see below). Inequality is determined according to reference inequality,\n       *   [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)\n       *    via the `!==` Javascript operator, unless `objectEquality == true`\n       *   (see next point)\n       * - When `objectEquality == true`, inequality of the `watchExpression` is determined\n       *   according to the {@link angular.equals} function. To save the value of the object for\n       *   later comparison, the {@link angular.copy} function is used. This therefore means that\n       *   watching complex objects will have adverse memory and performance implications.\n       * - The watch `listener` may change the model, which may trigger other `listener`s to fire.\n       *   This is achieved by rerunning the watchers until no changes are detected. The rerun\n       *   iteration limit is 10 to prevent an infinite loop deadlock.\n       *\n       *\n       * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,\n       * you can register a `watchExpression` function with no `listener`. (Since `watchExpression`\n       * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a\n       * change is detected, be prepared for multiple calls to your listener.)\n       *\n       * After a watcher is registered with the scope, the `listener` fn is called asynchronously\n       * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the\n       * watcher. In rare cases, this is undesirable because the listener is called when the result\n       * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you\n       * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the\n       * listener was called due to initialization.\n       *\n       *\n       *\n       * # Example\n       * ```js\n           // let's assume that scope was dependency injected as the $rootScope\n           var scope = $rootScope;\n           scope.name = 'misko';\n           scope.counter = 0;\n\n           expect(scope.counter).toEqual(0);\n           scope.$watch('name', function(newValue, oldValue) {\n             scope.counter = scope.counter + 1;\n           });\n           expect(scope.counter).toEqual(0);\n\n           scope.$digest();\n           // the listener is always called during the first $digest loop after it was registered\n           expect(scope.counter).toEqual(1);\n\n           scope.$digest();\n           // but now it will not be called unless the value changes\n           expect(scope.counter).toEqual(1);\n\n           scope.name = 'adam';\n           scope.$digest();\n           expect(scope.counter).toEqual(2);\n\n\n\n           // Using a function as a watchExpression\n           var food;\n           scope.foodCounter = 0;\n           expect(scope.foodCounter).toEqual(0);\n           scope.$watch(\n             // This function returns the value being watched. It is called for each turn of the $digest loop\n             function() { return food; },\n             // This is the change listener, called when the value returned from the above function changes\n             function(newValue, oldValue) {\n               if ( newValue !== oldValue ) {\n                 // Only increment the counter if the value changed\n                 scope.foodCounter = scope.foodCounter + 1;\n               }\n             }\n           );\n           // No digest has been run so the counter will be zero\n           expect(scope.foodCounter).toEqual(0);\n\n           // Run the digest but since food has not changed count will still be zero\n           scope.$digest();\n           expect(scope.foodCounter).toEqual(0);\n\n           // Update food and run digest.  Now the counter will increment\n           food = 'cheeseburger';\n           scope.$digest();\n           expect(scope.foodCounter).toEqual(1);\n\n       * ```\n       *\n       *\n       *\n       * @param {(function()|string)} watchExpression Expression that is evaluated on each\n       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers\n       *    a call to the `listener`.\n       *\n       *    - `string`: Evaluated as {@link guide/expression expression}\n       *    - `function(scope)`: called with current `scope` as a parameter.\n       * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value\n       *    of `watchExpression` changes.\n       *\n       *    - `newVal` contains the current value of the `watchExpression`\n       *    - `oldVal` contains the previous value of the `watchExpression`\n       *    - `scope` refers to the current scope\n       * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of\n       *     comparing for reference equality.\n       * @returns {function()} Returns a deregistration function for this listener.\n       */\n      $watch: function(watchExp, listener, objectEquality) {\n        var get = $parse(watchExp);\n\n        if (get.$$watchDelegate) {\n          return get.$$watchDelegate(this, listener, objectEquality, get);\n        }\n        var scope = this,\n            array = scope.$$watchers,\n            watcher = {\n              fn: listener,\n              last: initWatchVal,\n              get: get,\n              exp: watchExp,\n              eq: !!objectEquality\n            };\n\n        lastDirtyWatch = null;\n\n        if (!isFunction(listener)) {\n          watcher.fn = noop;\n        }\n\n        if (!array) {\n          array = scope.$$watchers = [];\n        }\n        // we use unshift since we use a while loop in $digest for speed.\n        // the while loop reads in reverse order.\n        array.unshift(watcher);\n\n        return function deregisterWatch() {\n          arrayRemove(array, watcher);\n          lastDirtyWatch = null;\n        };\n      },\n\n      /**\n       * @ngdoc method\n       * @name $rootScope.Scope#$watchGroup\n       * @kind function\n       *\n       * @description\n       * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.\n       * If any one expression in the collection changes the `listener` is executed.\n       *\n       * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every\n       *   call to $digest() to see if any items changes.\n       * - The `listener` is called whenever any expression in the `watchExpressions` array changes.\n       *\n       * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually\n       * watched using {@link ng.$rootScope.Scope#$watch $watch()}\n       *\n       * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any\n       *    expression in `watchExpressions` changes\n       *    The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching\n       *    those of `watchExpression`\n       *    and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching\n       *    those of `watchExpression`\n       *    The `scope` refers to the current scope.\n       * @returns {function()} Returns a de-registration function for all listeners.\n       */\n      $watchGroup: function(watchExpressions, listener) {\n        var oldValues = new Array(watchExpressions.length);\n        var newValues = new Array(watchExpressions.length);\n        var deregisterFns = [];\n        var self = this;\n        var changeReactionScheduled = false;\n        var firstRun = true;\n\n        if (!watchExpressions.length) {\n          // No expressions means we call the listener ASAP\n          var shouldCall = true;\n          self.$evalAsync(function() {\n            if (shouldCall) listener(newValues, newValues, self);\n          });\n          return function deregisterWatchGroup() {\n            shouldCall = false;\n          };\n        }\n\n        if (watchExpressions.length === 1) {\n          // Special case size of one\n          return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {\n            newValues[0] = value;\n            oldValues[0] = oldValue;\n            listener(newValues, (value === oldValue) ? newValues : oldValues, scope);\n          });\n        }\n\n        forEach(watchExpressions, function(expr, i) {\n          var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {\n            newValues[i] = value;\n            oldValues[i] = oldValue;\n            if (!changeReactionScheduled) {\n              changeReactionScheduled = true;\n              self.$evalAsync(watchGroupAction);\n            }\n          });\n          deregisterFns.push(unwatchFn);\n        });\n\n        function watchGroupAction() {\n          changeReactionScheduled = false;\n\n          if (firstRun) {\n            firstRun = false;\n            listener(newValues, newValues, self);\n          } else {\n            listener(newValues, oldValues, self);\n          }\n        }\n\n        return function deregisterWatchGroup() {\n          while (deregisterFns.length) {\n            deregisterFns.shift()();\n          }\n        };\n      },\n\n\n      /**\n       * @ngdoc method\n       * @name $rootScope.Scope#$watchCollection\n       * @kind function\n       *\n       * @description\n       * Shallow watches the properties of an object and fires whenever any of the properties change\n       * (for arrays, this implies watching the array items; for object maps, this implies watching\n       * the properties). If a change is detected, the `listener` callback is fired.\n       *\n       * - The `obj` collection is observed via standard $watch operation and is examined on every\n       *   call to $digest() to see if any items have been added, removed, or moved.\n       * - The `listener` is called whenever anything within the `obj` has changed. Examples include\n       *   adding, removing, and moving items belonging to an object or array.\n       *\n       *\n       * # Example\n       * ```js\n          $scope.names = ['igor', 'matias', 'misko', 'james'];\n          $scope.dataCount = 4;\n\n          $scope.$watchCollection('names', function(newNames, oldNames) {\n            $scope.dataCount = newNames.length;\n          });\n\n          expect($scope.dataCount).toEqual(4);\n          $scope.$digest();\n\n          //still at 4 ... no changes\n          expect($scope.dataCount).toEqual(4);\n\n          $scope.names.pop();\n          $scope.$digest();\n\n          //now there's been a change\n          expect($scope.dataCount).toEqual(3);\n       * ```\n       *\n       *\n       * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The\n       *    expression value should evaluate to an object or an array which is observed on each\n       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the\n       *    collection will trigger a call to the `listener`.\n       *\n       * @param {function(newCollection, oldCollection, scope)} listener a callback function called\n       *    when a change is detected.\n       *    - The `newCollection` object is the newly modified data obtained from the `obj` expression\n       *    - The `oldCollection` object is a copy of the former collection data.\n       *      Due to performance considerations, the`oldCollection` value is computed only if the\n       *      `listener` function declares two or more arguments.\n       *    - The `scope` argument refers to the current scope.\n       *\n       * @returns {function()} Returns a de-registration function for this listener. When the\n       *    de-registration function is executed, the internal watch operation is terminated.\n       */\n      $watchCollection: function(obj, listener) {\n        $watchCollectionInterceptor.$stateful = true;\n\n        var self = this;\n        // the current value, updated on each dirty-check run\n        var newValue;\n        // a shallow copy of the newValue from the last dirty-check run,\n        // updated to match newValue during dirty-check run\n        var oldValue;\n        // a shallow copy of the newValue from when the last change happened\n        var veryOldValue;\n        // only track veryOldValue if the listener is asking for it\n        var trackVeryOldValue = (listener.length > 1);\n        var changeDetected = 0;\n        var changeDetector = $parse(obj, $watchCollectionInterceptor);\n        var internalArray = [];\n        var internalObject = {};\n        var initRun = true;\n        var oldLength = 0;\n\n        function $watchCollectionInterceptor(_value) {\n          newValue = _value;\n          var newLength, key, bothNaN, newItem, oldItem;\n\n          // If the new value is undefined, then return undefined as the watch may be a one-time watch\n          if (isUndefined(newValue)) return;\n\n          if (!isObject(newValue)) { // if primitive\n            if (oldValue !== newValue) {\n              oldValue = newValue;\n              changeDetected++;\n            }\n          } else if (isArrayLike(newValue)) {\n            if (oldValue !== internalArray) {\n              // we are transitioning from something which was not an array into array.\n              oldValue = internalArray;\n              oldLength = oldValue.length = 0;\n              changeDetected++;\n            }\n\n            newLength = newValue.length;\n\n            if (oldLength !== newLength) {\n              // if lengths do not match we need to trigger change notification\n              changeDetected++;\n              oldValue.length = oldLength = newLength;\n            }\n            // copy the items to oldValue and look for changes.\n            for (var i = 0; i < newLength; i++) {\n              oldItem = oldValue[i];\n              newItem = newValue[i];\n\n              bothNaN = (oldItem !== oldItem) && (newItem !== newItem);\n              if (!bothNaN && (oldItem !== newItem)) {\n                changeDetected++;\n                oldValue[i] = newItem;\n              }\n            }\n          } else {\n            if (oldValue !== internalObject) {\n              // we are transitioning from something which was not an object into object.\n              oldValue = internalObject = {};\n              oldLength = 0;\n              changeDetected++;\n            }\n            // copy the items to oldValue and look for changes.\n            newLength = 0;\n            for (key in newValue) {\n              if (newValue.hasOwnProperty(key)) {\n                newLength++;\n                newItem = newValue[key];\n                oldItem = oldValue[key];\n\n                if (key in oldValue) {\n                  bothNaN = (oldItem !== oldItem) && (newItem !== newItem);\n                  if (!bothNaN && (oldItem !== newItem)) {\n                    changeDetected++;\n                    oldValue[key] = newItem;\n                  }\n                } else {\n                  oldLength++;\n                  oldValue[key] = newItem;\n                  changeDetected++;\n                }\n              }\n            }\n            if (oldLength > newLength) {\n              // we used to have more keys, need to find them and destroy them.\n              changeDetected++;\n              for (key in oldValue) {\n                if (!newValue.hasOwnProperty(key)) {\n                  oldLength--;\n                  delete oldValue[key];\n                }\n              }\n            }\n          }\n          return changeDetected;\n        }\n\n        function $watchCollectionAction() {\n          if (initRun) {\n            initRun = false;\n            listener(newValue, newValue, self);\n          } else {\n            listener(newValue, veryOldValue, self);\n          }\n\n          // make a copy for the next time a collection is changed\n          if (trackVeryOldValue) {\n            if (!isObject(newValue)) {\n              //primitive\n              veryOldValue = newValue;\n            } else if (isArrayLike(newValue)) {\n              veryOldValue = new Array(newValue.length);\n              for (var i = 0; i < newValue.length; i++) {\n                veryOldValue[i] = newValue[i];\n              }\n            } else { // if object\n              veryOldValue = {};\n              for (var key in newValue) {\n                if (hasOwnProperty.call(newValue, key)) {\n                  veryOldValue[key] = newValue[key];\n                }\n              }\n            }\n          }\n        }\n\n        return this.$watch(changeDetector, $watchCollectionAction);\n      },\n\n      /**\n       * @ngdoc method\n       * @name $rootScope.Scope#$digest\n       * @kind function\n       *\n       * @description\n       * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and\n       * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change\n       * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}\n       * until no more listeners are firing. This means that it is possible to get into an infinite\n       * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of\n       * iterations exceeds 10.\n       *\n       * Usually, you don't call `$digest()` directly in\n       * {@link ng.directive:ngController controllers} or in\n       * {@link ng.$compileProvider#directive directives}.\n       * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within\n       * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.\n       *\n       * If you want to be notified whenever `$digest()` is called,\n       * you can register a `watchExpression` function with\n       * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.\n       *\n       * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.\n       *\n       * # Example\n       * ```js\n           var scope = ...;\n           scope.name = 'misko';\n           scope.counter = 0;\n\n           expect(scope.counter).toEqual(0);\n           scope.$watch('name', function(newValue, oldValue) {\n             scope.counter = scope.counter + 1;\n           });\n           expect(scope.counter).toEqual(0);\n\n           scope.$digest();\n           // the listener is always called during the first $digest loop after it was registered\n           expect(scope.counter).toEqual(1);\n\n           scope.$digest();\n           // but now it will not be called unless the value changes\n           expect(scope.counter).toEqual(1);\n\n           scope.name = 'adam';\n           scope.$digest();\n           expect(scope.counter).toEqual(2);\n       * ```\n       *\n       */\n      $digest: function() {\n        var watch, value, last,\n            watchers,\n            length,\n            dirty, ttl = TTL,\n            next, current, target = this,\n            watchLog = [],\n            logIdx, logMsg, asyncTask;\n\n        beginPhase('$digest');\n        // Check for changes to browser url that happened in sync before the call to $digest\n        $browser.$$checkUrlChange();\n\n        if (this === $rootScope && applyAsyncId !== null) {\n          // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then\n          // cancel the scheduled $apply and flush the queue of expressions to be evaluated.\n          $browser.defer.cancel(applyAsyncId);\n          flushApplyAsync();\n        }\n\n        lastDirtyWatch = null;\n\n        do { // \"while dirty\" loop\n          dirty = false;\n          current = target;\n\n          while (asyncQueue.length) {\n            try {\n              asyncTask = asyncQueue.shift();\n              asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);\n            } catch (e) {\n              $exceptionHandler(e);\n            }\n            lastDirtyWatch = null;\n          }\n\n          traverseScopesLoop:\n          do { // \"traverse the scopes\" loop\n            if ((watchers = current.$$watchers)) {\n              // process our watches\n              length = watchers.length;\n              while (length--) {\n                try {\n                  watch = watchers[length];\n                  // Most common watches are on primitives, in which case we can short\n                  // circuit it with === operator, only when === fails do we use .equals\n                  if (watch) {\n                    if ((value = watch.get(current)) !== (last = watch.last) &&\n                        !(watch.eq\n                            ? equals(value, last)\n                            : (typeof value === 'number' && typeof last === 'number'\n                               && isNaN(value) && isNaN(last)))) {\n                      dirty = true;\n                      lastDirtyWatch = watch;\n                      watch.last = watch.eq ? copy(value, null) : value;\n                      watch.fn(value, ((last === initWatchVal) ? value : last), current);\n                      if (ttl < 5) {\n                        logIdx = 4 - ttl;\n                        if (!watchLog[logIdx]) watchLog[logIdx] = [];\n                        watchLog[logIdx].push({\n                          msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,\n                          newVal: value,\n                          oldVal: last\n                        });\n                      }\n                    } else if (watch === lastDirtyWatch) {\n                      // If the most recently dirty watcher is now clean, short circuit since the remaining watchers\n                      // have already been tested.\n                      dirty = false;\n                      break traverseScopesLoop;\n                    }\n                  }\n                } catch (e) {\n                  $exceptionHandler(e);\n                }\n              }\n            }\n\n            // Insanity Warning: scope depth-first traversal\n            // yes, this code is a bit crazy, but it works and we have tests to prove it!\n            // this piece should be kept in sync with the traversal in $broadcast\n            if (!(next = (current.$$childHead ||\n                (current !== target && current.$$nextSibling)))) {\n              while (current !== target && !(next = current.$$nextSibling)) {\n                current = current.$parent;\n              }\n            }\n          } while ((current = next));\n\n          // `break traverseScopesLoop;` takes us to here\n\n          if ((dirty || asyncQueue.length) && !(ttl--)) {\n            clearPhase();\n            throw $rootScopeMinErr('infdig',\n                '{0} $digest() iterations reached. Aborting!\\n' +\n                'Watchers fired in the last 5 iterations: {1}',\n                TTL, watchLog);\n          }\n\n        } while (dirty || asyncQueue.length);\n\n        clearPhase();\n\n        while (postDigestQueue.length) {\n          try {\n            postDigestQueue.shift()();\n          } catch (e) {\n            $exceptionHandler(e);\n          }\n        }\n      },\n\n\n      /**\n       * @ngdoc event\n       * @name $rootScope.Scope#$destroy\n       * @eventType broadcast on scope being destroyed\n       *\n       * @description\n       * Broadcasted when a scope and its children are being destroyed.\n       *\n       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to\n       * clean up DOM bindings before an element is removed from the DOM.\n       */\n\n      /**\n       * @ngdoc method\n       * @name $rootScope.Scope#$destroy\n       * @kind function\n       *\n       * @description\n       * Removes the current scope (and all of its children) from the parent scope. Removal implies\n       * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer\n       * propagate to the current scope and its children. Removal also implies that the current\n       * scope is eligible for garbage collection.\n       *\n       * The `$destroy()` is usually used by directives such as\n       * {@link ng.directive:ngRepeat ngRepeat} for managing the\n       * unrolling of the loop.\n       *\n       * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.\n       * Application code can register a `$destroy` event handler that will give it a chance to\n       * perform any necessary cleanup.\n       *\n       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to\n       * clean up DOM bindings before an element is removed from the DOM.\n       */\n      $destroy: function() {\n        // we can't destroy the root scope or a scope that has been already destroyed\n        if (this.$$destroyed) return;\n        var parent = this.$parent;\n\n        this.$broadcast('$destroy');\n        this.$$destroyed = true;\n        if (this === $rootScope) return;\n\n        for (var eventName in this.$$listenerCount) {\n          decrementListenerCount(this, this.$$listenerCount[eventName], eventName);\n        }\n\n        // sever all the references to parent scopes (after this cleanup, the current scope should\n        // not be retained by any of our references and should be eligible for garbage collection)\n        if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;\n        if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;\n        if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;\n        if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;\n\n        // Disable listeners, watchers and apply/digest methods\n        this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;\n        this.$on = this.$watch = this.$watchGroup = function() { return noop; };\n        this.$$listeners = {};\n\n        // All of the code below is bogus code that works around V8's memory leak via optimized code\n        // and inline caches.\n        //\n        // see:\n        // - https://code.google.com/p/v8/issues/detail?id=2073#c26\n        // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909\n        // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451\n\n        this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =\n            this.$$childTail = this.$root = this.$$watchers = null;\n      },\n\n      /**\n       * @ngdoc method\n       * @name $rootScope.Scope#$eval\n       * @kind function\n       *\n       * @description\n       * Executes the `expression` on the current scope and returns the result. Any exceptions in\n       * the expression are propagated (uncaught). This is useful when evaluating Angular\n       * expressions.\n       *\n       * # Example\n       * ```js\n           var scope = ng.$rootScope.Scope();\n           scope.a = 1;\n           scope.b = 2;\n\n           expect(scope.$eval('a+b')).toEqual(3);\n           expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);\n       * ```\n       *\n       * @param {(string|function())=} expression An angular expression to be executed.\n       *\n       *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.\n       *    - `function(scope)`: execute the function with the current `scope` parameter.\n       *\n       * @param {(object)=} locals Local variables object, useful for overriding values in scope.\n       * @returns {*} The result of evaluating the expression.\n       */\n      $eval: function(expr, locals) {\n        return $parse(expr)(this, locals);\n      },\n\n      /**\n       * @ngdoc method\n       * @name $rootScope.Scope#$evalAsync\n       * @kind function\n       *\n       * @description\n       * Executes the expression on the current scope at a later point in time.\n       *\n       * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only\n       * that:\n       *\n       *   - it will execute after the function that scheduled the evaluation (preferably before DOM\n       *     rendering).\n       *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after\n       *     `expression` execution.\n       *\n       * Any exceptions from the execution of the expression are forwarded to the\n       * {@link ng.$exceptionHandler $exceptionHandler} service.\n       *\n       * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle\n       * will be scheduled. However, it is encouraged to always call code that changes the model\n       * from within an `$apply` call. That includes code evaluated via `$evalAsync`.\n       *\n       * @param {(string|function())=} expression An angular expression to be executed.\n       *\n       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.\n       *    - `function(scope)`: execute the function with the current `scope` parameter.\n       *\n       * @param {(object)=} locals Local variables object, useful for overriding values in scope.\n       */\n      $evalAsync: function(expr, locals) {\n        // if we are outside of an $digest loop and this is the first time we are scheduling async\n        // task also schedule async auto-flush\n        if (!$rootScope.$$phase && !asyncQueue.length) {\n          $browser.defer(function() {\n            if (asyncQueue.length) {\n              $rootScope.$digest();\n            }\n          });\n        }\n\n        asyncQueue.push({scope: this, expression: expr, locals: locals});\n      },\n\n      $$postDigest: function(fn) {\n        postDigestQueue.push(fn);\n      },\n\n      /**\n       * @ngdoc method\n       * @name $rootScope.Scope#$apply\n       * @kind function\n       *\n       * @description\n       * `$apply()` is used to execute an expression in angular from outside of the angular\n       * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).\n       * Because we are calling into the angular framework we need to perform proper scope life\n       * cycle of {@link ng.$exceptionHandler exception handling},\n       * {@link ng.$rootScope.Scope#$digest executing watches}.\n       *\n       * ## Life cycle\n       *\n       * # Pseudo-Code of `$apply()`\n       * ```js\n           function $apply(expr) {\n             try {\n               return $eval(expr);\n             } catch (e) {\n               $exceptionHandler(e);\n             } finally {\n               $root.$digest();\n             }\n           }\n       * ```\n       *\n       *\n       * Scope's `$apply()` method transitions through the following stages:\n       *\n       * 1. The {@link guide/expression expression} is executed using the\n       *    {@link ng.$rootScope.Scope#$eval $eval()} method.\n       * 2. Any exceptions from the execution of the expression are forwarded to the\n       *    {@link ng.$exceptionHandler $exceptionHandler} service.\n       * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the\n       *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.\n       *\n       *\n       * @param {(string|function())=} exp An angular expression to be executed.\n       *\n       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.\n       *    - `function(scope)`: execute the function with current `scope` parameter.\n       *\n       * @returns {*} The result of evaluating the expression.\n       */\n      $apply: function(expr) {\n        try {\n          beginPhase('$apply');\n          return this.$eval(expr);\n        } catch (e) {\n          $exceptionHandler(e);\n        } finally {\n          clearPhase();\n          try {\n            $rootScope.$digest();\n          } catch (e) {\n            $exceptionHandler(e);\n            throw e;\n          }\n        }\n      },\n\n      /**\n       * @ngdoc method\n       * @name $rootScope.Scope#$applyAsync\n       * @kind function\n       *\n       * @description\n       * Schedule the invocation of $apply to occur at a later time. The actual time difference\n       * varies across browsers, but is typically around ~10 milliseconds.\n       *\n       * This can be used to queue up multiple expressions which need to be evaluated in the same\n       * digest.\n       *\n       * @param {(string|function())=} exp An angular expression to be executed.\n       *\n       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.\n       *    - `function(scope)`: execute the function with current `scope` parameter.\n       */\n      $applyAsync: function(expr) {\n        var scope = this;\n        expr && applyAsyncQueue.push($applyAsyncExpression);\n        scheduleApplyAsync();\n\n        function $applyAsyncExpression() {\n          scope.$eval(expr);\n        }\n      },\n\n      /**\n       * @ngdoc method\n       * @name $rootScope.Scope#$on\n       * @kind function\n       *\n       * @description\n       * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for\n       * discussion of event life cycle.\n       *\n       * The event listener function format is: `function(event, args...)`. The `event` object\n       * passed into the listener has the following attributes:\n       *\n       *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or\n       *     `$broadcast`-ed.\n       *   - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the\n       *     event propagates through the scope hierarchy, this property is set to null.\n       *   - `name` - `{string}`: name of the event.\n       *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel\n       *     further event propagation (available only for events that were `$emit`-ed).\n       *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag\n       *     to true.\n       *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.\n       *\n       * @param {string} name Event name to listen on.\n       * @param {function(event, ...args)} listener Function to call when the event is emitted.\n       * @returns {function()} Returns a deregistration function for this listener.\n       */\n      $on: function(name, listener) {\n        var namedListeners = this.$$listeners[name];\n        if (!namedListeners) {\n          this.$$listeners[name] = namedListeners = [];\n        }\n        namedListeners.push(listener);\n\n        var current = this;\n        do {\n          if (!current.$$listenerCount[name]) {\n            current.$$listenerCount[name] = 0;\n          }\n          current.$$listenerCount[name]++;\n        } while ((current = current.$parent));\n\n        var self = this;\n        return function() {\n          var indexOfListener = namedListeners.indexOf(listener);\n          if (indexOfListener !== -1) {\n            namedListeners[indexOfListener] = null;\n            decrementListenerCount(self, 1, name);\n          }\n        };\n      },\n\n\n      /**\n       * @ngdoc method\n       * @name $rootScope.Scope#$emit\n       * @kind function\n       *\n       * @description\n       * Dispatches an event `name` upwards through the scope hierarchy notifying the\n       * registered {@link ng.$rootScope.Scope#$on} listeners.\n       *\n       * The event life cycle starts at the scope on which `$emit` was called. All\n       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get\n       * notified. Afterwards, the event traverses upwards toward the root scope and calls all\n       * registered listeners along the way. The event will stop propagating if one of the listeners\n       * cancels it.\n       *\n       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed\n       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.\n       *\n       * @param {string} name Event name to emit.\n       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.\n       * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).\n       */\n      $emit: function(name, args) {\n        var empty = [],\n            namedListeners,\n            scope = this,\n            stopPropagation = false,\n            event = {\n              name: name,\n              targetScope: scope,\n              stopPropagation: function() {stopPropagation = true;},\n              preventDefault: function() {\n                event.defaultPrevented = true;\n              },\n              defaultPrevented: false\n            },\n            listenerArgs = concat([event], arguments, 1),\n            i, length;\n\n        do {\n          namedListeners = scope.$$listeners[name] || empty;\n          event.currentScope = scope;\n          for (i = 0, length = namedListeners.length; i < length; i++) {\n\n            // if listeners were deregistered, defragment the array\n            if (!namedListeners[i]) {\n              namedListeners.splice(i, 1);\n              i--;\n              length--;\n              continue;\n            }\n            try {\n              //allow all listeners attached to the current scope to run\n              namedListeners[i].apply(null, listenerArgs);\n            } catch (e) {\n              $exceptionHandler(e);\n            }\n          }\n          //if any listener on the current scope stops propagation, prevent bubbling\n          if (stopPropagation) {\n            event.currentScope = null;\n            return event;\n          }\n          //traverse upwards\n          scope = scope.$parent;\n        } while (scope);\n\n        event.currentScope = null;\n\n        return event;\n      },\n\n\n      /**\n       * @ngdoc method\n       * @name $rootScope.Scope#$broadcast\n       * @kind function\n       *\n       * @description\n       * Dispatches an event `name` downwards to all child scopes (and their children) notifying the\n       * registered {@link ng.$rootScope.Scope#$on} listeners.\n       *\n       * The event life cycle starts at the scope on which `$broadcast` was called. All\n       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get\n       * notified. Afterwards, the event propagates to all direct and indirect scopes of the current\n       * scope and calls all registered listeners along the way. The event cannot be canceled.\n       *\n       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed\n       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.\n       *\n       * @param {string} name Event name to broadcast.\n       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.\n       * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}\n       */\n      $broadcast: function(name, args) {\n        var target = this,\n            current = target,\n            next = target,\n            event = {\n              name: name,\n              targetScope: target,\n              preventDefault: function() {\n                event.defaultPrevented = true;\n              },\n              defaultPrevented: false\n            };\n\n        if (!target.$$listenerCount[name]) return event;\n\n        var listenerArgs = concat([event], arguments, 1),\n            listeners, i, length;\n\n        //down while you can, then up and next sibling or up and next sibling until back at root\n        while ((current = next)) {\n          event.currentScope = current;\n          listeners = current.$$listeners[name] || [];\n          for (i = 0, length = listeners.length; i < length; i++) {\n            // if listeners were deregistered, defragment the array\n            if (!listeners[i]) {\n              listeners.splice(i, 1);\n              i--;\n              length--;\n              continue;\n            }\n\n            try {\n              listeners[i].apply(null, listenerArgs);\n            } catch (e) {\n              $exceptionHandler(e);\n            }\n          }\n\n          // Insanity Warning: scope depth-first traversal\n          // yes, this code is a bit crazy, but it works and we have tests to prove it!\n          // this piece should be kept in sync with the traversal in $digest\n          // (though it differs due to having the extra check for $$listenerCount)\n          if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||\n              (current !== target && current.$$nextSibling)))) {\n            while (current !== target && !(next = current.$$nextSibling)) {\n              current = current.$parent;\n            }\n          }\n        }\n\n        event.currentScope = null;\n        return event;\n      }\n    };\n\n    var $rootScope = new Scope();\n\n    //The internal queues. Expose them on the $rootScope for debugging/testing purposes.\n    var asyncQueue = $rootScope.$$asyncQueue = [];\n    var postDigestQueue = $rootScope.$$postDigestQueue = [];\n    var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];\n\n    return $rootScope;\n\n\n    function beginPhase(phase) {\n      if ($rootScope.$$phase) {\n        throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);\n      }\n\n      $rootScope.$$phase = phase;\n    }\n\n    function clearPhase() {\n      $rootScope.$$phase = null;\n    }\n\n\n    function decrementListenerCount(current, count, name) {\n      do {\n        current.$$listenerCount[name] -= count;\n\n        if (current.$$listenerCount[name] === 0) {\n          delete current.$$listenerCount[name];\n        }\n      } while ((current = current.$parent));\n    }\n\n    /**\n     * function used as an initial value for watchers.\n     * because it's unique we can easily tell it apart from other values\n     */\n    function initWatchVal() {}\n\n    function flushApplyAsync() {\n      while (applyAsyncQueue.length) {\n        try {\n          applyAsyncQueue.shift()();\n        } catch (e) {\n          $exceptionHandler(e);\n        }\n      }\n      applyAsyncId = null;\n    }\n\n    function scheduleApplyAsync() {\n      if (applyAsyncId === null) {\n        applyAsyncId = $browser.defer(function() {\n          $rootScope.$apply(flushApplyAsync);\n        });\n      }\n    }\n  }];\n}\n\n/**\n * @description\n * Private service to sanitize uris for links and images. Used by $compile and $sanitize.\n */\nfunction $$SanitizeUriProvider() {\n  var aHrefSanitizationWhitelist = /^\\s*(https?|ftp|mailto|tel|file):/,\n    imgSrcSanitizationWhitelist = /^\\s*((https?|ftp|file|blob):|data:image\\/)/;\n\n  /**\n   * @description\n   * Retrieves or overrides the default regular expression that is used for whitelisting of safe\n   * urls during a[href] sanitization.\n   *\n   * The sanitization is a security measure aimed at prevent XSS attacks via html links.\n   *\n   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into\n   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`\n   * regular expression. If a match is found, the original url is written into the dom. Otherwise,\n   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.\n   *\n   * @param {RegExp=} regexp New regexp to whitelist urls with.\n   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for\n   *    chaining otherwise.\n   */\n  this.aHrefSanitizationWhitelist = function(regexp) {\n    if (isDefined(regexp)) {\n      aHrefSanitizationWhitelist = regexp;\n      return this;\n    }\n    return aHrefSanitizationWhitelist;\n  };\n\n\n  /**\n   * @description\n   * Retrieves or overrides the default regular expression that is used for whitelisting of safe\n   * urls during img[src] sanitization.\n   *\n   * The sanitization is a security measure aimed at prevent XSS attacks via html links.\n   *\n   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into\n   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`\n   * regular expression. If a match is found, the original url is written into the dom. Otherwise,\n   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.\n   *\n   * @param {RegExp=} regexp New regexp to whitelist urls with.\n   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for\n   *    chaining otherwise.\n   */\n  this.imgSrcSanitizationWhitelist = function(regexp) {\n    if (isDefined(regexp)) {\n      imgSrcSanitizationWhitelist = regexp;\n      return this;\n    }\n    return imgSrcSanitizationWhitelist;\n  };\n\n  this.$get = function() {\n    return function sanitizeUri(uri, isImage) {\n      var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;\n      var normalizedVal;\n      normalizedVal = urlResolve(uri).href;\n      if (normalizedVal !== '' && !normalizedVal.match(regex)) {\n        return 'unsafe:' + normalizedVal;\n      }\n      return uri;\n    };\n  };\n}\n\n/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n *     Any commits to this file should be reviewed with security in mind.  *\n *   Changes to this file can potentially create security vulnerabilities. *\n *          An approval from 2 Core members with history of modifying      *\n *                         this file is required.                          *\n *                                                                         *\n *  Does the change somehow allow for arbitrary javascript to be executed? *\n *    Or allows for someone to change the prototype of built-in objects?   *\n *     Or gives undesired access to variables likes document or window?    *\n * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */\n\nvar $sceMinErr = minErr('$sce');\n\nvar SCE_CONTEXTS = {\n  HTML: 'html',\n  CSS: 'css',\n  URL: 'url',\n  // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a\n  // url.  (e.g. ng-include, script src, templateUrl)\n  RESOURCE_URL: 'resourceUrl',\n  JS: 'js'\n};\n\n// Helper functions follow.\n\nfunction adjustMatcher(matcher) {\n  if (matcher === 'self') {\n    return matcher;\n  } else if (isString(matcher)) {\n    // Strings match exactly except for 2 wildcards - '*' and '**'.\n    // '*' matches any character except those from the set ':/.?&'.\n    // '**' matches any character (like .* in a RegExp).\n    // More than 2 *'s raises an error as it's ill defined.\n    if (matcher.indexOf('***') > -1) {\n      throw $sceMinErr('iwcard',\n          'Illegal sequence *** in string matcher.  String: {0}', matcher);\n    }\n    matcher = escapeForRegexp(matcher).\n                  replace('\\\\*\\\\*', '.*').\n                  replace('\\\\*', '[^:/.?&;]*');\n    return new RegExp('^' + matcher + '$');\n  } else if (isRegExp(matcher)) {\n    // The only other type of matcher allowed is a Regexp.\n    // Match entire URL / disallow partial matches.\n    // Flags are reset (i.e. no global, ignoreCase or multiline)\n    return new RegExp('^' + matcher.source + '$');\n  } else {\n    throw $sceMinErr('imatcher',\n        'Matchers may only be \"self\", string patterns or RegExp objects');\n  }\n}\n\n\nfunction adjustMatchers(matchers) {\n  var adjustedMatchers = [];\n  if (isDefined(matchers)) {\n    forEach(matchers, function(matcher) {\n      adjustedMatchers.push(adjustMatcher(matcher));\n    });\n  }\n  return adjustedMatchers;\n}\n\n\n/**\n * @ngdoc service\n * @name $sceDelegate\n * @kind function\n *\n * @description\n *\n * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict\n * Contextual Escaping (SCE)} services to AngularJS.\n *\n * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of\n * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is\n * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to\n * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things\n * work because `$sce` delegates to `$sceDelegate` for these operations.\n *\n * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.\n *\n * The default instance of `$sceDelegate` should work out of the box with little pain.  While you\n * can override it completely to change the behavior of `$sce`, the common case would\n * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting\n * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as\n * templates.  Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist\n * $sceDelegateProvider.resourceUrlWhitelist} and {@link\n * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}\n */\n\n/**\n * @ngdoc provider\n * @name $sceDelegateProvider\n * @description\n *\n * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate\n * $sceDelegate} service.  This allows one to get/set the whitelists and blacklists used to ensure\n * that the URLs used for sourcing Angular templates are safe.  Refer {@link\n * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and\n * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}\n *\n * For the general details about this service in Angular, read the main page for {@link ng.$sce\n * Strict Contextual Escaping (SCE)}.\n *\n * **Example**:  Consider the following case. <a name=\"example\"></a>\n *\n * - your app is hosted at url `http://myapp.example.com/`\n * - but some of your templates are hosted on other domains you control such as\n *   `http://srv01.assets.example.com/`,Â  `http://srv02.assets.example.com/`, etc.\n * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.\n *\n * Here is what a secure configuration for this scenario might look like:\n *\n * ```\n *  angular.module('myApp', []).config(function($sceDelegateProvider) {\n *    $sceDelegateProvider.resourceUrlWhitelist([\n *      // Allow same origin resource loads.\n *      'self',\n *      // Allow loading from our assets domain.  Notice the difference between * and **.\n *      'http://srv*.assets.example.com/**'\n *    ]);\n *\n *    // The blacklist overrides the whitelist so the open redirect here is blocked.\n *    $sceDelegateProvider.resourceUrlBlacklist([\n *      'http://myapp.example.com/clickThru**'\n *    ]);\n *  });\n * ```\n */\n\nfunction $SceDelegateProvider() {\n  this.SCE_CONTEXTS = SCE_CONTEXTS;\n\n  // Resource URLs can also be trusted by policy.\n  var resourceUrlWhitelist = ['self'],\n      resourceUrlBlacklist = [];\n\n  /**\n   * @ngdoc method\n   * @name $sceDelegateProvider#resourceUrlWhitelist\n   * @kind function\n   *\n   * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value\n   *     provided.  This must be an array or null.  A snapshot of this array is used so further\n   *     changes to the array are ignored.\n   *\n   *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items\n   *     allowed in this array.\n   *\n   *     Note: **an empty whitelist array will block all URLs**!\n   *\n   * @return {Array} the currently set whitelist array.\n   *\n   * The **default value** when no whitelist has been explicitly set is `['self']` allowing only\n   * same origin resource requests.\n   *\n   * @description\n   * Sets/Gets the whitelist of trusted resource URLs.\n   */\n  this.resourceUrlWhitelist = function(value) {\n    if (arguments.length) {\n      resourceUrlWhitelist = adjustMatchers(value);\n    }\n    return resourceUrlWhitelist;\n  };\n\n  /**\n   * @ngdoc method\n   * @name $sceDelegateProvider#resourceUrlBlacklist\n   * @kind function\n   *\n   * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value\n   *     provided.  This must be an array or null.  A snapshot of this array is used so further\n   *     changes to the array are ignored.\n   *\n   *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items\n   *     allowed in this array.\n   *\n   *     The typical usage for the blacklist is to **block\n   *     [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as\n   *     these would otherwise be trusted but actually return content from the redirected domain.\n   *\n   *     Finally, **the blacklist overrides the whitelist** and has the final say.\n   *\n   * @return {Array} the currently set blacklist array.\n   *\n   * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there\n   * is no blacklist.)\n   *\n   * @description\n   * Sets/Gets the blacklist of trusted resource URLs.\n   */\n\n  this.resourceUrlBlacklist = function(value) {\n    if (arguments.length) {\n      resourceUrlBlacklist = adjustMatchers(value);\n    }\n    return resourceUrlBlacklist;\n  };\n\n  this.$get = ['$injector', function($injector) {\n\n    var htmlSanitizer = function htmlSanitizer(html) {\n      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');\n    };\n\n    if ($injector.has('$sanitize')) {\n      htmlSanitizer = $injector.get('$sanitize');\n    }\n\n\n    function matchUrl(matcher, parsedUrl) {\n      if (matcher === 'self') {\n        return urlIsSameOrigin(parsedUrl);\n      } else {\n        // definitely a regex.  See adjustMatchers()\n        return !!matcher.exec(parsedUrl.href);\n      }\n    }\n\n    function isResourceUrlAllowedByPolicy(url) {\n      var parsedUrl = urlResolve(url.toString());\n      var i, n, allowed = false;\n      // Ensure that at least one item from the whitelist allows this url.\n      for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {\n        if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {\n          allowed = true;\n          break;\n        }\n      }\n      if (allowed) {\n        // Ensure that no item from the blacklist blocked this url.\n        for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {\n          if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {\n            allowed = false;\n            break;\n          }\n        }\n      }\n      return allowed;\n    }\n\n    function generateHolderType(Base) {\n      var holderType = function TrustedValueHolderType(trustedValue) {\n        this.$$unwrapTrustedValue = function() {\n          return trustedValue;\n        };\n      };\n      if (Base) {\n        holderType.prototype = new Base();\n      }\n      holderType.prototype.valueOf = function sceValueOf() {\n        return this.$$unwrapTrustedValue();\n      };\n      holderType.prototype.toString = function sceToString() {\n        return this.$$unwrapTrustedValue().toString();\n      };\n      return holderType;\n    }\n\n    var trustedValueHolderBase = generateHolderType(),\n        byType = {};\n\n    byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);\n    byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);\n    byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);\n    byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);\n    byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);\n\n    /**\n     * @ngdoc method\n     * @name $sceDelegate#trustAs\n     *\n     * @description\n     * Returns an object that is trusted by angular for use in specified strict\n     * contextual escaping contexts (such as ng-bind-html, ng-include, any src\n     * attribute interpolation, any dom event binding attribute interpolation\n     * such as for onclick,  etc.) that uses the provided value.\n     * See {@link ng.$sce $sce} for enabling strict contextual escaping.\n     *\n     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,\n     *   resourceUrl, html, js and css.\n     * @param {*} value The value that that should be considered trusted/safe.\n     * @returns {*} A value that can be used to stand in for the provided `value` in places\n     * where Angular expects a $sce.trustAs() return value.\n     */\n    function trustAs(type, trustedValue) {\n      var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);\n      if (!Constructor) {\n        throw $sceMinErr('icontext',\n            'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',\n            type, trustedValue);\n      }\n      if (trustedValue === null || trustedValue === undefined || trustedValue === '') {\n        return trustedValue;\n      }\n      // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting\n      // mutable objects, we ensure here that the value passed in is actually a string.\n      if (typeof trustedValue !== 'string') {\n        throw $sceMinErr('itype',\n            'Attempted to trust a non-string value in a content requiring a string: Context: {0}',\n            type);\n      }\n      return new Constructor(trustedValue);\n    }\n\n    /**\n     * @ngdoc method\n     * @name $sceDelegate#valueOf\n     *\n     * @description\n     * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs\n     * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link\n     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.\n     *\n     * If the passed parameter is not a value that had been returned by {@link\n     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.\n     *\n     * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}\n     *      call or anything else.\n     * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs\n     *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns\n     *     `value` unchanged.\n     */\n    function valueOf(maybeTrusted) {\n      if (maybeTrusted instanceof trustedValueHolderBase) {\n        return maybeTrusted.$$unwrapTrustedValue();\n      } else {\n        return maybeTrusted;\n      }\n    }\n\n    /**\n     * @ngdoc method\n     * @name $sceDelegate#getTrusted\n     *\n     * @description\n     * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and\n     * returns the originally supplied value if the queried context type is a supertype of the\n     * created type.  If this condition isn't satisfied, throws an exception.\n     *\n     * @param {string} type The kind of context in which this value is to be used.\n     * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs\n     *     `$sceDelegate.trustAs`} call.\n     * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs\n     *     `$sceDelegate.trustAs`} if valid in this context.  Otherwise, throws an exception.\n     */\n    function getTrusted(type, maybeTrusted) {\n      if (maybeTrusted === null || maybeTrusted === undefined || maybeTrusted === '') {\n        return maybeTrusted;\n      }\n      var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);\n      if (constructor && maybeTrusted instanceof constructor) {\n        return maybeTrusted.$$unwrapTrustedValue();\n      }\n      // If we get here, then we may only take one of two actions.\n      // 1. sanitize the value for the requested type, or\n      // 2. throw an exception.\n      if (type === SCE_CONTEXTS.RESOURCE_URL) {\n        if (isResourceUrlAllowedByPolicy(maybeTrusted)) {\n          return maybeTrusted;\n        } else {\n          throw $sceMinErr('insecurl',\n              'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',\n              maybeTrusted.toString());\n        }\n      } else if (type === SCE_CONTEXTS.HTML) {\n        return htmlSanitizer(maybeTrusted);\n      }\n      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');\n    }\n\n    return { trustAs: trustAs,\n             getTrusted: getTrusted,\n             valueOf: valueOf };\n  }];\n}\n\n\n/**\n * @ngdoc provider\n * @name $sceProvider\n * @description\n *\n * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.\n * -   enable/disable Strict Contextual Escaping (SCE) in a module\n * -   override the default implementation with a custom delegate\n *\n * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.\n */\n\n/* jshint maxlen: false*/\n\n/**\n * @ngdoc service\n * @name $sce\n * @kind function\n *\n * @description\n *\n * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.\n *\n * # Strict Contextual Escaping\n *\n * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain\n * contexts to result in a value that is marked as safe to use for that context.  One example of\n * such a context is binding arbitrary html controlled by the user via `ng-bind-html`.  We refer\n * to these contexts as privileged or SCE contexts.\n *\n * As of version 1.2, Angular ships with SCE enabled by default.\n *\n * Note:  When enabled (the default), IE<11 in quirks mode is not supported.  In this mode, IE<11 allow\n * one to execute arbitrary javascript by the use of the expression() syntax.  Refer\n * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.\n * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`\n * to the top of your HTML document.\n *\n * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for\n * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.\n *\n * Here's an example of a binding in a privileged context:\n *\n * ```\n * <input ng-model=\"userHtml\">\n * <div ng-bind-html=\"userHtml\"></div>\n * ```\n *\n * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE\n * disabled, this application allows the user to render arbitrary HTML into the DIV.\n * In a more realistic example, one may be rendering user comments, blog articles, etc. via\n * bindings.  (HTML is just one example of a context where rendering user controlled input creates\n * security vulnerabilities.)\n *\n * For the case of HTML, you might use a library, either on the client side, or on the server side,\n * to sanitize unsafe HTML before binding to the value and rendering it in the document.\n *\n * How would you ensure that every place that used these types of bindings was bound to a value that\n * was sanitized by your library (or returned as safe for rendering by your server?)  How can you\n * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some\n * properties/fields and forgot to update the binding to the sanitized value?\n *\n * To be secure by default, you want to ensure that any such bindings are disallowed unless you can\n * determine that something explicitly says it's safe to use a value for binding in that\n * context.  You can then audit your code (a simple grep would do) to ensure that this is only done\n * for those values that you can easily tell are safe - because they were received from your server,\n * sanitized by your library, etc.  You can organize your codebase to help with this - perhaps\n * allowing only the files in a specific directory to do this.  Ensuring that the internal API\n * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.\n *\n * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}\n * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to\n * obtain values that will be accepted by SCE / privileged contexts.\n *\n *\n * ## How does it work?\n *\n * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted\n * $sce.getTrusted(context, value)} rather than to the value directly.  Directives use {@link\n * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the\n * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.\n *\n * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link\n * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly\n * simplified):\n *\n * ```\n * var ngBindHtmlDirective = ['$sce', function($sce) {\n *   return function(scope, element, attr) {\n *     scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {\n *       element.html(value || '');\n *     });\n *   };\n * }];\n * ```\n *\n * ## Impact on loading templates\n *\n * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as\n * `templateUrl`'s specified by {@link guide/directive directives}.\n *\n * By default, Angular only loads templates from the same domain and protocol as the application\n * document.  This is done by calling {@link ng.$sce#getTrustedResourceUrl\n * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or\n * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist\n * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.\n *\n * *Please note*:\n * The browser's\n * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)\n * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)\n * policy apply in addition to this and may further restrict whether the template is successfully\n * loaded.  This means that without the right CORS policy, loading templates from a different domain\n * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some\n * browsers.\n *\n * ## This feels like too much overhead\n *\n * It's important to remember that SCE only applies to interpolation expressions.\n *\n * If your expressions are constant literals, they're automatically trusted and you don't need to\n * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.\n * `<div ng-bind-html=\"'<b>implicitly trusted</b>'\"></div>`) just works.\n *\n * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them\n * through {@link ng.$sce#getTrusted $sce.getTrusted}.  SCE doesn't play a role here.\n *\n * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load\n * templates in `ng-include` from your application's domain without having to even know about SCE.\n * It blocks loading templates from other domains or loading templates over http from an https\n * served document.  You can change these by setting your own custom {@link\n * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link\n * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.\n *\n * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an\n * application that's secure and can be audited to verify that with much more ease than bolting\n * security onto an application later.\n *\n * <a name=\"contexts\"></a>\n * ## What trusted context types are supported?\n *\n * | Context             | Notes          |\n * |---------------------|----------------|\n * | `$sce.HTML`         | For HTML that's safe to source into the application.  The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered and the {@link ngSanitize $sanitize} module is present this will sanitize the value instead of throwing an error. |\n * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |\n * | `$sce.URL`          | For URLs that are safe to follow as links.  Currently unused (`<a href=` and `<img src=` sanitize their urls and don't constitute an SCE context. |\n * | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application.  Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.)  <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |\n * | `$sce.JS`           | For JavaScript that is safe to execute in your application's context.  Currently unused.  Feel free to use it in your own directives. |\n *\n * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name=\"resourceUrlPatternItem\"></a>\n *\n *  Each element in these arrays must be one of the following:\n *\n *  - **'self'**\n *    - The special **string**, `'self'`, can be used to match against all URLs of the **same\n *      domain** as the application document using the **same protocol**.\n *  - **String** (except the special value `'self'`)\n *    - The string is matched against the full *normalized / absolute URL* of the resource\n *      being tested (substring matches are not good enough.)\n *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters\n *      match themselves.\n *    - `*`: matches zero or more occurrences of any character other than one of the following 6\n *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and ';'.  It's a useful wildcard for use\n *      in a whitelist.\n *    - `**`: matches zero or more occurrences of *any* character.  As such, it's not\n *      not appropriate to use in for a scheme, domain, etc. as it would match too much.  (e.g.\n *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might\n *      not have been the intention.)  Its usage at the very end of the path is ok.  (e.g.\n *      http://foo.example.com/templates/**).\n *  - **RegExp** (*see caveat below*)\n *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax\n *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to\n *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should\n *      have good test coverage.).  For instance, the use of `.` in the regex is correct only in a\n *      small number of cases.  A `.` character in the regex used when matching the scheme or a\n *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It\n *      is highly recommended to use the string patterns and only fall back to regular expressions\n *      if they as a last resort.\n *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is\n *      matched against the **entire** *normalized / absolute URL* of the resource being tested\n *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags\n *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.\n *    - If you are generating your JavaScript from some other templating engine (not\n *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),\n *      remember to escape your regular expression (and be aware that you might need more than\n *      one level of escaping depending on your templating engine and the way you interpolated\n *      the value.)  Do make use of your platform's escaping mechanism as it might be good\n *      enough before coding your own.  e.g. Ruby has\n *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)\n *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).\n *      Javascript lacks a similar built in function for escaping.  Take a look at Google\n *      Closure library's [goog.string.regExpEscape(s)](\n *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).\n *\n * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.\n *\n * ## Show me an example using SCE.\n *\n * <example module=\"mySceApp\" deps=\"angular-sanitize.js\">\n * <file name=\"index.html\">\n *   <div ng-controller=\"AppController as myCtrl\">\n *     <i ng-bind-html=\"myCtrl.explicitlyTrustedHtml\" id=\"explicitlyTrustedHtml\"></i><br><br>\n *     <b>User comments</b><br>\n *     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when\n *     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an\n *     exploit.\n *     <div class=\"well\">\n *       <div ng-repeat=\"userComment in myCtrl.userComments\">\n *         <b>{{userComment.name}}</b>:\n *         <span ng-bind-html=\"userComment.htmlComment\" class=\"htmlComment\"></span>\n *         <br>\n *       </div>\n *     </div>\n *   </div>\n * </file>\n *\n * <file name=\"script.js\">\n *   angular.module('mySceApp', ['ngSanitize'])\n *     .controller('AppController', ['$http', '$templateCache', '$sce',\n *       function($http, $templateCache, $sce) {\n *         var self = this;\n *         $http.get(\"test_data.json\", {cache: $templateCache}).success(function(userComments) {\n *           self.userComments = userComments;\n *         });\n *         self.explicitlyTrustedHtml = $sce.trustAsHtml(\n *             '<span onmouseover=\"this.textContent=&quot;Explicitly trusted HTML bypasses ' +\n *             'sanitization.&quot;\">Hover over this text.</span>');\n *       }]);\n * </file>\n *\n * <file name=\"test_data.json\">\n * [\n *   { \"name\": \"Alice\",\n *     \"htmlComment\":\n *         \"<span onmouseover='this.textContent=\\\"PWN3D!\\\"'>Is <i>anyone</i> reading this?</span>\"\n *   },\n *   { \"name\": \"Bob\",\n *     \"htmlComment\": \"<i>Yes!</i>  Am I the only other one?\"\n *   }\n * ]\n * </file>\n *\n * <file name=\"protractor.js\" type=\"protractor\">\n *   describe('SCE doc demo', function() {\n *     it('should sanitize untrusted values', function() {\n *       expect(element.all(by.css('.htmlComment')).first().getInnerHtml())\n *           .toBe('<span>Is <i>anyone</i> reading this?</span>');\n *     });\n *\n *     it('should NOT sanitize explicitly trusted values', function() {\n *       expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(\n *           '<span onmouseover=\"this.textContent=&quot;Explicitly trusted HTML bypasses ' +\n *           'sanitization.&quot;\">Hover over this text.</span>');\n *     });\n *   });\n * </file>\n * </example>\n *\n *\n *\n * ## Can I disable SCE completely?\n *\n * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits\n * for little coding overhead.  It will be much harder to take an SCE disabled application and\n * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE\n * for cases where you have a lot of existing code that was written before SCE was introduced and\n * you're migrating them a module at a time.\n *\n * That said, here's how you can completely disable SCE:\n *\n * ```\n * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {\n *   // Completely disable SCE.  For demonstration purposes only!\n *   // Do not use in new projects.\n *   $sceProvider.enabled(false);\n * });\n * ```\n *\n */\n/* jshint maxlen: 100 */\n\nfunction $SceProvider() {\n  var enabled = true;\n\n  /**\n   * @ngdoc method\n   * @name $sceProvider#enabled\n   * @kind function\n   *\n   * @param {boolean=} value If provided, then enables/disables SCE.\n   * @return {boolean} true if SCE is enabled, false otherwise.\n   *\n   * @description\n   * Enables/disables SCE and returns the current value.\n   */\n  this.enabled = function(value) {\n    if (arguments.length) {\n      enabled = !!value;\n    }\n    return enabled;\n  };\n\n\n  /* Design notes on the default implementation for SCE.\n   *\n   * The API contract for the SCE delegate\n   * -------------------------------------\n   * The SCE delegate object must provide the following 3 methods:\n   *\n   * - trustAs(contextEnum, value)\n   *     This method is used to tell the SCE service that the provided value is OK to use in the\n   *     contexts specified by contextEnum.  It must return an object that will be accepted by\n   *     getTrusted() for a compatible contextEnum and return this value.\n   *\n   * - valueOf(value)\n   *     For values that were not produced by trustAs(), return them as is.  For values that were\n   *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if\n   *     trustAs is wrapping the given values into some type, this operation unwraps it when given\n   *     such a value.\n   *\n   * - getTrusted(contextEnum, value)\n   *     This function should return the a value that is safe to use in the context specified by\n   *     contextEnum or throw and exception otherwise.\n   *\n   * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be\n   * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For\n   * instance, an implementation could maintain a registry of all trusted objects by context.  In\n   * such a case, trustAs() would return the same object that was passed in.  getTrusted() would\n   * return the same object passed in if it was found in the registry under a compatible context or\n   * throw an exception otherwise.  An implementation might only wrap values some of the time based\n   * on some criteria.  getTrusted() might return a value and not throw an exception for special\n   * constants or objects even if not wrapped.  All such implementations fulfill this contract.\n   *\n   *\n   * A note on the inheritance model for SCE contexts\n   * ------------------------------------------------\n   * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This\n   * is purely an implementation details.\n   *\n   * The contract is simply this:\n   *\n   *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)\n   *     will also succeed.\n   *\n   * Inheritance happens to capture this in a natural way.  In some future, we\n   * may not use inheritance anymore.  That is OK because no code outside of\n   * sce.js and sceSpecs.js would need to be aware of this detail.\n   */\n\n  this.$get = ['$parse', '$sceDelegate', function(\n                $parse,   $sceDelegate) {\n    // Prereq: Ensure that we're not running in IE<11 quirks mode.  In that mode, IE < 11 allow\n    // the \"expression(javascript expression)\" syntax which is insecure.\n    if (enabled && msie < 8) {\n      throw $sceMinErr('iequirks',\n        'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +\n        'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +\n        'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');\n    }\n\n    var sce = shallowCopy(SCE_CONTEXTS);\n\n    /**\n     * @ngdoc method\n     * @name $sce#isEnabled\n     * @kind function\n     *\n     * @return {Boolean} true if SCE is enabled, false otherwise.  If you want to set the value, you\n     * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.\n     *\n     * @description\n     * Returns a boolean indicating if SCE is enabled.\n     */\n    sce.isEnabled = function() {\n      return enabled;\n    };\n    sce.trustAs = $sceDelegate.trustAs;\n    sce.getTrusted = $sceDelegate.getTrusted;\n    sce.valueOf = $sceDelegate.valueOf;\n\n    if (!enabled) {\n      sce.trustAs = sce.getTrusted = function(type, value) { return value; };\n      sce.valueOf = identity;\n    }\n\n    /**\n     * @ngdoc method\n     * @name $sce#parseAs\n     *\n     * @description\n     * Converts Angular {@link guide/expression expression} into a function.  This is like {@link\n     * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it\n     * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,\n     * *result*)}\n     *\n     * @param {string} type The kind of SCE context in which this result will be used.\n     * @param {string} expression String expression to compile.\n     * @returns {function(context, locals)} a function which represents the compiled expression:\n     *\n     *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings\n     *      are evaluated against (typically a scope object).\n     *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in\n     *      `context`.\n     */\n    sce.parseAs = function sceParseAs(type, expr) {\n      var parsed = $parse(expr);\n      if (parsed.literal && parsed.constant) {\n        return parsed;\n      } else {\n        return $parse(expr, function(value) {\n          return sce.getTrusted(type, value);\n        });\n      }\n    };\n\n    /**\n     * @ngdoc method\n     * @name $sce#trustAs\n     *\n     * @description\n     * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.  As such,\n     * returns an object that is trusted by angular for use in specified strict contextual\n     * escaping contexts (such as ng-bind-html, ng-include, any src attribute\n     * interpolation, any dom event binding attribute interpolation such as for onclick,  etc.)\n     * that uses the provided value.  See * {@link ng.$sce $sce} for enabling strict contextual\n     * escaping.\n     *\n     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,\n     *   resource_url, html, js and css.\n     * @param {*} value The value that that should be considered trusted/safe.\n     * @returns {*} A value that can be used to stand in for the provided `value` in places\n     * where Angular expects a $sce.trustAs() return value.\n     */\n\n    /**\n     * @ngdoc method\n     * @name $sce#trustAsHtml\n     *\n     * @description\n     * Shorthand method.  `$sce.trustAsHtml(value)` â†’\n     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}\n     *\n     * @param {*} value The value to trustAs.\n     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml\n     *     $sce.getTrustedHtml(value)} to obtain the original value.  (privileged directives\n     *     only accept expressions that are either literal constants or are the\n     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)\n     */\n\n    /**\n     * @ngdoc method\n     * @name $sce#trustAsUrl\n     *\n     * @description\n     * Shorthand method.  `$sce.trustAsUrl(value)` â†’\n     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}\n     *\n     * @param {*} value The value to trustAs.\n     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl\n     *     $sce.getTrustedUrl(value)} to obtain the original value.  (privileged directives\n     *     only accept expressions that are either literal constants or are the\n     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)\n     */\n\n    /**\n     * @ngdoc method\n     * @name $sce#trustAsResourceUrl\n     *\n     * @description\n     * Shorthand method.  `$sce.trustAsResourceUrl(value)` â†’\n     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}\n     *\n     * @param {*} value The value to trustAs.\n     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl\n     *     $sce.getTrustedResourceUrl(value)} to obtain the original value.  (privileged directives\n     *     only accept expressions that are either literal constants or are the return\n     *     value of {@link ng.$sce#trustAs $sce.trustAs}.)\n     */\n\n    /**\n     * @ngdoc method\n     * @name $sce#trustAsJs\n     *\n     * @description\n     * Shorthand method.  `$sce.trustAsJs(value)` â†’\n     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}\n     *\n     * @param {*} value The value to trustAs.\n     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs\n     *     $sce.getTrustedJs(value)} to obtain the original value.  (privileged directives\n     *     only accept expressions that are either literal constants or are the\n     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)\n     */\n\n    /**\n     * @ngdoc method\n     * @name $sce#getTrusted\n     *\n     * @description\n     * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}.  As such,\n     * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the\n     * originally supplied value if the queried context type is a supertype of the created type.\n     * If this condition isn't satisfied, throws an exception.\n     *\n     * @param {string} type The kind of context in which this value is to be used.\n     * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}\n     *                         call.\n     * @returns {*} The value the was originally provided to\n     *              {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.\n     *              Otherwise, throws an exception.\n     */\n\n    /**\n     * @ngdoc method\n     * @name $sce#getTrustedHtml\n     *\n     * @description\n     * Shorthand method.  `$sce.getTrustedHtml(value)` â†’\n     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}\n     *\n     * @param {*} value The value to pass to `$sce.getTrusted`.\n     * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`\n     */\n\n    /**\n     * @ngdoc method\n     * @name $sce#getTrustedCss\n     *\n     * @description\n     * Shorthand method.  `$sce.getTrustedCss(value)` â†’\n     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}\n     *\n     * @param {*} value The value to pass to `$sce.getTrusted`.\n     * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`\n     */\n\n    /**\n     * @ngdoc method\n     * @name $sce#getTrustedUrl\n     *\n     * @description\n     * Shorthand method.  `$sce.getTrustedUrl(value)` â†’\n     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}\n     *\n     * @param {*} value The value to pass to `$sce.getTrusted`.\n     * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`\n     */\n\n    /**\n     * @ngdoc method\n     * @name $sce#getTrustedResourceUrl\n     *\n     * @description\n     * Shorthand method.  `$sce.getTrustedResourceUrl(value)` â†’\n     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}\n     *\n     * @param {*} value The value to pass to `$sceDelegate.getTrusted`.\n     * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`\n     */\n\n    /**\n     * @ngdoc method\n     * @name $sce#getTrustedJs\n     *\n     * @description\n     * Shorthand method.  `$sce.getTrustedJs(value)` â†’\n     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}\n     *\n     * @param {*} value The value to pass to `$sce.getTrusted`.\n     * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`\n     */\n\n    /**\n     * @ngdoc method\n     * @name $sce#parseAsHtml\n     *\n     * @description\n     * Shorthand method.  `$sce.parseAsHtml(expression string)` â†’\n     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}\n     *\n     * @param {string} expression String expression to compile.\n     * @returns {function(context, locals)} a function which represents the compiled expression:\n     *\n     *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings\n     *      are evaluated against (typically a scope object).\n     *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in\n     *      `context`.\n     */\n\n    /**\n     * @ngdoc method\n     * @name $sce#parseAsCss\n     *\n     * @description\n     * Shorthand method.  `$sce.parseAsCss(value)` â†’\n     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}\n     *\n     * @param {string} expression String expression to compile.\n     * @returns {function(context, locals)} a function which represents the compiled expression:\n     *\n     *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings\n     *      are evaluated against (typically a scope object).\n     *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in\n     *      `context`.\n     */\n\n    /**\n     * @ngdoc method\n     * @name $sce#parseAsUrl\n     *\n     * @description\n     * Shorthand method.  `$sce.parseAsUrl(value)` â†’\n     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}\n     *\n     * @param {string} expression String expression to compile.\n     * @returns {function(context, locals)} a function which represents the compiled expression:\n     *\n     *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings\n     *      are evaluated against (typically a scope object).\n     *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in\n     *      `context`.\n     */\n\n    /**\n     * @ngdoc method\n     * @name $sce#parseAsResourceUrl\n     *\n     * @description\n     * Shorthand method.  `$sce.parseAsResourceUrl(value)` â†’\n     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}\n     *\n     * @param {string} expression String expression to compile.\n     * @returns {function(context, locals)} a function which represents the compiled expression:\n     *\n     *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings\n     *      are evaluated against (typically a scope object).\n     *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in\n     *      `context`.\n     */\n\n    /**\n     * @ngdoc method\n     * @name $sce#parseAsJs\n     *\n     * @description\n     * Shorthand method.  `$sce.parseAsJs(value)` â†’\n     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}\n     *\n     * @param {string} expression String expression to compile.\n     * @returns {function(context, locals)} a function which represents the compiled expression:\n     *\n     *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings\n     *      are evaluated against (typically a scope object).\n     *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in\n     *      `context`.\n     */\n\n    // Shorthand delegations.\n    var parse = sce.parseAs,\n        getTrusted = sce.getTrusted,\n        trustAs = sce.trustAs;\n\n    forEach(SCE_CONTEXTS, function(enumValue, name) {\n      var lName = lowercase(name);\n      sce[camelCase(\"parse_as_\" + lName)] = function(expr) {\n        return parse(enumValue, expr);\n      };\n      sce[camelCase(\"get_trusted_\" + lName)] = function(value) {\n        return getTrusted(enumValue, value);\n      };\n      sce[camelCase(\"trust_as_\" + lName)] = function(value) {\n        return trustAs(enumValue, value);\n      };\n    });\n\n    return sce;\n  }];\n}\n\n/**\n * !!! This is an undocumented \"private\" service !!!\n *\n * @name $sniffer\n * @requires $window\n * @requires $document\n *\n * @property {boolean} history Does the browser support html5 history api ?\n * @property {boolean} transitions Does the browser support CSS transition events ?\n * @property {boolean} animations Does the browser support CSS animation events ?\n *\n * @description\n * This is very simple implementation of testing browser's features.\n */\nfunction $SnifferProvider() {\n  this.$get = ['$window', '$document', function($window, $document) {\n    var eventSupport = {},\n        android =\n          int((/android (\\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),\n        boxee = /Boxee/i.test(($window.navigator || {}).userAgent),\n        document = $document[0] || {},\n        vendorPrefix,\n        vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,\n        bodyStyle = document.body && document.body.style,\n        transitions = false,\n        animations = false,\n        match;\n\n    if (bodyStyle) {\n      for (var prop in bodyStyle) {\n        if (match = vendorRegex.exec(prop)) {\n          vendorPrefix = match[0];\n          vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);\n          break;\n        }\n      }\n\n      if (!vendorPrefix) {\n        vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';\n      }\n\n      transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));\n      animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));\n\n      if (android && (!transitions ||  !animations)) {\n        transitions = isString(document.body.style.webkitTransition);\n        animations = isString(document.body.style.webkitAnimation);\n      }\n    }\n\n\n    return {\n      // Android has history.pushState, but it does not update location correctly\n      // so let's not use the history API at all.\n      // http://code.google.com/p/android/issues/detail?id=17471\n      // https://github.com/angular/angular.js/issues/904\n\n      // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has\n      // so let's not use the history API also\n      // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined\n      // jshint -W018\n      history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),\n      // jshint +W018\n      hasEvent: function(event) {\n        // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have\n        // it. In particular the event is not fired when backspace or delete key are pressed or\n        // when cut operation is performed.\n        // IE10+ implements 'input' event but it erroneously fires under various situations,\n        // e.g. when placeholder changes, or a form is focused.\n        if (event === 'input' && msie <= 11) return false;\n\n        if (isUndefined(eventSupport[event])) {\n          var divElm = document.createElement('div');\n          eventSupport[event] = 'on' + event in divElm;\n        }\n\n        return eventSupport[event];\n      },\n      csp: csp(),\n      vendorPrefix: vendorPrefix,\n      transitions: transitions,\n      animations: animations,\n      android: android\n    };\n  }];\n}\n\nvar $compileMinErr = minErr('$compile');\n\n/**\n * @ngdoc service\n * @name $templateRequest\n *\n * @description\n * The `$templateRequest` service downloads the provided template using `$http` and, upon success,\n * stores the contents inside of `$templateCache`. If the HTTP request fails or the response data\n * of the HTTP request is empty, a `$compile` error will be thrown (the exception can be thwarted\n * by setting the 2nd parameter of the function to true).\n *\n * @param {string} tpl The HTTP request template URL\n * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty\n *\n * @return {Promise} the HTTP Promise for the given.\n *\n * @property {number} totalPendingRequests total amount of pending template requests being downloaded.\n */\nfunction $TemplateRequestProvider() {\n  this.$get = ['$templateCache', '$http', '$q', function($templateCache, $http, $q) {\n    function handleRequestFn(tpl, ignoreRequestError) {\n      handleRequestFn.totalPendingRequests++;\n\n      var transformResponse = $http.defaults && $http.defaults.transformResponse;\n\n      if (isArray(transformResponse)) {\n        transformResponse = transformResponse.filter(function(transformer) {\n          return transformer !== defaultHttpResponseTransform;\n        });\n      } else if (transformResponse === defaultHttpResponseTransform) {\n        transformResponse = null;\n      }\n\n      var httpOptions = {\n        cache: $templateCache,\n        transformResponse: transformResponse\n      };\n\n      return $http.get(tpl, httpOptions)\n        ['finally'](function() {\n          handleRequestFn.totalPendingRequests--;\n        })\n        .then(function(response) {\n          return response.data;\n        }, handleError);\n\n      function handleError(resp) {\n        if (!ignoreRequestError) {\n          throw $compileMinErr('tpload', 'Failed to load template: {0}', tpl);\n        }\n        return $q.reject(resp);\n      }\n    }\n\n    handleRequestFn.totalPendingRequests = 0;\n\n    return handleRequestFn;\n  }];\n}\n\nfunction $$TestabilityProvider() {\n  this.$get = ['$rootScope', '$browser', '$location',\n       function($rootScope,   $browser,   $location) {\n\n    /**\n     * @name $testability\n     *\n     * @description\n     * The private $$testability service provides a collection of methods for use when debugging\n     * or by automated test and debugging tools.\n     */\n    var testability = {};\n\n    /**\n     * @name $$testability#findBindings\n     *\n     * @description\n     * Returns an array of elements that are bound (via ng-bind or {{}})\n     * to expressions matching the input.\n     *\n     * @param {Element} element The element root to search from.\n     * @param {string} expression The binding expression to match.\n     * @param {boolean} opt_exactMatch If true, only returns exact matches\n     *     for the expression. Filters and whitespace are ignored.\n     */\n    testability.findBindings = function(element, expression, opt_exactMatch) {\n      var bindings = element.getElementsByClassName('ng-binding');\n      var matches = [];\n      forEach(bindings, function(binding) {\n        var dataBinding = angular.element(binding).data('$binding');\n        if (dataBinding) {\n          forEach(dataBinding, function(bindingName) {\n            if (opt_exactMatch) {\n              var matcher = new RegExp('(^|\\\\s)' + escapeForRegexp(expression) + '(\\\\s|\\\\||$)');\n              if (matcher.test(bindingName)) {\n                matches.push(binding);\n              }\n            } else {\n              if (bindingName.indexOf(expression) != -1) {\n                matches.push(binding);\n              }\n            }\n          });\n        }\n      });\n      return matches;\n    };\n\n    /**\n     * @name $$testability#findModels\n     *\n     * @description\n     * Returns an array of elements that are two-way found via ng-model to\n     * expressions matching the input.\n     *\n     * @param {Element} element The element root to search from.\n     * @param {string} expression The model expression to match.\n     * @param {boolean} opt_exactMatch If true, only returns exact matches\n     *     for the expression.\n     */\n    testability.findModels = function(element, expression, opt_exactMatch) {\n      var prefixes = ['ng-', 'data-ng-', 'ng\\\\:'];\n      for (var p = 0; p < prefixes.length; ++p) {\n        var attributeEquals = opt_exactMatch ? '=' : '*=';\n        var selector = '[' + prefixes[p] + 'model' + attributeEquals + '\"' + expression + '\"]';\n        var elements = element.querySelectorAll(selector);\n        if (elements.length) {\n          return elements;\n        }\n      }\n    };\n\n    /**\n     * @name $$testability#getLocation\n     *\n     * @description\n     * Shortcut for getting the location in a browser agnostic way. Returns\n     *     the path, search, and hash. (e.g. /path?a=b#hash)\n     */\n    testability.getLocation = function() {\n      return $location.url();\n    };\n\n    /**\n     * @name $$testability#setLocation\n     *\n     * @description\n     * Shortcut for navigating to a location without doing a full page reload.\n     *\n     * @param {string} url The location url (path, search and hash,\n     *     e.g. /path?a=b#hash) to go to.\n     */\n    testability.setLocation = function(url) {\n      if (url !== $location.url()) {\n        $location.url(url);\n        $rootScope.$digest();\n      }\n    };\n\n    /**\n     * @name $$testability#whenStable\n     *\n     * @description\n     * Calls the callback when $timeout and $http requests are completed.\n     *\n     * @param {function} callback\n     */\n    testability.whenStable = function(callback) {\n      $browser.notifyWhenNoOutstandingRequests(callback);\n    };\n\n    return testability;\n  }];\n}\n\nfunction $TimeoutProvider() {\n  this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',\n       function($rootScope,   $browser,   $q,   $$q,   $exceptionHandler) {\n    var deferreds = {};\n\n\n     /**\n      * @ngdoc service\n      * @name $timeout\n      *\n      * @description\n      * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch\n      * block and delegates any exceptions to\n      * {@link ng.$exceptionHandler $exceptionHandler} service.\n      *\n      * The return value of registering a timeout function is a promise, which will be resolved when\n      * the timeout is reached and the timeout function is executed.\n      *\n      * To cancel a timeout request, call `$timeout.cancel(promise)`.\n      *\n      * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to\n      * synchronously flush the queue of deferred functions.\n      *\n      * @param {function()} fn A function, whose execution should be delayed.\n      * @param {number=} [delay=0] Delay in milliseconds.\n      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise\n      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.\n      * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this\n      *   promise will be resolved with is the return value of the `fn` function.\n      *\n      */\n    function timeout(fn, delay, invokeApply) {\n      var skipApply = (isDefined(invokeApply) && !invokeApply),\n          deferred = (skipApply ? $$q : $q).defer(),\n          promise = deferred.promise,\n          timeoutId;\n\n      timeoutId = $browser.defer(function() {\n        try {\n          deferred.resolve(fn());\n        } catch (e) {\n          deferred.reject(e);\n          $exceptionHandler(e);\n        }\n        finally {\n          delete deferreds[promise.$$timeoutId];\n        }\n\n        if (!skipApply) $rootScope.$apply();\n      }, delay);\n\n      promise.$$timeoutId = timeoutId;\n      deferreds[timeoutId] = deferred;\n\n      return promise;\n    }\n\n\n     /**\n      * @ngdoc method\n      * @name $timeout#cancel\n      *\n      * @description\n      * Cancels a task associated with the `promise`. As a result of this, the promise will be\n      * resolved with a rejection.\n      *\n      * @param {Promise=} promise Promise returned by the `$timeout` function.\n      * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully\n      *   canceled.\n      */\n    timeout.cancel = function(promise) {\n      if (promise && promise.$$timeoutId in deferreds) {\n        deferreds[promise.$$timeoutId].reject('canceled');\n        delete deferreds[promise.$$timeoutId];\n        return $browser.defer.cancel(promise.$$timeoutId);\n      }\n      return false;\n    };\n\n    return timeout;\n  }];\n}\n\n// NOTE:  The usage of window and document instead of $window and $document here is\n// deliberate.  This service depends on the specific behavior of anchor nodes created by the\n// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and\n// cause us to break tests.  In addition, when the browser resolves a URL for XHR, it\n// doesn't know about mocked locations and resolves URLs to the real document - which is\n// exactly the behavior needed here.  There is little value is mocking these out for this\n// service.\nvar urlParsingNode = document.createElement(\"a\");\nvar originUrl = urlResolve(window.location.href);\n\n\n/**\n *\n * Implementation Notes for non-IE browsers\n * ----------------------------------------\n * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,\n * results both in the normalizing and parsing of the URL.  Normalizing means that a relative\n * URL will be resolved into an absolute URL in the context of the application document.\n * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related\n * properties are all populated to reflect the normalized URL.  This approach has wide\n * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc.  See\n * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html\n *\n * Implementation Notes for IE\n * ---------------------------\n * IE >= 8 and <= 10 normalizes the URL when assigned to the anchor node similar to the other\n * browsers.  However, the parsed components will not be set if the URL assigned did not specify\n * them.  (e.g. if you assign a.href = \"foo\", then a.protocol, a.host, etc. will be empty.)  We\n * work around that by performing the parsing in a 2nd step by taking a previously normalized\n * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the\n * properties such as protocol, hostname, port, etc.\n *\n * IE7 does not normalize the URL when assigned to an anchor node.  (Apparently, it does, if one\n * uses the inner HTML approach to assign the URL as part of an HTML snippet -\n * http://stackoverflow.com/a/472729)  However, setting img[src] does normalize the URL.\n * Unfortunately, setting img[src] to something like \"javascript:foo\" on IE throws an exception.\n * Since the primary usage for normalizing URLs is to sanitize such URLs, we can't use that\n * method and IE < 8 is unsupported.\n *\n * References:\n *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement\n *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html\n *   http://url.spec.whatwg.org/#urlutils\n *   https://github.com/angular/angular.js/pull/2902\n *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/\n *\n * @kind function\n * @param {string} url The URL to be parsed.\n * @description Normalizes and parses a URL.\n * @returns {object} Returns the normalized URL as a dictionary.\n *\n *   | member name   | Description    |\n *   |---------------|----------------|\n *   | href          | A normalized version of the provided URL if it was not an absolute URL |\n *   | protocol      | The protocol including the trailing colon                              |\n *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |\n *   | search        | The search params, minus the question mark                             |\n *   | hash          | The hash string, minus the hash symbol\n *   | hostname      | The hostname\n *   | port          | The port, without \":\"\n *   | pathname      | The pathname, beginning with \"/\"\n *\n */\nfunction urlResolve(url) {\n  var href = url;\n\n  if (msie) {\n    // Normalize before parse.  Refer Implementation Notes on why this is\n    // done in two steps on IE.\n    urlParsingNode.setAttribute(\"href\", href);\n    href = urlParsingNode.href;\n  }\n\n  urlParsingNode.setAttribute('href', href);\n\n  // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils\n  return {\n    href: urlParsingNode.href,\n    protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',\n    host: urlParsingNode.host,\n    search: urlParsingNode.search ? urlParsingNode.search.replace(/^\\?/, '') : '',\n    hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',\n    hostname: urlParsingNode.hostname,\n    port: urlParsingNode.port,\n    pathname: (urlParsingNode.pathname.charAt(0) === '/')\n      ? urlParsingNode.pathname\n      : '/' + urlParsingNode.pathname\n  };\n}\n\n/**\n * Parse a request URL and determine whether this is a same-origin request as the application document.\n *\n * @param {string|object} requestUrl The url of the request as a string that will be resolved\n * or a parsed URL object.\n * @returns {boolean} Whether the request is for the same origin as the application document.\n */\nfunction urlIsSameOrigin(requestUrl) {\n  var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;\n  return (parsed.protocol === originUrl.protocol &&\n          parsed.host === originUrl.host);\n}\n\n/**\n * @ngdoc service\n * @name $window\n *\n * @description\n * A reference to the browser's `window` object. While `window`\n * is globally available in JavaScript, it causes testability problems, because\n * it is a global variable. In angular we always refer to it through the\n * `$window` service, so it may be overridden, removed or mocked for testing.\n *\n * Expressions, like the one defined for the `ngClick` directive in the example\n * below, are evaluated with respect to the current scope.  Therefore, there is\n * no risk of inadvertently coding in a dependency on a global value in such an\n * expression.\n *\n * @example\n   <example module=\"windowExample\">\n     <file name=\"index.html\">\n       <script>\n         angular.module('windowExample', [])\n           .controller('ExampleController', ['$scope', '$window', function($scope, $window) {\n             $scope.greeting = 'Hello, World!';\n             $scope.doGreeting = function(greeting) {\n               $window.alert(greeting);\n             };\n           }]);\n       </script>\n       <div ng-controller=\"ExampleController\">\n         <input type=\"text\" ng-model=\"greeting\" />\n         <button ng-click=\"doGreeting(greeting)\">ALERT</button>\n       </div>\n     </file>\n     <file name=\"protractor.js\" type=\"protractor\">\n      it('should display the greeting in the input box', function() {\n       element(by.model('greeting')).sendKeys('Hello, E2E Tests');\n       // If we click the button it will block the test runner\n       // element(':button').click();\n      });\n     </file>\n   </example>\n */\nfunction $WindowProvider() {\n  this.$get = valueFn(window);\n}\n\n/* global currencyFilter: true,\n dateFilter: true,\n filterFilter: true,\n jsonFilter: true,\n limitToFilter: true,\n lowercaseFilter: true,\n numberFilter: true,\n orderByFilter: true,\n uppercaseFilter: true,\n */\n\n/**\n * @ngdoc provider\n * @name $filterProvider\n * @description\n *\n * Filters are just functions which transform input to an output. However filters need to be\n * Dependency Injected. To achieve this a filter definition consists of a factory function which is\n * annotated with dependencies and is responsible for creating a filter function.\n *\n * ```js\n *   // Filter registration\n *   function MyModule($provide, $filterProvider) {\n *     // create a service to demonstrate injection (not always needed)\n *     $provide.value('greet', function(name){\n *       return 'Hello ' + name + '!';\n *     });\n *\n *     // register a filter factory which uses the\n *     // greet service to demonstrate DI.\n *     $filterProvider.register('greet', function(greet){\n *       // return the filter function which uses the greet service\n *       // to generate salutation\n *       return function(text) {\n *         // filters need to be forgiving so check input validity\n *         return text && greet(text) || text;\n *       };\n *     });\n *   }\n * ```\n *\n * The filter function is registered with the `$injector` under the filter name suffix with\n * `Filter`.\n *\n * ```js\n *   it('should be the same instance', inject(\n *     function($filterProvider) {\n *       $filterProvider.register('reverse', function(){\n *         return ...;\n *       });\n *     },\n *     function($filter, reverseFilter) {\n *       expect($filter('reverse')).toBe(reverseFilter);\n *     });\n * ```\n *\n *\n * For more information about how angular filters work, and how to create your own filters, see\n * {@link guide/filter Filters} in the Angular Developer Guide.\n */\n\n/**\n * @ngdoc service\n * @name $filter\n * @kind function\n * @description\n * Filters are used for formatting data displayed to the user.\n *\n * The general syntax in templates is as follows:\n *\n *         {{ expression [| filter_name[:parameter_value] ... ] }}\n *\n * @param {String} name Name of the filter function to retrieve\n * @return {Function} the filter function\n * @example\n   <example name=\"$filter\" module=\"filterExample\">\n     <file name=\"index.html\">\n       <div ng-controller=\"MainCtrl\">\n        <h3>{{ originalText }}</h3>\n        <h3>{{ filteredText }}</h3>\n       </div>\n     </file>\n\n     <file name=\"script.js\">\n      angular.module('filterExample', [])\n      .controller('MainCtrl', function($scope, $filter) {\n        $scope.originalText = 'hello';\n        $scope.filteredText = $filter('uppercase')($scope.originalText);\n      });\n     </file>\n   </example>\n  */\n$FilterProvider.$inject = ['$provide'];\nfunction $FilterProvider($provide) {\n  var suffix = 'Filter';\n\n  /**\n   * @ngdoc method\n   * @name $filterProvider#register\n   * @param {string|Object} name Name of the filter function, or an object map of filters where\n   *    the keys are the filter names and the values are the filter factories.\n   * @returns {Object} Registered filter instance, or if a map of filters was provided then a map\n   *    of the registered filter instances.\n   */\n  function register(name, factory) {\n    if (isObject(name)) {\n      var filters = {};\n      forEach(name, function(filter, key) {\n        filters[key] = register(key, filter);\n      });\n      return filters;\n    } else {\n      return $provide.factory(name + suffix, factory);\n    }\n  }\n  this.register = register;\n\n  this.$get = ['$injector', function($injector) {\n    return function(name) {\n      return $injector.get(name + suffix);\n    };\n  }];\n\n  ////////////////////////////////////////\n\n  /* global\n    currencyFilter: false,\n    dateFilter: false,\n    filterFilter: false,\n    jsonFilter: false,\n    limitToFilter: false,\n    lowercaseFilter: false,\n    numberFilter: false,\n    orderByFilter: false,\n    uppercaseFilter: false,\n  */\n\n  register('currency', currencyFilter);\n  register('date', dateFilter);\n  register('filter', filterFilter);\n  register('json', jsonFilter);\n  register('limitTo', limitToFilter);\n  register('lowercase', lowercaseFilter);\n  register('number', numberFilter);\n  register('orderBy', orderByFilter);\n  register('uppercase', uppercaseFilter);\n}\n\n/**\n * @ngdoc filter\n * @name filter\n * @kind function\n *\n * @description\n * Selects a subset of items from `array` and returns it as a new array.\n *\n * @param {Array} array The source array.\n * @param {string|Object|function()} expression The predicate to be used for selecting items from\n *   `array`.\n *\n *   Can be one of:\n *\n *   - `string`: The string is used for matching against the contents of the `array`. All strings or\n *     objects with string properties in `array` that match this string will be returned. This also\n *     applies to nested object properties.\n *     The predicate can be negated by prefixing the string with `!`.\n *\n *   - `Object`: A pattern object can be used to filter specific properties on objects contained\n *     by `array`. For example `{name:\"M\", phone:\"1\"}` predicate will return an array of items\n *     which have property `name` containing \"M\" and property `phone` containing \"1\". A special\n *     property name `$` can be used (as in `{$:\"text\"}`) to accept a match against any\n *     property of the object or its nested object properties. That's equivalent to the simple\n *     substring match with a `string` as described above. The predicate can be negated by prefixing\n *     the string with `!`.\n *     For example `{name: \"!M\"}` predicate will return an array of items which have property `name`\n *     not containing \"M\".\n *\n *     Note that a named property will match properties on the same level only, while the special\n *     `$` property will match properties on the same level or deeper. E.g. an array item like\n *     `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but\n *     **will** be matched by `{$: 'John'}`.\n *\n *   - `function(value, index)`: A predicate function can be used to write arbitrary filters. The\n *     function is called for each element of `array`. The final result is an array of those\n *     elements that the predicate returned true for.\n *\n * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in\n *     determining if the expected value (from the filter expression) and actual value (from\n *     the object in the array) should be considered a match.\n *\n *   Can be one of:\n *\n *   - `function(actual, expected)`:\n *     The function will be given the object value and the predicate value to compare and\n *     should return true if both values should be considered equal.\n *\n *   - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.\n *     This is essentially strict comparison of expected and actual.\n *\n *   - `false|undefined`: A short hand for a function which will look for a substring match in case\n *     insensitive way.\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n       <div ng-init=\"friends = [{name:'John', phone:'555-1276'},\n                                {name:'Mary', phone:'800-BIG-MARY'},\n                                {name:'Mike', phone:'555-4321'},\n                                {name:'Adam', phone:'555-5678'},\n                                {name:'Julie', phone:'555-8765'},\n                                {name:'Juliette', phone:'555-5678'}]\"></div>\n\n       Search: <input ng-model=\"searchText\">\n       <table id=\"searchTextResults\">\n         <tr><th>Name</th><th>Phone</th></tr>\n         <tr ng-repeat=\"friend in friends | filter:searchText\">\n           <td>{{friend.name}}</td>\n           <td>{{friend.phone}}</td>\n         </tr>\n       </table>\n       <hr>\n       Any: <input ng-model=\"search.$\"> <br>\n       Name only <input ng-model=\"search.name\"><br>\n       Phone only <input ng-model=\"search.phone\"><br>\n       Equality <input type=\"checkbox\" ng-model=\"strict\"><br>\n       <table id=\"searchObjResults\">\n         <tr><th>Name</th><th>Phone</th></tr>\n         <tr ng-repeat=\"friendObj in friends | filter:search:strict\">\n           <td>{{friendObj.name}}</td>\n           <td>{{friendObj.phone}}</td>\n         </tr>\n       </table>\n     </file>\n     <file name=\"protractor.js\" type=\"protractor\">\n       var expectFriendNames = function(expectedNames, key) {\n         element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {\n           arr.forEach(function(wd, i) {\n             expect(wd.getText()).toMatch(expectedNames[i]);\n           });\n         });\n       };\n\n       it('should search across all fields when filtering with a string', function() {\n         var searchText = element(by.model('searchText'));\n         searchText.clear();\n         searchText.sendKeys('m');\n         expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');\n\n         searchText.clear();\n         searchText.sendKeys('76');\n         expectFriendNames(['John', 'Julie'], 'friend');\n       });\n\n       it('should search in specific fields when filtering with a predicate object', function() {\n         var searchAny = element(by.model('search.$'));\n         searchAny.clear();\n         searchAny.sendKeys('i');\n         expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');\n       });\n       it('should use a equal comparison when comparator is true', function() {\n         var searchName = element(by.model('search.name'));\n         var strict = element(by.model('strict'));\n         searchName.clear();\n         searchName.sendKeys('Julie');\n         strict.click();\n         expectFriendNames(['Julie'], 'friendObj');\n       });\n     </file>\n   </example>\n */\nfunction filterFilter() {\n  return function(array, expression, comparator) {\n    if (!isArray(array)) return array;\n\n    var predicateFn;\n    var matchAgainstAnyProp;\n\n    switch (typeof expression) {\n      case 'function':\n        predicateFn = expression;\n        break;\n      case 'boolean':\n      case 'number':\n      case 'string':\n        matchAgainstAnyProp = true;\n        //jshint -W086\n      case 'object':\n        //jshint +W086\n        predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);\n        break;\n      default:\n        return array;\n    }\n\n    return array.filter(predicateFn);\n  };\n}\n\n// Helper functions for `filterFilter`\nfunction createPredicateFn(expression, comparator, matchAgainstAnyProp) {\n  var shouldMatchPrimitives = isObject(expression) && ('$' in expression);\n  var predicateFn;\n\n  if (comparator === true) {\n    comparator = equals;\n  } else if (!isFunction(comparator)) {\n    comparator = function(actual, expected) {\n      if (isObject(actual) || isObject(expected)) {\n        // Prevent an object to be considered equal to a string like `'[object'`\n        return false;\n      }\n\n      actual = lowercase('' + actual);\n      expected = lowercase('' + expected);\n      return actual.indexOf(expected) !== -1;\n    };\n  }\n\n  predicateFn = function(item) {\n    if (shouldMatchPrimitives && !isObject(item)) {\n      return deepCompare(item, expression.$, comparator, false);\n    }\n    return deepCompare(item, expression, comparator, matchAgainstAnyProp);\n  };\n\n  return predicateFn;\n}\n\nfunction deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {\n  var actualType = (actual !== null) ? typeof actual : 'null';\n  var expectedType = (expected !== null) ? typeof expected : 'null';\n\n  if ((expectedType === 'string') && (expected.charAt(0) === '!')) {\n    return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);\n  } else if (isArray(actual)) {\n    // In case `actual` is an array, consider it a match\n    // if ANY of it's items matches `expected`\n    return actual.some(function(item) {\n      return deepCompare(item, expected, comparator, matchAgainstAnyProp);\n    });\n  }\n\n  switch (actualType) {\n    case 'object':\n      var key;\n      if (matchAgainstAnyProp) {\n        for (key in actual) {\n          if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {\n            return true;\n          }\n        }\n        return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);\n      } else if (expectedType === 'object') {\n        for (key in expected) {\n          var expectedVal = expected[key];\n          if (isFunction(expectedVal) || isUndefined(expectedVal)) {\n            continue;\n          }\n\n          var matchAnyProperty = key === '$';\n          var actualVal = matchAnyProperty ? actual : actual[key];\n          if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {\n            return false;\n          }\n        }\n        return true;\n      } else {\n        return comparator(actual, expected);\n      }\n      break;\n    case 'function':\n      return false;\n    default:\n      return comparator(actual, expected);\n  }\n}\n\n/**\n * @ngdoc filter\n * @name currency\n * @kind function\n *\n * @description\n * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default\n * symbol for current locale is used.\n *\n * @param {number} amount Input to filter.\n * @param {string=} symbol Currency symbol or identifier to be displayed.\n * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale\n * @returns {string} Formatted number.\n *\n *\n * @example\n   <example module=\"currencyExample\">\n     <file name=\"index.html\">\n       <script>\n         angular.module('currencyExample', [])\n           .controller('ExampleController', ['$scope', function($scope) {\n             $scope.amount = 1234.56;\n           }]);\n       </script>\n       <div ng-controller=\"ExampleController\">\n         <input type=\"number\" ng-model=\"amount\"> <br>\n         default currency symbol ($): <span id=\"currency-default\">{{amount | currency}}</span><br>\n         custom currency identifier (USD$): <span id=\"currency-custom\">{{amount | currency:\"USD$\"}}</span>\n         no fractions (0): <span id=\"currency-no-fractions\">{{amount | currency:\"USD$\":0}}</span>\n       </div>\n     </file>\n     <file name=\"protractor.js\" type=\"protractor\">\n       it('should init with 1234.56', function() {\n         expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');\n         expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');\n         expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');\n       });\n       it('should update', function() {\n         if (browser.params.browser == 'safari') {\n           // Safari does not understand the minus key. See\n           // https://github.com/angular/protractor/issues/481\n           return;\n         }\n         element(by.model('amount')).clear();\n         element(by.model('amount')).sendKeys('-1234');\n         expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)');\n         expect(element(by.id('currency-custom')).getText()).toBe('(USD$1,234.00)');\n         expect(element(by.id('currency-no-fractions')).getText()).toBe('(USD$1,234)');\n       });\n     </file>\n   </example>\n */\ncurrencyFilter.$inject = ['$locale'];\nfunction currencyFilter($locale) {\n  var formats = $locale.NUMBER_FORMATS;\n  return function(amount, currencySymbol, fractionSize) {\n    if (isUndefined(currencySymbol)) {\n      currencySymbol = formats.CURRENCY_SYM;\n    }\n\n    if (isUndefined(fractionSize)) {\n      fractionSize = formats.PATTERNS[1].maxFrac;\n    }\n\n    // if null or undefined pass it through\n    return (amount == null)\n        ? amount\n        : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).\n            replace(/\\u00A4/g, currencySymbol);\n  };\n}\n\n/**\n * @ngdoc filter\n * @name number\n * @kind function\n *\n * @description\n * Formats a number as text.\n *\n * If the input is not a number an empty string is returned.\n *\n * @param {number|string} number Number to format.\n * @param {(number|string)=} fractionSize Number of decimal places to round the number to.\n * If this is not provided then the fraction size is computed from the current locale's number\n * formatting pattern. In the case of the default locale, it will be 3.\n * @returns {string} Number rounded to decimalPlaces and places a â€œ,â€ after each third digit.\n *\n * @example\n   <example module=\"numberFilterExample\">\n     <file name=\"index.html\">\n       <script>\n         angular.module('numberFilterExample', [])\n           .controller('ExampleController', ['$scope', function($scope) {\n             $scope.val = 1234.56789;\n           }]);\n       </script>\n       <div ng-controller=\"ExampleController\">\n         Enter number: <input ng-model='val'><br>\n         Default formatting: <span id='number-default'>{{val | number}}</span><br>\n         No fractions: <span>{{val | number:0}}</span><br>\n         Negative number: <span>{{-val | number:4}}</span>\n       </div>\n     </file>\n     <file name=\"protractor.js\" type=\"protractor\">\n       it('should format numbers', function() {\n         expect(element(by.id('number-default')).getText()).toBe('1,234.568');\n         expect(element(by.binding('val | number:0')).getText()).toBe('1,235');\n         expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');\n       });\n\n       it('should update', function() {\n         element(by.model('val')).clear();\n         element(by.model('val')).sendKeys('3374.333');\n         expect(element(by.id('number-default')).getText()).toBe('3,374.333');\n         expect(element(by.binding('val | number:0')).getText()).toBe('3,374');\n         expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');\n      });\n     </file>\n   </example>\n */\n\n\nnumberFilter.$inject = ['$locale'];\nfunction numberFilter($locale) {\n  var formats = $locale.NUMBER_FORMATS;\n  return function(number, fractionSize) {\n\n    // if null or undefined pass it through\n    return (number == null)\n        ? number\n        : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,\n                       fractionSize);\n  };\n}\n\nvar DECIMAL_SEP = '.';\nfunction formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {\n  if (!isFinite(number) || isObject(number)) return '';\n\n  var isNegative = number < 0;\n  number = Math.abs(number);\n  var numStr = number + '',\n      formatedText = '',\n      parts = [];\n\n  var hasExponent = false;\n  if (numStr.indexOf('e') !== -1) {\n    var match = numStr.match(/([\\d\\.]+)e(-?)(\\d+)/);\n    if (match && match[2] == '-' && match[3] > fractionSize + 1) {\n      number = 0;\n    } else {\n      formatedText = numStr;\n      hasExponent = true;\n    }\n  }\n\n  if (!hasExponent) {\n    var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;\n\n    // determine fractionSize if it is not specified\n    if (isUndefined(fractionSize)) {\n      fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);\n    }\n\n    // safely round numbers in JS without hitting imprecisions of floating-point arithmetics\n    // inspired by:\n    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round\n    number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);\n\n    var fraction = ('' + number).split(DECIMAL_SEP);\n    var whole = fraction[0];\n    fraction = fraction[1] || '';\n\n    var i, pos = 0,\n        lgroup = pattern.lgSize,\n        group = pattern.gSize;\n\n    if (whole.length >= (lgroup + group)) {\n      pos = whole.length - lgroup;\n      for (i = 0; i < pos; i++) {\n        if ((pos - i) % group === 0 && i !== 0) {\n          formatedText += groupSep;\n        }\n        formatedText += whole.charAt(i);\n      }\n    }\n\n    for (i = pos; i < whole.length; i++) {\n      if ((whole.length - i) % lgroup === 0 && i !== 0) {\n        formatedText += groupSep;\n      }\n      formatedText += whole.charAt(i);\n    }\n\n    // format fraction part.\n    while (fraction.length < fractionSize) {\n      fraction += '0';\n    }\n\n    if (fractionSize && fractionSize !== \"0\") formatedText += decimalSep + fraction.substr(0, fractionSize);\n  } else {\n    if (fractionSize > 0 && number < 1) {\n      formatedText = number.toFixed(fractionSize);\n      number = parseFloat(formatedText);\n    }\n  }\n\n  if (number === 0) {\n    isNegative = false;\n  }\n\n  parts.push(isNegative ? pattern.negPre : pattern.posPre,\n             formatedText,\n             isNegative ? pattern.negSuf : pattern.posSuf);\n  return parts.join('');\n}\n\nfunction padNumber(num, digits, trim) {\n  var neg = '';\n  if (num < 0) {\n    neg =  '-';\n    num = -num;\n  }\n  num = '' + num;\n  while (num.length < digits) num = '0' + num;\n  if (trim)\n    num = num.substr(num.length - digits);\n  return neg + num;\n}\n\n\nfunction dateGetter(name, size, offset, trim) {\n  offset = offset || 0;\n  return function(date) {\n    var value = date['get' + name]();\n    if (offset > 0 || value > -offset)\n      value += offset;\n    if (value === 0 && offset == -12) value = 12;\n    return padNumber(value, size, trim);\n  };\n}\n\nfunction dateStrGetter(name, shortForm) {\n  return function(date, formats) {\n    var value = date['get' + name]();\n    var get = uppercase(shortForm ? ('SHORT' + name) : name);\n\n    return formats[get][value];\n  };\n}\n\nfunction timeZoneGetter(date) {\n  var zone = -1 * date.getTimezoneOffset();\n  var paddedZone = (zone >= 0) ? \"+\" : \"\";\n\n  paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +\n                padNumber(Math.abs(zone % 60), 2);\n\n  return paddedZone;\n}\n\nfunction getFirstThursdayOfYear(year) {\n    // 0 = index of January\n    var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();\n    // 4 = index of Thursday (+1 to account for 1st = 5)\n    // 11 = index of *next* Thursday (+1 account for 1st = 12)\n    return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);\n}\n\nfunction getThursdayThisWeek(datetime) {\n    return new Date(datetime.getFullYear(), datetime.getMonth(),\n      // 4 = index of Thursday\n      datetime.getDate() + (4 - datetime.getDay()));\n}\n\nfunction weekGetter(size) {\n   return function(date) {\n      var firstThurs = getFirstThursdayOfYear(date.getFullYear()),\n         thisThurs = getThursdayThisWeek(date);\n\n      var diff = +thisThurs - +firstThurs,\n         result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week\n\n      return padNumber(result, size);\n   };\n}\n\nfunction ampmGetter(date, formats) {\n  return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];\n}\n\nfunction eraGetter(date, formats) {\n  return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];\n}\n\nfunction longEraGetter(date, formats) {\n  return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];\n}\n\nvar DATE_FORMATS = {\n  yyyy: dateGetter('FullYear', 4),\n    yy: dateGetter('FullYear', 2, 0, true),\n     y: dateGetter('FullYear', 1),\n  MMMM: dateStrGetter('Month'),\n   MMM: dateStrGetter('Month', true),\n    MM: dateGetter('Month', 2, 1),\n     M: dateGetter('Month', 1, 1),\n    dd: dateGetter('Date', 2),\n     d: dateGetter('Date', 1),\n    HH: dateGetter('Hours', 2),\n     H: dateGetter('Hours', 1),\n    hh: dateGetter('Hours', 2, -12),\n     h: dateGetter('Hours', 1, -12),\n    mm: dateGetter('Minutes', 2),\n     m: dateGetter('Minutes', 1),\n    ss: dateGetter('Seconds', 2),\n     s: dateGetter('Seconds', 1),\n     // while ISO 8601 requires fractions to be prefixed with `.` or `,`\n     // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions\n   sss: dateGetter('Milliseconds', 3),\n  EEEE: dateStrGetter('Day'),\n   EEE: dateStrGetter('Day', true),\n     a: ampmGetter,\n     Z: timeZoneGetter,\n    ww: weekGetter(2),\n     w: weekGetter(1),\n     G: eraGetter,\n     GG: eraGetter,\n     GGG: eraGetter,\n     GGGG: longEraGetter\n};\n\nvar DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,\n    NUMBER_STRING = /^\\-?\\d+$/;\n\n/**\n * @ngdoc filter\n * @name date\n * @kind function\n *\n * @description\n *   Formats `date` to a string based on the requested `format`.\n *\n *   `format` string can be composed of the following elements:\n *\n *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)\n *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)\n *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)\n *   * `'MMMM'`: Month in year (January-December)\n *   * `'MMM'`: Month in year (Jan-Dec)\n *   * `'MM'`: Month in year, padded (01-12)\n *   * `'M'`: Month in year (1-12)\n *   * `'dd'`: Day in month, padded (01-31)\n *   * `'d'`: Day in month (1-31)\n *   * `'EEEE'`: Day in Week,(Sunday-Saturday)\n *   * `'EEE'`: Day in Week, (Sun-Sat)\n *   * `'HH'`: Hour in day, padded (00-23)\n *   * `'H'`: Hour in day (0-23)\n *   * `'hh'`: Hour in AM/PM, padded (01-12)\n *   * `'h'`: Hour in AM/PM, (1-12)\n *   * `'mm'`: Minute in hour, padded (00-59)\n *   * `'m'`: Minute in hour (0-59)\n *   * `'ss'`: Second in minute, padded (00-59)\n *   * `'s'`: Second in minute (0-59)\n *   * `'sss'`: Millisecond in second, padded (000-999)\n *   * `'a'`: AM/PM marker\n *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)\n *   * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year\n *   * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year\n *   * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')\n *   * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')\n *\n *   `format` string can also be one of the following predefined\n *   {@link guide/i18n localizable formats}:\n *\n *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale\n *     (e.g. Sep 3, 2010 12:05:08 PM)\n *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 PM)\n *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale\n *     (e.g. Friday, September 3, 2010)\n *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)\n *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)\n *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)\n *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)\n *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)\n *\n *   `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.\n *   `\"h 'in the morning'\"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence\n *   (e.g. `\"h 'o''clock'\"`).\n *\n * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or\n *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its\n *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is\n *    specified in the string input, the time is considered to be in the local timezone.\n * @param {string=} format Formatting rules (see Description). If not specified,\n *    `mediumDate` is used.\n * @param {string=} timezone Timezone to be used for formatting. Right now, only `'UTC'` is supported.\n *    If not specified, the timezone of the browser will be used.\n * @returns {string} Formatted string or the input if input is not recognized as date/millis.\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n       <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:\n           <span>{{1288323623006 | date:'medium'}}</span><br>\n       <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:\n          <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>\n       <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:\n          <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>\n       <span ng-non-bindable>{{1288323623006 | date:\"MM/dd/yyyy 'at' h:mma\"}}</span>:\n          <span>{{'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"}}</span><br>\n     </file>\n     <file name=\"protractor.js\" type=\"protractor\">\n       it('should format date', function() {\n         expect(element(by.binding(\"1288323623006 | date:'medium'\")).getText()).\n            toMatch(/Oct 2\\d, 2010 \\d{1,2}:\\d{2}:\\d{2} (AM|PM)/);\n         expect(element(by.binding(\"1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'\")).getText()).\n            toMatch(/2010\\-10\\-2\\d \\d{2}:\\d{2}:\\d{2} (\\-|\\+)?\\d{4}/);\n         expect(element(by.binding(\"'1288323623006' | date:'MM/dd/yyyy @ h:mma'\")).getText()).\n            toMatch(/10\\/2\\d\\/2010 @ \\d{1,2}:\\d{2}(AM|PM)/);\n         expect(element(by.binding(\"'1288323623006' | date:\\\"MM/dd/yyyy 'at' h:mma\\\"\")).getText()).\n            toMatch(/10\\/2\\d\\/2010 at \\d{1,2}:\\d{2}(AM|PM)/);\n       });\n     </file>\n   </example>\n */\ndateFilter.$inject = ['$locale'];\nfunction dateFilter($locale) {\n\n\n  var R_ISO8601_STR = /^(\\d{4})-?(\\d\\d)-?(\\d\\d)(?:T(\\d\\d)(?::?(\\d\\d)(?::?(\\d\\d)(?:\\.(\\d+))?)?)?(Z|([+-])(\\d\\d):?(\\d\\d))?)?$/;\n                     // 1        2       3         4          5          6          7          8  9     10      11\n  function jsonStringToDate(string) {\n    var match;\n    if (match = string.match(R_ISO8601_STR)) {\n      var date = new Date(0),\n          tzHour = 0,\n          tzMin  = 0,\n          dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,\n          timeSetter = match[8] ? date.setUTCHours : date.setHours;\n\n      if (match[9]) {\n        tzHour = int(match[9] + match[10]);\n        tzMin = int(match[9] + match[11]);\n      }\n      dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3]));\n      var h = int(match[4] || 0) - tzHour;\n      var m = int(match[5] || 0) - tzMin;\n      var s = int(match[6] || 0);\n      var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);\n      timeSetter.call(date, h, m, s, ms);\n      return date;\n    }\n    return string;\n  }\n\n\n  return function(date, format, timezone) {\n    var text = '',\n        parts = [],\n        fn, match;\n\n    format = format || 'mediumDate';\n    format = $locale.DATETIME_FORMATS[format] || format;\n    if (isString(date)) {\n      date = NUMBER_STRING.test(date) ? int(date) : jsonStringToDate(date);\n    }\n\n    if (isNumber(date)) {\n      date = new Date(date);\n    }\n\n    if (!isDate(date)) {\n      return date;\n    }\n\n    while (format) {\n      match = DATE_FORMATS_SPLIT.exec(format);\n      if (match) {\n        parts = concat(parts, match, 1);\n        format = parts.pop();\n      } else {\n        parts.push(format);\n        format = null;\n      }\n    }\n\n    if (timezone && timezone === 'UTC') {\n      date = new Date(date.getTime());\n      date.setMinutes(date.getMinutes() + date.getTimezoneOffset());\n    }\n    forEach(parts, function(value) {\n      fn = DATE_FORMATS[value];\n      text += fn ? fn(date, $locale.DATETIME_FORMATS)\n                 : value.replace(/(^'|'$)/g, '').replace(/''/g, \"'\");\n    });\n\n    return text;\n  };\n}\n\n\n/**\n * @ngdoc filter\n * @name json\n * @kind function\n *\n * @description\n *   Allows you to convert a JavaScript object into JSON string.\n *\n *   This filter is mostly useful for debugging. When using the double curly {{value}} notation\n *   the binding is automatically converted to JSON.\n *\n * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.\n * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.\n * @returns {string} JSON string.\n *\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n       <pre id=\"default-spacing\">{{ {'name':'value'} | json }}</pre>\n       <pre id=\"custom-spacing\">{{ {'name':'value'} | json:4 }}</pre>\n     </file>\n     <file name=\"protractor.js\" type=\"protractor\">\n       it('should jsonify filtered objects', function() {\n         expect(element(by.id('default-spacing')).getText()).toMatch(/\\{\\n  \"name\": ?\"value\"\\n}/);\n         expect(element(by.id('custom-spacing')).getText()).toMatch(/\\{\\n    \"name\": ?\"value\"\\n}/);\n       });\n     </file>\n   </example>\n *\n */\nfunction jsonFilter() {\n  return function(object, spacing) {\n    if (isUndefined(spacing)) {\n        spacing = 2;\n    }\n    return toJson(object, spacing);\n  };\n}\n\n\n/**\n * @ngdoc filter\n * @name lowercase\n * @kind function\n * @description\n * Converts string to lowercase.\n * @see angular.lowercase\n */\nvar lowercaseFilter = valueFn(lowercase);\n\n\n/**\n * @ngdoc filter\n * @name uppercase\n * @kind function\n * @description\n * Converts string to uppercase.\n * @see angular.uppercase\n */\nvar uppercaseFilter = valueFn(uppercase);\n\n/**\n * @ngdoc filter\n * @name limitTo\n * @kind function\n *\n * @description\n * Creates a new array or string containing only a specified number of elements. The elements\n * are taken from either the beginning or the end of the source array, string or number, as specified by\n * the value and sign (positive or negative) of `limit`. If a number is used as input, it is\n * converted to a string.\n *\n * @param {Array|string|number} input Source array, string or number to be limited.\n * @param {string|number} limit The length of the returned array or string. If the `limit` number\n *     is positive, `limit` number of items from the beginning of the source array/string are copied.\n *     If the number is negative, `limit` number  of items from the end of the source array/string\n *     are copied. The `limit` will be trimmed if it exceeds `array.length`\n * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array\n *     had less than `limit` elements.\n *\n * @example\n   <example module=\"limitToExample\">\n     <file name=\"index.html\">\n       <script>\n         angular.module('limitToExample', [])\n           .controller('ExampleController', ['$scope', function($scope) {\n             $scope.numbers = [1,2,3,4,5,6,7,8,9];\n             $scope.letters = \"abcdefghi\";\n             $scope.longNumber = 2345432342;\n             $scope.numLimit = 3;\n             $scope.letterLimit = 3;\n             $scope.longNumberLimit = 3;\n           }]);\n       </script>\n       <div ng-controller=\"ExampleController\">\n         Limit {{numbers}} to: <input type=\"number\" step=\"1\" ng-model=\"numLimit\">\n         <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>\n         Limit {{letters}} to: <input type=\"number\" step=\"1\" ng-model=\"letterLimit\">\n         <p>Output letters: {{ letters | limitTo:letterLimit }}</p>\n         Limit {{longNumber}} to: <input type=\"number\" step=\"1\" ng-model=\"longNumberLimit\">\n         <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>\n       </div>\n     </file>\n     <file name=\"protractor.js\" type=\"protractor\">\n       var numLimitInput = element(by.model('numLimit'));\n       var letterLimitInput = element(by.model('letterLimit'));\n       var longNumberLimitInput = element(by.model('longNumberLimit'));\n       var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));\n       var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));\n       var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));\n\n       it('should limit the number array to first three items', function() {\n         expect(numLimitInput.getAttribute('value')).toBe('3');\n         expect(letterLimitInput.getAttribute('value')).toBe('3');\n         expect(longNumberLimitInput.getAttribute('value')).toBe('3');\n         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');\n         expect(limitedLetters.getText()).toEqual('Output letters: abc');\n         expect(limitedLongNumber.getText()).toEqual('Output long number: 234');\n       });\n\n       // There is a bug in safari and protractor that doesn't like the minus key\n       // it('should update the output when -3 is entered', function() {\n       //   numLimitInput.clear();\n       //   numLimitInput.sendKeys('-3');\n       //   letterLimitInput.clear();\n       //   letterLimitInput.sendKeys('-3');\n       //   longNumberLimitInput.clear();\n       //   longNumberLimitInput.sendKeys('-3');\n       //   expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');\n       //   expect(limitedLetters.getText()).toEqual('Output letters: ghi');\n       //   expect(limitedLongNumber.getText()).toEqual('Output long number: 342');\n       // });\n\n       it('should not exceed the maximum size of input array', function() {\n         numLimitInput.clear();\n         numLimitInput.sendKeys('100');\n         letterLimitInput.clear();\n         letterLimitInput.sendKeys('100');\n         longNumberLimitInput.clear();\n         longNumberLimitInput.sendKeys('100');\n         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');\n         expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');\n         expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');\n       });\n     </file>\n   </example>\n*/\nfunction limitToFilter() {\n  return function(input, limit) {\n    if (isNumber(input)) input = input.toString();\n    if (!isArray(input) && !isString(input)) return input;\n\n    if (Math.abs(Number(limit)) === Infinity) {\n      limit = Number(limit);\n    } else {\n      limit = int(limit);\n    }\n\n    //NaN check on limit\n    if (limit) {\n      return limit > 0 ? input.slice(0, limit) : input.slice(limit);\n    } else {\n      return isString(input) ? \"\" : [];\n    }\n  };\n}\n\n/**\n * @ngdoc filter\n * @name orderBy\n * @kind function\n *\n * @description\n * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically\n * for strings and numerically for numbers. Note: if you notice numbers are not being sorted\n * correctly, make sure they are actually being saved as numbers and not strings.\n *\n * @param {Array} array The array to sort.\n * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be\n *    used by the comparator to determine the order of elements.\n *\n *    Can be one of:\n *\n *    - `function`: Getter function. The result of this function will be sorted using the\n *      `<`, `=`, `>` operator.\n *    - `string`: An Angular expression. The result of this expression is used to compare elements\n *      (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by\n *      3 first characters of a property called `name`). The result of a constant expression\n *      is interpreted as a property name to be used in comparisons (for example `\"special name\"`\n *      to sort object by the value of their `special name` property). An expression can be\n *      optionally prefixed with `+` or `-` to control ascending or descending sort order\n *      (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array\n *      element itself is used to compare where sorting.\n *    - `Array`: An array of function or string predicates. The first predicate in the array\n *      is used for sorting, but when two items are equivalent, the next predicate is used.\n *\n *    If the predicate is missing or empty then it defaults to `'+'`.\n *\n * @param {boolean=} reverse Reverse the order of the array.\n * @returns {Array} Sorted copy of the source array.\n *\n *\n * @example\n * The example below demonstrates a simple ngRepeat, where the data is sorted\n * by age in descending order (predicate is set to `'-age'`).\n * `reverse` is not set, which means it defaults to `false`.\n   <example module=\"orderByExample\">\n     <file name=\"index.html\">\n       <script>\n         angular.module('orderByExample', [])\n           .controller('ExampleController', ['$scope', function($scope) {\n             $scope.friends =\n                 [{name:'John', phone:'555-1212', age:10},\n                  {name:'Mary', phone:'555-9876', age:19},\n                  {name:'Mike', phone:'555-4321', age:21},\n                  {name:'Adam', phone:'555-5678', age:35},\n                  {name:'Julie', phone:'555-8765', age:29}];\n           }]);\n       </script>\n       <div ng-controller=\"ExampleController\">\n         <table class=\"friend\">\n           <tr>\n             <th>Name</th>\n             <th>Phone Number</th>\n             <th>Age</th>\n           </tr>\n           <tr ng-repeat=\"friend in friends | orderBy:'-age'\">\n             <td>{{friend.name}}</td>\n             <td>{{friend.phone}}</td>\n             <td>{{friend.age}}</td>\n           </tr>\n         </table>\n       </div>\n     </file>\n   </example>\n *\n * The predicate and reverse parameters can be controlled dynamically through scope properties,\n * as shown in the next example.\n * @example\n   <example module=\"orderByExample\">\n     <file name=\"index.html\">\n       <script>\n         angular.module('orderByExample', [])\n           .controller('ExampleController', ['$scope', function($scope) {\n             $scope.friends =\n                 [{name:'John', phone:'555-1212', age:10},\n                  {name:'Mary', phone:'555-9876', age:19},\n                  {name:'Mike', phone:'555-4321', age:21},\n                  {name:'Adam', phone:'555-5678', age:35},\n                  {name:'Julie', phone:'555-8765', age:29}];\n             $scope.predicate = '-age';\n           }]);\n       </script>\n       <div ng-controller=\"ExampleController\">\n         <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>\n         <hr/>\n         [ <a href=\"\" ng-click=\"predicate=''\">unsorted</a> ]\n         <table class=\"friend\">\n           <tr>\n             <th><a href=\"\" ng-click=\"predicate = 'name'; reverse=false\">Name</a>\n                 (<a href=\"\" ng-click=\"predicate = '-name'; reverse=false\">^</a>)</th>\n             <th><a href=\"\" ng-click=\"predicate = 'phone'; reverse=!reverse\">Phone Number</a></th>\n             <th><a href=\"\" ng-click=\"predicate = 'age'; reverse=!reverse\">Age</a></th>\n           </tr>\n           <tr ng-repeat=\"friend in friends | orderBy:predicate:reverse\">\n             <td>{{friend.name}}</td>\n             <td>{{friend.phone}}</td>\n             <td>{{friend.age}}</td>\n           </tr>\n         </table>\n       </div>\n     </file>\n   </example>\n *\n * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the\n * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the\n * desired parameters.\n *\n * Example:\n *\n * @example\n  <example module=\"orderByExample\">\n    <file name=\"index.html\">\n      <div ng-controller=\"ExampleController\">\n        <table class=\"friend\">\n          <tr>\n            <th><a href=\"\" ng-click=\"reverse=false;order('name', false)\">Name</a>\n              (<a href=\"\" ng-click=\"order('-name',false)\">^</a>)</th>\n            <th><a href=\"\" ng-click=\"reverse=!reverse;order('phone', reverse)\">Phone Number</a></th>\n            <th><a href=\"\" ng-click=\"reverse=!reverse;order('age',reverse)\">Age</a></th>\n          </tr>\n          <tr ng-repeat=\"friend in friends\">\n            <td>{{friend.name}}</td>\n            <td>{{friend.phone}}</td>\n            <td>{{friend.age}}</td>\n          </tr>\n        </table>\n      </div>\n    </file>\n\n    <file name=\"script.js\">\n      angular.module('orderByExample', [])\n        .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {\n          var orderBy = $filter('orderBy');\n          $scope.friends = [\n            { name: 'John',    phone: '555-1212',    age: 10 },\n            { name: 'Mary',    phone: '555-9876',    age: 19 },\n            { name: 'Mike',    phone: '555-4321',    age: 21 },\n            { name: 'Adam',    phone: '555-5678',    age: 35 },\n            { name: 'Julie',   phone: '555-8765',    age: 29 }\n          ];\n          $scope.order = function(predicate, reverse) {\n            $scope.friends = orderBy($scope.friends, predicate, reverse);\n          };\n          $scope.order('-age',false);\n        }]);\n    </file>\n</example>\n */\norderByFilter.$inject = ['$parse'];\nfunction orderByFilter($parse) {\n  return function(array, sortPredicate, reverseOrder) {\n    if (!(isArrayLike(array))) return array;\n    sortPredicate = isArray(sortPredicate) ? sortPredicate : [sortPredicate];\n    if (sortPredicate.length === 0) { sortPredicate = ['+']; }\n    sortPredicate = sortPredicate.map(function(predicate) {\n      var descending = false, get = predicate || identity;\n      if (isString(predicate)) {\n        if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {\n          descending = predicate.charAt(0) == '-';\n          predicate = predicate.substring(1);\n        }\n        if (predicate === '') {\n          // Effectively no predicate was passed so we compare identity\n          return reverseComparator(compare, descending);\n        }\n        get = $parse(predicate);\n        if (get.constant) {\n          var key = get();\n          return reverseComparator(function(a, b) {\n            return compare(a[key], b[key]);\n          }, descending);\n        }\n      }\n      return reverseComparator(function(a, b) {\n        return compare(get(a),get(b));\n      }, descending);\n    });\n    return slice.call(array).sort(reverseComparator(comparator, reverseOrder));\n\n    function comparator(o1, o2) {\n      for (var i = 0; i < sortPredicate.length; i++) {\n        var comp = sortPredicate[i](o1, o2);\n        if (comp !== 0) return comp;\n      }\n      return 0;\n    }\n    function reverseComparator(comp, descending) {\n      return descending\n          ? function(a, b) {return comp(b,a);}\n          : comp;\n    }\n\n    function isPrimitive(value) {\n      switch (typeof value) {\n        case 'number': /* falls through */\n        case 'boolean': /* falls through */\n        case 'string':\n          return true;\n        default:\n          return false;\n      }\n    }\n\n    function objectToString(value) {\n      if (value === null) return 'null';\n      if (typeof value.valueOf === 'function') {\n        value = value.valueOf();\n        if (isPrimitive(value)) return value;\n      }\n      if (typeof value.toString === 'function') {\n        value = value.toString();\n        if (isPrimitive(value)) return value;\n      }\n      return '';\n    }\n\n    function compare(v1, v2) {\n      var t1 = typeof v1;\n      var t2 = typeof v2;\n      if (t1 === t2 && t1 === \"object\") {\n        v1 = objectToString(v1);\n        v2 = objectToString(v2);\n      }\n      if (t1 === t2) {\n        if (t1 === \"string\") {\n           v1 = v1.toLowerCase();\n           v2 = v2.toLowerCase();\n        }\n        if (v1 === v2) return 0;\n        return v1 < v2 ? -1 : 1;\n      } else {\n        return t1 < t2 ? -1 : 1;\n      }\n    }\n  };\n}\n\nfunction ngDirective(directive) {\n  if (isFunction(directive)) {\n    directive = {\n      link: directive\n    };\n  }\n  directive.restrict = directive.restrict || 'AC';\n  return valueFn(directive);\n}\n\n/**\n * @ngdoc directive\n * @name a\n * @restrict E\n *\n * @description\n * Modifies the default behavior of the html A tag so that the default action is prevented when\n * the href attribute is empty.\n *\n * This change permits the easy creation of action links with the `ngClick` directive\n * without changing the location or causing page reloads, e.g.:\n * `<a href=\"\" ng-click=\"list.addItem()\">Add Item</a>`\n */\nvar htmlAnchorDirective = valueFn({\n  restrict: 'E',\n  compile: function(element, attr) {\n    if (!attr.href && !attr.xlinkHref && !attr.name) {\n      return function(scope, element) {\n        // If the linked element is not an anchor tag anymore, do nothing\n        if (element[0].nodeName.toLowerCase() !== 'a') return;\n\n        // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.\n        var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?\n                   'xlink:href' : 'href';\n        element.on('click', function(event) {\n          // if we have no href url, then don't navigate anywhere.\n          if (!element.attr(href)) {\n            event.preventDefault();\n          }\n        });\n      };\n    }\n  }\n});\n\n/**\n * @ngdoc directive\n * @name ngHref\n * @restrict A\n * @priority 99\n *\n * @description\n * Using Angular markup like `{{hash}}` in an href attribute will\n * make the link go to the wrong URL if the user clicks it before\n * Angular has a chance to replace the `{{hash}}` markup with its\n * value. Until Angular replaces the markup the link will be broken\n * and will most likely return a 404 error. The `ngHref` directive\n * solves this problem.\n *\n * The wrong way to write it:\n * ```html\n * <a href=\"http://www.gravatar.com/avatar/{{hash}}\">link1</a>\n * ```\n *\n * The correct way to write it:\n * ```html\n * <a ng-href=\"http://www.gravatar.com/avatar/{{hash}}\">link1</a>\n * ```\n *\n * @element A\n * @param {template} ngHref any string which can contain `{{}}` markup.\n *\n * @example\n * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes\n * in links and their different behaviors:\n    <example>\n      <file name=\"index.html\">\n        <input ng-model=\"value\" /><br />\n        <a id=\"link-1\" href ng-click=\"value = 1\">link 1</a> (link, don't reload)<br />\n        <a id=\"link-2\" href=\"\" ng-click=\"value = 2\">link 2</a> (link, don't reload)<br />\n        <a id=\"link-3\" ng-href=\"/{{'123'}}\">link 3</a> (link, reload!)<br />\n        <a id=\"link-4\" href=\"\" name=\"xx\" ng-click=\"value = 4\">anchor</a> (link, don't reload)<br />\n        <a id=\"link-5\" name=\"xxx\" ng-click=\"value = 5\">anchor</a> (no link)<br />\n        <a id=\"link-6\" ng-href=\"{{value}}\">link</a> (link, change location)\n      </file>\n      <file name=\"protractor.js\" type=\"protractor\">\n        it('should execute ng-click but not reload when href without value', function() {\n          element(by.id('link-1')).click();\n          expect(element(by.model('value')).getAttribute('value')).toEqual('1');\n          expect(element(by.id('link-1')).getAttribute('href')).toBe('');\n        });\n\n        it('should execute ng-click but not reload when href empty string', function() {\n          element(by.id('link-2')).click();\n          expect(element(by.model('value')).getAttribute('value')).toEqual('2');\n          expect(element(by.id('link-2')).getAttribute('href')).toBe('');\n        });\n\n        it('should execute ng-click and change url when ng-href specified', function() {\n          expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\\/123$/);\n\n          element(by.id('link-3')).click();\n\n          // At this point, we navigate away from an Angular page, so we need\n          // to use browser.driver to get the base webdriver.\n\n          browser.wait(function() {\n            return browser.driver.getCurrentUrl().then(function(url) {\n              return url.match(/\\/123$/);\n            });\n          }, 5000, 'page should navigate to /123');\n        });\n\n        xit('should execute ng-click but not reload when href empty string and name specified', function() {\n          element(by.id('link-4')).click();\n          expect(element(by.model('value')).getAttribute('value')).toEqual('4');\n          expect(element(by.id('link-4')).getAttribute('href')).toBe('');\n        });\n\n        it('should execute ng-click but not reload when no href but name specified', function() {\n          element(by.id('link-5')).click();\n          expect(element(by.model('value')).getAttribute('value')).toEqual('5');\n          expect(element(by.id('link-5')).getAttribute('href')).toBe(null);\n        });\n\n        it('should only change url when only ng-href', function() {\n          element(by.model('value')).clear();\n          element(by.model('value')).sendKeys('6');\n          expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\\/6$/);\n\n          element(by.id('link-6')).click();\n\n          // At this point, we navigate away from an Angular page, so we need\n          // to use browser.driver to get the base webdriver.\n          browser.wait(function() {\n            return browser.driver.getCurrentUrl().then(function(url) {\n              return url.match(/\\/6$/);\n            });\n          }, 5000, 'page should navigate to /6');\n        });\n      </file>\n    </example>\n */\n\n/**\n * @ngdoc directive\n * @name ngSrc\n * @restrict A\n * @priority 99\n *\n * @description\n * Using Angular markup like `{{hash}}` in a `src` attribute doesn't\n * work right: The browser will fetch from the URL with the literal\n * text `{{hash}}` until Angular replaces the expression inside\n * `{{hash}}`. The `ngSrc` directive solves this problem.\n *\n * The buggy way to write it:\n * ```html\n * <img src=\"http://www.gravatar.com/avatar/{{hash}}\"/>\n * ```\n *\n * The correct way to write it:\n * ```html\n * <img ng-src=\"http://www.gravatar.com/avatar/{{hash}}\"/>\n * ```\n *\n * @element IMG\n * @param {template} ngSrc any string which can contain `{{}}` markup.\n */\n\n/**\n * @ngdoc directive\n * @name ngSrcset\n * @restrict A\n * @priority 99\n *\n * @description\n * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't\n * work right: The browser will fetch from the URL with the literal\n * text `{{hash}}` until Angular replaces the expression inside\n * `{{hash}}`. The `ngSrcset` directive solves this problem.\n *\n * The buggy way to write it:\n * ```html\n * <img srcset=\"http://www.gravatar.com/avatar/{{hash}} 2x\"/>\n * ```\n *\n * The correct way to write it:\n * ```html\n * <img ng-srcset=\"http://www.gravatar.com/avatar/{{hash}} 2x\"/>\n * ```\n *\n * @element IMG\n * @param {template} ngSrcset any string which can contain `{{}}` markup.\n */\n\n/**\n * @ngdoc directive\n * @name ngDisabled\n * @restrict A\n * @priority 100\n *\n * @description\n *\n * This directive sets the `disabled` attribute on the element if the\n * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.\n *\n * A special directive is necessary because we cannot use interpolation inside the `disabled`\n * attribute.  The following example would make the button enabled on Chrome/Firefox\n * but not on older IEs:\n *\n * ```html\n * <!-- See below for an example of ng-disabled being used correctly -->\n * <div ng-init=\"isDisabled = false\">\n *  <button disabled=\"{{isDisabled}}\">Disabled</button>\n * </div>\n * ```\n *\n * This is because the HTML specification does not require browsers to preserve the values of\n * boolean attributes such as `disabled` (Their presence means true and their absence means false.)\n * If we put an Angular interpolation expression into such an attribute then the\n * binding information would be lost when the browser removes the attribute.\n *\n * @example\n    <example>\n      <file name=\"index.html\">\n        Click me to toggle: <input type=\"checkbox\" ng-model=\"checked\"><br/>\n        <button ng-model=\"button\" ng-disabled=\"checked\">Button</button>\n      </file>\n      <file name=\"protractor.js\" type=\"protractor\">\n        it('should toggle button', function() {\n          expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();\n          element(by.model('checked')).click();\n          expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();\n        });\n      </file>\n    </example>\n *\n * @element INPUT\n * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,\n *     then the `disabled` attribute will be set on the element\n */\n\n\n/**\n * @ngdoc directive\n * @name ngChecked\n * @restrict A\n * @priority 100\n *\n * @description\n * The HTML specification does not require browsers to preserve the values of boolean attributes\n * such as checked. (Their presence means true and their absence means false.)\n * If we put an Angular interpolation expression into such an attribute then the\n * binding information would be lost when the browser removes the attribute.\n * The `ngChecked` directive solves this problem for the `checked` attribute.\n * This complementary directive is not removed by the browser and so provides\n * a permanent reliable place to store the binding information.\n * @example\n    <example>\n      <file name=\"index.html\">\n        Check me to check both: <input type=\"checkbox\" ng-model=\"master\"><br/>\n        <input id=\"checkSlave\" type=\"checkbox\" ng-checked=\"master\">\n      </file>\n      <file name=\"protractor.js\" type=\"protractor\">\n        it('should check both checkBoxes', function() {\n          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();\n          element(by.model('master')).click();\n          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();\n        });\n      </file>\n    </example>\n *\n * @element INPUT\n * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,\n *     then special attribute \"checked\" will be set on the element\n */\n\n\n/**\n * @ngdoc directive\n * @name ngReadonly\n * @restrict A\n * @priority 100\n *\n * @description\n * The HTML specification does not require browsers to preserve the values of boolean attributes\n * such as readonly. (Their presence means true and their absence means false.)\n * If we put an Angular interpolation expression into such an attribute then the\n * binding information would be lost when the browser removes the attribute.\n * The `ngReadonly` directive solves this problem for the `readonly` attribute.\n * This complementary directive is not removed by the browser and so provides\n * a permanent reliable place to store the binding information.\n * @example\n    <example>\n      <file name=\"index.html\">\n        Check me to make text readonly: <input type=\"checkbox\" ng-model=\"checked\"><br/>\n        <input type=\"text\" ng-readonly=\"checked\" value=\"I'm Angular\"/>\n      </file>\n      <file name=\"protractor.js\" type=\"protractor\">\n        it('should toggle readonly attr', function() {\n          expect(element(by.css('[type=\"text\"]')).getAttribute('readonly')).toBeFalsy();\n          element(by.model('checked')).click();\n          expect(element(by.css('[type=\"text\"]')).getAttribute('readonly')).toBeTruthy();\n        });\n      </file>\n    </example>\n *\n * @element INPUT\n * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,\n *     then special attribute \"readonly\" will be set on the element\n */\n\n\n/**\n * @ngdoc directive\n * @name ngSelected\n * @restrict A\n * @priority 100\n *\n * @description\n * The HTML specification does not require browsers to preserve the values of boolean attributes\n * such as selected. (Their presence means true and their absence means false.)\n * If we put an Angular interpolation expression into such an attribute then the\n * binding information would be lost when the browser removes the attribute.\n * The `ngSelected` directive solves this problem for the `selected` attribute.\n * This complementary directive is not removed by the browser and so provides\n * a permanent reliable place to store the binding information.\n *\n * @example\n    <example>\n      <file name=\"index.html\">\n        Check me to select: <input type=\"checkbox\" ng-model=\"selected\"><br/>\n        <select>\n          <option>Hello!</option>\n          <option id=\"greet\" ng-selected=\"selected\">Greetings!</option>\n        </select>\n      </file>\n      <file name=\"protractor.js\" type=\"protractor\">\n        it('should select Greetings!', function() {\n          expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();\n          element(by.model('selected')).click();\n          expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();\n        });\n      </file>\n    </example>\n *\n * @element OPTION\n * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,\n *     then special attribute \"selected\" will be set on the element\n */\n\n/**\n * @ngdoc directive\n * @name ngOpen\n * @restrict A\n * @priority 100\n *\n * @description\n * The HTML specification does not require browsers to preserve the values of boolean attributes\n * such as open. (Their presence means true and their absence means false.)\n * If we put an Angular interpolation expression into such an attribute then the\n * binding information would be lost when the browser removes the attribute.\n * The `ngOpen` directive solves this problem for the `open` attribute.\n * This complementary directive is not removed by the browser and so provides\n * a permanent reliable place to store the binding information.\n * @example\n     <example>\n       <file name=\"index.html\">\n         Check me check multiple: <input type=\"checkbox\" ng-model=\"open\"><br/>\n         <details id=\"details\" ng-open=\"open\">\n            <summary>Show/Hide me</summary>\n         </details>\n       </file>\n       <file name=\"protractor.js\" type=\"protractor\">\n         it('should toggle open', function() {\n           expect(element(by.id('details')).getAttribute('open')).toBeFalsy();\n           element(by.model('open')).click();\n           expect(element(by.id('details')).getAttribute('open')).toBeTruthy();\n         });\n       </file>\n     </example>\n *\n * @element DETAILS\n * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,\n *     then special attribute \"open\" will be set on the element\n */\n\nvar ngAttributeAliasDirectives = {};\n\n\n// boolean attrs are evaluated\nforEach(BOOLEAN_ATTR, function(propName, attrName) {\n  // binding to multiple is not supported\n  if (propName == \"multiple\") return;\n\n  var normalized = directiveNormalize('ng-' + attrName);\n  ngAttributeAliasDirectives[normalized] = function() {\n    return {\n      restrict: 'A',\n      priority: 100,\n      link: function(scope, element, attr) {\n        scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {\n          attr.$set(attrName, !!value);\n        });\n      }\n    };\n  };\n});\n\n// aliased input attrs are evaluated\nforEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {\n  ngAttributeAliasDirectives[ngAttr] = function() {\n    return {\n      priority: 100,\n      link: function(scope, element, attr) {\n        //special case ngPattern when a literal regular expression value\n        //is used as the expression (this way we don't have to watch anything).\n        if (ngAttr === \"ngPattern\" && attr.ngPattern.charAt(0) == \"/\") {\n          var match = attr.ngPattern.match(REGEX_STRING_REGEXP);\n          if (match) {\n            attr.$set(\"ngPattern\", new RegExp(match[1], match[2]));\n            return;\n          }\n        }\n\n        scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {\n          attr.$set(ngAttr, value);\n        });\n      }\n    };\n  };\n});\n\n// ng-src, ng-srcset, ng-href are interpolated\nforEach(['src', 'srcset', 'href'], function(attrName) {\n  var normalized = directiveNormalize('ng-' + attrName);\n  ngAttributeAliasDirectives[normalized] = function() {\n    return {\n      priority: 99, // it needs to run after the attributes are interpolated\n      link: function(scope, element, attr) {\n        var propName = attrName,\n            name = attrName;\n\n        if (attrName === 'href' &&\n            toString.call(element.prop('href')) === '[object SVGAnimatedString]') {\n          name = 'xlinkHref';\n          attr.$attr[name] = 'xlink:href';\n          propName = null;\n        }\n\n        attr.$observe(normalized, function(value) {\n          if (!value) {\n            if (attrName === 'href') {\n              attr.$set(name, null);\n            }\n            return;\n          }\n\n          attr.$set(name, value);\n\n          // on IE, if \"ng:src\" directive declaration is used and \"src\" attribute doesn't exist\n          // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need\n          // to set the property as well to achieve the desired effect.\n          // we use attr[attrName] value since $set can sanitize the url.\n          if (msie && propName) element.prop(propName, attr[name]);\n        });\n      }\n    };\n  };\n});\n\n/* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true\n */\nvar nullFormCtrl = {\n  $addControl: noop,\n  $$renameControl: nullFormRenameControl,\n  $removeControl: noop,\n  $setValidity: noop,\n  $setDirty: noop,\n  $setPristine: noop,\n  $setSubmitted: noop\n},\nSUBMITTED_CLASS = 'ng-submitted';\n\nfunction nullFormRenameControl(control, name) {\n  control.$name = name;\n}\n\n/**\n * @ngdoc type\n * @name form.FormController\n *\n * @property {boolean} $pristine True if user has not interacted with the form yet.\n * @property {boolean} $dirty True if user has already interacted with the form.\n * @property {boolean} $valid True if all of the containing forms and controls are valid.\n * @property {boolean} $invalid True if at least one containing control or form is invalid.\n * @property {boolean} $submitted True if user has submitted the form even if its invalid.\n *\n * @property {Object} $error Is an object hash, containing references to controls or\n *  forms with failing validators, where:\n *\n *  - keys are validation tokens (error names),\n *  - values are arrays of controls or forms that have a failing validator for given error name.\n *\n *  Built-in validation tokens:\n *\n *  - `email`\n *  - `max`\n *  - `maxlength`\n *  - `min`\n *  - `minlength`\n *  - `number`\n *  - `pattern`\n *  - `required`\n *  - `url`\n *  - `date`\n *  - `datetimelocal`\n *  - `time`\n *  - `week`\n *  - `month`\n *\n * @description\n * `FormController` keeps track of all its controls and nested forms as well as the state of them,\n * such as being valid/invalid or dirty/pristine.\n *\n * Each {@link ng.directive:form form} directive creates an instance\n * of `FormController`.\n *\n */\n//asks for $scope to fool the BC controller module\nFormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];\nfunction FormController(element, attrs, $scope, $animate, $interpolate) {\n  var form = this,\n      controls = [];\n\n  var parentForm = form.$$parentForm = element.parent().controller('form') || nullFormCtrl;\n\n  // init state\n  form.$error = {};\n  form.$$success = {};\n  form.$pending = undefined;\n  form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);\n  form.$dirty = false;\n  form.$pristine = true;\n  form.$valid = true;\n  form.$invalid = false;\n  form.$submitted = false;\n\n  parentForm.$addControl(form);\n\n  /**\n   * @ngdoc method\n   * @name form.FormController#$rollbackViewValue\n   *\n   * @description\n   * Rollback all form controls pending updates to the `$modelValue`.\n   *\n   * Updates may be pending by a debounced event or because the input is waiting for a some future\n   * event defined in `ng-model-options`. This method is typically needed by the reset button of\n   * a form that uses `ng-model-options` to pend updates.\n   */\n  form.$rollbackViewValue = function() {\n    forEach(controls, function(control) {\n      control.$rollbackViewValue();\n    });\n  };\n\n  /**\n   * @ngdoc method\n   * @name form.FormController#$commitViewValue\n   *\n   * @description\n   * Commit all form controls pending updates to the `$modelValue`.\n   *\n   * Updates may be pending by a debounced event or because the input is waiting for a some future\n   * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`\n   * usually handles calling this in response to input events.\n   */\n  form.$commitViewValue = function() {\n    forEach(controls, function(control) {\n      control.$commitViewValue();\n    });\n  };\n\n  /**\n   * @ngdoc method\n   * @name form.FormController#$addControl\n   *\n   * @description\n   * Register a control with the form.\n   *\n   * Input elements using ngModelController do this automatically when they are linked.\n   */\n  form.$addControl = function(control) {\n    // Breaking change - before, inputs whose name was \"hasOwnProperty\" were quietly ignored\n    // and not added to the scope.  Now we throw an error.\n    assertNotHasOwnProperty(control.$name, 'input');\n    controls.push(control);\n\n    if (control.$name) {\n      form[control.$name] = control;\n    }\n  };\n\n  // Private API: rename a form control\n  form.$$renameControl = function(control, newName) {\n    var oldName = control.$name;\n\n    if (form[oldName] === control) {\n      delete form[oldName];\n    }\n    form[newName] = control;\n    control.$name = newName;\n  };\n\n  /**\n   * @ngdoc method\n   * @name form.FormController#$removeControl\n   *\n   * @description\n   * Deregister a control from the form.\n   *\n   * Input elements using ngModelController do this automatically when they are destroyed.\n   */\n  form.$removeControl = function(control) {\n    if (control.$name && form[control.$name] === control) {\n      delete form[control.$name];\n    }\n    forEach(form.$pending, function(value, name) {\n      form.$setValidity(name, null, control);\n    });\n    forEach(form.$error, function(value, name) {\n      form.$setValidity(name, null, control);\n    });\n    forEach(form.$$success, function(value, name) {\n      form.$setValidity(name, null, control);\n    });\n\n    arrayRemove(controls, control);\n  };\n\n\n  /**\n   * @ngdoc method\n   * @name form.FormController#$setValidity\n   *\n   * @description\n   * Sets the validity of a form control.\n   *\n   * This method will also propagate to parent forms.\n   */\n  addSetValidityMethod({\n    ctrl: this,\n    $element: element,\n    set: function(object, property, controller) {\n      var list = object[property];\n      if (!list) {\n        object[property] = [controller];\n      } else {\n        var index = list.indexOf(controller);\n        if (index === -1) {\n          list.push(controller);\n        }\n      }\n    },\n    unset: function(object, property, controller) {\n      var list = object[property];\n      if (!list) {\n        return;\n      }\n      arrayRemove(list, controller);\n      if (list.length === 0) {\n        delete object[property];\n      }\n    },\n    parentForm: parentForm,\n    $animate: $animate\n  });\n\n  /**\n   * @ngdoc method\n   * @name form.FormController#$setDirty\n   *\n   * @description\n   * Sets the form to a dirty state.\n   *\n   * This method can be called to add the 'ng-dirty' class and set the form to a dirty\n   * state (ng-dirty class). This method will also propagate to parent forms.\n   */\n  form.$setDirty = function() {\n    $animate.removeClass(element, PRISTINE_CLASS);\n    $animate.addClass(element, DIRTY_CLASS);\n    form.$dirty = true;\n    form.$pristine = false;\n    parentForm.$setDirty();\n  };\n\n  /**\n   * @ngdoc method\n   * @name form.FormController#$setPristine\n   *\n   * @description\n   * Sets the form to its pristine state.\n   *\n   * This method can be called to remove the 'ng-dirty' class and set the form to its pristine\n   * state (ng-pristine class). This method will also propagate to all the controls contained\n   * in this form.\n   *\n   * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after\n   * saving or resetting it.\n   */\n  form.$setPristine = function() {\n    $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);\n    form.$dirty = false;\n    form.$pristine = true;\n    form.$submitted = false;\n    forEach(controls, function(control) {\n      control.$setPristine();\n    });\n  };\n\n  /**\n   * @ngdoc method\n   * @name form.FormController#$setUntouched\n   *\n   * @description\n   * Sets the form to its untouched state.\n   *\n   * This method can be called to remove the 'ng-touched' class and set the form controls to their\n   * untouched state (ng-untouched class).\n   *\n   * Setting a form controls back to their untouched state is often useful when setting the form\n   * back to its pristine state.\n   */\n  form.$setUntouched = function() {\n    forEach(controls, function(control) {\n      control.$setUntouched();\n    });\n  };\n\n  /**\n   * @ngdoc method\n   * @name form.FormController#$setSubmitted\n   *\n   * @description\n   * Sets the form to its submitted state.\n   */\n  form.$setSubmitted = function() {\n    $animate.addClass(element, SUBMITTED_CLASS);\n    form.$submitted = true;\n    parentForm.$setSubmitted();\n  };\n}\n\n/**\n * @ngdoc directive\n * @name ngForm\n * @restrict EAC\n *\n * @description\n * Nestable alias of {@link ng.directive:form `form`} directive. HTML\n * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a\n * sub-group of controls needs to be determined.\n *\n * Note: the purpose of `ngForm` is to group controls,\n * but not to be a replacement for the `<form>` tag with all of its capabilities\n * (e.g. posting to the server, ...).\n *\n * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into\n *                       related scope, under this name.\n *\n */\n\n /**\n * @ngdoc directive\n * @name form\n * @restrict E\n *\n * @description\n * Directive that instantiates\n * {@link form.FormController FormController}.\n *\n * If the `name` attribute is specified, the form controller is published onto the current scope under\n * this name.\n *\n * # Alias: {@link ng.directive:ngForm `ngForm`}\n *\n * In Angular, forms can be nested. This means that the outer form is valid when all of the child\n * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so\n * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to\n * `<form>` but can be nested.  This allows you to have nested forms, which is very useful when\n * using Angular validation directives in forms that are dynamically generated using the\n * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`\n * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an\n * `ngForm` directive and nest these in an outer `form` element.\n *\n *\n * # CSS classes\n *  - `ng-valid` is set if the form is valid.\n *  - `ng-invalid` is set if the form is invalid.\n *  - `ng-pristine` is set if the form is pristine.\n *  - `ng-dirty` is set if the form is dirty.\n *  - `ng-submitted` is set if the form was submitted.\n *\n * Keep in mind that ngAnimate can detect each of these classes when added and removed.\n *\n *\n * # Submitting a form and preventing the default action\n *\n * Since the role of forms in client-side Angular applications is different than in classical\n * roundtrip apps, it is desirable for the browser not to translate the form submission into a full\n * page reload that sends the data to the server. Instead some javascript logic should be triggered\n * to handle the form submission in an application-specific way.\n *\n * For this reason, Angular prevents the default action (form submission to the server) unless the\n * `<form>` element has an `action` attribute specified.\n *\n * You can use one of the following two ways to specify what javascript method should be called when\n * a form is submitted:\n *\n * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element\n * - {@link ng.directive:ngClick ngClick} directive on the first\n  *  button or input field of type submit (input[type=submit])\n *\n * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}\n * or {@link ng.directive:ngClick ngClick} directives.\n * This is because of the following form submission rules in the HTML specification:\n *\n * - If a form has only one input field then hitting enter in this field triggers form submit\n * (`ngSubmit`)\n * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter\n * doesn't trigger submit\n * - if a form has one or more input fields and one or more buttons or input[type=submit] then\n * hitting enter in any of the input fields will trigger the click handler on the *first* button or\n * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)\n *\n * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is\n * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`\n * to have access to the updated model.\n *\n * ## Animation Hooks\n *\n * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.\n * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any\n * other validations that are performed within the form. Animations in ngForm are similar to how\n * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well\n * as JS animations.\n *\n * The following example shows a simple way to utilize CSS transitions to style a form element\n * that has been rendered as invalid after it has been validated:\n *\n * <pre>\n * //be sure to include ngAnimate as a module to hook into more\n * //advanced animations\n * .my-form {\n *   transition:0.5s linear all;\n *   background: white;\n * }\n * .my-form.ng-invalid {\n *   background: red;\n *   color:white;\n * }\n * </pre>\n *\n * @example\n    <example deps=\"angular-animate.js\" animations=\"true\" fixBase=\"true\" module=\"formExample\">\n      <file name=\"index.html\">\n       <script>\n         angular.module('formExample', [])\n           .controller('FormController', ['$scope', function($scope) {\n             $scope.userType = 'guest';\n           }]);\n       </script>\n       <style>\n        .my-form {\n          -webkit-transition:all linear 0.5s;\n          transition:all linear 0.5s;\n          background: transparent;\n        }\n        .my-form.ng-invalid {\n          background: red;\n        }\n       </style>\n       <form name=\"myForm\" ng-controller=\"FormController\" class=\"my-form\">\n         userType: <input name=\"input\" ng-model=\"userType\" required>\n         <span class=\"error\" ng-show=\"myForm.input.$error.required\">Required!</span><br>\n         <tt>userType = {{userType}}</tt><br>\n         <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br>\n         <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br>\n         <tt>myForm.$valid = {{myForm.$valid}}</tt><br>\n         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>\n        </form>\n      </file>\n      <file name=\"protractor.js\" type=\"protractor\">\n        it('should initialize to model', function() {\n          var userType = element(by.binding('userType'));\n          var valid = element(by.binding('myForm.input.$valid'));\n\n          expect(userType.getText()).toContain('guest');\n          expect(valid.getText()).toContain('true');\n        });\n\n        it('should be invalid if empty', function() {\n          var userType = element(by.binding('userType'));\n          var valid = element(by.binding('myForm.input.$valid'));\n          var userInput = element(by.model('userType'));\n\n          userInput.clear();\n          userInput.sendKeys('');\n\n          expect(userType.getText()).toEqual('userType =');\n          expect(valid.getText()).toContain('false');\n        });\n      </file>\n    </example>\n *\n * @param {string=} name Name of the form. If specified, the form controller will be published into\n *                       related scope, under this name.\n */\nvar formDirectiveFactory = function(isNgForm) {\n  return ['$timeout', function($timeout) {\n    var formDirective = {\n      name: 'form',\n      restrict: isNgForm ? 'EAC' : 'E',\n      controller: FormController,\n      compile: function ngFormCompile(formElement, attr) {\n        // Setup initial state of the control\n        formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);\n\n        var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);\n\n        return {\n          pre: function ngFormPreLink(scope, formElement, attr, controller) {\n            // if `action` attr is not present on the form, prevent the default action (submission)\n            if (!('action' in attr)) {\n              // we can't use jq events because if a form is destroyed during submission the default\n              // action is not prevented. see #1238\n              //\n              // IE 9 is not affected because it doesn't fire a submit event and try to do a full\n              // page reload if the form was destroyed by submission of the form via a click handler\n              // on a button in the form. Looks like an IE9 specific bug.\n              var handleFormSubmission = function(event) {\n                scope.$apply(function() {\n                  controller.$commitViewValue();\n                  controller.$setSubmitted();\n                });\n\n                event.preventDefault();\n              };\n\n              addEventListenerFn(formElement[0], 'submit', handleFormSubmission);\n\n              // unregister the preventDefault listener so that we don't not leak memory but in a\n              // way that will achieve the prevention of the default action.\n              formElement.on('$destroy', function() {\n                $timeout(function() {\n                  removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);\n                }, 0, false);\n              });\n            }\n\n            var parentFormCtrl = controller.$$parentForm;\n\n            if (nameAttr) {\n              setter(scope, null, controller.$name, controller, controller.$name);\n              attr.$observe(nameAttr, function(newValue) {\n                if (controller.$name === newValue) return;\n                setter(scope, null, controller.$name, undefined, controller.$name);\n                parentFormCtrl.$$renameControl(controller, newValue);\n                setter(scope, null, controller.$name, controller, controller.$name);\n              });\n            }\n            formElement.on('$destroy', function() {\n              parentFormCtrl.$removeControl(controller);\n              if (nameAttr) {\n                setter(scope, null, attr[nameAttr], undefined, controller.$name);\n              }\n              extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards\n            });\n          }\n        };\n      }\n    };\n\n    return formDirective;\n  }];\n};\n\nvar formDirective = formDirectiveFactory();\nvar ngFormDirective = formDirectiveFactory(true);\n\n/* global VALID_CLASS: false,\n  INVALID_CLASS: false,\n  PRISTINE_CLASS: false,\n  DIRTY_CLASS: false,\n  UNTOUCHED_CLASS: false,\n  TOUCHED_CLASS: false,\n  $ngModelMinErr: false,\n*/\n\n// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231\nvar ISO_DATE_REGEXP = /\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d:[0-5]\\d|Z)/;\nvar URL_REGEXP = /^(ftp|http|https):\\/\\/(\\w+:{0,1}\\w*@)?(\\S+)(:[0-9]+)?(\\/|\\/([\\w#!:.?+=&%@!\\-\\/]))?$/;\nvar EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;\nvar NUMBER_REGEXP = /^\\s*(\\-|\\+)?(\\d+|(\\d*(\\.\\d*)))\\s*$/;\nvar DATE_REGEXP = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nvar DATETIMELOCAL_REGEXP = /^(\\d{4})-(\\d\\d)-(\\d\\d)T(\\d\\d):(\\d\\d)(?::(\\d\\d)(\\.\\d{1,3})?)?$/;\nvar WEEK_REGEXP = /^(\\d{4})-W(\\d\\d)$/;\nvar MONTH_REGEXP = /^(\\d{4})-(\\d\\d)$/;\nvar TIME_REGEXP = /^(\\d\\d):(\\d\\d)(?::(\\d\\d)(\\.\\d{1,3})?)?$/;\n\nvar inputType = {\n\n  /**\n   * @ngdoc input\n   * @name input[text]\n   *\n   * @description\n   * Standard HTML text input with angular data binding, inherited by most of the `input` elements.\n   *\n   *\n   * @param {string} ngModel Assignable angular expression to data-bind to.\n   * @param {string=} name Property name of the form under which the control is published.\n   * @param {string=} required Adds `required` validation error key if the value is not entered.\n   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to\n   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of\n   *    `required` when you want to data-bind to the `required` attribute.\n   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than\n   *    minlength.\n   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than\n   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of\n   *    any length.\n   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string\n   *    that contains the regular expression body that will be converted to a regular expression\n   *    as in the ngPattern directive.\n   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match\n   *    a RegExp found by evaluating the Angular expression given in the attribute value.\n   *    If the expression evaluates to a RegExp object then this is used directly.\n   *    If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`\n   *    characters. For instance, `\"abc\"` will be converted to `new RegExp('^abc$')`.\n   * @param {string=} ngChange Angular expression to be executed when input changes due to user\n   *    interaction with the input element.\n   * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.\n   *    This parameter is ignored for input[type=password] controls, which will never trim the\n   *    input.\n   *\n   * @example\n      <example name=\"text-input-directive\" module=\"textInputExample\">\n        <file name=\"index.html\">\n         <script>\n           angular.module('textInputExample', [])\n             .controller('ExampleController', ['$scope', function($scope) {\n               $scope.example = {\n                 text: 'guest',\n                 word: /^\\s*\\w*\\s*$/\n               };\n             }]);\n         </script>\n         <form name=\"myForm\" ng-controller=\"ExampleController\">\n           Single word: <input type=\"text\" name=\"input\" ng-model=\"example.text\"\n                               ng-pattern=\"example.word\" required ng-trim=\"false\">\n           <span class=\"error\" ng-show=\"myForm.input.$error.required\">\n             Required!</span>\n           <span class=\"error\" ng-show=\"myForm.input.$error.pattern\">\n             Single word only!</span>\n\n           <tt>text = {{example.text}}</tt><br/>\n           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>\n           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>\n           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>\n           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>\n          </form>\n        </file>\n        <file name=\"protractor.js\" type=\"protractor\">\n          var text = element(by.binding('example.text'));\n          var valid = element(by.binding('myForm.input.$valid'));\n          var input = element(by.model('example.text'));\n\n          it('should initialize to model', function() {\n            expect(text.getText()).toContain('guest');\n            expect(valid.getText()).toContain('true');\n          });\n\n          it('should be invalid if empty', function() {\n            input.clear();\n            input.sendKeys('');\n\n            expect(text.getText()).toEqual('text =');\n            expect(valid.getText()).toContain('false');\n          });\n\n          it('should be invalid if multi word', function() {\n            input.clear();\n            input.sendKeys('hello world');\n\n            expect(valid.getText()).toContain('false');\n          });\n        </file>\n      </example>\n   */\n  'text': textInputType,\n\n    /**\n     * @ngdoc input\n     * @name input[date]\n     *\n     * @description\n     * Input with date validation and transformation. In browsers that do not yet support\n     * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601\n     * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many\n     * modern browsers do not yet support this input type, it is important to provide cues to users on the\n     * expected input format via a placeholder or label.\n     *\n     * The model must always be a Date object, otherwise Angular will throw an error.\n     * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.\n     *\n     * The timezone to be used to read/write the `Date` instance in the model can be defined using\n     * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.\n     *\n     * @param {string} ngModel Assignable angular expression to data-bind to.\n     * @param {string=} name Property name of the form under which the control is published.\n     * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a\n     * valid ISO date string (yyyy-MM-dd).\n     * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be\n     * a valid ISO date string (yyyy-MM-dd).\n     * @param {string=} required Sets `required` validation error key if the value is not entered.\n     * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to\n     *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of\n     *    `required` when you want to data-bind to the `required` attribute.\n     * @param {string=} ngChange Angular expression to be executed when input changes due to user\n     *    interaction with the input element.\n     *\n     * @example\n     <example name=\"date-input-directive\" module=\"dateInputExample\">\n     <file name=\"index.html\">\n       <script>\n          angular.module('dateInputExample', [])\n            .controller('DateController', ['$scope', function($scope) {\n              $scope.example = {\n                value: new Date(2013, 9, 22)\n              };\n            }]);\n       </script>\n       <form name=\"myForm\" ng-controller=\"DateController as dateCtrl\">\n          Pick a date in 2013:\n          <input type=\"date\" id=\"exampleInput\" name=\"input\" ng-model=\"example.value\"\n              placeholder=\"yyyy-MM-dd\" min=\"2013-01-01\" max=\"2013-12-31\" required />\n          <span class=\"error\" ng-show=\"myForm.input.$error.required\">\n              Required!</span>\n          <span class=\"error\" ng-show=\"myForm.input.$error.date\">\n              Not a valid date!</span>\n           <tt>value = {{example.value | date: \"yyyy-MM-dd\"}}</tt><br/>\n           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>\n           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>\n           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>\n           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>\n       </form>\n     </file>\n     <file name=\"protractor.js\" type=\"protractor\">\n        var value = element(by.binding('example.value | date: \"yyyy-MM-dd\"'));\n        var valid = element(by.binding('myForm.input.$valid'));\n        var input = element(by.model('example.value'));\n\n        // currently protractor/webdriver does not support\n        // sending keys to all known HTML5 input controls\n        // for various browsers (see https://github.com/angular/protractor/issues/562).\n        function setInput(val) {\n          // set the value of the element and force validation.\n          var scr = \"var ipt = document.getElementById('exampleInput'); \" +\n          \"ipt.value = '\" + val + \"';\" +\n          \"angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('\" + val + \"'); });\";\n          browser.executeScript(scr);\n        }\n\n        it('should initialize to model', function() {\n          expect(value.getText()).toContain('2013-10-22');\n          expect(valid.getText()).toContain('myForm.input.$valid = true');\n        });\n\n        it('should be invalid if empty', function() {\n          setInput('');\n          expect(value.getText()).toEqual('value =');\n          expect(valid.getText()).toContain('myForm.input.$valid = false');\n        });\n\n        it('should be invalid if over max', function() {\n          setInput('2015-01-01');\n          expect(value.getText()).toContain('');\n          expect(valid.getText()).toContain('myForm.input.$valid = false');\n        });\n     </file>\n     </example>\n     */\n  'date': createDateInputType('date', DATE_REGEXP,\n         createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),\n         'yyyy-MM-dd'),\n\n   /**\n    * @ngdoc input\n    * @name input[datetime-local]\n    *\n    * @description\n    * Input with datetime validation and transformation. In browsers that do not yet support\n    * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601\n    * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.\n    *\n    * The model must always be a Date object, otherwise Angular will throw an error.\n    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.\n    *\n    * The timezone to be used to read/write the `Date` instance in the model can be defined using\n    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.\n    *\n    * @param {string} ngModel Assignable angular expression to data-bind to.\n    * @param {string=} name Property name of the form under which the control is published.\n    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a\n    * valid ISO datetime format (yyyy-MM-ddTHH:mm:ss).\n    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be\n    * a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss).\n    * @param {string=} required Sets `required` validation error key if the value is not entered.\n    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to\n    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of\n    *    `required` when you want to data-bind to the `required` attribute.\n    * @param {string=} ngChange Angular expression to be executed when input changes due to user\n    *    interaction with the input element.\n    *\n    * @example\n    <example name=\"datetimelocal-input-directive\" module=\"dateExample\">\n    <file name=\"index.html\">\n      <script>\n        angular.module('dateExample', [])\n          .controller('DateController', ['$scope', function($scope) {\n            $scope.example = {\n              value: new Date(2010, 11, 28, 14, 57)\n            };\n          }]);\n      </script>\n      <form name=\"myForm\" ng-controller=\"DateController as dateCtrl\">\n        Pick a date between in 2013:\n        <input type=\"datetime-local\" id=\"exampleInput\" name=\"input\" ng-model=\"example.value\"\n            placeholder=\"yyyy-MM-ddTHH:mm:ss\" min=\"2001-01-01T00:00:00\" max=\"2013-12-31T00:00:00\" required />\n        <span class=\"error\" ng-show=\"myForm.input.$error.required\">\n            Required!</span>\n        <span class=\"error\" ng-show=\"myForm.input.$error.datetimelocal\">\n            Not a valid date!</span>\n        <tt>value = {{example.value | date: \"yyyy-MM-ddTHH:mm:ss\"}}</tt><br/>\n        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>\n        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>\n        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>\n        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>\n      </form>\n    </file>\n    <file name=\"protractor.js\" type=\"protractor\">\n      var value = element(by.binding('example.value | date: \"yyyy-MM-ddTHH:mm:ss\"'));\n      var valid = element(by.binding('myForm.input.$valid'));\n      var input = element(by.model('example.value'));\n\n      // currently protractor/webdriver does not support\n      // sending keys to all known HTML5 input controls\n      // for various browsers (https://github.com/angular/protractor/issues/562).\n      function setInput(val) {\n        // set the value of the element and force validation.\n        var scr = \"var ipt = document.getElementById('exampleInput'); \" +\n        \"ipt.value = '\" + val + \"';\" +\n        \"angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('\" + val + \"'); });\";\n        browser.executeScript(scr);\n      }\n\n      it('should initialize to model', function() {\n        expect(value.getText()).toContain('2010-12-28T14:57:00');\n        expect(valid.getText()).toContain('myForm.input.$valid = true');\n      });\n\n      it('should be invalid if empty', function() {\n        setInput('');\n        expect(value.getText()).toEqual('value =');\n        expect(valid.getText()).toContain('myForm.input.$valid = false');\n      });\n\n      it('should be invalid if over max', function() {\n        setInput('2015-01-01T23:59:00');\n        expect(value.getText()).toContain('');\n        expect(valid.getText()).toContain('myForm.input.$valid = false');\n      });\n    </file>\n    </example>\n    */\n  'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,\n      createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),\n      'yyyy-MM-ddTHH:mm:ss.sss'),\n\n  /**\n   * @ngdoc input\n   * @name input[time]\n   *\n   * @description\n   * Input with time validation and transformation. In browsers that do not yet support\n   * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601\n   * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a\n   * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.\n   *\n   * The model must always be a Date object, otherwise Angular will throw an error.\n   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.\n   *\n   * The timezone to be used to read/write the `Date` instance in the model can be defined using\n   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.\n   *\n   * @param {string} ngModel Assignable angular expression to data-bind to.\n   * @param {string=} name Property name of the form under which the control is published.\n   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a\n   * valid ISO time format (HH:mm:ss).\n   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be a\n   * valid ISO time format (HH:mm:ss).\n   * @param {string=} required Sets `required` validation error key if the value is not entered.\n   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to\n   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of\n   *    `required` when you want to data-bind to the `required` attribute.\n   * @param {string=} ngChange Angular expression to be executed when input changes due to user\n   *    interaction with the input element.\n   *\n   * @example\n   <example name=\"time-input-directive\" module=\"timeExample\">\n   <file name=\"index.html\">\n     <script>\n      angular.module('timeExample', [])\n        .controller('DateController', ['$scope', function($scope) {\n          $scope.example = {\n            value: new Date(1970, 0, 1, 14, 57, 0)\n          };\n        }]);\n     </script>\n     <form name=\"myForm\" ng-controller=\"DateController as dateCtrl\">\n        Pick a between 8am and 5pm:\n        <input type=\"time\" id=\"exampleInput\" name=\"input\" ng-model=\"example.value\"\n            placeholder=\"HH:mm:ss\" min=\"08:00:00\" max=\"17:00:00\" required />\n        <span class=\"error\" ng-show=\"myForm.input.$error.required\">\n            Required!</span>\n        <span class=\"error\" ng-show=\"myForm.input.$error.time\">\n            Not a valid date!</span>\n        <tt>value = {{example.value | date: \"HH:mm:ss\"}}</tt><br/>\n        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>\n        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>\n        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>\n        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>\n     </form>\n   </file>\n   <file name=\"protractor.js\" type=\"protractor\">\n      var value = element(by.binding('example.value | date: \"HH:mm:ss\"'));\n      var valid = element(by.binding('myForm.input.$valid'));\n      var input = element(by.model('example.value'));\n\n      // currently protractor/webdriver does not support\n      // sending keys to all known HTML5 input controls\n      // for various browsers (https://github.com/angular/protractor/issues/562).\n      function setInput(val) {\n        // set the value of the element and force validation.\n        var scr = \"var ipt = document.getElementById('exampleInput'); \" +\n        \"ipt.value = '\" + val + \"';\" +\n        \"angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('\" + val + \"'); });\";\n        browser.executeScript(scr);\n      }\n\n      it('should initialize to model', function() {\n        expect(value.getText()).toContain('14:57:00');\n        expect(valid.getText()).toContain('myForm.input.$valid = true');\n      });\n\n      it('should be invalid if empty', function() {\n        setInput('');\n        expect(value.getText()).toEqual('value =');\n        expect(valid.getText()).toContain('myForm.input.$valid = false');\n      });\n\n      it('should be invalid if over max', function() {\n        setInput('23:59:00');\n        expect(value.getText()).toContain('');\n        expect(valid.getText()).toContain('myForm.input.$valid = false');\n      });\n   </file>\n   </example>\n   */\n  'time': createDateInputType('time', TIME_REGEXP,\n      createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),\n     'HH:mm:ss.sss'),\n\n   /**\n    * @ngdoc input\n    * @name input[week]\n    *\n    * @description\n    * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support\n    * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601\n    * week format (yyyy-W##), for example: `2013-W02`.\n    *\n    * The model must always be a Date object, otherwise Angular will throw an error.\n    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.\n    *\n    * The timezone to be used to read/write the `Date` instance in the model can be defined using\n    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.\n    *\n    * @param {string} ngModel Assignable angular expression to data-bind to.\n    * @param {string=} name Property name of the form under which the control is published.\n    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a\n    * valid ISO week format (yyyy-W##).\n    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be\n    * a valid ISO week format (yyyy-W##).\n    * @param {string=} required Sets `required` validation error key if the value is not entered.\n    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to\n    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of\n    *    `required` when you want to data-bind to the `required` attribute.\n    * @param {string=} ngChange Angular expression to be executed when input changes due to user\n    *    interaction with the input element.\n    *\n    * @example\n    <example name=\"week-input-directive\" module=\"weekExample\">\n    <file name=\"index.html\">\n      <script>\n      angular.module('weekExample', [])\n        .controller('DateController', ['$scope', function($scope) {\n          $scope.example = {\n            value: new Date(2013, 0, 3)\n          };\n        }]);\n      </script>\n      <form name=\"myForm\" ng-controller=\"DateController as dateCtrl\">\n        Pick a date between in 2013:\n        <input id=\"exampleInput\" type=\"week\" name=\"input\" ng-model=\"example.value\"\n            placeholder=\"YYYY-W##\" min=\"2012-W32\" max=\"2013-W52\" required />\n        <span class=\"error\" ng-show=\"myForm.input.$error.required\">\n            Required!</span>\n        <span class=\"error\" ng-show=\"myForm.input.$error.week\">\n            Not a valid date!</span>\n        <tt>value = {{example.value | date: \"yyyy-Www\"}}</tt><br/>\n        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>\n        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>\n        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>\n        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>\n      </form>\n    </file>\n    <file name=\"protractor.js\" type=\"protractor\">\n      var value = element(by.binding('example.value | date: \"yyyy-Www\"'));\n      var valid = element(by.binding('myForm.input.$valid'));\n      var input = element(by.model('example.value'));\n\n      // currently protractor/webdriver does not support\n      // sending keys to all known HTML5 input controls\n      // for various browsers (https://github.com/angular/protractor/issues/562).\n      function setInput(val) {\n        // set the value of the element and force validation.\n        var scr = \"var ipt = document.getElementById('exampleInput'); \" +\n        \"ipt.value = '\" + val + \"';\" +\n        \"angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('\" + val + \"'); });\";\n        browser.executeScript(scr);\n      }\n\n      it('should initialize to model', function() {\n        expect(value.getText()).toContain('2013-W01');\n        expect(valid.getText()).toContain('myForm.input.$valid = true');\n      });\n\n      it('should be invalid if empty', function() {\n        setInput('');\n        expect(value.getText()).toEqual('value =');\n        expect(valid.getText()).toContain('myForm.input.$valid = false');\n      });\n\n      it('should be invalid if over max', function() {\n        setInput('2015-W01');\n        expect(value.getText()).toContain('');\n        expect(valid.getText()).toContain('myForm.input.$valid = false');\n      });\n    </file>\n    </example>\n    */\n  'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),\n\n  /**\n   * @ngdoc input\n   * @name input[month]\n   *\n   * @description\n   * Input with month validation and transformation. In browsers that do not yet support\n   * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601\n   * month format (yyyy-MM), for example: `2009-01`.\n   *\n   * The model must always be a Date object, otherwise Angular will throw an error.\n   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.\n   * If the model is not set to the first of the month, the next view to model update will set it\n   * to the first of the month.\n   *\n   * The timezone to be used to read/write the `Date` instance in the model can be defined using\n   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.\n   *\n   * @param {string} ngModel Assignable angular expression to data-bind to.\n   * @param {string=} name Property name of the form under which the control is published.\n   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be\n   * a valid ISO month format (yyyy-MM).\n   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must\n   * be a valid ISO month format (yyyy-MM).\n   * @param {string=} required Sets `required` validation error key if the value is not entered.\n   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to\n   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of\n   *    `required` when you want to data-bind to the `required` attribute.\n   * @param {string=} ngChange Angular expression to be executed when input changes due to user\n   *    interaction with the input element.\n   *\n   * @example\n   <example name=\"month-input-directive\" module=\"monthExample\">\n   <file name=\"index.html\">\n     <script>\n      angular.module('monthExample', [])\n        .controller('DateController', ['$scope', function($scope) {\n          $scope.example = {\n            value: new Date(2013, 9, 1)\n          };\n        }]);\n     </script>\n     <form name=\"myForm\" ng-controller=\"DateController as dateCtrl\">\n       Pick a month in 2013:\n       <input id=\"exampleInput\" type=\"month\" name=\"input\" ng-model=\"example.value\"\n          placeholder=\"yyyy-MM\" min=\"2013-01\" max=\"2013-12\" required />\n       <span class=\"error\" ng-show=\"myForm.input.$error.required\">\n          Required!</span>\n       <span class=\"error\" ng-show=\"myForm.input.$error.month\">\n          Not a valid month!</span>\n       <tt>value = {{example.value | date: \"yyyy-MM\"}}</tt><br/>\n       <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>\n       <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>\n       <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>\n       <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>\n     </form>\n   </file>\n   <file name=\"protractor.js\" type=\"protractor\">\n      var value = element(by.binding('example.value | date: \"yyyy-MM\"'));\n      var valid = element(by.binding('myForm.input.$valid'));\n      var input = element(by.model('example.value'));\n\n      // currently protractor/webdriver does not support\n      // sending keys to all known HTML5 input controls\n      // for various browsers (https://github.com/angular/protractor/issues/562).\n      function setInput(val) {\n        // set the value of the element and force validation.\n        var scr = \"var ipt = document.getElementById('exampleInput'); \" +\n        \"ipt.value = '\" + val + \"';\" +\n        \"angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('\" + val + \"'); });\";\n        browser.executeScript(scr);\n      }\n\n      it('should initialize to model', function() {\n        expect(value.getText()).toContain('2013-10');\n        expect(valid.getText()).toContain('myForm.input.$valid = true');\n      });\n\n      it('should be invalid if empty', function() {\n        setInput('');\n        expect(value.getText()).toEqual('value =');\n        expect(valid.getText()).toContain('myForm.input.$valid = false');\n      });\n\n      it('should be invalid if over max', function() {\n        setInput('2015-01');\n        expect(value.getText()).toContain('');\n        expect(valid.getText()).toContain('myForm.input.$valid = false');\n      });\n   </file>\n   </example>\n   */\n  'month': createDateInputType('month', MONTH_REGEXP,\n     createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),\n     'yyyy-MM'),\n\n  /**\n   * @ngdoc input\n   * @name input[number]\n   *\n   * @description\n   * Text input with number validation and transformation. Sets the `number` validation\n   * error if not a valid number.\n   *\n   * The model must always be a number, otherwise Angular will throw an error.\n   *\n   * @param {string} ngModel Assignable angular expression to data-bind to.\n   * @param {string=} name Property name of the form under which the control is published.\n   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.\n   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.\n   * @param {string=} required Sets `required` validation error key if the value is not entered.\n   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to\n   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of\n   *    `required` when you want to data-bind to the `required` attribute.\n   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than\n   *    minlength.\n   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than\n   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of\n   *    any length.\n   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string\n   *    that contains the regular expression body that will be converted to a regular expression\n   *    as in the ngPattern directive.\n   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match\n   *    a RegExp found by evaluating the Angular expression given in the attribute value.\n   *    If the expression evaluates to a RegExp object then this is used directly.\n   *    If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`\n   *    characters. For instance, `\"abc\"` will be converted to `new RegExp('^abc$')`.\n   * @param {string=} ngChange Angular expression to be executed when input changes due to user\n   *    interaction with the input element.\n   *\n   * @example\n      <example name=\"number-input-directive\" module=\"numberExample\">\n        <file name=\"index.html\">\n         <script>\n           angular.module('numberExample', [])\n             .controller('ExampleController', ['$scope', function($scope) {\n               $scope.example = {\n                 value: 12\n               };\n             }]);\n         </script>\n         <form name=\"myForm\" ng-controller=\"ExampleController\">\n           Number: <input type=\"number\" name=\"input\" ng-model=\"example.value\"\n                          min=\"0\" max=\"99\" required>\n           <span class=\"error\" ng-show=\"myForm.input.$error.required\">\n             Required!</span>\n           <span class=\"error\" ng-show=\"myForm.input.$error.number\">\n             Not valid number!</span>\n           <tt>value = {{example.value}}</tt><br/>\n           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>\n           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>\n           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>\n           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>\n          </form>\n        </file>\n        <file name=\"protractor.js\" type=\"protractor\">\n          var value = element(by.binding('example.value'));\n          var valid = element(by.binding('myForm.input.$valid'));\n          var input = element(by.model('example.value'));\n\n          it('should initialize to model', function() {\n            expect(value.getText()).toContain('12');\n            expect(valid.getText()).toContain('true');\n          });\n\n          it('should be invalid if empty', function() {\n            input.clear();\n            input.sendKeys('');\n            expect(value.getText()).toEqual('value =');\n            expect(valid.getText()).toContain('false');\n          });\n\n          it('should be invalid if over max', function() {\n            input.clear();\n            input.sendKeys('123');\n            expect(value.getText()).toEqual('value =');\n            expect(valid.getText()).toContain('false');\n          });\n        </file>\n      </example>\n   */\n  'number': numberInputType,\n\n\n  /**\n   * @ngdoc input\n   * @name input[url]\n   *\n   * @description\n   * Text input with URL validation. Sets the `url` validation error key if the content is not a\n   * valid URL.\n   *\n   * <div class=\"alert alert-warning\">\n   * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex\n   * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify\n   * the built-in validators (see the {@link guide/forms Forms guide})\n   * </div>\n   *\n   * @param {string} ngModel Assignable angular expression to data-bind to.\n   * @param {string=} name Property name of the form under which the control is published.\n   * @param {string=} required Sets `required` validation error key if the value is not entered.\n   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to\n   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of\n   *    `required` when you want to data-bind to the `required` attribute.\n   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than\n   *    minlength.\n   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than\n   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of\n   *    any length.\n   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string\n   *    that contains the regular expression body that will be converted to a regular expression\n   *    as in the ngPattern directive.\n   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match\n   *    a RegExp found by evaluating the Angular expression given in the attribute value.\n   *    If the expression evaluates to a RegExp object then this is used directly.\n   *    If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`\n   *    characters. For instance, `\"abc\"` will be converted to `new RegExp('^abc$')`.\n   * @param {string=} ngChange Angular expression to be executed when input changes due to user\n   *    interaction with the input element.\n   *\n   * @example\n      <example name=\"url-input-directive\" module=\"urlExample\">\n        <file name=\"index.html\">\n         <script>\n           angular.module('urlExample', [])\n             .controller('ExampleController', ['$scope', function($scope) {\n               $scope.url = {\n                 text: 'http://google.com'\n               };\n             }]);\n         </script>\n         <form name=\"myForm\" ng-controller=\"ExampleController\">\n           URL: <input type=\"url\" name=\"input\" ng-model=\"url.text\" required>\n           <span class=\"error\" ng-show=\"myForm.input.$error.required\">\n             Required!</span>\n           <span class=\"error\" ng-show=\"myForm.input.$error.url\">\n             Not valid url!</span>\n           <tt>text = {{url.text}}</tt><br/>\n           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>\n           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>\n           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>\n           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>\n           <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>\n          </form>\n        </file>\n        <file name=\"protractor.js\" type=\"protractor\">\n          var text = element(by.binding('url.text'));\n          var valid = element(by.binding('myForm.input.$valid'));\n          var input = element(by.model('url.text'));\n\n          it('should initialize to model', function() {\n            expect(text.getText()).toContain('http://google.com');\n            expect(valid.getText()).toContain('true');\n          });\n\n          it('should be invalid if empty', function() {\n            input.clear();\n            input.sendKeys('');\n\n            expect(text.getText()).toEqual('text =');\n            expect(valid.getText()).toContain('false');\n          });\n\n          it('should be invalid if not url', function() {\n            input.clear();\n            input.sendKeys('box');\n\n            expect(valid.getText()).toContain('false');\n          });\n        </file>\n      </example>\n   */\n  'url': urlInputType,\n\n\n  /**\n   * @ngdoc input\n   * @name input[email]\n   *\n   * @description\n   * Text input with email validation. Sets the `email` validation error key if not a valid email\n   * address.\n   *\n   * <div class=\"alert alert-warning\">\n   * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex\n   * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can\n   * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})\n   * </div>\n   *\n   * @param {string} ngModel Assignable angular expression to data-bind to.\n   * @param {string=} name Property name of the form under which the control is published.\n   * @param {string=} required Sets `required` validation error key if the value is not entered.\n   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to\n   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of\n   *    `required` when you want to data-bind to the `required` attribute.\n   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than\n   *    minlength.\n   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than\n   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of\n   *    any length.\n   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string\n   *    that contains the regular expression body that will be converted to a regular expression\n   *    as in the ngPattern directive.\n   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match\n   *    a RegExp found by evaluating the Angular expression given in the attribute value.\n   *    If the expression evaluates to a RegExp object then this is used directly.\n   *    If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`\n   *    characters. For instance, `\"abc\"` will be converted to `new RegExp('^abc$')`.\n   * @param {string=} ngChange Angular expression to be executed when input changes due to user\n   *    interaction with the input element.\n   *\n   * @example\n      <example name=\"email-input-directive\" module=\"emailExample\">\n        <file name=\"index.html\">\n         <script>\n           angular.module('emailExample', [])\n             .controller('ExampleController', ['$scope', function($scope) {\n               $scope.email = {\n                 text: 'me@example.com'\n               };\n             }]);\n         </script>\n           <form name=\"myForm\" ng-controller=\"ExampleController\">\n             Email: <input type=\"email\" name=\"input\" ng-model=\"email.text\" required>\n             <span class=\"error\" ng-show=\"myForm.input.$error.required\">\n               Required!</span>\n             <span class=\"error\" ng-show=\"myForm.input.$error.email\">\n               Not valid email!</span>\n             <tt>text = {{email.text}}</tt><br/>\n             <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>\n             <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>\n             <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>\n             <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>\n             <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>\n           </form>\n         </file>\n        <file name=\"protractor.js\" type=\"protractor\">\n          var text = element(by.binding('email.text'));\n          var valid = element(by.binding('myForm.input.$valid'));\n          var input = element(by.model('email.text'));\n\n          it('should initialize to model', function() {\n            expect(text.getText()).toContain('me@example.com');\n            expect(valid.getText()).toContain('true');\n          });\n\n          it('should be invalid if empty', function() {\n            input.clear();\n            input.sendKeys('');\n            expect(text.getText()).toEqual('text =');\n            expect(valid.getText()).toContain('false');\n          });\n\n          it('should be invalid if not email', function() {\n            input.clear();\n            input.sendKeys('xxx');\n\n            expect(valid.getText()).toContain('false');\n          });\n        </file>\n      </example>\n   */\n  'email': emailInputType,\n\n\n  /**\n   * @ngdoc input\n   * @name input[radio]\n   *\n   * @description\n   * HTML radio button.\n   *\n   * @param {string} ngModel Assignable angular expression to data-bind to.\n   * @param {string} value The value to which the expression should be set when selected.\n   * @param {string=} name Property name of the form under which the control is published.\n   * @param {string=} ngChange Angular expression to be executed when input changes due to user\n   *    interaction with the input element.\n   * @param {string} ngValue Angular expression which sets the value to which the expression should\n   *    be set when selected.\n   *\n   * @example\n      <example name=\"radio-input-directive\" module=\"radioExample\">\n        <file name=\"index.html\">\n         <script>\n           angular.module('radioExample', [])\n             .controller('ExampleController', ['$scope', function($scope) {\n               $scope.color = {\n                 name: 'blue'\n               };\n               $scope.specialValue = {\n                 \"id\": \"12345\",\n                 \"value\": \"green\"\n               };\n             }]);\n         </script>\n         <form name=\"myForm\" ng-controller=\"ExampleController\">\n           <input type=\"radio\" ng-model=\"color.name\" value=\"red\">  Red <br/>\n           <input type=\"radio\" ng-model=\"color.name\" ng-value=\"specialValue\"> Green <br/>\n           <input type=\"radio\" ng-model=\"color.name\" value=\"blue\"> Blue <br/>\n           <tt>color = {{color.name | json}}</tt><br/>\n          </form>\n          Note that `ng-value=\"specialValue\"` sets radio item's value to be the value of `$scope.specialValue`.\n        </file>\n        <file name=\"protractor.js\" type=\"protractor\">\n          it('should change state', function() {\n            var color = element(by.binding('color.name'));\n\n            expect(color.getText()).toContain('blue');\n\n            element.all(by.model('color.name')).get(0).click();\n\n            expect(color.getText()).toContain('red');\n          });\n        </file>\n      </example>\n   */\n  'radio': radioInputType,\n\n\n  /**\n   * @ngdoc input\n   * @name input[checkbox]\n   *\n   * @description\n   * HTML checkbox.\n   *\n   * @param {string} ngModel Assignable angular expression to data-bind to.\n   * @param {string=} name Property name of the form under which the control is published.\n   * @param {expression=} ngTrueValue The value to which the expression should be set when selected.\n   * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.\n   * @param {string=} ngChange Angular expression to be executed when input changes due to user\n   *    interaction with the input element.\n   *\n   * @example\n      <example name=\"checkbox-input-directive\" module=\"checkboxExample\">\n        <file name=\"index.html\">\n         <script>\n           angular.module('checkboxExample', [])\n             .controller('ExampleController', ['$scope', function($scope) {\n               $scope.checkboxModel = {\n                value1 : true,\n                value2 : 'YES'\n              };\n             }]);\n         </script>\n         <form name=\"myForm\" ng-controller=\"ExampleController\">\n           Value1: <input type=\"checkbox\" ng-model=\"checkboxModel.value1\"> <br/>\n           Value2: <input type=\"checkbox\" ng-model=\"checkboxModel.value2\"\n                          ng-true-value=\"'YES'\" ng-false-value=\"'NO'\"> <br/>\n           <tt>value1 = {{checkboxModel.value1}}</tt><br/>\n           <tt>value2 = {{checkboxModel.value2}}</tt><br/>\n          </form>\n        </file>\n        <file name=\"protractor.js\" type=\"protractor\">\n          it('should change state', function() {\n            var value1 = element(by.binding('checkboxModel.value1'));\n            var value2 = element(by.binding('checkboxModel.value2'));\n\n            expect(value1.getText()).toContain('true');\n            expect(value2.getText()).toContain('YES');\n\n            element(by.model('checkboxModel.value1')).click();\n            element(by.model('checkboxModel.value2')).click();\n\n            expect(value1.getText()).toContain('false');\n            expect(value2.getText()).toContain('NO');\n          });\n        </file>\n      </example>\n   */\n  'checkbox': checkboxInputType,\n\n  'hidden': noop,\n  'button': noop,\n  'submit': noop,\n  'reset': noop,\n  'file': noop\n};\n\nfunction stringBasedInputType(ctrl) {\n  ctrl.$formatters.push(function(value) {\n    return ctrl.$isEmpty(value) ? value : value.toString();\n  });\n}\n\nfunction textInputType(scope, element, attr, ctrl, $sniffer, $browser) {\n  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);\n  stringBasedInputType(ctrl);\n}\n\nfunction baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {\n  var type = lowercase(element[0].type);\n\n  // In composition mode, users are still inputing intermediate text buffer,\n  // hold the listener until composition is done.\n  // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent\n  if (!$sniffer.android) {\n    var composing = false;\n\n    element.on('compositionstart', function(data) {\n      composing = true;\n    });\n\n    element.on('compositionend', function() {\n      composing = false;\n      listener();\n    });\n  }\n\n  var listener = function(ev) {\n    if (timeout) {\n      $browser.defer.cancel(timeout);\n      timeout = null;\n    }\n    if (composing) return;\n    var value = element.val(),\n        event = ev && ev.type;\n\n    // By default we will trim the value\n    // If the attribute ng-trim exists we will avoid trimming\n    // If input type is 'password', the value is never trimmed\n    if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {\n      value = trim(value);\n    }\n\n    // If a control is suffering from bad input (due to native validators), browsers discard its\n    // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the\n    // control's value is the same empty value twice in a row.\n    if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {\n      ctrl.$setViewValue(value, event);\n    }\n  };\n\n  // if the browser does support \"input\" event, we are fine - except on IE9 which doesn't fire the\n  // input event on backspace, delete or cut\n  if ($sniffer.hasEvent('input')) {\n    element.on('input', listener);\n  } else {\n    var timeout;\n\n    var deferListener = function(ev, input, origValue) {\n      if (!timeout) {\n        timeout = $browser.defer(function() {\n          timeout = null;\n          if (!input || input.value !== origValue) {\n            listener(ev);\n          }\n        });\n      }\n    };\n\n    element.on('keydown', function(event) {\n      var key = event.keyCode;\n\n      // ignore\n      //    command            modifiers                   arrows\n      if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;\n\n      deferListener(event, this, this.value);\n    });\n\n    // if user modifies input value using context menu in IE, we need \"paste\" and \"cut\" events to catch it\n    if ($sniffer.hasEvent('paste')) {\n      element.on('paste cut', deferListener);\n    }\n  }\n\n  // if user paste into input using mouse on older browser\n  // or form autocomplete on newer browser, we need \"change\" event to catch it\n  element.on('change', listener);\n\n  ctrl.$render = function() {\n    element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue);\n  };\n}\n\nfunction weekParser(isoWeek, existingDate) {\n  if (isDate(isoWeek)) {\n    return isoWeek;\n  }\n\n  if (isString(isoWeek)) {\n    WEEK_REGEXP.lastIndex = 0;\n    var parts = WEEK_REGEXP.exec(isoWeek);\n    if (parts) {\n      var year = +parts[1],\n          week = +parts[2],\n          hours = 0,\n          minutes = 0,\n          seconds = 0,\n          milliseconds = 0,\n          firstThurs = getFirstThursdayOfYear(year),\n          addDays = (week - 1) * 7;\n\n      if (existingDate) {\n        hours = existingDate.getHours();\n        minutes = existingDate.getMinutes();\n        seconds = existingDate.getSeconds();\n        milliseconds = existingDate.getMilliseconds();\n      }\n\n      return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);\n    }\n  }\n\n  return NaN;\n}\n\nfunction createDateParser(regexp, mapping) {\n  return function(iso, date) {\n    var parts, map;\n\n    if (isDate(iso)) {\n      return iso;\n    }\n\n    if (isString(iso)) {\n      // When a date is JSON'ified to wraps itself inside of an extra\n      // set of double quotes. This makes the date parsing code unable\n      // to match the date string and parse it as a date.\n      if (iso.charAt(0) == '\"' && iso.charAt(iso.length - 1) == '\"') {\n        iso = iso.substring(1, iso.length - 1);\n      }\n      if (ISO_DATE_REGEXP.test(iso)) {\n        return new Date(iso);\n      }\n      regexp.lastIndex = 0;\n      parts = regexp.exec(iso);\n\n      if (parts) {\n        parts.shift();\n        if (date) {\n          map = {\n            yyyy: date.getFullYear(),\n            MM: date.getMonth() + 1,\n            dd: date.getDate(),\n            HH: date.getHours(),\n            mm: date.getMinutes(),\n            ss: date.getSeconds(),\n            sss: date.getMilliseconds() / 1000\n          };\n        } else {\n          map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };\n        }\n\n        forEach(parts, function(part, index) {\n          if (index < mapping.length) {\n            map[mapping[index]] = +part;\n          }\n        });\n        return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);\n      }\n    }\n\n    return NaN;\n  };\n}\n\nfunction createDateInputType(type, regexp, parseDate, format) {\n  return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {\n    badInputChecker(scope, element, attr, ctrl);\n    baseInputType(scope, element, attr, ctrl, $sniffer, $browser);\n    var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;\n    var previousDate;\n\n    ctrl.$$parserName = type;\n    ctrl.$parsers.push(function(value) {\n      if (ctrl.$isEmpty(value)) return null;\n      if (regexp.test(value)) {\n        // Note: We cannot read ctrl.$modelValue, as there might be a different\n        // parser/formatter in the processing chain so that the model\n        // contains some different data format!\n        var parsedDate = parseDate(value, previousDate);\n        if (timezone === 'UTC') {\n          parsedDate.setMinutes(parsedDate.getMinutes() - parsedDate.getTimezoneOffset());\n        }\n        return parsedDate;\n      }\n      return undefined;\n    });\n\n    ctrl.$formatters.push(function(value) {\n      if (value && !isDate(value)) {\n        throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);\n      }\n      if (isValidDate(value)) {\n        previousDate = value;\n        if (previousDate && timezone === 'UTC') {\n          var timezoneOffset = 60000 * previousDate.getTimezoneOffset();\n          previousDate = new Date(previousDate.getTime() + timezoneOffset);\n        }\n        return $filter('date')(value, format, timezone);\n      } else {\n        previousDate = null;\n        return '';\n      }\n    });\n\n    if (isDefined(attr.min) || attr.ngMin) {\n      var minVal;\n      ctrl.$validators.min = function(value) {\n        return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;\n      };\n      attr.$observe('min', function(val) {\n        minVal = parseObservedDateValue(val);\n        ctrl.$validate();\n      });\n    }\n\n    if (isDefined(attr.max) || attr.ngMax) {\n      var maxVal;\n      ctrl.$validators.max = function(value) {\n        return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;\n      };\n      attr.$observe('max', function(val) {\n        maxVal = parseObservedDateValue(val);\n        ctrl.$validate();\n      });\n    }\n\n    function isValidDate(value) {\n      // Invalid Date: getTime() returns NaN\n      return value && !(value.getTime && value.getTime() !== value.getTime());\n    }\n\n    function parseObservedDateValue(val) {\n      return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined;\n    }\n  };\n}\n\nfunction badInputChecker(scope, element, attr, ctrl) {\n  var node = element[0];\n  var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);\n  if (nativeValidation) {\n    ctrl.$parsers.push(function(value) {\n      var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};\n      // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):\n      // - also sets validity.badInput (should only be validity.typeMismatch).\n      // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)\n      // - can ignore this case as we can still read out the erroneous email...\n      return validity.badInput && !validity.typeMismatch ? undefined : value;\n    });\n  }\n}\n\nfunction numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {\n  badInputChecker(scope, element, attr, ctrl);\n  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);\n\n  ctrl.$$parserName = 'number';\n  ctrl.$parsers.push(function(value) {\n    if (ctrl.$isEmpty(value))      return null;\n    if (NUMBER_REGEXP.test(value)) return parseFloat(value);\n    return undefined;\n  });\n\n  ctrl.$formatters.push(function(value) {\n    if (!ctrl.$isEmpty(value)) {\n      if (!isNumber(value)) {\n        throw $ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);\n      }\n      value = value.toString();\n    }\n    return value;\n  });\n\n  if (isDefined(attr.min) || attr.ngMin) {\n    var minVal;\n    ctrl.$validators.min = function(value) {\n      return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;\n    };\n\n    attr.$observe('min', function(val) {\n      if (isDefined(val) && !isNumber(val)) {\n        val = parseFloat(val, 10);\n      }\n      minVal = isNumber(val) && !isNaN(val) ? val : undefined;\n      // TODO(matsko): implement validateLater to reduce number of validations\n      ctrl.$validate();\n    });\n  }\n\n  if (isDefined(attr.max) || attr.ngMax) {\n    var maxVal;\n    ctrl.$validators.max = function(value) {\n      return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;\n    };\n\n    attr.$observe('max', function(val) {\n      if (isDefined(val) && !isNumber(val)) {\n        val = parseFloat(val, 10);\n      }\n      maxVal = isNumber(val) && !isNaN(val) ? val : undefined;\n      // TODO(matsko): implement validateLater to reduce number of validations\n      ctrl.$validate();\n    });\n  }\n}\n\nfunction urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {\n  // Note: no badInputChecker here by purpose as `url` is only a validation\n  // in browsers, i.e. we can always read out input.value even if it is not valid!\n  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);\n  stringBasedInputType(ctrl);\n\n  ctrl.$$parserName = 'url';\n  ctrl.$validators.url = function(modelValue, viewValue) {\n    var value = modelValue || viewValue;\n    return ctrl.$isEmpty(value) || URL_REGEXP.test(value);\n  };\n}\n\nfunction emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {\n  // Note: no badInputChecker here by purpose as `url` is only a validation\n  // in browsers, i.e. we can always read out input.value even if it is not valid!\n  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);\n  stringBasedInputType(ctrl);\n\n  ctrl.$$parserName = 'email';\n  ctrl.$validators.email = function(modelValue, viewValue) {\n    var value = modelValue || viewValue;\n    return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);\n  };\n}\n\nfunction radioInputType(scope, element, attr, ctrl) {\n  // make the name unique, if not defined\n  if (isUndefined(attr.name)) {\n    element.attr('name', nextUid());\n  }\n\n  var listener = function(ev) {\n    if (element[0].checked) {\n      ctrl.$setViewValue(attr.value, ev && ev.type);\n    }\n  };\n\n  element.on('click', listener);\n\n  ctrl.$render = function() {\n    var value = attr.value;\n    element[0].checked = (value == ctrl.$viewValue);\n  };\n\n  attr.$observe('value', ctrl.$render);\n}\n\nfunction parseConstantExpr($parse, context, name, expression, fallback) {\n  var parseFn;\n  if (isDefined(expression)) {\n    parseFn = $parse(expression);\n    if (!parseFn.constant) {\n      throw minErr('ngModel')('constexpr', 'Expected constant expression for `{0}`, but saw ' +\n                                   '`{1}`.', name, expression);\n    }\n    return parseFn(context);\n  }\n  return fallback;\n}\n\nfunction checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {\n  var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);\n  var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);\n\n  var listener = function(ev) {\n    ctrl.$setViewValue(element[0].checked, ev && ev.type);\n  };\n\n  element.on('click', listener);\n\n  ctrl.$render = function() {\n    element[0].checked = ctrl.$viewValue;\n  };\n\n  // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`\n  // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert\n  // it to a boolean.\n  ctrl.$isEmpty = function(value) {\n    return value === false;\n  };\n\n  ctrl.$formatters.push(function(value) {\n    return equals(value, trueValue);\n  });\n\n  ctrl.$parsers.push(function(value) {\n    return value ? trueValue : falseValue;\n  });\n}\n\n\n/**\n * @ngdoc directive\n * @name textarea\n * @restrict E\n *\n * @description\n * HTML textarea element control with angular data-binding. The data-binding and validation\n * properties of this element are exactly the same as those of the\n * {@link ng.directive:input input element}.\n *\n * @param {string} ngModel Assignable angular expression to data-bind to.\n * @param {string=} name Property name of the form under which the control is published.\n * @param {string=} required Sets `required` validation error key if the value is not entered.\n * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to\n *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of\n *    `required` when you want to data-bind to the `required` attribute.\n * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than\n *    minlength.\n * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than\n *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any\n *    length.\n * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the\n *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for\n *    patterns defined as scope expressions.\n * @param {string=} ngChange Angular expression to be executed when input changes due to user\n *    interaction with the input element.\n * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.\n */\n\n\n/**\n * @ngdoc directive\n * @name input\n * @restrict E\n *\n * @description\n * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,\n * input state control, and validation.\n * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.\n *\n * <div class=\"alert alert-warning\">\n * **Note:** Not every feature offered is available for all input types.\n * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.\n * </div>\n *\n * @param {string} ngModel Assignable angular expression to data-bind to.\n * @param {string=} name Property name of the form under which the control is published.\n * @param {string=} required Sets `required` validation error key if the value is not entered.\n * @param {boolean=} ngRequired Sets `required` attribute if set to true\n * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than\n *    minlength.\n * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than\n *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any\n *    length.\n * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the\n *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for\n *    patterns defined as scope expressions.\n * @param {string=} ngChange Angular expression to be executed when input changes due to user\n *    interaction with the input element.\n * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.\n *    This parameter is ignored for input[type=password] controls, which will never trim the\n *    input.\n *\n * @example\n    <example name=\"input-directive\" module=\"inputExample\">\n      <file name=\"index.html\">\n       <script>\n          angular.module('inputExample', [])\n            .controller('ExampleController', ['$scope', function($scope) {\n              $scope.user = {name: 'guest', last: 'visitor'};\n            }]);\n       </script>\n       <div ng-controller=\"ExampleController\">\n         <form name=\"myForm\">\n           User name: <input type=\"text\" name=\"userName\" ng-model=\"user.name\" required>\n           <span class=\"error\" ng-show=\"myForm.userName.$error.required\">\n             Required!</span><br>\n           Last name: <input type=\"text\" name=\"lastName\" ng-model=\"user.last\"\n             ng-minlength=\"3\" ng-maxlength=\"10\">\n           <span class=\"error\" ng-show=\"myForm.lastName.$error.minlength\">\n             Too short!</span>\n           <span class=\"error\" ng-show=\"myForm.lastName.$error.maxlength\">\n             Too long!</span><br>\n         </form>\n         <hr>\n         <tt>user = {{user}}</tt><br/>\n         <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br>\n         <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br>\n         <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br>\n         <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br>\n         <tt>myForm.$valid = {{myForm.$valid}}</tt><br>\n         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>\n         <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br>\n         <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br>\n       </div>\n      </file>\n      <file name=\"protractor.js\" type=\"protractor\">\n        var user = element(by.exactBinding('user'));\n        var userNameValid = element(by.binding('myForm.userName.$valid'));\n        var lastNameValid = element(by.binding('myForm.lastName.$valid'));\n        var lastNameError = element(by.binding('myForm.lastName.$error'));\n        var formValid = element(by.binding('myForm.$valid'));\n        var userNameInput = element(by.model('user.name'));\n        var userLastInput = element(by.model('user.last'));\n\n        it('should initialize to model', function() {\n          expect(user.getText()).toContain('{\"name\":\"guest\",\"last\":\"visitor\"}');\n          expect(userNameValid.getText()).toContain('true');\n          expect(formValid.getText()).toContain('true');\n        });\n\n        it('should be invalid if empty when required', function() {\n          userNameInput.clear();\n          userNameInput.sendKeys('');\n\n          expect(user.getText()).toContain('{\"last\":\"visitor\"}');\n          expect(userNameValid.getText()).toContain('false');\n          expect(formValid.getText()).toContain('false');\n        });\n\n        it('should be valid if empty when min length is set', function() {\n          userLastInput.clear();\n          userLastInput.sendKeys('');\n\n          expect(user.getText()).toContain('{\"name\":\"guest\",\"last\":\"\"}');\n          expect(lastNameValid.getText()).toContain('true');\n          expect(formValid.getText()).toContain('true');\n        });\n\n        it('should be invalid if less than required min length', function() {\n          userLastInput.clear();\n          userLastInput.sendKeys('xx');\n\n          expect(user.getText()).toContain('{\"name\":\"guest\"}');\n          expect(lastNameValid.getText()).toContain('false');\n          expect(lastNameError.getText()).toContain('minlength');\n          expect(formValid.getText()).toContain('false');\n        });\n\n        it('should be invalid if longer than max length', function() {\n          userLastInput.clear();\n          userLastInput.sendKeys('some ridiculously long name');\n\n          expect(user.getText()).toContain('{\"name\":\"guest\"}');\n          expect(lastNameValid.getText()).toContain('false');\n          expect(lastNameError.getText()).toContain('maxlength');\n          expect(formValid.getText()).toContain('false');\n        });\n      </file>\n    </example>\n */\nvar inputDirective = ['$browser', '$sniffer', '$filter', '$parse',\n    function($browser, $sniffer, $filter, $parse) {\n  return {\n    restrict: 'E',\n    require: ['?ngModel'],\n    link: {\n      pre: function(scope, element, attr, ctrls) {\n        if (ctrls[0]) {\n          (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,\n                                                              $browser, $filter, $parse);\n        }\n      }\n    }\n  };\n}];\n\n\n\nvar CONSTANT_VALUE_REGEXP = /^(true|false|\\d+)$/;\n/**\n * @ngdoc directive\n * @name ngValue\n *\n * @description\n * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},\n * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to\n * the bound value.\n *\n * `ngValue` is useful when dynamically generating lists of radio buttons using\n * {@link ngRepeat `ngRepeat`}, as shown below.\n *\n * Likewise, `ngValue` can be used to generate `<option>` elements for\n * the {@link select `select`} element. In that case however, only strings are supported\n * for the `value `attribute, so the resulting `ngModel` will always be a string.\n * Support for `select` models with non-string values is available via `ngOptions`.\n *\n * @element input\n * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute\n *   of the `input` element\n *\n * @example\n    <example name=\"ngValue-directive\" module=\"valueExample\">\n      <file name=\"index.html\">\n       <script>\n          angular.module('valueExample', [])\n            .controller('ExampleController', ['$scope', function($scope) {\n              $scope.names = ['pizza', 'unicorns', 'robots'];\n              $scope.my = { favorite: 'unicorns' };\n            }]);\n       </script>\n        <form ng-controller=\"ExampleController\">\n          <h2>Which is your favorite?</h2>\n            <label ng-repeat=\"name in names\" for=\"{{name}}\">\n              {{name}}\n              <input type=\"radio\"\n                     ng-model=\"my.favorite\"\n                     ng-value=\"name\"\n                     id=\"{{name}}\"\n                     name=\"favorite\">\n            </label>\n          <div>You chose {{my.favorite}}</div>\n        </form>\n      </file>\n      <file name=\"protractor.js\" type=\"protractor\">\n        var favorite = element(by.binding('my.favorite'));\n\n        it('should initialize to model', function() {\n          expect(favorite.getText()).toContain('unicorns');\n        });\n        it('should bind the values to the inputs', function() {\n          element.all(by.model('my.favorite')).get(0).click();\n          expect(favorite.getText()).toContain('pizza');\n        });\n      </file>\n    </example>\n */\nvar ngValueDirective = function() {\n  return {\n    restrict: 'A',\n    priority: 100,\n    compile: function(tpl, tplAttr) {\n      if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {\n        return function ngValueConstantLink(scope, elm, attr) {\n          attr.$set('value', scope.$eval(attr.ngValue));\n        };\n      } else {\n        return function ngValueLink(scope, elm, attr) {\n          scope.$watch(attr.ngValue, function valueWatchAction(value) {\n            attr.$set('value', value);\n          });\n        };\n      }\n    }\n  };\n};\n\n/**\n * @ngdoc directive\n * @name ngBind\n * @restrict AC\n *\n * @description\n * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element\n * with the value of a given expression, and to update the text content when the value of that\n * expression changes.\n *\n * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like\n * `{{ expression }}` which is similar but less verbose.\n *\n * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily\n * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an\n * element attribute, it makes the bindings invisible to the user while the page is loading.\n *\n * An alternative solution to this problem would be using the\n * {@link ng.directive:ngCloak ngCloak} directive.\n *\n *\n * @element ANY\n * @param {expression} ngBind {@link guide/expression Expression} to evaluate.\n *\n * @example\n * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.\n   <example module=\"bindExample\">\n     <file name=\"index.html\">\n       <script>\n         angular.module('bindExample', [])\n           .controller('ExampleController', ['$scope', function($scope) {\n             $scope.name = 'Whirled';\n           }]);\n       </script>\n       <div ng-controller=\"ExampleController\">\n         Enter name: <input type=\"text\" ng-model=\"name\"><br>\n         Hello <span ng-bind=\"name\"></span>!\n       </div>\n     </file>\n     <file name=\"protractor.js\" type=\"protractor\">\n       it('should check ng-bind', function() {\n         var nameInput = element(by.model('name'));\n\n         expect(element(by.binding('name')).getText()).toBe('Whirled');\n         nameInput.clear();\n         nameInput.sendKeys('world');\n         expect(element(by.binding('name')).getText()).toBe('world');\n       });\n     </file>\n   </example>\n */\nvar ngBindDirective = ['$compile', function($compile) {\n  return {\n    restrict: 'AC',\n    compile: function ngBindCompile(templateElement) {\n      $compile.$$addBindingClass(templateElement);\n      return function ngBindLink(scope, element, attr) {\n        $compile.$$addBindingInfo(element, attr.ngBind);\n        element = element[0];\n        scope.$watch(attr.ngBind, function ngBindWatchAction(value) {\n          element.textContent = value === undefined ? '' : value;\n        });\n      };\n    }\n  };\n}];\n\n\n/**\n * @ngdoc directive\n * @name ngBindTemplate\n *\n * @description\n * The `ngBindTemplate` directive specifies that the element\n * text content should be replaced with the interpolation of the template\n * in the `ngBindTemplate` attribute.\n * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`\n * expressions. This directive is needed since some HTML elements\n * (such as TITLE and OPTION) cannot contain SPAN elements.\n *\n * @element ANY\n * @param {string} ngBindTemplate template of form\n *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.\n *\n * @example\n * Try it here: enter text in text box and watch the greeting change.\n   <example module=\"bindExample\">\n     <file name=\"index.html\">\n       <script>\n         angular.module('bindExample', [])\n           .controller('ExampleController', ['$scope', function($scope) {\n             $scope.salutation = 'Hello';\n             $scope.name = 'World';\n           }]);\n       </script>\n       <div ng-controller=\"ExampleController\">\n        Salutation: <input type=\"text\" ng-model=\"salutation\"><br>\n        Name: <input type=\"text\" ng-model=\"name\"><br>\n        <pre ng-bind-template=\"{{salutation}} {{name}}!\"></pre>\n       </div>\n     </file>\n     <file name=\"protractor.js\" type=\"protractor\">\n       it('should check ng-bind', function() {\n         var salutationElem = element(by.binding('salutation'));\n         var salutationInput = element(by.model('salutation'));\n         var nameInput = element(by.model('name'));\n\n         expect(salutationElem.getText()).toBe('Hello World!');\n\n         salutationInput.clear();\n         salutationInput.sendKeys('Greetings');\n         nameInput.clear();\n         nameInput.sendKeys('user');\n\n         expect(salutationElem.getText()).toBe('Greetings user!');\n       });\n     </file>\n   </example>\n */\nvar ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {\n  return {\n    compile: function ngBindTemplateCompile(templateElement) {\n      $compile.$$addBindingClass(templateElement);\n      return function ngBindTemplateLink(scope, element, attr) {\n        var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));\n        $compile.$$addBindingInfo(element, interpolateFn.expressions);\n        element = element[0];\n        attr.$observe('ngBindTemplate', function(value) {\n          element.textContent = value === undefined ? '' : value;\n        });\n      };\n    }\n  };\n}];\n\n\n/**\n * @ngdoc directive\n * @name ngBindHtml\n *\n * @description\n * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,\n * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.\n * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link\n * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}\n * in your module's dependencies, you need to include \"angular-sanitize.js\" in your application.\n *\n * You may also bypass sanitization for values you know are safe. To do so, bind to\n * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}.  See the example\n * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.\n *\n * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you\n * will have an exception (instead of an exploit.)\n *\n * @element ANY\n * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.\n *\n * @example\n\n   <example module=\"bindHtmlExample\" deps=\"angular-sanitize.js\">\n     <file name=\"index.html\">\n       <div ng-controller=\"ExampleController\">\n        <p ng-bind-html=\"myHTML\"></p>\n       </div>\n     </file>\n\n     <file name=\"script.js\">\n       angular.module('bindHtmlExample', ['ngSanitize'])\n         .controller('ExampleController', ['$scope', function($scope) {\n           $scope.myHTML =\n              'I am an <code>HTML</code>string with ' +\n              '<a href=\"#\">links!</a> and other <em>stuff</em>';\n         }]);\n     </file>\n\n     <file name=\"protractor.js\" type=\"protractor\">\n       it('should check ng-bind-html', function() {\n         expect(element(by.binding('myHTML')).getText()).toBe(\n             'I am an HTMLstring with links! and other stuff');\n       });\n     </file>\n   </example>\n */\nvar ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {\n  return {\n    restrict: 'A',\n    compile: function ngBindHtmlCompile(tElement, tAttrs) {\n      var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);\n      var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {\n        return (value || '').toString();\n      });\n      $compile.$$addBindingClass(tElement);\n\n      return function ngBindHtmlLink(scope, element, attr) {\n        $compile.$$addBindingInfo(element, attr.ngBindHtml);\n\n        scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {\n          // we re-evaluate the expr because we want a TrustedValueHolderType\n          // for $sce, not a string\n          element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');\n        });\n      };\n    }\n  };\n}];\n\n/**\n * @ngdoc directive\n * @name ngChange\n *\n * @description\n * Evaluate the given expression when the user changes the input.\n * The expression is evaluated immediately, unlike the JavaScript onchange event\n * which only triggers at the end of a change (usually, when the user leaves the\n * form element or presses the return key).\n *\n * The `ngChange` expression is only evaluated when a change in the input value causes\n * a new value to be committed to the model.\n *\n * It will not be evaluated:\n * * if the value returned from the `$parsers` transformation pipeline has not changed\n * * if the input has continued to be invalid since the model will stay `null`\n * * if the model is changed programmatically and not by a change to the input value\n *\n *\n * Note, this directive requires `ngModel` to be present.\n *\n * @element input\n * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change\n * in input value.\n *\n * @example\n * <example name=\"ngChange-directive\" module=\"changeExample\">\n *   <file name=\"index.html\">\n *     <script>\n *       angular.module('changeExample', [])\n *         .controller('ExampleController', ['$scope', function($scope) {\n *           $scope.counter = 0;\n *           $scope.change = function() {\n *             $scope.counter++;\n *           };\n *         }]);\n *     </script>\n *     <div ng-controller=\"ExampleController\">\n *       <input type=\"checkbox\" ng-model=\"confirmed\" ng-change=\"change()\" id=\"ng-change-example1\" />\n *       <input type=\"checkbox\" ng-model=\"confirmed\" id=\"ng-change-example2\" />\n *       <label for=\"ng-change-example2\">Confirmed</label><br />\n *       <tt>debug = {{confirmed}}</tt><br/>\n *       <tt>counter = {{counter}}</tt><br/>\n *     </div>\n *   </file>\n *   <file name=\"protractor.js\" type=\"protractor\">\n *     var counter = element(by.binding('counter'));\n *     var debug = element(by.binding('confirmed'));\n *\n *     it('should evaluate the expression if changing from view', function() {\n *       expect(counter.getText()).toContain('0');\n *\n *       element(by.id('ng-change-example1')).click();\n *\n *       expect(counter.getText()).toContain('1');\n *       expect(debug.getText()).toContain('true');\n *     });\n *\n *     it('should not evaluate the expression if changing from model', function() {\n *       element(by.id('ng-change-example2')).click();\n\n *       expect(counter.getText()).toContain('0');\n *       expect(debug.getText()).toContain('true');\n *     });\n *   </file>\n * </example>\n */\nvar ngChangeDirective = valueFn({\n  restrict: 'A',\n  require: 'ngModel',\n  link: function(scope, element, attr, ctrl) {\n    ctrl.$viewChangeListeners.push(function() {\n      scope.$eval(attr.ngChange);\n    });\n  }\n});\n\nfunction classDirective(name, selector) {\n  name = 'ngClass' + name;\n  return ['$animate', function($animate) {\n    return {\n      restrict: 'AC',\n      link: function(scope, element, attr) {\n        var oldVal;\n\n        scope.$watch(attr[name], ngClassWatchAction, true);\n\n        attr.$observe('class', function(value) {\n          ngClassWatchAction(scope.$eval(attr[name]));\n        });\n\n\n        if (name !== 'ngClass') {\n          scope.$watch('$index', function($index, old$index) {\n            // jshint bitwise: false\n            var mod = $index & 1;\n            if (mod !== (old$index & 1)) {\n              var classes = arrayClasses(scope.$eval(attr[name]));\n              mod === selector ?\n                addClasses(classes) :\n                removeClasses(classes);\n            }\n          });\n        }\n\n        function addClasses(classes) {\n          var newClasses = digestClassCounts(classes, 1);\n          attr.$addClass(newClasses);\n        }\n\n        function removeClasses(classes) {\n          var newClasses = digestClassCounts(classes, -1);\n          attr.$removeClass(newClasses);\n        }\n\n        function digestClassCounts(classes, count) {\n          var classCounts = element.data('$classCounts') || {};\n          var classesToUpdate = [];\n          forEach(classes, function(className) {\n            if (count > 0 || classCounts[className]) {\n              classCounts[className] = (classCounts[className] || 0) + count;\n              if (classCounts[className] === +(count > 0)) {\n                classesToUpdate.push(className);\n              }\n            }\n          });\n          element.data('$classCounts', classCounts);\n          return classesToUpdate.join(' ');\n        }\n\n        function updateClasses(oldClasses, newClasses) {\n          var toAdd = arrayDifference(newClasses, oldClasses);\n          var toRemove = arrayDifference(oldClasses, newClasses);\n          toAdd = digestClassCounts(toAdd, 1);\n          toRemove = digestClassCounts(toRemove, -1);\n          if (toAdd && toAdd.length) {\n            $animate.addClass(element, toAdd);\n          }\n          if (toRemove && toRemove.length) {\n            $animate.removeClass(element, toRemove);\n          }\n        }\n\n        function ngClassWatchAction(newVal) {\n          if (selector === true || scope.$index % 2 === selector) {\n            var newClasses = arrayClasses(newVal || []);\n            if (!oldVal) {\n              addClasses(newClasses);\n            } else if (!equals(newVal,oldVal)) {\n              var oldClasses = arrayClasses(oldVal);\n              updateClasses(oldClasses, newClasses);\n            }\n          }\n          oldVal = shallowCopy(newVal);\n        }\n      }\n    };\n\n    function arrayDifference(tokens1, tokens2) {\n      var values = [];\n\n      outer:\n      for (var i = 0; i < tokens1.length; i++) {\n        var token = tokens1[i];\n        for (var j = 0; j < tokens2.length; j++) {\n          if (token == tokens2[j]) continue outer;\n        }\n        values.push(token);\n      }\n      return values;\n    }\n\n    function arrayClasses(classVal) {\n      if (isArray(classVal)) {\n        return classVal;\n      } else if (isString(classVal)) {\n        return classVal.split(' ');\n      } else if (isObject(classVal)) {\n        var classes = [];\n        forEach(classVal, function(v, k) {\n          if (v) {\n            classes = classes.concat(k.split(' '));\n          }\n        });\n        return classes;\n      }\n      return classVal;\n    }\n  }];\n}\n\n/**\n * @ngdoc directive\n * @name ngClass\n * @restrict AC\n *\n * @description\n * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding\n * an expression that represents all classes to be added.\n *\n * The directive operates in three different ways, depending on which of three types the expression\n * evaluates to:\n *\n * 1. If the expression evaluates to a string, the string should be one or more space-delimited class\n * names.\n *\n * 2. If the expression evaluates to an array, each element of the array should be a string that is\n * one or more space-delimited class names.\n *\n * 3. If the expression evaluates to an object, then for each key-value pair of the\n * object with a truthy value the corresponding key is used as a class name.\n *\n * The directive won't add duplicate classes if a particular class was already set.\n *\n * When the expression changes, the previously added classes are removed and only then the\n * new classes are added.\n *\n * @animations\n * **add** - happens just before the class is applied to the elements\n *\n * **remove** - happens just before the class is removed from the element\n *\n * @element ANY\n * @param {expression} ngClass {@link guide/expression Expression} to eval. The result\n *   of the evaluation can be a string representing space delimited class\n *   names, an array, or a map of class names to boolean values. In the case of a map, the\n *   names of the properties whose values are truthy will be added as css classes to the\n *   element.\n *\n * @example Example that demonstrates basic bindings via ngClass directive.\n   <example>\n     <file name=\"index.html\">\n       <p ng-class=\"{strike: deleted, bold: important, red: error}\">Map Syntax Example</p>\n       <input type=\"checkbox\" ng-model=\"deleted\"> deleted (apply \"strike\" class)<br>\n       <input type=\"checkbox\" ng-model=\"important\"> important (apply \"bold\" class)<br>\n       <input type=\"checkbox\" ng-model=\"error\"> error (apply \"red\" class)\n       <hr>\n       <p ng-class=\"style\">Using String Syntax</p>\n       <input type=\"text\" ng-model=\"style\" placeholder=\"Type: bold strike red\">\n       <hr>\n       <p ng-class=\"[style1, style2, style3]\">Using Array Syntax</p>\n       <input ng-model=\"style1\" placeholder=\"Type: bold, strike or red\"><br>\n       <input ng-model=\"style2\" placeholder=\"Type: bold, strike or red\"><br>\n       <input ng-model=\"style3\" placeholder=\"Type: bold, strike or red\"><br>\n     </file>\n     <file name=\"style.css\">\n       .strike {\n         text-decoration: line-through;\n       }\n       .bold {\n           font-weight: bold;\n       }\n       .red {\n           color: red;\n       }\n     </file>\n     <file name=\"protractor.js\" type=\"protractor\">\n       var ps = element.all(by.css('p'));\n\n       it('should let you toggle the class', function() {\n\n         expect(ps.first().getAttribute('class')).not.toMatch(/bold/);\n         expect(ps.first().getAttribute('class')).not.toMatch(/red/);\n\n         element(by.model('important')).click();\n         expect(ps.first().getAttribute('class')).toMatch(/bold/);\n\n         element(by.model('error')).click();\n         expect(ps.first().getAttribute('class')).toMatch(/red/);\n       });\n\n       it('should let you toggle string example', function() {\n         expect(ps.get(1).getAttribute('class')).toBe('');\n         element(by.model('style')).clear();\n         element(by.model('style')).sendKeys('red');\n         expect(ps.get(1).getAttribute('class')).toBe('red');\n       });\n\n       it('array example should have 3 classes', function() {\n         expect(ps.last().getAttribute('class')).toBe('');\n         element(by.model('style1')).sendKeys('bold');\n         element(by.model('style2')).sendKeys('strike');\n         element(by.model('style3')).sendKeys('red');\n         expect(ps.last().getAttribute('class')).toBe('bold strike red');\n       });\n     </file>\n   </example>\n\n   ## Animations\n\n   The example below demonstrates how to perform animations using ngClass.\n\n   <example module=\"ngAnimate\" deps=\"angular-animate.js\" animations=\"true\">\n     <file name=\"index.html\">\n      <input id=\"setbtn\" type=\"button\" value=\"set\" ng-click=\"myVar='my-class'\">\n      <input id=\"clearbtn\" type=\"button\" value=\"clear\" ng-click=\"myVar=''\">\n      <br>\n      <span class=\"base-class\" ng-class=\"myVar\">Sample Text</span>\n     </file>\n     <file name=\"style.css\">\n       .base-class {\n         -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;\n         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;\n       }\n\n       .base-class.my-class {\n         color: red;\n         font-size:3em;\n       }\n     </file>\n     <file name=\"protractor.js\" type=\"protractor\">\n       it('should check ng-class', function() {\n         expect(element(by.css('.base-class')).getAttribute('class')).not.\n           toMatch(/my-class/);\n\n         element(by.id('setbtn')).click();\n\n         expect(element(by.css('.base-class')).getAttribute('class')).\n           toMatch(/my-class/);\n\n         element(by.id('clearbtn')).click();\n\n         expect(element(by.css('.base-class')).getAttribute('class')).not.\n           toMatch(/my-class/);\n       });\n     </file>\n   </example>\n\n\n   ## ngClass and pre-existing CSS3 Transitions/Animations\n   The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.\n   Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder\n   any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure\n   to view the step by step details of {@link ng.$animate#addClass $animate.addClass} and\n   {@link ng.$animate#removeClass $animate.removeClass}.\n */\nvar ngClassDirective = classDirective('', true);\n\n/**\n * @ngdoc directive\n * @name ngClassOdd\n * @restrict AC\n *\n * @description\n * The `ngClassOdd` and `ngClassEven` directives work exactly as\n * {@link ng.directive:ngClass ngClass}, except they work in\n * conjunction with `ngRepeat` and take effect only on odd (even) rows.\n *\n * This directive can be applied only within the scope of an\n * {@link ng.directive:ngRepeat ngRepeat}.\n *\n * @element ANY\n * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result\n *   of the evaluation can be a string representing space delimited class names or an array.\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n        <ol ng-init=\"names=['John', 'Mary', 'Cate', 'Suz']\">\n          <li ng-repeat=\"name in names\">\n           <span ng-class-odd=\"'odd'\" ng-class-even=\"'even'\">\n             {{name}}\n           </span>\n          </li>\n        </ol>\n     </file>\n     <file name=\"style.css\">\n       .odd {\n         color: red;\n       }\n       .even {\n         color: blue;\n       }\n     </file>\n     <file name=\"protractor.js\" type=\"protractor\">\n       it('should check ng-class-odd and ng-class-even', function() {\n         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).\n           toMatch(/odd/);\n         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).\n           toMatch(/even/);\n       });\n     </file>\n   </example>\n */\nvar ngClassOddDirective = classDirective('Odd', 0);\n\n/**\n * @ngdoc directive\n * @name ngClassEven\n * @restrict AC\n *\n * @description\n * The `ngClassOdd` and `ngClassEven` directives work exactly as\n * {@link ng.directive:ngClass ngClass}, except they work in\n * conjunction with `ngRepeat` and take effect only on odd (even) rows.\n *\n * This directive can be applied only within the scope of an\n * {@link ng.directive:ngRepeat ngRepeat}.\n *\n * @element ANY\n * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The\n *   result of the evaluation can be a string representing space delimited class names or an array.\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n        <ol ng-init=\"names=['John', 'Mary', 'Cate', 'Suz']\">\n          <li ng-repeat=\"name in names\">\n           <span ng-class-odd=\"'odd'\" ng-class-even=\"'even'\">\n             {{name}} &nbsp; &nbsp; &nbsp;\n           </span>\n          </li>\n        </ol>\n     </file>\n     <file name=\"style.css\">\n       .odd {\n         color: red;\n       }\n       .even {\n         color: blue;\n       }\n     </file>\n     <file name=\"protractor.js\" type=\"protractor\">\n       it('should check ng-class-odd and ng-class-even', function() {\n         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).\n           toMatch(/odd/);\n         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).\n           toMatch(/even/);\n       });\n     </file>\n   </example>\n */\nvar ngClassEvenDirective = classDirective('Even', 1);\n\n/**\n * @ngdoc directive\n * @name ngCloak\n * @restrict AC\n *\n * @description\n * The `ngCloak` directive is used to prevent the Angular html template from being briefly\n * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this\n * directive to avoid the undesirable flicker effect caused by the html template display.\n *\n * The directive can be applied to the `<body>` element, but the preferred usage is to apply\n * multiple `ngCloak` directives to small portions of the page to permit progressive rendering\n * of the browser view.\n *\n * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and\n * `angular.min.js`.\n * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).\n *\n * ```css\n * [ng\\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {\n *   display: none !important;\n * }\n * ```\n *\n * When this css rule is loaded by the browser, all html elements (including their children) that\n * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive\n * during the compilation of the template it deletes the `ngCloak` element attribute, making\n * the compiled element visible.\n *\n * For the best result, the `angular.js` script must be loaded in the head section of the html\n * document; alternatively, the css rule above must be included in the external stylesheet of the\n * application.\n *\n * Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they\n * cannot match the `[ng\\:cloak]` selector. To work around this limitation, you must add the css\n * class `ng-cloak` in addition to the `ngCloak` directive as shown in the example below.\n *\n * @element ANY\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n        <div id=\"template1\" ng-cloak>{{ 'hello' }}</div>\n        <div id=\"template2\" ng-cloak class=\"ng-cloak\">{{ 'hello IE7' }}</div>\n     </file>\n     <file name=\"protractor.js\" type=\"protractor\">\n       it('should remove the template directive and css class', function() {\n         expect($('#template1').getAttribute('ng-cloak')).\n           toBeNull();\n         expect($('#template2').getAttribute('ng-cloak')).\n           toBeNull();\n       });\n     </file>\n   </example>\n *\n */\nvar ngCloakDirective = ngDirective({\n  compile: function(element, attr) {\n    attr.$set('ngCloak', undefined);\n    element.removeClass('ng-cloak');\n  }\n});\n\n/**\n * @ngdoc directive\n * @name ngController\n *\n * @description\n * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular\n * supports the principles behind the Model-View-Controller design pattern.\n *\n * MVC components in angular:\n *\n * * Model â€” Models are the properties of a scope; scopes are attached to the DOM where scope properties\n *   are accessed through bindings.\n * * View â€” The template (HTML with data bindings) that is rendered into the View.\n * * Controller â€” The `ngController` directive specifies a Controller class; the class contains business\n *   logic behind the application to decorate the scope with functions and values\n *\n * Note that you can also attach controllers to the DOM by declaring it in a route definition\n * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller\n * again using `ng-controller` in the template itself.  This will cause the controller to be attached\n * and executed twice.\n *\n * @element ANY\n * @scope\n * @priority 500\n * @param {expression} ngController Name of a constructor function registered with the current\n * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}\n * that on the current scope evaluates to a constructor function.\n *\n * The controller instance can be published into a scope property by specifying\n * `ng-controller=\"as propertyName\"`.\n *\n * If the current `$controllerProvider` is configured to use globals (via\n * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may\n * also be the name of a globally accessible constructor function (not recommended).\n *\n * @example\n * Here is a simple form for editing user contact information. Adding, removing, clearing, and\n * greeting are methods declared on the controller (see source tab). These methods can\n * easily be called from the angular markup. Any changes to the data are automatically reflected\n * in the View without the need for a manual update.\n *\n * Two different declaration styles are included below:\n *\n * * one binds methods and properties directly onto the controller using `this`:\n * `ng-controller=\"SettingsController1 as settings\"`\n * * one injects `$scope` into the controller:\n * `ng-controller=\"SettingsController2\"`\n *\n * The second option is more common in the Angular community, and is generally used in boilerplates\n * and in this guide. However, there are advantages to binding properties directly to the controller\n * and avoiding scope.\n *\n * * Using `controller as` makes it obvious which controller you are accessing in the template when\n * multiple controllers apply to an element.\n * * If you are writing your controllers as classes you have easier access to the properties and\n * methods, which will appear on the scope, from inside the controller code.\n * * Since there is always a `.` in the bindings, you don't have to worry about prototypal\n * inheritance masking primitives.\n *\n * This example demonstrates the `controller as` syntax.\n *\n * <example name=\"ngControllerAs\" module=\"controllerAsExample\">\n *   <file name=\"index.html\">\n *    <div id=\"ctrl-as-exmpl\" ng-controller=\"SettingsController1 as settings\">\n *      Name: <input type=\"text\" ng-model=\"settings.name\"/>\n *      [ <a href=\"\" ng-click=\"settings.greet()\">greet</a> ]<br/>\n *      Contact:\n *      <ul>\n *        <li ng-repeat=\"contact in settings.contacts\">\n *          <select ng-model=\"contact.type\">\n *             <option>phone</option>\n *             <option>email</option>\n *          </select>\n *          <input type=\"text\" ng-model=\"contact.value\"/>\n *          [ <a href=\"\" ng-click=\"settings.clearContact(contact)\">clear</a>\n *          | <a href=\"\" ng-click=\"settings.removeContact(contact)\">X</a> ]\n *        </li>\n *        <li>[ <a href=\"\" ng-click=\"settings.addContact()\">add</a> ]</li>\n *     </ul>\n *    </div>\n *   </file>\n *   <file name=\"app.js\">\n *    angular.module('controllerAsExample', [])\n *      .controller('SettingsController1', SettingsController1);\n *\n *    function SettingsController1() {\n *      this.name = \"John Smith\";\n *      this.contacts = [\n *        {type: 'phone', value: '408 555 1212'},\n *        {type: 'email', value: 'john.smith@example.org'} ];\n *    }\n *\n *    SettingsController1.prototype.greet = function() {\n *      alert(this.name);\n *    };\n *\n *    SettingsController1.prototype.addContact = function() {\n *      this.contacts.push({type: 'email', value: 'yourname@example.org'});\n *    };\n *\n *    SettingsController1.prototype.removeContact = function(contactToRemove) {\n *     var index = this.contacts.indexOf(contactToRemove);\n *      this.contacts.splice(index, 1);\n *    };\n *\n *    SettingsController1.prototype.clearContact = function(contact) {\n *      contact.type = 'phone';\n *      contact.value = '';\n *    };\n *   </file>\n *   <file name=\"protractor.js\" type=\"protractor\">\n *     it('should check controller as', function() {\n *       var container = element(by.id('ctrl-as-exmpl'));\n *         expect(container.element(by.model('settings.name'))\n *           .getAttribute('value')).toBe('John Smith');\n *\n *       var firstRepeat =\n *           container.element(by.repeater('contact in settings.contacts').row(0));\n *       var secondRepeat =\n *           container.element(by.repeater('contact in settings.contacts').row(1));\n *\n *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))\n *           .toBe('408 555 1212');\n *\n *       expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))\n *           .toBe('john.smith@example.org');\n *\n *       firstRepeat.element(by.linkText('clear')).click();\n *\n *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))\n *           .toBe('');\n *\n *       container.element(by.linkText('add')).click();\n *\n *       expect(container.element(by.repeater('contact in settings.contacts').row(2))\n *           .element(by.model('contact.value'))\n *           .getAttribute('value'))\n *           .toBe('yourname@example.org');\n *     });\n *   </file>\n * </example>\n *\n * This example demonstrates the \"attach to `$scope`\" style of controller.\n *\n * <example name=\"ngController\" module=\"controllerExample\">\n *  <file name=\"index.html\">\n *   <div id=\"ctrl-exmpl\" ng-controller=\"SettingsController2\">\n *     Name: <input type=\"text\" ng-model=\"name\"/>\n *     [ <a href=\"\" ng-click=\"greet()\">greet</a> ]<br/>\n *     Contact:\n *     <ul>\n *       <li ng-repeat=\"contact in contacts\">\n *         <select ng-model=\"contact.type\">\n *            <option>phone</option>\n *            <option>email</option>\n *         </select>\n *         <input type=\"text\" ng-model=\"contact.value\"/>\n *         [ <a href=\"\" ng-click=\"clearContact(contact)\">clear</a>\n *         | <a href=\"\" ng-click=\"removeContact(contact)\">X</a> ]\n *       </li>\n *       <li>[ <a href=\"\" ng-click=\"addContact()\">add</a> ]</li>\n *    </ul>\n *   </div>\n *  </file>\n *  <file name=\"app.js\">\n *   angular.module('controllerExample', [])\n *     .controller('SettingsController2', ['$scope', SettingsController2]);\n *\n *   function SettingsController2($scope) {\n *     $scope.name = \"John Smith\";\n *     $scope.contacts = [\n *       {type:'phone', value:'408 555 1212'},\n *       {type:'email', value:'john.smith@example.org'} ];\n *\n *     $scope.greet = function() {\n *       alert($scope.name);\n *     };\n *\n *     $scope.addContact = function() {\n *       $scope.contacts.push({type:'email', value:'yourname@example.org'});\n *     };\n *\n *     $scope.removeContact = function(contactToRemove) {\n *       var index = $scope.contacts.indexOf(contactToRemove);\n *       $scope.contacts.splice(index, 1);\n *     };\n *\n *     $scope.clearContact = function(contact) {\n *       contact.type = 'phone';\n *       contact.value = '';\n *     };\n *   }\n *  </file>\n *  <file name=\"protractor.js\" type=\"protractor\">\n *    it('should check controller', function() {\n *      var container = element(by.id('ctrl-exmpl'));\n *\n *      expect(container.element(by.model('name'))\n *          .getAttribute('value')).toBe('John Smith');\n *\n *      var firstRepeat =\n *          container.element(by.repeater('contact in contacts').row(0));\n *      var secondRepeat =\n *          container.element(by.repeater('contact in contacts').row(1));\n *\n *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))\n *          .toBe('408 555 1212');\n *      expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))\n *          .toBe('john.smith@example.org');\n *\n *      firstRepeat.element(by.linkText('clear')).click();\n *\n *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))\n *          .toBe('');\n *\n *      container.element(by.linkText('add')).click();\n *\n *      expect(container.element(by.repeater('contact in contacts').row(2))\n *          .element(by.model('contact.value'))\n *          .getAttribute('value'))\n *          .toBe('yourname@example.org');\n *    });\n *  </file>\n *</example>\n\n */\nvar ngControllerDirective = [function() {\n  return {\n    restrict: 'A',\n    scope: true,\n    controller: '@',\n    priority: 500\n  };\n}];\n\n/**\n * @ngdoc directive\n * @name ngCsp\n *\n * @element html\n * @description\n * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.\n *\n * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.\n *\n * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).\n * For Angular to be CSP compatible there are only two things that we need to do differently:\n *\n * - don't use `Function` constructor to generate optimized value getters\n * - don't inject custom stylesheet into the document\n *\n * AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp`\n * directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will\n * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will\n * be raised.\n *\n * CSP forbids JavaScript to inline stylesheet rules. In non CSP mode Angular automatically\n * includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}).\n * To make those directives work in CSP mode, include the `angular-csp.css` manually.\n *\n * Angular tries to autodetect if CSP is active and automatically turn on the CSP-safe mode. This\n * autodetection however triggers a CSP error to be logged in the console:\n *\n * ```\n * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of\n * script in the following Content Security Policy directive: \"default-src 'self'\". Note that\n * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.\n * ```\n *\n * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`\n * directive on the root element of the application or on the `angular.js` script tag, whichever\n * appears first in the html document.\n *\n * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*\n *\n * @example\n * This example shows how to apply the `ngCsp` directive to the `html` tag.\n   ```html\n     <!doctype html>\n     <html ng-app ng-csp>\n     ...\n     ...\n     </html>\n   ```\n  * @example\n      // Note: the suffix `.csp` in the example name triggers\n      // csp mode in our http server!\n      <example name=\"example.csp\" module=\"cspExample\" ng-csp=\"true\">\n        <file name=\"index.html\">\n          <div ng-controller=\"MainController as ctrl\">\n            <div>\n              <button ng-click=\"ctrl.inc()\" id=\"inc\">Increment</button>\n              <span id=\"counter\">\n                {{ctrl.counter}}\n              </span>\n            </div>\n\n            <div>\n              <button ng-click=\"ctrl.evil()\" id=\"evil\">Evil</button>\n              <span id=\"evilError\">\n                {{ctrl.evilError}}\n              </span>\n            </div>\n          </div>\n        </file>\n        <file name=\"script.js\">\n           angular.module('cspExample', [])\n             .controller('MainController', function() {\n                this.counter = 0;\n                this.inc = function() {\n                  this.counter++;\n                };\n                this.evil = function() {\n                  // jshint evil:true\n                  try {\n                    eval('1+2');\n                  } catch (e) {\n                    this.evilError = e.message;\n                  }\n                };\n              });\n        </file>\n        <file name=\"protractor.js\" type=\"protractor\">\n          var util, webdriver;\n\n          var incBtn = element(by.id('inc'));\n          var counter = element(by.id('counter'));\n          var evilBtn = element(by.id('evil'));\n          var evilError = element(by.id('evilError'));\n\n          function getAndClearSevereErrors() {\n            return browser.manage().logs().get('browser').then(function(browserLog) {\n              return browserLog.filter(function(logEntry) {\n                return logEntry.level.value > webdriver.logging.Level.WARNING.value;\n              });\n            });\n          }\n\n          function clearErrors() {\n            getAndClearSevereErrors();\n          }\n\n          function expectNoErrors() {\n            getAndClearSevereErrors().then(function(filteredLog) {\n              expect(filteredLog.length).toEqual(0);\n              if (filteredLog.length) {\n                console.log('browser console errors: ' + util.inspect(filteredLog));\n              }\n            });\n          }\n\n          function expectError(regex) {\n            getAndClearSevereErrors().then(function(filteredLog) {\n              var found = false;\n              filteredLog.forEach(function(log) {\n                if (log.message.match(regex)) {\n                  found = true;\n                }\n              });\n              if (!found) {\n                throw new Error('expected an error that matches ' + regex);\n              }\n            });\n          }\n\n          beforeEach(function() {\n            util = require('util');\n            webdriver = require('protractor/node_modules/selenium-webdriver');\n          });\n\n          // For now, we only test on Chrome,\n          // as Safari does not load the page with Protractor's injected scripts,\n          // and Firefox webdriver always disables content security policy (#6358)\n          if (browser.params.browser !== 'chrome') {\n            return;\n          }\n\n          it('should not report errors when the page is loaded', function() {\n            // clear errors so we are not dependent on previous tests\n            clearErrors();\n            // Need to reload the page as the page is already loaded when\n            // we come here\n            browser.driver.getCurrentUrl().then(function(url) {\n              browser.get(url);\n            });\n            expectNoErrors();\n          });\n\n          it('should evaluate expressions', function() {\n            expect(counter.getText()).toEqual('0');\n            incBtn.click();\n            expect(counter.getText()).toEqual('1');\n            expectNoErrors();\n          });\n\n          it('should throw and report an error when using \"eval\"', function() {\n            evilBtn.click();\n            expect(evilError.getText()).toMatch(/Content Security Policy/);\n            expectError(/Content Security Policy/);\n          });\n        </file>\n      </example>\n  */\n\n// ngCsp is not implemented as a proper directive any more, because we need it be processed while we\n// bootstrap the system (before $parse is instantiated), for this reason we just have\n// the csp.isActive() fn that looks for ng-csp attribute anywhere in the current doc\n\n/**\n * @ngdoc directive\n * @name ngClick\n *\n * @description\n * The ngClick directive allows you to specify custom behavior when\n * an element is clicked.\n *\n * @element ANY\n * @priority 0\n * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon\n * click. ({@link guide/expression#-event- Event object is available as `$event`})\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n      <button ng-click=\"count = count + 1\" ng-init=\"count=0\">\n        Increment\n      </button>\n      <span>\n        count: {{count}}\n      </span>\n     </file>\n     <file name=\"protractor.js\" type=\"protractor\">\n       it('should check ng-click', function() {\n         expect(element(by.binding('count')).getText()).toMatch('0');\n         element(by.css('button')).click();\n         expect(element(by.binding('count')).getText()).toMatch('1');\n       });\n     </file>\n   </example>\n */\n/*\n * A collection of directives that allows creation of custom event handlers that are defined as\n * angular expressions and are compiled and executed within the current scope.\n */\nvar ngEventDirectives = {};\n\n// For events that might fire synchronously during DOM manipulation\n// we need to execute their event handlers asynchronously using $evalAsync,\n// so that they are not executed in an inconsistent state.\nvar forceAsyncEvents = {\n  'blur': true,\n  'focus': true\n};\nforEach(\n  'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),\n  function(eventName) {\n    var directiveName = directiveNormalize('ng-' + eventName);\n    ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {\n      return {\n        restrict: 'A',\n        compile: function($element, attr) {\n          // We expose the powerful $event object on the scope that provides access to the Window,\n          // etc. that isn't protected by the fast paths in $parse.  We explicitly request better\n          // checks at the cost of speed since event handler expressions are not executed as\n          // frequently as regular change detection.\n          var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);\n          return function ngEventHandler(scope, element) {\n            element.on(eventName, function(event) {\n              var callback = function() {\n                fn(scope, {$event:event});\n              };\n              if (forceAsyncEvents[eventName] && $rootScope.$$phase) {\n                scope.$evalAsync(callback);\n              } else {\n                scope.$apply(callback);\n              }\n            });\n          };\n        }\n      };\n    }];\n  }\n);\n\n/**\n * @ngdoc directive\n * @name ngDblclick\n *\n * @description\n * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.\n *\n * @element ANY\n * @priority 0\n * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon\n * a dblclick. (The Event object is available as `$event`)\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n      <button ng-dblclick=\"count = count + 1\" ng-init=\"count=0\">\n        Increment (on double click)\n      </button>\n      count: {{count}}\n     </file>\n   </example>\n */\n\n\n/**\n * @ngdoc directive\n * @name ngMousedown\n *\n * @description\n * The ngMousedown directive allows you to specify custom behavior on mousedown event.\n *\n * @element ANY\n * @priority 0\n * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon\n * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n      <button ng-mousedown=\"count = count + 1\" ng-init=\"count=0\">\n        Increment (on mouse down)\n      </button>\n      count: {{count}}\n     </file>\n   </example>\n */\n\n\n/**\n * @ngdoc directive\n * @name ngMouseup\n *\n * @description\n * Specify custom behavior on mouseup event.\n *\n * @element ANY\n * @priority 0\n * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon\n * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n      <button ng-mouseup=\"count = count + 1\" ng-init=\"count=0\">\n        Increment (on mouse up)\n      </button>\n      count: {{count}}\n     </file>\n   </example>\n */\n\n/**\n * @ngdoc directive\n * @name ngMouseover\n *\n * @description\n * Specify custom behavior on mouseover event.\n *\n * @element ANY\n * @priority 0\n * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon\n * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n      <button ng-mouseover=\"count = count + 1\" ng-init=\"count=0\">\n        Increment (when mouse is over)\n      </button>\n      count: {{count}}\n     </file>\n   </example>\n */\n\n\n/**\n * @ngdoc directive\n * @name ngMouseenter\n *\n * @description\n * Specify custom behavior on mouseenter event.\n *\n * @element ANY\n * @priority 0\n * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon\n * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n      <button ng-mouseenter=\"count = count + 1\" ng-init=\"count=0\">\n        Increment (when mouse enters)\n      </button>\n      count: {{count}}\n     </file>\n   </example>\n */\n\n\n/**\n * @ngdoc directive\n * @name ngMouseleave\n *\n * @description\n * Specify custom behavior on mouseleave event.\n *\n * @element ANY\n * @priority 0\n * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon\n * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n      <button ng-mouseleave=\"count = count + 1\" ng-init=\"count=0\">\n        Increment (when mouse leaves)\n      </button>\n      count: {{count}}\n     </file>\n   </example>\n */\n\n\n/**\n * @ngdoc directive\n * @name ngMousemove\n *\n * @description\n * Specify custom behavior on mousemove event.\n *\n * @element ANY\n * @priority 0\n * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon\n * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n      <button ng-mousemove=\"count = count + 1\" ng-init=\"count=0\">\n        Increment (when mouse moves)\n      </button>\n      count: {{count}}\n     </file>\n   </example>\n */\n\n\n/**\n * @ngdoc directive\n * @name ngKeydown\n *\n * @description\n * Specify custom behavior on keydown event.\n *\n * @element ANY\n * @priority 0\n * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon\n * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n      <input ng-keydown=\"count = count + 1\" ng-init=\"count=0\">\n      key down count: {{count}}\n     </file>\n   </example>\n */\n\n\n/**\n * @ngdoc directive\n * @name ngKeyup\n *\n * @description\n * Specify custom behavior on keyup event.\n *\n * @element ANY\n * @priority 0\n * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon\n * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n       <p>Typing in the input box below updates the key count</p>\n       <input ng-keyup=\"count = count + 1\" ng-init=\"count=0\"> key up count: {{count}}\n\n       <p>Typing in the input box below updates the keycode</p>\n       <input ng-keyup=\"event=$event\">\n       <p>event keyCode: {{ event.keyCode }}</p>\n       <p>event altKey: {{ event.altKey }}</p>\n     </file>\n   </example>\n */\n\n\n/**\n * @ngdoc directive\n * @name ngKeypress\n *\n * @description\n * Specify custom behavior on keypress event.\n *\n * @element ANY\n * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon\n * keypress. ({@link guide/expression#-event- Event object is available as `$event`}\n * and can be interrogated for keyCode, altKey, etc.)\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n      <input ng-keypress=\"count = count + 1\" ng-init=\"count=0\">\n      key press count: {{count}}\n     </file>\n   </example>\n */\n\n\n/**\n * @ngdoc directive\n * @name ngSubmit\n *\n * @description\n * Enables binding angular expressions to onsubmit events.\n *\n * Additionally it prevents the default action (which for form means sending the request to the\n * server and reloading the current page), but only if the form does not contain `action`,\n * `data-action`, or `x-action` attributes.\n *\n * <div class=\"alert alert-warning\">\n * **Warning:** Be careful not to cause \"double-submission\" by using both the `ngClick` and\n * `ngSubmit` handlers together. See the\n * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}\n * for a detailed discussion of when `ngSubmit` may be triggered.\n * </div>\n *\n * @element form\n * @priority 0\n * @param {expression} ngSubmit {@link guide/expression Expression} to eval.\n * ({@link guide/expression#-event- Event object is available as `$event`})\n *\n * @example\n   <example module=\"submitExample\">\n     <file name=\"index.html\">\n      <script>\n        angular.module('submitExample', [])\n          .controller('ExampleController', ['$scope', function($scope) {\n            $scope.list = [];\n            $scope.text = 'hello';\n            $scope.submit = function() {\n              if ($scope.text) {\n                $scope.list.push(this.text);\n                $scope.text = '';\n              }\n            };\n          }]);\n      </script>\n      <form ng-submit=\"submit()\" ng-controller=\"ExampleController\">\n        Enter text and hit enter:\n        <input type=\"text\" ng-model=\"text\" name=\"text\" />\n        <input type=\"submit\" id=\"submit\" value=\"Submit\" />\n        <pre>list={{list}}</pre>\n      </form>\n     </file>\n     <file name=\"protractor.js\" type=\"protractor\">\n       it('should check ng-submit', function() {\n         expect(element(by.binding('list')).getText()).toBe('list=[]');\n         element(by.css('#submit')).click();\n         expect(element(by.binding('list')).getText()).toContain('hello');\n         expect(element(by.model('text')).getAttribute('value')).toBe('');\n       });\n       it('should ignore empty strings', function() {\n         expect(element(by.binding('list')).getText()).toBe('list=[]');\n         element(by.css('#submit')).click();\n         element(by.css('#submit')).click();\n         expect(element(by.binding('list')).getText()).toContain('hello');\n        });\n     </file>\n   </example>\n */\n\n/**\n * @ngdoc directive\n * @name ngFocus\n *\n * @description\n * Specify custom behavior on focus event.\n *\n * Note: As the `focus` event is executed synchronously when calling `input.focus()`\n * AngularJS executes the expression using `scope.$evalAsync` if the event is fired\n * during an `$apply` to ensure a consistent state.\n *\n * @element window, input, select, textarea, a\n * @priority 0\n * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon\n * focus. ({@link guide/expression#-event- Event object is available as `$event`})\n *\n * @example\n * See {@link ng.directive:ngClick ngClick}\n */\n\n/**\n * @ngdoc directive\n * @name ngBlur\n *\n * @description\n * Specify custom behavior on blur event.\n *\n * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when\n * an element has lost focus.\n *\n * Note: As the `blur` event is executed synchronously also during DOM manipulations\n * (e.g. removing a focussed input),\n * AngularJS executes the expression using `scope.$evalAsync` if the event is fired\n * during an `$apply` to ensure a consistent state.\n *\n * @element window, input, select, textarea, a\n * @priority 0\n * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon\n * blur. ({@link guide/expression#-event- Event object is available as `$event`})\n *\n * @example\n * See {@link ng.directive:ngClick ngClick}\n */\n\n/**\n * @ngdoc directive\n * @name ngCopy\n *\n * @description\n * Specify custom behavior on copy event.\n *\n * @element window, input, select, textarea, a\n * @priority 0\n * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon\n * copy. ({@link guide/expression#-event- Event object is available as `$event`})\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n      <input ng-copy=\"copied=true\" ng-init=\"copied=false; value='copy me'\" ng-model=\"value\">\n      copied: {{copied}}\n     </file>\n   </example>\n */\n\n/**\n * @ngdoc directive\n * @name ngCut\n *\n * @description\n * Specify custom behavior on cut event.\n *\n * @element window, input, select, textarea, a\n * @priority 0\n * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon\n * cut. ({@link guide/expression#-event- Event object is available as `$event`})\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n      <input ng-cut=\"cut=true\" ng-init=\"cut=false; value='cut me'\" ng-model=\"value\">\n      cut: {{cut}}\n     </file>\n   </example>\n */\n\n/**\n * @ngdoc directive\n * @name ngPaste\n *\n * @description\n * Specify custom behavior on paste event.\n *\n * @element window, input, select, textarea, a\n * @priority 0\n * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon\n * paste. ({@link guide/expression#-event- Event object is available as `$event`})\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n      <input ng-paste=\"paste=true\" ng-init=\"paste=false\" placeholder='paste here'>\n      pasted: {{paste}}\n     </file>\n   </example>\n */\n\n/**\n * @ngdoc directive\n * @name ngIf\n * @restrict A\n *\n * @description\n * The `ngIf` directive removes or recreates a portion of the DOM tree based on an\n * {expression}. If the expression assigned to `ngIf` evaluates to a false\n * value then the element is removed from the DOM, otherwise a clone of the\n * element is reinserted into the DOM.\n *\n * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the\n * element in the DOM rather than changing its visibility via the `display` css property.  A common\n * case when this difference is significant is when using css selectors that rely on an element's\n * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.\n *\n * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope\n * is created when the element is restored.  The scope created within `ngIf` inherits from\n * its parent scope using\n * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).\n * An important implication of this is if `ngModel` is used within `ngIf` to bind to\n * a javascript primitive defined in the parent scope. In this case any modifications made to the\n * variable within the child scope will override (hide) the value in the parent scope.\n *\n * Also, `ngIf` recreates elements using their compiled state. An example of this behavior\n * is if an element's class attribute is directly modified after it's compiled, using something like\n * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element\n * the added class will be lost because the original compiled state is used to regenerate the element.\n *\n * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`\n * and `leave` effects.\n *\n * @animations\n * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container\n * leave - happens just before the `ngIf` contents are removed from the DOM\n *\n * @element ANY\n * @scope\n * @priority 600\n * @param {expression} ngIf If the {@link guide/expression expression} is falsy then\n *     the element is removed from the DOM tree. If it is truthy a copy of the compiled\n *     element is added to the DOM tree.\n *\n * @example\n  <example module=\"ngAnimate\" deps=\"angular-animate.js\" animations=\"true\">\n    <file name=\"index.html\">\n      Click me: <input type=\"checkbox\" ng-model=\"checked\" ng-init=\"checked=true\" /><br/>\n      Show when checked:\n      <span ng-if=\"checked\" class=\"animate-if\">\n        This is removed when the checkbox is unchecked.\n      </span>\n    </file>\n    <file name=\"animations.css\">\n      .animate-if {\n        background:white;\n        border:1px solid black;\n        padding:10px;\n      }\n\n      .animate-if.ng-enter, .animate-if.ng-leave {\n        -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;\n        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;\n      }\n\n      .animate-if.ng-enter,\n      .animate-if.ng-leave.ng-leave-active {\n        opacity:0;\n      }\n\n      .animate-if.ng-leave,\n      .animate-if.ng-enter.ng-enter-active {\n        opacity:1;\n      }\n    </file>\n  </example>\n */\nvar ngIfDirective = ['$animate', function($animate) {\n  return {\n    multiElement: true,\n    transclude: 'element',\n    priority: 600,\n    terminal: true,\n    restrict: 'A',\n    $$tlb: true,\n    link: function($scope, $element, $attr, ctrl, $transclude) {\n        var block, childScope, previousElements;\n        $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {\n\n          if (value) {\n            if (!childScope) {\n              $transclude(function(clone, newScope) {\n                childScope = newScope;\n                clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');\n                // Note: We only need the first/last node of the cloned nodes.\n                // However, we need to keep the reference to the jqlite wrapper as it might be changed later\n                // by a directive with templateUrl when its template arrives.\n                block = {\n                  clone: clone\n                };\n                $animate.enter(clone, $element.parent(), $element);\n              });\n            }\n          } else {\n            if (previousElements) {\n              previousElements.remove();\n              previousElements = null;\n            }\n            if (childScope) {\n              childScope.$destroy();\n              childScope = null;\n            }\n            if (block) {\n              previousElements = getBlockNodes(block.clone);\n              $animate.leave(previousElements).then(function() {\n                previousElements = null;\n              });\n              block = null;\n            }\n          }\n        });\n    }\n  };\n}];\n\n/**\n * @ngdoc directive\n * @name ngInclude\n * @restrict ECA\n *\n * @description\n * Fetches, compiles and includes an external HTML fragment.\n *\n * By default, the template URL is restricted to the same domain and protocol as the\n * application document. This is done by calling {@link $sce#getTrustedResourceUrl\n * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols\n * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or\n * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link\n * ng.$sce Strict Contextual Escaping}.\n *\n * In addition, the browser's\n * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)\n * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)\n * policy may further restrict whether the template is successfully loaded.\n * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`\n * access on some browsers.\n *\n * @animations\n * enter - animation is used to bring new content into the browser.\n * leave - animation is used to animate existing content away.\n *\n * The enter and leave animation occur concurrently.\n *\n * @scope\n * @priority 400\n *\n * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,\n *                 make sure you wrap it in **single** quotes, e.g. `src=\"'myPartialTemplate.html'\"`.\n * @param {string=} onload Expression to evaluate when a new partial is loaded.\n *\n * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll\n *                  $anchorScroll} to scroll the viewport after the content is loaded.\n *\n *                  - If the attribute is not set, disable scrolling.\n *                  - If the attribute is set without value, enable scrolling.\n *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.\n *\n * @example\n  <example module=\"includeExample\" deps=\"angular-animate.js\" animations=\"true\">\n    <file name=\"index.html\">\n     <div ng-controller=\"ExampleController\">\n       <select ng-model=\"template\" ng-options=\"t.name for t in templates\">\n        <option value=\"\">(blank)</option>\n       </select>\n       url of the template: <code>{{template.url}}</code>\n       <hr/>\n       <div class=\"slide-animate-container\">\n         <div class=\"slide-animate\" ng-include=\"template.url\"></div>\n       </div>\n     </div>\n    </file>\n    <file name=\"script.js\">\n      angular.module('includeExample', ['ngAnimate'])\n        .controller('ExampleController', ['$scope', function($scope) {\n          $scope.templates =\n            [ { name: 'template1.html', url: 'template1.html'},\n              { name: 'template2.html', url: 'template2.html'} ];\n          $scope.template = $scope.templates[0];\n        }]);\n     </file>\n    <file name=\"template1.html\">\n      Content of template1.html\n    </file>\n    <file name=\"template2.html\">\n      Content of template2.html\n    </file>\n    <file name=\"animations.css\">\n      .slide-animate-container {\n        position:relative;\n        background:white;\n        border:1px solid black;\n        height:40px;\n        overflow:hidden;\n      }\n\n      .slide-animate {\n        padding:10px;\n      }\n\n      .slide-animate.ng-enter, .slide-animate.ng-leave {\n        -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;\n        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;\n\n        position:absolute;\n        top:0;\n        left:0;\n        right:0;\n        bottom:0;\n        display:block;\n        padding:10px;\n      }\n\n      .slide-animate.ng-enter {\n        top:-50px;\n      }\n      .slide-animate.ng-enter.ng-enter-active {\n        top:0;\n      }\n\n      .slide-animate.ng-leave {\n        top:0;\n      }\n      .slide-animate.ng-leave.ng-leave-active {\n        top:50px;\n      }\n    </file>\n    <file name=\"protractor.js\" type=\"protractor\">\n      var templateSelect = element(by.model('template'));\n      var includeElem = element(by.css('[ng-include]'));\n\n      it('should load template1.html', function() {\n        expect(includeElem.getText()).toMatch(/Content of template1.html/);\n      });\n\n      it('should load template2.html', function() {\n        if (browser.params.browser == 'firefox') {\n          // Firefox can't handle using selects\n          // See https://github.com/angular/protractor/issues/480\n          return;\n        }\n        templateSelect.click();\n        templateSelect.all(by.css('option')).get(2).click();\n        expect(includeElem.getText()).toMatch(/Content of template2.html/);\n      });\n\n      it('should change to blank', function() {\n        if (browser.params.browser == 'firefox') {\n          // Firefox can't handle using selects\n          return;\n        }\n        templateSelect.click();\n        templateSelect.all(by.css('option')).get(0).click();\n        expect(includeElem.isPresent()).toBe(false);\n      });\n    </file>\n  </example>\n */\n\n\n/**\n * @ngdoc event\n * @name ngInclude#$includeContentRequested\n * @eventType emit on the scope ngInclude was declared in\n * @description\n * Emitted every time the ngInclude content is requested.\n *\n * @param {Object} angularEvent Synthetic event object.\n * @param {String} src URL of content to load.\n */\n\n\n/**\n * @ngdoc event\n * @name ngInclude#$includeContentLoaded\n * @eventType emit on the current ngInclude scope\n * @description\n * Emitted every time the ngInclude content is reloaded.\n *\n * @param {Object} angularEvent Synthetic event object.\n * @param {String} src URL of content to load.\n */\n\n\n/**\n * @ngdoc event\n * @name ngInclude#$includeContentError\n * @eventType emit on the scope ngInclude was declared in\n * @description\n * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)\n *\n * @param {Object} angularEvent Synthetic event object.\n * @param {String} src URL of content to load.\n */\nvar ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate', '$sce',\n                  function($templateRequest,   $anchorScroll,   $animate,   $sce) {\n  return {\n    restrict: 'ECA',\n    priority: 400,\n    terminal: true,\n    transclude: 'element',\n    controller: angular.noop,\n    compile: function(element, attr) {\n      var srcExp = attr.ngInclude || attr.src,\n          onloadExp = attr.onload || '',\n          autoScrollExp = attr.autoscroll;\n\n      return function(scope, $element, $attr, ctrl, $transclude) {\n        var changeCounter = 0,\n            currentScope,\n            previousElement,\n            currentElement;\n\n        var cleanupLastIncludeContent = function() {\n          if (previousElement) {\n            previousElement.remove();\n            previousElement = null;\n          }\n          if (currentScope) {\n            currentScope.$destroy();\n            currentScope = null;\n          }\n          if (currentElement) {\n            $animate.leave(currentElement).then(function() {\n              previousElement = null;\n            });\n            previousElement = currentElement;\n            currentElement = null;\n          }\n        };\n\n        scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) {\n          var afterAnimation = function() {\n            if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {\n              $anchorScroll();\n            }\n          };\n          var thisChangeId = ++changeCounter;\n\n          if (src) {\n            //set the 2nd param to true to ignore the template request error so that the inner\n            //contents and scope can be cleaned up.\n            $templateRequest(src, true).then(function(response) {\n              if (thisChangeId !== changeCounter) return;\n              var newScope = scope.$new();\n              ctrl.template = response;\n\n              // Note: This will also link all children of ng-include that were contained in the original\n              // html. If that content contains controllers, ... they could pollute/change the scope.\n              // However, using ng-include on an element with additional content does not make sense...\n              // Note: We can't remove them in the cloneAttchFn of $transclude as that\n              // function is called before linking the content, which would apply child\n              // directives to non existing elements.\n              var clone = $transclude(newScope, function(clone) {\n                cleanupLastIncludeContent();\n                $animate.enter(clone, null, $element).then(afterAnimation);\n              });\n\n              currentScope = newScope;\n              currentElement = clone;\n\n              currentScope.$emit('$includeContentLoaded', src);\n              scope.$eval(onloadExp);\n            }, function() {\n              if (thisChangeId === changeCounter) {\n                cleanupLastIncludeContent();\n                scope.$emit('$includeContentError', src);\n              }\n            });\n            scope.$emit('$includeContentRequested', src);\n          } else {\n            cleanupLastIncludeContent();\n            ctrl.template = null;\n          }\n        });\n      };\n    }\n  };\n}];\n\n// This directive is called during the $transclude call of the first `ngInclude` directive.\n// It will replace and compile the content of the element with the loaded template.\n// We need this directive so that the element content is already filled when\n// the link function of another directive on the same element as ngInclude\n// is called.\nvar ngIncludeFillContentDirective = ['$compile',\n  function($compile) {\n    return {\n      restrict: 'ECA',\n      priority: -400,\n      require: 'ngInclude',\n      link: function(scope, $element, $attr, ctrl) {\n        if (/SVG/.test($element[0].toString())) {\n          // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not\n          // support innerHTML, so detect this here and try to generate the contents\n          // specially.\n          $element.empty();\n          $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,\n              function namespaceAdaptedClone(clone) {\n            $element.append(clone);\n          }, {futureParentElement: $element});\n          return;\n        }\n\n        $element.html(ctrl.template);\n        $compile($element.contents())(scope);\n      }\n    };\n  }];\n\n/**\n * @ngdoc directive\n * @name ngInit\n * @restrict AC\n *\n * @description\n * The `ngInit` directive allows you to evaluate an expression in the\n * current scope.\n *\n * <div class=\"alert alert-error\">\n * The only appropriate use of `ngInit` is for aliasing special properties of\n * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you\n * should use {@link guide/controller controllers} rather than `ngInit`\n * to initialize values on a scope.\n * </div>\n * <div class=\"alert alert-warning\">\n * **Note**: If you have assignment in `ngInit` along with {@link ng.$filter `$filter`}, make\n * sure you have parenthesis for correct precedence:\n * <pre class=\"prettyprint\">\n * `<div ng-init=\"test1 = (data | orderBy:'name')\"></div>`\n * </pre>\n * </div>\n *\n * @priority 450\n *\n * @element ANY\n * @param {expression} ngInit {@link guide/expression Expression} to eval.\n *\n * @example\n   <example module=\"initExample\">\n     <file name=\"index.html\">\n   <script>\n     angular.module('initExample', [])\n       .controller('ExampleController', ['$scope', function($scope) {\n         $scope.list = [['a', 'b'], ['c', 'd']];\n       }]);\n   </script>\n   <div ng-controller=\"ExampleController\">\n     <div ng-repeat=\"innerList in list\" ng-init=\"outerIndex = $index\">\n       <div ng-repeat=\"value in innerList\" ng-init=\"innerIndex = $index\">\n          <span class=\"example-init\">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>\n       </div>\n     </div>\n   </div>\n     </file>\n     <file name=\"protractor.js\" type=\"protractor\">\n       it('should alias index positions', function() {\n         var elements = element.all(by.css('.example-init'));\n         expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');\n         expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');\n         expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');\n         expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');\n       });\n     </file>\n   </example>\n */\nvar ngInitDirective = ngDirective({\n  priority: 450,\n  compile: function() {\n    return {\n      pre: function(scope, element, attrs) {\n        scope.$eval(attrs.ngInit);\n      }\n    };\n  }\n});\n\n/**\n * @ngdoc directive\n * @name ngList\n *\n * @description\n * Text input that converts between a delimited string and an array of strings. The default\n * delimiter is a comma followed by a space - equivalent to `ng-list=\", \"`. You can specify a custom\n * delimiter as the value of the `ngList` attribute - for example, `ng-list=\" | \"`.\n *\n * The behaviour of the directive is affected by the use of the `ngTrim` attribute.\n * * If `ngTrim` is set to `\"false\"` then whitespace around both the separator and each\n *   list item is respected. This implies that the user of the directive is responsible for\n *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a\n *   tab or newline character.\n * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected\n *   when joining the list items back together) and whitespace around each list item is stripped\n *   before it is added to the model.\n *\n * ### Example with Validation\n *\n * <example name=\"ngList-directive\" module=\"listExample\">\n *   <file name=\"app.js\">\n *      angular.module('listExample', [])\n *        .controller('ExampleController', ['$scope', function($scope) {\n *          $scope.names = ['morpheus', 'neo', 'trinity'];\n *        }]);\n *   </file>\n *   <file name=\"index.html\">\n *    <form name=\"myForm\" ng-controller=\"ExampleController\">\n *      List: <input name=\"namesInput\" ng-model=\"names\" ng-list required>\n *      <span class=\"error\" ng-show=\"myForm.namesInput.$error.required\">\n *        Required!</span>\n *      <br>\n *      <tt>names = {{names}}</tt><br/>\n *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>\n *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>\n *      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>\n *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>\n *     </form>\n *   </file>\n *   <file name=\"protractor.js\" type=\"protractor\">\n *     var listInput = element(by.model('names'));\n *     var names = element(by.exactBinding('names'));\n *     var valid = element(by.binding('myForm.namesInput.$valid'));\n *     var error = element(by.css('span.error'));\n *\n *     it('should initialize to model', function() {\n *       expect(names.getText()).toContain('[\"morpheus\",\"neo\",\"trinity\"]');\n *       expect(valid.getText()).toContain('true');\n *       expect(error.getCssValue('display')).toBe('none');\n *     });\n *\n *     it('should be invalid if empty', function() {\n *       listInput.clear();\n *       listInput.sendKeys('');\n *\n *       expect(names.getText()).toContain('');\n *       expect(valid.getText()).toContain('false');\n *       expect(error.getCssValue('display')).not.toBe('none');\n *     });\n *   </file>\n * </example>\n *\n * ### Example - splitting on whitespace\n * <example name=\"ngList-directive-newlines\">\n *   <file name=\"index.html\">\n *    <textarea ng-model=\"list\" ng-list=\"&#10;\" ng-trim=\"false\"></textarea>\n *    <pre>{{ list | json }}</pre>\n *   </file>\n *   <file name=\"protractor.js\" type=\"protractor\">\n *     it(\"should split the text by newlines\", function() {\n *       var listInput = element(by.model('list'));\n *       var output = element(by.binding('list | json'));\n *       listInput.sendKeys('abc\\ndef\\nghi');\n *       expect(output.getText()).toContain('[\\n  \"abc\",\\n  \"def\",\\n  \"ghi\"\\n]');\n *     });\n *   </file>\n * </example>\n *\n * @element input\n * @param {string=} ngList optional delimiter that should be used to split the value.\n */\nvar ngListDirective = function() {\n  return {\n    restrict: 'A',\n    priority: 100,\n    require: 'ngModel',\n    link: function(scope, element, attr, ctrl) {\n      // We want to control whitespace trimming so we use this convoluted approach\n      // to access the ngList attribute, which doesn't pre-trim the attribute\n      var ngList = element.attr(attr.$attr.ngList) || ', ';\n      var trimValues = attr.ngTrim !== 'false';\n      var separator = trimValues ? trim(ngList) : ngList;\n\n      var parse = function(viewValue) {\n        // If the viewValue is invalid (say required but empty) it will be `undefined`\n        if (isUndefined(viewValue)) return;\n\n        var list = [];\n\n        if (viewValue) {\n          forEach(viewValue.split(separator), function(value) {\n            if (value) list.push(trimValues ? trim(value) : value);\n          });\n        }\n\n        return list;\n      };\n\n      ctrl.$parsers.push(parse);\n      ctrl.$formatters.push(function(value) {\n        if (isArray(value)) {\n          return value.join(ngList);\n        }\n\n        return undefined;\n      });\n\n      // Override the standard $isEmpty because an empty array means the input is empty.\n      ctrl.$isEmpty = function(value) {\n        return !value || !value.length;\n      };\n    }\n  };\n};\n\n/* global VALID_CLASS: true,\n  INVALID_CLASS: true,\n  PRISTINE_CLASS: true,\n  DIRTY_CLASS: true,\n  UNTOUCHED_CLASS: true,\n  TOUCHED_CLASS: true,\n*/\n\nvar VALID_CLASS = 'ng-valid',\n    INVALID_CLASS = 'ng-invalid',\n    PRISTINE_CLASS = 'ng-pristine',\n    DIRTY_CLASS = 'ng-dirty',\n    UNTOUCHED_CLASS = 'ng-untouched',\n    TOUCHED_CLASS = 'ng-touched',\n    PENDING_CLASS = 'ng-pending';\n\n\nvar $ngModelMinErr = new minErr('ngModel');\n\n/**\n * @ngdoc type\n * @name ngModel.NgModelController\n *\n * @property {string} $viewValue Actual string value in the view.\n * @property {*} $modelValue The value in the model that the control is bound to.\n * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever\n       the control reads value from the DOM. The functions are called in array order, each passing\n       its return value through to the next. The last return value is forwarded to the\n       {@link ngModel.NgModelController#$validators `$validators`} collection.\n\nParsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue\n`$viewValue`}.\n\nReturning `undefined` from a parser means a parse error occurred. In that case,\nno {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`\nwill be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}\nis set to `true`. The parse error is stored in `ngModel.$error.parse`.\n\n *\n * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever\n       the model value changes. The functions are called in reverse array order, each passing the value through to the\n       next. The last return value is used as the actual DOM value.\n       Used to format / convert values for display in the control.\n * ```js\n * function formatter(value) {\n *   if (value) {\n *     return value.toUpperCase();\n *   }\n * }\n * ngModel.$formatters.push(formatter);\n * ```\n *\n * @property {Object.<string, function>} $validators A collection of validators that are applied\n *      whenever the model value changes. The key value within the object refers to the name of the\n *      validator while the function refers to the validation operation. The validation operation is\n *      provided with the model value as an argument and must return a true or false value depending\n *      on the response of that validation.\n *\n * ```js\n * ngModel.$validators.validCharacters = function(modelValue, viewValue) {\n *   var value = modelValue || viewValue;\n *   return /[0-9]+/.test(value) &&\n *          /[a-z]+/.test(value) &&\n *          /[A-Z]+/.test(value) &&\n *          /\\W+/.test(value);\n * };\n * ```\n *\n * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to\n *      perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided\n *      is expected to return a promise when it is run during the model validation process. Once the promise\n *      is delivered then the validation status will be set to true when fulfilled and false when rejected.\n *      When the asynchronous validators are triggered, each of the validators will run in parallel and the model\n *      value will only be updated once all validators have been fulfilled. As long as an asynchronous validator\n *      is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators\n *      will only run once all synchronous validators have passed.\n *\n * Please note that if $http is used then it is important that the server returns a success HTTP response code\n * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.\n *\n * ```js\n * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {\n *   var value = modelValue || viewValue;\n *\n *   // Lookup user by username\n *   return $http.get('/api/users/' + value).\n *      then(function resolved() {\n *        //username exists, this means validation fails\n *        return $q.reject('exists');\n *      }, function rejected() {\n *        //username does not exist, therefore this validation passes\n *        return true;\n *      });\n * };\n * ```\n *\n * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the\n *     view value has changed. It is called with no arguments, and its return value is ignored.\n *     This can be used in place of additional $watches against the model value.\n *\n * @property {Object} $error An object hash with all failing validator ids as keys.\n * @property {Object} $pending An object hash with all pending validator ids as keys.\n *\n * @property {boolean} $untouched True if control has not lost focus yet.\n * @property {boolean} $touched True if control has lost focus.\n * @property {boolean} $pristine True if user has not interacted with the control yet.\n * @property {boolean} $dirty True if user has already interacted with the control.\n * @property {boolean} $valid True if there is no error.\n * @property {boolean} $invalid True if at least one error on the control.\n * @property {string} $name The name attribute of the control.\n *\n * @description\n *\n * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.\n * The controller contains services for data-binding, validation, CSS updates, and value formatting\n * and parsing. It purposefully does not contain any logic which deals with DOM rendering or\n * listening to DOM events.\n * Such DOM related logic should be provided by other directives which make use of\n * `NgModelController` for data-binding to control elements.\n * Angular provides this DOM logic for most {@link input `input`} elements.\n * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example\n * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.\n *\n * @example\n * ### Custom Control Example\n * This example shows how to use `NgModelController` with a custom control to achieve\n * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)\n * collaborate together to achieve the desired result.\n *\n * `contenteditable` is an HTML5 attribute, which tells the browser to let the element\n * contents be edited in place by the user.\n *\n * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}\n * module to automatically remove \"bad\" content like inline event listener (e.g. `<span onclick=\"...\">`).\n * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks\n * that content using the `$sce` service.\n *\n * <example name=\"NgModelController\" module=\"customControl\" deps=\"angular-sanitize.js\">\n    <file name=\"style.css\">\n      [contenteditable] {\n        border: 1px solid black;\n        background-color: white;\n        min-height: 20px;\n      }\n\n      .ng-invalid {\n        border: 1px solid red;\n      }\n\n    </file>\n    <file name=\"script.js\">\n      angular.module('customControl', ['ngSanitize']).\n        directive('contenteditable', ['$sce', function($sce) {\n          return {\n            restrict: 'A', // only activate on element attribute\n            require: '?ngModel', // get a hold of NgModelController\n            link: function(scope, element, attrs, ngModel) {\n              if (!ngModel) return; // do nothing if no ng-model\n\n              // Specify how UI should be updated\n              ngModel.$render = function() {\n                element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));\n              };\n\n              // Listen for change events to enable binding\n              element.on('blur keyup change', function() {\n                scope.$evalAsync(read);\n              });\n              read(); // initialize\n\n              // Write data to the model\n              function read() {\n                var html = element.html();\n                // When we clear the content editable the browser leaves a <br> behind\n                // If strip-br attribute is provided then we strip this out\n                if ( attrs.stripBr && html == '<br>' ) {\n                  html = '';\n                }\n                ngModel.$setViewValue(html);\n              }\n            }\n          };\n        }]);\n    </file>\n    <file name=\"index.html\">\n      <form name=\"myForm\">\n       <div contenteditable\n            name=\"myWidget\" ng-model=\"userContent\"\n            strip-br=\"true\"\n            required>Change me!</div>\n        <span ng-show=\"myForm.myWidget.$error.required\">Required!</span>\n       <hr>\n       <textarea ng-model=\"userContent\"></textarea>\n      </form>\n    </file>\n    <file name=\"protractor.js\" type=\"protractor\">\n    it('should data-bind and become invalid', function() {\n      if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {\n        // SafariDriver can't handle contenteditable\n        // and Firefox driver can't clear contenteditables very well\n        return;\n      }\n      var contentEditable = element(by.css('[contenteditable]'));\n      var content = 'Change me!';\n\n      expect(contentEditable.getText()).toEqual(content);\n\n      contentEditable.clear();\n      contentEditable.sendKeys(protractor.Key.BACK_SPACE);\n      expect(contentEditable.getText()).toEqual('');\n      expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);\n    });\n    </file>\n * </example>\n *\n *\n */\nvar NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',\n    function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {\n  this.$viewValue = Number.NaN;\n  this.$modelValue = Number.NaN;\n  this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.\n  this.$validators = {};\n  this.$asyncValidators = {};\n  this.$parsers = [];\n  this.$formatters = [];\n  this.$viewChangeListeners = [];\n  this.$untouched = true;\n  this.$touched = false;\n  this.$pristine = true;\n  this.$dirty = false;\n  this.$valid = true;\n  this.$invalid = false;\n  this.$error = {}; // keep invalid keys here\n  this.$$success = {}; // keep valid keys here\n  this.$pending = undefined; // keep pending keys here\n  this.$name = $interpolate($attr.name || '', false)($scope);\n\n\n  var parsedNgModel = $parse($attr.ngModel),\n      parsedNgModelAssign = parsedNgModel.assign,\n      ngModelGet = parsedNgModel,\n      ngModelSet = parsedNgModelAssign,\n      pendingDebounce = null,\n      parserValid,\n      ctrl = this;\n\n  this.$$setOptions = function(options) {\n    ctrl.$options = options;\n    if (options && options.getterSetter) {\n      var invokeModelGetter = $parse($attr.ngModel + '()'),\n          invokeModelSetter = $parse($attr.ngModel + '($$$p)');\n\n      ngModelGet = function($scope) {\n        var modelValue = parsedNgModel($scope);\n        if (isFunction(modelValue)) {\n          modelValue = invokeModelGetter($scope);\n        }\n        return modelValue;\n      };\n      ngModelSet = function($scope, newValue) {\n        if (isFunction(parsedNgModel($scope))) {\n          invokeModelSetter($scope, {$$$p: ctrl.$modelValue});\n        } else {\n          parsedNgModelAssign($scope, ctrl.$modelValue);\n        }\n      };\n    } else if (!parsedNgModel.assign) {\n      throw $ngModelMinErr('nonassign', \"Expression '{0}' is non-assignable. Element: {1}\",\n          $attr.ngModel, startingTag($element));\n    }\n  };\n\n  /**\n   * @ngdoc method\n   * @name ngModel.NgModelController#$render\n   *\n   * @description\n   * Called when the view needs to be updated. It is expected that the user of the ng-model\n   * directive will implement this method.\n   *\n   * The `$render()` method is invoked in the following situations:\n   *\n   * * `$rollbackViewValue()` is called.  If we are rolling back the view value to the last\n   *   committed value then `$render()` is called to update the input control.\n   * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and\n   *   the `$viewValue` are different to last time.\n   *\n   * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of\n   * `$modelValue` and `$viewValue` are actually different to their previous value. If `$modelValue`\n   * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be\n   * invoked if you only change a property on the objects.\n   */\n  this.$render = noop;\n\n  /**\n   * @ngdoc method\n   * @name ngModel.NgModelController#$isEmpty\n   *\n   * @description\n   * This is called when we need to determine if the value of an input is empty.\n   *\n   * For instance, the required directive does this to work out if the input has data or not.\n   *\n   * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.\n   *\n   * You can override this for input directives whose concept of being empty is different to the\n   * default. The `checkboxInputType` directive does this because in its case a value of `false`\n   * implies empty.\n   *\n   * @param {*} value The value of the input to check for emptiness.\n   * @returns {boolean} True if `value` is \"empty\".\n   */\n  this.$isEmpty = function(value) {\n    return isUndefined(value) || value === '' || value === null || value !== value;\n  };\n\n  var parentForm = $element.inheritedData('$formController') || nullFormCtrl,\n      currentValidationRunId = 0;\n\n  /**\n   * @ngdoc method\n   * @name ngModel.NgModelController#$setValidity\n   *\n   * @description\n   * Change the validity state, and notify the form.\n   *\n   * This method can be called within $parsers/$formatters or a custom validation implementation.\n   * However, in most cases it should be sufficient to use the `ngModel.$validators` and\n   * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.\n   *\n   * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned\n   *        to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`\n   *        (for unfulfilled `$asyncValidators`), so that it is available for data-binding.\n   *        The `validationErrorKey` should be in camelCase and will get converted into dash-case\n   *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`\n   *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .\n   * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),\n   *                          or skipped (null). Pending is used for unfulfilled `$asyncValidators`.\n   *                          Skipped is used by Angular when validators do not run because of parse errors and\n   *                          when `$asyncValidators` do not run because any of the `$validators` failed.\n   */\n  addSetValidityMethod({\n    ctrl: this,\n    $element: $element,\n    set: function(object, property) {\n      object[property] = true;\n    },\n    unset: function(object, property) {\n      delete object[property];\n    },\n    parentForm: parentForm,\n    $animate: $animate\n  });\n\n  /**\n   * @ngdoc method\n   * @name ngModel.NgModelController#$setPristine\n   *\n   * @description\n   * Sets the control to its pristine state.\n   *\n   * This method can be called to remove the `ng-dirty` class and set the control to its pristine\n   * state (`ng-pristine` class). A model is considered to be pristine when the control\n   * has not been changed from when first compiled.\n   */\n  this.$setPristine = function() {\n    ctrl.$dirty = false;\n    ctrl.$pristine = true;\n    $animate.removeClass($element, DIRTY_CLASS);\n    $animate.addClass($element, PRISTINE_CLASS);\n  };\n\n  /**\n   * @ngdoc method\n   * @name ngModel.NgModelController#$setDirty\n   *\n   * @description\n   * Sets the control to its dirty state.\n   *\n   * This method can be called to remove the `ng-pristine` class and set the control to its dirty\n   * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed\n   * from when first compiled.\n   */\n  this.$setDirty = function() {\n    ctrl.$dirty = true;\n    ctrl.$pristine = false;\n    $animate.removeClass($element, PRISTINE_CLASS);\n    $animate.addClass($element, DIRTY_CLASS);\n    parentForm.$setDirty();\n  };\n\n  /**\n   * @ngdoc method\n   * @name ngModel.NgModelController#$setUntouched\n   *\n   * @description\n   * Sets the control to its untouched state.\n   *\n   * This method can be called to remove the `ng-touched` class and set the control to its\n   * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched\n   * by default, however this function can be used to restore that state if the model has\n   * already been touched by the user.\n   */\n  this.$setUntouched = function() {\n    ctrl.$touched = false;\n    ctrl.$untouched = true;\n    $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);\n  };\n\n  /**\n   * @ngdoc method\n   * @name ngModel.NgModelController#$setTouched\n   *\n   * @description\n   * Sets the control to its touched state.\n   *\n   * This method can be called to remove the `ng-untouched` class and set the control to its\n   * touched state (`ng-touched` class). A model is considered to be touched when the user has\n   * first focused the control element and then shifted focus away from the control (blur event).\n   */\n  this.$setTouched = function() {\n    ctrl.$touched = true;\n    ctrl.$untouched = false;\n    $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);\n  };\n\n  /**\n   * @ngdoc method\n   * @name ngModel.NgModelController#$rollbackViewValue\n   *\n   * @description\n   * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,\n   * which may be caused by a pending debounced event or because the input is waiting for a some\n   * future event.\n   *\n   * If you have an input that uses `ng-model-options` to set up debounced events or events such\n   * as blur you can have a situation where there is a period when the `$viewValue`\n   * is out of synch with the ngModel's `$modelValue`.\n   *\n   * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue`\n   * programmatically before these debounced/future events have resolved/occurred, because Angular's\n   * dirty checking mechanism is not able to tell whether the model has actually changed or not.\n   *\n   * The `$rollbackViewValue()` method should be called before programmatically changing the model of an\n   * input which may have such events pending. This is important in order to make sure that the\n   * input field will be updated with the new model value and any pending operations are cancelled.\n   *\n   * <example name=\"ng-model-cancel-update\" module=\"cancel-update-example\">\n   *   <file name=\"app.js\">\n   *     angular.module('cancel-update-example', [])\n   *\n   *     .controller('CancelUpdateController', ['$scope', function($scope) {\n   *       $scope.resetWithCancel = function(e) {\n   *         if (e.keyCode == 27) {\n   *           $scope.myForm.myInput1.$rollbackViewValue();\n   *           $scope.myValue = '';\n   *         }\n   *       };\n   *       $scope.resetWithoutCancel = function(e) {\n   *         if (e.keyCode == 27) {\n   *           $scope.myValue = '';\n   *         }\n   *       };\n   *     }]);\n   *   </file>\n   *   <file name=\"index.html\">\n   *     <div ng-controller=\"CancelUpdateController\">\n   *       <p>Try typing something in each input.  See that the model only updates when you\n   *          blur off the input.\n   *        </p>\n   *        <p>Now see what happens if you start typing then press the Escape key</p>\n   *\n   *       <form name=\"myForm\" ng-model-options=\"{ updateOn: 'blur' }\">\n   *         <p>With $rollbackViewValue()</p>\n   *         <input name=\"myInput1\" ng-model=\"myValue\" ng-keydown=\"resetWithCancel($event)\"><br/>\n   *         myValue: \"{{ myValue }}\"\n   *\n   *         <p>Without $rollbackViewValue()</p>\n   *         <input name=\"myInput2\" ng-model=\"myValue\" ng-keydown=\"resetWithoutCancel($event)\"><br/>\n   *         myValue: \"{{ myValue }}\"\n   *       </form>\n   *     </div>\n   *   </file>\n   * </example>\n   */\n  this.$rollbackViewValue = function() {\n    $timeout.cancel(pendingDebounce);\n    ctrl.$viewValue = ctrl.$$lastCommittedViewValue;\n    ctrl.$render();\n  };\n\n  /**\n   * @ngdoc method\n   * @name ngModel.NgModelController#$validate\n   *\n   * @description\n   * Runs each of the registered validators (first synchronous validators and then\n   * asynchronous validators).\n   * If the validity changes to invalid, the model will be set to `undefined`,\n   * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.\n   * If the validity changes to valid, it will set the model to the last available valid\n   * modelValue, i.e. either the last parsed value or the last value set from the scope.\n   */\n  this.$validate = function() {\n    // ignore $validate before model is initialized\n    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {\n      return;\n    }\n\n    var viewValue = ctrl.$$lastCommittedViewValue;\n    // Note: we use the $$rawModelValue as $modelValue might have been\n    // set to undefined during a view -> model update that found validation\n    // errors. We can't parse the view here, since that could change\n    // the model although neither viewValue nor the model on the scope changed\n    var modelValue = ctrl.$$rawModelValue;\n\n    var prevValid = ctrl.$valid;\n    var prevModelValue = ctrl.$modelValue;\n\n    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;\n\n    ctrl.$$runValidators(modelValue, viewValue, function(allValid) {\n      // If there was no change in validity, don't update the model\n      // This prevents changing an invalid modelValue to undefined\n      if (!allowInvalid && prevValid !== allValid) {\n        // Note: Don't check ctrl.$valid here, as we could have\n        // external validators (e.g. calculated on the server),\n        // that just call $setValidity and need the model value\n        // to calculate their validity.\n        ctrl.$modelValue = allValid ? modelValue : undefined;\n\n        if (ctrl.$modelValue !== prevModelValue) {\n          ctrl.$$writeModelToScope();\n        }\n      }\n    });\n\n  };\n\n  this.$$runValidators = function(modelValue, viewValue, doneCallback) {\n    currentValidationRunId++;\n    var localValidationRunId = currentValidationRunId;\n\n    // check parser error\n    if (!processParseErrors()) {\n      validationDone(false);\n      return;\n    }\n    if (!processSyncValidators()) {\n      validationDone(false);\n      return;\n    }\n    processAsyncValidators();\n\n    function processParseErrors() {\n      var errorKey = ctrl.$$parserName || 'parse';\n      if (parserValid === undefined) {\n        setValidity(errorKey, null);\n      } else {\n        if (!parserValid) {\n          forEach(ctrl.$validators, function(v, name) {\n            setValidity(name, null);\n          });\n          forEach(ctrl.$asyncValidators, function(v, name) {\n            setValidity(name, null);\n          });\n        }\n        // Set the parse error last, to prevent unsetting it, should a $validators key == parserName\n        setValidity(errorKey, parserValid);\n        return parserValid;\n      }\n      return true;\n    }\n\n    function processSyncValidators() {\n      var syncValidatorsValid = true;\n      forEach(ctrl.$validators, function(validator, name) {\n        var result = validator(modelValue, viewValue);\n        syncValidatorsValid = syncValidatorsValid && result;\n        setValidity(name, result);\n      });\n      if (!syncValidatorsValid) {\n        forEach(ctrl.$asyncValidators, function(v, name) {\n          setValidity(name, null);\n        });\n        return false;\n      }\n      return true;\n    }\n\n    function processAsyncValidators() {\n      var validatorPromises = [];\n      var allValid = true;\n      forEach(ctrl.$asyncValidators, function(validator, name) {\n        var promise = validator(modelValue, viewValue);\n        if (!isPromiseLike(promise)) {\n          throw $ngModelMinErr(\"$asyncValidators\",\n            \"Expected asynchronous validator to return a promise but got '{0}' instead.\", promise);\n        }\n        setValidity(name, undefined);\n        validatorPromises.push(promise.then(function() {\n          setValidity(name, true);\n        }, function(error) {\n          allValid = false;\n          setValidity(name, false);\n        }));\n      });\n      if (!validatorPromises.length) {\n        validationDone(true);\n      } else {\n        $q.all(validatorPromises).then(function() {\n          validationDone(allValid);\n        }, noop);\n      }\n    }\n\n    function setValidity(name, isValid) {\n      if (localValidationRunId === currentValidationRunId) {\n        ctrl.$setValidity(name, isValid);\n      }\n    }\n\n    function validationDone(allValid) {\n      if (localValidationRunId === currentValidationRunId) {\n\n        doneCallback(allValid);\n      }\n    }\n  };\n\n  /**\n   * @ngdoc method\n   * @name ngModel.NgModelController#$commitViewValue\n   *\n   * @description\n   * Commit a pending update to the `$modelValue`.\n   *\n   * Updates may be pending by a debounced event or because the input is waiting for a some future\n   * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`\n   * usually handles calling this in response to input events.\n   */\n  this.$commitViewValue = function() {\n    var viewValue = ctrl.$viewValue;\n\n    $timeout.cancel(pendingDebounce);\n\n    // If the view value has not changed then we should just exit, except in the case where there is\n    // a native validator on the element. In this case the validation state may have changed even though\n    // the viewValue has stayed empty.\n    if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {\n      return;\n    }\n    ctrl.$$lastCommittedViewValue = viewValue;\n\n    // change to dirty\n    if (ctrl.$pristine) {\n      this.$setDirty();\n    }\n    this.$$parseAndValidate();\n  };\n\n  this.$$parseAndValidate = function() {\n    var viewValue = ctrl.$$lastCommittedViewValue;\n    var modelValue = viewValue;\n    parserValid = isUndefined(modelValue) ? undefined : true;\n\n    if (parserValid) {\n      for (var i = 0; i < ctrl.$parsers.length; i++) {\n        modelValue = ctrl.$parsers[i](modelValue);\n        if (isUndefined(modelValue)) {\n          parserValid = false;\n          break;\n        }\n      }\n    }\n    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {\n      // ctrl.$modelValue has not been touched yet...\n      ctrl.$modelValue = ngModelGet($scope);\n    }\n    var prevModelValue = ctrl.$modelValue;\n    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;\n    ctrl.$$rawModelValue = modelValue;\n\n    if (allowInvalid) {\n      ctrl.$modelValue = modelValue;\n      writeToModelIfNeeded();\n    }\n\n    // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.\n    // This can happen if e.g. $setViewValue is called from inside a parser\n    ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {\n      if (!allowInvalid) {\n        // Note: Don't check ctrl.$valid here, as we could have\n        // external validators (e.g. calculated on the server),\n        // that just call $setValidity and need the model value\n        // to calculate their validity.\n        ctrl.$modelValue = allValid ? modelValue : undefined;\n        writeToModelIfNeeded();\n      }\n    });\n\n    function writeToModelIfNeeded() {\n      if (ctrl.$modelValue !== prevModelValue) {\n        ctrl.$$writeModelToScope();\n      }\n    }\n  };\n\n  this.$$writeModelToScope = function() {\n    ngModelSet($scope, ctrl.$modelValue);\n    forEach(ctrl.$viewChangeListeners, function(listener) {\n      try {\n        listener();\n      } catch (e) {\n        $exceptionHandler(e);\n      }\n    });\n  };\n\n  /**\n   * @ngdoc method\n   * @name ngModel.NgModelController#$setViewValue\n   *\n   * @description\n   * Update the view value.\n   *\n   * This method should be called when an input directive want to change the view value; typically,\n   * this is done from within a DOM event handler.\n   *\n   * For example {@link ng.directive:input input} calls it when the value of the input changes and\n   * {@link ng.directive:select select} calls it when an option is selected.\n   *\n   * If the new `value` is an object (rather than a string or a number), we should make a copy of the\n   * object before passing it to `$setViewValue`.  This is because `ngModel` does not perform a deep\n   * watch of objects, it only looks for a change of identity. If you only change the property of\n   * the object then ngModel will not realise that the object has changed and will not invoke the\n   * `$parsers` and `$validators` pipelines.\n   *\n   * For this reason, you should not change properties of the copy once it has been passed to\n   * `$setViewValue`. Otherwise you may cause the model value on the scope to change incorrectly.\n   *\n   * When this method is called, the new `value` will be staged for committing through the `$parsers`\n   * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged\n   * value sent directly for processing, finally to be applied to `$modelValue` and then the\n   * **expression** specified in the `ng-model` attribute.\n   *\n   * Lastly, all the registered change listeners, in the `$viewChangeListeners` list, are called.\n   *\n   * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`\n   * and the `default` trigger is not listed, all those actions will remain pending until one of the\n   * `updateOn` events is triggered on the DOM element.\n   * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}\n   * directive is used with a custom debounce for this particular event.\n   *\n   * Note that calling this function does not trigger a `$digest`.\n   *\n   * @param {string} value Value from the view.\n   * @param {string} trigger Event that triggered the update.\n   */\n  this.$setViewValue = function(value, trigger) {\n    ctrl.$viewValue = value;\n    if (!ctrl.$options || ctrl.$options.updateOnDefault) {\n      ctrl.$$debounceViewValueCommit(trigger);\n    }\n  };\n\n  this.$$debounceViewValueCommit = function(trigger) {\n    var debounceDelay = 0,\n        options = ctrl.$options,\n        debounce;\n\n    if (options && isDefined(options.debounce)) {\n      debounce = options.debounce;\n      if (isNumber(debounce)) {\n        debounceDelay = debounce;\n      } else if (isNumber(debounce[trigger])) {\n        debounceDelay = debounce[trigger];\n      } else if (isNumber(debounce['default'])) {\n        debounceDelay = debounce['default'];\n      }\n    }\n\n    $timeout.cancel(pendingDebounce);\n    if (debounceDelay) {\n      pendingDebounce = $timeout(function() {\n        ctrl.$commitViewValue();\n      }, debounceDelay);\n    } else if ($rootScope.$$phase) {\n      ctrl.$commitViewValue();\n    } else {\n      $scope.$apply(function() {\n        ctrl.$commitViewValue();\n      });\n    }\n  };\n\n  // model -> value\n  // Note: we cannot use a normal scope.$watch as we want to detect the following:\n  // 1. scope value is 'a'\n  // 2. user enters 'b'\n  // 3. ng-change kicks in and reverts scope value to 'a'\n  //    -> scope value did not change since the last digest as\n  //       ng-change executes in apply phase\n  // 4. view should be changed back to 'a'\n  $scope.$watch(function ngModelWatch() {\n    var modelValue = ngModelGet($scope);\n\n    // if scope model value and ngModel value are out of sync\n    // TODO(perf): why not move this to the action fn?\n    if (modelValue !== ctrl.$modelValue) {\n      ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;\n      parserValid = undefined;\n\n      var formatters = ctrl.$formatters,\n          idx = formatters.length;\n\n      var viewValue = modelValue;\n      while (idx--) {\n        viewValue = formatters[idx](viewValue);\n      }\n      if (ctrl.$viewValue !== viewValue) {\n        ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;\n        ctrl.$render();\n\n        ctrl.$$runValidators(modelValue, viewValue, noop);\n      }\n    }\n\n    return modelValue;\n  });\n}];\n\n\n/**\n * @ngdoc directive\n * @name ngModel\n *\n * @element input\n * @priority 1\n *\n * @description\n * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a\n * property on the scope using {@link ngModel.NgModelController NgModelController},\n * which is created and exposed by this directive.\n *\n * `ngModel` is responsible for:\n *\n * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`\n *   require.\n * - Providing validation behavior (i.e. required, number, email, url).\n * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).\n * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.\n * - Registering the control with its parent {@link ng.directive:form form}.\n *\n * Note: `ngModel` will try to bind to the property given by evaluating the expression on the\n * current scope. If the property doesn't already exist on this scope, it will be created\n * implicitly and added to the scope.\n *\n * For best practices on using `ngModel`, see:\n *\n *  - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)\n *\n * For basic examples, how to use `ngModel`, see:\n *\n *  - {@link ng.directive:input input}\n *    - {@link input[text] text}\n *    - {@link input[checkbox] checkbox}\n *    - {@link input[radio] radio}\n *    - {@link input[number] number}\n *    - {@link input[email] email}\n *    - {@link input[url] url}\n *    - {@link input[date] date}\n *    - {@link input[datetime-local] datetime-local}\n *    - {@link input[time] time}\n *    - {@link input[month] month}\n *    - {@link input[week] week}\n *  - {@link ng.directive:select select}\n *  - {@link ng.directive:textarea textarea}\n *\n * # CSS classes\n * The following CSS classes are added and removed on the associated input/select/textarea element\n * depending on the validity of the model.\n *\n *  - `ng-valid`: the model is valid\n *  - `ng-invalid`: the model is invalid\n *  - `ng-valid-[key]`: for each valid key added by `$setValidity`\n *  - `ng-invalid-[key]`: for each invalid key added by `$setValidity`\n *  - `ng-pristine`: the control hasn't been interacted with yet\n *  - `ng-dirty`: the control has been interacted with\n *  - `ng-touched`: the control has been blurred\n *  - `ng-untouched`: the control hasn't been blurred\n *  - `ng-pending`: any `$asyncValidators` are unfulfilled\n *\n * Keep in mind that ngAnimate can detect each of these classes when added and removed.\n *\n * ## Animation Hooks\n *\n * Animations within models are triggered when any of the associated CSS classes are added and removed\n * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,\n * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.\n * The animations that are triggered within ngModel are similar to how they work in ngClass and\n * animations can be hooked into using CSS transitions, keyframes as well as JS animations.\n *\n * The following example shows a simple way to utilize CSS transitions to style an input element\n * that has been rendered as invalid after it has been validated:\n *\n * <pre>\n * //be sure to include ngAnimate as a module to hook into more\n * //advanced animations\n * .my-input {\n *   transition:0.5s linear all;\n *   background: white;\n * }\n * .my-input.ng-invalid {\n *   background: red;\n *   color:white;\n * }\n * </pre>\n *\n * @example\n * <example deps=\"angular-animate.js\" animations=\"true\" fixBase=\"true\" module=\"inputExample\">\n     <file name=\"index.html\">\n       <script>\n        angular.module('inputExample', [])\n          .controller('ExampleController', ['$scope', function($scope) {\n            $scope.val = '1';\n          }]);\n       </script>\n       <style>\n         .my-input {\n           -webkit-transition:all linear 0.5s;\n           transition:all linear 0.5s;\n           background: transparent;\n         }\n         .my-input.ng-invalid {\n           color:white;\n           background: red;\n         }\n       </style>\n       Update input to see transitions when valid/invalid.\n       Integer is a valid value.\n       <form name=\"testForm\" ng-controller=\"ExampleController\">\n         <input ng-model=\"val\" ng-pattern=\"/^\\d+$/\" name=\"anim\" class=\"my-input\" />\n       </form>\n     </file>\n * </example>\n *\n * ## Binding to a getter/setter\n *\n * Sometimes it's helpful to bind `ngModel` to a getter/setter function.  A getter/setter is a\n * function that returns a representation of the model when called with zero arguments, and sets\n * the internal state of a model when called with an argument. It's sometimes useful to use this\n * for models that have an internal representation that's different than what the model exposes\n * to the view.\n *\n * <div class=\"alert alert-success\">\n * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more\n * frequently than other parts of your code.\n * </div>\n *\n * You use this behavior by adding `ng-model-options=\"{ getterSetter: true }\"` to an element that\n * has `ng-model` attached to it. You can also add `ng-model-options=\"{ getterSetter: true }\"` to\n * a `<form>`, which will enable this behavior for all `<input>`s within it. See\n * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.\n *\n * The following example shows how to use `ngModel` with a getter/setter:\n *\n * @example\n * <example name=\"ngModel-getter-setter\" module=\"getterSetterExample\">\n     <file name=\"index.html\">\n       <div ng-controller=\"ExampleController\">\n         <form name=\"userForm\">\n           Name:\n           <input type=\"text\" name=\"userName\"\n                  ng-model=\"user.name\"\n                  ng-model-options=\"{ getterSetter: true }\" />\n         </form>\n         <pre>user.name = <span ng-bind=\"user.name()\"></span></pre>\n       </div>\n     </file>\n     <file name=\"app.js\">\n       angular.module('getterSetterExample', [])\n         .controller('ExampleController', ['$scope', function($scope) {\n           var _name = 'Brian';\n           $scope.user = {\n             name: function(newName) {\n               if (angular.isDefined(newName)) {\n                 _name = newName;\n               }\n               return _name;\n             }\n           };\n         }]);\n     </file>\n * </example>\n */\nvar ngModelDirective = ['$rootScope', function($rootScope) {\n  return {\n    restrict: 'A',\n    require: ['ngModel', '^?form', '^?ngModelOptions'],\n    controller: NgModelController,\n    // Prelink needs to run before any input directive\n    // so that we can set the NgModelOptions in NgModelController\n    // before anyone else uses it.\n    priority: 1,\n    compile: function ngModelCompile(element) {\n      // Setup initial state of the control\n      element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);\n\n      return {\n        pre: function ngModelPreLink(scope, element, attr, ctrls) {\n          var modelCtrl = ctrls[0],\n              formCtrl = ctrls[1] || nullFormCtrl;\n\n          modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);\n\n          // notify others, especially parent forms\n          formCtrl.$addControl(modelCtrl);\n\n          attr.$observe('name', function(newValue) {\n            if (modelCtrl.$name !== newValue) {\n              formCtrl.$$renameControl(modelCtrl, newValue);\n            }\n          });\n\n          scope.$on('$destroy', function() {\n            formCtrl.$removeControl(modelCtrl);\n          });\n        },\n        post: function ngModelPostLink(scope, element, attr, ctrls) {\n          var modelCtrl = ctrls[0];\n          if (modelCtrl.$options && modelCtrl.$options.updateOn) {\n            element.on(modelCtrl.$options.updateOn, function(ev) {\n              modelCtrl.$$debounceViewValueCommit(ev && ev.type);\n            });\n          }\n\n          element.on('blur', function(ev) {\n            if (modelCtrl.$touched) return;\n\n            if ($rootScope.$$phase) {\n              scope.$evalAsync(modelCtrl.$setTouched);\n            } else {\n              scope.$apply(modelCtrl.$setTouched);\n            }\n          });\n        }\n      };\n    }\n  };\n}];\n\nvar DEFAULT_REGEXP = /(\\s+|^)default(\\s+|$)/;\n\n/**\n * @ngdoc directive\n * @name ngModelOptions\n *\n * @description\n * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of\n * events that will trigger a model update and/or a debouncing delay so that the actual update only\n * takes place when a timer expires; this timer will be reset after another change takes place.\n *\n * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might\n * be different than the value in the actual model. This means that if you update the model you\n * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in\n * order to make sure it is synchronized with the model and that any debounced action is canceled.\n *\n * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}\n * method is by making sure the input is placed inside a form that has a `name` attribute. This is\n * important because `form` controllers are published to the related scope under the name in their\n * `name` attribute.\n *\n * Any pending changes will take place immediately when an enclosing form is submitted via the\n * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`\n * to have access to the updated model.\n *\n * `ngModelOptions` has an effect on the element it's declared on and its descendants.\n *\n * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:\n *   - `updateOn`: string specifying which event should the input be bound to. You can set several\n *     events using an space delimited list. There is a special event called `default` that\n *     matches the default events belonging of the control.\n *   - `debounce`: integer value which contains the debounce model update value in milliseconds. A\n *     value of 0 triggers an immediate update. If an object is supplied instead, you can specify a\n *     custom value for each event. For example:\n *     `ng-model-options=\"{ updateOn: 'default blur', debounce: {'default': 500, 'blur': 0} }\"`\n *   - `allowInvalid`: boolean value which indicates that the model can be set with values that did\n *     not validate correctly instead of the default behavior of setting the model to undefined.\n *   - `getterSetter`: boolean value which determines whether or not to treat functions bound to\n       `ngModel` as getters/setters.\n *   - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for\n *     `<input type=\"date\">`, `<input type=\"time\">`, ... . Right now, the only supported value is `'UTC'`,\n *     otherwise the default timezone of the browser will be used.\n *\n * @example\n\n  The following example shows how to override immediate updates. Changes on the inputs within the\n  form will update the model only when the control loses focus (blur event). If `escape` key is\n  pressed while the input field is focused, the value is reset to the value in the current model.\n\n  <example name=\"ngModelOptions-directive-blur\" module=\"optionsExample\">\n    <file name=\"index.html\">\n      <div ng-controller=\"ExampleController\">\n        <form name=\"userForm\">\n          Name:\n          <input type=\"text\" name=\"userName\"\n                 ng-model=\"user.name\"\n                 ng-model-options=\"{ updateOn: 'blur' }\"\n                 ng-keyup=\"cancel($event)\" /><br />\n\n          Other data:\n          <input type=\"text\" ng-model=\"user.data\" /><br />\n        </form>\n        <pre>user.name = <span ng-bind=\"user.name\"></span></pre>\n      </div>\n    </file>\n    <file name=\"app.js\">\n      angular.module('optionsExample', [])\n        .controller('ExampleController', ['$scope', function($scope) {\n          $scope.user = { name: 'say', data: '' };\n\n          $scope.cancel = function(e) {\n            if (e.keyCode == 27) {\n              $scope.userForm.userName.$rollbackViewValue();\n            }\n          };\n        }]);\n    </file>\n    <file name=\"protractor.js\" type=\"protractor\">\n      var model = element(by.binding('user.name'));\n      var input = element(by.model('user.name'));\n      var other = element(by.model('user.data'));\n\n      it('should allow custom events', function() {\n        input.sendKeys(' hello');\n        input.click();\n        expect(model.getText()).toEqual('say');\n        other.click();\n        expect(model.getText()).toEqual('say hello');\n      });\n\n      it('should $rollbackViewValue when model changes', function() {\n        input.sendKeys(' hello');\n        expect(input.getAttribute('value')).toEqual('say hello');\n        input.sendKeys(protractor.Key.ESCAPE);\n        expect(input.getAttribute('value')).toEqual('say');\n        other.click();\n        expect(model.getText()).toEqual('say');\n      });\n    </file>\n  </example>\n\n  This one shows how to debounce model changes. Model will be updated only 1 sec after last change.\n  If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.\n\n  <example name=\"ngModelOptions-directive-debounce\" module=\"optionsExample\">\n    <file name=\"index.html\">\n      <div ng-controller=\"ExampleController\">\n        <form name=\"userForm\">\n          Name:\n          <input type=\"text\" name=\"userName\"\n                 ng-model=\"user.name\"\n                 ng-model-options=\"{ debounce: 1000 }\" />\n          <button ng-click=\"userForm.userName.$rollbackViewValue(); user.name=''\">Clear</button><br />\n        </form>\n        <pre>user.name = <span ng-bind=\"user.name\"></span></pre>\n      </div>\n    </file>\n    <file name=\"app.js\">\n      angular.module('optionsExample', [])\n        .controller('ExampleController', ['$scope', function($scope) {\n          $scope.user = { name: 'say' };\n        }]);\n    </file>\n  </example>\n\n  This one shows how to bind to getter/setters:\n\n  <example name=\"ngModelOptions-directive-getter-setter\" module=\"getterSetterExample\">\n    <file name=\"index.html\">\n      <div ng-controller=\"ExampleController\">\n        <form name=\"userForm\">\n          Name:\n          <input type=\"text\" name=\"userName\"\n                 ng-model=\"user.name\"\n                 ng-model-options=\"{ getterSetter: true }\" />\n        </form>\n        <pre>user.name = <span ng-bind=\"user.name()\"></span></pre>\n      </div>\n    </file>\n    <file name=\"app.js\">\n      angular.module('getterSetterExample', [])\n        .controller('ExampleController', ['$scope', function($scope) {\n          var _name = 'Brian';\n          $scope.user = {\n            name: function(newName) {\n              return angular.isDefined(newName) ? (_name = newName) : _name;\n            }\n          };\n        }]);\n    </file>\n  </example>\n */\nvar ngModelOptionsDirective = function() {\n  return {\n    restrict: 'A',\n    controller: ['$scope', '$attrs', function($scope, $attrs) {\n      var that = this;\n      this.$options = $scope.$eval($attrs.ngModelOptions);\n      // Allow adding/overriding bound events\n      if (this.$options.updateOn !== undefined) {\n        this.$options.updateOnDefault = false;\n        // extract \"default\" pseudo-event from list of events that can trigger a model update\n        this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {\n          that.$options.updateOnDefault = true;\n          return ' ';\n        }));\n      } else {\n        this.$options.updateOnDefault = true;\n      }\n    }]\n  };\n};\n\n\n\n// helper methods\nfunction addSetValidityMethod(context) {\n  var ctrl = context.ctrl,\n      $element = context.$element,\n      classCache = {},\n      set = context.set,\n      unset = context.unset,\n      parentForm = context.parentForm,\n      $animate = context.$animate;\n\n  classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));\n\n  ctrl.$setValidity = setValidity;\n\n  function setValidity(validationErrorKey, state, controller) {\n    if (state === undefined) {\n      createAndSet('$pending', validationErrorKey, controller);\n    } else {\n      unsetAndCleanup('$pending', validationErrorKey, controller);\n    }\n    if (!isBoolean(state)) {\n      unset(ctrl.$error, validationErrorKey, controller);\n      unset(ctrl.$$success, validationErrorKey, controller);\n    } else {\n      if (state) {\n        unset(ctrl.$error, validationErrorKey, controller);\n        set(ctrl.$$success, validationErrorKey, controller);\n      } else {\n        set(ctrl.$error, validationErrorKey, controller);\n        unset(ctrl.$$success, validationErrorKey, controller);\n      }\n    }\n    if (ctrl.$pending) {\n      cachedToggleClass(PENDING_CLASS, true);\n      ctrl.$valid = ctrl.$invalid = undefined;\n      toggleValidationCss('', null);\n    } else {\n      cachedToggleClass(PENDING_CLASS, false);\n      ctrl.$valid = isObjectEmpty(ctrl.$error);\n      ctrl.$invalid = !ctrl.$valid;\n      toggleValidationCss('', ctrl.$valid);\n    }\n\n    // re-read the state as the set/unset methods could have\n    // combined state in ctrl.$error[validationError] (used for forms),\n    // where setting/unsetting only increments/decrements the value,\n    // and does not replace it.\n    var combinedState;\n    if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {\n      combinedState = undefined;\n    } else if (ctrl.$error[validationErrorKey]) {\n      combinedState = false;\n    } else if (ctrl.$$success[validationErrorKey]) {\n      combinedState = true;\n    } else {\n      combinedState = null;\n    }\n\n    toggleValidationCss(validationErrorKey, combinedState);\n    parentForm.$setValidity(validationErrorKey, combinedState, ctrl);\n  }\n\n  function createAndSet(name, value, controller) {\n    if (!ctrl[name]) {\n      ctrl[name] = {};\n    }\n    set(ctrl[name], value, controller);\n  }\n\n  function unsetAndCleanup(name, value, controller) {\n    if (ctrl[name]) {\n      unset(ctrl[name], value, controller);\n    }\n    if (isObjectEmpty(ctrl[name])) {\n      ctrl[name] = undefined;\n    }\n  }\n\n  function cachedToggleClass(className, switchValue) {\n    if (switchValue && !classCache[className]) {\n      $animate.addClass($element, className);\n      classCache[className] = true;\n    } else if (!switchValue && classCache[className]) {\n      $animate.removeClass($element, className);\n      classCache[className] = false;\n    }\n  }\n\n  function toggleValidationCss(validationErrorKey, isValid) {\n    validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';\n\n    cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);\n    cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);\n  }\n}\n\nfunction isObjectEmpty(obj) {\n  if (obj) {\n    for (var prop in obj) {\n      return false;\n    }\n  }\n  return true;\n}\n\n/**\n * @ngdoc directive\n * @name ngNonBindable\n * @restrict AC\n * @priority 1000\n *\n * @description\n * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current\n * DOM element. This is useful if the element contains what appears to be Angular directives and\n * bindings but which should be ignored by Angular. This could be the case if you have a site that\n * displays snippets of code, for instance.\n *\n * @element ANY\n *\n * @example\n * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,\n * but the one wrapped in `ngNonBindable` is left alone.\n *\n * @example\n    <example>\n      <file name=\"index.html\">\n        <div>Normal: {{1 + 2}}</div>\n        <div ng-non-bindable>Ignored: {{1 + 2}}</div>\n      </file>\n      <file name=\"protractor.js\" type=\"protractor\">\n       it('should check ng-non-bindable', function() {\n         expect(element(by.binding('1 + 2')).getText()).toContain('3');\n         expect(element.all(by.css('div')).last().getText()).toMatch(/1 \\+ 2/);\n       });\n      </file>\n    </example>\n */\nvar ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });\n\n/**\n * @ngdoc directive\n * @name ngPluralize\n * @restrict EA\n *\n * @description\n * `ngPluralize` is a directive that displays messages according to en-US localization rules.\n * These rules are bundled with angular.js, but can be overridden\n * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive\n * by specifying the mappings between\n * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)\n * and the strings to be displayed.\n *\n * # Plural categories and explicit number rules\n * There are two\n * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)\n * in Angular's default en-US locale: \"one\" and \"other\".\n *\n * While a plural category may match many numbers (for example, in en-US locale, \"other\" can match\n * any number that is not 1), an explicit number rule can only match one number. For example, the\n * explicit number rule for \"3\" matches the number 3. There are examples of plural categories\n * and explicit number rules throughout the rest of this documentation.\n *\n * # Configuring ngPluralize\n * You configure ngPluralize by providing 2 attributes: `count` and `when`.\n * You can also provide an optional attribute, `offset`.\n *\n * The value of the `count` attribute can be either a string or an {@link guide/expression\n * Angular expression}; these are evaluated on the current scope for its bound value.\n *\n * The `when` attribute specifies the mappings between plural categories and the actual\n * string to be displayed. The value of the attribute should be a JSON object.\n *\n * The following example shows how to configure ngPluralize:\n *\n * ```html\n * <ng-pluralize count=\"personCount\"\n                 when=\"{'0': 'Nobody is viewing.',\n *                      'one': '1 person is viewing.',\n *                      'other': '{} people are viewing.'}\">\n * </ng-pluralize>\n *```\n *\n * In the example, `\"0: Nobody is viewing.\"` is an explicit number rule. If you did not\n * specify this rule, 0 would be matched to the \"other\" category and \"0 people are viewing\"\n * would be shown instead of \"Nobody is viewing\". You can specify an explicit number rule for\n * other numbers, for example 12, so that instead of showing \"12 people are viewing\", you can\n * show \"a dozen people are viewing\".\n *\n * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted\n * into pluralized strings. In the previous example, Angular will replace `{}` with\n * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder\n * for <span ng-non-bindable>{{numberExpression}}</span>.\n *\n * # Configuring ngPluralize with offset\n * The `offset` attribute allows further customization of pluralized text, which can result in\n * a better user experience. For example, instead of the message \"4 people are viewing this document\",\n * you might display \"John, Kate and 2 others are viewing this document\".\n * The offset attribute allows you to offset a number by any desired value.\n * Let's take a look at an example:\n *\n * ```html\n * <ng-pluralize count=\"personCount\" offset=2\n *               when=\"{'0': 'Nobody is viewing.',\n *                      '1': '{{person1}} is viewing.',\n *                      '2': '{{person1}} and {{person2}} are viewing.',\n *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',\n *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}\">\n * </ng-pluralize>\n * ```\n *\n * Notice that we are still using two plural categories(one, other), but we added\n * three explicit number rules 0, 1 and 2.\n * When one person, perhaps John, views the document, \"John is viewing\" will be shown.\n * When three people view the document, no explicit number rule is found, so\n * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.\n * In this case, plural category 'one' is matched and \"John, Mary and one other person are viewing\"\n * is shown.\n *\n * Note that when you specify offsets, you must provide explicit number rules for\n * numbers from 0 up to and including the offset. If you use an offset of 3, for example,\n * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for\n * plural categories \"one\" and \"other\".\n *\n * @param {string|expression} count The variable to be bound to.\n * @param {string} when The mapping between plural category to its corresponding strings.\n * @param {number=} offset Offset to deduct from the total number.\n *\n * @example\n    <example module=\"pluralizeExample\">\n      <file name=\"index.html\">\n        <script>\n          angular.module('pluralizeExample', [])\n            .controller('ExampleController', ['$scope', function($scope) {\n              $scope.person1 = 'Igor';\n              $scope.person2 = 'Misko';\n              $scope.personCount = 1;\n            }]);\n        </script>\n        <div ng-controller=\"ExampleController\">\n          Person 1:<input type=\"text\" ng-model=\"person1\" value=\"Igor\" /><br/>\n          Person 2:<input type=\"text\" ng-model=\"person2\" value=\"Misko\" /><br/>\n          Number of People:<input type=\"text\" ng-model=\"personCount\" value=\"1\" /><br/>\n\n          <!--- Example with simple pluralization rules for en locale --->\n          Without Offset:\n          <ng-pluralize count=\"personCount\"\n                        when=\"{'0': 'Nobody is viewing.',\n                               'one': '1 person is viewing.',\n                               'other': '{} people are viewing.'}\">\n          </ng-pluralize><br>\n\n          <!--- Example with offset --->\n          With Offset(2):\n          <ng-pluralize count=\"personCount\" offset=2\n                        when=\"{'0': 'Nobody is viewing.',\n                               '1': '{{person1}} is viewing.',\n                               '2': '{{person1}} and {{person2}} are viewing.',\n                               'one': '{{person1}}, {{person2}} and one other person are viewing.',\n                               'other': '{{person1}}, {{person2}} and {} other people are viewing.'}\">\n          </ng-pluralize>\n        </div>\n      </file>\n      <file name=\"protractor.js\" type=\"protractor\">\n        it('should show correct pluralized string', function() {\n          var withoutOffset = element.all(by.css('ng-pluralize')).get(0);\n          var withOffset = element.all(by.css('ng-pluralize')).get(1);\n          var countInput = element(by.model('personCount'));\n\n          expect(withoutOffset.getText()).toEqual('1 person is viewing.');\n          expect(withOffset.getText()).toEqual('Igor is viewing.');\n\n          countInput.clear();\n          countInput.sendKeys('0');\n\n          expect(withoutOffset.getText()).toEqual('Nobody is viewing.');\n          expect(withOffset.getText()).toEqual('Nobody is viewing.');\n\n          countInput.clear();\n          countInput.sendKeys('2');\n\n          expect(withoutOffset.getText()).toEqual('2 people are viewing.');\n          expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');\n\n          countInput.clear();\n          countInput.sendKeys('3');\n\n          expect(withoutOffset.getText()).toEqual('3 people are viewing.');\n          expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');\n\n          countInput.clear();\n          countInput.sendKeys('4');\n\n          expect(withoutOffset.getText()).toEqual('4 people are viewing.');\n          expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');\n        });\n        it('should show data-bound names', function() {\n          var withOffset = element.all(by.css('ng-pluralize')).get(1);\n          var personCount = element(by.model('personCount'));\n          var person1 = element(by.model('person1'));\n          var person2 = element(by.model('person2'));\n          personCount.clear();\n          personCount.sendKeys('4');\n          person1.clear();\n          person1.sendKeys('Di');\n          person2.clear();\n          person2.sendKeys('Vojta');\n          expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');\n        });\n      </file>\n    </example>\n */\nvar ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) {\n  var BRACE = /{}/g,\n      IS_WHEN = /^when(Minus)?(.+)$/;\n\n  return {\n    restrict: 'EA',\n    link: function(scope, element, attr) {\n      var numberExp = attr.count,\n          whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs\n          offset = attr.offset || 0,\n          whens = scope.$eval(whenExp) || {},\n          whensExpFns = {},\n          startSymbol = $interpolate.startSymbol(),\n          endSymbol = $interpolate.endSymbol(),\n          braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,\n          watchRemover = angular.noop,\n          lastCount;\n\n      forEach(attr, function(expression, attributeName) {\n        var tmpMatch = IS_WHEN.exec(attributeName);\n        if (tmpMatch) {\n          var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);\n          whens[whenKey] = element.attr(attr.$attr[attributeName]);\n        }\n      });\n      forEach(whens, function(expression, key) {\n        whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));\n\n      });\n\n      scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {\n        var count = parseFloat(newVal);\n        var countIsNaN = isNaN(count);\n\n        if (!countIsNaN && !(count in whens)) {\n          // If an explicit number rule such as 1, 2, 3... is defined, just use it.\n          // Otherwise, check it against pluralization rules in $locale service.\n          count = $locale.pluralCat(count - offset);\n        }\n\n        // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.\n        // In JS `NaN !== NaN`, so we have to exlicitly check.\n        if ((count !== lastCount) && !(countIsNaN && isNaN(lastCount))) {\n          watchRemover();\n          watchRemover = scope.$watch(whensExpFns[count], updateElementText);\n          lastCount = count;\n        }\n      });\n\n      function updateElementText(newText) {\n        element.text(newText || '');\n      }\n    }\n  };\n}];\n\n/**\n * @ngdoc directive\n * @name ngRepeat\n *\n * @description\n * The `ngRepeat` directive instantiates a template once per item from a collection. Each template\n * instance gets its own scope, where the given loop variable is set to the current collection item,\n * and `$index` is set to the item index or key.\n *\n * Special properties are exposed on the local scope of each template instance, including:\n *\n * | Variable  | Type            | Details                                                                     |\n * |-----------|-----------------|-----------------------------------------------------------------------------|\n * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |\n * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |\n * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |\n * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |\n * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |\n * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |\n *\n * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.\n * This may be useful when, for instance, nesting ngRepeats.\n *\n * # Iterating over object properties\n *\n * It is possible to get `ngRepeat` to iterate over the properties of an object using the following\n * syntax:\n *\n * ```js\n * <div ng-repeat=\"(key, value) in myObj\"> ... </div>\n * ```\n *\n * You need to be aware that the JavaScript specification does not define what order\n * it will return the keys for an object. In order to have a guaranteed deterministic order\n * for the keys, Angular versions up to and including 1.3 **sort the keys alphabetically**.\n *\n * If this is not desired, the recommended workaround is to convert your object into an array\n * that is sorted into the order that you prefer before providing it to `ngRepeat`.  You could\n * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)\n * or implement a `$watch` on the object yourself.\n *\n * In version 1.4 we will remove the sorting, since it seems that browsers generally follow the\n * strategy of providing keys in the order in which they were defined, although there are exceptions\n * when keys are deleted and reinstated.\n *\n *\n * # Tracking and Duplicates\n *\n * When the contents of the collection change, `ngRepeat` makes the corresponding changes to the DOM:\n *\n * * When an item is added, a new instance of the template is added to the DOM.\n * * When an item is removed, its template instance is removed from the DOM.\n * * When items are reordered, their respective templates are reordered in the DOM.\n *\n * By default, `ngRepeat` does not allow duplicate items in arrays. This is because when\n * there are duplicates, it is not possible to maintain a one-to-one mapping between collection\n * items and DOM elements.\n *\n * If you do need to repeat duplicate items, you can substitute the default tracking behavior\n * with your own using the `track by` expression.\n *\n * For example, you may track items by the index of each item in the collection, using the\n * special scope property `$index`:\n * ```html\n *    <div ng-repeat=\"n in [42, 42, 43, 43] track by $index\">\n *      {{n}}\n *    </div>\n * ```\n *\n * You may use arbitrary expressions in `track by`, including references to custom functions\n * on the scope:\n * ```html\n *    <div ng-repeat=\"n in [42, 42, 43, 43] track by myTrackingFunction(n)\">\n *      {{n}}\n *    </div>\n * ```\n *\n * If you are working with objects that have an identifier property, you can track\n * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`\n * will not have to rebuild the DOM elements for items it has already rendered, even if the\n * JavaScript objects in the collection have been substituted for new ones:\n * ```html\n *    <div ng-repeat=\"model in collection track by model.id\">\n *      {{model.name}}\n *    </div>\n * ```\n *\n * When no `track by` expression is provided, it is equivalent to tracking by the built-in\n * `$id` function, which tracks items by their identity:\n * ```html\n *    <div ng-repeat=\"obj in collection track by $id(obj)\">\n *      {{obj.prop}}\n *    </div>\n * ```\n *\n * # Special repeat start and end points\n * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending\n * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.\n * The **ng-repeat-start** directive works the same as **ng-repeat**, but will repeat all the HTML code (including the tag it's defined on)\n * up to and including the ending HTML tag where **ng-repeat-end** is placed.\n *\n * The example below makes use of this feature:\n * ```html\n *   <header ng-repeat-start=\"item in items\">\n *     Header {{ item }}\n *   </header>\n *   <div class=\"body\">\n *     Body {{ item }}\n *   </div>\n *   <footer ng-repeat-end>\n *     Footer {{ item }}\n *   </footer>\n * ```\n *\n * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:\n * ```html\n *   <header>\n *     Header A\n *   </header>\n *   <div class=\"body\">\n *     Body A\n *   </div>\n *   <footer>\n *     Footer A\n *   </footer>\n *   <header>\n *     Header B\n *   </header>\n *   <div class=\"body\">\n *     Body B\n *   </div>\n *   <footer>\n *     Footer B\n *   </footer>\n * ```\n *\n * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such\n * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).\n *\n * @animations\n * **.enter** - when a new item is added to the list or when an item is revealed after a filter\n *\n * **.leave** - when an item is removed from the list or when an item is filtered out\n *\n * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered\n *\n * @element ANY\n * @scope\n * @priority 1000\n * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These\n *   formats are currently supported:\n *\n *   * `variable in expression` â€“ where variable is the user defined loop variable and `expression`\n *     is a scope expression giving the collection to enumerate.\n *\n *     For example: `album in artist.albums`.\n *\n *   * `(key, value) in expression` â€“ where `key` and `value` can be any user defined identifiers,\n *     and `expression` is the scope expression giving the collection to enumerate.\n *\n *     For example: `(name, age) in {'adam':10, 'amalie':12}`.\n *\n *   * `variable in expression track by tracking_expression` â€“ You can also provide an optional tracking expression\n *     which can be used to associate the objects in the collection with the DOM elements. If no tracking expression\n *     is specified, ng-repeat associates elements by identity. It is an error to have\n *     more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are\n *     mapped to the same DOM element, which is not possible.)  If filters are used in the expression, they should be\n *     applied before the tracking expression.\n *\n *     For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements\n *     will be associated by item identity in the array.\n *\n *     For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique\n *     `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements\n *     with the corresponding item in the array by identity. Moving the same object in array would move the DOM\n *     element in the same way in the DOM.\n *\n *     For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this\n *     case the object identity does not matter. Two objects are considered equivalent as long as their `id`\n *     property is same.\n *\n *     For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter\n *     to items in conjunction with a tracking expression.\n *\n *   * `variable in expression as alias_expression` â€“ You can also provide an optional alias expression which will then store the\n *     intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message\n *     when a filter is active on the repeater, but the filtered result set is empty.\n *\n *     For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after\n *     the items have been processed through the filter.\n *\n * @example\n * This example initializes the scope to a list of names and\n * then uses `ngRepeat` to display every person:\n  <example module=\"ngAnimate\" deps=\"angular-animate.js\" animations=\"true\">\n    <file name=\"index.html\">\n      <div ng-init=\"friends = [\n        {name:'John', age:25, gender:'boy'},\n        {name:'Jessie', age:30, gender:'girl'},\n        {name:'Johanna', age:28, gender:'girl'},\n        {name:'Joy', age:15, gender:'girl'},\n        {name:'Mary', age:28, gender:'girl'},\n        {name:'Peter', age:95, gender:'boy'},\n        {name:'Sebastian', age:50, gender:'boy'},\n        {name:'Erika', age:27, gender:'girl'},\n        {name:'Patrick', age:40, gender:'boy'},\n        {name:'Samantha', age:60, gender:'girl'}\n      ]\">\n        I have {{friends.length}} friends. They are:\n        <input type=\"search\" ng-model=\"q\" placeholder=\"filter friends...\" />\n        <ul class=\"example-animate-container\">\n          <li class=\"animate-repeat\" ng-repeat=\"friend in friends | filter:q as results\">\n            [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.\n          </li>\n          <li class=\"animate-repeat\" ng-if=\"results.length == 0\">\n            <strong>No results found...</strong>\n          </li>\n        </ul>\n      </div>\n    </file>\n    <file name=\"animations.css\">\n      .example-animate-container {\n        background:white;\n        border:1px solid black;\n        list-style:none;\n        margin:0;\n        padding:0 10px;\n      }\n\n      .animate-repeat {\n        line-height:40px;\n        list-style:none;\n        box-sizing:border-box;\n      }\n\n      .animate-repeat.ng-move,\n      .animate-repeat.ng-enter,\n      .animate-repeat.ng-leave {\n        -webkit-transition:all linear 0.5s;\n        transition:all linear 0.5s;\n      }\n\n      .animate-repeat.ng-leave.ng-leave-active,\n      .animate-repeat.ng-move,\n      .animate-repeat.ng-enter {\n        opacity:0;\n        max-height:0;\n      }\n\n      .animate-repeat.ng-leave,\n      .animate-repeat.ng-move.ng-move-active,\n      .animate-repeat.ng-enter.ng-enter-active {\n        opacity:1;\n        max-height:40px;\n      }\n    </file>\n    <file name=\"protractor.js\" type=\"protractor\">\n      var friends = element.all(by.repeater('friend in friends'));\n\n      it('should render initial data set', function() {\n        expect(friends.count()).toBe(10);\n        expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');\n        expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');\n        expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');\n        expect(element(by.binding('friends.length')).getText())\n            .toMatch(\"I have 10 friends. They are:\");\n      });\n\n       it('should update repeater when filter predicate changes', function() {\n         expect(friends.count()).toBe(10);\n\n         element(by.model('q')).sendKeys('ma');\n\n         expect(friends.count()).toBe(2);\n         expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');\n         expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');\n       });\n      </file>\n    </example>\n */\nvar ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {\n  var NG_REMOVED = '$$NG_REMOVED';\n  var ngRepeatMinErr = minErr('ngRepeat');\n\n  var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {\n    // TODO(perf): generate setters to shave off ~40ms or 1-1.5%\n    scope[valueIdentifier] = value;\n    if (keyIdentifier) scope[keyIdentifier] = key;\n    scope.$index = index;\n    scope.$first = (index === 0);\n    scope.$last = (index === (arrayLength - 1));\n    scope.$middle = !(scope.$first || scope.$last);\n    // jshint bitwise: false\n    scope.$odd = !(scope.$even = (index&1) === 0);\n    // jshint bitwise: true\n  };\n\n  var getBlockStart = function(block) {\n    return block.clone[0];\n  };\n\n  var getBlockEnd = function(block) {\n    return block.clone[block.clone.length - 1];\n  };\n\n\n  return {\n    restrict: 'A',\n    multiElement: true,\n    transclude: 'element',\n    priority: 1000,\n    terminal: true,\n    $$tlb: true,\n    compile: function ngRepeatCompile($element, $attr) {\n      var expression = $attr.ngRepeat;\n      var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');\n\n      var match = expression.match(/^\\s*([\\s\\S]+?)\\s+in\\s+([\\s\\S]+?)(?:\\s+as\\s+([\\s\\S]+?))?(?:\\s+track\\s+by\\s+([\\s\\S]+?))?\\s*$/);\n\n      if (!match) {\n        throw ngRepeatMinErr('iexp', \"Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.\",\n            expression);\n      }\n\n      var lhs = match[1];\n      var rhs = match[2];\n      var aliasAs = match[3];\n      var trackByExp = match[4];\n\n      match = lhs.match(/^(?:(\\s*[\\$\\w]+)|\\(\\s*([\\$\\w]+)\\s*,\\s*([\\$\\w]+)\\s*\\))$/);\n\n      if (!match) {\n        throw ngRepeatMinErr('iidexp', \"'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.\",\n            lhs);\n      }\n      var valueIdentifier = match[3] || match[1];\n      var keyIdentifier = match[2];\n\n      if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||\n          /^(null|undefined|this|\\$index|\\$first|\\$middle|\\$last|\\$even|\\$odd|\\$parent|\\$root|\\$id)$/.test(aliasAs))) {\n        throw ngRepeatMinErr('badident', \"alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.\",\n          aliasAs);\n      }\n\n      var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;\n      var hashFnLocals = {$id: hashKey};\n\n      if (trackByExp) {\n        trackByExpGetter = $parse(trackByExp);\n      } else {\n        trackByIdArrayFn = function(key, value) {\n          return hashKey(value);\n        };\n        trackByIdObjFn = function(key) {\n          return key;\n        };\n      }\n\n      return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {\n\n        if (trackByExpGetter) {\n          trackByIdExpFn = function(key, value, index) {\n            // assign key, value, and $index to the locals so that they can be used in hash functions\n            if (keyIdentifier) hashFnLocals[keyIdentifier] = key;\n            hashFnLocals[valueIdentifier] = value;\n            hashFnLocals.$index = index;\n            return trackByExpGetter($scope, hashFnLocals);\n          };\n        }\n\n        // Store a list of elements from previous run. This is a hash where key is the item from the\n        // iterator, and the value is objects with following properties.\n        //   - scope: bound scope\n        //   - element: previous element.\n        //   - index: position\n        //\n        // We are using no-proto object so that we don't need to guard against inherited props via\n        // hasOwnProperty.\n        var lastBlockMap = createMap();\n\n        //watch props\n        $scope.$watchCollection(rhs, function ngRepeatAction(collection) {\n          var index, length,\n              previousNode = $element[0],     // node that cloned nodes should be inserted after\n                                              // initialized to the comment node anchor\n              nextNode,\n              // Same as lastBlockMap but it has the current state. It will become the\n              // lastBlockMap on the next iteration.\n              nextBlockMap = createMap(),\n              collectionLength,\n              key, value, // key/value of iteration\n              trackById,\n              trackByIdFn,\n              collectionKeys,\n              block,       // last object information {scope, element, id}\n              nextBlockOrder,\n              elementsToRemove;\n\n          if (aliasAs) {\n            $scope[aliasAs] = collection;\n          }\n\n          if (isArrayLike(collection)) {\n            collectionKeys = collection;\n            trackByIdFn = trackByIdExpFn || trackByIdArrayFn;\n          } else {\n            trackByIdFn = trackByIdExpFn || trackByIdObjFn;\n            // if object, extract keys, sort them and use to determine order of iteration over obj props\n            collectionKeys = [];\n            for (var itemKey in collection) {\n              if (collection.hasOwnProperty(itemKey) && itemKey.charAt(0) != '$') {\n                collectionKeys.push(itemKey);\n              }\n            }\n            collectionKeys.sort();\n          }\n\n          collectionLength = collectionKeys.length;\n          nextBlockOrder = new Array(collectionLength);\n\n          // locate existing items\n          for (index = 0; index < collectionLength; index++) {\n            key = (collection === collectionKeys) ? index : collectionKeys[index];\n            value = collection[key];\n            trackById = trackByIdFn(key, value, index);\n            if (lastBlockMap[trackById]) {\n              // found previously seen block\n              block = lastBlockMap[trackById];\n              delete lastBlockMap[trackById];\n              nextBlockMap[trackById] = block;\n              nextBlockOrder[index] = block;\n            } else if (nextBlockMap[trackById]) {\n              // if collision detected. restore lastBlockMap and throw an error\n              forEach(nextBlockOrder, function(block) {\n                if (block && block.scope) lastBlockMap[block.id] = block;\n              });\n              throw ngRepeatMinErr('dupes',\n                  \"Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}\",\n                  expression, trackById, value);\n            } else {\n              // new never before seen block\n              nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};\n              nextBlockMap[trackById] = true;\n            }\n          }\n\n          // remove leftover items\n          for (var blockKey in lastBlockMap) {\n            block = lastBlockMap[blockKey];\n            elementsToRemove = getBlockNodes(block.clone);\n            $animate.leave(elementsToRemove);\n            if (elementsToRemove[0].parentNode) {\n              // if the element was not removed yet because of pending animation, mark it as deleted\n              // so that we can ignore it later\n              for (index = 0, length = elementsToRemove.length; index < length; index++) {\n                elementsToRemove[index][NG_REMOVED] = true;\n              }\n            }\n            block.scope.$destroy();\n          }\n\n          // we are not using forEach for perf reasons (trying to avoid #call)\n          for (index = 0; index < collectionLength; index++) {\n            key = (collection === collectionKeys) ? index : collectionKeys[index];\n            value = collection[key];\n            block = nextBlockOrder[index];\n\n            if (block.scope) {\n              // if we have already seen this object, then we need to reuse the\n              // associated scope/element\n\n              nextNode = previousNode;\n\n              // skip nodes that are already pending removal via leave animation\n              do {\n                nextNode = nextNode.nextSibling;\n              } while (nextNode && nextNode[NG_REMOVED]);\n\n              if (getBlockStart(block) != nextNode) {\n                // existing item which got moved\n                $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));\n              }\n              previousNode = getBlockEnd(block);\n              updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);\n            } else {\n              // new item which we don't know about\n              $transclude(function ngRepeatTransclude(clone, scope) {\n                block.scope = scope;\n                // http://jsperf.com/clone-vs-createcomment\n                var endNode = ngRepeatEndComment.cloneNode(false);\n                clone[clone.length++] = endNode;\n\n                // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?\n                $animate.enter(clone, null, jqLite(previousNode));\n                previousNode = endNode;\n                // Note: We only need the first/last node of the cloned nodes.\n                // However, we need to keep the reference to the jqlite wrapper as it might be changed later\n                // by a directive with templateUrl when its template arrives.\n                block.clone = clone;\n                nextBlockMap[block.id] = block;\n                updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);\n              });\n            }\n          }\n          lastBlockMap = nextBlockMap;\n        });\n      };\n    }\n  };\n}];\n\nvar NG_HIDE_CLASS = 'ng-hide';\nvar NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';\n/**\n * @ngdoc directive\n * @name ngShow\n *\n * @description\n * The `ngShow` directive shows or hides the given HTML element based on the expression\n * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding\n * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined\n * in AngularJS and sets the display style to none (using an !important flag).\n * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).\n *\n * ```html\n * <!-- when $scope.myValue is truthy (element is visible) -->\n * <div ng-show=\"myValue\"></div>\n *\n * <!-- when $scope.myValue is falsy (element is hidden) -->\n * <div ng-show=\"myValue\" class=\"ng-hide\"></div>\n * ```\n *\n * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class\n * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed\n * from the element causing the element not to appear hidden.\n *\n * ## Why is !important used?\n *\n * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector\n * can be easily overridden by heavier selectors. For example, something as simple\n * as changing the display style on a HTML list item would make hidden elements appear visible.\n * This also becomes a bigger issue when dealing with CSS frameworks.\n *\n * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector\n * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the\n * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.\n *\n * ### Overriding `.ng-hide`\n *\n * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change\n * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`\n * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope\n * with extra animation classes that can be added.\n *\n * ```css\n * .ng-hide:not(.ng-hide-animate) {\n *   /&#42; this is just another form of hiding an element &#42;/\n *   display: block!important;\n *   position: absolute;\n *   top: -9999px;\n *   left: -9999px;\n * }\n * ```\n *\n * By default you don't need to override in CSS anything and the animations will work around the display style.\n *\n * ## A note about animations with `ngShow`\n *\n * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression\n * is true and false. This system works like the animation system present with ngClass except that\n * you must also include the !important flag to override the display property\n * so that you can perform an animation when the element is hidden during the time of the animation.\n *\n * ```css\n * //\n * //a working example can be found at the bottom of this page\n * //\n * .my-element.ng-hide-add, .my-element.ng-hide-remove {\n *   /&#42; this is required as of 1.3x to properly\n *      apply all styling in a show/hide animation &#42;/\n *   transition: 0s linear all;\n * }\n *\n * .my-element.ng-hide-add-active,\n * .my-element.ng-hide-remove-active {\n *   /&#42; the transition is defined in the active class &#42;/\n *   transition: 1s linear all;\n * }\n *\n * .my-element.ng-hide-add { ... }\n * .my-element.ng-hide-add.ng-hide-add-active { ... }\n * .my-element.ng-hide-remove { ... }\n * .my-element.ng-hide-remove.ng-hide-remove-active { ... }\n * ```\n *\n * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display\n * property to block during animation states--ngAnimate will handle the style toggling automatically for you.\n *\n * @animations\n * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible\n * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden\n *\n * @element ANY\n * @param {expression} ngShow If the {@link guide/expression expression} is truthy\n *     then the element is shown or hidden respectively.\n *\n * @example\n  <example module=\"ngAnimate\" deps=\"angular-animate.js\" animations=\"true\">\n    <file name=\"index.html\">\n      Click me: <input type=\"checkbox\" ng-model=\"checked\"><br/>\n      <div>\n        Show:\n        <div class=\"check-element animate-show\" ng-show=\"checked\">\n          <span class=\"glyphicon glyphicon-thumbs-up\"></span> I show up when your checkbox is checked.\n        </div>\n      </div>\n      <div>\n        Hide:\n        <div class=\"check-element animate-show\" ng-hide=\"checked\">\n          <span class=\"glyphicon glyphicon-thumbs-down\"></span> I hide when your checkbox is checked.\n        </div>\n      </div>\n    </file>\n    <file name=\"glyphicons.css\">\n      @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);\n    </file>\n    <file name=\"animations.css\">\n      .animate-show {\n        line-height: 20px;\n        opacity: 1;\n        padding: 10px;\n        border: 1px solid black;\n        background: white;\n      }\n\n      .animate-show.ng-hide-add.ng-hide-add-active,\n      .animate-show.ng-hide-remove.ng-hide-remove-active {\n        -webkit-transition: all linear 0.5s;\n        transition: all linear 0.5s;\n      }\n\n      .animate-show.ng-hide {\n        line-height: 0;\n        opacity: 0;\n        padding: 0 10px;\n      }\n\n      .check-element {\n        padding: 10px;\n        border: 1px solid black;\n        background: white;\n      }\n    </file>\n    <file name=\"protractor.js\" type=\"protractor\">\n      var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));\n      var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));\n\n      it('should check ng-show / ng-hide', function() {\n        expect(thumbsUp.isDisplayed()).toBeFalsy();\n        expect(thumbsDown.isDisplayed()).toBeTruthy();\n\n        element(by.model('checked')).click();\n\n        expect(thumbsUp.isDisplayed()).toBeTruthy();\n        expect(thumbsDown.isDisplayed()).toBeFalsy();\n      });\n    </file>\n  </example>\n */\nvar ngShowDirective = ['$animate', function($animate) {\n  return {\n    restrict: 'A',\n    multiElement: true,\n    link: function(scope, element, attr) {\n      scope.$watch(attr.ngShow, function ngShowWatchAction(value) {\n        // we're adding a temporary, animation-specific class for ng-hide since this way\n        // we can control when the element is actually displayed on screen without having\n        // to have a global/greedy CSS selector that breaks when other animations are run.\n        // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845\n        $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {\n          tempClasses: NG_HIDE_IN_PROGRESS_CLASS\n        });\n      });\n    }\n  };\n}];\n\n\n/**\n * @ngdoc directive\n * @name ngHide\n *\n * @description\n * The `ngHide` directive shows or hides the given HTML element based on the expression\n * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding\n * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined\n * in AngularJS and sets the display style to none (using an !important flag).\n * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).\n *\n * ```html\n * <!-- when $scope.myValue is truthy (element is hidden) -->\n * <div ng-hide=\"myValue\" class=\"ng-hide\"></div>\n *\n * <!-- when $scope.myValue is falsy (element is visible) -->\n * <div ng-hide=\"myValue\"></div>\n * ```\n *\n * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class\n * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed\n * from the element causing the element not to appear hidden.\n *\n * ## Why is !important used?\n *\n * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector\n * can be easily overridden by heavier selectors. For example, something as simple\n * as changing the display style on a HTML list item would make hidden elements appear visible.\n * This also becomes a bigger issue when dealing with CSS frameworks.\n *\n * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector\n * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the\n * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.\n *\n * ### Overriding `.ng-hide`\n *\n * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change\n * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`\n * class in CSS:\n *\n * ```css\n * .ng-hide {\n *   /&#42; this is just another form of hiding an element &#42;/\n *   display: block!important;\n *   position: absolute;\n *   top: -9999px;\n *   left: -9999px;\n * }\n * ```\n *\n * By default you don't need to override in CSS anything and the animations will work around the display style.\n *\n * ## A note about animations with `ngHide`\n *\n * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression\n * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`\n * CSS class is added and removed for you instead of your own CSS class.\n *\n * ```css\n * //\n * //a working example can be found at the bottom of this page\n * //\n * .my-element.ng-hide-add, .my-element.ng-hide-remove {\n *   transition: 0.5s linear all;\n * }\n *\n * .my-element.ng-hide-add { ... }\n * .my-element.ng-hide-add.ng-hide-add-active { ... }\n * .my-element.ng-hide-remove { ... }\n * .my-element.ng-hide-remove.ng-hide-remove-active { ... }\n * ```\n *\n * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display\n * property to block during animation states--ngAnimate will handle the style toggling automatically for you.\n *\n * @animations\n * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden\n * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible\n *\n * @element ANY\n * @param {expression} ngHide If the {@link guide/expression expression} is truthy then\n *     the element is shown or hidden respectively.\n *\n * @example\n  <example module=\"ngAnimate\" deps=\"angular-animate.js\" animations=\"true\">\n    <file name=\"index.html\">\n      Click me: <input type=\"checkbox\" ng-model=\"checked\"><br/>\n      <div>\n        Show:\n        <div class=\"check-element animate-hide\" ng-show=\"checked\">\n          <span class=\"glyphicon glyphicon-thumbs-up\"></span> I show up when your checkbox is checked.\n        </div>\n      </div>\n      <div>\n        Hide:\n        <div class=\"check-element animate-hide\" ng-hide=\"checked\">\n          <span class=\"glyphicon glyphicon-thumbs-down\"></span> I hide when your checkbox is checked.\n        </div>\n      </div>\n    </file>\n    <file name=\"glyphicons.css\">\n      @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);\n    </file>\n    <file name=\"animations.css\">\n      .animate-hide {\n        -webkit-transition: all linear 0.5s;\n        transition: all linear 0.5s;\n        line-height: 20px;\n        opacity: 1;\n        padding: 10px;\n        border: 1px solid black;\n        background: white;\n      }\n\n      .animate-hide.ng-hide {\n        line-height: 0;\n        opacity: 0;\n        padding: 0 10px;\n      }\n\n      .check-element {\n        padding: 10px;\n        border: 1px solid black;\n        background: white;\n      }\n    </file>\n    <file name=\"protractor.js\" type=\"protractor\">\n      var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));\n      var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));\n\n      it('should check ng-show / ng-hide', function() {\n        expect(thumbsUp.isDisplayed()).toBeFalsy();\n        expect(thumbsDown.isDisplayed()).toBeTruthy();\n\n        element(by.model('checked')).click();\n\n        expect(thumbsUp.isDisplayed()).toBeTruthy();\n        expect(thumbsDown.isDisplayed()).toBeFalsy();\n      });\n    </file>\n  </example>\n */\nvar ngHideDirective = ['$animate', function($animate) {\n  return {\n    restrict: 'A',\n    multiElement: true,\n    link: function(scope, element, attr) {\n      scope.$watch(attr.ngHide, function ngHideWatchAction(value) {\n        // The comment inside of the ngShowDirective explains why we add and\n        // remove a temporary class for the show/hide animation\n        $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {\n          tempClasses: NG_HIDE_IN_PROGRESS_CLASS\n        });\n      });\n    }\n  };\n}];\n\n/**\n * @ngdoc directive\n * @name ngStyle\n * @restrict AC\n *\n * @description\n * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.\n *\n * @element ANY\n * @param {expression} ngStyle\n *\n * {@link guide/expression Expression} which evals to an\n * object whose keys are CSS style names and values are corresponding values for those CSS\n * keys.\n *\n * Since some CSS style names are not valid keys for an object, they must be quoted.\n * See the 'background-color' style in the example below.\n *\n * @example\n   <example>\n     <file name=\"index.html\">\n        <input type=\"button\" value=\"set color\" ng-click=\"myStyle={color:'red'}\">\n        <input type=\"button\" value=\"set background\" ng-click=\"myStyle={'background-color':'blue'}\">\n        <input type=\"button\" value=\"clear\" ng-click=\"myStyle={}\">\n        <br/>\n        <span ng-style=\"myStyle\">Sample Text</span>\n        <pre>myStyle={{myStyle}}</pre>\n     </file>\n     <file name=\"style.css\">\n       span {\n         color: black;\n       }\n     </file>\n     <file name=\"protractor.js\" type=\"protractor\">\n       var colorSpan = element(by.css('span'));\n\n       it('should check ng-style', function() {\n         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');\n         element(by.css('input[value=\\'set color\\']')).click();\n         expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');\n         element(by.css('input[value=clear]')).click();\n         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');\n       });\n     </file>\n   </example>\n */\nvar ngStyleDirective = ngDirective(function(scope, element, attr) {\n  scope.$watchCollection(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {\n    if (oldStyles && (newStyles !== oldStyles)) {\n      forEach(oldStyles, function(val, style) { element.css(style, '');});\n    }\n    if (newStyles) element.css(newStyles);\n  });\n});\n\n/**\n * @ngdoc directive\n * @name ngSwitch\n * @restrict EA\n *\n * @description\n * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.\n * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location\n * as specified in the template.\n *\n * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it\n * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element\n * matches the value obtained from the evaluated expression. In other words, you define a container element\n * (where you place the directive), place an expression on the **`on=\"...\"` attribute**\n * (or the **`ng-switch=\"...\"` attribute**), define any inner elements inside of the directive and place\n * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on\n * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default\n * attribute is displayed.\n *\n * <div class=\"alert alert-info\">\n * Be aware that the attribute values to match against cannot be expressions. They are interpreted\n * as literal string values to match against.\n * For example, **`ng-switch-when=\"someVal\"`** will match against the string `\"someVal\"` not against the\n * value of the expression `$scope.someVal`.\n * </div>\n\n * @animations\n * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container\n * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM\n *\n * @usage\n *\n * ```\n * <ANY ng-switch=\"expression\">\n *   <ANY ng-switch-when=\"matchValue1\">...</ANY>\n *   <ANY ng-switch-when=\"matchValue2\">...</ANY>\n *   <ANY ng-switch-default>...</ANY>\n * </ANY>\n * ```\n *\n *\n * @scope\n * @priority 1200\n * @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.\n * On child elements add:\n *\n * * `ngSwitchWhen`: the case statement to match against. If match then this\n *   case will be displayed. If the same match appears multiple times, all the\n *   elements will be displayed.\n * * `ngSwitchDefault`: the default case when no other case match. If there\n *   are multiple default cases, all of them will be displayed when no other\n *   case match.\n *\n *\n * @example\n  <example module=\"switchExample\" deps=\"angular-animate.js\" animations=\"true\">\n    <file name=\"index.html\">\n      <div ng-controller=\"ExampleController\">\n        <select ng-model=\"selection\" ng-options=\"item for item in items\">\n        </select>\n        <tt>selection={{selection}}</tt>\n        <hr/>\n        <div class=\"animate-switch-container\"\n          ng-switch on=\"selection\">\n            <div class=\"animate-switch\" ng-switch-when=\"settings\">Settings Div</div>\n            <div class=\"animate-switch\" ng-switch-when=\"home\">Home Span</div>\n            <div class=\"animate-switch\" ng-switch-default>default</div>\n        </div>\n      </div>\n    </file>\n    <file name=\"script.js\">\n      angular.module('switchExample', ['ngAnimate'])\n        .controller('ExampleController', ['$scope', function($scope) {\n          $scope.items = ['settings', 'home', 'other'];\n          $scope.selection = $scope.items[0];\n        }]);\n    </file>\n    <file name=\"animations.css\">\n      .animate-switch-container {\n        position:relative;\n        background:white;\n        border:1px solid black;\n        height:40px;\n        overflow:hidden;\n      }\n\n      .animate-switch {\n        padding:10px;\n      }\n\n      .animate-switch.ng-animate {\n        -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;\n        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;\n\n        position:absolute;\n        top:0;\n        left:0;\n        right:0;\n        bottom:0;\n      }\n\n      .animate-switch.ng-leave.ng-leave-active,\n      .animate-switch.ng-enter {\n        top:-50px;\n      }\n      .animate-switch.ng-leave,\n      .animate-switch.ng-enter.ng-enter-active {\n        top:0;\n      }\n    </file>\n    <file name=\"protractor.js\" type=\"protractor\">\n      var switchElem = element(by.css('[ng-switch]'));\n      var select = element(by.model('selection'));\n\n      it('should start in settings', function() {\n        expect(switchElem.getText()).toMatch(/Settings Div/);\n      });\n      it('should change to home', function() {\n        select.all(by.css('option')).get(1).click();\n        expect(switchElem.getText()).toMatch(/Home Span/);\n      });\n      it('should select default', function() {\n        select.all(by.css('option')).get(2).click();\n        expect(switchElem.getText()).toMatch(/default/);\n      });\n    </file>\n  </example>\n */\nvar ngSwitchDirective = ['$animate', function($animate) {\n  return {\n    restrict: 'EA',\n    require: 'ngSwitch',\n\n    // asks for $scope to fool the BC controller module\n    controller: ['$scope', function ngSwitchController() {\n     this.cases = {};\n    }],\n    link: function(scope, element, attr, ngSwitchController) {\n      var watchExpr = attr.ngSwitch || attr.on,\n          selectedTranscludes = [],\n          selectedElements = [],\n          previousLeaveAnimations = [],\n          selectedScopes = [];\n\n      var spliceFactory = function(array, index) {\n          return function() { array.splice(index, 1); };\n      };\n\n      scope.$watch(watchExpr, function ngSwitchWatchAction(value) {\n        var i, ii;\n        for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {\n          $animate.cancel(previousLeaveAnimations[i]);\n        }\n        previousLeaveAnimations.length = 0;\n\n        for (i = 0, ii = selectedScopes.length; i < ii; ++i) {\n          var selected = getBlockNodes(selectedElements[i].clone);\n          selectedScopes[i].$destroy();\n          var promise = previousLeaveAnimations[i] = $animate.leave(selected);\n          promise.then(spliceFactory(previousLeaveAnimations, i));\n        }\n\n        selectedElements.length = 0;\n        selectedScopes.length = 0;\n\n        if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {\n          forEach(selectedTranscludes, function(selectedTransclude) {\n            selectedTransclude.transclude(function(caseElement, selectedScope) {\n              selectedScopes.push(selectedScope);\n              var anchor = selectedTransclude.element;\n              caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');\n              var block = { clone: caseElement };\n\n              selectedElements.push(block);\n              $animate.enter(caseElement, anchor.parent(), anchor);\n            });\n          });\n        }\n      });\n    }\n  };\n}];\n\nvar ngSwitchWhenDirective = ngDirective({\n  transclude: 'element',\n  priority: 1200,\n  require: '^ngSwitch',\n  multiElement: true,\n  link: function(scope, element, attrs, ctrl, $transclude) {\n    ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);\n    ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });\n  }\n});\n\nvar ngSwitchDefaultDirective = ngDirective({\n  transclude: 'element',\n  priority: 1200,\n  require: '^ngSwitch',\n  multiElement: true,\n  link: function(scope, element, attr, ctrl, $transclude) {\n    ctrl.cases['?'] = (ctrl.cases['?'] || []);\n    ctrl.cases['?'].push({ transclude: $transclude, element: element });\n   }\n});\n\n/**\n * @ngdoc directive\n * @name ngTransclude\n * @restrict EAC\n *\n * @description\n * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.\n *\n * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.\n *\n * @element ANY\n *\n * @example\n   <example module=\"transcludeExample\">\n     <file name=\"index.html\">\n       <script>\n         angular.module('transcludeExample', [])\n          .directive('pane', function(){\n             return {\n               restrict: 'E',\n               transclude: true,\n               scope: { title:'@' },\n               template: '<div style=\"border: 1px solid black;\">' +\n                           '<div style=\"background-color: gray\">{{title}}</div>' +\n                           '<ng-transclude></ng-transclude>' +\n                         '</div>'\n             };\n         })\n         .controller('ExampleController', ['$scope', function($scope) {\n           $scope.title = 'Lorem Ipsum';\n           $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';\n         }]);\n       </script>\n       <div ng-controller=\"ExampleController\">\n         <input ng-model=\"title\"> <br/>\n         <textarea ng-model=\"text\"></textarea> <br/>\n         <pane title=\"{{title}}\">{{text}}</pane>\n       </div>\n     </file>\n     <file name=\"protractor.js\" type=\"protractor\">\n        it('should have transcluded', function() {\n          var titleElement = element(by.model('title'));\n          titleElement.clear();\n          titleElement.sendKeys('TITLE');\n          var textElement = element(by.model('text'));\n          textElement.clear();\n          textElement.sendKeys('TEXT');\n          expect(element(by.binding('title')).getText()).toEqual('TITLE');\n          expect(element(by.binding('text')).getText()).toEqual('TEXT');\n        });\n     </file>\n   </example>\n *\n */\nvar ngTranscludeDirective = ngDirective({\n  restrict: 'EAC',\n  link: function($scope, $element, $attrs, controller, $transclude) {\n    if (!$transclude) {\n      throw minErr('ngTransclude')('orphan',\n       'Illegal use of ngTransclude directive in the template! ' +\n       'No parent directive that requires a transclusion found. ' +\n       'Element: {0}',\n       startingTag($element));\n    }\n\n    $transclude(function(clone) {\n      $element.empty();\n      $element.append(clone);\n    });\n  }\n});\n\n/**\n * @ngdoc directive\n * @name script\n * @restrict E\n *\n * @description\n * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the\n * template can be used by {@link ng.directive:ngInclude `ngInclude`},\n * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the\n * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be\n * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.\n *\n * @param {string} type Must be set to `'text/ng-template'`.\n * @param {string} id Cache name of the template.\n *\n * @example\n  <example>\n    <file name=\"index.html\">\n      <script type=\"text/ng-template\" id=\"/tpl.html\">\n        Content of the template.\n      </script>\n\n      <a ng-click=\"currentTpl='/tpl.html'\" id=\"tpl-link\">Load inlined template</a>\n      <div id=\"tpl-content\" ng-include src=\"currentTpl\"></div>\n    </file>\n    <file name=\"protractor.js\" type=\"protractor\">\n      it('should load template defined inside script tag', function() {\n        element(by.css('#tpl-link')).click();\n        expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);\n      });\n    </file>\n  </example>\n */\nvar scriptDirective = ['$templateCache', function($templateCache) {\n  return {\n    restrict: 'E',\n    terminal: true,\n    compile: function(element, attr) {\n      if (attr.type == 'text/ng-template') {\n        var templateUrl = attr.id,\n            text = element[0].text;\n\n        $templateCache.put(templateUrl, text);\n      }\n    }\n  };\n}];\n\nvar ngOptionsMinErr = minErr('ngOptions');\n/**\n * @ngdoc directive\n * @name select\n * @restrict E\n *\n * @description\n * HTML `SELECT` element with angular data-binding.\n *\n * # `ngOptions`\n *\n * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`\n * elements for the `<select>` element using the array or object obtained by evaluating the\n * `ngOptions` comprehension expression.\n *\n * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a\n * similar result. However, `ngOptions` provides some benefits such as reducing memory and\n * increasing speed by not creating a new scope for each repeated instance, as well as providing\n * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the\n * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound\n *  to a non-string value. This is because an option element can only be bound to string values at\n * present.\n *\n * When an item in the `<select>` menu is selected, the array element or object property\n * represented by the selected option will be bound to the model identified by the `ngModel`\n * directive.\n *\n * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can\n * be nested into the `<select>` element. This element will then represent the `null` or \"not selected\"\n * option. See example below for demonstration.\n *\n * <div class=\"alert alert-warning\">\n * **Note:** `ngModel` compares by reference, not value. This is important when binding to an\n * array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/).\n * </div>\n *\n * ## `select` **`as`**\n *\n * Using `select` **`as`** will bind the result of the `select` expression to the model, but\n * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)\n * or property name (for object data sources) of the value within the collection. If a **`track by`** expression\n * is used, the result of that expression will be set as the value of the `option` and `select` elements.\n *\n *\n * ### `select` **`as`** and **`track by`**\n *\n * <div class=\"alert alert-warning\">\n * Do not use `select` **`as`** and **`track by`** in the same expression. They are not designed to work together.\n * </div>\n *\n * Consider the following example:\n *\n * ```html\n * <select ng-options=\"item.subItem as item.label for item in values track by item.id\" ng-model=\"selected\">\n * ```\n *\n * ```js\n * $scope.values = [{\n *   id: 1,\n *   label: 'aLabel',\n *   subItem: { name: 'aSubItem' }\n * }, {\n *   id: 2,\n *   label: 'bLabel',\n *   subItem: { name: 'bSubItem' }\n * }];\n *\n * $scope.selected = { name: 'aSubItem' };\n * ```\n *\n * With the purpose of preserving the selection, the **`track by`** expression is always applied to the element\n * of the data source (to `item` in this example). To calculate whether an element is selected, we do the\n * following:\n *\n * 1. Apply **`track by`** to the elements in the array. In the example: `[1, 2]`\n * 2. Apply **`track by`** to the already selected value in `ngModel`.\n *    In the example: this is not possible as **`track by`** refers to `item.id`, but the selected\n *    value from `ngModel` is `{name: 'aSubItem'}`, so the **`track by`** expression is applied to\n *    a wrong object, the selected element can't be found, `<select>` is always reset to the \"not\n *    selected\" option.\n *\n *\n * @param {string} ngModel Assignable angular expression to data-bind to.\n * @param {string=} name Property name of the form under which the control is published.\n * @param {string=} required The control is considered valid only if value is entered.\n * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to\n *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of\n *    `required` when you want to data-bind to the `required` attribute.\n * @param {comprehension_expression=} ngOptions in one of the following forms:\n *\n *   * for array data sources:\n *     * `label` **`for`** `value` **`in`** `array`\n *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`\n *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array`\n *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`\n *     * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`\n *        (for including a filter with `track by`)\n *   * for object data sources:\n *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`\n *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`\n *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`\n *     * `select` **`as`** `label` **`group by`** `group`\n *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`\n *\n * Where:\n *\n *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.\n *   * `value`: local variable which will refer to each item in the `array` or each property value\n *      of `object` during iteration.\n *   * `key`: local variable which will refer to a property name in `object` during iteration.\n *   * `label`: The result of this expression will be the label for `<option>` element. The\n *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).\n *   * `select`: The result of this expression will be bound to the model of the parent `<select>`\n *      element. If not specified, `select` expression will default to `value`.\n *   * `group`: The result of this expression will be used to group options using the `<optgroup>`\n *      DOM element.\n *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be\n *      used to identify the objects in the array. The `trackexpr` will most likely refer to the\n *     `value` variable (e.g. `value.propertyName`). With this the selection is preserved\n *      even when the options are recreated (e.g. reloaded from the server).\n *\n * @example\n    <example module=\"selectExample\">\n      <file name=\"index.html\">\n        <script>\n        angular.module('selectExample', [])\n          .controller('ExampleController', ['$scope', function($scope) {\n            $scope.colors = [\n              {name:'black', shade:'dark'},\n              {name:'white', shade:'light'},\n              {name:'red', shade:'dark'},\n              {name:'blue', shade:'dark'},\n              {name:'yellow', shade:'light'}\n            ];\n            $scope.myColor = $scope.colors[2]; // red\n          }]);\n        </script>\n        <div ng-controller=\"ExampleController\">\n          <ul>\n            <li ng-repeat=\"color in colors\">\n              Name: <input ng-model=\"color.name\">\n              [<a href ng-click=\"colors.splice($index, 1)\">X</a>]\n            </li>\n            <li>\n              [<a href ng-click=\"colors.push({})\">add</a>]\n            </li>\n          </ul>\n          <hr/>\n          Color (null not allowed):\n          <select ng-model=\"myColor\" ng-options=\"color.name for color in colors\"></select><br>\n\n          Color (null allowed):\n          <span  class=\"nullable\">\n            <select ng-model=\"myColor\" ng-options=\"color.name for color in colors\">\n              <option value=\"\">-- choose color --</option>\n            </select>\n          </span><br/>\n\n          Color grouped by shade:\n          <select ng-model=\"myColor\" ng-options=\"color.name group by color.shade for color in colors\">\n          </select><br/>\n\n\n          Select <a href ng-click=\"myColor = { name:'not in list', shade: 'other' }\">bogus</a>.<br>\n          <hr/>\n          Currently selected: {{ {selected_color:myColor} }}\n          <div style=\"border:solid 1px black; height:20px\"\n               ng-style=\"{'background-color':myColor.name}\">\n          </div>\n        </div>\n      </file>\n      <file name=\"protractor.js\" type=\"protractor\">\n         it('should check ng-options', function() {\n           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');\n           element.all(by.model('myColor')).first().click();\n           element.all(by.css('select[ng-model=\"myColor\"] option')).first().click();\n           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');\n           element(by.css('.nullable select[ng-model=\"myColor\"]')).click();\n           element.all(by.css('.nullable select[ng-model=\"myColor\"] option')).first().click();\n           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');\n         });\n      </file>\n    </example>\n */\n\nvar ngOptionsDirective = valueFn({\n  restrict: 'A',\n  terminal: true\n});\n\n// jshint maxlen: false\nvar selectDirective = ['$compile', '$parse', function($compile,   $parse) {\n                         //000011111111110000000000022222222220000000000000000000003333333333000000000000004444444444444440000000005555555555555550000000666666666666666000000000000000777777777700000000000000000008888888888\n  var NG_OPTIONS_REGEXP = /^\\s*([\\s\\S]+?)(?:\\s+as\\s+([\\s\\S]+?))?(?:\\s+group\\s+by\\s+([\\s\\S]+?))?\\s+for\\s+(?:([\\$\\w][\\$\\w]*)|(?:\\(\\s*([\\$\\w][\\$\\w]*)\\s*,\\s*([\\$\\w][\\$\\w]*)\\s*\\)))\\s+in\\s+([\\s\\S]+?)(?:\\s+track\\s+by\\s+([\\s\\S]+?))?$/,\n      nullModelCtrl = {$setViewValue: noop};\n// jshint maxlen: 100\n\n  return {\n    restrict: 'E',\n    require: ['select', '?ngModel'],\n    controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {\n      var self = this,\n          optionsMap = {},\n          ngModelCtrl = nullModelCtrl,\n          nullOption,\n          unknownOption;\n\n\n      self.databound = $attrs.ngModel;\n\n\n      self.init = function(ngModelCtrl_, nullOption_, unknownOption_) {\n        ngModelCtrl = ngModelCtrl_;\n        nullOption = nullOption_;\n        unknownOption = unknownOption_;\n      };\n\n\n      self.addOption = function(value, element) {\n        assertNotHasOwnProperty(value, '\"option value\"');\n        optionsMap[value] = true;\n\n        if (ngModelCtrl.$viewValue == value) {\n          $element.val(value);\n          if (unknownOption.parent()) unknownOption.remove();\n        }\n        // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459\n        // Adding an <option selected=\"selected\"> element to a <select required=\"required\"> should\n        // automatically select the new element\n        if (element && element[0].hasAttribute('selected')) {\n          element[0].selected = true;\n        }\n      };\n\n\n      self.removeOption = function(value) {\n        if (this.hasOption(value)) {\n          delete optionsMap[value];\n          if (ngModelCtrl.$viewValue === value) {\n            this.renderUnknownOption(value);\n          }\n        }\n      };\n\n\n      self.renderUnknownOption = function(val) {\n        var unknownVal = '? ' + hashKey(val) + ' ?';\n        unknownOption.val(unknownVal);\n        $element.prepend(unknownOption);\n        $element.val(unknownVal);\n        unknownOption.prop('selected', true); // needed for IE\n      };\n\n\n      self.hasOption = function(value) {\n        return optionsMap.hasOwnProperty(value);\n      };\n\n      $scope.$on('$destroy', function() {\n        // disable unknown option so that we don't do work when the whole select is being destroyed\n        self.renderUnknownOption = noop;\n      });\n    }],\n\n    link: function(scope, element, attr, ctrls) {\n      // if ngModel is not defined, we don't need to do anything\n      if (!ctrls[1]) return;\n\n      var selectCtrl = ctrls[0],\n          ngModelCtrl = ctrls[1],\n          multiple = attr.multiple,\n          optionsExp = attr.ngOptions,\n          nullOption = false, // if false, user will not be able to select it (used by ngOptions)\n          emptyOption,\n          renderScheduled = false,\n          // we can't just jqLite('<option>') since jqLite is not smart enough\n          // to create it in <select> and IE barfs otherwise.\n          optionTemplate = jqLite(document.createElement('option')),\n          optGroupTemplate =jqLite(document.createElement('optgroup')),\n          unknownOption = optionTemplate.clone();\n\n      // find \"null\" option\n      for (var i = 0, children = element.children(), ii = children.length; i < ii; i++) {\n        if (children[i].value === '') {\n          emptyOption = nullOption = children.eq(i);\n          break;\n        }\n      }\n\n      selectCtrl.init(ngModelCtrl, nullOption, unknownOption);\n\n      // required validator\n      if (multiple) {\n        ngModelCtrl.$isEmpty = function(value) {\n          return !value || value.length === 0;\n        };\n      }\n\n      if (optionsExp) setupAsOptions(scope, element, ngModelCtrl);\n      else if (multiple) setupAsMultiple(scope, element, ngModelCtrl);\n      else setupAsSingle(scope, element, ngModelCtrl, selectCtrl);\n\n\n      ////////////////////////////\n\n\n\n      function setupAsSingle(scope, selectElement, ngModelCtrl, selectCtrl) {\n        ngModelCtrl.$render = function() {\n          var viewValue = ngModelCtrl.$viewValue;\n\n          if (selectCtrl.hasOption(viewValue)) {\n            if (unknownOption.parent()) unknownOption.remove();\n            selectElement.val(viewValue);\n            if (viewValue === '') emptyOption.prop('selected', true); // to make IE9 happy\n          } else {\n            if (isUndefined(viewValue) && emptyOption) {\n              selectElement.val('');\n            } else {\n              selectCtrl.renderUnknownOption(viewValue);\n            }\n          }\n        };\n\n        selectElement.on('change', function() {\n          scope.$apply(function() {\n            if (unknownOption.parent()) unknownOption.remove();\n            ngModelCtrl.$setViewValue(selectElement.val());\n          });\n        });\n      }\n\n      function setupAsMultiple(scope, selectElement, ctrl) {\n        var lastView;\n        ctrl.$render = function() {\n          var items = new HashMap(ctrl.$viewValue);\n          forEach(selectElement.find('option'), function(option) {\n            option.selected = isDefined(items.get(option.value));\n          });\n        };\n\n        // we have to do it on each watch since ngModel watches reference, but\n        // we need to work of an array, so we need to see if anything was inserted/removed\n        scope.$watch(function selectMultipleWatch() {\n          if (!equals(lastView, ctrl.$viewValue)) {\n            lastView = shallowCopy(ctrl.$viewValue);\n            ctrl.$render();\n          }\n        });\n\n        selectElement.on('change', function() {\n          scope.$apply(function() {\n            var array = [];\n            forEach(selectElement.find('option'), function(option) {\n              if (option.selected) {\n                array.push(option.value);\n              }\n            });\n            ctrl.$setViewValue(array);\n          });\n        });\n      }\n\n      function setupAsOptions(scope, selectElement, ctrl) {\n        var match;\n\n        if (!(match = optionsExp.match(NG_OPTIONS_REGEXP))) {\n          throw ngOptionsMinErr('iexp',\n            \"Expected expression in form of \" +\n            \"'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'\" +\n            \" but got '{0}'. Element: {1}\",\n            optionsExp, startingTag(selectElement));\n        }\n\n        var displayFn = $parse(match[2] || match[1]),\n            valueName = match[4] || match[6],\n            selectAs = / as /.test(match[0]) && match[1],\n            selectAsFn = selectAs ? $parse(selectAs) : null,\n            keyName = match[5],\n            groupByFn = $parse(match[3] || ''),\n            valueFn = $parse(match[2] ? match[1] : valueName),\n            valuesFn = $parse(match[7]),\n            track = match[8],\n            trackFn = track ? $parse(match[8]) : null,\n            trackKeysCache = {},\n            // This is an array of array of existing option groups in DOM.\n            // We try to reuse these if possible\n            // - optionGroupsCache[0] is the options with no option group\n            // - optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element\n            optionGroupsCache = [[{element: selectElement, label:''}]],\n            //re-usable object to represent option's locals\n            locals = {};\n\n        if (nullOption) {\n          // compile the element since there might be bindings in it\n          $compile(nullOption)(scope);\n\n          // remove the class, which is added automatically because we recompile the element and it\n          // becomes the compilation root\n          nullOption.removeClass('ng-scope');\n\n          // we need to remove it before calling selectElement.empty() because otherwise IE will\n          // remove the label from the element. wtf?\n          nullOption.remove();\n        }\n\n        // clear contents, we'll add what's needed based on the model\n        selectElement.empty();\n\n        selectElement.on('change', selectionChanged);\n\n        ctrl.$render = render;\n\n        scope.$watchCollection(valuesFn, scheduleRendering);\n        scope.$watchCollection(getLabels, scheduleRendering);\n\n        if (multiple) {\n          scope.$watchCollection(function() { return ctrl.$modelValue; }, scheduleRendering);\n        }\n\n        // ------------------------------------------------------------------ //\n\n        function callExpression(exprFn, key, value) {\n          locals[valueName] = value;\n          if (keyName) locals[keyName] = key;\n          return exprFn(scope, locals);\n        }\n\n        function selectionChanged() {\n          scope.$apply(function() {\n            var collection = valuesFn(scope) || [];\n            var viewValue;\n            if (multiple) {\n              viewValue = [];\n              forEach(selectElement.val(), function(selectedKey) {\n                  selectedKey = trackFn ? trackKeysCache[selectedKey] : selectedKey;\n                viewValue.push(getViewValue(selectedKey, collection[selectedKey]));\n              });\n            } else {\n              var selectedKey = trackFn ? trackKeysCache[selectElement.val()] : selectElement.val();\n              viewValue = getViewValue(selectedKey, collection[selectedKey]);\n            }\n            ctrl.$setViewValue(viewValue);\n            render();\n          });\n        }\n\n        function getViewValue(key, value) {\n          if (key === '?') {\n            return undefined;\n          } else if (key === '') {\n            return null;\n          } else {\n            var viewValueFn = selectAsFn ? selectAsFn : valueFn;\n            return callExpression(viewValueFn, key, value);\n          }\n        }\n\n        function getLabels() {\n          var values = valuesFn(scope);\n          var toDisplay;\n          if (values && isArray(values)) {\n            toDisplay = new Array(values.length);\n            for (var i = 0, ii = values.length; i < ii; i++) {\n              toDisplay[i] = callExpression(displayFn, i, values[i]);\n            }\n            return toDisplay;\n          } else if (values) {\n            // TODO: Add a test for this case\n            toDisplay = {};\n            for (var prop in values) {\n              if (values.hasOwnProperty(prop)) {\n                toDisplay[prop] = callExpression(displayFn, prop, values[prop]);\n              }\n            }\n          }\n          return toDisplay;\n        }\n\n        function createIsSelectedFn(viewValue) {\n          var selectedSet;\n          if (multiple) {\n            if (trackFn && isArray(viewValue)) {\n\n              selectedSet = new HashMap([]);\n              for (var trackIndex = 0; trackIndex < viewValue.length; trackIndex++) {\n                // tracking by key\n                selectedSet.put(callExpression(trackFn, null, viewValue[trackIndex]), true);\n              }\n            } else {\n              selectedSet = new HashMap(viewValue);\n            }\n          } else if (trackFn) {\n            viewValue = callExpression(trackFn, null, viewValue);\n          }\n\n          return function isSelected(key, value) {\n            var compareValueFn;\n            if (trackFn) {\n              compareValueFn = trackFn;\n            } else if (selectAsFn) {\n              compareValueFn = selectAsFn;\n            } else {\n              compareValueFn = valueFn;\n            }\n\n            if (multiple) {\n              return isDefined(selectedSet.remove(callExpression(compareValueFn, key, value)));\n            } else {\n              return viewValue === callExpression(compareValueFn, key, value);\n            }\n          };\n        }\n\n        function scheduleRendering() {\n          if (!renderScheduled) {\n            scope.$$postDigest(render);\n            renderScheduled = true;\n          }\n        }\n\n        /**\n         * A new labelMap is created with each render.\n         * This function is called for each existing option with added=false,\n         * and each new option with added=true.\n         * - Labels that are passed to this method twice,\n         * (once with added=true and once with added=false) will end up with a value of 0, and\n         * will cause no change to happen to the corresponding option.\n         * - Labels that are passed to this method only once with added=false will end up with a\n         * value of -1 and will eventually be passed to selectCtrl.removeOption()\n         * - Labels that are passed to this method only once with added=true will end up with a\n         * value of 1 and will eventually be passed to selectCtrl.addOption()\n        */\n        function updateLabelMap(labelMap, label, added) {\n          labelMap[label] = labelMap[label] || 0;\n          labelMap[label] += (added ? 1 : -1);\n        }\n\n        function render() {\n          renderScheduled = false;\n\n          // Temporary location for the option groups before we render them\n          var optionGroups = {'':[]},\n              optionGroupNames = [''],\n              optionGroupName,\n              optionGroup,\n              option,\n              existingParent, existingOptions, existingOption,\n              viewValue = ctrl.$viewValue,\n              values = valuesFn(scope) || [],\n              keys = keyName ? sortedKeys(values) : values,\n              key,\n              value,\n              groupLength, length,\n              groupIndex, index,\n              labelMap = {},\n              selected,\n              isSelected = createIsSelectedFn(viewValue),\n              anySelected = false,\n              lastElement,\n              element,\n              label,\n              optionId;\n\n          trackKeysCache = {};\n\n          // We now build up the list of options we need (we merge later)\n          for (index = 0; length = keys.length, index < length; index++) {\n            key = index;\n            if (keyName) {\n              key = keys[index];\n              if (key.charAt(0) === '$') continue;\n            }\n            value = values[key];\n\n            optionGroupName = callExpression(groupByFn, key, value) || '';\n            if (!(optionGroup = optionGroups[optionGroupName])) {\n              optionGroup = optionGroups[optionGroupName] = [];\n              optionGroupNames.push(optionGroupName);\n            }\n\n            selected = isSelected(key, value);\n            anySelected = anySelected || selected;\n\n            label = callExpression(displayFn, key, value); // what will be seen by the user\n\n            // doing displayFn(scope, locals) || '' overwrites zero values\n            label = isDefined(label) ? label : '';\n            optionId = trackFn ? trackFn(scope, locals) : (keyName ? keys[index] : index);\n            if (trackFn) {\n              trackKeysCache[optionId] = key;\n            }\n\n            optionGroup.push({\n              // either the index into array or key from object\n              id: optionId,\n              label: label,\n              selected: selected                   // determine if we should be selected\n            });\n          }\n          if (!multiple) {\n            if (nullOption || viewValue === null) {\n              // insert null option if we have a placeholder, or the model is null\n              optionGroups[''].unshift({id:'', label:'', selected:!anySelected});\n            } else if (!anySelected) {\n              // option could not be found, we have to insert the undefined item\n              optionGroups[''].unshift({id:'?', label:'', selected:true});\n            }\n          }\n\n          // Now we need to update the list of DOM nodes to match the optionGroups we computed above\n          for (groupIndex = 0, groupLength = optionGroupNames.length;\n               groupIndex < groupLength;\n               groupIndex++) {\n            // current option group name or '' if no group\n            optionGroupName = optionGroupNames[groupIndex];\n\n            // list of options for that group. (first item has the parent)\n            optionGroup = optionGroups[optionGroupName];\n\n            if (optionGroupsCache.length <= groupIndex) {\n              // we need to grow the optionGroups\n              existingParent = {\n                element: optGroupTemplate.clone().attr('label', optionGroupName),\n                label: optionGroup.label\n              };\n              existingOptions = [existingParent];\n              optionGroupsCache.push(existingOptions);\n              selectElement.append(existingParent.element);\n            } else {\n              existingOptions = optionGroupsCache[groupIndex];\n              existingParent = existingOptions[0];  // either SELECT (no group) or OPTGROUP element\n\n              // update the OPTGROUP label if not the same.\n              if (existingParent.label != optionGroupName) {\n                existingParent.element.attr('label', existingParent.label = optionGroupName);\n              }\n            }\n\n            lastElement = null;  // start at the beginning\n            for (index = 0, length = optionGroup.length; index < length; index++) {\n              option = optionGroup[index];\n              if ((existingOption = existingOptions[index + 1])) {\n                // reuse elements\n                lastElement = existingOption.element;\n                if (existingOption.label !== option.label) {\n                  updateLabelMap(labelMap, existingOption.label, false);\n                  updateLabelMap(labelMap, option.label, true);\n                  lastElement.text(existingOption.label = option.label);\n                  lastElement.prop('label', existingOption.label);\n                }\n                if (existingOption.id !== option.id) {\n                  lastElement.val(existingOption.id = option.id);\n                }\n                // lastElement.prop('selected') provided by jQuery has side-effects\n                if (lastElement[0].selected !== option.selected) {\n                  lastElement.prop('selected', (existingOption.selected = option.selected));\n                  if (msie) {\n                    // See #7692\n                    // The selected item wouldn't visually update on IE without this.\n                    // Tested on Win7: IE9, IE10 and IE11. Future IEs should be tested as well\n                    lastElement.prop('selected', existingOption.selected);\n                  }\n                }\n              } else {\n                // grow elements\n\n                // if it's a null option\n                if (option.id === '' && nullOption) {\n                  // put back the pre-compiled element\n                  element = nullOption;\n                } else {\n                  // jQuery(v1.4.2) Bug: We should be able to chain the method calls, but\n                  // in this version of jQuery on some browser the .text() returns a string\n                  // rather then the element.\n                  (element = optionTemplate.clone())\n                      .val(option.id)\n                      .prop('selected', option.selected)\n                      .attr('selected', option.selected)\n                      .prop('label', option.label)\n                      .text(option.label);\n                }\n\n                existingOptions.push(existingOption = {\n                    element: element,\n                    label: option.label,\n                    id: option.id,\n                    selected: option.selected\n                });\n                updateLabelMap(labelMap, option.label, true);\n                if (lastElement) {\n                  lastElement.after(element);\n                } else {\n                  existingParent.element.append(element);\n                }\n                lastElement = element;\n              }\n            }\n            // remove any excessive OPTIONs in a group\n            index++; // increment since the existingOptions[0] is parent element not OPTION\n            while (existingOptions.length > index) {\n              option = existingOptions.pop();\n              updateLabelMap(labelMap, option.label, false);\n              option.element.remove();\n            }\n          }\n          // remove any excessive OPTGROUPs from select\n          while (optionGroupsCache.length > groupIndex) {\n            // remove all the labels in the option group\n            optionGroup = optionGroupsCache.pop();\n            for (index = 1; index < optionGroup.length; ++index) {\n              updateLabelMap(labelMap, optionGroup[index].label, false);\n            }\n            optionGroup[0].element.remove();\n          }\n          forEach(labelMap, function(count, label) {\n            if (count > 0) {\n              selectCtrl.addOption(label);\n            } else if (count < 0) {\n              selectCtrl.removeOption(label);\n            }\n          });\n        }\n      }\n    }\n  };\n}];\n\nvar optionDirective = ['$interpolate', function($interpolate) {\n  var nullSelectCtrl = {\n    addOption: noop,\n    removeOption: noop\n  };\n\n  return {\n    restrict: 'E',\n    priority: 100,\n    compile: function(element, attr) {\n      if (isUndefined(attr.value)) {\n        var interpolateFn = $interpolate(element.text(), true);\n        if (!interpolateFn) {\n          attr.$set('value', element.text());\n        }\n      }\n\n      return function(scope, element, attr) {\n        var selectCtrlName = '$selectController',\n            parent = element.parent(),\n            selectCtrl = parent.data(selectCtrlName) ||\n              parent.parent().data(selectCtrlName); // in case we are in optgroup\n\n        if (!selectCtrl || !selectCtrl.databound) {\n          selectCtrl = nullSelectCtrl;\n        }\n\n        if (interpolateFn) {\n          scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) {\n            attr.$set('value', newVal);\n            if (oldVal !== newVal) {\n              selectCtrl.removeOption(oldVal);\n            }\n            selectCtrl.addOption(newVal, element);\n          });\n        } else {\n          selectCtrl.addOption(attr.value, element);\n        }\n\n        element.on('$destroy', function() {\n          selectCtrl.removeOption(attr.value);\n        });\n      };\n    }\n  };\n}];\n\nvar styleDirective = valueFn({\n  restrict: 'E',\n  terminal: false\n});\n\nvar requiredDirective = function() {\n  return {\n    restrict: 'A',\n    require: '?ngModel',\n    link: function(scope, elm, attr, ctrl) {\n      if (!ctrl) return;\n      attr.required = true; // force truthy in case we are on non input element\n\n      ctrl.$validators.required = function(modelValue, viewValue) {\n        return !attr.required || !ctrl.$isEmpty(viewValue);\n      };\n\n      attr.$observe('required', function() {\n        ctrl.$validate();\n      });\n    }\n  };\n};\n\n\nvar patternDirective = function() {\n  return {\n    restrict: 'A',\n    require: '?ngModel',\n    link: function(scope, elm, attr, ctrl) {\n      if (!ctrl) return;\n\n      var regexp, patternExp = attr.ngPattern || attr.pattern;\n      attr.$observe('pattern', function(regex) {\n        if (isString(regex) && regex.length > 0) {\n          regex = new RegExp('^' + regex + '$');\n        }\n\n        if (regex && !regex.test) {\n          throw minErr('ngPattern')('noregexp',\n            'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,\n            regex, startingTag(elm));\n        }\n\n        regexp = regex || undefined;\n        ctrl.$validate();\n      });\n\n      ctrl.$validators.pattern = function(value) {\n        return ctrl.$isEmpty(value) || isUndefined(regexp) || regexp.test(value);\n      };\n    }\n  };\n};\n\n\nvar maxlengthDirective = function() {\n  return {\n    restrict: 'A',\n    require: '?ngModel',\n    link: function(scope, elm, attr, ctrl) {\n      if (!ctrl) return;\n\n      var maxlength = -1;\n      attr.$observe('maxlength', function(value) {\n        var intVal = int(value);\n        maxlength = isNaN(intVal) ? -1 : intVal;\n        ctrl.$validate();\n      });\n      ctrl.$validators.maxlength = function(modelValue, viewValue) {\n        return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);\n      };\n    }\n  };\n};\n\nvar minlengthDirective = function() {\n  return {\n    restrict: 'A',\n    require: '?ngModel',\n    link: function(scope, elm, attr, ctrl) {\n      if (!ctrl) return;\n\n      var minlength = 0;\n      attr.$observe('minlength', function(value) {\n        minlength = int(value) || 0;\n        ctrl.$validate();\n      });\n      ctrl.$validators.minlength = function(modelValue, viewValue) {\n        return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;\n      };\n    }\n  };\n};\n\n  if (window.angular.bootstrap) {\n    //AngularJS is already loaded, so we can return here...\n    console.log('WARNING: Tried to load angular more than once.');\n    return;\n  }\n\n  //try to bind to jquery now so that one can write jqLite(document).ready()\n  //but we will rebind on bootstrap again.\n  bindJQuery();\n\n  publishExternalAPI(angular);\n\n  jqLite(document).ready(function() {\n    angularInit(document, bootstrap);\n  });\n\n})(window, document);\n\n!window.angular.$$csp() && window.angular.element(document).find('head').prepend('<style type=\"text/css\">@charset \"UTF-8\";[ng\\\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\\\:form{display:block;}</style>');"
  },
  {
    "path": "public/dist/js/chart.js",
    "content": "/*!\n * Chart.js\n * http://chartjs.org/\n * Version: 1.0.2\n *\n * Copyright 2015 Nick Downie\n * Released under the MIT license\n * https://github.com/nnnick/Chart.js/blob/master/LICENSE.md\n */\n\n\n(function(){\n\n\t\"use strict\";\n\n\t//Declare root variable - window in the browser, global on the server\n\tvar root = this,\n\t\tprevious = root.Chart;\n\n\t//Occupy the global variable of Chart, and create a simple base class\n\tvar Chart = function(context){\n\t\tvar chart = this;\n\t\tthis.canvas = context.canvas;\n\n\t\tthis.ctx = context;\n\n\t\t//Variables global to the chart\n\t\tvar computeDimension = function(element,dimension)\n\t\t{\n\t\t\tif (element['offset'+dimension])\n\t\t\t{\n\t\t\t\treturn element['offset'+dimension];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\treturn document.defaultView.getComputedStyle(element).getPropertyValue(dimension);\n\t\t\t}\n\t\t}\n\n\t\tvar width = this.width = computeDimension(context.canvas,'Width');\n\t\tvar height = this.height = computeDimension(context.canvas,'Height');\n\n\t\t// Firefox requires this to work correctly\n\t\tcontext.canvas.width  = width;\n\t\tcontext.canvas.height = height;\n\n\t\tvar width = this.width = context.canvas.width;\n\t\tvar height = this.height = context.canvas.height;\n\t\tthis.aspectRatio = this.width / this.height;\n\t\t//High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale.\n\t\thelpers.retinaScale(this);\n\n\t\treturn this;\n\t};\n\t//Globally expose the defaults to allow for user updating/changing\n\tChart.defaults = {\n\t\tglobal: {\n\t\t\t// Boolean - Whether to animate the chart\n\t\t\tanimation: true,\n\n\t\t\t// Number - Number of animation steps\n\t\t\tanimationSteps: 60,\n\n\t\t\t// String - Animation easing effect\n\t\t\tanimationEasing: \"easeOutQuart\",\n\n\t\t\t// Boolean - If we should show the scale at all\n\t\t\tshowScale: true,\n\n\t\t\t// Boolean - If we want to override with a hard coded scale\n\t\t\tscaleOverride: false,\n\n\t\t\t// ** Required if scaleOverride is true **\n\t\t\t// Number - The number of steps in a hard coded scale\n\t\t\tscaleSteps: null,\n\t\t\t// Number - The value jump in the hard coded scale\n\t\t\tscaleStepWidth: null,\n\t\t\t// Number - The scale starting value\n\t\t\tscaleStartValue: null,\n\n\t\t\t// String - Colour of the scale line\n\t\t\tscaleLineColor: \"rgba(0,0,0,.1)\",\n\n\t\t\t// Number - Pixel width of the scale line\n\t\t\tscaleLineWidth: 1,\n\n\t\t\t// Boolean - Whether to show labels on the scale\n\t\t\tscaleShowLabels: true,\n\n\t\t\t// Interpolated JS string - can access value\n\t\t\tscaleLabel: \"<%=value%>\",\n\n\t\t\t// Boolean - Whether the scale should stick to integers, and not show any floats even if drawing space is there\n\t\t\tscaleIntegersOnly: true,\n\n\t\t\t// Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value\n\t\t\tscaleBeginAtZero: false,\n\n\t\t\t// String - Scale label font declaration for the scale label\n\t\t\tscaleFontFamily: \"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif\",\n\n\t\t\t// Number - Scale label font size in pixels\n\t\t\tscaleFontSize: 12,\n\n\t\t\t// String - Scale label font weight style\n\t\t\tscaleFontStyle: \"normal\",\n\n\t\t\t// String - Scale label font colour\n\t\t\tscaleFontColor: \"#666\",\n\n\t\t\t// Boolean - whether or not the chart should be responsive and resize when the browser does.\n\t\t\tresponsive: false,\n\n\t\t\t// Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container\n\t\t\tmaintainAspectRatio: true,\n\n\t\t\t// Boolean - Determines whether to draw tooltips on the canvas or not - attaches events to touchmove & mousemove\n\t\t\tshowTooltips: true,\n\n\t\t\t// Boolean - Determines whether to draw built-in tooltip or call custom tooltip function\n\t\t\tcustomTooltips: false,\n\n\t\t\t// Array - Array of string names to attach tooltip events\n\t\t\ttooltipEvents: [\"mousemove\", \"touchstart\", \"touchmove\", \"mouseout\"],\n\n\t\t\t// String - Tooltip background colour\n\t\t\ttooltipFillColor: \"rgba(0,0,0,0.8)\",\n\n\t\t\t// String - Tooltip label font declaration for the scale label\n\t\t\ttooltipFontFamily: \"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif\",\n\n\t\t\t// Number - Tooltip label font size in pixels\n\t\t\ttooltipFontSize: 14,\n\n\t\t\t// String - Tooltip font weight style\n\t\t\ttooltipFontStyle: \"normal\",\n\n\t\t\t// String - Tooltip label font colour\n\t\t\ttooltipFontColor: \"#fff\",\n\n\t\t\t// String - Tooltip title font declaration for the scale label\n\t\t\ttooltipTitleFontFamily: \"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif\",\n\n\t\t\t// Number - Tooltip title font size in pixels\n\t\t\ttooltipTitleFontSize: 14,\n\n\t\t\t// String - Tooltip title font weight style\n\t\t\ttooltipTitleFontStyle: \"bold\",\n\n\t\t\t// String - Tooltip title font colour\n\t\t\ttooltipTitleFontColor: \"#fff\",\n\n\t\t\t// Number - pixel width of padding around tooltip text\n\t\t\ttooltipYPadding: 6,\n\n\t\t\t// Number - pixel width of padding around tooltip text\n\t\t\ttooltipXPadding: 6,\n\n\t\t\t// Number - Size of the caret on the tooltip\n\t\t\ttooltipCaretSize: 8,\n\n\t\t\t// Number - Pixel radius of the tooltip border\n\t\t\ttooltipCornerRadius: 6,\n\n\t\t\t// Number - Pixel offset from point x to tooltip edge\n\t\t\ttooltipXOffset: 10,\n\n\t\t\t// String - Template string for single tooltips\n\t\t\ttooltipTemplate: \"<%if (label){%><%=label%>: <%}%><%= value %>\",\n\n\t\t\t// String - Template string for single tooltips\n\t\t\tmultiTooltipTemplate: \"<%= value %>\",\n\n\t\t\t// String - Colour behind the legend colour block\n\t\t\tmultiTooltipKeyBackground: '#fff',\n\n\t\t\t// Function - Will fire on animation progression.\n\t\t\tonAnimationProgress: function(){},\n\n\t\t\t// Function - Will fire on animation completion.\n\t\t\tonAnimationComplete: function(){}\n\n\t\t}\n\t};\n\n\t//Create a dictionary of chart types, to allow for extension of existing types\n\tChart.types = {};\n\n\t//Global Chart helpers object for utility methods and classes\n\tvar helpers = Chart.helpers = {};\n\n\t\t//-- Basic js utility methods\n\tvar each = helpers.each = function(loopable,callback,self){\n\t\t\tvar additionalArgs = Array.prototype.slice.call(arguments, 3);\n\t\t\t// Check to see if null or undefined firstly.\n\t\t\tif (loopable){\n\t\t\t\tif (loopable.length === +loopable.length){\n\t\t\t\t\tvar i;\n\t\t\t\t\tfor (i=0; i<loopable.length; i++){\n\t\t\t\t\t\tcallback.apply(self,[loopable[i], i].concat(additionalArgs));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse{\n\t\t\t\t\tfor (var item in loopable){\n\t\t\t\t\t\tcallback.apply(self,[loopable[item],item].concat(additionalArgs));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tclone = helpers.clone = function(obj){\n\t\t\tvar objClone = {};\n\t\t\teach(obj,function(value,key){\n\t\t\t\tif (obj.hasOwnProperty(key)) objClone[key] = value;\n\t\t\t});\n\t\t\treturn objClone;\n\t\t},\n\t\textend = helpers.extend = function(base){\n\t\t\teach(Array.prototype.slice.call(arguments,1), function(extensionObject) {\n\t\t\t\teach(extensionObject,function(value,key){\n\t\t\t\t\tif (extensionObject.hasOwnProperty(key)) base[key] = value;\n\t\t\t\t});\n\t\t\t});\n\t\t\treturn base;\n\t\t},\n\t\tmerge = helpers.merge = function(base,master){\n\t\t\t//Merge properties in left object over to a shallow clone of object right.\n\t\t\tvar args = Array.prototype.slice.call(arguments,0);\n\t\t\targs.unshift({});\n\t\t\treturn extend.apply(null, args);\n\t\t},\n\t\tindexOf = helpers.indexOf = function(arrayToSearch, item){\n\t\t\tif (Array.prototype.indexOf) {\n\t\t\t\treturn arrayToSearch.indexOf(item);\n\t\t\t}\n\t\t\telse{\n\t\t\t\tfor (var i = 0; i < arrayToSearch.length; i++) {\n\t\t\t\t\tif (arrayToSearch[i] === item) return i;\n\t\t\t\t}\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t},\n\t\twhere = helpers.where = function(collection, filterCallback){\n\t\t\tvar filtered = [];\n\n\t\t\thelpers.each(collection, function(item){\n\t\t\t\tif (filterCallback(item)){\n\t\t\t\t\tfiltered.push(item);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn filtered;\n\t\t},\n\t\tfindNextWhere = helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex){\n\t\t\t// Default to start of the array\n\t\t\tif (!startIndex){\n\t\t\t\tstartIndex = -1;\n\t\t\t}\n\t\t\tfor (var i = startIndex + 1; i < arrayToSearch.length; i++) {\n\t\t\t\tvar currentItem = arrayToSearch[i];\n\t\t\t\tif (filterCallback(currentItem)){\n\t\t\t\t\treturn currentItem;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tfindPreviousWhere = helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex){\n\t\t\t// Default to end of the array\n\t\t\tif (!startIndex){\n\t\t\t\tstartIndex = arrayToSearch.length;\n\t\t\t}\n\t\t\tfor (var i = startIndex - 1; i >= 0; i--) {\n\t\t\t\tvar currentItem = arrayToSearch[i];\n\t\t\t\tif (filterCallback(currentItem)){\n\t\t\t\t\treturn currentItem;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tinherits = helpers.inherits = function(extensions){\n\t\t\t//Basic javascript inheritance based on the model created in Backbone.js\n\t\t\tvar parent = this;\n\t\t\tvar ChartElement = (extensions && extensions.hasOwnProperty(\"constructor\")) ? extensions.constructor : function(){ return parent.apply(this, arguments); };\n\n\t\t\tvar Surrogate = function(){ this.constructor = ChartElement;};\n\t\t\tSurrogate.prototype = parent.prototype;\n\t\t\tChartElement.prototype = new Surrogate();\n\n\t\t\tChartElement.extend = inherits;\n\n\t\t\tif (extensions) extend(ChartElement.prototype, extensions);\n\n\t\t\tChartElement.__super__ = parent.prototype;\n\n\t\t\treturn ChartElement;\n\t\t},\n\t\tnoop = helpers.noop = function(){},\n\t\tuid = helpers.uid = (function(){\n\t\t\tvar id=0;\n\t\t\treturn function(){\n\t\t\t\treturn \"chart-\" + id++;\n\t\t\t};\n\t\t})(),\n\t\twarn = helpers.warn = function(str){\n\t\t\t//Method for warning of errors\n\t\t\tif (window.console && typeof window.console.warn == \"function\") console.warn(str);\n\t\t},\n\t\tamd = helpers.amd = (typeof define == 'function' && define.amd),\n\t\t//-- Math methods\n\t\tisNumber = helpers.isNumber = function(n){\n\t\t\treturn !isNaN(parseFloat(n)) && isFinite(n);\n\t\t},\n\t\tmax = helpers.max = function(array){\n\t\t\treturn Math.max.apply( Math, array );\n\t\t},\n\t\tmin = helpers.min = function(array){\n\t\t\treturn Math.min.apply( Math, array );\n\t\t},\n\t\tcap = helpers.cap = function(valueToCap,maxValue,minValue){\n\t\t\tif(isNumber(maxValue)) {\n\t\t\t\tif( valueToCap > maxValue ) {\n\t\t\t\t\treturn maxValue;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if(isNumber(minValue)){\n\t\t\t\tif ( valueToCap < minValue ){\n\t\t\t\t\treturn minValue;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn valueToCap;\n\t\t},\n\t\tgetDecimalPlaces = helpers.getDecimalPlaces = function(num){\n\t\t\tif (num%1!==0 && isNumber(num)){\n\t\t\t\treturn num.toString().split(\".\")[1].length;\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t},\n\t\ttoRadians = helpers.radians = function(degrees){\n\t\t\treturn degrees * (Math.PI/180);\n\t\t},\n\t\t// Gets the angle from vertical upright to the point about a centre.\n\t\tgetAngleFromPoint = helpers.getAngleFromPoint = function(centrePoint, anglePoint){\n\t\t\tvar distanceFromXCenter = anglePoint.x - centrePoint.x,\n\t\t\t\tdistanceFromYCenter = anglePoint.y - centrePoint.y,\n\t\t\t\tradialDistanceFromCenter = Math.sqrt( distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);\n\n\n\t\t\tvar angle = Math.PI * 2 + Math.atan2(distanceFromYCenter, distanceFromXCenter);\n\n\t\t\t//If the segment is in the top left quadrant, we need to add another rotation to the angle\n\t\t\tif (distanceFromXCenter < 0 && distanceFromYCenter < 0){\n\t\t\t\tangle += Math.PI*2;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tangle: angle,\n\t\t\t\tdistance: radialDistanceFromCenter\n\t\t\t};\n\t\t},\n\t\taliasPixel = helpers.aliasPixel = function(pixelWidth){\n\t\t\treturn (pixelWidth % 2 === 0) ? 0 : 0.5;\n\t\t},\n\t\tsplineCurve = helpers.splineCurve = function(FirstPoint,MiddlePoint,AfterPoint,t){\n\t\t\t//Props to Rob Spencer at scaled innovation for his post on splining between points\n\t\t\t//http://scaledinnovation.com/analytics/splines/aboutSplines.html\n\t\t\tvar d01=Math.sqrt(Math.pow(MiddlePoint.x-FirstPoint.x,2)+Math.pow(MiddlePoint.y-FirstPoint.y,2)),\n\t\t\t\td12=Math.sqrt(Math.pow(AfterPoint.x-MiddlePoint.x,2)+Math.pow(AfterPoint.y-MiddlePoint.y,2)),\n\t\t\t\tfa=t*d01/(d01+d12),// scaling factor for triangle Ta\n\t\t\t\tfb=t*d12/(d01+d12);\n\t\t\treturn {\n\t\t\t\tinner : {\n\t\t\t\t\tx : MiddlePoint.x-fa*(AfterPoint.x-FirstPoint.x),\n\t\t\t\t\ty : MiddlePoint.y-fa*(AfterPoint.y-FirstPoint.y)\n\t\t\t\t},\n\t\t\t\touter : {\n\t\t\t\t\tx: MiddlePoint.x+fb*(AfterPoint.x-FirstPoint.x),\n\t\t\t\t\ty : MiddlePoint.y+fb*(AfterPoint.y-FirstPoint.y)\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\tcalculateOrderOfMagnitude = helpers.calculateOrderOfMagnitude = function(val){\n\t\t\treturn Math.floor(Math.log(val) / Math.LN10);\n\t\t},\n\t\tcalculateScaleRange = helpers.calculateScaleRange = function(valuesArray, drawingSize, textSize, startFromZero, integersOnly){\n\n\t\t\t//Set a minimum step of two - a point at the top of the graph, and a point at the base\n\t\t\tvar minSteps = 2,\n\t\t\t\tmaxSteps = Math.floor(drawingSize/(textSize * 1.5)),\n\t\t\t\tskipFitting = (minSteps >= maxSteps);\n\n\t\t\tvar maxValue = max(valuesArray),\n\t\t\t\tminValue = min(valuesArray);\n\n\t\t\t// We need some degree of seperation here to calculate the scales if all the values are the same\n\t\t\t// Adding/minusing 0.5 will give us a range of 1.\n\t\t\tif (maxValue === minValue){\n\t\t\t\tmaxValue += 0.5;\n\t\t\t\t// So we don't end up with a graph with a negative start value if we've said always start from zero\n\t\t\t\tif (minValue >= 0.5 && !startFromZero){\n\t\t\t\t\tminValue -= 0.5;\n\t\t\t\t}\n\t\t\t\telse{\n\t\t\t\t\t// Make up a whole number above the values\n\t\t\t\t\tmaxValue += 0.5;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvar\tvalueRange = Math.abs(maxValue - minValue),\n\t\t\t\trangeOrderOfMagnitude = calculateOrderOfMagnitude(valueRange),\n\t\t\t\tgraphMax = Math.ceil(maxValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude),\n\t\t\t\tgraphMin = (startFromZero) ? 0 : Math.floor(minValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude),\n\t\t\t\tgraphRange = graphMax - graphMin,\n\t\t\t\tstepValue = Math.pow(10, rangeOrderOfMagnitude),\n\t\t\t\tnumberOfSteps = Math.round(graphRange / stepValue);\n\n\t\t\t//If we have more space on the graph we'll use it to give more definition to the data\n\t\t\twhile((numberOfSteps > maxSteps || (numberOfSteps * 2) < maxSteps) && !skipFitting) {\n\t\t\t\tif(numberOfSteps > maxSteps){\n\t\t\t\t\tstepValue *=2;\n\t\t\t\t\tnumberOfSteps = Math.round(graphRange/stepValue);\n\t\t\t\t\t// Don't ever deal with a decimal number of steps - cancel fitting and just use the minimum number of steps.\n\t\t\t\t\tif (numberOfSteps % 1 !== 0){\n\t\t\t\t\t\tskipFitting = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t//We can fit in double the amount of scale points on the scale\n\t\t\t\telse{\n\t\t\t\t\t//If user has declared ints only, and the step value isn't a decimal\n\t\t\t\t\tif (integersOnly && rangeOrderOfMagnitude >= 0){\n\t\t\t\t\t\t//If the user has said integers only, we need to check that making the scale more granular wouldn't make it a float\n\t\t\t\t\t\tif(stepValue/2 % 1 === 0){\n\t\t\t\t\t\t\tstepValue /=2;\n\t\t\t\t\t\t\tnumberOfSteps = Math.round(graphRange/stepValue);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//If it would make it a float break out of the loop\n\t\t\t\t\t\telse{\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t//If the scale doesn't have to be an int, make the scale more granular anyway.\n\t\t\t\t\telse{\n\t\t\t\t\t\tstepValue /=2;\n\t\t\t\t\t\tnumberOfSteps = Math.round(graphRange/stepValue);\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (skipFitting){\n\t\t\t\tnumberOfSteps = minSteps;\n\t\t\t\tstepValue = graphRange / numberOfSteps;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tsteps : numberOfSteps,\n\t\t\t\tstepValue : stepValue,\n\t\t\t\tmin : graphMin,\n\t\t\t\tmax\t: graphMin + (numberOfSteps * stepValue)\n\t\t\t};\n\n\t\t},\n\t\t/* jshint ignore:start */\n\t\t// Blows up jshint errors based on the new Function constructor\n\t\t//Templating methods\n\t\t//Javascript micro templating by John Resig - source at http://ejohn.org/blog/javascript-micro-templating/\n\t\ttemplate = helpers.template = function(templateString, valuesObject){\n\n\t\t\t// If templateString is function rather than string-template - call the function for valuesObject\n\n\t\t\tif(templateString instanceof Function){\n\t\t\t \treturn templateString(valuesObject);\n\t\t \t}\n\n\t\t\tvar cache = {};\n\t\t\tfunction tmpl(str, data){\n\t\t\t\t// Figure out if we're getting a template, or if we need to\n\t\t\t\t// load the template - and be sure to cache the result.\n\t\t\t\tvar fn = !/\\W/.test(str) ?\n\t\t\t\tcache[str] = cache[str] :\n\n\t\t\t\t// Generate a reusable function that will serve as a template\n\t\t\t\t// generator (and which will be cached).\n\t\t\t\tnew Function(\"obj\",\n\t\t\t\t\t\"var p=[],print=function(){p.push.apply(p,arguments);};\" +\n\n\t\t\t\t\t// Introduce the data as local variables using with(){}\n\t\t\t\t\t\"with(obj){p.push('\" +\n\n\t\t\t\t\t// Convert the template into pure JavaScript\n\t\t\t\t\tstr\n\t\t\t\t\t\t.replace(/[\\r\\t\\n]/g, \" \")\n\t\t\t\t\t\t.split(\"<%\").join(\"\\t\")\n\t\t\t\t\t\t.replace(/((^|%>)[^\\t]*)'/g, \"$1\\r\")\n\t\t\t\t\t\t.replace(/\\t=(.*?)%>/g, \"',$1,'\")\n\t\t\t\t\t\t.split(\"\\t\").join(\"');\")\n\t\t\t\t\t\t.split(\"%>\").join(\"p.push('\")\n\t\t\t\t\t\t.split(\"\\r\").join(\"\\\\'\") +\n\t\t\t\t\t\"');}return p.join('');\"\n\t\t\t\t);\n\n\t\t\t\t// Provide some basic currying to the user\n\t\t\t\treturn data ? fn( data ) : fn;\n\t\t\t}\n\t\t\treturn tmpl(templateString,valuesObject);\n\t\t},\n\t\t/* jshint ignore:end */\n\t\tgenerateLabels = helpers.generateLabels = function(templateString,numberOfSteps,graphMin,stepValue){\n\t\t\tvar labelsArray = new Array(numberOfSteps);\n\t\t\tif (labelTemplateString){\n\t\t\t\teach(labelsArray,function(val,index){\n\t\t\t\t\tlabelsArray[index] = template(templateString,{value: (graphMin + (stepValue*(index+1)))});\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn labelsArray;\n\t\t},\n\t\t//--Animation methods\n\t\t//Easing functions adapted from Robert Penner's easing equations\n\t\t//http://www.robertpenner.com/easing/\n\t\teasingEffects = helpers.easingEffects = {\n\t\t\tlinear: function (t) {\n\t\t\t\treturn t;\n\t\t\t},\n\t\t\teaseInQuad: function (t) {\n\t\t\t\treturn t * t;\n\t\t\t},\n\t\t\teaseOutQuad: function (t) {\n\t\t\t\treturn -1 * t * (t - 2);\n\t\t\t},\n\t\t\teaseInOutQuad: function (t) {\n\t\t\t\tif ((t /= 1 / 2) < 1) return 1 / 2 * t * t;\n\t\t\t\treturn -1 / 2 * ((--t) * (t - 2) - 1);\n\t\t\t},\n\t\t\teaseInCubic: function (t) {\n\t\t\t\treturn t * t * t;\n\t\t\t},\n\t\t\teaseOutCubic: function (t) {\n\t\t\t\treturn 1 * ((t = t / 1 - 1) * t * t + 1);\n\t\t\t},\n\t\t\teaseInOutCubic: function (t) {\n\t\t\t\tif ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t;\n\t\t\t\treturn 1 / 2 * ((t -= 2) * t * t + 2);\n\t\t\t},\n\t\t\teaseInQuart: function (t) {\n\t\t\t\treturn t * t * t * t;\n\t\t\t},\n\t\t\teaseOutQuart: function (t) {\n\t\t\t\treturn -1 * ((t = t / 1 - 1) * t * t * t - 1);\n\t\t\t},\n\t\t\teaseInOutQuart: function (t) {\n\t\t\t\tif ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t * t;\n\t\t\t\treturn -1 / 2 * ((t -= 2) * t * t * t - 2);\n\t\t\t},\n\t\t\teaseInQuint: function (t) {\n\t\t\t\treturn 1 * (t /= 1) * t * t * t * t;\n\t\t\t},\n\t\t\teaseOutQuint: function (t) {\n\t\t\t\treturn 1 * ((t = t / 1 - 1) * t * t * t * t + 1);\n\t\t\t},\n\t\t\teaseInOutQuint: function (t) {\n\t\t\t\tif ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t * t * t;\n\t\t\t\treturn 1 / 2 * ((t -= 2) * t * t * t * t + 2);\n\t\t\t},\n\t\t\teaseInSine: function (t) {\n\t\t\t\treturn -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1;\n\t\t\t},\n\t\t\teaseOutSine: function (t) {\n\t\t\t\treturn 1 * Math.sin(t / 1 * (Math.PI / 2));\n\t\t\t},\n\t\t\teaseInOutSine: function (t) {\n\t\t\t\treturn -1 / 2 * (Math.cos(Math.PI * t / 1) - 1);\n\t\t\t},\n\t\t\teaseInExpo: function (t) {\n\t\t\t\treturn (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1));\n\t\t\t},\n\t\t\teaseOutExpo: function (t) {\n\t\t\t\treturn (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1);\n\t\t\t},\n\t\t\teaseInOutExpo: function (t) {\n\t\t\t\tif (t === 0) return 0;\n\t\t\t\tif (t === 1) return 1;\n\t\t\t\tif ((t /= 1 / 2) < 1) return 1 / 2 * Math.pow(2, 10 * (t - 1));\n\t\t\t\treturn 1 / 2 * (-Math.pow(2, -10 * --t) + 2);\n\t\t\t},\n\t\t\teaseInCirc: function (t) {\n\t\t\t\tif (t >= 1) return t;\n\t\t\t\treturn -1 * (Math.sqrt(1 - (t /= 1) * t) - 1);\n\t\t\t},\n\t\t\teaseOutCirc: function (t) {\n\t\t\t\treturn 1 * Math.sqrt(1 - (t = t / 1 - 1) * t);\n\t\t\t},\n\t\t\teaseInOutCirc: function (t) {\n\t\t\t\tif ((t /= 1 / 2) < 1) return -1 / 2 * (Math.sqrt(1 - t * t) - 1);\n\t\t\t\treturn 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1);\n\t\t\t},\n\t\t\teaseInElastic: function (t) {\n\t\t\t\tvar s = 1.70158;\n\t\t\t\tvar p = 0;\n\t\t\t\tvar a = 1;\n\t\t\t\tif (t === 0) return 0;\n\t\t\t\tif ((t /= 1) == 1) return 1;\n\t\t\t\tif (!p) p = 1 * 0.3;\n\t\t\t\tif (a < Math.abs(1)) {\n\t\t\t\t\ta = 1;\n\t\t\t\t\ts = p / 4;\n\t\t\t\t} else s = p / (2 * Math.PI) * Math.asin(1 / a);\n\t\t\t\treturn -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));\n\t\t\t},\n\t\t\teaseOutElastic: function (t) {\n\t\t\t\tvar s = 1.70158;\n\t\t\t\tvar p = 0;\n\t\t\t\tvar a = 1;\n\t\t\t\tif (t === 0) return 0;\n\t\t\t\tif ((t /= 1) == 1) return 1;\n\t\t\t\tif (!p) p = 1 * 0.3;\n\t\t\t\tif (a < Math.abs(1)) {\n\t\t\t\t\ta = 1;\n\t\t\t\t\ts = p / 4;\n\t\t\t\t} else s = p / (2 * Math.PI) * Math.asin(1 / a);\n\t\t\t\treturn a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1;\n\t\t\t},\n\t\t\teaseInOutElastic: function (t) {\n\t\t\t\tvar s = 1.70158;\n\t\t\t\tvar p = 0;\n\t\t\t\tvar a = 1;\n\t\t\t\tif (t === 0) return 0;\n\t\t\t\tif ((t /= 1 / 2) == 2) return 1;\n\t\t\t\tif (!p) p = 1 * (0.3 * 1.5);\n\t\t\t\tif (a < Math.abs(1)) {\n\t\t\t\t\ta = 1;\n\t\t\t\t\ts = p / 4;\n\t\t\t\t} else s = p / (2 * Math.PI) * Math.asin(1 / a);\n\t\t\t\tif (t < 1) return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));\n\t\t\t\treturn a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1;\n\t\t\t},\n\t\t\teaseInBack: function (t) {\n\t\t\t\tvar s = 1.70158;\n\t\t\t\treturn 1 * (t /= 1) * t * ((s + 1) * t - s);\n\t\t\t},\n\t\t\teaseOutBack: function (t) {\n\t\t\t\tvar s = 1.70158;\n\t\t\t\treturn 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1);\n\t\t\t},\n\t\t\teaseInOutBack: function (t) {\n\t\t\t\tvar s = 1.70158;\n\t\t\t\tif ((t /= 1 / 2) < 1) return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s));\n\t\t\t\treturn 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);\n\t\t\t},\n\t\t\teaseInBounce: function (t) {\n\t\t\t\treturn 1 - easingEffects.easeOutBounce(1 - t);\n\t\t\t},\n\t\t\teaseOutBounce: function (t) {\n\t\t\t\tif ((t /= 1) < (1 / 2.75)) {\n\t\t\t\t\treturn 1 * (7.5625 * t * t);\n\t\t\t\t} else if (t < (2 / 2.75)) {\n\t\t\t\t\treturn 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75);\n\t\t\t\t} else if (t < (2.5 / 2.75)) {\n\t\t\t\t\treturn 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375);\n\t\t\t\t} else {\n\t\t\t\t\treturn 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375);\n\t\t\t\t}\n\t\t\t},\n\t\t\teaseInOutBounce: function (t) {\n\t\t\t\tif (t < 1 / 2) return easingEffects.easeInBounce(t * 2) * 0.5;\n\t\t\t\treturn easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5;\n\t\t\t}\n\t\t},\n\t\t//Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/\n\t\trequestAnimFrame = helpers.requestAnimFrame = (function(){\n\t\t\treturn window.requestAnimationFrame ||\n\t\t\t\twindow.webkitRequestAnimationFrame ||\n\t\t\t\twindow.mozRequestAnimationFrame ||\n\t\t\t\twindow.oRequestAnimationFrame ||\n\t\t\t\twindow.msRequestAnimationFrame ||\n\t\t\t\tfunction(callback) {\n\t\t\t\t\treturn window.setTimeout(callback, 1000 / 60);\n\t\t\t\t};\n\t\t})(),\n\t\tcancelAnimFrame = helpers.cancelAnimFrame = (function(){\n\t\t\treturn window.cancelAnimationFrame ||\n\t\t\t\twindow.webkitCancelAnimationFrame ||\n\t\t\t\twindow.mozCancelAnimationFrame ||\n\t\t\t\twindow.oCancelAnimationFrame ||\n\t\t\t\twindow.msCancelAnimationFrame ||\n\t\t\t\tfunction(callback) {\n\t\t\t\t\treturn window.clearTimeout(callback, 1000 / 60);\n\t\t\t\t};\n\t\t})(),\n\t\tanimationLoop = helpers.animationLoop = function(callback,totalSteps,easingString,onProgress,onComplete,chartInstance){\n\n\t\t\tvar currentStep = 0,\n\t\t\t\teasingFunction = easingEffects[easingString] || easingEffects.linear;\n\n\t\t\tvar animationFrame = function(){\n\t\t\t\tcurrentStep++;\n\t\t\t\tvar stepDecimal = currentStep/totalSteps;\n\t\t\t\tvar easeDecimal = easingFunction(stepDecimal);\n\n\t\t\t\tcallback.call(chartInstance,easeDecimal,stepDecimal, currentStep);\n\t\t\t\tonProgress.call(chartInstance,easeDecimal,stepDecimal);\n\t\t\t\tif (currentStep < totalSteps){\n\t\t\t\t\tchartInstance.animationFrame = requestAnimFrame(animationFrame);\n\t\t\t\t} else{\n\t\t\t\t\tonComplete.apply(chartInstance);\n\t\t\t\t}\n\t\t\t};\n\t\t\trequestAnimFrame(animationFrame);\n\t\t},\n\t\t//-- DOM methods\n\t\tgetRelativePosition = helpers.getRelativePosition = function(evt){\n\t\t\tvar mouseX, mouseY;\n\t\t\tvar e = evt.originalEvent || evt,\n\t\t\t\tcanvas = evt.currentTarget || evt.srcElement,\n\t\t\t\tboundingRect = canvas.getBoundingClientRect();\n\n\t\t\tif (e.touches){\n\t\t\t\tmouseX = e.touches[0].clientX - boundingRect.left;\n\t\t\t\tmouseY = e.touches[0].clientY - boundingRect.top;\n\n\t\t\t}\n\t\t\telse{\n\t\t\t\tmouseX = e.clientX - boundingRect.left;\n\t\t\t\tmouseY = e.clientY - boundingRect.top;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tx : mouseX,\n\t\t\t\ty : mouseY\n\t\t\t};\n\n\t\t},\n\t\taddEvent = helpers.addEvent = function(node,eventType,method){\n\t\t\tif (node.addEventListener){\n\t\t\t\tnode.addEventListener(eventType,method);\n\t\t\t} else if (node.attachEvent){\n\t\t\t\tnode.attachEvent(\"on\"+eventType, method);\n\t\t\t} else {\n\t\t\t\tnode[\"on\"+eventType] = method;\n\t\t\t}\n\t\t},\n\t\tremoveEvent = helpers.removeEvent = function(node, eventType, handler){\n\t\t\tif (node.removeEventListener){\n\t\t\t\tnode.removeEventListener(eventType, handler, false);\n\t\t\t} else if (node.detachEvent){\n\t\t\t\tnode.detachEvent(\"on\"+eventType,handler);\n\t\t\t} else{\n\t\t\t\tnode[\"on\" + eventType] = noop;\n\t\t\t}\n\t\t},\n\t\tbindEvents = helpers.bindEvents = function(chartInstance, arrayOfEvents, handler){\n\t\t\t// Create the events object if it's not already present\n\t\t\tif (!chartInstance.events) chartInstance.events = {};\n\n\t\t\teach(arrayOfEvents,function(eventName){\n\t\t\t\tchartInstance.events[eventName] = function(){\n\t\t\t\t\thandler.apply(chartInstance, arguments);\n\t\t\t\t};\n\t\t\t\taddEvent(chartInstance.chart.canvas,eventName,chartInstance.events[eventName]);\n\t\t\t});\n\t\t},\n\t\tunbindEvents = helpers.unbindEvents = function (chartInstance, arrayOfEvents) {\n\t\t\teach(arrayOfEvents, function(handler,eventName){\n\t\t\t\tremoveEvent(chartInstance.chart.canvas, eventName, handler);\n\t\t\t});\n\t\t},\n\t\tgetMaximumWidth = helpers.getMaximumWidth = function(domNode){\n\t\t\tvar container = domNode.parentNode;\n\t\t\t// TODO = check cross browser stuff with this.\n\t\t\treturn container.clientWidth;\n\t\t},\n\t\tgetMaximumHeight = helpers.getMaximumHeight = function(domNode){\n\t\t\tvar container = domNode.parentNode;\n\t\t\t// TODO = check cross browser stuff with this.\n\t\t\treturn container.clientHeight;\n\t\t},\n\t\tgetMaximumSize = helpers.getMaximumSize = helpers.getMaximumWidth, // legacy support\n\t\tretinaScale = helpers.retinaScale = function(chart){\n\t\t\tvar ctx = chart.ctx,\n\t\t\t\twidth = chart.canvas.width,\n\t\t\t\theight = chart.canvas.height;\n\n\t\t\tif (window.devicePixelRatio) {\n\t\t\t\tctx.canvas.style.width = width + \"px\";\n\t\t\t\tctx.canvas.style.height = height + \"px\";\n\t\t\t\tctx.canvas.height = height * window.devicePixelRatio;\n\t\t\t\tctx.canvas.width = width * window.devicePixelRatio;\n\t\t\t\tctx.scale(window.devicePixelRatio, window.devicePixelRatio);\n\t\t\t}\n\t\t},\n\t\t//-- Canvas methods\n\t\tclear = helpers.clear = function(chart){\n\t\t\tchart.ctx.clearRect(0,0,chart.width,chart.height);\n\t\t},\n\t\tfontString = helpers.fontString = function(pixelSize,fontStyle,fontFamily){\n\t\t\treturn fontStyle + \" \" + pixelSize+\"px \" + fontFamily;\n\t\t},\n\t\tlongestText = helpers.longestText = function(ctx,font,arrayOfStrings){\n\t\t\tctx.font = font;\n\t\t\tvar longest = 0;\n\t\t\teach(arrayOfStrings,function(string){\n\t\t\t\tvar textWidth = ctx.measureText(string).width;\n\t\t\t\tlongest = (textWidth > longest) ? textWidth : longest;\n\t\t\t});\n\t\t\treturn longest;\n\t\t},\n\t\tdrawRoundedRectangle = helpers.drawRoundedRectangle = function(ctx,x,y,width,height,radius){\n\t\t\tctx.beginPath();\n\t\t\tctx.moveTo(x + radius, y);\n\t\t\tctx.lineTo(x + width - radius, y);\n\t\t\tctx.quadraticCurveTo(x + width, y, x + width, y + radius);\n\t\t\tctx.lineTo(x + width, y + height - radius);\n\t\t\tctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);\n\t\t\tctx.lineTo(x + radius, y + height);\n\t\t\tctx.quadraticCurveTo(x, y + height, x, y + height - radius);\n\t\t\tctx.lineTo(x, y + radius);\n\t\t\tctx.quadraticCurveTo(x, y, x + radius, y);\n\t\t\tctx.closePath();\n\t\t};\n\n\n\t//Store a reference to each instance - allowing us to globally resize chart instances on window resize.\n\t//Destroy method on the chart will remove the instance of the chart from this reference.\n\tChart.instances = {};\n\n\tChart.Type = function(data,options,chart){\n\t\tthis.options = options;\n\t\tthis.chart = chart;\n\t\tthis.id = uid();\n\t\t//Add the chart instance to the global namespace\n\t\tChart.instances[this.id] = this;\n\n\t\t// Initialize is always called when a chart type is created\n\t\t// By default it is a no op, but it should be extended\n\t\tif (options.responsive){\n\t\t\tthis.resize();\n\t\t}\n\t\tthis.initialize.call(this,data);\n\t};\n\n\t//Core methods that'll be a part of every chart type\n\textend(Chart.Type.prototype,{\n\t\tinitialize : function(){return this;},\n\t\tclear : function(){\n\t\t\tclear(this.chart);\n\t\t\treturn this;\n\t\t},\n\t\tstop : function(){\n\t\t\t// Stops any current animation loop occuring\n\t\t\tcancelAnimFrame(this.animationFrame);\n\t\t\treturn this;\n\t\t},\n\t\tresize : function(callback){\n\t\t\tthis.stop();\n\t\t\tvar canvas = this.chart.canvas,\n\t\t\t\tnewWidth = getMaximumWidth(this.chart.canvas),\n\t\t\t\tnewHeight = this.options.maintainAspectRatio ? newWidth / this.chart.aspectRatio : getMaximumHeight(this.chart.canvas);\n\n\t\t\tcanvas.width = this.chart.width = newWidth;\n\t\t\tcanvas.height = this.chart.height = newHeight;\n\n\t\t\tretinaScale(this.chart);\n\n\t\t\tif (typeof callback === \"function\"){\n\t\t\t\tcallback.apply(this, Array.prototype.slice.call(arguments, 1));\n\t\t\t}\n\t\t\treturn this;\n\t\t},\n\t\treflow : noop,\n\t\trender : function(reflow){\n\t\t\tif (reflow){\n\t\t\t\tthis.reflow();\n\t\t\t}\n\t\t\tif (this.options.animation && !reflow){\n\t\t\t\thelpers.animationLoop(\n\t\t\t\t\tthis.draw,\n\t\t\t\t\tthis.options.animationSteps,\n\t\t\t\t\tthis.options.animationEasing,\n\t\t\t\t\tthis.options.onAnimationProgress,\n\t\t\t\t\tthis.options.onAnimationComplete,\n\t\t\t\t\tthis\n\t\t\t\t);\n\t\t\t}\n\t\t\telse{\n\t\t\t\tthis.draw();\n\t\t\t\tthis.options.onAnimationComplete.call(this);\n\t\t\t}\n\t\t\treturn this;\n\t\t},\n\t\tgenerateLegend : function(){\n\t\t\treturn template(this.options.legendTemplate,this);\n\t\t},\n\t\tdestroy : function(){\n\t\t\tthis.clear();\n\t\t\tunbindEvents(this, this.events);\n\t\t\tvar canvas = this.chart.canvas;\n\n\t\t\t// Reset canvas height/width attributes starts a fresh with the canvas context\n\t\t\tcanvas.width = this.chart.width;\n\t\t\tcanvas.height = this.chart.height;\n\n\t\t\t// < IE9 doesn't support removeProperty\n\t\t\tif (canvas.style.removeProperty) {\n\t\t\t\tcanvas.style.removeProperty('width');\n\t\t\t\tcanvas.style.removeProperty('height');\n\t\t\t} else {\n\t\t\t\tcanvas.style.removeAttribute('width');\n\t\t\t\tcanvas.style.removeAttribute('height');\n\t\t\t}\n\n\t\t\tdelete Chart.instances[this.id];\n\t\t},\n\t\tshowTooltip : function(ChartElements, forceRedraw){\n\t\t\t// Only redraw the chart if we've actually changed what we're hovering on.\n\t\t\tif (typeof this.activeElements === 'undefined') this.activeElements = [];\n\n\t\t\tvar isChanged = (function(Elements){\n\t\t\t\tvar changed = false;\n\n\t\t\t\tif (Elements.length !== this.activeElements.length){\n\t\t\t\t\tchanged = true;\n\t\t\t\t\treturn changed;\n\t\t\t\t}\n\n\t\t\t\teach(Elements, function(element, index){\n\t\t\t\t\tif (element !== this.activeElements[index]){\n\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t}\n\t\t\t\t}, this);\n\t\t\t\treturn changed;\n\t\t\t}).call(this, ChartElements);\n\n\t\t\tif (!isChanged && !forceRedraw){\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse{\n\t\t\t\tthis.activeElements = ChartElements;\n\t\t\t}\n\t\t\tthis.draw();\n\t\t\tif(this.options.customTooltips){\n\t\t\t\tthis.options.customTooltips(false);\n\t\t\t}\n\t\t\tif (ChartElements.length > 0){\n\t\t\t\t// If we have multiple datasets, show a MultiTooltip for all of the data points at that index\n\t\t\t\tif (this.datasets && this.datasets.length > 1) {\n\t\t\t\t\tvar dataArray,\n\t\t\t\t\t\tdataIndex;\n\n\t\t\t\t\tfor (var i = this.datasets.length - 1; i >= 0; i--) {\n\t\t\t\t\t\tdataArray = this.datasets[i].points || this.datasets[i].bars || this.datasets[i].segments;\n\t\t\t\t\t\tdataIndex = indexOf(dataArray, ChartElements[0]);\n\t\t\t\t\t\tif (dataIndex !== -1){\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tvar tooltipLabels = [],\n\t\t\t\t\t\ttooltipColors = [],\n\t\t\t\t\t\tmedianPosition = (function(index) {\n\n\t\t\t\t\t\t\t// Get all the points at that particular index\n\t\t\t\t\t\t\tvar Elements = [],\n\t\t\t\t\t\t\t\tdataCollection,\n\t\t\t\t\t\t\t\txPositions = [],\n\t\t\t\t\t\t\t\tyPositions = [],\n\t\t\t\t\t\t\t\txMax,\n\t\t\t\t\t\t\t\tyMax,\n\t\t\t\t\t\t\t\txMin,\n\t\t\t\t\t\t\t\tyMin;\n\t\t\t\t\t\t\thelpers.each(this.datasets, function(dataset){\n\t\t\t\t\t\t\t\tdataCollection = dataset.points || dataset.bars || dataset.segments;\n\t\t\t\t\t\t\t\tif (dataCollection[dataIndex] && dataCollection[dataIndex].hasValue()){\n\t\t\t\t\t\t\t\t\tElements.push(dataCollection[dataIndex]);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\thelpers.each(Elements, function(element) {\n\t\t\t\t\t\t\t\txPositions.push(element.x);\n\t\t\t\t\t\t\t\tyPositions.push(element.y);\n\n\n\t\t\t\t\t\t\t\t//Include any colour information about the element\n\t\t\t\t\t\t\t\ttooltipLabels.push(helpers.template(this.options.multiTooltipTemplate, element));\n\t\t\t\t\t\t\t\ttooltipColors.push({\n\t\t\t\t\t\t\t\t\tfill: element._saved.fillColor || element.fillColor,\n\t\t\t\t\t\t\t\t\tstroke: element._saved.strokeColor || element.strokeColor\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t}, this);\n\n\t\t\t\t\t\t\tyMin = min(yPositions);\n\t\t\t\t\t\t\tyMax = max(yPositions);\n\n\t\t\t\t\t\t\txMin = min(xPositions);\n\t\t\t\t\t\t\txMax = max(xPositions);\n\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tx: (xMin > this.chart.width/2) ? xMin : xMax,\n\t\t\t\t\t\t\t\ty: (yMin + yMax)/2\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}).call(this, dataIndex);\n\n\t\t\t\t\tnew Chart.MultiTooltip({\n\t\t\t\t\t\tx: medianPosition.x,\n\t\t\t\t\t\ty: medianPosition.y,\n\t\t\t\t\t\txPadding: this.options.tooltipXPadding,\n\t\t\t\t\t\tyPadding: this.options.tooltipYPadding,\n\t\t\t\t\t\txOffset: this.options.tooltipXOffset,\n\t\t\t\t\t\tfillColor: this.options.tooltipFillColor,\n\t\t\t\t\t\ttextColor: this.options.tooltipFontColor,\n\t\t\t\t\t\tfontFamily: this.options.tooltipFontFamily,\n\t\t\t\t\t\tfontStyle: this.options.tooltipFontStyle,\n\t\t\t\t\t\tfontSize: this.options.tooltipFontSize,\n\t\t\t\t\t\ttitleTextColor: this.options.tooltipTitleFontColor,\n\t\t\t\t\t\ttitleFontFamily: this.options.tooltipTitleFontFamily,\n\t\t\t\t\t\ttitleFontStyle: this.options.tooltipTitleFontStyle,\n\t\t\t\t\t\ttitleFontSize: this.options.tooltipTitleFontSize,\n\t\t\t\t\t\tcornerRadius: this.options.tooltipCornerRadius,\n\t\t\t\t\t\tlabels: tooltipLabels,\n\t\t\t\t\t\tlegendColors: tooltipColors,\n\t\t\t\t\t\tlegendColorBackground : this.options.multiTooltipKeyBackground,\n\t\t\t\t\t\ttitle: ChartElements[0].label,\n\t\t\t\t\t\tchart: this.chart,\n\t\t\t\t\t\tctx: this.chart.ctx,\n\t\t\t\t\t\tcustom: this.options.customTooltips\n\t\t\t\t\t}).draw();\n\n\t\t\t\t} else {\n\t\t\t\t\teach(ChartElements, function(Element) {\n\t\t\t\t\t\tvar tooltipPosition = Element.tooltipPosition();\n\t\t\t\t\t\tnew Chart.Tooltip({\n\t\t\t\t\t\t\tx: Math.round(tooltipPosition.x),\n\t\t\t\t\t\t\ty: Math.round(tooltipPosition.y),\n\t\t\t\t\t\t\txPadding: this.options.tooltipXPadding,\n\t\t\t\t\t\t\tyPadding: this.options.tooltipYPadding,\n\t\t\t\t\t\t\tfillColor: this.options.tooltipFillColor,\n\t\t\t\t\t\t\ttextColor: this.options.tooltipFontColor,\n\t\t\t\t\t\t\tfontFamily: this.options.tooltipFontFamily,\n\t\t\t\t\t\t\tfontStyle: this.options.tooltipFontStyle,\n\t\t\t\t\t\t\tfontSize: this.options.tooltipFontSize,\n\t\t\t\t\t\t\tcaretHeight: this.options.tooltipCaretSize,\n\t\t\t\t\t\t\tcornerRadius: this.options.tooltipCornerRadius,\n\t\t\t\t\t\t\ttext: template(this.options.tooltipTemplate, Element),\n\t\t\t\t\t\t\tchart: this.chart,\n\t\t\t\t\t\t\tcustom: this.options.customTooltips\n\t\t\t\t\t\t}).draw();\n\t\t\t\t\t}, this);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn this;\n\t\t},\n\t\ttoBase64Image : function(){\n\t\t\treturn this.chart.canvas.toDataURL.apply(this.chart.canvas, arguments);\n\t\t}\n\t});\n\n\tChart.Type.extend = function(extensions){\n\n\t\tvar parent = this;\n\n\t\tvar ChartType = function(){\n\t\t\treturn parent.apply(this,arguments);\n\t\t};\n\n\t\t//Copy the prototype object of the this class\n\t\tChartType.prototype = clone(parent.prototype);\n\t\t//Now overwrite some of the properties in the base class with the new extensions\n\t\textend(ChartType.prototype, extensions);\n\n\t\tChartType.extend = Chart.Type.extend;\n\n\t\tif (extensions.name || parent.prototype.name){\n\n\t\t\tvar chartName = extensions.name || parent.prototype.name;\n\t\t\t//Assign any potential default values of the new chart type\n\n\t\t\t//If none are defined, we'll use a clone of the chart type this is being extended from.\n\t\t\t//I.e. if we extend a line chart, we'll use the defaults from the line chart if our new chart\n\t\t\t//doesn't define some defaults of their own.\n\n\t\t\tvar baseDefaults = (Chart.defaults[parent.prototype.name]) ? clone(Chart.defaults[parent.prototype.name]) : {};\n\n\t\t\tChart.defaults[chartName] = extend(baseDefaults,extensions.defaults);\n\n\t\t\tChart.types[chartName] = ChartType;\n\n\t\t\t//Register this new chart type in the Chart prototype\n\t\t\tChart.prototype[chartName] = function(data,options){\n\t\t\t\tvar config = merge(Chart.defaults.global, Chart.defaults[chartName], options || {});\n\t\t\t\treturn new ChartType(data,config,this);\n\t\t\t};\n\t\t} else{\n\t\t\twarn(\"Name not provided for this chart, so it hasn't been registered\");\n\t\t}\n\t\treturn parent;\n\t};\n\n\tChart.Element = function(configuration){\n\t\textend(this,configuration);\n\t\tthis.initialize.apply(this,arguments);\n\t\tthis.save();\n\t};\n\textend(Chart.Element.prototype,{\n\t\tinitialize : function(){},\n\t\trestore : function(props){\n\t\t\tif (!props){\n\t\t\t\textend(this,this._saved);\n\t\t\t} else {\n\t\t\t\teach(props,function(key){\n\t\t\t\t\tthis[key] = this._saved[key];\n\t\t\t\t},this);\n\t\t\t}\n\t\t\treturn this;\n\t\t},\n\t\tsave : function(){\n\t\t\tthis._saved = clone(this);\n\t\t\tdelete this._saved._saved;\n\t\t\treturn this;\n\t\t},\n\t\tupdate : function(newProps){\n\t\t\teach(newProps,function(value,key){\n\t\t\t\tthis._saved[key] = this[key];\n\t\t\t\tthis[key] = value;\n\t\t\t},this);\n\t\t\treturn this;\n\t\t},\n\t\ttransition : function(props,ease){\n\t\t\teach(props,function(value,key){\n\t\t\t\tthis[key] = ((value - this._saved[key]) * ease) + this._saved[key];\n\t\t\t},this);\n\t\t\treturn this;\n\t\t},\n\t\ttooltipPosition : function(){\n\t\t\treturn {\n\t\t\t\tx : this.x,\n\t\t\t\ty : this.y\n\t\t\t};\n\t\t},\n\t\thasValue: function(){\n\t\t\treturn isNumber(this.value);\n\t\t}\n\t});\n\n\tChart.Element.extend = inherits;\n\n\n\tChart.Point = Chart.Element.extend({\n\t\tdisplay: true,\n\t\tinRange: function(chartX,chartY){\n\t\t\tvar hitDetectionRange = this.hitDetectionRadius + this.radius;\n\t\t\treturn ((Math.pow(chartX-this.x, 2)+Math.pow(chartY-this.y, 2)) < Math.pow(hitDetectionRange,2));\n\t\t},\n\t\tdraw : function(){\n\t\t\tif (this.display){\n\t\t\t\tvar ctx = this.ctx;\n\t\t\t\tctx.beginPath();\n\n\t\t\t\tctx.arc(this.x, this.y, this.radius, 0, Math.PI*2);\n\t\t\t\tctx.closePath();\n\n\t\t\t\tctx.strokeStyle = this.strokeColor;\n\t\t\t\tctx.lineWidth = this.strokeWidth;\n\n\t\t\t\tctx.fillStyle = this.fillColor;\n\n\t\t\t\tctx.fill();\n\t\t\t\tctx.stroke();\n\t\t\t}\n\n\n\t\t\t//Quick debug for bezier curve splining\n\t\t\t//Highlights control points and the line between them.\n\t\t\t//Handy for dev - stripped in the min version.\n\n\t\t\t// ctx.save();\n\t\t\t// ctx.fillStyle = \"black\";\n\t\t\t// ctx.strokeStyle = \"black\"\n\t\t\t// ctx.beginPath();\n\t\t\t// ctx.arc(this.controlPoints.inner.x,this.controlPoints.inner.y, 2, 0, Math.PI*2);\n\t\t\t// ctx.fill();\n\n\t\t\t// ctx.beginPath();\n\t\t\t// ctx.arc(this.controlPoints.outer.x,this.controlPoints.outer.y, 2, 0, Math.PI*2);\n\t\t\t// ctx.fill();\n\n\t\t\t// ctx.moveTo(this.controlPoints.inner.x,this.controlPoints.inner.y);\n\t\t\t// ctx.lineTo(this.x, this.y);\n\t\t\t// ctx.lineTo(this.controlPoints.outer.x,this.controlPoints.outer.y);\n\t\t\t// ctx.stroke();\n\n\t\t\t// ctx.restore();\n\n\n\n\t\t}\n\t});\n\n\tChart.Arc = Chart.Element.extend({\n\t\tinRange : function(chartX,chartY){\n\n\t\t\tvar pointRelativePosition = helpers.getAngleFromPoint(this, {\n\t\t\t\tx: chartX,\n\t\t\t\ty: chartY\n\t\t\t});\n\n\t\t\t//Check if within the range of the open/close angle\n\t\t\tvar betweenAngles = (pointRelativePosition.angle >= this.startAngle && pointRelativePosition.angle <= this.endAngle),\n\t\t\t\twithinRadius = (pointRelativePosition.distance >= this.innerRadius && pointRelativePosition.distance <= this.outerRadius);\n\n\t\t\treturn (betweenAngles && withinRadius);\n\t\t\t//Ensure within the outside of the arc centre, but inside arc outer\n\t\t},\n\t\ttooltipPosition : function(){\n\t\t\tvar centreAngle = this.startAngle + ((this.endAngle - this.startAngle) / 2),\n\t\t\t\trangeFromCentre = (this.outerRadius - this.innerRadius) / 2 + this.innerRadius;\n\t\t\treturn {\n\t\t\t\tx : this.x + (Math.cos(centreAngle) * rangeFromCentre),\n\t\t\t\ty : this.y + (Math.sin(centreAngle) * rangeFromCentre)\n\t\t\t};\n\t\t},\n\t\tdraw : function(animationPercent){\n\n\t\t\tvar easingDecimal = animationPercent || 1;\n\n\t\t\tvar ctx = this.ctx;\n\n\t\t\tctx.beginPath();\n\n\t\t\tctx.arc(this.x, this.y, this.outerRadius, this.startAngle, this.endAngle);\n\n\t\t\tctx.arc(this.x, this.y, this.innerRadius, this.endAngle, this.startAngle, true);\n\n\t\t\tctx.closePath();\n\t\t\tctx.strokeStyle = this.strokeColor;\n\t\t\tctx.lineWidth = this.strokeWidth;\n\n\t\t\tctx.fillStyle = this.fillColor;\n\n\t\t\tctx.fill();\n\t\t\tctx.lineJoin = 'bevel';\n\n\t\t\tif (this.showStroke){\n\t\t\t\tctx.stroke();\n\t\t\t}\n\t\t}\n\t});\n\n\tChart.Rectangle = Chart.Element.extend({\n\t\tdraw : function(){\n\t\t\tvar ctx = this.ctx,\n\t\t\t\thalfWidth = this.width/2,\n\t\t\t\tleftX = this.x - halfWidth,\n\t\t\t\trightX = this.x + halfWidth,\n\t\t\t\ttop = this.base - (this.base - this.y),\n\t\t\t\thalfStroke = this.strokeWidth / 2;\n\n\t\t\t// Canvas doesn't allow us to stroke inside the width so we can\n\t\t\t// adjust the sizes to fit if we're setting a stroke on the line\n\t\t\tif (this.showStroke){\n\t\t\t\tleftX += halfStroke;\n\t\t\t\trightX -= halfStroke;\n\t\t\t\ttop += halfStroke;\n\t\t\t}\n\n\t\t\tctx.beginPath();\n\n\t\t\tctx.fillStyle = this.fillColor;\n\t\t\tctx.strokeStyle = this.strokeColor;\n\t\t\tctx.lineWidth = this.strokeWidth;\n\n\t\t\t// It'd be nice to keep this class totally generic to any rectangle\n\t\t\t// and simply specify which border to miss out.\n\t\t\tctx.moveTo(leftX, this.base);\n\t\t\tctx.lineTo(leftX, top);\n\t\t\tctx.lineTo(rightX, top);\n\t\t\tctx.lineTo(rightX, this.base);\n\t\t\tctx.fill();\n\t\t\tif (this.showStroke){\n\t\t\t\tctx.stroke();\n\t\t\t}\n\t\t},\n\t\theight : function(){\n\t\t\treturn this.base - this.y;\n\t\t},\n\t\tinRange : function(chartX,chartY){\n\t\t\treturn (chartX >= this.x - this.width/2 && chartX <= this.x + this.width/2) && (chartY >= this.y && chartY <= this.base);\n\t\t}\n\t});\n\n\tChart.Tooltip = Chart.Element.extend({\n\t\tdraw : function(){\n\n\t\t\tvar ctx = this.chart.ctx;\n\n\t\t\tctx.font = fontString(this.fontSize,this.fontStyle,this.fontFamily);\n\n\t\t\tthis.xAlign = \"center\";\n\t\t\tthis.yAlign = \"above\";\n\n\t\t\t//Distance between the actual element.y position and the start of the tooltip caret\n\t\t\tvar caretPadding = this.caretPadding = 2;\n\n\t\t\tvar tooltipWidth = ctx.measureText(this.text).width + 2*this.xPadding,\n\t\t\t\ttooltipRectHeight = this.fontSize + 2*this.yPadding,\n\t\t\t\ttooltipHeight = tooltipRectHeight + this.caretHeight + caretPadding;\n\n\t\t\tif (this.x + tooltipWidth/2 >this.chart.width){\n\t\t\t\tthis.xAlign = \"left\";\n\t\t\t} else if (this.x - tooltipWidth/2 < 0){\n\t\t\t\tthis.xAlign = \"right\";\n\t\t\t}\n\n\t\t\tif (this.y - tooltipHeight < 0){\n\t\t\t\tthis.yAlign = \"below\";\n\t\t\t}\n\n\n\t\t\tvar tooltipX = this.x - tooltipWidth/2,\n\t\t\t\ttooltipY = this.y - tooltipHeight;\n\n\t\t\tctx.fillStyle = this.fillColor;\n\n\t\t\t// Custom Tooltips\n\t\t\tif(this.custom){\n\t\t\t\tthis.custom(this);\n\t\t\t}\n\t\t\telse{\n\t\t\t\tswitch(this.yAlign)\n\t\t\t\t{\n\t\t\t\tcase \"above\":\n\t\t\t\t\t//Draw a caret above the x/y\n\t\t\t\t\tctx.beginPath();\n\t\t\t\t\tctx.moveTo(this.x,this.y - caretPadding);\n\t\t\t\t\tctx.lineTo(this.x + this.caretHeight, this.y - (caretPadding + this.caretHeight));\n\t\t\t\t\tctx.lineTo(this.x - this.caretHeight, this.y - (caretPadding + this.caretHeight));\n\t\t\t\t\tctx.closePath();\n\t\t\t\t\tctx.fill();\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"below\":\n\t\t\t\t\ttooltipY = this.y + caretPadding + this.caretHeight;\n\t\t\t\t\t//Draw a caret below the x/y\n\t\t\t\t\tctx.beginPath();\n\t\t\t\t\tctx.moveTo(this.x, this.y + caretPadding);\n\t\t\t\t\tctx.lineTo(this.x + this.caretHeight, this.y + caretPadding + this.caretHeight);\n\t\t\t\t\tctx.lineTo(this.x - this.caretHeight, this.y + caretPadding + this.caretHeight);\n\t\t\t\t\tctx.closePath();\n\t\t\t\t\tctx.fill();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tswitch(this.xAlign)\n\t\t\t\t{\n\t\t\t\tcase \"left\":\n\t\t\t\t\ttooltipX = this.x - tooltipWidth + (this.cornerRadius + this.caretHeight);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"right\":\n\t\t\t\t\ttooltipX = this.x - (this.cornerRadius + this.caretHeight);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tdrawRoundedRectangle(ctx,tooltipX,tooltipY,tooltipWidth,tooltipRectHeight,this.cornerRadius);\n\n\t\t\t\tctx.fill();\n\n\t\t\t\tctx.fillStyle = this.textColor;\n\t\t\t\tctx.textAlign = \"center\";\n\t\t\t\tctx.textBaseline = \"middle\";\n\t\t\t\tctx.fillText(this.text, tooltipX + tooltipWidth/2, tooltipY + tooltipRectHeight/2);\n\t\t\t}\n\t\t}\n\t});\n\n\tChart.MultiTooltip = Chart.Element.extend({\n\t\tinitialize : function(){\n\t\t\tthis.font = fontString(this.fontSize,this.fontStyle,this.fontFamily);\n\n\t\t\tthis.titleFont = fontString(this.titleFontSize,this.titleFontStyle,this.titleFontFamily);\n\n\t\t\tthis.height = (this.labels.length * this.fontSize) + ((this.labels.length-1) * (this.fontSize/2)) + (this.yPadding*2) + this.titleFontSize *1.5;\n\n\t\t\tthis.ctx.font = this.titleFont;\n\n\t\t\tvar titleWidth = this.ctx.measureText(this.title).width,\n\t\t\t\t//Label has a legend square as well so account for this.\n\t\t\t\tlabelWidth = longestText(this.ctx,this.font,this.labels) + this.fontSize + 3,\n\t\t\t\tlongestTextWidth = max([labelWidth,titleWidth]);\n\n\t\t\tthis.width = longestTextWidth + (this.xPadding*2);\n\n\n\t\t\tvar halfHeight = this.height/2;\n\n\t\t\t//Check to ensure the height will fit on the canvas\n\t\t\tif (this.y - halfHeight < 0 ){\n\t\t\t\tthis.y = halfHeight;\n\t\t\t} else if (this.y + halfHeight > this.chart.height){\n\t\t\t\tthis.y = this.chart.height - halfHeight;\n\t\t\t}\n\n\t\t\t//Decide whether to align left or right based on position on canvas\n\t\t\tif (this.x > this.chart.width/2){\n\t\t\t\tthis.x -= this.xOffset + this.width;\n\t\t\t} else {\n\t\t\t\tthis.x += this.xOffset;\n\t\t\t}\n\n\n\t\t},\n\t\tgetLineHeight : function(index){\n\t\t\tvar baseLineHeight = this.y - (this.height/2) + this.yPadding,\n\t\t\t\tafterTitleIndex = index-1;\n\n\t\t\t//If the index is zero, we're getting the title\n\t\t\tif (index === 0){\n\t\t\t\treturn baseLineHeight + this.titleFontSize/2;\n\t\t\t} else{\n\t\t\t\treturn baseLineHeight + ((this.fontSize*1.5*afterTitleIndex) + this.fontSize/2) + this.titleFontSize * 1.5;\n\t\t\t}\n\n\t\t},\n\t\tdraw : function(){\n\t\t\t// Custom Tooltips\n\t\t\tif(this.custom){\n\t\t\t\tthis.custom(this);\n\t\t\t}\n\t\t\telse{\n\t\t\t\tdrawRoundedRectangle(this.ctx,this.x,this.y - this.height/2,this.width,this.height,this.cornerRadius);\n\t\t\t\tvar ctx = this.ctx;\n\t\t\t\tctx.fillStyle = this.fillColor;\n\t\t\t\tctx.fill();\n\t\t\t\tctx.closePath();\n\n\t\t\t\tctx.textAlign = \"left\";\n\t\t\t\tctx.textBaseline = \"middle\";\n\t\t\t\tctx.fillStyle = this.titleTextColor;\n\t\t\t\tctx.font = this.titleFont;\n\n\t\t\t\tctx.fillText(this.title,this.x + this.xPadding, this.getLineHeight(0));\n\n\t\t\t\tctx.font = this.font;\n\t\t\t\thelpers.each(this.labels,function(label,index){\n\t\t\t\t\tctx.fillStyle = this.textColor;\n\t\t\t\t\tctx.fillText(label,this.x + this.xPadding + this.fontSize + 3, this.getLineHeight(index + 1));\n\n\t\t\t\t\t//A bit gnarly, but clearing this rectangle breaks when using explorercanvas (clears whole canvas)\n\t\t\t\t\t//ctx.clearRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);\n\t\t\t\t\t//Instead we'll make a white filled block to put the legendColour palette over.\n\n\t\t\t\t\tctx.fillStyle = this.legendColorBackground;\n\t\t\t\t\tctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);\n\n\t\t\t\t\tctx.fillStyle = this.legendColors[index].fill;\n\t\t\t\t\tctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);\n\n\n\t\t\t\t},this);\n\t\t\t}\n\t\t}\n\t});\n\n\tChart.Scale = Chart.Element.extend({\n\t\tinitialize : function(){\n\t\t\tthis.fit();\n\t\t},\n\t\tbuildYLabels : function(){\n\t\t\tthis.yLabels = [];\n\n\t\t\tvar stepDecimalPlaces = getDecimalPlaces(this.stepValue);\n\n\t\t\tfor (var i=0; i<=this.steps; i++){\n\t\t\t\tthis.yLabels.push(template(this.templateString,{value:(this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)}));\n\t\t\t}\n\t\t\tthis.yLabelWidth = (this.display && this.showLabels) ? longestText(this.ctx,this.font,this.yLabels) : 0;\n\t\t},\n\t\taddXLabel : function(label){\n\t\t\tthis.xLabels.push(label);\n\t\t\tthis.valuesCount++;\n\t\t\tthis.fit();\n\t\t},\n\t\tremoveXLabel : function(){\n\t\t\tthis.xLabels.shift();\n\t\t\tthis.valuesCount--;\n\t\t\tthis.fit();\n\t\t},\n\t\t// Fitting loop to rotate x Labels and figure out what fits there, and also calculate how many Y steps to use\n\t\tfit: function(){\n\t\t\t// First we need the width of the yLabels, assuming the xLabels aren't rotated\n\n\t\t\t// To do that we need the base line at the top and base of the chart, assuming there is no x label rotation\n\t\t\tthis.startPoint = (this.display) ? this.fontSize : 0;\n\t\t\tthis.endPoint = (this.display) ? this.height - (this.fontSize * 1.5) - 5 : this.height; // -5 to pad labels\n\n\t\t\t// Apply padding settings to the start and end point.\n\t\t\tthis.startPoint += this.padding;\n\t\t\tthis.endPoint -= this.padding;\n\n\t\t\t// Cache the starting height, so can determine if we need to recalculate the scale yAxis\n\t\t\tvar cachedHeight = this.endPoint - this.startPoint,\n\t\t\t\tcachedYLabelWidth;\n\n\t\t\t// Build the current yLabels so we have an idea of what size they'll be to start\n\t\t\t/*\n\t\t\t *\tThis sets what is returned from calculateScaleRange as static properties of this class:\n\t\t\t *\n\t\t\t\tthis.steps;\n\t\t\t\tthis.stepValue;\n\t\t\t\tthis.min;\n\t\t\t\tthis.max;\n\t\t\t *\n\t\t\t */\n\t\t\tthis.calculateYRange(cachedHeight);\n\n\t\t\t// With these properties set we can now build the array of yLabels\n\t\t\t// and also the width of the largest yLabel\n\t\t\tthis.buildYLabels();\n\n\t\t\tthis.calculateXLabelRotation();\n\n\t\t\twhile((cachedHeight > this.endPoint - this.startPoint)){\n\t\t\t\tcachedHeight = this.endPoint - this.startPoint;\n\t\t\t\tcachedYLabelWidth = this.yLabelWidth;\n\n\t\t\t\tthis.calculateYRange(cachedHeight);\n\t\t\t\tthis.buildYLabels();\n\n\t\t\t\t// Only go through the xLabel loop again if the yLabel width has changed\n\t\t\t\tif (cachedYLabelWidth < this.yLabelWidth){\n\t\t\t\t\tthis.calculateXLabelRotation();\n\t\t\t\t}\n\t\t\t}\n\n\t\t},\n\t\tcalculateXLabelRotation : function(){\n\t\t\t//Get the width of each grid by calculating the difference\n\t\t\t//between x offsets between 0 and 1.\n\n\t\t\tthis.ctx.font = this.font;\n\n\t\t\tvar firstWidth = this.ctx.measureText(this.xLabels[0]).width,\n\t\t\t\tlastWidth = this.ctx.measureText(this.xLabels[this.xLabels.length - 1]).width,\n\t\t\t\tfirstRotated,\n\t\t\t\tlastRotated;\n\n\n\t\t\tthis.xScalePaddingRight = lastWidth/2 + 3;\n\t\t\tthis.xScalePaddingLeft = (firstWidth/2 > this.yLabelWidth + 10) ? firstWidth/2 : this.yLabelWidth + 10;\n\n\t\t\tthis.xLabelRotation = 0;\n\t\t\tif (this.display){\n\t\t\t\tvar originalLabelWidth = longestText(this.ctx,this.font,this.xLabels),\n\t\t\t\t\tcosRotation,\n\t\t\t\t\tfirstRotatedWidth;\n\t\t\t\tthis.xLabelWidth = originalLabelWidth;\n\t\t\t\t//Allow 3 pixels x2 padding either side for label readability\n\t\t\t\tvar xGridWidth = Math.floor(this.calculateX(1) - this.calculateX(0)) - 6;\n\n\t\t\t\t//Max label rotate should be 90 - also act as a loop counter\n\t\t\t\twhile ((this.xLabelWidth > xGridWidth && this.xLabelRotation === 0) || (this.xLabelWidth > xGridWidth && this.xLabelRotation <= 90 && this.xLabelRotation > 0)){\n\t\t\t\t\tcosRotation = Math.cos(toRadians(this.xLabelRotation));\n\n\t\t\t\t\tfirstRotated = cosRotation * firstWidth;\n\t\t\t\t\tlastRotated = cosRotation * lastWidth;\n\n\t\t\t\t\t// We're right aligning the text now.\n\t\t\t\t\tif (firstRotated + this.fontSize / 2 > this.yLabelWidth + 8){\n\t\t\t\t\t\tthis.xScalePaddingLeft = firstRotated + this.fontSize / 2;\n\t\t\t\t\t}\n\t\t\t\t\tthis.xScalePaddingRight = this.fontSize/2;\n\n\n\t\t\t\t\tthis.xLabelRotation++;\n\t\t\t\t\tthis.xLabelWidth = cosRotation * originalLabelWidth;\n\n\t\t\t\t}\n\t\t\t\tif (this.xLabelRotation > 0){\n\t\t\t\t\tthis.endPoint -= Math.sin(toRadians(this.xLabelRotation))*originalLabelWidth + 3;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse{\n\t\t\t\tthis.xLabelWidth = 0;\n\t\t\t\tthis.xScalePaddingRight = this.padding;\n\t\t\t\tthis.xScalePaddingLeft = this.padding;\n\t\t\t}\n\n\t\t},\n\t\t// Needs to be overidden in each Chart type\n\t\t// Otherwise we need to pass all the data into the scale class\n\t\tcalculateYRange: noop,\n\t\tdrawingArea: function(){\n\t\t\treturn this.startPoint - this.endPoint;\n\t\t},\n\t\tcalculateY : function(value){\n\t\t\tvar scalingFactor = this.drawingArea() / (this.min - this.max);\n\t\t\treturn this.endPoint - (scalingFactor * (value - this.min));\n\t\t},\n\t\tcalculateX : function(index){\n\t\t\tvar isRotated = (this.xLabelRotation > 0),\n\t\t\t\t// innerWidth = (this.offsetGridLines) ? this.width - offsetLeft - this.padding : this.width - (offsetLeft + halfLabelWidth * 2) - this.padding,\n\t\t\t\tinnerWidth = this.width - (this.xScalePaddingLeft + this.xScalePaddingRight),\n\t\t\t\tvalueWidth = innerWidth/Math.max((this.valuesCount - ((this.offsetGridLines) ? 0 : 1)), 1),\n\t\t\t\tvalueOffset = (valueWidth * index) + this.xScalePaddingLeft;\n\n\t\t\tif (this.offsetGridLines){\n\t\t\t\tvalueOffset += (valueWidth/2);\n\t\t\t}\n\n\t\t\treturn Math.round(valueOffset);\n\t\t},\n\t\tupdate : function(newProps){\n\t\t\thelpers.extend(this, newProps);\n\t\t\tthis.fit();\n\t\t},\n\t\tdraw : function(){\n\t\t\tvar ctx = this.ctx,\n\t\t\t\tyLabelGap = (this.endPoint - this.startPoint) / this.steps,\n\t\t\t\txStart = Math.round(this.xScalePaddingLeft);\n\t\t\tif (this.display){\n\t\t\t\tctx.fillStyle = this.textColor;\n\t\t\t\tctx.font = this.font;\n\t\t\t\teach(this.yLabels,function(labelString,index){\n\t\t\t\t\tvar yLabelCenter = this.endPoint - (yLabelGap * index),\n\t\t\t\t\t\tlinePositionY = Math.round(yLabelCenter),\n\t\t\t\t\t\tdrawHorizontalLine = this.showHorizontalLines;\n\n\t\t\t\t\tctx.textAlign = \"right\";\n\t\t\t\t\tctx.textBaseline = \"middle\";\n\t\t\t\t\tif (this.showLabels){\n\t\t\t\t\t\tctx.fillText(labelString,xStart - 10,yLabelCenter);\n\t\t\t\t\t}\n\n\t\t\t\t\t// This is X axis, so draw it\n\t\t\t\t\tif (index === 0 && !drawHorizontalLine){\n\t\t\t\t\t\tdrawHorizontalLine = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (drawHorizontalLine){\n\t\t\t\t\t\tctx.beginPath();\n\t\t\t\t\t}\n\n\t\t\t\t\tif (index > 0){\n\t\t\t\t\t\t// This is a grid line in the centre, so drop that\n\t\t\t\t\t\tctx.lineWidth = this.gridLineWidth;\n\t\t\t\t\t\tctx.strokeStyle = this.gridLineColor;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// This is the first line on the scale\n\t\t\t\t\t\tctx.lineWidth = this.lineWidth;\n\t\t\t\t\t\tctx.strokeStyle = this.lineColor;\n\t\t\t\t\t}\n\n\t\t\t\t\tlinePositionY += helpers.aliasPixel(ctx.lineWidth);\n\n\t\t\t\t\tif(drawHorizontalLine){\n\t\t\t\t\t\tctx.moveTo(xStart, linePositionY);\n\t\t\t\t\t\tctx.lineTo(this.width, linePositionY);\n\t\t\t\t\t\tctx.stroke();\n\t\t\t\t\t\tctx.closePath();\n\t\t\t\t\t}\n\n\t\t\t\t\tctx.lineWidth = this.lineWidth;\n\t\t\t\t\tctx.strokeStyle = this.lineColor;\n\t\t\t\t\tctx.beginPath();\n\t\t\t\t\tctx.moveTo(xStart - 5, linePositionY);\n\t\t\t\t\tctx.lineTo(xStart, linePositionY);\n\t\t\t\t\tctx.stroke();\n\t\t\t\t\tctx.closePath();\n\n\t\t\t\t},this);\n\n\t\t\t\teach(this.xLabels,function(label,index){\n\t\t\t\t\tvar xPos = this.calculateX(index) + aliasPixel(this.lineWidth),\n\t\t\t\t\t\t// Check to see if line/bar here and decide where to place the line\n\t\t\t\t\t\tlinePos = this.calculateX(index - (this.offsetGridLines ? 0.5 : 0)) + aliasPixel(this.lineWidth),\n\t\t\t\t\t\tisRotated = (this.xLabelRotation > 0),\n\t\t\t\t\t\tdrawVerticalLine = this.showVerticalLines;\n\n\t\t\t\t\t// This is Y axis, so draw it\n\t\t\t\t\tif (index === 0 && !drawVerticalLine){\n\t\t\t\t\t\tdrawVerticalLine = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (drawVerticalLine){\n\t\t\t\t\t\tctx.beginPath();\n\t\t\t\t\t}\n\n\t\t\t\t\tif (index > 0){\n\t\t\t\t\t\t// This is a grid line in the centre, so drop that\n\t\t\t\t\t\tctx.lineWidth = this.gridLineWidth;\n\t\t\t\t\t\tctx.strokeStyle = this.gridLineColor;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// This is the first line on the scale\n\t\t\t\t\t\tctx.lineWidth = this.lineWidth;\n\t\t\t\t\t\tctx.strokeStyle = this.lineColor;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (drawVerticalLine){\n\t\t\t\t\t\tctx.moveTo(linePos,this.endPoint);\n\t\t\t\t\t\tctx.lineTo(linePos,this.startPoint - 3);\n\t\t\t\t\t\tctx.stroke();\n\t\t\t\t\t\tctx.closePath();\n\t\t\t\t\t}\n\n\n\t\t\t\t\tctx.lineWidth = this.lineWidth;\n\t\t\t\t\tctx.strokeStyle = this.lineColor;\n\n\n\t\t\t\t\t// Small lines at the bottom of the base grid line\n\t\t\t\t\tctx.beginPath();\n\t\t\t\t\tctx.moveTo(linePos,this.endPoint);\n\t\t\t\t\tctx.lineTo(linePos,this.endPoint + 5);\n\t\t\t\t\tctx.stroke();\n\t\t\t\t\tctx.closePath();\n\n\t\t\t\t\tctx.save();\n\t\t\t\t\tctx.translate(xPos,(isRotated) ? this.endPoint + 12 : this.endPoint + 8);\n\t\t\t\t\tctx.rotate(toRadians(this.xLabelRotation)*-1);\n\t\t\t\t\tctx.font = this.font;\n\t\t\t\t\tctx.textAlign = (isRotated) ? \"right\" : \"center\";\n\t\t\t\t\tctx.textBaseline = (isRotated) ? \"middle\" : \"top\";\n\t\t\t\t\tctx.fillText(label, 0, 0);\n\t\t\t\t\tctx.restore();\n\t\t\t\t},this);\n\n\t\t\t}\n\t\t}\n\n\t});\n\n\tChart.RadialScale = Chart.Element.extend({\n\t\tinitialize: function(){\n\t\t\tthis.size = min([this.height, this.width]);\n\t\t\tthis.drawingArea = (this.display) ? (this.size/2) - (this.fontSize/2 + this.backdropPaddingY) : (this.size/2);\n\t\t},\n\t\tcalculateCenterOffset: function(value){\n\t\t\t// Take into account half font size + the yPadding of the top value\n\t\t\tvar scalingFactor = this.drawingArea / (this.max - this.min);\n\n\t\t\treturn (value - this.min) * scalingFactor;\n\t\t},\n\t\tupdate : function(){\n\t\t\tif (!this.lineArc){\n\t\t\t\tthis.setScaleSize();\n\t\t\t} else {\n\t\t\t\tthis.drawingArea = (this.display) ? (this.size/2) - (this.fontSize/2 + this.backdropPaddingY) : (this.size/2);\n\t\t\t}\n\t\t\tthis.buildYLabels();\n\t\t},\n\t\tbuildYLabels: function(){\n\t\t\tthis.yLabels = [];\n\n\t\t\tvar stepDecimalPlaces = getDecimalPlaces(this.stepValue);\n\n\t\t\tfor (var i=0; i<=this.steps; i++){\n\t\t\t\tthis.yLabels.push(template(this.templateString,{value:(this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)}));\n\t\t\t}\n\t\t},\n\t\tgetCircumference : function(){\n\t\t\treturn ((Math.PI*2) / this.valuesCount);\n\t\t},\n\t\tsetScaleSize: function(){\n\t\t\t/*\n\t\t\t * Right, this is really confusing and there is a lot of maths going on here\n\t\t\t * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9\n\t\t\t *\n\t\t\t * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif\n\t\t\t *\n\t\t\t * Solution:\n\t\t\t *\n\t\t\t * We assume the radius of the polygon is half the size of the canvas at first\n\t\t\t * at each index we check if the text overlaps.\n\t\t\t *\n\t\t\t * Where it does, we store that angle and that index.\n\t\t\t *\n\t\t\t * After finding the largest index and angle we calculate how much we need to remove\n\t\t\t * from the shape radius to move the point inwards by that x.\n\t\t\t *\n\t\t\t * We average the left and right distances to get the maximum shape radius that can fit in the box\n\t\t\t * along with labels.\n\t\t\t *\n\t\t\t * Once we have that, we can find the centre point for the chart, by taking the x text protrusion\n\t\t\t * on each side, removing that from the size, halving it and adding the left x protrusion width.\n\t\t\t *\n\t\t\t * This will mean we have a shape fitted to the canvas, as large as it can be with the labels\n\t\t\t * and position it in the most space efficient manner\n\t\t\t *\n\t\t\t * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif\n\t\t\t */\n\n\n\t\t\t// Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.\n\t\t\t// Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points\n\t\t\tvar largestPossibleRadius = min([(this.height/2 - this.pointLabelFontSize - 5), this.width/2]),\n\t\t\t\tpointPosition,\n\t\t\t\ti,\n\t\t\t\ttextWidth,\n\t\t\t\thalfTextWidth,\n\t\t\t\tfurthestRight = this.width,\n\t\t\t\tfurthestRightIndex,\n\t\t\t\tfurthestRightAngle,\n\t\t\t\tfurthestLeft = 0,\n\t\t\t\tfurthestLeftIndex,\n\t\t\t\tfurthestLeftAngle,\n\t\t\t\txProtrusionLeft,\n\t\t\t\txProtrusionRight,\n\t\t\t\tradiusReductionRight,\n\t\t\t\tradiusReductionLeft,\n\t\t\t\tmaxWidthRadius;\n\t\t\tthis.ctx.font = fontString(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily);\n\t\t\tfor (i=0;i<this.valuesCount;i++){\n\t\t\t\t// 5px to space the text slightly out - similar to what we do in the draw function.\n\t\t\t\tpointPosition = this.getPointPosition(i, largestPossibleRadius);\n\t\t\t\ttextWidth = this.ctx.measureText(template(this.templateString, { value: this.labels[i] })).width + 5;\n\t\t\t\tif (i === 0 || i === this.valuesCount/2){\n\t\t\t\t\t// If we're at index zero, or exactly the middle, we're at exactly the top/bottom\n\t\t\t\t\t// of the radar chart, so text will be aligned centrally, so we'll half it and compare\n\t\t\t\t\t// w/left and right text sizes\n\t\t\t\t\thalfTextWidth = textWidth/2;\n\t\t\t\t\tif (pointPosition.x + halfTextWidth > furthestRight) {\n\t\t\t\t\t\tfurthestRight = pointPosition.x + halfTextWidth;\n\t\t\t\t\t\tfurthestRightIndex = i;\n\t\t\t\t\t}\n\t\t\t\t\tif (pointPosition.x - halfTextWidth < furthestLeft) {\n\t\t\t\t\t\tfurthestLeft = pointPosition.x - halfTextWidth;\n\t\t\t\t\t\tfurthestLeftIndex = i;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (i < this.valuesCount/2) {\n\t\t\t\t\t// Less than half the values means we'll left align the text\n\t\t\t\t\tif (pointPosition.x + textWidth > furthestRight) {\n\t\t\t\t\t\tfurthestRight = pointPosition.x + textWidth;\n\t\t\t\t\t\tfurthestRightIndex = i;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (i > this.valuesCount/2){\n\t\t\t\t\t// More than half the values means we'll right align the text\n\t\t\t\t\tif (pointPosition.x - textWidth < furthestLeft) {\n\t\t\t\t\t\tfurthestLeft = pointPosition.x - textWidth;\n\t\t\t\t\t\tfurthestLeftIndex = i;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\txProtrusionLeft = furthestLeft;\n\n\t\t\txProtrusionRight = Math.ceil(furthestRight - this.width);\n\n\t\t\tfurthestRightAngle = this.getIndexAngle(furthestRightIndex);\n\n\t\t\tfurthestLeftAngle = this.getIndexAngle(furthestLeftIndex);\n\n\t\t\tradiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI/2);\n\n\t\t\tradiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI/2);\n\n\t\t\t// Ensure we actually need to reduce the size of the chart\n\t\t\tradiusReductionRight = (isNumber(radiusReductionRight)) ? radiusReductionRight : 0;\n\t\t\tradiusReductionLeft = (isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0;\n\n\t\t\tthis.drawingArea = largestPossibleRadius - (radiusReductionLeft + radiusReductionRight)/2;\n\n\t\t\t//this.drawingArea = min([maxWidthRadius, (this.height - (2 * (this.pointLabelFontSize + 5)))/2])\n\t\t\tthis.setCenterPoint(radiusReductionLeft, radiusReductionRight);\n\n\t\t},\n\t\tsetCenterPoint: function(leftMovement, rightMovement){\n\n\t\t\tvar maxRight = this.width - rightMovement - this.drawingArea,\n\t\t\t\tmaxLeft = leftMovement + this.drawingArea;\n\n\t\t\tthis.xCenter = (maxLeft + maxRight)/2;\n\t\t\t// Always vertically in the centre as the text height doesn't change\n\t\t\tthis.yCenter = (this.height/2);\n\t\t},\n\n\t\tgetIndexAngle : function(index){\n\t\t\tvar angleMultiplier = (Math.PI * 2) / this.valuesCount;\n\t\t\t// Start from the top instead of right, so remove a quarter of the circle\n\n\t\t\treturn index * angleMultiplier - (Math.PI/2);\n\t\t},\n\t\tgetPointPosition : function(index, distanceFromCenter){\n\t\t\tvar thisAngle = this.getIndexAngle(index);\n\t\t\treturn {\n\t\t\t\tx : (Math.cos(thisAngle) * distanceFromCenter) + this.xCenter,\n\t\t\t\ty : (Math.sin(thisAngle) * distanceFromCenter) + this.yCenter\n\t\t\t};\n\t\t},\n\t\tdraw: function(){\n\t\t\tif (this.display){\n\t\t\t\tvar ctx = this.ctx;\n\t\t\t\teach(this.yLabels, function(label, index){\n\t\t\t\t\t// Don't draw a centre value\n\t\t\t\t\tif (index > 0){\n\t\t\t\t\t\tvar yCenterOffset = index * (this.drawingArea/this.steps),\n\t\t\t\t\t\t\tyHeight = this.yCenter - yCenterOffset,\n\t\t\t\t\t\t\tpointPosition;\n\n\t\t\t\t\t\t// Draw circular lines around the scale\n\t\t\t\t\t\tif (this.lineWidth > 0){\n\t\t\t\t\t\t\tctx.strokeStyle = this.lineColor;\n\t\t\t\t\t\t\tctx.lineWidth = this.lineWidth;\n\n\t\t\t\t\t\t\tif(this.lineArc){\n\t\t\t\t\t\t\t\tctx.beginPath();\n\t\t\t\t\t\t\t\tctx.arc(this.xCenter, this.yCenter, yCenterOffset, 0, Math.PI*2);\n\t\t\t\t\t\t\t\tctx.closePath();\n\t\t\t\t\t\t\t\tctx.stroke();\n\t\t\t\t\t\t\t} else{\n\t\t\t\t\t\t\t\tctx.beginPath();\n\t\t\t\t\t\t\t\tfor (var i=0;i<this.valuesCount;i++)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tpointPosition = this.getPointPosition(i, this.calculateCenterOffset(this.min + (index * this.stepValue)));\n\t\t\t\t\t\t\t\t\tif (i === 0){\n\t\t\t\t\t\t\t\t\t\tctx.moveTo(pointPosition.x, pointPosition.y);\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tctx.lineTo(pointPosition.x, pointPosition.y);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tctx.closePath();\n\t\t\t\t\t\t\t\tctx.stroke();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif(this.showLabels){\n\t\t\t\t\t\t\tctx.font = fontString(this.fontSize,this.fontStyle,this.fontFamily);\n\t\t\t\t\t\t\tif (this.showLabelBackdrop){\n\t\t\t\t\t\t\t\tvar labelWidth = ctx.measureText(label).width;\n\t\t\t\t\t\t\t\tctx.fillStyle = this.backdropColor;\n\t\t\t\t\t\t\t\tctx.fillRect(\n\t\t\t\t\t\t\t\t\tthis.xCenter - labelWidth/2 - this.backdropPaddingX,\n\t\t\t\t\t\t\t\t\tyHeight - this.fontSize/2 - this.backdropPaddingY,\n\t\t\t\t\t\t\t\t\tlabelWidth + this.backdropPaddingX*2,\n\t\t\t\t\t\t\t\t\tthis.fontSize + this.backdropPaddingY*2\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tctx.textAlign = 'center';\n\t\t\t\t\t\t\tctx.textBaseline = \"middle\";\n\t\t\t\t\t\t\tctx.fillStyle = this.fontColor;\n\t\t\t\t\t\t\tctx.fillText(label, this.xCenter, yHeight);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}, this);\n\n\t\t\t\tif (!this.lineArc){\n\t\t\t\t\tctx.lineWidth = this.angleLineWidth;\n\t\t\t\t\tctx.strokeStyle = this.angleLineColor;\n\t\t\t\t\tfor (var i = this.valuesCount - 1; i >= 0; i--) {\n\t\t\t\t\t\tif (this.angleLineWidth > 0){\n\t\t\t\t\t\t\tvar outerPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max));\n\t\t\t\t\t\t\tctx.beginPath();\n\t\t\t\t\t\t\tctx.moveTo(this.xCenter, this.yCenter);\n\t\t\t\t\t\t\tctx.lineTo(outerPosition.x, outerPosition.y);\n\t\t\t\t\t\t\tctx.stroke();\n\t\t\t\t\t\t\tctx.closePath();\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Extra 3px out for some label spacing\n\t\t\t\t\t\tvar pointLabelPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max) + 5);\n\t\t\t\t\t\tctx.font = fontString(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily);\n\t\t\t\t\t\tctx.fillStyle = this.pointLabelFontColor;\n\n\t\t\t\t\t\tvar labelsCount = this.labels.length,\n\t\t\t\t\t\t\thalfLabelsCount = this.labels.length/2,\n\t\t\t\t\t\t\tquarterLabelsCount = halfLabelsCount/2,\n\t\t\t\t\t\t\tupperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount),\n\t\t\t\t\t\t\texactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount);\n\t\t\t\t\t\tif (i === 0){\n\t\t\t\t\t\t\tctx.textAlign = 'center';\n\t\t\t\t\t\t} else if(i === halfLabelsCount){\n\t\t\t\t\t\t\tctx.textAlign = 'center';\n\t\t\t\t\t\t} else if (i < halfLabelsCount){\n\t\t\t\t\t\t\tctx.textAlign = 'left';\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tctx.textAlign = 'right';\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Set the correct text baseline based on outer positioning\n\t\t\t\t\t\tif (exactQuarter){\n\t\t\t\t\t\t\tctx.textBaseline = 'middle';\n\t\t\t\t\t\t} else if (upperHalf){\n\t\t\t\t\t\t\tctx.textBaseline = 'bottom';\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tctx.textBaseline = 'top';\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tctx.fillText(this.labels[i], pointLabelPosition.x, pointLabelPosition.y);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\t// Attach global event to resize each chart instance when the browser resizes\n\thelpers.addEvent(window, \"resize\", (function(){\n\t\t// Basic debounce of resize function so it doesn't hurt performance when resizing browser.\n\t\tvar timeout;\n\t\treturn function(){\n\t\t\tclearTimeout(timeout);\n\t\t\ttimeout = setTimeout(function(){\n\t\t\t\teach(Chart.instances,function(instance){\n\t\t\t\t\t// If the responsive flag is set in the chart instance config\n\t\t\t\t\t// Cascade the resize event down to the chart.\n\t\t\t\t\tif (instance.options.responsive){\n\t\t\t\t\t\tinstance.resize(instance.render, true);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}, 50);\n\t\t};\n\t})());\n\n\n\tif (amd) {\n\t\tdefine(function(){\n\t\t\treturn Chart;\n\t\t});\n\t} else if (typeof module === 'object' && module.exports) {\n\t\tmodule.exports = Chart;\n\t}\n\n\troot.Chart = Chart;\n\n\tChart.noConflict = function(){\n\t\troot.Chart = previous;\n\t\treturn Chart;\n\t};\n\n}).call(this);\n\n(function(){\n\t\"use strict\";\n\n\tvar root = this,\n\t\tChart = root.Chart,\n\t\thelpers = Chart.helpers;\n\n\n\tvar defaultConfig = {\n\t\t//Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value\n\t\tscaleBeginAtZero : true,\n\n\t\t//Boolean - Whether grid lines are shown across the chart\n\t\tscaleShowGridLines : true,\n\n\t\t//String - Colour of the grid lines\n\t\tscaleGridLineColor : \"rgba(0,0,0,.05)\",\n\n\t\t//Number - Width of the grid lines\n\t\tscaleGridLineWidth : 1,\n\n\t\t//Boolean - Whether to show horizontal lines (except X axis)\n\t\tscaleShowHorizontalLines: true,\n\n\t\t//Boolean - Whether to show vertical lines (except Y axis)\n\t\tscaleShowVerticalLines: true,\n\n\t\t//Boolean - If there is a stroke on each bar\n\t\tbarShowStroke : true,\n\n\t\t//Number - Pixel width of the bar stroke\n\t\tbarStrokeWidth : 2,\n\n\t\t//Number - Spacing between each of the X value sets\n\t\tbarValueSpacing : 5,\n\n\t\t//Number - Spacing between data sets within X values\n\t\tbarDatasetSpacing : 1,\n\n\t\t//String - A legend template\n\t\tlegendTemplate : \"<ul class=\\\"<%=name.toLowerCase()%>-legend\\\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\\\"background-color:<%=datasets[i].fillColor%>\\\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>\"\n\n\t};\n\n\n\tChart.Type.extend({\n\t\tname: \"Bar\",\n\t\tdefaults : defaultConfig,\n\t\tinitialize:  function(data){\n\n\t\t\t//Expose options as a scope variable here so we can access it in the ScaleClass\n\t\t\tvar options = this.options;\n\n\t\t\tthis.ScaleClass = Chart.Scale.extend({\n\t\t\t\toffsetGridLines : true,\n\t\t\t\tcalculateBarX : function(datasetCount, datasetIndex, barIndex){\n\t\t\t\t\t//Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar\n\t\t\t\t\tvar xWidth = this.calculateBaseWidth(),\n\t\t\t\t\t\txAbsolute = this.calculateX(barIndex) - (xWidth/2),\n\t\t\t\t\t\tbarWidth = this.calculateBarWidth(datasetCount);\n\n\t\t\t\t\treturn xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth/2;\n\t\t\t\t},\n\t\t\t\tcalculateBaseWidth : function(){\n\t\t\t\t\treturn (this.calculateX(1) - this.calculateX(0)) - (2*options.barValueSpacing);\n\t\t\t\t},\n\t\t\t\tcalculateBarWidth : function(datasetCount){\n\t\t\t\t\t//The padding between datasets is to the right of each bar, providing that there are more than 1 dataset\n\t\t\t\t\tvar baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing);\n\n\t\t\t\t\treturn (baseWidth / datasetCount);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tthis.datasets = [];\n\n\t\t\t//Set up tooltip events on the chart\n\t\t\tif (this.options.showTooltips){\n\t\t\t\thelpers.bindEvents(this, this.options.tooltipEvents, function(evt){\n\t\t\t\t\tvar activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : [];\n\n\t\t\t\t\tthis.eachBars(function(bar){\n\t\t\t\t\t\tbar.restore(['fillColor', 'strokeColor']);\n\t\t\t\t\t});\n\t\t\t\t\thelpers.each(activeBars, function(activeBar){\n\t\t\t\t\t\tactiveBar.fillColor = activeBar.highlightFill;\n\t\t\t\t\t\tactiveBar.strokeColor = activeBar.highlightStroke;\n\t\t\t\t\t});\n\t\t\t\t\tthis.showTooltip(activeBars);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t//Declare the extension of the default point, to cater for the options passed in to the constructor\n\t\t\tthis.BarClass = Chart.Rectangle.extend({\n\t\t\t\tstrokeWidth : this.options.barStrokeWidth,\n\t\t\t\tshowStroke : this.options.barShowStroke,\n\t\t\t\tctx : this.chart.ctx\n\t\t\t});\n\n\t\t\t//Iterate through each of the datasets, and build this into a property of the chart\n\t\t\thelpers.each(data.datasets,function(dataset,datasetIndex){\n\n\t\t\t\tvar datasetObject = {\n\t\t\t\t\tlabel : dataset.label || null,\n\t\t\t\t\tfillColor : dataset.fillColor,\n\t\t\t\t\tstrokeColor : dataset.strokeColor,\n\t\t\t\t\tbars : []\n\t\t\t\t};\n\n\t\t\t\tthis.datasets.push(datasetObject);\n\n\t\t\t\thelpers.each(dataset.data,function(dataPoint,index){\n\t\t\t\t\t//Add a new point for each piece of data, passing any required data to draw.\n\t\t\t\t\tdatasetObject.bars.push(new this.BarClass({\n\t\t\t\t\t\tvalue : dataPoint,\n\t\t\t\t\t\tlabel : data.labels[index],\n\t\t\t\t\t\tdatasetLabel: dataset.label,\n\t\t\t\t\t\tstrokeColor : dataset.strokeColor,\n\t\t\t\t\t\tfillColor : dataset.fillColor,\n\t\t\t\t\t\thighlightFill : dataset.highlightFill || dataset.fillColor,\n\t\t\t\t\t\thighlightStroke : dataset.highlightStroke || dataset.strokeColor\n\t\t\t\t\t}));\n\t\t\t\t},this);\n\n\t\t\t},this);\n\n\t\t\tthis.buildScale(data.labels);\n\n\t\t\tthis.BarClass.prototype.base = this.scale.endPoint;\n\n\t\t\tthis.eachBars(function(bar, index, datasetIndex){\n\t\t\t\thelpers.extend(bar, {\n\t\t\t\t\twidth : this.scale.calculateBarWidth(this.datasets.length),\n\t\t\t\t\tx: this.scale.calculateBarX(this.datasets.length, datasetIndex, index),\n\t\t\t\t\ty: this.scale.endPoint\n\t\t\t\t});\n\t\t\t\tbar.save();\n\t\t\t}, this);\n\n\t\t\tthis.render();\n\t\t},\n\t\tupdate : function(){\n\t\t\tthis.scale.update();\n\t\t\t// Reset any highlight colours before updating.\n\t\t\thelpers.each(this.activeElements, function(activeElement){\n\t\t\t\tactiveElement.restore(['fillColor', 'strokeColor']);\n\t\t\t});\n\n\t\t\tthis.eachBars(function(bar){\n\t\t\t\tbar.save();\n\t\t\t});\n\t\t\tthis.render();\n\t\t},\n\t\teachBars : function(callback){\n\t\t\thelpers.each(this.datasets,function(dataset, datasetIndex){\n\t\t\t\thelpers.each(dataset.bars, callback, this, datasetIndex);\n\t\t\t},this);\n\t\t},\n\t\tgetBarsAtEvent : function(e){\n\t\t\tvar barsArray = [],\n\t\t\t\teventPosition = helpers.getRelativePosition(e),\n\t\t\t\tdatasetIterator = function(dataset){\n\t\t\t\t\tbarsArray.push(dataset.bars[barIndex]);\n\t\t\t\t},\n\t\t\t\tbarIndex;\n\n\t\t\tfor (var datasetIndex = 0; datasetIndex < this.datasets.length; datasetIndex++) {\n\t\t\t\tfor (barIndex = 0; barIndex < this.datasets[datasetIndex].bars.length; barIndex++) {\n\t\t\t\t\tif (this.datasets[datasetIndex].bars[barIndex].inRange(eventPosition.x,eventPosition.y)){\n\t\t\t\t\t\thelpers.each(this.datasets, datasetIterator);\n\t\t\t\t\t\treturn barsArray;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn barsArray;\n\t\t},\n\t\tbuildScale : function(labels){\n\t\t\tvar self = this;\n\n\t\t\tvar dataTotal = function(){\n\t\t\t\tvar values = [];\n\t\t\t\tself.eachBars(function(bar){\n\t\t\t\t\tvalues.push(bar.value);\n\t\t\t\t});\n\t\t\t\treturn values;\n\t\t\t};\n\n\t\t\tvar scaleOptions = {\n\t\t\t\ttemplateString : this.options.scaleLabel,\n\t\t\t\theight : this.chart.height,\n\t\t\t\twidth : this.chart.width,\n\t\t\t\tctx : this.chart.ctx,\n\t\t\t\ttextColor : this.options.scaleFontColor,\n\t\t\t\tfontSize : this.options.scaleFontSize,\n\t\t\t\tfontStyle : this.options.scaleFontStyle,\n\t\t\t\tfontFamily : this.options.scaleFontFamily,\n\t\t\t\tvaluesCount : labels.length,\n\t\t\t\tbeginAtZero : this.options.scaleBeginAtZero,\n\t\t\t\tintegersOnly : this.options.scaleIntegersOnly,\n\t\t\t\tcalculateYRange: function(currentHeight){\n\t\t\t\t\tvar updatedRanges = helpers.calculateScaleRange(\n\t\t\t\t\t\tdataTotal(),\n\t\t\t\t\t\tcurrentHeight,\n\t\t\t\t\t\tthis.fontSize,\n\t\t\t\t\t\tthis.beginAtZero,\n\t\t\t\t\t\tthis.integersOnly\n\t\t\t\t\t);\n\t\t\t\t\thelpers.extend(this, updatedRanges);\n\t\t\t\t},\n\t\t\t\txLabels : labels,\n\t\t\t\tfont : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),\n\t\t\t\tlineWidth : this.options.scaleLineWidth,\n\t\t\t\tlineColor : this.options.scaleLineColor,\n\t\t\t\tshowHorizontalLines : this.options.scaleShowHorizontalLines,\n\t\t\t\tshowVerticalLines : this.options.scaleShowVerticalLines,\n\t\t\t\tgridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,\n\t\t\t\tgridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : \"rgba(0,0,0,0)\",\n\t\t\t\tpadding : (this.options.showScale) ? 0 : (this.options.barShowStroke) ? this.options.barStrokeWidth : 0,\n\t\t\t\tshowLabels : this.options.scaleShowLabels,\n\t\t\t\tdisplay : this.options.showScale\n\t\t\t};\n\n\t\t\tif (this.options.scaleOverride){\n\t\t\t\thelpers.extend(scaleOptions, {\n\t\t\t\t\tcalculateYRange: helpers.noop,\n\t\t\t\t\tsteps: this.options.scaleSteps,\n\t\t\t\t\tstepValue: this.options.scaleStepWidth,\n\t\t\t\t\tmin: this.options.scaleStartValue,\n\t\t\t\t\tmax: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tthis.scale = new this.ScaleClass(scaleOptions);\n\t\t},\n\t\taddData : function(valuesArray,label){\n\t\t\t//Map the values array for each of the datasets\n\t\t\thelpers.each(valuesArray,function(value,datasetIndex){\n\t\t\t\t//Add a new point for each piece of data, passing any required data to draw.\n\t\t\t\tthis.datasets[datasetIndex].bars.push(new this.BarClass({\n\t\t\t\t\tvalue : value,\n\t\t\t\t\tlabel : label,\n\t\t\t\t\tx: this.scale.calculateBarX(this.datasets.length, datasetIndex, this.scale.valuesCount+1),\n\t\t\t\t\ty: this.scale.endPoint,\n\t\t\t\t\twidth : this.scale.calculateBarWidth(this.datasets.length),\n\t\t\t\t\tbase : this.scale.endPoint,\n\t\t\t\t\tstrokeColor : this.datasets[datasetIndex].strokeColor,\n\t\t\t\t\tfillColor : this.datasets[datasetIndex].fillColor\n\t\t\t\t}));\n\t\t\t},this);\n\n\t\t\tthis.scale.addXLabel(label);\n\t\t\t//Then re-render the chart.\n\t\t\tthis.update();\n\t\t},\n\t\tremoveData : function(){\n\t\t\tthis.scale.removeXLabel();\n\t\t\t//Then re-render the chart.\n\t\t\thelpers.each(this.datasets,function(dataset){\n\t\t\t\tdataset.bars.shift();\n\t\t\t},this);\n\t\t\tthis.update();\n\t\t},\n\t\treflow : function(){\n\t\t\thelpers.extend(this.BarClass.prototype,{\n\t\t\t\ty: this.scale.endPoint,\n\t\t\t\tbase : this.scale.endPoint\n\t\t\t});\n\t\t\tvar newScaleProps = helpers.extend({\n\t\t\t\theight : this.chart.height,\n\t\t\t\twidth : this.chart.width\n\t\t\t});\n\t\t\tthis.scale.update(newScaleProps);\n\t\t},\n\t\tdraw : function(ease){\n\t\t\tvar easingDecimal = ease || 1;\n\t\t\tthis.clear();\n\n\t\t\tvar ctx = this.chart.ctx;\n\n\t\t\tthis.scale.draw(easingDecimal);\n\n\t\t\t//Draw all the bars for each dataset\n\t\t\thelpers.each(this.datasets,function(dataset,datasetIndex){\n\t\t\t\thelpers.each(dataset.bars,function(bar,index){\n\t\t\t\t\tif (bar.hasValue()){\n\t\t\t\t\t\tbar.base = this.scale.endPoint;\n\t\t\t\t\t\t//Transition then draw\n\t\t\t\t\t\tbar.transition({\n\t\t\t\t\t\t\tx : this.scale.calculateBarX(this.datasets.length, datasetIndex, index),\n\t\t\t\t\t\t\ty : this.scale.calculateY(bar.value),\n\t\t\t\t\t\t\twidth : this.scale.calculateBarWidth(this.datasets.length)\n\t\t\t\t\t\t}, easingDecimal).draw();\n\t\t\t\t\t}\n\t\t\t\t},this);\n\n\t\t\t},this);\n\t\t}\n\t});\n\n\n}).call(this);\n\n(function(){\n\t\"use strict\";\n\n\tvar root = this,\n\t\tChart = root.Chart,\n\t\t//Cache a local reference to Chart.helpers\n\t\thelpers = Chart.helpers;\n\n\tvar defaultConfig = {\n\t\t//Boolean - Whether we should show a stroke on each segment\n\t\tsegmentShowStroke : true,\n\n\t\t//String - The colour of each segment stroke\n\t\tsegmentStrokeColor : \"#fff\",\n\n\t\t//Number - The width of each segment stroke\n\t\tsegmentStrokeWidth : 2,\n\n\t\t//The percentage of the chart that we cut out of the middle.\n\t\tpercentageInnerCutout : 50,\n\n\t\t//Number - Amount of animation steps\n\t\tanimationSteps : 100,\n\n\t\t//String - Animation easing effect\n\t\tanimationEasing : \"easeOutBounce\",\n\n\t\t//Boolean - Whether we animate the rotation of the Doughnut\n\t\tanimateRotate : true,\n\n\t\t//Boolean - Whether we animate scaling the Doughnut from the centre\n\t\tanimateScale : false,\n\n\t\t//String - A legend template\n\t\tlegendTemplate : \"<ul class=\\\"<%=name.toLowerCase()%>-legend\\\"><% for (var i=0; i<segments.length; i++){%><li><span style=\\\"background-color:<%=segments[i].fillColor%>\\\"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>\"\n\n\t};\n\n\n\tChart.Type.extend({\n\t\t//Passing in a name registers this chart in the Chart namespace\n\t\tname: \"Doughnut\",\n\t\t//Providing a defaults will also register the deafults in the chart namespace\n\t\tdefaults : defaultConfig,\n\t\t//Initialize is fired when the chart is initialized - Data is passed in as a parameter\n\t\t//Config is automatically merged by the core of Chart.js, and is available at this.options\n\t\tinitialize:  function(data){\n\n\t\t\t//Declare segments as a static property to prevent inheriting across the Chart type prototype\n\t\t\tthis.segments = [];\n\t\t\tthis.outerRadius = (helpers.min([this.chart.width,this.chart.height]) -\tthis.options.segmentStrokeWidth/2)/2;\n\n\t\t\tthis.SegmentArc = Chart.Arc.extend({\n\t\t\t\tctx : this.chart.ctx,\n\t\t\t\tx : this.chart.width/2,\n\t\t\t\ty : this.chart.height/2\n\t\t\t});\n\n\t\t\t//Set up tooltip events on the chart\n\t\t\tif (this.options.showTooltips){\n\t\t\t\thelpers.bindEvents(this, this.options.tooltipEvents, function(evt){\n\t\t\t\t\tvar activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : [];\n\n\t\t\t\t\thelpers.each(this.segments,function(segment){\n\t\t\t\t\t\tsegment.restore([\"fillColor\"]);\n\t\t\t\t\t});\n\t\t\t\t\thelpers.each(activeSegments,function(activeSegment){\n\t\t\t\t\t\tactiveSegment.fillColor = activeSegment.highlightColor;\n\t\t\t\t\t});\n\t\t\t\t\tthis.showTooltip(activeSegments);\n\t\t\t\t});\n\t\t\t}\n\t\t\tthis.calculateTotal(data);\n\n\t\t\thelpers.each(data,function(datapoint, index){\n\t\t\t\tthis.addData(datapoint, index, true);\n\t\t\t},this);\n\n\t\t\tthis.render();\n\t\t},\n\t\tgetSegmentsAtEvent : function(e){\n\t\t\tvar segmentsArray = [];\n\n\t\t\tvar location = helpers.getRelativePosition(e);\n\n\t\t\thelpers.each(this.segments,function(segment){\n\t\t\t\tif (segment.inRange(location.x,location.y)) segmentsArray.push(segment);\n\t\t\t},this);\n\t\t\treturn segmentsArray;\n\t\t},\n\t\taddData : function(segment, atIndex, silent){\n\t\t\tvar index = atIndex || this.segments.length;\n\t\t\tthis.segments.splice(index, 0, new this.SegmentArc({\n\t\t\t\tvalue : segment.value,\n\t\t\t\touterRadius : (this.options.animateScale) ? 0 : this.outerRadius,\n\t\t\t\tinnerRadius : (this.options.animateScale) ? 0 : (this.outerRadius/100) * this.options.percentageInnerCutout,\n\t\t\t\tfillColor : segment.color,\n\t\t\t\thighlightColor : segment.highlight || segment.color,\n\t\t\t\tshowStroke : this.options.segmentShowStroke,\n\t\t\t\tstrokeWidth : this.options.segmentStrokeWidth,\n\t\t\t\tstrokeColor : this.options.segmentStrokeColor,\n\t\t\t\tstartAngle : Math.PI * 1.5,\n\t\t\t\tcircumference : (this.options.animateRotate) ? 0 : this.calculateCircumference(segment.value),\n\t\t\t\tlabel : segment.label\n\t\t\t}));\n\t\t\tif (!silent){\n\t\t\t\tthis.reflow();\n\t\t\t\tthis.update();\n\t\t\t}\n\t\t},\n\t\tcalculateCircumference : function(value){\n\t\t\treturn (Math.PI*2)*(Math.abs(value) / this.total);\n\t\t},\n\t\tcalculateTotal : function(data){\n\t\t\tthis.total = 0;\n\t\t\thelpers.each(data,function(segment){\n\t\t\t\tthis.total += Math.abs(segment.value);\n\t\t\t},this);\n\t\t},\n\t\tupdate : function(){\n\t\t\tthis.calculateTotal(this.segments);\n\n\t\t\t// Reset any highlight colours before updating.\n\t\t\thelpers.each(this.activeElements, function(activeElement){\n\t\t\t\tactiveElement.restore(['fillColor']);\n\t\t\t});\n\n\t\t\thelpers.each(this.segments,function(segment){\n\t\t\t\tsegment.save();\n\t\t\t});\n\t\t\tthis.render();\n\t\t},\n\n\t\tremoveData: function(atIndex){\n\t\t\tvar indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1;\n\t\t\tthis.segments.splice(indexToDelete, 1);\n\t\t\tthis.reflow();\n\t\t\tthis.update();\n\t\t},\n\n\t\treflow : function(){\n\t\t\thelpers.extend(this.SegmentArc.prototype,{\n\t\t\t\tx : this.chart.width/2,\n\t\t\t\ty : this.chart.height/2\n\t\t\t});\n\t\t\tthis.outerRadius = (helpers.min([this.chart.width,this.chart.height]) -\tthis.options.segmentStrokeWidth/2)/2;\n\t\t\thelpers.each(this.segments, function(segment){\n\t\t\t\tsegment.update({\n\t\t\t\t\touterRadius : this.outerRadius,\n\t\t\t\t\tinnerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout\n\t\t\t\t});\n\t\t\t}, this);\n\t\t},\n\t\tdraw : function(easeDecimal){\n\t\t\tvar animDecimal = (easeDecimal) ? easeDecimal : 1;\n\t\t\tthis.clear();\n\t\t\thelpers.each(this.segments,function(segment,index){\n\t\t\t\tsegment.transition({\n\t\t\t\t\tcircumference : this.calculateCircumference(segment.value),\n\t\t\t\t\touterRadius : this.outerRadius,\n\t\t\t\t\tinnerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout\n\t\t\t\t},animDecimal);\n\n\t\t\t\tsegment.endAngle = segment.startAngle + segment.circumference;\n\n\t\t\t\tsegment.draw();\n\t\t\t\tif (index === 0){\n\t\t\t\t\tsegment.startAngle = Math.PI * 1.5;\n\t\t\t\t}\n\t\t\t\t//Check to see if it's the last segment, if not get the next and update the start angle\n\t\t\t\tif (index < this.segments.length-1){\n\t\t\t\t\tthis.segments[index+1].startAngle = segment.endAngle;\n\t\t\t\t}\n\t\t\t},this);\n\n\t\t}\n\t});\n\n\tChart.types.Doughnut.extend({\n\t\tname : \"Pie\",\n\t\tdefaults : helpers.merge(defaultConfig,{percentageInnerCutout : 0})\n\t});\n\n}).call(this);\n(function(){\n\t\"use strict\";\n\n\tvar root = this,\n\t\tChart = root.Chart,\n\t\thelpers = Chart.helpers;\n\n\tvar defaultConfig = {\n\n\t\t///Boolean - Whether grid lines are shown across the chart\n\t\tscaleShowGridLines : true,\n\n\t\t//String - Colour of the grid lines\n\t\tscaleGridLineColor : \"rgba(0,0,0,.05)\",\n\n\t\t//Number - Width of the grid lines\n\t\tscaleGridLineWidth : 1,\n\n\t\t//Boolean - Whether to show horizontal lines (except X axis)\n\t\tscaleShowHorizontalLines: true,\n\n\t\t//Boolean - Whether to show vertical lines (except Y axis)\n\t\tscaleShowVerticalLines: true,\n\n\t\t//Boolean - Whether the line is curved between points\n\t\tbezierCurve : true,\n\n\t\t//Number - Tension of the bezier curve between points\n\t\tbezierCurveTension : 0.4,\n\n\t\t//Boolean - Whether to show a dot for each point\n\t\tpointDot : true,\n\n\t\t//Number - Radius of each point dot in pixels\n\t\tpointDotRadius : 4,\n\n\t\t//Number - Pixel width of point dot stroke\n\t\tpointDotStrokeWidth : 1,\n\n\t\t//Number - amount extra to add to the radius to cater for hit detection outside the drawn point\n\t\tpointHitDetectionRadius : 20,\n\n\t\t//Boolean - Whether to show a stroke for datasets\n\t\tdatasetStroke : true,\n\n\t\t//Number - Pixel width of dataset stroke\n\t\tdatasetStrokeWidth : 2,\n\n\t\t//Boolean - Whether to fill the dataset with a colour\n\t\tdatasetFill : true,\n\n\t\t//String - A legend template\n\t\tlegendTemplate : \"<ul class=\\\"<%=name.toLowerCase()%>-legend\\\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\\\"background-color:<%=datasets[i].strokeColor%>\\\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>\"\n\n\t};\n\n\n\tChart.Type.extend({\n\t\tname: \"Line\",\n\t\tdefaults : defaultConfig,\n\t\tinitialize:  function(data){\n\t\t\t//Declare the extension of the default point, to cater for the options passed in to the constructor\n\t\t\tthis.PointClass = Chart.Point.extend({\n\t\t\t\tstrokeWidth : this.options.pointDotStrokeWidth,\n\t\t\t\tradius : this.options.pointDotRadius,\n\t\t\t\tdisplay: this.options.pointDot,\n\t\t\t\thitDetectionRadius : this.options.pointHitDetectionRadius,\n\t\t\t\tctx : this.chart.ctx,\n\t\t\t\tinRange : function(mouseX){\n\t\t\t\t\treturn (Math.pow(mouseX-this.x, 2) < Math.pow(this.radius + this.hitDetectionRadius,2));\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tthis.datasets = [];\n\n\t\t\t//Set up tooltip events on the chart\n\t\t\tif (this.options.showTooltips){\n\t\t\t\thelpers.bindEvents(this, this.options.tooltipEvents, function(evt){\n\t\t\t\t\tvar activePoints = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : [];\n\t\t\t\t\tthis.eachPoints(function(point){\n\t\t\t\t\t\tpoint.restore(['fillColor', 'strokeColor']);\n\t\t\t\t\t});\n\t\t\t\t\thelpers.each(activePoints, function(activePoint){\n\t\t\t\t\t\tactivePoint.fillColor = activePoint.highlightFill;\n\t\t\t\t\t\tactivePoint.strokeColor = activePoint.highlightStroke;\n\t\t\t\t\t});\n\t\t\t\t\tthis.showTooltip(activePoints);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t//Iterate through each of the datasets, and build this into a property of the chart\n\t\t\thelpers.each(data.datasets,function(dataset){\n\n\t\t\t\tvar datasetObject = {\n\t\t\t\t\tlabel : dataset.label || null,\n\t\t\t\t\tfillColor : dataset.fillColor,\n\t\t\t\t\tstrokeColor : dataset.strokeColor,\n\t\t\t\t\tpointColor : dataset.pointColor,\n\t\t\t\t\tpointStrokeColor : dataset.pointStrokeColor,\n\t\t\t\t\tpoints : []\n\t\t\t\t};\n\n\t\t\t\tthis.datasets.push(datasetObject);\n\n\n\t\t\t\thelpers.each(dataset.data,function(dataPoint,index){\n\t\t\t\t\t//Add a new point for each piece of data, passing any required data to draw.\n\t\t\t\t\tdatasetObject.points.push(new this.PointClass({\n\t\t\t\t\t\tvalue : dataPoint,\n\t\t\t\t\t\tlabel : data.labels[index],\n\t\t\t\t\t\tdatasetLabel: dataset.label,\n\t\t\t\t\t\tstrokeColor : dataset.pointStrokeColor,\n\t\t\t\t\t\tfillColor : dataset.pointColor,\n\t\t\t\t\t\thighlightFill : dataset.pointHighlightFill || dataset.pointColor,\n\t\t\t\t\t\thighlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor\n\t\t\t\t\t}));\n\t\t\t\t},this);\n\n\t\t\t\tthis.buildScale(data.labels);\n\n\n\t\t\t\tthis.eachPoints(function(point, index){\n\t\t\t\t\thelpers.extend(point, {\n\t\t\t\t\t\tx: this.scale.calculateX(index),\n\t\t\t\t\t\ty: this.scale.endPoint\n\t\t\t\t\t});\n\t\t\t\t\tpoint.save();\n\t\t\t\t}, this);\n\n\t\t\t},this);\n\n\n\t\t\tthis.render();\n\t\t},\n\t\tupdate : function(){\n\t\t\tthis.scale.update();\n\t\t\t// Reset any highlight colours before updating.\n\t\t\thelpers.each(this.activeElements, function(activeElement){\n\t\t\t\tactiveElement.restore(['fillColor', 'strokeColor']);\n\t\t\t});\n\t\t\tthis.eachPoints(function(point){\n\t\t\t\tpoint.save();\n\t\t\t});\n\t\t\tthis.render();\n\t\t},\n\t\teachPoints : function(callback){\n\t\t\thelpers.each(this.datasets,function(dataset){\n\t\t\t\thelpers.each(dataset.points,callback,this);\n\t\t\t},this);\n\t\t},\n\t\tgetPointsAtEvent : function(e){\n\t\t\tvar pointsArray = [],\n\t\t\t\teventPosition = helpers.getRelativePosition(e);\n\t\t\thelpers.each(this.datasets,function(dataset){\n\t\t\t\thelpers.each(dataset.points,function(point){\n\t\t\t\t\tif (point.inRange(eventPosition.x,eventPosition.y)) pointsArray.push(point);\n\t\t\t\t});\n\t\t\t},this);\n\t\t\treturn pointsArray;\n\t\t},\n\t\tbuildScale : function(labels){\n\t\t\tvar self = this;\n\n\t\t\tvar dataTotal = function(){\n\t\t\t\tvar values = [];\n\t\t\t\tself.eachPoints(function(point){\n\t\t\t\t\tvalues.push(point.value);\n\t\t\t\t});\n\n\t\t\t\treturn values;\n\t\t\t};\n\n\t\t\tvar scaleOptions = {\n\t\t\t\ttemplateString : this.options.scaleLabel,\n\t\t\t\theight : this.chart.height,\n\t\t\t\twidth : this.chart.width,\n\t\t\t\tctx : this.chart.ctx,\n\t\t\t\ttextColor : this.options.scaleFontColor,\n\t\t\t\tfontSize : this.options.scaleFontSize,\n\t\t\t\tfontStyle : this.options.scaleFontStyle,\n\t\t\t\tfontFamily : this.options.scaleFontFamily,\n\t\t\t\tvaluesCount : labels.length,\n\t\t\t\tbeginAtZero : this.options.scaleBeginAtZero,\n\t\t\t\tintegersOnly : this.options.scaleIntegersOnly,\n\t\t\t\tcalculateYRange : function(currentHeight){\n\t\t\t\t\tvar updatedRanges = helpers.calculateScaleRange(\n\t\t\t\t\t\tdataTotal(),\n\t\t\t\t\t\tcurrentHeight,\n\t\t\t\t\t\tthis.fontSize,\n\t\t\t\t\t\tthis.beginAtZero,\n\t\t\t\t\t\tthis.integersOnly\n\t\t\t\t\t);\n\t\t\t\t\thelpers.extend(this, updatedRanges);\n\t\t\t\t},\n\t\t\t\txLabels : labels,\n\t\t\t\tfont : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),\n\t\t\t\tlineWidth : this.options.scaleLineWidth,\n\t\t\t\tlineColor : this.options.scaleLineColor,\n\t\t\t\tshowHorizontalLines : this.options.scaleShowHorizontalLines,\n\t\t\t\tshowVerticalLines : this.options.scaleShowVerticalLines,\n\t\t\t\tgridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,\n\t\t\t\tgridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : \"rgba(0,0,0,0)\",\n\t\t\t\tpadding: (this.options.showScale) ? 0 : this.options.pointDotRadius + this.options.pointDotStrokeWidth,\n\t\t\t\tshowLabels : this.options.scaleShowLabels,\n\t\t\t\tdisplay : this.options.showScale\n\t\t\t};\n\n\t\t\tif (this.options.scaleOverride){\n\t\t\t\thelpers.extend(scaleOptions, {\n\t\t\t\t\tcalculateYRange: helpers.noop,\n\t\t\t\t\tsteps: this.options.scaleSteps,\n\t\t\t\t\tstepValue: this.options.scaleStepWidth,\n\t\t\t\t\tmin: this.options.scaleStartValue,\n\t\t\t\t\tmax: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)\n\t\t\t\t});\n\t\t\t}\n\n\n\t\t\tthis.scale = new Chart.Scale(scaleOptions);\n\t\t},\n\t\taddData : function(valuesArray,label){\n\t\t\t//Map the values array for each of the datasets\n\n\t\t\thelpers.each(valuesArray,function(value,datasetIndex){\n\t\t\t\t//Add a new point for each piece of data, passing any required data to draw.\n\t\t\t\tthis.datasets[datasetIndex].points.push(new this.PointClass({\n\t\t\t\t\tvalue : value,\n\t\t\t\t\tlabel : label,\n\t\t\t\t\tx: this.scale.calculateX(this.scale.valuesCount+1),\n\t\t\t\t\ty: this.scale.endPoint,\n\t\t\t\t\tstrokeColor : this.datasets[datasetIndex].pointStrokeColor,\n\t\t\t\t\tfillColor : this.datasets[datasetIndex].pointColor\n\t\t\t\t}));\n\t\t\t},this);\n\n\t\t\tthis.scale.addXLabel(label);\n\t\t\t//Then re-render the chart.\n\t\t\tthis.update();\n\t\t},\n\t\tremoveData : function(){\n\t\t\tthis.scale.removeXLabel();\n\t\t\t//Then re-render the chart.\n\t\t\thelpers.each(this.datasets,function(dataset){\n\t\t\t\tdataset.points.shift();\n\t\t\t},this);\n\t\t\tthis.update();\n\t\t},\n\t\treflow : function(){\n\t\t\tvar newScaleProps = helpers.extend({\n\t\t\t\theight : this.chart.height,\n\t\t\t\twidth : this.chart.width\n\t\t\t});\n\t\t\tthis.scale.update(newScaleProps);\n\t\t},\n\t\tdraw : function(ease){\n\t\t\tvar easingDecimal = ease || 1;\n\t\t\tthis.clear();\n\n\t\t\tvar ctx = this.chart.ctx;\n\n\t\t\t// Some helper methods for getting the next/prev points\n\t\t\tvar hasValue = function(item){\n\t\t\t\treturn item.value !== null;\n\t\t\t},\n\t\t\tnextPoint = function(point, collection, index){\n\t\t\t\treturn helpers.findNextWhere(collection, hasValue, index) || point;\n\t\t\t},\n\t\t\tpreviousPoint = function(point, collection, index){\n\t\t\t\treturn helpers.findPreviousWhere(collection, hasValue, index) || point;\n\t\t\t};\n\n\t\t\tthis.scale.draw(easingDecimal);\n\n\n\t\t\thelpers.each(this.datasets,function(dataset){\n\t\t\t\tvar pointsWithValues = helpers.where(dataset.points, hasValue);\n\n\t\t\t\t//Transition each point first so that the line and point drawing isn't out of sync\n\t\t\t\t//We can use this extra loop to calculate the control points of this dataset also in this loop\n\n\t\t\t\thelpers.each(dataset.points, function(point, index){\n\t\t\t\t\tif (point.hasValue()){\n\t\t\t\t\t\tpoint.transition({\n\t\t\t\t\t\t\ty : this.scale.calculateY(point.value),\n\t\t\t\t\t\t\tx : this.scale.calculateX(index)\n\t\t\t\t\t\t}, easingDecimal);\n\t\t\t\t\t}\n\t\t\t\t},this);\n\n\n\t\t\t\t// Control points need to be calculated in a seperate loop, because we need to know the current x/y of the point\n\t\t\t\t// This would cause issues when there is no animation, because the y of the next point would be 0, so beziers would be skewed\n\t\t\t\tif (this.options.bezierCurve){\n\t\t\t\t\thelpers.each(pointsWithValues, function(point, index){\n\t\t\t\t\t\tvar tension = (index > 0 && index < pointsWithValues.length - 1) ? this.options.bezierCurveTension : 0;\n\t\t\t\t\t\tpoint.controlPoints = helpers.splineCurve(\n\t\t\t\t\t\t\tpreviousPoint(point, pointsWithValues, index),\n\t\t\t\t\t\t\tpoint,\n\t\t\t\t\t\t\tnextPoint(point, pointsWithValues, index),\n\t\t\t\t\t\t\ttension\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Prevent the bezier going outside of the bounds of the graph\n\n\t\t\t\t\t\t// Cap puter bezier handles to the upper/lower scale bounds\n\t\t\t\t\t\tif (point.controlPoints.outer.y > this.scale.endPoint){\n\t\t\t\t\t\t\tpoint.controlPoints.outer.y = this.scale.endPoint;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (point.controlPoints.outer.y < this.scale.startPoint){\n\t\t\t\t\t\t\tpoint.controlPoints.outer.y = this.scale.startPoint;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Cap inner bezier handles to the upper/lower scale bounds\n\t\t\t\t\t\tif (point.controlPoints.inner.y > this.scale.endPoint){\n\t\t\t\t\t\t\tpoint.controlPoints.inner.y = this.scale.endPoint;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (point.controlPoints.inner.y < this.scale.startPoint){\n\t\t\t\t\t\t\tpoint.controlPoints.inner.y = this.scale.startPoint;\n\t\t\t\t\t\t}\n\t\t\t\t\t},this);\n\t\t\t\t}\n\n\n\t\t\t\t//Draw the line between all the points\n\t\t\t\tctx.lineWidth = this.options.datasetStrokeWidth;\n\t\t\t\tctx.strokeStyle = dataset.strokeColor;\n\t\t\t\tctx.beginPath();\n\n\t\t\t\thelpers.each(pointsWithValues, function(point, index){\n\t\t\t\t\tif (index === 0){\n\t\t\t\t\t\tctx.moveTo(point.x, point.y);\n\t\t\t\t\t}\n\t\t\t\t\telse{\n\t\t\t\t\t\tif(this.options.bezierCurve){\n\t\t\t\t\t\t\tvar previous = previousPoint(point, pointsWithValues, index);\n\n\t\t\t\t\t\t\tctx.bezierCurveTo(\n\t\t\t\t\t\t\t\tprevious.controlPoints.outer.x,\n\t\t\t\t\t\t\t\tprevious.controlPoints.outer.y,\n\t\t\t\t\t\t\t\tpoint.controlPoints.inner.x,\n\t\t\t\t\t\t\t\tpoint.controlPoints.inner.y,\n\t\t\t\t\t\t\t\tpoint.x,\n\t\t\t\t\t\t\t\tpoint.y\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse{\n\t\t\t\t\t\t\tctx.lineTo(point.x,point.y);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}, this);\n\n\t\t\t\tctx.stroke();\n\n\t\t\t\tif (this.options.datasetFill && pointsWithValues.length > 0){\n\t\t\t\t\t//Round off the line by going to the base of the chart, back to the start, then fill.\n\t\t\t\t\tctx.lineTo(pointsWithValues[pointsWithValues.length - 1].x, this.scale.endPoint);\n\t\t\t\t\tctx.lineTo(pointsWithValues[0].x, this.scale.endPoint);\n\t\t\t\t\tctx.fillStyle = dataset.fillColor;\n\t\t\t\t\tctx.closePath();\n\t\t\t\t\tctx.fill();\n\t\t\t\t}\n\n\t\t\t\t//Now draw the points over the line\n\t\t\t\t//A little inefficient double looping, but better than the line\n\t\t\t\t//lagging behind the point positions\n\t\t\t\thelpers.each(pointsWithValues,function(point){\n\t\t\t\t\tpoint.draw();\n\t\t\t\t});\n\t\t\t},this);\n\t\t}\n\t});\n\n\n}).call(this);\n\n(function(){\n\t\"use strict\";\n\n\tvar root = this,\n\t\tChart = root.Chart,\n\t\t//Cache a local reference to Chart.helpers\n\t\thelpers = Chart.helpers;\n\n\tvar defaultConfig = {\n\t\t//Boolean - Show a backdrop to the scale label\n\t\tscaleShowLabelBackdrop : true,\n\n\t\t//String - The colour of the label backdrop\n\t\tscaleBackdropColor : \"rgba(255,255,255,0.75)\",\n\n\t\t// Boolean - Whether the scale should begin at zero\n\t\tscaleBeginAtZero : true,\n\n\t\t//Number - The backdrop padding above & below the label in pixels\n\t\tscaleBackdropPaddingY : 2,\n\n\t\t//Number - The backdrop padding to the side of the label in pixels\n\t\tscaleBackdropPaddingX : 2,\n\n\t\t//Boolean - Show line for each value in the scale\n\t\tscaleShowLine : true,\n\n\t\t//Boolean - Stroke a line around each segment in the chart\n\t\tsegmentShowStroke : true,\n\n\t\t//String - The colour of the stroke on each segement.\n\t\tsegmentStrokeColor : \"#fff\",\n\n\t\t//Number - The width of the stroke value in pixels\n\t\tsegmentStrokeWidth : 2,\n\n\t\t//Number - Amount of animation steps\n\t\tanimationSteps : 100,\n\n\t\t//String - Animation easing effect.\n\t\tanimationEasing : \"easeOutBounce\",\n\n\t\t//Boolean - Whether to animate the rotation of the chart\n\t\tanimateRotate : true,\n\n\t\t//Boolean - Whether to animate scaling the chart from the centre\n\t\tanimateScale : false,\n\n\t\t//String - A legend template\n\t\tlegendTemplate : \"<ul class=\\\"<%=name.toLowerCase()%>-legend\\\"><% for (var i=0; i<segments.length; i++){%><li><span style=\\\"background-color:<%=segments[i].fillColor%>\\\"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>\"\n\t};\n\n\n\tChart.Type.extend({\n\t\t//Passing in a name registers this chart in the Chart namespace\n\t\tname: \"PolarArea\",\n\t\t//Providing a defaults will also register the deafults in the chart namespace\n\t\tdefaults : defaultConfig,\n\t\t//Initialize is fired when the chart is initialized - Data is passed in as a parameter\n\t\t//Config is automatically merged by the core of Chart.js, and is available at this.options\n\t\tinitialize:  function(data){\n\t\t\tthis.segments = [];\n\t\t\t//Declare segment class as a chart instance specific class, so it can share props for this instance\n\t\t\tthis.SegmentArc = Chart.Arc.extend({\n\t\t\t\tshowStroke : this.options.segmentShowStroke,\n\t\t\t\tstrokeWidth : this.options.segmentStrokeWidth,\n\t\t\t\tstrokeColor : this.options.segmentStrokeColor,\n\t\t\t\tctx : this.chart.ctx,\n\t\t\t\tinnerRadius : 0,\n\t\t\t\tx : this.chart.width/2,\n\t\t\t\ty : this.chart.height/2\n\t\t\t});\n\t\t\tthis.scale = new Chart.RadialScale({\n\t\t\t\tdisplay: this.options.showScale,\n\t\t\t\tfontStyle: this.options.scaleFontStyle,\n\t\t\t\tfontSize: this.options.scaleFontSize,\n\t\t\t\tfontFamily: this.options.scaleFontFamily,\n\t\t\t\tfontColor: this.options.scaleFontColor,\n\t\t\t\tshowLabels: this.options.scaleShowLabels,\n\t\t\t\tshowLabelBackdrop: this.options.scaleShowLabelBackdrop,\n\t\t\t\tbackdropColor: this.options.scaleBackdropColor,\n\t\t\t\tbackdropPaddingY : this.options.scaleBackdropPaddingY,\n\t\t\t\tbackdropPaddingX: this.options.scaleBackdropPaddingX,\n\t\t\t\tlineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0,\n\t\t\t\tlineColor: this.options.scaleLineColor,\n\t\t\t\tlineArc: true,\n\t\t\t\twidth: this.chart.width,\n\t\t\t\theight: this.chart.height,\n\t\t\t\txCenter: this.chart.width/2,\n\t\t\t\tyCenter: this.chart.height/2,\n\t\t\t\tctx : this.chart.ctx,\n\t\t\t\ttemplateString: this.options.scaleLabel,\n\t\t\t\tvaluesCount: data.length\n\t\t\t});\n\n\t\t\tthis.updateScaleRange(data);\n\n\t\t\tthis.scale.update();\n\n\t\t\thelpers.each(data,function(segment,index){\n\t\t\t\tthis.addData(segment,index,true);\n\t\t\t},this);\n\n\t\t\t//Set up tooltip events on the chart\n\t\t\tif (this.options.showTooltips){\n\t\t\t\thelpers.bindEvents(this, this.options.tooltipEvents, function(evt){\n\t\t\t\t\tvar activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : [];\n\t\t\t\t\thelpers.each(this.segments,function(segment){\n\t\t\t\t\t\tsegment.restore([\"fillColor\"]);\n\t\t\t\t\t});\n\t\t\t\t\thelpers.each(activeSegments,function(activeSegment){\n\t\t\t\t\t\tactiveSegment.fillColor = activeSegment.highlightColor;\n\t\t\t\t\t});\n\t\t\t\t\tthis.showTooltip(activeSegments);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tthis.render();\n\t\t},\n\t\tgetSegmentsAtEvent : function(e){\n\t\t\tvar segmentsArray = [];\n\n\t\t\tvar location = helpers.getRelativePosition(e);\n\n\t\t\thelpers.each(this.segments,function(segment){\n\t\t\t\tif (segment.inRange(location.x,location.y)) segmentsArray.push(segment);\n\t\t\t},this);\n\t\t\treturn segmentsArray;\n\t\t},\n\t\taddData : function(segment, atIndex, silent){\n\t\t\tvar index = atIndex || this.segments.length;\n\n\t\t\tthis.segments.splice(index, 0, new this.SegmentArc({\n\t\t\t\tfillColor: segment.color,\n\t\t\t\thighlightColor: segment.highlight || segment.color,\n\t\t\t\tlabel: segment.label,\n\t\t\t\tvalue: segment.value,\n\t\t\t\touterRadius: (this.options.animateScale) ? 0 : this.scale.calculateCenterOffset(segment.value),\n\t\t\t\tcircumference: (this.options.animateRotate) ? 0 : this.scale.getCircumference(),\n\t\t\t\tstartAngle: Math.PI * 1.5\n\t\t\t}));\n\t\t\tif (!silent){\n\t\t\t\tthis.reflow();\n\t\t\t\tthis.update();\n\t\t\t}\n\t\t},\n\t\tremoveData: function(atIndex){\n\t\t\tvar indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1;\n\t\t\tthis.segments.splice(indexToDelete, 1);\n\t\t\tthis.reflow();\n\t\t\tthis.update();\n\t\t},\n\t\tcalculateTotal: function(data){\n\t\t\tthis.total = 0;\n\t\t\thelpers.each(data,function(segment){\n\t\t\t\tthis.total += segment.value;\n\t\t\t},this);\n\t\t\tthis.scale.valuesCount = this.segments.length;\n\t\t},\n\t\tupdateScaleRange: function(datapoints){\n\t\t\tvar valuesArray = [];\n\t\t\thelpers.each(datapoints,function(segment){\n\t\t\t\tvaluesArray.push(segment.value);\n\t\t\t});\n\n\t\t\tvar scaleSizes = (this.options.scaleOverride) ?\n\t\t\t\t{\n\t\t\t\t\tsteps: this.options.scaleSteps,\n\t\t\t\t\tstepValue: this.options.scaleStepWidth,\n\t\t\t\t\tmin: this.options.scaleStartValue,\n\t\t\t\t\tmax: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)\n\t\t\t\t} :\n\t\t\t\thelpers.calculateScaleRange(\n\t\t\t\t\tvaluesArray,\n\t\t\t\t\thelpers.min([this.chart.width, this.chart.height])/2,\n\t\t\t\t\tthis.options.scaleFontSize,\n\t\t\t\t\tthis.options.scaleBeginAtZero,\n\t\t\t\t\tthis.options.scaleIntegersOnly\n\t\t\t\t);\n\n\t\t\thelpers.extend(\n\t\t\t\tthis.scale,\n\t\t\t\tscaleSizes,\n\t\t\t\t{\n\t\t\t\t\tsize: helpers.min([this.chart.width, this.chart.height]),\n\t\t\t\t\txCenter: this.chart.width/2,\n\t\t\t\t\tyCenter: this.chart.height/2\n\t\t\t\t}\n\t\t\t);\n\n\t\t},\n\t\tupdate : function(){\n\t\t\tthis.calculateTotal(this.segments);\n\n\t\t\thelpers.each(this.segments,function(segment){\n\t\t\t\tsegment.save();\n\t\t\t});\n\t\t\t\n\t\t\tthis.reflow();\n\t\t\tthis.render();\n\t\t},\n\t\treflow : function(){\n\t\t\thelpers.extend(this.SegmentArc.prototype,{\n\t\t\t\tx : this.chart.width/2,\n\t\t\t\ty : this.chart.height/2\n\t\t\t});\n\t\t\tthis.updateScaleRange(this.segments);\n\t\t\tthis.scale.update();\n\n\t\t\thelpers.extend(this.scale,{\n\t\t\t\txCenter: this.chart.width/2,\n\t\t\t\tyCenter: this.chart.height/2\n\t\t\t});\n\n\t\t\thelpers.each(this.segments, function(segment){\n\t\t\t\tsegment.update({\n\t\t\t\t\touterRadius : this.scale.calculateCenterOffset(segment.value)\n\t\t\t\t});\n\t\t\t}, this);\n\n\t\t},\n\t\tdraw : function(ease){\n\t\t\tvar easingDecimal = ease || 1;\n\t\t\t//Clear & draw the canvas\n\t\t\tthis.clear();\n\t\t\thelpers.each(this.segments,function(segment, index){\n\t\t\t\tsegment.transition({\n\t\t\t\t\tcircumference : this.scale.getCircumference(),\n\t\t\t\t\touterRadius : this.scale.calculateCenterOffset(segment.value)\n\t\t\t\t},easingDecimal);\n\n\t\t\t\tsegment.endAngle = segment.startAngle + segment.circumference;\n\n\t\t\t\t// If we've removed the first segment we need to set the first one to\n\t\t\t\t// start at the top.\n\t\t\t\tif (index === 0){\n\t\t\t\t\tsegment.startAngle = Math.PI * 1.5;\n\t\t\t\t}\n\n\t\t\t\t//Check to see if it's the last segment, if not get the next and update the start angle\n\t\t\t\tif (index < this.segments.length - 1){\n\t\t\t\t\tthis.segments[index+1].startAngle = segment.endAngle;\n\t\t\t\t}\n\t\t\t\tsegment.draw();\n\t\t\t}, this);\n\t\t\tthis.scale.draw();\n\t\t}\n\t});\n\n}).call(this);\n(function(){\n\t\"use strict\";\n\n\tvar root = this,\n\t\tChart = root.Chart,\n\t\thelpers = Chart.helpers;\n\n\n\n\tChart.Type.extend({\n\t\tname: \"Radar\",\n\t\tdefaults:{\n\t\t\t//Boolean - Whether to show lines for each scale point\n\t\t\tscaleShowLine : true,\n\n\t\t\t//Boolean - Whether we show the angle lines out of the radar\n\t\t\tangleShowLineOut : true,\n\n\t\t\t//Boolean - Whether to show labels on the scale\n\t\t\tscaleShowLabels : false,\n\n\t\t\t// Boolean - Whether the scale should begin at zero\n\t\t\tscaleBeginAtZero : true,\n\n\t\t\t//String - Colour of the angle line\n\t\t\tangleLineColor : \"rgba(0,0,0,.1)\",\n\n\t\t\t//Number - Pixel width of the angle line\n\t\t\tangleLineWidth : 1,\n\n\t\t\t//String - Point label font declaration\n\t\t\tpointLabelFontFamily : \"'Arial'\",\n\n\t\t\t//String - Point label font weight\n\t\t\tpointLabelFontStyle : \"normal\",\n\n\t\t\t//Number - Point label font size in pixels\n\t\t\tpointLabelFontSize : 10,\n\n\t\t\t//String - Point label font colour\n\t\t\tpointLabelFontColor : \"#666\",\n\n\t\t\t//Boolean - Whether to show a dot for each point\n\t\t\tpointDot : true,\n\n\t\t\t//Number - Radius of each point dot in pixels\n\t\t\tpointDotRadius : 3,\n\n\t\t\t//Number - Pixel width of point dot stroke\n\t\t\tpointDotStrokeWidth : 1,\n\n\t\t\t//Number - amount extra to add to the radius to cater for hit detection outside the drawn point\n\t\t\tpointHitDetectionRadius : 20,\n\n\t\t\t//Boolean - Whether to show a stroke for datasets\n\t\t\tdatasetStroke : true,\n\n\t\t\t//Number - Pixel width of dataset stroke\n\t\t\tdatasetStrokeWidth : 2,\n\n\t\t\t//Boolean - Whether to fill the dataset with a colour\n\t\t\tdatasetFill : true,\n\n\t\t\t//String - A legend template\n\t\t\tlegendTemplate : \"<ul class=\\\"<%=name.toLowerCase()%>-legend\\\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\\\"background-color:<%=datasets[i].strokeColor%>\\\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>\"\n\n\t\t},\n\n\t\tinitialize: function(data){\n\t\t\tthis.PointClass = Chart.Point.extend({\n\t\t\t\tstrokeWidth : this.options.pointDotStrokeWidth,\n\t\t\t\tradius : this.options.pointDotRadius,\n\t\t\t\tdisplay: this.options.pointDot,\n\t\t\t\thitDetectionRadius : this.options.pointHitDetectionRadius,\n\t\t\t\tctx : this.chart.ctx\n\t\t\t});\n\n\t\t\tthis.datasets = [];\n\n\t\t\tthis.buildScale(data);\n\n\t\t\t//Set up tooltip events on the chart\n\t\t\tif (this.options.showTooltips){\n\t\t\t\thelpers.bindEvents(this, this.options.tooltipEvents, function(evt){\n\t\t\t\t\tvar activePointsCollection = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : [];\n\n\t\t\t\t\tthis.eachPoints(function(point){\n\t\t\t\t\t\tpoint.restore(['fillColor', 'strokeColor']);\n\t\t\t\t\t});\n\t\t\t\t\thelpers.each(activePointsCollection, function(activePoint){\n\t\t\t\t\t\tactivePoint.fillColor = activePoint.highlightFill;\n\t\t\t\t\t\tactivePoint.strokeColor = activePoint.highlightStroke;\n\t\t\t\t\t});\n\n\t\t\t\t\tthis.showTooltip(activePointsCollection);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t//Iterate through each of the datasets, and build this into a property of the chart\n\t\t\thelpers.each(data.datasets,function(dataset){\n\n\t\t\t\tvar datasetObject = {\n\t\t\t\t\tlabel: dataset.label || null,\n\t\t\t\t\tfillColor : dataset.fillColor,\n\t\t\t\t\tstrokeColor : dataset.strokeColor,\n\t\t\t\t\tpointColor : dataset.pointColor,\n\t\t\t\t\tpointStrokeColor : dataset.pointStrokeColor,\n\t\t\t\t\tpoints : []\n\t\t\t\t};\n\n\t\t\t\tthis.datasets.push(datasetObject);\n\n\t\t\t\thelpers.each(dataset.data,function(dataPoint,index){\n\t\t\t\t\t//Add a new point for each piece of data, passing any required data to draw.\n\t\t\t\t\tvar pointPosition;\n\t\t\t\t\tif (!this.scale.animation){\n\t\t\t\t\t\tpointPosition = this.scale.getPointPosition(index, this.scale.calculateCenterOffset(dataPoint));\n\t\t\t\t\t}\n\t\t\t\t\tdatasetObject.points.push(new this.PointClass({\n\t\t\t\t\t\tvalue : dataPoint,\n\t\t\t\t\t\tlabel : data.labels[index],\n\t\t\t\t\t\tdatasetLabel: dataset.label,\n\t\t\t\t\t\tx: (this.options.animation) ? this.scale.xCenter : pointPosition.x,\n\t\t\t\t\t\ty: (this.options.animation) ? this.scale.yCenter : pointPosition.y,\n\t\t\t\t\t\tstrokeColor : dataset.pointStrokeColor,\n\t\t\t\t\t\tfillColor : dataset.pointColor,\n\t\t\t\t\t\thighlightFill : dataset.pointHighlightFill || dataset.pointColor,\n\t\t\t\t\t\thighlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor\n\t\t\t\t\t}));\n\t\t\t\t},this);\n\n\t\t\t},this);\n\n\t\t\tthis.render();\n\t\t},\n\t\teachPoints : function(callback){\n\t\t\thelpers.each(this.datasets,function(dataset){\n\t\t\t\thelpers.each(dataset.points,callback,this);\n\t\t\t},this);\n\t\t},\n\n\t\tgetPointsAtEvent : function(evt){\n\t\t\tvar mousePosition = helpers.getRelativePosition(evt),\n\t\t\t\tfromCenter = helpers.getAngleFromPoint({\n\t\t\t\t\tx: this.scale.xCenter,\n\t\t\t\t\ty: this.scale.yCenter\n\t\t\t\t}, mousePosition);\n\n\t\t\tvar anglePerIndex = (Math.PI * 2) /this.scale.valuesCount,\n\t\t\t\tpointIndex = Math.round((fromCenter.angle - Math.PI * 1.5) / anglePerIndex),\n\t\t\t\tactivePointsCollection = [];\n\n\t\t\t// If we're at the top, make the pointIndex 0 to get the first of the array.\n\t\t\tif (pointIndex >= this.scale.valuesCount || pointIndex < 0){\n\t\t\t\tpointIndex = 0;\n\t\t\t}\n\n\t\t\tif (fromCenter.distance <= this.scale.drawingArea){\n\t\t\t\thelpers.each(this.datasets, function(dataset){\n\t\t\t\t\tactivePointsCollection.push(dataset.points[pointIndex]);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn activePointsCollection;\n\t\t},\n\n\t\tbuildScale : function(data){\n\t\t\tthis.scale = new Chart.RadialScale({\n\t\t\t\tdisplay: this.options.showScale,\n\t\t\t\tfontStyle: this.options.scaleFontStyle,\n\t\t\t\tfontSize: this.options.scaleFontSize,\n\t\t\t\tfontFamily: this.options.scaleFontFamily,\n\t\t\t\tfontColor: this.options.scaleFontColor,\n\t\t\t\tshowLabels: this.options.scaleShowLabels,\n\t\t\t\tshowLabelBackdrop: this.options.scaleShowLabelBackdrop,\n\t\t\t\tbackdropColor: this.options.scaleBackdropColor,\n\t\t\t\tbackdropPaddingY : this.options.scaleBackdropPaddingY,\n\t\t\t\tbackdropPaddingX: this.options.scaleBackdropPaddingX,\n\t\t\t\tlineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0,\n\t\t\t\tlineColor: this.options.scaleLineColor,\n\t\t\t\tangleLineColor : this.options.angleLineColor,\n\t\t\t\tangleLineWidth : (this.options.angleShowLineOut) ? this.options.angleLineWidth : 0,\n\t\t\t\t// Point labels at the edge of each line\n\t\t\t\tpointLabelFontColor : this.options.pointLabelFontColor,\n\t\t\t\tpointLabelFontSize : this.options.pointLabelFontSize,\n\t\t\t\tpointLabelFontFamily : this.options.pointLabelFontFamily,\n\t\t\t\tpointLabelFontStyle : this.options.pointLabelFontStyle,\n\t\t\t\theight : this.chart.height,\n\t\t\t\twidth: this.chart.width,\n\t\t\t\txCenter: this.chart.width/2,\n\t\t\t\tyCenter: this.chart.height/2,\n\t\t\t\tctx : this.chart.ctx,\n\t\t\t\ttemplateString: this.options.scaleLabel,\n\t\t\t\tlabels: data.labels,\n\t\t\t\tvaluesCount: data.datasets[0].data.length\n\t\t\t});\n\n\t\t\tthis.scale.setScaleSize();\n\t\t\tthis.updateScaleRange(data.datasets);\n\t\t\tthis.scale.buildYLabels();\n\t\t},\n\t\tupdateScaleRange: function(datasets){\n\t\t\tvar valuesArray = (function(){\n\t\t\t\tvar totalDataArray = [];\n\t\t\t\thelpers.each(datasets,function(dataset){\n\t\t\t\t\tif (dataset.data){\n\t\t\t\t\t\ttotalDataArray = totalDataArray.concat(dataset.data);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\thelpers.each(dataset.points, function(point){\n\t\t\t\t\t\t\ttotalDataArray.push(point.value);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\treturn totalDataArray;\n\t\t\t})();\n\n\n\t\t\tvar scaleSizes = (this.options.scaleOverride) ?\n\t\t\t\t{\n\t\t\t\t\tsteps: this.options.scaleSteps,\n\t\t\t\t\tstepValue: this.options.scaleStepWidth,\n\t\t\t\t\tmin: this.options.scaleStartValue,\n\t\t\t\t\tmax: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)\n\t\t\t\t} :\n\t\t\t\thelpers.calculateScaleRange(\n\t\t\t\t\tvaluesArray,\n\t\t\t\t\thelpers.min([this.chart.width, this.chart.height])/2,\n\t\t\t\t\tthis.options.scaleFontSize,\n\t\t\t\t\tthis.options.scaleBeginAtZero,\n\t\t\t\t\tthis.options.scaleIntegersOnly\n\t\t\t\t);\n\n\t\t\thelpers.extend(\n\t\t\t\tthis.scale,\n\t\t\t\tscaleSizes\n\t\t\t);\n\n\t\t},\n\t\taddData : function(valuesArray,label){\n\t\t\t//Map the values array for each of the datasets\n\t\t\tthis.scale.valuesCount++;\n\t\t\thelpers.each(valuesArray,function(value,datasetIndex){\n\t\t\t\tvar pointPosition = this.scale.getPointPosition(this.scale.valuesCount, this.scale.calculateCenterOffset(value));\n\t\t\t\tthis.datasets[datasetIndex].points.push(new this.PointClass({\n\t\t\t\t\tvalue : value,\n\t\t\t\t\tlabel : label,\n\t\t\t\t\tx: pointPosition.x,\n\t\t\t\t\ty: pointPosition.y,\n\t\t\t\t\tstrokeColor : this.datasets[datasetIndex].pointStrokeColor,\n\t\t\t\t\tfillColor : this.datasets[datasetIndex].pointColor\n\t\t\t\t}));\n\t\t\t},this);\n\n\t\t\tthis.scale.labels.push(label);\n\n\t\t\tthis.reflow();\n\n\t\t\tthis.update();\n\t\t},\n\t\tremoveData : function(){\n\t\t\tthis.scale.valuesCount--;\n\t\t\tthis.scale.labels.shift();\n\t\t\thelpers.each(this.datasets,function(dataset){\n\t\t\t\tdataset.points.shift();\n\t\t\t},this);\n\t\t\tthis.reflow();\n\t\t\tthis.update();\n\t\t},\n\t\tupdate : function(){\n\t\t\tthis.eachPoints(function(point){\n\t\t\t\tpoint.save();\n\t\t\t});\n\t\t\tthis.reflow();\n\t\t\tthis.render();\n\t\t},\n\t\treflow: function(){\n\t\t\thelpers.extend(this.scale, {\n\t\t\t\twidth : this.chart.width,\n\t\t\t\theight: this.chart.height,\n\t\t\t\tsize : helpers.min([this.chart.width, this.chart.height]),\n\t\t\t\txCenter: this.chart.width/2,\n\t\t\t\tyCenter: this.chart.height/2\n\t\t\t});\n\t\t\tthis.updateScaleRange(this.datasets);\n\t\t\tthis.scale.setScaleSize();\n\t\t\tthis.scale.buildYLabels();\n\t\t},\n\t\tdraw : function(ease){\n\t\t\tvar easeDecimal = ease || 1,\n\t\t\t\tctx = this.chart.ctx;\n\t\t\tthis.clear();\n\t\t\tthis.scale.draw();\n\n\t\t\thelpers.each(this.datasets,function(dataset){\n\n\t\t\t\t//Transition each point first so that the line and point drawing isn't out of sync\n\t\t\t\thelpers.each(dataset.points,function(point,index){\n\t\t\t\t\tif (point.hasValue()){\n\t\t\t\t\t\tpoint.transition(this.scale.getPointPosition(index, this.scale.calculateCenterOffset(point.value)), easeDecimal);\n\t\t\t\t\t}\n\t\t\t\t},this);\n\n\n\n\t\t\t\t//Draw the line between all the points\n\t\t\t\tctx.lineWidth = this.options.datasetStrokeWidth;\n\t\t\t\tctx.strokeStyle = dataset.strokeColor;\n\t\t\t\tctx.beginPath();\n\t\t\t\thelpers.each(dataset.points,function(point,index){\n\t\t\t\t\tif (index === 0){\n\t\t\t\t\t\tctx.moveTo(point.x,point.y);\n\t\t\t\t\t}\n\t\t\t\t\telse{\n\t\t\t\t\t\tctx.lineTo(point.x,point.y);\n\t\t\t\t\t}\n\t\t\t\t},this);\n\t\t\t\tctx.closePath();\n\t\t\t\tctx.stroke();\n\n\t\t\t\tctx.fillStyle = dataset.fillColor;\n\t\t\t\tctx.fill();\n\n\t\t\t\t//Now draw the points over the line\n\t\t\t\t//A little inefficient double looping, but better than the line\n\t\t\t\t//lagging behind the point positions\n\t\t\t\thelpers.each(dataset.points,function(point){\n\t\t\t\t\tif (point.hasValue()){\n\t\t\t\t\t\tpoint.draw();\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t},this);\n\n\t\t}\n\n\t});\n\n\n\n\n\n}).call(this);"
  },
  {
    "path": "public/dist/js/dirDisqus.js",
    "content": "/**\n * A directive to embed a Disqus comments widget on your AngularJS page.\n *\n * For documentation, see the README.md file in this directory\n *\n * Created by Michael on 22/01/14.\n * Copyright Michael Bromley 2014\n * Available under the MIT license.\n */\nangular.module('dirDisqus', []).directive('disqus', ['$window', function($window) {\n        return {\n            restrict: 'E',\n            scope: {\n                disqus_shortname: '@disqusShortname',\n                disqus_identifier: '@disqusIdentifier',\n                disqus_title: '@disqusTitle',\n                disqus_url: '@disqusUrl',\n                disqus_category_id: '@disqusCategoryId',\n                disqus_disable_mobile: '@disqusDisableMobile',\n                readyToBind: \"@\",\n                disqusId: \"@\"\n            },\n            template: '<div id=\"{{disqusId}}\"></div>',\n            link: function(scope) {\n\n                // 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\n                // see http://help.disqus.com/customer/portal/articles/662547-why-are-the-same-comments-showing-up-on-multiple-pages-\n                if (typeof scope.disqus_identifier === 'undefined' || typeof scope.disqus_url === 'undefined') {\n                    throw \"Please ensure that the `disqus-identifier` and `disqus-url` attributes are both set.\";\n                }\n\n                scope.$watch(\"readyToBind\", function(isReady) {\n\n                    // If the directive has been called without the 'ready-to-bind' attribute, we\n                    // set the default to \"true\" so that Disqus will be loaded straight away.\n                    if ( !angular.isDefined( isReady ) ) {\n                        isReady = \"true\";\n                    }\n                    if (scope.$eval(isReady)) {\n                        // put the config variables into separate global vars so that the Disqus script can see them\n                        $window.disqus_shortname = scope.disqus_shortname;\n                        $window.disqus_identifier = scope.disqus_identifier;\n                        $window.disqus_title = scope.disqus_title;\n                        $window.disqus_url = scope.disqus_url;\n                        $window.disqus_category_id = scope.disqus_category_id;\n                        $window.disqus_disable_mobile = scope.disqus_disable_mobile;\n\n                        // get the remote Disqus script and insert it into the DOM, but only if it not already loaded (as that will cause warnings)\n                        if (!$window.DISQUS) {\n                            var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;\n                            dsq.src = '//' + scope.disqus_shortname + '.disqus.com/embed.js';\n                            (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);\n                        } else {\n                            $window.DISQUS.reset({\n                                reload: true,\n                                config: function () {\n                                    this.page.identifier = scope.disqus_identifier;\n                                    this.page.url = scope.disqus_url;\n                                    this.page.title = scope.disqus_title;\n                                }\n                            });\n                        }\n                    }\n                });\n            }\n        };\n    }]);"
  },
  {
    "path": "public/dist/js/tc-angular-chartjs.js",
    "content": "/**\n * tc-angular-chartjs - v1.0.9 - 2014-10-14\n * Copyright (c) 2014 Carl Craig <carlcraig@3c-studios.com>\n * Dual licensed with the Apache-2.0 or MIT license.\n */\n(function() {\n    \"use strict\";\n    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);\n    function TcChartjs(TcChartjsFactory) {\n        return new TcChartjsFactory();\n    }\n    TcChartjs.$inject = [ \"TcChartjsFactory\" ];\n    function TcChartjsLine(TcChartjsFactory) {\n        return new TcChartjsFactory(\"line\");\n    }\n    TcChartjsLine.$inject = [ \"TcChartjsFactory\" ];\n    function TcChartjsBar(TcChartjsFactory) {\n        return new TcChartjsFactory(\"bar\");\n    }\n    TcChartjsBar.$inject = [ \"TcChartjsFactory\" ];\n    function TcChartjsRadar(TcChartjsFactory) {\n        return new TcChartjsFactory(\"radar\");\n    }\n    TcChartjsRadar.$inject = [ \"TcChartjsFactory\" ];\n    function TcChartjsPolararea(TcChartjsFactory) {\n        return new TcChartjsFactory(\"polararea\");\n    }\n    TcChartjsPolararea.$inject = [ \"TcChartjsFactory\" ];\n    function TcChartjsPie(TcChartjsFactory) {\n        return new TcChartjsFactory(\"pie\");\n    }\n    TcChartjsPie.$inject = [ \"TcChartjsFactory\" ];\n    function TcChartjsDoughnut(TcChartjsFactory) {\n        return new TcChartjsFactory(\"doughnut\");\n    }\n    TcChartjsDoughnut.$inject = [ \"TcChartjsFactory\" ];\n    function TcChartjsFactory() {\n        return function(chartType) {\n            var directive = {\n                restrict: \"A\",\n                scope: {\n                    data: \"=chartData\",\n                    options: \"=chartOptions\",\n                    type: \"@chartType\",\n                    legend: \"=chartLegend\",\n                    chart: \"=chart\"\n                },\n                link: link\n            };\n            return directive;\n            function link($scope, $elem, $attrs) {\n                var ctx = $elem[0].getContext(\"2d\");\n                var chart = new Chart(ctx);\n                var chartObj;\n                var showLegend = false;\n                var autoLegend = false;\n                var exposeChart = false;\n                var legendElem = null;\n                for (var attr in $attrs) {\n                    if (attr === \"chartLegend\") {\n                        showLegend = true;\n                    } else if (attr === \"chart\") {\n                        exposeChart = true;\n                    } else if (attr === \"autoLegend\") {\n                        autoLegend = true;\n                    }\n                }\n                $scope.$watch(\"data\", function(value) {\n                    if (value) {\n                        if (chartObj) {\n                            chartObj.destroy();\n                        }\n                        if (chartType) {\n                            chartObj = chart[cleanChartName(chartType)]($scope.data, $scope.options);\n                        } else if ($scope.type) {\n                            chartObj = chart[cleanChartName($scope.type)]($scope.data, $scope.options);\n                        } else {\n                            throw \"Error creating chart: Chart type required.\";\n                        }\n                        if (showLegend) {\n                            $scope.legend = chartObj.generateLegend();\n                        }\n                        if (autoLegend) {\n                            if (legendElem) {\n                                legendElem.remove();\n                            }\n                            angular.element($elem[0]).after(chartObj.generateLegend());\n                            legendElem = angular.element($elem[0]).next();\n                        }\n                        if (exposeChart) {\n                            $scope.chart = chartObj;\n                        }\n                    }\n                }, true);\n            }\n            function cleanChartName(type) {\n                var typeLowerCase = type.toLowerCase();\n                switch (typeLowerCase) {\n                  case \"line\":\n                    return \"Line\";\n\n                  case \"bar\":\n                    return \"Bar\";\n\n                  case \"radar\":\n                    return \"Radar\";\n\n                  case \"polararea\":\n                    return \"PolarArea\";\n\n                  case \"pie\":\n                    return \"Pie\";\n\n                  case \"doughnut\":\n                    return \"Doughnut\";\n\n                  default:\n                    return type;\n                }\n            }\n        };\n    }\n    function TcChartjsLegend() {\n        var directive = {\n            restrict: \"A\",\n            scope: {\n                legend: \"=chartLegend\"\n            },\n            link: link\n        };\n        return directive;\n        function link($scope, $elem) {\n            $scope.$watch(\"legend\", function(value) {\n                if (value) {\n                    $elem.html(value);\n                }\n            }, true);\n        }\n    }\n})();"
  },
  {
    "path": "public/googled8153283379da1fb.html",
    "content": "google-site-verification: googled8153283379da1fb.html"
  },
  {
    "path": "public/js/app.js",
    "content": "var sortData = function(champ1, champ2, position) {\n\tchamp1 = matchupData.championList[champ1].id;\n\tchamp2 = matchupData.championList[champ2].id;\n\treturn (champ1 < champ2) ? [champ1, champ2] : [champ2, champ1];\n};\n\n\n(function(angular, matchupData) {\n\n\tvar appCore = angular.module('core', ['ui.bootstrap', 'championggTooltip']);\n\n\tappCore.run(['$templateCache', function($templateCache) {\n\t\t$templateCache.put('template/typeahead/typeahead-popup.html',\n\t\t\t'<ul class=\"dropdown-menu\" ng-show=\"isOpen()\" ng-style=\"{top: position.top+\\'px\\','  +\n\t\t\t\t'left: position.left+\\'px\\'}\" style=\"display: block;\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">' +\n\t\t\t \t\t'<li ng-repeat=\"match in matches\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\"' +\n\t\t\t\t\t'ng-click=\"selectMatch($index)\" role=\"option\" id=\"{{match.id}}\">' +\n\t            \t'<div typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>' +\n\t        '</li></ul>');\n\n\t\t$templateCache.put('menu.html', \n\t\t\t'<a href=\"/champion/{{match.model.key}}\">'+\n\t          '<div class=\"dropdown-img matchup-champion {{match.model.key}}\"></div>'+\n\t          '<span bind-html-unsafe=\"match.label | typeaheadHighlight:query\" class=\"dropdown-search\"></span>'+\n\t      \t'</a>');\n\n\t    $templateCache.put('template/tooltip/tooltip-popup.html', \n\t\t\t'<div class=\"tooltip {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">'+ \n\t\t      '<div class=\"tooltip-arrow\"></div>'+\n\t\t      '<div class=\"tooltip-inner\">{{content}}</div>'+\n\t\t    '</div>');\n\t}]);\n\n\n\tappCore.controller('searchCtrl', ['$scope', function($scope) {\n\t\t$scope.selected = undefined;\n\t\t$scope.championMenu = [];\n\n\t\tfor (var prop in matchupData.championList) {\n\t\t\tif (Object.hasOwnProperty.call(matchupData.championList, prop)) {\n\t\t\t\t$scope.championMenu.push({\n\t\t\t\t\tkey: matchupData.championList[prop].key,\n\t\t\t\t\tname: matchupData.championList[prop].name\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t$scope.getPage = function(){\n\t\t\twindow.location.assign('/champion/'+ $scope.selected);\n\t\t};\n\n\t\t$scope.determineSend = function(keyCode) {\n\t\t\tif (keyCode === 13) {\n\t\t\t\tvar name = $scope.selected.key || $scope.selected;\n\t\t\t\twindow.location.assign('/champion/' + name);\n\t\t\t}\n\t\t};\n\t}]);\n\n\tappCore.factory('localStorageAccess', ['$window', '$rootScope', function($window, $rootScope) {\n\t\tangular.element($window).on('storage', function(event) {\n\t\t\tconsole.log('local storage changed');\n\t\t\tif (event.key === 'matchupRating' || event.key === 'chosenSort') {\n\t\t\t\t$rootScope.$apply();\n\t\t\t}\n\t\t});\n\n\t\tvar service = {\n\n\t\t\tsave: function(key, data) {\n\t\t\t\tlocalStorage[key] = angular.toJson(data);\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\tretrieve: function(key) {\n\t\t\t\treturn angular.fromJson(localStorage[key]);\n\t\t\t}\n\t\t};\n\n\t\treturn service;\n\t}]);\n\n\n\tappCore.filter('startsWith', function() {\n\t\tfunction strStartsWith(str, prefix) {\n\t\t\tvar wordSplit = str.split(' ');\n\n\t\t\tfunction determineString(word) {\n\t\t\t\treturn ((word + \"\").toLowerCase()).indexOf(prefix.toLowerCase()) === 0 || ((str + \"\").toLowerCase()).indexOf(prefix.toLowerCase()) === 0;\n\t\t\t}\n\n\t\t\tfor (var i = 0; i < wordSplit.length; i++) {\n\t\t\t\tif (determineString(wordSplit[i])) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\n\t\treturn function(items, amount) {\n\t\t\tvar filtered = [];\n\n\t\t\tangular.forEach(items, function(item) {\n\t\t\t\tif (strStartsWith(item.name || item.title, amount)) {\n\t\t\t\t\tfiltered.push(item);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn filtered;\n\t\t};\n\t});\n\n})(angular, matchupData);"
  },
  {
    "path": "public/js/champion_data.js",
    "content": "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\"}};"
  },
  {
    "path": "public/js/champion_page.js",
    "content": "(function(angular, matchupData, sortData, radarChartSettings, lineChartSettings) {\n\n    var appChampion = angular.module('championPage', ['tc.chartjs', 'dirDisqus', 'core', 'ui.bootstrap']);\n\n    appChampion.factory('processNewData', function() {\n        return function(data, type) {\n            if (data instanceof Array) {\n                for (var i = 0; i < data.length; i++) {\n                    data[i].name = matchupData.championList[data[i].key].name;\n                }\n            }\n            return data;\n        };\n    });\n\n    appChampion.controller('generalChampion', ['$scope', function($scope) {\n        $scope.champion = matchupData.champion;\n        $scope.Math = window.Math;\n        $scope.currentDiscussion = '';\n        $scope.currentSide = '';\n    }]);\n\n    appChampion.controller('championData', ['$scope', function($scope) {\n        $scope.generalData = matchupData.championData.general;\n        $scope.overallPlacement = matchupData.championData.overallPlacement;\n        $scope.generalRole = matchupData.generalRole;\n        $scope.championMatrix = matchupData.championData.championMatrix;\n        $scope.gameLengthData = matchupData.championData.gameLength;\n        $scope.experienceRate = matchupData.championData.experienceRate;\n        $scope.experienceSample = matchupData.championData.experienceSample;\n        $scope.patchWinData = matchupData.championData.patchWin;\n        $scope.patchPlayData = matchupData.championData.patchPlay;\n        $scope.summoners = matchupData.championData.summoners;\n        $scope.items = matchupData.championData.items;\n\n        var createArray = function(total) {\n            var arr = [];\n            for (var i = 0; i < total; i++) {\n                arr.push(100);\n            }\n            return arr;\n        };\n        // First graph\n        $scope.overallComparison = {\n            data: {\n                labels: $scope.generalRole.matrixLabels,\n                datasets: [{\n                    label: $scope.champion.name,\n                    fillColor: \"rgba(137,245,162,0.7)\",\n                    strokeColor: \"#89f5a2\",\n                    pointColor: \"#89f5a2\",\n                    pointStrokeColor: \"#fff\",\n                    pointHighlightFill: \"#fff\",\n                    pointHighlightStroke: \"#89f5a2\",\n                    data: $scope.championMatrix\n                }, {\n                    label: \"Average of \" + $scope.champion.roleTitle + \" Champs\",\n                    fillColor: \"rgba(220,220,220,0.3)\",\n                    strokeColor: \"rgba(220,220,220,1)\",\n                    pointColor: \"rgba(220,220,220,1)\",\n                    pointStrokeColor: \"#fff\",\n                    pointHighlightFill: \"#fff\",\n                    pointHighlightStroke: \"rgba(220,220,220,1)\",\n                    data: createArray($scope.generalRole.matrixLabels.length)\n\n                }\n\n                ]\n            },\n            settings: radarChartSettings\n\n        };\n\n        $scope.experienceRate = {\n            data: {\n                labels: [\"1-5\", \"5-15\", \"15-50\", \"50-125\", \"125+\"],\n                datasets: [{\n                    label: $scope.champion.name + \" Experience\",\n                    fillColor: \"rgba(137,245,162,0.6)\",\n                    strokeColor: \"#89f5a2\",\n                    pointColor: \"#89f5a2\",\n                    pointStrokeColor: \"#fff\",\n                    pointHighlightFill: \"#fff\",\n                    pointHighlightStroke: \"#89f5a2\",\n                    data: $scope.experienceRate\n                }, {\n                    label: $scope.champion.name + \" Overall\",\n                    fillColor: \"rgba(220,220,220,0.3)\",\n                    strokeColor: \"rgba(220,220,220,1)\",\n                    pointColor: \"rgba(220,220,220,1)\",\n                    pointStrokeColor: \"#fff\",\n                    pointHighlightFill: \"#fff\",\n                    pointHighlightStroke: \"rgba(220,220,220,1)\",\n                    data: [$scope.generalData[0].val, $scope.generalData[0].val, $scope.generalData[0].val, $scope.generalData[0].val, $scope.generalData[0].val]\n                }]\n            },\n            settings: lineChartSettings\n        };\n\n        $scope.experienceDistribution = {\n            data: {\n                datasets: [{\n                    value: $scope.experienceSample[0],\n                    color:'#65e4f5',\n                    highlight: '#55ebff',\n                    label: '1-5'\n                },{\n                    value: $scope.experienceSample[1],\n                    color:'#88f4a1',\n                    highlight: '#81ff9e',\n                    label: '5-15'\n                }, {\n                    value: $scope.experienceSample[2],\n                    color:'#fff06d',\n                    highlight: '#ffed56',\n                    label: '15-50'\n                },\n                {\n                    value: $scope.experienceSample[3],\n                    color: '#ffa94d',\n                    highlight: '#ff9e37',\n                    label: '50-125'\n                },\n                {\n                    value: $scope.experienceSample[4],\n                    color: '#ff5353',\n                    highlight: '#ff4242',\n                    label: '125+'\n                }]\n            },\n            settings: pieChartSettings\n        };\n\n        $scope.gameLength = {\n            data: {\n                labels: [\"0-25\", \"25-30\", \"30-35\", \"35-40\", \"40+\"],\n                datasets: [{\n                    label: $scope.champion.name,\n                    fillColor: \"rgba(137,245,162,0.6)\",\n                    strokeColor: \"#89f5a2\",\n                    pointColor: \"#89f5a2\",\n                    pointStrokeColor: \"#fff\",\n                    pointHighlightFill: \"#fff\",\n                    pointHighlightStroke: \"#89f5a2\",\n                    data: $scope.gameLengthData\n                }, {\n                    label: \"Average of \" + $scope.champion.roleTitle + \" Champs\",\n                    fillColor: \"rgba(220,220,220,0.3)\",\n                    strokeColor: \"rgba(220,220,220,1)\",\n                    pointColor: \"rgba(220,220,220,1)\",\n                    pointStrokeColor: \"#fff\",\n                    pointHighlightFill: \"#fff\",\n                    pointHighlightStroke: \"rgba(220,220,220,1)\",\n                    data: [50, 50, 50, 50, 50]\n                }]\n            },\n            settings: lineChartSettings\n        };\n\n        $scope.patchRate = {\n            data: {\n                labels: matchupData.patchHistory,\n                datasets: [{\n                    label: $scope.champion.name,\n                    fillColor: \"rgba(137,245,162,0.6)\",\n                    strokeColor: \"#89f5a2\",\n                    pointColor: \"#89f5a2\",\n                    pointStrokeColor: \"#fff\",\n                    pointHighlightFill: \"#fff\",\n                    pointHighlightStroke: \"#89f5a2\",\n                    data: $scope.patchWinData\n                }, {\n                    label: \"Average of \" + $scope.champion.roleTitle + \" Champs\",\n                    fillColor: \"rgba(220,220,220,0.3)\",\n                    strokeColor: \"rgba(220,220,220,1)\",\n                    pointColor: \"rgba(220,220,220,1)\",\n                    pointStrokeColor: \"#fff\",\n                    pointHighlightFill: \"#fff\",\n                    pointHighlightStroke: \"rgba(220,220,220,1)\",\n                    data: [50, 50, 50, 50, 50]\n                }]\n            },\n            settings: lineChartSettings\n        };\n\n        $scope.patchPlay = {\n            data: {\n                labels: matchupData.patchHistory,\n                datasets: [{\n                    label: $scope.champion.name,\n                    fillColor: \"rgba(137,245,162,0.6)\",\n                    strokeColor: \"#89f5a2\",\n                    pointColor: \"#89f5a2\",\n                    pointStrokeColor: \"#fff\",\n                    pointHighlightFill: \"#fff\",\n                    pointHighlightStroke: \"#89f5a2\",\n                    data: $scope.patchPlayData\n                }, {\n                    label: \"Average of \" + $scope.champion.roleTitle + \" Champs\",\n                    fillColor: \"rgba(220,220,220,0.3)\",\n                    strokeColor: \"rgba(220,220,220,1)\",\n                    pointColor: \"rgba(220,220,220,1)\",\n                    pointStrokeColor: \"#fff\",\n                    pointHighlightFill: \"#fff\",\n                    pointHighlightStroke: \"rgba(220,220,220,1)\",\n                    data: $scope.generalRole.patchPlay\n                }]\n            },\n            settings: lineChartSettings\n        };\n\n    }]);\n\n    appChampion.directive('filters', function() {\n        return {\n            restrict: \"E\",\n            templateUrl: \"filters.html\",\n            scope: true,\n            link: function(scope, elm, attrs) {\n                scope.matchupType = attrs.matchupType;\n            }\n        };\n    });\n\n    appChampion.directive('matchups', function() {\n        return {\n            restrict: \"E\",\n            templateUrl: \"matchups.html\",\n            scope: true,\n            link: function(scope, elm, attrs) {\n                scope.order = attrs.order; //Get it from attributes\n                scope.matchupType = attrs.matchupType;\n            }\n        };\n    });\n\n    appChampion.service('anchorSmoothScroll', function() {\n\n        this.scrollTo = function(eID) {\n\n            // This scrolling function \n            // is from http://www.itnewb.com/tutorial/Creating-the-Smooth-Scroll-Effect-with-JavaScript\n\n            var startY = currentYPosition();\n            var stopY = elmYPosition(eID) - 65; //to include stat area\n            var distance = stopY > startY ? stopY - startY : startY - stopY;\n            if (distance < 100) {\n                scrollTo(0, stopY);\n                return;\n            }\n            var speed = Math.round(distance / 100);\n            if (speed >= 20) speed = 20;\n            var step = Math.round(distance / 25);\n            var leapY = stopY > startY ? startY + step : startY - step;\n            var timer = 0;\n            if (stopY > startY) {\n                for (var i = startY; i < stopY; i += step) {\n                    setTimeout(\"window.scrollTo(0, \" + leapY + \")\", timer * speed);\n                    leapY += step;\n                    if (leapY > stopY) leapY = stopY;\n                    timer++;\n                }\n                return;\n            }\n            for (var d = startY; d > stopY; d -= step) {\n                setTimeout(\"window.scrollTo(0, \" + leapY + \")\", timer * speed);\n                leapY -= step;\n                if (leapY < stopY) leapY = stopY;\n                timer++;\n            }\n\n            function currentYPosition() {\n                console.log('wut, y are i running');\n                // Firefox, Chrome, Opera, Safari\n                if (self.pageYOffset) return self.pageYOffset;\n                // Internet Explorer 6 - standards mode\n                if (document.documentElement && document.documentElement.scrollTop)\n                    return document.documentElement.scrollTop;\n                // Internet Explorer 6, 7 and 8\n                if (document.body.scrollTop) return document.body.scrollTop;\n                return 0;\n            }\n\n            function elmYPosition(eID) {\n                var elm = document.getElementById(eID);\n                var y = elm.offsetTop;\n                var node = elm;\n                while (node.offsetParent && node.offsetParent != document.body) {\n                    node = node.offsetParent;\n                    y += node.offsetTop;\n                }\n                return y;\n            }\n\n        };\n\n    });\n\n    appChampion.controller('matchupData', ['$scope', '$http', 'anchorSmoothScroll', 'processNewData', 'localStorageAccess', function($scope, $http, anchorSmoothScroll, processNewData, localStorageAccess) {\n\n        $scope.allMatchups = {\n            matchups: processNewData(matchupData.championData.matchups, 'matchups'),\n            adcsupport: processNewData(matchupData.championData.adcsupport, 'adcsupport'),\n            synergy: processNewData(matchupData.championData.synergy, 'synergy')\n        };\n\n        $scope.filtered = {\n\n        };\n\n        $scope.search = {\n            matchups: {\n                name: ''\n            },\n            adcsupport: {\n                name: ''\n            },\n            synergy: {\n                name: ''\n            }\n        };\n\n        $scope.minGames = localStorageAccess.retrieve('minGames') || 100;\n\n        $scope.matchAmount = function(item) {\n            return item.games >= $scope.minGames;\n        };\n\n        //default sort\n        var previousSort = localStorageAccess.retrieve('chosenSort');\n        $scope.sortExpression = {\n            sortBy: (previousSort === 'statScore' || previousSort === 'winRate') ? previousSort : 'statScore'\n        };\n\n        $scope.generateId = function(matchupType, matchupKey) {\n            var arr = sortData(matchupKey, $scope.champion.key);\n            return (matchupType === 'matchups') ? arr[0] + 'v' + arr[1] + $scope.champion.role : arr[0] + 'v' + arr[1] + (matchupType.toUpperCase());\n        };\n\n        $scope.matchupDiscussion = function(matchupType, matchupKey, funcType, matchupSide) {\n            if (funcType === 'toggle') {\n                $scope.currentSide = matchupSide;\n                setTimeout(function() {\n                    anchorSmoothScroll.scrollTo('disqus_thread');\n                }, 100);\n            }\n\n            if ($scope.currentDiscussion === $scope.generateId(matchupType, matchupKey) && ($scope.currentSide === matchupSide)) {\n\n                if (funcType === 'id') {\n\n                    return 'disqus_thread';\n\n                } else if (funcType === 'toggle') {\n                    $scope.currentDiscussion = '';\n                } else if (funcType === \"bind\") {\n                    return true;\n                }\n            } else {\n                if (funcType === 'id') {\n                    return ' ';\n                } else if (funcType === 'toggle') {\n                    $scope.currentDiscussion = $scope.generateId(matchupType, matchupKey);\n                } else if (funcType === \"bind\") {\n                    return false;\n                }\n            }\n        };\n\n        $scope.saveMinGames = function() {\n            localStorageAccess.save('minGames', $scope.minGames);\n        };\n\n        $scope.saveSort = function(type) {\n            $scope.sortExpression.sortBy = type;\n            localStorageAccess.save('chosenSort', $scope.sortExpression.sortBy);\n        };\n\n        //Show more buttons\n        var numberShown = 5;\n        var timesShown = {\n            \"matchups\": 1,\n            \"matchups-\": 1,\n            \"adcsupport\": 1,\n            \"adcsupport-\": 1,\n            \"synergy\": 1,\n            \"synergy-\": 1\n        };\n\n        $scope.matchupsCount = function(matchType) {\n            return $scope.filtered[matchType].length;\n        };\n\n        $scope.itemsLimit = function(matchType, order) {\n            return numberShown * timesShown[matchType + order];\n        };\n\n        $scope.hasMoreItemsToShow = function(matchType, order) {\n            return timesShown[matchType + order] < ($scope.filtered[matchType].length / numberShown) && $scope.search[matchType].name.length === 0;\n        };\n\n        $scope.showMoreItems = function(matchType, order) {\n            timesShown[matchType + order] ++;\n        };\n\n        // show stats\n        // First graph\n\n    }]);\n\n    appChampion.controller('specificMatchup', ['$scope', '$http', function($scope, $http) {\n        $scope.showMatchups = false;\n        $scope.matchupRetrieved = false;\n        $scope.showDiscussion = false;\n        $scope.specificMatchupCharts = {\n            championMatrix: {\n                labels: [],\n                champ1: [],\n                champ2: []\n            },\n            goldLength: {\n                champ1: [],\n                champ2: []\n            }\n        };\n\n        $scope.matchupId = matchupData.championList[$scope.matchup.key].id;\n        $scope.championId = matchupData.championList[$scope.champion.key].id;\n\n        $scope.generateChampUrl = function(matchupType) {\n            if (matchupType === 'matchups') {\n                return $scope.champion.roleTitle;\n            }\n            return ($scope.champion.roleTitle === 'Support') ? 'ADC' : 'Support';\n        };\n\n\n        $scope.toggleMatchup = function(matchupType, key) {\n            $scope.showDiscussion = false;\n            $scope.showMatchups = !$scope.showMatchups;\n            $scope.getSpecificMatchup(matchupType, key);\n        };\n\n        $scope.generateMatchupUrl = function(key, matchupType) {\n            var championKey = $scope.champion.key;\n            var urlStart = '/matchup/';\n            var urlMiddle = (matchupData.championList[key].id < matchupData.championList[championKey].id) ? key + '/' + championKey + '/' : championKey + '/' + key + '/';\n            var urlEnd = (matchupType === 'matchups') ? $scope.champion.roleTitle : matchupType;\n\n            return urlStart + urlMiddle + urlEnd;\n        };\n\n\n        $scope.champtype = function(key, first) {\n\n            if ($scope.matchupId > $scope.championId && !first) {\n                return 'champ2';\n            } else if ($scope.matchupId < $scope.championId && !first) {\n                return 'champ1';\n            } else if ($scope.matchupId > $scope.championId && first) {\n                return 'champ1';\n            }\n            return 'champ2';\n        };\n\n        $scope.getSpecificMatchup = function(matchupType, matchupKey) {\n            var idOrder = sortData(matchupKey, $scope.champion.key);\n            var role = (matchupType === 'matchups') ? $scope.champion.role : matchupType.toUpperCase();\n\n\n            // add cacheing\n            $http.get('/matchupJson/' + idOrder[0] + '/' + idOrder[1] + '/' + role).success(function(data) {\n                console.log('retrieved');\n                for (var i = 0; i < $scope.allMatchups[matchupType].length; i++) {\n                    if ($scope.allMatchups[matchupType][i].key === matchupKey) {\n                        $scope.allMatchups[matchupType][i].specificMatchup = data;\n\n                        //manually update data for charts - sucks I know..\n                        for (var t = 0; t < data.championMatrix.labels.length; t++) {\n                            $scope.specificMatchupCharts.championMatrix.labels[t] = data.championMatrix.labels[t];\n                            $scope.specificMatchupCharts.championMatrix.champ1[t] = data.championMatrix.champ1[t];\n                            $scope.specificMatchupCharts.championMatrix.champ2[t] = data.championMatrix.champ2[t];\n                        }\n\n                        for (var y = 0; y < data.goldLength.champ1.length; y++) {\n                            $scope.specificMatchupCharts.goldLength.champ1[y] = data.goldLength.champ1[y];\n                            $scope.specificMatchupCharts.goldLength.champ2[y] = data.goldLength.champ2[y];\n                        }\n\n\n                        $scope.matchupRetrieved = true;\n                    }\n                }\n            });\n        };\n\n        var determineChamp = function(index) {\n            if ($scope.championId !== 0) {\n                if (index === 0) {\n                    return ($scope.championId < $scope.matchupId) ? 'champ1' : 'champ2';\n                } else if (index === 1) {\n                    return ($scope.championId < $scope.matchupId) ? 'champ2' : 'champ1';\n                }\n            }\n        };\n\n        $scope.champComparison = {\n            data: {\n                labels: $scope.specificMatchupCharts.championMatrix.labels,\n                datasets: [\n\n                    {\n                        label: 'champ1',\n                        fillColor: \"rgba(255, 83, 83, 0.48)\",\n                        strokeColor: \"rgba(255, 83, 83, 0.99)\",\n                        pointColor: \"rgba(255, 83, 83, 0.99)\",\n                        pointStrokeColor: \"#fff\",\n                        pointHighlightFill: \"#fff\",\n                        pointHighlightStroke: \"rgba(255, 83, 83, 0.99)\",\n                        data: $scope.specificMatchupCharts.championMatrix[determineChamp(1)]\n                    }, {\n                        label: 'champ2',\n                        fillColor: \"rgba(101, 228, 245, 0.48)\",\n                        strokeColor: \"rgba(101, 228, 245, 1)\",\n                        pointColor: \"rgba(101, 228, 245, 1)\",\n                        pointStrokeColor: \"#fff\",\n                        pointHighlightFill: \"#fff\",\n                        pointHighlightStroke: \"rgba(101, 228, 245, 1)\",\n                        data: $scope.specificMatchupCharts.championMatrix[determineChamp(0)]\n                    }\n\n                ]\n            },\n            settings: radarChartSettings\n        };\n\n        $scope.goldIncome = {\n            data: {\n                labels: [\"0-10\", \"10-20\", \"20-30\", \"30+\"],\n                datasets: [{\n                    label: \"champ2\",\n                    fillColor: \"rgba(255, 83, 83, 0.48)\",\n                    strokeColor: \"rgba(255, 83, 83, 0.99)\",\n                    pointColor: \"rgba(255, 83, 83, 0.99)\",\n                    pointStrokeColor: \"#fff\",\n                    pointHighlightFill: \"#fff\",\n                    pointHighlightStroke: \"rgba(255, 83, 83, 0.99)\",\n                    data: $scope.specificMatchupCharts.goldLength[determineChamp(1)]\n                }, {\n                    label: \"champ1\",\n                    fillColor: \"rgba(101, 228, 245, 0.48)\",\n                    strokeColor: \"rgba(101, 228, 245, 1)\",\n                    pointColor: \"rgba(101, 228, 245, 1)\",\n                    pointStrokeColor: \"#fff\",\n                    pointHighlightFill: \"#fff\",\n                    pointHighlightStroke: \"rgba(101, 228, 245, 1)\",\n                    data: $scope.specificMatchupCharts.goldLength[determineChamp(0)]\n                }]\n            },\n            settings: lineChartSettings\n        };\n\n    }]);\n\n    var decodeEntities = (function() {\n        // this prevents any overhead from creating the object each time\n        var element = document.createElement('div');\n\n        function decodeHTMLEntities(str) {\n            if (str && typeof str === 'string') {\n                // strip script/html tags\n                str = str.replace(/<script[^>]*>([\\S\\s]*?)<\\/script>/gmi, '');\n                str = str.replace(/<\\/?\\w(?:[^\"'>]|\"[^\"]*\"|'[^']*')*>/gmi, '');\n                element.innerHTML = str;\n                str = element.textContent;\n                element.textContent = '';\n            }\n            return str;\n        }\n\n        return decodeHTMLEntities;\n    })();\n\n    appChampion.controller('reddit', ['$scope', '$http', '$timeout', function($scope, $http, $timeout) {\n        $scope.redditData = [];\n        $scope.enoughUpvotes = function(thread) {\n            return thread.data.score >= 2;\n        };\n        $timeout(function() {\n            $http.get('http://api.reddit.com/r/summonerschool/search?q=' + $scope.champion.name + '&sort=relevance&restrict_sr=on&t=month').success(function(data) {\n                $scope.redditData = data.data.children;\n                for (var i = 0; i < $scope.redditData.length; i++) {\n                    $scope.redditData[i].data.selftext_html = decodeEntities($scope.redditData[i].data.selftext_html);\n                }\n            });\n        }, 1500);\n    }]);\n\n    appChampion.controller('redditThread', ['$scope', function($scope) {\n        $scope.summaryVisible = false;\n        $scope.toggleSummary = function() {\n            $scope.summaryVisible = ($scope.summaryVisible) ? false : true;\n        };\n    }]);\n\n})(angular, matchupData, sortData, radarChartSettings, lineChartSettings);"
  },
  {
    "path": "public/js/championgg_tooltip.js",
    "content": "(function(angular, matchupData) {\n\n\tvar champggTooltip = angular.module('championggTooltip', []);\n\n\t\n\tchampggTooltip.filter('to_trusted', ['$sce', function($sce) {\n\t\treturn function(text) {\n\t\t\treturn $sce.trustAsHtml(text);\n\t\t};\n\t}]);\n\n\t\n\tchampggTooltip.factory('ToolTip', ['$window', function($window){\n\t\treturn {\n\t\t\tdetermineDirection: function(position, tipDetails, width){\n\t\t\t\tvar ref = position;\n\t\t\t\tvar newWidth = 240 || width;\n\t\t\t\tvar cssObj = {\n\t\t\t\t\t\"opacity\":1,\n\t\t\t\t\t\"top\" : ref.bottom - ref.height - tipDetails.height + $window.scrollY + \"px\",\n\t\t\t\t\t\"left\" : ref.right - (newWidth - ((newWidth - ref.width)/2)) + $window.scrollX + \"px\",\n\t\t\t\t\t\"height\" : \"auto\",\n\t\t\t\t\t\"width\" : newWidth + \"px\"\n\t\t\t\t};\n\t\t\t\treturn cssObj;\n\t\t\t},\n\t\t\tgetTemplate: function(tElement, tAttrs){\n\t\t\t\tvar beginning = \"<div class='primary-content'>\";\n\t\t\t\tvar obj = {\n\t\t\t\t\tmasteries: \"<div ng-if=\\\"tooltipContent\\\"><h4>{{tooltipContent.name}}</h4>\"+\n\t\t\t\t\t\t\t\t\"<div class=\\\"description\\\" ng-repeat=\\\"tip in tooltipContent.description\\\">\"+ \n\t\t\t\t\t\t\t\t\"<div ng-class=\\\"{'highlight' : $index == apiSecondaryId}\\\" ng-bind-html=\\\"tip | to_trusted\\\">\"+ \n\t\t\t\t\t\t\t\t\"</div></div></div>\"\n\t\t\t\t};\n\t\t\t\tvar end = \"<div ng-if=\\\"!tooltipContent\\\">Loading...</div>\"+\n\t\t\t\t\t\t\"</div><div class='arrow-down'></div>\";\n\t\t\t\treturn beginning + obj[tAttrs.apiType] + end;\n\t\t\t}\n\t\t};\n\t}]);\n\t\n\n\tchampggTooltip.directive('toolContainer', ['ToolTip', '$timeout', '$http', function(ToolTip, $timeout, $http){\n\t\treturn {\n\t\t\trestrict: \"E\", \n\t\t\ttemplate:  ToolTip.getTemplate,\n\t\t\tscope:{\n\t\t\t\tposition:\"=\",\n\t\t\t\tapiType: \"@\",\n\t\t\t\tapiPrimaryId: \"@\",\n\t\t\t\tapiSecondaryId: \"@\"\n\t\t\t},\n\t\t\tlink: function(scope, elem, attr){\n\t\t\t\telem.addClass('tooltip-hover');\n\n\t\t\t\tvar adjustCss = function(){     \n\t\t\t\t\tscope.tipDetails = elem[0].getBoundingClientRect();\n\t\t\t\t\telem.css(ToolTip.determineDirection(scope.position, scope.tipDetails));\n\t\t\t\t};\n\t\t\t\t\n\t\t\t\t$timeout(function(){\n\t\t\t\t\tadjustCss();\n\t\t\t\t}, 1);\n\n\t\t\t\tscope.tooltipContent = false;\n\t\t\t\tscope.$watch('tooltipContent', function(){\n\t\t\t\t\t$timeout(function(){\n\t\t\t\t\t\tadjustCss();\n\t\t\t\t\t}, 1);\n\t\t\t\t}, true);\n\n\t\t\t\t$http.get('/static/'+scope.apiType+'/'+scope.apiPrimaryId).success(function(data, status, headers, config){\n\t\t\t\t\tscope.tooltipContent = data; \n\t\t\t\t}).error(function(data,status,headers,config){\n\t\t\t\t\tconsole.log(data);\n\t\t\t\t});\n\t\t\t\t \n\t\t\t}\n\t\t};\n\t}]);\n\n\tchampggTooltip.directive('championTip', ['$compile', '$timeout', function($compile, $timeout){\n\t\treturn {\n\t\t\trestrict: \"A\", \n\t\t\tscope:{\n\t\t\t\tapiType: \"@\",\n\t\t\t\tapiPrimaryId: \"@\",\n\t\t\t\tapiSecondaryId: \"@\"\n\t\t\t},\n\t\t\tcontroller: ['$scope', function($scope){\n\t\t\t\t$scope.positionInfo = {};\n\t\t\t}],\n\t\t\tlink: function(scope, elem, attr){\n\t\t\t\tvar elemCopy = elem;\n\t\t\t\tvar timed = [];\n\t\t\t\tmouseenterFunction = function(){\n\t\t\t\t\ttimed[timed.length] = $timeout(function(){\n\t\t\t\t\t\tscope.positionInfo = this.getBoundingClientRect();                    \n\t\t\t\t\t\tvar currentToolTip = \"<tool-container api-type=\"+scope.apiType+\" api-primary-id=\"+scope.apiPrimaryId+\" api-secondary-id=\"+(scope.apiSecondaryId || 'none')+\" position='positionInfo' class='currentTooltip'></tool-container>\";\n\t\t\t\t\t\tvar tool = $compile(currentToolTip)(scope);\n\t\t\t\t\t\tangular.element(document.getElementsByTagName('body')[0]).prepend(tool);\n\t\t\t\t\t}.bind(this), 50);\n\t\t\t\t};\n\n\t\t\t\tmouseoutFunction = function(){\n\t\t\t\t\tfor(var y = 0; y < timed.length; y++){\n\t\t\t\t\t\t$timeout.cancel(timed[y]);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tvar ref = angular.element(document.getElementsByClassName('currentTooltip'));\n\t\t\t\t\tfor(var i = 0; i < ref.length; i++){\n\t\t\t\t\t\tref[i].remove();\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\telem.bind('mouseover', mouseenterFunction);\n\t\t\t\telem.bind('mouseleave', mouseoutFunction);\n\n\t\t\t}\n\t\t};\n\t}]);\n\n})(angular, matchupData);"
  },
  {
    "path": "public/js/chart_options.js",
    "content": "var radarChartSettings = {\n    responsive: true,\n    //Boolean - If we show the scale above the chart data     \n    scaleOverlay: false,\n\n    tooltipFillColor: \"rgba(0,0,0,0)\",\n    showTooltips: true,\n    multiTooltipTemplate: \"<%= value %>\",\n    customTooltips: function(tooltip) {\n        var id, row;\n        if (tooltip && tooltip.title) {\n            if (!this.chart.highlighted) {\n                this.chart.highlighted = [];\n            } else {\n                this.chart.highlighted.forEach(function(e) {\n                    e.className = e.className.replace(' highlighted', '');\n                });\n            }\n            id = 'statistics-' + tooltip.title.replace(' ', '-').toLowerCase() + '-row';\n            row = document.getElementById(id);\n            row.className += ' highlighted';\n\n            if (this.chart.highlighted.indexOf(row) === -1) this.chart.highlighted.push(row);\n        }\n    },\n\n    //Boolean - Whether to show lines for each scale point\n    scaleShowLine: true,\n\n    //String - Colour of the scale line \n    scaleLineColor: \"#333\",\n\n    //Number - Pixel width of the scale line  \n    scaleLineWidth: 1,\n\n    //Boolean - Whether to show labels on the scale \n    scaleShowLabels: false,\n\n    //Interpolated JS string - can access value\n    scaleLabel: \"<%=value%>\",\n\n    //String - Scale label font declaration for the scale label\n    scaleFontFamily: \"'Arial'\",\n\n    //Number - Scale label font size in pixels  \n    scaleFontSize: 12,\n\n    //String - Scale label font weight style  \n    scaleFontStyle: \"normal\",\n\n    //String - Scale label font colour  \n    scaleFontColor: \"#E0E0E0\",\n\n    //Boolean - Show a backdrop to the scale label\n    scaleShowLabelBackdrop: true,\n\n    //String - The colour of the label backdrop \n    scaleBackdropColor: \"rgba(255,255,255,0.75)\",\n\n    //Number - The backdrop padding above & below the label in pixels\n    scaleBackdropPaddingY: 2,\n\n    //Number - The backdrop padding to the side of the label in pixels  \n    scaleBackdropPaddingX: 2,\n\n    //Boolean - Whether we show the angle lines out of the radar\n    angleShowLineOut: true,\n\n    //String - Colour of the angle line\n    angleLineColor: \"rgba(255,255,255,0.3)\",\n\n    //Number - Pixel width of the angle line\n    angleLineWidth: 1,\n\n    //String - Point label font declaration\n    pointLabelFontFamily: \"'Arial'\",\n\n    //String - Point label font weight\n    pointLabelFontStyle: \"normal\",\n\n    //Number - Point label font size in pixels  \n    pointLabelFontSize: 12,\n\n    //String - Point label font colour  \n    pointLabelFontColor: \"#E0E0E0\",\n\n    //Boolean - Whether to show a dot for each point\n    pointDot: true,\n\n    //Number - Radius of each point dot in pixels\n    pointDotRadius: 3,\n\n    //Number - Pixel width of point dot stroke\n    pointDotStrokeWidth: 1,\n\n    //Boolean - Whether to show a stroke for datasets\n    datasetStroke: true,\n\n    //Number - Pixel width of dataset stroke\n    datasetStrokeWidth: 1,\n\n    //Boolean - Whether to fill the dataset with a colour\n    datasetFill: true,\n\n    //Boolean - Whether to animate the chart\n    animation: false,\n\n    //Number - Number of animation steps\n    animationSteps: 60,\n\n    //String - Animation easing effect\n    animationEasing: \"easeOutQuart\",\n    legendTemplate: \"<ul class=\\\"<%=name.toLowerCase()%>-legend\\\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\\\"background-color:<%=datasets[i].pointColor%>\\\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>\"\n};\n\nvar lineChartSettings = {\n    animation: false,\n    responsive: true,\n    //Boolean - If we show the scale above the chart data     \n    scaleOverlay: false,\n\n    //Boolean - Whether to show lines for each scale point\n    scaleShowLine: true,\n\n    //String - Colour of the scale line \n    scaleLineColor: \"#333\",\n\n    //Number - Pixel width of the scale line  \n    scaleLineWidth: 1,\n\n    //Boolean - Whether to show labels on the scale \n    //scaleShowLabels : false,\n\n    //Interpolated JS string - can access value\n    scaleLabel: \"<%=value%>\",\n\n    //String - Scale label font declaration for the scale label\n    scaleFontFamily: \"'Arial'\",\n\n    //Number - Scale label font size in pixels  \n    scaleFontSize: 12,\n\n    //String - Scale label font weight style  \n    scaleFontStyle: \"normal\",\n    //String - Scale label font colour  \n    scaleFontColor: \"rgb(179, 179, 179)\",\n    legendTemplate: '<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].pointColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>'\n\n};\n\nvar pieChartSettings =  {\n\n      // Sets the chart to be responsive\n      responsive: true,\n\n      //Boolean - Whether we should show a stroke on each segment\n      segmentShowStroke : true,\n\n      //String - The colour of each segment stroke\n      segmentStrokeColor : 'rgb(30, 35, 37)',\n\n      //Number - The width of each segment stroke\n      segmentStrokeWidth : 3,\n\n      //Number - The percentage of the chart that we cut out of the middle\n      percentageInnerCutout : 0, // This is 0 for Pie charts\n\n      //Boolean - Whether we animate the rotation of the Doughnut\n      animateRotate : false,\n\n      //Boolean - Whether we animate scaling the Doughnut from the centre\n      animateScale : false,\n\n      tooltipTemplate: \"<%if (label){%><%=label%> Games: <%}%><%= value %>%\",\n      //String - A legend template\n      legendTemplate : '<ul class=\"pie-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>'\n\n    };\n"
  },
  {
    "path": "public/js/matchup_page.js",
    "content": "(function(angular, matchupData, sortData, radarChartSettings, lineChartSettings) {\n\n    var appMatchup = angular.module('matchupPage', ['tc.chartjs', 'dirDisqus', 'core']);\n\n    appMatchup.controller('matchupGraphs', ['$scope', function($scope) {\n        $scope.champComparison = {\n            data: {\n                labels: matchupData.championMatrix.labels,\n                datasets: [{\n                    label: matchupData.champ1.name,\n                    fillColor: \"rgba(101, 228, 245, 0.65)\",\n                    strokeColor: \"rgba(101, 228, 245, 1)\",\n                    pointColor: \"rgba(101, 228, 245, 1)\",\n                    pointStrokeColor: \"#fff\",\n                    pointHighlightFill: \"#fff\",\n                    pointHighlightStroke: \"rgba(101, 228, 245, 1)\",\n                    data: matchupData.championMatrix.champ1\n                }, {\n                    label: matchupData.champ2.name,\n                    fillColor: \"rgba(255, 83, 83, 0.55)\",\n                    strokeColor: \"rgba(255, 83, 83, 0.99)\",\n                    pointColor: \"rgba(255, 83, 83, 0.99)\",\n                    pointStrokeColor: \"#fff\",\n                    pointHighlightFill: \"#fff\",\n                    pointHighlightStroke: \"rgba(255, 83, 83, 0.99)\",\n                    data: matchupData.championMatrix.champ2\n                }]\n            },\n            settings: radarChartSettings\n        };\n\n        $scope.goldIncome = {\n            data: {\n                labels: [\"0-10\", \"10-20\", \"20-30\", \"30+\"],\n                datasets: [{\n                    label: \"champ1\",\n                    fillColor: \"rgba(101, 228, 245, 0.65)\",\n                    strokeColor: \"rgba(101, 228, 245, 1)\",\n                    pointColor: \"rgba(101, 228, 245, 1)\",\n                    pointStrokeColor: \"#fff\",\n                    pointHighlightFill: \"#fff\",\n                    pointHighlightStroke: \"rgba(101, 228, 245, 1)\",\n                    data: matchupData.goldLength.champ1\n                }, {\n                    label: \"champ2\",\n                    fillColor: \"rgba(255, 83, 83, 0.55)\",\n                    strokeColor: \"rgba(255, 83, 83, 0.99)\",\n                    pointColor: \"rgba(255, 83, 83, 0.99)\",\n                    pointStrokeColor: \"#fff\",\n                    pointHighlightFill: \"#fff\",\n                    pointHighlightStroke: \"rgba(255, 83, 83, 0.99)\",\n                    data: matchupData.goldLength.champ2\n                }]\n            },\n            settings: lineChartSettings\n        };\n    }]);\n})(angular, matchupData, sortData, radarChartSettings, lineChartSettings);"
  },
  {
    "path": "public/js/statistics_jquery.js",
    "content": "// jquery\n(function($){\n  var $fixedHeader = $(\"#header-fixed\");\n  var $table = $(\"#table-1\");\n  var $fixedHeaderTd = $(\"#header-fixed td\");\n  var $originalHeaderTd = $(\"#original-header td\");\n\n  function fixWidths(){\n     clearTimeout(timeOut);\n     var timeOut = setTimeout(function(){\n        $fixedHeader.width($table.width());\n       for(var t=0;t<$fixedHeaderTd.length;t++){\n        $fixedHeaderTd.eq(t).width($originalHeaderTd.eq(t).width());\n       }\n    },200);\n  }\n\n  fixWidths();\n\n  $(window).resize(fixWidths);\n\n  $fixedHeaderTd.add($originalHeaderTd).on('click', fixWidths);\n\n  $(window).bind(\"scroll\", function() {\n      var $this = $(this);\n      var leftOffset = $this.scrollLeft();\n      var offset = $this.scrollTop();\n      var tableOffset = $table.offset().top;\n\n      if (offset >= tableOffset) {\n          \n        $fixedHeader.css('margin-left','-'+leftOffset+'px');\n          \n        if($fixedHeader.is(\":hidden\")){\n          //Fixes discrepancy between fixedHeader and tables width upon filtering or changing sort role. \n          if($fixedHeader.width() != $table.width()){\n            $fixedHeader.width($table.width());\n              for(var t=0;t<$fixedHeaderTd.length;t++){\n                $fixedHeaderTd.eq(t).width($originalHeaderTd.eq(t).width());\n              }\n          }\n          $fixedHeader.show();\n        }\n      }\n      else if (offset < tableOffset) {\n          $fixedHeader.hide();\n      }\n  });\n})(jQuery);\n"
  },
  {
    "path": "public/js/statistics_page.js",
    "content": "// angular\n(function(angular, matchupData) {\n\n    var statsApp = angular.module('statsPage', ['core']);\n\n    statsApp.controller('data', ['$scope', '$location', function($scope, $location) {\n        var urlParams = $location.search();\n        $scope.championData = matchupData.stats;\n\n        $scope.search = {\n            title: urlParams.search || ''\n        };\n\n        $scope.roleSort = {\n            role: urlParams.roleSort || ''\n        };\n\n        $scope.Math = Math;\n\n        $scope.indexNumber = function(index, dataLength){\n            var reverseList = false;\n            if($scope.sortExpression.sortBy === 'role' || $scope.sortExpression.sortBy === 'title' || $scope.sortExpression.sortBy === 'general.overallPosition'){\n                reverseList = true;\n            }\n\n            if((!reverseList && $scope.order === '-') || (reverseList && $scope.order === '')){\n                return index + 1;\n            } else {\n                return dataLength - index;\n            }\n        };\n\n        $scope.searchUrl = function() {\n            $location.search('search', $scope.search.title);\n        };\n\n        $scope.chosenRole = function() {\n            $location.search('roleSort', $scope.roleSort.role);\n        };\n\n        $scope.changeOrder = function(){\n            $scope.order = ($scope.order === '-') ? '' : '-';\n            $location.search('order', ($scope.order === '-') ? 'descend' : 'ascend');\n        };\n\n        $scope.changeSelection = function(property) {\n            if (property !== 'role' && property !== 'title') {\n                property = 'general.' + property;\n            }\n            if ($scope.sortExpression.sortBy === property) {\n                $scope.order = ($scope.order === '-') ? '' : '-';\n            } else {\n                $scope.sortExpression.lastSortBy = $scope.sortExpression.sortBy;\n                $scope.sortExpression.sortBy = property;\n                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'))) {\n                    $scope.order = ($scope.order === '-') ? '' : '-';\n                }\n            }\n            $location.search('sortBy', $scope.sortExpression.sortBy);\n            $location.search('order', ($scope.order === '-') ? 'descend' : 'ascend');\n        };\n        $scope.determineSelected = function(property) {\n            var propName = $scope.sortExpression.sortBy.split('.').reverse()[0];\n            if (property === propName) {\n                return true;\n            }\n        };\n\n        $scope.determineOrder = function(direction) {\n            if (direction === 'down') {\n                if ($scope.sortExpression.sortBy === 'role' || $scope.sortExpression.sortBy === 'title' || $scope.sortExpression.sortBy === 'general.overallPosition') {\n                    return $scope.order !== '';\n                }\n                return $scope.order === '';\n            }\n\n            if ($scope.sortExpression.sortBy === 'role' || $scope.sortExpression.sortBy === 'title' || $scope.sortExpression.sortBy === 'general.overallPosition') {\n                return $scope.order === '';\n            }\n            return $scope.order !== '';\n        };\n\n        $scope.sortExpression = {\n            sortBy: urlParams.sortBy || 'title',\n            lastSortBy: 'role'\n        };\n        $scope.order = '';\n        if (urlParams.order === 'descend') {\n            $scope.order = '-';\n        }\n\n    }]);\n})(angular, matchupData);"
  },
  {
    "path": "public/opensearchdescription.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<OpenSearchDescription xmlns=\"http://a9.com/-/spec/opensearch/1.1/\">\n <ShortName>Champion.gg</ShortName>\n <Description>Search Champion.gg</Description>\n <Tags>Champion.gg</Tags>\n <Url type=\"text/html\"\n      template=\"http://www.champion.gg/champion/{searchTerms}\"/>\n</OpenSearchDescription>\n"
  },
  {
    "path": "public/riot.html",
    "content": "36fabc0e-3ed0-461d-805b-0dcf0151fd6b"
  },
  {
    "path": "public/template/typeahead/typeahead-popup.html",
    "content": "<ul class=\"dropdown-menu\" ng-show=\"isOpen()\" ng-style=\"{top: position.top+'px', left: position.left+'px'}\" style=\"display: block;\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\n    <li ng-repeat=\"match in matches track by $index\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index)\" role=\"option\" id=\"{{match.id}}\">\n        <div typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n    </li>\n</ul>"
  },
  {
    "path": "routes/api_static.js",
    "content": "/* GET users listing. */\n\"use strict\";\nvar apiData = require('../api_data');\nvar express = require('express');\nvar router = express.Router();\n\n/**\n * Route set up for indepth mastery info.\n */\nrouter.get('/masteries/:id', function(req, res) {\n    res.setHeader('Cache-Control', 'public, max-age=86400'); //cache masteries for 3 minutes.\n\n    var id = req.params.id;\n    if(apiData.masteries.hasOwnProperty(id)){\n        res.json(apiData.masteries[id]); \n    } else {\n        res.statusCode = 404;\n        res.send('invalid request');\n    }\n});\n\n/**\n * Route set up for when we add indepth tool tips to items\n * --> not currently used.\n */\nrouter.get('/items/:id', function(req, res) {\n    var id = req.params.id;\n    if(apiData.items.hasOwnProperty(id)){\n        res.json(apiData.items[id]); \n    } else {\n        res.statusCode = 404;\n        res.send('invalid request');\n    }\n});\n\n\n\n/**\n * Route set up for when we add indepth summoner spell information\n * --> not currently used.\n */\nrouter.get('/summoners/:id', function(req, res) {\n    var id = req.params.id;\n    var match = false;\n    for (var prop in apiData.summoners){\n        if(apiData.summoners.hasOwnProperty(prop) && apiData.summoners[prop].id === Number(id)){\n            res.json(apiData.summoners[prop]);\n            match = true;\n            break;\n        }\n    } \n    if (!match){\n        res.statusCode = 404;\n        res.send('invalid request');\n    }\n});\n\n\n/**\n * Route set up for when we add summoner skill order information\n * --> not currently used - may be working, but commented out incase,\n *     there are potential bugs.\n */\n\n// router.get('/skills/:champion/:id', function(req, res) {\n//     var id = req.params.id;\n//     var champion = req.params.champion;\n//     var championSkills;\n\n  \n//     var champFound = apiData.skills.hasOwnProperty(champion);\n//     var keys = Object.keys(apiData.skills);\n//     for (var i in keys) {\n//         if (champFound) break;\n//         if (keys[i].toLowerCase() === champion.toLowerCase()) {\n//             champion = keys[i];\n//             champFound = true;\n//             break;\n//         }\n//     }\n\n//     if (champFound) {\n//         championSkills = apiData.skills[champion].spells;\n//         if (id in championSkills) {\n//             res.json(championSkills[id]);\n//         }\n//         else {\n//             res.statusCode = 404;\n//             res.send(champion + \" doesn't have that skill.\");\n//         }\n//     }\n// });\n\n\nmodule.exports = router;\n"
  },
  {
    "path": "routes/champion.js",
    "content": "\"use strict\";\nvar WebChampionPage = require('../models/web_champion_page.js');\nvar WebChampionRoles = require('../models/web_champion_roles.js');\nvar WebOverallRoleData = require('../models/web_overall_role_data.js');\nvar champList = require('../api_data/champions.json');\nvar roleHashTable = require('../logic/role_hash_table.js');\nvar produceError = require('../logic/produce_error.js');\nvar lowerCaseChamp = require('../logic/lower_case_champ.js');\nvar express = require('express');\nvar q = require('q');\nvar router = express.Router();\n\n/**\n * pageData sets the general page info for the current route.\n * For example - championPage is the name of the angular module.\n */\nvar pageData = {\n    appName: 'championPage',\n    name: 'champion',\n    description: '',\n    title: ''\n};\n\nfunction getChampionRoles(champKey){\n    var deferred = q.defer();\n\n    WebChampionRoles.findOne({\n        key: champKey\n    }, function(err, doc) {\n        if (err) {\n            deferred.reject('server_maintenance');\n        \n        } else if (!doc || doc.roles.length === 0) {\n            deferred.reject('new_champion');\n\n        } else {\n            deferred.resolve(doc.toObject());\n        }\n    });\n    return deferred.promise;\n}\n\nfunction getChampionPage(champKey, role, res){\n    var deferred = q.defer();\n\n    WebChampionPage.findOne({\n        key: champKey,\n        role: role\n    }, function(err, doc) {\n        if (err) {\n            deferred.reject('server_maintenance');\n        \n        } else if (!doc) {\n            if (res){\n                res.redirect('/champion/'+champKey);\n            } else {\n                deferred.reject('server_maintenance');\n            }\n            \n         \n        } else {\n            deferred.resolve(doc.toObject());\n        }\n    });\n\n    return deferred.promise;\n}\n\nfunction getOverallRoleData(role){\n    var deferred = q.defer();\n\n    WebOverallRoleData.findOne({\n        role: role\n    }, function(err, doc) {\n        if (err) {\n            deferred.reject('server_maintenance');\n        \n        } else if (!doc) {\n            deferred.reject('server_maintenance');\n         \n        } else {\n            deferred.resolve(doc.toObject());\n        }\n    });\n\n    return deferred.promise;\n}\n\nfunction generateResponseObj(champion, generalRole, championData){\n    var resObj = {\n        pageData: pageData,\n        champion: champion,\n        generalRole: generalRole,\n        championData: championData\n    };\n    resObj.pageData.title = champion.name + ' ' + champion.roleTitle + ' Stats, Builds, Runes, Masteries and Counters';\n    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.\"; \n    return resObj;\n}\n\nrouter.get('/:champ', function(req, res, next) {\n    var champKey = req.params.champ;\n    var champion = {};\n    var generalRole = {};\n    var championData = {};\n    var champMatch = typeof champList[champKey] !== 'undefined';\n\n    if (champMatch || lowerCaseChamp(champKey)) {\n        if (!champMatch) {\n            champKey = lowerCaseChamp(champKey);\n        }\n\n        getChampionRoles(champKey)\n\n        .then(function(doc){\n            champion = doc;\n            champion.role = doc.roles[0].role;\n            champion.roleTitle = roleHashTable.roleKey[doc.roles[0].role];\n            \n            return q.all([\n                    getChampionPage(champKey, champion.role),\n                    getOverallRoleData(champion.role)\n                ]).spread(function(_championData_, _generalRole_){\n                    championData = _championData_;\n                    generalRole =  _generalRole_;\n                    res.render('champion', generateResponseObj(champion, generalRole, championData));\n\n                }, function(){\n                    next(produceError('serverMaintenance', 503));\n\n                });\n\n        }, function(err){\n            if(err === 'new_champion'){\n                championData = null;\n                generalRole = null;\n                champion = {\n                    key: champKey,\n                    name: champList[champKey].name\n                };\n                res.render('new_champion', generateResponseObj(champion, generalRole, championData));\n            } else {\n                next(produceError('serverMaintenance', 503));\n            }\n        });\n\n    } else {\n        return next(produceError('champNotFound'));\n    }\n});\n\nrouter.get('/:champ/:role', function(req, res, next) {\n    var champKey = req.params.champ;\n    var champRole = req.params.role; //make function that converts role title to role key.\n    var champion = {};\n    var generalRole = {};\n    var championData = {};\n\n    var champMatch = typeof champList[champKey] !== 'undefined';\n    if (typeof roleHashTable.roleList[champRole] !== 'undefined' && (champMatch || lowerCaseChamp(champKey))) {\n        if (!champMatch) {\n            champKey = lowerCaseChamp(champKey);\n        }\n        champRole = roleHashTable.roleList[champRole];\n\n        getChampionPage(champKey, champRole, res)\n            .then(function(_championData_){\n                q.all([\n                    getChampionRoles(champKey),   \n                    getOverallRoleData(champRole)\n                ]).spread(function(_championRoles_, _generalRole_){\n                    champion = _championRoles_;\n                    champion.role = champRole;\n                    champion.roleTitle = roleHashTable.roleKey[champRole];\n                    championData = _championData_;\n                    generalRole = _generalRole_;\n                    res.render('champion', generateResponseObj(champion, generalRole, championData));\n               \n                }, function(){\n                    next(produceError('serverMaintenance', 503));\n                \n                });\n\n            }, function(){\n                next(produceError('serverMaintenance', 503));\n            \n            });\n\n    } else {\n        return next(produceError('champNotFound'));\n    }\n\n});\n\nmodule.exports = router;\n"
  },
  {
    "path": "routes/faq.js",
    "content": "\"use strict\";\nvar express = require('express');\nvar router = express.Router();\n\nrouter.get('/', function(req, res) {\n    res.render('faq', {\n        pageData: {\n            appName: 'core',\n            name: 'faq',\n            title: 'All your questions answered here!'\n        }\n    });\n});\n\nmodule.exports = router;"
  },
  {
    "path": "routes/index.js",
    "content": "\"use strict\";\nvar WebChampionRoles = require('../models/web_champion_roles.js');\nvar WebHomePageSummaries = require('../models/web_home_page_summaries.js');\nvar lowerCaseChamp = require('../logic/lower_case_champ.js');\nvar produceError = require('../logic/produce_error.js');\nvar champList = require('../api_data/champions.json');\nvar express = require('express');\nvar router = express.Router();\n\nrouter.get('/', function(req, res, next) {\n    var retrievedYet = false;\n    var champData = [];\n    var summaries = [];\n\n    function responseObj() {\n        res.render('index', {\n            summaries: summaries,\n            data: champData,\n            pageData: {\n                appName: 'core',\n                name: 'home',\n                title: 'LoL Champion Stats, Builds, Runes, Masteries, Counters and Matchups!',\n                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!' \n            }\n        });\n    }\n\n    WebChampionRoles.find({}).sort({\n        name: 1\n    }).exec(function(err, data) {\n        if (err) {\n            return next(produceError('serverMaintenance', 503));\n        } else if (!data) {\n            return next(produceError('serverMaintenance', 503));\n        } else {\n            champData = data;\n            if (retrievedYet) {\n                responseObj();\n            } else {\n                retrievedYet = true;\n            }\n        }\n    });\n\n    WebHomePageSummaries.findOne({\n        id: 1\n    }, function(err, data) {\n        if (err) {\n            return next(produceError('serverMaintenance', 503));\n        } else if (!data) {\n            return next(produceError('serverMaintenance', 503));\n        } else {\n            summaries = data.data;\n            if (retrievedYet) {\n                responseObj();\n            } else {\n                retrievedYet = true;\n            }\n        }\n    });\n});\n\nrouter.get('/:champ', function(req, res, next) {\n    var champKey = req.params.champ;\n    if (typeof champList[champKey] !== 'undefined' || lowerCaseChamp(champKey)) {\n        res.redirect('/champion/' + req.params.champ);\n    } else {\n        return next(produceError('pageNotFound', 404));\n    }\n});\n\nmodule.exports = router;"
  },
  {
    "path": "routes/matchup.js",
    "content": "/* GET users listing. */\n\"use strict\";\nvar WebMatchupPage = require('../models/web_matchup_page.js');\nvar produceError = require('../logic/produce_error.js');\nvar lowerCaseChamp = require('../logic/lower_case_champ.js');\nvar roleHashTable = require('../logic/role_hash_table.js');\nvar champList = require('../api_data/champions.json');\nvar express = require('express');\nvar router = express.Router();\n\nrouter.get('/:champ1/:champ2/:role', function(req, res, next) {\n\n    var champ1 = req.params.champ1;\n    var champ2 = req.params.champ2;\n    var champRole = req.params.role;\n    var pageData, votes;\n    var champ1Match = typeof champList[champ1] !== 'undefined';\n    var champ2Match = typeof champList[champ2] !== 'undefined';\n\n    function matchupResponse() {\n        var title = generateTitle();\n        res.render('matchup', {\n            data: pageData,\n            pageData: {\n                appName: 'matchupPage',\n                name: 'matchups',\n                title: title,\n                description: title\n            }\n        });\n    }\n\n    function generateTitle() {\n        if (pageData.role === 'SYNERGY') {\n            return pageData.champ1.name + ' ' + pageData.champ1.roleTitle + ' Synergy With ' + pageData.champ2.name + ' ' + pageData.champ2.roleTitle;\n        }\n        return pageData.champ1.name + ' ' + pageData.champ1.roleTitle + ' against ' + pageData.champ2.name + ' ' + pageData.champ2.roleTitle;\n    }\n\n    if ((champ1Match || lowerCaseChamp(champ1)) && (champ2Match || lowerCaseChamp(champ2)) && typeof roleHashTable.roleList[champRole] !== 'undefined') {\n        if (!champ1Match) {\n            champ1 = lowerCaseChamp(champ1);\n        }\n        if (!champ2Match) {\n            champ2 = lowerCaseChamp(champ2);\n        }\n        champ1 = champList[champ1].id;\n        champ2 = champList[champ2].id;\n        champRole = roleHashTable.roleList[champRole];\n\n        WebMatchupPage.findOne({\n            'champ1.id': champ1,\n            'champ2.id': champ2,\n            role: champRole\n        }, function(err, doc) {\n\n            if (err) {\n                return next(produceError('serverMaintenance', 503));\n            } else if (!doc) {\n                return next(produceError('invalidMatchup'));\n            } else {\n                pageData = doc;\n                pageData.roleTitle = roleHashTable.roleKey[pageData.role];\n                matchupResponse();\n            }\n        });\n\n    } else {\n        return next(produceError('invalidMatchup'));\n    }\n});\n\nmodule.exports = router;"
  },
  {
    "path": "routes/matchup_json.js",
    "content": "/* GET users listing. */\n\"use strict\";\nvar WebMatchupPage = require('../models/web_matchup_page.js');\nvar roleHashTable = require('../logic/role_hash_table.js');\nvar express = require('express');\nvar router = express.Router();\n\nrouter.get('/:champ1/:champ2/:role', function(req, res) {\n\n    var champ1 = req.params.champ1;\n    var champ2 = req.params.champ2;\n    var champRole = req.params.role;\n\n    if (typeof roleHashTable.roleKey[champRole] !== 'undefined') {\n        WebMatchupPage.findOne({\n            'champ1.id': champ1,\n            'champ2.id': champ2,\n            role: champRole\n        }, function(err, data) {\n            if (err) {\n                console.log(err);\n                res.send('Try Again');\n            } else {\n                res.json(data);\n            }\n        });\n    } else {\n        res.statusCode = 404;\n        res.send('invalid request');\n    }\n});\n\nmodule.exports = router;"
  },
  {
    "path": "routes/statistics.js",
    "content": "\"use strict\";\nvar WebStatisticsPage = require('../models/web_statistics_page.js');\nvar produceError = require('../logic/produce_error.js');\nvar express = require('express');\nvar router = express.Router();\nrouter.get('/', function(req, res, next) {\n\n    WebStatisticsPage.find({}, function(err, doc) {\n        if (err) {\n            return next(produceError('serverMaintenance', 503));\n        } else if (!doc) {\n            return next(produceError('serverMaintenance', 503));\n        } else {\n            res.render('statistics', {\n                data: doc,\n                pageData: {\n                    appName: 'statsPage',\n                    name: 'stats',\n                    title: 'League of Legends Stats by Champion Role for the Current Patch',\n                    description: \"League of Legends Statistics including Win Rate, Ban Rate, Play Rate, Kills, Deaths by Champions and the roles they play.\"\n                }\n            });\n        }\n    });\n});\n\nmodule.exports = router;"
  },
  {
    "path": "update_data.sh",
    "content": "defaultmsg=\"updated data\"\nread -p \"Please enter a git message (default : '$defaultmsg'):\" gitmsg\nif [ \"$gitmsg\" == \"\" ]; then\n\tgitmsg=\"$defaultmsg\"\nfi\necho $gitmsg\n\nmongodump --db championgg --collection weboverallstats --out ./db\nmongodump --db championgg --collection webchampionpages --out ./db\nmongodump --db championgg --collection webchampionroles --out ./db\nmongodump --db championgg --collection webmatchuppages --out ./db\nmongodump --db championgg --collection weboverallroledatas --out ./db\nmongodump --db championgg --collection webhomepagesummaries --out ./db\nmongodump --db championgg --collection webstatisticspages --out ./db\n\ngrunt production\n\ngit add -A && git commit -m \"$gitmsg\" && git push origin master\n\ncd ../ssh && sh ssh_in.sh;"
  },
  {
    "path": "update_server.sh",
    "content": "#!/bin/bash\ncd /home/ubuntu/championweb/\ngit remote update\n\nLOCAL=$(git rev-parse @)\nREMOTE=$(git rev-parse @{u})\nBASE=$(git merge-base @ @{u})\n\nif [ $LOCAL = $REMOTE ]; then\n    echo \"Up-to-date\"\nelif [ $LOCAL = $BASE ]; then\n    echo \"Behind\"\n    git pull\n    sh ./bin/update_server.sh\nelif [ $REMOTE = $BASE ]; then\n    echo \"Ahead\"\n    git reset --hard origin/master\n    sh ./bin/update_server.sh\nelse\n    echo \"Diverged\"\n    git reset --hard origin/master\n    sh ./bin/update_server.sh\nfi\n"
  },
  {
    "path": "views/champion/advertisement.ejs",
    "content": "<h2> Advertising Support </h2>  \n<div class=\"advert-holder\" style=\"margin:auto;margin-left:15px\">\n  <div style=\"float:left; width:300px;\">\n    <!-- /22280732/ChampionGG_300x250_Champion_BTF1 -->\n    <div id='div-gpt-ad-1443488528500-2'>\n    <script type='text/javascript'>\n    googletag.cmd.push(function() { googletag.display('div-gpt-ad-1443488528500-2'); });\n    </script>\n    </div>\n  </div>\n\n  <div style=\"float:right; width:300px;\">\n    <!-- /22280732/ChampionGG_300x250_Champion_BTF2 -->\n    <div id='div-gpt-ad-1443488528500-3'>\n    <script type='text/javascript'>\n    googletag.cmd.push(function() { googletag.display('div-gpt-ad-1443488528500-3'); });\n    </script>\n    </div>\n  </div>\n  \n  <div style=\"clear:both\"></div>\n</div>\n"
  },
  {
    "path": "views/champion/champion_image_roles.ejs",
    "content": "<img src=\"//ddragon.leagueoflegends.com/cdn/<%= core.ddPatch %>/img/champion/<%= champion.key %>.png\" class=\"champ-img\"/>\n<h1><%= champion.name %></h1>\n<ul>\n\n  <% for(var i=0; i<champion.roles.length; i++) {%>\n  <li <%if (champion.role === champion.roles[i].role) { %> class=\"selected-role\"<% } %>>\n    <a href=\"/champion/<%= champion.key %>/<%= champion.roles[i].title %>\">\n      <h3>\n        <%= champion.roles[i].title %>\n      </h3>\n    </a>\n    <small> <%= champion.roles[i].percentPlayed %>% Role Rate </small>\n    <small style=\"display:block\"> <%= champion.roles[i].games %> Analyzed This Patch </small>\n  </li>\n  <% } %>\n  \n  <% if (champion.name === 'Viktor'){ %>\n    <% include viktor_upgrade.ejs %>\n  <% } %>\n</ul>"
  },
  {
    "path": "views/champion/champion_statistics.ejs",
    "content": "<h2 style=\"margin-bottom:8px\">Statistics</h2>\n<table class=\"table table-striped\">\n  <thead>\n    <tr >\n      <th class=\"first-column\"><span class=\"text-indent\">Type</span></th>\n      <th class=\"second-column\">Average</th>\n      <th class=\"third-column\"><span class=\"glyphicon glyphicon-question-sign\" tooltip=\"<%= champion.name %>'s ranking compared to other <%= champion.roleTitle %> champions.\"></span> Role Placement</th>\n      <th class=\"fourth-column\">Placement Change this patch</th>\n    </tr>\n  </thead>\n  <tbody>\n   <% for(var i=0; i<championData.general.length; i++) {%>\n   <tr id=<%= 'statistics-' + championData.general[i].title.replace(' ', '-').toLowerCase() + '-row' %> >\n    <td>\n      <a href=\"/statistics/#?roleSort=<%= champion.roleTitle %>&sortBy=general.<%= championData.general[i].titleLink %>&order=descend\">\n        <%= championData.general[i].title %>\n      </a>\n    </td>\n\n    <td><%= championData.general[i].val %><% if(i < 3){ %>%<%}%></td>\n    <td>  \n      <strong class=\"<%if (championData.general[i].position <= (generalRole.totalNumber/2)) { %> top-half <% } else { %> bottom-half <% } %>\">\n        <%= championData.general[i].position %> \n      </strong>\n      <small>/ <%= generalRole.totalNumber %></small>\n    </td>\n    <td>\n      <span class=\"glyphicon <%if (championData.general[i].change > 0) { %> glyphicon-arrow-up <% } %> <%if (championData.general[i].change < 0) { %> glyphicon-arrow-down <% } %> <%if (championData.general[i].change === 0) { %> same-position <% } %>\">\n      </span>\n      <%- Math.abs(championData.general[i].change) %></td>\n    </tr>\n\n    <% } %>\n\n    <tr>\n      <td colspan=2> <a href=\"/statistics/#?roleSort=<%= champion.roleTitle %>&sortBy=general.overallPosition&order=ascend\">Overall Placement</a></td>\n      <td style=\"text-align:center\"><strong class=\"<%if (championData.overallPosition.position <= (generalRole.totalNumber/2)) { %> top-half <% } else { %> bottom-half <% } %>\"><%= championData.overallPosition.position %> </strong><small>/ <%= generalRole.totalNumber %></small></td>\n      <td><span class=\"glyphicon <%if (championData.overallPosition.change > 0) { %> glyphicon-arrow-up <% } %> <%if (championData.overallPosition.change < 0) { %> glyphicon-arrow-down <% } %> <%if (championData.overallPosition.change === 0) { %> same-position <% } %>\">\n      </span> <%- Math.abs(championData.overallPosition.change) %></td>\n    </tr>\n  </tbody>\n</table>\n\n\n<h2>Overall Champion Matrix</h2>\n<div class=\"chart-holder\" style=\"max-width:310px\">\n  <canvas tc-chartjs-radar chart-data=\"overallComparison.data\" chart-options=\"overallComparison.settings\" chart-legend=\"generalComparison\" id=\"canvas2\" height=\"242\" width=\"339\"></canvas>\n</div>\n<div tc-chartjs-legend chart-legend=\"generalComparison\"></div>"
  },
  {
    "path": "views/champion/core_build.ejs",
    "content": "<h2 class=\"champion-stats\">Most Frequent Core Build</h2>\n<div class=\"build-wrapper\">\n  <% for(var k=0; k<championData.items.mostGames.items.length; k++) {%>\n    <a href=\"http://leagueoflegends.wikia.com/wiki/<%= championData.items.mostGames.items[k].name %>\" rel=\"nofollow\" target=\"_blank\">\n      <img src=\"//ddragon.leagueoflegends.com/cdn/<%= core.ddPatch %>/img/item/<%= championData.items.mostGames.items[k].id %>.png\" class=\"possible-build\" tooltip=\"<%= championData.items.mostGames.items[k].name %>\"/>\n    </a>\n    <% if (k!==5){ %>\n     <small>></small> \n    <%}%>\n  <% } %>\n  <div class=\"build-text\">\n    <strong><%= championData.items.mostGames.winPercent %>%</strong> Win Rate | <strong><%= championData.items.mostGames.games %></strong> Games \n  </div>\n</div>\n\n<h2 class=\"champion-stats\"  style=\"margin-top:40px\">Highest Win % Core Build</h2>\n<div class=\"build-wrapper\">\n  <% for(var l=0; l<championData.items.highestWinPercent.items.length; l++) {%>\n    <a href=\"http://leagueoflegends.wikia.com/wiki/<%= championData.items.highestWinPercent.items[l].name %>\" rel=\"nofollow\" target=\"_blank\">\n      <img src=\"//ddragon.leagueoflegends.com/cdn/<%= core.ddPatch %>/img/item/<%= championData.items.highestWinPercent.items[l].id %>.png\" class=\"possible-build\" tooltip=\"<%= championData.items.highestWinPercent.items[l].name %>\"/>\n    </a>\n    <% if (l!==5){ %>\n      <small>></small>\n    <%}%>\n  <% } %>\n  <div class=\"build-text\">\n    <strong><%= championData.items.highestWinPercent.winPercent %>%</strong> Win Rate | <strong><%= championData.items.highestWinPercent.games %></strong> Games \n  </div>\n</div>\n"
  },
  {
    "path": "views/champion/counters_matchups.ejs",
    "content": "<div class=\"container-fluid\"  ng-controller=\"matchupData\">\n  \n  <div class=\"matchup-wrapper\">\n    <div class=\"matchup-settings\">\n      <span>Display matchups with at least</span> \n      <select class=\"minimum-games\" ng-change=\"saveMinGames()\" ng-model=\"minGames\">\n        <option value=\"0\">Show All</option>\n        <option value=\"100\">100+</option>\n        <option value=\"250\">250+</option>\n        <option value=\"500\">500+</option>\n        <option value=\"1000\">1000+</option>\n        <option value=\"10000\">10000+</option>\n      </select>\n      <span>games.</span>\n    </div>\n  </div>\n\n  <div class=\"row counter-row\">\n\n    <div class=\"col-xs-12 col-sm-12 col-md-6 counter-column\">\n      <div class=\"matchup-header\">\n        <h2><%= champion.roleTitle %> Champions that Counter <%= champion.name %></h2>\n        <span class=\"glyphicon glyphicon-question-sign\" tooltip=\"The coloured bars below show <%= champion.name %>'s performance in the matchup. Red = Weak, Yellow = Even, Green = Strong.\"></span>\n      </div>\n      <filters matchup-type=\"matchups\"></filters>\n      <matchups order=\"\" matchup-type=\"matchups\"></matchups>\n    </div>\n\n    <div class=\"col-xs-12 col-sm-12 col-md-6\">\n      <div class=\"matchup-header\">\n        <h2><%= champion.roleTitle %> Champions that <%= champion.name %> Counters</h2> \n        <span class=\"glyphicon glyphicon-question-sign\" tooltip=\"The coloured bars below show <%= champion.name %>'s performance in the matchup. Green = Strong, Yellow = Even, Red = Weak.\"></span>\n      </div>\n      <filters matchup-type=\"matchups\"></filters>\n      <matchups order=\"-\" matchup-type=\"matchups\"></matchups>\n    </div>\n\n  </div>\n\n  <%if (champion.role === 'DUO_SUPPORT' || champion.role === 'DUO_CARRY') { %>\n\n    <div class=\"row counter-row\">\n      <div class=\"col-xs-12 col-sm-12 col-md-6 counter-column\">\n        <div class=\"matchup-header\">\n          <h2><%if (champion.role === 'DUO_SUPPORT') { %> ADC <% } else { %> Support <% } %> Champions that Counter <%= champion.name %></h2>\n        </div>\n        <filters matchup-type=\"adcsupport\"></filters>\n        <matchups order=\"\" matchup-type=\"adcsupport\"></matchups>\n      </div>\n\n      <div class=\"col-xs-12 col-sm-12 col-md-6\">\n        <div class=\"matchup-header\">\n          <h2><%if (champion.role === 'DUO_SUPPORT') { %> ADC <% } else { %> Support <% } %> Champions that <%= champion.name %> Counters</h2>\n        </div>\n        <filters matchup-type=\"adcsupport\"></filters>\n        <matchups order=\"-\" matchup-type=\"adcsupport\"></matchups>\n      </div>\n    </div>\n\n    <div class=\"row counter-row\">\n      <div class=\"col-xs-12 col-sm-12 col-md-6 counter-column\">\n        <div class=\"matchup-header\">\n          <h2><%if (champion.role === 'DUO_SUPPORT') { %> ADC <% } else { %> Support <% } %> Champions that Synergise poorly with <%= champion.name %></h2>\n        </div>\n        <filters matchup-type=\"synergy\"></filters>\n        <matchups order=\"\" matchup-type=\"synergy\"></matchups>\n      </div>\n\n      <div class=\"col-xs-12 col-sm-12 col-md-6\">\n        <div class=\"matchup-header\">\n          <h2><%if (champion.role === 'DUO_SUPPORT') { %> ADC <% } else { %> Support <% } %> Champions that Synergise well with <%= champion.name %></h2>\n        </div>\n        <filters matchup-type=\"synergy\"></filters>\n        <matchups order=\"-\" matchup-type=\"synergy\"></matchups>\n      </div>\n    </div>\n\n  <% } %>\n\n</div>\n  \n<script type=\"text/ng-template\" id=\"filters.html\">\n  <div class=\"clearfix\">\n    <div class=\"search-sorter\">\n      <input class=\"matchupSearch\" type=\"text\" ng-model=\"search[matchupType].name\" ng-model-options=\"{debounce: 30}\" placeholder=\"Search By Name\" />\n    </div>\n    <div ng-click=\"saveSort('statScore')\" class=\"stat-sorter\" ng-class=\"{'selected-sorter': sortExpression.sortBy === 'statScore'}\">\n      Statistical Rating <span class=\"glyphicon glyphicon-question-sign\" tooltip=\"Statistical rating takes more than win rate into account. Click the graph button under the champion name for more details.\"></span>\n    </div>\n    <div ng-click=\"saveSort('winRate')\" class=\"winrate-sorter\" ng-class=\"{'selected-sorter': sortExpression.sortBy === 'winRate'}\">\n      {{champion.name}} Win Rate\n    </div>\n  </div>\n</script>\n\n\n\n<script type=\"text/ng-template\" id=\"matchups.html\">\n\n<div ng-repeat=\"matchup in filtered[matchupType] = (allMatchups[matchupType] | orderBy:[order+sortExpression.sortBy,order+'overallScore'] | startsWith:search[matchupType].name | filter:matchAmount) | limitTo:itemsLimit(matchupType,order)\" ng-controller=\"specificMatchup\" class=\"animate-repeat\" ng-class=\"{'left': order === '', 'right': order === '-'}\">  \n  <div class=\"row counter-matchups\">\n\n    <div class=\"matchup-champion-info\">\n      <a href=\"/champion/{{matchup.key}}/{{generateChampUrl(matchupType)}}\">\n        <div class=\"matchup-champion {{matchup.key}}\"></div>\n        <h3>{{matchup.name}}</h3>\n      </a>\n      <span class=\"glyphicon glyphicon-stats view-more-stats\" tooltip=\"View Indepth Stats\" ng-click=\"toggleMatchup(matchupType, matchup.key)\"></span>\n      <span class=\"glyphicon glyphicon-comment view-more-comment\" tooltip=\"View Discussion\" ng-click=\"matchupDiscussion(matchupType, matchup.key, 'toggle', order)\"></span>\n      <a href=\"{{generateMatchupUrl(matchup.key, matchupType)}}\">\n        <span class=\"glyphicon glyphicon-link view-linked-page\" tooltip=\"View Matchup Page\"></span>\n      </a>\n    </div>\n    \n    <div class=\"matchup-stats\">\n      <div class=\"progress\">\n        <div class=\"progress-bar\" role=\"progressbar\" aria-valuenow=\"60\" aria-valuemin=\"0\" aria-valuemax=\"100\" ng-class=\"{'above-average': matchup.statScore > 6, 'below-average': matchup.statScore < 4, 'average': matchup.statScore >= 4 && matchup.statScore <= 6}\" ng-style=\"{'width': order==='' ? (10-matchup.statScore)*10+'%' : matchup.statScore*10+'%'}\">\n        </div>\n      </div>\n      <small><strong>{{matchup.games}}</strong> Games </small>\n    </div>\n    \n    <div class=\"winrating-area\">\n      <strong>{{matchup.winRate}}%</strong>\n    </div>\n  </div>\n\n  <div class=\"row\" ng-if=\"matchupType === 'matchups' && showMatchups && matchupRetrieved\">\n    <div class=\"col-xxs-12 col-xs-6\">\n      <h4 ng-if=\"showMatchups && matchupRetrieved\"> Matchup Performance </h4>\n      <div class=\"middle-graphic-holder\">\n        <canvas ng-if=\"showMatchups && matchupRetrieved\" tc-chartjs-radar chart-data=\"champComparison.data\" chart-options=\"champComparison.settings\" chart-legend=\"championMatchup\" height=\"400\" width=\"560\"></canvas>\n      </div> \n    </div>\n    <ul class=\"radar-legend visible-xxs\">\n      <li><span class=\"magic-dmg\"></span>{{champion.name}}</li>\n      <li><span class=\"physical-dmg\"></span>{{matchup.name}}</li>\n    </ul>\n    <div class=\"col-xxs-12 col-xs-6\">\n      <h4 ng-if=\"showMatchups && matchupRetrieved\"> Gold Over Time </h4>\n      <div class=\"middle-graphic-holder\">\n        <canvas ng-if=\"showMatchups && matchupRetrieved\" tc-chartjs-line chart-data=\"goldIncome.data\" chart-options=\"goldIncome.settings\" height=\"400\" width=\"560\"> </canvas>\n      </div>\n    </div>\n    <ul class=\"radar-legend\">\n      <li><span class=\"magic-dmg\"></span>{{champion.name}}</li>\n      <li><span class=\"physical-dmg\"></span>{{matchup.name}}</li>\n    </ul>\n  </div>\n\n  <div class=\"row chart-area matchup-stat-area\" ng-if=\"showMatchups\">\n    <div class=\"loading-symbol\" ng-if=\"!matchupRetrieved\">Loading...</div>\n\n    <div class=\"col-xs-12 col-sm-12 col-md-12\" ng-if=\"matchupRetrieved\">\n      <div class=\"matchup-title-width matchup-div-header\"></div>\n      <div class=\"matchup-champ-img-width matchup-div-header\">\n        <div class=\"matchup-champion matchup-table {{champion.key}}\"></div>\n      </div>\n      <div class=\"matchup-champ-img-width matchup-div-header\">\n        <div class=\"matchup-champion matchup-table {{matchup.key}}\"></div>\n      </div>\n      \n      <table class=\"table table-striped\">\n        <thead>\n          <tr>\n            <th class=\"matchup-title-width\" >Type</th>\n            <th class=\"matchup-values-width\">Matchup Average</th>\n            <th class=\"matchup-values-width\">Change</th>\n            <th class=\"matchup-values-width\">Matchup Average</th>\n            <th class=\"matchup-values-width\">Change</th>\n          </tr>\n        </thead>\n        <tbody>\n          <tr ng-repeat=\"matchupInfo in matchup.specificMatchup.general\">\n            <td class=\"matchup-title-width\">\n              {{matchupInfo.title}}\n            </td>\n            <td class=\"matchup-values-width\">\n              {{matchupInfo[champtype(matchup.key, true)].val}}<span ng-if=\"$index===0\">%</span>\n            </td>\n            <td class=\"matchup-values-width\"> \n              <span class=\"glyphicon {{matchupInfo.title}}-title\" ng-class=\"{'glyphicon-arrow-up': matchupInfo[champtype(matchup.key, true)].change > (0), 'glyphicon-arrow-down': matchupInfo[champtype(matchup.key, true)].change < 0}\"></span>\n              {{Math.abs(matchupInfo[champtype(matchup.key, true)].change)}}\n            </td>\n            <td class=\"matchup-values-width\">\n              {{matchupInfo[champtype(matchup.key)].val}}<span ng-if=\"$index===0\">%</span>\n            </td>\n            <td class=\"matchup-values-width\">\n              <span class=\"glyphicon {{matchupInfo.title}}-title\" ng-class=\"{'glyphicon-arrow-up': matchupInfo[champtype(matchup.key)].change > (0), 'glyphicon-arrow-down': matchupInfo[champtype(matchup.key)].change < 0}\"></span>\n              {{Math.abs(matchupInfo[champtype(matchup.key)].change)}}</small>\n            </td>\n          </tr>\n        </tbody>\n      </table>\n    </div>\n  </div>\n  <div class=\"row\" ng-if=\"matchupDiscussion(matchupType, matchup.key, 'bind', order)\">\n    <disqus disqus-shortname=\"championgg\" disqus-identifier=\"{{generateMatchupUrl(matchup.key, matchupType)}}\" disqus-url=\"http://champion.gg{{generateMatchupUrl(matchup.key, matchupType)}}\" disqus-id=\"{{matchupDiscussion(matchupType, matchup.key, 'id', order)}}\" ready-to-bind=\"{{matchupDiscussion(matchupType, matchup.key, 'bind', order)}}\" ></disqus>\n  </div>\n</div>\n<div class=\"show-more\" ng-if=\"hasMoreItemsToShow(matchupType,order)\" ng-click=\"showMoreItems(matchupType,order)\">\n  <small>{{itemsLimit(matchupType,order)}} / {{matchupsCount(matchupType)}} - </small><strong>Show More</strong> \n</div>\n</script> "
  },
  {
    "path": "views/champion/first_items.ejs",
    "content": "<h2 class=\"champion-stats\">Most Frequent Starters</h2>\n<div class=\"build-wrapper\">\n  <% for(var k=0; k<championData.firstItems.mostGames.items.length; k++) {%>\n    <a href=\"http://leagueoflegends.wikia.com/wiki/<%= championData.firstItems.mostGames.items[k].name %>\" rel=\"nofollow\" target=\"_blank\">\n      <img src=\"//ddragon.leagueoflegends.com/cdn/<%= core.ddPatch %>/img/item/<%= championData.firstItems.mostGames.items[k].id %>.png\" class=\"possible-build\" tooltip=\"<%= championData.firstItems.mostGames.items[k].name %>\"/>\n    </a>\n  <% } %>\n  <div class=\"build-text\">\n    <strong><%= championData.firstItems.mostGames.winPercent %>%</strong> Win Rate | <strong><%= championData.firstItems.mostGames.games %></strong> Games\n  </div>\n</div>\n<h2 class=\"champion-stats\" style=\"margin-top:40px\">Highest Win % Starters</h2>\n<div class=\"build-wrapper\">\n  <% for(var l=0; l<championData.firstItems.highestWinPercent.items.length; l++) {%>\n    <a href=\"http://leagueoflegends.wikia.com/wiki/<%= championData.firstItems.highestWinPercent.items[l].name %>\" rel=\"nofollow\" target=\"_blank\">\n      <img src=\"//ddragon.leagueoflegends.com/cdn/<%= core.ddPatch %>/img/item/<%= championData.firstItems.highestWinPercent.items[l].id %>.png\" class=\"possible-build\" tooltip=\"<%= championData.firstItems.highestWinPercent.items[l].name %>\"/>\n    </a>\n  <% } %>\n  <div class=\"build-text\">\n    <strong><%= championData.firstItems.highestWinPercent.winPercent %>%</strong> Win Rate | <strong><%= championData.firstItems.highestWinPercent.games %></strong> Games\n  </div>\n</div>"
  },
  {
    "path": "views/champion/gamelength_experience_summoners.ejs",
    "content": " <h2 class=\"line-chart-header\">Win Rate % by Game Length</h2> \n <div class=\"chart-holder\">\n   <canvas tc-chartjs-line chart-data=\"gameLength.data\" chart-options=\"gameLength.settings\" id=\"canvas\" height=\"150\" width=\"300\"></canvas>\n </div>\n <div tc-chartjs-legend chart-legend=\"playRate\"></div>\n<h2 class=\"line-chart-header\">\n  Win Rate % by Games Played \n  <small style=\"display:block;margin-top: 10px;\">(2014-2015 Ranked Experience)</small>\n</h2> \n<div class=\"chart-holder\">\n  <canvas tc-chartjs-line chart-data=\"experienceRate.data\" chart-options=\"experienceRate.settings\" chart-legend=\"experienceRate\" id=\"canvas\" height=\"150\" width=\"300\"></canvas>\n</div>\n<div tc-chartjs-legend chart-legend=\"experienceRate\"></div>\n\n<h2 class=\"line-chart-header\" style=\"margin-bottom: 20px;\">Playerbase Experience Distribution</h2> \n<div class=\"chart-holder clearfix\">\n  <div style=\"width:65%;float:left\">\n    <canvas tc-chartjs-pie chart-data=\"experienceDistribution.data.datasets\" chart-options=\"experienceDistribution.settings\" chart-legend=\"experienceDist\" id=\"canvas\" height=\"220\" width=\"300\" ></canvas>\n  </div>\n  <div style=\"width:35%;float:left\">\n    <small style=\"display: block;margin-bottom:10px\">2015-2016 Ranked Games</small>\n    <div tc-chartjs-legend chart-legend=\"experienceDist\"></div>\n  </div>\n</div>\n\n <h2 class=\"champion-stats\">Most Frequent Summoners</h2>\n <div class=\"summoner-wrapper\">\n  <a href=\"http://leagueoflegends.wikia.com/wiki/<%= championData.summoners.mostGames.summoner1.name %>\" rel=\"nofollow\" target=\"_blank\"><img src=\"//ddragon.leagueoflegends.com/cdn/<%= core.ddPatch %>/img/spell/<%= championData.summoners.mostGames.summoner1.url %>\" class=\"possible-build\" tooltip=\"<%= championData.summoners.mostGames.summoner1.name %>\"/></a>\n  \n  <a href=\"http://leagueoflegends.wikia.com/wiki/<%= championData.summoners.mostGames.summoner2.name %>\" rel=\"nofollow\" target=\"_blank\"><img src=\"//ddragon.leagueoflegends.com/cdn/<%= core.ddPatch %>/img/spell/<%= championData.summoners.mostGames.summoner2.url %>\" class=\"possible-build\" tooltip=\"<%= championData.summoners.mostGames.summoner2.name %>\"/></a>                 \n  <div class=\"summoner-text\"><strong><%= championData.summoners.mostGames.winPercent %>%</strong> Win Rate | <strong><%= championData.summoners.mostGames.games %></strong> Games</div>\n</div>\n\n<h2 class=\"champion-stats\">Highest Win % Summoners</h2>\n<div class=\"summoner-wrapper\">\n  <a href=\"http://leagueoflegends.wikia.com/wiki/<%= championData.summoners.highestWinPercent.summoner1.name %>\" rel=\"nofollow\" target=\"_blank\"><img src=\"//ddragon.leagueoflegends.com/cdn/<%= core.ddPatch %>/img/spell/<%= championData.summoners.highestWinPercent.summoner1.url %>\" class=\"possible-build\" tooltip=\"<%= championData.summoners.highestWinPercent.summoner1.name %>\"/></a> \n  <a href=\"http://leagueoflegends.wikia.com/wiki/<%= championData.summoners.highestWinPercent.summoner2.name %>\" rel=\"nofollow\" target=\"_blank\"><img src=\"//ddragon.leagueoflegends.com/cdn/<%= core.ddPatch %>/img/spell/<%= championData.summoners.highestWinPercent.summoner2.url %>\" class=\"possible-build\" tooltip=\"<%= championData.summoners.highestWinPercent.summoner2.name %>\"/></a>                \n  <div class=\"summoner-text\"><strong><%= championData.summoners.highestWinPercent.winPercent %>%</strong> Win Rate | <strong><%= championData.summoners.highestWinPercent.games %></strong> Games</div>\n</div>\n"
  },
  {
    "path": "views/champion/masteries.ejs",
    "content": "<h2 class=\"champion-stats\">Most Frequent Masteries</h2>\n<div class=\"mastery-container clearfix\">\n  <% for (var k = 0; k<core.masteryOrder.length; k++){ %>\n    <div class=\"mastery<%= k %>\">\n      <div class=\"mastery-header\">\n        <%= championData.masteries.mostGames.masteries[k].tree %> - <%= championData.masteries.mostGames.masteries[k].total %>\n      </div>\n      <% for (var z = 1; z<=6; z++){ %>\n        <% var masteryColumn = championData.masteries.mostGames.masteries[k].data['row'+z]; %>\n        <div class=\"clearfix mastery-row\">\n          <% for(var y = 0; y < masteryColumn.length; y++) { %>\n            <% if(masteryColumn[y].mastery){ %>\n              <% if(masteryColumn[y].points){ %>\n                <div class=\"mastery-icon mastery-<%= masteryColumn[y].mastery %> mastery-active\" champion-tip api-type=\"masteries\" api-primary-id=\"<%= masteryColumn[y].mastery %>\" api-secondary-id=\"<%- masteryColumn[y].points-1 %>\">\n                  <div class=\"points\">\n                    <% for(var p = 0; p < masteryColumn[y].points; p++){ %>\n                      <div class=\"point\"></div>\n                    <% } %>\n                   </div>\n                </div>\n              <% } else { %>\n                <div class=\"mastery-icon mastery-<%= masteryColumn[y].mastery %>\" champion-tip api-type=\"masteries\" api-primary-id=\"<%= masteryColumn[y].mastery %>\">\n                </div>\n              <% } %>\n              <% if(z % 2 == 1 && y == 0){ %>\n                <div class=\"mastery-spacer\"></div>\n              <% } %>\n            <% } %>\n          <% } %>\n        </div>\n      <% } %>\n    </div>\n  <% } %>\n</div>\n<div class=\"build-text\">\n  <strong><%= championData.masteries.mostGames.winPercent %>%</strong> Win Rate | <strong><%= championData.masteries.mostGames.games %></strong> Games\n</div>\n\n\n<h2 class=\"champion-stats\">Highest Win % Masteries</h2>\n<div class=\"mastery-container clearfix\">\n  <% for (var k = 0; k<core.masteryOrder.length; k++){ %>\n    <div class=\"mastery<%= k %>\">\n      <div class=\"mastery-header\">\n        <%= championData.masteries.highestWinPercent.masteries[k].tree %> - <%= championData.masteries.highestWinPercent.masteries[k].total %>\n      </div>\n      <% for (var z = 1; z<=6; z++){ %>\n        <% var masteryColumn = championData.masteries.highestWinPercent.masteries[k].data['row'+z]; %>\n        <div class=\"clearfix mastery-row\">\n          <% for(var y = 0; y < masteryColumn.length; y++) { %>\n            <% if(masteryColumn[y].mastery){ %>\n              <% if(masteryColumn[y].points){ %>\n                <div class=\"mastery-icon mastery-<%= masteryColumn[y].mastery %> mastery-active\" champion-tip api-type=\"masteries\" api-primary-id=\"<%= masteryColumn[y].mastery %>\" api-secondary-id=\"<%- masteryColumn[y].points-1 %>\">\n                  <div class=\"points\">\n                    <% for(var p = 0; p < masteryColumn[y].points; p++){ %>\n                      <div class=\"point\"></div>\n                    <% } %>\n                   </div>\n                </div>\n              <% } else { %>\n                <div class=\"mastery-icon mastery-<%= masteryColumn[y].mastery %>\" champion-tip api-type=\"masteries\" api-primary-id=\"<%= masteryColumn[y].mastery %>\">\n                </div>\n              <% } %>\n              <% if(z % 2 == 1 && y == 0){ %>\n                <div class=\"mastery-spacer\"></div>\n              <% } %>\n            <% } %>\n          <% } %>\n        </div>\n      <% } %>\n    </div>\n  <% } %>\n</div>\n<div class=\"build-text\">\n  <strong><%= championData.masteries.highestWinPercent.winPercent %>%</strong> Win Rate | <strong><%= championData.masteries.highestWinPercent.games %></strong> Games\n</div>\n"
  },
  {
    "path": "views/champion/reddit.ejs",
    "content": "<h2> Recent <%= champion.name %> Threads on Reddit </h2>\n<div ng-repeat=\"thread in redditData | filter:enoughUpvotes | limitTo:5\" ng-controller=\"redditThread\">\n  <div class=\"container-reddit\">\n    <div class=\"reddit-titles\">\n      <h4>\n        <a href=\"{{thread.data.url}}\" target=\"_blank\">\n          {{thread.data.title}}\n        </a>\n      </h4>\n      <span ng-click=\"toggleSummary()\" class=\"reddit-summary\">\n        View Summary<span ng-if=\"!summaryVisible\">&#x25B6</span><span ng-if=\"summaryVisible\">&#x25BC</span>\n      </span>\n      <a href=\"{{thread.data.url}}\" target=\"_blank\" class=\"reddit-comments\">\n        {{thread.data.num_comments}} comments.\n      </a>\n      <small class=\"reddit-date\">\n        Created on <strong>{{thread.data.created * 1000 | date:'d MMMM yyyy'}}</strong>\n      </small>\n    </div>\n    <div ng-if=\"summaryVisible\" class=\"reddit-summary-area\">\n      <div ng-bind-html=\"thread.data.selftext_html | to_trusted\"></div>\n    </div>\n  </div>\n</div>"
  },
  {
    "path": "views/champion/runes.ejs",
    "content": "<h2 class=\"champion-stats\" style=\"margin-top:40px\">Most Frequent Runes</h2>\n<div class=\"rune-collection\">\n  <% for(var i = 0; i < championData.runes.mostGames.runes.length; i++){ %>\n  <div class=\"rune-type-area\" style=\"width:98%\">\n    <div class=\"rune-img\">\n      <img src=\"//ddragon.leagueoflegends.com/cdn/<%= core.ddPatch %>/img/rune/<%= championData.runes.mostGames.runes[i].img %>\" /> <strong><%= championData.runes.mostGames.runes[i].number %>x</strong> <span class=\"rune-title\"><%= championData.runes.mostGames.runes[i].name %> </span>\n      <span class=\"rune-benefits\"><small><%= championData.runes.mostGames.runes[i].description %></small></span>\n    </div>\n    <div class=\"rune-info\">\n    </div>\n  </div>\n  <% } %>\n</div>\n<div class=\"build-text\">\n  <strong><%= championData.runes.mostGames.winPercent %>%</strong> Win Rate | <strong><%= championData.runes.mostGames.games %></strong> Games\n</div>\n\n\n<h2 class=\"champion-stats\" style=\"margin-top:45px\">Highest Win % Runes</h2>\n<div class=\"rune-collection\">\n  <% for(var i = 0; i < championData.runes.highestWinPercent.runes.length; i++){ %>\n  <div class=\"rune-type-area\" style=\"width:98%\">\n    <div class=\"rune-img\">\n      <img src=\"//ddragon.leagueoflegends.com/cdn/<%= core.ddPatch %>/img/rune/<%= championData.runes.highestWinPercent.runes[i].img %>\" /> <strong><%= championData.runes.highestWinPercent.runes[i].number %>x</strong> <span class=\"rune-title\"><%= championData.runes.highestWinPercent.runes[i].name %> </span>\n      <span class=\"rune-benefits\"><small><%= championData.runes.highestWinPercent.runes[i].description %></small></span>\n    </div>\n    <div class=\"rune-info\">\n    </div>\n  </div>\n  <% } %>\n</div>\n<div class=\"build-text\">\n  <strong><%= championData.runes.highestWinPercent.winPercent %>%</strong> Win Rate | <strong><%= championData.runes.highestWinPercent.games %></strong> Games\n</div>"
  },
  {
    "path": "views/champion/skill_order.ejs",
    "content": "<h2 class=\"champion-stats\">Most Frequent Skill Order</h2>\n<div class=\"skill-order clearfix\">\n  <div class=\"skill\">\n    <div class=\"img-placeholder\"></div>\n    <div class=\"skill-selections\">\n      <% for (var d = 0; d < 18; d++){ %>\n      <div>\n        <span><%= d + 1 %></span>\n      </div>\n      <% } %>\n    </div>\n  </div>\n  <% for (var i = 0 ; i < 4; i++){ %>\n  <div class=\"skill\">\n    <img src=\"//ddragon.leagueoflegends.com/cdn/<%= core.ddPatch %>/img/spell/<%= championData.skills.skillInfo[i].img %>\" tooltip=\"<%= championData.skills.skillInfo[i].name %>\"/>\n    <div class=\"skill-selections\">\n      <% var skillGames = championData.skills.mostGames.order %>\n      <% for (var b = 0 ; b < skillGames.length; b++){ %>\n      <div class=\"<%=skillGames[b][0] === ''+(i+1) ? 'selected' : '' %>\">\n        <span><%= skillGames[b][0] === ''+(i+1) ? championData.skills.skillInfo[i].key : \"\" %></span>\n        <% if(skillGames[b].length === 3 && skillGames[b][0] === ''+(i+1)){ %>\n        <span><%= championData.skills.skillInfo[skillGames[b][2]-1].key %></span>\n        <% } %>\n      </div>\n      <% } %>\n    </div>\n  </div>\n  <% } %>\n</div>\n<div class=\"build-text\">\n  <strong><%= championData.skills.mostGames.winPercent %>%</strong> Win Rate | <strong><%= championData.skills.mostGames.games %></strong> Games\n</div>\n\n\n<h2 class=\"champion-stats\" style=\"margin-top:55px\">Highest Win % Skill Order</h2>\n<div class=\"skill-order clearfix\">\n  <div class=\"skill\">\n    <div class=\"img-placeholder\"></div>\n    <div class=\"skill-selections\">\n      <% for (var d = 0; d < 18; d++){ %>\n      <div>\n        <span><%= d + 1 %></span>\n      </div>\n      <% } %>\n    </div>\n  </div>\n  <% for (var i = 0 ; i < 4; i++){ %>\n  <div class=\"skill\">\n    <img src=\"//ddragon.leagueoflegends.com/cdn/<%= core.ddPatch %>/img/spell/<%= championData.skills.skillInfo[i].img %>\" tooltip=\"<%= championData.skills.skillInfo[i].name %>\"/>\n    <div class=\"skill-selections\">\n      <% var skillGames = championData.skills.highestWinPercent.order %>\n      <% for (var b = 0 ; b < skillGames.length; b++){ %>\n      <div class=\"<%=skillGames[b][0] === ''+(i+1) ? 'selected' : '' %>\">\n        <span><%= skillGames[b][0] === ''+(i+1) ? championData.skills.skillInfo[i].key : \"\" %></span>\n        <% if(skillGames[b].length === 3 && skillGames[b][0] === ''+(i+1)){ %>\n        <span><%= championData.skills.skillInfo[skillGames[b][2]-1].key %></span>\n        <% } %>\n      </div>\n      <% } %>\n    </div>\n  </div>\n  <% } %>\n</div>\n<div class=\"build-text\">\n  <strong> <%= championData.skills.highestWinPercent.winPercent %>%</strong> Win Rate | <strong><%= championData.skills.highestWinPercent.games %></strong> Games\n</div>"
  },
  {
    "path": "views/champion/viktor_upgrade.ejs",
    "content": "<div class=\"viktor-skills\">\n  <h2 class=\"champion-stats\">Most Frequent Upgrades</h2>\n  <% var viktor = championData.unique.mostGames; %>\n  <% for(var i = 0; i<viktor.order.length; i++){ %>\n  <div class=\"viktor-skill\">\n    <img src=\"//ddragon.leagueoflegends.com/cdn/<%= core.ddPatch %>/img/spell/<%= championData.skills.skillInfo[viktor.order[i]-1].img %>\" tooltip=\"<%= championData.skills.skillInfo[viktor.order[i]-1].name %>\"/>\n    <span class=\"viktor-key\"><%= championData.skills.skillInfo[viktor.order[i]-1].key %></span>\n    <img src=\"//ddragon.leagueoflegends.com/cdn/<%= core.ddPatch %>/img/item/<%= 3196+i %>.png\" class=\"possible-build\"/>\n  </div>\n  <% if (i!==2){ %> <small>></small> <%}%>\n  \n  <% } %>\n  <div class=\"build-text\"><strong><%= viktor.winPercent %>%</strong> Win Rate | <strong><%= viktor.games %></strong> Games </div>\n</div>\n<div class=\"viktor-skills\">\n  <h2 class=\"champion-stats\">Highest Win % Upgrades</h2>\n\n  <% var viktor = championData.unique.highestWinPercent; %>\n  <% for(var i = 0; i<viktor.order.length; i++){ %>\n  <div class=\"viktor-skill\">\n    <img src=\"//ddragon.leagueoflegends.com/cdn/<%= core.ddPatch %>/img/spell/<%= championData.skills.skillInfo[viktor.order[i]-1].img %>\" tooltip=\"<%= championData.skills.skillInfo[viktor.order[i]-1].name %>\"/>\n    <span class=\"viktor-key\"><%= championData.skills.skillInfo[viktor.order[i]-1].key %></span>\n    <img src=\"//ddragon.leagueoflegends.com/cdn/<%= core.ddPatch %>/img/item/<%= 3196+i %>.png\" class=\"possible-build\"/>\n  </div>\n  <% if (i!==2){ %> <small>></small> <%}%>\n  \n  <% } %>\n  <div class=\"build-text\"><strong><%= viktor.winPercent %>%</strong> Win Rate | <strong><%= viktor.games %></strong> Games </div>\n</div>"
  },
  {
    "path": "views/champion/winrate_playrate_damage_advert_trinket.ejs",
    "content": "<h2 class=\"line-chart-header\">Win Rate % By Patch</h2>\n<div class=\"chart-holder\">\n  <canvas tc-chartjs-line chart-data=\"patchRate.data\" chart-options=\"patchRate.settings\" id=\"canvas3\" height=\"150\" width=\"300\"></canvas>\n</div>\n<div tc-chartjs-legend chart-legend=\"playRate\"></div>\n<h2 class=\"line-chart-header\">Play Rate % By Patch</h2>\n<div class=\"chart-holder\">\n  <canvas tc-chartjs-line chart-data=\"patchPlay.data\" chart-options=\"patchPlay.settings\" chart-legend=\"playRate\" id=\"canvas\" height=\"150\" width=\"300\"></canvas>\n</div>\n<div tc-chartjs-legend chart-legend=\"playRate\"></div>\n<div class=\"center-google\">\n  <!-- /22280732/ChampionGG_300x250_Champion_ATF1 -->\n  <div id='div-gpt-ad-1443488528500-1'>\n  <script type='text/javascript'>\n  googletag.cmd.push(function() { googletag.display('div-gpt-ad-1443488528500-1'); });\n  </script>\n  </div>\n</div>\n\n<h2 class=\"champion-stats\">Damage Composition</h2>\n<div class=\"clearfix middle-graphic-holder damage-dealt\">\n  <div class=\"physical-dmg\" tooltip=\"<%= championData.dmgComposition.physicalDmg %>% Physical\" style=\"width:<%= championData.dmgComposition.physicalDmg %>%\"></div>\n  <div class=\"magic-dmg\" tooltip=\"<%= championData.dmgComposition.magicDmg %>% Magic\" style=\"left:<%= championData.dmgComposition.physicalDmg %>%;width:<%= championData.dmgComposition.magicDmg %>%\"></div>\n  <div class=\"true-dmg\" tooltip=\"<%= championData.dmgComposition.trueDmg %>% True\" style=\"right:0;width:<%= championData.dmgComposition.trueDmg %>%\"></div>\n</div>\n<ul class=\"radar-legend\" style=\"margin-bottom:20px\">\n  <li><span class=\"physical-dmg\"></span>Physical</li>\n  <li><span class=\"magic-dmg\"></span>Magic</li>\n  <li><span class=\"true-dmg\"></span>True</li>\n</ul>\n\n<div class=\"trinket-stats\">\n  <h2 class=\"champion-stats\">Trinket Stats</h2>\n  <% var trinkets = championData.trinkets; %>\n  <% for(var i = 0; i<trinkets.length; i++){ %>\n    <div style=\"width:33%;float:left;text-align:center\">\n      <img class=\"possible-build\" src=\"//ddragon.leagueoflegends.com/cdn/<%= core.ddPatch %>/img/item/<%= trinkets[i].item.id %>.png\" tooltip=\"<%= trinkets[i].item.name %>\"/>\n      <div class=\"build-text\">\n        <strong style=\"display:block\"><%= trinkets[i].winPercent %>%</strong>\n        Win Rate\n        <strong style=\"display:block;margin-top:10px\"><%= trinkets[i].games %></strong> \n        Games </div>\n    </div>        \n  <% } %>\n \n</div>\n"
  },
  {
    "path": "views/champion.ejs",
    "content": "<% include header.ejs %>\n\n<div ng-controller=\"generalChampion\">\n  <div class=\"champion-area\" ng-controller=\"championData\">\n    <div class=\"container-fluid\">\n      <div class=\"row\">\n\n        <div class=\"col-xs-12 col-sm-3 col-md-2 champion-profile\">\n          <% include champion/champion_image_roles.ejs %>\n        </div>\n\n        <div class=\"col-xs-12 col-sm-9 col-md-4 champion-statistics\">\n          <% include champion/champion_statistics.ejs %>\n        </div>\n\n        <div class=\"col-xxs-12 col-xs-6 col-sm-6 col-md-3\">\n          <% include champion/winrate_playrate_damage_advert_trinket.ejs %>\n        </div>\n\n        <div class=\"col-xxs-12 col-xs-6 col-sm-6 col-md-3\">\n          <% include champion/gamelength_experience_summoners.ejs %>\n       </div>\n\n      </div>\n    </div>\n  </div>\n\n  <div class=\"champion-area\">\n    <div class=\"container-fluid\">\n\n      <div class=\"row\">\n\n        <div class=\"col-xs-12 col-sm-12 col-md-6\">\n          <div>\n            <% if (championData.skills.highestWinPercent.winPercent) { %>\n              <% include champion/skill_order.ejs %>\n            <% } %>\n          </div>\n          <div>\n             <% if (championData.runes.highestWinPercent) { %>\n              <% include champion/runes.ejs %>\n             <% } %>\n          </div>\n        </div>\n\n        <div class=\"col-xs-12 col-sm-12 col-md-6\">\n          <div class=\"row\" style=\"margin-top:0px\">  \n            <div class=\"col-xs-12 col-sm-12 col-md-7\">\n              <% if (championData.items.highestWinPercent) { %>\n                <% include champion/core_build.ejs %>\n              <% } %>\n            </div>\n            <div class=\"col-xs-12 col-sm-12 col-md-5\">\n              <% include champion/first_items.ejs %>  \n            </div>\n          </div>\n          <div class=\"row\">\n            <% if (championData.masteries.mostGames.masteries[0]) { %>\n               <% include champion/masteries.ejs %> \n            <% } %>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n\n  <div class=\"matchups\">\n    <% include champion/counters_matchups.ejs %> \n  </div> \n\n  <div class=\"champion-area\">\n    <div class=\"container-fluid\">\n      <div class=\"row counter-row\">\n        <div class=\"col-xs-12 col-md-6\">\n         <% include champion/advertisement.ejs %>\n        </div>\n        <div class=\"col-xs-12 col-md-6\" ng-controller=\"reddit\">\n         <% include champion/reddit.ejs %>\n        </div>\n      </div>\n    </div>\n  </div>\n\n</div>\n\n<script>\n  matchupData.champion = <%- JSON.stringify(champion) %>;\n  matchupData.generalRole = <%- JSON.stringify(generalRole) %>;\n  matchupData.championData = <%- JSON.stringify(championData) %>;\n  matchupData.patchHistory = <%- JSON.stringify(core.patchHistory) %>;\n  /* Has general data on the chosen champion */\n</script>\n<% include scripts.ejs %>\n\n<% include footer.ejs %>\n"
  },
  {
    "path": "views/error.ejs",
    "content": "<% include header.ejs %>\n\n<div class=\"large-header\">\n\t<h2>Teemo, we have a problem...</h2>\n\t<h2><%= message %></h2>\n</div>\n<div style=\"width:100%;padding:35px;text-align:center\">\nThink something on the site is broken? Please send me a message... (on reddit) <a href=\"http://reddit.com/u/joeldo/\">/u/joeldo</a>\n</div>\n<% include scripts.ejs %>\n\n<% include footer.ejs %>\n"
  },
  {
    "path": "views/faq.ejs",
    "content": "<% include header.ejs %>\n\n<h2 style=\"margin-top:0px;padding-top:25px\">Frequently Asked Questions</h2>\n\n<div class=\"container-fluid\">\n<div class=\"row faq\">\n\t<div class=\"col-xs-12 col-sm-12 col-md-6 faq-column\">\n\t   <h2> What stats do you use? </h2>\n       <p> Champion.gg uses game data from Platinum to Challenger from NA, EUW, EUNE and Korea for the current patch, available through <a href=\"https://developer.riotgames.com/\">Riot's API</a>\n\n       <h2> What advantage does Champion.gg have over any other statistic sites? </h2>\n       <p> 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.</p>\n       <p>\n       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.\n       </p>\n       <p>\n       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.\n       </p>\n\n       <h2>How is the Overall Placement/Performance Ranking determined?</h2>\n       <p>\n       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. \n       </p>\n\n       <h2>How is the Counters Statistical Rating determined?</h2>\n       <p>\n       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.\n       </p>\n\n       <h2>How are the win rates/play rates of full builds calculated?</h2>\n       <p>\n       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).\n       </p>\n       <p>\n       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. \n       </p>\n\n       \n    </div>\n    <div class=\"col-xs-12 col-sm-12 col-md-6 faq-column\">\n    <h2>How do you determine what role a champion plays in?</h2>\n    <p>\n    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. \n    </p>\n\n    <h2>How frequently is data updated?</h2>\n       <p>\n       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.\n       </p>\n\n       <h2>Why does x champion have such a high/low win rate?</h2>\n       <p>\n       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.\n       </p>\n\n       <h2>How can I support Champion.gg?</h2>\n       <p>\n       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.\n       </p>\n\n       <h2>Is Champion.gg open source?</h2>\n       <p>\n       I have the web development/design side of Champion.gg on <a href=\"https://github.com/joel1st/championweb\">GitHub</a> - You can take a look for yourself! The data aggregation/processing side of things is in a private repo until I clean it up.\n       </p>\n\n       <h2> What programming language(s) did you use? </h2>\n       <p>\n       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.\n       </p>\n\n       <h2> How can I contact you? </h2>\n       <p>\n       Send me a message on Reddit at <a href=\"http://www.reddit.com/user/joeldo\">/u/joeldo</a>, through the Champion.gg <a href=\"https://www.facebook.com/championgg\">Facebook page</a> or <a href=\"https://github.com/joel1st\">GitHub</a>\n       </p>\n    </div>\n</div>\n</div>\n\n\t\n<% include scripts.ejs %>\n\n\n\n\n<% include footer.ejs %>\n"
  },
  {
    "path": "views/footer.ejs",
    "content": "<div class=\"footer-attr\">\nChampion.gg isn't endorsed by Riot Games and doesn't reflect the views or opinions of Riot Games or anyone officially involved in producing or managing League of Legends. League of Legends and Riot Games are trademarks or registered trademarks of Riot Games, Inc. League of Legends © Riot Games, Inc.\n</div>\n\n<script>\n  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){\n  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),\n  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)\n  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');\n\n  ga('create', 'UA-64942437-1', 'auto');\n  ga('send', 'pageview');\n\n</script>\n<script>\n\nga('create', 'UA-45985961-1', 'solomidnetwork.com');\nga('send', 'pageview');\n\n</script>\n<div id=\"fb-root\"></div>\n<script>(function(d, s, id) {\n  var js, fjs = d.getElementsByTagName(s)[0];\n  if (d.getElementById(id)) return;\n  js = d.createElement(s); js.id = id;\n  js.src = \"//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.0\";\n  fjs.parentNode.insertBefore(js, fjs);\n}(document, 'script', 'facebook-jssdk'));</script>\n</div>\n<!-- Quantcast Tag -->\n<script type=\"text/javascript\">\nvar _qevents = _qevents || [];\n\n(function() {\nvar elem = document.createElement('script');\nelem.src = (document.location.protocol == \"https:\" ? \"https://secure\" : \"http://edge\") + \".quantserve.com/quant.js\";\nelem.async = true;\nelem.type = \"text/javascript\";\nvar scpt = document.getElementsByTagName('script')[0];\nscpt.parentNode.insertBefore(elem, scpt);\n})();\n\n_qevents.push({\nqacct:\"p-gZzLr0R4qLK_S\"\n});\n</script>\n\n<noscript>\n<div style=\"display:none;\">\n<img src=\"//pixel.quantserve.com/pixel/p-gZzLr0R4qLK_S.gif\" border=\"0\" height=\"1\" width=\"1\" alt=\"Quantcast\"/>\n</div>\n</noscript>\n<!-- End Quantcast tag -->\n</body>\n</html>\n\n<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>\n\n  <script async src=\"//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js\"></script>\n  <script>\n  [].forEach.call(document.querySelectorAll('.adsbygoogle'), function(){\n    (adsbygoogle = window.adsbygoogle || []).push({});\n  });\n  </script>\n"
  },
  {
    "path": "views/header.ejs",
    "content": "<!doctype html>\n<html lang=\"en\" ng-app=\"<%= pageData.appName %>\"><head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n  <meta name=\"description\" content=\"<%= pageData.description %>\">\n  <title>Champion.gg - <%= pageData.title %></title>\n  <!-- Bootstrap core CSS -->\n  <link href=\"/dist/css/bootstrap.min.css\" rel=\"stylesheet\">\n  <% if(process.env.NODE_ENV === 'production') { %>\n    <link href=\"/css/master.min.css?v=<%=core.resetCache%>\" rel=\"stylesheet\">\n  <% } else { %>\n     <link href=\"/css/master.css\" rel=\"stylesheet\">\n     <link href=\"/css/sprite.css\" rel=\"stylesheet\">\n  <% } %>\n  <link rel=\"search\" href=\"/opensearchdescription.xml\" type=\"application/opensearchdescription+xml\" title=\"Champion.gg\" />\n  <script>\n   var _prum = [['id', '57277832abe53de9428062df'],\n   ['mark', 'firstbyte', (new Date()).getTime()]];\n   (function() {\n     var s = document.getElementsByTagName('script')[0]\n     , p = document.createElement('script');\n     p.async = 'async';\n     p.src = '//rum-static.pingdom.net/prum.min.js';\n     s.parentNode.insertBefore(p, s);\n   })();\n  </script>\n  <script>\n  var matchupData = {};\n  </script>\n  <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->\n  <!--[if lt IE 9]>\n  <script src=\"https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js\"></script>\n  <script src=\"https://oss.maxcdn.com/respond/1.4.2/respond.min.js\"></script>\n  <![endif]-->\n  \n  \n    <%if (pageData.name === 'home') { %>\n    <script type='text/javascript'>\n      var googletag = googletag || {};\n      googletag.cmd = googletag.cmd || [];\n      (function() {\n        var gads = document.createElement('script');\n        gads.async = true;\n        gads.type = 'text/javascript';\n        var useSSL = 'https:' == document.location.protocol;\n        gads.src = (useSSL ? 'https:' : 'http:') +\n          '//www.googletagservices.com/tag/js/gpt.js';\n        var node = document.getElementsByTagName('script')[0];\n        node.parentNode.insertBefore(gads, node);\n      })();\n    </script>\n\n    <script type='text/javascript'>\n      googletag.cmd.push(function() {\n        googletag.defineSlot('/22280732/ChampionGG_1x1_ROS_Wallpaper', [1, 1], 'div-gpt-ad-1443488528500-0').addService(googletag.pubads());\n        googletag.defineSlot('/22280732/ChampionGG_728x90_HP_ATF1', [[320, 50], [320, 100], [970, 90], [970, 250], [728, 90]], 'div-gpt-ad-1443727656867-1').addService(googletag.pubads());\n        googletag.defineSlot('/22280732/ChampionGG_300x250_HP_ATF1', [[300, 250], [300, 600], [300, 1050], [336, 280]], 'div-gpt-ad-1443488528500-4').addService(googletag.pubads());\n        googletag.defineSlot('/22280732/ChampionGG_300x250_HP_BTF1', [[300, 250], [300, 600], [300, 1050], [336, 280]], 'div-gpt-ad-1443488528500-5').addService(googletag.pubads());\n        googletag.pubads().enableSingleRequest();\n        googletag.pubads().collapseEmptyDivs();\n        googletag.enableServices();\n      });\n    </script>  \n  <% } else if (pageData.name === 'champion') { %>\n    <script type='text/javascript'>\n      var googletag = googletag || {};\n      googletag.cmd = googletag.cmd || [];\n      (function() {\n        var gads = document.createElement('script');\n        gads.async = true;\n        gads.type = 'text/javascript';\n        var useSSL = 'https:' == document.location.protocol;\n        gads.src = (useSSL ? 'https:' : 'http:') +\n          '//www.googletagservices.com/tag/js/gpt.js';\n        var node = document.getElementsByTagName('script')[0];\n        node.parentNode.insertBefore(gads, node);\n      })();\n    </script>\n\n    <script type='text/javascript'>\n      googletag.cmd.push(function() {\n        googletag.defineSlot('/22280732/ChampionGG_1x1_ROS_Wallpaper', [1, 1], 'div-gpt-ad-1443488528500-0').addService(googletag.pubads());\n        googletag.defineSlot('/22280732/ChampionGG_728x90_Champion_ATF1', [[320, 50], [320, 100], [970, 90], [970, 250], [728, 90]], 'div-gpt-ad-1443727656867-0').addService(googletag.pubads());\n        googletag.defineSlot('/22280732/ChampionGG_300x250_Champion_ATF1', [[300, 250], [300, 600], [300, 1050], [336, 280]], 'div-gpt-ad-1443488528500-1').addService(googletag.pubads());\n        googletag.defineSlot('/22280732/ChampionGG_300x250_Champion_BTF1', [[300, 250], [300, 600], [300, 1050], [336, 280]], 'div-gpt-ad-1443488528500-2').addService(googletag.pubads());\n        googletag.defineSlot('/22280732/ChampionGG_300x250_Champion_BTF2', [[300, 250], [300, 600], [300, 1050], [336, 280]], 'div-gpt-ad-1443488528500-3').addService(googletag.pubads());\n        googletag.pubads().enableSingleRequest();\n        googletag.pubads().collapseEmptyDivs();\n        googletag.enableServices();\n      });\n    </script>\n  <% } else { %>\n    <script type='text/javascript'>\n      var googletag = googletag || {};\n      googletag.cmd = googletag.cmd || [];\n      (function() {\n        var gads = document.createElement('script');\n        gads.async = true;\n        gads.type = 'text/javascript';\n        var useSSL = 'https:' == document.location.protocol;\n        gads.src = (useSSL ? 'https:' : 'http:') +\n          '//www.googletagservices.com/tag/js/gpt.js';\n        var node = document.getElementsByTagName('script')[0];\n        node.parentNode.insertBefore(gads, node);\n      })();\n    </script>\n\n    <script type='text/javascript'>\n      googletag.cmd.push(function() {\n        googletag.defineSlot('/22280732/ChampionGG_1x1_ROS_Wallpaper', [1, 1], 'div-gpt-ad-1443488528500-0').addService(googletag.pubads());\n        googletag.defineSlot('/22280732/ChampionGG_728x90_Other_ATF1', [[728, 90], [320, 50], [970, 90], [970, 250], [320, 100]], 'div-gpt-ad-1443488528500-6').addService(googletag.pubads());\n        googletag.defineSlot('/22280732/ChampionGG_728x90_Other_BTF1', [[728, 90], [320, 50], [970, 90], [970, 250], [320, 100]], 'div-gpt-ad-1443488528500-7').addService(googletag.pubads());\n        googletag.pubads().enableSingleRequest();\n        googletag.pubads().collapseEmptyDivs();\n        googletag.enableServices();\n      });\n    </script>\n  <% } %>\n  \n</head>\n<body>\n  <div class=\"primary-hue\">\n    <div class=\"main-container\">\n      <div class=\"navigation-elem\">\n        <div class=\"inner-nav clearfix\">\n          <ul class=\"nav navbar-nav\">\n            <li class=\"first-button <%if (pageData.name === 'home') { %> active <% } %>\"><a href=\"/\">Home</a></li>\n            <li class=\"<%if (pageData.name === 'stats') { %> active <% } %>\"><a href=\"/statistics/\">Statistics</a></li>\n            <li class=\"<%if (pageData.name === 'faq') { %> active <% } %>\"><a href=\"/faq/\">F.A.Q.</a></li>\n          </ul>\n          \n          <div class=\"social-media\">\n            <a href=\"https://twitter.com/share\" class=\"twitter-share-button\" data-via=\"champion_gg\">Tweet</a>\n            <div class=\"fb-like\" data-href=\"https://www.facebook.com/championgg\" data-layout=\"button_count\" data-action=\"like\" data-show-faces=\"false\" data-share=\"false\"></div>\n          </div>\n          <div class=\"update-happening\">\n            <% if (core.headline) { %>\n             <span class=\"important-message\"> <%= core.headline %> </span>\n            <% } %>\n          </div>\n        </div>\n      </div>\n      <div class=\"navbar navbar-inverse\" role=\"navigation\">\n        <div class=\"container-fluid\">\n         <div class=\"row clearfix\">\n            <div class=\"logo-holder\">\n            <a class=\"navbar-brand\" href=\"/\"><img src=\"/img/logo.png\" /></a>\n          </div>\n          <div class=\"search-holder\">\n            \n            <div class=\"search-fb-holder\">\n              <div class=\"input-group\" ng-controller=\"searchCtrl\">\n\n                <input type=\"text\" class=\"form-control\" placeholder=\"Champion Name\" id=\"query\" value=\"\" ng-model=\"selected\" ng-model-options=\"{debounce: 40}\" typeahead=\"champ as champ.name for champ in championMenu | startsWith:$viewValue | limitTo: 12\" typeahead-template-url=\"menu.html\" style=\"width:188px;display: inline-block;\" ng-keyup=\"determineSend($event.keyCode)\" autocomplete=\"off\">\n                <div class=\"input-group-btn\" style=\"cursor:auto;display: inline-block;\">  \n                  <button ng-click=\"getPage()\" style=\"cursor:auto;\" type=\"submit\" class=\"btn btn-success\"><span style=\"cursor:auto;\" class=\"glyphicon glyphicon-search\" ></span></button>\n                </div>\n              \n              </div>\n            </div>\n            \n          </div>\n          <div class=\"navigation-holder cleafix\">\n            <!--<a class=\"donate\" href=\"https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=championgg%40outlook%2ecom&lc=AU&item_name=ChampionGG&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted\" alt=\"donate\"> Donate </a>-->\n            <div class=\"analysis-holder\">\n              <small>\n                Patch \n                <strong><%=core.patch%></strong> \n                <span class=\"spacer\">|</span> \n                Champions Analyzed \n                <strong><%=core.championsAnalyzed%></strong>\n                <span class=\"spacer\">|</span> \n                <strong> Platinum+ Ranked </strong>\n              </small> \n            </div>\n            \n          </div>\n        </div>\n      </div>\n    </div>\n    <div class=\"page-content\">\n\n\n    <div style=\"text-align:center\">\n    <%if (pageData.name === 'home') { %>\n      <!-- /22280732/ChampionGG_728x90_HP_ATF1 -->\n      <div id='div-gpt-ad-1443727656867-1'>\n      <script type='text/javascript'>\n      googletag.cmd.push(function() { googletag.display('div-gpt-ad-1443727656867-1'); });\n      </script>\n      </div>\n    <% } else if (pageData.name === 'champion') { %>\n      <!-- /22280732/ChampionGG_728x90_Champion_ATF1 -->\n      <div id='div-gpt-ad-1443727656867-0'>\n      <script type='text/javascript'>\n      googletag.cmd.push(function() { googletag.display('div-gpt-ad-1443727656867-0'); });\n      </script>\n      </div>\n    <% } else { %>\n      <!-- /22280732/ChampionGG_728x90_Other_ATF1 -->\n      <div id='div-gpt-ad-1443488528500-6'>\n      <script type='text/javascript'>\n      googletag.cmd.push(function() { googletag.display('div-gpt-ad-1443488528500-6'); });\n      </script>\n      </div>\n    <% } %>\n    </div>\n    \n    \n    <noscript><div class=\"update-happening\">To make the most of this website, we strongly recommend turning JavaScript on! It powers the graphs, data sorting and other cool features!</div></noscript>\n"
  },
  {
    "path": "views/index.ejs",
    "content": "<% include header.ejs %>\n<div class=\"container-fluid\">\n\t<div class=\"row summary\">\n\t\t<div class=\"win-summary summary-side\" style=\"border-right: 1px solid rgb(36, 45, 48);\">\n\t\t\t<span class=\"champion-name champion-list-header\"><a href=\"statistics/#?sortBy=general.winPercent&order=descend\"><span class=\"summary-highlight\">\n\t\t\tWin Rates</span></a></span>\n\n\t\t\t<table class=\"table table-striped\">\n              <thead>\n                <tr >\n                  <th class=\"first-summary\">Role</th>\n                  <th class=\"second-summary\"><span class=\"top-half\">Highest</span></th>\n                  <th class=\"third-summary\"><span class=\"bottom-half\">Lowest</span></th>\n                </tr>\n              </thead>\n              <tbody>\n                 <% for(var y=0;y<summaries.length;y++){ %>\n                 <tr>\n                  <td><a href=\"statistics/#?sortBy=general.winPercent&order=descend&roleSort=<%= summaries[y].title %>\"><%= summaries[y].title %></a></td>\n                  <td><a href=\"/champion/<%=summaries[y].highestWinRate.key%>/<%=summaries[y].title%>\">\n\t\t\t\t\t\t\t<div class=\"matchup-champion <%=summaries[y].highestWinRate.key%>\"></div>\n\t\t\t\t\t\t\t\t<span class=\"champion-name\"><%= summaries[y].highestWinRate.name %></span>\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t<span class=\"top-half summary-value\"><%= summaries[y].highestWinRate.value %>%</span>\n\t\t\t\t\t</td>\n                  <td>  \n                    <a href=\"/champion/<%=summaries[y].lowestWinRate.key%>/<%=summaries[y].title%>\">\n\t\t\t\t\t\t\t<div class=\"matchup-champion <%=summaries[y].lowestWinRate.key%>\"></div>\n\t\t\t\t\t\t\t\t<span class=\"champion-name\"><%= summaries[y].lowestWinRate.name %></span>\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t<span class=\"bottom-half summary-value\"><%= summaries[y].lowestWinRate.value %>%</span>\n                  </td>\n                  </tr>\n\n                <% } %>\n              </tbody>\n            </table>\n\t\t</div>\n\t\t<div class=\"overall-summary summary-side\" style=\"border-right: 1px solid rgb(36, 45, 48);position:relative\">\n\t\t\t<span class=\"champion-name champion-list-header\"><a href=\"statistics/#?sortBy=general.overallPosition&order=ascend\"><span class=\"summary-highlight\">Overall Performance Ranking</span></a></span><span class=\"glyphicon glyphicon-question-sign\" style=\"position: absolute;right: 20px;top:20px;\" tooltip=\"Overall Performance takes more than win rate into account - including play rate, ban rate, kda, gold, cs, damage and other role dependant stats.\"></span>\n\t\t\t<table class=\"table table-striped\">\n              <thead>\n                <tr >\n                  <th style=\"width:22%\">Role</th>\n                  <th style=\"width:39%\"><span class=\"top-half\">Best</span></th>\n                  <th style=\"width:39%\"><span class=\"bottom-half\">Worst</span></th>\n                </tr>\n              </thead>\n              <tbody>\n                 <% for(var l=0;l<summaries.length;l++){ %>\n                 <tr>\n                  <td><a href=\"statistics/#?sortBy=general.overallPosition&order=ascend&roleSort=<%= summaries[l].title %>\"><%= summaries[l].title %></a></td>\n                  <td><a href=\"/champion/<%=summaries[l].bestOverall.key%>/<%=summaries[l].title%>\">\n\t\t\t\t\t\t\t<div class=\"matchup-champion <%=summaries[l].bestOverall.key%>\"></div>\n\t\t\t\t\t\t\t<span class=\"champion-name\"><%= summaries[l].bestOverall.name %></span></a>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n                  <td>  \n                    <a href=\"/champion/<%=summaries[l].worstOverall.key%>/<%=summaries[l].title%>\">\n\t\t\t\t\t\t\t<div class=\"matchup-champion <%=summaries[l].worstOverall.key%>\"></div>\n\t\t\t\t\t\t\t<span class=\"champion-name\"><%= summaries[l].worstOverall.name %></span></a>\n                  </td>\n                  </tr>\n\n                <% } %>\n              </tbody>\n            </table>\n\t\t\t\n\t\t</div>\n\t\t<div class=\"change-summary summary-side\">\n\t\t\t<span class=\"champion-name champion-list-header\"><a href=\"statistics/#?sortBy=general.overallPositionChange&order=descend\"><span class=\"summary-highlight\">Champion Ranking Changes</span></a></span>\n\n\t\t\t<table class=\"table table-striped\">\n              <thead>\n                <tr >\n                  <th class=\"first-summary\">Role</th>\n                  <th class=\"second-summary\"><span class=\"top-half\">Most Improved</span></th>\n                  <th class=\"third-summary\"><span class=\"bottom-half\">Least Improved</span></th>\n                </tr>\n              </thead>\n              <tbody>\n                 <% for(var x=0;x<summaries.length;x++){ %>\n                 <tr>\n                  <td><a href=\"statistics/#?sortBy=general.overallPositionChange&order=descend&roleSort=<%= summaries[x].title %>\"><%= summaries[x].title %></a></td>\n                  <td><a href=\"/champion/<%=summaries[x].mostImproved.key%>/<%=summaries[x].title%>\">\n\t\t\t\t\t\t\t<div class=\"matchup-champion <%=summaries[x].mostImproved.key%>\"></div>\n\t\t\t\t\t\t\t<span class=\"champion-name\"><%= summaries[x].mostImproved.name %></span></a>\n\t\t\t\t\t\t\t<span class=\"top-half summary-value\"><span class=\"glyphicon  glyphicon-arrow-up\"></span> <%=summaries[x].mostImproved.difference %></span>\n\t\t\t\t\t\t\t</td>\n                  <td>  \n                    <a href=\"/champion/<%=summaries[x].leastImproved.key%>/<%=summaries[x].title%>\">\n\t\t\t\t\t\t\t<div class=\"matchup-champion <%=summaries[x].leastImproved.key%>\"></div>\n\t\t\t\t\t\t\t<span class=\"champion-name\"><%= summaries[x].leastImproved.name %></span></a>\n\t\t\t\t\t\t\t<span class=\"bottom-half summary-value\"><span class=\"glyphicon  glyphicon-arrow-down\"></span> <%=summaries[x].leastImproved.difference %> </span>\n                  </td>\n                  </tr>\n\n                <% } %>\n              </tbody>\n            </table>\n\t\t</div>\n\t</div>\n\n\t\t\n\n\t<div class=\"row\" style=\"margin-top:40px\" id=\"home\">\n\t\t<span class=\"champion-name champion-list-header\">Choose a champion for <span class=\"summary-highlight\">in-depth statistics</span> and <span class=\"summary-highlight\">counters</span>.</span>\n\t\t<div class=\"col-md-9 clearfix\" style=\"margin-top:15px;\">\n\t\t<% for(var i=0;i<data.length;i++){ %>\n\t\t\t<div class=\"champ-height\">\n\t\t\t\t<div class=\"champ-index-img <%= data[i].key %>\">\n\t\t\t\t<a href=\"/champion/<%= data[i].key %>\">\n\t\t\t\t<div class=\"home-champion <%= data[i].key %>\"></div>\n\t\t\t\t<span class=\"champion-name\"><%= data[i].name %></span>\n\t\t\t\t</a>\n\t\t\t\t\t<% for(var t=0;t<data[i].roles.length;t++){ %>\n\t\t\t\t\t\t<a href=\"/champion/<%= data[i].key %>/<%= data[i].roles[t].title %>\" style=\"display:block\">\n\t\t\t\t\t\t\t<%= data[i].roles[t].title %>\n\t\t\t\t\t\t</a>\n\t\t\t\t\t<% } %>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t<% } %>\n\t\t</div>\n\t\t<div class=\"col-md-3\">\n\t\t      <!-- /22280732/ChampionGG_300x250_HP_ATF1 -->\n\t\t      <div id='div-gpt-ad-1443488528500-4'>\n\t\t      <script type='text/javascript'>\n\t\t      googletag.cmd.push(function() { googletag.display('div-gpt-ad-1443488528500-4'); });\n\t\t      </script>\n\t\t      </div>\n\t\t\n\t\t      <!-- /22280732/ChampionGG_300x250_HP_BTF1 -->\n\t\t      <div id='div-gpt-ad-1443488528500-5'>\n\t\t      <script type='text/javascript'>\n\t\t      googletag.cmd.push(function() { googletag.display('div-gpt-ad-1443488528500-5'); });\n\t\t      </script>\n\t\t      </div>\n\t\t</div>\n\n\n\t</div>\n</div>\n\n<% include scripts.ejs %>\n\n<% include footer.ejs %>\n"
  },
  {
    "path": "views/matchup.ejs",
    "content": "<% include header.ejs %>\n<div class=\"container-fluid\" id=\"matchups\">\n<h2>\n  <%= data.champ1.name %> <%= data.champ1.roleTitle %> <%= (data.role==='SYNERGY')? 'Synergy With' : 'Against'%> <%= data.champ2.name %> <%= data.champ2.roleTitle %>\n</h2>\n<div class=\"row champion-area matchup-area\">\n  <% for(var t=1; t<3; t++) {%>\n\n  <div class=\"col-xs-6 col-sm-3 col-md-2 matchup-block champion-profile <% if (t===2) { %>pull-right<% } %>\">\n   <a href=\"/champion/<%- data['champ'+t].key %>/<%- data['champ'+t].roleTitle %>\">\n    <img src=\"//ddragon.leagueoflegends.com/cdn/<%- core.ddPatch %>/img/champion/<%- data['champ'+t].key %>.png\" class=\"champ-img\"/>\n    <h1 class=\"champ<%=t%>\"><%-data['champ'+t].name%></h1>\n    <ul>\n      <li>      \n          <h3 class=\"selected\">\n              <%- data['champ'+t].roleTitle %>\n          </h3>         \n        </li>      \n    </ul>\n    </a>\n  </div>\n  <% } %>\n  <div class=\"col-xs-12 col-sm-6 col-md-8 matchup-block\" style=\"text-align:center\">\n\n\n    <h3>\n    <% if(data.role !== 'SYNERGY'){ %>\n      <% if(data.champ1.performance >= 5.75){ %>  <span class=\"victor1\"><%=data.champ1.name%></span> has the advantage! <% } %>\n      <% if(data.champ1.performance <= 4.25) { %>  <span class=\"victor2\"><%=data.champ2.name%></span> has the advantage! <% } %> \n      <% if(!(data.champ1.performance >= 5.75)&&!(data.champ1.performance <= 4.25)){ %>\n        Skill based matchup!\n      <% } %>\n    <% } else { %>\n      <% if(data.champ1.performance >= 5.75){ %>  <span class=\"victor1\"><%=data.champ1.name%></span> and <span class=\"victor2\"><%=data.champ2.name%></span> have Strong Synergy! <% } %>\n      <% if(data.champ1.performance <= 4.25) { %>  <span class=\"victor1\"><%=data.champ1.name%></span> and <span class=\"victor2\"><%=data.champ2.name%></span> have Weak Synergy! <% } %> \n      <% if(!(data.champ1.performance >= 5.75)&&!(data.champ1.performance <= 4.25)){ %>\n        Standard Synergy\n      <% } %>\n    <% } %>\n\n    </h3>\n\n\n\n  \t<strong> Total Games Analyzed: <%= data.totalGames %> </strong> <br />\n        <div class=\"matchup-progress-bars <%if(data.role === 'SYNERGY'){%> synergy <%}%>\">\n                <h4> Statistical Performance </h4>\n                <div class=\"progress\">\n                  <strong class=\"statistic-rating\"> </strong>\n                  <div class=\"progress-bar\" role=\"progressbar\" aria-valuenow=\"60\" aria-valuemin=\"0\" aria-valuemax=\"100\"  style=\"width:<%- data.champ1.performance * 10 %>%\"></div>\n                </div>           \n      </div>\n\n\n \t       <% if(data.role !== 'ADCSUPPORT' && data.role !== 'SYNERGY'){ %>\n           <div class=\"row\" ng-controller=\"matchupGraphs\">\n              <div class=\"col-xs-12 col-sm-12 col-md-6 matchup-block\" style=\"border-right: 1px solid rgb(59, 62, 66);\">\n              \t<div class=\"matchup-chart-holder\">\n                  <h4> Matchup Performance </h4>\n                  <div>\n                    <canvas tc-chartjs-radar chart-data=\"champComparison.data\" chart-options=\"champComparison.settings\" chart-legend=\"championMatchup\" height=\"400\" width=\"560\"></canvas>\n                  </div>\n                 </div>\n              </div>\n              <div class=\"col-xs-12 col-sm-12 col-md-6\">\n              <div class=\"matchup-chart-holder\">\n                <h4> Gold Over Time </h4>\n                <div>\n                  <canvas tc-chartjs-line chart-data=\"goldIncome.data\" chart-options=\"goldIncome.settings\" height=\"400\" width=\"560\"> </canvas>\n                </div>\n                </div>\n              </div>\n              \n              <div tc-chartjs-legend chart-legend=\"championMatchup\"></div>\n            </div>\n          <% } %>\n\n            <div class=\"row chart-area matchup-stat-area\">\n             <div class=\"col-xs-12 col-sm-12 col-md-12 matchup-block\">\n              <div class=\"matchup-title-width matchup-div-header\"></div><div class=\"matchup-champ-img-width matchup-div-header\">\n                <div class=\"matchup-champion matchup-table <%= data.champ1.key %>\"></div>\n                </div><div class=\"matchup-champ-img-width matchup-div-header\">\n                <div class=\"matchup-champion matchup-table <%= data.champ2.key %>\"></div>\n                </div>\n              <table class=\"table table-striped\">\n              <thead>  \n                <tr>\n                  <th class=\"matchup-title-width\">Type</th>\n                  <th class=\"matchup-values-width\">Matchup Average</th>\n                  <th class=\"matchup-values-width\">Change</th>\n                  <th class=\"matchup-values-width\">Matchup Average</th>\n                  <th class=\"matchup-values-width\">Change</th>\n                </tr>\n              </thead>\n              <tbody style=\"text-align:left\">\n                <% for(var i=0; i<data.general.length; i++) {%>\n                     <tr>\n                  <td class=\"matchup-title-width\"><%=data.general[i].title%></td>\n                  <td class=\"matchup-values-width\"><%=data.general[i].champ1.val%><% if (data.general[i].title === 'Win Rate'){%>%<% } %></td>\n                  <td class=\"matchup-values-width\"> <span class=\"glyphicon <% if (data.general[i].title === 'Deaths'){%> Deaths-title <% } %><%if (data.general[i].champ1.change > 0) { %> glyphicon-arrow-up <% } if (data.general[i].champ1.change < 0) { %> glyphicon-arrow-down <% } %>\"></span><%- Math.abs(data.general[i].champ1.change) %></td>\n                  <td class=\"matchup-values-width\"><%=data.general[i].champ2.val%><% if (data.general[i].title === 'Win Rate'){%>%<% } %></td>\n                   <td class=\"matchup-values-width\"> <span class=\"glyphicon  <% if (data.general[i].title === 'Deaths'){%> Deaths-title <% } %><%if (data.general[i].champ2.change > 0) { %> glyphicon-arrow-up <% } if (data.general[i].champ2.change < 0) { %> glyphicon-arrow-down <% } %>\"></span><%- Math.abs(data.general[i].champ2.change) %></small></td>    \n                </tr>\n                  <% } %>\n               \n              </tbody>\n            </table>\n               \n            </div>\n         </div>\n    <div class=\"row\">\n      <disqus disqus-shortname=\"championgg\" disqus-identifier=\"/matchup/<%= data.champ1.key %>/<%= data.champ2.key %>/<%= data.roleTitle %>\" disqus-url=\"http://champion.gg/matchup/<%= data.champ1.key %>/<%= data.champ2.key %>/<%= data.roleTitle %>\" disqus-id=\"disqus_thread\" ready-to-bind=\"true\" ></disqus>\n    </div>\n</div>\n</div>\n\n<script>\n  matchupData.champ1 = <%- JSON.stringify(data.champ1) %>\n  matchupData.champ2 = <%- JSON.stringify(data.champ2) %>\n\tmatchupData.championMatrix = <%- JSON.stringify(data.championMatrix) %>;\n\tmatchupData.goldLength = <%- JSON.stringify(data.goldLength) %>;\n</script>\n\n<% include scripts.ejs %>\n<% include footer.ejs %>\n"
  },
  {
    "path": "views/new_champion.ejs",
    "content": "<% include header.ejs %>\n\n<div class=\"large-header\">\n\t<div class=\"container-fluid\">\n\t\t<div class=\"row\">\n\t          <div class=\"col-xs-12 col-sm-4 col-md-3 champion-profile\">\n\t            <img src=\"//ddragon.leagueoflegends.com/cdn/<%= core.ddPatch %>/img/champion/<%= champion.key %>.png\" class=\"champ-img\"/>\n\t            <h1><%= champion.name %></h1>\n\t          </div>\n\t          <div class=\"col-xs-12 col-sm-8 col-md-9\">\n\t            <h2 style=\"margin-top:30px\"> \n\t            \tWe're currently in the process of generating stats for <%= champion.name %> - Check Back Soon! \n\t            </h2>\n\t          </div>\n\t    </div>\n\t</div>\n</div>\n<div style=\"width:100%;padding:35px;text-align:center\">\nThink something on the site is broken? Please send me a message... (on reddit) <a href=\"http://reddit.com/u/joeldo/\">/u/joeldo</a>\n</div>\n<% include scripts.ejs %>\n\n<% include footer.ejs %>\n"
  },
  {
    "path": "views/scripts.ejs",
    "content": "<% if(process.env.NODE_ENV === 'production') { %>\n\t<script src=\"/js/master.min.js?v=<%=core.resetCache%>\"></script>\n<% } else { %>\n\t<script src=\"/dist/js/angular.js\"></script>\n\t<script src=\"/dist/js/angular-bootstrap.js\"></script>\n\t<script src=\"/dist/js/chart.js\"></script>\n\t<script src=\"/dist/js/dirDisqus.js\"></script>\n\t<script src=\"/dist/js/tc-angular-chartjs.js\"></script>\n\n\t<script src=\"/js/champion_data.js\"></script>\n\t<script src=\"/js/chart_options.js\"></script>\n\t<script src=\"/js/championgg_tooltip.js\"></script>\n\t<script src=\"/js/app.js\"></script>\n\t<script src=\"/js/champion_page.js\"></script>\n\t<script src=\"/js/matchup_page.js\"></script>\n\t<script src=\"/js/statistics_page.js\"></script>\n<% } %>\n\n\t"
  },
  {
    "path": "views/statistics.ejs",
    "content": "<% include header.ejs %>\n\n\n<div class=\"container-fluid\">\n  <h2 style=\"margin-top: 35px;\n  margin-bottom: -5px;\">Current Patch Statistics</h2>\n  <div class=\"row\" id=\"stats\"  ng-controller=\"data\">\n    <div class=\"search-sorter\" style=\"display:inline-block;\">\n      <input class=\"matchupSearch\" type=\"text\" ng-model=\"search.title\" ng-change=\"searchUrl()\" ng-model-options=\"{debounce: 40}\" style=\"margin-left:-22px;\" placeholder=\"Filter By Name\" />\n    </div>\n    <div style=\"display:inline-block\">\n      Sort Role \n      <select class=\"minimum-games\" ng-change=\"chosenRole()\" ng-model=\"roleSort.role\" style=\"padding:5px\">\n        <option value=\"\">Show All</option>\n        <option value=\"Top\">Top</option>\n        <option value=\"Middle\">Middle</option>\n        <option value=\"Support\">Support</option>\n        <option value=\"ADC\">ADC</option>\n        <option value=\"Jungle\">Jungle</option>\n      </select>\n    </div>\n\n    <table class=\"table table-striped\" id=\"table-1\" style=\"table-layout: auto;\">\n      <thead id=\"original-header\">\n        <tr ng-class=\"{'down':determineOrder('down'),'up':determineOrder('up')}\">\n          <td style=\"width:3%\" ng-click=\"changeOrder()\"><span>Rank</span></td>\n          <td ng-class=\"{'selected-column':determineSelected('title')}\" style=\"width:10%;min-width:134px\" ng-click=\"changeSelection('title')\"><span>Champion</span></td>\n          <td ng-class=\"{'selected-column':determineSelected('role')}\" ng-click=\"changeSelection('role')\"><span>Role</span></td>\n          <td ng-class=\"{'selected-column':determineSelected('winPercent')}\" ng-click=\"changeSelection('winPercent')\"><span>Win Percent</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('playPercent')}\" ng-click=\"changeSelection('playPercent')\"><span>Play Percent</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('banRate')}\" ng-click=\"changeSelection('banRate')\"><span>Ban Rate</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('experience')}\" ng-click=\"changeSelection('experience')\"><span>Playerbase Avg. Games</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('kills')}\" style=\"width: 4%;\"ng-click=\"changeSelection('kills')\"><span>Kills</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('deaths')}\" style=\"width: 5%;\" ng-click=\"changeSelection('deaths')\"><span>Deaths</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('assists')}\" style=\"width: 5%;\" ng-click=\"changeSelection('assists')\"><span>Assists</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('largestKillingSpree')}\" ng-click=\"changeSelection('largestKillingSpree')\"><span>Largest Killing Spree</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('totalDamageDealtToChampions')}\" ng-click=\"changeSelection('totalDamageDealtToChampions')\"><span>Damage Dealt</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('totalDamageTaken')}\" ng-click=\"changeSelection('totalDamageTaken')\"><span>Damage Taken</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('totalHeal')}\" ng-click=\"changeSelection('totalHeal')\"><span>Total Healing</span></td>\n\n          <td  ng-class=\"{'selected-column':determineSelected('minionsKilled')}\" ng-click=\"changeSelection('minionsKilled')\"><span>Minions Killed</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('neutralMinionsKilledEnemyJungle')}\" ng-click=\"changeSelection('neutralMinionsKilledEnemyJungle')\"><span>Enemy Jungle CS</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('neutralMinionsKilledTeamJungle')}\" ng-click=\"changeSelection('neutralMinionsKilledTeamJungle')\"><span>Team Jungle CS</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('goldEarned')}\" ng-click=\"changeSelection('goldEarned')\"><span>Gold Earned</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('overallPosition')}\" ng-click=\"changeSelection('overallPosition')\"><span>Role Position</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('overallPositionChange')}\" ng-click=\"changeSelection('overallPositionChange')\"><span>Position Change</span></td>\n        </tr>\n      </thead>\n      <thead id=\"header-fixed\">\n        <tr ng-class=\"{'down':determineOrder('down'),'up':determineOrder('up')}\">\n          <td style=\"width:3%\" ng-click=\"changeOrder()\"><span>Rank</span></td>\n          <td ng-class=\"{'selected-column':determineSelected('title')}\" style=\"width:10%;min-width:134px\" ng-click=\"changeSelection('title')\"><span>Champion</span></td>\n          <td ng-class=\"{'selected-column':determineSelected('role')}\" ng-click=\"changeSelection('role')\"><span>Role</span></td>\n          <td ng-class=\"{'selected-column':determineSelected('winPercent')}\" ng-click=\"changeSelection('winPercent')\"><span>Win Percent</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('playPercent')}\" ng-click=\"changeSelection('playPercent')\"><span>Play Percent</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('banRate')}\" ng-click=\"changeSelection('banRate')\"><span>Ban Rate</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('experience')}\" ng-click=\"changeSelection('experience')\"><span>Playerbase Avg. Games</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('kills')}\" style=\"width: 4%;\"ng-click=\"changeSelection('kills')\"><span>Kills</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('deaths')}\" style=\"width: 5%;\" ng-click=\"changeSelection('deaths')\"><span>Deaths</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('assists')}\" style=\"width: 5%;\" ng-click=\"changeSelection('assists')\"><span>Assists</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('largestKillingSpree')}\" ng-click=\"changeSelection('largestKillingSpree')\"><span>Largest Killing Spree</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('totalDamageDealtToChampions')}\" ng-click=\"changeSelection('totalDamageDealtToChampions')\"><span>Damage Dealt</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('totalDamageTaken')}\" ng-click=\"changeSelection('totalDamageTaken')\"><span>Damage Taken</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('totalHeal')}\" ng-click=\"changeSelection('totalHeal')\"><span>Total Healing</span></td>\n\n          <td  ng-class=\"{'selected-column':determineSelected('minionsKilled')}\" ng-click=\"changeSelection('minionsKilled')\"><span>Minions Killed</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('neutralMinionsKilledEnemyJungle')}\" ng-click=\"changeSelection('neutralMinionsKilledEnemyJungle')\"><span>Enemy Jungle CS</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('neutralMinionsKilledTeamJungle')}\" ng-click=\"changeSelection('neutralMinionsKilledTeamJungle')\"><span>Team Jungle CS</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('goldEarned')}\" ng-click=\"changeSelection('goldEarned')\"><span>Gold Earned</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('overallPosition')}\" ng-click=\"changeSelection('overallPosition')\"><span>Role Position</span></td>\n          <td  ng-class=\"{'selected-column':determineSelected('overallPositionChange')}\" ng-click=\"changeSelection('overallPositionChange')\"><span>Position Change</span></td>\n        </tr>\n      </thead>\n\n      <tbody ng-cloak>\n       <tr ng-repeat=\"champion in filteredChampions = (championData | startsWith:search.title | filter:roleSort | orderBy:[order+sortExpression.sortBy,order+sortExpression.lastSortBy])\">\n        <td class=\"rank\">{{indexNumber($index, filteredChampions.length)}}</td>\n        <td ng-class=\"{'selected-column':determineSelected('title')}\">\n          <a href=\"/champion/{{champion.key}}/{{champion.role}}\">\n            <div class=\"matchup-champion {{champion.key}}\"></div>\n            <span class=\"stat-champ-title\">{{champion.title}}</span>\n          </a>\n        </td>\n        <td class=\"stats-role-title\" ng-class=\"{'selected-column':determineSelected('role')}\" >{{champion.role}}</td>\n        <td ng-class=\"{'selected-column':determineSelected('winPercent')}\">\n          <span ng-class=\"{'top-half': (champion.general.winPercent >= 50), 'bottom-half': (champion.general.winPercent < 50)}\">{{champion.general.winPercent}}%</span>\n        </td>\n        <td  ng-class=\"{'selected-column':determineSelected('playPercent')}\">{{champion.general.playPercent}}%</td>\n        <td  ng-class=\"{'selected-column':determineSelected('banRate')}\">{{champion.general.banRate}}%</td>\n        <td  ng-class=\"{'selected-column':determineSelected('experience')}\">{{champion.general.experience}}</td>\n        <td  ng-class=\"{'selected-column':determineSelected('kills')}\">{{champion.general.kills}}</td>\n        <td  ng-class=\"{'selected-column':determineSelected('deaths')}\">{{champion.general.deaths}}</td>\n        <td  ng-class=\"{'selected-column':determineSelected('assists')}\">{{champion.general.assists}}</td>\n        <td ng-class=\"{'selected-column':determineSelected('largestKillingSpree')}\" >{{champion.general.largestKillingSpree}}</td>\n        <td ng-class=\"{'selected-column':determineSelected('totalDamageDealtToChampions')}\">{{champion.general.totalDamageDealtToChampions}}</td>\n        <td ng-class=\"{'selected-column':determineSelected('totalDamageTaken')}\">{{champion.general.totalDamageTaken}}</td>\n        <td  ng-class=\"{'selected-column':determineSelected('totalHeal')}\">{{champion.general.totalHeal}}</td>\n\n        <td  ng-class=\"{'selected-column':determineSelected('minionsKilled')}\">{{champion.general.minionsKilled}}</td>\n        <td ng-class=\"{'selected-column':determineSelected('neutralMinionsKilledEnemyJungle')}\">{{champion.general.neutralMinionsKilledEnemyJungle}}</td>\n        <td ng-class=\"{'selected-column':determineSelected('neutralMinionsKilledTeamJungle')}\">{{champion.general.neutralMinionsKilledTeamJungle}}</td>\n        <td ng-class=\"{'selected-column':determineSelected('goldEarned')}\">{{champion.general.goldEarned}}</td>\n        <td ng-class=\"{'selected-column':determineSelected('overallPosition')}\">{{champion.general.overallPosition}}</td>\n        <td ng-class=\"{'selected-column':determineSelected('overallPositionChange')}\"><span class=\"glyphicon\" ng-class=\"{'glyphicon-arrow-up': (champion.general.overallPositionChange > 0), 'glyphicon-arrow-down': (champion.general.overallPositionChange < 0), 'same-position': (champion.general.overallPositionChange === 0)}\">{{Math.abs(champion.general.overallPositionChange)}}</span></td>\n      </tbody>\n    </table>\n  </div>\n</div>\n\n<div style=\"text-align:center\">\n  <!-- /22280732/ChampionGG_728x90_Other_BTF1 -->\n  <div id='div-gpt-ad-1443488528500-7'>\n  <script type='text/javascript'>\n  googletag.cmd.push(function() { googletag.display('div-gpt-ad-1443488528500-7'); });\n  </script>\n  </div>\n</div>\n\n<script>\n  matchupData.stats = <%- JSON.stringify(data) %>;\n</script>\n<% include scripts.ejs %>\n<script src=\"//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\n<script src=\"/js/statistics_jquery.js?v=<%=core.resetCache%>\" ></script>\n\n<% include footer.ejs %>\n"
  }
]