Merge branch 'master' of https://github.com/codecombat/codecombat
This commit is contained in:
commit
033e3a6c0b
10 changed files with 341 additions and 9 deletions
app
lib
styles/editor/thang
templates/editor/thang
views
server
|
@ -1,9 +1,13 @@
|
||||||
|
{hexToHSL, hslToHex} = require 'lib/utils'
|
||||||
|
|
||||||
module.exports = class SpriteBuilder
|
module.exports = class SpriteBuilder
|
||||||
constructor: (@thangType, @options) ->
|
constructor: (@thangType, @options) ->
|
||||||
|
@options ?= {}
|
||||||
raw = _.cloneDeep(@thangType.get('raw'))
|
raw = _.cloneDeep(@thangType.get('raw'))
|
||||||
@shapeStore = raw.shapes
|
@shapeStore = raw.shapes
|
||||||
@containerStore = raw.containers
|
@containerStore = raw.containers
|
||||||
@animationStore = raw.animations
|
@animationStore = raw.animations
|
||||||
|
@buildColorMaps()
|
||||||
|
|
||||||
setOptions: (@options) ->
|
setOptions: (@options) ->
|
||||||
|
|
||||||
|
@ -93,7 +97,7 @@ module.exports = class SpriteBuilder
|
||||||
if shapeData.lf?
|
if shapeData.lf?
|
||||||
shape.graphics.lf shapeData.lf...
|
shape.graphics.lf shapeData.lf...
|
||||||
else if shapeData.fc?
|
else if shapeData.fc?
|
||||||
shape.graphics.f shapeData.fc
|
shape.graphics.f @colorMap[shapeKey] or shapeData.fc
|
||||||
if shapeData.ls?
|
if shapeData.ls?
|
||||||
shape.graphics.ls shapeData.ls...
|
shape.graphics.ls shapeData.ls...
|
||||||
else if shapeData.sc?
|
else if shapeData.sc?
|
||||||
|
@ -118,3 +122,64 @@ module.exports = class SpriteBuilder
|
||||||
cont.addChild(child)
|
cont.addChild(child)
|
||||||
cont.bounds = new createjs.Rectangle(contData.b...)
|
cont.bounds = new createjs.Rectangle(contData.b...)
|
||||||
cont
|
cont
|
||||||
|
|
||||||
|
buildColorMaps: ->
|
||||||
|
@colorMap = {}
|
||||||
|
colorGroups = @thangType.get('colorGroups')
|
||||||
|
return if _.isEmpty colorGroups
|
||||||
|
colorConfig = @options.colorConfig
|
||||||
|
# colorConfig ?= {team: {hue:0.4, saturation: -0.5, lightness: -0.5}} # test config
|
||||||
|
return if not colorConfig
|
||||||
|
|
||||||
|
for group, config of colorConfig
|
||||||
|
continue unless colorGroups[group] # color group not found...
|
||||||
|
@buildColorMapForGroup(colorGroups[group], config)
|
||||||
|
|
||||||
|
buildColorMapForGroup: (shapes, config) ->
|
||||||
|
return unless shapes.length
|
||||||
|
colors = @initColorMap(shapes)
|
||||||
|
@adjustHuesForColorMap(colors, config.hue)
|
||||||
|
@adjustColorMap(colors, 1, config.lightness)
|
||||||
|
@adjustColorMap(colors, 2, config.saturation)
|
||||||
|
@applyColorMap(shapes, colors)
|
||||||
|
|
||||||
|
initColorMap: (shapes) ->
|
||||||
|
colors = {}
|
||||||
|
for shapeKey in shapes
|
||||||
|
shape = @shapeStore[shapeKey]
|
||||||
|
continue if (not shape.fc?) or colors[shape.fc]
|
||||||
|
hsl = hexToHSL(shape.fc)
|
||||||
|
colors[shape.fc] = hsl
|
||||||
|
colors
|
||||||
|
|
||||||
|
adjustHuesForColorMap: (colors, targetHue) ->
|
||||||
|
hues = (hsl[0] for hex, hsl of colors)
|
||||||
|
|
||||||
|
# 'rotate' the hue spectrum so averaging works
|
||||||
|
if Math.max(hues) - Math.min(hues) > 0.5
|
||||||
|
hues = (if h < 0.5 then h + 1.0 else h for h in hues)
|
||||||
|
averageHue = sum(hues) / hues.length
|
||||||
|
averageHue %= 1
|
||||||
|
# end result should be something like a hue array of [0.9, 0.3] gets an average of 0.1
|
||||||
|
|
||||||
|
targetHue ?= 0
|
||||||
|
diff = targetHue - averageHue
|
||||||
|
hsl[0] = (hsl[0] + diff + 1) % 1 for hex, hsl of colors
|
||||||
|
|
||||||
|
adjustColorMap: (colorMap, index, adjustment) ->
|
||||||
|
return unless adjustment
|
||||||
|
for hex, hsl of colorMap
|
||||||
|
value = hsl[index]
|
||||||
|
if adjustment > 0
|
||||||
|
hsl[index] = value + (1 - value) * adjustment
|
||||||
|
else
|
||||||
|
hsl[index] = value + value * adjustment
|
||||||
|
|
||||||
|
applyColorMap: (shapes, colors) ->
|
||||||
|
for shapeKey in shapes
|
||||||
|
shape = @shapeStore[shapeKey]
|
||||||
|
continue if (not shape.fc?) or not(colors[shape.fc])
|
||||||
|
@colorMap[shapeKey] = hslToHex(colors[shape.fc])
|
||||||
|
|
||||||
|
|
||||||
|
sum = (nums) -> _.reduce(nums, (s, num) -> s + num)
|
|
@ -28,4 +28,20 @@ module.exports.normalizeFunc = (func_thing, object) ->
|
||||||
console.error("Could not find method", func_thing, 'in object', @)
|
console.error("Could not find method", func_thing, 'in object', @)
|
||||||
return => null # always return a func, or Mediator will go boom
|
return => null # always return a func, or Mediator will go boom
|
||||||
func_thing = func
|
func_thing = func
|
||||||
return func_thing
|
return func_thing
|
||||||
|
|
||||||
|
module.exports.hexToHSL = (hex) ->
|
||||||
|
rgbToHsl(hexToR(hex), hexToG(hex), hexToB(hex))
|
||||||
|
|
||||||
|
hexToR = (h) -> parseInt (cutHex(h)).substring(0, 2), 16
|
||||||
|
hexToG = (h) -> parseInt (cutHex(h)).substring(2, 4), 16
|
||||||
|
hexToB = (h) -> parseInt (cutHex(h)).substring(4, 6), 16
|
||||||
|
cutHex = (h) -> (if (h.charAt(0) is "#") then h.substring(1, 7) else h)
|
||||||
|
|
||||||
|
module.exports.hslToHex = (hsl) ->
|
||||||
|
'#' + (toHex(n) for n in hslToRgb(hsl...)).join('')
|
||||||
|
|
||||||
|
toHex = (n) ->
|
||||||
|
h = Math.floor(n).toString(16)
|
||||||
|
h = '0'+h if h.length is 1
|
||||||
|
h
|
55
app/styles/editor/thang/colors_tab.sass
Normal file
55
app/styles/editor/thang/colors_tab.sass
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
$height: 550px
|
||||||
|
|
||||||
|
#editor-thang-colors-tab-view
|
||||||
|
#color-group-settings
|
||||||
|
width: 75%
|
||||||
|
box-sizing: border-box
|
||||||
|
float: right
|
||||||
|
|
||||||
|
#color-groups-treema
|
||||||
|
float: left
|
||||||
|
width: 25%
|
||||||
|
height: $height
|
||||||
|
box-sizing: border-box
|
||||||
|
margin-bottom: 20px
|
||||||
|
|
||||||
|
#shape-buttons
|
||||||
|
border: 1px solid saddlebrown
|
||||||
|
margin: 0 20px 0 10px
|
||||||
|
width: 30%
|
||||||
|
float: left
|
||||||
|
height: $height
|
||||||
|
overflow: scroll
|
||||||
|
background: sandybrown
|
||||||
|
padding: 5px
|
||||||
|
box-sizing: border-box
|
||||||
|
|
||||||
|
button
|
||||||
|
width: 20px
|
||||||
|
height: 25px
|
||||||
|
border: 2px solid white
|
||||||
|
margin: 1px
|
||||||
|
|
||||||
|
&.selected
|
||||||
|
border: 2px solid black
|
||||||
|
|
||||||
|
#controls
|
||||||
|
//clear: both
|
||||||
|
float: left
|
||||||
|
width: 60%
|
||||||
|
text-align: center
|
||||||
|
background-color: aliceblue
|
||||||
|
border: 1px solid blue
|
||||||
|
|
||||||
|
.slider-cell
|
||||||
|
margin: 20px 10px
|
||||||
|
border-color: black
|
||||||
|
.ui-slider
|
||||||
|
border: 1px solid black
|
||||||
|
.ui-slider-handle
|
||||||
|
border: 1px solid black
|
||||||
|
|
||||||
|
canvas
|
||||||
|
width: 60%
|
||||||
|
background: steelblue
|
||||||
|
border: 1px solid darkblue
|
22
app/templates/editor/thang/colors_tab.jade
Normal file
22
app/templates/editor/thang/colors_tab.jade
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
div#color-groups-treema
|
||||||
|
|
||||||
|
div#color-group-settings.hide
|
||||||
|
div#shape-buttons
|
||||||
|
|
||||||
|
canvas#tinting-display(width=400, height=400)
|
||||||
|
|
||||||
|
div#controls
|
||||||
|
div.slider-cell
|
||||||
|
| Hue:
|
||||||
|
span.hue-label
|
||||||
|
.selector#hue-slider
|
||||||
|
|
||||||
|
div.slider-cell
|
||||||
|
| Saturation:
|
||||||
|
span.saturation-label
|
||||||
|
.selector#saturation-slider
|
||||||
|
|
||||||
|
div.slider-cell
|
||||||
|
| Lightness:
|
||||||
|
span.lightness-label
|
||||||
|
.selector#lightness-slider
|
|
@ -16,8 +16,12 @@ block content
|
||||||
a(href="#editor-thang-components-tab-view", data-toggle="tab") Components
|
a(href="#editor-thang-components-tab-view", data-toggle="tab") Components
|
||||||
li
|
li
|
||||||
a(href="#editor-thang-canvases-view", data-toggle="tab") Canvases
|
a(href="#editor-thang-canvases-view", data-toggle="tab") Canvases
|
||||||
|
li
|
||||||
|
a(href="#editor-thang-colors-tab-view", data-toggle="tab")#color-tab Colors
|
||||||
|
|
||||||
div.tab-content
|
div.tab-content
|
||||||
|
div.tab-pane#editor-thang-colors-tab-view
|
||||||
|
|
||||||
div.tab-pane.active#editor-thang-main-tab-view
|
div.tab-pane.active#editor-thang-main-tab-view
|
||||||
|
|
||||||
div.main-area.well
|
div.main-area.well
|
||||||
|
|
158
app/views/editor/thang/colors_tab_view.coffee
Normal file
158
app/views/editor/thang/colors_tab_view.coffee
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
CocoView = require 'views/kinds/CocoView'
|
||||||
|
template = require 'templates/editor/thang/colors_tab'
|
||||||
|
SpriteBuilder = require 'lib/sprites/SpriteBuilder'
|
||||||
|
{hexToHSL} = require 'lib/utils'
|
||||||
|
|
||||||
|
module.exports = class ColorsTabView extends CocoView
|
||||||
|
id: 'editor-thang-colors-tab-view'
|
||||||
|
template: template
|
||||||
|
className: 'tab-pane'
|
||||||
|
|
||||||
|
offset: 0
|
||||||
|
|
||||||
|
constructor: (@thangType, options) ->
|
||||||
|
@thangType.once 'sync', @tryToBuild, @
|
||||||
|
@thangType.schema().once 'sync', @tryToBuild, @
|
||||||
|
@colorConfig = { hue: 0, saturation: 0, lightness: 0 }
|
||||||
|
@spriteBuilder = new SpriteBuilder(@thangType)
|
||||||
|
f = =>
|
||||||
|
@offset++
|
||||||
|
@updateMovieClip()
|
||||||
|
@interval = setInterval f, 1000
|
||||||
|
super options
|
||||||
|
|
||||||
|
afterRender: ->
|
||||||
|
super()
|
||||||
|
@createShapeButtons()
|
||||||
|
@initStage()
|
||||||
|
@initSliders()
|
||||||
|
@tryToBuild()
|
||||||
|
|
||||||
|
# sliders
|
||||||
|
|
||||||
|
initSliders: ->
|
||||||
|
@hueSlider = @initSlider $("#hue-slider", @$el), 0, @updateHue
|
||||||
|
@saturationSlider = @initSlider $("#saturation-slider", @$el), 50, @updateSaturation
|
||||||
|
@lightnessSlider = @initSlider $("#lightness-slider", @$el), 50, @updateLightness
|
||||||
|
|
||||||
|
updateHue: =>
|
||||||
|
@colorConfig.hue = @hueSlider.slider('value') / 100
|
||||||
|
@updateMovieClip()
|
||||||
|
|
||||||
|
updateSaturation: =>
|
||||||
|
@colorConfig.saturation = (@saturationSlider.slider('value') / 50) - 1
|
||||||
|
@updateMovieClip()
|
||||||
|
|
||||||
|
updateLightness: =>
|
||||||
|
@colorConfig.lightness = (@lightnessSlider.slider('value') / 50) - 1
|
||||||
|
@updateMovieClip()
|
||||||
|
|
||||||
|
# movie clip
|
||||||
|
|
||||||
|
initStage: ->
|
||||||
|
canvas = @$el.find('#tinting-display')
|
||||||
|
@stage = new createjs.Stage(canvas[0])
|
||||||
|
createjs.Ticker.setFPS 20
|
||||||
|
createjs.Ticker.addEventListener("tick", @stage)
|
||||||
|
@updateMovieClip()
|
||||||
|
|
||||||
|
updateMovieClip: ->
|
||||||
|
return unless @currentColorGroupTreema
|
||||||
|
actionDict = @thangType.getActions()
|
||||||
|
animations = (a.animation for key, a of actionDict when a.animation)
|
||||||
|
index = @offset % animations.length
|
||||||
|
animation = animations[index]
|
||||||
|
return unless animation
|
||||||
|
@stage.removeChild(@movieClip) if @movieClip
|
||||||
|
options = {colorConfig: {}}
|
||||||
|
options.colorConfig[@currentColorGroupTreema.keyForParent] = @colorConfig
|
||||||
|
@spriteBuilder.setOptions options
|
||||||
|
@spriteBuilder.buildColorMaps()
|
||||||
|
@movieClip = @spriteBuilder.buildMovieClip animation
|
||||||
|
larger = Math.min(400 / @movieClip.nominalBounds.width, 400 / @movieClip.nominalBounds.height)
|
||||||
|
@movieClip.scaleX = larger
|
||||||
|
@movieClip.scaleY = larger
|
||||||
|
@movieClip.regX = @movieClip.nominalBounds.x
|
||||||
|
@movieClip.regY = @movieClip.nominalBounds.y
|
||||||
|
@stage.addChild @movieClip
|
||||||
|
|
||||||
|
createShapeButtons: ->
|
||||||
|
buttons = $('<div></div>').prop('id', 'shape-buttons')
|
||||||
|
shapes = (shape for key, shape of @thangType.get('raw')?.shapes or {})
|
||||||
|
colors = (s.fc for s in shapes when s.fc?)
|
||||||
|
colors = _.uniq(colors)
|
||||||
|
colors.sort (a, b) ->
|
||||||
|
aHSL = hexToHSL(a)
|
||||||
|
bHSL = hexToHSL(b)
|
||||||
|
if aHSL[0] > bHSL[0] then -1 else 1
|
||||||
|
|
||||||
|
for color in colors
|
||||||
|
button = $('<button></button>').addClass('btn')
|
||||||
|
button.css('background', color)
|
||||||
|
button.val color
|
||||||
|
buttons.append(button)
|
||||||
|
buttons.click (e) =>
|
||||||
|
$(e.target).toggleClass('selected')
|
||||||
|
@updateColorGroup()
|
||||||
|
@$el.find('#shape-buttons').replaceWith(buttons)
|
||||||
|
@buttons = buttons
|
||||||
|
|
||||||
|
tryToBuild: ->
|
||||||
|
return unless @thangType.loaded and @thangType.schema().loaded
|
||||||
|
data = @thangType.get('colorGroups')
|
||||||
|
data ?= {}
|
||||||
|
schema = @thangType.schema().attributes.properties?.colorGroups
|
||||||
|
treemaOptions =
|
||||||
|
data: data
|
||||||
|
schema: schema
|
||||||
|
callbacks:
|
||||||
|
change: @onColorGroupsChanged
|
||||||
|
select: @onColorGroupSelected
|
||||||
|
nodeClasses:
|
||||||
|
'thang-color-group': ColorGroupNode
|
||||||
|
@colorGroups = @$el.find('#color-groups-treema').treema treemaOptions
|
||||||
|
@colorGroups.build()
|
||||||
|
@colorGroups.open()
|
||||||
|
keys = Object.keys @colorGroups.childrenTreemas
|
||||||
|
@colorGroups.childrenTreemas[keys[0]]?.$el.click() if keys[0]
|
||||||
|
|
||||||
|
onColorGroupsChanged: =>
|
||||||
|
@thangType.set('colorGroups', @colorGroups.data)
|
||||||
|
|
||||||
|
onColorGroupSelected: (e, selected) =>
|
||||||
|
@$el.find('#color-group-settings').toggleClass('hide', not selected.length)
|
||||||
|
treema = @colorGroups.getLastSelectedTreema()
|
||||||
|
return unless treema
|
||||||
|
@currentColorGroupTreema = treema
|
||||||
|
|
||||||
|
shapes = {}
|
||||||
|
shapes[shape] = true for shape in treema.data
|
||||||
|
|
||||||
|
colors = {}
|
||||||
|
for key, shape of @thangType.get('raw')?.shapes or {}
|
||||||
|
continue unless shape.fc?
|
||||||
|
colors[shape.fc] = true if shapes[key]
|
||||||
|
|
||||||
|
@buttons.find('button').removeClass('selected')
|
||||||
|
@buttons.find('button').each (i, button) ->
|
||||||
|
$(button).addClass('selected') if colors[$(button).val()]
|
||||||
|
|
||||||
|
@updateMovieClip()
|
||||||
|
|
||||||
|
updateColorGroup: ->
|
||||||
|
colors = {}
|
||||||
|
@buttons.find('button').each (i, button) ->
|
||||||
|
return unless $(button).hasClass('selected')
|
||||||
|
window.button = button
|
||||||
|
colors[$(button).val()] = true
|
||||||
|
|
||||||
|
shapes = []
|
||||||
|
for key, shape of @thangType.get('raw')?.shapes or {}
|
||||||
|
continue unless shape.fc?
|
||||||
|
shapes.push(key) if colors[shape.fc]
|
||||||
|
|
||||||
|
@currentColorGroupTreema.set('/', shapes)
|
||||||
|
|
||||||
|
class ColorGroupNode extends TreemaNode.nodeMap.array
|
||||||
|
collection: false
|
||||||
|
canAddChild: -> false
|
|
@ -8,6 +8,8 @@ Camera = require 'lib/surface/Camera'
|
||||||
ThangComponentEditView = require 'views/editor/components/main'
|
ThangComponentEditView = require 'views/editor/components/main'
|
||||||
DocumentFiles = require 'collections/DocumentFiles'
|
DocumentFiles = require 'collections/DocumentFiles'
|
||||||
|
|
||||||
|
ColorsTabView = require './colors_tab_view'
|
||||||
|
|
||||||
CENTER = {x:200, y:300}
|
CENTER = {x:200, y:300}
|
||||||
|
|
||||||
module.exports = class ThangTypeEditView extends View
|
module.exports = class ThangTypeEditView extends View
|
||||||
|
@ -66,6 +68,7 @@ module.exports = class ThangTypeEditView extends View
|
||||||
@buildTreema()
|
@buildTreema()
|
||||||
@initSliders()
|
@initSliders()
|
||||||
@initComponents()
|
@initComponents()
|
||||||
|
@insertSubView(new ColorsTabView(@thangType))
|
||||||
|
|
||||||
initComponents: =>
|
initComponents: =>
|
||||||
options =
|
options =
|
||||||
|
@ -243,13 +246,6 @@ module.exports = class ThangTypeEditView extends View
|
||||||
|
|
||||||
# sliders
|
# sliders
|
||||||
|
|
||||||
initSlider: ($el, startValue, changeCallback) ->
|
|
||||||
slider = $el.slider({ animate: "fast" })
|
|
||||||
slider.slider('value', startValue)
|
|
||||||
slider.on('slide',changeCallback)
|
|
||||||
slider.on('slidechange',changeCallback)
|
|
||||||
slider
|
|
||||||
|
|
||||||
initSliders: ->
|
initSliders: ->
|
||||||
@rotationSlider = @initSlider $("#rotation-slider", @$el), 50, @updateRotation
|
@rotationSlider = @initSlider $("#rotation-slider", @$el), 50, @updateRotation
|
||||||
@scaleSlider = @initSlider $('#scale-slider', @$el), 29, @updateScale
|
@scaleSlider = @initSlider $('#scale-slider', @$el), 29, @updateScale
|
||||||
|
|
|
@ -217,5 +217,14 @@ module.exports = class CocoView extends Backbone.View
|
||||||
ua = navigator.userAgent or navigator.vendor or window.opera
|
ua = navigator.userAgent or navigator.vendor or window.opera
|
||||||
return ua.search("MSIE") != -1
|
return ua.search("MSIE") != -1
|
||||||
|
|
||||||
|
initSlider: ($el, startValue, changeCallback) ->
|
||||||
|
slider = $el.slider({ animate: "fast" })
|
||||||
|
slider.slider('value', startValue)
|
||||||
|
slider.on('slide',changeCallback)
|
||||||
|
slider.on('slidechange',changeCallback)
|
||||||
|
slider
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mobileRELong = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i
|
mobileRELong = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i
|
||||||
mobileREShort = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i
|
mobileREShort = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i
|
||||||
|
|
|
@ -19,6 +19,7 @@ ThangTypeHandler = class ThangTypeHandler extends Handler
|
||||||
'positions',
|
'positions',
|
||||||
'snap',
|
'snap',
|
||||||
'components'
|
'components'
|
||||||
|
'colorGroups'
|
||||||
]
|
]
|
||||||
|
|
||||||
hasAccess: (req) ->
|
hasAccess: (req) ->
|
||||||
|
|
|
@ -122,6 +122,12 @@ _.extend ThangTypeSchema.properties,
|
||||||
title: 'Scale'
|
title: 'Scale'
|
||||||
type: 'number'
|
type: 'number'
|
||||||
positions: PositionsSchema
|
positions: PositionsSchema
|
||||||
|
colorGroups: c.object
|
||||||
|
title: 'Color Groups'
|
||||||
|
additionalProperties:
|
||||||
|
type:'array'
|
||||||
|
format: 'thang-color-group'
|
||||||
|
items: {type:'string'}
|
||||||
snap: c.object { title: "Snap", description: "In the level editor, snap positioning to these intervals.", required: ['x', 'y'] },
|
snap: c.object { title: "Snap", description: "In the level editor, snap positioning to these intervals.", required: ['x', 'y'] },
|
||||||
x:
|
x:
|
||||||
title: "Snap X"
|
title: "Snap X"
|
||||||
|
|
Reference in a new issue