Merge branch 'master' into production

This commit is contained in:
Nick Winter 2014-05-09 15:09:21 -07:00
commit 9e4fdf6d16
116 changed files with 1761 additions and 182 deletions

11
.gitignore vendored
View file

@ -28,6 +28,9 @@ Thumbs.db
*.sublime-project *.sublime-project
*.sublime-workspace *.sublime-workspace
# IntelliJ/WebStorm
*.iml
# NPM packages folder. # NPM packages folder.
node_modules/ node_modules/
bower_components/ bower_components/
@ -77,4 +80,10 @@ bin/mongo/
# windows # windows
/SCOCODE.bat /SCOCODE.bat
### If you add something here, copy it to the end of .npmignore, too. ### # local settings
login.coffee
# debugging
*.heapsnapshot
### If you add something here, copy it to the end of .npmignore, too. ###

View file

@ -53,6 +53,9 @@ Thumbs.db
*.sublime-project *.sublime-project
*.sublime-workspace *.sublime-workspace
# IntelliJ/WebStorm
*.iml
# NPM packages folder. # NPM packages folder.
node_modules/ node_modules/
@ -89,6 +92,12 @@ mongo/
bin/node/ bin/node/
bin/mongo/ bin/mongo/
# Karma coverage # Karma coverage
coverage/ coverage/
# local settings
login.coffee
# debugging
*.heapsnapshot

View file

@ -14,8 +14,8 @@ if (!Function.prototype.bind) {
throw new TypeError("Function.prototype.bind (Shim) - target is not callable"); throw new TypeError("Function.prototype.bind (Shim) - target is not callable");
} }
var aArgs = Array.prototype.slice.call(arguments, 1), var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this, fToBind = this,
fNOP = function () {}, fNOP = function () {},
fBound = function () { fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis return fToBind.apply(this instanceof fNOP && oThis
@ -83,13 +83,277 @@ self.transferableSupported = function transferableSupported() {
var World = self.require('lib/world/world'); var World = self.require('lib/world/world');
var GoalManager = self.require('lib/world/GoalManager'); var GoalManager = self.require('lib/world/GoalManager');
Aether.addGlobal('Vector', require('lib/world/vector'));
Aether.addGlobal('_', _);
var serializedClasses = {
"Thang": self.require('lib/world/thang'),
"Vector": self.require('lib/world/vector'),
"Rectangle": self.require('lib/world/rectangle')
};
self.currentUserCodeMapCopy = "";
self.currentDebugWorldFrame = 0;
self.stringifyValue = function(value, depth) {
var brackets, i, isArray, isObject, key, prefix, s, sep, size, v, values, _i, _j, _len, _len1, _ref, _ref1, _ref2, _ref3;
if (!value || _.isString(value)) {
return value;
}
if (_.isFunction(value)) {
if (depth === 2) {
return void 0;
} else {
return "<Function>";
}
}
if (value === this.thang && depth) {
return "<this " + value.id + ">";
}
if (depth === 2) {
if (((_ref = value.constructor) != null ? _ref.className : void 0) === "Thang") {
value = "<" + (value.type || value.spriteName) + " - " + value.id + ", " + (value.pos ? value.pos.toString() : 'non-physical') + ">";
} else {
value = value.toString();
}
return value;
}
isArray = _.isArray(value);
isObject = _.isObject(value);
if (!(isArray || isObject)) {
return value.toString();
}
brackets = isArray ? ["[", "]"] : ["{", "}"];
size = _.size(value);
if (!size) {
return brackets.join("");
}
values = [];
if (isArray) {
for (_i = 0, _len = value.length; _i < _len; _i++) {
v = value[_i];
s = this.stringifyValue(v, depth + 1);
if (s !== void 0) {
values.push("" + s);
}
}
} else {
_ref2 = (_ref1 = value.apiProperties) != null ? _ref1 : _.keys(value);
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
key = _ref2[_j];
if (key[0] === "_") continue;
s = this.stringifyValue(value[key], depth + 1);
if (s !== void 0) {
values.push(key + ": " + s);
}
}
}
sep = '\n' + ((function() {
var _k, _results;
_results = [];
for (i = _k = 0; 0 <= depth ? _k < depth : _k > depth; i = 0 <= depth ? ++_k : --_k) {
_results.push(" ");
}
return _results;
})()).join('');
prefix = (_ref3 = value.constructor) != null ? _ref3.className : void 0;
if (isArray) {
if (prefix == null) {
prefix = "Array";
}
}
if (isObject) {
if (prefix == null) {
prefix = "Object";
}
}
prefix = prefix ? prefix + " " : "";
return "" + prefix + brackets[0] + sep + " " + (values.join(sep + ' ')) + sep + brackets[1];
};
var cache = {};
self.invalidateCache = function () {
cache = {};
};
self.retrieveValueFromCache = function (thangID, spellID, variableChain, frame) {
var frameCache, thangCache, spellCache;
if ((frameCache = cache[frame]) && (thangCache = frameCache[thangID]) && (spellCache = thangCache[spellID]))
return spellCache[variableChain.join()];
return undefined;
};
self.updateCache = function (thangID, spellID, variableChain, frame, value) {
var key, keys, currentObject;
keys = [frame,thangID, spellID, variableChain.join()];
currentObject = cache;
for (var i = 0, len = keys.length - 1; i < len; i++)
{
key = keys[i];
if (!(key in currentObject))
currentObject[key] = {};
currentObject = currentObject[key];
}
currentObject[keys[keys.length - 1]] = value;
};
self.retrieveValueFromFrame = function retrieveValueFromFrame(args) {
var cacheValue;
if (args.frame === self.currentDebugWorldFrame && (cacheValue = self.retrieveValueFromCache(args.currentThangID, args.currentSpellID, args.variableChain, args.frame)))
return self.postMessage({type: 'debug-value-return', serialized: {"key": args.variableChain.join("."), "value": cacheValue}});
var retrieveProperty = function retrieveProperty(currentThangID, currentSpellID, variableChain)
{
var prop;
var value;
var keys = [];
for (var i = 0, len = variableChain.length; i < len; i++) {
prop = variableChain[i];
if (prop === "this")
{
value = self.debugWorld.thangMap[currentThangID];
}
else if (i === 0)
{
try
{
var flowStates = self.debugWorld.userCodeMap[currentThangID][currentSpellID].flow.states;
//we have to go to the second last flowState as we run the world for one additional frame
//to collect the flow
value = _.last(flowStates[flowStates.length - 1].statements).variables[prop];
}
catch (e)
{
value = undefined;
}
}
else
{
value = value[prop];
}
keys.push(prop);
if (!value) break;
var classOfValue;
if (classOfValue = serializedClasses[value.CN])
{
if (value.CN === "Thang")
{
var thang = self.debugWorld.thangMap[value.id];
value = thang || "<Thang " + value.id + " (non-existent)>"
}
else
{
value = classOfValue.deserializeFromAether(value);
}
}
}
var serializedProperty = {
"key": keys.join("."),
"value": self.stringifyValue(value,0)
};
self.updateCache(currentThangID,currentSpellID,variableChain, args.frame, serializedProperty.value);
self.postMessage({type: 'debug-value-return', serialized: serializedProperty});
};
self.enableFlowOnThangSpell(args.currentThangID, args.currentSpellID, args.userCodeMap);
self.setupDebugWorldToRunUntilFrame(args);
self.debugWorld.loadFrames(
retrieveProperty.bind({},args.currentThangID, args.currentSpellID, args.variableChain),
self.onDebugWorldError,
self.onDebugWorldProgress,
false,
args.frame
);
};
self.enableFlowOnThangSpell = function (thangID, spellID, userCodeMap) {
try {
if (userCodeMap[thangID][spellID].originalOptions.includeFlow === true &&
userCodeMap[thangID][spellID].originalOptions.noSerializationInFlow === true)
return;
else
{
userCodeMap[thangID][spellID].originalOptions.includeFlow = true;
userCodeMap[thangID][spellID].originalOptions.noSerializationInFlow = true;
var temporaryAether = Aether.deserialize(userCodeMap[thangID][spellID]);
temporaryAether.transpile(temporaryAether.raw);
userCodeMap[thangID][spellID] = temporaryAether.serialize();
}
}
catch (e) {
console.log("there was an error enabling flow on thang spell:" + e)
}
};
self.setupDebugWorldToRunUntilFrame = function (args) {
self.debugPostedErrors = {};
self.debugt0 = new Date();
self.debugPostedErrors = false;
self.logsLogged = 0;
var stringifiedUserCodeMap = JSON.stringify(args.userCodeMap);
var userCodeMapHasChanged = ! _.isEqual(self.currentUserCodeMapCopy, stringifiedUserCodeMap);
self.currentUserCodeMapCopy = stringifiedUserCodeMap;
if (args.frame != self.currentDebugWorldFrame) self.invalidateCache();
if (!self.debugWorld || userCodeMapHasChanged || args.frame < self.currentDebugWorldFrame) {
try {
self.debugWorld = new World(args.worldName, args.userCodeMap);
if (args.level)
self.debugWorld.loadFromLevel(args.level, true);
self.debugGoalManager = new GoalManager(self.debugWorld);
self.debugGoalManager.setGoals(args.goals);
self.debugGoalManager.setCode(args.userCodeMap);
self.debugGoalManager.worldGenerationWillBegin();
self.debugWorld.setGoalManager(self.debugGoalManager);
}
catch (error) {
self.onDebugWorldError(error);
return;
}
Math.random = self.debugWorld.rand.randf; // so user code is predictable
}
self.debugWorld.totalFrames = args.frame; //hack to work around error checking
self.currentDebugWorldFrame = args.frame;
};
self.onDebugWorldLoaded = function onDebugWorldLoaded() {
console.log("World loaded!");
};
self.onDebugWorldError = function onDebugWorldError(error) {
if(!error.isUserCodeProblem) {
console.log("Debug Non-UserCodeError:", error.toString() + "\n" + error.stack || error.stackTrace);
}
return true;
};
self.onDebugWorldProgress = function onDebugWorldProgress(progress) {
self.postMessage({type: 'debug-world-load-progress-changed', progress: progress});
};
self.debugAbort = function () {
if(self.debugWorld && self.debugWorld.name) {
console.log("About to abort:", self.debugWorld.name, typeof self.debugWorld.abort);
if(typeof self.debugWorld !== "undefined")
self.debugWorld.abort();
self.debugWorld = null;
}
self.postMessage({type: 'debugAbort'});
};
self.runWorld = function runWorld(args) { self.runWorld = function runWorld(args) {
self.postedErrors = {}; self.postedErrors = {};
self.t0 = new Date(); self.t0 = new Date();
self.firstWorld = args.firstWorld; self.firstWorld = args.firstWorld;
self.postedErrors = false; self.postedErrors = false;
self.logsLogged = 0; self.logsLogged = 0;
try { try {
self.world = new World(args.worldName, args.userCodeMap); self.world = new World(args.worldName, args.userCodeMap);
if(args.level) if(args.level)
@ -136,11 +400,11 @@ self.onWorldLoaded = function onWorldLoaded() {
}; };
self.onWorldError = function onWorldError(error) { self.onWorldError = function onWorldError(error) {
if(error instanceof Aether.problems.UserCodeProblem) { if(error.isUserCodeProblem) {
if(!self.postedErrors[error.key]) { var errorKey = error.userInfo.key;
var problem = error.serialize(); if(!errorKey || !self.postedErrors[errorKey]) {
self.postMessage({type: 'user-code-problem', problem: problem}); self.postMessage({type: 'user-code-problem', problem: error});
self.postedErrors[error.key] = problem; self.postedErrors[errorKey] = error;
} }
} }
else { else {

241
app/lib/Buddha.coffee Normal file
View file

@ -0,0 +1,241 @@
#Sane rewrite of God (a thread pool)
{now} = require 'lib/world/world_utils'
World = require 'lib/world/world'
###
Every Angel has exactly one WebWorker attached to it.
It will call methods inside the webwrker and kill it if it times out.
###
class Angel
@cyanide: 0xDEADBEEF
infiniteLoopIntervalDuration: 7500 # check this often (must be more than the others added)
infiniteLoopTimeoutDuration: 10000 # wait this long when we check
abortTimeoutDuration: 500 # give in-process or dying workers this long to give up
constructor: (@id, @shared) ->
console.log @id + ": Creating Angel"
if (navigator.userAgent or navigator.vendor or window.opera).search("MSIE") isnt -1
@infiniteLoopIntervalDuration *= 20 # since it's so slow to serialize without transferable objects, we can't trust it
@infiniteLoopTimeoutDuration *= 20
@abortTimeoutDuration *= 10
@initialized = false
@running = false
@hireWorker()
@shared.angels.push @
testWorker: =>
if @initialized
@worker.postMessage {func: 'reportIn'}
# Are there any errors when webworker isn't loaded properly?
onWorkerMessage: (event) =>
#console.log JSON.stringify event
if @aborting and not
event.data.type is 'abort'
console.log id + " is currently aborting old work."
return
switch event.data.type
when 'start-load-frames'
clearTimeout(@condemnTimeout)
@condemnTimeout = _.delay @infinitelyLooped, @infiniteLoopTimeoutDuration
when 'end-load-frames'
console.log @id + ': No condemn this time.'
clearTimeout(@condemnTimeout)
when 'worker-initialized'
unless @initialized
console.log @id + ": Worker initialized after", ((new Date()) - @worker.creationTime), "ms"
@initialized = true
@doWork()
when 'new-world'
@beholdWorld event.data.serialized, event.data.goalStates
when 'world-load-progress-changed'
Backbone.Mediator.publish 'god:world-load-progress-changed', event.data
when 'console-log'
console.log "|" + @id + "|", event.data.args...
when 'user-code-problem'
Backbone.Mediator.publish 'god:user-code-problem', problem: event.data.problem
when 'abort'
console.log @id, "aborted."
clearTimeout @abortTimeout
@aborting = false
@running = false
@shared.busyAngels.pop @
@doWork()
when 'reportIn'
clearTimeout @condemnTimeout
else
console.log @id + " received unsupported message:", event.data
beholdWorld: (serialized, goalStates) ->
return if @aborting
unless serialized
# We're only interested in goalStates. (Simulator)
@latestGoalStates = goalStates
Backbone.Mediator.publish('god:goals-calculated', goalStates: goalStates)
@running = false
@shared.busyAngels.pop @
# console.warn "Goal states: " + JSON.stringify(goalStates)
window.BOX2D_ENABLED = false # Flip this off so that if we have box2d in the namespace, the Collides Components still don't try to create bodies for deserialized Thangs upon attachment
World.deserialize serialized, @shared.worldClassMap, @lastSerializedWorldFrames, @finishBeholdingWorld(goalStates)
window.BOX2D_ENABLED = true
@lastSerializedWorldFrames = serialized.frames
finishBeholdingWorld: (goalStates) => (world) =>
return if @aborting
world.findFirstChangedFrame @shared.world
@shared.world = world
errorCount = (t for t in @shared.world.thangs when t.errorsOut).length
Backbone.Mediator.publish('god:new-world-created', world: world, firstWorld: @shared.firstWorld, errorCount: errorCount, goalStates: goalStates)
for scriptNote in @shared.world.scriptNotes
Backbone.Mediator.publish scriptNote.channel, scriptNote.event
@shared.goalManager?.world = world
@running = false
@shared.busyAngels.pop @
@shared.firstWorld = false
@doWork()
infinitelyLooped: =>
unless @aborting
problem = type: "runtime", level: "error", id: "runtime_InfiniteLoop", message: "Code never finished. It's either really slow or has an infinite loop."
Backbone.Mediator.publish 'god:user-code-problem', problem: problem
Backbone.Mediator.publish 'god:infinite-loop', firstWorld: @shared.firstWorld
@fireWorker()
workIfIdle: ->
@doWork() unless @running
doWork: =>
#console.log "work."
return if @aborted
console.log @id + " ready and looking for work. WorkQueue length is " + @shared.workQueue.length
if @initialized and @shared.workQueue.length
work = @shared.workQueue.pop()
if work is Angel.cyanide # Kill all other Angels, too
console.log @id + ": 'work is poison'"
@shared.workQueue.push Angel.cyanide
@free()
else
console.log @id + ": Sending the worker to work."
@running = true
@shared.busyAngels.push @
console.log "Running world..."
#console.error "worker.postMessage: " + @worker.postMessage + ", work: " + work
@worker.postMessage func: 'runWorld', args: work
console.log @id + ": Setting interval."
clearTimeout @purgatoryTimer
@purgatoryTimer = setInterval @testWorker, @infiniteLoopIntervalDuration
else
console.log "No work for " + @id
@hireWorker()
abort: =>
if @worker and @running
console.log "Aborting " + @id
@running = false
@shared.busyAngels.pop @
@abortTimeout = _.delay @terminate, @fireWorker, @abortTimeoutDuration
@worker.postMessage func: 'abort'
@aborting = true
@work = null
fireWorker: (rehire=true) =>
@aborting = false
@running = false
@shared.busyAngels.pop @
@worker?.removeEventListener 'message', @onWorkerMessage
@worker?.terminate()
@worker = null
clearTimeout @condemnTimeout
clearInterval @purgatoryTimer
console.log "Fired worker."
@initialized = false
@work = null
@hireWorker() if rehire
hireWorker: ->
unless @worker
console.log @id + ": Hiring worker."
@worker = new Worker @shared.workerCode
@worker.addEventListener 'message', @onWorkerMessage
@worker.creationTime = new Date()
#@worker.postMessage func: 'initialized' else
kill: ->
@fireWorker false
@shared.angels.pop @
clearTimeout @condemnTimeout
clearTimeout @purgatoryTimer
@purgatoryTimer = null
@condemnTimeout = null
module.exports = class God
ids: ['Athena', 'Baldr', 'Crom', 'Dagr', 'Eris', 'Freyja', 'Great Gish', 'Hades', 'Ishtar', 'Janus', 'Khronos', 'Loki', 'Marduk', 'Negafook', 'Odin', 'Poseidon', 'Quetzalcoatl', 'Ra', 'Shiva', 'Thor', 'Umvelinqangi', 'Týr', 'Vishnu', 'Wepwawet', 'Xipe Totec', 'Yahweh', 'Zeus', '上帝', 'Tiamat', '盘古', 'Phoebe', 'Artemis', 'Osiris', "嫦娥", 'Anhur', 'Teshub', 'Enlil', 'Perkele', 'Chaos', 'Hera', 'Iris', 'Theia', 'Uranus', 'Stribog', 'Sabazios', 'Izanagi', 'Ao', 'Tāwhirimātea', 'Tengri', 'Inmar', 'Torngarsuk', 'Centzonhuitznahua', 'Hunab Ku', 'Apollo', 'Helios', 'Thoth', 'Hyperion', 'Alectrona', 'Eos', 'Mitra', 'Saranyu', 'Freyr', 'Koyash', 'Atropos', 'Clotho', 'Lachesis', 'Tyche', 'Skuld', 'Urðr', 'Verðandi', 'Camaxtli', 'Huhetotl', 'Set', 'Anu', 'Allah', 'Anshar', 'Hermes', 'Lugh', 'Brigit', 'Manannan Mac Lir', 'Persephone', 'Mercury', 'Venus', 'Mars', 'Azrael', 'He-Man', 'Anansi', 'Issek', 'Mog', 'Kos', 'Amaterasu Omikami', 'Raijin', 'Susanowo', 'Blind Io', 'The Lady', 'Offler', 'Ptah', 'Anubis', 'Ereshkigal', 'Nergal', 'Thanatos', 'Macaria', 'Angelos', 'Erebus', 'Hecate', 'Hel', 'Orcus', 'Ishtar-Deela Nakh', 'Prometheus', 'Hephaestos', 'Sekhmet', 'Ares', 'Enyo', 'Otrera', 'Pele', 'Hadúr', 'Hachiman', 'Dayisun Tngri', 'Ullr', 'Lua', 'Minerva']
nextID: ->
@lastID = (if @lastID? then @lastID + 1 else Math.floor(@ids.length * Math.random())) % @ids.length
@ids[@lastID]
# Charlie's Angels are all given access to this.
angelsShare: {
workerCode: '/javascripts/workers/worker_world.js' # Either path or function
workQueue: []
firstWorld: true
world: undefined
goalManager: undefined
worldClassMap: undefined
angels: []
busyAngels: [] # Busy angels will automatically register here.
}
constructor: (options) ->
options ?= {}
@angelsShare.workerCode = options.workerCode if options.workerCode
# ~20MB per idle worker + angel overhead - in this implementation, every Angel maps to 1 worker
angelCount = options.maxAngels ? options.maxWorkerPoolSize ? 2 # How many concurrent Angels/web workers to use at a time
_.delay (=>new Angel @nextID(), @angelsShare), 250 * i for i in [0...angelCount] # Don't generate all Angels at once.
Backbone.Mediator.subscribe 'tome:cast-spells', @onTomeCast, @
onTomeCast: (e) ->
@createWorld e.spells
setGoalManager: (goalManager) =>
@angelsShare.goalManager = goalManager
setWorldClassMap: (worldClassMap) =>
@angelsShare.worldClassMap = worldClassMap
getUserCodeMap: (spells) ->
userCodeMap = {}
for spellKey, spell of spells
for thangID, spellThang of spell.thangs
(userCodeMap[thangID] ?= {})[spell.name] = spellThang.aether.serialize()
#console.log userCodeMap
userCodeMap
createWorld: (spells) =>
angel.abort() for angel in @angelsShare.busyAngels # We really only ever want one world calculated per God
#console.log "Level: " + @level
@angelsShare.workQueue.push
worldName: @level.name
userCodeMap: @getUserCodeMap(spells)
level: @level
goals: @angelsShare.goalManager?.getGoals()
angel.workIfIdle() for angel in @angelsShare.angels
destroy: =>
console.log "Destroying Buddha"
@createWorld = -> console.log "CreateWorld already gone."
@angelsShare.workQueue.push Angel.cyanide
angel.kill for angel in @angelsShare.busyAngels
Backbone.Mediator.unsubscribe('tome:cast-spells', @onTomeCast, @)
@angelsShare.goalManager?.destroy()
@angelsShare.goalManager = null
@angelsShare = null

View file

@ -18,16 +18,23 @@ module.exports = class God
options ?= {} options ?= {}
@maxAngels = options.maxAngels ? 2 # How many concurrent web workers to use; if set past 8, make up more names @maxAngels = options.maxAngels ? 2 # How many concurrent web workers to use; if set past 8, make up more names
@maxWorkerPoolSize = options.maxWorkerPoolSize ? 2 # ~20MB per idle worker @maxWorkerPoolSize = options.maxWorkerPoolSize ? 2 # ~20MB per idle worker
@workerCode = options.workerCode if options.workerCode?
@angels = [] @angels = []
@firstWorld = true @firstWorld = true
Backbone.Mediator.subscribe 'tome:cast-spells', @onTomeCast, @ Backbone.Mediator.subscribe 'tome:cast-spells', @onTomeCast, @
@retriveValueFromFrame = _.throttle @retrieveValueFromFrame, 1000
Backbone.Mediator.subscribe 'tome:spell-debug-value-request', @retrieveValueFromFrame, @
@fillWorkerPool = _.throttle @fillWorkerPool, 3000, leading: false @fillWorkerPool = _.throttle @fillWorkerPool, 3000, leading: false
@fillWorkerPool() @fillWorkerPool()
#TODO: have this as a constructor option
@debugWorker = @createDebugWorker()
@currentUserCodeMap = {}
workerCode: '/javascripts/workers/worker_world.js' #Can be a string or a function.
onTomeCast: (e) -> onTomeCast: (e) ->
return if @dead return if @dead
@spells = e.spells @createWorld e.spells
@createWorld()
fillWorkerPool: => fillWorkerPool: =>
return unless Worker and not @dead return unless Worker and not @dead
@ -44,17 +51,40 @@ module.exports = class God
@createWorker() @createWorker()
createWorker: -> createWorker: ->
worker = new Worker '/javascripts/workers/worker_world.js' worker = new Worker @workerCode
worker.creationTime = new Date() worker.creationTime = new Date()
worker.addEventListener 'message', @onWorkerMessage worker.addEventListener 'message', @onWorkerMessage(worker)
worker worker
onWorkerMessage: (event) => createDebugWorker: ->
worker = new Worker '/javascripts/workers/worker_world.js'
worker.creationTime = new Date()
worker.addEventListener 'message', @onDebugWorkerMessage
worker
onWorkerMessage: (worker) =>
unless worker.onMessage?
worker.onMessage = (event) =>
if event.data.type is 'worker-initialized'
console.log @id, "worker initialized after", ((new Date()) - worker.creationTime), "ms (before it was needed)"
worker.initialized = true
worker.removeEventListener 'message', worker.onMessage
else
console.warn "Received strange word from God: #{event.data.type}"
worker.onMessage
onDebugWorkerMessage: (event) =>
worker = event.target worker = event.target
if event.data.type is 'worker-initialized' switch event.data.type
#console.log @id, "worker initialized after", ((new Date()) - worker.creationTime), "ms (before it was needed)" when "worker-initialized"
worker.initialized = true worker.initialized = true
worker.removeEventListener 'message', @onWorkerMessage when 'new-debug-world'
console.log "New Debug world!"
when 'console-log'
console.log "|" + @id + "'s debugger|", event.data.args...
when 'debug-value-return'
Backbone.Mediator.publish 'god:debug-value-return', event.data.serialized
getAngel: -> getAngel: ->
freeAngel = null freeAngel = null
@ -83,10 +113,10 @@ module.exports = class God
angelUserCodeProblem: (angel, problem) -> angelUserCodeProblem: (angel, problem) ->
return if @dead return if @dead
#console.log "UserCodeProblem:", '"' + problem.message + '"', "for", problem.userInfo.thangID, "-", problem.userInfo.methodName, 'at line', problem.ranges?[0][0][0], 'column', problem.ranges?[0][0][1] #console.log "UserCodeProblem:", '"' + problem.message + '"', "for", problem.userInfo.thangID, "-", problem.userInfo.methodName, 'at', problem.range?[0]
Backbone.Mediator.publish 'god:user-code-problem', problem: problem Backbone.Mediator.publish 'god:user-code-problem', problem: problem
createWorld: -> createWorld: (@spells) ->
#console.log @id + ': "Let there be light upon', @world.name + '!"' #console.log @id + ': "Let there be light upon', @world.name + '!"'
unless Worker? # profiling world simulation is easier on main thread, or we are IE9 unless Worker? # profiling world simulation is easier on main thread, or we are IE9
setTimeout @simulateWorld, 1 setTimeout @simulateWorld, 1
@ -101,26 +131,59 @@ module.exports = class God
#console.log "going to run world with code", @getUserCodeMap() #console.log "going to run world with code", @getUserCodeMap()
angel.worker.postMessage {func: 'runWorld', args: { angel.worker.postMessage {func: 'runWorld', args: {
worldName: @level.name worldName: @level.name
userCodeMap: @getUserCodeMap() userCodeMap: @getUserCodeMap(spells)
level: @level level: @level
firstWorld: @firstWorld firstWorld: @firstWorld
goals: @goalManager?.getGoals() goals: @goalManager?.getGoals()
}} }}
retrieveValueFromFrame: (args) ->
if not args.thangID or not args.spellID or not args.variableChain then return
args.frame ?= @world.age / @world.dt
@debugWorker.postMessage
func: 'retrieveValueFromFrame'
args:
worldName: @level.name
userCodeMap: @currentUserCodeMap
level: @level
goals: @goalManager?.getGoals()
frame: args.frame
currentThangID: args.thangID
currentSpellID: args.spellID
variableChain: args.variableChain
#Coffeescript needs getters and setters.
setGoalManager: (@goalManager) =>
setWorldClassMap: (@worldClassMap) =>
beholdWorld: (angel, serialized, goalStates) -> beholdWorld: (angel, serialized, goalStates) ->
unless serialized
# We're only interested in goalStates.
@latestGoalStates = goalStates
Backbone.Mediator.publish('god:goals-calculated', goalStates: goalStates, team: me.team)
unless _.find @angels, 'busy'
@spells = null # Don't hold onto old spells; memory leaks
return
console.log "Beholding world."
worldCreation = angel.started worldCreation = angel.started
angel.free() angel.free()
return if @latestWorldCreation? and worldCreation < @latestWorldCreation return if @latestWorldCreation? and worldCreation < @latestWorldCreation
@latestWorldCreation = worldCreation @latestWorldCreation = worldCreation
@latestGoalStates = goalStates @latestGoalStates = goalStates
console.warn "Goal states: " + JSON.stringify(goalStates)
window.BOX2D_ENABLED = false # Flip this off 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 # Flip this off so that if we have box2d in the namespace, the Collides Components still don't try to create bodies for deserialized Thangs upon attachment
World.deserialize serialized, @worldClassMap, @lastSerializedWorldFrames, worldCreation, @finishBeholdingWorld World.deserialize serialized, @worldClassMap, @lastSerializedWorldFrames, @finishBeholdingWorld
window.BOX2D_ENABLED = true window.BOX2D_ENABLED = true
@lastSerializedWorldFrames = serialized.frames @lastSerializedWorldFrames = serialized.frames
finishBeholdingWorld: (newWorld) => finishBeholdingWorld: (newWorld) =>
newWorld.findFirstChangedFrame @world newWorld.findFirstChangedFrame @world
@world = newWorld @world = newWorld
@currentUserCodeMap = @filterUserCodeMapWhenFromWorld @world.userCodeMap
errorCount = (t for t in @world.thangs when t.errorsOut).length errorCount = (t for t in @world.thangs when t.errorsOut).length
Backbone.Mediator.publish('god:new-world-created', world: @world, firstWorld: @firstWorld, errorCount: errorCount, goalStates: @latestGoalStates, team: me.team) Backbone.Mediator.publish('god:new-world-created', world: @world, firstWorld: @firstWorld, errorCount: errorCount, goalStates: @latestGoalStates, team: me.team)
for scriptNote in @world.scriptNotes for scriptNote in @world.scriptNotes
@ -131,6 +194,23 @@ module.exports = class God
unless _.find @angels, 'busy' unless _.find @angels, 'busy'
@spells = null # Don't hold onto old spells; memory leaks @spells = null # Don't hold onto old spells; memory leaks
filterUserCodeMapWhenFromWorld: (worldUserCodeMap) ->
newUserCodeMap = {}
for thangName, thang of worldUserCodeMap
newUserCodeMap[thangName] = {}
for spellName,aether of thang
shallowFilteredObject = _.pick aether, ['raw','pure','originalOptions']
newUserCodeMap[thangName][spellName] = _.cloneDeep shallowFilteredObject
newUserCodeMap[thangName][spellName] = _.defaults newUserCodeMap[thangName][spellName],
flow: {}
metrics: {}
problems:
errors: []
infos: []
warnings: []
style: {}
newUserCodeMap
getUserCodeMap: -> getUserCodeMap: ->
userCodeMap = {} userCodeMap = {}
for spellKey, spell of @spells for spellKey, spell of @spells
@ -144,6 +224,10 @@ module.exports = class God
@dead = true @dead = true
Backbone.Mediator.unsubscribe('tome:cast-spells', @onTomeCast, @) Backbone.Mediator.unsubscribe('tome:cast-spells', @onTomeCast, @)
@goalManager?.destroy() @goalManager?.destroy()
@debugWorker?.terminate()
@debugWorker?.removeEventListener 'message', @onDebugWorkerMessage
@debugWorker ?= null
@currentUserCodeMap = null
@goalManager = null @goalManager = null
@fillWorkerPool = null @fillWorkerPool = null
@simulateWorld = null @simulateWorld = null
@ -171,7 +255,7 @@ module.exports = class God
@latestGoalStates = @testGM?.getGoalStates() @latestGoalStates = @testGM?.getGoalStates()
serialized = @testWorld.serialize().serializedWorld serialized = @testWorld.serialize().serializedWorld
window.BOX2D_ENABLED = false window.BOX2D_ENABLED = false
World.deserialize serialized, @worldClassMap, @lastSerializedWorldFrames, @t0, @finishBeholdingWorld World.deserialize serialized, @worldClassMap, @lastSerializedWorldFrames, @finishBeholdingWorld
window.BOX2D_ENABLED = true window.BOX2D_ENABLED = true
@lastSerializedWorldFrames = serialized.frames @lastSerializedWorldFrames = serialized.frames
@ -255,7 +339,7 @@ class Angel
testWorker: => testWorker: =>
unless @worker.initialized unless @worker.initialized
console.warn "Worker", @id, "hadn't even loaded the scripts yet after", @infiniteLoopIntervalDuration, "ms." console.warn "Worker", @id, " hadn't even loaded the scripts yet after", @infiniteLoopIntervalDuration, "ms."
return return
@worker.postMessage {func: 'reportIn'} @worker.postMessage {func: 'reportIn'}
@condemnTimeout = _.delay @condemnWorker, @infiniteLoopTimeoutDuration @condemnTimeout = _.delay @condemnWorker, @infiniteLoopTimeoutDuration
@ -271,6 +355,7 @@ class Angel
switch event.data.type switch event.data.type
when 'worker-initialized' when 'worker-initialized'
console.log "Worker", @id, "initialized after", ((new Date()) - @worker.creationTime), "ms (we had been waiting for it)" console.log "Worker", @id, "initialized after", ((new Date()) - @worker.creationTime), "ms (we had been waiting for it)"
@worker.initialized = true
when 'new-world' when 'new-world'
@god.beholdWorld @, event.data.serialized, event.data.goalStates @god.beholdWorld @, event.data.serialized, event.data.goalStates
when 'world-load-progress-changed' when 'world-load-progress-changed'

View file

@ -59,13 +59,13 @@ module.exports = class LevelLoader extends CocoClass
url = "/db/level/#{@levelID}/session" url = "/db/level/#{@levelID}/session"
url += "?team=#{@team}" if @team url += "?team=#{@team}" if @team
@session = new LevelSession().setURL url session = new LevelSession().setURL url
@supermodel.loadModel(@session, 'level_session', {cache:false}) @session = @supermodel.loadModel(session, 'level_session', {cache:false}).model
@session.once 'sync', -> @url = -> '/db/level.session/' + @id @session.once 'sync', -> @url = -> '/db/level.session/' + @id
if @opponentSessionID if @opponentSessionID
@opponentSession = new LevelSession().setURL "/db/level_session/#{@opponentSessionID}" opponentSession = new LevelSession().setURL "/db/level_session/#{@opponentSessionID}"
@supermodel.loadModel(@opponentSession, 'opponent_session') @opponentSession = @supermodel.loadModel(opponentSession, 'opponent_session').model
# Supermodel (Level) Loading # Supermodel (Level) Loading
@ -155,6 +155,7 @@ module.exports = class LevelLoader extends CocoClass
# Building sprite sheets # Building sprite sheets
buildSpriteSheetsForThangType: (thangType) -> buildSpriteSheetsForThangType: (thangType) ->
return if @headless
@grabThangTypeTeams() unless @thangTypeTeams @grabThangTypeTeams() unless @thangTypeTeams
for team in @thangTypeTeams[thangType.get('original')] ? [null] for team in @thangTypeTeams[thangType.get('original')] ? [null]
spriteOptions = {resolutionFactor: 4, async: false} spriteOptions = {resolutionFactor: 4, async: false}

View file

@ -16,7 +16,7 @@ init = ->
module.exports.createUser = (userObject, failure=backboneFailure, nextURL=null) -> module.exports.createUser = (userObject, failure=backboneFailure, nextURL=null) ->
user = new User(userObject) user = new User(userObject)
user.save({}, { user.save({}, {
error: (model,jqxhr,options) -> error: (model,jqxhr,options) ->
error = parseServerError(jqxhr.responseText) error = parseServerError(jqxhr.responseText)
property = error.property if error.property property = error.property if error.property
if jqxhr.status is 409 and property is 'name' if jqxhr.status is 409 and property is 'name'
@ -26,12 +26,12 @@ module.exports.createUser = (userObject, failure=backboneFailure, nextURL=null)
genericFailure(jqxhr) genericFailure(jqxhr)
success: -> if nextURL then window.location.href = nextURL else window.location.reload() success: -> if nextURL then window.location.href = nextURL else window.location.reload()
}) })
module.exports.createUserWithoutReload = (userObject, failure=backboneFailure) -> module.exports.createUserWithoutReload = (userObject, failure=backboneFailure) ->
user = new User(userObject) user = new User(userObject)
user.save({}, { user.save({}, {
error: failure error: failure
success: -> success: ->
Backbone.Mediator.publish("created-user-without-reload") Backbone.Mediator.publish("created-user-without-reload")
}) })

View file

@ -2,15 +2,21 @@ SuperModel = require 'models/SuperModel'
CocoClass = require 'lib/CocoClass' CocoClass = require 'lib/CocoClass'
LevelLoader = require 'lib/LevelLoader' LevelLoader = require 'lib/LevelLoader'
GoalManager = require 'lib/world/GoalManager' GoalManager = require 'lib/world/GoalManager'
God = require 'lib/God' God = require 'lib/Buddha'
Aether.addGlobal 'Vector', require 'lib/world/vector'
Aether.addGlobal '_', _
module.exports = class Simulator extends CocoClass module.exports = class Simulator extends CocoClass
constructor: -> constructor: (@options) ->
@options ?= {}
_.extend @, Backbone.Events _.extend @, Backbone.Events
@trigger 'statusUpdate', 'Starting simulation!' @trigger 'statusUpdate', 'Starting simulation!'
@retryDelayInSeconds = 10 @retryDelayInSeconds = 10
@taskURL = '/queue/scoring' @taskURL = '/queue/scoring'
@simulatedByYou = 0
@god = new God maxWorkerPoolSize: 1, maxAngels: 1, workerCode: @options.workerCode # Start loading worker.
destroy: -> destroy: ->
@off() @off()
@ -19,6 +25,17 @@ module.exports = class Simulator extends CocoClass
fetchAndSimulateTask: => fetchAndSimulateTask: =>
return if @destroyed return if @destroyed
if @options.headlessClient
if @dumpThisTime # The first heapdump would be useless to find leaks.
console.log "Writing snapshot."
@options.heapdump.writeSnapshot()
@dumpThisTime = true if @options.heapdump
if @options.testing
_.delay @setupSimulationAndLoadLevel, 0, @options.testFile, "Testing...", status: 400
return
@trigger 'statusUpdate', 'Fetching simulation data!' @trigger 'statusUpdate', 'Fetching simulation data!'
$.ajax $.ajax
url: @taskURL url: @taskURL
@ -32,7 +49,9 @@ module.exports = class Simulator extends CocoClass
@simulateAnotherTaskAfterDelay() @simulateAnotherTaskAfterDelay()
handleNoGamesResponse: -> handleNoGamesResponse: ->
@trigger 'statusUpdate', 'There were no games to simulate--all simulations are done or in process. Retrying in 10 seconds.' info = 'There were no games to simulate--all simulations are done or in process. Retrying in 10 seconds.'
console.log info
@trigger 'statusUpdate', info
@simulateAnotherTaskAfterDelay() @simulateAnotherTaskAfterDelay()
simulateAnotherTaskAfterDelay: => simulateAnotherTaskAfterDelay: =>
@ -53,7 +72,6 @@ module.exports = class Simulator extends CocoClass
return return
@supermodel ?= new SuperModel() @supermodel ?= new SuperModel()
@god = new God maxWorkerPoolSize: 1, maxAngels: 1 # Start loading worker.
@levelLoader = new LevelLoader supermodel: @supermodel, levelID: levelID, sessionID: @task.getFirstSessionID(), headless: true @levelLoader = new LevelLoader supermodel: @supermodel, levelID: levelID, sessionID: @task.getFirstSessionID(), headless: true
if @supermodel.finished() if @supermodel.finished()
@ -63,7 +81,9 @@ module.exports = class Simulator extends CocoClass
simulateGame: -> simulateGame: ->
return if @destroyed return if @destroyed
@trigger 'statusUpdate', 'All resources loaded, simulating!', @task.getSessions() info = 'All resources loaded, simulating!'
console.log info
@trigger 'statusUpdate', info, @task.getSessions()
@assignWorldAndLevelFromLevelLoaderAndDestroyIt() @assignWorldAndLevelFromLevelLoaderAndDestroyIt()
@setupGod() @setupGod()
@ -74,6 +94,7 @@ module.exports = class Simulator extends CocoClass
@simulateAnotherTaskAfterDelay() @simulateAnotherTaskAfterDelay()
assignWorldAndLevelFromLevelLoaderAndDestroyIt: -> assignWorldAndLevelFromLevelLoaderAndDestroyIt: ->
console.log "Assigning world and level"
@world = @levelLoader.world @world = @levelLoader.world
@level = @levelLoader.level @level = @levelLoader.level
@levelLoader.destroy() @levelLoader.destroy()
@ -81,18 +102,45 @@ module.exports = class Simulator extends CocoClass
setupGod: -> setupGod: ->
@god.level = @level.serialize @supermodel @god.level = @level.serialize @supermodel
@god.worldClassMap = @world.classMap @god.setWorldClassMap @world.classMap
@setupGoalManager() @setupGoalManager()
@setupGodSpells() @setupGodSpells()
setupGoalManager: -> setupGoalManager: ->
@god.goalManager = new GoalManager @world, @level.get 'goals' @god.setGoalManager new GoalManager(@world, @level.get 'goals')
commenceSimulationAndSetupCallback: -> commenceSimulationAndSetupCallback: ->
@god.createWorld() @god.createWorld @generateSpellsObject()
Backbone.Mediator.subscribeOnce 'god:infinite-loop', @onInfiniteLoop, @ Backbone.Mediator.subscribeOnce 'god:infinite-loop', @onInfiniteLoop, @
Backbone.Mediator.subscribeOnce 'god:new-world-created', @processResults, @ Backbone.Mediator.subscribeOnce 'god:new-world-created', @processResults, @
#Search for leaks, headless-client only.
if @options.headlessClient and @options.leakTest and not @memwatch?
leakcount = 0
maxleakcount = 0
console.log "Setting leak callbacks."
@memwatch = require 'memwatch'
@memwatch.on 'leak', (info) =>
console.warn "LEAK!!\n" + JSON.stringify(info)
unless @hd?
if (leakcount++ is maxleakcount)
@hd = new @memwatch.HeapDiff()
@memwatch.on 'stats', (stats) =>
console.warn "stats callback: " + stats
diff = @hd.end()
console.warn "HeapDiff:\n" + JSON.stringify(diff)
if @options.exitOnLeak
console.warn "Exiting because of Leak."
process.exit()
@hd = new @memwatch.HeapDiff()
onInfiniteLoop: -> onInfiniteLoop: ->
console.warn "Skipping infinitely looping game." console.warn "Skipping infinitely looping game."
@trigger 'statusUpdate', "Infinite loop detected; grabbing a new game in #{@retryDelayInSeconds} seconds." @trigger 'statusUpdate', "Infinite loop detected; grabbing a new game in #{@retryDelayInSeconds} seconds."
@ -106,6 +154,9 @@ module.exports = class Simulator extends CocoClass
@trigger 'statusUpdate', 'Simulation completed, sending results back to server!' @trigger 'statusUpdate', 'Simulation completed, sending results back to server!'
console.log "Sending result back to server!" console.log "Sending result back to server!"
if @options.headlessClient and @options.testing
return @fetchAndSimulateTask()
$.ajax $.ajax
url: "/queue/scoring" url: "/queue/scoring"
data: results data: results
@ -117,8 +168,11 @@ module.exports = class Simulator extends CocoClass
handleTaskResultsTransferSuccess: (result) => handleTaskResultsTransferSuccess: (result) =>
console.log "Task registration result: #{JSON.stringify result}" console.log "Task registration result: #{JSON.stringify result}"
@trigger 'statusUpdate', 'Results were successfully sent back to server!' @trigger 'statusUpdate', 'Results were successfully sent back to server!'
simulatedBy = parseInt($('#simulated-by-you').text(), 10) + 1 console.log "Simulated by you: " + @simulatedByYou
$('#simulated-by-you').text(simulatedBy) @simulatedByYou++
unless @options.headlessClient
simulatedBy = parseInt($('#simulated-by-you').text(), 10) + 1
$('#simulated-by-you').text(simulatedBy)
handleTaskResultsTransferError: (error) => handleTaskResultsTransferError: (error) =>
@trigger 'statusUpdate', 'There was an error sending the results back to the server.' @trigger 'statusUpdate', 'There was an error sending the results back to the server.'
@ -144,7 +198,6 @@ module.exports = class Simulator extends CocoClass
sessions: [] sessions: []
for session in @task.getSessions() for session in @task.getSessions()
sessionResult = sessionResult =
sessionID: session.sessionID sessionID: session.sessionID
submitDate: session.submitDate submitDate: session.submitDate
@ -242,8 +295,8 @@ module.exports = class Simulator extends CocoClass
functionName: methodName functionName: methodName
protectAPI: useProtectAPI protectAPI: useProtectAPI
includeFlow: false includeFlow: false
requiresThis: true
yieldConditionally: false yieldConditionally: false
globals: ['Vector', '_']
problems: problems:
jshint_W040: {level: "ignore"} jshint_W040: {level: "ignore"}
jshint_W030: {level: "ignore"} # aether_NoEffect instead jshint_W030: {level: "ignore"} # aether_NoEffect instead

View file

@ -379,7 +379,9 @@ module.exports = class SpriteParser
argsSource = argsSource.replace(/cjs(.+)\)/, '"createjs$1)"') # turns cjs.Ease.get(0.5) argsSource = argsSource.replace(/cjs(.+)\)/, '"createjs$1)"') # turns cjs.Ease.get(0.5)
args = eval "[#{argsSource}]" args = eval "[#{argsSource}]"
if args[0]?.state?[0]?.t?.search?("shape") is 0 and not _.find(localShapes, bn: args[0].state[0].t) shadowTween = args[0]?.search?('shape') is 0 and not _.find(localShapes, bn: args[0])
shadowTween = shadowTween or args[0]?.state?[0]?.t?.search?("shape") is 0 and not _.find(localShapes, bn: args[0].state[0].t)
if shadowTween
console.log "Skipping tween", name, argsSource, args, "from localShapes", localShapes, "presumably because it's a shadow we skipped." console.log "Skipping tween", name, argsSource, args, "from localShapes", localShapes, "presumably because it's a shadow we skipped."
return return
callExpressions.push {n: name, a: args} callExpressions.push {n: name, a: args}

View file

@ -168,10 +168,12 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
stop: -> stop: ->
@imageObject?.stop?() @imageObject?.stop?()
mark.stop() for name, mark of @marks mark.stop() for name, mark of @marks
@stopped = true
play: -> play: ->
@imageObject?.play?() @imageObject?.play?()
mark.play() for name, mark of @marks mark.play() for name, mark of @marks
@stopped = false
update: (frameChanged) -> update: (frameChanged) ->
# Gets the sprite to reflect what the current state of the thangs and surface are # Gets the sprite to reflect what the current state of the thangs and surface are
@ -222,7 +224,8 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
getBobOffset: -> getBobOffset: ->
return 0 unless @thang.bobHeight return 0 unless @thang.bobHeight
@thang.bobHeight * (1 + Math.sin(@age * Math.PI / @thang.bobTime)) return @lastBobOffset if @stopped
return @lastBobOffset = @thang.bobHeight * (1 + Math.sin(@age * Math.PI / @thang.bobTime))
getWorldPosition: -> getWorldPosition: ->
p1 = if @possessed then @shadow.pos else @thang.pos p1 = if @possessed then @shadow.pos else @thang.pos
@ -495,6 +498,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
updateEffectMarks: -> updateEffectMarks: ->
return if _.isEqual @thang.effectNames, @previousEffectNames return if _.isEqual @thang.effectNames, @previousEffectNames
return if @stopped
for effect in @thang.effectNames for effect in @thang.effectNames
mark = @addMark effect, @options.floatingLayer, effect mark = @addMark effect, @options.floatingLayer, effect
mark.statusEffect = true mark.statusEffect = true

View file

@ -246,6 +246,7 @@ module.exports = class Mark extends CocoClass
size = @sprite.getAverageDimension() size = @sprite.getAverageDimension()
size += 60 if @name is 'selection' size += 60 if @name is 'selection'
size += 60 if @name is 'repair' size += 60 if @name is 'repair'
size *= @sprite.scaleFactor
scale = size / {selection: 128, target: 128, repair: 320, highlight: 160}[@name] scale = size / {selection: 128, target: 128, repair: 320, highlight: 160}[@name]
if @sprite?.thang.spriteName.search(/(dungeon|indoor).wall/i) isnt -1 if @sprite?.thang.spriteName.search(/(dungeon|indoor).wall/i) isnt -1
scale *= 2 scale *= 2

View file

@ -221,12 +221,12 @@ module.exports = class SpriteBoss extends CocoClass
onCastSpells: -> @stop() onCastSpells: -> @stop()
play: -> play: ->
sprite.imageObject.play() for sprite in @spriteArray sprite.play() for sprite in @spriteArray
@selectionMark?.play() @selectionMark?.play()
@targetMark?.play() @targetMark?.play()
stop: -> stop: ->
sprite.imageObject.stop() for sprite in @spriteArray sprite.stop() for sprite in @spriteArray
@selectionMark?.stop() @selectionMark?.stop()
@targetMark?.stop() @targetMark?.stop()

View file

@ -65,6 +65,7 @@ module.exports = Surface = class Surface extends CocoClass
'god:new-world-created': 'onNewWorld' 'god:new-world-created': 'onNewWorld'
'tome:cast-spells': 'onCastSpells' 'tome:cast-spells': 'onCastSpells'
'level-set-letterbox': 'onSetLetterbox' 'level-set-letterbox': 'onSetLetterbox'
'application:idle-changed': 'onIdleChanged'
shortcuts: shortcuts:
'ctrl+\\, ⌘+\\': 'onToggleDebug' 'ctrl+\\, ⌘+\\': 'onToggleDebug'
@ -304,15 +305,32 @@ module.exports = Surface = class Surface extends CocoClass
@spriteBoss.stop() @spriteBoss.stop()
@playbackOverScreen.show() @playbackOverScreen.show()
@ended = true @ended = true
@setPaused true
Backbone.Mediator.publish 'surface:playback-ended' Backbone.Mediator.publish 'surface:playback-ended'
else if @currentFrame < @world.totalFrames and @ended else if @currentFrame < @world.totalFrames and @ended
@spriteBoss.play() @spriteBoss.play()
@playbackOverScreen.hide() @playbackOverScreen.hide()
@ended = false @ended = false
@setPaused false
Backbone.Mediator.publish 'surface:playback-restarted' Backbone.Mediator.publish 'surface:playback-restarted'
@lastFrame = @currentFrame @lastFrame = @currentFrame
onIdleChanged: (e) ->
@setPaused e.idle unless @ended
setPaused: (to) ->
# We want to be able to essentially stop rendering the surface if it doesn't need to animate anything.
# If pausing, though, we want to give it enough time to finish any tweens.
performToggle = =>
createjs.Ticker.setFPS if to then 1 else @options.frameRate
@surfacePauseInterval = null
clearTimeout @surfacePauseInterval if @surfacePauseInterval
if to
@surfacePauseInterval = _.delay performToggle, 2000
else
performToggle()
onCastSpells: -> onCastSpells: ->
@casting = true @casting = true
@wasPlayingWhenCastingBegan = @playing @wasPlayingWhenCastingBegan = @playing
@ -575,8 +593,6 @@ module.exports = Surface = class Surface extends CocoClass
@paths.parent.removeChild @paths @paths.parent.removeChild @paths
@paths = null @paths = null
# Screenshot
screenshot: (scale=0.25, format='image/jpeg', quality=0.8, zoom=2) -> screenshot: (scale=0.25, format='image/jpeg', quality=0.8, zoom=2) ->
# Quality doesn't work with image/png, just image/jpeg and image/webp # Quality doesn't work with image/png, just image/jpeg and image/webp
[w, h] = [@camera.canvasWidth, @camera.canvasHeight] [w, h] = [@camera.canvasWidth, @camera.canvasHeight]
@ -586,6 +602,5 @@ module.exports = Surface = class Surface extends CocoClass
#console.log "Screenshot with scale", scale, "format", format, "quality", quality, "was", Math.floor(imageData.length / 1024), "kB" #console.log "Screenshot with scale", scale, "format", format, "quality", quality, "was", Math.floor(imageData.length / 1024), "kB"
screenshot = document.createElement("img") screenshot = document.createElement("img")
screenshot.src = imageData screenshot.src = imageData
#$('body').append(screenshot)
@stage.uncache() @stage.uncache()
imageData imageData

View file

@ -204,7 +204,7 @@ module.exports = class GoalManager extends CocoClass
arrays = (prop for prop in whos when prop?.length) arrays = (prop for prop in whos when prop?.length)
return unless arrays.length return unless arrays.length
state[progressObjectName] = {} state[progressObjectName] ?= {}
for array in arrays for array in arrays
for thang in array for thang in array
if @thangTeams[thang]? if @thangTeams[thang]?
@ -235,7 +235,7 @@ module.exports = class GoalManager extends CocoClass
numNeeded = goal.howMany ? Math.max(1, _.size stateThangs) numNeeded = goal.howMany ? Math.max(1, _.size stateThangs)
else else
# saveThangs: by default we would want to save all the Thangs, which means that we would want none of them to be "done" # saveThangs: by default we would want to save all the Thangs, which means that we would want none of them to be "done"
numNeeded = _.size(stateThangs) - Math.min((goal.howMany ? 1), _.size stateThangs) + 1 numNeeded = _.size(stateThangs) - Math.max((goal.howMany ? 1), _.size stateThangs) + 1
numDone = _.filter(stateThangs).length numDone = _.filter(stateThangs).length
console.log "needed", numNeeded, "done", numDone, "of total", _.size(stateThangs), "with how many", goal.howMany, "and stateThangs", stateThangs console.log "needed", numNeeded, "done", numDone, "of total", _.size(stateThangs), "with how many", goal.howMany, "and stateThangs", stateThangs
return unless numDone >= numNeeded return unless numDone >= numNeeded

View file

@ -29,8 +29,6 @@ module.exports.thangNames = thangNames =
"Phineas" "Phineas"
"Ferb" "Ferb"
"Felix" "Felix"
"Arthur"
"Galahad"
"Ezra" "Ezra"
"Lucian" "Lucian"
"Augustus" "Augustus"
@ -174,6 +172,7 @@ module.exports.thangNames = thangNames =
"Snortt" "Snortt"
"Kog" "Kog"
"Ursa" "Ursa"
"Ragtime"
] ]
"Ogre Munchkin F": [ "Ogre Munchkin F": [
"Iyert" "Iyert"
@ -184,6 +183,7 @@ module.exports.thangNames = thangNames =
"Dosha" "Dosha"
"Inski" "Inski"
"Lacos" "Lacos"
"Upfish"
] ]
"Ogre M": [ "Ogre M": [
"Krogg" "Krogg"
@ -202,6 +202,7 @@ module.exports.thangNames = thangNames =
"Mokrul" "Mokrul"
"Polifemo" "Polifemo"
"Muthyala" "Muthyala"
"Saltporker"
] ]
"Ogre F": [ "Ogre F": [
"Nareng" "Nareng"
@ -231,6 +232,8 @@ module.exports.thangNames = thangNames =
"Tuguro" "Tuguro"
"York" "York"
"Ork'han" "Ork'han"
"Roast Beefy"
"Haggar"
] ]
"Ogre Fangrider": [ "Ogre Fangrider": [
"Dreek" "Dreek"
@ -272,6 +275,7 @@ module.exports.thangNames = thangNames =
"Gogg" "Gogg"
"Ghuk" "Ghuk"
"Makas" "Makas"
"Drun"
] ]
"Ogre Thrower": [ "Ogre Thrower": [
"Kyrgg" "Kyrgg"
@ -293,10 +297,12 @@ module.exports.thangNames = thangNames =
"Burl": [ "Burl": [
"Borlit" "Borlit"
"Burlosh" "Burlosh"
"Dorf"
] ]
"Griffin Rider": [ "Griffin Rider": [
"Aeoldan" "Aeoldan"
"Bestarius" "Bestarius"
] ]
"Potion Master": [ "Potion Master": [
"Snake" "Snake"
@ -305,6 +311,7 @@ module.exports.thangNames = thangNames =
"Arora" "Arora"
"Curie" "Curie"
"Clause" "Clause"
"Vanders"
] ]
"Librarian": [ "Librarian": [
"Hushbaum" "Hushbaum"
@ -318,4 +325,30 @@ module.exports.thangNames = thangNames =
"Ryder" "Ryder"
"Thoron" "Thoron"
"Mirial" "Mirial"
"Neely"
]
"Knight": [
"Tharin"
"Arthur"
"Galahad"
"Mace"
"Drake"
"Duran"
"Almeric"
"Hunfray"
"Hank"
"Jeph"
"Neville"
]
"Captain": [
"Anya"
"Brigette"
"Sarre"
"Katana"
"Lily"
"Isa"
"Dimia"
"Jane"
"Lia"
"Hardcastle"
] ]

View file

@ -32,7 +32,7 @@ module.exports.CollisionCategory = class CollisionCategory
# "ground_and_air_<team>" units don't hit ground or air units on their team (so missiles don't hit same team) # "ground_and_air_<team>" units don't hit ground or air units on their team (so missiles don't hit same team)
sameTeam = @superteamIndex and cat.superteamIndex is @superteamIndex sameTeam = @superteamIndex and cat.superteamIndex is @superteamIndex
return false if sameTeam and @ground and @air return false if sameTeam and @ground and @air
# actually, "ground_and_air<team>" units don't hit any ground_and_air units (temp missile collision fix) # actually, "ground_and_air<team>" units don't hit any ground_and_air units (temp missile collision fix)
return false if @ground and @air and cat.ground and cat.air return false if @ground and @air and cat.ground and cat.air

View file

@ -8,7 +8,6 @@ WorldScriptNote = require './world_script_note'
{now, consolidateThangs, typedArraySupport} = require './world_utils' {now, consolidateThangs, typedArraySupport} = require './world_utils'
Component = require 'lib/world/component' Component = require 'lib/world/component'
System = require 'lib/world/system' System = require 'lib/world/system'
PROGRESS_UPDATE_INTERVAL = 200 PROGRESS_UPDATE_INTERVAL = 200
DESERIALIZATION_INTERVAL = 20 DESERIALIZATION_INTERVAL = 20
@ -72,14 +71,18 @@ module.exports = class World
(@runtimeErrors ?= []).push error (@runtimeErrors ?= []).push error
(@unhandledRuntimeErrors ?= []).push error (@unhandledRuntimeErrors ?= []).push error
loadFrames: (loadedCallback, errorCallback, loadProgressCallback) -> loadFrames: (loadedCallback, errorCallback, loadProgressCallback, skipDeferredLoading, loadUntilFrame) ->
return if @aborted return if @aborted
unless @thangs.length unless @thangs.length
console.log "Warning: loadFrames called on empty World (no thangs)." console.log "Warning: loadFrames called on empty World (no thangs)."
t1 = now() t1 = now()
@t0 ?= t1 @t0 ?= t1
if loadUntilFrame
frameToLoadUntil = loadUntilFrame + 1
else
frameToLoadUntil = @totalFrames
i = @frames.length i = @frames.length
while i < @totalFrames while i < frameToLoadUntil
try try
@getFrame(i) @getFrame(i)
++i # increment this after we have succeeded in getting the frame, otherwise we'll have to do that frame again ++i # increment this after we have succeeded in getting the frame, otherwise we'll have to do that frame again
@ -96,10 +99,19 @@ module.exports = class World
if t2 - @t0 > 1000 if t2 - @t0 > 1000
console.log(' Loaded', i, 'of', @totalFrames, "(+" + (t2 - @t0).toFixed(0) + "ms)") console.log(' Loaded', i, 'of', @totalFrames, "(+" + (t2 - @t0).toFixed(0) + "ms)")
@t0 = t2 @t0 = t2
setTimeout((=> @loadFrames(loadedCallback, errorCallback, loadProgressCallback)), 0) continueFn = =>
if loadUntilFrame
@loadFrames(loadedCallback,errorCallback,loadProgressCallback, skipDeferredLoading, loadUntilFrame)
else
@loadFrames(loadedCallback, errorCallback, loadProgressCallback, skipDeferredLoading)
if skipDeferredLoading
continueFn()
else
setTimeout(continueFn, 0)
return return
@ended = true unless loadUntilFrame
system.finish @thangs for system in @systems @ended = true
system.finish @thangs for system in @systems
loadProgressCallback? 1 loadProgressCallback? 1
loadedCallback() loadedCallback()
@ -221,7 +233,7 @@ module.exports = class World
@scriptNotes.push scriptNote @scriptNotes.push scriptNote
return unless @goalManager return unless @goalManager
@goalManager.submitWorldGenerationEvent(channel, event, @frames.length) @goalManager.submitWorldGenerationEvent(channel, event, @frames.length)
setGoalState: (goalID, status) -> setGoalState: (goalID, status) ->
@goalManager.setGoalState(goalID, status) @goalManager.setGoalState(goalID, status)
@ -336,7 +348,7 @@ module.exports = class World
console.log "Whoa, serializing a lot of WorldScriptNotes here:", o.scriptNotes.length console.log "Whoa, serializing a lot of WorldScriptNotes here:", o.scriptNotes.length
{serializedWorld: o, transferableObjects: [o.storageBuffer]} {serializedWorld: o, transferableObjects: [o.storageBuffer]}
@deserialize: (o, classMap, oldSerializedWorldFrames, worldCreationTime, finishedWorldCallback) -> @deserialize: (o, classMap, oldSerializedWorldFrames, finishedWorldCallback) ->
# Code hotspot; optimize it # Code hotspot; optimize it
#console.log "Deserializing", o, "length", JSON.stringify(o).length #console.log "Deserializing", o, "length", JSON.stringify(o).length
#console.log JSON.stringify(o) #console.log JSON.stringify(o)

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "български език", englishDescri
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "български език", englishDescri
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr
multiplayer_hint_label: "Tip:" multiplayer_hint_label: "Tip:"
multiplayer_hint: " Klikněte na odkaz pro jeho výběr, poté stiskněte ⌘-C nebo Ctrl-C pro kopírování odkazu." multiplayer_hint: " Klikněte na odkaz pro jeho výběr, poté stiskněte ⌘-C nebo Ctrl-C pro kopírování odkazu."
multiplayer_coming_soon: "Další vlastnosti multiplayeru jsou na cestě!" multiplayer_coming_soon: "Další vlastnosti multiplayeru jsou na cestě!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Průvodce" guide_title: "Průvodce"
tome_minion_spells: "Vaše oblíbená kouzla" tome_minion_spells: "Vaše oblíbená kouzla"
tome_read_only_spells: "Kouzla jen pro čtení" tome_read_only_spells: "Kouzla jen pro čtení"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans
multiplayer_hint_label: "Tip:" multiplayer_hint_label: "Tip:"
multiplayer_hint: " Klik på linket for markere alt; tryk derefter ⌘-C eller Ctrl-C tfr at kopiere linket." multiplayer_hint: " Klik på linket for markere alt; tryk derefter ⌘-C eller Ctrl-C tfr at kopiere linket."
multiplayer_coming_soon: "Yderligere flerspillermuligheder er på vej!" multiplayer_coming_soon: "Yderligere flerspillermuligheder er på vej!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Instruktioner" guide_title: "Instruktioner"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription:
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription:
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
multiplayer_hint_label: "Hinweis:" multiplayer_hint_label: "Hinweis:"
multiplayer_hint: " Klick den Link, um alles auszuwählen, dann drück ⌘-C oder Strg-C um den Link zu kopieren." multiplayer_hint: " Klick den Link, um alles auszuwählen, dann drück ⌘-C oder Strg-C um den Link zu kopieren."
multiplayer_coming_soon: "Mehr Multiplayerfeatures werden kommen!" multiplayer_coming_soon: "Mehr Multiplayerfeatures werden kommen!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Anleitung" guide_title: "Anleitung"
tome_minion_spells: "Die Zaubersprüche Deiner Knechte" tome_minion_spells: "Die Zaubersprüche Deiner Knechte"
tome_read_only_spells: "Nur-lesen Zauberspüche" tome_read_only_spells: "Nur-lesen Zauberspüche"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "Deutsch", englishDescription: "German", tra
multiplayer_hint_label: "Hinweis:" multiplayer_hint_label: "Hinweis:"
multiplayer_hint: " Klick den Link, um alles auszuwählen, dann drück ⌘-C oder Strg-C um den Link zu kopieren." multiplayer_hint: " Klick den Link, um alles auszuwählen, dann drück ⌘-C oder Strg-C um den Link zu kopieren."
multiplayer_coming_soon: "Mehr Multiplayerfeatures werden kommen!" multiplayer_coming_soon: "Mehr Multiplayerfeatures werden kommen!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Anleitung" guide_title: "Anleitung"
tome_minion_spells: "Die Zaubersprüche Deiner Knechte" tome_minion_spells: "Die Zaubersprüche Deiner Knechte"
tome_read_only_spells: "Nur-lesen Zauberspüche" tome_read_only_spells: "Nur-lesen Zauberspüche"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Deutsch", englishDescription: "German", tra
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "ελληνικά", englishDescription: "Gre
multiplayer_hint_label: "Συμβουλή:" multiplayer_hint_label: "Συμβουλή:"
multiplayer_hint: " Κάντε κλικ στο σύνδεσμο για να επιλέξετε όλα, στη συνέχεια, πατήστε την Apple-C ή Ctrl-C για να αντιγράψετε το σύνδεσμο." multiplayer_hint: " Κάντε κλικ στο σύνδεσμο για να επιλέξετε όλα, στη συνέχεια, πατήστε την Apple-C ή Ctrl-C για να αντιγράψετε το σύνδεσμο."
multiplayer_coming_soon: "Περισσότερα multiplayer χαρακτιριστηκα προσεχως!" multiplayer_coming_soon: "Περισσότερα multiplayer χαρακτιριστηκα προσεχως!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Οδηγός" guide_title: "Οδηγός"
tome_minion_spells: "Ξόρκια για τα τσιράκια σας" tome_minion_spells: "Ξόρκια για τα τσιράκια σας"
tome_read_only_spells: "Ξορκια μονο για αναγνωση" tome_read_only_spells: "Ξορκια μονο για αναγνωση"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "ελληνικά", englishDescription: "Gre
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -712,3 +712,14 @@
files: "Files" files: "Files"
top_simulators: "Top Simulators" top_simulators: "Top Simulators"
source_document: "Source Document" source_document: "Source Document"
document: "Document" # note to diplomats: not a physical document, a document in MongoDB, ie a record in a database
delta:
added: "Added"
modified: "Modified"
deleted: "Deleted"
moved_index: "Moved Index"
text_diff: "Text Diff"
merge_conflict_with: "MERGE CONFLICT WITH"
no_changes: "No Changes"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip
multiplayer_hint_label: "Consejo:" multiplayer_hint_label: "Consejo:"
multiplayer_hint: " Cliquea el enlace para seleccionar todo, luego presiona ⌘-C o Ctrl-C para copiar el enlace." multiplayer_hint: " Cliquea el enlace para seleccionar todo, luego presiona ⌘-C o Ctrl-C para copiar el enlace."
multiplayer_coming_soon: "¡Más características de multijugador por venir!" multiplayer_coming_soon: "¡Más características de multijugador por venir!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Guía" guide_title: "Guía"
tome_minion_spells: "Hechizos de tus Secuaces" tome_minion_spells: "Hechizos de tus Secuaces"
tome_read_only_spells: "Hechizos de Sólo Lectura" tome_read_only_spells: "Hechizos de Sólo Lectura"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
multiplayer_hint_label: "Pista:" multiplayer_hint_label: "Pista:"
multiplayer_hint: " Haz un click en el link para que se seleccione, después utiliza Ctrl-C o ⌘-C para copiar el link." multiplayer_hint: " Haz un click en el link para que se seleccione, después utiliza Ctrl-C o ⌘-C para copiar el link."
multiplayer_coming_soon: "¡Más opciones de Multijugador están por venir!" multiplayer_coming_soon: "¡Más opciones de Multijugador están por venir!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Guía" guide_title: "Guía"
tome_minion_spells: "Los hechizos de tus súbditos" tome_minion_spells: "Los hechizos de tus súbditos"
tome_read_only_spells: "Hechizos de solo lectura" tome_read_only_spells: "Hechizos de solo lectura"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "español", englishDescription: "Spanish", t
multiplayer_hint_label: "Consejo:" multiplayer_hint_label: "Consejo:"
multiplayer_hint: " Cliquea el enlace para seleccionar todo, luego presiona ⌘-C o Ctrl-C para copiar el enlace." multiplayer_hint: " Cliquea el enlace para seleccionar todo, luego presiona ⌘-C o Ctrl-C para copiar el enlace."
multiplayer_coming_soon: "¡Más características de multijugador por venir!" multiplayer_coming_soon: "¡Más características de multijugador por venir!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Guía" guide_title: "Guía"
tome_minion_spells: "Hechizos de tus Secuaces" tome_minion_spells: "Hechizos de tus Secuaces"
tome_read_only_spells: "Hechizos de Sólo Lectura" tome_read_only_spells: "Hechizos de Sólo Lectura"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "español", englishDescription: "Spanish", t
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian",
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian",
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
multiplayer_hint_label: "Astuce:" multiplayer_hint_label: "Astuce:"
multiplayer_hint: " Cliquez sur le lien pour tout sélectionner, puis appuyer sur Pomme-C ou Ctrl-C pour copier le lien." multiplayer_hint: " Cliquez sur le lien pour tout sélectionner, puis appuyer sur Pomme-C ou Ctrl-C pour copier le lien."
multiplayer_coming_soon: "Plus de fonctionnalités multijoueurs sont à venir" multiplayer_coming_soon: "Plus de fonctionnalités multijoueurs sont à venir"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Guide" guide_title: "Guide"
tome_minion_spells: "Les sorts de vos soldats" tome_minion_spells: "Les sorts de vos soldats"
tome_read_only_spells: "Sorts en lecture-seule" tome_read_only_spells: "Sorts en lecture-seule"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew",
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew",
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t
multiplayer_hint_label: "Tipp:" multiplayer_hint_label: "Tipp:"
multiplayer_hint: " Kattints a linkre, és Ctrl+C-vel (vagy ⌘+C-vel) másold a vágólapra!" multiplayer_hint: " Kattints a linkre, és Ctrl+C-vel (vagy ⌘+C-vel) másold a vágólapra!"
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Útmutató" guide_title: "Útmutató"
tome_minion_spells: "Egységeid varázslatai" tome_minion_spells: "Egységeid varázslatai"
tome_read_only_spells: "Csak olvasható varázslatok" tome_read_only_spells: "Csak olvasható varázslatok"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t
multiplayer_hint_label: "Suggerimento:" multiplayer_hint_label: "Suggerimento:"
multiplayer_hint: " Clicca il link per selezionare tutto, quindi premi CMD-C o Ctrl-C per copiare il link." multiplayer_hint: " Clicca il link per selezionare tutto, quindi premi CMD-C o Ctrl-C per copiare il link."
multiplayer_coming_soon: "Ulteriori aggiunte per il multigiocatore in arrivo!" multiplayer_coming_soon: "Ulteriori aggiunte per il multigiocatore in arrivo!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Guida" guide_title: "Guida"
tome_minion_spells: "Incantesimi dei tuoi seguaci" tome_minion_spells: "Incantesimi dei tuoi seguaci"
tome_read_only_spells: "Incantesimi in sola lettura" tome_read_only_spells: "Incantesimi in sola lettura"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese",
multiplayer_hint_label: "ヒント:" multiplayer_hint_label: "ヒント:"
multiplayer_hint: " リンクを選択後、 ⌘-C(MacOS) or Ctrl-C(Windows) でリンクをコピーできます。" multiplayer_hint: " リンクを選択後、 ⌘-C(MacOS) or Ctrl-C(Windows) でリンクをコピーできます。"
multiplayer_coming_soon: "今後より多くのマルチプレイ機能が追加されます。" multiplayer_coming_soon: "今後より多くのマルチプレイ機能が追加されます。"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "ガイド" guide_title: "ガイド"
tome_minion_spells: "操作できるキャラクターの呪文" tome_minion_spells: "操作できるキャラクターの呪文"
tome_read_only_spells: "読込専用の呪文" tome_read_only_spells: "読込専用の呪文"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese",
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t
multiplayer_hint_label: "힌트:" multiplayer_hint_label: "힌트:"
multiplayer_hint: " 모두 선택하려면 링크를 클릭하세요, 그리고 ⌘-C 또는 Ctrl-C 를 눌러서 링크를 복사하세요." multiplayer_hint: " 모두 선택하려면 링크를 클릭하세요, 그리고 ⌘-C 또는 Ctrl-C 를 눌러서 링크를 복사하세요."
multiplayer_coming_soon: "곧 좀 더 다양한 멀티플레이어 모드가 업데이트 됩니다!" multiplayer_coming_soon: "곧 좀 더 다양한 멀티플레이어 모드가 업데이트 됩니다!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "가이드" guide_title: "가이드"
tome_minion_spells: "당신 미니언의' 마법" tome_minion_spells: "당신 미니언의' 마법"
tome_read_only_spells: "읽기 전용 마법" tome_read_only_spells: "읽기 전용 마법"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg
multiplayer_hint_label: "Hint:" multiplayer_hint_label: "Hint:"
multiplayer_hint: " Klikk lenken for å velge alle, så trykker du Apple-C eller Ctrl-C for å kopiere lenken." multiplayer_hint: " Klikk lenken for å velge alle, så trykker du Apple-C eller Ctrl-C for å kopiere lenken."
multiplayer_coming_soon: "Det kommer flere flerspillsmuligheter!" multiplayer_coming_soon: "Det kommer flere flerspillsmuligheter!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Guide" guide_title: "Guide"
tome_minion_spells: "Din Minions' Trylleformularer" tome_minion_spells: "Din Minions' Trylleformularer"
tome_read_only_spells: "Kun-Lesbare Trylleformularer" tome_read_only_spells: "Kun-Lesbare Trylleformularer"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription:
multiplayer_hint_label: "Hint:" multiplayer_hint_label: "Hint:"
multiplayer_hint: " Klik de link om alles te selecteren, druk dan op Apple-C of Ctrl-C om de link te kopiëren." multiplayer_hint: " Klik de link om alles te selecteren, druk dan op Apple-C of Ctrl-C om de link te kopiëren."
multiplayer_coming_soon: "Binnenkort komen er meer Multiplayermogelijkheden!" multiplayer_coming_soon: "Binnenkort komen er meer Multiplayermogelijkheden!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Handleiding" guide_title: "Handleiding"
tome_minion_spells: "Jouw Minions' Spreuken" tome_minion_spells: "Jouw Minions' Spreuken"
tome_read_only_spells: "Read-Only Spreuken" tome_read_only_spells: "Read-Only Spreuken"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription:
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
multiplayer_hint_label: "Hint:" multiplayer_hint_label: "Hint:"
multiplayer_hint: " Klik de link om alles te selecteren, druk dan op Apple-C of Ctrl-C om de link te kopiëren." multiplayer_hint: " Klik de link om alles te selecteren, druk dan op Apple-C of Ctrl-C om de link te kopiëren."
multiplayer_coming_soon: "Binnenkort komen er meer Multiplayermogelijkheden!" multiplayer_coming_soon: "Binnenkort komen er meer Multiplayermogelijkheden!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Handleiding" guide_title: "Handleiding"
tome_minion_spells: "Jouw Minions' Spreuken" tome_minion_spells: "Jouw Minions' Spreuken"
tome_read_only_spells: "Read-Only Spreuken" tome_read_only_spells: "Read-Only Spreuken"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -16,7 +16,7 @@ module.exports = nativeDescription: "Nederlands", englishDescription: "Dutch", t
play: "Spelen" play: "Spelen"
retry: "Probeer opnieuw" retry: "Probeer opnieuw"
watch: "Volgen" watch: "Volgen"
unwatch: "Ontvolgen" unwatch: "Ontvolgen"
submit_patch: "Correctie Opsturen" submit_patch: "Correctie Opsturen"
units: units:
@ -199,7 +199,7 @@ module.exports = nativeDescription: "Nederlands", englishDescription: "Dutch", t
employers: employers:
want_to_hire_our_players: "Wil je expert CodeCombat spelers aanwerven? " want_to_hire_our_players: "Wil je expert CodeCombat spelers aanwerven? "
see_candidates: "Klik om je kandidaten te zien" see_candidates: "Klik om je kandidaten te zien"
candidates_count_prefix: "Momenteel hebben we " candidates_count_prefix: "Momenteel hebben we "
candidates_count_many: "veel" candidates_count_many: "veel"
candidates_count_suffix: "zeer getalenteerde en ervaren ontwikkelaars die werk zoeken." candidates_count_suffix: "zeer getalenteerde en ervaren ontwikkelaars die werk zoeken."
@ -246,6 +246,7 @@ module.exports = nativeDescription: "Nederlands", englishDescription: "Dutch", t
multiplayer_hint_label: "Hint:" multiplayer_hint_label: "Hint:"
multiplayer_hint: " Klik de link om alles te selecteren, druk dan op Apple-C of Ctrl-C om de link te kopiëren." multiplayer_hint: " Klik de link om alles te selecteren, druk dan op Apple-C of Ctrl-C om de link te kopiëren."
multiplayer_coming_soon: "Binnenkort komen er meer Multiplayermogelijkheden!" multiplayer_coming_soon: "Binnenkort komen er meer Multiplayermogelijkheden!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Handleiding" guide_title: "Handleiding"
tome_minion_spells: "Jouw Minions' Spreuken" tome_minion_spells: "Jouw Minions' Spreuken"
tome_read_only_spells: "Read-Only Spreuken" tome_read_only_spells: "Read-Only Spreuken"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Nederlands", englishDescription: "Dutch", t
user_names: "Gebruikersnamen" user_names: "Gebruikersnamen"
files: "Bestanden" files: "Bestanden"
top_simulators: "Top Simulatoren" top_simulators: "Top Simulatoren"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr
multiplayer_hint_label: "Hint:" multiplayer_hint_label: "Hint:"
multiplayer_hint: " Klikk lenken for å velge alle, så trykker du Apple-C eller Ctrl-C for å kopiere lenken." multiplayer_hint: " Klikk lenken for å velge alle, så trykker du Apple-C eller Ctrl-C for å kopiere lenken."
multiplayer_coming_soon: "Det kommer flere flerspillsmuligheter!" multiplayer_coming_soon: "Det kommer flere flerspillsmuligheter!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Guide" guide_title: "Guide"
tome_minion_spells: "Din Minions' Trylleformularer" tome_minion_spells: "Din Minions' Trylleformularer"
tome_read_only_spells: "Kun-Lesbare Trylleformularer" tome_read_only_spells: "Kun-Lesbare Trylleformularer"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish
multiplayer_hint_label: "Podpowiedź:" multiplayer_hint_label: "Podpowiedź:"
multiplayer_hint: "Kliknij link by zaznaczyć wszystko, potem wciśnij Cmd-C lub Ctrl-C by skopiować ten link." multiplayer_hint: "Kliknij link by zaznaczyć wszystko, potem wciśnij Cmd-C lub Ctrl-C by skopiować ten link."
multiplayer_coming_soon: "Wkrótce więcej opcji multiplayer" multiplayer_coming_soon: "Wkrótce więcej opcji multiplayer"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Przewodnik" guide_title: "Przewodnik"
tome_minion_spells: "Czary twojego podopiecznego" tome_minion_spells: "Czary twojego podopiecznego"
tome_read_only_spells: "Czary tylko do odczytu" tome_read_only_spells: "Czary tylko do odczytu"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
multiplayer_hint_label: "Dica:" multiplayer_hint_label: "Dica:"
multiplayer_hint: " Clique no link para selecionar tudo, então dê Ctrl+C ou ⌘+C para copiar o link. " multiplayer_hint: " Clique no link para selecionar tudo, então dê Ctrl+C ou ⌘+C para copiar o link. "
multiplayer_coming_soon: "Mais novidades no multiplayer estão chegando!" multiplayer_coming_soon: "Mais novidades no multiplayer estão chegando!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Guia" guide_title: "Guia"
tome_minion_spells: "Magias dos seus subordinados" tome_minion_spells: "Magias dos seus subordinados"
tome_read_only_spells: "Magias não editáveis" tome_read_only_spells: "Magias não editáveis"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "Português europeu", englishDescription: "P
multiplayer_hint_label: "Dica:" multiplayer_hint_label: "Dica:"
multiplayer_hint: " Carrega no link para seleccionar tudp, depois pressiona ⌘-C ou Ctrl-C para copiar o link." multiplayer_hint: " Carrega no link para seleccionar tudp, depois pressiona ⌘-C ou Ctrl-C para copiar o link."
multiplayer_coming_soon: "Mais funcionalidades de multiplayer brevemente!" multiplayer_coming_soon: "Mais funcionalidades de multiplayer brevemente!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Guia" guide_title: "Guia"
tome_minion_spells: "Feitiços dos teus Minions" tome_minion_spells: "Feitiços dos teus Minions"
tome_read_only_spells: "Feitiços apenas de leitura" tome_read_only_spells: "Feitiços apenas de leitura"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Português europeu", englishDescription: "P
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "português", englishDescription: "Portugues
multiplayer_hint_label: "Dica:" multiplayer_hint_label: "Dica:"
multiplayer_hint: " Clique no link para selecionar tudo, então dê Ctrl+C ou ⌘+C para copiar o link. " multiplayer_hint: " Clique no link para selecionar tudo, então dê Ctrl+C ou ⌘+C para copiar o link. "
multiplayer_coming_soon: "Mais novidades no multiplayer estão chegando!" multiplayer_coming_soon: "Mais novidades no multiplayer estão chegando!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Guia" guide_title: "Guia"
tome_minion_spells: "Magias dos seus subordinados" tome_minion_spells: "Magias dos seus subordinados"
tome_read_only_spells: "Magias não editáveis" tome_read_only_spells: "Magias não editáveis"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "português", englishDescription: "Portugues
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman
multiplayer_hint_label: "Hint:" multiplayer_hint_label: "Hint:"
multiplayer_hint: " Apasă pe link pentru a selecta tot, apoi apasă ⌘-C sau Ctrl-C pentru a copia link-ul." multiplayer_hint: " Apasă pe link pentru a selecta tot, apoi apasă ⌘-C sau Ctrl-C pentru a copia link-ul."
multiplayer_coming_soon: "Mai multe feature-uri multiplayer în curând!" multiplayer_coming_soon: "Mai multe feature-uri multiplayer în curând!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Ghid" guide_title: "Ghid"
tome_minion_spells: "Vrăjile Minion-ilor tăi" tome_minion_spells: "Vrăjile Minion-ilor tăi"
tome_read_only_spells: "Vrăji Read-Only" tome_read_only_spells: "Vrăji Read-Only"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
multiplayer_hint_label: "Подсказка: " multiplayer_hint_label: "Подсказка: "
multiplayer_hint: "кликните на ссылку, чтобы выделить её, затем нажмите ⌘-С или Ctrl-C, чтобы скопировать." multiplayer_hint: "кликните на ссылку, чтобы выделить её, затем нажмите ⌘-С или Ctrl-C, чтобы скопировать."
multiplayer_coming_soon: "Больше возможностей мультиплеера на подходе!" multiplayer_coming_soon: "Больше возможностей мультиплеера на подходе!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Руководство" guide_title: "Руководство"
tome_minion_spells: "Заклинания ваших миньонов" tome_minion_spells: "Заклинания ваших миньонов"
tome_read_only_spells: "Заклинания только для чтения" tome_read_only_spells: "Заклинания только для чтения"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak",
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak",
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian
multiplayer_hint_label: "Мала помоћ" multiplayer_hint_label: "Мала помоћ"
multiplayer_hint: " Кликни на линк да обележиш све, затим притисни Apple-C или Ctrl-C да копираш линк." multiplayer_hint: " Кликни на линк да обележиш све, затим притисни Apple-C или Ctrl-C да копираш линк."
multiplayer_coming_soon: "Стиже још нових карактеристика!" multiplayer_coming_soon: "Стиже још нових карактеристика!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Водич" guide_title: "Водич"
tome_minion_spells: "Чини твојих поданика" tome_minion_spells: "Чини твојих поданика"
tome_read_only_spells: "Чини које се могу само гледати" tome_read_only_spells: "Чини које се могу само гледати"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr
multiplayer_hint_label: "Tips:" multiplayer_hint_label: "Tips:"
multiplayer_hint: " Klicka på länken för att välja allt, tryck sedan på Cmd-C eller Ctrl-C för att kopiera länken." multiplayer_hint: " Klicka på länken för att välja allt, tryck sedan på Cmd-C eller Ctrl-C för att kopiera länken."
multiplayer_coming_soon: "Fler flerspelarlägen kommer!" multiplayer_coming_soon: "Fler flerspelarlägen kommer!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Guide" guide_title: "Guide"
tome_minion_spells: "Dina soldaters förmågor" tome_minion_spells: "Dina soldaters förmågor"
tome_read_only_spells: "Skrivskyddade förmågor" tome_read_only_spells: "Skrivskyddade förmågor"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra
multiplayer_hint_label: "คำใบ้" multiplayer_hint_label: "คำใบ้"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t
multiplayer_hint_label: "İpucu:" multiplayer_hint_label: "İpucu:"
multiplayer_hint: " Kopyalamak için önce linke tıklayın, ardından CTRL+C veya ⌘+C kombinasyonuna basın." multiplayer_hint: " Kopyalamak için önce linke tıklayın, ardından CTRL+C veya ⌘+C kombinasyonuna basın."
multiplayer_coming_soon: "Daha bir çok çoklu oyuncu özelliği eklenecek!" multiplayer_coming_soon: "Daha bir çok çoklu oyuncu özelliği eklenecek!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Rehber" guide_title: "Rehber"
tome_minion_spells: "Minyonlarınızın Büyüleri" tome_minion_spells: "Minyonlarınızın Büyüleri"
tome_read_only_spells: "Salt Okunur Büyüler" tome_read_only_spells: "Salt Okunur Büyüler"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "українська мова", englishDesc
multiplayer_hint_label: "Підказка:" multiplayer_hint_label: "Підказка:"
multiplayer_hint: "Натисніть на посилання, щоб обрати всіх, та натисніть Apple-C або Ctrl-C, щоб скопіювати посилання." multiplayer_hint: "Натисніть на посилання, щоб обрати всіх, та натисніть Apple-C або Ctrl-C, щоб скопіювати посилання."
multiplayer_coming_soon: "Скоро - більше можливостей у мультиплеєрі!" multiplayer_coming_soon: "Скоро - більше можливостей у мультиплеєрі!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "Посібник" guide_title: "Посібник"
tome_minion_spells: "Закляття ваших міньонів" tome_minion_spells: "Закляття ваших міньонів"
tome_read_only_spells: "Закляття тільки для читання" tome_read_only_spells: "Закляття тільки для читання"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "українська мова", englishDesc
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu",
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu",
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
multiplayer_hint_label: "提示:" multiplayer_hint_label: "提示:"
multiplayer_hint: " 点击全选,然后按 Apple-C苹果电脑或 Ctrl-C 复制链接。" multiplayer_hint: " 点击全选,然后按 Apple-C苹果电脑或 Ctrl-C 复制链接。"
multiplayer_coming_soon: "多人游戏的更多特性!" multiplayer_coming_soon: "多人游戏的更多特性!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "指南" guide_title: "指南"
tome_minion_spells: "助手的咒语" tome_minion_spells: "助手的咒语"
tome_read_only_spells: "只读的咒语" tome_read_only_spells: "只读的咒语"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese
multiplayer_hint_label: "提示:" multiplayer_hint_label: "提示:"
multiplayer_hint: " 點擊全選,然後按 ⌘-C 或 Ctrl-C 複製連結。" multiplayer_hint: " 點擊全選,然後按 ⌘-C 或 Ctrl-C 複製連結。"
multiplayer_coming_soon: "請期待更多的多人關卡!" multiplayer_coming_soon: "請期待更多的多人關卡!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "指南" guide_title: "指南"
tome_minion_spells: "助手的咒語" tome_minion_spells: "助手的咒語"
tome_read_only_spells: "唯讀的咒語" tome_read_only_spells: "唯讀的咒語"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -246,6 +246,7 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio
multiplayer_hint_label: "提醒:" multiplayer_hint_label: "提醒:"
multiplayer_hint: " 點牢全選,再捺 Apple-C蘋果電腦要勿 Ctrl-C 複製鏈接。" multiplayer_hint: " 點牢全選,再捺 Apple-C蘋果電腦要勿 Ctrl-C 複製鏈接。"
multiplayer_coming_soon: "多人遊戲還多特性!" multiplayer_coming_soon: "多人遊戲還多特性!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
guide_title: "指南" guide_title: "指南"
tome_minion_spells: "下手個咒語" tome_minion_spells: "下手個咒語"
tome_read_only_spells: "只讀個咒語" tome_read_only_spells: "只讀個咒語"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -17,7 +17,7 @@ module.exports = nativeDescription: "中文", englishDescription: "Chinese", tra
retry: "重试" retry: "重试"
# watch: "Watch" # watch: "Watch"
# unwatch: "Unwatch" # unwatch: "Unwatch"
# submit_patch: "Submit Patch" submit_patch: "提交补丁"
units: units:
second: "" second: ""
@ -36,7 +36,7 @@ module.exports = nativeDescription: "中文", englishDescription: "Chinese", tra
nav: nav:
play: "" play: ""
# community: "Community" community: "社区"
editor: "编辑" editor: "编辑"
blog: "博客" blog: "博客"
forum: "论坛" forum: "论坛"
@ -53,7 +53,7 @@ module.exports = nativeDescription: "中文", englishDescription: "Chinese", tra
versions: versions:
save_version_title: "保存新版本" save_version_title: "保存新版本"
new_major_version: "最新主要版本" new_major_version: "最新主要版本"
# cla_prefix: "To save changes, first you must agree to our" cla_prefix: "要保存更改, 首先你必须要统一我们的"
# cla_url: "CLA" # cla_url: "CLA"
# cla_suffix: "." # cla_suffix: "."
cla_agree: "我同意" cla_agree: "我同意"
@ -61,7 +61,7 @@ module.exports = nativeDescription: "中文", englishDescription: "Chinese", tra
login: login:
sign_up: "注册" sign_up: "注册"
log_in: "登录" log_in: "登录"
# logging_in: "Logging In" logging_in: "登录中..."
log_out: "登出" log_out: "登出"
recover: "找回账户" recover: "找回账户"
@ -78,7 +78,7 @@ module.exports = nativeDescription: "中文", englishDescription: "Chinese", tra
creating: "账户在创新中" creating: "账户在创新中"
sign_up: "注册" sign_up: "注册"
log_in: "以密码登录" log_in: "以密码登录"
# social_signup: "Or, you can sign up through Facebook or G+:" social_signup: "或者, 你可以通过Facebook 或者 G+ 注册:"
home: home:
slogan: "通过游戏学习Javascript脚本语言" slogan: "通过游戏学习Javascript脚本语言"
@ -130,21 +130,21 @@ module.exports = nativeDescription: "中文", englishDescription: "Chinese", tra
# learn_more: "Learn more about being a Diplomat" # learn_more: "Learn more about being a Diplomat"
# subscribe_as_diplomat: "Subscribe as a Diplomat" # subscribe_as_diplomat: "Subscribe as a Diplomat"
# wizard_settings: wizard_settings:
# title: "Wizard Settings" title: "巫师设定"
# customize_avatar: "Customize Your Avatar" customize_avatar: "设置你的头像"
# active: "Active" active: "启用"
# color: "Color" color: "颜色"
# group: "Group" group: "类别"
# clothes: "Clothes" clothes: "衣服"
# trim: "Trim" trim: "条纹"
# cloud: "Cloud" cloud: ""
# team: "Team" team: "队伍"
# spell: "Spell" spell: "魔法球"
# boots: "Boots" boots: "鞋子"
# hue: "Hue" hue: "色彩"
# saturation: "Saturation" saturation: "饱和度"
# lightness: "Lightness" lightness: "亮度"
# account_settings: # account_settings:
# title: "Account Settings" # title: "Account Settings"
@ -246,6 +246,7 @@ module.exports = nativeDescription: "中文", englishDescription: "Chinese", tra
# multiplayer_hint_label: "Hint:" # multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." # multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells" # tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells" # tome_read_only_spells: "Read-Only Spells"
@ -710,3 +711,4 @@ module.exports = nativeDescription: "中文", englishDescription: "Chinese", tra
# user_names: "User Names" # user_names: "User Names"
# files: "Files" # files: "Files"
# top_simulators: "Top Simulators" # top_simulators: "Top Simulators"
# source_document: "Source Document"

View file

@ -1,6 +1,5 @@
storage = require 'lib/storage' storage = require 'lib/storage'
deltasLib = require 'lib/deltas' deltasLib = require 'lib/deltas'
auth = require 'lib/auth'
class CocoModel extends Backbone.Model class CocoModel extends Backbone.Model
idAttribute: "_id" idAttribute: "_id"
@ -9,6 +8,8 @@ class CocoModel extends Backbone.Model
saveBackups: false saveBackups: false
@schema: null @schema: null
getMe: -> @me or @me = require('lib/auth').me
initialize: -> initialize: ->
super() super()
if not @constructor.className if not @constructor.className
@ -27,7 +28,7 @@ class CocoModel extends Backbone.Model
clone = super() clone = super()
clone.set($.extend(true, {}, if withChanges then @attributes else @_revertAttributes)) clone.set($.extend(true, {}, if withChanges then @attributes else @_revertAttributes))
clone clone
onError: -> onError: ->
@loading = false @loading = false
@ -96,7 +97,8 @@ class CocoModel extends Backbone.Model
not _.isEqual @attributes, @_revertAttributes not _.isEqual @attributes, @_revertAttributes
cloneNewMinorVersion: -> cloneNewMinorVersion: ->
newData = $.extend(null, {}, @attributes) newData = _.clone @attributes
clone = new @constructor(newData) clone = new @constructor(newData)
clone clone
@ -136,7 +138,7 @@ class CocoModel extends Backbone.Model
hasReadAccess: (actor) -> hasReadAccess: (actor) ->
# actor is a User object # actor is a User object
actor ?= auth.me actor ?= @getMe()
return true if actor.isAdmin() return true if actor.isAdmin()
if @get('permissions')? if @get('permissions')?
for permission in @get('permissions') for permission in @get('permissions')
@ -148,7 +150,7 @@ class CocoModel extends Backbone.Model
hasWriteAccess: (actor) -> hasWriteAccess: (actor) ->
# actor is a User object # actor is a User object
actor ?= auth.me actor ?= @getMe()
return true if actor.isAdmin() return true if actor.isAdmin()
if @get('permissions')? if @get('permissions')?
for permission in @get('permissions') for permission in @get('permissions')
@ -160,6 +162,10 @@ class CocoModel extends Backbone.Model
getDelta: -> getDelta: ->
differ = deltasLib.makeJSONDiffer() differ = deltasLib.makeJSONDiffer()
differ.diff @_revertAttributes, @attributes differ.diff @_revertAttributes, @attributes
getDeltaWith: (otherModel) ->
differ = deltasLib.makeJSONDiffer()
differ.diff @attributes, otherModel.attributes
applyDelta: (delta) -> applyDelta: (delta) ->
newAttributes = $.extend(true, {}, @attributes) newAttributes = $.extend(true, {}, @attributes)
@ -170,13 +176,17 @@ class CocoModel extends Backbone.Model
delta = @getDelta() delta = @getDelta()
deltasLib.expandDelta(delta, @_revertAttributes, @schema()) deltasLib.expandDelta(delta, @_revertAttributes, @schema())
getExpandedDeltaWith: (otherModel) ->
delta = @getDeltaWith(otherModel)
deltasLib.expandDelta(delta, @attributes, @schema())
watch: (doWatch=true) -> watch: (doWatch=true) ->
$.ajax("#{@urlRoot}/#{@id}/watch", {type:'PUT', data:{on:doWatch}}) $.ajax("#{@urlRoot}/#{@id}/watch", {type:'PUT', data:{on:doWatch}})
@watching = -> doWatch @watching = -> doWatch
watching: -> watching: ->
return me.id in (@get('watchers') or []) return me.id in (@get('watchers') or [])
populateI18N: (data, schema, path='') -> populateI18N: (data, schema, path='') ->
# TODO: Better schema/json walking # TODO: Better schema/json walking
sum = 0 sum = 0
@ -185,7 +195,7 @@ class CocoModel extends Backbone.Model
if schema.properties?.i18n and _.isPlainObject(data) and not data.i18n? if schema.properties?.i18n and _.isPlainObject(data) and not data.i18n?
data.i18n = {} data.i18n = {}
sum += 1 sum += 1
if _.isPlainObject data if _.isPlainObject data
for key, value of data for key, value of data
numChanged = 0 numChanged = 0
@ -193,10 +203,10 @@ class CocoModel extends Backbone.Model
if numChanged and not path # should only do this for the root object if numChanged and not path # should only do this for the root object
@set key, value @set key, value
sum += numChanged sum += numChanged
if schema.items and _.isArray data if schema.items and _.isArray data
sum += @populateI18N(value, schema.items, path+'/'+index) for value, index in data sum += @populateI18N(value, schema.items, path+'/'+index) for value, index in data
sum sum
@getReferencedModel: (data, schema) -> @getReferencedModel: (data, schema) ->
@ -227,13 +237,13 @@ class CocoModel extends Backbone.Model
model = new Model() model = new Model()
model.url = makeUrlFunc(link) model.url = makeUrlFunc(link)
return model return model
setURL: (url) -> setURL: (url) ->
makeURLFunc = (u) -> -> u makeURLFunc = (u) -> -> u
@url = makeURLFunc(url) @url = makeURLFunc(url)
@ @
getURL: -> getURL: ->
return if _.isString @url then @url else @url() return if _.isString @url then @url else @url()
module.exports = CocoModel module.exports = CocoModel

View file

@ -31,8 +31,9 @@ module.exports = class SuperModel extends Backbone.Model
else else
@registerModel(model) @registerModel(model)
console.debug 'Registering model', model.getURL() res = @addModelResource(model, name, fetchOptions, value)
return @addModelResource(model, name, fetchOptions, value).load() if model.loaded then res.markLoaded() else res.load()
return res
loadCollection: (collection, name, fetchOptions, value=1) -> loadCollection: (collection, name, fetchOptions, value=1) ->
url = collection.getURL() url = collection.getURL()
@ -52,7 +53,9 @@ module.exports = class SuperModel extends Backbone.Model
@listenToOnce collection, 'sync', (c) -> @listenToOnce collection, 'sync', (c) ->
console.debug 'Registering collection', url console.debug 'Registering collection', url
@registerCollection c @registerCollection c
return @addModelResource(collection, name, fetchOptions, value).load() res = @addModelResource(collection, name, fetchOptions, value)
res.load() if not (res.isLoading or res.isLoaded)
return res
# replace or overwrite # replace or overwrite
shouldSaveBackups: (model) -> false shouldSaveBackups: (model) -> false

View file

@ -5,7 +5,10 @@
#component-patches #component-patches
padding: 0 10px 10px padding: 0 10px 10px
background: white background: white
.patches-view
padding: 10px 20px 10px 0px
.navbar-text .navbar-text
float: left float: left

View file

@ -1,4 +1,14 @@
#editor-level-system-edit-view #editor-level-system-edit-view
nav
margin-bottom: 0
#system-patches
padding: 0 10px 10px
background: white
.patches-view
padding: 10px 20px 10px 0px
.navbar-text .navbar-text
float: left float: left

View file

@ -17,7 +17,7 @@ block content
if user.id != me.id if user.id != me.id
button.btn.edit-settings-button#enter-espionage-mode 007 button.btn.edit-settings-button#enter-espionage-mode 007
if user.get('jobProfile') if user.get('jobProfile') && allowedToViewJobProfile
- var profile = user.get('jobProfile'); - var profile = user.get('jobProfile');
.job-profile-container .job-profile-container
.job-profile-row .job-profile-row
@ -112,7 +112,11 @@ block content
.project-image(style="background-image: url('/file/" + project.picture + "')") .project-image(style="background-image: url('/file/" + project.picture + "')")
p= project.name p= project.name
div!= marked(project.description) div!= marked(project.description)
else if allowedToViewJobProfile
.public-profile-container
h2 Loading...
else else
.public-profile-container .public-profile-container
h2 h2

View file

@ -37,10 +37,12 @@ mixin deltaPanel(delta, conflict)
if delta.conflict && !conflict if delta.conflict && !conflict
.panel-body .panel-body
strong MERGE CONFLICT WITH strong(data-i18n="delta.merge_conflict_with") MERGE CONFLICT WITH
+deltaPanel(delta.conflict, true) +deltaPanel(delta.conflict, true)
.panel-group(id='delta-accordion-'+(counter)) .panel-group(id='delta-accordion-'+(counter))
for delta in deltas for delta in deltas
+deltaPanel(delta) +deltaPanel(delta)
if !deltas.length
alert.alert-warning(data-i18n="delta.no_changes") No changes

View file

@ -24,7 +24,7 @@ block header
li li
a(href="#editor-level-settings-tab-view", data-toggle="tab", data-i18n="editor.level_tab_settings") Settings a(href="#editor-level-settings-tab-view", data-toggle="tab", data-i18n="editor.level_tab_settings") Settings
li li
a(href="#editor-level-components-tab-view", data-toggle="tab", data-i18n="editor.level_tab_components") Components a(href="#editor-level-components-tab-view", data-toggle="tab", data-i18n="editor.level_tab_components")#components-tab Components
li li
a(href="#editor-level-systems-tab-view", data-toggle="tab", data-i18n="editor.level_tab_systems") Systems a(href="#editor-level-systems-tab-view", data-toggle="tab", data-i18n="editor.level_tab_systems") Systems
li li

View file

@ -10,15 +10,21 @@ block modal-body-content
if dataList if dataList
table.table.table-condensed table.table.table-condensed
tr tr
th
th(data-i18n="general.name") Name th(data-i18n="general.name") Name
th(data-i18n="general.version") Version th(data-i18n="general.version") Version
th(data-i18n="general.commit_msg") Commit Message th(data-i18n="general.commit_msg") Commit Message
for data in dataList for data in dataList
tr tr
td
input(type="checkbox", value=data._id).select
td td
a(href="/editor/#{page}/#{data.slug || data._id}") a(href="/editor/#{page}/#{data.slug || data._id}")
| #{data.name} | #{data.name}
td #{data.version.major}.#{data.version.minor} td #{data.version.major}.#{data.version.minor}
td #{data.commitMessage} td #{data.commitMessage}
div.delta-container
div.delta-view
block modal-footer-content block modal-footer-content

View file

@ -18,7 +18,7 @@ module.exports = class ProfileView extends View
super options super options
if @userID is me.id if @userID is me.id
@user = me @user = me
else else if me.isAdmin() or "employer" in me.get('permissions')
@user = User.getByID(@userID) @user = User.getByID(@userID)
@user.fetch() @user.fetch()
@listenTo @user, "sync", => @listenTo @user, "sync", =>
@ -27,6 +27,7 @@ module.exports = class ProfileView extends View
getRenderData: -> getRenderData: ->
context = super() context = super()
context.user = @user context.user = @user
context.allowedToViewJobProfile = me.isAdmin() or "employer" in me.get('permissions')
context.myProfile = @user.id is context.me.id context.myProfile = @user.id is context.me.id
context.marked = marked context.marked = marked
context.moment = moment context.moment = moment

View file

@ -49,7 +49,7 @@ module.exports = class ArticleEditView extends View
data: data data: data
filePath: "db/thang.type/#{@article.get('original')}" filePath: "db/thang.type/#{@article.get('original')}"
schema: Article.schema schema: Article.schema
readOnly: true unless me.isAdmin() or @article.hasWriteAccess(me) readOnly: me.get('anonymous')
callbacks: callbacks:
change: @pushChangesToPreview change: @pushChangesToPreview
@treema = @$el.find('#article-treema').treema(options) @treema = @$el.find('#article-treema').treema(options)

View file

@ -9,19 +9,62 @@ TEXTDIFF_OPTIONS =
viewType: 1 viewType: 1
module.exports = class DeltaView extends CocoView module.exports = class DeltaView extends CocoView
###
Takes a CocoModel instance (model) and displays changes since the
last save (attributes vs _revertAttributes).
* If headModel is included, will look for and display conflicts with the changes in model.
* If comparisonModel is included, will show deltas between model and comparisonModel instead
of changes within model itself.
###
@deltaCounter: 0 @deltaCounter: 0
className: "delta-view" className: "delta-view"
template: template template: template
constructor: (options) -> constructor: (options) ->
super(options) super(options)
@model = options.model @expandedDeltas = []
@headModel = options.headModel @skipPaths = options.skipPaths
@expandedDeltas = @model.getExpandedDelta()
for modelName in ['model', 'headModel', 'comparisonModel']
continue unless m = options[modelName]
@[modelName] = @supermodel.loadModel(m, 'document').model
@buildDeltas() if @supermodel.finished()
onLoaded: ->
@buildDeltas()
super()
buildDeltas: ->
if @comparisonModel
@expandedDeltas = @model.getExpandedDeltaWith(@comparisonModel)
else
@expandedDeltas = @model.getExpandedDelta()
@filterExpandedDeltas()
if @headModel if @headModel
@headDeltas = @headModel.getExpandedDelta() @headDeltas = @headModel.getExpandedDelta()
@conflicts = deltasLib.getConflicts(@headDeltas, @expandedDeltas) @conflicts = deltasLib.getConflicts(@headDeltas, @expandedDeltas)
filterExpandedDeltas: ->
return unless @skipPaths
for path, i in @skipPaths
@skipPaths[i] = [path] if _.isString(path)
newDeltas = []
for delta in @expandedDeltas
skip = false
for skipPath in @skipPaths
if _.isEqual _.first(delta.dataPath, skipPath.length), skipPath
skip = true
break
newDeltas.push delta unless skip
@expandedDeltas = newDeltas
getRenderData: -> getRenderData: ->
c = super() c = super()
c.deltas = @expandedDeltas c.deltas = @expandedDeltas

View file

@ -46,11 +46,12 @@ module.exports = class LevelComponentEditView extends View
schema = _.cloneDeep LevelComponent.schema schema = _.cloneDeep LevelComponent.schema
schema.properties = _.pick schema.properties, (value, key) => key in @editableSettings schema.properties = _.pick schema.properties, (value, key) => key in @editableSettings
schema.required = _.intersection schema.required, @editableSettings schema.required = _.intersection schema.required, @editableSettings
treemaOptions = treemaOptions =
supermodel: @supermodel supermodel: @supermodel
schema: schema schema: schema
data: data data: data
readonly: me.get('anonymous')
callbacks: {change: @onComponentSettingsEdited} callbacks: {change: @onComponentSettingsEdited}
@componentSettingsTreema = @$el.find('#edit-component-treema').treema treemaOptions @componentSettingsTreema = @$el.find('#edit-component-treema').treema treemaOptions
@componentSettingsTreema.build() @componentSettingsTreema.build()
@ -68,6 +69,7 @@ module.exports = class LevelComponentEditView extends View
supermodel: @supermodel supermodel: @supermodel
schema: LevelComponent.schema.properties.configSchema schema: LevelComponent.schema.properties.configSchema
data: @levelComponent.get 'configSchema' data: @levelComponent.get 'configSchema'
readOnly: me.get('anonymous')
callbacks: {change: @onConfigSchemaEdited} callbacks: {change: @onConfigSchemaEdited}
@configSchemaTreema = @$el.find('#config-schema-treema').treema treemaOptions @configSchemaTreema = @$el.find('#config-schema-treema').treema treemaOptions
@configSchemaTreema.build() @configSchemaTreema.build()
@ -84,13 +86,14 @@ module.exports = class LevelComponentEditView extends View
editorEl = $('<div></div>').text(@levelComponent.get('code')).addClass('inner-editor') editorEl = $('<div></div>').text(@levelComponent.get('code')).addClass('inner-editor')
@$el.find('#component-code-editor').empty().append(editorEl) @$el.find('#component-code-editor').empty().append(editorEl)
@editor = ace.edit(editorEl[0]) @editor = ace.edit(editorEl[0])
@editor.setReadOnly(me.get('anonymous'))
session = @editor.getSession() session = @editor.getSession()
session.setMode 'ace/mode/coffee' session.setMode 'ace/mode/coffee'
session.setTabSize 2 session.setTabSize 2
session.setNewLineMode = 'unix' session.setNewLineMode = 'unix'
session.setUseSoftTabs true session.setUseSoftTabs true
@editor.on('change', @onEditorChange) @editor.on('change', @onEditorChange)
onEditorChange: => onEditorChange: =>
@levelComponent.set 'code', @editor.getValue() @levelComponent.set 'code', @editor.getValue()
Backbone.Mediator.publish 'level-component-edited', levelComponent: @levelComponent Backbone.Mediator.publish 'level-component-edited', levelComponent: @levelComponent
@ -104,7 +107,7 @@ module.exports = class LevelComponentEditView extends View
versionHistoryView = new VersionHistoryView {}, @levelComponent.id versionHistoryView = new VersionHistoryView {}, @levelComponent.id
@openModalView versionHistoryView @openModalView versionHistoryView
Backbone.Mediator.publish 'level:view-switched', e Backbone.Mediator.publish 'level:view-switched', e
startPatchingComponent: (e) -> startPatchingComponent: (e) ->
@openModalView new SaveVersionModal({model:@levelComponent}) @openModalView new SaveVersionModal({model:@levelComponent})
Backbone.Mediator.publish 'level:view-switched', e Backbone.Mediator.publish 'level:view-switched', e
@ -117,4 +120,3 @@ module.exports = class LevelComponentEditView extends View
destroy: -> destroy: ->
@editor?.destroy() @editor?.destroy()
super() super()

View file

@ -14,7 +14,6 @@ module.exports = class ComponentsTabView extends View
className: 'tab-pane' className: 'tab-pane'
subscriptions: subscriptions:
'level-thangs-changed': 'onLevelThangsChanged'
'edit-level-component': 'editLevelComponent' 'edit-level-component': 'editLevelComponent'
'level-component-edited': 'onLevelComponentEdited' 'level-component-edited': 'onLevelComponentEdited'
'level-component-editing-ended': 'onLevelComponentEditingEnded' 'level-component-editing-ended': 'onLevelComponentEditingEnded'
@ -24,8 +23,8 @@ module.exports = class ComponentsTabView extends View
'click #create-new-component-button-no-select': 'createNewLevelComponent' 'click #create-new-component-button-no-select': 'createNewLevelComponent'
onLoaded: -> onLoaded: ->
onLevelThangsChanged: (e) ->
thangsData = e.thangsData refreshLevelThangsTreema: (thangsData) ->
presentComponents = {} presentComponents = {}
for thang in thangsData for thang in thangsData
for component in thang.components for component in thang.components

View file

@ -29,6 +29,7 @@ module.exports = class EditorLevelView extends View
'click #fork-level-start-button': 'startForkingLevel' 'click #fork-level-start-button': 'startForkingLevel'
'click #level-history-button': 'showVersionHistory' 'click #level-history-button': 'showVersionHistory'
'click #patches-tab': -> @patchesView.load() 'click #patches-tab': -> @patchesView.load()
'click #components-tab': -> @componentsTab.refreshLevelThangsTreema @level.get('thangs')
'click #level-patch-button': 'startPatchingLevel' 'click #level-patch-button': 'startPatchingLevel'
'click #level-watch-button': 'toggleWatchLevel' 'click #level-watch-button': 'toggleWatchLevel'
'click #pop-level-i18n-button': -> @level.populateI18N() 'click #pop-level-i18n-button': -> @level.populateI18N()

View file

@ -35,6 +35,7 @@ module.exports = class LevelSaveView extends SaveVersionModal
models = if @lastContext.levelNeedsSave then [@level] else [] models = if @lastContext.levelNeedsSave then [@level] else []
models = models.concat @lastContext.modifiedComponents models = models.concat @lastContext.modifiedComponents
models = models.concat @lastContext.modifiedSystems models = models.concat @lastContext.modifiedSystems
models = (m for m in models when m.hasWriteAccess())
for changeEl, i in changeEls for changeEl, i in changeEls
model = models[i] model = models[i]
try try
@ -44,6 +45,7 @@ module.exports = class LevelSaveView extends SaveVersionModal
console.error "Couldn't create delta view:", e console.error "Couldn't create delta view:", e
shouldSaveEntity: (m) -> shouldSaveEntity: (m) ->
return false unless m.hasWriteAccess()
return true if m.hasLocalChanges() return true if m.hasLocalChanges()
return true if (m.get('version').major is 0 and m.get('version').minor is 0) or not m.isPublished() and not m.collection return true if (m.get('version').major is 0 and m.get('version').minor is 0) or not m.isPublished() and not m.collection
# Sometimes we have two versions: one in a search collection and one with a URL. We only save changes to the latter. # Sometimes we have two versions: one in a search collection and one with a URL. We only save changes to the latter.

View file

@ -58,7 +58,7 @@ module.exports = class ScriptsTabView extends View
thangIDs: thangIDs thangIDs: thangIDs
dimensions: @dimensions dimensions: @dimensions
supermodel: @supermodel supermodel: @supermodel
readOnly: true unless me.isAdmin() or @level.hasWriteAccess(me) readOnly: me.get('anonymous')
callbacks: callbacks:
change: @onScriptChanged change: @onScriptChanged
nodeClasses: nodeClasses:

View file

@ -3,6 +3,7 @@ template = require 'templates/editor/level/settings_tab'
Level = require 'models/Level' Level = require 'models/Level'
Surface = require 'lib/surface/Surface' Surface = require 'lib/surface/Surface'
nodes = require './treema_nodes' nodes = require './treema_nodes'
{me} = require 'lib/auth'
module.exports = class SettingsTabView extends View module.exports = class SettingsTabView extends View
id: 'editor-level-settings-tab-view' id: 'editor-level-settings-tab-view'
@ -34,7 +35,7 @@ module.exports = class SettingsTabView extends View
supermodel: @supermodel supermodel: @supermodel
schema: schema schema: schema
data: data data: data
readOnly: true unless me.isAdmin() or @level.hasWriteAccess(me) readOnly: me.get('anonymous')
callbacks: {change: @onSettingsChanged} callbacks: {change: @onSettingsChanged}
thangIDs: thangIDs thangIDs: thangIDs
nodeClasses: nodeClasses:

View file

@ -49,7 +49,7 @@ module.exports = class LevelSystemEditView extends View
schema: schema schema: schema
data: data data: data
callbacks: {change: @onSystemSettingsEdited} callbacks: {change: @onSystemSettingsEdited}
treemaOptions.readOnly = true unless me.isAdmin() treemaOptions.readOnly = me.get('anonymous')
@systemSettingsTreema = @$el.find('#edit-system-treema').treema treemaOptions @systemSettingsTreema = @$el.find('#edit-system-treema').treema treemaOptions
@systemSettingsTreema.build() @systemSettingsTreema.build()
@systemSettingsTreema.open() @systemSettingsTreema.open()
@ -67,7 +67,7 @@ module.exports = class LevelSystemEditView extends View
schema: LevelSystem.schema.properties.configSchema schema: LevelSystem.schema.properties.configSchema
data: @levelSystem.get 'configSchema' data: @levelSystem.get 'configSchema'
callbacks: {change: @onConfigSchemaEdited} callbacks: {change: @onConfigSchemaEdited}
treemaOptions.readOnly = true unless me.isAdmin() treemaOptions.readOnly = me.get('anonymous')
@configSchemaTreema = @$el.find('#config-schema-treema').treema treemaOptions @configSchemaTreema = @$el.find('#config-schema-treema').treema treemaOptions
@configSchemaTreema.build() @configSchemaTreema.build()
@configSchemaTreema.open() @configSchemaTreema.open()
@ -83,7 +83,7 @@ module.exports = class LevelSystemEditView extends View
editorEl = $('<div></div>').text(@levelSystem.get('code')).addClass('inner-editor') editorEl = $('<div></div>').text(@levelSystem.get('code')).addClass('inner-editor')
@$el.find('#system-code-editor').empty().append(editorEl) @$el.find('#system-code-editor').empty().append(editorEl)
@editor = ace.edit(editorEl[0]) @editor = ace.edit(editorEl[0])
@editor.setReadOnly(not me.isAdmin()) @editor.setReadOnly(me.get('anonymous'))
session = @editor.getSession() session = @editor.getSession()
session.setMode 'ace/mode/coffee' session.setMode 'ace/mode/coffee'
session.setTabSize 2 session.setTabSize 2

View file

@ -53,7 +53,7 @@ module.exports = class SystemsTabView extends View
supermodel: @supermodel supermodel: @supermodel
schema: Level.schema.properties.systems schema: Level.schema.properties.systems
data: systems data: systems
readOnly: true unless me.isAdmin() or @level.hasWriteAccess(me) readOnly: me.get('anonymous')
callbacks: callbacks:
change: @onSystemsChanged change: @onSystemsChanged
select: @onSystemSelected select: @onSystemSelected

View file

@ -37,9 +37,11 @@ module.exports = class PatchModal extends ModalView
if @targetModel.hasWriteAccess() if @targetModel.hasWriteAccess()
headModel = @originalSource.clone(false) headModel = @originalSource.clone(false)
headModel.set(@targetModel.attributes) headModel.set(@targetModel.attributes)
headModel.loaded = true
pendingModel = @originalSource.clone(false) pendingModel = @originalSource.clone(false)
pendingModel.applyDelta(@patch.get('delta')) pendingModel.applyDelta(@patch.get('delta'))
pendingModel.loaded = true
@deltaView = new DeltaView({model:pendingModel, headModel:headModel}) @deltaView = new DeltaView({model:pendingModel, headModel:headModel})
changeEl = @$el.find('.changes-stub') changeEl = @$el.find('.changes-stub')

View file

@ -22,7 +22,6 @@ module.exports = class PatchesView extends CocoView
@patches = new PatchesCollection([], {}, @model, @status) @patches = new PatchesCollection([], {}, @model, @status)
load: -> load: ->
console.log 'load patches view?'
@initPatches() @initPatches()
@patches = @supermodel.loadCollection(@patches, 'patches').model @patches = @supermodel.loadCollection(@patches, 'patches').model
@listenTo @patches, 'sync', @onPatchesLoaded @listenTo @patches, 'sync', @onPatchesLoaded

View file

@ -50,7 +50,9 @@ module.exports = class EmployersView extends View
renderCandidatesAndSetupScrolling: => renderCandidatesAndSetupScrolling: =>
@render() @render()
$(".nano").nanoScroller() $(".nano").nanoScroller()
if window.location.hash.length is 25 if window.history?.state?.lastViewedCandidateID
$(".nano").nanoScroller({scrollTo:$("#" + window.history.state.lastViewedCandidateID)})
else if window.location.hash.length is 25
$(".nano").nanoScroller({scrollTo:$(window.location.hash)}) $(".nano").nanoScroller({scrollTo:$(window.location.hash)})
sortTable: -> sortTable: ->
@ -175,8 +177,13 @@ module.exports = class EmployersView extends View
onCandidateClicked: (e) -> onCandidateClicked: (e) ->
id = $(e.target).closest('tr').data('candidate-id') id = $(e.target).closest('tr').data('candidate-id')
window.location.hash = id
if id if id
if window.history
oldState = _.cloneDeep window.history.state ? {}
oldState["lastViewedCandidateID"] = id
window.history.replaceState(oldState,"")
else
window.location.hash = id
url = "/account/profile/#{id}" url = "/account/profile/#{id}"
app.router.navigate url, {trigger: true} app.router.navigate url, {trigger: true}
else else

View file

@ -38,9 +38,9 @@ module.exports = class RootView extends CocoView
# force the browser to scroll to the hash # force the browser to scroll to the hash
# also messes with the browser history, so perhaps come up with a better solution # also messes with the browser history, so perhaps come up with a better solution
super() super()
hash = location.hash #hash = location.hash
location.hash = '' #location.hash = ''
location.hash = hash #location.hash = hash
@renderScrollbar() @renderScrollbar()
#@$('.antiscroll-wrap').antiscroll() # not yet, buggy #@$('.antiscroll-wrap').antiscroll() # not yet, buggy

View file

@ -20,6 +20,7 @@ module.exports = class EmployerSignupView extends View
"click #contract-agreement-button": "agreeToContract" "click #contract-agreement-button": "agreeToContract"
"click #create-account-button": "createAccount" "click #create-account-button": "createAccount"
"click .login-link": "setHashToOpenModalAutomatically" "click .login-link": "setHashToOpenModalAutomatically"
"keydown": "checkForFormSubmissionEnterPress"
constructor: (options) -> constructor: (options) ->
@ -78,6 +79,9 @@ module.exports = class EmployerSignupView extends View
handleAgreementFailure: (error) -> handleAgreementFailure: (error) ->
alert "There was an error signing the contract. Please contact team@codecombat.com with this error: #{error.responseText}" alert "There was an error signing the contract. Please contact team@codecombat.com with this error: #{error.responseText}"
checkForFormSubmissionEnterPress: (e) ->
if e.which is 13 then @createAccount(e)
createAccount: (e) => createAccount: (e) =>
window.tracker?.trackEvent 'Finished Employer Signup' window.tracker?.trackEvent 'Finished Employer Signup'
e.stopPropagation() e.stopPropagation()

View file

@ -1,6 +1,7 @@
ModalView = require 'views/kinds/ModalView' ModalView = require 'views/kinds/ModalView'
template = require 'templates/modal/versions' template = require 'templates/modal/versions'
tableTemplate = require 'templates/kinds/table' tableTemplate = require 'templates/kinds/table'
DeltaView = require 'views/editor/delta'
class VersionsViewCollection extends Backbone.Collection class VersionsViewCollection extends Backbone.Collection
url: "" url: ""
@ -20,6 +21,9 @@ module.exports = class VersionsModalView extends ModalView
id: "" id: ""
url: "" url: ""
page: "" page: ""
events:
'change input.select': 'onSelectionChanged'
constructor: (options, @ID, @model) -> constructor: (options, @ID, @model) ->
super options super options
@ -36,6 +40,18 @@ module.exports = class VersionsModalView extends ModalView
@startsLoading = false @startsLoading = false
@render() @render()
onSelectionChanged: ->
rows = @$el.find 'input.select:checked'
deltaEl = @$el.find '.delta-view'
@removeSubView(@deltaView) if @deltaView
@deltaView = null
if rows.length isnt 2 then return
laterVersion = new @model(_id:$(rows[0]).val())
earlierVersion = new @model(_id:$(rows[1]).val())
@deltaView = new DeltaView({model:earlierVersion, comparisonModel:laterVersion, skipPaths:['_id','version', 'commitMessage', 'parent', 'created', 'slug', 'index']})
@insertSubView(@deltaView, deltaEl)
getRenderData: (context={}) -> getRenderData: (context={}) ->
context = super(context) context = super(context)
context.page = @page context.page = @page

View file

@ -38,7 +38,7 @@ module.exports = class SimulateTabView extends CocoView
# Simulations # Simulations
onSimulateButtonClick: (e) -> onSimulateButtonClick: (e) ->
$("#simulate-button").prop "disabled",true $("#simulate-button").prop "disabled", true
$("#simulate-button").text "Simulating..." $("#simulate-button").text "Simulating..."
@simulator.fetchAndSimulateTask() @simulator.fetchAndSimulateTask()

View file

@ -38,7 +38,7 @@ module.exports = class LadderView extends RootView
super(options) super(options)
@level = @supermodel.loadModel(new Level(_id:@levelID), 'level').model @level = @supermodel.loadModel(new Level(_id:@levelID), 'level').model
@sessions = @supermodel.loadCollection(new LevelSessionsCollection(levelID), 'your_sessions').model @sessions = @supermodel.loadCollection(new LevelSessionsCollection(levelID), 'your_sessions').model
@teams = [] @teams = []
onLoaded: -> onLoaded: ->

View file

@ -17,9 +17,9 @@ module.exports = class Problem
@removeMarkerRange() @removeMarkerRange()
buildAnnotation: -> buildAnnotation: ->
return unless @aetherProblem.ranges return unless @aetherProblem.range
text = @aetherProblem.message.replace /^Line \d+: /, '' text = @aetherProblem.message.replace /^Line \d+: /, ''
start = @aetherProblem.ranges[0][0] start = @aetherProblem.range[0]
@annotation = @annotation =
row: start.row, row: start.row,
column: start.col, column: start.col,
@ -33,8 +33,8 @@ module.exports = class Problem
$(@ace.container).append @alertView.el $(@ace.container).append @alertView.el
buildMarkerRange: -> buildMarkerRange: ->
return unless @aetherProblem.ranges return unless @aetherProblem.range
[start, end] = @aetherProblem.ranges[0] [start, end] = @aetherProblem.range
clazz = "problem-marker-#{@aetherProblem.level}" clazz = "problem-marker-#{@aetherProblem.level}"
@markerRange = new Range start.row, start.col, end.row, end.col @markerRange = new Range start.row, start.col, end.row, end.col
@markerRange.start = @ace.getSession().getDocument().createAnchor @markerRange.start @markerRange.start = @ace.getSession().getDocument().createAnchor @markerRange.start

Some files were not shown because too many files have changed in this diff Show more