merge conflict solved

This commit is contained in:
Dominik Kundel 2014-06-18 17:59:26 +02:00
commit d3e0299465
49 changed files with 854 additions and 269 deletions

View file

@ -160,12 +160,7 @@
*
* Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
*/
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
window.runJasmine = function() {
htmlReporter.initialize();
env.execute();
};

View file

@ -1,5 +1,6 @@
// Helper for running tests through Karma.
// Hooks into the test view logic for running tests.
TestView = require('views/test');
require('initialize');
TestView = require('views/TestView');
TestView.runTests();

View file

@ -43,135 +43,14 @@
<script>require('initialize');</script>
<!-- begin LinkedIn code -->
<script>
window.linkedInAsyncInit = function() {
Backbone.Mediator.publish('linkedin-loaded');
};
</script>
<script type="text/javascript" src="http://platform.linkedin.com/in.js">
api_key: 75v8mv4ictvmx6
onLoad: linkedInAsyncInit
authorize: true
</script>
<!-- end LinkedIn code -->
<!-- begin segment.io code -->
<script type="text/javascript">
var analytics=analytics||[];(function(){var e=["identify","track","trackLink","trackForm","trackClick","trackSubmit","page","pageview","ab","alias","ready","group"],t=function(e){return function(){analytics.push([e].concat(Array.prototype.slice.call(arguments,0)))}};for(var n=0;n<e.length;n++)analytics[e[n]]=t(e[n])})(),analytics.load=function(e){var t=document.createElement("script");t.type="text/javascript",t.async=!0,t.src=("https:"===document.location.protocol?"https://":"http://")+"d2dq2ahtl5zl1z.cloudfront.net/analytics.js/v1/"+e+"/analytics.min.js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(t,n)};
analytics.load("jsjzx9n4d2");
</script>
<!-- end segment.io code -->
<!-- begin olark code -->
<script data-cfasync="false" type='text/javascript'>/*<![CDATA[*/window.olark||(function(c){var f=window,d=document,l=f.location.protocol=="https:"?"https:":"http:",z=c.name,r="load";var nt=function(){
f[z]=function(){
(a.s=a.s||[]).push(arguments)};var a=f[z]._={
},q=c.methods.length;while(q--){(function(n){f[z][n]=function(){
f[z]("call",n,arguments)}})(c.methods[q])}a.l=c.loader;a.i=nt;a.p={
0:+new Date};a.P=function(u){
a.p[u]=new Date-a.p[0]};function s(){
a.P(r);f[z](r)}f.addEventListener?f.addEventListener(r,s,false):f.attachEvent("on"+r,s);var ld=function(){function p(hd){
hd="head";return["<",hd,"></",hd,"><",i,' onl' + 'oad="var d=',g,";d.getElementsByTagName('head')[0].",j,"(d.",h,"('script')).",k,"='",l,"//",a.l,"'",'"',"></",i,">"].join("")}var i="body",m=d[i];if(!m){
return setTimeout(ld,100)}a.P(1);var j="appendChild",h="createElement",k="src",n=d[h]("div"),v=n[j](d[h](z)),b=d[h]("iframe"),g="document",e="domain",o;n.style.display="none";m.insertBefore(n,m.firstChild).id=z;b.frameBorder="0";b.id=z+"-loader";if(/MSIE[ ]+6/.test(navigator.userAgent)){
b.src="javascript:false"}b.allowTransparency="true";v[j](b);try{
b.contentWindow[g].open()}catch(w){
c[e]=d[e];o="javascript:var d="+g+".open();d.domain='"+d.domain+"';";b[k]=o+"void(0);"}try{
var t=b.contentWindow[g];t.write(p());t.close()}catch(x){
b[k]=o+'d.write("'+p().replace(/"/g,String.fromCharCode(92)+'"')+'");d.close();'}a.P(2)};ld()};nt()})({
loader: "static.olark.com/jsclient/loader0.js",name:"olark",methods:["configure","extend","declare","identify"]});
/* custom configuration goes here (www.olark.com/documentation) */
olark.identify('1451-787-10-5544');/*]]>*/</script>
<!-- end olark code -->
</head>
<body class="nano clearfix">
<div id="fb-root"></div>
<!-- begin facebook code -->
<script>
// Additional JS functions here
window.fbAsyncInit = function() {
Backbone.Mediator.publish('fbapi-loaded');
FB.init({
appId : document.location.origin === 'http://localhost:3000' ? '607435142676437' : '148832601965463', // App ID
channelUrl : document.location.origin +'/channel.html', // Channel File
status : true, // check login status
cookie : true, // enable cookies to allow the server to access the session
xfbml : true // parse XFBML
});
// This is fired for any auth related change, such as login, logout or session refresh.
FB.Event.subscribe('auth.authResponseChange', function(response) {
// Here we specify what we do with the response anytime this event occurs.
if (response.status === 'connected') {
// They have logged in to the app.
Backbone.Mediator.publish('facebook-logged-in', {response:response});
} else if (response.status === 'not_authorized') {
// The person is logged into Facebook, but not into the app.
} else {
// The person is not logged into Facebook
}
});
};
// Load the SDK asynchronously
(function(d){
var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0];
if (d.getElementById(id)) {return;}
js = d.createElement('script'); js.id = id; js.async = true;
js.src = "//connect.facebook.net/en_US/all.js";
//js.src = "//connect.facebook.net/en_US/all/debug.js";
ref.parentNode.insertBefore(js, ref);
}(document));
</script>
<!-- end facebook code -->
<header class="header-container" id="header-container"></header>
<div id="page-container" class="nano-content"></div>
<div id="modal-wrapper" class="modal-content"></div>
<!-- begin google api/plus code -->
<script type="text/javascript">
(function() {
var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
po.src = 'https://apis.google.com/js/client:plusone.js?onload=onGPlusLoaded';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
})();
function onGPlusLoaded() {
Backbone.Mediator.publish('gapi-loaded');
}
function signinCallback(authResult) {
if (authResult['access_token']) {
Backbone.Mediator.publish('gplus-logged-in', authResult)
}
}
</script>
<!-- end google api/plus code -->
<!-- begin twitter code -->
<script>
(function (d,s,id) {
var js, fjs = d.getElementsByTagName(s)[0], p = /^http:/.test(d.location) ? 'http' : 'https';
if(!d.getElementById(id)) {
js = d.createElement(s);
js.id = id;
js.src = p + '://platform.twitter.com/widgets.js';
fjs.parentNode.insertBefore(js, fjs);
}
})(document, 'script', 'twitter-wjs');
</script>
<!-- end twitter code -->
<!-- begin filepicker.io code -->
<script type="text/javascript">
(function(a){if(window.filepicker){return}var b=a.createElement("script");b.type="text/javascript";b.async=!0;b.src=("https:"===a.location.protocol?"https:":"http:")+"//api.filepicker.io/v1/filepicker.js";var c=a.getElementsByTagName("script")[0];c.parentNode.insertBefore(b,c);var d={};d._queue=[];var e="pick,pickMultiple,pickAndStore,read,write,writeUrl,export,convert,store,storeUrl,remove,stat,setKey,constructWidget,makeDropPane".split(",");var f=function(a,b){return function(){b.push([a,arguments])}};for(var g=0;g<e.length;g++){d[e[g]]=f(e[g],d._queue)}window.filepicker=d})(document);
</script>
<!-- end filepicker.io code -->
</body>
</html>

View file

@ -18,6 +18,11 @@ definitionSchemas =
'misc': require './schemas/definitions/misc'
init = ->
path = document.location.pathname
testing = path.startsWith '/test'
demoing = path.startsWith '/demo'
initializeServices() unless testing or demoing
# Set up Backbone.Mediator schemas
setUpDefinitions()
setUpChannels()
@ -28,7 +33,6 @@ init = ->
treemaExt = require 'treema-ext'
treemaExt.setup()
filepicker.setKey('AvlkNoldcTOU4PvKi2Xm7z')
$ -> init()
@ -60,3 +64,18 @@ setUpChannels = ->
setUpDefinitions = ->
for definition of definitionSchemas
Backbone.Mediator.addDefSchemas definitionSchemas[definition]
initializeServices = ->
services = [
'./lib/services/filepicker'
'./lib/services/segmentio'
'./lib/services/olark'
'./lib/services/facebook'
'./lib/services/google'
'./lib/services/twitter'
'./lib/services/linkedin'
]
for service in services
service = require service
service()

View file

@ -16,8 +16,9 @@ module.exports = class CocoRouter extends Backbone.Router
# editor views tend to have the same general structure
'editor/:model(/:slug_or_id)(/:subview)': 'editorModelView'
# Experimenting with direct links
'test/*subpath': go('test')
# Direct links
'test/*subpath': go('TestView')
'demo/*subpath': go('DemoView')
'play/ladder/:levelID': go('play/ladder/ladder_view')
'play/ladder': go('play/ladder_home')

View file

@ -0,0 +1,48 @@
module.exports.getParentFolders = (subPath, urlPrefix='/test/') ->
return [] unless subPath
paths = []
parts = subPath.split('/')
while parts.length
parts.pop()
paths.unshift {
name: parts[parts.length-1] or 'All'
url: urlPrefix + parts.join('/')
}
paths
module.exports.parseImmediateChildren = (allChildren, subPath, baseRequirePath='test/app/', urlPrefix='/test/') ->
return [] unless allChildren
folders = {}
files = {}
requirePrefix = baseRequirePath + subPath
if requirePrefix[requirePrefix.length-1] isnt '/'
requirePrefix += '/'
for f in allChildren
f = f[requirePrefix.length..]
continue unless f
parts = f.split('/')
name = parts[0]
group = if parts.length is 1 then files else folders
group[name] ?= 0
group[name] += 1
children = []
urlPrefix += subPath
urlPrefix += '/' if urlPrefix[urlPrefix.length-1] isnt '/'
for name in _.keys(folders)
children.push {
type:'folder',
url: urlPrefix+name
name: name+'/'
size: folders[name]
}
for name in _.keys(files)
children.push {
type:'file',
url: urlPrefix+name
name: name
}
children

View file

@ -0,0 +1,42 @@
module.exports = initializeFacebook = ->
# Additional JS functions here
window.fbAsyncInit = ->
Backbone.Mediator.publish "fbapi-loaded"
FB.init
appId: (if document.location.origin is "http://localhost:3000" then "607435142676437" else "148832601965463") # App ID
channelUrl: document.location.origin + "/channel.html" # Channel File
status: true # check login status
cookie: true # enable cookies to allow the server to access the session
xfbml: true # parse XFBML
# This is fired for any auth related change, such as login, logout or session refresh.
FB.Event.subscribe "auth.authResponseChange", (response) ->
# Here we specify what we do with the response anytime this event occurs.
if response.status is "connected"
# They have logged in to the app.
Backbone.Mediator.publish "facebook-logged-in",
response: response
else if response.status is "not_authorized"
#
else
#
# Load the SDK asynchronously
((d) ->
js = undefined
id = "facebook-jssdk"
ref = d.getElementsByTagName("script")[0]
return if d.getElementById(id)
js = d.createElement("script")
js.id = id
js.async = true
js.src = "//connect.facebook.net/en_US/all.js"
#js.src = "//connect.facebook.net/en_US/all/debug.js";
ref.parentNode.insertBefore js, ref
return
) document

View file

@ -0,0 +1,29 @@
module.exports = initializeFilepicker = ->
((a) ->
return if window.filepicker
b = a.createElement("script")
b.type = "text/javascript"
b.async = not 0
b.src = ((if "https:" is a.location.protocol then "https:" else "http:")) + "//api.filepicker.io/v1/filepicker.js"
c = a.getElementsByTagName("script")[0]
c.parentNode.insertBefore b, c
d = {}
d._queue = []
e = "pick,pickMultiple,pickAndStore,read,write,writeUrl,export,convert,store,storeUrl,remove,stat,setKey,constructWidget,makeDropPane".split(",")
f = (a, b) ->
->
b.push [
a
arguments
]
return
g = 0
while g < e.length
d[e[g]] = f(e[g], d._queue)
g++
d.setKey('AvlkNoldcTOU4PvKi2Xm7z')
window.filepicker = d
return
) document

View file

@ -0,0 +1,16 @@
module.exports = initializeGoogle = ->
onGPlusLoaded = ->
Backbone.Mediator.publish "gapi-loaded"
return
signinCallback = (authResult) ->
Backbone.Mediator.publish "gplus-logged-in", authResult if authResult["access_token"]
return
(->
po = document.createElement("script")
po.type = "text/javascript"
po.async = true
po.src = "https://apis.google.com/js/client:plusone.js?onload=onGPlusLoaded"
s = document.getElementsByTagName("script")[0]
s.parentNode.insertBefore po, s
return
)()

View file

@ -0,0 +1,12 @@
module.exports = initializeLinkedIn = ->
window.linkedInAsyncInit = ->
Backbone.Mediator.publish 'linkedin-loaded'
linkedInSnippet =
'<script type="text/javascript" async src="http://platform.linkedin.com/in.js">
api_key: 75v8mv4ictvmx6
onLoad: linkedInAsyncInit
authorize: true
</script>'
$('head').append(linkedInSnippet)

View file

@ -0,0 +1,116 @@
module.exports = initializeOlark = ->
window.olark or ((c) -> #<![CDATA[
f = window
d = document
l = (if f.location.protocol is "https:" then "https:" else "http:")
z = c.name
r = "load"
nt = ->
s = ->
a.P r
f[z] r
return
f[z] = ->
(a.s = a.s or []).push arguments
return
a = f[z]._ = {}
q = c.methods.length
while q--
((n) ->
f[z][n] = ->
f[z] "call", n, arguments
return
return
) c.methods[q]
a.l = c.loader
a.i = nt
a.p = 0: +new Date
a.P = (u) ->
a.p[u] = new Date - a.p[0]
return
(if f.addEventListener then f.addEventListener(r, s, false) else f.attachEvent("on" + r, s))
ld = ->
p = (hd) ->
hd = "head"
[
"<"
hd
"></"
hd
"><"
i
" onl" + "oad=\"var d="
g
";d.getElementsByTagName('head')[0]."
j
"(d."
h
"('script'))."
k
"='"
l
"//"
a.l
"'"
"\""
"></"
i
">"
].join ""
i = "body"
m = d[i]
return setTimeout(ld, 100) unless m
a.P 1
j = "appendChild"
h = "createElement"
k = "src"
n = d[h]("div")
v = n[j](d[h](z))
b = d[h]("iframe")
g = "document"
e = "domain"
o = undefined
n.style.display = "none"
m.insertBefore(n, m.firstChild).id = z
b.frameBorder = "0"
b.id = z + "-loader"
b.src = "javascript:false" if /MSIE[ ]+6/.test(navigator.userAgent)
b.allowTransparency = "true"
v[j] b
try
b.contentWindow[g].open()
catch w
c[e] = d[e]
o = "javascript:var d=" + g + ".open();d.domain='" + d.domain + "';"
b[k] = o + "void(0);"
try
t = b.contentWindow[g]
t.write p()
t.close()
catch x
b[k] = o + "d.write(\"" + p().replace(/"/g, String.fromCharCode(92) + "\"") + "\");d.close();"
a.P 2
return
ld()
return
nt()
return
)(
loader: "static.olark.com/jsclient/loader0.js"
name: "olark"
methods: [
"configure"
"extend"
"declare"
"identify"
]
)
# custom configuration goes here (www.olark.com/documentation)
olark.identify "1451-787-10-5544" #]]>

View file

@ -0,0 +1,41 @@
module.exports = initializeSegmentio = ->
analytics = analytics or []
(->
e = [
"identify"
"track"
"trackLink"
"trackForm"
"trackClick"
"trackSubmit"
"page"
"pageview"
"ab"
"alias"
"ready"
"group"
]
t = (e) ->
->
analytics.push [e].concat(Array::slice.call(arguments, 0))
return
n = 0
while n < e.length
analytics[e[n]] = t(e[n])
n++
return
)()
analytics.load = (e) ->
t = document.createElement("script")
t.type = "text/javascript"
t.async = not 0
t.src = ((if "https:" is document.location.protocol then "https://" else "http://")) + "d2dq2ahtl5zl1z.cloudfront.net/analytics.js/v1/" + e + "/analytics.min.js"
n = document.getElementsByTagName("script")[0]
n.parentNode.insertBefore t, n
return
analytics.load "jsjzx9n4d2"

View file

@ -0,0 +1,12 @@
module.exports = initializeTwitter = ->
((d, s, id) ->
js = undefined
fjs = d.getElementsByTagName(s)[0]
p = (if /^http:/.test(d.location) then "http" else "https")
unless d.getElementById(id)
js = d.createElement(s)
js.id = id
js.src = p + "://platform.twitter.com/widgets.js"
fjs.parentNode.insertBefore js, fjs
return
) document, "script", "twitter-wjs"

View file

@ -282,6 +282,7 @@
education_description: "Description"
education_description_help: "Highlight anything about this educational experience. (140 chars; optional)"
our_notes: "Our Notes"
remarks: "Remarks"
projects: "Projects"
projects_header: "Add 3 projects"
projects_header_2: "Projects (Top 3)"
@ -320,6 +321,7 @@
candidate_top_skills: "Top Skills"
candidate_years_experience: "Yrs Exp"
candidate_last_updated: "Last Updated"
candidate_who: "Who"
featured_developers: "Featured Developers"
other_developers: "Other Developers"
inactive_developers: "Inactive Developers"
@ -884,6 +886,7 @@
document: "Document"
sprite_sheet: "Sprite Sheet"
candidate_sessions: "Candidate Sessions"
user_remark: "User Remark"
delta:
added: "Added"

View file

@ -193,13 +193,13 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
view_profile: "Voir votre profil"
account_profile:
# settings: "Settings"
# edit_profile: "Edit Profile"
# done_editing: "Done Editing"
settings: "Paramètres"
edit_profile: "Editer Profil"
done_editing: "Modifications effectué"
profile_for_prefix: "Profil pour "
profile_for_suffix: ""
# featured: "Featured"
# not_featured: "Not Featured"
featured: "Complet"
not_featured: "Incomplet"
looking_for: "à la recherche de:"
last_updated: "Dernière Mise à jour:"
contact: "Contact"
@ -220,7 +220,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
next_photo: "ajouter une photo professionelle (optionnel)."
next_active: "déclarez vous ouvert aux offres pour apparaitre dans les recherches."
example_blog: "Votre blog"
# example_personal_site: "Personal Site"
example_personal_site: "Site Web"
links_header: "Liens personnels"
links_blurb: "Lien vers d'autres sites ou profils que vous souhaitez mettre en avant, comme votre GitHub, LinkedIn ou votre blog."
links_name: "Nom du lien"
@ -448,7 +448,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
av_entities_sub_title: "Entités"
av_entities_users_url: "Utilisateurs"
av_entities_active_instances_url: "Instances actives"
# av_entities_employer_list_url: "Employer List"
av_entities_employer_list_url: "Liste des employés"
av_other_sub_title: "Autre"
av_other_debug_base_url: "Base (pour debugger base.jade)"
u_title: "Liste des utilisateurs"
@ -653,7 +653,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
introduction_desc_ending: "Nous espérons que vous allez joindre notre aventure!"
introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy et Glen"
alert_account_message_intro: "Et tiens!"
# alert_account_message: "To subscribe for class emails, you'll need to be logged in first."
alert_account_message: "Pour souscrire aux e-mails, vous devez être connecté"
# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever."
archmage_introduction: "L'une des meilleures parties de la création d'un jeu est qu'il regroupe tant de choses différentes. Graphismes, sons, réseau en temps réel, réseaux sociaux, et bien sur bien d'autres aspects de la programmation, de la gestion bas niveau de base de données, et de l'administration de serveur à l'élaboration d'interfaces utilisateur. Il y a tant à faire, et si vous êtes un programmeur expérimenté avec une aspiration à vraiment plonger dans le fond de CodeCombat, cette classe est faite pour vous. Nous aimerions avoir votre aide pour le meilleur jeu de développement de tous les temps."
class_attributes: "Attributs de classe"
@ -860,7 +860,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
# facebook_friend_sessions: "Facebook Friend Sessions"
# gplus_friends: "G+ Friends"
# gplus_friend_sessions: "G+ Friend Sessions"
# leaderboard: "Leaderboard"
leaderboard: "Classement"
# user_schema: "User Schema"
# user_profile: "User Profile"
patches: "Patchs"

View file

@ -26,14 +26,14 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t
minutes: "percek"
hour: "óra"
hours: "órák"
# day: "day"
# days: "days"
# week: "week"
# weeks: "weeks"
# month: "month"
# months: "months"
# year: "year"
# years: "years"
day: "nap"
days: "napok"
week: "hét"
weeks: "hetek"
month: "hónap"
months: "hónapok"
year: "év"
years: "évek"
modal:
close: "Mégse"

View file

@ -161,7 +161,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
autosave: "Настройки сохраняются автоматически"
me_tab: "Я"
picture_tab: "Аватар"
# upload_picture: "Upload a picture"
upload_picture: "Загрузить изображение"
wizard_tab: "Волшебник"
password_tab: "Пароль"
emails_tab: "Email-адреса"
@ -175,9 +175,9 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
email_notifications: "Уведомления"
# email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity."
# email_any_notes: "Any Notifications"
# email_any_notes_description: "Disable to stop all activity notification emails."
# email_recruit_notes: "Job Opportunities"
# email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job."
email_any_notes_description: "Отключите, чтобы больше не получать извещения."
email_recruit_notes: "Возможности для работы"
email_recruit_notes_description: "Если вы действительно хорошо играете, то мы можем связаться с вами для предложения (лучшей) работы."
contributor_emails: "Рассылки по классам участников"
contribute_prefix: "Нам нужны люди, которые присоединятся к нашей команде! Зайдите на "
contribute_page: "страницу участников,"
@ -206,9 +206,9 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
# active: "Looking for interview offers now"
# inactive: "Not looking for offers right now"
# complete: "complete"
# next: "Next"
# next_city: "city?"
# next_country: "pick your country."
next: "Далее"
next_city: "Город?"
next_country: "Выберите вашу страну."
# next_name: "name?"
# next_short_description: "write a short description."
# next_long_description: "describe your desired position."
@ -333,7 +333,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
multiplayer: "Мультиплеер"
restart: "Перезапустить"
goals: "Цели"
# success: "Success!"
success: "Успешно!"
# incomplete: "Incomplete"
# timed_out: "Ran out of time"
# failing: "Failing"
@ -358,7 +358,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."
multiplayer_sign_in_leaderboard: "Войдите или создайте аккаунт, чтобы ваше решение оказалось в таблице лидеров."
guide_title: "Руководство"
tome_minion_spells: "Заклинания ваших миньонов"
tome_read_only_spells: "Заклинания только для чтения"
@ -375,9 +375,9 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
skip_tutorial: "Пропуск (Esc)"
editor_config: "Настройки редактора"
editor_config_title: "Настройки редактора"
# editor_config_level_language_label: "Language for This Level"
editor_config_level_language_label: "Язык для этого уровня"
# editor_config_level_language_description: "Define the programming language for this particular level."
# editor_config_default_language_label: "Default Programming Language"
editor_config_default_language_label: "Язык по умолчанию"
# editor_config_default_language_description: "Define the programming language you want to code in when starting new levels."
editor_config_keybindings_label: "Сочетания клавиш"
editor_config_keybindings_default: "По умолчанию (Ace)"
@ -388,7 +388,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
editor_config_indentguides_description: "Отображение вертикальных линий для лучшего обзора отступов."
editor_config_behaviors_label: "Умное поведение"
editor_config_behaviors_description: "Автозавершать квадратные, фигурные скобки и кавычки."
# keyboard_shortcuts: "Key Shortcuts"
keyboard_shortcuts: "Горячие клавиши"
loading_ready: "Готово!"
tip_insert_positions: "Shift+Клик по карте вставит координаты в редактор заклинаний."
tip_toggle_play: "Переключайте воспроизведение/паузу комбинацией Ctrl+P."
@ -425,12 +425,12 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
infinite_loop_reset_level: "Сбросить уровень"
infinite_loop_comment_out: "Закомментировать мой код"
# keyboard_shortcuts:
# keyboard_shortcuts: "Keyboard Shortcuts"
# space: "Space"
# enter: "Enter"
# escape: "Escape"
# cast_spell: "Cast current spell."
keyboard_shortcuts:
keyboard_shortcuts: "Горячие клавиши"
space: "Пробел"
enter: "Enter"
escape: "Escape"
cast_spell: "Произнести текущее заклинание."
# continue_script: "Continue past current script."
# skip_scripts: "Skip past all skippable scripts."
# toggle_playback: "Toggle play/pause."
@ -471,7 +471,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
thang_description: "Создавайте юнитов, определяйте их логику по умолчанию, графику и звук. В настоящий момент поддерживается импорт только векторной графики Flash."
level_title: "Редактор уровней"
level_description: "Включает в себя инструменты для написания сценариев, загрузки аудио и построения собственной логики для создания всевозможных уровней. Всё, что мы используем сами!"
# achievement_title: "Achievement Editor"
achievement_title: "Редактор достижений"
got_questions: "Вопросы по использованию редакторов CodeCombat?"
contact_us: "свяжитесь с нами!"
hipchat_prefix: "Также вы можете найти нас в нашей"
@ -522,7 +522,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
article_search_title: "Искать статьи"
thang_search_title: "Искать типы объектов"
level_search_title: "Искать уровни"
# achievement_search_title: "Search Achievements"
achievement_search_title: "Искать достижения"
read_only_warning2: "Примечание: вы не можете сохранять любые правки здесь, потому что вы не авторизованы."
article:
@ -653,7 +653,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
introduction_desc_ending: "Мы надеемся, что вы присоединитесь к нашей команде!"
introduction_desc_signature: "- Ник, Джордж, Скотт, Михаэль, Джереми и Глен"
alert_account_message_intro: "Привет!"
# alert_account_message: "To subscribe for class emails, you'll need to be logged in first."
alert_account_message: "Чтобы подписаться на классовые сообщения, необходимо войти в аккаунт"
archmage_summary: "Интересует работа над игровой графикой, дизайном пользовательского интерфейса, базой данных и организацией сервера, сетевым мультиплеером, физикой, звуком или производительностью игрового движка? Хотите помочь создать игру для помощи другим людям в изучении того, в чём вы хорошо разбираетесь? У нас много работы, и если вы опытный программист и хотите разрабатывать для CodeCombat, этот класс для вас. Мы будем рады вашей помощи в создании самой лучшей игры для программистов."
archmage_introduction: "Одна из лучших черт в создании игр - то, что они синтезируют так много различных вещей. Графика, звук, сетевое взаимодействие в режиме реального времени, социальное сетевое взаимодействие, и, конечно, большинство из более распространённых аспектов программирования, от низкоуровневого управления базами данных и администрирования сервера до построения дизайна и интерфейсов, видимых пользователю. У нас много работы, и если вы опытный программист со страстным желанием погрузиться в действительно мельчайшие детали CodeCombat, этот класс для вас. Мы будем рады вашей помощи в создании самой лучшей игры для программистов."
class_attributes: "Атрибуты класса"

View file

@ -0,0 +1,6 @@
CocoModel = require('./CocoModel')
module.exports = class UserRemark extends CocoModel
@className: "UserRemark"
@schema: require 'schemas/models/user_remark'
urlRoot: "/db/user.remark"

View file

@ -7,8 +7,8 @@ module.exports =
type: "object"
properties: # TODO
joined:
type: "boolean"
type: ["boolean", "null"]
players:
type: "object"
required: ["joined", "players"]
additionalProperties: false
additionalProperties: true

View file

@ -0,0 +1,24 @@
c = require './../schemas'
UserRemarkSchema = c.object {
title: "Remark"
description: "Remarks on a user, point of contact, tasks."
}
_.extend UserRemarkSchema.properties,
user: c.objectId links: [{rel: 'extra', href: "/db/user/{($)}"}]
contact: c.objectId links: [{rel: 'extra', href: "/db/user/{($)}"}]
created: c.date title: 'Created', readOnly: true
history: c.array {title: 'History', description: 'Records of our interactions with the user.'},
c.object {title: 'Record'}, {date: c.date(title: 'Date'), content: {title: 'Content', type: 'string', format: 'markdown'}}
tasks: c.array {title: 'Tasks', description: 'Task entries: when to email the contact about something.'},
c.object {title: 'Task'}, {date: c.date(title: 'Date'), action: {title: 'Action', type: 'string'}}
# denormalization
userName: { title: "Player Name", type: 'string' }
contactName: { title: "Contact Name", type: 'string' } # Not actually our usernames
c.extendBasicProperties UserRemarkSchema, 'user.remark'
module.exports = UserRemarkSchema

View file

@ -33,7 +33,7 @@ module.exports =
type: "object"
properties:
message:
type: "string"
type: "object"
bus:
$ref: "bus"

View file

@ -193,6 +193,11 @@
width: 100%
height: 100px
#remark-treema
background-color: white
border: 0
padding-top: 0
.right-column
width: $side-width
background-color: $sideBackground

14
app/styles/demo.sass Normal file
View file

@ -0,0 +1,14 @@
#demo-view
margin: 0 20px
h2
background: #add8e6
font-family: Arial, Geneva, sans-serif
padding: 20px
font-weight: bold
#demo-wrapper
width: 78%
#demo-nav
width: 20%

View file

@ -43,7 +43,9 @@ block content
i.icon-eye-close
span(data-i18n='account_profile.not_featured') Not Featured
if me.isAdmin() && !myProfile
button.btn.edit-settings-button#enter-espionage-mode 007
button.btn#enter-espionage-mode 007
if me.isAdmin()
button.btn#open-model-modal Raw
if profile && allowedToViewJobProfile
div(class="job-profile-container" + (editing ? " editable-profile" : ""))
@ -169,6 +171,10 @@ block content
button#contact-candidate.btn.btn-large.btn-inverse.flat-button
span(data-i18n="account_profile.contact") Contact
| #{profile.name.split(' ')[0]}
if me.isAdmin()
select#admin-contact.form-control
for contact in adminContacts
option(value=contact.id, selected=remark && remark.get('contact') == contact.id)= contact.name
if !editing && sessions.length
h3(data-i18n="account_profile.player_code") Player Code
@ -191,9 +197,12 @@ block content
if editing && !profile.name
h3.edit-label(data-i18n="account_profile.name_header") Fill in your name
else if profile.name
h3= profile.name
h3= profile.name + (me.isAdmin() ? ' (' + user.get('name') + ')' : '')
else
h3(data-i18n="account_profile.name_anonymous") Anonymous Developer
h3
span(data-i18n="account_profile.name_anonymous") Anonymous Developer
if me.isAdmin()
span (#{user.get('name')})
form.editable-form
.editable-icon.glyphicon.glyphicon-remove
@ -396,6 +405,10 @@ block content
else
div!= marked(notes)
if me.isAdmin()
h3(data-i18n="account_profile.remarks") Remarks
#remark-treema
.right-column.full-height-column
.sub-column
#projects-container.editable-section

22
app/templates/demo.jade Normal file
View file

@ -0,0 +1,22 @@
h2 Demo Page
ol.breadcrumb
for path in parentFolders
li
a(href=path.url)= path.name
li.active= currentFolder
.well.pull-left#demo-wrapper
#demo-area
.nav.nav-pills.nav-stacked.pull-right.well#demo-nav
for child in children
li(class=child.type)
a(href=child.url).small
if child.type == 'folder'
span.glyphicon.glyphicon-folder-close
else
span.glyphicon.glyphicon-file
span.spl= child.name
if child.type == 'folder'
strong (#{child.size})

View file

@ -84,6 +84,8 @@ block content
th(data-i18n="employers.candidate_top_skills") Top Skills
th(data-i18n="employers.candidate_years_experience") Yrs Exp
th(data-i18n="employers.candidate_last_updated") Last Updated
if me.isAdmin()
th(data-i18n="employers.candidate_who") Who
if me.isAdmin() && area.id == 'inactive-candidates'
th ✓?
@ -95,7 +97,10 @@ block content
td
if authorized
img(src=candidate.getPhotoURL(50), alt=profile.name, title=profile.name, height=50)
p= profile.name
if profile.name
p= profile.name
else if me.isAdmin()
p (#{candidate.get('name')})
else
img(src="/images/pages/contribute/archmage.png", alt="", title="Sign up as an employer to see our candidates", width=50)
p Developer ##{index + 1 + (area.id == 'featured-candidates' ? 0 : featuredCandidates.length)}
@ -111,6 +116,8 @@ block content
span
td= profile.experience
td(data-profile-age=(new Date() - new Date(profile.updated)) / 86400 / 1000)= moment(profile.updated).fromNow()
if me.isAdmin()
td= remarks[candidate.id] ? remarks[candidate.id].get('contactName') : ''
if me.isAdmin() && area.id == 'inactive-candidates'
if candidate.get('jobProfileApproved')
td ✓

View file

@ -4,6 +4,8 @@ block modal-header
block modal-body-content
for model in models
h3= model.type() + ': ' + model.id
.model-treema(data-model-id=model.id)
.model-container(data-model-id=model.id)
h3= model.type() + ': ' + model.id
.model-treema(data-model-id=model.id)
btn.btn.btn-success.save-model(data-i18n="common.save") Save
hr

View file

@ -6,6 +6,8 @@ locale = require 'locale/locale'
class DateTimeTreema extends TreemaNode.nodeMap.string
valueClass: 'treema-date-time'
buildValueForDisplay: (el) -> el.text(moment(@data).format('llll'))
buildValueForEditing: (valEl) ->
@buildValueForEditingSimply valEl, null, 'date'
class VersionTreema extends TreemaNode
valueClass: 'treema-version'

88
app/views/DemoView.coffee Normal file
View file

@ -0,0 +1,88 @@
CocoView = require 'views/kinds/CocoView'
template = require 'templates/demo'
requireUtils = require 'lib/requireUtils'
DEMO_REQUIRE_PREFIX = 'test/demo/'
DEMO_URL_PREFIX = '/demo/'
###
What are demo files?
They could be a function which returns an element to insert into the demo page.
But what about demoing achievements? They'll get put into the main html. Or modals.
Well, I was thinking that a single folder would show all demos at the same time, line them up.
But it'd be confusing to have a whole bunch of achievement demos show up all at the same time?
Maybe there could be a button to show all the demos. Hmm, that'd be cool.
It could work like Jasmine, where it modifies the path and so when you select to run them, they all run with page reloads.
I think for now, I'll just say: have it be a function which we can run anytime.
It may or may not return an element to be inserted into the main area.
Another idea. Do we want root views to just take over the full view?
Or should they just go into the central part?
Probably should take over the full view, and if you want to get out of the demo, you navigate back.
###
module.exports = DemoView = class DemoView extends CocoView
id: "demo-view"
template: template
# INITIALIZE
constructor: (options, @subPath='') ->
super(options)
@subPath = @subPath[1..] if @subPath[0] is '/'
@loadDemoingLibs() unless DemoView.loaded
loadDemoingLibs: ->
@queue = new createjs.LoadQueue()
@queue.on('complete', @scriptsLoaded, @)
window.jasmine = {} # so that mock-ajax properly loads. It expects jasmine to be loaded
for f in ['mock-ajax', 'demo-app']
@queue.loadFile({
src: "/javascripts/#{f}.js"
type: createjs.LoadQueue.JAVASCRIPT
})
scriptsLoaded: ->
@initDemoFiles()
@children = requireUtils.parseImmediateChildren(@demoFiles, @subPath, DEMO_REQUIRE_PREFIX, DEMO_URL_PREFIX)
@render()
@runDemo()
# RENDER DATA
getRenderData: ->
c = super(arguments...)
c.parentFolders = requireUtils.getParentFolders(@subPath, DEMO_URL_PREFIX)
c.children = @children or []
parts = @subPath.split('/')
c.currentFolder = parts[parts.length-1] or parts[parts.length-2] or 'All'
c
# RUNNING DEMOS
initDemoFiles: ->
@demoFiles = @getAllDemoFiles()
if @subPath
prefix = DEMO_REQUIRE_PREFIX + @subPath
@demoFiles = (f for f in @demoFiles when f.startsWith prefix)
runDemo: ->
# TODO: Maybe have an option to run all demos in this folder at the same time?
return unless @subPath and _.last(@subPath.split('/')).indexOf('.demo') > -1
requirePath = DEMO_REQUIRE_PREFIX + @subPath
demoFunc = require requirePath
if not _.isFunction(demoFunc)
console.error "Demo files must export a function. #{requirePath} does not."
return
jasmine.Ajax.install()
view = demoFunc()
return unless view
@$el.find('#demo-area').empty().append(view.$el)
# TODO, maybe handle root views differently than modal views differently than everything else?
getAllDemoFiles: ->
allFiles = window.require.list()
(f for f in allFiles when f.indexOf('.demo') > -1)

View file

@ -1,7 +1,9 @@
CocoView = require 'views/kinds/CocoView'
template = require 'templates/test'
requireUtils = require 'lib/requireUtils'
TEST_BASE_PATH = 'test/app/'
TEST_REQUIRE_PREFIX = 'test/app/'
TEST_URL_PREFIX = '/test/'
module.exports = TestView = class TestView extends CocoView
id: "test-view"
@ -13,7 +15,7 @@ module.exports = TestView = class TestView extends CocoView
constructor: (options, @subPath='') ->
super(options)
@subPath = @subPath[1..] if @subPath[0] is '/'
@loadTestingLibs() unless TestView.loaded
@loadTestingLibs()
loadTestingLibs: ->
@queue = new createjs.LoadQueue()
@ -28,72 +30,24 @@ module.exports = TestView = class TestView extends CocoView
@initSpecFiles()
@render()
TestView.runTests(@specFiles)
window.runJasmine()
# RENDER DATA
getRenderData: ->
c = super(arguments...)
c.parentFolders = @getParentFolders()
c.children = @getChildren()
c.parentFolders = requireUtils.getParentFolders(@subPath, TEST_URL_PREFIX)
c.children = requireUtils.parseImmediateChildren(@specFiles, @subPath, TEST_REQUIRE_PREFIX, TEST_URL_PREFIX)
parts = @subPath.split('/')
c.currentFolder = parts[parts.length-1] or parts[parts.length-2] or 'All'
c
getParentFolders: ->
return [] unless @subPath
paths = []
parts = @subPath.split('/')
while parts.length
parts.pop()
paths.unshift {
name: parts[parts.length-1] or 'All'
url: '/test/' + parts.join('/')
}
paths
getChildren: ->
return [] unless @specFiles
folders = {}
files = {}
requirePrefix = TEST_BASE_PATH + @subPath
if requirePrefix[requirePrefix.length-1] isnt '/'
requirePrefix += '/'
for f in @specFiles
f = f[requirePrefix.length..]
continue unless f
parts = f.split('/')
name = parts[0]
group = if parts.length is 1 then files else folders
group[name] ?= 0
group[name] += 1
children = []
urlPrefix = '/test/'+@subPath
urlPrefix += '/' if urlPrefix[urlPrefix.length-1] isnt '/'
for name in _.keys(folders)
children.push {
type:'folder',
url: urlPrefix+name
name: name+'/'
size: folders[name]
}
for name in _.keys(files)
children.push {
type:'file',
url: urlPrefix+name
name: name
}
children
# RUNNING TESTS
initSpecFiles: ->
@specFiles = TestView.getAllSpecFiles()
if @subPath
prefix = TEST_BASE_PATH + @subPath
prefix = TEST_REQUIRE_PREFIX + @subPath
@specFiles = (f for f in @specFiles when f.startsWith prefix)
@runTests: (specFiles) ->

View file

@ -19,10 +19,10 @@ module.exports = class JobProfileView extends CocoView
buildJobProfileTreema: ->
visibleSettings = @editableSettings.concat @readOnlySettings
data = _.pick (me.get('jobProfile') ? {}), (value, key) => key in visibleSettings
data = _.pick (me.get('jobProfile') ? {}), (value, key) -> key in visibleSettings
data.name ?= (me.get('firstName') + ' ' + me.get('lastName')).trim() if me.get('firstName')
schema = _.cloneDeep me.schema().properties.jobProfile
schema.properties = _.pick schema.properties, (value, key) => key in visibleSettings
schema.properties = _.pick schema.properties, (value, key) -> key in visibleSettings
schema.required = _.intersection schema.required, visibleSettings
for prop in @readOnlySettings
schema.properties[prop].readOnly = true

View file

@ -6,7 +6,9 @@ CocoCollection = require 'collections/CocoCollection'
{me} = require 'lib/auth'
JobProfileContactView = require 'views/modal/job_profile_contact_modal'
JobProfileView = require 'views/account/job_profile_view'
UserRemark = require 'models/UserRemark'
forms = require 'lib/forms'
ModelModal = require 'views/modal/model_modal'
class LevelSessionsCollection extends CocoCollection
url: -> "/db/user/#{@userID}/level.sessions/employer"
@ -14,6 +16,14 @@ class LevelSessionsCollection extends CocoCollection
constructor: (@userID) ->
super()
adminContacts = [
{id: "", name: "Assign a Contact"}
{id: "512ef4805a67a8c507000001", name: "Nick"}
{id: "5162fab9c92b4c751e000274", name: "Scott"}
{id: "51eb2714fa058cb20d0006ef", name: "Michael"}
{id: "51538fdb812dd9af02000001", name: "George"}
]
module.exports = class ProfileView extends View
id: "profile-view"
template: template
@ -28,6 +38,7 @@ module.exports = class ProfileView extends View
'click #save-notes-button': 'onJobProfileNotesChanged'
'click #contact-candidate': 'onContactCandidate'
'click #enter-espionage-mode': 'enterEspionageMode'
'click #open-model-modal': 'openModelModal'
'click .editable-profile .profile-photo': 'onEditProfilePhoto'
'click .editable-profile .project-image': 'onEditProjectImage'
'click .editable-profile .editable-display': 'onEditSection'
@ -36,12 +47,14 @@ module.exports = class ProfileView extends View
'change .editable-profile .editable-array input': 'onEditArray'
'keyup .editable-profile .editable-array input': 'onEditArray'
'click .editable-profile a': 'onClickLinkWhileEditing'
'change #admin-contact': 'onAdminContactChanged'
constructor: (options, @userID) ->
@userID ?= me.id
@onJobProfileNotesChanged = _.debounce @onJobProfileNotesChanged, 1000
@onRemarkChanged = _.debounce @onRemarkChanged, 1000
@authorizedWithLinkedIn = IN?.User?.isAuthorized()
@linkedInLoaded = Boolean(IN.parse)
@linkedInLoaded = Boolean(IN?.parse)
@waitingForLinkedIn = false
window.contractCallback = =>
@authorizedWithLinkedIn = IN?.User?.isAuthorized()
@ -70,6 +83,22 @@ module.exports = class ProfileView extends View
else
@user = User.getByID(@userID)
@sessions = @supermodel.loadCollection(new LevelSessionsCollection(@userID), 'candidate_sessions').model
if me.isAdmin()
# Mimicking how the VictoryModal fetches LevelFeedback
@remark = new UserRemark()
@remark.setURL "/db/user/#{@userID}/remark"
@remark.fetch()
@listenToOnce @remark, 'sync', @onRemarkLoaded
@listenToOnce @remark, 'error', @onRemarkNotFound
onRemarkLoaded: ->
@remark.setURL "/db/user.remark/#{@remark.id}"
@render()
onRemarkNotFound: ->
@remark = new UserRemark() # hmm, why do we create a new one here?
@remark.set 'user', @userID
@remark.set 'userName', name if name = @user.get('name')
onLinkedInLoaded: =>
@linkedinLoaded = true
@ -229,6 +258,8 @@ module.exports = class ProfileView extends View
context.sessions.sort (a, b) -> (b.playtime ? 0) - (a.playtime ? 0)
else
context.sessions = []
context.adminContacts = adminContacts
context.remark = @remark
context
afterRender: ->
@ -249,6 +280,31 @@ module.exports = class ProfileView extends View
_.delay ->
justSavedSection.removeClass "just-saved", duration: 1500, easing: 'easeOutQuad'
, 500
if me.isAdmin()
visibleSettings = ['history', 'tasks']
data = _.pick (@remark.attributes), (value, key) -> key in visibleSettings
data.history ?= []
data.tasks ?= []
schema = _.cloneDeep @remark.schema()
schema.properties = _.pick schema.properties, (value, key) => key in visibleSettings
schema.required = _.intersection (schema.required ? []), visibleSettings
treemaOptions =
filePath: "db/user/#{@userID}"
schema: schema
data: data
aceUseWrapMode: true
callbacks: {change: @onRemarkChanged}
@remarkTreema = @$el.find('#remark-treema').treema treemaOptions
@remarkTreema.build()
@remarkTreema.open(3)
onRemarkChanged: (e) =>
return unless @remarkTreema.isValid()
for key in ['history', 'tasks']
val = _.filter(@remarkTreema.get(key), (entry) -> entry?.content or entry?.action)
entry.date ?= (new Date()).toISOString() for entry in val if key is 'history'
@remark.set key, val
@saveRemark()
initializeAutocomplete: (container) ->
(container ? @$el).find('input[data-autocomplete]').each ->
@ -286,6 +342,9 @@ module.exports = class ProfileView extends View
espionageSuccess: (model) ->
window.location.reload()
openModelModal: (e) ->
@openModalView new ModelModal models: [@user]
onJobProfileNotesChanged: (e) =>
notes = @$el.find("#job-profile-notes").val()
@user.set 'jobProfileNotes', notes
@ -455,6 +514,26 @@ module.exports = class ProfileView extends View
onClickLinkWhileEditing: (e) ->
e.preventDefault()
onAdminContactChanged: (e) ->
newContact = @$el.find('#admin-contact').val()
newContactName = if newContact then _.find(adminContacts, id: newContact).name else ''
@remark.set 'contact', newContact
@remark.set 'contactName', newContactName
@saveRemark()
saveRemark: ->
@remark.set 'user', @user.id
@remark.set 'userName', @user.get('name')
if errors = @remark.validate()
return console.error "UserRemark", @remark, "failed validation with errors:", errors
res = @remark.save()
res.error =>
return if @destroyed
console.error "UserRemark", @remark, "failed to save with error:", res.responseText
res.success (model, response, options) =>
return if @destroyed
console.log "Saved UserRemark", @remark, "with response", response
updateProgress: (highlightNext) ->
return unless @user
completed = 0

View file

@ -25,6 +25,7 @@ module.exports = class LevelComponentEditView extends View
super options
@levelComponent = @supermodel.getModelByOriginalAndMajorVersion LevelComponent, options.original, options.majorVersion or 0
console.log "Couldn't get levelComponent for", options, "from", @supermodel.models unless @levelComponent
@onEditorChange = _.debounce @onEditorChange, 1500
getRenderData: (context={}) ->
context = super(context)
@ -95,6 +96,7 @@ module.exports = class LevelComponentEditView extends View
@editor.on('change', @onEditorChange)
onEditorChange: =>
return if @destroyed
@levelComponent.set 'code', @editor.getValue()
Backbone.Mediator.publish 'level-component-edited', levelComponent: @levelComponent
null

View file

@ -35,13 +35,13 @@ module.exports = class ComponentsTabView extends View
componentModels = @supermodel.getModels LevelComponent
componentModelMap = {}
componentModelMap[comp.get('original')] = comp for comp in componentModels
componentModelMap[comp.get('original')] = comp for comp in componentModels
components = ({original: key.split('.')[0], majorVersion: parseInt(key.split('.')[1], 10), thangs: value, count: value.length} for key, value of @presentComponents)
treemaData = _.sortBy components, (comp) ->
comp = componentModelMap[comp.original]
res = [comp.get('system'), comp.get('name')]
return res
treemaOptions =
supermodel: @supermodel
schema: {type: 'array', items: {type: 'object', format: 'level-component'}}

View file

@ -2,6 +2,7 @@ View = require 'views/kinds/RootView'
template = require 'templates/employers'
app = require 'application'
User = require 'models/User'
UserRemark = require 'models/UserRemark'
{me} = require 'lib/auth'
CocoCollection = require 'collections/CocoCollection'
EmployerSignupView = require 'views/modal/employer_signup_modal'
@ -10,6 +11,10 @@ class CandidatesCollection extends CocoCollection
url: '/db/user/x/candidates'
model: User
class UserRemarksCollection extends CocoCollection
url: '/db/user.remark?project=contact,contactName,user'
model: UserRemark
module.exports = class EmployersView extends View
id: "employers-view"
template: template
@ -37,6 +42,8 @@ module.exports = class EmployersView extends View
ctx.inactiveCandidates = _.reject ctx.candidates, (c) -> c.get('jobProfile').active
ctx.featuredCandidates = _.filter ctx.activeCandidates, (c) -> c.get('jobProfileApproved')
ctx.otherCandidates = _.reject ctx.activeCandidates, (c) -> c.get('jobProfileApproved')
ctx.remarks = {}
ctx.remarks[remark.get('user')] = remark for remark in @remarks.models
ctx.moment = moment
ctx._ = _
ctx
@ -48,11 +55,13 @@ module.exports = class EmployersView extends View
getCandidates: ->
@candidates = new CandidatesCollection()
@candidates.fetch()
@remarks = new UserRemarksCollection()
@remarks.fetch()
# Re-render when we have fetched them, but don't wait and show a progress bar while loading.
@listenToOnce @candidates, 'all', @renderCandidatesAndSetupScrolling
@listenToOnce @remarks, 'all', @renderCandidatesAndSetupScrolling
renderCandidatesAndSetupScrolling: =>
@render()
$(".nano").nanoScroller()
if window.history?.state?.lastViewedCandidateID
@ -179,7 +188,7 @@ module.exports = class EmployersView extends View
"Last 4 weeks": (e, n, f, i, $r) ->
days = parseFloat $($r.find('td')[i]).data('profile-age')
days <= 28
7:
8:
"": filterSelectExactMatch
"": filterSelectExactMatch

View file

@ -6,6 +6,8 @@ module.exports = class ModelModal extends View
template: template
plain: true
events: 'click .save-model': 'onSaveModel'
constructor: (options) ->
super options
@models = options.models
@ -20,6 +22,7 @@ module.exports = class ModelModal extends View
afterRender: ->
return unless @supermodel.finished()
@modelTreemas = {}
for model in @models
data = $.extend true, {}, model.attributes
schema = $.extend true, {}, model.schema()
@ -31,6 +34,7 @@ module.exports = class ModelModal extends View
modelTreema?.build()
modelTreema?.open()
@openTastyTreemas modelTreema, model
@modelTreemas[model.id] = modelTreema
openTastyTreemas: (modelTreema, model) ->
# To save on quick inspection, let's auto-open the properties we're most likely to want to see.
@ -45,3 +49,26 @@ module.exports = class ModelModal extends View
}[team]
for dessert in desserts
child.childrenTreemas[dessert]?.open()
onSaveModel: (e) ->
container = $(e.target).closest('.model-container')
model = _.find @models, id: container.data('model-id')
treema = @modelTreemas[model.id]
changes = {}
for key, val of treema.data when not _.isEqual val, model.get key
console.log "Updating", key, "from", model.get(key), "to", val
model.set key, val
changes[key] = val
for key, val of model.attributes when treema.get(key) is undefined and not _.string.startsWith key, '_'
console.log "Deleting", key, "which was", val, "but man, that ain't going to work, now is it?"
#changes[key] = undefined
model.unset key
if errors = model.validate()
return console.warn model, "failed validation with errors:", errors
res = model.save changes, {patch: true}
res.error =>
return if @destroyed
console.error model, "failed to save with error:", res.responseText
res.success (model, response, options) =>
return if @destroyed
@hide()

View file

@ -18,19 +18,19 @@ module.exports = class Spell
@supermodel = options.supermodel
@skipProtectAPI = options.skipProtectAPI
@worker = options.worker
p = options.programmableMethod
p = options.programmableMethod
@languages = p.languages ? {}
@languages.javascript ?= p.source
@name = p.name
@permissions = read: p.permissions?.read ? [], readwrite: p.permissions?.readwrite ? [] # teams
teamSpells = @session.get('teamSpells')
team = @session.get('team') ? 'humans'
@useTranspiledCode = @permissions.readwrite.length and ((teamSpells and not _.contains(teamSpells[team], @spellKey)) or (@session.get('creator') isnt me.id and not (me.isAdmin() or 'employer' in me.get('permissions'))) or @spectateView)
#console.log @spellKey, "using transpiled code?", @useTranspiledCode
@source = @originalSource = p.source
@setLanguage if @canWrite() then options.language else 'javascript'
@useTranspiledCode = @shouldUseTranspiledCode()
@source = @originalSource
@parameters = p.parameters
if @permissions.readwrite.length and sessionSource = @session.getSourceFor(@spellKey)
@source = sessionSource
@language = if @canWrite() then options.language else 'javascript'
@thangs = {}
@view = new SpellView {spell: @, session: @session, worker: @worker}
@view.render() # Get it ready and code loaded in advance
@ -45,6 +45,9 @@ module.exports = class Spell
@thangs = null
@worker = null
setLanguage: (@language) ->
@originalSource = @languages[language] ? @languages.javascript
addThang: (thang) ->
if @thangs[thang.id]
@thangs[thang.id].thang = thang
@ -151,3 +154,14 @@ module.exports = class Spell
toString: ->
"<Spell: #{@spellKey}>"
shouldUseTranspiledCode: ->
# Determine whether this code has already been transpiled, or whether it's raw source needing transpilation.
return false unless @permissions.readwrite.length # Only player-writable code will be stored transpiled.
return true if @spectateView # Use transpiled code for both teams if we're just spectating.
teamSpells = @session.get('teamSpells')
team = @session.get('team') ? 'humans'
return true if teamSpells and not _.contains(teamSpells[team], @spellKey) # Use transpiled for enemy spells.
# Players without permissions can't view the raw code.
return true if @session.get('creator') isnt me.id and not (me.isAdmin() or 'employer' in me.get('permissions'))
false

View file

@ -647,9 +647,12 @@ module.exports = class SpellView extends View
@zatanna.set 'liveCompletion', (aceConfig.liveCompletion ? false)
onChangeLanguage: (e) ->
if @spell.canWrite()
@aceSession.setMode @editModes[e.language]
@zatanna.set 'language', @editModes[e.language].substr('ace/mode/')
return unless @spell.canWrite()
@aceSession.setMode @editModes[e.language]
@zatanna.set 'language', @editModes[e.language].substr('ace/mode/')
wasDefault = @getSource() is @spell.originalSource
@spell.setLanguage e.language
@reloadCode true if wasDefault
dismiss: ->
@spell.hasChangedSignificantly @getSource(), null, (hasChanged) =>

View file

@ -35,7 +35,8 @@ exports.config =
(bower_components[\/\\]aether[\/\\]build[\/\\]aether.js)
)///
'javascripts/test-app.js': /^test[\/\\]app/
# 'test/javascripts/test-vendor.js': /^test[\/\\](?=vendor)/
'javascripts/demo-app.js': /^test[\/\\]demo/
order:
before: [
'bower_components/jquery/dist/jquery.js'

14
multicore.coffee Normal file
View file

@ -0,0 +1,14 @@
cluster = require 'cluster'
numCPUs = require('os').cpus().length
if cluster.isMaster
for i in [0...numCPUs]
cluster.fork()
cluster.on 'exit', (worker, code, signal) ->
console.log 'worker' + worker.process.id + 'died'
cluster.fork()
else
require('coffee-script')
require('coffee-script/register')
server = require('./server')
server.startServer()

View file

@ -58,7 +58,13 @@ checkDependencies deps[@] basicDependenciesErrorHandling
if command -v node >/dev/null 2>&1; then
checkNodeVersion
fi
#install git repository
git clone $repositoryUrl coco
#python ./coco/scripts/devSetup/setup.py
echo "Now copy and paste 'sudo python ./coco/scripts/devSetup/setup.py' into the terminal!"
#check if a git repository already exists here
if [ -d .git ]; then
echo "A git repository already exists here!"
else
#install git repository
git clone $repositoryUrl coco
#python ./coco/scripts/devSetup/setup.py
echo "Now copy and paste 'sudo python ./coco/scripts/devSetup/setup.py' into the terminal!"
fi

View file

@ -33,7 +33,8 @@ class SystemConfiguration(object):
return 64
else:
if self.operating_system == u"mac":
raise NotSupportedError(u"Your processor is determined to have a maxSize of" + sys.maxsize +
if os.uname()[4] == u"x86_64":
return 64
raise NotSupportedError(u"Your processor is determined to have a maxSize of" + str(sys.maxsize) +
u",\n which doesn't correspond with a 64-bit architecture.")
return 32

View file

@ -39,6 +39,6 @@ module.exports.gatewayTimeoutError = (res, message="Gateway timeout") ->
res.send 504, message
res.end()
module.exports.clientTimeout = (res, message="The server did not recieve the client response in a timely manner") ->
module.exports.clientTimeout = (res, message="The server did not receive the client response in a timely manner") ->
res.send 408, message
res.end()

View file

@ -8,6 +8,7 @@ module.exports.handlers =
'patch': 'patches/patch_handler'
'thang_type': 'levels/thangs/thang_type_handler'
'user': 'users/user_handler'
'user_remark': 'users/remarks/user_remark_handler'
'achievement': 'achievements/achievement_handler'
'earned_achievement': 'achievements/earned_achievement_handler'

View file

@ -0,0 +1,11 @@
mongoose = require('mongoose')
plugins = require('../../plugins/plugins')
jsonschema = require('../../../app/schemas/models/user_remark')
UserRemarkSchema = new mongoose.Schema({
created:
type: Date
'default': Date.now
}, {strict: false})
module.exports = UserRemark = mongoose.model('user.remark', UserRemarkSchema)

View file

@ -0,0 +1,12 @@
UserRemark = require('./UserRemark')
Handler = require('../../commons/Handler')
class UserRemarkHandler extends Handler
modelClass: UserRemark
editableProperties: ['user', 'contact', 'history', 'tasks', 'userName', 'contactName']
jsonSchema: require '../../../app/schemas/models/user_remark'
hasAccess: (req) ->
req.user?.isAdmin()
module.exports = new UserRemarkHandler()

View file

@ -11,6 +11,7 @@ log = require 'winston'
LevelSession = require('../levels/sessions/LevelSession')
LevelSessionHandler = require '../levels/sessions/level_session_handler'
EarnedAchievement = require '../achievements/EarnedAchievement'
UserRemark = require './remarks/UserRemark'
serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset']
privateProperties = [
@ -23,7 +24,7 @@ candidateProperties = [
UserHandler = class UserHandler extends Handler
modelClass: User
jsonSchema: schema
editableProperties: [
'name', 'photoURL', 'password', 'anonymous', 'wizardColor1', 'volume',
'firstName', 'lastName', 'gender', 'facebookID', 'gplusID', 'emails',
@ -31,15 +32,11 @@ UserHandler = class UserHandler extends Handler
'wizard', 'aceConfig', 'autocastDelay', 'lastLevel', 'jobProfile'
]
jsonSchema: schema
constructor: ->
super(arguments...)
@editableProperties.push('permissions') unless config.isProduction
getEditableProperties: (req, document) ->
props = super req, document
props.push 'jobProfileApproved', 'jobProfileNotes' if req.user.isAdmin()
props.push 'permissions' unless config.isProduction
props.push 'jobProfileApproved', 'jobProfileNotes' if req.user.isAdmin() # Admins naturally edit these
props.push privateProperties... if req.user.isAdmin() # Admins are mad with power
props
formatEntity: (req, document) ->
@ -197,6 +194,7 @@ UserHandler = class UserHandler extends Handler
return @getMySimulatorLeaderboardRank(req, res, args[0]) if args[1] is 'simulator_leaderboard_rank'
return @getEarnedAchievements(req, res, args[0]) if args[1] is 'achievements'
return @trackActivity(req, res, args[0], args[2], args[3]) if args[1] is 'track' and args[2]
return @getRemark(req, res, args[0]) if args[1] is 'remark'
return @sendNotFoundError(res)
super(arguments...)
@ -313,7 +311,7 @@ UserHandler = class UserHandler extends Handler
#query.jobProfileApproved = true unless req.user.isAdmin() # We split into featured and other now.
query['jobProfile.active'] = true unless req.user.isAdmin()
selection = 'jobProfile jobProfileApproved photoURL'
selection += ' email' if authorized
selection += ' email name' if authorized
User.find(query).select(selection).exec (err, documents) =>
return @sendDatabaseError(res, err) if err
candidates = (candidate for candidate in documents when @employerCanViewCandidate req.user, candidate.toObject())
@ -321,7 +319,7 @@ UserHandler = class UserHandler extends Handler
@sendSuccess(res, candidates)
formatCandidate: (authorized, document) ->
fields = if authorized then ['jobProfile', 'jobProfileApproved', 'photoURL', '_id'] else ['jobProfile', 'jobProfileApproved']
fields = if authorized then ['name', 'jobProfile', 'jobProfileApproved', 'photoURL', '_id'] else ['jobProfile', 'jobProfileApproved']
obj = _.pick document.toObject(), fields
obj.photoURL ||= obj.jobProfile.photoURL if authorized
subfields = ['country', 'city', 'lookingFor', 'jobTitle', 'skills', 'experience', 'updated', 'active']
@ -342,7 +340,7 @@ UserHandler = class UserHandler extends Handler
getEmployers: (req, res) ->
return @sendUnauthorizedError(res) unless req.user.isAdmin()
query = {employerAt: {$exists: true}}
query = {employerAt: {$exists: true, $ne: ''}}
selection = 'name firstName lastName email activity signedEmployerAgreement photoURL employerAt'
User.find(query).select(selection).lean().exec (err, documents) =>
return @sendDatabaseError res, err if err
@ -363,4 +361,17 @@ UserHandler = class UserHandler extends Handler
hash.update(user.get('_id') + '')
hash.digest('hex')
getRemark: (req, res, userID) ->
return @sendUnauthorizedError(res) unless req.user.isAdmin()
query = user: userID
projection = null
if req.query.project
projection = {}
projection[field] = 1 for field in req.query.project.split(',')
UserRemark.findOne(query).select(projection).exec (err, remark) =>
return @sendDatabaseError res, err if err
return @sendNotFoundError res unless remark?
@sendSuccess res, remark
module.exports = new UserHandler()

View file

@ -0,0 +1,43 @@
PatchesView = require 'views/editor/patches_view'
CocoModel = require 'models/CocoModel'
class BlandModel extends CocoModel
@className: 'Bland'
@schema: {
type: 'object'
additionalProperties: false
properties:
number: {type: 'number'}
object: {type: 'object'}
string: {type: 'string'}
_id: {type: 'string'}
}
urlRoot: '/db/bland'
module.exports = ->
model = new BlandModel({_id:'12345', name:'name', original:'original'})
v = new PatchesView(model)
v.load()
# Respond to request for pending patches.
r = jasmine.Ajax.requests.mostRecent()
patches = [
{
delta: null
commitMessage: 'Demo message'
creator: '12345'
created: "2014-01-01T12:00:00.000Z"
status: 'pending'
}
]
r.response({ status:200, responseText: JSON.stringify patches })
# Respond to request for user ids -> names
r = jasmine.Ajax.requests.mostRecent()
names = { '12345': { name: 'Patchman' } }
r.response({ status:200, responseText: JSON.stringify names })
v.render()
v

View file

@ -124,7 +124,7 @@
//final_score = (word_score + abbreviation_score) / 2;
final_score = ((abbreviation_score * (abbreviation_length / string_length)) + abbreviation_score) / 2;
final_score = final_score / fuzzies;
final_score /= fuzzies;
if (start_of_string_bonus && (final_score + 0.15 < 1)) {
final_score += 0.15;