Test table of contents is set to active

Test table of contents is set to active

--- test/components/reference-book.spec.coffee
+++ test/components/reference-book.spec.coffee
@@ -14,17 +14,19 @@

 COURSE_ID = '1'
 TOC  = require '../../api/courses/1/readings.json'
-FIRST_PAGE_ID  = '0e58aa87-2e09-40a7-8bf3-269b2fa16509'
+FIRST_PAGE_ID = '0e58aa87-2e09-40a7-8bf3-269b2fa16509'
+FIRST_PAGE  = '1.1'
+SECOND_PAGE = '1.2'
 SECOND_PAGE_ID = '0e58aa87-2e09-40a7-8bf3-269b2fa16510'
-THIRD_PAGE_ID  = '0e58aa87-2e09-40a7-8bf3-269b2fa16511'
+THIRD_PAGE  = '1.3'

 PAGE = require '../../api/pages/0e58aa87-2e09-40a7-8bf3-269b2fa16509.json'

-renderBook = (page) ->
+renderBook = (section) ->
   new Promise (resolve, reject) ->
     url = "/books/#{COURSE_ID}"
-    url += "/page/#{page}" if page
+    url += "/section/#{section}" if section
     routerStub.goTo(url).then( (result) ->
       resolve(_.extend({
         book: ReactTestUtils.findRenderedComponentWithType(result.component, ReferenceBook)
@@ -38,7 +40,7 @@
   beforeEach ->
     ReferenceBookActions.loaded(TOC, COURSE_ID)
     ReferenceBookPageActions.loaded(PAGE, FIRST_PAGE_ID)
-    renderBook(FIRST_PAGE_ID).then (state) =>
+    renderBook(FIRST_PAGE).then (state) =>
       @state = state

   it 'renders the section title on the navbar',  ->
@@ -49,9 +51,19 @@
     expect(@state.div.querySelector('.page').textContent)
       .to.equal('A bunch of html')

+  it 'sets current page to active on menu', ->
+    page = ReferenceBookActions.loaded(TOC, COURSE_ID)
+    console.log page
+    expect(@state.div.querySelector('.page').textContent)
+      .to.equal('A bunch of html')
+
   it 'toggles menu when navbar control is clicked', ->
     toggle = @state.div.querySelector('.menu-toggle')

+    expect(_.toArray(@state.div.querySelector('.reference-book').classList))
+      .to.contain('menu-open')
+
+    commonActions.click(toggle)
     expect(_.toArray(@state.div.querySelector('.reference-book').classList))
       .to.not.contain('menu-open')

@@ -59,29 +71,30 @@
     expect(_.toArray(@state.div.querySelector('.reference-book').classList))
       .to.contain('menu-open')

-    commonActions.click(toggle)
-    expect(_.toArray(@state.div.querySelector('.reference-book').classList))
-      .to.not.contain('menu-open')
-
   it 'navigates forward and back between pages', ->
     prevControl = @state.div.querySelector('.page-wrapper > .prev')
     expect(prevControl).to.not.exist # on first page

     nextControl = @state.div.querySelector('.page-wrapper > .next')
     expect(nextControl).to.exist
-    expect(nextControl.href).to.contain(SECOND_PAGE_ID)
+    expect(nextControl.href).to.contain(SECOND_PAGE)
     page = ReactTestUtils.findRenderedComponentWithType(@state.book, Page)
-

     ReferenceBookPageActions.loaded(PAGE, SECOND_PAGE_ID)
     # button:0 is a mystery argument needed by ReactRouter, taken from their specs
     commonActions.click(nextControl, {button: 0})

-    expect(page.context.router.getCurrentParams().cnxId)
-      .to.equal(SECOND_PAGE_ID)
+    expect(page.context.router.getCurrentParams().section)
+      .to.equal(SECOND_PAGE)

     expect(@state.div.querySelector('.page-wrapper > .next').href)
-      .to.contain(THIRD_PAGE_ID)
+      .to.contain(THIRD_PAGE)

     expect(@state.div.querySelector('.page-wrapper > .prev').href)
-      .to.contain(FIRST_PAGE_ID)
+      .to.contain(FIRST_PAGE)
+
+
+  it 'sets the menu item to be active based on the current page', ->
+    selection = @state.div.querySelector(".toc [data-section='#{FIRST_PAGE}'] a")
+    expect(selection).not.to.be.null
+    expect(_.toArray(selection.classList)).to.include('active')


Update micro-modal.coffee

Update micro-modal.coffee

--- source/micro-modal.coffee
+++ source/micro-modal.coffee
@@ -12,7 +12,6 @@
 initialize = (options) ->
   options = $.extend {
     backdrop: true
-    keyboard: true
     vertical: true
   }, options


fix missing default value

fix missing default value

--- lib/commands.coffee
+++ lib/commands.coffee
@@ -36,7 +36,7 @@
     if matches && !matches[3]
       return replaceLine: true, value: matches[1] || "\n"
     else
-      return replaceLine: false, value: value
+      return replaceLine: false, value: value || "\n"

   jumpToPreviousHeading: ->
     editor = atom.workspace.getActiveEditor()


KiteSubscription: changes on JKite schema

--- workers/social/lib/social/models/kite.coffee
+++ workers/social/lib/social/models/kite.coffee
@@ -1,11 +1,12 @@
 jraphical = require 'jraphical'
 module.exports = class JKite extends jraphical.Module
+
+  KodingError         = require '../error'
   {permit}            = require './group/permissionset'
-  {secure, signature} = require 'bongo'
-  JKitePlan           = require './kiteplan'
-  JAccount            = require './account'
-  KodingError         = require '../error'
+  {signature}         = require 'bongo'
   {Relationship}      = jraphical
+
+  @trait __dirname, '../traits/protected'

   @share()
   @set
@@ -14,6 +15,7 @@
       'list kites'        : ['member']
     schema                :
       name                : String
+      description         : String
       createdAt           :
         type              : Date
         default           : -> new Date
@@ -21,25 +23,29 @@
       instance            : []
       static              : []
     sharedMethods         :
-      static:
-        create:
+      static              :
+        create            :
           (signature Object, Function)
-        list:
+        list              :
           (signature Object, Object, Function)
-      instance:
-        modify:
+        one               :
           (signature Object, Function)
-        deleteKite:
+      instance            :
+        modify            :
+          (signature Object, Function)
+        deleteKite        :
           (signature Function)
-        fetchPlans:
+        fetchPlans        :
           (signature Function)
-        removePlan:
+        createPlan        :
+          (signature Object, Function)
+        deletePlan        :
           (signature Object, Function)

-    relationships: ->
-      plan          :
-        as          : 'kitePlan'
-        targetType  : JKitePlan
+    relationships       :
+      plan              :
+        targetType      : 'JPaymentPlan'
+        as              : 'kitePlan'

   @create: permit 'create kite',
     success:(client, formData, callback)->
@@ -47,7 +53,7 @@
       kite.save (err)->
         return  callback new KodingError "kite couldn't saved" if err
         account = client.connection.delegate
-        account.addKite (err, res)->
+        account.addKite kite, (err, res)->
           return  callback new KodingError "kite couldn't added to account" if err
           callback null, kite

@@ -57,9 +63,9 @@

   modify: permit 'edit kite',
     success: (client, formData, callback)->
-      @update $set: {name: formData.name} , callback
+      @update $set: {name: formData.name, description: formData.description} , callback

-  deleteKite : permit 'delete kite',
+  deleteKite: permit 'delete kite',
     success: (client, callback)->
       account = client.connection.delegate
       Relationship.one {


Increase test timeout for integration

Increase test timeout for integration

--- server/test/integration/indicator_admin.coffee
+++ server/test/integration/indicator_admin.coffee
@@ -13,7 +13,7 @@
 test("User can visit the admin page, click 'Add Indicator', enter a google
  spreadsheet key and import a new indicator ", (done)->
   Browser = require("zombie")
-  @timeout(5000)
+  @timeout(7000)

   importedIndicator =
     name: "Spiffing new indiator"


#656 don't throw error when specified register have no text

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -596,6 +596,14 @@

   execute: ->
     @mutationsBySelection = new Map()
+    @registerBySelection = new Map()
+    for selection in @editor.getSelections()
+      register = @vimState.register.get(null, selection)
+      if register.text?
+        @registerBySelection.set(selection, register)
+
+    return unless @registerBySelection.size
+
     @onDidFinishMutation(@adjustCursorPosition.bind(this))

     @onDidFinishOperation =>
@@ -623,7 +631,7 @@
           cursor.setBufferPosition(start)

   mutateSelection: (selection) ->
-    {text, type} = @vimState.register.get(null, selection)
+    {text, type} = @registerBySelection.get(selection)
     return unless text
     text = _.multiplyString(text, @getCount())
     @linewisePaste = type is 'linewise' or @isMode('visual', 'linewise')


Fixes an apparent mistake in checking object existence.

--- lib/renderer.coffee
+++ lib/renderer.coffee
@@ -43,7 +43,7 @@
   text = text.replace(/^\s*<!doctype(\s+.*)?>\s*/i, '')

   roaster text, options, (error, html) =>
-    return callback(error) if error
+    return callback(error) if error?

     html = sanitize(html)
     html = resolveImagePaths(html, filePath)


refactor

refactor

--- lib/resource.coffee
+++ lib/resource.coffee
@@ -6,25 +6,29 @@

 module.exports =
     register: (params) ->
-      app = config.get('app')
-      prefix = config.get('prefix')
-      globalQueryLimit = config.get('queryLimit')
+      defaultParams =
+        options: config.get('queryLimit') || {}
+        rest: {}
+        actions: {}
+
+      params = _.extend(defaultParams, params)

       params.url = params.url[1..]  if params.url.indexOf('/') is 0
       if params.url.indexOf('/') >= 0
         throw new Error("Resource of url can't contain '/', it can only be allowed to exist in the beginning.")

+      app = config.get 'app'
+      prefix = config.get 'prefix'
       resource = params.url
       model = params.model
+      options = params.options
+      rest = params.rest
+      actions = params.actions

       metadata.add(resource, model)
       schema = new mongoose.Schema(model, { _id: false, versionKey: false, collection: resource })
       schema.plugin(id)
       mongooseModel = mongoose.model resource, schema
-
-      options = _.extend(globalQueryLimit, params.options) || {}
-      rest = params.rest || {}
-      actions = params.actions || []

       resource = "#{prefix}/#{resource}"


:bomb: new default highlightSearch to true( WOW no spec fail it's bad ! )

--- lib/settings.coffee
+++ lib/settings.coffee
@@ -125,7 +125,7 @@
   useSmartcaseForSearchCurrentWord:
     default: false
     description: 'For `*` and `#`. Override `ignoreCaseForSearchCurrentWord`'
-  highlightSearch: false
+  highlightSearch: true
   highlightSearchExcludeScopes:
     default: []
     items: type: 'string'


Bug Fix: Cannot show torpedo attack miss

--- lib/simulate.coffee
+++ lib/simulate.coffee
@@ -113,16 +113,16 @@
     dmg = []
     dmg.push damage
     # クリティカルフラグ 0=ミス, 1=命中, 2=クリティカル
-    critical = []
-    for crt, j in raigeki.api_fcl[i]
-      if crt == 2
-        critical.push HitType.Critical
-      else if crt == 1
-        critical.push HitType.Hit
-      else
-        critical.push HitType.Miss
+    hit = []
+    api_hit = raigeki.api_fcl[i]
+    if api_hit == 2
+      hit.push HitType.Critical
+    else if api_hit == 1
+      hit.push HitType.Hit
+    else
+      hit.push HitType.Miss
     enemyShip[target - 1].nowHP -= damage
-    list.push new Attack AttackType.Normal, sortieShip[i - 1], enemyShip[target - 1], enemyShip[target - 1].maxHP, enemyShip[target - 1].nowHP, dmg, critical
+    list.push new Attack AttackType.Normal, sortieShip[i - 1], enemyShip[target - 1], enemyShip[target - 1].maxHP, enemyShip[target - 1].nowHP, dmg, hit
   # 雷撃ターゲット
   for target, i in raigeki.api_erai
     continue if target <= 0
@@ -130,17 +130,17 @@
     dmg = []
     dmg.push damage
     # api_cl_list		：クリティカルフラグ 0=ミス, 1=命中, 2=クリティカル
-    critical = []
-    for crt, j in raigeki.api_ecl[i]
-      if crt == 2
-        critical.push HitType.Critical
-      else if crt == 1
-        critical.push HitType.Hit
-      else
-        critical.push HitType.Miss
+    hit = []
+    api_hit = raigeki.api_ecl[i]
+    if api_hit == 2
+      hit.push HitType.Critical
+    else if api_hit == 1
+      hit.push HitType.Hit
+    else
+      hit.push HitType.Miss
     sortieShip[target - 1].nowHP -= damage
     useItem = checkRepairItem sortieShip[target - 1]
-    list.push new Attack AttackType.Normal, enemyShip[i - 1], sortieShip[target - 1], sortieShip[target - 1].maxHP, sortieShip[target - 1].nowHP, dmg, critical, useItem
+    list.push new Attack AttackType.Normal, enemyShip[i - 1], sortieShip[target - 1], sortieShip[target - 1].maxHP, sortieShip[target - 1].nowHP, dmg, hit, useItem
   # test log
   #console.log list
   list


Scroll #L links to upper third of the screen.

Fixes travis-ci/travis-ci#2276

--- assets/scripts/app/views/log.coffee
+++ assets/scripts/app/views/log.coffee
@@ -93,7 +93,7 @@
   tryScroll: ->
     if element = $("#log p:visible.highlight:first")
       $('#main').scrollTop(0)
-      $('html, body').scrollTop(element.offset()?.top) # weird, html works in chrome, body in firefox
+      $('html, body').scrollTop(element.offset()?.top - (window.innerHeight / 3)) # weird, html works in chrome, body in firefox

 # Log.Logger = ->
 # Log.Logger.prototype = $.extend new Log.Listener,


Validate only known types are set as props

--- src/components/learning-guide/info-link.cjsx
+++ src/components/learning-guide/info-link.cjsx
@@ -19,7 +19,7 @@
 module.exports = React.createClass

   displayName: 'LearningGuideInfoLink'
-  type: React.PropTypes.string.isRequired
+  type: React.PropTypes.oneOf(['student', 'teacher', 'teacher_student']).isRequired

   render: ->
     tooltip =


[Artist] removes unused import

[Artist] removes unused import

--- src/desktop/apps/artist/client/router.coffee
+++ src/desktop/apps/artist/client/router.coffee
@@ -18,7 +18,6 @@
 AuctionLots = require '../../../collections/auction_lots.coffee'
 ArtistAuctionResultsView = require './views/auction_results.coffee'
 Artist = require '../../../models/artist.coffee'
-splitTest = require '../../../components/split_test/index.coffee'

 module.exports = class ArtistRouter extends Backbone.Router
   routes:


Add support for recently viewed artwork rail on homepage

--- src/desktop/apps/home/queries/initial.coffee
+++ src/desktop/apps/home/queries/initial.coffee
@@ -2,9 +2,10 @@
   query($showHeroUnits: Boolean!) {
     home_page {
       artwork_modules(
-        max_rails: 6,
+        max_rails: 7,
         order: [
           ACTIVE_BIDS,
+          RECENTLY_VIEWED_WORKS
           RECOMMENDED_WORKS,
           FOLLOWED_ARTISTS,
           RELATED_ARTISTS,


fix #65 by passing config file location to wakatime-cli

--- lib/wakatime.coffee
+++ lib/wakatime.coffee
@@ -413,6 +413,10 @@
       if atom.config.get 'wakatime.debug'
         args.push('--verbose')

+      # fix for wakatime/atom-wakatime#65
+      args.push('--config')
+      args.push(path.join getUserHome(), '.wakatime.cfg')
+
       if atom.project.contains(file.path)
         currentFile = file.path
         for rootDir in atom.project.rootDirectories


Allow comments to be edited

Allow comments to be edited

--- web/scripts/codeland.coffee
+++ web/scripts/codeland.coffee
@@ -253,9 +253,10 @@
     if gameData.code.comments
         # Also ensures newlines in the data are properly commented out
         one= '// '+ ((gameData.code.comments.join('\n')).replace(/\n/g,'\n// '))
-        # gameData.code.initial = one + '\n' + gameData.code.initial
-        gameData.code.prefix = one + '\n'
-        gameData.code.show = true
+        gameData.code.initial = one + '\n' + gameData.code.initial
+        # To prevent comments from being edited
+        #gameData.code.prefix = one + '\n'
+        #gameData.code.show = true
     return

 root.getGameDescriptions = ->


Added documentation to codeland.coffee

Added documentation to codeland.coffee

--- web/scripts/codeland.coffee
+++ web/scripts/codeland.coffee
@@ -2,24 +2,39 @@

 # We will update this when running JVM /Beanshell code so that we can show the user the currently executing line
 window.notifyEvalSourcePosition = (startLine,startCol,endLine,endCol) ->
+    ###
+        Shows the user the currently executing line.
+        --> Where is this function called?
+    ###
     console.log startLine,startCol,endLine,endCol
     ##window.gameManager.codeEditor.editorGoToLine startLine
     return;

-# things assigned to root will be available outside this module
+# Things assigned to root will be available outside this module
 root = exports ? this.codeland = {}
+
+# root.UIcont is the main div codeland has to work with.
 root.UIcont = null

-#IE Support ....
-
+# Internet Explorer does not support console.log.
+# The following code was an attempt to work around this, I cannot remember how it failed.
 # if ( ! window.console)
 #     window.console= { log: -> }

-#Chrome support: 'this' must be the console object
-#log = (mesg)-> console.log mesg
+# Chrome support: 'this' must be the console object
+# This also failed somehow, I believe it was not logging messages correctly.
+# log = (mesg)-> console.log mesg

 root.initialize = (UIcont) ->
+    ###
+        External Function (used by something outside of this file)
+
+        Reads the config files, initializes Doppio, should be the first codeland function called.
+
+        @param UICont
+            A JQuery div where everything created by codeland will live.
+    ###
     $('#copyrightinfo').click -> window.AboutPage()
     root.gameSelectionScrollPosition = 0
     root.loadJSONConfigs()
@@ -28,28 +43,52 @@
     return

 root.initializeDoppio = ->
+    ###
+        Internal Function (used only by the code in this file)
+
+        Constructs the doppioAPI and tells it to preload the required code.
+    ###
     root.doppioReady = false
     root.doppioPreloaded = false
-    progress= $('#progress')
+    progress = $('#progress')
     count = 0;
     last_display = ""
-    progress_cb = (ignore_incorrect_fraction)->
+    progress_cb = (ignore_incorrect_fraction) ->
+        ###
+            Handles the progress bar and displaying what part of Doppio is loading
+            to the user.
+        ###
         count = count + 1
         display = Math.floor((100*count) / 391)
-        if(display==100)
+        if (display == 100)
             display = "Starting Java Virtual Machine..."
-        else display = "Opening "+display
-        if(last_display != display)
+        else display = "Opening " + display
+        if (last_display != display)
             last_display = display
             progress.html display
+        return
     preload_cb = ->
+        ###
+            Once doppio has intialized, this function is called to tell
+            doppio to pre-compile the functions used by the games.
+        ###
         root.doppioAPI.preload root.beanshellPreload, root.wrapperCompiled_cb
         root.doppioPreloaded = true
+        return
     root.doppioAPI = new DoppioApi null, preload_cb, progress_cb
     return

-
 root.wrapperCompiled_cb = =>
+    ###
+        Internal Function (used only by the code in this file)
+
+        Once doppio has finished pre-compiling, this draws the game-map
+        and, should something be waiting on doppio to compile and has
+        presented codeland with a callback, runs that callback.
+
+        Note: In the current implementation, only the latest thing to
+        assign a callback to codeland is run.
+    ###
     root.doppioReady = true
     console.log 'Finished Preloading Doppio'
     player = root.getPlayer()
@@ -61,13 +100,40 @@
     return

 root.waitForWrapper = (callback) ->
+    ###
+        External Function (used by something outside of this file)
+
+        Should there be something waiting for doppio to compile, it calls
+        this functions and provides a callback which will then be run when
+        doppio has finished compiling.
+
+        Note: In the current implementation, only the latest thing to
+        assign a callback to codeland is run.
+
+        @param callback
+            The function to call when doppio is finished compiling.
+    ###
     root.wrapperCompiledCallback = callback
     return

 root.reference = () ->
-
- # FRONTEND UI
+    ###
+        ???
+        Could the author (or whomever knows) provide here information as to
+        what this function does?
+    ###
+    return
+
+# FRONTEND UI
 root.drawGameMap = (player) ->
+    ###
+        Internal Function (used only by the code in this file)
+
+        Draws the game selection screen.
+
+        @param player
+            The player whose games to display.
+    ###
     descriptions = root.getGameDescriptions()
     mapDiv = $(root.UIcont)
     mapDiv.empty()
@@ -81,7 +147,7 @@
         # console.log "Game: #{game}"
         count = count + 1
         sel.buildDiv(count, game, descriptions[game], player.games[game], root.canPlay(game), codeland)
-#    addGameToMap game for game in gameSequence
+        return
     qcount = 0
     for quest in root.quests
         $("
Quest #{++qcount}:#{quest.title}
").appendTo tmp1
@@ -98,6 +164,15 @@
     return

 root.startGame = (game) ->
+    ###
+        External Function (used by something outside of this file)
+
+        Starts the given game, setting up the environment and creating the
+        gameManager.
+
+        @param game
+            The game to start.
+    ###
     console.log("Starting #{game}")
     for quest, index in root.quests
         found = quest.games.indexOf game
@@ -135,19 +210,70 @@
     #Not used ... window.location.hash='game='+encodeURIComponent(game)
     root.currentGame = new GameManager env
     root.currentGame.startGame()
-    root.currentGame.helpTips() unless env.stats.runCount >0
-    return
-
-deepcopy = (src) -> $.extend(true, {}, src)
+    root.currentGame.helpTips() unless env.stats.runCount > 0
+    return
+
+# Some browsers have a deepcopy function, others do not.
+# For those who do not, we use the JQuery deepcopy function.
+if not deepcopy?
+    deepcopy = (src) -> $.extend(true, {}, src)

 # BACKEND Methods useful for all games
-root.getString = (key) -> localStorage.getItem key
-
-root.setString = (key, value) -> localStorage.setItem key, value
-
-root.clearString = (key) -> localStorage.removeItem key
+
+# How Storage Works:
+# The browser has a persistant localstorage dictionary.
+# We currently take data, turn it into a JSON object, and store it in this
+# local storage.
+
+root.getString = (key) ->
+    ###
+        Internal Function (used only by the code in this file)
+
+        Retrieves a string from the browser's internal storage dictionary.
+
+        @param key
+            The key for the string we are retrieving.
+    ###
+    return localStorage.getItem key
+
+root.setString = (key, value) ->
+    ###
+        Internal Function (used only by the code in this file)
+
+        Sets a string in the browser's internal storage dictionary.
+
+        @param key
+            The key for the string we are storing.
+            This should be a DOMString.
+        @param value
+            The value we want associated with the key in the browser's
+            internal storage.
+            This should be a DOMString.
+    ###
+    localStorage.setItem key, value
+
+root.clearString = (key) ->
+    ###
+        Internal Function (used only by the code in this file)
+
+        Clears a string in the browser's internal storage dictionary.
+
+        @param key
+            The key for the string we are clearing.
+            This should be a DOMString.
+    ###
+    localStorage.removeItem key
+    return

 root.load = (key) ->
+    ###
+        Internal Function (used only by the code in this file)
+
+        Retreives and parses data from the localstorage and returns it.
+
+        @param key
+            The key of the data to load.
+    ###
     val = root.getString key
     return null unless val?
     result = JSON.parse val
@@ -155,14 +281,36 @@
     throw new Error("Could not parse " + val)

 root.store = (key, val) ->
+    ###
+        Internal Function (used only by the code in this file)
+
+        JSON-ifies the given value and stores it in the localstorage
+        associated to the given key.
+
+        @param key
+            The key which which to associate the stored data.
+        @param val
+            The data to store.
+    ###
     throw new Error("Value must exist") unless val?
     root.setString(key, JSON.stringify val)
     return

 root.loadGameStats = (gameKey) ->
-    p=root.getPlayer()
+    ###
+        Internal Function (used only by the code in this file)
+
+        Returns the game statistics for the game associated with gameKey.
+        Returns default values for all statistics shouled the game not
+        exist.
+
+        @param gameKey
+            The gameKey with which the game we want the statistics for
+            is associated.
+    ###
+    p = root.getPlayer()
     data = p.games[gameKey] ?= {}
-#Ensure we have the minimum number of expect properties
+    #Ensure we have the minimum number of expect properties
     data.abortCount ?=0
     data.runCount ?=0
     data.winCount ?=0
@@ -177,14 +325,30 @@

 #Updates the player data
 root.storeGameStats = (key, data) ->
+    ###
+        External Function (used by something outside of this file)
+
+        Adds the given statistics to the game corresponding to the given
+        key and saves these to the current player.
+
+        @param key
+            The key of the game whose statistics we are storing.
+        @param data
+            The statistics to be added to the game's data.
+    ###
     throw new Error("Cannot be null") unless key? && data?
-    root.updatePlayer((p)->
+    root.updatePlayer((p) ->
         p.games[key] ?= {}
         $.extend(p.games[key],data)
     )
     return

 root.showMap = () ->
+    ###
+        External Function (used by something outside of this file)
+
+        Stops the current game and draws the game selection screen.
+    ###
     root.currentGame.finishGame() if root.currentGame
     root.wrapperCompiledCallback = null if root.wrapperCompiledCallback?
     root.currentGame = null
@@ -192,9 +356,20 @@
     return

 root.getGame = ->
+    ###
+        Internal Function (used only by the code in this file)
+
+        Gets the game currently being played.
+    ###
     return getPlayer().currentGame

 root.getPlayer = ->
+    ###
+        Internal Function (used only by the code in this file)
+
+        Retrieves and returns the current player.
+        Returns a generic player should the current player not exist.
+    ###
     root.currentPlayer ?= root.load("CurrentPlayer")
     root.currentPlayer ?= {
         id : +(new Date())
@@ -204,18 +379,49 @@
         avator : 'generic'
         games : { }
     }
+    return root.currentPlayer

 root.updatePlayer = (callback) ->
+    ###
+        Internal Function (used only by the code in this file)
+
+        Updates the current player.
+
+        @param callback
+            A function which is passed the current player and expected
+            to change the player.
+            Its return value is ignored.
+
+            See codeland.storeGameStats for an example.
+    ###
     player = root.getPlayer()
     callback(player)
     root.store("CurrentPlayer", player)
     return

 root.clearPlayer = ->
+    ###
+        Internal Function (used only by the code in this file)
+
+        Clears all data bout the current player.
+    ###
     root.clearString "CurrentPlayer"
     return

 root.readJSON = (theurl, cb) ->
+    ###
+        Internal Function (used only by the code in this file)
+
+        Reads the json data at the given url via an ajax request.
+        Passes the resulting data to the provided callback, passing
+        undefined should the ajax request have failed.
+
+        @param theurl
+            The location and name of the json file.
+        @param cb
+            The callback function which will be passed the data from
+            the json file.
+    ###
     fail = false
     console.log "Reading #{theurl}"
     try
@@ -233,13 +439,18 @@
                 return
             }
     catch exception
-        fail= true
+        fail = true
         console.log "#{theurl}: #{exception} #{exception.message} #{exception.stack}"
     if(fail)
         throw "Configuration Exception reading #{theurl}"
     return

 root.loadJSONConfigs = () ->
+    ###
+        Internal Function (used only by the code in this file)
+
+        Reads and parses all of the config files.
+    ###
     if not root.gameDescriptions?
         root.gameDescriptions = {}
     configFail = false
@@ -290,6 +501,18 @@
     return

 root.addToObject = (source, destination) ->
+    ###
+        Internal Function (used only by the code in this file)
+
+        Recursively adds all attributes of source into
+        destination.
+        Does not overwrite attributes of destination.
+
+        @param source
+            The source dictionary where attributes are copied from.
+        @param destination
+            The destination dictionary where attributes are added to.
+    ###
     for key, value of source
         if key of destination
             if typeof value == "object"
@@ -299,6 +522,15 @@
     return

 root.stringifyConfigArrays = (gameData) ->
+    ###
+        Internal Function (used only by the code in this file)
+
+        Converts gameData config information from arrays into newline
+        deliminated strings.
+
+        @param gameData
+            The game's data.
+    ###
     gameData.game.map = gameData.game.map.join '\n' if gameData?.game.map?.join?
     gameData.code.prefix = gameData.code.prefix.join '\n' if gameData?.code.prefix.join?
     if gameData.code.prefix.charAt(gameData.code.prefix.length - 1) != '\n'
@@ -308,6 +540,15 @@
     return

 root.convertShorthandToCode = (gameData) ->
+    ###
+        Internal Function (used only by the code in this file)
+
+        Converts the shorthand code found in the game's json config
+        to actual java code.
+
+        @param gameData
+            The game's data.
+    ###
     if gameData.code.initial?
         return
     initial = ''
@@ -341,7 +582,16 @@
     return

 root.addHintsToCode = (gameData) ->
-    gameData.code.initial?=''
+    ###
+        Internal Function (used only by the code in this file)
+
+        Takes the hints found in the comments array in the json config of
+        the game and places them as java-style comments in the game's code.
+
+        @param gameData
+            The game's data.
+    ###
+    gameData.code.initial ?= ''
     if gameData.code.comments
         # Also ensures newlines in the data are properly commented out
         one = '// '+ ((gameData.code.comments.join('\n')).replace(/\n/g,'\n// '))+ '\n'
@@ -353,27 +603,48 @@
     return

 root.getGameDescriptions = ->
+    ###
+        Internal Function (used only by the code in this file)
+
+        Returns the game descriptions, loading from the json config
+        files if the descriptions do not already exist.
+    ###
     if root.gameDescriptions?
         return root.gameDescriptions
     root.loadJSONConfigs()
     return root.gameDescriptions

 root.getGameSequence = ->
+    ###
+        Internal Function (used only by the code in this file)
+
+        Returns the sequence of all of the games, creating it should
+        it not already exist.
+    ###
     return root.gameSequence if root.gameSequence
     root.gameSequence = []
     games = root.getGameDescriptions()
     addGame = (name) =>
-        return if $.inArray(name, root.gameSequence)!=-1
+        return if $.inArray(name, root.gameSequence) != -1
         doFirst = games[name].depends ?= []
         addGame g for g in doFirst
         root.gameSequence.push name
         return
-    addGame(g) for g,ignore of games
+    addGame(g) for g, ignored of games
     return root.gameSequence

 root.canPlay = (game) ->
+    ###
+        Internal Function (used only by the code in this file)
+
+        Returns whether or not the current player can play the
+        given game.
+
+        @param game
+            The game we are checking.
+    ###
     player = root.getPlayer()
-    #If already completed then no need to check dependencies
+    # If already completed then no need to check dependencies
     return true if player?.games[game]?.passed

     depends = root.getGameDescriptions()[game]?.depends
@@ -384,4 +655,3 @@

     passCount++ for g in depends when player?.games[g]?.passed
     return passCount == depends.length
-    return


Use Written.toHTML() instead of prototype call

--- lib/written/app/assets/javascripts/written/attachments/image.coffee
+++ lib/written/app/assets/javascripts/written/attachments/image.coffee
@@ -6,7 +6,7 @@
     @node.querySelector('img').addEventListener 'click', @select.bind(this, @node)

   template: ->
-    "

+    Written.toHTML("

@@ -19,7 +19,7 @@

-      ".toHTML()
+      ")

   select: (node) =>
     @selection = @getSelection()
@@ -65,12 +65,12 @@
     dialog.classList.add 'failed'
     dialog.querySelector('h3').textContent = "Failed"
     dialog.querySelector('figure').remove()
-    dialog.appendChild("
+    dialog.appendChild(Written.toHTML("

An error occured while trying to process your image.

         <button>Close</button>

-    ".toHTML())
+    "))
     dialog.querySelector('button').addEventListener('click', @cancel)

   upload: (e) =>


pause now works again

pause now works again

--- examples/audio/static/coffee/spectrogram.coffee
+++ examples/audio/static/coffee/spectrogram.coffee
@@ -82,8 +82,9 @@
       #console.log "AJAX Error: #{textStatus}"
     success: (data, textStatus, jqXHR) =>
       @on_data(data)
-      requestAnimationFrame(
-        => @request_data())
+      if not @paused
+        requestAnimationFrame(
+          => @request_data())

     new_render = new Date()
     console.log("render_time", new_render - @last_render)
@@ -216,8 +217,8 @@
       else
         button.text('pause')
         @paused = false
-        @request_data()
-    )
+        @request_data())
+
     controls.append(button)

     controls.css('clear', 'both')


minor update for impac api minor refactor

--- src/components/widgets-settings/accounts-list/accounts-list.directive.coffee
+++ src/components/widgets-settings/accounts-list/accounts-list.directive.coffee
@@ -39,7 +39,7 @@
     sort = ->
       baseAccount = src.shift()
       matcher = normalise(baseAccount[groupByKey])
-      group = { name: baseAccount.account_name, uid: counter++ }
+      group = { name: baseAccount.account_name || baseAccount.name, uid: counter++ }
       group.accounts = _.select src, (acc, index) ->
         src.splice(index, 1)[0] if !!acc && normalise(acc[groupByKey]) == matcher
       if group.accounts.length > 0


Set version 1.0.1 for DownloadBitlyLinks

--- model/Recipe/Bitly/ExportBitlyDataToGoogleSpreadsheet.coffee
+++ model/Recipe/Bitly/ExportBitlyDataToGoogleSpreadsheet.coffee
@@ -13,6 +13,7 @@
     @insertStep
       key: "ExportBitlyDataToGoogleSpreadsheet"
       cls: "ExportBitlyDataToGoogleSpreadsheet"
+      version: "1.0.1"

   input: (step) ->
     switch step.key


Less bracey.

Less bracey.

--- library/Keys.coffee
+++ library/Keys.coffee
@@ -26,13 +26,13 @@
     when callback?.constructor isnt Function
       return "Can’t make a pair of keys without a callback function."
     when secretPhrase is undefined
-      callback("Can’t make a pair of keys without a secret phrase.")
+      callback "Can’t make a pair of keys without a secret phrase."
     when exports.secretPhraseIsAcceptable(secretPhrase) is no
-      callback("Can’t make a pair of keys because the secret phrase is unacceptable.")
+      callback "Can’t make a pair of keys because the secret phrase is unacceptable."
     when emailAddress is undefined
-      callback("Can’t make a pair of keys without an email address.")
+      callback "Can’t make a pair of keys without an email address."
     when exports.emailAddressIsAcceptable(emailAddress) is no
-      callback("Can’t make a pair of keys because the email address is unacceptable.")
+      callback "Can’t make a pair of keys because the email address is unacceptable."
     when secretPhrase and emailAddress and callback
       # Decode each input into a Uint8Array of bytes.
       decodedSecretPhrase = NACL.util.decodeUTF8(secretPhrase)


actual fix to done state in bucket item

actual fix to done state in bucket item

--- src/node_modules/bucket-item/bucket-item-link.coffee
+++ src/node_modules/bucket-item/bucket-item-link.coffee
@@ -99,7 +99,7 @@

   if bucket.myContribution.amount > 0
     scope.model.inputStatus = "saved"
-  else if round.myAllocationLeft.eq(0)
+  else if round.status.name != "open"
     scope.model.inputStatus = "done"
   else
     scope.model.inputStatus = "ready"


Fix keystroke trigger method in specs

Fix keystroke trigger method in specs

--- spec/vim-mode-spec.coffee
+++ spec/vim-mode-spec.coffee
@@ -185,8 +185,8 @@
           editor.setText("abcd efg")
           editor.setCursorScreenPosition([0,2])

-          editor.trigger keydownEvent('d')
-          editor.trigger keydownEvent('b')
+          keydown('d', element: editor[0])
+          keydown('b', element: editor[0])

           expect(editor.getText()).toBe "cd efg"
           expect(editor.getCursorScreenPosition()).toEqual([0,0])
@@ -194,9 +194,9 @@
           editor.setText("one two three four")
           editor.setCursorScreenPosition([0,11])

-          editor.trigger keydownEvent('d')
-          editor.trigger keydownEvent('3')
-          editor.trigger keydownEvent('b')
+          keydown('d', element: editor[0])
+          keydown('3', element: editor[0])
+          keydown('b', element: editor[0])

           expect(editor.getText()).toBe "ee four"
           expect(editor.getCursorScreenPosition()).toEqual([0,0])
@@ -208,31 +208,31 @@

       describe "the h keybinding", ->
         it "moves the cursor left, but not to the previous line", ->
-          editor.trigger keydownEvent('h')
-          expect(editor.getCursorScreenPosition()).toEqual([1,0])
-          editor.trigger keydownEvent('h')
+          keydown('h', element: editor[0])
+          expect(editor.getCursorScreenPosition()).toEqual([1,0])
+          keydown('h', element: editor[0])
           expect(editor.getCursorScreenPosition()).toEqual([1,0])

       describe "the j keybinding", ->
         it "moves the cursor down, but not to the end of the last line", ->
-          editor.trigger keydownEvent 'j'
+          keydown('j', element: editor[0])
           expect(editor.getCursorScreenPosition()).toEqual([2,1])
-          editor.trigger keydownEvent 'j'
+          keydown('j', element: editor[0])
           expect(editor.getCursorScreenPosition()).toEqual([2,1])

       describe "the k keybinding", ->
         it "moves the cursor up, but not to the beginning of the first line", ->
-          editor.trigger keydownEvent('k')
+          keydown('k', element: editor[0])
           expect(editor.getCursorScreenPosition()).toEqual([0,1])
-          editor.trigger keydownEvent('k')
+          keydown('k', element: editor[0])
           expect(editor.getCursorScreenPosition()).toEqual([0,1])

       describe "the l keybinding", ->
         it "moves the cursor right, but not to the next line", ->
           editor.setCursorScreenPosition([1,3])
-          editor.trigger keydownEvent('l')
+          keydown('l', element: editor[0])
           expect(editor.getCursorScreenPosition()).toEqual([1,4])
-          editor.trigger keydownEvent('l')
+          keydown('l', element: editor[0])
           expect(editor.getCursorScreenPosition()).toEqual([1,4])

       describe "the w keybinding", ->


Added chat page to the navbar.

Added chat page to the navbar.

--- picoCTF-web/web/coffee/navbar.coffee
+++ picoCTF-web/web/coffee/navbar.coffee
@@ -1,74 +1,63 @@
-apiOffline =
-  FAQ: "/faq"
+apiOffline =
+  FAQ: "/faq"
   Teachers: "/teachers"
   Sponsors: "/sponsors"
   About: "/about"

 teacherLoggedIn =
   Game: "/game/"
-  Problems: "/problems"
   Shell: "/shell"
   Scoreboard: "/scoreboard"
   Classroom: "/classroom"
   About:
     FAQ: "/faq"
     Sponsors: "/sponsors"
-    #News: "/news"
     Teachers: "/teachers"
     About: "/about"
   Account:
     Manage: "/account"
     Logout: "#"

-teacherLoggedInNoCompetition =
-  Classroom: "/classroom"
-  About: "/about"
-  FAQ: "/faq"
-  #News: "/news"
+teacherLoggedInNoCompetition =
+  Classroom: "/classroom"
+  About: "/about"
+  FAQ: "/faq"
   Teachers: "/teachers"
-  Sponsors: "/sponsors"
+  Sponsors: "/sponsors"
   Account:
     Manage: "/account"
     Logout: "#"

 userLoggedIn =
   Game: "/game/"
-  Problems: "/problems"
   Shell: "/shell"
   Team: "/team"
+  Chat: "/chat"
   Scoreboard: "/scoreboard"
   About:
     FAQ: "/faq"
-    About: "/about"
+    About: "/about"
     Sponsors: "/sponsors"
-    #News: "/news"
   Account:
     Manage: "/account"
     Logout: "#"

-userLoggedInNoCompetition =
-  Team: "/team"
-  About: "/about"
-  FAQ: "/faq"
-  # News: "/news"
-  Sponsors: "/sponsors"
+userLoggedInNoCompetition =
+  Team: "/team"
+  Chat: "/chat"
+  About: "/about"
+  FAQ: "/faq"
+  Sponsors: "/sponsors"
   Account:
     Manage: "/account"
     Logout: "#"

 userNotLoggedIn =
-  #Registration: "/registration"
-  # Scoreboard: "/scoreboard"
-  # About:
-  #  FAQ: "/faq"
-  #  Sponsors: "/sponsors"
-  #  News: "/news"
   About: "/about"
   FAQ: "/faq"
-  # News: "/news"
   Teachers: "/teachers"
-  Sponsors: "/sponsors"
+  Sponsors: "/sponsors"
   Login: "/login"

 loadNavbar = (renderNavbarLinks, renderNestedNavbarLinks) ->
@@ -83,18 +72,18 @@
     navbarLayout.links = userNotLoggedIn
     navbarLayout.topLevel = true
     if data["status"] == 1
-      if not data.data["logged_in"]
+      if not data.data["logged_in"]
         $(".show-when-logged-out").css("display", "inline-block")
       if data.data["teacher"]
         if data.data["competition_active"]
            navbarLayout.links = teacherLoggedIn
         else
            navbarLayout.links = teacherLoggedInNoCompetition
-      else if data.data["logged_in"]
+      else if data.data["logged_in"]
          if data.data["competition_active"]
             navbarLayout.links = userLoggedIn
          else
-            navbarLayout.links = userLoggedInNoCompetition
+            navbarLayout.links = userLoggedInNoCompetition
     $("#navbar-links").html renderNavbarLinks(navbarLayout)
     $("#navbar-item-logout").on("click", logout)

@@ -107,4 +96,3 @@
   renderNestedNavbarLinks = _.template($("#navbar-links-dropdown-template").remove().text())

   loadNavbar(renderNavbarLinks, renderNestedNavbarLinks)
-


Added game to navbar

Added game to navbar

--- picoCTF-web/web/coffee/navbar.coffee
+++ picoCTF-web/web/coffee/navbar.coffee
@@ -3,6 +3,7 @@
   News: "/news"

 teacherLoggedIn =
+  Game: "/game"
   Problems: "/problems"
   Scoreboard: "/scoreboard"
   Classroom: "/classroom"
@@ -13,6 +14,7 @@
   Logout: "/logout"

 userLoggedIn =
+  Game: "/game"
   Problems: "/problems"
   Team: "/team"
   Scoreboard: "/scoreboard"


add zip perf test

add zip perf test

--- performance/PerformanceTest.coffee
+++ performance/PerformanceTest.coffee
@@ -31,10 +31,15 @@
       for i in [1..width]
         template[i] = f.combineTemplate gen, width, depth-1
       Bacon.combineTemplate(template)
+  zip: (gen) ->
+    gen.stream().zip(gen.stream())

 suite = new Benchmark.Suite

-suite.add 'Bacon.combineTemplate.sample', ->
+suite.add 'zip', ->
+  f.withGenerator (gen) ->
+    f.zip(gen)
+.add 'Bacon.combineTemplate.sample', ->
   f.withGenerator (gen) ->
     f.combineTemplate(gen, 5, 1)
       .sampledBy(f.everyNth(10, gen.stream()))


posted messages now get the current_user class

--- app/assets/javascripts/backbone/views/show_activity.js.coffee
+++ app/assets/javascripts/backbone/views/show_activity.js.coffee
@@ -16,8 +16,9 @@
         @compiledTemplate = Kandan.Helpers.Activities.buildFromMessageTemplate activity

     $(@el).data("activity-id", activity.id)
-    if activity.action == "message" && activity.user_id == Kandan.Helpers.Users.currentUser().id
+    if activity.action == "message" && activity.user.id == Kandan.Helpers.Users.currentUser().id
       $(@el).addClass("current_user")
+

     if activity.id == undefined
       $(@el).attr("id", "activity-c#{activity.cid}")


Disables sortable when in marking done mode

--- app/assets/javascripts/boards/components/board_list.js.coffee
+++ app/assets/javascripts/boards/components/board_list.js.coffee
@@ -9,6 +9,9 @@
   data: ->
     scrollOffset: 20
     loadMore: false
+  watch:
+    done: (done) ->
+      this.sortable.option('disabled', done.issue?)
   methods:
     listHeight: -> this.$els.list.getBoundingClientRect().height
     scrollHeight: -> this.$els.list.scrollHeight
@@ -39,7 +42,7 @@

       return returnIssue
   ready: ->
-    Sortable.create this.$els.list,
+    this.sortable = Sortable.create this.$els.list,
       group: 'issues'
       disabled: this.disabled
       scrollSensitivity: 150


The mocha option is irrelevant.

The mocha option is irrelevant.

--- test/conf/mocha-conf.coffee
+++ test/conf/mocha-conf.coffee
@@ -1,3 +0,0 @@
-mocha.setup
-  # Every test times out in 5 seconds.
-  timeout: 5000


fixed more namespace errors

fixed more namespace errors

--- src/LLRedBlackTree.coffee
+++ src/LLRedBlackTree.coffee
@@ -139,7 +139,7 @@
   values = (optionNode) ->
     arr = []
     inorderTraversal(optionNode, (node) -> arr.push(node.value) )
-    new List().buildFromArray(arr)
+    new mugs.List().buildFromArray(arr)

   ###
     Returns the keys in the tree in sorted order.
@@ -147,7 +147,7 @@
   keys = (optionNode) ->
     arr = []
     inorderTraversal(optionNode, (node) -> arr.push(node.key) )
-    new List().buildFromArray(arr)
+    new mugs.List().buildFromArray(arr)

   ###
     This will do an inorderTraversal of the tree applying the function 'f'


if Recurly error, parse body xml rather than using regex match

--- services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee
+++ services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee
@@ -418,10 +418,15 @@
 			url: "subscriptions/#{subscriptionId}/cancel",
 			method: "put"
 		}, (error, response, body) ->
-			if error? and body?.match(/.*A canceled subscription can't transition to canceled.*/)
-				logger.log {subscriptionId, error, body}, "subscription already cancelled, not really an error, proceeding"
-				error = null
-			callback(error)
+			if error?
+				RecurlyWrapper._parseXml body, (_err, parsed) ->
+					if parsed?.error?.description == "A canceled subscription can't transition to canceled"
+						logger.log {subscriptionId, error, body}, "subscription already cancelled, not really an error, proceeding"
+						callback(null)
+					else
+						callback(error)
+			else
+				callback(null)
 		)

 	reactivateSubscription: (subscriptionId, callback) ->


Convert end_to_end_test to use Emblem's compiler directly, just as bin/emblem2hbs does

--- test/end_to_end_test.coffee
+++ test/end_to_end_test.coffee
@@ -2,12 +2,13 @@
 assert = chai.assert

 fs = require("fs")
-Processor = require("../lib/processor")
 Indentation = require("../lib/indentation")
+
+emblem = require('emblem')

 describe 'emblem2hbs', ->
   it 'should process a real-world complex emblem template correctly', ->
     input = fs.readFileSync('./test/fixtures/test_template.emblem', 'utf8')
     expected = fs.readFileSync('./test/fixtures/test_template.js.hbs', 'utf8')

-    assert.equal(Indentation.indent(Processor.process(input)), expected)+    assert.equal(Indentation.indent(emblem.default.compile(input)), expected)


Fix link href replacements

Fix link href replacements

--- src/components/Specimen/Project/Project.coffee
+++ src/components/Specimen/Project/Project.coffee
@@ -109,7 +109,7 @@
     continue if src is null
     for file in files
       if isSamePath(file.source, src)
-        script.setAttribute('src', file.target)
+        script.setAttribute('href', file.target)

   doc.documentElement.innerHTML


change job scroll size

change job scroll size

--- server/job.coffee
+++ server/job.coffee
@@ -37,7 +37,8 @@
         if not API.job.allowed job, this.user
           return 401
         else
-          job.progress = API.job.progress job
+          job.progress = API.job.progress(job) if this.queryParams.progress
+          delete job.processes if this.queryParams.processes
           return job
       else
         return 404
@@ -98,7 +99,7 @@
       else
         results = []
         done = 0
-        job_job.each {email:this.urlParams.email}, (if this.queryParams.processes then {size:this.queryParams.size ? 50} else {_source: {exclude:['processes']}}), ((job) -> done += if job.done then 1 else 0; results.push job)
+        job_job.each {email:this.urlParams.email}, (if this.queryParams.processes then {size:this.queryParams.size ? 20} else {_source: {exclude:['processes']}}), ((job) -> done += if job.done then 1 else 0; results.push job)
         return total: results.length, jobs: results, done: done

 API.add 'job/results',
@@ -514,7 +515,7 @@
         p.reloaded.push p.createdAt
         reloads.push p
   if q is true
-    job_job.each 'NOT done:true', {size:50}, ((job) -> _reload_job_processes(job))
+    job_job.each 'NOT done:true', {size:20}, ((job) -> _reload_job_processes(job))
   else if q isnt '*' and job = job_job.get q
     _reload_job_processes job
   else


Positive offsets. Add all timezones.

Positive offsets. Add all timezones.

Importing all of the timezones so that the program can test timezones outside of
North America. At this point, Detroit, Chicago and Menominee are working
correctly, so it's time to explore some of the old cities of Europe, looking for
those edge cases.

Already, a quick test against Amsterdam shows that I've got the formatting of
timezone offsets for positive offsets incorrect. The plus symbol must be there.

--- adjustments.coffee
+++ adjustments.coffee
@@ -13,7 +13,13 @@
 HOUR    = MINUTE * 60
 DAY     = HOUR   * 24

-data = require("./timezones/northamerica")
+data = { zones: {}, rules: {} }
+fs = require "fs"
+for file in fs.readdirSync "./timezones"
+  continue if file is "index.js"
+  continent = require "./timezones/#{file}", "utf8"
+  data.zones[k] = v for k, v of continent.zones
+  data.rules[k] = v for k, v of continent.rules

 en_US =
   day:
@@ -174,7 +180,7 @@

 formatOffset = (offset) ->
   increment = offset / Math.abs(offset)
-  sign = if offset < 0 then "-" else ""
+  sign = if offset < 0 then "-" else "+"
   offset = Math.abs(offset)
   offset -= (millis = offset % 1000)
   offset /= 1000


users cant add options for proposal

users cant add options for proposal

--- vue/src/shared/helpers/poll.coffee
+++ vue/src/shared/helpers/poll.coffee
@@ -36,7 +36,7 @@
     ('canRespondMaybe'       if poll.pollType == 'meeting'),
     ('anonymous'             if !fieldFromTemplate(poll.pollType, 'prevent_anonymous')),
     ('deanonymizeAfterClose' if poll.anonymous),
-    ('voterCanAddOptions'    if fieldFromTemplate(poll.pollType, 'can_add_options'))
+    ('voterCanAddOptions'    if fieldFromTemplate(poll.pollType, 'can_add_options') && poll.pollType != 'proposal')
   ]

 export myLastStanceFor = (poll) ->


EnvironmentMachineStateModal: disable stack building for disabled users' stacks

--- client/app/lib/providers/environmentsmachinestatemodal.coffee
+++ client/app/lib/providers/environmentsmachinestatemodal.coffee
@@ -730,6 +730,10 @@
         action = 'buildStack'
         target = @stack

+        if @stack.config?.oldOwner?
+          return showError "Stack building is not allowed
+                            for disabled users' stacks."
+
       if @machine.jMachine.generatedFrom?.templateId?
         unless computeController.verifyStackRequirements @stack
           computeController.off  'StackRequirementsProvided'


reorganized Class.compileNode to be more robust so it can handle coffeescript 'privates'

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -1190,60 +1190,63 @@
     name = "_#{name}" if name.reserved
     indent = "#{o.indent}"

-    answer = flatten [
+    prelude = []
+    declaration = [
       @makeCode indent
       @makeCode "class #{name}"
-      if @parent then [@makeCode( " extends "), @parent.compileToFragments o] else []
+      if @parent then [
+        @makeCode( " extends "),
+        @parent.compileToFragments o
+      ] else []
       @makeCode " {\n"
     ]

     @body.spaced = yes
     o.indent += TAB

-    for assign, assign_index in @getMembers(o)
-      answer.push @makeCode "\n\n" if assign_index isnt 0
-
+    members = for assign in @getMembers(o)
       if assign not instanceof Assign
-        answer.push (assign.compileToFragments o)...
-        continue
-
-      visibility_flag = "public "
-      visibility_flag = "" if assign.value.isConstructor
-
-      if vname = atProperty assign.variable
-        isStatic = yes
-      else if (vname = assign.variable.vanillaName()) and assign.context != 'object'
-        isStatic = yes
-        visibility_flag = "private "
-      else if vname = assign.variable.vanillaName()
-        isStatic = no
+        assign.compileToFragments o
       else
-        assign.nogen "can't handle complex assignment in class body"
-        continue
-
-      static_flag = if isStatic                 then "static "    else ""
-      answer.push @makeCode "#{o.indent}#{visibility_flag}#{static_flag}"
-
-      if (fn = assign.value) instanceof Code
-        fn.isMethod = yes
-        fn.static = isStatic
-        fn.name = vname
-        fn.klass = this
-
-        answer = answer.concat flatten [
-          fn.compileToFragments(o)
-        ]
-
-      else
-        answer = answer.concat flatten [
-          vname.compileToFragments o
-          @makeCode " = "
-          assign.value.compileToFragments o
-          @makeCode ";"
-        ]
-
-    answer.push @makeCode("\n#{indent}}")
-    answer
+        memberCode = []
+
+        visibility_flag = "public "
+        visibility_flag = "" if assign.value.isConstructor
+
+        if vname = atProperty assign.variable
+          isStatic = yes
+        else if assign.context is 'object' and vname = assign.variable.vanillaName()
+          isStatic = no
+        else if assign.context isnt 'object'
+          continue  #FIXME FIXME
+        else
+          assign.nogen "can't handle complex assignment in class body"
+          continue
+
+        static_flag = if isStatic                 then "static "    else ""
+        memberCode.push @makeCode "#{o.indent}#{visibility_flag}#{static_flag}"
+
+        if (fn = assign.value) instanceof Code
+          fn.isMethod = yes
+          fn.static = isStatic
+          fn.name = vname
+          fn.klass = this
+          memberCode.push fn.compileToFragments(o)
+        else
+          memberCode.push [
+            vname.compileToFragments o
+            @makeCode " = "
+            assign.value.compileToFragments o
+            @makeCode ";"
+          ]
+        flatten memberCode
+
+    flatten [
+      prelude
+      declaration
+      @joinFragmentArrays(members, '\n\n')
+      @makeCode("\n#{indent}}")
+    ]

 #### Assign


Activated some translations

Activated some translations

--- app/locale/nl-NL.coffee
+++ app/locale/nl-NL.coffee
@@ -198,9 +198,9 @@
     hard: "Moeilijk"
     player: "Speler"
     player_level: "Niveau" # Like player level 5, not like level: Dungeons of Kithgard
-#    warrior: "Krijger"
+    warrior: "Krijger"
 #    ranger: "Ranger"
-#    wizard: "Tovenaar"
+    wizard: "Tovenaar"

   units:
     second: "seconde"
@@ -269,16 +269,16 @@
 #    tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos).
     tome_select_a_thang: "Selecteer Iemand voor "
     tome_available_spells: "Beschikbare spreuken"
-#    tome_your_skills: "Jouw Vaardigheden"
+    tome_your_skills: "Jouw Vaardigheden"
 #    tome_help: "Help"
 #    tome_current_method: "Current Method"
-#    hud_continue_short: "Doorgaan"
-#    code_saved: "Code Opgeslagen"
+    hud_continue_short: "Doorgaan"
+    code_saved: "Code Opgeslagen"
     skip_tutorial: "Overslaan (esc)"
 #    keyboard_shortcuts: "Key Shortcuts"
     loading_ready: "Klaar!"
 #    loading_start: "Start Level"
-#    problem_alert_title: "Verbeter je Code"
+    problem_alert_title: "Verbeter je Code"
 #    problem_alert_help: "Help"
     time_current: "Nu:"
     time_total: "Maximum:"
@@ -312,34 +312,34 @@
     tip_talk_is_cheap: "Je kunt het goed uitleggen, maar toon me de code. - Linus Torvalds"
     tip_first_language: "Het ergste dat je kan leren is je eerste programmeertaal. - Alan Kay"
 #    tip_hardware_problem: "Q: How many programmers does it take to change a light bulb?  A: None, it's a hardware problem."
-#    tip_hofstadters_law: "De Wet van Hofstadter: Het duurt altijd langer dan je verwacht, zelfs wanneer je rekening houdt met de Wet van Hofstadter."
+    tip_hofstadters_law: "De Wet van Hofstadter: Het duurt altijd langer dan je verwacht, zelfs wanneer je rekening houdt met de Wet van Hofstadter."
 #    tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
 #    tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
 #    tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
 #    tip_superpower: "Coding is the closest thing we have to a superpower."

   game_menu:
-#    inventory_tab: "Inventaris"
-#    save_load_tab: "Opslaan/Laden"
-#    options_tab: "Opties"
-#    guide_tab: "Handleiding"
+    inventory_tab: "Inventaris"
+    save_load_tab: "Opslaan/Laden"
+    options_tab: "Opties"
+    guide_tab: "Handleiding"
 #    guide_video_tutorial: "Video Tutorial"
 #    guide_tips: "Tips"
     multiplayer_tab: "Multiplayer"
-#    auth_tab: "Inschrijven"
+    auth_tab: "Inschrijven"
 #    inventory_caption: "Equip your hero"
-#    choose_hero_caption: "Kies held, taal"
+    choose_hero_caption: "Kies held, taal"
 #    save_load_caption: "... and view history"
-#    options_caption: "Instellingen"
+    options_caption: "Instellingen"
 #    guide_caption: "Docs and tips"
-#    multiplayer_caption: "Speel met vrienden!"
-#    auth_caption: "Bewaar je voortgang."
-
-#  inventory:
+    multiplayer_caption: "Speel met vrienden!"
+    auth_caption: "Bewaar je voortgang."
+
+  inventory:
 #    choose_inventory: "Equip Items"
 #    equipped_item: "Equipped"
-#    required_purchase_title: "Verplicht"
-#    available_item: "Beschikbaar"
+    required_purchase_title: "Verplicht"
+    available_item: "Beschikbaar"
 #    restricted_title: "Restricted"
 #    should_equip: "(double-click to equip)"
 #    equipped: "(equipped)"
@@ -348,26 +348,26 @@
 #    equip: "Equip"
 #    unequip: "Unequip"

-#  buy_gems:
-#    few_gems: "Een paar edelstenen"
-#    pile_gems: "Berg edelstenen"
-#    chest_gems: "Schatkist met edelstenen"
-#    purchasing: "Aan het kopen..."
-#    declined: "Je kaart is geweigerd"
+  buy_gems:
+    few_gems: "Een paar edelstenen"
+    pile_gems: "Berg edelstenen"
+    chest_gems: "Schatkist met edelstenen"
+    purchasing: "Aan het kopen..."
+    declined: "Je kaart is geweigerd"
 #    retrying: "Server error, retrying."
-#    prompt_title: "Niet genoeg edelstenen"
+    prompt_title: "Niet genoeg edelstenen"
 #    prompt_body: "Do you want to get more?"
-#    prompt_button: "Naar de winkel"
+    prompt_button: "Naar de winkel"
 #    recovered: "Previous gems purchase recovered. Please refresh the page."

-#  subscribe:
+  subscribe:
 #    subscribe_title: "Subscribe"
 #    unsubscribe: "Unsubscribe"
 #    levels: "Get more practice with bonus levels!"
 #    heroes: "More powerful heroes!"
-#    gems: "3500 extra edelstenen elke maand!"
-#    items: "Meer dan 250 bonus items!"
-#    parents: "Voor ouders"
+    gems: "3500 extra edelstenen elke maand!"
+    items: "Meer dan 250 bonus items!"
+    parents: "Voor ouders"
 #    parents_title: "Your child will learn to code."
 #    parents_blurb1: "With CodeCombat, your child learns by writing real code. They start by learning simple commands, and progress to more advanced topics."
 #    parents_blurb2: "For $9.99 USD/mo, they get new challenges every week and personal email support from professional programmers."
@@ -376,12 +376,12 @@
 #    stripe_description: "Monthly Subscription"
 #    subscription_required_to_play: "You'll need a subscription to play this level."

-#  choose_hero:
-#    choose_hero: "Kies je held"
-#    programming_language: "Programmeertaal"
-#    programming_language_description: "Welke programmeertaal wil je gebruiken?"
-#    default: "standaard"
-#    experimental: "Experimenteel"
+  choose_hero:
+    choose_hero: "Kies je held"
+    programming_language: "Programmeertaal"
+    programming_language_description: "Welke programmeertaal wil je gebruiken?"
+    default: "standaard"
+    experimental: "Experimenteel"
 #    python_blurb: "Simple yet powerful, great for beginners and experts."
 #    javascript_blurb: "The language of the web. (Not the same as Java.)"
 #    coffeescript_blurb: "Nicer JavaScript syntax."
@@ -389,18 +389,18 @@
 #    lua_blurb: "Game scripting language."
 #    io_blurb: "Simple but obscure."
 #    status: "Status"
-#    weapons: "Wapens"
+    weapons: "Wapens"
 #    weapons_warrior: "Swords - Short Range, No Magic"
 #    weapons_ranger: "Crossbows, Guns - Long Range, No Magic"
 #    weapons_wizard: "Wands, Staffs - Long Range, Magic"
 #    attack: "Damage" # Can also translate as "Attack"
-#    health: "Gezondheid"
-#    speed: "Snelheid"
+    health: "Gezondheid"
+    speed: "Snelheid"
 #    regeneration: "Regeneration"
 #    range: "Range" # As in "attack or visual range"
 #    blocks: "Blocks" # As in "this shield blocks this much damage"
 #    backstab: "Backstab" # As in "this dagger does this much backstab damage"
-#    skills: "Vaardigheden"
+    skills: "Vaardigheden"
 #    available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store
 #    level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see)
 #    restricted_to_certain_heroes: "Only certain heroes can play this level."


Almost done!

Almost done!

--- app/locale/pt-BR.coffee
+++ app/locale/pt-BR.coffee
@@ -901,79 +901,79 @@
     next_city: "Cidade?"
     next_country: "escolha seu país."
     next_name: "Nome?"
-#    next_short_description: "write a short description."
-#    next_long_description: "describe your desired position."
-#    next_skills: "list at least five skills."
-#    next_work: "chronicle your work history."
-#    next_education: "recount your educational ordeals."
-#    next_projects: "show off up to three projects you've worked on."
-#    next_links: "add any personal or social links."
-#    next_photo: "add an optional professional photo."
-#    next_active: "mark yourself open to offers to show up in searches."
+    next_short_description: "escreva uma breve descrição."
+    next_long_description: "descreva o tipo de vaga ou cargo desejados."
+    next_skills: "relacione pelo menos cinco habilidades."
+    next_work: "descreva seu histórico de trabalho."
+    next_education: "descreva seu percurso acadêmico."
+    next_projects: "mostre até três projetos nos quais você trabalhou."
+    next_links: "adicione links pessoais ou sociais."
+    next_photo: "adicione uma foto profissional (opcional)."
+    next_active: "marque-se como \"aberto a oportunidades\"."
     example_blog: "Blog"
     example_personal_site: "Site Pessoal"
-#    links_header: "Personal Links"
-#    links_blurb: "Link any other sites or profiles you want to highlight, like your GitHub, your LinkedIn, or your blog."
-#    links_name: "Link Name"
-#    links_name_help: "What are you linking to?"
-#    links_link_blurb: "Link URL"
-#    basics_header: "Update basic info"
-    basics_active: "Aberto para Ofertas"
-#    basics_active_help: "Want interview offers right now?"
-#    basics_job_title: "Desired Job Title"
-#    basics_job_title_help: "What role are you looking for?"
+    links_header: "Links Pessoais"
+    links_blurb: "Adicione links para sites ou perfis que você gostaria de destacar, como seu GitHub, LinkedIn ou seu blog."
+    links_name: "Nome do Link"
+    links_name_help: "Para onde você está linkando?"
+    links_link_blurb: "URL do Link"
+    basics_header: "Atualizar informações básicas"
+    basics_active: "Aberto para Oportunidades"
+    basics_active_help: "Está procurando oportunidades de entrevista agora?"
+    basics_job_title: "Título do Trabalho Desejado"
+    basics_job_title_help: "Qual cargo você está procurando?"
     basics_city: "Cidade"
-#    basics_city_help: "City you want to work in (or live in now)."
+    basics_city_help: "Cidade que você quer trabalhar (ou morar)."
     basics_country: "País"
-#    basics_country_help: "Country you want to work in (or live in now)."
-#    basics_visa: "US Work Status"
-#    basics_visa_help: "Are you authorized to work in the US, or do you need visa sponsorship? (If you live in Canada or Australia, mark authorized.)"
-#    basics_looking_for: "Looking For"
-#    basics_looking_for_full_time: "Full-time"
-#    basics_looking_for_part_time: "Part-time"
-#    basics_looking_for_remote: "Remote"
-#    basics_looking_for_contracting: "Contracting"
-#    basics_looking_for_internship: "Internship"
-#    basics_looking_for_help: "What kind of developer position do you want?"
-#    name_header: "Fill in your name"
-#    name_anonymous: "Anonymous Developer"
-#    name_help: "Name you want employers to see, like 'Nick Winter'."
-#    short_description_header: "Write a short description of yourself"
-#    short_description_blurb: "Add a tagline to help an employer quickly learn more about you."
-#    short_description: "Tagline"
-#    short_description_help: "Who are you, and what are you looking for? 140 characters max."
-#    skills_header: "Skills"
-#    skills_help: "Tag relevant developer skills in order of proficiency."
-#    long_description_header: "Describe your desired position"
-#    long_description_blurb: "Tell employers how awesome you are and what role you want."
-#    long_description: "Self Description"
-#    long_description_help: "Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max."
-#    work_experience: "Work Experience"
-#    work_header: "Chronicle your work history"
-#    work_years: "Years of Experience"
-#    work_years_help: "How many years of professional experience (getting paid) developing software do you have?"
-#    work_blurb: "List your relevant work experience, most recent first."
-#    work_employer: "Employer"
-#    work_employer_help: "Name of your employer."
-#    work_role: "Job Title"
-#    work_role_help: "What was your job title or role?"
+    basics_country_help: "País que você quer trabalhar (ou morar)."
+    basics_visa: "Status de Trabalho nos EUA"
+    basics_visa_help: "Você está autorizado a trabalhar nos EUA ou você precisa de patrocínio para o VISA? (Se você mora no Canadá ou na Austrália, marque como autorizado.)"
+    basics_looking_for: "Procurando por"
+    basics_looking_for_full_time: "Tempo integral"
+    basics_looking_for_part_time: "Meio-turno"
+    basics_looking_for_remote: "Remoto"
+    basics_looking_for_contracting: "Contratando"
+    basics_looking_for_internship: "Estágio"
+    basics_looking_for_help: "Que tipo de cargo de desenvolvendor você procura?"
+    name_header: "Preencha seu nome"
+    name_anonymous: "Desenvolvedor Anônimo"
+    name_help: "Nome que você quer que os empregadores vejam, por exemplo 'Nick Winter'."
+    short_description_header: "Escreva uma breve descrição de si mesmo"
+    short_description_blurb: "Adicione um slogan (frase) que ajudará o empregador a lhe conhecer rapidamente."
+    short_description: "Slogan"
+    short_description_help: "Quem é você e o que você está procurando? Máximo de 140 caracteres."
+    skills_header: "Habilidades"
+    skills_help: "Marque habilidades de desenvolvimento relevantes em ordem de proficiência."
+    long_description_header: "Descreva o cargo desejado"
+    long_description_blurb: "Diga aos empregadores o quão incrível você é e qual cargo você deseja."
+    long_description: "Autodescrição"
+    long_description_help: "Descreva a si mesmo para seu potencial empregador. Mantenha curto e direto. Recomendamos esboçar o cargo que mais lhe interessa. Máximo de 600 caracteres."
+    work_experience: "Experiência de Trabalho"
+    work_header: "Descreva seu histórico de trabalho"
+    work_years: "Anos de Experiência"
+    work_years_help: "Quantos anos de experiência profissional (sendo pago) de desenvolvimento de software você tem?"
+    work_blurb: "Liste experiências relevantes de trabalho. As mais recentes primeiro."
+    work_employer: "Empregador"
+    work_employer_help: "Nome do empregador."
+    work_role: "Título do Emprego"
+    work_role_help: "Qual era a sua função ou cargo no emprego?"
     work_duration: "Duração"
-#    work_duration_help: "When did you hold this gig?"
+    work_duration_help: "Por quanto tempo?"
     work_description: "Descrição"
-#    work_description_help: "What did you do there? (140 chars; optional)"
+    work_description_help: "O que você fez lá? (140 carac.; opcional)"
     education: "Educação"
-#    education_header: "Recount your academic ordeals"
-#    education_blurb: "List your academic ordeals."
+    education_header: "Descreva seu percurso acadêmico"
+    education_blurb: "Liste seu percurso acadêmico."
     education_school: "Escola"
     education_school_help: "Nome da sua escola."
     education_degree: "Grau"
-#    education_degree_help: "What was your degree and field of study?"
+    education_degree_help: "Qual é o seu grau e área de estudo?"
     education_duration: "Datas"
     education_duration_help: "Quando?"
     education_description: "Descrição"
-#    education_description_help: "Highlight anything about this educational experience. (140 chars; optional)"
+    education_description_help: "Destaque qualquer coisa sobre essa experiência acadêmica. (140 carac.; opcional)"
     our_notes: "Nossas notas"
-#    remarks: "Remarks"
+    remarks: "Observações"
     projects: "Projetos"
     projects_header: "Adicione 3 projetos"
     projects_header_2: "Projetos (Top 3)"
@@ -988,11 +988,11 @@
     project_link_help: "Link para o projeto."
     player_code: "Código do Jogador"

-#  employers:
-#    deprecation_warning_title: "Sorry, CodeCombat is not recruiting right now."
-#    deprecation_warning: "We are focusing on beginner levels instead of finding expert developers for the time being."
-#    hire_developers_not_credentials: "Hire developers, not credentials." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section.
-#    get_started: "Get Started"
+  employers:
+    deprecation_warning_title: "Desculpe, o CodeCombat não está recrutando no momento."
+    deprecation_warning: "Estamos nos concentrando em níveis iniciante, em vez de encontrar desenvolvedores especializados no momento."
+    hire_developers_not_credentials: "Contrate desenvolvedores, não referências." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section.
+    get_started: "Comece"
 #    already_screened: "We've already technically screened all our candidates"
 #    filter_further: ", but you can also filter further:"
 #    filter_visa: "Visa"


initial tabbing for create view

initial tabbing for create view

--- client/app/Applications/Activity.kdapplication/widgets/codesharewidget.coffee
+++ client/app/Applications/Activity.kdapplication/widgets/codesharewidget.coffee
@@ -179,6 +179,7 @@
               syntax:"javascript"
             }
             ]
+
           classesHTML:@libHTMLClasses.getValue()
           extrasHTML:@libHTMLHeadExtras.getValue()
           externalCSS:@libCSSExternal.getValue()
@@ -234,8 +235,6 @@
       title : "External Scripts:"
       cssClass : "legend-for-text"

-
-
     @librariesHTMLContainer = new KDView
       cssClass : "libs-container libs-html"

@@ -390,13 +389,13 @@
       title : "Modernizr"
       partial : "Modernizr"

+
     @librariesHTMLContent.addSubView @labelHTMLSelect
     @librariesHTMLContent.addSubView @libHTMLSelect
     @librariesHTMLContent.addSubView @labelHTMLClasses
     @librariesHTMLContent.addSubView @libHTMLClasses
     @librariesHTMLContent.addSubView @labelHTMLExtras
     @librariesHTMLContent.addSubView @libHTMLHeadExtras
-

     @librariesCSSContent.addSubView @labelCSSSelect
     @librariesCSSContent.addSubView @libCSSSelect
@@ -450,6 +449,93 @@
       , =>
         @$("div.libs-js").css "opacity":0
         @$("div.libs-js").css "z-index":-1
+
+    @codeShareContainer = new KDTabView
+      cssClass: "code-share-container"
+
+    @codeShareResultPane = new KDTabPaneView
+      name:"Code Share"
+
+    # HTML Tab
+
+    @codeShareHTMLPane = new KDTabPaneView
+      name:"HTML"
+
+    @codeShareHTMLCodeSnipContainer = new KDCustomHTMLView
+      tagName : "div"
+      cssClass :"code-snip-container"
+
+    @codeShareHTMLCodeSnipContainer.addSubView @labelHTMLContent
+    @codeShareHTMLCodeSnipContainer.addSubView @librariesHTMLContainer
+
+    @codeShareHTMLPane.addSubView @codeShareHTMLCodeSnipContainer
+
+    @codeShareHTMLCodeSnipHolder = new KDCustomHTMLView
+      tagName:"div"
+      cssClass : "code-snip-holder share"
+
+    @codeShareHTMLCodeSnipHolder.addSubView @HTMLloader
+    @codeShareHTMLCodeSnipHolder.addSubView @aceHTMLWrapper
+
+    @codeShareHTMLCodeSnipContainer.addSubView @codeShareHTMLCodeSnipHolder
+
+    # CSS Tab
+
+    @codeShareCSSPane = new KDTabPaneView
+      name:"CSS"
+
+    @codeShareCSSCodeSnipContainer = new KDCustomHTMLView
+      tagName : "div"
+      cssClass :"code-snip-container"
+
+    @codeShareCSSCodeSnipContainer.addSubView @labelCSSContent
+    @codeShareCSSCodeSnipContainer.addSubView @librariesCSSContainer
+
+    @codeShareCSSPane.addSubView @codeShareCSSCodeSnipContainer
+
+    @codeShareCSSCodeSnipHolder = new KDCustomHTMLView
+      tagName:"div"
+      cssClass : "code-snip-holder share"
+
+    @codeShareCSSCodeSnipHolder.addSubView @CSSloader
+    @codeShareCSSCodeSnipHolder.addSubView @aceCSSWrapper
+
+    @codeShareCSSCodeSnipContainer.addSubView @codeShareCSSCodeSnipHolder
+
+    # JS Tab
+
+    @codeShareJSPane = new KDTabPaneView
+      name:"JavaScript"
+
+    @codeShareJSCodeSnipContainer = new KDCustomHTMLView
+      tagName : "div"
+      cssClass :"code-snip-container"
+
+    @codeShareJSCodeSnipContainer.addSubView @labelJSContent
+    @codeShareJSCodeSnipContainer.addSubView @librariesJSContainer
+
+    @codeShareJSPane.addSubView @codeShareJSCodeSnipContainer
+
+    @codeShareJSCodeSnipHolder = new KDCustomHTMLView
+      tagName:"div"
+      cssClass : "code-snip-holder share"
+
+    @codeShareJSCodeSnipHolder.addSubView @JSloader
+    @codeShareJSCodeSnipHolder.addSubView @aceJSWrapper
+
+    @codeShareJSCodeSnipContainer.addSubView @codeShareJSCodeSnipHolder
+
+
+    @codeShareContainer.addPane @codeShareResultPane
+    @codeShareContainer.addPane @codeShareHTMLPane
+    @codeShareContainer.addPane @codeShareCSSPane
+    @codeShareContainer.addPane @codeShareJSPane
+
+    @codeShareResultPane.hideTabCloseIcon()
+    @codeShareHTMLPane.hideTabCloseIcon()
+    @codeShareCSSPane.hideTabCloseIcon()
+    @codeShareJSPane.hideTabCloseIcon()
+

   submit:=>
@@ -691,31 +777,8 @@

-

-          {{> @labelHTMLContent}}
-          {{> @librariesHTMLContainer}}
-

-            {{> @HTMLloader}}
-            {{> @aceHTMLWrapper}}
-

-

-

-          {{> @labelCSSContent}}
-          {{> @librariesCSSContainer}}
-

-            {{> @CSSloader}}
-            {{> @aceCSSWrapper}}
-

-

-

-          {{> @labelJSContent}}
-          {{> @librariesJSContainer}}
-

-            {{> @JSloader}}
-            {{> @aceJSWrapper}}
-

-

-

+        {{> @codeShareContainer}}
+

           {{> @codeShareResultView}}
           {{> @codeShareResultButton}}


Added parsing of JSON

Added parsing of JSON

--- rest_controller.coffee
+++ rest_controller.coffee
@@ -74,7 +74,7 @@

   create: (req, res) =>
     try
-      json = if @white_lists.create then _.pick(req.body, @white_lists.create) else req.body
+      json = ORMUtils.parseValues(if @white_lists.create then _.pick(req.body, @white_lists.create) else req.body)
       model = new @model_type(@model_type::parse(json))
       model.save {}, {
         success: =>
@@ -91,7 +91,7 @@

   update: (req, res) =>
     try
-      json = if @white_lists.update then _.pick(req.body, @white_lists.update) else req.body
+      json = ORMUtils.parseValues(if @white_lists.update then _.pick(req.body, @white_lists.update) else req.body)
       @model_type.find req.params.id, (err, model) =>
         return res.status(404).send(error: err.toString()) if err
         return res.status(404).send(error: "Model not found with id: #{req.params.id}") unless model


Despawning enemies as rooms vanish

Despawning enemies as rooms vanish

--- src/javascript/game/systems/room_system.coffee
+++ src/javascript/game/systems/room_system.coffee
@@ -1,6 +1,9 @@
 StateMachineSystem = require '../../ecs/state_machine_system'
 Common = require '../entity/components'
 Enemies = require '../entity/enemies'
+
+FilterExpander = require '../../ecs/filter_expander'
+enemyFilter = FilterExpander.expandFilterGroups(['enemy','position'])

 class RoomSystem extends StateMachineSystem
   @Subscribe: [ 'room', 'position' ]
@@ -24,22 +27,36 @@

   setupRoomAction: ->
     roomId = @getProp 'room', 'roomId'
-    pos = @getComp 'position'
+    roomPos = @getComp 'position'
     worldMap = @input.getIn(['static','worldMap'])
     roomDef = worldMap.getRoomById(roomId)
+
+    # Spawn enemies:
     for [col,row,id] in (roomDef.enemies || [])
-      x = pos.get('x') + (col * worldMap.tileWidth)
-      y = pos.get('y') + (row * worldMap.tileHeight)
+      x = roomPos.get('x') + (col * worldMap.tileWidth)
+      y = roomPos.get('y') + (row * worldMap.tileHeight)
       ecomps = Enemies.factory.createComponents(id, x:x, y:y)
       @newEntity ecomps

+  teardownRoomAction: ->
+    roomId = @getProp 'room', 'roomId'
+    roomPos = @getComp 'position'
+    worldMap = @input.getIn(['static','worldMap'])

-
-
+    roomLeft = roomPos.get('x')
+    roomTop = roomPos.get('y')
+    roomRight = roomLeft + worldMap.roomWidthInPx
+    roomBottom = roomTop + worldMap.roomHeightInPx

-  teardownRoomAction: ->
-    # TODO: de-spawn creatures
+    # Despawn enemies
+    @estore.search(enemyFilter).forEach (comps) =>
+      epos = comps.get('position')
+      if epos.get('x') >= roomLeft and epos.get('x') < roomRight and epos.get('y') >= roomTop and epos.get('y') < roomBottom
+        @destroyEntity epos.get('eid')
+
+    # Remove room
     @destroyEntity()
+

 module.exports = RoomSystem


Added require keyword

Added require keyword

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -2,7 +2,8 @@
   "#{string}"

 keywords = 'def defn data class fn proc match if type :: \\
-            access call new'.split ' '
+            access call new
+            require'.split ' '

 controls = '\\(\\)\\[\\]'

@@ -538,6 +539,7 @@
   ':': (elems...) ->
     "[#{elems.join ', '}]"

+
 trueMacros =
   'match': (onwhat, cases...) ->
     varNames = []
@@ -565,6 +567,12 @@
     content = mainCache.concat(varDecls, compiledCases.join '').join '\n'
     """(function(){
       #{content}}())"""
+  'require': (from, list) ->
+    args = inside(list).map(compileImpl).map(toJsString).join ', '
+    "$listize(window.requireModule(#{toJsString from.token}, [#{args}]))"
+
+toJsString = (token) ->
+  "'#{token}'"

 compileAssign = ([to, from]) ->
   "var #{to} = #{from};"


Fixed where clause typing

Fixed where clause typing

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -444,12 +444,7 @@
     if not kind
       throw new Error "Provide kind in freshTypeVariable"
     name = (freshName @typeVariabeIndex++)
-    # Massive hack to avoid clashes in type classes, need to fix this properly
-    # by renaming
-    if inSet @allBoundTypeVariables(), name
-      @freshTypeVariable kind
-    else
-      new TypeVariable name, kind
+    new TypeVariable name, kind

   extendSubstitution: (substitution) ->
     @substitution = joinSubs substitution, @substitution
@@ -936,11 +931,14 @@
 #   with the right subs for type vars on the left
 # For each defined name in the LHS, we declare it
 assignCompile = (ctx, expression, translatedExpression) ->
+  assignCompileAs ctx, expression, translatedExpression, no
+
+assignCompileAs = (ctx, expression, translatedExpression, polymorphic) ->
   # if not translatedExpression # TODO: throw here?
   if ctx.isAtDefinition()
     to = ctx.definitionPattern()
     ctx.setAssignTo (irDefinition expression.tea, translatedExpression)
-    {precs, assigns} = patternCompile ctx, to, expression
+    {precs, assigns} = patternCompile ctx, to, expression, polymorphic
     translationCache = ctx.resetAssignTo()

     #log "ASSIGN #{ctx.definitionName()}", ctx.shouldDefer()
@@ -954,7 +952,10 @@
   else
     translatedExpression

-patternCompile = (ctx, pattern, matched) ->
+polymorphicAssignCompile = (ctx, expression, translatedExpression) ->
+  assignCompileAs ctx, expression, translatedExpression, yes
+
+patternCompile = (ctx, pattern, matched, polymorphic) ->

   # caching can occur while compiling the pattern
   # precs are {cond}s and {cache}s, sorted in order they need to be executed
@@ -1003,7 +1004,7 @@
           (substituteList ctx.substitution, matched.tea.constraints)
         # log "assign type", name, (printType currentType), retainedConstraints
         ctx.assignType name,
-          if ctx.isAtDeferrableDefinition()
+          if polymorphic
             quantifyUnbound ctx, (addConstraints currentType, retainedConstraints)
           else
             toForAll currentType
@@ -1159,7 +1160,7 @@
       if body and not isWellformed body
         return 'malformed'

-      assignCompile ctx, call,
+      polymorphicAssignCompile ctx, call,
         if ctx.shouldDefer()
           deferredExpression()
         else
@@ -1540,7 +1541,7 @@

       ctx.newScope() # for variables defined inside pattern
       ctx.defineNonDeferrablePattern pattern
-      {precs, assigns} = patternCompile ctx, pattern, subject
+      {precs, assigns} = patternCompile ctx, pattern, subject, no

       # Compile the result, given current scope
       ctx.setAssignTo undefined
@@ -1948,6 +1949,7 @@
           [[(validIdentifier symbol), exp]]
   else
     # Name typed, use a fresh instance
+    # log symbol, contextType
     if contextType and contextType not instanceof TempType
       type = freshInstance ctx, contextType
       {
@@ -4025,6 +4027,9 @@
 typeEnumaration = (ctx) ->
   values mapMap _type, ctx._scope()

+toJs = (compileFn, source) ->
+  (compileToJs compileFn, "(#{source})", -1, -1)?.js
+
 compileToJs = (compileFn, source, posOffset = 0, depthOffset = 0) ->
   compileAstToJs compileFn, (astFromSource source, posOffset, depthOffset)

@@ -4302,6 +4307,24 @@
         (match x
           [fst snd] (show snd))))"""
   """(show ["Adam" "Michal"])""", "Michal"
+
+  'instance constraints on array' # Tests where clause typing
+  """
+    Show (class [a]
+      show (fn [x] (: (Fn a String))))
+
+    show-string (instance (Show String)
+      show (fn [x] x))
+
+    head (fn [array]
+      x
+      {x ..xs} array)
+
+    show-snd (instance (Show (Array a))
+      {(Show a)}
+      show (fn [array]
+        (show (head array))))"""
+  """(show {"Michal" "Adam"})""", "Michal"

   'multiple constraints'
   """
@@ -4639,7 +4662,7 @@

 testNamed = (givenName) ->
   for [name, source, expression, result] in tuplize 4, tests when name is givenName
-    return source
+    return source + "\n" + "_ " + expression
   throw new Error "Test #{givenName} not found!"

 logError = (message, error) ->
@@ -4652,6 +4675,11 @@
     fun()
   catch e
     logError "debug", e
+
+runTest = (givenName) ->
+  for [name, source, expression, result] in tuplize 4, tests when name is givenName
+    test name, source + "\n" + expression, result
+  "Done"

 runTests = (tests) ->
   for [name, source, expression, result] in tuplize 4, tests


Dont show completions which were fabricated - calls - but dont match typewise

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -5849,7 +5849,8 @@
     validDefinitions = concatMaps validDefinitions,
       (for name, def of values validDefinitions when returnType = maybeFunctionReturnType def.type
         newMapWith "(#{name} #{Array(def.arity.length).join ' '})",
-          type: new ForAll def.type.kinds, new Constrained [], returnType)...
+          type: new ForAll def.type.kinds, new Constrained [], returnType
+          fabricated: yes)...
     # typesUnify = (def) ->
     #   not isFailed mostGeneralUnifier (freshInstance ctx, def.type).type, type.type
     # [typed, notTyped] = partitionMap typesUnify, validDefinitions
@@ -5867,10 +5868,13 @@
       docs: def.docs
       rawType: def.type
       score: score
+      fabricated: def.fabricated
+    originalOrTyped = (name, completion) ->
+      (isTyped completion) or not completion.fabricated
     isTyped = (completion) ->
       completion.score isnt UNTYPED_PENALTY

-    partitionMap isTyped, (mapMap scoreAndPrint, validDefinitions))
+    partitionMap isTyped, filterMap originalOrTyped, (mapMap scoreAndPrint, validDefinitions))
   values concatMaps typed..., untyped...
   # allDefs = concatMaps typed, notTyped # TODO: don't use object key ordering for ordering
   # values mapMap (__ plainPrettyPrint, _type), allDefs


Strip node_modules before building

Strip node_modules before building

--- tasks/compile.coffee
+++ tasks/compile.coffee
@@ -94,7 +94,14 @@

   # Move the node modules
   gulp.task 'compile:' + dist + ':deps', ['clean:build:' + dist], ->
-    gulp.src './src/node_modules/**/*'
+    gulp.src [
+      './src/node_modules/**/*'
+      '!./src/node_modules/**/test*/**'
+      '!./src/node_modules/**/example*/**'
+      '!./src/node_modules/**/changelog*'
+      '!./src/node_modules/**/notice*'
+      '!./src/node_modules/**/readme*'
+    ]
       .pipe gulp.dest dir + '/node_modules'

   # Move package.json


Adds more logging for --verbose in rebuild task

--- tasks/rebuild.coffee
+++ tasks/rebuild.coffee
@@ -2,6 +2,7 @@
 {platform} = require './utils'
 manifest = require '../src/package.json'
 rebuild = require 'electron-rebuild'
+args = require './args'
 path = require 'path'

 electronPath = '...'
@@ -11,11 +12,18 @@
   gulp.task 'rebuild:' + dist, ['download:' + dist], ->
     rebuild.shouldRebuildNativeModules electronPath, electronVersion
       .then (shouldBuild) ->
+        if args.verbose
+          console.log 'should build', shouldBuild
         if shouldBuild
           arch = if '32' in dist then 'ia32' else 'x64'
           modulesPath = path.resolve __dirname, '..', 'src', 'node_modules'
+          if args.verbose
+            console.log 'installing headers', electronVersion, arch
           rebuild.installNodeHeaders electronVersion, null, null, arch
-            .then () -> rebuild.rebuildNativeModules electronVersion, modulesPath
+            .then () ->
+              if args.verbose
+                console.log 'building', electronVersion, modulesPath
+              rebuild.rebuildNativeModules electronVersion, modulesPath
         else
           true


Make test less likely to fail

Make test less likely to fail

--- test/unit/specs/lazy-runtime/LazyRuntime-spec.coffee
+++ test/unit/specs/lazy-runtime/LazyRuntime-spec.coffee
@@ -235,7 +235,7 @@
 			hits = []
 			# run for a couple of times
 			# just enough so all directions should be hit
-			for i in [1..20]
+			for i in [1..40]
 				programState = thunk()
 				output = programState.outRecord[0]
 				sum += output


peillis/helpapp#3455 some rearrangement

peillis/helpapp#3455 some rearrangement

--- test/test_controller.coffee
+++ test/test_controller.coffee
@@ -144,6 +144,14 @@
       controller = new doofinder.Controller client_mock, [widget_mock], {rpp: 11, filters: {color: ['Azul']}}
       controller.search 'silla'

+  context 'filters management', ->
+
+    beforeEach ()->
+      client_mock =
+        search: ()->
+        hit: ()->
+        hashid: mock.request.hashid
+
     it 'when adding terms filters, filters params change', (done) ->
       # we need to make a search first in order to "refresh" it with
       # applied filters
@@ -161,6 +169,57 @@
       controller.addFilter 'color', 'Rojo'

       controller.refresh() # search wigh color: ['Azul', 'Rojo'] and brand: ['Nike']
+
+    it 'when removing terms filters, filters params change', (done) ->
+      controller = new doofinder.Controller client_mock, [widget_mock]
+      # make 1 search to refresh later
+      controller.search 'silla'
+      # add some filters
+      controller.addFilter 'color', 'Azul'
+      controller.addFilter 'brand', 'Nike'
+      controller.addFilter 'color', 'Rojo'
+      # refresh. we're not testing anything yet
+      controller.refresh()
+      # now remove some
+      controller.removeFilter 'color', 'Azul'
+      controller.removeFilter 'brand', 'Nike'
+      # those filters should be removed
+      client_mock.search = (query, params, cb) ->
+        params.filters.should.have.keys 'color', 'brand'
+        params.filters.color.should.be.eql ['Rojo']
+        params.filters.brand.should.be.empty
+        done()
+      # go!
+      controller.refresh()
+
+    it 'when adding range filters, filters params change', (done) ->
+      controller = new doofinder.Controller client_mock, [widget_mock]
+      controller.search 'silla'
+      client_mock.search = (query, params, cb) ->
+        params.filters.price.should.be.eql gt: 10
+        done()
+      controller.addFilter 'price', gt: 10
+      controller.refresh()
+
+    it 'when removing range filters, filters params change', (done) ->
+      controller = new doofinder.Controller client_mock, [widget_mock]
+      controller.search 'silla'
+      controller.addFilter 'price', gt: 10
+      controller.refresh()
+      controller.removeFilter 'price', lte: 5
+      client_mock.search = (query, params, cb) ->
+        params.filters.should.be.empty
+        done()
+      controller.refresh()
+
+  context ' pagination methods ', ->
+
+    beforeEach ()->
+      client_mock =
+        search: ()->
+        hit: ()->
+        hashid: mock.request.hashid
+

     it 'nextPage redo the search with next page', (done) ->
       controller = new doofinder.Controller client_mock, [widget_mock]
@@ -193,46 +252,3 @@

       # first search
       controller.search 'silla'
-
-
-    it 'when removing terms filters, filters params change', (done) ->
-      controller = new doofinder.Controller client_mock, [widget_mock]
-      # make 1 search to refresh later
-      controller.search 'silla'
-      # add some filters
-      controller.addFilter 'color', 'Azul'
-      controller.addFilter 'brand', 'Nike'
-      controller.addFilter 'color', 'Rojo'
-      # refresh. we're not testing anything yet
-      controller.refresh()
-      # now remove some
-      controller.removeFilter 'color', 'Azul'
-      controller.removeFilter 'brand', 'Nike'
-      # those filters should be removed
-      client_mock.search = (query, params, cb) ->
-        params.filters.should.have.keys 'color', 'brand'
-        params.filters.color.should.be.eql ['Rojo']
-        params.filters.brand.should.be.empty
-        done()
-      # go!
-      controller.refresh()
-
-    it 'when adding range filters, filters params change', (done) ->
-      controller = new doofinder.Controller client_mock, [widget_mock]
-      controller.search 'silla'
-      client_mock.search = (query, params, cb) ->
-        params.filters.price.should.be.eql gt: 10
-        done()
-      controller.addFilter 'price', gt: 10
-      controller.refresh()
-
-    it 'when removing range filters, filters params change', (done) ->
-      controller = new doofinder.Controller client_mock, [widget_mock]
-      controller.search 'silla'
-      controller.addFilter 'price', gt: 10
-      controller.refresh()
-      controller.removeFilter 'price', lte: 5
-      client_mock.search = (query, params, cb) ->
-        params.filters.should.be.empty
-        done()
-      controller.refresh()


Fix specs for pager.

Fix specs for pager.

--- spec/javascripts/e2e/documents_pagination_scenario.js.coffee
+++ spec/javascripts/e2e/documents_pagination_scenario.js.coffee
@@ -20,14 +20,14 @@
       element("div.pagination:first li a:contains('Next')").click()
       expect(documentsList.count()).toEqual(25)
       expect(browser().location().url())
-        .toBe("/databases/first_database/collections/first_collection/documents?page=2")
+        .toBe("/databases/first_database/collections/second_collection/documents?page=2")

       element("div.pagination:first li a:contains('Next')").click()
       expect(documentsList.count()).toEqual(20)
       expect(browser().location().url())
-        .toBe("/databases/first_database/collections/first_collection/documents/?page=3")
+        .toBe("/databases/first_database/collections/second_collection/documents?page=3")

       element("div.pagination:first li a:contains('1')").click()
       expect(documentsList.count()).toEqual(25)
       expect(browser().location().url())
-        .toBe("/databases/first_database/collections/first_collection/documents")
+        .toBe("/databases/first_database/collections/second_collection/documents")


Redirect to dashboard on root folder delete

--- MediaAdminBundle/Resources/public/coffee/view/FolderDeleteButtonView.coffee
+++ MediaAdminBundle/Resources/public/coffee/view/FolderDeleteButtonView.coffee
@@ -30,7 +30,7 @@

   deleteFolder: ->
     if @options.medias.get('parent_id') == undefined
-      redirectUrl = appRouter.generateUrl('showHome')
+      redirectUrl = appRouter.generateUrl('showDashboard')
     else
       redirectUrl = appRouter.generateUrl('listFolder', appRouter.addParametersToRoute(
         'folderId':  @options.medias.get('parent_id')
@@ -39,6 +39,7 @@
       url:  @options.medias.get('links')._self_delete
       method: 'DELETE'
       success: ->
-        Backbone.history.loadUrl(redirectUrl)
-        displayMenu(redirectUrl)
+        appRouter.navigate redirectUrl,
+            trigger: true
+        displayMenu redirectUrl
 )


Make space for the avatar

Make space for the avatar

--- app/pages/settings.cjsx
+++ app/pages/settings.cjsx
@@ -15,34 +15,44 @@
     user: {}

   render: ->
-

-

-

-          Credited name
-
-            <input type="text" className="standard-input full" name="credited_name" value={@props.user.credited_name} onChange={@handleChange} />
-
We’ll use this to give acknowledgement in papers, on posters, etc.

-
-

+

+

+

+          TODO: Avatar
+

-

-          Any other stuff?
-
-            TODO
-
-

-

+

-

-        <button type="button" disabled={@state.saveInProgress or not @props.user.hasUnsavedChanges()} onClick={@saveResource}>Save profile</button>{' '}
-        {@renderSaveStatus()}
-

+

+

+

+              Credited name
+
+                <input type="text" className="standard-input full" name="credited_name" value={@props.user.credited_name} onChange={@handleChange} />
+
Public; we’ll use this to give acknowledgement in papers, on posters, etc.

+
+

+
+

+              Any other stuff?
+
+                TODO
+
+

+

+
+

+            <button type="button" className="standard-button" disabled={@state.saveInProgress or not @props.user.hasUnsavedChanges()} onClick={@saveResource}>Save profile</button>{' '}
+            {@renderSaveStatus()}
+

+

+

-
-
Email preferences

-
-
TODO

+

+
Email preferences

+
TODO

+

 module.exports = React.createClass


add .coffee extension when run with coffee

--- fork_thread.coffee
+++ fork_thread.coffee
@@ -11,6 +11,10 @@
    @program = opt.program
    @params = opt.params
    @cwd = opt.cwd
+
+  regex = /\.coffee$/
+  if @program? and (regex.test __filename) and not (regex.test @program)
+   @program += '.coffee'

   @spawn = false
   @fork = true if @parent? and @parent


Allow arrays as arguments to Point::compare

--- src/point.coffee
+++ src/point.coffee
@@ -177,6 +177,7 @@
   # Returns `0` if this point is equivalent to the argument.
   # Returns `1` if this point follows the argument.
   compare: (other) ->
+    other = Point.fromObject(other)
     if @row > other.row
       1
     else if @row < other.row


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/point.coffee
+++ src/point.coffee
@@ -46,7 +46,7 @@
     ###*

     ###
-    clamp: ->
+    clamp: (n) ->
       @copy().clamp()

     clamp$: ->


FIX: Ensure ticket header data loads only if data is present

--- src/app/common/header/helpdesk-header/helpdesk-header.coffee
+++ src/app/common/header/helpdesk-header/helpdesk-header.coffee
@@ -15,10 +15,18 @@
     #
     updateVisibility = ->
       # These variables help us know if the user teaches or studies a unit
-      $scope.studiesUnit = $scope.projects.length > 0
-      $scope.teachesUnit = $scope.unitRoles.length > 0
+      $scope.studiesUnit = $scope.projects?.length > 0
+      $scope.teachesUnit = $scope.unitRoles?.length > 0
       # Only show the helpdesk dropown if I'm learning or teaching a unit
       $scope.showHelpdeskDropdown = $scope.studiesUnit or $scope.teachesUnit
+      # Check if the user has a ticket|session open to switch which modals they can open
+      if $scope.studiesUnit
+        HelpdeskTicket.currentOpenTicket $scope.currentUser.id, (error, data) ->
+          $scope.currentOpenTicket = data
+      if $scope.teachesUnit
+        HelpdeskSession.currentWorkingSession $scope.currentUser.id, (error, data) ->
+          $scope.currentWorkingSession = data
+

     #
     # Watch for changes in projects and unitRoles length's and update the
@@ -29,12 +37,6 @@

     # Current user binding
     $scope.currentUser = currentUser.profile
-
-    # Check if the user has a ticket|session open to switch which modals they can open
-    HelpdeskTicket.currentOpenTicket $scope.currentUser.id, (error, data) ->
-      $scope.currentOpenTicket = data
-    HelpdeskSession.currentWorkingSession $scope.currentUser.id, (error, data) ->
-      $scope.currentWorkingSession = data

     # Watch the root scope for changes to the current ticket|session and set if needed
     $rootScope.$on 'CurrentOpenTicket', (event, ticket) ->