app/assets/javascripts/views/character_modal_view.js.coffee.erb
<%
require 'smalruby_editor'
require 'smalruby_editor/blockly_message_helper'
%>
# キャラクター設定ダイアログを表現するビュー
Smalruby.CharacterModalView = Backbone.View.extend
model: new Smalruby.Character()
events:
'click #character-modal-costume-selector a.thumbnail': 'onSelectCostume'
'click #character-modal-costume-selector a#character-modal-upload-costume': 'onUploadCostume'
'click #character_rotation_style_free': 'onRotationStyleFree'
'click #character_rotation_style_left_right': 'onRotationStyleLeftRight'
'click #character_rotation_style_none': 'onRotationStyleNone'
'click #character-modal-ok-button': 'onOk'
previewZoomLevel: 0.5
initialize: ->
Smalruby.removeBackdropOnHidden(@$el)
@target = null
$('#character-modal-costume-selector img').on 'dragstart', (e) ->
e.preventDefault()
setPosition = (pos) =>
@model.set
x: parseInt(pos.left / @previewZoomLevel)
y: parseInt(pos.top / @previewZoomLevel)
$('#character-modal-character').draggable
containment: '#character-modal-preview'
cursor: 'move'
drag: (event, ui) ->
setPosition(ui.position)
$('#character-modal-preview').droppable
drop: (event, ui) ->
setPosition(ui.draggable.position())
Smalruby.ignoreEnterKey(@$el)
@templateText = $('#character-modal-costume-template').text()
$('#character-modal-add-costume-button').click (e) =>
e.preventDefault()
costumes = @model.get('costumes').slice(0)
costumes.push(@model.costume())
costumeNames = @model.get('costumeNames').slice(0)
costumeNames.push("costume#{costumes.length}")
@model.set
costumes: costumes
costumeNames: costumeNames
costumeIndex: costumes.length - 1
self = @
changeNameFunc = (e) ->
self.model.set({ name: $(@).val() })
self.nameChanged = true
@$el.find('input[name="character[name]"]').change(changeNameFunc)
@$el.find('input[name="character[name]"]').keyup(changeNameFunc)
@$el.find('input[name="character[x]"]').change (e) ->
self.model.set({ x: $(@).val() })
@$el.find('input[name="character[y]"]').change (e) ->
self.model.set({ y: $(@).val() })
@$el.find('input[name="character[angle]"]').change (e) ->
self.model.set({ angle: $(@).val() })
changeCostumeNameFunc = (e) ->
val = $(@).val()
costumeNames = _.clone(self.model.get('costumeNames'))
costumeNames[self.model.get('costumeIndex')] = val
self.model.set({ costumeNames: costumeNames })
@$el.find('input[name="costume[name]"]').change(changeCostumeNameFunc)
@$el.find('input[name="costume[name]"]').keyup(changeCostumeNameFunc)
@listenTo(@model, 'change:name', @onChangeName)
@listenTo(@model, 'change:x', @onChangeX)
@listenTo(@model, 'change:y', @onChangeY)
@listenTo(@model, 'change:angle', @onChangeAngle)
@listenTo(@model, 'change:costumes', @onChangeCostumes)
@listenTo(@model, 'change:costumeNames', @onChangeCostumeNames)
@listenTo(@model, 'change:costumeIndex', @onChangeCostumeIndex)
@listenTo(@model, 'change:rotationStyle', @onChangeRotationStyle)
@listenTo(@model, 'change', @onChange)
@callAllOnChangeAttributes_()
render: ->
@onChange(@model)
@$el.find('.modal-body').block
message: null
unblock = =>
@$el.find('.modal-body').unblock()
null # HACK: if return unblock(), does not call fail().
@$el.modal
backdrop: 'static'
@$el.modal('show')
dfr = $.Deferred()
$.ajax
url: '/costumes/'
type: 'GET'
cache: false
success: (data, textStatus, jqXHR) -> dfr.resolve(data)
error: dfr.reject
dfr.promise()
.then (data) =>
@replaceCostumeSelectorHtml_(data)
.then(unblock, unblock)
.fail =>
@$el.modal('hide')
errorMessage(<%= bm('.error') %>)
replaceCostumeSelectorHtml_: (html) ->
@$el.find('#character-modal-costume-selector').replaceWith(html)
self = @
@$el.find('#character-modal-costume-selector a.remove-button').click (e) ->
e.preventDefault()
link = $(@)
if confirm(link.attr("data-message"))
dfr = $.Deferred()
$.ajax
url: link.attr("data-url")
type: "DELETE"
cache: false
data: { "_method": "DELETE" },
success: (data, textStatus, jqXHR) -> dfr.resolve(data)
error: dfr.reject
dfr.promise()
.then (data) ->
self.replaceCostumeSelectorHtml_(data)
.fail ->
errorMessage(<%= bm('.error_remove_costume') %>)
@$el.find('#character-modal-upload-costume-form').fileupload
dataType: "html"
done: (e, data) =>
@replaceCostumeSelectorHtml_(data.result)
@setActiveCostume_()
@renderCostumeSet_()
# HACK: ダイアログを表示して50ms程度待たないと画像のサイズが取得できなかった
f = ->
if @readImageSizeflag
img = $('#character-modal-costume-selector .active img')
if img.width() > 0
$('#character-modal-character').css
width: "#{img.width() / 2}px"
height: "#{img.height() / 2}px"
@readImageSizeflag = false
else
setTimeout(_.bind(f, @), 50)
if @readImageSizeflag
setTimeout(_.bind(f, @), 1)
setActiveCostume_: ->
@$el.find('#character-modal-costume-selector a.thumbnail').removeClass('active')
thumb = @$el.find("#character-modal-costume-selector img[alt=\"#{@model.costume()}\"]")
thumb.parent().addClass('active')
if thumb.width() > 0
$('#character-modal-character').css
width: "#{thumb.width() / 2}px"
height: "#{thumb.height() / 2}px"
else
@readImageSizeflag = true
renderCostumeSet_: ->
costumesEl = @$el.find('#character-modal-costume-set')
costumesEl.children().remove()
costumes = @model.get('costumes')
$.each costumes, (i, name) =>
costume =
name: @model.costumeName(i)
basename: name
path: @model.costumeUrl(i)
index: i + 1
html = $(_.template(@templateText, costume))
costumesEl.append(html)
html.find('a.costume').click (e) =>
e.preventDefault()
@model.set({ costumeIndex: i })
removeButton = html.find('a.remove-button')
if costumes.length <= 1
removeButton.hide()
else
removeButton.click (e) =>
e.preventDefault()
@removeCostume_(i)
@renderActiveCostume_()
removeCostume_: (i) ->
costumes = @model.get('costumes').slice(0)
costumes.splice(i, 1)
costumeNames = @model.get('costumeNames').slice(0)
costumeNames.splice(i, 1)
costumeIndex = @model.get('costumeIndex')
if costumeIndex >= costumes.length
costumeIndex = costumes.length - 1
@model.set
costumes: costumes
costumeNames: costumeNames
costumeIndex: costumeIndex
renderActiveCostume_: ->
costumesEl = @$el.find('#character-modal-costume-set')
costumesEl.find('.item').removeClass('active')
costumeIndex = @model.get('costumeIndex')
costumesEl.find(".item:nth-child(#{costumeIndex + 1})").addClass('active')
callAllOnChangeAttributes_: ->
@onChangeName(@model, @model.get('name'))
@onChangeX(@model, @model.get('x'))
@onChangeY(@model, @model.get('y'))
@onChangeAngle(@model, @model.get('angle'))
@onChangeCostumes(@model, @model.get('costumes'))
@onChangeRotationStyle(@model, @model.get('rotationStyle'))
onChangeName: (model, value, options) ->
@$el.find('input[name="character[name]"]').val(value)
onChangeX: (model, value, options) ->
@$el.find('input[name="character[x]"]').val(value)
$('#character_x_value').text(value)
$('#character-modal-character').css('left', parseInt(value * @previewZoomLevel))
onChangeY: (model, value, options) ->
@$el.find('input[name="character[y]"]').val(value)
$('#character_y_value').text(value)
$('#character-modal-character').css('top', parseInt(value * @previewZoomLevel))
onChangeAngle: (model, value, options) ->
@$el.find('input[name="character[angle]"]').val(value)
$('#character_angle_value').text("#{value}°")
rotate = "rotate(#{value}deg)"
$('#character_angle_vector').css
'-moz-transform': rotate
'-webkit-transform': rotate
transform: rotate
@model.rotateImage('#character-modal-character')
onChangeCostumes: (model, value, options) ->
@renderCostumeSet_()
@onChangeCostumeIndex(model, model.get('costumeIndex'))
onChangeCostumeNames: (model, value, options) ->
$.each value, (i, costumeName) =>
@$el.find("#character-modal-costume-set .item:nth-child(#{i + 1}) .name").text(costumeName)
onChangeCostumeIndex: (model, value, options) ->
img = $('<img>').attr
src: model.costumeUrl()
alt: model.costume()
$('#character-modal-character img').replaceWith(img)
$('#character-modal input[name="costume[name]"]').val(model.costumeName())
@setActiveCostume_()
@renderActiveCostume_()
onChangeRotationStyle: (model, value, options) ->
$('#character_rotation_style button.btn').removeClass('btn-primary')
$("#character_rotation_style_#{value}").addClass('btn-primary')
@onChangeAngle(@model, @model.get('angle'))
onChange: (model, options) ->
@validate_()
validate_: ->
return unless @target
valid = true
@$el.find('.control-group').removeClass('error')
@$el.find('#character-modal-costume-set .item').removeClass('error')
unless @model.isValid()
valid = false
$.each @model.validationError, (i, error) =>
if error.attr == 'name'
@$el.find('.control-group[for="character[name]"]').addClass('error')
if error.attr == 'costumeNames'
if error.index == @model.get('costumeIndex')
@$el.find('.control-group[for="costume[name]"]').addClass('error')
@$el.find("#character-modal-costume-set .item:nth-child(#{error.index + 1})").addClass('error')
name = @model.get('name')
if @target.get('name') != name &&
Smalruby.Collections.CharacterSet.findWhere({ name: name })
valid = false
@$el.find('.control-group[for="character[name]"]').addClass('error')
valid
onSelectCostume: (e) ->
e.preventDefault()
attrs = {}
costume = $(e.target).attr('alt') || $(e.target).find('img').attr('alt')
if @model.costume() != costume
costumes = @model.get('costumes').slice(0)
costumes[@model.get('costumeIndex')] = costume
attrs['costumes'] = costumes
unless @nameChanged
prefix = Smalruby.Character.costumeToNamePrefix(costume)
if prefix != @model.namePrefix()
if prefix == @target.namePrefix()
attrs['name'] = @target.get('name')
else
attrs['name'] = Smalruby.Collections.CharacterSet.uniqueName(costume)
@model.set(attrs)
onUploadCostume: (e) ->
e.preventDefault()
@$el.find('input[name="costume[file]"]').click()
onRotationStyleFree: ->
@model.set({ rotationStyle: 'free' })
onRotationStyleLeftRight: ->
@model.set({ rotationStyle: 'left_right' })
onRotationStyleNone: ->
@model.set({ rotationStyle: 'none' })
onOk: ->
if @validate_()
@$el.modal('hide')
if @target
@target.set(_.clone(@model.attributes))
setCharacter: (character)->
@target = character
@model.set(_.clone(@target.attributes))
@nameChanged = false
@