mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-12-01 03:16:56 -05:00
Merge branch 'master' into production
This commit is contained in:
commit
b2e6bcf872
74 changed files with 40328 additions and 40387 deletions
|
@ -119,13 +119,12 @@ module.exports = class Angel extends CocoClass
|
||||||
return if @aborting
|
return if @aborting
|
||||||
# Toggle BOX2D_ENABLED during deserialization so that if we have box2d in the namespace, the Collides Components still don't try to create bodies for deserialized Thangs upon attachment.
|
# Toggle BOX2D_ENABLED during deserialization so that if we have box2d in the namespace, the Collides Components still don't try to create bodies for deserialized Thangs upon attachment.
|
||||||
window.BOX2D_ENABLED = false
|
window.BOX2D_ENABLED = false
|
||||||
World.deserialize serialized, @shared.worldClassMap, @shared.lastSerializedWorldFrames, @finishBeholdingWorld(goalStates), startFrame, endFrame, streamingWorld
|
@streamingWorld = World.deserialize serialized, @shared.worldClassMap, @shared.lastSerializedWorldFrames, @finishBeholdingWorld(goalStates), startFrame, endFrame, streamingWorld
|
||||||
window.BOX2D_ENABLED = true
|
window.BOX2D_ENABLED = true
|
||||||
@shared.lastSerializedWorldFrames = serialized.frames
|
@shared.lastSerializedWorldFrames = serialized.frames
|
||||||
|
|
||||||
finishBeholdingWorld: (goalStates) -> (world) =>
|
finishBeholdingWorld: (goalStates) -> (world) =>
|
||||||
return if @aborting
|
return if @aborting
|
||||||
@streamingWorld = world
|
|
||||||
finished = world.frames.length is world.totalFrames
|
finished = world.frames.length is world.totalFrames
|
||||||
firstChangedFrame = world.findFirstChangedFrame @shared.world
|
firstChangedFrame = world.findFirstChangedFrame @shared.world
|
||||||
eventType = if finished then 'god:new-world-created' else 'god:streaming-world-updated'
|
eventType = if finished then 'god:new-world-created' else 'god:streaming-world-updated'
|
||||||
|
@ -208,6 +207,8 @@ module.exports = class Angel extends CocoClass
|
||||||
@say 'Fired worker.'
|
@say 'Fired worker.'
|
||||||
@initialized = false
|
@initialized = false
|
||||||
@work = null
|
@work = null
|
||||||
|
@streamingWorld = null
|
||||||
|
@deserializationQueue = null
|
||||||
@hireWorker() if rehire
|
@hireWorker() if rehire
|
||||||
|
|
||||||
hireWorker: ->
|
hireWorker: ->
|
||||||
|
|
|
@ -63,7 +63,13 @@ module.exports = class SpriteParser
|
||||||
instructions.push {t: c.t, gn: c.gn}
|
instructions.push {t: c.t, gn: c.gn}
|
||||||
break
|
break
|
||||||
@addContainer {c: instructions, b: container.bounds}, container.name
|
@addContainer {c: instructions, b: container.bounds}, container.name
|
||||||
for movieClip in movieClips
|
for movieClip, index in movieClips
|
||||||
|
if index is 0
|
||||||
|
for bounds in movieClip.frameBounds
|
||||||
|
bounds[0] -= @width / 2
|
||||||
|
bounds[1] -= @height / 2
|
||||||
|
movieClip.bounds[0] -= @width / 2
|
||||||
|
movieClip.bounds[1] -= @height / 2
|
||||||
localGraphics = @getGraphicsFromBlock(movieClip, source)
|
localGraphics = @getGraphicsFromBlock(movieClip, source)
|
||||||
[shapeKeys, localShapes] = @getShapesFromBlock movieClip, source
|
[shapeKeys, localShapes] = @getShapesFromBlock movieClip, source
|
||||||
localContainers = @getContainersFromMovieClip movieClip, source, true
|
localContainers = @getContainersFromMovieClip movieClip, source, true
|
||||||
|
@ -174,7 +180,7 @@ module.exports = class SpriteParser
|
||||||
frameBoundsSource = @subSourceFromRange frameBoundsRange, source
|
frameBoundsSource = @subSourceFromRange frameBoundsRange, source
|
||||||
if frameBoundsSource.search(/\[rect/) is -1 # some other statement; we don't have multiframe bounds
|
if frameBoundsSource.search(/\[rect/) is -1 # some other statement; we don't have multiframe bounds
|
||||||
console.log 'Didn\'t have multiframe bounds for this movie clip.'
|
console.log 'Didn\'t have multiframe bounds for this movie clip.'
|
||||||
frameBounds = [nominalBounds]
|
frameBounds = [_.clone(nominalBounds)]
|
||||||
else
|
else
|
||||||
lastRect = nominalBounds
|
lastRect = nominalBounds
|
||||||
frameBounds = []
|
frameBounds = []
|
||||||
|
@ -192,12 +198,7 @@ module.exports = class SpriteParser
|
||||||
bounds = [0, 0, 1, 1] # Let's try this.
|
bounds = [0, 0, 1, 1] # Let's try this.
|
||||||
frameBounds.push _.clone bounds
|
frameBounds.push _.clone bounds
|
||||||
else
|
else
|
||||||
frameBounds = [nominalBounds]
|
frameBounds = [_.clone(nominalBounds)]
|
||||||
|
|
||||||
# Subtract half of width/height parsed from lib.properties
|
|
||||||
for bounds in frameBounds
|
|
||||||
bounds[0] -= @width / 2
|
|
||||||
bounds[1] -= @height / 2
|
|
||||||
|
|
||||||
functionExpressions.push {name: name, bounds: nominalBounds, frameBounds: frameBounds, expression: node.parent.parent, kind: kind}
|
functionExpressions.push {name: name, bounds: nominalBounds, frameBounds: frameBounds, expression: node.parent.parent, kind: kind}
|
||||||
@walk ast, null, gatherFunctionExpressions
|
@walk ast, null, gatherFunctionExpressions
|
||||||
|
|
|
@ -141,9 +141,9 @@ module.exports = Lank = class Lank extends CocoClass
|
||||||
playAction: (action) ->
|
playAction: (action) ->
|
||||||
return if @isRaster
|
return if @isRaster
|
||||||
@currentAction = action
|
@currentAction = action
|
||||||
return @hide() unless action.animation or action.container or action.relatedActions
|
return @hide() unless action.animation or action.container or action.relatedActions or action.goesTo
|
||||||
@show()
|
@show()
|
||||||
return @updateActionDirection() unless action.animation or action.container
|
return @updateActionDirection() unless action.animation or action.container or action.goesTo
|
||||||
return if @sprite.placeholder
|
return if @sprite.placeholder
|
||||||
m = if action.container then 'gotoAndStop' else 'gotoAndPlay'
|
m = if action.container then 'gotoAndStop' else 'gotoAndPlay'
|
||||||
@sprite[m]?(action.name)
|
@sprite[m]?(action.name)
|
||||||
|
|
|
@ -340,7 +340,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
|
||||||
if action.container
|
if action.container
|
||||||
containersToRender[action.container] = true
|
containersToRender[action.container] = true
|
||||||
else if action.animation
|
else if action.animation
|
||||||
animationContainers = @getContainersForAnimation(thangType, action.animation)
|
animationContainers = @getContainersForAnimation(thangType, action.animation, action)
|
||||||
containersToRender[container.gn] = true for container in animationContainers
|
containersToRender[container.gn] = true for container in animationContainers
|
||||||
|
|
||||||
spriteBuilder = new SpriteBuilder(thangType, {colorConfig: colorConfig})
|
spriteBuilder = new SpriteBuilder(thangType, {colorConfig: colorConfig})
|
||||||
|
@ -355,10 +355,13 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
|
||||||
frame = spriteSheetBuilder.addFrame(container, null, @resolutionFactor * (thangType.get('scale') or 1))
|
frame = spriteSheetBuilder.addFrame(container, null, @resolutionFactor * (thangType.get('scale') or 1))
|
||||||
spriteSheetBuilder.addAnimation(containerKey, [frame], false)
|
spriteSheetBuilder.addAnimation(containerKey, [frame], false)
|
||||||
|
|
||||||
getContainersForAnimation: (thangType, animation) ->
|
getContainersForAnimation: (thangType, animation, action) ->
|
||||||
containers = thangType.get('raw').animations[animation].containers
|
rawAnimation = thangType.get('raw').animations[animation]
|
||||||
|
if not rawAnimation
|
||||||
|
console.error 'thang type', thangType.get('name'), 'is missing animation', animation, 'from action', action
|
||||||
|
containers = rawAnimation.containers
|
||||||
for animation in thangType.get('raw').animations[animation].animations
|
for animation in thangType.get('raw').animations[animation].animations
|
||||||
containers = containers.concat(@getContainersForAnimation(thangType, animation.gn))
|
containers = containers.concat(@getContainersForAnimation(thangType, animation.gn, action))
|
||||||
return containers
|
return containers
|
||||||
|
|
||||||
#- Rendering sprite sheets for singular thang types
|
#- Rendering sprite sheets for singular thang types
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
SpriteBuilder = require 'lib/sprites/SpriteBuilder'
|
SpriteBuilder = require 'lib/sprites/SpriteBuilder'
|
||||||
|
|
||||||
|
# Put this on MovieClips
|
||||||
|
specialGoToAndStop = (frame) ->
|
||||||
|
if frame is @currentFrame and @childrenCopy
|
||||||
|
@addChild(@childrenCopy...)
|
||||||
|
else
|
||||||
|
@gotoAndStop(frame)
|
||||||
|
@childrenCopy = @children.slice(0)
|
||||||
|
|
||||||
module.exports = class SegmentedSprite extends createjs.SpriteContainer
|
module.exports = class SegmentedSprite extends createjs.SpriteContainer
|
||||||
childMovieClips: null
|
childMovieClips: null
|
||||||
|
|
||||||
|
@ -59,9 +67,14 @@ module.exports = class SegmentedSprite extends createjs.SpriteContainer
|
||||||
else
|
else
|
||||||
@currentFrame = 0
|
@currentFrame = 0
|
||||||
|
|
||||||
@baseMovieClip.gotoAndStop(@currentFrame)
|
@baseMovieClip.specialGoToAndStop(@currentFrame)
|
||||||
movieClip.gotoAndStop(@currentFrame) for movieClip in @childMovieClips
|
for movieClip in @childMovieClips
|
||||||
@takeChildrenFromMovieClip()
|
if movieClip.mode is 'single'
|
||||||
|
movieClip.specialGoToAndStop(movieClip.startPosition)
|
||||||
|
else
|
||||||
|
movieClip.specialGoToAndStop(@currentFrame)
|
||||||
|
|
||||||
|
@takeChildrenFromMovieClip(@baseMovieClip, @)
|
||||||
@loop = action.loops isnt false
|
@loop = action.loops isnt false
|
||||||
@goesTo = action.goesTo
|
@goesTo = action.goesTo
|
||||||
@notifyActionNeedsRender(action) if @actionNotSupported
|
@notifyActionNeedsRender(action) if @actionNotSupported
|
||||||
|
@ -93,7 +106,11 @@ module.exports = class SegmentedSprite extends createjs.SpriteContainer
|
||||||
sprite.scaleX = sprite.scaleY = 1 / @resolutionFactor
|
sprite.scaleX = sprite.scaleY = 1 / @resolutionFactor
|
||||||
@children = []
|
@children = []
|
||||||
@addChild(sprite)
|
@addChild(sprite)
|
||||||
|
|
||||||
|
else if action.goesTo
|
||||||
|
@goto(action.goesTo, @paused)
|
||||||
|
return
|
||||||
|
|
||||||
@scaleX *= -1 if action.flipX
|
@scaleX *= -1 if action.flipX
|
||||||
@scaleY *= -1 if action.flipY
|
@scaleY *= -1 if action.flipY
|
||||||
@baseScaleX = @scaleX
|
@baseScaleX = @scaleX
|
||||||
|
@ -101,7 +118,7 @@ module.exports = class SegmentedSprite extends createjs.SpriteContainer
|
||||||
return
|
return
|
||||||
|
|
||||||
notifyActionNeedsRender: (action) ->
|
notifyActionNeedsRender: (action) ->
|
||||||
@sprite?.trigger('action-needs-render', @sprite, action)
|
@lank?.trigger('action-needs-render', @lank, action)
|
||||||
|
|
||||||
buildMovieClip: (animationName, mode, startPosition, loops) ->
|
buildMovieClip: (animationName, mode, startPosition, loops) ->
|
||||||
key = JSON.stringify([@spriteSheetPrefix].concat(arguments))
|
key = JSON.stringify([@spriteSheetPrefix].concat(arguments))
|
||||||
|
@ -115,7 +132,6 @@ module.exports = class SegmentedSprite extends createjs.SpriteContainer
|
||||||
raw = @thangType.get('raw')
|
raw = @thangType.get('raw')
|
||||||
animData = raw.animations[animationName]
|
animData = raw.animations[animationName]
|
||||||
@lastAnimData = animData
|
@lastAnimData = animData
|
||||||
movieClip = new createjs.MovieClip()
|
|
||||||
|
|
||||||
locals = {}
|
locals = {}
|
||||||
_.extend locals, @buildMovieClipContainers(animData.containers)
|
_.extend locals, @buildMovieClipContainers(animData.containers)
|
||||||
|
@ -127,6 +143,7 @@ module.exports = class SegmentedSprite extends createjs.SpriteContainer
|
||||||
|
|
||||||
anim = new createjs.MovieClip()
|
anim = new createjs.MovieClip()
|
||||||
anim.initialize(mode ? createjs.MovieClip.INDEPENDENT, startPosition ? 0, loops ? true)
|
anim.initialize(mode ? createjs.MovieClip.INDEPENDENT, startPosition ? 0, loops ? true)
|
||||||
|
anim.specialGoToAndStop = specialGoToAndStop
|
||||||
|
|
||||||
for tweenData in animData.tweens
|
for tweenData in animData.tweens
|
||||||
stopped = false
|
stopped = false
|
||||||
|
@ -221,7 +238,7 @@ module.exports = class SegmentedSprite extends createjs.SpriteContainer
|
||||||
else if not @loop
|
else if not @loop
|
||||||
@paused = true
|
@paused = true
|
||||||
newFrame = @animLength - 1
|
newFrame = @animLength - 1
|
||||||
@dispatchEvent('animationend')
|
_.defer => @dispatchEvent('animationend')
|
||||||
else
|
else
|
||||||
newFrame = newFrame % @animLength
|
newFrame = newFrame % @animLength
|
||||||
|
|
||||||
|
@ -243,30 +260,23 @@ module.exports = class SegmentedSprite extends createjs.SpriteContainer
|
||||||
@currentFrame = newFrame
|
@currentFrame = newFrame
|
||||||
return if translatedFrame is @baseMovieClip.currentFrame
|
return if translatedFrame is @baseMovieClip.currentFrame
|
||||||
|
|
||||||
@baseMovieClip.gotoAndStop(translatedFrame)
|
@baseMovieClip.specialGoToAndStop(translatedFrame)
|
||||||
movieClip.gotoAndStop(newFrame) for movieClip in @childMovieClips
|
for movieClip in @childMovieClips
|
||||||
|
movieClip.specialGoToAndStop(if movieClip.mode is 'single' then movieClip.startPosition else newFrame)
|
||||||
|
|
||||||
@children = []
|
@children = []
|
||||||
@takeChildrenFromMovieClip()
|
@takeChildrenFromMovieClip(@baseMovieClip, @)
|
||||||
|
|
||||||
takeChildrenFromMovieClip: ->
|
takeChildrenFromMovieClip: (movieClip, recipientContainer) ->
|
||||||
i = 0
|
for child in movieClip.childrenCopy
|
||||||
while i < @baseMovieClip.children.length
|
|
||||||
child = @baseMovieClip.children[i]
|
|
||||||
if child instanceof createjs.MovieClip
|
if child instanceof createjs.MovieClip
|
||||||
newChild = new createjs.SpriteContainer(@spriteSheet)
|
childRecipient = new createjs.SpriteContainer(@spriteSheet)
|
||||||
j = 0
|
@takeChildrenFromMovieClip(child, childRecipient)
|
||||||
while j < child.children.length
|
for prop in ['regX', 'regY', 'rotation', 'scaleX', 'scaleY', 'skewX', 'skewY', 'x', 'y']
|
||||||
grandChild = child.children[j]
|
childRecipient[prop] = child[prop]
|
||||||
if grandChild instanceof createjs.MovieClip
|
recipientContainer.addChild(childRecipient)
|
||||||
console.error('MovieClip Segmentedsprites not currently working at this depth!')
|
|
||||||
continue
|
|
||||||
newChild.addChild(grandChild)
|
|
||||||
for prop in ['regX', 'regY', 'rotation', 'scaleX', 'scaleY', 'skewX', 'skewY', 'x', 'y']
|
|
||||||
newChild[prop] = child[prop]
|
|
||||||
@addChild(newChild)
|
|
||||||
i += 1
|
|
||||||
else
|
else
|
||||||
@addChild(child)
|
recipientContainer.addChild(child)
|
||||||
|
|
||||||
|
|
||||||
# _getBounds: createjs.SpriteContainer.prototype.getBounds
|
# _getBounds: createjs.SpriteContainer.prototype.getBounds
|
||||||
|
|
|
@ -75,4 +75,4 @@ module.exports = class SingularSprite extends createjs.Sprite
|
||||||
return
|
return
|
||||||
|
|
||||||
notifyActionNeedsRender: (action) ->
|
notifyActionNeedsRender: (action) ->
|
||||||
@sprite?.trigger('action-needs-render', @sprite, action)
|
@lank?.trigger('action-needs-render', @lank, action)
|
|
@ -98,6 +98,7 @@ module.exports = Surface = class Surface extends CocoClass
|
||||||
initEasel: ->
|
initEasel: ->
|
||||||
@normalStage = new createjs.Stage(@normalCanvas[0])
|
@normalStage = new createjs.Stage(@normalCanvas[0])
|
||||||
@webGLStage = new createjs.SpriteStage(@webGLCanvas[0])
|
@webGLStage = new createjs.SpriteStage(@webGLCanvas[0])
|
||||||
|
@normalStage.nextStage = @webGLStage
|
||||||
@camera = AudioPlayer.camera = new Camera @webGLCanvas
|
@camera = AudioPlayer.camera = new Camera @webGLCanvas
|
||||||
|
|
||||||
@normalLayers.push @surfaceTextLayer = new Layer name: 'Surface Text', layerPriority: 1, transform: Layer.TRANSFORM_SURFACE_TEXT, camera: @camera
|
@normalLayers.push @surfaceTextLayer = new Layer name: 'Surface Text', layerPriority: 1, transform: Layer.TRANSFORM_SURFACE_TEXT, camera: @camera
|
||||||
|
|
|
@ -364,7 +364,6 @@ module.exports = class World
|
||||||
o.trackedPropertiesPerThangValuesOffsets = [] # Needed to reconstruct ArrayBufferViews on other end, since Firefox has bugs transfering those: https://bugzilla.mozilla.org/show_bug.cgi?id=841904 and https://bugzilla.mozilla.org/show_bug.cgi?id=861925 # Actually, as of January 2014, it should be fixed. So we could try to undo the workaround.
|
o.trackedPropertiesPerThangValuesOffsets = [] # Needed to reconstruct ArrayBufferViews on other end, since Firefox has bugs transfering those: https://bugzilla.mozilla.org/show_bug.cgi?id=841904 and https://bugzilla.mozilla.org/show_bug.cgi?id=861925 # Actually, as of January 2014, it should be fixed. So we could try to undo the workaround.
|
||||||
transferableStorageBytesNeeded = 0
|
transferableStorageBytesNeeded = 0
|
||||||
nFrames = endFrame - startFrame
|
nFrames = endFrame - startFrame
|
||||||
streaming = nFrames < @totalFrames
|
|
||||||
for thang in @thangs
|
for thang in @thangs
|
||||||
# Don't serialize empty trackedProperties for stateless Thangs which haven't changed (like obstacles).
|
# Don't serialize empty trackedProperties for stateless Thangs which haven't changed (like obstacles).
|
||||||
# Check both, since sometimes people mark stateless Thangs but then change them, and those should still be tracked, and the inverse doesn't work on the other end (we'll just think it doesn't exist then).
|
# Check both, since sometimes people mark stateless Thangs but then change them, and those should still be tracked, and the inverse doesn't work on the other end (we'll just think it doesn't exist then).
|
||||||
|
@ -477,6 +476,7 @@ module.exports = class World
|
||||||
w.frames = [] unless streamingWorld
|
w.frames = [] unless streamingWorld
|
||||||
clearTimeout @deserializationTimeout if @deserializationTimeout
|
clearTimeout @deserializationTimeout if @deserializationTimeout
|
||||||
@deserializationTimeout = _.delay @deserializeSomeFrames, 1, o, w, finishedWorldCallback, perf, startFrame, endFrame
|
@deserializationTimeout = _.delay @deserializeSomeFrames, 1, o, w, finishedWorldCallback, perf, startFrame, endFrame
|
||||||
|
w # Return in-progress deserializing world
|
||||||
|
|
||||||
# Spread deserialization out across multiple calls so the interface stays responsive
|
# Spread deserialization out across multiple calls so the interface stays responsive
|
||||||
@deserializeSomeFrames: (o, w, finishedWorldCallback, perf, startFrame, endFrame) =>
|
@deserializeSomeFrames: (o, w, finishedWorldCallback, perf, startFrame, endFrame) =>
|
||||||
|
|
1597
app/locale/ar.coffee
1597
app/locale/ar.coffee
File diff suppressed because it is too large
Load diff
1597
app/locale/bg.coffee
1597
app/locale/bg.coffee
File diff suppressed because it is too large
Load diff
1599
app/locale/ca.coffee
1599
app/locale/ca.coffee
File diff suppressed because it is too large
Load diff
1599
app/locale/cs.coffee
1599
app/locale/cs.coffee
File diff suppressed because it is too large
Load diff
1599
app/locale/da.coffee
1599
app/locale/da.coffee
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1599
app/locale/el.coffee
1599
app/locale/el.coffee
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1600
app/locale/en.coffee
1600
app/locale/en.coffee
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1597
app/locale/fa.coffee
1597
app/locale/fa.coffee
File diff suppressed because it is too large
Load diff
1599
app/locale/fi.coffee
1599
app/locale/fi.coffee
File diff suppressed because it is too large
Load diff
1599
app/locale/fr.coffee
1599
app/locale/fr.coffee
File diff suppressed because it is too large
Load diff
1597
app/locale/he.coffee
1597
app/locale/he.coffee
File diff suppressed because it is too large
Load diff
1599
app/locale/hi.coffee
1599
app/locale/hi.coffee
File diff suppressed because it is too large
Load diff
1599
app/locale/hu.coffee
1599
app/locale/hu.coffee
File diff suppressed because it is too large
Load diff
1599
app/locale/id.coffee
1599
app/locale/id.coffee
File diff suppressed because it is too large
Load diff
1599
app/locale/it.coffee
1599
app/locale/it.coffee
File diff suppressed because it is too large
Load diff
1599
app/locale/ja.coffee
1599
app/locale/ja.coffee
File diff suppressed because it is too large
Load diff
1599
app/locale/ko.coffee
1599
app/locale/ko.coffee
File diff suppressed because it is too large
Load diff
1599
app/locale/lt.coffee
1599
app/locale/lt.coffee
File diff suppressed because it is too large
Load diff
1597
app/locale/ms.coffee
1597
app/locale/ms.coffee
File diff suppressed because it is too large
Load diff
1599
app/locale/nb.coffee
1599
app/locale/nb.coffee
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1599
app/locale/nn.coffee
1599
app/locale/nn.coffee
File diff suppressed because it is too large
Load diff
1599
app/locale/no.coffee
1599
app/locale/no.coffee
File diff suppressed because it is too large
Load diff
1597
app/locale/pl.coffee
1597
app/locale/pl.coffee
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1599
app/locale/ro.coffee
1599
app/locale/ro.coffee
File diff suppressed because it is too large
Load diff
1597
app/locale/ru.coffee
1597
app/locale/ru.coffee
File diff suppressed because it is too large
Load diff
1597
app/locale/sk.coffee
1597
app/locale/sk.coffee
File diff suppressed because it is too large
Load diff
1599
app/locale/sl.coffee
1599
app/locale/sl.coffee
File diff suppressed because it is too large
Load diff
1599
app/locale/sr.coffee
1599
app/locale/sr.coffee
File diff suppressed because it is too large
Load diff
1599
app/locale/sv.coffee
1599
app/locale/sv.coffee
File diff suppressed because it is too large
Load diff
1597
app/locale/th.coffee
1597
app/locale/th.coffee
File diff suppressed because it is too large
Load diff
1599
app/locale/tr.coffee
1599
app/locale/tr.coffee
File diff suppressed because it is too large
Load diff
1599
app/locale/uk.coffee
1599
app/locale/uk.coffee
File diff suppressed because it is too large
Load diff
1599
app/locale/ur.coffee
1599
app/locale/ur.coffee
File diff suppressed because it is too large
Load diff
1597
app/locale/vi.coffee
1597
app/locale/vi.coffee
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -58,7 +58,7 @@
|
||||||
select
|
select
|
||||||
margin-top: 10px
|
margin-top: 10px
|
||||||
|
|
||||||
#marker-button, #end-button
|
#marker-button, #play-button, #stop-button
|
||||||
margin: 10px 10px 10px 0
|
margin: 10px 10px 10px 0
|
||||||
|
|
||||||
.slider-cell
|
.slider-cell
|
||||||
|
|
|
@ -1,4 +1,12 @@
|
||||||
#employers-view
|
#employers-view
|
||||||
|
.deprecation-warning
|
||||||
|
text-align: center
|
||||||
|
margin-bottom: 50px
|
||||||
|
|
||||||
|
.deprecated
|
||||||
|
cursor: default
|
||||||
|
opacity: 0.25
|
||||||
|
|
||||||
.artisanal-claim
|
.artisanal-claim
|
||||||
background: transparent url(/images/pages/employer/artisanal_claim.png) no-repeat center
|
background: transparent url(/images/pages/employer/artisanal_claim.png) no-repeat center
|
||||||
margin-bottom: 5px
|
margin-bottom: 5px
|
||||||
|
|
|
@ -5,7 +5,8 @@ $mapHeight: 1536
|
||||||
$forestMapWidth: 2500
|
$forestMapWidth: 2500
|
||||||
$dungeonMapWidth: 2350
|
$dungeonMapWidth: 2350
|
||||||
$forestMapSeaBackground: #71bad0
|
$forestMapSeaBackground: #71bad0
|
||||||
$dungeonMapCaveBackground: rgb(54, 43, 34)
|
$dungeonMapCaveBackground: rgba(68, 54, 45, 1)
|
||||||
|
$dungeonMapCaveBackgroundTransparent: rgba(68, 54, 45, 0)
|
||||||
$levelDotWidth: 2%
|
$levelDotWidth: 2%
|
||||||
$levelDotHeight: $levelDotWidth * $forestMapWidth / $mapHeight
|
$levelDotHeight: $levelDotWidth * $forestMapWidth / $mapHeight
|
||||||
$levelDotZ: $levelDotHeight * 0.25
|
$levelDotZ: $levelDotHeight * 0.25
|
||||||
|
@ -30,6 +31,7 @@ $gameControlMargin: 30px
|
||||||
#world-map-view
|
#world-map-view
|
||||||
width: 100%
|
width: 100%
|
||||||
height: 100%
|
height: 100%
|
||||||
|
position: absolute
|
||||||
|
|
||||||
&.forest
|
&.forest
|
||||||
background-color: $forestMapSeaBackground
|
background-color: $forestMapSeaBackground
|
||||||
|
@ -37,6 +39,36 @@ $gameControlMargin: 30px
|
||||||
&.dungeon
|
&.dungeon
|
||||||
background-color: $dungeonMapCaveBackground
|
background-color: $dungeonMapCaveBackground
|
||||||
|
|
||||||
|
.gradient
|
||||||
|
position: absolute
|
||||||
|
z-index: 0
|
||||||
|
|
||||||
|
&.horizontal-gradient
|
||||||
|
left: 0
|
||||||
|
right: 0
|
||||||
|
height: 3%
|
||||||
|
|
||||||
|
&.vertical-gradient
|
||||||
|
top: 0
|
||||||
|
bottom: 0
|
||||||
|
width: 3%
|
||||||
|
|
||||||
|
&.top-gradient
|
||||||
|
top: 0
|
||||||
|
background: linear-gradient(to bottom, $dungeonMapCaveBackground 0%, $dungeonMapCaveBackgroundTransparent 100%)
|
||||||
|
|
||||||
|
&.right-gradient
|
||||||
|
right: 0
|
||||||
|
background: linear-gradient(to left, $dungeonMapCaveBackground 0%, $dungeonMapCaveBackgroundTransparent 100%)
|
||||||
|
|
||||||
|
&.bottom-gradient
|
||||||
|
bottom: 0
|
||||||
|
background: linear-gradient(to top, $dungeonMapCaveBackground 0%, $dungeonMapCaveBackgroundTransparent 100%)
|
||||||
|
|
||||||
|
&.left-gradient
|
||||||
|
left: 0
|
||||||
|
background: linear-gradient(to right, $dungeonMapCaveBackground 0%, $dungeonMapCaveBackgroundTransparent 100%)
|
||||||
|
|
||||||
.map
|
.map
|
||||||
position: relative
|
position: relative
|
||||||
|
|
||||||
|
|
|
@ -104,11 +104,11 @@ block content
|
||||||
h3.panel-title
|
h3.panel-title
|
||||||
i.glyphicon.glyphicon-wrench
|
i.glyphicon.glyphicon-wrench
|
||||||
a(href="account/settings#password" data-i18n="general.password") Password
|
a(href="account/settings#password" data-i18n="general.password") Password
|
||||||
.panel.panel-default
|
//.panel.panel-default
|
||||||
.panel-heading
|
// .panel-heading
|
||||||
h3.panel-title
|
// h3.panel-title
|
||||||
i.glyphicon.glyphicon-briefcase
|
// i.glyphicon.glyphicon-briefcase
|
||||||
a(href="account/settings#job-profile" data-i18n="account_settings.job_profile") Job Profile
|
// a(href="account/settings#job-profile" data-i18n="account_settings.job_profile") Job Profile
|
||||||
.col-sm-6
|
.col-sm-6
|
||||||
h2(data-i18n="user.recently_played") Recently Played
|
h2(data-i18n="user.recently_played") Recently Played
|
||||||
hr
|
hr
|
||||||
|
|
|
@ -63,10 +63,7 @@ body
|
||||||
.footer.clearfix
|
.footer.clearfix
|
||||||
.content
|
.content
|
||||||
p.footer-link-text
|
p.footer-link-text
|
||||||
if pathname == "/" || (me.get('permissions', true)).indexOf('employer') != -1
|
a(href='/', tabindex=-1, data-i18n="nav.home") Home
|
||||||
a(href='/employers', tabindex=-1, data-i18n="nav.employers") Employers
|
|
||||||
else
|
|
||||||
a(href='/', tabindex=-1, data-i18n="nav.home") Home
|
|
||||||
a(href='/play/ladder', tabindex=-1, data-i18n="home.multiplayer") Multiplayer
|
a(href='/play/ladder', tabindex=-1, data-i18n="home.multiplayer") Multiplayer
|
||||||
a(href='/community', tabindex=-1, data-i18n="nav.community") Community
|
a(href='/community', tabindex=-1, data-i18n="nav.community") Community
|
||||||
a(href='/contribute', tabindex=-1, data-i18n="nav.contribute") Contribute
|
a(href='/contribute', tabindex=-1, data-i18n="nav.contribute") Contribute
|
||||||
|
|
|
@ -90,7 +90,7 @@ block outer_content
|
||||||
div#thang-type-treema
|
div#thang-type-treema
|
||||||
.clearfix
|
.clearfix
|
||||||
div#display-col.well
|
div#display-col.well
|
||||||
canvas#canvas(width="400", height="400")
|
canvas#canvas(width="400", height="600")
|
||||||
select#animations-select.form-control
|
select#animations-select.form-control
|
||||||
for animation in animations
|
for animation in animations
|
||||||
option #{animation}
|
option #{animation}
|
||||||
|
@ -98,7 +98,10 @@ block outer_content
|
||||||
button.btn.btn-small.btn-primary#marker-button
|
button.btn.btn-small.btn-primary#marker-button
|
||||||
i.icon-map-marker
|
i.icon-map-marker
|
||||||
span.spl Markers
|
span.spl Markers
|
||||||
button.btn.btn-small.btn-primary#end-button
|
button.btn.btn-small.btn-primary#play-button
|
||||||
|
i.icon-play
|
||||||
|
span.spl Play
|
||||||
|
button.btn.btn-small.btn-primary#stop-button
|
||||||
i.icon-stop
|
i.icon-stop
|
||||||
span.spl Stop
|
span.spl Stop
|
||||||
div.slider-cell
|
div.slider-cell
|
||||||
|
|
|
@ -1,183 +1,188 @@
|
||||||
extends /templates/recruitment_base
|
extends /templates/recruitment_base
|
||||||
|
|
||||||
block content
|
block content
|
||||||
.artisanal-claim
|
.deprecation-warning
|
||||||
if me.get('anonymous')
|
h1(data-i18n="employers.deprecation_warning_title") Sorry, CodeCombat is not recruiting right now.
|
||||||
a#login-link(data-i18n="login.log_in") Log In
|
p(data-i18n="employers.deprecation_warning") We are focusing on beginner levels instead of finding expert developers for the time being.
|
||||||
br
|
|
||||||
if !isEmployer && !me.isAdmin()
|
.deprecated
|
||||||
#tagline
|
.artisanal-claim
|
||||||
h1(data-i18n="employers.hire_developers_not_credentials") Hire developers, not credentials.
|
if me.get('anonymous')
|
||||||
button.btn.get-started-button.employer-button(data-i18n="employers.get_started") Get Started
|
a#login-link(data-i18n="login.log_in") Log In
|
||||||
else
|
|
||||||
if !me.get('anonymous')
|
|
||||||
a#logout-link(data-i18n="login.log_out") Log Out
|
|
||||||
br
|
br
|
||||||
.row
|
if !isEmployer && !me.isAdmin()
|
||||||
- var fullProfiles = isEmployer || me.isAdmin();
|
#tagline
|
||||||
|
h1(data-i18n="employers.hire_developers_not_credentials") Hire developers, not credentials.
|
||||||
if fullProfiles
|
|
||||||
#filter-column.col-md-3
|
|
||||||
#filter
|
|
||||||
.panel-group#filter_panel
|
|
||||||
a#filter-link(data-toggle="collapse" data-target="#collapseOne")
|
|
||||||
.panel.panel-default
|
|
||||||
.panel-heading
|
|
||||||
h4.panel-title
|
|
||||||
span.glyphicon.glyphicon-folder-open#folder-icon
|
|
||||||
| Filter
|
|
||||||
.panel-collapse.collapse.in#collapseOne
|
|
||||||
.panel-body
|
|
||||||
p
|
|
||||||
strong(data-i18n="employers.already_screened") We've already technically screened all our candidates
|
|
||||||
span(data-i18n="employers.filter_further") , but you can also filter further:
|
|
||||||
form#filters
|
|
||||||
.filter_section#visa_filter
|
|
||||||
h4(data-i18n="employers.filter_visa") Visa
|
|
||||||
label
|
|
||||||
input(type="checkbox" name="visa" value="Authorized to work in the US")
|
|
||||||
span(data-i18n="employers.filter_visa_yes") US Authorized
|
|
||||||
| (#{candidatesInFilter("visa","Authorized to work in the US")})
|
|
||||||
label
|
|
||||||
input(type="checkbox" name="visa" value="Need visa sponsorship")
|
|
||||||
span(data-i18n="employers.filter_visa_no") Not Authorized
|
|
||||||
| (#{candidatesInFilter("visa","Need visa sponsorship")})
|
|
||||||
.filter_section#school_filter
|
|
||||||
h4(data-i18n="account_profile.education") Education
|
|
||||||
label
|
|
||||||
input(type="checkbox" name="schoolFilter" value="Top School")
|
|
||||||
span(data-i18n="employers.filter_education_top") Top School
|
|
||||||
| (#{candidatesInFilter("schoolFilter","Top School")})
|
|
||||||
label
|
|
||||||
input(type="checkbox" name="schoolFilter" value="Other")
|
|
||||||
span(data-i18n="employers.filter_education_other") Other
|
|
||||||
| (#{candidatesInFilter("schoolFilter","Other")})
|
|
||||||
.filter_section#role_filter
|
|
||||||
h4(data-i18n="employers.candidate_role") Role
|
|
||||||
label
|
|
||||||
input(type="checkbox" name="roleFilter" value="Web Developer")
|
|
||||||
span(data-i18n="employers.filter_role_web_developer") Web Developer
|
|
||||||
| (#{candidatesInFilter("roleFilter","Web Developer")})
|
|
||||||
label
|
|
||||||
input(type="checkbox" name="roleFilter" value="Software Developer")
|
|
||||||
span(data-i18n="employers.filter_role_software_developer") Software Developer
|
|
||||||
| (#{candidatesInFilter("roleFilter","Software Developer")})
|
|
||||||
label
|
|
||||||
input(type="checkbox" name="roleFilter" value="Mobile Developer")
|
|
||||||
span(data-i18n="employers.filter_role_mobile_developer") Mobile Developer
|
|
||||||
| (#{candidatesInFilter("roleFilter","Mobile Developer")})
|
|
||||||
.filter_section#seniority_filter
|
|
||||||
h4(data-i18n="employers.filter_experience") Experience
|
|
||||||
label
|
|
||||||
input(type="checkbox" name="seniorityFilter" value="Senior")
|
|
||||||
span(data-i18n="employers.filter_experience_senior") Senior
|
|
||||||
| (#{candidatesInFilter("seniorityFilter","Senior")})
|
|
||||||
label
|
|
||||||
input(type="checkbox" name="seniorityFilter" value="Junior")
|
|
||||||
span(data-i18n="employers.filter_experience_junior") Junior
|
|
||||||
| (#{candidatesInFilter("seniorityFilter","Junior")})
|
|
||||||
label
|
|
||||||
input(type="checkbox" name="seniorityFilter" value="Recent Grad")
|
|
||||||
span(data-i18n="employers.filter_experience_recent_grad") Recent Grad
|
|
||||||
| (#{candidatesInFilter("seniorityFilter","Recent Grad")})
|
|
||||||
label
|
|
||||||
input(type="checkbox" name="seniorityFilter" value="College Student")
|
|
||||||
span(data-i18n="employers.filter_experience_student") College Student
|
|
||||||
| (#{candidatesInFilter("seniorityFilter","College Student")})
|
|
||||||
|
|
||||||
//input#select_all_checkbox(type="checkbox" name="select_all" checked)
|
|
||||||
//| Select all
|
|
||||||
p#results
|
|
||||||
| #{numberOfCandidates}
|
|
||||||
span(data-i18n="employers.results") results
|
|
||||||
h4#filter-alerts-heading Filter Email Alerts
|
|
||||||
p Get an email whenever a candidate meeting certain criteria enters the system.
|
|
||||||
table#saved-filter-table
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th Filters
|
|
||||||
th Remove
|
|
||||||
tbody
|
|
||||||
button.btn#create-alert-button Create Alert with Current Filters
|
|
||||||
|
|
||||||
#candidates-column(class=fullProfiles ? "full-profiles col-md-9" : "teaser-profiles col-md-12")
|
|
||||||
if candidates.length
|
|
||||||
#candidate-table
|
|
||||||
table
|
|
||||||
tbody
|
|
||||||
for candidate, index in featuredCandidates
|
|
||||||
- var profile = candidate.get('jobProfile');
|
|
||||||
- var authorized = candidate.id; // If we have the id, then we are authorized.
|
|
||||||
- var profileAge = (new Date() - new Date(profile.updated)) / 86400 / 1000;
|
|
||||||
- var expired = profileAge > 2 * 30.4;
|
|
||||||
- var curated = profile.curated;
|
|
||||||
- var photoSize = fullProfiles ? 75 : 50;
|
|
||||||
|
|
||||||
tr.candidate-row(data-candidate-id=candidate.id, id=candidate.id, class=expired ? "expired" : "")
|
|
||||||
td(rowspan=3)
|
|
||||||
- var photoURL = candidate.getPhotoURL(photoSize, false, true);
|
|
||||||
div(class="candidate-picture " + (/^\/file/.test(photoURL) ? "" : "anonymous"), style='background-image: url(' + encodeURI(photoURL) + ')')
|
|
||||||
if fullProfiles
|
|
||||||
td.candidate-name-cell
|
|
||||||
strong= profile.name
|
|
||||||
| -
|
|
||||||
span= profile.jobTitle
|
|
||||||
tr.description_row(data-candidate-id=candidate.id)
|
|
||||||
if curated && curated.shortDescription
|
|
||||||
td.candidate-description
|
|
||||||
div #{curated.shortDescription}
|
|
||||||
else
|
|
||||||
td.candidate-description
|
|
||||||
div #{profile.shortDescription}
|
|
||||||
tr.border_row(data-candidate-id=candidate.id)
|
|
||||||
if curated
|
|
||||||
- var workHistory = curated.workHistory.join(",");
|
|
||||||
if !fullProfiles
|
|
||||||
td.tag_column
|
|
||||||
img(src="/images/pages/employer/tag.png")
|
|
||||||
| #{profile.jobTitle}
|
|
||||||
td.location_column
|
|
||||||
img(src="/images/pages/employer/location.png")
|
|
||||||
| #{curated.location}
|
|
||||||
td.education_column
|
|
||||||
img(src="/images/pages/employer/education.png")
|
|
||||||
| #{curated.education}
|
|
||||||
td.work_column
|
|
||||||
if workHistory
|
|
||||||
img(src="/images/pages/employer/briefcase.png")
|
|
||||||
| #{workHistory}
|
|
||||||
|
|
||||||
if !fullProfiles
|
|
||||||
div#info_wrapper
|
|
||||||
span.hiring-call-to-action
|
|
||||||
h2#start-hiring(data-i18n="employers.start_hiring") Start hiring.
|
|
||||||
button.btn.get-started-button.employer-button(data-i18n="employers.get_started") Get Started
|
button.btn.get-started-button.employer-button(data-i18n="employers.get_started") Get Started
|
||||||
|
else
|
||||||
h2#hiring-reasons.hiring-call-to-action(data-i18n="employers.reasons") Three reasons you should hire through us:
|
if !me.get('anonymous')
|
||||||
.reasons#top_row
|
a#logout-link(data-i18n="login.log_out") Log Out
|
||||||
.reason
|
br
|
||||||
img.employer_icon(src="/images/pages/employer/employer_icon2.png")
|
.row
|
||||||
h3(data-i18n="employers.everyone_looking") Everyone here is looking for their next opportunity.
|
- var fullProfiles = isEmployer || me.isAdmin();
|
||||||
p(data-i18n="employers.everyone_looking_blurb") Forget about 20% LinkedIn InMail response rates. Everyone that we list on this site wants to find their next position and will respond to your request for an introduction.
|
|
||||||
.reason
|
if fullProfiles
|
||||||
img.employer_icon(src="/images/pages/employer/employer_icon6.png")
|
#filter-column.col-md-3
|
||||||
h3(data-i18n="employers.weeding") Sit back; we've done the weeding for you.
|
#filter
|
||||||
p(data-i18n="employers.weeding_blurb") Every player that we list has been screened for technical ability. We also perform phone screens for select candidates and make notes on their profiles to save you time.
|
.panel-group#filter_panel
|
||||||
.reason
|
a#filter-link(data-toggle="collapse" data-target="#collapseOne")
|
||||||
img(class="employer_icon" src="/images/pages/employer/employer_icon3.png")
|
.panel.panel-default
|
||||||
h3(data-i18n="employers.pass_screen") They will pass your technical screen.
|
.panel-heading
|
||||||
p(data-i18n="employers.pass_screen_blurb") Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News.
|
h4.panel-title
|
||||||
span.hiring-call-to-action
|
span.glyphicon.glyphicon-folder-open#folder-icon
|
||||||
h2(data-i18n="employers.make_hiring_easier") Make my hiring easier, please.
|
| Filter
|
||||||
button.btn.get-started-button.employer-button(data-i18n="employers.get_started") Get Started
|
.panel-collapse.collapse.in#collapseOne
|
||||||
.reasons#bottom_row
|
.panel-body
|
||||||
.reason_long
|
p
|
||||||
img.employer_icon(src="/images/pages/employer/employer_icon1.png")
|
strong(data-i18n="employers.already_screened") We've already technically screened all our candidates
|
||||||
.reason_text
|
span(data-i18n="employers.filter_further") , but you can also filter further:
|
||||||
h3(data-i18n="employers.what") What is CodeCombat?
|
form#filters
|
||||||
p(data-i18n="employers.what_blurb") CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. We support JavaScript, Python, Lua, Clojure, CoffeeScript, and Io.
|
.filter_section#visa_filter
|
||||||
.reason_long
|
h4(data-i18n="employers.filter_visa") Visa
|
||||||
img.employer_icon(src="/images/pages/employer/employer_icon5.png")
|
label
|
||||||
.reason_text
|
input(type="checkbox" name="visa" value="Authorized to work in the US")
|
||||||
h3(data-i18n="employers.cost") How much do we charge?
|
span(data-i18n="employers.filter_visa_yes") US Authorized
|
||||||
p(data-i18n="employers.cost_blurb") We charge 15% of first year's salary and offer a 100% money back guarantee for 90 days. We don't charge for candidates who are already actively being interviewed at your company.
|
| (#{candidatesInFilter("visa","Authorized to work in the US")})
|
||||||
|
label
|
||||||
|
input(type="checkbox" name="visa" value="Need visa sponsorship")
|
||||||
|
span(data-i18n="employers.filter_visa_no") Not Authorized
|
||||||
|
| (#{candidatesInFilter("visa","Need visa sponsorship")})
|
||||||
|
.filter_section#school_filter
|
||||||
|
h4(data-i18n="account_profile.education") Education
|
||||||
|
label
|
||||||
|
input(type="checkbox" name="schoolFilter" value="Top School")
|
||||||
|
span(data-i18n="employers.filter_education_top") Top School
|
||||||
|
| (#{candidatesInFilter("schoolFilter","Top School")})
|
||||||
|
label
|
||||||
|
input(type="checkbox" name="schoolFilter" value="Other")
|
||||||
|
span(data-i18n="employers.filter_education_other") Other
|
||||||
|
| (#{candidatesInFilter("schoolFilter","Other")})
|
||||||
|
.filter_section#role_filter
|
||||||
|
h4(data-i18n="employers.candidate_role") Role
|
||||||
|
label
|
||||||
|
input(type="checkbox" name="roleFilter" value="Web Developer")
|
||||||
|
span(data-i18n="employers.filter_role_web_developer") Web Developer
|
||||||
|
| (#{candidatesInFilter("roleFilter","Web Developer")})
|
||||||
|
label
|
||||||
|
input(type="checkbox" name="roleFilter" value="Software Developer")
|
||||||
|
span(data-i18n="employers.filter_role_software_developer") Software Developer
|
||||||
|
| (#{candidatesInFilter("roleFilter","Software Developer")})
|
||||||
|
label
|
||||||
|
input(type="checkbox" name="roleFilter" value="Mobile Developer")
|
||||||
|
span(data-i18n="employers.filter_role_mobile_developer") Mobile Developer
|
||||||
|
| (#{candidatesInFilter("roleFilter","Mobile Developer")})
|
||||||
|
.filter_section#seniority_filter
|
||||||
|
h4(data-i18n="employers.filter_experience") Experience
|
||||||
|
label
|
||||||
|
input(type="checkbox" name="seniorityFilter" value="Senior")
|
||||||
|
span(data-i18n="employers.filter_experience_senior") Senior
|
||||||
|
| (#{candidatesInFilter("seniorityFilter","Senior")})
|
||||||
|
label
|
||||||
|
input(type="checkbox" name="seniorityFilter" value="Junior")
|
||||||
|
span(data-i18n="employers.filter_experience_junior") Junior
|
||||||
|
| (#{candidatesInFilter("seniorityFilter","Junior")})
|
||||||
|
label
|
||||||
|
input(type="checkbox" name="seniorityFilter" value="Recent Grad")
|
||||||
|
span(data-i18n="employers.filter_experience_recent_grad") Recent Grad
|
||||||
|
| (#{candidatesInFilter("seniorityFilter","Recent Grad")})
|
||||||
|
label
|
||||||
|
input(type="checkbox" name="seniorityFilter" value="College Student")
|
||||||
|
span(data-i18n="employers.filter_experience_student") College Student
|
||||||
|
| (#{candidatesInFilter("seniorityFilter","College Student")})
|
||||||
|
|
||||||
|
//input#select_all_checkbox(type="checkbox" name="select_all" checked)
|
||||||
|
//| Select all
|
||||||
|
p#results
|
||||||
|
| #{numberOfCandidates}
|
||||||
|
span(data-i18n="employers.results") results
|
||||||
|
h4#filter-alerts-heading Filter Email Alerts
|
||||||
|
p Get an email whenever a candidate meeting certain criteria enters the system.
|
||||||
|
table#saved-filter-table
|
||||||
|
thead
|
||||||
|
tr
|
||||||
|
th Filters
|
||||||
|
th Remove
|
||||||
|
tbody
|
||||||
|
button.btn#create-alert-button Create Alert with Current Filters
|
||||||
|
|
||||||
|
#candidates-column(class=fullProfiles ? "full-profiles col-md-9" : "teaser-profiles col-md-12")
|
||||||
|
if candidates.length
|
||||||
|
#candidate-table
|
||||||
|
table
|
||||||
|
tbody
|
||||||
|
for candidate, index in featuredCandidates
|
||||||
|
- var profile = candidate.get('jobProfile');
|
||||||
|
- var authorized = candidate.id; // If we have the id, then we are authorized.
|
||||||
|
- var profileAge = (new Date() - new Date(profile.updated)) / 86400 / 1000;
|
||||||
|
- var expired = profileAge > 2 * 30.4;
|
||||||
|
- var curated = profile.curated;
|
||||||
|
- var photoSize = fullProfiles ? 75 : 50;
|
||||||
|
|
||||||
|
tr.candidate-row(data-candidate-id=candidate.id, id=candidate.id, class=expired ? "expired" : "")
|
||||||
|
td(rowspan=3)
|
||||||
|
- var photoURL = candidate.getPhotoURL(photoSize, false, true);
|
||||||
|
div(class="candidate-picture " + (/^\/file/.test(photoURL) ? "" : "anonymous"), style='background-image: url(' + encodeURI(photoURL) + ')')
|
||||||
|
if fullProfiles
|
||||||
|
td.candidate-name-cell
|
||||||
|
strong= profile.name
|
||||||
|
| -
|
||||||
|
span= profile.jobTitle
|
||||||
|
tr.description_row(data-candidate-id=candidate.id)
|
||||||
|
if curated && curated.shortDescription
|
||||||
|
td.candidate-description
|
||||||
|
div #{curated.shortDescription}
|
||||||
|
else
|
||||||
|
td.candidate-description
|
||||||
|
div #{profile.shortDescription}
|
||||||
|
tr.border_row(data-candidate-id=candidate.id)
|
||||||
|
if curated
|
||||||
|
- var workHistory = curated.workHistory.join(",");
|
||||||
|
if !fullProfiles
|
||||||
|
td.tag_column
|
||||||
|
img(src="/images/pages/employer/tag.png")
|
||||||
|
| #{profile.jobTitle}
|
||||||
|
td.location_column
|
||||||
|
img(src="/images/pages/employer/location.png")
|
||||||
|
| #{curated.location}
|
||||||
|
td.education_column
|
||||||
|
img(src="/images/pages/employer/education.png")
|
||||||
|
| #{curated.education}
|
||||||
|
td.work_column
|
||||||
|
if workHistory
|
||||||
|
img(src="/images/pages/employer/briefcase.png")
|
||||||
|
| #{workHistory}
|
||||||
|
|
||||||
|
if !fullProfiles
|
||||||
|
div#info_wrapper
|
||||||
|
span.hiring-call-to-action
|
||||||
|
h2#start-hiring(data-i18n="employers.start_hiring") Start hiring.
|
||||||
|
button.btn.get-started-button.employer-button(data-i18n="employers.get_started") Get Started
|
||||||
|
|
||||||
|
h2#hiring-reasons.hiring-call-to-action(data-i18n="employers.reasons") Three reasons you should hire through us:
|
||||||
|
.reasons#top_row
|
||||||
|
.reason
|
||||||
|
img.employer_icon(src="/images/pages/employer/employer_icon2.png")
|
||||||
|
h3(data-i18n="employers.everyone_looking") Everyone here is looking for their next opportunity.
|
||||||
|
p(data-i18n="employers.everyone_looking_blurb") Forget about 20% LinkedIn InMail response rates. Everyone that we list on this site wants to find their next position and will respond to your request for an introduction.
|
||||||
|
.reason
|
||||||
|
img.employer_icon(src="/images/pages/employer/employer_icon6.png")
|
||||||
|
h3(data-i18n="employers.weeding") Sit back; we've done the weeding for you.
|
||||||
|
p(data-i18n="employers.weeding_blurb") Every player that we list has been screened for technical ability. We also perform phone screens for select candidates and make notes on their profiles to save you time.
|
||||||
|
.reason
|
||||||
|
img(class="employer_icon" src="/images/pages/employer/employer_icon3.png")
|
||||||
|
h3(data-i18n="employers.pass_screen") They will pass your technical screen.
|
||||||
|
p(data-i18n="employers.pass_screen_blurb") Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News.
|
||||||
|
span.hiring-call-to-action
|
||||||
|
h2(data-i18n="employers.make_hiring_easier") Make my hiring easier, please.
|
||||||
|
button.btn.get-started-button.employer-button(data-i18n="employers.get_started") Get Started
|
||||||
|
.reasons#bottom_row
|
||||||
|
.reason_long
|
||||||
|
img.employer_icon(src="/images/pages/employer/employer_icon1.png")
|
||||||
|
.reason_text
|
||||||
|
h3(data-i18n="employers.what") What is CodeCombat?
|
||||||
|
p(data-i18n="employers.what_blurb") CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. We support JavaScript, Python, Lua, Clojure, CoffeeScript, and Io.
|
||||||
|
.reason_long
|
||||||
|
img.employer_icon(src="/images/pages/employer/employer_icon5.png")
|
||||||
|
.reason_text
|
||||||
|
h3(data-i18n="employers.cost") How much do we charge?
|
||||||
|
p(data-i18n="employers.cost_blurb") We charge 15% of first year's salary and offer a 100% money back guarantee for 90 days. We don't charge for candidates who are already actively being interviewed at your company.
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
extends /templates/modal/modal_base
|
extends /templates/modal/modal_base
|
||||||
|
|
||||||
block modal-header-content
|
block modal-header-content
|
||||||
h1#choose-hero-header(data-i18n="choose_hero.choose_hero") Choose Your Hero
|
h1#choose-hero-header.choose-hero-active.secret(data-i18n="choose_hero.choose_hero") Choose Your Hero
|
||||||
h1#choose-inventory-header.secret(data-i18n="inventory.choose_inventory") Equip Items
|
h1#choose-inventory-header.choose-inventory-active.secret(data-i18n="inventory.choose_inventory") Equip Items
|
||||||
|
|
||||||
block modal-body-content
|
block modal-body-content
|
||||||
#choose-hero-view
|
#choose-hero-view
|
||||||
|
@ -10,7 +10,7 @@ block modal-body-content
|
||||||
#inventory-view
|
#inventory-view
|
||||||
|
|
||||||
block modal-footer-content
|
block modal-footer-content
|
||||||
button#choose-inventory-button.btn.btn-lg.btn-success(data-i18n="play.next") Next
|
button#choose-inventory-button.btn.btn-lg.btn-success.choose-hero-active.secret(data-i18n="play.next") Next
|
||||||
button#choose-hero-button.btn.btn-lg.btn-primary.secret.pull-left(data-i18n="play.previous") Previous
|
button#choose-hero-button.btn.btn-lg.btn-primary.choose-inventory-active.secret.pull-left(data-i18n="play.change_hero") Change Hero
|
||||||
button#play-level-button.btn.btn-lg.btn-success.secret(data-i18n="common.play") Play
|
button#play-level-button.btn.btn-lg.btn-success.choose-inventory-active.secret(data-i18n="common.play") Play
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
.map
|
.map
|
||||||
|
.gradient.horizontal-gradient.top-gradient
|
||||||
|
.gradient.vertical-gradient.right-gradient
|
||||||
|
.gradient.horizontal-gradient.bottom-gradient
|
||||||
|
.gradient.vertical-gradient.left-gradient
|
||||||
img.map-background(src="/images/pages/play/map_" + mapType + ".jpg", alt="")
|
img.map-background(src="/images/pages/play/map_" + mapType + ".jpg", alt="")
|
||||||
|
|
||||||
- var seenNext = false;
|
- var seenNext = false;
|
||||||
|
@ -17,7 +21,7 @@
|
||||||
each i in Array(level.difficulty)
|
each i in Array(level.difficulty)
|
||||||
i.icon-star
|
i.icon-star
|
||||||
- var playCount = levelPlayCountMap[level.id]
|
- var playCount = levelPlayCountMap[level.id]
|
||||||
if playCount && playCount > 20
|
if playCount && playCount.sessions > 20
|
||||||
div
|
div
|
||||||
span.spr #{playCount.sessions}
|
span.spr #{playCount.sessions}
|
||||||
span(data-i18n="play.players") players
|
span(data-i18n="play.players") players
|
||||||
|
|
|
@ -336,7 +336,7 @@ module.exports = class ThangsTabView extends CocoView
|
||||||
return if e? and $(e.target).closest('#thang-search').length # Ignore if you're trying to search thangs
|
return if e? and $(e.target).closest('#thang-search').length # Ignore if you're trying to search thangs
|
||||||
return unless (e? and $(e.target).closest('#thangs-tab-view').length) or key.isPressed('esc') or forceDeselect
|
return unless (e? and $(e.target).closest('#thangs-tab-view').length) or key.isPressed('esc') or forceDeselect
|
||||||
if e then target = $(e.target) else target = @$el.find('.add-thangs-palette') # pretend to click on background if no event
|
if e then target = $(e.target) else target = @$el.find('.add-thangs-palette') # pretend to click on background if no event
|
||||||
return true if target.attr('id') is 'surface'
|
return true if target.attr('id') is 'webgl-surface'
|
||||||
target = target.closest('.add-thang-palette-icon')
|
target = target.closest('.add-thang-palette-icon')
|
||||||
wasSelected = target.hasClass 'selected'
|
wasSelected = target.hasClass 'selected'
|
||||||
@$el.find('.add-thangs-palette .add-thang-palette-icon.selected').removeClass('selected')
|
@$el.find('.add-thangs-palette .add-thang-palette-icon.selected').removeClass('selected')
|
||||||
|
@ -400,6 +400,7 @@ module.exports = class ThangsTabView extends CocoView
|
||||||
pos.y = Math.round((pos.y - (thang.height ? 1) / 2) / snap.y) * snap.y + (thang.height ? 1) / 2
|
pos.y = Math.round((pos.y - (thang.height ? 1) / 2) / snap.y) * snap.y + (thang.height ? 1) / 2
|
||||||
pos.z = thang.depth / 2
|
pos.z = thang.depth / 2
|
||||||
thang.pos = pos
|
thang.pos = pos
|
||||||
|
thang.stateChanged = true
|
||||||
@surface.lankBoss.update true # Make sure Obstacle layer resets cache
|
@surface.lankBoss.update true # Make sure Obstacle layer resets cache
|
||||||
|
|
||||||
onSurfaceMouseMoved: (e) ->
|
onSurfaceMouseMoved: (e) ->
|
||||||
|
|
|
@ -28,6 +28,7 @@ module.exports = class ThangTypeEditView extends RootView
|
||||||
health: 10.0
|
health: 10.0
|
||||||
maxHealth: 10.0
|
maxHealth: 10.0
|
||||||
hudProperties: ['health']
|
hudProperties: ['health']
|
||||||
|
acts: true
|
||||||
|
|
||||||
events:
|
events:
|
||||||
'click #clear-button': 'clearRawData'
|
'click #clear-button': 'clearRawData'
|
||||||
|
@ -35,7 +36,8 @@ module.exports = class ThangTypeEditView extends RootView
|
||||||
'change #real-upload-button': 'animationFileChosen'
|
'change #real-upload-button': 'animationFileChosen'
|
||||||
'change #animations-select': 'showAnimation'
|
'change #animations-select': 'showAnimation'
|
||||||
'click #marker-button': 'toggleDots'
|
'click #marker-button': 'toggleDots'
|
||||||
'click #end-button': 'endAnimation'
|
'click #stop-button': 'stopAnimation'
|
||||||
|
'click #play-button': 'playAnimation'
|
||||||
'click #history-button': 'showVersionHistory'
|
'click #history-button': 'showVersionHistory'
|
||||||
'click #fork-start-button': 'startForking'
|
'click #fork-start-button': 'startForking'
|
||||||
'click #save-button': 'openSaveModal'
|
'click #save-button': 'openSaveModal'
|
||||||
|
@ -74,13 +76,8 @@ module.exports = class ThangTypeEditView extends RootView
|
||||||
context.fileSizeString = @fileSizeString
|
context.fileSizeString = @fileSizeString
|
||||||
context
|
context
|
||||||
|
|
||||||
getAnimationNames: ->
|
getAnimationNames: -> _.keys(@thangType.get('actions') or {})
|
||||||
raw = _.keys((@thangType.get('raw') or {}).animations)
|
|
||||||
return [] unless raw
|
|
||||||
raw = ("raw:#{name}" for name in raw)
|
|
||||||
main = _.keys(@thangType.get('actions') or {})
|
|
||||||
main.concat(raw)
|
|
||||||
|
|
||||||
afterRender: ->
|
afterRender: ->
|
||||||
super()
|
super()
|
||||||
return unless @supermodel.finished()
|
return unless @supermodel.finished()
|
||||||
|
@ -113,17 +110,20 @@ module.exports = class ThangTypeEditView extends RootView
|
||||||
makeDot: (color) ->
|
makeDot: (color) ->
|
||||||
circle = new createjs.Shape()
|
circle = new createjs.Shape()
|
||||||
circle.graphics.beginFill(color).beginStroke('black').drawCircle(0, 0, 5)
|
circle.graphics.beginFill(color).beginStroke('black').drawCircle(0, 0, 5)
|
||||||
circle.x = CENTER.x
|
circle.scaleY = 0.2
|
||||||
circle.y = CENTER.y
|
circle.scaleX = 0.5
|
||||||
circle.scaleY = 0.5
|
|
||||||
circle
|
circle
|
||||||
|
|
||||||
initStage: ->
|
initStage: ->
|
||||||
canvas = @$el.find('#canvas')
|
canvas = @$el.find('#canvas')
|
||||||
@stage = new createjs.Stage(canvas[0])
|
@stage = new createjs.Stage(canvas[0])
|
||||||
@layerAdapter = new LayerAdapter({name:'Default', webGL: true})
|
@layerAdapter = new LayerAdapter({name:'Default', webGL: true})
|
||||||
|
@topLayer = new createjs.Container()
|
||||||
|
|
||||||
|
@layerAdapter.container.x = @topLayer.x = CENTER.x
|
||||||
|
@layerAdapter.container.y = @topLayer.y = CENTER.y
|
||||||
|
@stage.addChild(@layerAdapter.container, @topLayer)
|
||||||
@listenTo @layerAdapter, 'new-spritesheet', @onNewSpriteSheet
|
@listenTo @layerAdapter, 'new-spritesheet', @onNewSpriteSheet
|
||||||
@stage.addChild(@layerAdapter.container)
|
|
||||||
@camera?.destroy()
|
@camera?.destroy()
|
||||||
@camera = new Camera canvas
|
@camera = new Camera canvas
|
||||||
|
|
||||||
|
@ -131,35 +131,39 @@ module.exports = class ThangTypeEditView extends RootView
|
||||||
@mouthDot = @makeDot('yellow')
|
@mouthDot = @makeDot('yellow')
|
||||||
@aboveHeadDot = @makeDot('green')
|
@aboveHeadDot = @makeDot('green')
|
||||||
@groundDot = @makeDot('red')
|
@groundDot = @makeDot('red')
|
||||||
@stage.addChild(@groundDot, @torsoDot, @mouthDot, @aboveHeadDot)
|
@topLayer.addChild(@groundDot, @torsoDot, @mouthDot, @aboveHeadDot)
|
||||||
@updateGrid()
|
@updateGrid()
|
||||||
_.defer @refreshAnimation
|
_.defer @refreshAnimation
|
||||||
|
@toggleDots(false)
|
||||||
|
|
||||||
createjs.Ticker.setFPS(30)
|
createjs.Ticker.setFPS(30)
|
||||||
createjs.Ticker.addEventListener('tick', @stage)
|
createjs.Ticker.addEventListener('tick', @stage)
|
||||||
|
|
||||||
toggleDots: ->
|
toggleDots: (newShowDots) ->
|
||||||
@showDots = not @showDots
|
@showDots = if typeof(newShowDots) is 'boolean' then newShowDots else not @showDots
|
||||||
@updateDots()
|
@updateDots()
|
||||||
|
|
||||||
updateDots: ->
|
updateDots: ->
|
||||||
@stage.removeChild(@torsoDot, @mouthDot, @aboveHeadDot, @groundDot)
|
@topLayer.removeChild(@torsoDot, @mouthDot, @aboveHeadDot, @groundDot)
|
||||||
return unless @currentLank
|
return unless @currentLank
|
||||||
return unless @showDots
|
return unless @showDots
|
||||||
torso = @currentLank.getOffset 'torso'
|
torso = @currentLank.getOffset 'torso'
|
||||||
mouth = @currentLank.getOffset 'mouth'
|
mouth = @currentLank.getOffset 'mouth'
|
||||||
aboveHead = @currentLank.getOffset 'aboveHead'
|
aboveHead = @currentLank.getOffset 'aboveHead'
|
||||||
@torsoDot.x = CENTER.x + torso.x * @scale
|
@torsoDot.x = torso.x
|
||||||
@torsoDot.y = CENTER.y + torso.y * @scale
|
@torsoDot.y = torso.y
|
||||||
@mouthDot.x = CENTER.x + mouth.x * @scale
|
@mouthDot.x = mouth.x
|
||||||
@mouthDot.y = CENTER.y + mouth.y * @scale
|
@mouthDot.y = mouth.y
|
||||||
@aboveHeadDot.x = CENTER.x + aboveHead.x * @scale
|
@aboveHeadDot.x = aboveHead.x
|
||||||
@aboveHeadDot.y = CENTER.y + aboveHead.y * @scale
|
@aboveHeadDot.y = aboveHead.y
|
||||||
@stage.addChild(@groundDot, @torsoDot, @mouthDot, @aboveHeadDot)
|
@topLayer.addChild(@groundDot, @torsoDot, @mouthDot, @aboveHeadDot)
|
||||||
|
|
||||||
endAnimation: ->
|
stopAnimation: ->
|
||||||
@currentLank?.queueAction('idle')
|
@currentLank?.queueAction('idle')
|
||||||
|
|
||||||
|
playAnimation: ->
|
||||||
|
@currentLank?.queueAction(@$el.find('#animations-select').val())
|
||||||
|
|
||||||
updateGrid: ->
|
updateGrid: ->
|
||||||
grid = new createjs.Container()
|
grid = new createjs.Container()
|
||||||
line = new createjs.Shape()
|
line = new createjs.Shape()
|
||||||
|
@ -247,19 +251,18 @@ module.exports = class ThangTypeEditView extends RootView
|
||||||
$('#spritesheets').empty()
|
$('#spritesheets').empty()
|
||||||
for image in @layerAdapter.spriteSheet._images
|
for image in @layerAdapter.spriteSheet._images
|
||||||
$('#spritesheets').append(image)
|
$('#spritesheets').append(image)
|
||||||
|
@layerAdapter.container.x = CENTER.x
|
||||||
|
@layerAdapter.container.y = CENTER.y
|
||||||
@updateScale()
|
@updateScale()
|
||||||
|
|
||||||
showAnimation: (animationName) ->
|
showAnimation: (animationName) ->
|
||||||
animationName = @$el.find('#animations-select').val() unless _.isString animationName
|
animationName = @$el.find('#animations-select').val() unless _.isString animationName
|
||||||
return unless animationName
|
return unless animationName
|
||||||
if animationName.startsWith('raw:')
|
@mockThang.action = animationName
|
||||||
animationName = animationName[4...]
|
@showAction(animationName)
|
||||||
@showMovieClip(animationName)
|
|
||||||
else
|
|
||||||
@showAction(animationName)
|
|
||||||
@updateRotation()
|
@updateRotation()
|
||||||
@updateScale() # must happen after update rotation, because updateRotation calls the sprite update() method.
|
@updateScale() # must happen after update rotation, because updateRotation calls the sprite update() method.
|
||||||
|
|
||||||
showMovieClip: (animationName) ->
|
showMovieClip: (animationName) ->
|
||||||
vectorParser = new SpriteBuilder(@thangType)
|
vectorParser = new SpriteBuilder(@thangType)
|
||||||
movieClip = vectorParser.buildMovieClip(animationName)
|
movieClip = vectorParser.buildMovieClip(animationName)
|
||||||
|
@ -268,6 +271,9 @@ module.exports = class ThangTypeEditView extends RootView
|
||||||
if reg
|
if reg
|
||||||
movieClip.regX = -reg.x
|
movieClip.regX = -reg.x
|
||||||
movieClip.regY = -reg.y
|
movieClip.regY = -reg.y
|
||||||
|
scale = @thangType.get('scale')
|
||||||
|
if scale
|
||||||
|
movieClip.scaleX = movieClip.scaleY = scale
|
||||||
@showSprite(movieClip)
|
@showSprite(movieClip)
|
||||||
|
|
||||||
getLankOptions: -> {resolutionFactor: @resolution, thang: @mockThang}
|
getLankOptions: -> {resolutionFactor: @resolution, thang: @mockThang}
|
||||||
|
@ -292,23 +298,16 @@ module.exports = class ThangTypeEditView extends RootView
|
||||||
@layerAdapter.resetSpriteSheet()
|
@layerAdapter.resetSpriteSheet()
|
||||||
@layerAdapter.addLank(lank)
|
@layerAdapter.addLank(lank)
|
||||||
@currentLank = lank
|
@currentLank = lank
|
||||||
lank.sprite.x = CENTER.x
|
|
||||||
lank.sprite.y = CENTER.y
|
|
||||||
lank.on 'new-sprite', ->
|
|
||||||
lank.sprite.x = CENTER.x
|
|
||||||
lank.sprite.y = CENTER.y
|
|
||||||
|
|
||||||
showSprite: (sprite) ->
|
showSprite: (sprite) ->
|
||||||
@clearDisplayObject()
|
@clearDisplayObject()
|
||||||
@clearLank()
|
@clearLank()
|
||||||
sprite.x = CENTER.x
|
@topLayer.addChild(sprite)
|
||||||
sprite.y = CENTER.y
|
|
||||||
@stage.addChildAt(sprite, 1)
|
|
||||||
@currentObject = sprite
|
@currentObject = sprite
|
||||||
@updateDots()
|
@updateDots()
|
||||||
|
|
||||||
clearDisplayObject: ->
|
clearDisplayObject: ->
|
||||||
@stage.removeChild(@currentObject) if @currentObject?
|
@topLayer.removeChild(@currentObject) if @currentObject?
|
||||||
|
|
||||||
clearLank: ->
|
clearLank: ->
|
||||||
@layerAdapter.removeLank(@currentLank) if @currentLank
|
@layerAdapter.removeLank(@currentLank) if @currentLank
|
||||||
|
@ -330,18 +329,12 @@ module.exports = class ThangTypeEditView extends RootView
|
||||||
@currentLank.update(true)
|
@currentLank.update(true)
|
||||||
|
|
||||||
updateScale: =>
|
updateScale: =>
|
||||||
resValue = (@resolutionSlider.slider('value') + 1) / 10
|
|
||||||
scaleValue = (@scaleSlider.slider('value') + 1) / 10
|
scaleValue = (@scaleSlider.slider('value') + 1) / 10
|
||||||
|
@layerAdapter.container.scaleX = @layerAdapter.container.scaleY = @topLayer.scaleX = @topLayer.scaleY = scaleValue
|
||||||
fixed = scaleValue.toFixed(1)
|
fixed = scaleValue.toFixed(1)
|
||||||
@scale = scaleValue
|
@scale = scaleValue
|
||||||
@$el.find('.scale-label').text " #{fixed}x "
|
@$el.find('.scale-label').text " #{fixed}x "
|
||||||
if @currentLank
|
|
||||||
@currentLank.sprite.scaleX = @currentLank.sprite.baseScaleX * scaleValue
|
|
||||||
@currentLank.sprite.scaleY = @currentLank.sprite.baseScaleY * scaleValue
|
|
||||||
else if @currentObject?
|
|
||||||
@currentObject.scaleX = @currentObject.scaleY = scaleValue / resValue
|
|
||||||
@updateGrid()
|
@updateGrid()
|
||||||
@updateDots()
|
|
||||||
|
|
||||||
updateResolution: =>
|
updateResolution: =>
|
||||||
value = (@resolutionSlider.slider('value') + 1) / 10
|
value = (@resolutionSlider.slider('value') + 1) / 10
|
||||||
|
@ -427,6 +420,7 @@ module.exports = class ThangTypeEditView extends RootView
|
||||||
|
|
||||||
onSelectNode: (e, selected) =>
|
onSelectNode: (e, selected) =>
|
||||||
selected = selected[0]
|
selected = selected[0]
|
||||||
|
@topLayer.removeChild(@boundsBox) if @boundsBox
|
||||||
return @stopShowingSelectedNode() if not selected
|
return @stopShowingSelectedNode() if not selected
|
||||||
path = selected.getPath()
|
path = selected.getPath()
|
||||||
parts = path.split('/')
|
parts = path.split('/')
|
||||||
|
@ -437,15 +431,16 @@ module.exports = class ThangTypeEditView extends RootView
|
||||||
obj = vectorParser.buildMovieClip(key) if type is 'animations'
|
obj = vectorParser.buildMovieClip(key) if type is 'animations'
|
||||||
obj = vectorParser.buildContainerFromStore(key) if type is 'containers'
|
obj = vectorParser.buildContainerFromStore(key) if type is 'containers'
|
||||||
obj = vectorParser.buildShapeFromStore(key) if type is 'shapes'
|
obj = vectorParser.buildShapeFromStore(key) if type is 'shapes'
|
||||||
if obj?.bounds
|
|
||||||
obj.regX = obj.bounds.x + obj.bounds.width / 2
|
bounds = obj?.bounds or obj?.nominalBounds
|
||||||
obj.regY = obj.bounds.y + obj.bounds.height / 2
|
if bounds
|
||||||
else if obj?.frameBounds?[0]
|
@boundsBox = new createjs.Shape()
|
||||||
bounds = obj.frameBounds[0]
|
@boundsBox.graphics.beginFill('#aaaaaa').beginStroke('black').drawRect(bounds.x, bounds.y, bounds.width, bounds.height)
|
||||||
obj.regX = bounds.x + bounds.width / 2
|
@topLayer.addChild(@boundsBox)
|
||||||
obj.regY = bounds.y + bounds.height / 2
|
obj.regX = @boundsBox.regX = bounds.x + bounds.width / 2
|
||||||
|
obj.regY = @boundsBox.regY = bounds.y + bounds.height / 2
|
||||||
|
|
||||||
@showSprite(obj) if obj
|
@showSprite(obj) if obj
|
||||||
obj.y = 200 if obj # truly center the container
|
|
||||||
@showingSelectedNode = true
|
@showingSelectedNode = true
|
||||||
@currentLank?.destroy()
|
@currentLank?.destroy()
|
||||||
@currentLank = null
|
@currentLank = null
|
||||||
|
|
|
@ -115,6 +115,9 @@ module.exports = class InventoryView extends CocoView
|
||||||
@$el.find('#selected-items').hide() # Hide until one is selected
|
@$el.find('#selected-items').hide() # Hide until one is selected
|
||||||
@delegateEvents()
|
@delegateEvents()
|
||||||
|
|
||||||
|
if @selectedHero and not @startedLoadingFirstHero
|
||||||
|
@loadHero()
|
||||||
|
|
||||||
afterInsert: ->
|
afterInsert: ->
|
||||||
super()
|
super()
|
||||||
@canvasWidth = @$el.find('canvas').innerWidth()
|
@canvasWidth = @$el.find('canvas').innerWidth()
|
||||||
|
@ -329,7 +332,8 @@ module.exports = class InventoryView extends CocoView
|
||||||
@loadHero()
|
@loadHero()
|
||||||
|
|
||||||
loadHero: ->
|
loadHero: ->
|
||||||
return unless @selectedHero and not @$el.hasClass 'secret'
|
return unless @supermodel.finished() and @selectedHero and not @$el.hasClass 'secret'
|
||||||
|
@startedLoadingFirstHero = true
|
||||||
@stage?.removeAllChildren()
|
@stage?.removeAllChildren()
|
||||||
if @selectedHero.loaded and movieClip = @movieClips?[@selectedHero.get('original')]
|
if @selectedHero.loaded and movieClip = @movieClips?[@selectedHero.get('original')]
|
||||||
@stage.addChild(movieClip)
|
@stage.addChild(movieClip)
|
||||||
|
|
|
@ -17,6 +17,7 @@ class LevelSessionsCollection extends CocoCollection
|
||||||
module.exports = class WorldMapView extends RootView
|
module.exports = class WorldMapView extends RootView
|
||||||
id: 'world-map-view'
|
id: 'world-map-view'
|
||||||
template: template
|
template: template
|
||||||
|
terrain: 'Dungeon' # or Grass
|
||||||
|
|
||||||
events:
|
events:
|
||||||
'click .map-background': 'onClickMap'
|
'click .map-background': 'onClickMap'
|
||||||
|
@ -36,6 +37,7 @@ module.exports = class WorldMapView extends RootView
|
||||||
$(window).on 'resize', @onWindowResize
|
$(window).on 'resize', @onWindowResize
|
||||||
@playAmbientSound()
|
@playAmbientSound()
|
||||||
@preloadTopHeroes()
|
@preloadTopHeroes()
|
||||||
|
@hadEverChosenHero = me.get('heroConfig')?.thangType
|
||||||
|
|
||||||
destroy: ->
|
destroy: ->
|
||||||
$(window).off 'resize', @onWindowResize
|
$(window).off 'resize', @onWindowResize
|
||||||
|
@ -75,7 +77,7 @@ module.exports = class WorldMapView extends RootView
|
||||||
context.levelStatusMap = @levelStatusMap
|
context.levelStatusMap = @levelStatusMap
|
||||||
context.levelPlayCountMap = @levelPlayCountMap
|
context.levelPlayCountMap = @levelPlayCountMap
|
||||||
context.isIPadApp = application.isIPadApp
|
context.isIPadApp = application.isIPadApp
|
||||||
context.mapType = 'dungeon'
|
context.mapType = _.string.slugify @terrain
|
||||||
context
|
context
|
||||||
|
|
||||||
afterRender: ->
|
afterRender: ->
|
||||||
|
@ -84,6 +86,7 @@ module.exports = class WorldMapView extends RootView
|
||||||
unless application.isIPadApp
|
unless application.isIPadApp
|
||||||
_.defer => @$el.find('.game-controls .btn').tooltip() # Have to defer or i18n doesn't take effect.
|
_.defer => @$el.find('.game-controls .btn').tooltip() # Have to defer or i18n doesn't take effect.
|
||||||
@$el.find('.level').tooltip()
|
@$el.find('.level').tooltip()
|
||||||
|
@$el.addClass _.string.slugify @terrain
|
||||||
|
|
||||||
onSessionsLoaded: (e) ->
|
onSessionsLoaded: (e) ->
|
||||||
for session in @sessions.models
|
for session in @sessions.models
|
||||||
|
@ -113,7 +116,7 @@ module.exports = class WorldMapView extends RootView
|
||||||
@startLevel $(e.target).parents('.level-info-container')
|
@startLevel $(e.target).parents('.level-info-container')
|
||||||
|
|
||||||
startLevel: (levelElement) ->
|
startLevel: (levelElement) ->
|
||||||
playLevelModal = new PlayLevelModal supermodel: @supermodel, levelID: levelElement.data('level-id'), levelPath: levelElement.data('level-path'), levelName: levelElement.data('level-name')
|
playLevelModal = new PlayLevelModal supermodel: @supermodel, levelID: levelElement.data('level-id'), levelPath: levelElement.data('level-path'), levelName: levelElement.data('level-name'), hadEverChosenHero: @hadEverChosenHero
|
||||||
@openModalView playLevelModal
|
@openModalView playLevelModal
|
||||||
@$levelInfo?.hide()
|
@$levelInfo?.hide()
|
||||||
|
|
||||||
|
@ -148,28 +151,45 @@ module.exports = class WorldMapView extends RootView
|
||||||
@$levelInfo.css('top', top)
|
@$levelInfo.css('top', top)
|
||||||
|
|
||||||
onWindowResize: (e) =>
|
onWindowResize: (e) =>
|
||||||
mapHeight = 1536
|
mapHeight = iPadHeight = 1536
|
||||||
mapWidth = 2350 # 2500 for forest
|
mapWidth = if @terrain is 'Dungeon' then 2350 else 2500
|
||||||
|
iPadWidth = 2048
|
||||||
aspectRatio = mapWidth / mapHeight
|
aspectRatio = mapWidth / mapHeight
|
||||||
|
iPadAspectRatio = iPadWidth / iPadHeight
|
||||||
pageWidth = $(window).width()
|
pageWidth = $(window).width()
|
||||||
pageHeight = $(window).height()
|
pageHeight = $(window).height()
|
||||||
widthRatio = pageWidth / mapWidth
|
widthRatio = pageWidth / mapWidth
|
||||||
heightRatio = pageHeight / mapHeight
|
heightRatio = pageHeight / mapHeight
|
||||||
if widthRatio > heightRatio
|
iPadWidthRatio = pageWidth / iPadWidth
|
||||||
resultingWidth = pageWidth
|
if @terrain is 'Dungeon'
|
||||||
resultingHeight = resultingWidth / aspectRatio
|
# Make sure we can see almost the whole map, fading to background in one dimension.
|
||||||
|
if heightRatio <= iPadWidthRatio
|
||||||
|
# Full width, full height, left and right margin
|
||||||
|
resultingHeight = pageHeight
|
||||||
|
resultingWidth = resultingHeight * aspectRatio
|
||||||
|
else if iPadWidthRatio < heightRatio * (iPadAspectRatio / aspectRatio)
|
||||||
|
# Cropped width, full height, left and right margin
|
||||||
|
resultingWidth = pageWidth
|
||||||
|
resultingHeight = resultingWidth / aspectRatio
|
||||||
|
else
|
||||||
|
# Cropped width, full height, top and bottom margin
|
||||||
|
resultingWidth = pageWidth * aspectRatio / iPadAspectRatio
|
||||||
|
resultingHeight = resultingWidth / aspectRatio
|
||||||
else
|
else
|
||||||
resultingHeight = pageHeight
|
# Scale it in either dimension so that we're always full on one of the dimensions.
|
||||||
resultingWidth = resultingHeight * aspectRatio
|
if heightRatio > widthRatio
|
||||||
|
resultingHeight = pageHeight
|
||||||
|
resultingWidth = resultingHeight * aspectRatio
|
||||||
|
else
|
||||||
|
resultingWidth = pageWidth
|
||||||
|
resultingHeight = resultingWidth / aspectRatio
|
||||||
resultingMarginX = (pageWidth - resultingWidth) / 2
|
resultingMarginX = (pageWidth - resultingWidth) / 2
|
||||||
resultingMarginY = (pageHeight - resultingHeight) / 2
|
resultingMarginY = (pageHeight - resultingHeight) / 2
|
||||||
@$el.find('.map').css(width: resultingWidth, height: resultingHeight, 'margin-left': resultingMarginX, 'margin-top': resultingMarginY)
|
@$el.find('.map').css(width: resultingWidth, height: resultingHeight, 'margin-left': resultingMarginX, 'margin-top': resultingMarginY)
|
||||||
|
|
||||||
playAmbientSound: ->
|
playAmbientSound: ->
|
||||||
return if @ambientSound
|
return if @ambientSound
|
||||||
#terrain = 'Grass'
|
return unless file = {Dungeon: 'ambient-dungeon', Grass: 'ambient-map-grass'}[@terrain]
|
||||||
terrain = 'Dungeon'
|
|
||||||
return unless file = {Dungeon: 'ambient-dungeon', Grass: 'ambient-map-grass'}[terrain]
|
|
||||||
src = "/file/interface/#{file}#{AudioPlayer.ext}"
|
src = "/file/interface/#{file}#{AudioPlayer.ext}"
|
||||||
unless AudioPlayer.getStatus(src)?.loaded
|
unless AudioPlayer.getStatus(src)?.loaded
|
||||||
AudioPlayer.preloadSound src
|
AudioPlayer.preloadSound src
|
||||||
|
@ -531,8 +551,8 @@ hero = [
|
||||||
id: 'dungeons-of-kithgard'
|
id: 'dungeons-of-kithgard'
|
||||||
original: '528110f30268d018e3000001'
|
original: '528110f30268d018e3000001'
|
||||||
description: 'Grab the gem, but touch nothing else. Start here.'
|
description: 'Grab the gem, but touch nothing else. Start here.'
|
||||||
x: 20.24
|
x: 14
|
||||||
y: 32.93
|
y: 15.5
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name: 'Gems in the Deep'
|
name: 'Gems in the Deep'
|
||||||
|
@ -541,8 +561,8 @@ hero = [
|
||||||
id: 'gems-in-the-deep'
|
id: 'gems-in-the-deep'
|
||||||
original: '54173c90844506ae0195a0b4'
|
original: '54173c90844506ae0195a0b4'
|
||||||
description: 'Quickly collect the gems; you will need them.'
|
description: 'Quickly collect the gems; you will need them.'
|
||||||
x: 18.47
|
x: 32
|
||||||
y: 49.78
|
y: 15.5
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name: 'Shadow Guard'
|
name: 'Shadow Guard'
|
||||||
|
@ -551,8 +571,8 @@ hero = [
|
||||||
id: 'shadow-guard'
|
id: 'shadow-guard'
|
||||||
original: '54174347844506ae0195a0b8'
|
original: '54174347844506ae0195a0b8'
|
||||||
description: 'Evade the Kithgard minion.'
|
description: 'Evade the Kithgard minion.'
|
||||||
x: 30.89
|
x: 54
|
||||||
y: 61.30
|
y: 9
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name: 'True Names'
|
name: 'True Names'
|
||||||
|
@ -561,8 +581,8 @@ hero = [
|
||||||
id: 'true-names'
|
id: 'true-names'
|
||||||
original: '541875da4c16460000ab990f'
|
original: '541875da4c16460000ab990f'
|
||||||
description: 'Learn an enemy\'s true name to defeat it.'
|
description: 'Learn an enemy\'s true name to defeat it.'
|
||||||
x: 44.39
|
x: 74
|
||||||
y: 57.39
|
y: 12
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name: 'The Raised Sword'
|
name: 'The Raised Sword'
|
||||||
|
@ -571,8 +591,8 @@ hero = [
|
||||||
id: 'the-raised-sword'
|
id: 'the-raised-sword'
|
||||||
original: '5418aec24c16460000ab9aa6'
|
original: '5418aec24c16460000ab9aa6'
|
||||||
description: 'Learn to equip yourself for combat.'
|
description: 'Learn to equip yourself for combat.'
|
||||||
x: 41.83
|
x: 85
|
||||||
y: 41.74
|
y: 20
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name: 'The First Kithmaze'
|
name: 'The First Kithmaze'
|
||||||
|
@ -581,8 +601,8 @@ hero = [
|
||||||
id: 'the-first-kithmaze'
|
id: 'the-first-kithmaze'
|
||||||
original: '5418b9d64c16460000ab9ab4'
|
original: '5418b9d64c16460000ab9ab4'
|
||||||
description: 'The builders of Kith constructed many mazes to confuse travelers.'
|
description: 'The builders of Kith constructed many mazes to confuse travelers.'
|
||||||
x: 57.39
|
x: 70
|
||||||
y: 48.15
|
y: 28
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name: 'The Second Kithmaze'
|
name: 'The Second Kithmaze'
|
||||||
|
@ -591,8 +611,8 @@ hero = [
|
||||||
id: 'the-second-kithmaze'
|
id: 'the-second-kithmaze'
|
||||||
original: '5418cf256bae62f707c7e1c3'
|
original: '5418cf256bae62f707c7e1c3'
|
||||||
description: 'Many have tried, few have found their way through this maze.'
|
description: 'Many have tried, few have found their way through this maze.'
|
||||||
x: 61.72
|
x: 67
|
||||||
y: 37.07
|
y: 41
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name: 'New Sight'
|
name: 'New Sight'
|
||||||
|
@ -611,8 +631,8 @@ hero = [
|
||||||
id: 'lowly-kithmen'
|
id: 'lowly-kithmen'
|
||||||
original: '541b24511ccc8eaae19f3c1f'
|
original: '541b24511ccc8eaae19f3c1f'
|
||||||
description: 'Use your glasses to seek out and attack the Kithmen.'
|
description: 'Use your glasses to seek out and attack the Kithmen.'
|
||||||
x: 70.53
|
x: 74
|
||||||
y: 27.93
|
y: 48
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name: 'A Bolt in the Dark'
|
name: 'A Bolt in the Dark'
|
||||||
|
@ -621,8 +641,8 @@ hero = [
|
||||||
id: 'a-bolt-in-the-dark'
|
id: 'a-bolt-in-the-dark'
|
||||||
original: '541b288e1ccc8eaae19f3c25'
|
original: '541b288e1ccc8eaae19f3c25'
|
||||||
description: 'Kithmen are not the only ones to stand in your way.'
|
description: 'Kithmen are not the only ones to stand in your way.'
|
||||||
x: 86.08
|
x: 76
|
||||||
y: 40.76
|
y: 60
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name: 'The Final Kithmaze'
|
name: 'The Final Kithmaze'
|
||||||
|
@ -631,8 +651,8 @@ hero = [
|
||||||
id: 'the-final-kithmaze'
|
id: 'the-final-kithmaze'
|
||||||
original: '541b434e1ccc8eaae19f3c33'
|
original: '541b434e1ccc8eaae19f3c33'
|
||||||
description: 'To escape you must find your way through an Elder Kithman\'s maze.'
|
description: 'To escape you must find your way through an Elder Kithman\'s maze.'
|
||||||
x: 96.95
|
x: 82
|
||||||
y: 58.15
|
y: 70
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name: 'Kithgard Gates'
|
name: 'Kithgard Gates'
|
||||||
|
@ -642,8 +662,8 @@ hero = [
|
||||||
original: '541c9a30c6362edfb0f34479'
|
original: '541c9a30c6362edfb0f34479'
|
||||||
description: 'Escape the Kithgard dungeons and don\'t let the guardians get you.'
|
description: 'Escape the Kithgard dungeons and don\'t let the guardians get you.'
|
||||||
disabled: true
|
disabled: true
|
||||||
x: 84.02
|
x: 89
|
||||||
y: 72.39
|
y: 82
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name: 'Defence of Plainswood'
|
name: 'Defence of Plainswood'
|
||||||
|
|
|
@ -45,8 +45,14 @@ module.exports = class PlayLevelModal extends ModalView
|
||||||
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-open', volume: 1
|
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-open', volume: 1
|
||||||
@insertSubView @chooseHeroView = new ChooseHeroView @options
|
@insertSubView @chooseHeroView = new ChooseHeroView @options
|
||||||
@insertSubView @inventoryView = new InventoryView @options
|
@insertSubView @inventoryView = new InventoryView @options
|
||||||
@inventoryView.$el.addClass 'secret'
|
if @options.hadEverChosenHero
|
||||||
@chooseHeroView.onShown()
|
@$el.find('.choose-hero-active').add(@chooseHeroView.$el).addClass 'secret'
|
||||||
|
@$el.find('.choose-inventory-active').removeClass 'secret'
|
||||||
|
@inventoryView.onShown()
|
||||||
|
else
|
||||||
|
@$el.find('.choose-inventory-active').add(@inventoryView.$el).addClass 'secret'
|
||||||
|
@$el.find('.choose-hero-active').removeClass 'secret'
|
||||||
|
@chooseHeroView.onShown()
|
||||||
|
|
||||||
onHidden: ->
|
onHidden: ->
|
||||||
unless @navigatingToPlay
|
unless @navigatingToPlay
|
||||||
|
|
|
@ -75,7 +75,7 @@
|
||||||
"javascript-brunch": "> 1.0 < 1.8",
|
"javascript-brunch": "> 1.0 < 1.8",
|
||||||
"coffee-script-brunch": "https://github.com/brunch/coffee-script-brunch/tarball/master",
|
"coffee-script-brunch": "https://github.com/brunch/coffee-script-brunch/tarball/master",
|
||||||
"coffeelint-brunch": "> 1.0 < 1.8",
|
"coffeelint-brunch": "> 1.0 < 1.8",
|
||||||
"sass-brunch": "1.7.0",
|
"sass-brunch": "1.7.2",
|
||||||
"css-brunch": "> 1.0 < 1.8",
|
"css-brunch": "> 1.0 < 1.8",
|
||||||
"jade-brunch": "> 1.0 < 1.8",
|
"jade-brunch": "> 1.0 < 1.8",
|
||||||
"uglify-js-brunch": "~1.7.4",
|
"uglify-js-brunch": "~1.7.4",
|
||||||
|
@ -86,6 +86,7 @@
|
||||||
"marked": "0.2.x",
|
"marked": "0.2.x",
|
||||||
"telepath-brunch": "https://github.com/nwinter/telepath-brunch/tarball/master",
|
"telepath-brunch": "https://github.com/nwinter/telepath-brunch/tarball/master",
|
||||||
"bower": "~1.3.8",
|
"bower": "~1.3.8",
|
||||||
|
"bless-brunch": "https://github.com/ThomasConner/bless-brunch/tarball/master",
|
||||||
"karma-script-launcher": "~0.1.0",
|
"karma-script-launcher": "~0.1.0",
|
||||||
"karma-chrome-launcher": "~0.1.2",
|
"karma-chrome-launcher": "~0.1.2",
|
||||||
"karma-firefox-launcher": "~0.1.3",
|
"karma-firefox-launcher": "~0.1.3",
|
||||||
|
|
|
@ -21,20 +21,20 @@ module.exports.setup = (app) ->
|
||||||
setupScheduledEmails = ->
|
setupScheduledEmails = ->
|
||||||
testForLockManager()
|
testForLockManager()
|
||||||
mailTasks = [
|
mailTasks = [
|
||||||
taskFunction: candidateUpdateProfileTask
|
# taskFunction: candidateUpdateProfileTask
|
||||||
frequencyMs: 10 * 60 * 1000 #10 minutes
|
# frequencyMs: 10 * 60 * 1000 #10 minutes
|
||||||
,
|
#,
|
||||||
taskFunction: internalCandidateUpdateTask
|
# taskFunction: internalCandidateUpdateTask
|
||||||
frequencyMs: 10 * 60 * 1000 #10 minutes
|
# frequencyMs: 10 * 60 * 1000 #10 minutes
|
||||||
,
|
#,
|
||||||
taskFunction: employerNewCandidatesAvailableTask
|
# taskFunction: employerNewCandidatesAvailableTask
|
||||||
frequencyMs: 10 * 60 * 1000 #10 minutes
|
# frequencyMs: 10 * 60 * 1000 #10 minutes
|
||||||
,
|
#,
|
||||||
taskFunction: unapprovedCandidateFinishProfileTask
|
# taskFunction: unapprovedCandidateFinishProfileTask
|
||||||
frequencyMs: 10 * 60 * 1000
|
# frequencyMs: 10 * 60 * 1000
|
||||||
,
|
#,
|
||||||
taskFunction: emailUserRemarkTaskRemindersTask
|
# taskFunction: emailUserRemarkTaskRemindersTask
|
||||||
frequencyMs: 10 * 60 * 1000
|
# frequencyMs: 10 * 60 * 1000
|
||||||
]
|
]
|
||||||
|
|
||||||
for mailTask in mailTasks
|
for mailTask in mailTasks
|
||||||
|
|
Loading…
Reference in a new issue