mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-04-26 05:53:39 -04:00
Merge branch 'master' into production
This commit is contained in:
commit
9e4fdf6d16
116 changed files with 1761 additions and 182 deletions
.gitignore.npmignorepatch_modal.coffeepatches_view.coffeeemployers_view.coffee
app
assets/javascripts/workers
lib
locale
ar.coffeebg.coffeeca.coffeecs.coffeeda.coffeede-AT.coffeede-CH.coffeede-DE.coffeede.coffeeel.coffeeen-AU.coffeeen-GB.coffeeen-US.coffeeen.coffeees-419.coffeees-ES.coffeees.coffeefa.coffeefi.coffeefr.coffeehe.coffeehi.coffeehu.coffeeid.coffeeit.coffeeja.coffeeko.coffeelt.coffeems.coffeenb.coffeenl-BE.coffeenl-NL.coffeenl.coffeenn.coffeeno.coffeepl.coffeept-BR.coffeept-PT.coffeept.coffeero.coffeeru.coffeesk.coffeesl.coffeesr.coffeesv.coffeeth.coffeetr.coffeeuk.coffeeur.coffeevi.coffeezh-HANS.coffeezh-HANT.coffeezh-WUU-HANS.coffeezh-WUU-HANT.coffeezh.coffee
models
styles/editor/level
templates
views
account
editor
article
delta.coffeelevel
component
components_tab_view.coffeeedit.coffeesave_view.coffeescripts_tab_view.coffeesettings_tab_view.coffeesystem
systems_tab_view.coffeekinds
modal
play
11
.gitignore
vendored
11
.gitignore
vendored
|
@ -28,6 +28,9 @@ Thumbs.db
|
|||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
|
||||
# IntelliJ/WebStorm
|
||||
*.iml
|
||||
|
||||
# NPM packages folder.
|
||||
node_modules/
|
||||
bower_components/
|
||||
|
@ -77,4 +80,10 @@ bin/mongo/
|
|||
# windows
|
||||
/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. ###
|
||||
|
|
11
.npmignore
11
.npmignore
|
@ -53,6 +53,9 @@ Thumbs.db
|
|||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
|
||||
# IntelliJ/WebStorm
|
||||
*.iml
|
||||
|
||||
# NPM packages folder.
|
||||
node_modules/
|
||||
|
||||
|
@ -89,6 +92,12 @@ mongo/
|
|||
bin/node/
|
||||
bin/mongo/
|
||||
|
||||
|
||||
# Karma coverage
|
||||
coverage/
|
||||
|
||||
# local settings
|
||||
login.coffee
|
||||
|
||||
# debugging
|
||||
*.heapsnapshot
|
||||
|
||||
|
|
|
@ -14,8 +14,8 @@ if (!Function.prototype.bind) {
|
|||
throw new TypeError("Function.prototype.bind (Shim) - target is not callable");
|
||||
}
|
||||
|
||||
var aArgs = Array.prototype.slice.call(arguments, 1),
|
||||
fToBind = this,
|
||||
var aArgs = Array.prototype.slice.call(arguments, 1),
|
||||
fToBind = this,
|
||||
fNOP = function () {},
|
||||
fBound = function () {
|
||||
return fToBind.apply(this instanceof fNOP && oThis
|
||||
|
@ -83,13 +83,277 @@ self.transferableSupported = function transferableSupported() {
|
|||
var World = self.require('lib/world/world');
|
||||
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.postedErrors = {};
|
||||
self.t0 = new Date();
|
||||
self.firstWorld = args.firstWorld;
|
||||
self.postedErrors = false;
|
||||
self.logsLogged = 0;
|
||||
|
||||
|
||||
try {
|
||||
self.world = new World(args.worldName, args.userCodeMap);
|
||||
if(args.level)
|
||||
|
@ -136,11 +400,11 @@ self.onWorldLoaded = function onWorldLoaded() {
|
|||
};
|
||||
|
||||
self.onWorldError = function onWorldError(error) {
|
||||
if(error instanceof Aether.problems.UserCodeProblem) {
|
||||
if(!self.postedErrors[error.key]) {
|
||||
var problem = error.serialize();
|
||||
self.postMessage({type: 'user-code-problem', problem: problem});
|
||||
self.postedErrors[error.key] = problem;
|
||||
if(error.isUserCodeProblem) {
|
||||
var errorKey = error.userInfo.key;
|
||||
if(!errorKey || !self.postedErrors[errorKey]) {
|
||||
self.postMessage({type: 'user-code-problem', problem: error});
|
||||
self.postedErrors[errorKey] = error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
241
app/lib/Buddha.coffee
Normal file
241
app/lib/Buddha.coffee
Normal 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
|
|
@ -18,16 +18,23 @@ module.exports = class God
|
|||
options ?= {}
|
||||
@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
|
||||
@workerCode = options.workerCode if options.workerCode?
|
||||
@angels = []
|
||||
@firstWorld = true
|
||||
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()
|
||||
#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) ->
|
||||
return if @dead
|
||||
@spells = e.spells
|
||||
@createWorld()
|
||||
@createWorld e.spells
|
||||
|
||||
fillWorkerPool: =>
|
||||
return unless Worker and not @dead
|
||||
|
@ -44,17 +51,40 @@ module.exports = class God
|
|||
@createWorker()
|
||||
|
||||
createWorker: ->
|
||||
worker = new Worker '/javascripts/workers/worker_world.js'
|
||||
worker = new Worker @workerCode
|
||||
worker.creationTime = new Date()
|
||||
worker.addEventListener 'message', @onWorkerMessage
|
||||
worker.addEventListener 'message', @onWorkerMessage(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
|
||||
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', @onWorkerMessage
|
||||
switch event.data.type
|
||||
when "worker-initialized"
|
||||
worker.initialized = true
|
||||
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: ->
|
||||
freeAngel = null
|
||||
|
@ -83,10 +113,10 @@ module.exports = class God
|
|||
|
||||
angelUserCodeProblem: (angel, problem) ->
|
||||
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
|
||||
|
||||
createWorld: ->
|
||||
createWorld: (@spells) ->
|
||||
#console.log @id + ': "Let there be light upon', @world.name + '!"'
|
||||
unless Worker? # profiling world simulation is easier on main thread, or we are IE9
|
||||
setTimeout @simulateWorld, 1
|
||||
|
@ -101,26 +131,59 @@ module.exports = class God
|
|||
#console.log "going to run world with code", @getUserCodeMap()
|
||||
angel.worker.postMessage {func: 'runWorld', args: {
|
||||
worldName: @level.name
|
||||
userCodeMap: @getUserCodeMap()
|
||||
userCodeMap: @getUserCodeMap(spells)
|
||||
level: @level
|
||||
firstWorld: @firstWorld
|
||||
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) ->
|
||||
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
|
||||
angel.free()
|
||||
return if @latestWorldCreation? and worldCreation < @latestWorldCreation
|
||||
@latestWorldCreation = worldCreation
|
||||
@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
|
||||
World.deserialize serialized, @worldClassMap, @lastSerializedWorldFrames, worldCreation, @finishBeholdingWorld
|
||||
World.deserialize serialized, @worldClassMap, @lastSerializedWorldFrames, @finishBeholdingWorld
|
||||
window.BOX2D_ENABLED = true
|
||||
@lastSerializedWorldFrames = serialized.frames
|
||||
|
||||
finishBeholdingWorld: (newWorld) =>
|
||||
newWorld.findFirstChangedFrame @world
|
||||
@world = newWorld
|
||||
@currentUserCodeMap = @filterUserCodeMapWhenFromWorld @world.userCodeMap
|
||||
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)
|
||||
for scriptNote in @world.scriptNotes
|
||||
|
@ -131,6 +194,23 @@ module.exports = class God
|
|||
unless _.find @angels, 'busy'
|
||||
@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: ->
|
||||
userCodeMap = {}
|
||||
for spellKey, spell of @spells
|
||||
|
@ -144,6 +224,10 @@ module.exports = class God
|
|||
@dead = true
|
||||
Backbone.Mediator.unsubscribe('tome:cast-spells', @onTomeCast, @)
|
||||
@goalManager?.destroy()
|
||||
@debugWorker?.terminate()
|
||||
@debugWorker?.removeEventListener 'message', @onDebugWorkerMessage
|
||||
@debugWorker ?= null
|
||||
@currentUserCodeMap = null
|
||||
@goalManager = null
|
||||
@fillWorkerPool = null
|
||||
@simulateWorld = null
|
||||
|
@ -171,7 +255,7 @@ module.exports = class God
|
|||
@latestGoalStates = @testGM?.getGoalStates()
|
||||
serialized = @testWorld.serialize().serializedWorld
|
||||
window.BOX2D_ENABLED = false
|
||||
World.deserialize serialized, @worldClassMap, @lastSerializedWorldFrames, @t0, @finishBeholdingWorld
|
||||
World.deserialize serialized, @worldClassMap, @lastSerializedWorldFrames, @finishBeholdingWorld
|
||||
window.BOX2D_ENABLED = true
|
||||
@lastSerializedWorldFrames = serialized.frames
|
||||
|
||||
|
@ -255,7 +339,7 @@ class Angel
|
|||
|
||||
testWorker: =>
|
||||
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
|
||||
@worker.postMessage {func: 'reportIn'}
|
||||
@condemnTimeout = _.delay @condemnWorker, @infiniteLoopTimeoutDuration
|
||||
|
@ -271,6 +355,7 @@ class Angel
|
|||
switch event.data.type
|
||||
when 'worker-initialized'
|
||||
console.log "Worker", @id, "initialized after", ((new Date()) - @worker.creationTime), "ms (we had been waiting for it)"
|
||||
@worker.initialized = true
|
||||
when 'new-world'
|
||||
@god.beholdWorld @, event.data.serialized, event.data.goalStates
|
||||
when 'world-load-progress-changed'
|
||||
|
|
|
@ -59,13 +59,13 @@ module.exports = class LevelLoader extends CocoClass
|
|||
url = "/db/level/#{@levelID}/session"
|
||||
url += "?team=#{@team}" if @team
|
||||
|
||||
@session = new LevelSession().setURL url
|
||||
@supermodel.loadModel(@session, 'level_session', {cache:false})
|
||||
session = new LevelSession().setURL url
|
||||
@session = @supermodel.loadModel(session, 'level_session', {cache:false}).model
|
||||
@session.once 'sync', -> @url = -> '/db/level.session/' + @id
|
||||
|
||||
if @opponentSessionID
|
||||
@opponentSession = new LevelSession().setURL "/db/level_session/#{@opponentSessionID}"
|
||||
@supermodel.loadModel(@opponentSession, 'opponent_session')
|
||||
opponentSession = new LevelSession().setURL "/db/level_session/#{@opponentSessionID}"
|
||||
@opponentSession = @supermodel.loadModel(opponentSession, 'opponent_session').model
|
||||
|
||||
# Supermodel (Level) Loading
|
||||
|
||||
|
@ -155,6 +155,7 @@ module.exports = class LevelLoader extends CocoClass
|
|||
# Building sprite sheets
|
||||
|
||||
buildSpriteSheetsForThangType: (thangType) ->
|
||||
return if @headless
|
||||
@grabThangTypeTeams() unless @thangTypeTeams
|
||||
for team in @thangTypeTeams[thangType.get('original')] ? [null]
|
||||
spriteOptions = {resolutionFactor: 4, async: false}
|
||||
|
|
|
@ -16,7 +16,7 @@ init = ->
|
|||
module.exports.createUser = (userObject, failure=backboneFailure, nextURL=null) ->
|
||||
user = new User(userObject)
|
||||
user.save({}, {
|
||||
error: (model,jqxhr,options) ->
|
||||
error: (model,jqxhr,options) ->
|
||||
error = parseServerError(jqxhr.responseText)
|
||||
property = error.property if error.property
|
||||
if jqxhr.status is 409 and property is 'name'
|
||||
|
@ -26,12 +26,12 @@ module.exports.createUser = (userObject, failure=backboneFailure, nextURL=null)
|
|||
genericFailure(jqxhr)
|
||||
success: -> if nextURL then window.location.href = nextURL else window.location.reload()
|
||||
})
|
||||
|
||||
|
||||
module.exports.createUserWithoutReload = (userObject, failure=backboneFailure) ->
|
||||
user = new User(userObject)
|
||||
user.save({}, {
|
||||
error: failure
|
||||
success: ->
|
||||
success: ->
|
||||
Backbone.Mediator.publish("created-user-without-reload")
|
||||
})
|
||||
|
||||
|
|
|
@ -2,15 +2,21 @@ SuperModel = require 'models/SuperModel'
|
|||
CocoClass = require 'lib/CocoClass'
|
||||
LevelLoader = require 'lib/LevelLoader'
|
||||
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
|
||||
|
||||
constructor: ->
|
||||
constructor: (@options) ->
|
||||
@options ?= {}
|
||||
_.extend @, Backbone.Events
|
||||
@trigger 'statusUpdate', 'Starting simulation!'
|
||||
@retryDelayInSeconds = 10
|
||||
@taskURL = '/queue/scoring'
|
||||
@simulatedByYou = 0
|
||||
@god = new God maxWorkerPoolSize: 1, maxAngels: 1, workerCode: @options.workerCode # Start loading worker.
|
||||
|
||||
destroy: ->
|
||||
@off()
|
||||
|
@ -19,6 +25,17 @@ module.exports = class Simulator extends CocoClass
|
|||
|
||||
fetchAndSimulateTask: =>
|
||||
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!'
|
||||
$.ajax
|
||||
url: @taskURL
|
||||
|
@ -32,7 +49,9 @@ module.exports = class Simulator extends CocoClass
|
|||
@simulateAnotherTaskAfterDelay()
|
||||
|
||||
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: =>
|
||||
|
@ -53,7 +72,6 @@ module.exports = class Simulator extends CocoClass
|
|||
return
|
||||
|
||||
@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
|
||||
if @supermodel.finished()
|
||||
|
@ -63,7 +81,9 @@ module.exports = class Simulator extends CocoClass
|
|||
|
||||
simulateGame: ->
|
||||
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()
|
||||
@setupGod()
|
||||
|
||||
|
@ -74,6 +94,7 @@ module.exports = class Simulator extends CocoClass
|
|||
@simulateAnotherTaskAfterDelay()
|
||||
|
||||
assignWorldAndLevelFromLevelLoaderAndDestroyIt: ->
|
||||
console.log "Assigning world and level"
|
||||
@world = @levelLoader.world
|
||||
@level = @levelLoader.level
|
||||
@levelLoader.destroy()
|
||||
|
@ -81,18 +102,45 @@ module.exports = class Simulator extends CocoClass
|
|||
|
||||
setupGod: ->
|
||||
@god.level = @level.serialize @supermodel
|
||||
@god.worldClassMap = @world.classMap
|
||||
@god.setWorldClassMap @world.classMap
|
||||
@setupGoalManager()
|
||||
@setupGodSpells()
|
||||
|
||||
|
||||
setupGoalManager: ->
|
||||
@god.goalManager = new GoalManager @world, @level.get 'goals'
|
||||
@god.setGoalManager new GoalManager(@world, @level.get 'goals')
|
||||
|
||||
|
||||
commenceSimulationAndSetupCallback: ->
|
||||
@god.createWorld()
|
||||
@god.createWorld @generateSpellsObject()
|
||||
Backbone.Mediator.subscribeOnce 'god:infinite-loop', @onInfiniteLoop, @
|
||||
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: ->
|
||||
console.warn "Skipping infinitely looping game."
|
||||
@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!'
|
||||
console.log "Sending result back to server!"
|
||||
|
||||
if @options.headlessClient and @options.testing
|
||||
return @fetchAndSimulateTask()
|
||||
|
||||
$.ajax
|
||||
url: "/queue/scoring"
|
||||
data: results
|
||||
|
@ -117,8 +168,11 @@ module.exports = class Simulator extends CocoClass
|
|||
handleTaskResultsTransferSuccess: (result) =>
|
||||
console.log "Task registration result: #{JSON.stringify result}"
|
||||
@trigger 'statusUpdate', 'Results were successfully sent back to server!'
|
||||
simulatedBy = parseInt($('#simulated-by-you').text(), 10) + 1
|
||||
$('#simulated-by-you').text(simulatedBy)
|
||||
console.log "Simulated by you: " + @simulatedByYou
|
||||
@simulatedByYou++
|
||||
unless @options.headlessClient
|
||||
simulatedBy = parseInt($('#simulated-by-you').text(), 10) + 1
|
||||
$('#simulated-by-you').text(simulatedBy)
|
||||
|
||||
handleTaskResultsTransferError: (error) =>
|
||||
@trigger 'statusUpdate', 'There was an error sending the results back to the server.'
|
||||
|
@ -144,7 +198,6 @@ module.exports = class Simulator extends CocoClass
|
|||
sessions: []
|
||||
|
||||
for session in @task.getSessions()
|
||||
|
||||
sessionResult =
|
||||
sessionID: session.sessionID
|
||||
submitDate: session.submitDate
|
||||
|
@ -242,8 +295,8 @@ module.exports = class Simulator extends CocoClass
|
|||
functionName: methodName
|
||||
protectAPI: useProtectAPI
|
||||
includeFlow: false
|
||||
requiresThis: true
|
||||
yieldConditionally: false
|
||||
globals: ['Vector', '_']
|
||||
problems:
|
||||
jshint_W040: {level: "ignore"}
|
||||
jshint_W030: {level: "ignore"} # aether_NoEffect instead
|
||||
|
|
|
@ -379,7 +379,9 @@ module.exports = class SpriteParser
|
|||
argsSource = argsSource.replace(/cjs(.+)\)/, '"createjs$1)"') # turns cjs.Ease.get(0.5)
|
||||
|
||||
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."
|
||||
return
|
||||
callExpressions.push {n: name, a: args}
|
||||
|
|
|
@ -168,10 +168,12 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
stop: ->
|
||||
@imageObject?.stop?()
|
||||
mark.stop() for name, mark of @marks
|
||||
@stopped = true
|
||||
|
||||
play: ->
|
||||
@imageObject?.play?()
|
||||
mark.play() for name, mark of @marks
|
||||
@stopped = false
|
||||
|
||||
update: (frameChanged) ->
|
||||
# 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: ->
|
||||
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: ->
|
||||
p1 = if @possessed then @shadow.pos else @thang.pos
|
||||
|
@ -495,6 +498,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
|
||||
updateEffectMarks: ->
|
||||
return if _.isEqual @thang.effectNames, @previousEffectNames
|
||||
return if @stopped
|
||||
for effect in @thang.effectNames
|
||||
mark = @addMark effect, @options.floatingLayer, effect
|
||||
mark.statusEffect = true
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = class Mark extends CocoClass
|
|||
size = @sprite.getAverageDimension()
|
||||
size += 60 if @name is 'selection'
|
||||
size += 60 if @name is 'repair'
|
||||
size *= @sprite.scaleFactor
|
||||
scale = size / {selection: 128, target: 128, repair: 320, highlight: 160}[@name]
|
||||
if @sprite?.thang.spriteName.search(/(dungeon|indoor).wall/i) isnt -1
|
||||
scale *= 2
|
||||
|
|
|
@ -221,12 +221,12 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
onCastSpells: -> @stop()
|
||||
|
||||
play: ->
|
||||
sprite.imageObject.play() for sprite in @spriteArray
|
||||
sprite.play() for sprite in @spriteArray
|
||||
@selectionMark?.play()
|
||||
@targetMark?.play()
|
||||
|
||||
stop: ->
|
||||
sprite.imageObject.stop() for sprite in @spriteArray
|
||||
sprite.stop() for sprite in @spriteArray
|
||||
@selectionMark?.stop()
|
||||
@targetMark?.stop()
|
||||
|
||||
|
|
|
@ -65,6 +65,7 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
'god:new-world-created': 'onNewWorld'
|
||||
'tome:cast-spells': 'onCastSpells'
|
||||
'level-set-letterbox': 'onSetLetterbox'
|
||||
'application:idle-changed': 'onIdleChanged'
|
||||
|
||||
shortcuts:
|
||||
'ctrl+\\, ⌘+\\': 'onToggleDebug'
|
||||
|
@ -304,15 +305,32 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
@spriteBoss.stop()
|
||||
@playbackOverScreen.show()
|
||||
@ended = true
|
||||
@setPaused true
|
||||
Backbone.Mediator.publish 'surface:playback-ended'
|
||||
else if @currentFrame < @world.totalFrames and @ended
|
||||
@spriteBoss.play()
|
||||
@playbackOverScreen.hide()
|
||||
@ended = false
|
||||
@setPaused false
|
||||
Backbone.Mediator.publish 'surface:playback-restarted'
|
||||
|
||||
@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: ->
|
||||
@casting = true
|
||||
@wasPlayingWhenCastingBegan = @playing
|
||||
|
@ -575,8 +593,6 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
@paths.parent.removeChild @paths
|
||||
@paths = null
|
||||
|
||||
# Screenshot
|
||||
|
||||
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
|
||||
[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"
|
||||
screenshot = document.createElement("img")
|
||||
screenshot.src = imageData
|
||||
#$('body').append(screenshot)
|
||||
@stage.uncache()
|
||||
imageData
|
||||
|
|
|
@ -204,7 +204,7 @@ module.exports = class GoalManager extends CocoClass
|
|||
|
||||
arrays = (prop for prop in whos when prop?.length)
|
||||
return unless arrays.length
|
||||
state[progressObjectName] = {}
|
||||
state[progressObjectName] ?= {}
|
||||
for array in arrays
|
||||
for thang in array
|
||||
if @thangTeams[thang]?
|
||||
|
@ -235,7 +235,7 @@ module.exports = class GoalManager extends CocoClass
|
|||
numNeeded = goal.howMany ? Math.max(1, _.size stateThangs)
|
||||
else
|
||||
# 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
|
||||
console.log "needed", numNeeded, "done", numDone, "of total", _.size(stateThangs), "with how many", goal.howMany, "and stateThangs", stateThangs
|
||||
return unless numDone >= numNeeded
|
||||
|
|
|
@ -29,8 +29,6 @@ module.exports.thangNames = thangNames =
|
|||
"Phineas"
|
||||
"Ferb"
|
||||
"Felix"
|
||||
"Arthur"
|
||||
"Galahad"
|
||||
"Ezra"
|
||||
"Lucian"
|
||||
"Augustus"
|
||||
|
@ -174,6 +172,7 @@ module.exports.thangNames = thangNames =
|
|||
"Snortt"
|
||||
"Kog"
|
||||
"Ursa"
|
||||
"Ragtime"
|
||||
]
|
||||
"Ogre Munchkin F": [
|
||||
"Iyert"
|
||||
|
@ -184,6 +183,7 @@ module.exports.thangNames = thangNames =
|
|||
"Dosha"
|
||||
"Inski"
|
||||
"Lacos"
|
||||
"Upfish"
|
||||
]
|
||||
"Ogre M": [
|
||||
"Krogg"
|
||||
|
@ -202,6 +202,7 @@ module.exports.thangNames = thangNames =
|
|||
"Mokrul"
|
||||
"Polifemo"
|
||||
"Muthyala"
|
||||
"Saltporker"
|
||||
]
|
||||
"Ogre F": [
|
||||
"Nareng"
|
||||
|
@ -231,6 +232,8 @@ module.exports.thangNames = thangNames =
|
|||
"Tuguro"
|
||||
"York"
|
||||
"Ork'han"
|
||||
"Roast Beefy"
|
||||
"Haggar"
|
||||
]
|
||||
"Ogre Fangrider": [
|
||||
"Dreek"
|
||||
|
@ -272,6 +275,7 @@ module.exports.thangNames = thangNames =
|
|||
"Gogg"
|
||||
"Ghuk"
|
||||
"Makas"
|
||||
"Drun"
|
||||
]
|
||||
"Ogre Thrower": [
|
||||
"Kyrgg"
|
||||
|
@ -293,10 +297,12 @@ module.exports.thangNames = thangNames =
|
|||
"Burl": [
|
||||
"Borlit"
|
||||
"Burlosh"
|
||||
"Dorf"
|
||||
]
|
||||
"Griffin Rider": [
|
||||
"Aeoldan"
|
||||
"Bestarius"
|
||||
|
||||
]
|
||||
"Potion Master": [
|
||||
"Snake"
|
||||
|
@ -305,6 +311,7 @@ module.exports.thangNames = thangNames =
|
|||
"Arora"
|
||||
"Curie"
|
||||
"Clause"
|
||||
"Vanders"
|
||||
]
|
||||
"Librarian": [
|
||||
"Hushbaum"
|
||||
|
@ -318,4 +325,30 @@ module.exports.thangNames = thangNames =
|
|||
"Ryder"
|
||||
"Thoron"
|
||||
"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"
|
||||
]
|
||||
|
|
|
@ -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)
|
||||
sameTeam = @superteamIndex and cat.superteamIndex is @superteamIndex
|
||||
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)
|
||||
return false if @ground and @air and cat.ground and cat.air
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ WorldScriptNote = require './world_script_note'
|
|||
{now, consolidateThangs, typedArraySupport} = require './world_utils'
|
||||
Component = require 'lib/world/component'
|
||||
System = require 'lib/world/system'
|
||||
|
||||
PROGRESS_UPDATE_INTERVAL = 200
|
||||
DESERIALIZATION_INTERVAL = 20
|
||||
|
||||
|
@ -72,14 +71,18 @@ module.exports = class World
|
|||
(@runtimeErrors ?= []).push error
|
||||
(@unhandledRuntimeErrors ?= []).push error
|
||||
|
||||
loadFrames: (loadedCallback, errorCallback, loadProgressCallback) ->
|
||||
loadFrames: (loadedCallback, errorCallback, loadProgressCallback, skipDeferredLoading, loadUntilFrame) ->
|
||||
return if @aborted
|
||||
unless @thangs.length
|
||||
console.log "Warning: loadFrames called on empty World (no thangs)."
|
||||
t1 = now()
|
||||
@t0 ?= t1
|
||||
if loadUntilFrame
|
||||
frameToLoadUntil = loadUntilFrame + 1
|
||||
else
|
||||
frameToLoadUntil = @totalFrames
|
||||
i = @frames.length
|
||||
while i < @totalFrames
|
||||
while i < frameToLoadUntil
|
||||
try
|
||||
@getFrame(i)
|
||||
++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
|
||||
console.log(' Loaded', i, 'of', @totalFrames, "(+" + (t2 - @t0).toFixed(0) + "ms)")
|
||||
@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
|
||||
@ended = true
|
||||
system.finish @thangs for system in @systems
|
||||
unless loadUntilFrame
|
||||
@ended = true
|
||||
system.finish @thangs for system in @systems
|
||||
loadProgressCallback? 1
|
||||
loadedCallback()
|
||||
|
||||
|
@ -221,7 +233,7 @@ module.exports = class World
|
|||
@scriptNotes.push scriptNote
|
||||
return unless @goalManager
|
||||
@goalManager.submitWorldGenerationEvent(channel, event, @frames.length)
|
||||
|
||||
|
||||
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
|
||||
{serializedWorld: o, transferableObjects: [o.storageBuffer]}
|
||||
|
||||
@deserialize: (o, classMap, oldSerializedWorldFrames, worldCreationTime, finishedWorldCallback) ->
|
||||
@deserialize: (o, classMap, oldSerializedWorldFrames, finishedWorldCallback) ->
|
||||
# Code hotspot; optimize it
|
||||
#console.log "Deserializing", o, "length", JSON.stringify(o).length
|
||||
#console.log JSON.stringify(o)
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "български език", englishDescri
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "български език", englishDescri
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr
|
|||
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_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"
|
||||
tome_minion_spells: "Vaše oblíbená kouzla"
|
||||
tome_read_only_spells: "Kouzla jen pro čtení"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans
|
|||
multiplayer_hint_label: "Tip:"
|
||||
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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "Instruktioner"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription:
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription:
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
|
|||
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_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"
|
||||
tome_minion_spells: "Die Zaubersprüche Deiner Knechte"
|
||||
tome_read_only_spells: "Nur-lesen Zauberspüche"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "Deutsch", englishDescription: "German", tra
|
|||
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_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"
|
||||
tome_minion_spells: "Die Zaubersprüche Deiner Knechte"
|
||||
tome_read_only_spells: "Nur-lesen Zauberspüche"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Deutsch", englishDescription: "German", tra
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "ελληνικά", englishDescription: "Gre
|
|||
multiplayer_hint_label: "Συμβουλή:"
|
||||
multiplayer_hint: " Κάντε κλικ στο σύνδεσμο για να επιλέξετε όλα, στη συνέχεια, πατήστε την Apple-C ή Ctrl-C για να αντιγράψετε το σύνδεσμο."
|
||||
multiplayer_coming_soon: "Περισσότερα multiplayer χαρακτιριστηκα προσεχως!"
|
||||
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "Οδηγός"
|
||||
tome_minion_spells: "Ξόρκια για τα τσιράκια σας"
|
||||
tome_read_only_spells: "Ξορκια μονο για αναγνωση"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "ελληνικά", englishDescription: "Gre
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -712,3 +712,14 @@
|
|||
files: "Files"
|
||||
top_simulators: "Top Simulators"
|
||||
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"
|
||||
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip
|
|||
multiplayer_hint_label: "Consejo:"
|
||||
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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "Guía"
|
||||
tome_minion_spells: "Hechizos de tus Secuaces"
|
||||
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"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
|
|||
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_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"
|
||||
tome_minion_spells: "Los hechizos de tus súbditos"
|
||||
tome_read_only_spells: "Hechizos de solo lectura"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "español", englishDescription: "Spanish", t
|
|||
multiplayer_hint_label: "Consejo:"
|
||||
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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "Guía"
|
||||
tome_minion_spells: "Hechizos de tus Secuaces"
|
||||
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"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian",
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian",
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
|
|||
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_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"
|
||||
tome_minion_spells: "Les sorts de vos soldats"
|
||||
tome_read_only_spells: "Sorts en lecture-seule"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew",
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew",
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t
|
|||
multiplayer_hint_label: "Tipp:"
|
||||
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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "Útmutató"
|
||||
tome_minion_spells: "Egységeid varázslatai"
|
||||
tome_read_only_spells: "Csak olvasható varázslatok"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t
|
|||
multiplayer_hint_label: "Suggerimento:"
|
||||
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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "Guida"
|
||||
tome_minion_spells: "Incantesimi dei tuoi seguaci"
|
||||
tome_read_only_spells: "Incantesimi in sola lettura"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese",
|
|||
multiplayer_hint_label: "ヒント:"
|
||||
multiplayer_hint: " リンクを選択後、 ⌘-C(MacOS) or Ctrl-C(Windows) でリンクをコピーできます。"
|
||||
multiplayer_coming_soon: "今後より多くのマルチプレイ機能が追加されます。"
|
||||
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "ガイド"
|
||||
tome_minion_spells: "操作できるキャラクターの呪文"
|
||||
tome_read_only_spells: "読込専用の呪文"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese",
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t
|
|||
multiplayer_hint_label: "힌트:"
|
||||
multiplayer_hint: " 모두 선택하려면 링크를 클릭하세요, 그리고 ⌘-C 또는 Ctrl-C 를 눌러서 링크를 복사하세요."
|
||||
multiplayer_coming_soon: "곧 좀 더 다양한 멀티플레이어 모드가 업데이트 됩니다!"
|
||||
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "가이드"
|
||||
tome_minion_spells: "당신 미니언의' 마법"
|
||||
tome_read_only_spells: "읽기 전용 마법"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg
|
|||
multiplayer_hint_label: "Hint:"
|
||||
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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "Guide"
|
||||
tome_minion_spells: "Din Minions' Trylleformularer"
|
||||
tome_read_only_spells: "Kun-Lesbare Trylleformularer"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription:
|
|||
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_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"
|
||||
tome_minion_spells: "Jouw Minions' Spreuken"
|
||||
tome_read_only_spells: "Read-Only Spreuken"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription:
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
|
|||
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_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"
|
||||
tome_minion_spells: "Jouw Minions' Spreuken"
|
||||
tome_read_only_spells: "Read-Only Spreuken"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -16,7 +16,7 @@ module.exports = nativeDescription: "Nederlands", englishDescription: "Dutch", t
|
|||
play: "Spelen"
|
||||
retry: "Probeer opnieuw"
|
||||
watch: "Volgen"
|
||||
unwatch: "Ontvolgen"
|
||||
unwatch: "Ontvolgen"
|
||||
submit_patch: "Correctie Opsturen"
|
||||
|
||||
units:
|
||||
|
@ -199,7 +199,7 @@ module.exports = nativeDescription: "Nederlands", englishDescription: "Dutch", t
|
|||
|
||||
employers:
|
||||
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_many: "veel"
|
||||
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: " 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "Handleiding"
|
||||
tome_minion_spells: "Jouw Minions' Spreuken"
|
||||
tome_read_only_spells: "Read-Only Spreuken"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Nederlands", englishDescription: "Dutch", t
|
|||
user_names: "Gebruikersnamen"
|
||||
files: "Bestanden"
|
||||
top_simulators: "Top Simulatoren"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr
|
|||
multiplayer_hint_label: "Hint:"
|
||||
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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "Guide"
|
||||
tome_minion_spells: "Din Minions' Trylleformularer"
|
||||
tome_read_only_spells: "Kun-Lesbare Trylleformularer"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish
|
|||
multiplayer_hint_label: "Podpowiedź:"
|
||||
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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "Przewodnik"
|
||||
tome_minion_spells: "Czary twojego podopiecznego"
|
||||
tome_read_only_spells: "Czary tylko do odczytu"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
|
|||
multiplayer_hint_label: "Dica:"
|
||||
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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "Guia"
|
||||
tome_minion_spells: "Magias dos seus subordinados"
|
||||
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"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "Português europeu", englishDescription: "P
|
|||
multiplayer_hint_label: "Dica:"
|
||||
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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "Guia"
|
||||
tome_minion_spells: "Feitiços dos teus Minions"
|
||||
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"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "português", englishDescription: "Portugues
|
|||
multiplayer_hint_label: "Dica:"
|
||||
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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "Guia"
|
||||
tome_minion_spells: "Magias dos seus subordinados"
|
||||
tome_read_only_spells: "Magias não editáveis"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "português", englishDescription: "Portugues
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman
|
|||
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_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"
|
||||
tome_minion_spells: "Vrăjile Minion-ilor tăi"
|
||||
tome_read_only_spells: "Vrăji Read-Only"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
|
|||
multiplayer_hint_label: "Подсказка: "
|
||||
multiplayer_hint: "кликните на ссылку, чтобы выделить её, затем нажмите ⌘-С или Ctrl-C, чтобы скопировать."
|
||||
multiplayer_coming_soon: "Больше возможностей мультиплеера на подходе!"
|
||||
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "Руководство"
|
||||
tome_minion_spells: "Заклинания ваших миньонов"
|
||||
tome_read_only_spells: "Заклинания только для чтения"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak",
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak",
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian
|
|||
multiplayer_hint_label: "Мала помоћ"
|
||||
multiplayer_hint: " Кликни на линк да обележиш све, затим притисни Apple-C или Ctrl-C да копираш линк."
|
||||
multiplayer_coming_soon: "Стиже још нових карактеристика!"
|
||||
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "Водич"
|
||||
tome_minion_spells: "Чини твојих поданика"
|
||||
tome_read_only_spells: "Чини које се могу само гледати"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr
|
|||
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_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"
|
||||
tome_minion_spells: "Dina soldaters 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"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra
|
|||
multiplayer_hint_label: "คำใบ้"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t
|
|||
multiplayer_hint_label: "İpucu:"
|
||||
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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "Rehber"
|
||||
tome_minion_spells: "Minyonlarınızın Büyüleri"
|
||||
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"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "українська мова", englishDesc
|
|||
multiplayer_hint_label: "Підказка:"
|
||||
multiplayer_hint: "Натисніть на посилання, щоб обрати всіх, та натисніть Apple-C або Ctrl-C, щоб скопіювати посилання."
|
||||
multiplayer_coming_soon: "Скоро - більше можливостей у мультиплеєрі!"
|
||||
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "Посібник"
|
||||
tome_minion_spells: "Закляття ваших міньонів"
|
||||
tome_read_only_spells: "Закляття тільки для читання"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "українська мова", englishDesc
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu",
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu",
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' 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"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
|
|||
multiplayer_hint_label: "提示:"
|
||||
multiplayer_hint: " 点击全选,然后按 Apple-C(苹果电脑)或 Ctrl-C 复制链接。"
|
||||
multiplayer_coming_soon: "多人游戏的更多特性!"
|
||||
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "指南"
|
||||
tome_minion_spells: "助手的咒语"
|
||||
tome_read_only_spells: "只读的咒语"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese
|
|||
multiplayer_hint_label: "提示:"
|
||||
multiplayer_hint: " 點擊全選,然後按 ⌘-C 或 Ctrl-C 複製連結。"
|
||||
multiplayer_coming_soon: "請期待更多的多人關卡!"
|
||||
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "指南"
|
||||
tome_minion_spells: "助手的咒語"
|
||||
tome_read_only_spells: "唯讀的咒語"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio
|
|||
multiplayer_hint_label: "提醒:"
|
||||
multiplayer_hint: " 點牢全選,再捺 Apple-C(蘋果電腦)要勿 Ctrl-C 複製鏈接。"
|
||||
multiplayer_coming_soon: "多人遊戲還多特性!"
|
||||
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
guide_title: "指南"
|
||||
tome_minion_spells: "下手個咒語"
|
||||
tome_read_only_spells: "只讀個咒語"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -17,7 +17,7 @@ module.exports = nativeDescription: "中文", englishDescription: "Chinese", tra
|
|||
retry: "重试"
|
||||
# watch: "Watch"
|
||||
# unwatch: "Unwatch"
|
||||
# submit_patch: "Submit Patch"
|
||||
submit_patch: "提交补丁"
|
||||
|
||||
units:
|
||||
second: "秒"
|
||||
|
@ -36,7 +36,7 @@ module.exports = nativeDescription: "中文", englishDescription: "Chinese", tra
|
|||
|
||||
nav:
|
||||
play: "玩"
|
||||
# community: "Community"
|
||||
community: "社区"
|
||||
editor: "编辑"
|
||||
blog: "博客"
|
||||
forum: "论坛"
|
||||
|
@ -53,7 +53,7 @@ module.exports = nativeDescription: "中文", englishDescription: "Chinese", tra
|
|||
versions:
|
||||
save_version_title: "保存新版本"
|
||||
new_major_version: "最新主要版本"
|
||||
# cla_prefix: "To save changes, first you must agree to our"
|
||||
cla_prefix: "要保存更改, 首先你必须要统一我们的"
|
||||
# cla_url: "CLA"
|
||||
# cla_suffix: "."
|
||||
cla_agree: "我同意"
|
||||
|
@ -61,7 +61,7 @@ module.exports = nativeDescription: "中文", englishDescription: "Chinese", tra
|
|||
login:
|
||||
sign_up: "注册"
|
||||
log_in: "登录"
|
||||
# logging_in: "Logging In"
|
||||
logging_in: "登录中..."
|
||||
log_out: "登出"
|
||||
recover: "找回账户"
|
||||
|
||||
|
@ -78,7 +78,7 @@ module.exports = nativeDescription: "中文", englishDescription: "Chinese", tra
|
|||
creating: "账户在创新中"
|
||||
sign_up: "注册"
|
||||
log_in: "以密码登录"
|
||||
# social_signup: "Or, you can sign up through Facebook or G+:"
|
||||
social_signup: "或者, 你可以通过Facebook 或者 G+ 注册:"
|
||||
|
||||
home:
|
||||
slogan: "通过游戏学习Javascript脚本语言"
|
||||
|
@ -130,21 +130,21 @@ module.exports = nativeDescription: "中文", englishDescription: "Chinese", tra
|
|||
# learn_more: "Learn more about being a Diplomat"
|
||||
# subscribe_as_diplomat: "Subscribe as a Diplomat"
|
||||
|
||||
# wizard_settings:
|
||||
# title: "Wizard Settings"
|
||||
# customize_avatar: "Customize Your Avatar"
|
||||
# active: "Active"
|
||||
# color: "Color"
|
||||
# group: "Group"
|
||||
# clothes: "Clothes"
|
||||
# trim: "Trim"
|
||||
# cloud: "Cloud"
|
||||
# team: "Team"
|
||||
# spell: "Spell"
|
||||
# boots: "Boots"
|
||||
# hue: "Hue"
|
||||
# saturation: "Saturation"
|
||||
# lightness: "Lightness"
|
||||
wizard_settings:
|
||||
title: "巫师设定"
|
||||
customize_avatar: "设置你的头像"
|
||||
active: "启用"
|
||||
color: "颜色"
|
||||
group: "类别"
|
||||
clothes: "衣服"
|
||||
trim: "条纹"
|
||||
cloud: "云"
|
||||
team: "队伍"
|
||||
spell: "魔法球"
|
||||
boots: "鞋子"
|
||||
hue: "色彩"
|
||||
saturation: "饱和度"
|
||||
lightness: "亮度"
|
||||
|
||||
# account_settings:
|
||||
# title: "Account Settings"
|
||||
|
@ -246,6 +246,7 @@ module.exports = nativeDescription: "中文", englishDescription: "Chinese", tra
|
|||
# multiplayer_hint_label: "Hint:"
|
||||
# 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_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
# guide_title: "Guide"
|
||||
# tome_minion_spells: "Your Minions' Spells"
|
||||
# tome_read_only_spells: "Read-Only Spells"
|
||||
|
@ -710,3 +711,4 @@ module.exports = nativeDescription: "中文", englishDescription: "Chinese", tra
|
|||
# user_names: "User Names"
|
||||
# files: "Files"
|
||||
# top_simulators: "Top Simulators"
|
||||
# source_document: "Source Document"
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
storage = require 'lib/storage'
|
||||
deltasLib = require 'lib/deltas'
|
||||
auth = require 'lib/auth'
|
||||
|
||||
class CocoModel extends Backbone.Model
|
||||
idAttribute: "_id"
|
||||
|
@ -9,6 +8,8 @@ class CocoModel extends Backbone.Model
|
|||
saveBackups: false
|
||||
@schema: null
|
||||
|
||||
getMe: -> @me or @me = require('lib/auth').me
|
||||
|
||||
initialize: ->
|
||||
super()
|
||||
if not @constructor.className
|
||||
|
@ -27,7 +28,7 @@ class CocoModel extends Backbone.Model
|
|||
clone = super()
|
||||
clone.set($.extend(true, {}, if withChanges then @attributes else @_revertAttributes))
|
||||
clone
|
||||
|
||||
|
||||
onError: ->
|
||||
@loading = false
|
||||
|
||||
|
@ -96,7 +97,8 @@ class CocoModel extends Backbone.Model
|
|||
not _.isEqual @attributes, @_revertAttributes
|
||||
|
||||
cloneNewMinorVersion: ->
|
||||
newData = $.extend(null, {}, @attributes)
|
||||
newData = _.clone @attributes
|
||||
|
||||
clone = new @constructor(newData)
|
||||
clone
|
||||
|
||||
|
@ -136,7 +138,7 @@ class CocoModel extends Backbone.Model
|
|||
hasReadAccess: (actor) ->
|
||||
# actor is a User object
|
||||
|
||||
actor ?= auth.me
|
||||
actor ?= @getMe()
|
||||
return true if actor.isAdmin()
|
||||
if @get('permissions')?
|
||||
for permission in @get('permissions')
|
||||
|
@ -148,7 +150,7 @@ class CocoModel extends Backbone.Model
|
|||
hasWriteAccess: (actor) ->
|
||||
# actor is a User object
|
||||
|
||||
actor ?= auth.me
|
||||
actor ?= @getMe()
|
||||
return true if actor.isAdmin()
|
||||
if @get('permissions')?
|
||||
for permission in @get('permissions')
|
||||
|
@ -160,6 +162,10 @@ class CocoModel extends Backbone.Model
|
|||
getDelta: ->
|
||||
differ = deltasLib.makeJSONDiffer()
|
||||
differ.diff @_revertAttributes, @attributes
|
||||
|
||||
getDeltaWith: (otherModel) ->
|
||||
differ = deltasLib.makeJSONDiffer()
|
||||
differ.diff @attributes, otherModel.attributes
|
||||
|
||||
applyDelta: (delta) ->
|
||||
newAttributes = $.extend(true, {}, @attributes)
|
||||
|
@ -170,13 +176,17 @@ class CocoModel extends Backbone.Model
|
|||
delta = @getDelta()
|
||||
deltasLib.expandDelta(delta, @_revertAttributes, @schema())
|
||||
|
||||
getExpandedDeltaWith: (otherModel) ->
|
||||
delta = @getDeltaWith(otherModel)
|
||||
deltasLib.expandDelta(delta, @attributes, @schema())
|
||||
|
||||
watch: (doWatch=true) ->
|
||||
$.ajax("#{@urlRoot}/#{@id}/watch", {type:'PUT', data:{on:doWatch}})
|
||||
@watching = -> doWatch
|
||||
|
||||
watching: ->
|
||||
return me.id in (@get('watchers') or [])
|
||||
|
||||
|
||||
populateI18N: (data, schema, path='') ->
|
||||
# TODO: Better schema/json walking
|
||||
sum = 0
|
||||
|
@ -185,7 +195,7 @@ class CocoModel extends Backbone.Model
|
|||
if schema.properties?.i18n and _.isPlainObject(data) and not data.i18n?
|
||||
data.i18n = {}
|
||||
sum += 1
|
||||
|
||||
|
||||
if _.isPlainObject data
|
||||
for key, value of data
|
||||
numChanged = 0
|
||||
|
@ -193,10 +203,10 @@ class CocoModel extends Backbone.Model
|
|||
if numChanged and not path # should only do this for the root object
|
||||
@set key, value
|
||||
sum += numChanged
|
||||
|
||||
|
||||
if schema.items and _.isArray data
|
||||
sum += @populateI18N(value, schema.items, path+'/'+index) for value, index in data
|
||||
|
||||
|
||||
sum
|
||||
|
||||
@getReferencedModel: (data, schema) ->
|
||||
|
@ -227,13 +237,13 @@ class CocoModel extends Backbone.Model
|
|||
model = new Model()
|
||||
model.url = makeUrlFunc(link)
|
||||
return model
|
||||
|
||||
|
||||
setURL: (url) ->
|
||||
makeURLFunc = (u) -> -> u
|
||||
@url = makeURLFunc(url)
|
||||
@
|
||||
|
||||
|
||||
getURL: ->
|
||||
return if _.isString @url then @url else @url()
|
||||
|
||||
|
||||
module.exports = CocoModel
|
||||
|
|
|
@ -31,8 +31,9 @@ module.exports = class SuperModel extends Backbone.Model
|
|||
|
||||
else
|
||||
@registerModel(model)
|
||||
console.debug 'Registering model', model.getURL()
|
||||
return @addModelResource(model, name, fetchOptions, value).load()
|
||||
res = @addModelResource(model, name, fetchOptions, value)
|
||||
if model.loaded then res.markLoaded() else res.load()
|
||||
return res
|
||||
|
||||
loadCollection: (collection, name, fetchOptions, value=1) ->
|
||||
url = collection.getURL()
|
||||
|
@ -52,7 +53,9 @@ module.exports = class SuperModel extends Backbone.Model
|
|||
@listenToOnce collection, 'sync', (c) ->
|
||||
console.debug 'Registering collection', url
|
||||
@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
|
||||
shouldSaveBackups: (model) -> false
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
#component-patches
|
||||
padding: 0 10px 10px
|
||||
background: white
|
||||
|
||||
|
||||
.patches-view
|
||||
padding: 10px 20px 10px 0px
|
||||
|
||||
.navbar-text
|
||||
float: left
|
||||
|
||||
|
|
|
@ -1,4 +1,14 @@
|
|||
#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
|
||||
float: left
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ block content
|
|||
if user.id != me.id
|
||||
button.btn.edit-settings-button#enter-espionage-mode 007
|
||||
|
||||
if user.get('jobProfile')
|
||||
if user.get('jobProfile') && allowedToViewJobProfile
|
||||
- var profile = user.get('jobProfile');
|
||||
.job-profile-container
|
||||
.job-profile-row
|
||||
|
@ -112,7 +112,11 @@ block content
|
|||
.project-image(style="background-image: url('/file/" + project.picture + "')")
|
||||
p= project.name
|
||||
div!= marked(project.description)
|
||||
|
||||
else if allowedToViewJobProfile
|
||||
.public-profile-container
|
||||
h2 Loading...
|
||||
|
||||
|
||||
else
|
||||
.public-profile-container
|
||||
h2
|
||||
|
|
|
@ -37,10 +37,12 @@ mixin deltaPanel(delta, conflict)
|
|||
|
||||
if delta.conflict && !conflict
|
||||
.panel-body
|
||||
strong MERGE CONFLICT WITH
|
||||
strong(data-i18n="delta.merge_conflict_with") MERGE CONFLICT WITH
|
||||
+deltaPanel(delta.conflict, true)
|
||||
|
||||
.panel-group(id='delta-accordion-'+(counter))
|
||||
for delta in deltas
|
||||
+deltaPanel(delta)
|
||||
if !deltas.length
|
||||
alert.alert-warning(data-i18n="delta.no_changes") No changes
|
||||
|
|
@ -24,7 +24,7 @@ block header
|
|||
li
|
||||
a(href="#editor-level-settings-tab-view", data-toggle="tab", data-i18n="editor.level_tab_settings") Settings
|
||||
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
|
||||
a(href="#editor-level-systems-tab-view", data-toggle="tab", data-i18n="editor.level_tab_systems") Systems
|
||||
li
|
||||
|
|
|
@ -10,15 +10,21 @@ block modal-body-content
|
|||
if dataList
|
||||
table.table.table-condensed
|
||||
tr
|
||||
th
|
||||
th(data-i18n="general.name") Name
|
||||
th(data-i18n="general.version") Version
|
||||
th(data-i18n="general.commit_msg") Commit Message
|
||||
for data in dataList
|
||||
tr
|
||||
td
|
||||
input(type="checkbox", value=data._id).select
|
||||
td
|
||||
a(href="/editor/#{page}/#{data.slug || data._id}")
|
||||
| #{data.name}
|
||||
td #{data.version.major}.#{data.version.minor}
|
||||
td #{data.commitMessage}
|
||||
|
||||
div.delta-container
|
||||
div.delta-view
|
||||
|
||||
block modal-footer-content
|
|
@ -18,7 +18,7 @@ module.exports = class ProfileView extends View
|
|||
super options
|
||||
if @userID is me.id
|
||||
@user = me
|
||||
else
|
||||
else if me.isAdmin() or "employer" in me.get('permissions')
|
||||
@user = User.getByID(@userID)
|
||||
@user.fetch()
|
||||
@listenTo @user, "sync", =>
|
||||
|
@ -27,6 +27,7 @@ module.exports = class ProfileView extends View
|
|||
getRenderData: ->
|
||||
context = super()
|
||||
context.user = @user
|
||||
context.allowedToViewJobProfile = me.isAdmin() or "employer" in me.get('permissions')
|
||||
context.myProfile = @user.id is context.me.id
|
||||
context.marked = marked
|
||||
context.moment = moment
|
||||
|
|
|
@ -49,7 +49,7 @@ module.exports = class ArticleEditView extends View
|
|||
data: data
|
||||
filePath: "db/thang.type/#{@article.get('original')}"
|
||||
schema: Article.schema
|
||||
readOnly: true unless me.isAdmin() or @article.hasWriteAccess(me)
|
||||
readOnly: me.get('anonymous')
|
||||
callbacks:
|
||||
change: @pushChangesToPreview
|
||||
@treema = @$el.find('#article-treema').treema(options)
|
||||
|
|
|
@ -9,19 +9,62 @@ TEXTDIFF_OPTIONS =
|
|||
viewType: 1
|
||||
|
||||
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
|
||||
className: "delta-view"
|
||||
template: template
|
||||
|
||||
constructor: (options) ->
|
||||
super(options)
|
||||
@model = options.model
|
||||
@headModel = options.headModel
|
||||
@expandedDeltas = @model.getExpandedDelta()
|
||||
@expandedDeltas = []
|
||||
@skipPaths = options.skipPaths
|
||||
|
||||
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
|
||||
@headDeltas = @headModel.getExpandedDelta()
|
||||
@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: ->
|
||||
c = super()
|
||||
c.deltas = @expandedDeltas
|
||||
|
|
|
@ -46,11 +46,12 @@ module.exports = class LevelComponentEditView extends View
|
|||
schema = _.cloneDeep LevelComponent.schema
|
||||
schema.properties = _.pick schema.properties, (value, key) => key in @editableSettings
|
||||
schema.required = _.intersection schema.required, @editableSettings
|
||||
|
||||
|
||||
treemaOptions =
|
||||
supermodel: @supermodel
|
||||
schema: schema
|
||||
data: data
|
||||
readonly: me.get('anonymous')
|
||||
callbacks: {change: @onComponentSettingsEdited}
|
||||
@componentSettingsTreema = @$el.find('#edit-component-treema').treema treemaOptions
|
||||
@componentSettingsTreema.build()
|
||||
|
@ -68,6 +69,7 @@ module.exports = class LevelComponentEditView extends View
|
|||
supermodel: @supermodel
|
||||
schema: LevelComponent.schema.properties.configSchema
|
||||
data: @levelComponent.get 'configSchema'
|
||||
readOnly: me.get('anonymous')
|
||||
callbacks: {change: @onConfigSchemaEdited}
|
||||
@configSchemaTreema = @$el.find('#config-schema-treema').treema treemaOptions
|
||||
@configSchemaTreema.build()
|
||||
|
@ -84,13 +86,14 @@ module.exports = class LevelComponentEditView extends View
|
|||
editorEl = $('<div></div>').text(@levelComponent.get('code')).addClass('inner-editor')
|
||||
@$el.find('#component-code-editor').empty().append(editorEl)
|
||||
@editor = ace.edit(editorEl[0])
|
||||
@editor.setReadOnly(me.get('anonymous'))
|
||||
session = @editor.getSession()
|
||||
session.setMode 'ace/mode/coffee'
|
||||
session.setTabSize 2
|
||||
session.setNewLineMode = 'unix'
|
||||
session.setUseSoftTabs true
|
||||
@editor.on('change', @onEditorChange)
|
||||
|
||||
|
||||
onEditorChange: =>
|
||||
@levelComponent.set 'code', @editor.getValue()
|
||||
Backbone.Mediator.publish 'level-component-edited', levelComponent: @levelComponent
|
||||
|
@ -104,7 +107,7 @@ module.exports = class LevelComponentEditView extends View
|
|||
versionHistoryView = new VersionHistoryView {}, @levelComponent.id
|
||||
@openModalView versionHistoryView
|
||||
Backbone.Mediator.publish 'level:view-switched', e
|
||||
|
||||
|
||||
startPatchingComponent: (e) ->
|
||||
@openModalView new SaveVersionModal({model:@levelComponent})
|
||||
Backbone.Mediator.publish 'level:view-switched', e
|
||||
|
@ -117,4 +120,3 @@ module.exports = class LevelComponentEditView extends View
|
|||
destroy: ->
|
||||
@editor?.destroy()
|
||||
super()
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ module.exports = class ComponentsTabView extends View
|
|||
className: 'tab-pane'
|
||||
|
||||
subscriptions:
|
||||
'level-thangs-changed': 'onLevelThangsChanged'
|
||||
'edit-level-component': 'editLevelComponent'
|
||||
'level-component-edited': 'onLevelComponentEdited'
|
||||
'level-component-editing-ended': 'onLevelComponentEditingEnded'
|
||||
|
@ -24,8 +23,8 @@ module.exports = class ComponentsTabView extends View
|
|||
'click #create-new-component-button-no-select': 'createNewLevelComponent'
|
||||
|
||||
onLoaded: ->
|
||||
onLevelThangsChanged: (e) ->
|
||||
thangsData = e.thangsData
|
||||
|
||||
refreshLevelThangsTreema: (thangsData) ->
|
||||
presentComponents = {}
|
||||
for thang in thangsData
|
||||
for component in thang.components
|
||||
|
|
|
@ -29,6 +29,7 @@ module.exports = class EditorLevelView extends View
|
|||
'click #fork-level-start-button': 'startForkingLevel'
|
||||
'click #level-history-button': 'showVersionHistory'
|
||||
'click #patches-tab': -> @patchesView.load()
|
||||
'click #components-tab': -> @componentsTab.refreshLevelThangsTreema @level.get('thangs')
|
||||
'click #level-patch-button': 'startPatchingLevel'
|
||||
'click #level-watch-button': 'toggleWatchLevel'
|
||||
'click #pop-level-i18n-button': -> @level.populateI18N()
|
||||
|
|
|
@ -35,6 +35,7 @@ module.exports = class LevelSaveView extends SaveVersionModal
|
|||
models = if @lastContext.levelNeedsSave then [@level] else []
|
||||
models = models.concat @lastContext.modifiedComponents
|
||||
models = models.concat @lastContext.modifiedSystems
|
||||
models = (m for m in models when m.hasWriteAccess())
|
||||
for changeEl, i in changeEls
|
||||
model = models[i]
|
||||
try
|
||||
|
@ -44,6 +45,7 @@ module.exports = class LevelSaveView extends SaveVersionModal
|
|||
console.error "Couldn't create delta view:", e
|
||||
|
||||
shouldSaveEntity: (m) ->
|
||||
return false unless m.hasWriteAccess()
|
||||
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
|
||||
# Sometimes we have two versions: one in a search collection and one with a URL. We only save changes to the latter.
|
||||
|
|
|
@ -58,7 +58,7 @@ module.exports = class ScriptsTabView extends View
|
|||
thangIDs: thangIDs
|
||||
dimensions: @dimensions
|
||||
supermodel: @supermodel
|
||||
readOnly: true unless me.isAdmin() or @level.hasWriteAccess(me)
|
||||
readOnly: me.get('anonymous')
|
||||
callbacks:
|
||||
change: @onScriptChanged
|
||||
nodeClasses:
|
||||
|
|
|
@ -3,6 +3,7 @@ template = require 'templates/editor/level/settings_tab'
|
|||
Level = require 'models/Level'
|
||||
Surface = require 'lib/surface/Surface'
|
||||
nodes = require './treema_nodes'
|
||||
{me} = require 'lib/auth'
|
||||
|
||||
module.exports = class SettingsTabView extends View
|
||||
id: 'editor-level-settings-tab-view'
|
||||
|
@ -34,7 +35,7 @@ module.exports = class SettingsTabView extends View
|
|||
supermodel: @supermodel
|
||||
schema: schema
|
||||
data: data
|
||||
readOnly: true unless me.isAdmin() or @level.hasWriteAccess(me)
|
||||
readOnly: me.get('anonymous')
|
||||
callbacks: {change: @onSettingsChanged}
|
||||
thangIDs: thangIDs
|
||||
nodeClasses:
|
||||
|
|
|
@ -49,7 +49,7 @@ module.exports = class LevelSystemEditView extends View
|
|||
schema: schema
|
||||
data: data
|
||||
callbacks: {change: @onSystemSettingsEdited}
|
||||
treemaOptions.readOnly = true unless me.isAdmin()
|
||||
treemaOptions.readOnly = me.get('anonymous')
|
||||
@systemSettingsTreema = @$el.find('#edit-system-treema').treema treemaOptions
|
||||
@systemSettingsTreema.build()
|
||||
@systemSettingsTreema.open()
|
||||
|
@ -67,7 +67,7 @@ module.exports = class LevelSystemEditView extends View
|
|||
schema: LevelSystem.schema.properties.configSchema
|
||||
data: @levelSystem.get 'configSchema'
|
||||
callbacks: {change: @onConfigSchemaEdited}
|
||||
treemaOptions.readOnly = true unless me.isAdmin()
|
||||
treemaOptions.readOnly = me.get('anonymous')
|
||||
@configSchemaTreema = @$el.find('#config-schema-treema').treema treemaOptions
|
||||
@configSchemaTreema.build()
|
||||
@configSchemaTreema.open()
|
||||
|
@ -83,7 +83,7 @@ module.exports = class LevelSystemEditView extends View
|
|||
editorEl = $('<div></div>').text(@levelSystem.get('code')).addClass('inner-editor')
|
||||
@$el.find('#system-code-editor').empty().append(editorEl)
|
||||
@editor = ace.edit(editorEl[0])
|
||||
@editor.setReadOnly(not me.isAdmin())
|
||||
@editor.setReadOnly(me.get('anonymous'))
|
||||
session = @editor.getSession()
|
||||
session.setMode 'ace/mode/coffee'
|
||||
session.setTabSize 2
|
||||
|
|
|
@ -53,7 +53,7 @@ module.exports = class SystemsTabView extends View
|
|||
supermodel: @supermodel
|
||||
schema: Level.schema.properties.systems
|
||||
data: systems
|
||||
readOnly: true unless me.isAdmin() or @level.hasWriteAccess(me)
|
||||
readOnly: me.get('anonymous')
|
||||
callbacks:
|
||||
change: @onSystemsChanged
|
||||
select: @onSystemSelected
|
||||
|
|
|
@ -37,9 +37,11 @@ module.exports = class PatchModal extends ModalView
|
|||
if @targetModel.hasWriteAccess()
|
||||
headModel = @originalSource.clone(false)
|
||||
headModel.set(@targetModel.attributes)
|
||||
headModel.loaded = true
|
||||
|
||||
pendingModel = @originalSource.clone(false)
|
||||
pendingModel.applyDelta(@patch.get('delta'))
|
||||
pendingModel.loaded = true
|
||||
|
||||
@deltaView = new DeltaView({model:pendingModel, headModel:headModel})
|
||||
changeEl = @$el.find('.changes-stub')
|
||||
|
|
|
@ -22,7 +22,6 @@ module.exports = class PatchesView extends CocoView
|
|||
@patches = new PatchesCollection([], {}, @model, @status)
|
||||
|
||||
load: ->
|
||||
console.log 'load patches view?'
|
||||
@initPatches()
|
||||
@patches = @supermodel.loadCollection(@patches, 'patches').model
|
||||
@listenTo @patches, 'sync', @onPatchesLoaded
|
||||
|
|
|
@ -50,7 +50,9 @@ module.exports = class EmployersView extends View
|
|||
renderCandidatesAndSetupScrolling: =>
|
||||
@render()
|
||||
$(".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)})
|
||||
|
||||
sortTable: ->
|
||||
|
@ -175,8 +177,13 @@ module.exports = class EmployersView extends View
|
|||
|
||||
onCandidateClicked: (e) ->
|
||||
id = $(e.target).closest('tr').data('candidate-id')
|
||||
window.location.hash = 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}"
|
||||
app.router.navigate url, {trigger: true}
|
||||
else
|
||||
|
|
|
@ -38,9 +38,9 @@ module.exports = class RootView extends CocoView
|
|||
# force the browser to scroll to the hash
|
||||
# also messes with the browser history, so perhaps come up with a better solution
|
||||
super()
|
||||
hash = location.hash
|
||||
location.hash = ''
|
||||
location.hash = hash
|
||||
#hash = location.hash
|
||||
#location.hash = ''
|
||||
#location.hash = hash
|
||||
@renderScrollbar()
|
||||
#@$('.antiscroll-wrap').antiscroll() # not yet, buggy
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ module.exports = class EmployerSignupView extends View
|
|||
"click #contract-agreement-button": "agreeToContract"
|
||||
"click #create-account-button": "createAccount"
|
||||
"click .login-link": "setHashToOpenModalAutomatically"
|
||||
"keydown": "checkForFormSubmissionEnterPress"
|
||||
|
||||
|
||||
constructor: (options) ->
|
||||
|
@ -78,6 +79,9 @@ module.exports = class EmployerSignupView extends View
|
|||
handleAgreementFailure: (error) ->
|
||||
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) =>
|
||||
window.tracker?.trackEvent 'Finished Employer Signup'
|
||||
e.stopPropagation()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
ModalView = require 'views/kinds/ModalView'
|
||||
template = require 'templates/modal/versions'
|
||||
tableTemplate = require 'templates/kinds/table'
|
||||
DeltaView = require 'views/editor/delta'
|
||||
|
||||
class VersionsViewCollection extends Backbone.Collection
|
||||
url: ""
|
||||
|
@ -20,6 +21,9 @@ module.exports = class VersionsModalView extends ModalView
|
|||
id: ""
|
||||
url: ""
|
||||
page: ""
|
||||
|
||||
events:
|
||||
'change input.select': 'onSelectionChanged'
|
||||
|
||||
constructor: (options, @ID, @model) ->
|
||||
super options
|
||||
|
@ -36,6 +40,18 @@ module.exports = class VersionsModalView extends ModalView
|
|||
@startsLoading = false
|
||||
@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={}) ->
|
||||
context = super(context)
|
||||
context.page = @page
|
||||
|
|
|
@ -38,7 +38,7 @@ module.exports = class SimulateTabView extends CocoView
|
|||
# Simulations
|
||||
|
||||
onSimulateButtonClick: (e) ->
|
||||
$("#simulate-button").prop "disabled",true
|
||||
$("#simulate-button").prop "disabled", true
|
||||
$("#simulate-button").text "Simulating..."
|
||||
|
||||
@simulator.fetchAndSimulateTask()
|
||||
|
|
|
@ -38,7 +38,7 @@ module.exports = class LadderView extends RootView
|
|||
super(options)
|
||||
@level = @supermodel.loadModel(new Level(_id:@levelID), 'level').model
|
||||
@sessions = @supermodel.loadCollection(new LevelSessionsCollection(levelID), 'your_sessions').model
|
||||
|
||||
|
||||
@teams = []
|
||||
|
||||
onLoaded: ->
|
||||
|
|
|
@ -17,9 +17,9 @@ module.exports = class Problem
|
|||
@removeMarkerRange()
|
||||
|
||||
buildAnnotation: ->
|
||||
return unless @aetherProblem.ranges
|
||||
return unless @aetherProblem.range
|
||||
text = @aetherProblem.message.replace /^Line \d+: /, ''
|
||||
start = @aetherProblem.ranges[0][0]
|
||||
start = @aetherProblem.range[0]
|
||||
@annotation =
|
||||
row: start.row,
|
||||
column: start.col,
|
||||
|
@ -33,8 +33,8 @@ module.exports = class Problem
|
|||
$(@ace.container).append @alertView.el
|
||||
|
||||
buildMarkerRange: ->
|
||||
return unless @aetherProblem.ranges
|
||||
[start, end] = @aetherProblem.ranges[0]
|
||||
return unless @aetherProblem.range
|
||||
[start, end] = @aetherProblem.range
|
||||
clazz = "problem-marker-#{@aetherProblem.level}"
|
||||
@markerRange = new Range start.row, start.col, end.row, end.col
|
||||
@markerRange.start = @ace.getSession().getDocument().createAnchor @markerRange.start
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue