484 lines
15 KiB
CoffeeScript
484 lines
15 KiB
CoffeeScript
isMobileOrAndroid = ->
|
|
return true if /Mobile/i.test(window.navigator.userAgent)
|
|
return true if /Android/i.test(window.navigator.userAgent)
|
|
return false
|
|
|
|
# helper which attaches selectize
|
|
attachSelectize = ($element, options) ->
|
|
# Block selectize on mobile and all android operating systems (All androids are blocked due to a bug)
|
|
return if isMobileOrAndroid()
|
|
$element.selectize(options)
|
|
|
|
setSelectizeValue = ($element, value) ->
|
|
if isMobileOrAndroid()
|
|
$element.val(value)
|
|
else
|
|
$element.each ->
|
|
@selectize?.setValue(value)
|
|
|
|
setSelectizeDisabled = ($element, disabled) ->
|
|
$element.filter(".selectized").each ->
|
|
return unless @selectize
|
|
if disabled then @selectize.disable() else @selectize.enable()
|
|
|
|
class @PokemonEditView extends Backbone.View
|
|
editTemplate: JST['teambuilder/pokemon']
|
|
speciesTemplate: JST['teambuilder/species']
|
|
nonStatsTemplate: JST['teambuilder/non_stats']
|
|
movesTemplate: JST['teambuilder/moves']
|
|
|
|
events:
|
|
'change .sortSpecies': 'changeSort'
|
|
'change .species_list': 'changeSpecies'
|
|
'change .selected_nickname': 'changeNickname'
|
|
'click .selected_shininess': 'changeShiny'
|
|
'click .selected_happiness': 'changeHappiness'
|
|
'change .selected-forme': 'changeForme'
|
|
'change .selected_nature': 'changeNature'
|
|
'change .selected_ability': 'changeAbility'
|
|
'change .selected_item': 'changeItem'
|
|
'change .selected_gender': 'changeGender'
|
|
'change .selected_level': 'changeLevel'
|
|
'change .iv-entry': 'changeIv'
|
|
'focus .ev-entry': 'focusEv'
|
|
'blur .ev-entry': 'changeEv'
|
|
'change .ev-entry': 'changeEv'
|
|
'input .ev-entry[type=range]': 'changeEv' # fix for firefox
|
|
'change .select-hidden-power': 'changeHiddenPower'
|
|
'keydown .selected_moves input': 'keydownMoves'
|
|
'blur .selected_moves input': 'blurMoves'
|
|
'click .table-moves tbody tr': 'clickMoveName'
|
|
'mousedown .table-moves': 'preventBlurMoves'
|
|
'click .move-button': 'clickSelectedMove'
|
|
'click .move-button .close': 'removeSelectedMove'
|
|
|
|
initialize: (attributes={}) =>
|
|
@onPokemonChange = attributes.onPokemonChange
|
|
|
|
setFormat: (format) =>
|
|
format = Formats[format] || Formats[DEFAULT_FORMAT]
|
|
@setGeneration(format.generation)
|
|
# TODO: Set PBV limit based on conditions
|
|
|
|
changeSort:(e) =>
|
|
sort = $(e.currentTarget).val()
|
|
console.log(sort)
|
|
if sort =="Default Sort"
|
|
@sortSpecieslist("Default")
|
|
else if sort == "Sort by Dexnumber"
|
|
@sortSpecieslist("id", false)
|
|
else if sort == "Invert by Dexnumber"
|
|
@sortSpecieslist("id", true)
|
|
else if sort == "Sort Alphabetically"
|
|
@sortSpecieslist("pokename", false)
|
|
else if sort == "Invert Alphabetically"
|
|
@sortSpecieslist("pokename", true)
|
|
|
|
sortSpecieslist: (option, reverse) =>
|
|
{MoveData, SpeciesData, ItemData} = @generation
|
|
if option == "Default"
|
|
sortedlist = @getSpecies
|
|
else
|
|
sortedlist = @sortObject(SpeciesData, option, reverse)
|
|
@speciesList = (species for species, data of sortedlist)
|
|
@render()
|
|
|
|
sortObject: (data, option, reverse) ->
|
|
arr = []
|
|
for key, val of data
|
|
val.pokename = key
|
|
arr.push(val)
|
|
arr = _.sortBy(arr, option)
|
|
if reverse == true
|
|
arr.reverse()
|
|
newobj = {}
|
|
for thing in arr
|
|
newobj[thing.pokename] = thing
|
|
finished = newobj
|
|
|
|
setGeneration: (generation) =>
|
|
@generation = window.Generations[generation.toUpperCase()]
|
|
{MoveData, SpeciesData, ItemData} = @generation
|
|
@moveData = MoveData
|
|
@speciesList = (species for species, data of SpeciesData)
|
|
# TODO: filter irrelevant items
|
|
@itemList = (_(itemName for itemName, data of ItemData).sort())
|
|
|
|
@render()
|
|
|
|
setPokemon: (pokemon) =>
|
|
# Stop listening for change events on the previously set pokemon
|
|
@stopListening(@pokemon) if @pokemon
|
|
|
|
@pokemon = pokemon
|
|
@listenTo(pokemon, 'change:level', @renderStats)
|
|
@listenTo(pokemon, 'change:ivs', @renderStats)
|
|
@listenTo(pokemon, 'change:evs', @renderStats)
|
|
@listenTo(pokemon, 'change:nature', @renderStats)
|
|
@listenTo(pokemon, 'change:hiddenPowerType', @renderStats)
|
|
@listenTo(pokemon, 'change:shiny', @renderSpecies)
|
|
|
|
@renderPokemon()
|
|
|
|
setTeamPBV: (pbv) =>
|
|
@teamPBV = pbv
|
|
|
|
changeSpecies: (e) =>
|
|
return if not @onPokemonChange
|
|
species = $(e.currentTarget).val()
|
|
@pokemon = if species
|
|
new Pokemon(teambuilder: true, species: species)
|
|
else
|
|
new NullPokemon()
|
|
@onPokemonChange(@pokemon)
|
|
|
|
changeNickname: (e) =>
|
|
$input = $(e.currentTarget)
|
|
@pokemon.set("name", $input.val())
|
|
|
|
changeShiny: (e) =>
|
|
$switch = $(e.currentTarget).toggleClass("selected")
|
|
@pokemon.set("shiny", $switch.is(".selected"))
|
|
|
|
changeHappiness: (e) =>
|
|
$switch = $(e.currentTarget).toggleClass("selected")
|
|
happiness = if $switch.is(".selected") then 0 else 100
|
|
@pokemon.set("happiness", happiness)
|
|
|
|
changeForme: (e) =>
|
|
$forme = $(e.currentTarget)
|
|
@pokemon.set('forme', $forme.val())
|
|
# Forme changes may have different abilities, so we have to change this.
|
|
@pokemon.set('ability', @pokemon.getAbilities()[0])
|
|
|
|
changeNature: (e) =>
|
|
$list = $(e.currentTarget)
|
|
@pokemon.set("nature", $list.val())
|
|
|
|
changeAbility: (e) =>
|
|
$list = $(e.currentTarget)
|
|
@pokemon.set("ability", $list.val())
|
|
|
|
changeItem: (e) =>
|
|
$list = $(e.currentTarget)
|
|
@pokemon.set("item", $list.val())
|
|
|
|
changeGender: (e) =>
|
|
$list = $(e.currentTarget)
|
|
@pokemon.set("gender", $list.val())
|
|
|
|
changeLevel: (e) =>
|
|
$input = $(e.currentTarget)
|
|
value = parseInt($input.val(), 10)
|
|
value = 1120 if isNaN(value) || value > 120
|
|
value = 1 if value < 1
|
|
$input.val(value)
|
|
@pokemon.set("level", value)
|
|
|
|
changeIv: (e) =>
|
|
# todo: make changeIv and changeEv DRY
|
|
$input = $(e.currentTarget)
|
|
stat = $input.data("stat")
|
|
value = parseInt($input.val(), 10)
|
|
if isNaN(value) || value > 31 || value < 0
|
|
value = 31
|
|
|
|
@pokemon.setIv(stat, value)
|
|
|
|
focusEv: (e) =>
|
|
$input = $(e.currentTarget)
|
|
return if $input.is("[type=range]")
|
|
value = parseInt($input.val(), 10)
|
|
$input.val("") if value == 0
|
|
|
|
changeEv: (e) =>
|
|
# todo: make changeIv and changeEv DRY
|
|
$input = $(e.currentTarget)
|
|
stat = $input.data("stat")
|
|
value = parseInt($input.val(), 10)
|
|
value = 252 if value > 252
|
|
value = 0 if isNaN(value) || value < 0
|
|
|
|
value = @pokemon.setEv(stat, value)
|
|
$input.val(value) if not $input.is("[type=range]")
|
|
|
|
changeHiddenPower: (e) =>
|
|
$input = $(e.currentTarget)
|
|
type = $input.val()
|
|
@pokemon.set('hiddenPowerType', type.toLowerCase())
|
|
|
|
# Prevents the blurMoves event from activating for the duration of
|
|
# the remaining javascript events. This allows the click event to not fire
|
|
# the blur event.
|
|
preventBlurMoves: (e) =>
|
|
@_preventBlur = true
|
|
_.defer =>
|
|
@_preventBlur = false
|
|
|
|
blurMoves: (e) =>
|
|
$input = $(e.currentTarget)
|
|
if @_preventBlur
|
|
previousScrollPosition = @$el.scrollTop()
|
|
$input.focus()
|
|
e.preventDefault()
|
|
@$el.scrollTop(previousScrollPosition) # prevent scroll from refocus
|
|
return
|
|
|
|
$selectedMove = @$selectedMove()
|
|
moveName = $selectedMove.data('move-id')
|
|
|
|
# Remove filtering and row selection
|
|
@filterMovesBy("")
|
|
$(".table-moves .active").removeClass("active")
|
|
|
|
if $input.val().length == 0
|
|
@recordMoves()
|
|
else
|
|
@insertMove($input, moveName)
|
|
|
|
clickMoveName: (e) =>
|
|
$this = $(e.currentTarget)
|
|
moveName = $this.data('move-id')
|
|
$moves = @$el.find('.selected_moves')
|
|
$input = $moves.find('input:focus').first()
|
|
$input = $moves.find('input').first() if $input.length == 0
|
|
return if $input.length == 0
|
|
@insertMove($input, moveName)
|
|
|
|
insertMove: ($input, moveName) =>
|
|
currentScrollPosition = @$el.scrollTop()
|
|
|
|
@preventBlurMoves()
|
|
return if !@buttonify($input, moveName)
|
|
$moves = @$el.find('.selected_moves')
|
|
$firstInput = $moves.find('input').first()
|
|
if $firstInput.length > 0
|
|
$firstInput.focus()
|
|
@$el.scrollTop(currentScrollPosition)
|
|
else
|
|
@$el.scrollTop(0)
|
|
@recordMoves()
|
|
|
|
recordMoves: =>
|
|
movesArray = []
|
|
$moves = @$el.find('.selected_moves')
|
|
$moves.find('.move-button').each ->
|
|
moveName = $(this).find("span").text().trim()
|
|
if moveName != ""
|
|
movesArray.push(moveName)
|
|
@pokemon.set("moves", movesArray)
|
|
|
|
$selectedMove: =>
|
|
$table = @$el.find('.table-moves')
|
|
$allMoves = $table.find('tbody tr')
|
|
$allMoves.filter('.active').first()
|
|
|
|
clickSelectedMove: (e) =>
|
|
$this = $(e.currentTarget)
|
|
moveName = $this.find('span').text()
|
|
$input = $("<input type='text' value='#{moveName}'/>")
|
|
$this.replaceWith($input)
|
|
$input.focus().select()
|
|
|
|
# Set the current move row to active
|
|
$(".table-moves tr[data-move-id='#{moveName}']").addClass("active")
|
|
|
|
removeSelectedMove: (e) =>
|
|
$this = $(e.currentTarget).parent()
|
|
$input = $("<input type='text'/>")
|
|
$this.replaceWith($input)
|
|
$input.focus()
|
|
e.stopPropagation()
|
|
|
|
buttonify: ($input, moveName) =>
|
|
return false if moveName not of @moveData
|
|
|
|
# The blur event may have been cancelled, so when removing the input also
|
|
# remove the filter
|
|
if $input.is(":focus")
|
|
@filterMovesBy("")
|
|
$(".table-moves .active").removeClass("active")
|
|
|
|
type = @moveData[moveName].type.toLowerCase()
|
|
$input.replaceWith("""
|
|
<div class="button move-button #{type}"><span>#{moveName}</span><div class='close'>×</div></div>
|
|
""")
|
|
return true
|
|
|
|
keydownMoves: (e) =>
|
|
$input = $(e.currentTarget)
|
|
$table = @$el.find('.table-moves')
|
|
$allMoves = $table.find('tbody tr')
|
|
switch e.which
|
|
when 13 # [Enter]; we're selecting the active move.
|
|
$activeMove = @$selectedMove()
|
|
$activeMove.click()
|
|
when 38 # [Up arrow]; selects move above
|
|
$activeMove = $allMoves.filter('.active').first()
|
|
$prevMove = $activeMove.prevAll(":visible").first()
|
|
if $prevMove.length > 0
|
|
$activeMove.removeClass('active')
|
|
$prevMove.addClass('active')
|
|
when 40 # [Down arrow]; selects move below
|
|
$activeMove = $allMoves.filter('.active').first()
|
|
$nextMove = $activeMove.nextAll(":visible").first()
|
|
if $nextMove.length > 0
|
|
$activeMove.removeClass('active')
|
|
$nextMove.addClass('active')
|
|
else
|
|
# Otherwise we're filtering moves
|
|
# We defer since $input may not have updated yet
|
|
_.defer =>
|
|
return unless $input.is(":focus")
|
|
moveName = $input.val()
|
|
@filterMovesBy(moveName)
|
|
|
|
filterMovesBy: (moveName) =>
|
|
moveName = moveName.replace(/\s+|-/g, "")
|
|
$table = @$el.find('.table-moves')
|
|
$allMoves = $table.find('tbody tr')
|
|
moveRegex = new RegExp(moveName, "i")
|
|
$moves = $allMoves.filter ->
|
|
$move = $(this)
|
|
moveName = $move.data('move-search-id')
|
|
moveRegex.test(moveName)
|
|
$table.addClass('hidden')
|
|
$moves.removeClass('hidden')
|
|
$allMoves.not($moves).addClass('hidden')
|
|
$allMoves.removeClass('active')
|
|
$moves.first().addClass('active')
|
|
$table.removeClass('hidden')
|
|
|
|
disableEventsAndExecute: (callback) =>
|
|
isOutermost = !@_eventsDisabled
|
|
|
|
@_eventsDisabled = true
|
|
@undelegateEvents() if isOutermost # disable events
|
|
callback()
|
|
@delegateEvents() if isOutermost
|
|
@_eventsDisabled = false if isOutermost
|
|
|
|
render: =>
|
|
@$el.html @editTemplate(window: window, speciesList: @speciesList, itemList: @itemList)
|
|
attachSelectize(@$el.find(".species_list"),
|
|
render:
|
|
option: (item, escape) =>
|
|
pbv = PokeBattle.PBV.determinePBV(@generation, species: item.value)
|
|
return "<div class='clearfix'>#{item.text}<div class='pbv'>#{pbv}</div></div>"
|
|
)
|
|
attachSelectize(@$el.find(".selected_item"))
|
|
return this
|
|
|
|
renderPokemon: =>
|
|
@renderSpecies()
|
|
@renderNonStats()
|
|
@renderStats()
|
|
@renderMoves()
|
|
@renderPBV()
|
|
|
|
# Disable entering values if this is a NullPokemon
|
|
$elements = @$el.find("input, select").not(".species input, .species select")
|
|
$elements.prop("disabled", @pokemon.isNull)
|
|
setSelectizeDisabled($elements, @pokemon.isNull)
|
|
|
|
return this
|
|
|
|
renderPBV: =>
|
|
individualPBV = @pokemon.getPBV()
|
|
@$(".individual-pbv").text(individualPBV)
|
|
|
|
team = @pokemon.getTeam()
|
|
if team && team.hasPBV()
|
|
pbv = team.getPBV()
|
|
maxPBV = team.getMaxPBV()
|
|
@$(".total-pbv").text(pbv).toggleClass("red", pbv > maxPBV)
|
|
@$(".max-pbv").text(maxPBV)
|
|
|
|
renderSpecies: =>
|
|
@disableEventsAndExecute =>
|
|
setSelectizeValue(@$(".species_list"), @pokemon.get("species"))
|
|
html = if @pokemon.isNull then "" else @speciesTemplate(window: window, pokemon: @pokemon)
|
|
@$(".species-info").html(html)
|
|
@$(".selected_shininess").toggleClass("selected", @pokemon.get('shiny') == true)
|
|
@$(".selected_happiness").toggleClass("selected", @pokemon.get("happiness") == 0)
|
|
|
|
renderNonStats: =>
|
|
$nonStats = @$el.find(".non-stats")
|
|
|
|
populateSelect = (searchStr, valueTextPairs, selectedValue) ->
|
|
$select = $nonStats.find(searchStr).empty()
|
|
for pair in valueTextPairs
|
|
value = text = pair
|
|
if pair instanceof Array
|
|
value = pair[0]
|
|
text = pair[1]
|
|
|
|
$select.append($("<option>").attr("value", value).text(text))
|
|
$select.val(selectedValue)
|
|
|
|
displayedGenders =
|
|
F: "Female"
|
|
M: "Male"
|
|
Genderless: "Genderless"
|
|
|
|
@disableEventsAndExecute =>
|
|
genders = ([g, displayedGenders[g]] for g in @pokemon.getGenders())
|
|
$nonStats.find(".selected_nickname").val(@pokemon.get("name"))
|
|
populateSelect ".selected_ability", @pokemon.getAbilities(), @pokemon.get("ability")
|
|
populateSelect ".selected_nature", @pokemon.getNatures(), @pokemon.get("nature")
|
|
setSelectizeValue(@$(".selected_item"), @pokemon.get("item"))
|
|
populateSelect ".selected_gender", genders, @pokemon.get("gender")
|
|
$nonStats.find(".selected_level").val(@pokemon.get("level"))
|
|
|
|
renderStats: =>
|
|
pokemon = @pokemon
|
|
|
|
@$(".iv-entry").each ->
|
|
$input = $(this)
|
|
stat = $input.data("stat")
|
|
$input.val(pokemon.iv(stat))
|
|
|
|
@$(".ev-entry").each ->
|
|
return if $(this).is(":focus")
|
|
$input = $(this)
|
|
stat = $input.data("stat")
|
|
$input.val(pokemon.ev(stat))
|
|
|
|
@$('.base-stat').each ->
|
|
$this = $(this)
|
|
stat = $this.data("stat")
|
|
$this.text(pokemon.base(stat))
|
|
|
|
@$('.stat-total').each ->
|
|
$this = $(this)
|
|
stat = $this.data("stat")
|
|
$this.text(pokemon.stat(stat))
|
|
$this.removeClass('plus-nature minus-nature')
|
|
|
|
if pokemon.natureBoost(stat) > 1
|
|
$this.addClass('plus-nature')
|
|
$this.text($this.text() + '+')
|
|
|
|
if pokemon.natureBoost(stat) < 1
|
|
$this.addClass('minus-nature')
|
|
$this.text($this.text() + '-')
|
|
|
|
remainingEvs = 508 - @pokemon.getTotalEVs()
|
|
@$('.remaining-evs-amount')
|
|
.text(remainingEvs)
|
|
.toggleClass("over-limit", remainingEvs < 0)
|
|
|
|
@$('.select-hidden-power').val(@pokemon.get('hiddenPowerType'))
|
|
|
|
renderMoves: =>
|
|
# TODO: Cache the resultant html
|
|
$moveSection = @$el.find(".moves-section")
|
|
if @pokemon.isNull
|
|
$moveSection.html ""
|
|
return
|
|
|
|
$moveSection.html @movesTemplate(window: window, pokemon: @pokemon)
|
|
$moveSection.find('.selected_moves input').each (i, el) =>
|
|
$this = $(el)
|
|
moveName = $this.val()
|
|
@buttonify($this, moveName) |