2014-01-03 13:32:13 -05:00
|
|
|
|
View = require 'views/kinds/CocoView'
|
|
|
|
|
template = require 'templates/play/level/tome/spell_palette_entry'
|
2014-02-12 19:42:09 -05:00
|
|
|
|
popoverTemplate = require 'templates/play/level/tome/spell_palette_entry_popover'
|
2014-01-03 13:32:13 -05:00
|
|
|
|
{me} = require 'lib/auth'
|
|
|
|
|
filters = require 'lib/image_filter'
|
2014-02-12 19:42:09 -05:00
|
|
|
|
{downTheChain} = require 'lib/world/world_utils'
|
2014-02-16 18:30:00 -05:00
|
|
|
|
window.Vector = require 'lib/world/vector' # So we can document it
|
2014-02-12 19:42:09 -05:00
|
|
|
|
|
2014-02-13 14:18:20 -05:00
|
|
|
|
safeJSONStringify = (input, maxDepth) ->
|
|
|
|
|
recursion = (input, path, depth) ->
|
|
|
|
|
output = {}
|
|
|
|
|
pPath = undefined
|
|
|
|
|
refIdx = undefined
|
|
|
|
|
path = path or ""
|
|
|
|
|
depth = depth or 0
|
|
|
|
|
depth++
|
|
|
|
|
return "{depth over " + maxDepth + "}" if maxDepth and depth > maxDepth
|
|
|
|
|
for p of input
|
|
|
|
|
pPath = ((if path then (path + ".") else "")) + p
|
|
|
|
|
if typeof input[p] is "function"
|
|
|
|
|
output[p] = "{function}"
|
|
|
|
|
else if typeof input[p] is "object"
|
|
|
|
|
refIdx = refs.indexOf(input[p])
|
|
|
|
|
if -1 isnt refIdx
|
|
|
|
|
output[p] = "{reference to " + refsPaths[refIdx] + "}"
|
|
|
|
|
else
|
|
|
|
|
refs.push input[p]
|
|
|
|
|
refsPaths.push pPath
|
|
|
|
|
output[p] = recursion(input[p], pPath, depth)
|
|
|
|
|
else
|
|
|
|
|
output[p] = input[p]
|
|
|
|
|
output
|
|
|
|
|
refs = []
|
|
|
|
|
refsPaths = []
|
|
|
|
|
maxDepth = maxDepth or 5
|
|
|
|
|
if typeof input is "object"
|
|
|
|
|
output = recursion(input)
|
|
|
|
|
else
|
|
|
|
|
output = input
|
|
|
|
|
JSON.stringify output, null, 1
|
|
|
|
|
|
2014-02-16 18:30:00 -05:00
|
|
|
|
# http://stackoverflow.com/a/987376/540620
|
|
|
|
|
$.fn.selectText = ->
|
|
|
|
|
el = @[0]
|
|
|
|
|
if document.body.createTextRange
|
|
|
|
|
range = document.body.createTextRange()
|
|
|
|
|
range.moveToElementText(el)
|
|
|
|
|
range.select()
|
|
|
|
|
else if window.getSelection
|
|
|
|
|
selection = window.getSelection()
|
|
|
|
|
range = document.createRange()
|
|
|
|
|
range.selectNodeContents(el)
|
|
|
|
|
selection.removeAllRanges()
|
|
|
|
|
selection.addRange(range)
|
|
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
|
module.exports = class SpellPaletteEntryView extends View
|
|
|
|
|
tagName: 'div' # Could also try <code> instead of <div>, but would need to adjust colors
|
|
|
|
|
className: 'spell-palette-entry-view'
|
|
|
|
|
template: template
|
2014-02-16 18:30:00 -05:00
|
|
|
|
popoverPinned: false
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
|
|
subscriptions:
|
|
|
|
|
'surface:frame-changed': "onFrameChanged"
|
2014-02-20 13:39:16 -05:00
|
|
|
|
'tome:palette-hovered': "onPaletteHovered"
|
|
|
|
|
'tome:palette-pin-toggled': "onPalettePinToggled"
|
2014-02-23 19:34:03 -05:00
|
|
|
|
'tome:spell-debug-property-hovered': 'onSpellDebugPropertyHovered'
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
|
|
events:
|
2014-02-16 18:30:00 -05:00
|
|
|
|
'mouseenter': 'onMouseEnter'
|
|
|
|
|
'mouseleave': 'onMouseLeave'
|
2014-01-03 13:32:13 -05:00
|
|
|
|
'click': 'onClick'
|
|
|
|
|
|
|
|
|
|
constructor: (options) ->
|
|
|
|
|
super options
|
|
|
|
|
@thang = options.thang
|
|
|
|
|
@doc = options.doc
|
2014-02-12 19:42:09 -05:00
|
|
|
|
if _.isString @doc
|
|
|
|
|
@doc = name: @doc, type: typeof @thang[@doc]
|
|
|
|
|
if options.isSnippet
|
2014-02-16 18:30:00 -05:00
|
|
|
|
@doc.type = @doc.owner = 'snippet'
|
2014-02-12 21:54:45 -05:00
|
|
|
|
@doc.shortName = @doc.shorterName = @doc.title = @doc.name
|
2014-02-12 19:42:09 -05:00
|
|
|
|
else
|
2014-02-16 18:30:00 -05:00
|
|
|
|
@doc.owner ?= 'this'
|
2014-02-23 16:27:02 -05:00
|
|
|
|
suffix = ''
|
|
|
|
|
if @doc.type is 'function'
|
|
|
|
|
argNames = (arg.name for arg in @doc.args ? []).join(', ')
|
|
|
|
|
argNames = '...' if argNames.length > 6
|
|
|
|
|
suffix = "(#{argNames})"
|
2014-02-12 19:42:09 -05:00
|
|
|
|
@doc.shortName = "#{@doc.owner}.#{@doc.name}#{suffix};"
|
2014-02-16 18:30:00 -05:00
|
|
|
|
if @doc.owner is 'this' or options.tabbify
|
2014-02-12 19:42:09 -05:00
|
|
|
|
@doc.shorterName = "#{@doc.name}#{suffix}"
|
|
|
|
|
else
|
|
|
|
|
@doc.shorterName = @doc.shortName.replace ';', ''
|
|
|
|
|
@doc.title = if options.shortenize then @doc.shorterName else @doc.shortName
|
|
|
|
|
|
|
|
|
|
getRenderData: ->
|
|
|
|
|
c = super()
|
|
|
|
|
c.doc = @doc
|
|
|
|
|
c
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
|
|
afterRender: ->
|
|
|
|
|
super()
|
2014-02-12 19:42:09 -05:00
|
|
|
|
@$el.addClass(@doc.type)
|
|
|
|
|
@$el.popover(
|
2014-02-16 18:30:00 -05:00
|
|
|
|
animation: false
|
2014-01-03 13:32:13 -05:00
|
|
|
|
html: true
|
2014-02-20 13:39:16 -05:00
|
|
|
|
placement: 'top'
|
2014-02-16 18:30:00 -05:00
|
|
|
|
trigger: 'manual' # Hover, until they click, which will then pin it until unclick.
|
2014-02-12 19:42:09 -05:00
|
|
|
|
content: @formatPopover()
|
2014-02-16 18:30:00 -05:00
|
|
|
|
container: '#tome-view'
|
2014-01-03 13:32:13 -05:00
|
|
|
|
)
|
2014-01-30 19:36:36 -05:00
|
|
|
|
@$el.on 'show.bs.popover', =>
|
2014-02-20 13:39:16 -05:00
|
|
|
|
Backbone.Mediator.publish 'tome:palette-hovered', thang: @thang, prop: @doc.name, entry: @
|
2014-02-12 19:42:09 -05:00
|
|
|
|
|
|
|
|
|
formatPopover: ->
|
2014-02-12 21:54:45 -05:00
|
|
|
|
content = popoverTemplate doc: @doc, value: @formatValue(), marked: marked, argumentExamples: (arg.example or arg.default or arg.name for arg in @doc.args ? [])
|
2014-02-12 19:42:09 -05:00
|
|
|
|
owner = if @doc.owner is 'this' then @thang else window[@doc.owner]
|
2014-02-18 14:23:01 -05:00
|
|
|
|
content = content.replace /#{spriteName}/g, @thang.type ? @thang.spriteName # Prefer type, and excluded the quotes we'd get with @formatValue
|
2014-02-12 19:42:09 -05:00
|
|
|
|
content.replace /\#\{(.*?)\}/g, (s, properties) => @formatValue downTheChain(owner, properties.split('.'))
|
|
|
|
|
|
|
|
|
|
formatValue: (v) ->
|
2014-02-16 18:30:00 -05:00
|
|
|
|
return null if @doc.type is 'snippet'
|
2014-02-14 19:52:50 -05:00
|
|
|
|
return @thang.now() if @doc.name is 'now'
|
2014-02-13 12:26:21 -05:00
|
|
|
|
return '[Function]' if not v and @doc.type is 'function'
|
2014-02-12 19:42:09 -05:00
|
|
|
|
unless v?
|
|
|
|
|
if @doc.owner is 'this'
|
|
|
|
|
v = @thang[@doc.name]
|
|
|
|
|
else
|
2014-02-16 18:30:00 -05:00
|
|
|
|
v = window[@doc.owner][@doc.name] # grab Math or Vector
|
2014-02-13 14:18:20 -05:00
|
|
|
|
if @doc.type is 'number' and not isNaN v
|
2014-02-12 19:42:09 -05:00
|
|
|
|
if v == Math.round v
|
|
|
|
|
return v
|
|
|
|
|
return v.toFixed 2
|
|
|
|
|
if _.isString v
|
|
|
|
|
return "\"#{v}\""
|
|
|
|
|
if v?.id
|
|
|
|
|
return v.id
|
|
|
|
|
if v?.name
|
|
|
|
|
return v.name
|
|
|
|
|
if _.isArray v
|
|
|
|
|
return '[' + (@formatValue v2 for v2 in v).join(', ') + ']'
|
2014-02-13 14:18:20 -05:00
|
|
|
|
if _.isPlainObject v
|
|
|
|
|
return safeJSONStringify v, 2
|
2014-02-12 19:42:09 -05:00
|
|
|
|
v
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
2014-02-16 18:30:00 -05:00
|
|
|
|
onMouseEnter: (e) ->
|
2014-01-03 13:32:13 -05:00
|
|
|
|
# Make sure the doc has the updated Thang so it can regenerate its prop value
|
2014-02-12 19:42:09 -05:00
|
|
|
|
@$el.data('bs.popover').options.content = @formatPopover()
|
2014-01-03 13:32:13 -05:00
|
|
|
|
@$el.popover('setContent')
|
2014-02-20 13:39:16 -05:00
|
|
|
|
@$el.popover 'show' unless @popoverPinned or @otherPopoverPinned
|
2014-02-16 18:30:00 -05:00
|
|
|
|
|
|
|
|
|
onMouseLeave: (e) ->
|
2014-02-20 13:39:16 -05:00
|
|
|
|
@$el.popover 'hide' unless @popoverPinned or @otherPopoverPinned
|
2014-02-16 18:30:00 -05:00
|
|
|
|
|
|
|
|
|
togglePinned: ->
|
|
|
|
|
if @popoverPinned
|
|
|
|
|
@popoverPinned = false
|
|
|
|
|
@$el.add('#tome-view .popover').removeClass 'pinned'
|
2014-02-20 13:39:16 -05:00
|
|
|
|
$('#tome-view .popover .close').remove()
|
2014-02-16 18:30:00 -05:00
|
|
|
|
@$el.popover 'hide'
|
|
|
|
|
else
|
|
|
|
|
@popoverPinned = true
|
2014-02-20 13:39:16 -05:00
|
|
|
|
@$el.popover 'show'
|
2014-02-16 18:30:00 -05:00
|
|
|
|
@$el.add('#tome-view .popover').addClass 'pinned'
|
2014-02-20 13:39:16 -05:00
|
|
|
|
x = $('<button type="button" data-dismiss="modal" aria-hidden="true" class="close">×</button>')
|
|
|
|
|
$('#tome-view .popover').append x
|
|
|
|
|
x.on 'click', @onClick
|
|
|
|
|
Backbone.Mediator.publish 'tome:palette-pin-toggled', entry: @, pinned: @popoverPinned
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
2014-02-20 13:39:16 -05:00
|
|
|
|
onClick: (e) =>
|
2014-02-16 18:30:00 -05:00
|
|
|
|
@togglePinned()
|
2014-02-20 13:39:16 -05:00
|
|
|
|
Backbone.Mediator.publish 'tome:palette-clicked', thang: @thang, prop: @doc.name, entry: @
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
|
|
onFrameChanged: (e) ->
|
|
|
|
|
return unless e.selectedThang?.id is @thang.id
|
2014-01-15 16:04:48 -05:00
|
|
|
|
@options.thang = @thang = e.selectedThang # Update our thang to the current version
|
2014-02-11 14:31:02 -05:00
|
|
|
|
|
2014-02-20 13:39:16 -05:00
|
|
|
|
onPaletteHovered: (e) ->
|
|
|
|
|
return if e.entry is @
|
|
|
|
|
@togglePinned() if @popoverPinned
|
|
|
|
|
|
|
|
|
|
onPalettePinToggled: (e) ->
|
|
|
|
|
return if e.entry is @
|
|
|
|
|
@otherPopoverPinned = e.pinned
|
|
|
|
|
|
2014-02-23 19:34:03 -05:00
|
|
|
|
onSpellDebugPropertyHovered: (e) ->
|
|
|
|
|
matched = e.property is @doc.name and e.owner is @doc.owner
|
|
|
|
|
if matched and not @debugHovered
|
|
|
|
|
@debugHovered = true
|
|
|
|
|
@togglePinned() unless @popoverPinned
|
|
|
|
|
else if @debugHovered and not matched
|
|
|
|
|
@debugHovered = false
|
|
|
|
|
@togglePinned() if @popoverPinned
|
|
|
|
|
null
|
|
|
|
|
|
2014-02-11 14:31:02 -05:00
|
|
|
|
destroy: ->
|
2014-02-16 18:30:00 -05:00
|
|
|
|
$('.popover.pinned').remove() if @popoverPinned # @$el.popover('destroy') doesn't work
|
|
|
|
|
@togglePinned() if @popoverPinned
|
|
|
|
|
@$el.popover 'destroy'
|
2014-02-11 14:31:02 -05:00
|
|
|
|
@$el.off()
|
2014-02-12 19:42:09 -05:00
|
|
|
|
super()
|