diff --git a/app/lib/world/vector.coffee b/app/lib/world/vector.coffee index dd9efe891..72f761775 100644 --- a/app/lib/world/vector.coffee +++ b/app/lib/world/vector.coffee @@ -2,7 +2,7 @@ class Vector @className: "Vector" # Class methods for nondestructively operating - for name in ['add', 'subtract', 'multiply', 'divide'] + for name in ['add', 'subtract', 'multiply', 'divide', 'limit', 'normalize'] do (name) -> Vector[name] = (a, b, useZ) -> a.copy()[name](b, useZ) diff --git a/app/styles/play/level/hud.sass b/app/styles/play/level/hud.sass index 47d5376ae..a087f013c 100644 --- a/app/styles/play/level/hud.sass +++ b/app/styles/play/level/hud.sass @@ -89,6 +89,7 @@ margin: 8px 8px 0 0 overflow-y: scroll float: left + @include user-select(text) .prop img diff --git a/app/styles/play/level/tome/spell_palette.sass b/app/styles/play/level/tome/spell_palette.sass index ff697326d..e941f8def 100644 --- a/app/styles/play/level/tome/spell_palette.sass +++ b/app/styles/play/level/tome/spell_palette.sass @@ -6,12 +6,10 @@ left: 10px right: 10px height: 140px - padding-top: 50px - // Height and padding-top relate to .tab-content height - padding-left: 35px - padding-right: 60px - // Docs' popovers flicker when too far right, so we have big padding-right - // twilight: color: #E2E2E2 + // Height relates to .tab-content height + padding-top: 35px + padding-left: 12px + padding-right: 4px color: #333 // Get crazy with the backgrounds so that we can lower the opacity on the editor background above it, making a gradient of the disabled background color on the top around where it's usually covered background-color: transparent @@ -36,3 +34,23 @@ line-height: 16px margin: 0 4px font-weight: normal + + .nav > li > a + padding: 2px 20px 0px 20px + margin-bottom: 3px + + ul.nav.nav-pills + li.active a + background-color: transparent + &.multiple-tabs li.active a + background-color: lighten(rgb(230, 212, 146), 10%) + &.multiple-tabs li:not(.active) a + cursor: pointer + + //.nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus + // background-color: lighten(rgb(230, 212, 146), 10%) + + .property-entry-column + display: inline-block + margin-right: 3px + vertical-align: top diff --git a/app/styles/play/level/tome/spell_palette_entry.sass b/app/styles/play/level/tome/spell_palette_entry.sass index 9192ebcb0..f2ec74182 100644 --- a/app/styles/play/level/tome/spell_palette_entry.sass +++ b/app/styles/play/level/tome/spell_palette_entry.sass @@ -1,28 +1,32 @@ -@import "../../../bootstrap/mixins" +@import "app/styles/bootstrap/mixins" +@import "app/styles/mixins" .spell-palette-entry-view - display: inline-block - margin: 0px 4px + display: block + padding: 0px 4px font-family: Menlo, Monaco, Consolas, "Courier New", monospace font-size: 12px + border: 1px solid transparent + cursor: pointer + @include user-select(all) + + &:hover + border: 1px solid #BFF + + &.pinned + background-color: darken(#BFF, 20%) // Pulling these colors from the most relevant textmate-theme classes &.function - // twilight: color: #7587A6 color: #0000A2 &.object - // twilight: color: #AC885B color: rgb(6, 150, 14) &.string - // twilight: color: #CDA869 color: rgb(3, 106, 7) &.number - // twilight: color: #F9EE98 color: rgb(0, 0, 205) &.boolean - // twilight: color: #C9CEA8 color: rgb(88, 92, 246) &.undefined - // twilight: color: #CF6A4C color: rgb(197, 6, 11) diff --git a/app/styles/play/level/tome/tome.sass b/app/styles/play/level/tome/tome.sass index c8eaee5f3..1626dd134 100644 --- a/app/styles/play/level/tome/tome.sass +++ b/app/styles/play/level/tome/tome.sass @@ -1,15 +1,24 @@ -@import "../../../bootstrap/mixins" +@import "app/styles/bootstrap/mixins" +@import "app/styles/mixins" #tome-view height: 100% .popover padding: 10px - max-width: 400px + min-width: 400px background: transparent url(/images/level/popover_background.png) background-size: 100% 100% border: 0 + left: auto !important + top: auto !important + right: 100% + bottom: 151px @include box-shadow(0 0 0 #000) + @include user-select(text) + + &.pinned + @include box-shadow(0 0 500px white) h1:not(.not-code), h2:not(.not-code), h3:not(.not-code), h4:not(.not-code), h5:not(.not-code), h6:not(.not-code) font-family: Menlo, Monaco, Consolas, "Courier New", monospace diff --git a/app/templates/play/level/tome/spell_palette.jade b/app/templates/play/level/tome/spell_palette.jade index bf5c16aa3..19489b42c 100644 --- a/app/templates/play/level/tome/spell_palette.jade +++ b/app/templates/play/level/tome/spell_palette.jade @@ -1,3 +1,10 @@ img(src="/images/level/code_palette_background.png").code-palette-background -h4(data-i18n="play_level.tome_available_spells") Available Spells -.properties +ul(class="nav nav-pills" + (tabbed ? ' multiple-tabs' : '')) + each slug, group in entryGroupSlugs + li(class=group == "this" || slug == "available-spells" ? "active" : "") + a(data-toggle="pill", data-target='#palette-tab-' + slug) + h4= group +.tab-content + each slug, group in entryGroupSlugs + div(id="palette-tab-" + slug, class="tab-pane" + (group == "this" || slug == "available-spells" ? " active" : "")) + div(class="properties properties-" + slug) diff --git a/app/views/editor/level/save_view.coffee b/app/views/editor/level/save_view.coffee index 72c5306de..a1f0ee01b 100644 --- a/app/views/editor/level/save_view.coffee +++ b/app/views/editor/level/save_view.coffee @@ -16,7 +16,7 @@ module.exports = class LevelSaveView extends SaveVersionModal constructor: (options) -> super options @level = options.level - + getRenderData: (context={}) -> context = super(context) context.level = @level @@ -63,8 +63,8 @@ module.exports = class LevelSaveView extends SaveVersionModal console.log "Got errors:", JSON.parse(res.responseText) forms.applyErrorsToForm($(form), JSON.parse(res.responseText)) res.success => - @hide() modelsToSave = _.without modelsToSave, newModel unless modelsToSave.length url = "/editor/level/#{@level.get('slug') or @level.id}" document.location.href = url + @hide() # This will destroy everything, so do it last diff --git a/app/views/play/level/hud_view.coffee b/app/views/play/level/hud_view.coffee index baf141a25..a96f43acf 100644 --- a/app/views/play/level/hud_view.coffee +++ b/app/views/play/level/hud_view.coffee @@ -24,12 +24,15 @@ module.exports = class HUDView extends View 'god:new-world-created': 'onNewWorld' events: - 'click': -> Backbone.Mediator.publish 'focus-editor' + 'click': 'onClick' afterRender: -> super() @$el.addClass 'no-selection' + onClick: (e) -> + Backbone.Mediator.publish 'focus-editor' unless $(e.target).parents('.thang-props').length + onFrameChanged: (e) -> @timeProgress = e.progress @update() diff --git a/app/views/play/level/tome/spell_list_tab_entry_view.coffee b/app/views/play/level/tome/spell_list_tab_entry_view.coffee index 9c989b7aa..9c5110d9b 100644 --- a/app/views/play/level/tome/spell_list_tab_entry_view.coffee +++ b/app/views/play/level/tome/spell_list_tab_entry_view.coffee @@ -64,7 +64,7 @@ module.exports = class SpellListTabEntryView extends SpellListEntryView @$el.find('code').popover( animation: true html: true - placement: 'bottom' + placement: 'left' trigger: 'hover' content: @formatPopover doc container: @$el.parent() @@ -126,4 +126,5 @@ module.exports = class SpellListTabEntryView extends SpellListEntryView destroy: -> @avatar?.destroy() + @$el.find('code').popover 'destroy' super() diff --git a/app/views/play/level/tome/spell_palette_entry_view.coffee b/app/views/play/level/tome/spell_palette_entry_view.coffee index 7e435108c..310e7c5d4 100644 --- a/app/views/play/level/tome/spell_palette_entry_view.coffee +++ b/app/views/play/level/tome/spell_palette_entry_view.coffee @@ -4,6 +4,7 @@ popoverTemplate = require 'templates/play/level/tome/spell_palette_entry_popover {me} = require 'lib/auth' filters = require 'lib/image_filter' {downTheChain} = require 'lib/world/world_utils' +window.Vector = require 'lib/world/vector' # So we can document it # If we use marked somewhere else, we'll have to make sure to preserve options marked.setOptions {gfm: true, sanitize: false, smartLists: true, breaks: true} @@ -41,16 +42,32 @@ safeJSONStringify = (input, maxDepth) -> output = input JSON.stringify output, null, 1 +# 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) + module.exports = class SpellPaletteEntryView extends View tagName: 'div' # Could also try instead of
, but would need to adjust colors className: 'spell-palette-entry-view' template: template + popoverPinned: false subscriptions: 'surface:frame-changed': "onFrameChanged" events: - 'mouseover': 'onMouseOver' + 'mouseenter': 'onMouseEnter' + 'mouseleave': 'onMouseLeave' 'click': 'onClick' constructor: (options) -> @@ -59,14 +76,14 @@ module.exports = class SpellPaletteEntryView extends View @doc = options.doc if _.isString @doc @doc = name: @doc, type: typeof @thang[@doc] - @doc.owner ?= 'this' if options.isSnippet - @doc.type = 'snippet' + @doc.type = @doc.owner = 'snippet' @doc.shortName = @doc.shorterName = @doc.title = @doc.name else + @doc.owner ?= 'this' suffix = if @doc.type is 'function' then '()' else '' @doc.shortName = "#{@doc.owner}.#{@doc.name}#{suffix};" - if @doc.owner is 'this' + if @doc.owner is 'this' or options.tabbify @doc.shorterName = "#{@doc.name}#{suffix}" else @doc.shorterName = @doc.shortName.replace ';', '' @@ -81,12 +98,12 @@ module.exports = class SpellPaletteEntryView extends View super() @$el.addClass(@doc.type) @$el.popover( - animation: true + animation: false html: true - placement: 'top' - trigger: 'hover' + placement: 'left' + trigger: 'manual' # Hover, until they click, which will then pin it until unclick. content: @formatPopover() - container: @$el.parent().parent().parent() + container: '#tome-view' ) @$el.on 'show.bs.popover', => Backbone.Mediator.publish 'tome:palette-hovered', thang: @thang, prop: @doc.name @@ -98,13 +115,14 @@ module.exports = class SpellPaletteEntryView extends View content.replace /\#\{(.*?)\}/g, (s, properties) => @formatValue downTheChain(owner, properties.split('.')) formatValue: (v) -> + return null if @doc.type is 'snippet' return @thang.now() if @doc.name is 'now' return '[Function]' if not v and @doc.type is 'function' unless v? if @doc.owner is 'this' v = @thang[@doc.name] else - v = window[@doc.owner][@doc.name] + v = window[@doc.owner][@doc.name] # grab Math or Vector if @doc.type is 'number' and not isNaN v if v == Math.round v return v @@ -121,12 +139,29 @@ module.exports = class SpellPaletteEntryView extends View return safeJSONStringify v, 2 v - onMouseOver: (e) -> + onMouseEnter: (e) -> # Make sure the doc has the updated Thang so it can regenerate its prop value @$el.data('bs.popover').options.content = @formatPopover() @$el.popover('setContent') + @$el.popover 'show' unless @popoverPinned + + onMouseLeave: (e) -> + @$el.popover 'hide' unless @popoverPinned + + togglePinned: -> + if @popoverPinned + @popoverPinned = false + @$el.add('#tome-view .popover').removeClass 'pinned' + @$el.popover 'hide' + else + @popoverPinned = true + @$el.add('#tome-view .popover').addClass 'pinned' onClick: (e) -> + unless @popoverPinned + $(e.target).selectText() + e.stopPropagation() # don't re-focus editor since we might want to select text + @togglePinned() Backbone.Mediator.publish 'tome:palette-clicked', thang: @thang, prop: @doc.name onFrameChanged: (e) -> @@ -134,5 +169,8 @@ module.exports = class SpellPaletteEntryView extends View @options.thang = @thang = e.selectedThang # Update our thang to the current version destroy: -> + $('.popover.pinned').remove() if @popoverPinned # @$el.popover('destroy') doesn't work + @togglePinned() if @popoverPinned + @$el.popover 'destroy' @$el.off() super() diff --git a/app/views/play/level/tome/spell_palette_view.coffee b/app/views/play/level/tome/spell_palette_view.coffee index 8d4a336a4..d5c83fe71 100644 --- a/app/views/play/level/tome/spell_palette_view.coffee +++ b/app/views/play/level/tome/spell_palette_view.coffee @@ -5,6 +5,8 @@ filters = require 'lib/image_filter' SpellPaletteEntryView = require './spell_palette_entry_view' LevelComponent = require 'models/LevelComponent' +N_ROWS = 4 + module.exports = class SpellPaletteView extends View id: 'spell-palette-view' template: template @@ -18,28 +20,56 @@ module.exports = class SpellPaletteView extends View constructor: (options) -> super options @thang = options.thang + @createPalette() + + getRenderData: -> + c = super() + c.entryGroups = @entryGroups + c.entryGroupSlugs = @entryGroupSlugs + c.tabbed = _.size(@entryGroups) > 1 + c afterRender: -> super() - @createPalette() + for group, entries of @entryGroups + groupSlug = @entryGroupSlugs[group] + for columnNumber, entryColumn of entries + col = $('
').appendTo @$el.find(".properties-#{groupSlug}") + for entry in entryColumn + col.append entry.el + entry.render() # Render after appending so that we can access parent container for popover createPalette: -> lcs = @supermodel.getModels LevelComponent allDocs = {} allDocs[doc.name] = doc for doc in (lc.get('propertyDocumentation') ? []) for lc in lcs - props = @thang.programmableProperties ? [] - snippets = @thang.programmableSnippets ? [] + props = _.sortBy @thang.programmableProperties ? [] + snippets = _.sortBy @thang.programmableSnippets ? [] shortenize = props.length + snippets.length > 6 + tabbify = props.length + snippets.length >= 10 @entries = [] - @entries.push @addEntry(allDocs[prop] ? prop, shortenize) for prop in props - @entries.push @addEntry(allDocs[prop] ? prop, shortenize, true) for prop in snippets + @entries.push @addEntry(allDocs[prop] ? prop, shortenize, tabbify) for prop in props + @entries.push @addEntry(allDocs[prop] ? prop, shortenize, tabbify, true) for prop in snippets + @entries = _.sortBy @entries, (entry) -> + order = ['this', 'Math', 'Vector', 'snippets'] + index = order.indexOf entry.doc.owner + index = String.fromCharCode if index is -1 then order.length else index + index += entry.doc.name + if tabbify and _.find @entries, ((entry) -> entry.doc.owner isnt 'this') + @entryGroups = _.groupBy @entries, (entry) -> entry.doc.owner + else + defaultGroup = $.i18n.t("play_level.tome_available_spells", defaultValue: "Available Spells") + @entryGroups = {} + @entryGroups[defaultGroup] = @entries + @entryGroupSlugs = {} + for group, entries of @entryGroups + @entryGroupSlugs[group] = _.string.slugify group + @entryGroups[group] = _.groupBy entries, (entry, i) -> Math.floor i / N_ROWS + null - addEntry: (doc, shortenize, isSnippet=false) -> - entry = new SpellPaletteEntryView doc: doc, thang: @thang, shortenize: shortenize, isSnippet: isSnippet - @$el.find('.properties').append entry.el - entry.render() # Render after appending so that we can access parent container for popover - entry + addEntry: (doc, shortenize, tabbify, isSnippet=false) -> + new SpellPaletteEntryView doc: doc, thang: @thang, shortenize: shortenize, tabbify: tabbify, isSnippet: isSnippet onDisableControls: (e) -> @toggleControls e, false onEnableControls: (e) -> @toggleControls e, true diff --git a/app/views/play/level/tome/tome_view.coffee b/app/views/play/level/tome/tome_view.coffee index 05cd4d113..621481d31 100644 --- a/app/views/play/level/tome/tome_view.coffee +++ b/app/views/play/level/tome/tome_view.coffee @@ -51,7 +51,7 @@ module.exports = class TomeView extends View events: 'click #spell-view': 'onSpellViewClick' - 'click': -> Backbone.Mediator.publish 'focus-editor' + 'click': 'onClick' afterRender: -> super() @@ -140,6 +140,9 @@ module.exports = class TomeView extends View onSpellViewClick: (e) -> @spellList.$el.hide() + onClick: (e) -> + Backbone.Mediator.publish 'focus-editor' unless $(e.target).parents('.popover').length + clearSpellView: -> @spellView?.dismiss() @spellView?.$el.after('
').detach() diff --git a/app/views/play/level_view.coffee b/app/views/play/level_view.coffee index 4ff8e096c..1d7d33fe7 100644 --- a/app/views/play/level_view.coffee +++ b/app/views/play/level_view.coffee @@ -387,7 +387,6 @@ module.exports = class PlayLevelView extends View team = team?.team unless _.isString team team ?= 'humans' me.team = team - console.log "level:team-set to", team Backbone.Mediator.publish 'level:team-set', team: team destroy: ->