diff --git a/app/lib/storage.coffee b/app/lib/storage.coffee index 78ea56111..21e8d59a9 100644 --- a/app/lib/storage.coffee +++ b/app/lib/storage.coffee @@ -5,7 +5,7 @@ module.exports.load = (key) -> value = JSON.parse(s) return value catch SyntaxError - console.warning('error loading from storage', key) + console.warn('error loading from storage', key) return null module.exports.save = (key, value) -> diff --git a/app/locale/en.coffee b/app/locale/en.coffee index 1fc298454..1a5da3221 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -668,3 +668,4 @@ user_schema: "User Schema" user_profile: "User Profile" patches: "Patches" + model: "Model" diff --git a/app/locale/hu.coffee b/app/locale/hu.coffee index 3c4e4f26d..355eaf52b 100644 --- a/app/locale/hu.coffee +++ b/app/locale/hu.coffee @@ -175,29 +175,29 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t edit_settings: "Beállítások szerkesztése" # profile_for_prefix: "Profile for " # profile_for_suffix: "" -# approved: "Approved" -# not_approved: "Not Approved" -# looking_for: "Looking for:" -# last_updated: "Last updated:" -# contact: "Contact" -# work_experience: "Work Experience" -# education: "Education" + approved: "Jóváhagyva" + not_approved: "Nincs jóváhagyva" + looking_for: "Keres:" + last_updated: "Legutóbb napra-készre hozva:" + contact: "Kapcsolat" + work_experience: "Munkatapasztalat" + education: "Végzettség" # our_notes: "Our Notes" -# projects: "Projects" + projects: "Projektek" -# employers: -# want_to_hire_our_players: "Want to hire expert CodeCombat players?" -# contact_george: "Contact George to see our candidates" -# candidates_count_prefix: "We currently have " -# candidates_count_many: "many" -# candidates_count_suffix: "highly skilled and vetted developers looking for work." -# candidate_name: "Name" + munkaadók: + want_to_hire_our_players: "Akarsz szakértő CodeCombat játékosokat alkalmazni?" + contact_george: "Vedd fel a kapcsolatot George-dzsal, hogy megtekinthesd jelöltjeinket" + candidates_count_prefix: "Pillanatnyilag van" + candidates_count_many: "sok" + candidates_count_suffix: "magasan képzett és ellenőrzött fejlesztő, aki munkát keres." + candidate_name: "Név" # candidate_location: "Location" -# candidate_looking_for: "Looking For" -# candidate_role: "Role" + candidate_looking_for: "Keres" + candidate_role: "Szerep" # candidate_top_skills: "Top Skills" # candidate_years_experience: "Yrs Exp" -# candidate_last_updated: "Last Updated" + candidate_last_updated: "Legutóbb napra-készre hozva" play_level: level_load_error: "A pályát nem sikerült betölteni: " diff --git a/app/models/Level.coffee b/app/models/Level.coffee index 7832c329a..b180218fb 100644 --- a/app/models/Level.coffee +++ b/app/models/Level.coffee @@ -8,7 +8,8 @@ module.exports = class Level extends CocoModel urlRoot: "/db/level" serialize: (supermodel) -> - o = _.cloneDeep @attributes # slow in level editor when there are hundreds of Thangs + # o = _.cloneDeep @attributes # slow in level editor when there are hundreds of Thangs + o = $.extend true, {}, @attributes # Figure out Components o.levelComponents = _.cloneDeep (lc.attributes for lc in supermodel.getModels LevelComponent) diff --git a/app/schemas/models/level_session.coffee b/app/schemas/models/level_session.coffee index 670dc9ad4..28db97daf 100644 --- a/app/schemas/models/level_session.coffee +++ b/app/schemas/models/level_session.coffee @@ -101,9 +101,13 @@ _.extend LevelSessionSchema.properties, source: type: 'string' -# TODO: specify this more code: type: 'object' + additionalProperties: + type: 'object' + additionalProperties: + type: 'string' + format: 'javascript' teamSpells: type: 'object' @@ -134,6 +138,11 @@ _.extend LevelSessionSchema.properties, submittedCode: type: 'object' + additionalProperties: + type: 'object' + additionalProperties: + type: 'string' + format: 'javascript' isRanking: type: 'boolean' diff --git a/app/styles/modal/model.sass b/app/styles/modal/model.sass new file mode 100644 index 000000000..8ecf502dd --- /dev/null +++ b/app/styles/modal/model.sass @@ -0,0 +1,9 @@ +#model-modal + .treema-root + background-color: white + + .modal-dialog + width: 1000px + + .treema-ace .ace_editor + height: 600px diff --git a/app/styles/play/ladder/ladder_tab.sass b/app/styles/play/ladder/ladder_tab.sass index 6d65cc5a6..f722faf18 100644 --- a/app/styles/play/ladder/ladder_tab.sass +++ b/app/styles/play/ladder/ladder_tab.sass @@ -4,6 +4,9 @@ white-space: nowrap overflow: hidden text-overflow: ellipsis + + .histogram-display + height: 130px .bar rect fill: steelblue @@ -39,4 +42,8 @@ .ogres-rank-text fill: #3f44bf - \ No newline at end of file + + .load-more-ladder-entries + position: absolute + right: 15px + bottom: -5px diff --git a/app/templates/modal/model.jade b/app/templates/modal/model.jade new file mode 100644 index 000000000..4fae4c294 --- /dev/null +++ b/app/templates/modal/model.jade @@ -0,0 +1,9 @@ +extends /templates/modal/modal_base + +block modal-header + +block modal-body-content + for model in models + h3= model.type() + ': ' + model.id + .model-treema(data-model-id=model.id) + hr diff --git a/app/templates/play/ladder/ladder_tab.jade b/app/templates/play/ladder/ladder_tab.jade index 1b7d7351e..16bba4f8c 100644 --- a/app/templates/play/ladder/ladder_tab.jade +++ b/app/templates/play/ladder/ladder_tab.jade @@ -1,5 +1,5 @@ div#columns.row - for team in teams + for team, teamIndex in teams div.column.col-md-4 div(id="histogram-display-#{team.name}", class="histogram-display",data-team-name=team.name) table.table.table-bordered.table-condensed.table-hover @@ -17,10 +17,10 @@ div#columns.row - var topSessions = team.leaderboard.topPlayers.models; - var showJustTop = team.leaderboard.inTopSessions() || me.get('anonymous'); - - if(!showJustTop) topSessions = topSessions.slice(0, 10); + - if(!showJustTop && topSessions.length == 20) topSessions = topSessions.slice(0, 10); for session, rank in topSessions - var myRow = session.get('creator') == me.id - tr(class=myRow ? "success" : "") + tr(class=myRow ? "success" : "", data-player-id=session.get('creator'), data-session-id=session.id) td.rank-cell= rank + 1 td.score-cell= Math.round(session.get('totalScore') * 100) td.name-col-cell= session.get('creatorName') || "Anonymous" @@ -33,14 +33,16 @@ div#columns.row td(colspan=4).ellipsis-row ... for session in team.leaderboard.nearbySessions() - var myRow = session.get('creator') == me.id - tr(class=myRow ? "success" : "") + tr(class=myRow ? "success" : "", data-player-id=session.get('creator'), data-session-id=session.id) td.rank-cell= session.rank td.score-cell= Math.round(session.get('totalScore') * 100) td.name-col-cell= session.get('creatorName') || "Anonymous" td.fight-cell a(href="/play/level/#{level.get('slug') || level.id}/?team=#{team.otherTeam}&opponent=#{session.id}") span(data-i18n="ladder.fight") Fight! - + if teamIndex == 1 + .btn.btn-sm.load-more-ladder-entries moar + div.column.col-md-4 h4.friends-header Friends Playing if me.get('anonymous') diff --git a/app/views/account/settings_view.coffee b/app/views/account/settings_view.coffee index e1f2685c1..a9674c919 100644 --- a/app/views/account/settings_view.coffee +++ b/app/views/account/settings_view.coffee @@ -78,7 +78,7 @@ module.exports = class SettingsView extends View buildPictureTreema: -> data = photoURL: me.get('photoURL') data.photoURL = null if data.photoURL?.search('gravatar') isnt -1 # Old style - schema = _.cloneDeep me.schema() + schema = $.extend true, {}, me.schema() schema.properties = _.pick me.schema().properties, 'photoURL' schema.required = ['photoURL'] treemaOptions = @@ -93,7 +93,6 @@ module.exports = class SettingsView extends View @$el.find('.gravatar-fallback').toggle not me.get 'photoURL' onPictureChanged: (e) => - console.log "on Picture TChoinagaegd" @trigger 'change' @$el.find('.gravatar-fallback').toggle not me.get 'photoURL' diff --git a/app/views/kinds/CocoView.coffee b/app/views/kinds/CocoView.coffee index 6d295bdb2..b368f51ab 100644 --- a/app/views/kinds/CocoView.coffee +++ b/app/views/kinds/CocoView.coffee @@ -113,15 +113,18 @@ class CocoView extends Backbone.View @listenToOnce modelOrCollection, 'sync', @updateProgress @listenTo modelOrCollection, 'error', @onResourceLoadFailed @updateProgress() + @loaded = false addRequestToLoad: (jqxhr, name, retryFunc, value=1) -> @loadProgress.requests.push {request:jqxhr, value:value, name: name, retryFunc: retryFunc} jqxhr.done @updateProgress jqxhr.fail @onRequestLoadFailed + @loaded = false addSomethingToLoad: (name, value=1) -> @loadProgress.somethings.push {loaded: false, name: name, value: value} @updateProgress() + @loaded = false somethingLoaded: (name) -> r = _.find @loadProgress.somethings, {name: name} @@ -145,9 +148,9 @@ class CocoView extends Backbone.View console.debug 'Loaded', r.name if arguments[0] and r = _.find @loadProgress.somethings, {name:arguments[0]} denom = 0 - denom += r.value for r in @loadProgress.resources + denom += r.value for r in @loadProgress.resources when not r.resource.destroyed denom += r.value for r in @loadProgress.requests - denom += r.value for r in @loadProgress.somethings + denom += r.value for r in @loadProgress.somethings when not r.destroyed num = @loadProgress.num num += r.value for r in @loadProgress.resources when r.resource.loaded num += r.value for r in @loadProgress.requests when r.request.status diff --git a/app/views/modal/model_modal.coffee b/app/views/modal/model_modal.coffee new file mode 100644 index 000000000..4f56855e4 --- /dev/null +++ b/app/views/modal/model_modal.coffee @@ -0,0 +1,47 @@ +View = require 'views/kinds/ModalView' +template = require 'templates/modal/model' + +module.exports = class ModelModal extends View + id: 'model-modal' + template: template + + constructor: (options) -> + super options + @models = options.models + for model in @models when not model.loaded + @addResourceToLoad model, 'model' + model.fetch() + + getRenderData: -> + c = super() + c.models = @models + c + + afterRender: -> + super() + return if @loading() + for model in @models + data = $.extend true, {}, model.attributes + schema = $.extend true, {}, model.schema() + treemaOptions = + schema: schema + data: data + readOnly: true + modelTreema = @$el.find(".model-treema[data-model-id='#{model.id}']").treema treemaOptions + modelTreema?.build() + modelTreema?.open() + @openTastyTreemas modelTreema, model + + openTastyTreemas: (modelTreema, model) -> + # To save on quick inspection, let's auto-open the properties we're most likely to want to see. + delicacies = ['code'] + for dish in delicacies + child = modelTreema.childrenTreemas[dish] + child?.open() + if child and dish is 'code' and model.type() is 'LevelSession' and team = modelTreema.get('team') + desserts = { + humans: ['programmable-tharin', 'programmable-librarian'] + ogres: ['programmable-brawler', 'programmable-shaman'] + }[team] + for dessert in desserts + child.childrenTreemas[dessert]?.open() diff --git a/app/views/modal/save_version_modal.coffee b/app/views/modal/save_version_modal.coffee index fda6b3951..6533a741f 100644 --- a/app/views/modal/save_version_modal.coffee +++ b/app/views/modal/save_version_modal.coffee @@ -18,7 +18,6 @@ module.exports = class SaveVersionModal extends ModalView constructor: (options) -> super options @model = options.model or options.level - new Patch() # hack to get the schema to load, delete this later @isPatch = not @model.hasWriteAccess() getRenderData: -> diff --git a/app/views/play/ladder/ladder_tab.coffee b/app/views/play/ladder/ladder_tab.coffee index 59d04b102..d80131358 100644 --- a/app/views/play/ladder/ladder_tab.coffee +++ b/app/views/play/ladder/ladder_tab.coffee @@ -3,8 +3,10 @@ CocoClass = require 'lib/CocoClass' Level = require 'models/Level' LevelSession = require 'models/LevelSession' CocoCollection = require 'models/CocoCollection' +User = require 'models/User' LeaderboardCollection = require 'collections/LeaderboardCollection' {teamDataFromLevel} = require './utils' +ModelModal = require 'views/modal/model_modal' HIGHEST_SCORE = 1000000 @@ -23,6 +25,8 @@ module.exports = class LadderTabView extends CocoView events: 'click .connect-facebook': 'onConnectFacebook' 'click .connect-google-plus': 'onConnectGPlus' + 'click .name-col-cell': 'onClickPlayerName' + 'click .load-more-ladder-entries': 'onLoadMoreLadderEntries' subscriptions: 'fbapi-loaded': 'checkFriends' @@ -134,7 +138,8 @@ module.exports = class LadderTabView extends CocoView for team in @teams @leaderboards[team.id]?.destroy() teamSession = _.find @sessions.models, (session) -> session.get('team') is team.id - @leaderboards[team.id] = new LeaderboardData(@level, team.id, teamSession) + @ladderLimit ?= parseInt @getQueryVariable('top_players', 20) + @leaderboards[team.id] = new LeaderboardData(@level, team.id, teamSession, @ladderLimit) @addResourceToLoad @leaderboards[team.id], 'leaderboard', 3 @@ -245,17 +250,30 @@ module.exports = class LadderTabView extends CocoView sessions.reverse() sessions + # Admin view of players' code + onClickPlayerName: (e) -> + return unless me.isAdmin() + row = $(e.target).parent() + player = new User _id: row.data 'player-id' + session = new LevelSession _id: row.data 'session-id' + @openModalView new ModelModal models: [session, player] + + onLoadMoreLadderEntries: (e) -> + @ladderLimit ?= 100 + @ladderLimit += 100 + @refreshLadder() + class LeaderboardData extends CocoClass ### Consolidates what you need to load for a leaderboard into a single Backbone Model-like object. ### - constructor: (@level, @team, @session) -> + constructor: (@level, @team, @session, @limit) -> super() @fetch() fetch: -> - @topPlayers = new LeaderboardCollection(@level, {order:-1, scoreOffset: HIGHEST_SCORE, team: @team, limit: 20}) + @topPlayers = new LeaderboardCollection(@level, {order:-1, scoreOffset: HIGHEST_SCORE, team: @team, limit: @limit}) promises = [] promises.push @topPlayers.fetch() diff --git a/app/views/play/ladder_view.coffee b/app/views/play/ladder_view.coffee index 2b8e3a2b8..488e3edb7 100644 --- a/app/views/play/ladder_view.coffee +++ b/app/views/play/ladder_view.coffee @@ -71,7 +71,7 @@ module.exports = class LadderView extends RootView return if @loading() @insertSubView(@ladderTab = new LadderTabView({}, @level, @sessions)) @insertSubView(@myMatchesTab = new MyMatchesTabView({}, @level, @sessions)) - @refreshInterval = setInterval(@fetchSessionsAndRefreshViews.bind(@), 10 * 1000) + @refreshInterval = setInterval(@fetchSessionsAndRefreshViews.bind(@), 20 * 1000) hash = document.location.hash[1..] if document.location.hash if hash and not (hash in ['my-matches', 'simulate', 'ladder']) @showPlayModal(hash) if @sessions.loaded diff --git a/app/views/play/level/level_loading_view.coffee b/app/views/play/level/level_loading_view.coffee index f242acec3..2c8b608b0 100644 --- a/app/views/play/level/level_loading_view.coffee +++ b/app/views/play/level/level_loading_view.coffee @@ -17,6 +17,7 @@ module.exports = class LevelLoadingView extends View @$el.find('.to-remove').remove() onLevelLoaderProgressChanged: (e) -> + return if @destroyed @progress = e.progress @progress = 0.01 if @progress < 0.01 @updateProgressBar()