refactor(macAffix): Switched to use $document and $window. Better top and bottom offset validation

--- src/directives/affix.coffee
+++ src/directives/affix.coffee
@@ -5,100 +5,108 @@
 @description
 Fix the component at a certain position

-@param {Expr} mac-affix-disabled To unpin element
-@param {Expr} mac-affix-top      Top offset (default 0)
-@param {Expr} mac-affix-bottom   Bottom offset (default 0)
+@param {Expr}  mac-affix-disabled To unpin element (default false)
+@param {Expr}  mac-affix-top      Top offset (default 0)
+@param {Expr}  mac-affix-bottom   Bottom offset (default 0)
+@param {Event} refresh-mac-affix  To update the position of affixed element
 ###

-angular.module("Mac").directive "macAffix", ->
-  link: ($scope, element, attrs) ->
-    defaults =
-      top:      0
-      bottom:   0
-      disabled: false
-      classes:  "affix affix-top affix-bottom"
+angular.module("Mac").directive "macAffix", [
+  "$document"
+  "$window"
+  ($document, $window)->
+    link: ($scope, element, attrs) ->
+      defaults =
+        top:      0
+        bottom:   0
+        disabled: false
+        classes:  "affix affix-top affix-bottom"

-    offset    = top: defaults.top, bottom: defaults.bottom
-    position  = top: 0, left: 0
-    disabled  = defaults.disabled
-    lastAffix = null
-    unpin     = null
-    windowEl  = angular.element(window)
+      offset    = top: defaults.top, bottom: defaults.bottom
+      position  = element.offset()
+      disabled  = defaults.disabled
+      lastAffix = null
+      unpin     = null
+      windowEl  = angular.element($window)

-    if attrs.macAffixTop?
-      offset.top = $scope.$eval(attrs.macAffixTop) or defaults.top
-      $scope.$watch attrs.macAffixTop, (value) ->
-        offset.top = value if value?
+      ###
+      @name setOffset
+      @description
+      Update top or bottom offset. This function will make sure the value is
+      an integer and use default value
+      ###
+      setOffset = (key, value, useDefault = false) ->
+        value       = defaults[key] if useDefault and not value?
+        offset[key] = +value if value? and not isNaN(+value)

-    if attrs.macAffixBottom?
-      offset.bottom = $scope.$eval(attrs.macAffixBottom) or defaults.bottom
-      $scope.$watch attrs.macAffixBottom, (value) ->
-        offset.bottom = value if value?
+      if attrs.macAffixTop?
+        setOffset "top", $scope.$eval(attrs.macAffixTop), true
+        $scope.$watch attrs.macAffixTop, (value) -> setOffset "top", value

-    (getPosition = ->
-      position = element.offset()
-    )()
+      if attrs.macAffixBottom?
+        setOffset "bottom", $scope.$eval(attrs.macAffixBottom), true
+        $scope.$watch attrs.macAffixBottom, (value) -> setOffset "bottom", value

-    scrollEvent = ->
-      # Check if element is visible
-      return if element[0].offsetHeight <= 0 and element[0].offsetWidth <= 0
+      scrollEvent = ->
+        # Check if element is visible
+        return if element[0].offsetHeight <= 0 and element[0].offsetWidth <= 0

-      scrollTop    = windowEl.scrollTop()
-      scrollHeight = angular.element(document).height()
+        scrollTop    = windowEl.scrollTop()
+        scrollHeight = $document.height()

-      affix =
-        if unpin? and scrollTop + unpin <= position.top
-          false
-        else if position.top + element.height() >= scrollHeight - offset.bottom
-          "bottom"
-        else if scrollTop <= offset.top
-          "top"
+        affix =
+          if unpin? and scrollTop + unpin <= position.top
+            false
+          else if position.top + element.height() >= scrollHeight - offset.bottom
+            "bottom"
+          else if scrollTop <= offset.top
+            "top"
+          else
+            false
+
+        return if affix is lastAffix
+        lastAffix = affix
+
+        element.css "top", "" if unpin
+
+        element
+          .removeClass(defaults.classes)
+          .addClass "affix" + (if affix then "-#{affix}" else "")
+
+        if affix is "bottom"
+          unpin = position.top - scrollTop
+          element.css(
+            "top",
+            $document[0].body.offsetHeight - offset.bottom - element.height()
+          )
         else
-          false
+          unpin = null

-      return if affix is lastAffix
-      lastAffix = affix
+        return true

-      element.css "top", "" if unpin
+      if attrs.macAffixDisabled?
+        disabled = $scope.$eval(attrs.macAffixDisabled) or defaults.disabled
+        $scope.$watch attrs.macAffixDisabled, (value) ->
+          return if not value? or value is disabled

-      element
-        .removeClass(defaults.classes)
-        .addClass "affix" + (if affix then "-#{affix}" else "")
+          disabled = value
+          action   = if value then "unbind" else "bind"
+          windowEl[action] "scroll", scrollEvent

-      if affix is "bottom"
-        unpin = position.top - scrollTop
-        element.css(
-          "top",
-          document.body.offsetHeight - offset.bottom - element.height()
-        )
-      else
-        unpin = null
+          if disabled
+            # clear all styles and reset affix element
+            lastAffix = null
+            unpin     = null

-      return true
+            element
+              .css("top", "")
+              .removeClass defaults.classes
+          else
+            scrollEvent()

-    if attrs.macAffixDisabled?
-      disabled = $scope.$eval(attrs.macAffixDisabled) or defaults.disabled
-      $scope.$watch attrs.macAffixDisabled, (value) ->
-        return if not value? or value is disabled
+      unless disabled
+        windowEl.bind "scroll", scrollEvent

-        disabled = value
-        action   = if value then "unbind" else "bind"
-        windowEl[action] "scroll", scrollEvent
-
-        if disabled
-          # clear all styles and reset affix element
-          lastAffix = null
-          unpin     = null
-
-          element
-            .css("top", "")
-            .removeClass defaults.classes
-        else
-          scrollEvent()
-
-    unless disabled
-      windowEl.bind "scroll", scrollEvent
-
-    $scope.$on "refresh-mac-affix", getPosition
-    $scope.$on "$destroy", ->
-      windowEl.unbind "scroll", scrollEvent
+      $scope.$on "refresh-mac-affix", -> position = element.offset()
+      $scope.$on "$destroy",          -> windowEl.unbind "scroll", scrollEvent
+]


Allow callback to be passed to GrammarRegistry::decodeContent

--- src/grammar-registry.coffee
+++ src/grammar-registry.coffee
@@ -229,18 +229,20 @@
     grammar.path = grammarPath
     grammar

-  decodeContent: (lineText, tags, scopeTags = []) ->
+  decodeContent: (lineText, tags, scopeTags = [], fn) ->
     offset = 0
     scopeNames = scopeTags.map (tag) => @scopeForId(tag)

     tokens = []
-    for tag in tags
+    for tag, index in tags
       # positive numbers indicate string content with length equaling the number
       if tag >= 0
-        tokens.push({
+        token = {
           value: lineText.substring(offset, offset + tag)
           scopes: scopeNames.slice()
-        })
+        }
+        token = fn(token, index) if fn?
+        tokens.push(token)
         offset += tag

       # odd negative numbers are begin scope tags


made latest hametz times into an announcement

--- source/javascripts/announcements.coffee
+++ source/javascripts/announcements.coffee
@@ -7,3 +7,4 @@
     '22': "Mazal Tov to the Harari family on Marc's Bar Mitzva!"
     '25': "Sunday, March 22nd @ 3PM:
הַכְנָסַת סֵפֶר תּוֹרָה"
     '26': "Sunday, March 22nd @ 3PM:
הַכְנָסַת סֵפֶר תּוֹרָה"
+    '28': "Stop eating חָמֵץ before 10:55 AM
Burn חָמֵץ before 12:09 PM"


:art: Split render of linter-views-new into multiple functions

--- lib/linter-views-new.coffee
+++ lib/linter-views-new.coffee
@@ -20,10 +20,16 @@

   render: ->
     @messages = @linter.messages.getAllMessages()
-    @lineMessages = if @bottomContainer.getTab('File').attached then @linter.messages.getActiveFileMessagesForActiveRow() else []
+    @updateLineMessages()
+
+  renderCount: ->
     count = @linter.messages.getCount()
     count.Line = @lineMessages.length
     @bottomContainer.setCount(count)
+
+  updateLineMessages: ->
+    @lineMessages = if @bottomContainer.getTab('File').attached then @linter.messages.getActiveFileMessagesForActiveRow() else []
+    @renderCount()

   attachBottom: (statusBar) ->
     @bottomBar = statusBar.addLeftTile


nginx: s/302/301

nginx: s/302/301

--- deployment/nginx.coffee
+++ deployment/nginx.coffee
@@ -184,7 +184,7 @@
       # just a random port
       listen 81;
       # use generic names, do not hardcode values
-      return 302 https://$host$request_uri;
+      return 301 https://$host$request_uri;
     }


Redirect /{blog,docs} requests to www site

--- deployment/nginx.coffee
+++ deployment/nginx.coffee
@@ -324,6 +324,10 @@
         proxy_pass http://webserver;
       }

+      location ~ /(blog|docs)(.*) {
+        return 301 https://www.koding.com/$1$2$is_args$args;
+      }
+
       # mac and windows are case insensitive, redirect lowercased hackathon to
       # Uppercase Hackathon
       location ~ "(?-i)/hackathon" {


Sorting maps in serilized transactions

Sorting maps in serilized transactions

--- dl/src/chain/serializer_types.coffee
+++ dl/src/chain/serializer_types.coffee
@@ -309,6 +309,11 @@
             object = v.get_instance reserved_spaces, object_type, object

         "#{reserved_spaces}.#{object_type_id}."+object
+    compare: (a, b) ->
+        if(Array.isArray(a) && Array.isArray(b) && a.length > 0 && b.length > 0)
+            return a[0] - b[0]
+        else
+            return 0

 Types.protocol_id_type = (name)->
     id_type chain_types.reserved_spaces.protocol_ids, name
@@ -446,7 +451,10 @@
                 if dup_map[o[0]] isnt undefined
                     throw new Error "duplicate"
                 dup_map[o[0]] = on
-        array
+        if key_st_operation.compare
+            array.sort(key_st_operation.compare)
+        else
+            array

     fromByteBuffer:(b)->
         @validate (for i in [0...b.readVarint32()] by 1


Remove dl folder

Remove dl folder

--- dl/src/chain/serializer_types.coffee
+++ dl/src/chain/serializer_types.coffee
@@ -1,531 +0,0 @@
-ByteBuffer = require 'bytebuffer'
-ObjectId = require './object_id'
-Serializer = require './serializer'
-Address = require '../ecc/address'
-PublicKey = require '../ecc/key_public'
-
-v = require './serializer_validation'
-chain_types = require './chain_types'
-config = require './serializer_config'
-fp = require '../common/fast_parser'
-
-module.exports = Types = {}
-
-Types.uint8 =
-    fromByteBuffer:(b)->
-        b.readUint8()
-    appendByteBuffer:(b, object)->
-        v.require_range 0,0xFF,object, "uint8 #{object}"
-        b.writeUint8 object
-        return
-    fromObject:(object)->
-        v.require_range 0,0xFF,object, "uint8 #{object}"
-        object
-    toObject:(object, debug = {})->
-        return 0 if debug.use_default and object is undefined
-        v.require_range 0,0xFF,object, "uint8 #{object}"
-        parseInt object
-
-Types.uint16 =
-    fromByteBuffer:(b)->
-        b.readUint16()
-    appendByteBuffer:(b, object)->
-        v.require_range 0,0xFFFF,object, "uint16 #{object}"
-        b.writeUint16 object
-        return
-    fromObject:(object)->
-        v.require_range 0,0xFFFF,object, "uint16 #{object}"
-        object
-    toObject:(object, debug = {})->
-        return 0 if debug.use_default and object is undefined
-        v.require_range 0,0xFFFF,object, "uint16 #{object}"
-        parseInt object
-
-Types.uint32 =
-    fromByteBuffer:(b)->
-        b.readUint32()
-    appendByteBuffer:(b, object)->
-        v.require_range 0,0xFFFFFFFF,object, "uint32 #{object}"
-        b.writeUint32 object
-        return
-    fromObject:(object)->
-        v.require_range 0,0xFFFFFFFF,object, "uint32 #{object}"
-        object
-    toObject:(object, debug = {})->
-        return 0 if debug.use_default and object is undefined
-        v.require_range 0,0xFFFFFFFF,object, "uint32 #{object}"
-        parseInt object
-
-MIN_SIGNED_32 = -1 * Math.pow(2,31)
-MAX_SIGNED_32 = Math.pow(2,31) - 1
-
-Types.varint32 =
-    fromByteBuffer:(b)->
-        b.readVarint32()
-    appendByteBuffer:(b, object)->
-        v.require_range(
-            MIN_SIGNED_32
-            MAX_SIGNED_32
-            object
-            "uint32 #{object}"
-        )
-        b.writeVarint32 object
-        return
-    fromObject:(object)->
-        v.require_range(
-            MIN_SIGNED_32
-            MAX_SIGNED_32
-            object
-            "uint32 #{object}"
-        )
-        object
-    toObject:(object, debug = {})->
-        return 0 if debug.use_default and object is undefined
-        v.require_range(
-            MIN_SIGNED_32
-            MAX_SIGNED_32
-            object
-            "uint32 #{object}"
-        )
-        parseInt object
-
-Types.int64 =
-    fromByteBuffer:(b)->
-        b.readInt64()
-    appendByteBuffer:(b, object)->
-        v.required object
-        b.writeInt64 v.to_long object
-        return
-    fromObject:(object)->
-        v.required object
-        v.to_long object
-    toObject:(object, debug = {})->
-        return "0" if debug.use_default and object is undefined
-        v.required object
-        v.to_long(object).toString()
-
-Types.uint64 =
-    fromByteBuffer:(b)->
-        b.readUint64()
-    appendByteBuffer:(b, object)->
-        b.writeUint64 v.to_long v.unsigned object
-        return
-    fromObject:(object)->
-        v.to_long v.unsigned object
-    toObject:(object, debug = {})->
-        return "0" if debug.use_default and object is undefined
-        v.to_long(object).toString()
-
-Types.string =
-    fromByteBuffer:(b)->
-        len = b.readVarint32()
-        b_copy = b.copy(b.offset, b.offset + len); b.skip len
-        new Buffer(b_copy.toBinary(), 'binary')
-    appendByteBuffer:(b, object)->
-        v.required object
-        b.writeVarint32(object.length)
-        b.append(object.toString('binary'), 'binary')
-        return
-    fromObject:(object)->
-        v.required object
-        new Buffer object
-    toObject:(object, debug = {})->
-        return "" if debug.use_default and object is undefined
-        object.toString()
-
-Types.bytes = (size)->
-    fromByteBuffer:(b)->
-        if size is undefined
-            len = b.readVarint32()
-            b_copy = b.copy(b.offset, b.offset + len); b.skip len
-            new Buffer(b_copy.toBinary(), 'binary')
-        else
-            b_copy = b.copy(b.offset, b.offset + size); b.skip size
-            new Buffer(b_copy.toBinary(), 'binary')
-    appendByteBuffer:(b, object)->
-        v.required object
-        if size is undefined
-            b.writeVarint32(object.length)
-        b.append(object.toString('binary'), 'binary')
-        return
-    fromObject:(object)->
-        v.required object
-        new Buffer object, 'hex'
-    toObject:(object, debug = {})->
-        if debug.use_default and object is undefined
-            zeros=(num)-> new Array( num ).join( "00" )
-            return zeros size
-        v.required object
-        object.toString 'hex'
-
-Types.bool =
-    fromByteBuffer:(b)->
-        b.readUint8()
-    appendByteBuffer:(b, object)->
-        # supports boolean or integer
-        b.writeUint8 if object then 1 else 0
-        #b.writeUint8 object
-        return
-    fromObject:(object)->
-        if object then 1 else 0
-    toObject:(object, debug = {})->
-        return no if debug.use_default and object is undefined
-        if object then yes else no
-
-Types.void =
-    fromByteBuffer:(b)->
-        throw new Error "(void) undefined type"
-    appendByteBuffer:(b, object)->
-        throw new Error "(void) undefined type"
-    fromObject:(object)->
-        throw new Error "(void) undefined type"
-    toObject:(object, debug = {})->
-        if debug.use_default and object is undefined
-            return undefined
-        throw new Error "(void) undefined type"
-
-Types.array = (st_operation)->
-    fromByteBuffer:(b)->
-        size = b.readVarint32()
-        if config.hex_dump
-            console.log "varint32 size = " + size.toString(16)
-        for i in [0...size] by 1
-            st_operation.fromByteBuffer b
-    appendByteBuffer:(b, object)->
-        v.required object
-        b.writeVarint32 object.length
-        for o in object
-            st_operation.appendByteBuffer b, o
-        return
-    fromObject:(object)->
-        v.required object
-        for o in object
-            st_operation.fromObject o
-    toObject:(object, debug = {})->
-        if debug.use_default and object is undefined
-            return [ st_operation.toObject(object, debug) ]
-        v.required object
-        for o in object
-            st_operation.toObject o, debug
-
-Types.time_point_sec =
-    fromByteBuffer:(b)-> b.readUint32()
-    appendByteBuffer:(b, object)->
-        b.writeUint32 object
-        return
-    fromObject:(object)->
-        v.required object
-        Math.round( (new Date(object)).getTime() / 1000 )
-    toObject:(object, debug = {})->
-        if debug.use_default and object is undefined
-            return (new Date(0)).toISOString().split('.')[0]
-        v.required object
-        int = parseInt object
-        v.require_range 0,0xFFFFFFFF,int, "uint32 #{object}"
-        (new Date(int*1000)).toISOString().split('.')[0]
-
-Types.set = (st_operation)->
-    validate: (array)->
-        dup_map = {}
-        for o in array
-            if typeof o in ['string', 'number']
-                if dup_map[o] isnt undefined
-                    throw new Error "duplicate"
-                dup_map[o] = on
-        array.sort(st_operation.compare)
-    fromByteBuffer:(b)->
-        size = b.readVarint32()
-        if config.hex_dump
-            console.log "varint32 size = " + size.toString(16)
-        @validate (for i in [0...size] by 1
-            st_operation.fromByteBuffer b)
-    appendByteBuffer:(b, object)->
-        object = [] unless object
-        b.writeVarint32 object.length
-        for o in @validate object
-            st_operation.appendByteBuffer b, o
-        return
-    fromObject:(object)->
-        object = [] unless object
-        @validate (for o in object
-            st_operation.fromObject o)
-    toObject:(object, debug = {})->
-        if debug.use_default and object is undefined
-            return [ st_operation.toObject(object, debug) ]
-        object = [] unless object
-        @validate (for o in object
-            st_operation.toObject o, debug)
-
-# global_parameters_update_operation current_fees
-Types.fixed_array = (count, st_operation)->
-    fromByteBuffer:(b)->
-        for i in [0...count] by 1
-            st_operation.fromByteBuffer b
-    appendByteBuffer:(b, object)->
-        v.required object unless count is 0
-        for i in [0...count] by 1
-            st_operation.appendByteBuffer b, object[i]
-        return
-    fromObject:(object)->
-        v.required object unless count is 0
-        for i in [0...count] by 1
-            st_operation.fromObject object[i]
-    toObject:(object, debug = {})->
-        return if debug.use_default and object is undefined
-            for i in [0...count] by 1
-                st_operation.toObject undefined, debug
-        v.required object unless count is 0
-        for i in [0...count] by 1
-            st_operation.toObject object[i], debug
-
-### Supports instance numbers (11) or object types (1.2.11).  Object type
-validation is enforced when an object type is used. ###
-id_type = (reserved_spaces, object_type)->
-    v.required reserved_spaces, "reserved_spaces"
-    v.required object_type, "object_type"
-    fromByteBuffer:(b)->
-        b.readVarint32()
-    appendByteBuffer:(b, object)->
-        v.required object
-        object = object.resolve if object.resolve isnt undefined
-        # convert 1.2.n into just n
-        if /^[0-9]+\.[0-9]+\.[0-9]+$/.test object
-            object = v.get_instance reserved_spaces, object_type, object
-        b.writeVarint32 object
-        return
-    fromObject:(object)->
-        v.required object
-        object = object.resolve if object.resolve isnt undefined
-        if v.is_digits object
-            return v.to_number object
-        v.get_instance reserved_spaces, object_type, object
-    toObject:(object, debug = {})->
-        object_type_id = chain_types.object_type[object_type]
-        if debug.use_default and object is undefined
-            return "#{reserved_spaces}.#{object_type_id}.0"
-        v.required object
-        object = object.resolve if object.resolve isnt undefined
-        if /^[0-9]+\.[0-9]+\.[0-9]+$/.test object
-            object = v.get_instance reserved_spaces, object_type, object
-
-        "#{reserved_spaces}.#{object_type_id}."+object
-    compare: (a, b) ->
-        if(Array.isArray(a) && Array.isArray(b) && a.length > 0 && b.length > 0)
-            return a[0] - b[0]
-        else
-            return 0
-
-Types.protocol_id_type = (name)->
-    id_type chain_types.reserved_spaces.protocol_ids, name
-
-Types.object_id_type =
-    fromByteBuffer:(b)->
-        ObjectId.fromByteBuffer b
-    appendByteBuffer:(b, object)->
-        v.required object
-        object = object.resolve if object.resolve isnt undefined
-        object = ObjectId.fromString object
-        object.appendByteBuffer b
-        return
-    fromObject:(object)->
-        v.required object
-        object = object.resolve if object.resolve isnt undefined
-        ObjectId.fromString object
-    toObject:(object, debug = {})->
-        if debug.use_default and object is undefined
-            return "0.0.0"
-        v.required object
-        if object.resolve isnt undefined
-            object = object.resolve
-        object = ObjectId.fromString object
-        object.toString()
-
-Types.vote_id =
-    TYPE: 0x000000FF
-    ID:   0xFFFFFF00
-    fromByteBuffer:(b)->
-        value = b.readUint32()
-        type: value & @TYPE
-        id: value & @ID
-    appendByteBuffer:(b, object)->
-        v.required object
-        value = object.id << 8 | object.type
-        b.writeUint32 value
-        return
-    fromObject:(object)->
-        v.required object, "(type vote_id)"
-        v.require_test /^[0-9]+:[0-9]+$/, object, "vote_id format #{object}"
-        [type, id] = object.split ':'
-        v.require_range 0,0xff,type,"vote type #{object}"
-        v.require_range 0,0xffffff,id,"vote id #{object}"
-        type:type
-        id:id
-    toObject:(object, debug = {})->
-        if debug.use_default and object is undefined
-            return "0:0"
-        v.required object
-        object.type + ":" + object.id
-    compare: (a, b) ->
-        parseInt(a.id) - parseInt(b.id)
-
-Types.optional = (st_operation)->
-    v.required st_operation, "st_operation"
-    fromByteBuffer:(b)->
-        unless b.readUint8() is 1
-            return undefined
-        st_operation.fromByteBuffer b
-    appendByteBuffer:(b, object)->
-        if object isnt null and object isnt undefined
-            b.writeUint8 1
-            st_operation.appendByteBuffer b, object
-        else
-            b.writeUint8 0
-        return
-    fromObject:(object)->
-        return undefined if object is undefined
-        st_operation.fromObject object
-    toObject:(object, debug = {})->
-        # toObject is only null save if use_default is true
-        result_object = if not debug.use_default and object is undefined
-            undefined
-        else
-            st_operation.toObject object, debug
-
-        if debug.annotate
-            if typeof result_object is "object"
-                result_object.__optional = "parent is optional"
-            else
-                result_object = __optional: result_object
-        result_object
-
-Types.static_variant = (_st_operations)->
-    st_operations: _st_operations
-    fromByteBuffer:(b)->
-        type_id = b.readVarint32()
-        st_operation = @st_operations[type_id]
-        if config.hex_dump
-            console.error("static_variant id 0x#{type_id.toString(16)} (#{type_id})")
-        v.required st_operation, "operation #{type_id}"
-        [
-            type_id
-            st_operation.fromByteBuffer b
-        ]
-    appendByteBuffer:(b, object)->
-        v.required object
-        type_id = object[0]
-        st_operation = @st_operations[type_id]
-        v.required st_operation, "operation #{type_id}"
-        b.writeVarint32 type_id
-        st_operation.appendByteBuffer b, object[1]
-        return
-    fromObject:(object)->
-        v.required object
-        type_id = object[0]
-        st_operation = @st_operations[type_id]
-        v.required st_operation, "operation #{type_id}"
-        [
-            type_id
-            st_operation.fromObject object[1]
-        ]
-    toObject:(object, debug = {})->
-        if debug.use_default and object is undefined
-            return [0, @st_operations[0].toObject(undefined, debug)]
-        v.required object
-        type_id = object[0]
-        st_operation = @st_operations[type_id]
-        v.required st_operation, "operation #{type_id}"
-        [
-            type_id
-            st_operation.toObject object[1], debug
-        ]
-
-Types.map = (key_st_operation, value_st_operation)->
-    validate:(array)->
-        unless Array.isArray array
-            throw new Error "expecting array"
-        dup_map = {}
-        for o in array
-            unless o.length is 2
-                throw new Error "expecting two elements"
-            if typeof o[0] in ['number', 'string']
-                if dup_map[o[0]] isnt undefined
-                    throw new Error "duplicate"
-                dup_map[o[0]] = on
-        if key_st_operation.compare
-            array.sort(key_st_operation.compare)
-        else
-            array
-
-    fromByteBuffer:(b)->
-        @validate (for i in [0...b.readVarint32()] by 1
-            [
-                key_st_operation.fromByteBuffer b
-                value_st_operation.fromByteBuffer b
-            ])
-
-    appendByteBuffer:(b, object)->
-        @validate object
-        b.writeVarint32 object.length
-        for o in object
-            key_st_operation.appendByteBuffer b, o[0]
-            value_st_operation.appendByteBuffer b, o[1]
-        return
-    fromObject:(object)->
-        v.required object
-        @validate (for o in object
-            [
-                key_st_operation.fromObject o[0]
-                value_st_operation.fromObject o[1]
-            ])
-    toObject:(object, debug = {})->
-        if debug.use_default and object is undefined
-            return [
-                [
-                    key_st_operation.toObject(undefined, debug)
-                    value_st_operation.toObject(undefined, debug)
-                ]
-            ]
-        v.required object
-        @validate (for o in object
-            [
-                key_st_operation.toObject o[0], debug
-                value_st_operation.toObject o[1], debug
-            ])
-
-Types.public_key =
-    _to_public:(object)->
-        object = object.resolve if object.resolve isnt undefined
-        return object if object.Q
-        PublicKey.fromPublicKeyString object
-    fromByteBuffer:(b)->
-        fp.public_key b
-    appendByteBuffer:(b, object)->
-        v.required object
-        fp.public_key b, Types.public_key._to_public object
-        return
-    fromObject:(object)->
-        v.required object
-        return object if object.Q
-        PublicKey.fromPublicKeyString object
-    toObject:(object, debug = {})->
-        if debug.use_default and object is undefined
-            return "BTSXyz...public_key"
-        v.required object
-        Types.public_key._to_public(object).toPublicKeyString()
-
-Types.address =
-    _to_address:(object)->
-        v.required object
-        return object if object.addy
-        Address.fromString(object)
-    fromByteBuffer:(b)->
-        new Address(fp.ripemd160 b)
-    appendByteBuffer:(b, object)->
-        fp.ripemd160 b, Types.address._to_address(object).toBuffer()
-        return
-    fromObject:(object)->
-        Types.address._to_address(object)
-    toObject:(object, debug = {})->
-        if debug.use_default and object is undefined
-            return "BTSXyz...address"
-        Types.address._to_address(object).toString()


Fixing mood poll

Fixing mood poll

--- scripts/awesome.team.coffee
+++ scripts/awesome.team.coffee
@@ -28,7 +28,7 @@
     "http://i.imgur.com/aimsF.jpg"
   ]

-  achievementReminder = new CronJob '00 00 16 * * 5', ->
+  achievementReminder = new CronJob '00 18 20 * * 5', ->
     for room, board of robot.brain.data._private.board
       robot.messageRoom room, "Hello " + robot.brain.data._private.team[room].name + "!"
       robot.messageRoom room, "Are there any achievements from the week that someone @here needs to add to your board '" + board.name + "'?"
@@ -37,40 +37,42 @@
   moodPoll = new CronJob '00 05 20 * * 1-5', ->
     for room, board of robot.brain.data._private.board
       buttonName = "mood_" + robot.brain.data._private.team[room]._id
-      robot.emit 'slack.attachment',
-        message: robot.messageRoom
-        content:
-          text: "How was your day today?"
-          fallback: "Oh no! Something went horribly wrong!"
-          callback_id: "dab_mood"
-          color: "#3AA3E3"
-          attachment_type: "default"
-          actions: [{
-            name: buttonName
-            text: ":bolt-ecstatic:"
-            type: "button"
-            value: "ecstatic"
-          },{
-            name: buttonName
-            text: ":bolt-happy:"
-            type: "button"
-            value: "happy"
-          },{
-            name: buttonName
-            text: ":bolt-indifferent:"
-            type: "button"
-            value: "indifferent"
-          },{
-            name: buttonName
-            text: ":bolt-disappointed:"
-            type: "button"
-            value: "disappointed"
-          },{
-            name: buttonName
-            text: ":bolt-sad:"
-            type: "button"
-            value: "sad"
+      pollMessage = {
+        "attachments": [{
+          "text": "How was your day today?",
+          "fallback": "Oh no! Something went horribly wrong!"
+          "callback_id": "dab_mood"
+          "color": "#3AA3E3"
+          "attachment_type": "default"
+          "actions": [{
+            "name": buttonName
+            "text": ":bolt-ecstatic:"
+            "type": "button"
+            "value": "ecstatic"
+          },{
+            "name": buttonName
+            "text": ":bolt-happy:"
+            "type": "button"
+            "value": "happy"
+          },{
+            "name": buttonName
+            "text": ":bolt-indifferent:"
+            "type": "button"
+            "value": "indifferent"
+          },{
+            "name": buttonName
+            "text": ":bolt-disappointed:"
+            "type": "button"
+            "value": "disappointed"
+          },{
+            "name": buttonName
+            "text": ":bolt-sad:"
+            "type": "button"
+            "value": "sad"
           }]
+        }]
+      }
+      robot.messageRoom room, pollMessage
   , null, true, 'America/New_York'

   robot.hear /what is the team's mood in the past (.*) days/i, (msg) ->


Update status bar counter when local config files change

--- lib/deprecation-cop-status-bar-view.coffee
+++ lib/deprecation-cop-status-bar-view.coffee
@@ -1,4 +1,4 @@
-{CompositeDisposable} = require 'atom'
+{CompositeDisposable, File} = require 'atom'
 {$, $$, View} = require 'atom-space-pen-views'
 _ = require 'underscore-plus'
 Grim = require 'grim'
@@ -21,6 +21,15 @@
     @subscriptions.add atom.packages.onDidLoadPackage @updateDeprecatedSelectorCount
     @subscriptions.add atom.packages.onDidUnloadPackage @updateDeprecatedSelectorCount
     @subscriptions.add atom.packages.onDidActivatePackage @updateDeprecatedSelectorCount
+
+    @subscriptions.add atom.keymaps.onDidReloadKeymap (event) =>
+      @updateDeprecatedSelectorCount() if event.path is atom.keymaps.getUserKeymapPath()
+
+    try
+      userStylesheetFile = new File(atom.styles.getUserStyleSheetPath())
+      @subscriptions.add userStylesheetFile.onDidChange @updateDeprecatedSelectorCount
+    catch
+

   destroy: ->
     @subscriptions.dispose()


When a socket message has been split into two, make sure we prepend the leftovers of the last message while parsing

--- lib/engines/dbgp/dbgp-instance.coffee
+++ lib/engines/dbgp/dbgp-instance.coffee
@@ -73,7 +73,7 @@

   stuff: (data) =>
-    message = @parse(data)
+    @buffer = message = @parse(@buffer + data)

   executeCommand: (command, options, data) ->
     @command(command, options, data)


Allows custom exceptions to be defined in settings

--- lib/engines/dbgp/dbgp-instance.coffee
+++ lib/engines/dbgp/dbgp-instance.coffee
@@ -143,6 +143,11 @@
       commands.push @executeBreakpoint(new Breakpoint(type: Breakpoint.TYPE_EXCEPTION, exception: 'Unknown Error'))
     if atom.config.get('php-debug.PhpException.Notice')
       commands.push @executeBreakpoint(new Breakpoint(type: Breakpoint.TYPE_EXCEPTION, exception: 'Notice'))
+
+    for exception in atom.config.get('php-debug.CustomExceptions')
+      console.log exception
+      commands.push @executeBreakpoint(new Breakpoint(type: Breakpoint.TYPE_EXCEPTION, exception: exception))
+
     return Q.all(commands)

   executeBreakpoint: (breakpoint) =>


Allow forking into personal space

Allow forking into personal space

--- scripts/gh-book/auth.coffee
+++ scripts/gh-book/auth.coffee
@@ -166,7 +166,7 @@
       $block = $('

')
       $avatar = $('').attr('src', info.avatar_url)
       $name = $('').html(info.login)
-      $block.append($avatar).append($name)
+      $block.append($avatar).append($name).data('org-name', info.login)
       $body.append($block)
       for org in orgs
         $block = $('

')
@@ -207,24 +207,26 @@
             @__forkContent(userinfo.login)

     __forkContent: (login) ->
-      # If repo exists, go to it or cancel. Else fork.
+      # If repo exists, go to it or cancel. Else fork.
       @model.getClient().getRepo(login, @model.get('repoName')).getInfo().done () =>
         @$el.find('#fork-redirect-modal').data('login', login).modal
           show: true
       .fail () =>
         @___forkContent(login)

-    ___forkContent: (login) ->
+    ___forkContent: (org) ->
       $modal = @$el.find('#fork-progress-modal')
       $body = $modal.find('.modal-body')
       $body.html('Creating a Fork...')
       $modal.modal {show: true}
-      @model.getRepo()?.fork(login).done () =>
+
+      # If the chosen organisation is myself, leave it out.
+      @model.getRepo()?.fork(org != @model.get('id') and org or null).done () =>
         $body.html('Waiting for Fork to become available...')

         # Change upstream repo
         wait = 2000
-        @model.set 'repoUser', login
+        @model.set 'repoUser', org

         # Poll until repo becomes available
         pollRepo = () =>


restricts onLoadFinished to be called only once

- fixes an issue with iframes loading / potentially redirecting.

--- lib/teabag/drivers/phantomjs/runner.coffee
+++ lib/teabag/drivers/phantomjs/runner.coffee
@@ -50,6 +50,7 @@

     onLoadFinished: (status) =>
+      return if @start
       @start = new Date().getTime()
       defined = @page.evaluate(-> window.Teabag)
       unless status == "success" && defined


She was grow up

She was grow up

--- scripts/idobata-talk.coffee
+++ scripts/idobata-talk.coffee
@@ -72,3 +72,10 @@
       "うるさい！ニヤついてなんか無い！死ね！この変態！クズ！"
     ]
     msg.send msg.random words
+
+  robot.respond /かわいい|カワイイ|可愛い|きれい|キレイ|綺麗|すてき|ステキ|素敵/i, (msg) ->
+    msg.send msg.random [
+      "てっ、照れてないもんっ。"
+      "べべべべべ、べつに嬉しくなんかないからね！"
+      "うるさい！ニヤついてなんか無い！死ね！この変態！クズ！"
+    ]


Display message when there aren't any comments.

Changed the order of the load more method so the message is displayed at
the appropriate time.

--- core/app/backbone/views/react_feed_activities.coffee
+++ core/app/backbone/views/react_feed_activities.coffee
@@ -9,20 +9,22 @@

 window.ReactFeedActivitiesAutoLoading = React.createBackboneClass
   displayName: 'ReactFeedActivitiesAutoLoading'
+  changeOptions: 'sync'

   # this function loads more activities, if we're almost at the bottom of the list
   checkScrolledPosition: ->
     pixels_under_fold = $(document).height() - ($(window).scrollTop() + $(window).height())
-
     @model().loadMore() if pixels_under_fold < 700

   componentDidMount: ->
-    if !@model().length
-      @model().loadMore()
     $(window).on "scroll", @checkScrolledPosition
     @checkScrolledPosition()

   componentDidUpdate: -> @checkScrolledPosition()
+
+  componentWillMount: ->
+    if !@model().length
+      @model().loadMore()

   componentWillUnmount: ->
     # createBackboneClass unbinds model
@@ -30,6 +32,8 @@

   render: ->
     _div [],
+      if !@model().length && !@model().loading()
+        'There aren\'t any comments.'
       @model().map (model) =>
         ReactActivity model: model, key: model.id
       ReactLoadingIndicator model: @model()


Update el.coffee

Update el.coffee

Fixed quotes

--- app/locale/el.coffee
+++ app/locale/el.coffee
@@ -37,7 +37,7 @@
     compared: "συγκρίνονται"
     conventional: "με τις συμβατικές μεθόδους αξιολόγησης, η διαφορά είναι σαφής: τα παιχνίδια είναι καλύτερα για να βοηθήσουν τους μαθητές να διατηρήσουν τη γνώση, να συγκεντρωθούν και να"
     perform_at_higher_level: "πετύχουν υψηλότερο επίπεδο προόδου"
-    feedback: "Τα παιχνίδια παρέχουν επίσης αναπληροφόρηση σε πραγματικό χρόνο, που επιτρέπει στους μαθητές να προσαρμόζουν την πορεία της λύσης και να κατανοούν τις έννοιες πιο ολιστικά, αντί να περιορίζονται μόνο σε "σωστές" ή "εσφαλμένες" απαντήσεις."
+    feedback: "Τα παιχνίδια παρέχουν επίσης αναπληροφόρηση σε πραγματικό χρόνο, που επιτρέπει στους μαθητές να προσαρμόζουν την πορεία της λύσης και να κατανοούν τις έννοιες πιο ολιστικά, αντί να περιορίζονται μόνο σε \"σωστές\" ή \"εσφαλμένες\" απαντήσεις."
     real_game: "Ένα πραγματικό παιχνίδι, που παίζεται με πραγματικό γράψιμο κώδικα."
     great_game: "Ένα εξαιρετικό παιχνίδι είναι κάτι περισσότερο από απλά εμβλήματα και επιτεύγματα - πρόκειται για το ταξίδι ενός παίκτη, για καλά σχεδιασμένους γρίφους και την ικανότητα αντιμετώπισης προκλήσεων με αυτενέργεια και αυτοπεποίθηση."
     agency: "Το CodeCombat είναι ένα παιχνίδι που δίνει στους παίκτες αυτή την δυνατότητα δράσης και σιγουριάς με την ισχυρή μας μηχανή πληκτρολόγησης κώδικα, η οποία βοηθά τους αρχάριους και τους προχωρημένους μαθητές να γράφουν σωστό και έγκυρο κώδικα."
@@ -960,7 +960,7 @@
     story_sketch_caption: "Το πρώτο σκίτσο του Nick που απεικονίζει ένα παιχνίδι προγραμματισμού σε δράση."
     story_long_way_2: "έχουμε ακόμα πολλά να κάνουμε πριν ολοκληρώσουμε την αναζήτησή μας, έτσι..."
     jobs_title: "Ελάτε να συνεργαστείτε μαζί μας και να γράψετε την ιστορία του CodeCombat!"
-    jobs_subtitle: "Δεν βλέπετε κάτι που να σας κάνει αλλά ενδιαφέρεστε να διατηρείτε επαφή; Ανατρέξτε στο "Δημιουργήστε τη Δικιά Σας" θέση."
+    jobs_subtitle: "Δεν βλέπετε κάτι που να σας κάνει αλλά ενδιαφέρεστε να διατηρείτε επαφή; Ανατρέξτε στο \"Δημιουργήστε τη Δικιά Σας\" θέση."
     jobs_benefits: "Παροχές σε Εργαζόμενους"
     jobs_benefit_4: "Απεριόριστες διακοπές"
     jobs_benefit_5: "Επαγγελματική ανάπτυξη και υποστήριξη συνεχούς εκπαίδευσης - δωρεάν βιβλία και παιχνίδια!"


Fix indentation in it.coffee

Fix indentation in it.coffee

--- app/locale/it.coffee
+++ app/locale/it.coffee
@@ -1,5 +1,5 @@
 module.exports = nativeDescription: "Italiano", englishDescription: "Italian", translation:
- new_home:
+  new_home:
     slogan: "Il più avvincente gioco per imparare a programmare."
     classroom_edition: "Edizione Scuola:"
     learn_to_code: "Impara a programmare:"
@@ -552,7 +552,7 @@
     tip_programming_not_about_computers: "La Scienza dei Computer non riguarda i computer più di quanto l'astronomia riguardi i telescopi.. - Edsger Dijkstra"
     tip_mulan: "Se ci credi, raggiungerai i tuoi obbiettivi. - Mulan"

-play_game_dev_level:
+  play_game_dev_level:
     created_by: "Creato da {{name}}"
     how_to_play_1: "Usa il mouse per controllare l'eroe!"
     how_to_play_2: "Clicca dove vuoi sulla mappa per muoverti in quel punto."


Update ja.coffee

Update ja.coffee

--- app/locale/ja.coffee
+++ app/locale/ja.coffee
@@ -247,14 +247,14 @@
     victory_saving_progress: "セーブ中"
     victory_go_home: "ホームに戻る"
     victory_review: "フィードバック"
-#    victory_review_placeholder: "How was the level?"
+    victory_review_placeholder: "どのレベルでしたか?"
     victory_hour_of_code_done: "完了してよろしいですか?"
     victory_hour_of_code_done_yes: "はい、構いません"
     victory_experience_gained: "XP獲得"
     victory_gems_gained: "ジェム獲得"
     victory_new_item: "ニューアイテム"
-#    victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks."
-#    victory_become_a_viking: "Become a Viking"
+    victory_viking_code_school: "あなたはハードレベルのホーリー・スモークを打ち破りました! もしあなたが既にソフトウェア開発者でないなら, ぜひやってみましょう. あなたはちょうどViking Code Scoolで受け入れられるための出世街道にいます,そこでは次のレベルへのスキルを取得でき、14週間でプロのWeb開発者になれます。"
+    victory_become_a_viking: "バイキングになる"
     guide_title: "ガイド"
     tome_minion_spells: "操作できるキャラクターの呪文" # Only in old-style levels.
     tome_read_only_spells: "読込専用の呪文" # Only in old-style levels.
@@ -405,12 +405,12 @@
     feature4: "3500のジェムが毎月ボーナス!"
     feature5: "ビデオチュートリアル"
     feature6: "プレミアムメールサポート"
-#    feature7: "Private Clans"
+    feature7: "プライベートクラン"
     free: "無料"
     month: "月"
     subscribe_title: "課金"
     unsubscribe: "無課金"
-#    confirm_unsubscribe: "Confirm Unsubscribe"
+    confirm_unsubscribe: "課金の解約確認"
     never_mind: "気にしないでください, それでもあなたが好きです"
     thank_you_months_prefix: "私達を "
     thank_you_months_suffix: "ヶ月サポートしてくださりありがとうございます。"
@@ -492,14 +492,14 @@
     blocks: "ブロックパワー" # As in "this shield blocks this much damage"
     backstab: "バックスタブ" # As in "this dagger does this much backstab damage"
     skills: "スキル"
-#    attack_1: "Deals"
-#    attack_2: "of listed"
-#    attack_3: "weapon damage."
-#    health_1: "Gains"
-#    health_2: "of listed"
-#    health_3: "armor health."
-#    speed_1: "Moves at"
-#    speed_2: "meters per second."
+    attack_1: "取引"
+    attack_2: "リスト"
+    attack_3: "武器攻撃力"
+    health_1: "収益"
+    health_2: "リスト"
+    health_3: "防具防御力"
+    speed_1: "移動速度"
+    speed_2: "メートル/秒"
     available_for_purchase: "購入可能" # Shows up when you have unlocked, but not purchased, a hero in the hero store
     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: "特定のヒーローのみ、このレベルをプレイする事が出来ます。"
@@ -601,7 +601,7 @@
     sub_includes_4: "メールによるサポート"
     sub_includes_5: "10人の新しいヒーローとマスターのユニークなスキル"
     sub_includes_6: "3500のジェムが月々支給されます"
-#    sub_includes_7: "Private Clans"
+    sub_includes_7: "プライベートクラン"
 #    monitor_progress_title: "How do I monitor student progress?"
 #    monitor_progress_1: "Student progress can be monitored by creating a"
 #    monitor_progress_2: "for your class."
@@ -743,8 +743,8 @@
     shift: "シフト"
     run_code: "現在のコードを実行"
     run_real_time: "リアルタイムで実行"
-#    continue_script: "Continue past current script."
-#    skip_scripts: "Skip past all skippable scripts."
+    continue_script: "以前のスクリプトを続ける。"
+    skip_scripts: "過去のスキップできるスクリプトをスキップする。"
     toggle_playback: "トグル:プレイ/ポーズ"
 #    scrub_playback: "Scrub back and forward through time."
 #    single_scrub_playback: "Scrub back and forward through time by a single frame."
@@ -774,49 +774,49 @@
     social_hipchat: "公開されている CodeCombat の HipChat ルームで私たちとチャットする"
     contribute_to_the_project: "プロジェクトに貢献する"

-#  clans:
-#    clan: "Clan"
-#    clans: "Clans"
-#    new_name: "New clan name"
-#    new_description: "New clan description"
-#    make_private: "Make clan private"
-#    subs_only: "subscribers only"
-#    create_clan: "Create New Clan"
-#    private_preview: "Preview"
-#    public_clans: "Public Clans"
-#    my_clans: "My Clans"
-#    clan_name: "Clan Name"
-#    name: "Name"
-#    chieftain: "Chieftain"
-#    type: "Type"
-#    edit_clan_name: "Edit Clan Name"
-#    edit_clan_description: "Edit Clan Description"
-#    edit_name: "edit name"
-#    edit_description: "edit description"
-#    private: "(private)"
-#    summary: "Summary"
-#    average_level: "Average Level"
-#    average_achievements: "Average Achievements"
-#    delete_clan: "Delete Clan"
-#    leave_clan: "Leave Clan"
-#    join_clan: "Join Clan"
-#    invite_1: "Invite:"
-#    invite_2: "*Invite players to this Clan by sending them this link."
-#    members: "Members"
-#    progress: "Progress"
-#    not_started_1: "not started"
-#    started_1: "started"
-#    complete_1: "complete"
-#    exp_levels: "Expand levels"
-#    rem_hero: "Remove Hero"
-#    status: "Status"
-#    complete_2: "Complete"
-#    started_2: "Started"
-#    not_started_2: "Not Started"
-#    view_solution: "Click to view solution."
-#    latest_achievement: "Latest Achievement"
-#    playtime: "Playtime"
-#    last_played: "Last played"
+  clans:
+    clan: "クラン"
+    clans: "クラン"
+    new_name: "新規クラン名"
+    new_description: "新規クラン紹介文"
+    make_private: "クランをプライベートにする"
+    subs_only: "課金者限定"
+    create_clan: "新規クラン作成"
+    private_preview: "プレビュー"
+    public_clans: "公開クラン"
+    my_clans: "マイ・クラン"
+    clan_name: "クラン名"
+    name: "名前"
+    chieftain: "リーダー"
+    type: "タイプ"
+    edit_clan_name: "クラン名の編集"
+    edit_clan_description: "クランの紹介文の編集"
+    edit_name: "名前の編集"
+    edit_description: "紹介文の編集"
+    private: "(プライベート)"
+    summary: "概要"
+    average_level: "平均レベル"
+    average_achievements: "平均業績"
+    delete_clan: "クランの削除"
+    leave_clan: "クランの脱退"
+    join_clan: "クランの参加"
+    invite_1: "招待:"
+    invite_2: "*このリンクを送信してクランに招待してください。"
+    members: "メンバー"
+    progress: "進捗"
+    not_started_1: "未着手"
+    started_1: "進行中"
+    complete_1: "完了"
+    exp_levels: "レベルの拡大"
+    rem_hero: "ヒーローの削除"
+    status: "状態"
+    complete_2: "完了"
+    started_2: "進行中"
+    not_started_2: "未着手"
+    view_solution: "クリックしてソリューションを見る。"
+    latest_achievement: "最新業績"
+    playtime: "プレイ時間"
+    last_played: "最終プレイ"

   classes:
     archmage_title: "アークメイジ"
@@ -1058,7 +1058,7 @@
     winners: "勝者"

   user:
-#    stats: "Stats"
+    stats: "ステータス"
     singleplayer_title: "シングルプレイヤーレベル"
     multiplayer_title: "マルチプレイヤーレベル"
     achievements_title: "アーカイブ"
@@ -1070,8 +1070,8 @@
     no_multiplayer: "まだマルチプレイヤーのゲームをプレイしていません。"
     no_achievements: "まだアーカイブはありません。"
     favorite_prefix: "お気に入りの言語は "
-#    favorite_postfix: "."
-#    not_member_of_clans: "Not a member of any clans yet."
+    favorite_postfix: "。"
+    not_member_of_clans: "まだどのクランにも所属していません。"

 #  achievements:
 #    last_earned: "Last Earned"


finished review of nl.coffee

finished review of nl.coffee

--- app/locale/nl.coffee
+++ app/locale/nl.coffee
@@ -234,7 +234,7 @@
     tip_beta_launch: "CodeCombat lanceerde zijn beta versie in Oktober, 2013."
     tip_js_beginning: "JavaScript is nog maar het begin."
     tip_autocast_setting: "Verander de autocast instelling door te klikken op het tandwiel naast de cast knop."
-    tip_baby_coders: "Zelfs babies zullen in de toekomst Archmages zijn."
+    tip_baby_coders: "Zelfs babies zullen in de toekomst een Tovenaar zijn."
     tip_morale_improves: "Het spel zal blijven laden tot de moreel verbeterd."
     tip_all_species: "Wij geloven in gelijke kansen voor alle wezens om te leren programmeren."
 #    tip_reticulating: "Reticulating spines."
@@ -257,10 +257,10 @@
     article_title: "Artikel Editor"
     article_description: "Schrijf artikels die spelers een overzicht geven over programmeer concepten die kunnen gebruikt worden over een variëteit van levels en campagnes."
     thang_title: "Thang Editor"
-    thang_description: "Maak eenheden, beschrijf hun standaard logica, graphics en audio. Momenteel is enkel het importeren van vector graphics geëxporteerd in Flash ondersteund."
+    thang_description: "Maak eenheden, beschrijf hun standaard logica, graphics en audio. Momenteel is enkel het importeren van vector graphics geëxporteerd uit Flash ondersteund."
     level_title: "Level Editor"
-    level_description: "Bevat het programma om te programmeren, audio te uploaden en aangepaste logica te creëren om alle soorten levels te maken. Het is alles wat wijzelf ook gebruiken!"
-    security_notice: "Veel belangrijke elementen in deze editors zijn momenteel niet actief. Met dat wij de veiligheid van deze systemen verbeteren, zullen ook deze elementen beschikbaar worden. Indien u deze elementen al eerder wil gebruiken, "
+    level_description: "Bevat de benodigdheden om scripts te schrijven, audio te uploaden en aangepaste logica te creëren om alle soorten levels te maken. Het is alles wat wij zelf ook gebruiken!"
+    security_notice: "Veel belangrijke elementen in deze editors zijn momenteel niet actief. Als wij de veiligheid van deze systemen verbeteren, zullen ook deze elementen beschikbaar worden. Indien u deze elementen al eerder wil gebruiken, "
     contact_us: "contacteer ons!"
     hipchat_prefix: "Je kan ons ook vinden in ons"
     hipchat_url: "(Engelstalig) HipChat kanaal."
@@ -277,7 +277,7 @@
     level_tab_thangs_add: "Voeg element toe"
     level_settings_title: "Instellingen"
     level_component_tab_title: "Huidige Componenten"
-    level_component_btn_new: "Maak een nieuw component aan"
+    level_component_btn_new: "Maak een nieuwe component aan"
     level_systems_tab_title: "Huidige Systemen"
     level_systems_btn_new: "Maak een nieuw systeem aan"
     level_systems_btn_add: "Voeg Systeem toe"
@@ -288,7 +288,7 @@
     level_component_settings: "Instellingen"
     level_system_edit_title: "Wijzig Systeem"
     create_system_title: "Maak een nieuw Systeem aan"
-    new_component_title: "Maak een nieuw Component aan"
+    new_component_title: "Maak een nieuwe Component aan"
     new_component_field_system: "Systeem"
     new_article_title: "Maak een Nieuw Artikel"
     new_thang_title: "Maak een Nieuw Thang Type"
@@ -324,7 +324,7 @@
     score: "Score"
     win: "Win"
     loss: "Verlies"
-    tie: "Gelijk"
+    tie: "Gelijkstand"
     easy: "Gemakkelijk"
     medium: "Medium"
     hard: "Moeilijk"
@@ -335,7 +335,7 @@
     who_description_prefix: "hebben samen CodeCombat opgericht in 2013. We creëerden ook "
     who_description_suffix: "en in 2008, groeide het uit tot de #1 web en iOS applicatie om Chinese en Japanse karakters te leren schrijven."
     who_description_ending: "Nu is het tijd om mensen te leren programmeren."
-    why_paragraph_1: "Tijdens het maken van Skritter wist George niet hoe hij moest programmeren en was hij constant gefrustreerd doordat hij zijn ideeën niet kon verwezelijken. Nadien probeerde hij te studeren maar de lessen gingen te traag. Ook zijn huisgenoot wou opnieuw studeren en stopte met lesgeven. Hij probeerde Codecademy maar was al snel \"verveeld\". Iedere week startte een andere vriend met Codecademy, met telkens als resultaat dat hij/zij vrij snel met de lessen stopte. We realiseerden ons dat het hetzelfde probleem was zoals we al eerder hadden opgelost met Skritter: mensen leren iets via langzame en intensieve lessen, terwijl ze het eigenlijk zo snel mogelijk nodig hebben via uitgebreide oefeningen. Wij weten hoe dat op te lossen."
+    why_paragraph_1: "Tijdens het maken van Skritter wist George niet hoe hij moest programmeren en was hij constant gefrustreerd doordat hij zijn ideeën niet kon verwezelijken. Nadien probeerde hij te studeren maar de lessen gingen te traag. Ook zijn huisgenoot wou opnieuw studeren en stopte met lesgeven. Hij probeerde Codecademy maar was al snel \"verveeld\". Iedere week startte een andere vriend met Codecademy, met telkens als resultaat dat hij/zij vrij snel met de lessen stopte. We realiseerden ons dat het hetzelfde probleem was zoals we al eerder hadden opgelost met Skritter: mensen leren iets via langzame en intensieve lessen, terwijl ze eigenlijk beter een snelle en uitgebreide opleiding nodig hebben. Wij weten hoe dat op te lossen."
     why_paragraph_2: "Wil je leren programmeren? Je hebt geen lessen nodig. Je moet vooral veel code schrijven en je amuseren terwijl je dit doet."
     why_paragraph_3_prefix: "Dat is waar programmeren om draait. Het moet tof zijn. Niet tof zoals"
     why_paragraph_3_italic: "joepie een medaille"
@@ -357,13 +357,13 @@
     opensource_intro: "CodeCombat is gratis en volledig open source."
     opensource_description_prefix: "Bekijk "
     github_url: "onze GitHub"
-    opensource_description_center: "en help ons als je wil! CodeCombat is gebouwd met de hulp van duizende open source projecten, en wij zijn er gek van. Bekijk ook "
+    opensource_description_center: "en help ons als je wil! CodeCombat is gebouwd met de hulp van tientallen open source projecten, en wij zijn er gek op. Bekijk ook "
     archmage_wiki_url: "onze Tovenaar wiki"
-    opensource_description_suffix: "voor een lijst van de software dat dit spel mogelijk maakt."
+    opensource_description_suffix: "voor een lijst van de software die dit spel mogelijk maakt."
     practices_title: "Goede Respectvolle gewoonten"
-    practices_description: "Dit zijn onze beloften aan u, de speler, en iets minder juridische jargon."
+    practices_description: "Dit zijn onze beloften aan u, de speler, in een iets minder juridische jargon."
     privacy_title: "Privacy"
-    privacy_description: "We zullen nooit jouw persoonlijke informatie verkopen. We willen geld verdienen dankzij aanwerving in verloop van tijd, maar je mag op je twee oren slapen dat wij nooit jouw persoonlijke informatie zullen verspreiden aan geïnteresseerde bedrijven zonder dat jij daar expliciet mee akkoord gaat."
+    privacy_description: "We zullen nooit jouw persoonlijke informatie verkopen. We willen in verloop van tijd geld verdienen dankzij aanwervingen, maar je mag op je beide oren slapen dat wij nooit jouw persoonlijke informatie zullen verspreiden aan geïnteresseerde bedrijven zonder dat jij daar expliciet mee akkoord gaat."
     security_title: "Beveiliging"
     security_description: "We streven ernaar om jouw persoonlijke informatie veilig te bewaren. Onze website is open en beschikbaar voor iedereen, opdat ons beveiliging systeem kan worden nagekeken en geoptimaliseerd door iedereen die dat wil. Dit alles is mogelijk doordat we volledig open source en transparant zijn."
     email_title: "E-mail"
@@ -375,7 +375,7 @@
     recruitment_title: "Aanwervingen"
     recruitment_description_prefix: "Hier bij CodeCombat, ga je ontplooien tot een krachtige tovenoor-niet enkel virtueel, maar ook in het echt."
     url_hire_programmers: "Niemand kan snel genoeg programmeurs aanwerven"
-    recruitment_description_suffix: "dus eenmaal je jouw vaardigheden hebt aangescherp en ermee akkoord gaat, zullen we jouw beste codeer prestaties voorstellen aan duizenden bedrijven die niet kunnen wachten om jou aan te werven. Zij betalen ons een beetje, maar betalen jou"
+    recruitment_description_suffix: "dus eenmaal je jouw vaardigheden hebt aangescherp en ermee akkoord gaat, zullen we jouw beste programmeer prestaties voorstellen aan duizenden werkgevers die niet kunnen wachten om jou aan te werven. Zij betalen ons een beetje, maar betalen jou"
     recruitment_description_italic: "enorm veel"
     recruitment_description_ending: "de site blijft volledig gratis en iedereen is gelukkig. Dat is het plan."
     copyrights_title: "Auteursrechten en licenties"
@@ -384,25 +384,25 @@
     cla_url: "CLA"
     contributor_description_suffix: "waarmee je moet akkoord gaan voordat wij jouw bijdragen kunnen gebruiken."
     code_title: "Code - MIT"
-    code_description_prefix: "Alle code in het bezit van CodeCombat of aanwezig op codecombat.com, zowel in de GitHub respository of in de codecombat.com database, is erkend onder de"
+    code_description_prefix: "Alle code in het bezit van CodeCombat of aanwezig op codecombat.com, zowel in de GitHub respository als in de codecombat.com database, is erkend onder de"
     mit_license_url: "MIT licentie"
-    code_description_suffix: "Dit geldt ook voor code in Systemen en Componenten dat publiekelijk is gemaakt met als doelstellingen het maken van levels."
+    code_description_suffix: "Dit geldt ook voor code in Systemen en Componenten dat publiek is gemaakt met als doel het maken van levels."
     art_title: "Art/Music - Creative Commons "
     art_description_prefix: "Alle gemeenschappelijke inhoud valt onder de"
     cc_license_url: "Creative Commons Attribution 4.0 Internationale Licentie"
-    art_description_suffix: "Gemeenschappelijke inhoud is alles dat algemeen verkrijgbaar is bij CodeCombat voor het doel levels te maken. Dit omvat:"
+    art_description_suffix: "Gemeenschappelijke inhoud is alles dat algemeen verkrijgbaar is bij CodeCombat met als doel levels te maken. Dit omvat:"
     art_music: "Muziek"
     art_sound: "Geluid"
-    art_artwork: "Artwork"
+    art_artwork: "Kunst"
     art_sprites: "Sprites"
     art_other: "Eender wat en al het creatief werk dat niet als code aanzien wordt en verkrijgbaar is bij het aanmaken van levels."
-    art_access: "Momenteel is er geen universeel en gebruiksvriendelijk systeem voor het ophalen van deze assets. In het algemeen, worden deze opgehaald via de links zoals gebruikt door de website. Contacteer ons voor assitentie, of help ons met de website uit te breiden en de assets bereikbaarder te maken."
+    art_access: "Momenteel is er geen universeel en gebruiksvriendelijk systeem voor het ophalen van deze assets. In het algemeen, worden deze opgehaald via de links zoals gebruikt door de website. Contacteer ons voor assistentie, of help ons met de website uit te breiden en de assets bereikbaarder te maken."
     art_paragraph_1: "Voor toekenning, gelieve de naam en link naar codecombat.com te plaatsen waar dit passend is voor de vorm waarin het voorkomt. Bijvoorbeeld:"
     use_list_1: "Wanneer gebruikt in een film of een ander spel, voeg codecombat.com toe in de credits."
-    use_list_2: "Wanneer toegepast op een website, inclusief een link naar het gebruik, bijvoorbeeld onderaan een afbeelding. Of in een algemene webpagina waar je eventueel ook andere Create Commons werken en open source software vernoemd die je gebruikt op de website. Iets dat alreeds duidelijk is gespecificeerd met CodeCombat, zoals een blog artikel, dat CodeCombat vernoemt, heeft geen aparte vermelding nodig."
-    art_paragraph_2: "Wanneer de gebruikte inhoud is gemaakt door een gebruiker van codecombat.com, vernoem hem/haar in plaats van ons en volg verspreidingsaanwijzingen van die brons als die er zijn."
+    use_list_2: "Wanneer toegepast op een website, inclusief een link naar het gebruik, bijvoorbeeld onderaan een afbeelding. Of in een algemene webpagina waar je eventueel ook andere Creative Commons werken en open source software vernoemd die je gebruikt op de website. Iets dat al duidelijk gerelateerd is met CodeCombat, zoals een blog artikel dat CodeCombat vernoemd, heeft geen aparte vermelding nodig."
+    art_paragraph_2: "Wanneer de gebruikte inhoud is gemaakt door een gebruiker van codecombat.com, vernoem hem/haar in plaats van ons en volg toekenningsaanwijzingen als deze in de beschrijving van de bron staan."
     rights_title: "Rechten Voorbehouden"
-    rights_desc: "Alle rechten zijn voorbehouden voor de Levels. Dit omvat:"
+    rights_desc: "Alle rechten zijn voorbehouden voor de Levels zelf. Dit omvat:"
     rights_scripts: "Scripts"
     rights_unit: "Eenheid Configuratie"
     rights_description: "Beschrijvingen"
@@ -419,15 +419,15 @@
     introduction_desc_intro: "We hebben hoge verwachtingen over CodeCombat."
     introduction_desc_pref: "We willen zijn waar programmeurs van alle niveaus komen om te leren en samen te spelen, anderen introduceren aan de wondere wereld van code, en de beste delen van de gemeenschap te reflecteren. We kunnen en willen dit niet alleen doen; wat projecten zoals GitHub, Stack Overflow en Linux groots en succesvol maken, zijn de mensen die deze software gebruiken en verbeteren. Daartoe, "
     introduction_desc_github_url: "CodeCombat is volledig open source"
-    introduction_desc_suf: ", en we mikken ernaar om zoveel mogelijk manieren mogelijk maken voor u om deel te nemen en dit project van zowel jou als ons te maken."
+    introduction_desc_suf: ", en we streven ernaar om op zoveel mogelijk manieren het mogelijk te maken voor u om deel te nemen en dit project van zowel jou als ons te maken."
     introduction_desc_ending: "We hopen dat je met ons meedoet!"
     introduction_desc_signature: "- Nick, George, Scott, Michael, en Jeremy"
     alert_account_message_intro: "Hallo!"
     alert_account_message_pref: "Om je te abonneren voor de klasse e-mails, moet je eerst "
     alert_account_message_suf: "."
     alert_account_message_create_url: "een account aanmaken"
-    archmage_summary: "Geïnteresserd in werken aan game graphics, user interface design, database- en serverorganisatie, multiplayer networking, physics, geluid of game engine prestaties? Wil jij helpen een game te bouwen wat anderen leert waar jij goed in bent? We moeten nog veel doen en als jij een ervaren programmeur bent en wil ontwikkelen voor CodeCombat, dan is dit de klasse voor jou. We zouden graag je hulp hebben bij het maken van de beste programmeergame ooit."
-    archmage_introduction: "Een van de beste aspecten aan het maken van spelletjes is dat zij zoveel verschillende zaken omvatten. Visualisaties, geluid, real-time netwerken, sociale netwerken, en natuurlijk veel van de voorkomende aspecten van programmeren, van low-level database beheer en server administratie tot gebruiksvriendelijke interfaces maken. Er is veel te doen, en als jij een ervaren programmeur bent met de motivatie om je handen veel te maken met CodeCombat, dan ben je de tovenaar die wij zoeken! We zouden graag jouw help hebben met het bouwen aan het allerbeste programmeerspel ooit."
+    archmage_summary: "Geïnteresserd in het werken aan game graphics, user interface design, database- en serverorganisatie, multiplayer networking, physics, geluid of game engine prestaties? Wil jij helpen een game te bouwen wat anderen leert waar jij goed in bent? We moeten nog veel doen en als jij een ervaren programmeur bent en wil ontwikkelen voor CodeCombat, dan is dit de klasse voor jou. We zouden graag je hulp hebben bij het maken van de beste programmeergame ooit."
+    archmage_introduction: "Een van de beste aspecten aan het maken van spelletjes is dat zij zoveel verschillende zaken omvatten. Visualisaties, geluid, real-time netwerken, sociale netwerken, en natuurlijk enkele veelvoorkomende aspecten van programmeren, van low-level database beheer en server administratie tot gebruiksvriendelijke interfaces maken. Er is veel te doen, en als jij een ervaren programmeur bent met de motivatie om je volledig te verdiepen in de details van CodeCombat, dan ben je de tovenaar die wij zoeken! We zouden graag jouw hulp krijgen bij het bouwen van het allerbeste programmeerspel ooit."
     class_attributes: "Klasse kenmerken"
     archmage_attribute_1_pref: "Ervaring met "
     archmage_attribute_1_suf: ", of de wil om het te leren. De meeste van onze code is in deze taal. Indien je een fan van Ruby of Python bent, zal je je meteen thuis voelen! Het is zoals JavaScript, maar met een mooiere syntax."
@@ -440,12 +440,12 @@
     join_url_email: "E-mail ons"
     join_url_hipchat: "ons publiek (Engelstalig) HipChat kanaal"
     more_about_archmage: "Leer meer over hoe je een Machtige Tovenaar kan worden"
-    archmage_subscribe_desc: "Ontvang e-mails met nieuwe codeer mogelijkheden en aankondigingen." #TODO
-    artisan_summary_pref: "Wil je levels ontwerpen en CodeCombat's arsenaal vergroten? Mensen spelen sneller door onze content dan wij bij kunnen houden! Op dit moment is onze level editor nog wat kaal, dus wees daarvan bewust. Levels maken zal een beetje uitdagend en buggy zijn. Als jij een visie van campagnes hebt van for-loops tot"
+    archmage_subscribe_desc: "Ontvang e-mails met nieuwe programmeer mogelijkheden en aankondigingen."
+    artisan_summary_pref: "Wil je levels ontwerpen en CodeCombat's arsenaal vergroten? Mensen spelen sneller door onze content dan wij bij kunnen houden! Op dit moment is onze level editor nog wat beperkt, dus wees daarvan bewust. Het maken van levels zal een uitdaging zijn met een grote kans op fouten. Als jij een visie van campagnes hebt van for-loops tot"
     artisan_summary_suf: "dan is dit de klasse voor jou."
-    artisan_introduction_pref: "We moeten meer levels bouwen! Mensen schreeuwen om meer inhoud, en er zijn ook maar zoveel levels dat wij kunnen maken. Momenteel is jouw werkplaats level een; onze level editor is amper gebruikt door zelfs ons, wees dus voorzichtig. Indien je visioenen hebt van campagnes, gaande van for-loops tot"#TODO
+    artisan_introduction_pref: "We moeten meer levels bouwen! Mensen schreeuwen om meer inhoud, en er zijn ook maar zoveel levels dat wij kunnen maken. Momenteel is jouw werkplaats level een; onze level editor wordt zelfs door ons amper gebruikt, dus wees voorzichtig. Indien je een visie hebt van een campagne, gaande van for-loops tot"
     artisan_introduction_suf: "dan is deze klasse waarschijnlijk iets voor jou."
-    artisan_attribute_1: "Enige ervaring in het maken van vergelijkbare inhoud. Bijvoorbeeld ervaring het gebruiken van Blizzard's level editor. Maar dit is niet vereist!"
+    artisan_attribute_1: "Enige ervaring in het maken van vergelijkbare inhoud. Bijvoorbeeld ervaring in het gebruiken van Blizzard's level editor. Maar dit is niet vereist!"
     artisan_attribute_2: "Tot in het detail testen en opnieuw proberen staat voor jou gelijk aan plezier. Om goede levels te maken, moet je het door anderen laten spelen en bereid zijn om een hele boel aan te passen."
     artisan_attribute_3: "Momenteel heb je nog veel geduld nodig, doordat onze editor nog vrij ruw is en op je zenuwen kan werken. Samenwerken met een Avonturier kan jou ook veel helpen."
     artisan_join_desc: "Gebruik de Level Editor min of meer in deze volgorde:"
@@ -455,18 +455,18 @@
     artisan_join_step4: "Maak een bericht over jouw level op ons forum voor feedback."
     more_about_artisan: "Leer meer over hoe je een Creatieve Ambachtsman kan worden."
     artisan_subscribe_desc: "Ontvang e-mails met nieuws over de Level Editor."
-    adventurer_summary: "Laten we duidelijk zijn over je rol: jij bent de tank. Jij krijgt de zware klappen te verduren. We hebben mensen nodig om spiksplinternieuwe levels te proberen en te kijken hoe deze beter kunnen. De pijn zal groot zijn, het maken van een goede game is een lang proces en niemand doet het de eerste keer goed. Als jij dit kan verduren en een hoge constitution score hebt, dan is dit de klasse voor jou."
-    adventurer_introduction: "Laten we duidelijk zijn over je rol: jij bent de tank. Jij krijgt de zware klappen te verduren. We hebben mensen nodig om spiksplinternieuwe levels uit te proberen en te kijken hoe deze beter kunnen. Je zult veel afzien.Het maken van een goede game is een lang proces en niemand doet het de eerste keer goed. Als jij dit kan verduren en een hoge constitution score hebt, dan is dit de klasse voor jou."
+    adventurer_summary: "Laten we duidelijk zijn over je rol: jij bent de tank. Jij krijgt de zware klappen te verduren. We hebben mensen nodig om spiksplinternieuwe levels te proberen en te kijken hoe deze beter kunnen. Je zult veel afzien, want het maken van een goede game is een lang proces en niemand doet het de eerste keer goed. Als jij dit kan verduren en een hoog uihoudingsvermogen hebt, dan is dit de klasse voor jou."
+    adventurer_introduction: "Laten we duidelijk zijn over je rol: jij bent de tank. Jij krijgt de zware klappen te verduren. We hebben mensen nodig om spiksplinternieuwe levels uit te proberen en te kijken hoe deze beter kunnen. Je zult veel afzien.Het maken van een goede game is een lang proces en niemand doet het de eerste keer goed. Als jij dit kan verduren en een hoog uihoudingsvermogen hebt, dan is dit de klasse voor jou."
     adventurer_attribute_1: "Een wil om te leren. Jij wilt leren hoe je programmeert en wij willen het jou leren. Je zal overigens zelf het meeste leren doen."
     adventurer_attribute_2: "Charismatisch. Wees netjes maar duidelijk over wat er beter kan en geef suggesties over hoe het beter kan."
-    adventurer_join_pref: "Werk samen met een Ambachtsman of recruteer er een, of tik het veld hieronder aan om e-mails te ontvangen wanneer er nieuwe levels zijn om te testen. We zullen ook posten over levels die beoordeeld moeten worden op onze netwerken zoals" #TODO
+    adventurer_join_pref: "Werk samen met een Ambachtsman of recruteer er een, of tik het veld hieronder aan om e-mails te ontvangen wanneer er nieuwe levels zijn om te testen. We zullen ook berichten over levels die beoordeeld moeten worden op onze netwerken zoals"
     adventurer_forum_url: "ons forum"
     adventurer_join_suf: "dus als je liever op deze manier wordt geïnformeerd, schrijf je daar in!"
     more_about_adventurer: "Leer meer over hoe je een Dappere Avonturier kunt worden."
     adventurer_subscribe_desc: "Ontvang e-mails wanneer er nieuwe levels zijn die getest moeten worden."
-    scribe_summary_pref: "CodeCombat is meer dan slechts een aantal levels, het zal ook een bron van kennis zijn en een wiki met programmeerconcepten waar levels op in kunnen gaan. Op die manier zal een Ambachtslied een link kunnen geven naar een artikel dat past bij een level. Net zoiets als het  " #TODO
+    scribe_summary_pref: "CodeCombat is meer dan slechts een aantal levels, het zal ook een bron van kennis zijn die spelers kunnen nakijken. Op die manier zal een Ambachtsman een link kunnen geven naar een artikel dat past bij een level. Net zoiets als het  "
     scribe_summary_suf: " heeft gebouwd. Als jij het leuk vindt programmeerconcepten uit te leggen, dan is deze klasse iets voor jou."
-    scribe_introduction_pref: "CodeCombat is meer dan slechts een aantal levels, het zal ook een bron van kennis zijn en een wiki met programmeerconcepten waar levels op in kunnen gaan. Op die manier zal elk Ambachtslied niet in detail hoeven uit te leggen wat een vergelijkingsoperator is, maar een link kunnen geven naar een artikel wat deze informatie bevat voor de speler. Net zoiets als het  "
+    scribe_introduction_pref: "CodeCombat is meer dan slechts een aantal levels, het zal ook een bron van kennis zijn en een wiki met programmeerconcepten waar levels op in kunnen gaan. Op die manier zal niet elke Ambachtsman in detail hoeven uit te leggen wat een vergelijkingsoperator is, maar een link kunnen geven naar een artikel die deze informatie al verduidelijkt voor speler. Net zoiets als het  "
     scribe_introduction_url_mozilla: "Mozilla Developer Network"
     scribe_introduction_suf: " heeft gebouwd. Als jij het leuk vindt om programmeerconcepten uit te leggen in Markdown-vorm, dan is deze klasse wellicht iets voor jou."
     scribe_attribute_1: "Taalvaardigheid is praktisch alles wat je nodig hebt. Je moet niet enkel bedreven zijn in grammatica en spelling, maar ook moeilijke ideeën kunnen overbrengen aan anderen."
@@ -474,22 +474,22 @@
     scribe_join_description: "vertel ons wat over jezelf, je ervaring met programmeren en over wat voor soort dingen je graag zou schrijven. Verder zien we wel!"
     more_about_scribe: "Leer meer over het worden van een ijverige Klerk."
     scribe_subscribe_desc: "Ontvang e-mails met aankondigingen over het schrijven van artikelen."
-    diplomat_summary: "Er is grote interesse in CodeCombat in landen waar geen Engels wordt gesproken! We zijn op zoek naar vertalers die tijd willen spenderen aan het vertalen van de site's corpus aan woorden zodat CodeCombat zo snel mogelijk toegankelijk wordt voor heel de wereld. Als jij wilt helpen om CodeCombat internationaal maken, dan is dit de klasse voor jou."
+    diplomat_summary: "Er is grote interesse voor CodeCombat in landen waar geen Engels wordt gesproken! We zijn op zoek naar vertalers die tijd willen spenderen aan het vertalen van de site's corpus aan woorden zodat CodeCombat zo snel mogelijk toegankelijk wordt voor de hele wereld. Als jij wilt helpen om CodeCombat internationaal maken, dan is dit de klasse voor jou."
     diplomat_introduction_pref: "Dus, als er iets is wat we geleerd hebben van de "
     diplomat_launch_url: "release in oktober"
-    diplomat_introduction_suf: "dan is het wel dat er een significante interesse is in CodeCombat in andere landen, vooral Brazilië! We zijn een corps aan vertalers aan het creëren dat ijverig de ene set woorden in de andere omzet om CodeCombat zo toegankelijk mogelijk te maken in de hele wereld. Als jij het leuk vindt glimpsen op te vangen van aankomende content en deze levels zo snel mogelijk naar je landgenoten te krijgen, dan is dit de klasse voor jou."
-    diplomat_attribute_1: "Vloeiend Engels en de taal waar naar je wilt vertalen kunnen spreken. Wanneer je moeilijke ideeën wilt overbrengen, is het belangrijk beide goed te kunnen!"
+    diplomat_introduction_suf: "dan is het wel dat er een enorme belangstelling is voor CodeCombat in andere landen, vooral Brazilië! We zijn een groep van vertalers aan het creëren dat ijverig de ene set woorden in de andere omzet om CodeCombat zo toegankelijk mogelijk te maken in de hele wereld. Als jij het leuk vindt glimpsen op te vangen van aankomende content en deze levels zo snel mogelijk naar je landgenoten te krijgen, dan is dit de klasse voor jou."
+    diplomat_attribute_1: "Vloeiend Engels en de taal waar naar je wilt vertalen kunnen spreken. Wanneer je moeilijke ideeën wilt overbrengen, is het belangrijk beide talen goed te begrijpen!"
     diplomat_join_pref_github: "Vind van jouw taal het locale bestand "
     diplomat_github_url: "op GitHub"
     diplomat_join_suf_github: ", edit het online, en submit een pull request. Daarnaast kun je hieronder aanvinken als je up-to-date wilt worden gehouden met nieuwe internationalisatie-ontwikkelingen."
     more_about_diplomat: "Leer meer over het worden van een geweldige Diplomaat"
     diplomat_subscribe_desc: "Ontvang e-mails over i18n ontwikkelingen en levels om te vertalen."
     ambassador_summary: "We proberen een gemeenschap te bouwen en elke gemeenschap heeft een supportteam nodig wanneer er problemen zijn. We hebben chats, e-mails en sociale netwerken zodat onze gebruikers het spel kunnen leren kennen. Als jij mensen wilt helpen betrokken te raken, plezier te hebben en wat te leren programmeren, dan is dit wellicht de klasse voor jou."
-    ambassador_introduction: "We zijn een gemeenschap aan het uitbouwen, en jij maakt er deel van uit. We hebben Olark chatkamers, emails, en sociale netwerken met veel andere mensen waarmee je kan praten en hulp kan vragen over het spel en om bij te leren. Als jij mensen wil helpen en te werken nabij de hartslag van CodeCombat in het bijsturen van onze toekomstvisie, dan is dit de geknipte klasse voor jou!"
+    ambassador_introduction: "We zijn een gemeenschap aan het uitbouwen, en jij maakt er deel van uit. We hebben Olark chatkamers, emails, en sociale netwerken met veel andere mensen waarmee je kan praten en hulp aan kan vragen over het spel of om bij te leren. Als jij mensen wil helpen en te werken nabij de hartslag van CodeCombat in het bijsturen van onze toekomstvisie, dan is dit de geknipte klasse voor jou!"
     ambassador_attribute_1: "Communicatieskills. Problemen die spelers hebben kunnen identificeren en ze helpen deze op te lossen. Verder zul je ook de rest van ons geïnformeerd houden over wat de spelers zeggen, wat ze leuk vinden, wat ze minder vinden en waar er meer van moet zijn!"
     ambassador_join_desc: "vertel ons wat over jezelf, wat je hebt gedaan en wat je graag zou doen. We zien verder wel!"
     ambassador_join_note_strong: "Opmerking"
-    ambassador_join_note_desc: "Een van onze topprioriteiten is om een multiplayer te bouwen waar spelers die moeite hebben een level op te lossen een wizard met een hoger level kunnen oproepen om te helpen. Dit zal een goede manier zijn voor ambassadeurs om hun ding te doen. We houden je op de hoogte!"
+    ambassador_join_note_desc: "Een van onze topprioriteiten is om een multiplayer te bouwen waar spelers die moeite hebben een level op te lossen een tovenaar met een hoger level kunnen oproepen om te helpen. Dit zal een goede manier zijn voor ambassadeurs om hun ding te doen. We houden je op de hoogte!"
     more_about_ambassador: "Leer meer over het worden van een behulpzame Ambassadeur"
     ambassador_subscribe_desc: "Ontvang e-mails met updates over ondersteuning en multiplayer-ontwikkelingen."
     counselor_summary: "Geen van de rollen hierboven in jouw interessegebied? Maak je geen zorgen, we zijn op zoek naar iedereen die wil helpen met het ontwikkelen van CodeCombat! Als je geïnteresseerd bent in lesgeven, gameontwikkeling, open source management of iets anders waarvan je denkt dat het relevant voor ons is, dan is dit de klasse voor jou."
@@ -510,7 +510,7 @@
   classes:
     archmage_title: "Tovenaar"
     archmage_title_description: "(Programmeur)"
-    artisan_title: "Ambachtsman" #ambachtsman / ambachtslied ?
+    artisan_title: "Ambachtsman"
     artisan_title_description: "(Level Bouwer)"
     adventurer_title: "Avonturier"
     adventurer_title_description: "(Level Tester)"
@@ -551,7 +551,7 @@
     tutorial_play: "Speel de Tutorial"
     tutorial_recommended: "Aanbevolen als je nog niet eerder hebt gespeeld"
     tutorial_skip: "Sla Tutorial over"
-    tutorial_not_sure: "Niet zeker wat er aan de gang is?"
+    tutorial_not_sure: "Niet zeker wat er aan de hand is?"
     tutorial_play_first: "Speel eerst de Tutorial."
     simple_ai: "Simpele AI"
     warmup: "Opwarming"
@@ -561,12 +561,12 @@
     introducing_dungeon_arena: "Introductie van Dungeon Arena"
     new_way: "17 maart, 2014: De nieuwe manier om te concurreren met code."
     to_battle: "Naar het slagveld, ontwikkelaars!"
-    modern_day_sorcerer: "Kan jij programmeren? Hoe stoer is dat. Jij bent een modere voetballer! is het niet tijd dat je jouw magische krachten gebruikt voor het controlleren van jou minions in het slagveld? En nee, we praten hier niet over robots." #TODO
-    arenas_are_here: "CodeCombat head-to-head multiplayer arenas are here."
-    ladder_explanation: "Kies jouw helden, betover jouw mens of ogre legers, en beklim jouw weg naar de top in de ladder, door het verslagen van vriend en vijand. Daag nu je vrienden uit in multiplayer coding arenas en verkrijg faam en glorie. Indien je creatief bent, kan je zelfs"
+    modern_day_sorcerer: "Kan jij programmeren? Dat is pas stoer. Jij bent een modere tovenaar! Is het niet tijd dat je jouw magische krachten gebruikt voor het besturen van jou minions in het slagveld? En nee, we praten hier niet over robots."
+    arenas_are_here: "CodeCombat's kop aan kop multiplayer arena's zijn er."
+    ladder_explanation: "Kies jouw helden, betover jouw mensen of ogre legers, en beklim jouw weg naar de top in de ladder, door het verslagen van vriend en vijand. Daag nu je vrienden uit in de multiplayer programmeer arena's en verdien eeuwige roem. Indien je creatief bent, kan je zelfs"
     fork_our_arenas: "onze arenas forken"
     create_worlds: "en jouw eigen werelden creëren."
-    javascript_rusty: "Jouw JavaScript is een beetje roest? Wees niet bang, er is een"
+    javascript_rusty: "Jouw JavaScript is een beetje roestig? Wees niet bang, er is een"
     tutorial: "tutorial"
     new_to_programming: ". Ben je net begonnen met programmeren? Speel dan eerst onze beginners campagne."
     so_ready: "Ik ben hier zo klaar voor"


Update vi.coffee

Update vi.coffee

--- app/locale/vi.coffee
+++ app/locale/vi.coffee
@@ -7,7 +7,7 @@
 #    old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari
 #    old_browser_suffix: "You can try anyway, but it probably won't work."
 #    campaign: "Campaign"
-#    for_beginners: "For Beginners"
+    for_beginners: "Dành cho người bắt đầu chơi"
 #    multiplayer: "Multiplayer" # Not currently shown on home page
 #    for_developers: "For Developers" # Not currently shown on home page.


Enabling translations

Enabling translations

--- app/locale/vi.coffee
+++ app/locale/vi.coffee
@@ -94,7 +94,7 @@
 #    campaign_classic_algorithms: "Classic Algorithms"
 #    campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science."

-#  share_progress_modal:
+  share_progress_modal:
     blurb: "Bạn đang tiến bộ rõ rệt đấy! Hãy kể cho ai đó xem bạn đã học được nhiều chừng nào với CodeCombat."
     email_invalid: "Địa chỉ email không hợp lệ."
 #    form_blurb: "Enter their email below and we’ll show them!"
@@ -357,7 +357,7 @@
 #    multiplayer_caption: "Play with friends!"
 #    auth_caption: "Save your progress."

-#  leaderboard:
+  leaderboard:
     leaderboard: "Bảng xếp hạng"
 #    view_other_solutions: "View Other Solutions"
 #    scores: "Scores"
@@ -371,7 +371,7 @@
     difficulty: "Độ khó"
 #    gold_collected: "Gold Collected"

-#  inventory:
+  inventory:
 #    choose_inventory: "Equip Items"
     equipped_item: "Đã trang bị"
 #    required_purchase_title: "Required"
@@ -384,7 +384,7 @@
 #    equip: "Equip"
 #    unequip: "Unequip"

-#  buy_gems:
+  buy_gems:
 #    few_gems: "A few gems"
 #    pile_gems: "Pile of gems"
 #    chest_gems: "Chest of gems"
@@ -397,7 +397,7 @@
 #    recovered: "Previous gems purchase recovered. Please refresh the page."
 #    or_subscribe: "Or 3500 gems/mo if you..."

-#  subscribe:
+  subscribe:
 #    comparison_blurb: "Sharpen your skills with a CodeCombat subscription!"
 #    feature1: "60+ basic levels across 4 worlds"
 #    feature2: "7 powerful new heroes with unique skills!"
@@ -433,7 +433,7 @@
 #    subscription_required_to_play: "You'll need a subscription to play this level."
 #    unlock_help_videos: "Subscribe to unlock all video tutorials."

-#  choose_hero:
+  choose_hero:
 #    choose_hero: "Choose Your Hero"
     programming_language: "Ngôn ngữ lập trình"
     programming_language_description: "Bạn muốn sử dụng ngôn ngữ lập trình nào ?"
@@ -639,7 +639,7 @@
 #    sample_profile: "See a sample profile"
 #    view_profile: "View Your Profile"

-#  keyboard_shortcuts:
+  keyboard_shortcuts:
 #    keyboard_shortcuts: "Keyboard Shortcuts"
 #    space: "Space"
     enter: "Enter"
@@ -1114,7 +1114,7 @@
 #    license: "license"
 #    oreilly: "ebook of your choice"

-#  account_profile:
+  account_profile:
 #    settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section.
 #    edit_profile: "Edit Profile"
 #    done_editing: "Done Editing"


fix browser example

fix browser example

--- examples/browser.coffee
+++ examples/browser.coffee
@@ -31,7 +31,7 @@

 # create an input field. it will reflect the current search
 i = text_input()
-search.onValue (v) -> i.set_value v
+search.onValue (v) -> i.set v

 # the value of our form is the latest user input or clicked search
 input_search = search.merge(i.changes()).toProperty()


Guard against missing location in show2

Guard against missing location in show2

--- apps/show2/helpers/view_helpers.coffee
+++ apps/show2/helpers/view_helpers.coffee
@@ -173,7 +173,7 @@
       url: "#{sd.APP_URL}#{show.href}"
       startDate: (new Date(show.start_at)).toISOString()
       endDate: (new Date(show.end_at)).toISOString()
-      location: @toJSONLDLocation(show.location)
+      location: @toJSONLDLocation(show.location) if show.location
       performer: @performers(show)
     }

@@ -226,10 +226,10 @@

   singleLine: (location) ->
     _.compact([
-      location.city or ''
+      location?.city or ''
       _.compact([
-        location.address or ''
-        location.address_2 or ''
+        location?.address or ''
+        location?.address_2 or ''
       ]).join(' ')
     ]).join(', ')


Added plumbing for dashboard specific js

--- javascripts/lib.coffee
+++ javascripts/lib.coffee
@@ -10,6 +10,7 @@
   str += " }"
   str
 window.ttm ||= {}
+window.ttm.dashboard ||= {}
 window.ttm.decorators ||= {}
 window.ttm.lib ||= {}


activity: define `MessageEventHelper::removeLike` method

--- client/Social/Activity/messageeventhelper.coffee
+++ client/Social/Activity/messageeventhelper.coffee
@@ -16,3 +16,14 @@

     message.emit "LikeAdded"
     message.emit "update"
+
+
+  removeLike: (message, {accountOldId, count}) ->
+
+    {like} = message.interactions
+
+    like.actorsCount   = count
+    like.actorsPreview = like.actorsPreview.filter (id) -> id isnt accountOldId
+
+    message.emit "LikeRemoved"
+    message.emit "update"


activity: fix reference error for mostRecentPath

--- client/Social/Activity/views/activitypane.coffee
+++ client/Social/Activity/views/activitypane.coffee
@@ -192,7 +192,7 @@
     currentPath    = router.getCurrentPath()
     mostRecentPath = '/Activity/Public/Recent'

-    router.handleRoute mostRecent  unless currentPath is mostRecentPath
+    router.handleRoute mostRecentPath  unless currentPath is mostRecentPath

     @mostRecent.listController.addItem message, index


Client: Pressing Escape in a PM closes it.

--- client/app/js/views/private_messages_view.coffee
+++ client/app/js/views/private_messages_view.coffee
@@ -2,7 +2,8 @@
   messageTemplate: JST['private_message']

   events:
-    "keypress .chat_input"  : "sendMessageEvent"
+    "keypress .chat_input"  : "keyPressEvent"
+    "keyup .chat_input"  : "keyUpEvent"
     "click .challenge_button, .cancel_challenge" : "toggleChallengeEvent"
     "click .popup_messages" : "focusChatEvent"
     "click .title_minimize" : "minimizePopupEvent"
@@ -184,7 +185,7 @@
   # EVENTS #
   ##########

-  sendMessageEvent: (e) =>
+  keyPressEvent: (e) =>
     switch e.which
       when 13 # [ Enter ]
         $input = $(e.currentTarget)
@@ -195,6 +196,11 @@
         message.add("Me", text)
         $input.val('')

+  keyUpEvent: (e) =>
+    switch e.which
+      when 27 # [ Esc ]
+        @closePopupEvent(e)
+
   minimizePopupEvent: (e) =>
     message = @messageFromPopup(e.currentTarget)
     message.trigger('minimize', message)


test/virtualmachines: check for machine specs list

--- client/test/lib/dashboard/virtualmachines.coffee
+++ client/test/lib/dashboard/virtualmachines.coffee
@@ -39,6 +39,7 @@
     virtualMachineSelector = '.HomeAppView--section.virtual-machines'
     runningVMSelector = "#{virtualMachineSelector} .MachinesListItem-machineLabel.Running"
     machineDetailSelector = '.MachinesListItem-machineDetails'
+    machineDetailSpecsList = "#{machineDetailSelector} .MachineDetails-SpecsList"
     vmPowerSelector = "#{machineDetailSelector} .MachineDetails div.GenericToggler:nth-of-type(2)"
     alwaysOnSelector = "#{machineDetailSelector} .MachineDetails div.GenericToggler:nth-of-type(3)"
     vmSharingSelector = "#{machineDetailSelector} .MachineDetails div.GenericToggler:nth-of-type(4)"
@@ -67,6 +68,10 @@
       .waitForElementVisible runningVMSelector, 20000
       .click runningVMSelector
       .waitForElementVisible machineDetailSelector, 20000
+
+    # check for specsList
+    browser
+      .waitForElementVisible machineDetailSpecsList, 20000

     # check for alwaysOnToggle
     browser


Test/Onboarding fix css selector

Test/Onboarding fix css selector

--- client/test/lib/helpers/onboardinghelpers.coffee
+++ client/test/lib/helpers/onboardinghelpers.coffee
@@ -10,7 +10,7 @@
 installKDLink     = "#{WelcomeView} ul.bullets li:nth-of-type(5)"
 headerSelector    = '.HomeAppView--sectionHeader'
 stackEditor       = '.StackEditor-OnboardingModal'
-teamSettings      = '.HomeAppView--section.send-invites'
+sendInvites      = '.HomeAppView--section.send-invites'
 kodingUtilities   = '.HomeAppView--section.kd-cli'

 menuSelector      = '.SidebarMenu.kdcontextmenu .kdlistitemview-contextitem.default'


Load CDN config

Load CDN config

--- server/lib/_settings.coffee
+++ server/lib/_settings.coffee
@@ -12,14 +12,20 @@

 		ServiceConfiguration.configurations.insert config

+configCDN = (settings) ->
+	if settings.CDN_PREFIX?
+		WebAppInternals.setBundledJsCssPrefix settings.CDN_PREFIX
+
+
 Settings.find().observe
 	added: (settings) ->
 		Meteor.settings = settings
 		configLoginServices settings
 		loadEnvConfigs settings
+		configCDN settings

 	changed: (settings) ->
 		Meteor.settings = settings
 		configLoginServices settings
 		loadEnvConfigs settings
-
+		configCDN settings


-- Added TCFooter examples page to the nav -- Moved MenuBar examples nav item to Navbar group

--- components/ExampleNav/ExampleNavContainer.coffee
+++ components/ExampleNav/ExampleNavContainer.coffee
@@ -10,7 +10,9 @@
     'QuickLinksExample',
     'SearchSuggestionsExamples',
     'SearchBarExample',
-    'NavbarExample'
+    'NavbarExample',
+    'TCFooterExamples',
+    'MenuBarExamples'
   ]
   FileUploader: [
     'FileUploaderContainerExamples'
@@ -24,8 +26,7 @@
     'ImageViewerExamples'
     'LoaderExamples',
     'PanelExamples',
-    'StandardListItemExamples',
-    'MenuBarExamples'
+    'StandardListItemExamples'
   ]
   ManageSteps: [
     'ManageStepsExamples'


view is easier to manage as a class

view is easier to manage as a class

--- src/js/peteshow-view.coffee
+++ src/js/peteshow-view.coffee
@@ -1,10 +1,61 @@
+_ = require('underscore')
 indexTemplate = require('../templates/index.hbs')
+store = require('./peteshow-storage')

-PeteshowView =
+class PeteshowView
+  controller: require('./peteshow-controller')
+  _position: store.get('position') || {x:0, y:0}
+  _events: {}
+
+  constructor: ->
+    console.log("PeteshowView::init")
+    @_events =
+      '#fill-out-forms' : @controller.fillOutForms
+      '#fill-out-forms-and-submit' : @controller.fillOutFormsAndSubmit
+      '#peteshow-toggle': @show
+      '#peteshow-hide': @hide
+
+  _createEvents: (events) ->
+    for key, value of events
+      $(key).on 'click', (e) =>
+        e.preventDefault()
+        e.stopPropagation()
+        events["##{e.target.id}"]() unless @dragging
+
+    __handleDragMove = _.throttle(@_handleDragMove, 10)
+    __handleDragDown = _.debounce(@_handleDragDown, 100)
+    __handleDragUp = _.debounce(@_handleDragUp, 100)
+
+    $('#peteshow').on 'mousedown', __handleDragDown
+    $(document)
+      .on 'mousemove', __handleDragMove
+      .on 'mouseup', __handleDragUp
+
+  _handleDragUp: =>
+    @dragging = false
+    document.onmousedown= -> return false
+    store.set('position', @_position)
+
+  _handleDragDown: =>
+    @dragging = true
+    document.onmousedown= -> return true
+
+  _handleDragMove: (e) =>
+    if @dragging
+      @_position.x = e.pageX
+      @_position.y = e.pageY
+      @_positionWindow(@_position)
+
   render: ->
     console.log('PeteshowView::render')
     template = indexTemplate()
     $('body').append(template)
+    @_positionWindow()
+    @_createEvents(@_events)
+
+  _positionWindow: (position) ->
+    position ?= @_position
+    $('#peteshow').css(left: position.x, top: position.y)

   show: ->
     console.log('PeteshowView::show')
@@ -17,23 +68,4 @@

   destroy: -> console.log('PeteshowView::destroy')

-PeteshowView.controller = require('./peteshow-controller')
-
-events =
-  '#fill-out-forms' : PeteshowView.controller.fillOutForms
-  '#fill-out-forms-and-submit' : PeteshowView.controller.fillOutFormsAndSubmit
-  '#peteshow-toggle': PeteshowView.show
-  '#peteshow-hide': PeteshowView.hide
-
-createEvents = (events) ->
-  for key, value of events
-    $("body").on 'click', key, (e) =>
-      e.preventDefault()
-      e.stopPropagation()
-      events["##{e.target.id}"]()
-
-# Constructor
-console.log("PeteshowView::init")
-createEvents(events)
-
-module.exports = PeteshowView
+module.exports = new PeteshowView()


Fix doc deployment.

Fix doc deployment.

--- docs/_compile_docs.cjsx
+++ docs/_compile_docs.cjsx
@@ -11,6 +11,10 @@

 source_directory = __dirname
 build_directory = __dirname
+
+project_directory = path.join(__dirname, '..')
+asset_source_dir = asset_cache_dir = asset_dest_dir = path.join(__dirname, '_assets')
+
 if for_deploy
     build_directory = path.join(build_directory, '.build')
     if fs.existsSync(build_directory)
@@ -20,11 +24,10 @@
     unless fs.existsSync(deploy_config_path)
         throw new Error('You need a .env.json with the necessary config.')
     deploy_config = JSON.parse(fs.readFileSync(deploy_config_path).toString())
-    global.ASSET_URL = '/assets/'
+    global.ASSET_URL = '/_assets/'
 else
     global.ASSET_URL = './_assets/'
-project_directory = path.join(__dirname, '..')
-asset_source_dir = asset_cache_dir = asset_dest_dir = path.join(__dirname, '_assets')
+

 _writeFile = require('../compiler/writeFile')(build_directory)
 _emitFile = require('../compiler/emitFile')(
@@ -189,61 +192,55 @@
         asset               : path.join(asset_source_dir, 'style.sass')
         project_directory   : project_directory
         callback: ->
-            processAsset
-                asset_source_dir    : asset_source_dir
-                asset_cache_dir     : asset_cache_dir
-                asset_dest_dir      : asset_dest_dir
-                asset               : path.join(asset_source_dir, 'script.coffee')
-                project_directory   : project_directory
-                callback: ->
-                    global.build_info =
-                        project_directory       : project_directory
-                        commit                  : commit_sha
-                        date                    : new Date()
-                        build_directory         : build_directory
-                        asset_cache_directory   : asset_cache_dir
-
-                    doc_files = fs.readdirSync(source_directory).filter (f) ->
-                        f.split('.').pop() is 'md' and f isnt 'index.md'
-                    doc_files.unshift('index.md')
-                    doc_files.forEach (f) ->
-                        title = f.replace(/\.md$/, '')
-                        slug = slugify(title)
-                        if for_deploy and f isnt 'index.md'
-                            output_name = slug
-                        else
-                            output_name = "#{ slug }.html"
-
-                        if f is 'index.md'
-                            title = "Marquee Static SDK, v#{ pkg.version }"
-
-                        if for_deploy and deploy_config.PREFIX
-                            output_name = path.join(deploy_config.PREFIX, output_name)
-                        _emitFile(
-                            output_name,
-                            <Base title=title>
-

-
-

-

-                                    <MarkdownPage file={path.join(source_directory, f)} />
-

-                            </Base>
-                        )
-
-                    if for_deploy
-                        s3 = new AWS.S3
-                            accessKeyId     : deploy_config.AWS_ACCESS_KEY_ID
-                            secretAccessKey : deploy_config.AWS_SECRET_ACCESS_KEY
-
-                        walkSync(build_directory).forEach (file) ->
-                            s3_options =
-                                Bucket          : deploy_config.AWS_BUCKET
-                                Key             : file.replace(build_directory + '/', '')
-                                ACL             : 'public-read'
-                                ContentType     : mime.lookup(file)
-                                StorageClass    : 'REDUCED_REDUNDANCY'
-                                Body            : fs.readFileSync(file)
-                            console.log '\tUploading', s3_options.Key
-                            s3.putObject s3_options, (err, data) ->
-                                throw err if err
+
+            global.build_info =
+                project_directory       : project_directory
+                commit                  : commit_sha
+                date                    : new Date()
+                build_directory         : build_directory
+                asset_cache_directory   : asset_cache_dir
+
+            doc_files = fs.readdirSync(source_directory).filter (f) ->
+                f.split('.').pop() is 'md' and f isnt 'index.md'
+            doc_files.unshift('index.md')
+            doc_files.forEach (f) ->
+                title = f.replace(/\.md$/, '')
+                slug = slugify(title)
+                if for_deploy and f isnt 'index.md'
+                    output_name = slug
+                else
+                    output_name = "#{ slug }.html"
+
+                if f is 'index.md'
+                    title = "Marquee Static SDK, v#{ pkg.version }"
+
+                if for_deploy and deploy_config.PREFIX
+                    output_name = path.join(deploy_config.PREFIX, output_name)
+                _emitFile(
+                    output_name,
+                    <Base title=title>
+

+
+

+

+                            <MarkdownPage file={path.join(source_directory, f)} />
+

+                    </Base>
+                )
+
+            if for_deploy
+                s3 = new AWS.S3
+                    accessKeyId     : deploy_config.AWS_ACCESS_KEY_ID
+                    secretAccessKey : deploy_config.AWS_SECRET_ACCESS_KEY
+
+                walkSync(build_directory).forEach (file) ->
+                    s3_options =
+                        Bucket          : deploy_config.AWS_BUCKET
+                        Key             : file.replace(build_directory + '/', '')
+                        ACL             : 'public-read'
+                        ContentType     : mime.lookup(file)
+                        StorageClass    : 'REDUCED_REDUNDANCY'
+                        Body            : fs.readFileSync(file)
+                    console.log '\tUploading', s3_options.Key
+                    s3.putObject s3_options, (err, data) ->
+                        throw err if err


Fix redirect

Fix redirect

--- apps/partner/client/router.coffee
+++ apps/partner/client/router.coffee
@@ -25,7 +25,7 @@
   initialize: ({ @profile, @partner }) ->
     @baseView = new PartnerView el: $('#partner'), model: @profile, partner: @partner, currentSection: sd.SECTION
     mediator.on 'change:route', (route) =>
-      @navigate("#{@partner.id}/" + route, { trigger: true, replace: true })
+      @navigate "#{@partner.href()}/#{route}", trigger: true, replace: true

   index: ->
     section = 'overview' # default for galleries


fixes coffee syntax error

fixes coffee syntax error

--- drivers/javascript/src/errors.coffee
+++ drivers/javascript/src/errors.coffee
@@ -48,7 +48,7 @@
                         composeTerm(arg)

         optargs = {}
-        for own key,arg in term.optargs
+        for own key,arg of term.optargs
             optargs[key] = if key == argNum
                              composeCarrots(arg, frames)
                            else