1
0
mirror of https://gitlab.com/Deukhoofd/BattleSim.git synced 2025-10-28 18:20:04 +00:00

Lots of stuff

This commit is contained in:
Deukhoofd
2016-02-01 23:19:30 +01:00
commit d7316d5799
6681 changed files with 527969 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
class @BattleListView extends Backbone.View
template: JST['battle_list']
initialize: (attributes) =>
@battles = []
@render()
refreshList: =>
PokeBattle.primus.send "getBattleList", (battles) =>
@battles = _(battles).sortBy((battle) => battle[3])
@render()
render: =>
@$el.html @template(battles: @battles)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,244 @@
class @ChatView extends Backbone.View
template: JST['chat']
userListTemplate: JST['user_list']
events:
'click': 'focusChat'
'keydown .chat_input': 'handleKeys'
'click .chat_input_send': 'sendChat'
'scroll_to_bottom': 'scrollToBottom'
MAX_USERNAME_HISTORY = 10
MAX_MESSAGES_LENGTH = 500
# Takes a `model`, which is a Room instance.
initialize: (options) =>
{@noisy} = options
if @model
@listenTo(@model.get('users'), 'add remove reset', @renderUserList)
if @noisy
@listenTo(@model.get('users'), 'add', @userJoin)
@listenTo(@model.get('users'), 'remove', @userLeave)
for eventName in Room::EVENTS
callback = this[eventName] || throw new Error("ChatView must implement #{eventName}.")
@listenTo(@model, eventName, callback)
@chatHistory = []
@mostRecentNames = []
@tabCompleteIndex = -1
@tabCompleteNames = []
# Sets the channel topic
setTopic: (topic) =>
topic = @sanitize(topic)
@rawMessage("<div class='alert alert-info'><b>Topic:</b> #{topic}</div>")
render: =>
@$el.html @template()
if @model
@$el.removeClass('without_spectators')
@$el.removeClass('without_chat_input')
@renderUserList()
this
renderUserList: =>
@$('.user_count').text "Users (#{@model.get('users').length})"
@$('.users').html @userListTemplate(userList: @model.get('users').models)
this
getSelectedText: =>
text = ""
if window.getSelection
text = window.getSelection().toString()
else if document.selection && document.selection.type != "Control"
text = document.selection.createRange().text
return text
focusChat: =>
selectedText = @getSelectedText()
@$('.chat_input').focus() if selectedText.length == 0
sendChat: =>
$this = @$('.chat_input')
message = $this.val()
if @model.sendChat(message)
@chatHistory.push(message)
delete @chatHistoryIndex
$this.val('')
tabComplete: ($input, options = {}) =>
cursorIndex = $input.prop('selectionStart')
text = $input.val()
if @tabCompleteNames.length > 0 && @tabCompleteCursorIndex == cursorIndex
if options.reverse
@tabCompleteIndex -= 1
if @tabCompleteIndex < 0
@tabCompleteIndex = @tabCompleteNames.length - 1
else
@tabCompleteIndex = (@tabCompleteIndex + 1) % @tabCompleteNames.length
else
delete @tabCompleteCursorIndex
pieces = text[0...cursorIndex].split(' ')
possibleName = pieces.pop()
rest = pieces.join(' ')
rest += ' ' if pieces.length > 0 # Append a space if a word exists
length = possibleName.length
return if length == 0
candidates = _.union(@mostRecentNames, @model.get('users').pluck('id'))
candidates = candidates.filter (name) ->
name[...length].toLowerCase() == possibleName.toLowerCase()
return if candidates.length == 0
if options.reverse
@tabCompleteIndex = candidates.length - 1
else
@tabCompleteIndex = 0
@tabCompleteNames = candidates
@tabCompletePrefix = rest
@tabCompleteCursorIndex = cursorIndex
tabbedName = @tabCompleteNames[@tabCompleteIndex]
newPrefix = @tabCompletePrefix + tabbedName
newPrefixLength = newPrefix.length
$input.val(newPrefix + text[cursorIndex...])
$input[0].setSelectionRange(newPrefixLength, newPrefixLength)
@tabCompleteCursorIndex = newPrefixLength
handleKeys: (e) =>
$input = $(e.currentTarget)
switch e.which
when 13 # [Enter]
e.preventDefault()
@sendChat()
when 9 # [Tab]
e.preventDefault()
@tabComplete($input, reverse: e.shiftKey)
when 38 # [Up arrow]
e.preventDefault()
return if @chatHistory.length == 0
if !@chatHistoryIndex?
@chatHistoryIndex = @chatHistory.length
@chatHistoryText = $input.val()
if @chatHistoryIndex > 0
@chatHistoryIndex -= 1
$input.val(@chatHistory[@chatHistoryIndex])
when 40 # [Down arrow]
e.preventDefault()
return unless @chatHistoryIndex?
@chatHistoryIndex += 1
if @chatHistoryIndex == @chatHistory.length
$input.val(@chatHistoryText)
delete @chatHistoryIndex
else
$input.val(@chatHistory[@chatHistoryIndex])
userMessage: (username, message) =>
user = @model.get('users').get(username)
displayName = user?.getDisplayName() || username
yourName = PokeBattle.username
highlight = (new RegExp("\\b#{yourName}\\b", 'i').test(message))
# Render the chat message
u = "<b class='open_pm fake_link' data-user-id='#{username}'
style='color: #{@userColor(username)}'>#{displayName}:</b>"
@rawMessage("#{@timestamp()} #{u} #{@sanitize(message)}", {highlight})
# We might want to run something based on the message, e.g. !pbv from a mod.
@handleMessage(user, message)
# Record last few usernames who chatted
index = @mostRecentNames.indexOf(username)
@mostRecentNames.splice(index, 1) if index != -1
@mostRecentNames.push(username)
@mostRecentNames.shift() if @mostRecentNames.length > MAX_USERNAME_HISTORY
userColor: (username) =>
# Same hashing algorithm as in Java
hash = 0
for c, i in username
chr = username.charCodeAt(i)
hash = ((hash << 5) - hash) + chr
hash |= 0
h = hash % 360
hash /= 360
s = (hash % 25) + 75
l = 50
"hsl(#{h}, #{s}%, #{l}%)"
handleMessage: (user, message) =>
authority = user?.get('authority')
printableCommands = ['/pbv', '/data']
# TODO: no magic constants. '1' is a regular user.
if authority > 1 && message.split(/\s/, 1)[0] in printableCommands
PokeBattle.commands.execute(@model, message)
userJoin: (user) =>
@rawMessage("#{@timestamp()} #{user.id} joined!")
userLeave: (user) =>
@rawMessage("#{@timestamp()} #{user.id} left!")
rawMessage: (message, options = {}) =>
wasAtBottom = @isAtBottom()
klass = []
klass.push('bg-blue') if options.highlight
klass.push(options.class) if options.class
@print("<p class='chat_message #{klass.join(' ')}'>#{message}</p>")
@cleanChat()
if wasAtBottom then @scrollToBottom()
cleanChat: =>
$messages = @$('.chat_message')
numToRemove = ($messages.length - MAX_MESSAGES_LENGTH)
if numToRemove > 0
$messages.slice(0, numToRemove).remove()
announce: (klass, message) =>
wasAtBottom = @isAtBottom()
message = @linkify(message)
@print("<div class='alert alert-#{klass} clearfix'>#{message}</div>")
if wasAtBottom then @scrollToBottom()
print: (message) =>
@$('.messages').append(message)
clear: =>
@$('.messages').empty()
timestamp: =>
date = new Date()
hours = date.getHours()
minutes = date.getMinutes()
seconds = date.getSeconds()
minutes = "00#{minutes}".substr(-2)
seconds = "00#{seconds}".substr(-2)
"<span class='monospace'>[#{hours}:#{minutes}:#{seconds}]</span>"
# Escapes all HTML, but also converts links to clickable links.
sanitize: (message) =>
sanitizedMessage = $('<div/>').text(message).html()
@linkify(sanitizedMessage)
linkify: (message) =>
message = URI.withinString message, (url) ->
uri = URI(url)
[host, path] = [uri.host(), uri.path()]
battleRegex = /^\/battles\/([a-fA-F0-9]+)$/i
$a = $("<a/>").prop('href', url).prop('target', '_blank').text(url)
if host == URI(window.location.href).host() && battleRegex.test(path)
battleId = path.match(battleRegex)[1]
$a.addClass('spectate').attr('data-battle-id', battleId)
return $a.wrap("<div/>").parent().html()
message
# Returns true if the chat is scrolled to the bottom of the screen.
# This also returns true if the messages are hidden.
isAtBottom: =>
$el = @$('.messages')
($el[0].scrollHeight - $el.scrollTop() <= $el.outerHeight())
scrollToBottom: =>
messages = @$('.messages')[0]
messages.scrollTop = messages.scrollHeight
false

View File

@@ -0,0 +1,259 @@
class @PrivateMessagesView extends Backbone.View
messageTemplate: JST['private_message']
events:
"keypress .chat_input" : "keyPressEvent"
"keyup .chat_input" : "keyUpEvent"
"click .challenge_button, .cancel_challenge" : "toggleChallengeEvent"
"click .popup_messages" : "focusChatEvent"
"click .title_minimize" : "minimizePopupEvent"
"click .title_close" : "closePopupEvent"
"challenge .popup" : "sendChallengeEvent"
"cancelChallenge .popup" : "challengeCanceledEvent"
"focus .popup" : "focusPopupEvent"
initialize: =>
@listenTo(@collection, 'open', @createPopup)
@listenTo(@collection, 'focus', @focusPopup)
@listenTo(@collection, 'receive', @receiveMessage)
@listenTo(@collection, 'close', @closePopup)
@listenTo(@collection, 'minimize', @minimizePopup)
@listenTo(@collection, 'show', @showPopup)
@listenTo(@collection, 'openChallenge', @openChallenge)
@listenTo(@collection, 'cancelChallenge', @cancelChallenge)
@listenTo(@collection, 'closeChallenge', @closeChallenge)
@listenTo(@collection, 'focus show', @resetNotifications)
# @listenTo(PokeBattle.userList, 'add', @notifyJoin)
# @listenTo(PokeBattle.userList, 'remove', @notifyLeave)
createPopup: (message) =>
title = id = message.id
$html = @$findPopup(id)
if @$findPopup(id).length == 0
$html = $(@messageTemplate({window, id, title}))
@$el.append($html)
@positionPopup($html, @$(".popup:visible").length - 1)
@addLogMessages($html, message.getLog())
$html
focusPopup: (message) =>
id = message.id
$popup = @$findPopup(id)
$popup.find('.chat_input').focus()
closePopup: (message) =>
username = message.id
@$findPopup(username).remove()
@repositionPopups()
minimizePopup: (message) =>
username = message.id
$popup = @$findPopup(username)
$popup.addClass('hidden')
@repositionPopups()
showPopup: (message) =>
username = message.id
$popup = @$findPopup(username)
@$el.append($popup)
$popup.removeClass('hidden')
@scrollToBottom($popup)
@repositionPopups()
addMessage: ($popup, message) =>
$messages = $popup.find('.popup_messages')
$messages.append(message)
# todo: make this and receiveMessage construct messages from a common source
addLogMessages: ($popup, log) =>
messageHtml = ""
for {username, message, opts} in log
message = _.escape(message)
username = "Me" if username == PokeBattle.username
if opts.type in [ 'error', 'alert' ]
messageHtml += "<p class='grey'>#{message}</p>"
else
messageHtml += "<p class='grey'><strong>#{username}:</strong> #{message}</p>"
@addMessage($popup, messageHtml)
@scrollToBottom($popup)
# todo: make this and addLogMessages construct messages from a common source
receiveMessage: (messageModel, messageId, username, message, options) =>
message = _.escape(message)
$popup = @$findOrCreatePopup(messageId)
wasAtBottom = @isAtBottom($popup)
username = "Me" if username == PokeBattle.username
if options.type == 'error'
@addMessage($popup, "<p class='red italic'>#{message}</p>")
else if options.type == 'alert'
@addMessage($popup, "<p class='yellow italic'>#{message}</p>")
else
if username != "Me" && !$popup.find('.chat_input').is(":focus")
$popup.addClass('new_message')
PokeBattle.notifyUser(PokeBattle.NotificationTypes.PRIVATE_MESSAGE, username)
else
@resetNotifications(messageModel)
@addMessage($popup, "<p><strong>#{username}:</strong> #{message}</p>")
if wasAtBottom then @scrollToBottom($popup)
openChallenge: (messageId, generation, conditions) =>
$popup = @$findOrCreatePopup(messageId)
$challenge = @createChallenge($popup, generation, conditions)
if generation
$challenge.find('.is_not_challenger').addClass('hidden')
$challenge.find('.is_challenger').removeClass('hidden')
cancelChallenge: (messageId) =>
$popup = @$findOrCreatePopup(messageId)
$challenge = $popup.find('.challenge')
$challenge.find('.icon-spinner').addClass('hidden')
$challenge.find('.send_challenge, .select').removeClass('disabled')
$challenge.find('.challenge_text').text("Challenge")
$challenge.find(".cancel_challenge").text('Close')
closeChallenge: (messageId) =>
$popup = @$findOrCreatePopup(messageId)
$challenge = $popup.find('.challenge')
$challenge.addClass('hidden')
$popup.find('.popup_messages').removeClass('small')
resetNotifications: (message) =>
message.set('notifications', 0)
notifyJoin: (user) =>
message = @collection.get(user.id)
return unless @isOpen(message)
message?.add(user.id, "#{user.id} is now online!", type: "alert")
notifyLeave: (user) =>
message = @collection.get(user.id)
return unless @isOpen(message)
message?.add(user.id, "#{user.id} is now offline.", type: "alert")
isOpen: (message) =>
message && @$findPopup(message.id).length > 0
# Returns true if the chat is scrolled to the bottom of the screen.
# This also returns true if the messages are hidden.
isAtBottom: ($popup) =>
$el = $popup.find('.popup_messages')
($el[0].scrollHeight - $el.scrollTop() <= $el.outerHeight())
scrollToBottom: ($popup) =>
messages = $popup.find('.popup_messages')[0]
return unless messages
messages.scrollTop = messages.scrollHeight
false
positionPopup: ($popup, index) =>
leftOffset = $('#content').position().left
$popup.css(left: leftOffset + index * $popup.outerWidth(true))
repositionPopups: =>
@$(".popup:visible").each (index, self) =>
@positionPopup($(self), index)
$findPopup: (id) =>
@$(".popup[data-user-id='#{id}']")
$findOrCreatePopup: (messageId) =>
$popup = @$findPopup(messageId)
$popup = @createPopup(@collection.get(messageId)) if $popup.length == 0
$popup
$closestPopup: (target) =>
$target = $(target)
return $target if $target.hasClass("popup")
return $target.closest(".popup")
messageFromPopup: (target) =>
$popup = @$closestPopup(target)
message = @collection.get($popup.data('user-id'))
return message
createChallenge: ($popup, generation, conditions) =>
$challenge = $popup.find('.challenge')
$challenge.html(JST['challenge']())
createChallengePane
eventName: "challenge"
button: $popup.find('.send_challenge')
acceptButton: $popup.find('.accept_challenge')
rejectButton: $popup.find('.reject_challenge')
populate: $popup.find(".challenge_data")
generation: generation
personId: $popup.data('user-id')
defaultClauses: conditions || [
Conditions.TEAM_PREVIEW
Conditions.PBV_1000
Conditions.SLEEP_CLAUSE
Conditions.EVASION_CLAUSE
Conditions.SPECIES_CLAUSE
Conditions.OHKO_CLAUSE
Conditions.PRANKSTER_SWAGGER_CLAUSE
Conditions.UNRELEASED_BAN
]
blockedClauses: conditions? || [Conditions.RATED_BATTLE]
$popup.find('.popup_messages').addClass('small')
$challenge.removeClass('hidden')
$challenge
##########
# EVENTS #
##########
keyPressEvent: (e) =>
switch e.which
when 13 # [ Enter ]
$input = $(e.currentTarget)
message = @messageFromPopup(e.currentTarget)
text = $input.val()
return if text.length == 0
PokeBattle.primus.send('privateMessage', message.id, text)
$input.val('')
keyUpEvent: (e) =>
switch e.which
when 27 # [ Esc ]
@closePopupEvent(e)
minimizePopupEvent: (e) =>
message = @messageFromPopup(e.currentTarget)
message.trigger('minimize', message)
closePopupEvent: (e) =>
message = @messageFromPopup(e.currentTarget)
message.trigger('close', message)
focusChatEvent: (e) =>
@$closestPopup(e.currentTarget).find('input').focus()
toggleChallengeEvent: (e) =>
$popup = @$closestPopup(e.currentTarget)
$challenge = $popup.find('.challenge')
wasAtBottom = @isAtBottom($popup)
if $challenge.hasClass("hidden")
@createChallenge($popup)
else if $challenge.find('.cancel_challenge').text() == 'Cancel'
$popup.find('.send_challenge').click()
else
@closeChallenge(@messageFromPopup($popup))
if wasAtBottom then @scrollToBottom($popup)
sendChallengeEvent: (e) =>
$popup = @$closestPopup(e.currentTarget)
$challenge = $popup.find('.challenge')
$challenge.find(".icon-spinner").removeClass('hidden')
$challenge.find(".challenge_text").text('Challenging...')
$challenge.find(".cancel_challenge").text('Cancel')
challengeCanceledEvent: (e) =>
message = @messageFromPopup(e.currentTarget)
message.trigger('cancelChallenge', message.id)
focusPopupEvent: (e) =>
$popup = @$closestPopup(e.currentTarget)
$popup.removeClass('new_message')
@resetNotifications(@collection.get($popup.data('user-id')))

View File

@@ -0,0 +1,34 @@
class @ReplayView extends Backbone.View
replayTemplate: JST['replay']
events:
'click .delete-replay': 'deleteReplay'
render: =>
@$el.empty()
templates = @collection.map((replay) => @replayTemplate({window, replay}))
groups = for x in [0...templates.length] by 3
_.compact([ templates[x], templates[x + 1], templates[x + 2] ])
for groupHTML in groups
$row = $('<div/>').addClass('row-fluid')
$row.append(groupHTML)
$row.appendTo(@$el)
if @collection.length == 0
@$el.append($("<p/>").text("You have not saved any replays."))
this
deleteReplay: (e) =>
return unless confirm("Do you really want to delete this replay?")
$target = $(e.currentTarget)
$spinner = $target.closest('.clickable-box').find('.show_spinner')
$spinner.removeClass('hidden')
cid = $target.data('cid')
replay = @collection.get(cid)
replay
.destroy()
.complete(@render)

View File

@@ -0,0 +1,188 @@
class @SidebarView extends Backbone.View
template: JST['navigation']
events:
"click .logo" : "focusLobbyEvent"
"click .nav_rooms li" : 'focusRoomEvent'
"click .nav_battles li" : 'focusBattleEvent'
"click .nav_messages li": 'focusMessageEvent'
"click .nav_battles .close" : 'leaveRoomEvent'
"click .nav_messages .close" : 'closeMessageEvent'
"click .nav_teambuilder": 'showTeambuilder'
"click .nav_battle_list": 'showBattleList'
initialize: (attributes) =>
@currentWindow = null
@listenTo(PokeBattle.battles, 'add', @addBattle)
@listenTo(PokeBattle.battles, 'remove', @removeBattle)
@listenTo(PokeBattle.battles, 'reset', @resetBattles)
@listenTo(PokeBattle.battles, 'change:notifications', @renderNotifications)
@listenTo(PokeBattle.messages, 'open receive', @addMessage)
@listenTo(PokeBattle.messages, 'close', @removeMessage)
@listenTo(PokeBattle.messages, 'reset', @resetMessages)
@listenTo(PokeBattle.messages, 'change:notifications', @renderMessageNotifications)
@render()
showTeambuilder: =>
@changeWindowTo($("#teambuilder-section"), $(".nav_teambuilder"))
showBattleList: =>
@changeWindowTo($("#battle-list-section"), $(".nav_battle_list"))
PokeBattle.battleList.refreshList()
render: =>
@$el.html @template(battles: PokeBattle.battles)
renderNotifications: (battle) =>
$notifications = @$("[data-battle-id='#{battle.id}'] .notifications")
# We don't want to display notifications if this window is already focused.
if @currentWindow.data('battle-id') == battle.id
battle.set('notifications', 0, silent: true)
$notifications.addClass('hidden')
return
# Show notification count.
notificationCount = battle.get('notifications')
if notificationCount > 0
$notifications.text(notificationCount)
$notifications.removeClass('hidden')
else
$notifications.addClass('hidden')
addBattle: (battle) =>
@$(".header_battles, .nav_battles").removeClass("hidden")
$li = $("""<li class="nav_item fake_link" data-battle-id="#{battle.id}">
<div class="nav_meta">
<div class="notifications hidden">0</div>
<div class="close">&times;</div>
</div>#{battle.get('playerIds').join(' VS ')}</li>""")
$li.appendTo(@$('.nav_battles'))
$li.click()
removeBattle: (battle) =>
$navItems = @$(".nav_item")
$battle = @$(".nav_item[data-battle-id='#{battle.id}']")
index = $navItems.index($battle)
$battle.remove()
if PokeBattle.battles.size() == 0
@$(".header_battles, .nav_battles").addClass('hidden')
PokeBattle.navigation.focusLobby()
else
$next = $navItems.eq(index).add($navItems.eq(index - 1))
$next.first().click()
resetBattles: (battles) =>
for battle in battles
@addBattle(battle)
addMessage: (message) =>
# This event can trigger on already opened messages, so we need to verify
return if @$(".nav_item[data-message-id='#{message.id}']").length
@$(".header_messages, .nav_messages").removeClass("hidden")
$li = $("""<li class="nav_item fake_link" data-message-id="#{message.id}">
<div class="nav_meta">
<div class="notifications hidden">0</div>
<div class="close">&times;</div>
</div>#{message.id}</li>""")
$li.appendTo(@$('.nav_messages'))
@renderMessageNotifications(message)
removeMessage: (message) =>
@$(".nav_item[data-message-id='#{message.id}']").remove()
# If there are no messages, remove the header
# Note: We can't check the collection directly since messages are never actually removed from it
if @$('.nav_messages li').length == 0
@$(".header_messages").addClass("hidden")
resetMessages: (messages) =>
@addMessage(message) for message in messages
renderMessageNotifications: (message) =>
$notifications = @$("[data-message-id='#{message.id}'] .notifications")
notificationCount = message.get('notifications')
if notificationCount > 0
$notifications.text(notificationCount)
$notifications.removeClass('hidden')
else
$notifications.addClass('hidden')
focusLobby: =>
# TODO: Clean this up once rooms are implemented
# right now it duplicates part of focusRoom()
$lobbyLink = @$(".nav_rooms li").first()
@resetNotifications($lobbyLink)
$room = $('.chat_window')
@changeWindowTo($room, $lobbyLink)
PokeBattle.router.navigate("")
leaveRoomEvent: (e) =>
$navItem = $(e.currentTarget).closest('.nav_item')
battleId = $navItem.data('battle-id')
battle = PokeBattle.battles.get(battleId)
if battle.isPlaying()
return if !confirm("Are you sure you want to forfeit this battle?")
battle.forfeit()
PokeBattle.battles.remove(battle)
false
closeMessageEvent: (e) =>
$navItem = $(e.currentTarget).closest('.nav_item')
messageId = $navItem.data('message-id')
message = PokeBattle.messages.get(messageId)
message.trigger('close', message)
focusBattleEvent: (e) =>
$this = $(e.currentTarget)
@resetNotifications($this)
battleId = $this.data('battle-id')
@changeWindowToBattle(battleId)
focusLobbyEvent: (e) =>
@focusLobby()
focusRoomEvent: (e) =>
$this = $(e.currentTarget)
@resetNotifications($this)
# TODO: Remove hardcoding once rooms are implemented
$room = $('.chat_window')
@changeWindowTo($room, $this)
PokeBattle.router.navigate("")
focusMessageEvent: (e) =>
$navItem = $(e.currentTarget).closest('.nav_item')
messageId = $navItem.data('message-id')
message = PokeBattle.messages.get(messageId)
message.trigger('show', message)
message.trigger('focus', message)
changeWindowTo: ($toSelector, $navItem) =>
# Show window, hide others
$mainContent = $('#main-section')
$mainContent.children().addClass("hidden")
@currentWindow = $toSelector.first()
@currentWindow.removeClass("hidden")
@currentWindow.find('.chat').trigger('scroll_to_bottom')
# Add .active to navigation, remove from others
@$('.nav_item').removeClass('active')
$navItem.addClass('active')
changeWindowToBattle: (battleId) =>
$battle = $(""".battle_window[data-battle-id='#{battleId}']""")
$navItem = @$("[data-battle-id='#{battleId}']")
@changeWindowTo($battle, $navItem)
PokeBattle.router.navigate("battles/#{battleId}")
resetNotifications: ($link) =>
$link = $link.first()
$link = $link.closest('li') if $link[0].tagName != 'li'
if battleId = $link.data('battle-id')
battle = PokeBattle.battles.get(battleId)
battle.set('notifications', 0)

View File

@@ -0,0 +1,484 @@
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'>&times;</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)

View File

@@ -0,0 +1,301 @@
class @TeambuilderView extends Backbone.View
template: JST['teambuilder/main']
teamTemplate: JST['teambuilder/team']
teamsTemplate: JST['teambuilder/teams']
pokemonListTemplate: JST['teambuilder/pokemon_list']
events:
# Team view
'click .add-new-team': 'addNewTeamEvent'
'click .export-team': 'exportTeam'
'click .clone-team': 'cloneTeam'
'click .delete-team': 'deleteTeamEvent'
'click .go-to-team': 'clickTeam'
'click .import-team': 'renderImportTeamModal'
# Teambuild view
'click .change-format-dropdown a': 'changeTeamFormat'
'blur .team_name': 'blurTeamName'
'keypress .team_name': 'keypressTeamName'
'click .go_back': 'goBackToOverview'
'click .pokemon_list li': 'clickPokemon'
'click .add_pokemon': 'addNewPokemonEvent'
'click .save_team': 'saveTeam'
initialize: (attributes) =>
@selectedPokemon = 0
@selectedTeam = null
@render()
@listenTo(PokeBattle.TeamStore, 'reset', @resetTeams)
@listenTo(PokeBattle.TeamStore, 'add', @addNewTeam)
@listenTo(PokeBattle.TeamStore, 'remove', @deleteTeam)
@listenTo(PokeBattle.TeamStore, 'change:id', @changeTeamId)
@listenTo(PokeBattle.TeamStore, 'reset', @renderTeams)
@listenTo(PokeBattle.TeamStore, 'saving', @renderSaving)
@listenTo(PokeBattle.TeamStore, 'saved', @renderSaved)
@listenTo PokeBattle.TeamStore, 'render', (team) =>
@renderTeams()
if @getSelectedTeam() && team.id == @getSelectedTeam().id
@setSelectedTeam(team)
@pokemonEditView = new PokemonEditView(
el: @$('.pokemon_edit')
onPokemonChange: (newPokemon) =>
team = @getSelectedTeam()
team.replace(@selectedPokemon, newPokemon)
@renderPBV()
)
clickTeam: (e) =>
$team = $(e.currentTarget).closest('.select-team')
team = PokeBattle.TeamStore.get($team.data('cid'))
@setSelectedTeam(team)
clickPokemon: (e) =>
$listItem = $(e.currentTarget)
index = @$('.pokemon_list li').index($listItem)
@setSelectedPokemonIndex(index)
attachEventsToTeam: (team) =>
return if team.attachedTeambuildEvents
@listenTo(team, 'add:pokemon', @renderPokemon)
# Todo: Make this perform better
@listenTo(team, 'change:pokemon[*].species change:pokemon[*].forme', (pokemon) =>
@renderPokemonList()
@renderPokemon(pokemon)
)
@listenTo(team, 'add:pokemon remove:pokemon', @renderPokemonList)
@listenTo(team, 'reset:pokemon', (=> @changeTeam(team)))
@listenTo(team, 'change nested-change reset:pokemon add:pokemon remove:pokemon', @dirty)
@listenTo(team, 'change:pokemon[*] reset:pokemon add:pokemon remove:pokemon', @renderPBV)
# A temporary flag to attach until the teambuilder view is refactored
team.attachedTeambuildEvents = true
addEmptyPokemon: (team) =>
team.get('pokemon').add(new NullPokemon())
addNewTeamEvent: (e) =>
team = new Team()
PokeBattle.TeamStore.add(team)
team.save()
addNewTeam: (team) =>
@addEmptyPokemon(team) while team.get('pokemon').length < 6
@$('.teambuilder_teams').append @teamTemplate({team, window})
@attachEventsToTeam(team)
resetTeams: (teamStore) =>
teamStore.forEach (team) =>
@attachEventsToTeam(team)
cloneTeam: (e) =>
$team = $(e.currentTarget).closest('.select-team')
cid = $team.data('cid')
clone = @getTeam(cid).clone().set("id", null)
PokeBattle.TeamStore.add(clone)
clone.save()
return false
deleteTeamEvent: (e) =>
return false if !confirm("Do you really want to delete this team?")
$team = $(e.currentTarget).closest('.select-team')
team = @getTeam($team.data('cid'))
PokeBattle.TeamStore.remove(team)
team.destroy()
return false
deleteTeam: (team) =>
@$(".select-team[data-cid=#{team.cid}]").remove()
changeTeam: (team) =>
html = $(@teamTemplate({team, window})).html()
@$(".select-team[data-cid=#{team.cid}]").html(html)
changeTeamId: (team) =>
@$(".select-team[data-cid=#{team.cid}]").attr('data-id', team.id)
exportTeam: (e) =>
$team = $(e.currentTarget).closest('.select-team')
id = $team.data('id')
if not @getTeam(id).hasNonNullPokemon()
alert("You cannot export empty teams. Please add some pokemon first.")
return false
teamJSON = @getTeam(id).toNonNullJSON()
teamString = PokeBattle.exportTeam(teamJSON.pokemon)
$modal = PokeBattle.modal('modals/export_team')
$modal.find('.exported-team').val(teamString)
$modal.find('textarea, input').first().focus().select()
return false
addNewPokemonEvent: =>
@addNewPokemon(@getSelectedTeam())
addNewPokemon: (team) =>
@addEmptyPokemon(team)
@$('.pokemon_list li').last().click()
saveTeam: =>
clone = @getSelectedTeam()
team = PokeBattle.TeamStore.get(clone.id)
team.save(clone.toJSON(), silent: true)
@resetHeaderButtons()
changeTeamFormat: (e) =>
$link = $(e.currentTarget)
format = $link.data('format')
team = @getSelectedTeam()
if format != team.get('generation')
team.set('generation', format)
@renderTeam()
@dirty() # renderTeam() removes dirty, so call it again
setSelectedPokemonIndex: (index) =>
pokemon = @getSelectedTeam().at(index)
@selectedPokemon = index
# Render the pokemon
@pokemonEditView.setPokemon(pokemon)
@renderPokemon(pokemon)
# Set the correct list item to active
@$(".navigation li").removeClass("active")
@$(".navigation li").eq(index).addClass("active")
getSelectedPokemon: =>
@getSelectedTeam().at(@selectedPokemon)
setSelectedTeam: (team) =>
# Duplicate the team, so that changes don't stick until saved
@selectedTeam = team.clone()
@selectedTeam.id = team.id
@selectedTeam.cid = team.cid
@selectedPokemon = 0
@attachEventsToTeam(@selectedTeam)
@renderTeam()
getAllTeams: =>
PokeBattle.TeamStore.models
getSelectedTeam: =>
@selectedTeam
getTeam: (idx) =>
PokeBattle.TeamStore.get(idx)
blurTeamName: =>
teamName = @$('.team_name').text()
@getSelectedTeam().set('name', teamName)
keypressTeamName: (e) =>
if e.which == 13 # [Enter]
@$('.team_name').blur()
goBackToOverview: =>
@renderTeams()
dirty: =>
@$('.go_back').text('Discard changes')
@$('.save_team').removeClass('disabled')
resetHeaderButtons: =>
@$('.go_back').text('Back')
@$('.save_team').addClass('disabled')
render: =>
@$el.html @template(pokemon: @getSelectedTeam(), selected: @selectedPokemon)
@renderTeams()
renderTeams: =>
@$('.display_teams').html @teamsTemplate(teams: @getAllTeams(), window: window)
@$('.display_teams').removeClass('hidden')
@$('.display_pokemon').addClass('hidden')
this
renderTeam: =>
team = @getSelectedTeam()
@pokemonEditView.setFormat(team.get('generation') || DEFAULT_FORMAT)
@resetHeaderButtons()
@renderFormat()
@renderPokemonList()
@setSelectedPokemonIndex(@selectedPokemon)
@$('.team_name').text(team.getName())
@$('.display_teams').addClass('hidden')
@$('.display_pokemon').removeClass('hidden')
renderPokemonList: =>
team = @getSelectedTeam()
$pokemon_list = @$(".pokemon_list").empty()
$pokemon_list.html @pokemonListTemplate(window: window, pokemonList: team.get('pokemon').models)
$pokemon_list.find("li[data-pokemon-index=#{@selectedPokemon}]").addClass("active")
# NOTE: this isn't be used, and just amounts to hiding the button, however
# we may re-enable this functionality in the future
# Hide add pokemon if there's 6 pokemon
if team.length < 6
@$(".add_pokemon").show()
else
@$(".add_pokemon").hide()
renderPokemon: (pokemon) =>
@pokemonEditView.setPokemon(pokemon)
renderPBV: (pokemon) =>
if pokemon
individualPBV = pokemon.getPBV()
$listItem = @$(".pokemon_list li[data-pokemon-cid=#{pokemon.cid}]")
$listItem.find(".pbv-value").text(individualPBV)
totalPBV = @getSelectedTeam().getPBV()
@pokemonEditView.setTeamPBV(totalPBV)
@pokemonEditView.renderPBV()
renderFormat: =>
format = @getSelectedTeam().get("generation")
format = DEFAULT_FORMAT if format not of Formats
text = @$(".change-format-dropdown a[data-format='#{format}']").text()
@$(".current-format").text(text)
renderImportTeamModal: =>
$modal = PokeBattle.modal 'modals/import_team', ($modal) =>
$modal.on 'click', '.import-team-submit', (e) =>
teamString = $modal.find('.imported-team').val()
pokemonJSON = PokeBattle.parseTeam(teamString)
errors = @validateImportedTeam(pokemonJSON)
if errors.length > 0
listErrors = errors.map((e) -> "<li>#{e}</li>").join('')
$errors = $modal.find('.form-errors')
$errors.html("<ul>#{listErrors}</ul>").removeClass('hidden')
else
team = new Team(pokemon: pokemonJSON, teambuilder: true)
PokeBattle.TeamStore.add(team)
team.save()
$modal.find('.imported-team').val("")
$modal.modal('hide')
return false
$modal.find('.imported-team').first().focus()
validateImportedTeam: (json) =>
errors = []
pokemonSpecies = (pokemon.species for pokemon in json)
{SpeciesData} = window.Generations[DEFAULT_GENERATION.toUpperCase()]
pokemonSpecies = pokemonSpecies.filter((s) -> s not of SpeciesData)
if pokemonSpecies.length > 0
errors.push(pokemonSpecies.map((n) -> "#{n} is not a valid Pokemon.")...)
return errors
return errors
renderSaving: (team) =>
$team = $(".select-team[data-cid='#{team.cid}']")
$team.find('.show_spinner').removeClass('hidden')
renderSaved: (team) =>
$team = $(".select-team[data-cid='#{team.cid}']")
$team.find('.show_spinner').addClass('hidden')