payment: emit "PasswordRecoveryToken" through workflow

--- client/Main/payment/paymentcontroller.coffee
+++ client/Main/payment/paymentcontroller.coffee
@@ -152,9 +152,9 @@
         else if createAccount
           { cardFirstName: firstName, cardLastName: lastName } = billing
           { JUser } = KD.remote.api
-          JUser.convert { firstName, lastName, email }, (err, newToken, recoveryToken) ->
+          JUser.convert { firstName, lastName, email }, (err, newToken, recoveryToken) =>
+            workflow.emit "PasswordRecoveryToken", recoveryToken
             JUser.logout ->
-              window.location.href = "/Register/#{encodeURIComponent(recoveryToken)}"
               KD.singletons.dock.getView().show()

       .enter()


payment: call api as a function

payment: call api as a function

--- client/Main/payment/paymentcontroller.coffee
+++ client/Main/payment/paymentcontroller.coffee
@@ -35,7 +35,7 @@
       return callback err, card

-  canUserPurchase: (callback) -> @api.canUserPurchase callback
+  canUserPurchase: (callback) -> @api().canUserPurchase callback

   updateCreditCard: (token, callback) ->


Teambuilder: Fixed toggling shiny wiping the entire pokemon

--- client/app/js/views/pokemon_edit_view.coffee
+++ client/app/js/views/pokemon_edit_view.coffee
@@ -299,6 +299,15 @@
     $moves.first().addClass('active')
     $table.removeClass('hidden')

+  disableEventsAndExecute: (callback) =>
+    isOutermost = !@_eventsDisabled
+
+    @_eventsDisabled = true
+    @undelegateEvents()  if isOutermost # disable events
+    callback()
+    @delegateEvents()  if isOutermost
+    @_eventsDisabled = false  if isOutermost
+
   render: =>
     @$el.html @editTemplate(window: window, speciesList: @speciesList, itemList: @itemList, pokemon: @pokemon)
     attachSelectize(@$el.find(".species_list"),
@@ -311,13 +320,11 @@
     return this

   renderPokemon: =>
-    @undelegateEvents() # disable events (mostly change vevents)
     @renderSpecies()
     @renderNonStats()
     @renderStats()
     @renderMoves()
     @renderPBV()
-    @delegateEvents() # re-enable events

     # Disable entering values if this is a NullPokemon
     @$el.find("input, select")
@@ -336,10 +343,11 @@
       @$(".total-pbv").text(@teamPBV).toggleClass("red", @teamPBV > maxPBV)

   renderSpecies: =>
-    setSelectizeValue(@$(".species_list"), @pokemon.get("name"))
-    html = if @pokemon.isNull then "" else @speciesTemplate(window: window, pokemon: @pokemon)
-    @$(".species-info").html(html)
-    @$(".selected_shininess").toggleClass("selected", @pokemon.get('shiny') == true)
+    @disableEventsAndExecute =>
+      setSelectizeValue(@$(".species_list"), @pokemon.get("name"))
+      html = if @pokemon.isNull then "" else @speciesTemplate(window: window, pokemon: @pokemon)
+      @$(".species-info").html(html)
+      @$(".selected_shininess").toggleClass("selected", @pokemon.get('shiny') == true)

   renderNonStats: =>
     $nonStats = @$el.find(".non-stats")
@@ -359,12 +367,13 @@
       F: "Female"
       M: "Male"

-    populateSelect ".selected_ability", @pokemon.getAbilities(), @pokemon.get("ability")
-    populateSelect ".selected_nature", @pokemon.getNatures(), @pokemon.get("nature")
-    setSelectizeValue(@$(".selected_item"), @pokemon.get("item"))
-    populateSelect ".selected_gender", ([g, displayedGenders[g]] for g in @pokemon.getGenders()), @pokemon.get("gender")
-    $nonStats.find(".selected_level").val(@pokemon.get("level"))
-    $nonStats.find(".selected_happiness").val(@pokemon.get("happiness"))
+    @disableEventsAndExecute =>
+      populateSelect ".selected_ability", @pokemon.getAbilities(), @pokemon.get("ability")
+      populateSelect ".selected_nature", @pokemon.getNatures(), @pokemon.get("nature")
+      setSelectizeValue(@$(".selected_item"), @pokemon.get("item"))
+      populateSelect ".selected_gender", ([g, displayedGenders[g]] for g in @pokemon.getGenders()), @pokemon.get("gender")
+      $nonStats.find(".selected_level").val(@pokemon.get("level"))
+      $nonStats.find(".selected_happiness").val(@pokemon.get("happiness"))

   renderStats: =>
     pokemon = @pokemon


client: add CardAuth request to payment service

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- client/app/lib/redux/services/payment.coffee
+++ client/app/lib/redux/services/payment.coffee
@@ -1,11 +1,9 @@
-_ = require 'lodash'
+{ assign, omit } = require 'lodash'
+
 makeHttpClient = require 'app/util/makeHttpClient'
-{ pickData } = makeHttpClient.helpers
-
 { Status } = require 'app/redux/modules/payment/constants'

-exports.client = client = makeHttpClient { baseURL: '/api/social/payment' }
-
+# FIXME(umut): Request parameters validation and documentation.
 exports.Endpoints = Endpoints =
   SubscriptionDelete : '/subscription/delete'
   SubscriptionGet    : '/subscription/get'
@@ -20,6 +18,9 @@
   InvoiceList        : '/invoice/list'
   Info               : '/info'

+exports.client = client = makeHttpClient { baseURL: '/api/social/payment' }
+
+{ pickData } = makeHttpClient.helpers

 # fetchCustomer: fetches current group's payment customer.
 exports.fetchCustomer = fetchCustomer = pickData ->
@@ -59,11 +60,22 @@
   client.get(Endpoints.Info).then ({ data: info }) ->
     # if status is trialing, use `trialInfo`
     if info.subscription.status is Status.TRIALING
-      info = _.assign(_.omit(info, 'trialInfo'), info.trialInfo)
+      info = assign(omit(info, 'trialInfo'), info.trialInfo)
     return info

+exports.hasCreditCard = -> client.get Endpoints.CreditCardHas

-exports.hasCreditCard = -> client.get Endpoints.CreditCardHas
+exports.authorize = (params = {}) ->
+
+  { source, email } = params
+
+  unless source?.token
+    throw new Error "invalid param: source. expected: { token: String }"
+
+  unless email?
+    throw new Error "invaid param: email. expected: String"
+
+  client.post Endpoints.CreditCardAuth, { source, email }

 getTimestamp = (date) ->


Collaboration: chat head menu don't show watch for offline people

--- client/ide/views/statusbar/avatarview.coffee
+++ client/ide/views/statusbar/avatarview.coffee
@@ -82,12 +82,15 @@
       { watchMap, amIHost } = collaborationData

       isWatching  = watchMap.indexOf(@nickname) > -1
-      title       = if isWatching then 'Unwatch' else 'Watch'
       menuWidth   = 172

-      menuItems[title] =
-        title    : title
-        callback : (item, e) => @setWatchState isWatching, @nickname, item
+      unless @hasClass 'offline'
+        menuItems.Watch =
+          type         : 'customView'
+          view         : new IDE.ChatHeadWatchItemView
+            isWatching : isWatching
+            nickname   : @nickname
+            delegate   : this

       if amIHost
         menuItems.Kick =


Minor refactor of link-hints exit sequence.

The exit sequence is clearer like this.

--- content_scripts/link_hints.coffee
+++ content_scripts/link_hints.coffee
@@ -56,7 +56,7 @@
     chrome.runtime.sendMessage extend request, {handler: "linkHintsMessage", messageType, frameId}

   prepareToActivateMode: (mode, onExit) ->
-    @onExit = [(isSuccess) -> onExit() if isSuccess]
+    @onExit = [onExit]
     @sendMessage "prepareToActivateMode", modeIndex: availableModes.indexOf mode

   getHintDescriptors: ->
@@ -86,10 +86,11 @@
 LinkHints =
   activateMode: (count = 1, mode = OPEN_IN_CURRENT_TAB) ->
     if 0 < count or mode is OPEN_WITH_QUEUE
-      HintCoordinator.prepareToActivateMode mode, ->
-        # Wait for the next tick to allow the previous mode to exit.  It might yet generate a click event,
-        # which would cause our new mode to exit immediately.
-        Utils.nextTick -> LinkHints.activateMode count-1, mode
+      HintCoordinator.prepareToActivateMode mode, (isSuccess) ->
+        if isSuccess
+          # Wait for the next tick to allow the previous mode to exit.  It might yet generate a click event,
+          # which would cause our new mode to exit immediately.
+          Utils.nextTick -> LinkHints.activateMode count-1, mode

   activateModeToOpenInNewTab: (count) -> @activateMode count, OPEN_IN_NEW_BG_TAB
   activateModeToOpenInNewForegroundTab: (count) -> @activateMode count, OPEN_IN_NEW_FG_TAB


Deleting the browser folder

Deleting the browser folder

--- browser/coffee/controllers/todoCtrl.coffee
+++ browser/coffee/controllers/todoCtrl.coffee
@@ -1,23 +0,0 @@
-app = angular.module "TodoApp"
-
-
-app.controller "todoCtrl", ($scope, Todo) ->
-
-  # Load all todos from the server
-  Todo.query (todos) ->
-    $scope.todos = todos
-
-  # Save a todo on the server and update in current scope
-  $scope.save = (todo) ->
-    todoObj = new Todo text: todo.text
-    todoObj.$save().then (data, newTodo) ->
-      Todo.query (todos) ->
-        $scope.todos = todos
-    , (error) ->
-      console.log error
-
-  # delete todos
-  $scope.delete = (todo) ->
-    Todo.delete {id: todo._id}, ->
-      console.log "deleted todo with di #{todo._id}"
-      $scope.todos = $scope.todos.filter (td) -> td isnt todo


fix typo

fix typo

--- src/core/format.coffee
+++ src/core/format.coffee
@@ -142,7 +142,7 @@
     if _.isString(@config.class)
       for c in dom(node).classes()
         dom(node).removeClass(c) if c.indexOf(@config.class) == 0
-      node.removeAttribute('class') unless node.getAttribute('class')  # Some browsers leave empty style attribute
+      node.removeAttribute('class') unless node.getAttribute('class')  # Some browsers leave empty class attribute
     if _.isString(@config.tag)
       if this.isType(Format.types.LINE)
         if _.isString(@config.parentTag)


Stop handling individual content changes

Just reload the entire content tree each time
a file changes.

The small speed boost this provided was causing
more problems than it was worth. Namely that
content plugins that where referenced by other
plugins would get stuck in cache until both
files where updated.

--- src/core/server.coffee
+++ src/core/server.coffee
@@ -69,7 +69,6 @@
     templatesLoad: false
     viewsLoad: false
     localsLoad: false
-    files: {}

   isReady = ->
     ### Returns true if we have no running tasks ###
@@ -80,9 +79,10 @@
   logop = (error) ->
     env.logger.error(error.message, error) if error?

-  changeHandler = (error) ->
+  changeHandler = (error, path) ->
     ### Emits a change event if called without error ###
-    env.emit 'change' unless error?
+    unless error?
+      env.emit 'change', path
     logop error

   loadContents = (callback=logop) ->
@@ -128,61 +128,8 @@
       return false
     ignoreInitial: true

-  contentWatcher.on 'change', (path) ->
-
-    # ignore if we dont have the tree loaded or it's still loading
-    return if not contents? or block.contentsLoad
-
-    # also ignore if we are already working on this file
-    # (windows sometimes sends multiple change events for the same file)
-    return if block.files[path] is true
-
-    block.files[path] = true
-
-    content = null
-    for item in ContentTree.flatten(contents)
-      if item.__filename is path
-        content = item
-        break
-    if not content
-      throw new Error "Got a change event for item not previously in tree: #{ path }"
-
-    filepath =
-      relative: env.relativeContentsPath path
-      full: path
-
-    tree = content.parent
-    key = keyForValue tree, content
-    group = tree._[content.__plugin.group]
-
-    if not key?
-      throw new Error "Content #{ content.filename } not found in its parent tree!"
-
-    loadContent env, filepath, (error, newContent) ->
-      if error?
-        contents = null
-        lookup = {}
-        return
-
-      # replace old contents
-      newContent.parent = tree
-      tree[key] = newContent
-
-      # also in the trees plugin group
-      if not replaceInArray(group, content, newContent)
-        throw new Error "Content #{ content.filename } not found in its plugin group!"
-
-      # keep the lookup map fresh
-      delete lookup[normalizeUrl(content.url)]
-      lookup[normalizeUrl(newContent.url)] = newContent
-
-      delete block.files[path]
-      env.emit 'change', content.filename
-
-  # reload entire tree if a file is removed or added
-  # patches to modify the already loaded tree instead are welcome :-)
-  contentWatcher.on 'add', -> loadContents(changeHandler) if not block.contentsLoad
-  contentWatcher.on 'unlink', -> loadContents(changeHandler) if not block.contentsLoad
+  # reload content tree on changes
+  contentWatcher.on 'all', (path) -> loadContents(changeHandler, path) if not block.contentsLoad

   templateWatcher = chokidar.watch env.templatesPath,
     ignoreInitial: true


Add the custom environments to the list of snippets

--- services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/SnippetManager.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/SnippetManager.coffee
@@ -115,6 +115,19 @@
 			docText = session.getValue()
 			customEnvironmentNames = parseCustomEnvironmentNames(docText)
 			# console.log customEnvironmentNames
-			callback null, staticSnippets
+			snippets = staticSnippets.concat(
+				customEnvironmentNames.map (name) ->
+					{
+						caption: "\\begin{#{name}}..."
+						snippet: """
+							\\begin{#{name}}
+							\t$1
+							\\end{#{name}}
+						"""
+						meta: "env"
+
+					}
+			)
+			callback null, snippets

 	return SnippetManager


GameObject.Effect

GameObject.Effect

--- src/game_object_effect.coffee
+++ src/game_object_effect.coffee
@@ -8,7 +8,7 @@

       fadedOut = false

-      # this will fade the player object out over the next 30 frames.
+      # this will fade the player object out over the next 2 seconds.
       # once the player is faded out the fadedOut variable will be set to true.
       player.fadeOut 2, (player) ->
         fadedOut = true


Better comment for randArray

Better comment for randArray

--- app/lib/world/rand.coffee
+++ app/lib/world/rand.coffee
@@ -32,7 +32,7 @@
   rand2: (min, max) =>
     min + @rand max - min

-  # return an array of randomly shuffled numbers from 0 to n, exclusive
+  # return an array of numbers from 0 to n-1, shuffled
   randArray: (n) =>
     array = ( i for i in [0..(n-1)] )


Generate events from conditions, medications, and observations

--- app/models/patient.coffee
+++ app/models/patient.coffee
@@ -135,40 +135,6 @@
   hasNotifications: Ember.computed.gt('notificationCount', 0)

   patientLocation: 'Home'
-
-  events: Ember.computed 'medications', 'observations', 'conditions', ->
-    events = Ember.A()
-    @get("conditions").forEach (ev) =>
-      events.pushObject(@store.createRecord('event', {
-        startDate: moment(ev.get('onsetDate')).format('lll'),
-        text: ev.get('text')+" Started",
-        type: "condition"
-      }))
-      if ev.get('abatementDate') >= ev.get('onsetDate')
-        events.pushObject(@store.createRecord('event', {
-          startDate: moment(ev.get('abatementDate')).format('lll'),
-          text:ev.get('text')+" Ended",
-          type:"condition"
-        }))
-    @get("medications").forEach (ev) =>
-      events.pushObject(@store.createRecord('event', {
-        startDate: moment(ev.get('whenGiven.start')).format('lll'),
-        text:ev.get('medication.text')+" started.",
-        type:"medication"
-      }))
-      if ev.get('whenGiven.end') >= ev.get('whenGiven.start')
-        events.pushObject(@store.createRecord('event', {
-          startDate: moment(ev.get('whenGiven.end')).format('lll'),
-          text:ev.get('medication.text')+" stopped.",
-          type:"medication"
-        }))
-    #@get("observations").forEach (ev) =>
-      #events.pushObject(@store.createRecord('event', {
-        #startDate: moment(ev.get('appliesDateTime')).format(lll),
-        #text:ev.get('text')+".",
-        #type:"observation"
-      #}))
-    events.sortBy('startDate').reverse()
 )

 `export default Patient`


fill face properties

fill face properties

--- app/assets/javascripts/game.js.coffee
+++ app/assets/javascripts/game.js.coffee
@@ -12,6 +12,542 @@
     hair: ['brown', 'nothing']
     face: ['smart', 'triangle']

+  face_values = {
+    male: 'male'
+    female: 'female'
+    glasses: 'has glasses'
+    hair: 'haired'
+    hair_color: {
+      black: 'black'.
+      blonde: 'blonde',
+      brown: 'brown',
+      pink: 'pink',
+      yellow: 'yellow',
+      blue: 'blue',
+      green: 'green',
+      orange: 'orange'
+    }
+    teeth: 'visible teeth'
+    beard_or_mustaches: 'visible beard or mustaches'
+  }
+
+  faces = {
+    0: {
+      sex: face_values['female'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    1: {sex: face_values['male'], glasses: null, hair: null, teeth: face_values['teeth'], beard_or_mustaches: null},
+    2: {sex: face_values['male'], glasses: null, hair: null, teeth: face_values['teeth'], beard_or_mustaches: null},
+    3: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: face_values['beard_or_mustaches']
+    },
+    4: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['brown'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    5: {sex: face_values['male'], glasses: null, hair: null, teeth: face_values['teeth'], beard_or_mustaches: null},
+    6: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['pink'],
+      teeth: null,
+      beard_or_mustaches: face_values['beard_or_mustaches']
+    },
+    7: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['yellow'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    8: {
+      sex: face_values['female'],
+      glasses: face_values['glasses'],
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['orange'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    9: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: face_values['teeth'],
+      beard_or_mustaches: null
+    },
+    10: {
+      sex: face_values['female'],
+      glasses: face_values['glasses'],
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['green'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    11: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['yellow'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    12: {sex: face_values['male'], glasses: null, hair: null, teeth: null, beard_or_mustaches: null},
+    13: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    14: {sex: face_values['male'], glasses: null, hair: null, teeth: null, beard_or_mustaches: null},
+    15: {sex: face_values['male'], glasses: null, hair: null, teeth: null, beard_or_mustaches: null},
+    16: {
+      sex: face_values['female'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['brown'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    17: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    18: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    19: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['yellow'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    20: {
+      sex: face_values['male'],
+      glasses: face_values['glasses'],
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['brown'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    21: {
+      sex: face_values['male'],
+      glasses: face_values['glasses'],
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: face_values['beard_or_mustaches']
+    },
+    22: {
+      sex: face_values['male'],
+      glasses: face_values['glasses'],
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    23: {
+      sex: face_values['male'],
+      glasses: face_values['glasses'],
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth:
+        teeth: face_values['teeth'], beard_or_mustaches: null
+    },
+    24: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    25: {
+      sex: face_values['female'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    26: {
+      sex: face_values['male'],
+      glasses: face_values['glasses'],
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['blue'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    27: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['brown'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    28: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: face_values['beard_or_mustaches']
+    },
+    29: {
+      sex: face_values['male'],
+      glasses: face_values['glasses'],
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['brown'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    30: {
+      sex: face_values['female'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['brown'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    31: {
+      sex: face_values['male'],
+      glasses: face_values['glasses'],
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    32: {
+      sex: face_values['female'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['brown'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    33: {sex: face_values['male'], glasses: null, hair: null, teeth: null, beard_or_mustaches: null},
+    34: {sex: face_values['male'], glasses: null, hair: null, teeth: null, beard_or_mustaches: null},
+    35: {sex: face_values['male'], glasses: null, hair: null, teeth: null, beard_or_mustaches: null},
+    36: {
+      sex: face_values['female'],
+      glasses: face_values['glasses'],
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['brown'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    37: {
+      sex: face_values['female'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['pink'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    38: {
+      sex: face_values['female'],
+      glasses: face_values['glasses'],
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['brown'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    39: {
+      sex: face_values['male'],
+      glasses: face_values['glasses'],
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    40: {
+      sex: face_values['male'],
+      glasses: face_values['glasses'],
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['green'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    41: {
+      sex: face_values['female'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['yellow'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    42: {
+      sex: face_values['female'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    43: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    44: {sex: face_values['male'], glasses: null, hair: null, teeth: null, beard_or_mustaches: null},
+    45: {
+      sex: face_values['female'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    46: {sex: face_values['male'], glasses: null, hair: null, teeth: null, beard_or_mustaches: null},
+    47: {sex: face_values['male'], glasses: null, hair: null, teeth: face_values['teeth'], beard_or_mustaches: null},
+    48: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: face_values['beard_or_mustaches']
+    },
+    49: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    50: {sex: face_values['male'], glasses: null, hair: null, teeth: face_values['teeth'], beard_or_mustaches: null},
+    51: {
+      sex: face_values['male'],
+      glasses: face_values['glasses'],
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    52: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['brown'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    53: {sex: face_values['male'], glasses: null, hair: null, teeth: null, beard_or_mustaches: null},
+    54: {sex: face_values['male'], glasses: null, hair: null, teeth: null, beard_or_mustaches: null},
+    55: {
+      sex: face_values['male'],
+      glasses: face_values['glasses'],
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['brown'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    56: {sex: face_values['male'], glasses: null, hair: null, teeth: face_values['teeth'], beard_or_mustaches: null},
+    57: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['brown'],
+      teeth: face_values['teeth'],
+      beard_or_mustaches: null
+    },
+    58: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    59: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['brown'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    60: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    61: {sex: face_values['male'], glasses: null, hair: null, teeth: null, beard_or_mustaches: null},
+    62: {
+      sex: face_values['female'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    63: {
+      sex: face_values['female'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['yellow'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    64: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    65: {
+      sex: face_values['female'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['brown'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    66: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    67: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['brown'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    68: {
+      sex: face_values['female'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['pink'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    69: {sex: face_values['male'], glasses: null, hair: null, teeth: null, beard_or_mustaches: null},
+    70: {
+      sex: face_values['female'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['brown'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    71: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['brown'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    72: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    73: {
+      sex: face_values['male'],
+      glasses: face_values['glasses'],
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    74: {
+      sex: face_values['female'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    75: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    76: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    77: {sex: face_values['male'], glasses: null, hair: null, teeth: null, beard_or_mustaches: null},
+    78: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    79: {
+      sex: face_values['female'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['blonde'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+    80: {
+      sex: face_values['male'],
+      glasses: null,
+      hair: face_values['hair'],
+      hair_color: face_values['hair_color']['black'],
+      teeth: null,
+      beard_or_mustaches: null
+    },
+  }
   p1_cards_obj = []
   p2_cards_obj = []

@@ -76,4 +612,4 @@
     property_variants: property_variants
     choose_property_variant: choose_property_variant
     get_state: -> return state;
-  }    +  }


added another TODO to mock js

added another TODO to mock js

--- app/assets/javascripts/mock.js.coffee
+++ app/assets/javascripts/mock.js.coffee
@@ -11,6 +11,9 @@
 #     this code could probably be avoided if that html were built
 #     server-side
 #TODO make sure you're using all of coffeescript's bells and whistles
+#TODO hotkeys and click behavior should invoke the same code, this way
+#     when you get one thing done you don't have to do the whole
+#     thing over again for another control

 window.open "http://www.w3schools.com"


fix: coffee compilation error after merge

--- app/assets/javascripts/suggest.coffee
+++ app/assets/javascripts/suggest.coffee
@@ -148,8 +148,7 @@
     $( '#callbackNoResult' ).show()

   retrieveAllEvents: ({url}) ->
-    $.ajax(
-    {
+    $.ajax({
       type: 'GET'
       url: "#{url}/events",
       dataType: "json"
@@ -157,8 +156,7 @@
         SUGGEST.displayAllEvents data
       error: (data) ->
         SUGGEST.displayNoResult $('#suggest').val()
-    }
-  )
+    })

   retrievePreviewResults: ->
     $("#events").submit ->


Add loading feature to preview on new tip page

--- app/assets/javascripts/tips.js.coffee
+++ app/assets/javascripts/tips.js.coffee
@@ -12,7 +12,7 @@
     $counter_display.removeClass("approaching-limit")
   if (remaining < 0)
     $(selector).parent().addClass('has-error')
-  else
+  else
     $(selector).parent().removeClass('has-error')

 jQuery ->
@@ -33,18 +33,25 @@
   # Send the tip body to Markdown parser to generate the preview and show preview
   $('#code_preview_tab').click (event) ->
     event.preventDefault()
+
     unless $("#code_preview_tab").parent().hasClass("active")
+
       url = "/tips/preview"
       body = { body: $("#tip_body").val() }
-      $.post(url, body)
+      $.post url, body, success: ->
+      $("#code_preview_tab").addClass("load").html("loading...")
+      console.log("success")
+
       $("#write_input").hide()
       $("#code_preview").show()
       $("#code_preview_tab").parent().addClass("active")
       $("#write_tab").parent().removeClass("active")
+      # $("#code_preview_tab").addClass("preview").html("Preview")

   # Switch back to tip body input
   $('#write_tab').click (event) ->
     event.preventDefault()
+    $("#code_preview_tab").html("Preview")
     unless $("#write_tab").parent().hasClass("active")
       $("#write_input").show()
       $("#code_preview").hide()


increase legend width

increase legend width

--- app/assets/javascripts/zvg/zvg.coffee
+++ app/assets/javascripts/zvg/zvg.coffee
@@ -317,7 +317,7 @@
     @set_legend_x()
     @set_series_1_spacing()

-  legend_width: 300
+  legend_width: 400

   # x offset for point chart scale on left
   x_offset: 0


PointerEvents deprecated.

PointerEvents deprecated.

--- app/client/js/react/pages/song.coffee
+++ app/client/js/react/pages/song.coffee
@@ -68,6 +68,7 @@
         else
           @hideMenuAfterWhile()

+      # TODO(steida): Use polymer-gestures once pinch event will be supported.
       onFontResizeButtonPointerUp: (increase) ->
         if not goog.labs.userAgent.device.isDesktop()
           clearTimeout @hideMenuTimer


update to new api

update to new api

--- app/coffee/LearnedWordsManager.coffee
+++ app/coffee/LearnedWordsManager.coffee
@@ -30,13 +30,8 @@
 			callback null, words

-metrics.timeAsyncMethod(
-	LearnedWordsManager, 'learnWord',
-	'LearnedWordsManager.learnWord',
-	logger
-)
-metrics.timeAsyncMethod(
-	LearnedWordsManager, 'getLearnedWords',
-	'LearnedWordsManager.getLearnedWords',
-	logger
-)
+[
+	'learnWord',
+	'getLearnedWords'
+].map (method) ->
+	metrics.timeAsyncMethod(LearnedWordsManager, method, 'mongo.LearnedWordsManager', logger)


fix(kanban): unclassified userstories must contain the placeholder if it's present

--- app/coffee/modules/kanban/main.coffee
+++ app/coffee/modules/kanban/main.coffee
@@ -144,7 +144,7 @@
     filtersReloadContent: () ->
         @.loadUserstories().then (result) =>
             if @scope.project.swimlanes && !result.length
-                @.foldedSwimlane = @.foldedSwimlane.set(@scope.project.swimlanes[0].id.toString(), false)
+                @.foldedSwimlane = @.foldedSwimlane.set(@scope.swimlanesList.first().id.toString(), false)

             openArchived = _.difference(@kanbanUserstoriesService.archivedStatus,
                                         @kanbanUserstoriesService.statusHide)
@@ -237,7 +237,7 @@
         firstStatus = @scope.usStatusList[0].id == statusId && !@kanbanUserstoriesService.userstoriesRaw.length

         if swimlaneId
-            firstSwimlane = @scope.project.swimlanes[0].id == swimlaneId
+            firstSwimlane =  @scope.swimlanesList.first().id == swimlaneId
             return firstStatus && firstSwimlane

         return firstStatus


Show individual short orders from short wall in orderbook, sort shorts properly according to price, then interest, then quantity (for order stability)

--- app/js/services/market_service.coffee
+++ app/js/services/market_service.coffee
@@ -465,20 +465,34 @@
         else
             feed_price = market.feed_price
         deferred = @q.defer()
-        combined_asks = @asks[..]
-        combined_bids = @bids[..]
+
+        combined_asks = []
+        for ask in @asks
+            if ask.type != "short_wall"
+                combined_asks.push ask
+
+        combined_bids = []
+        for bid in @bids
+            if bid.type != "short_wall"
+                combined_bids.push bid

         price_string = null
         now = new Date()
         if market.shorts_available
             for short in @shorts
                 if inverted
-                    if not (short.type == "short" && short.short_price_limit <= feed_price)
-                        # short.price = short.short_price_limit
-                        combined_asks.push short
+                    if short.short_price_limit >= feed_price
+                        short.price = short.short_price_limit
+                        combined_asks.push short
+                    else if short.short_price_limit < feed_price
+                        short.price = feed_price
+                        combined_asks.push short
                 else
-                    if not (short.type == "short" && short.short_price_limit >= feed_price)
-                        # short.price = short.short_price_limit
+                    if short.short_price_limit <= feed_price
+                        short.price = short.short_price_limit
+                        combined_bids.push short
+                    else
+                        short.price = feed_price
                         combined_bids.push short
             for cover in @covers
                 # console.log cover
@@ -502,18 +516,27 @@
                 if bid.type == "short" and bid.short_price_limit < feed_price
                     bid.price = bid.short_price_limit

+        ###
         combined_asks.sort (a,b) ->
-            if a.price - b.price == 0 then a.index - b.index else a.price - b.price
+            if a.interest_rate and b.interest_rate
+                console.log a
+                if a.price - b.price == 0
+                    if b.interest_rate - a.interest_rate == 0 then a.index - b.index else b.interest_rate - a.interest_rate
+                else
+                    a.price - b.price
+            else if a.interest_rate and !b.interest_rate
+                if a.price - b.price == 0 then -1 else a.price - b.price
+            else if b.interest_rate and !a.interest_rate
+                if a.price - b.price == 0 then 1 else a.price - b.price
+            else
+                if a.price - b.price == 0 then a.index - b.index else a.price - b.price

-        combined_bids.sort (a,b) ->
-            b.price - a.price
-            if b.price - a.price == 0 then a.index - b.index else b.price - a.price
-
         for ask, index in combined_asks
             ask.index = index

         for bid, index in combined_bids
             bid.index = index
+        ###

         @helper.update_array {
             target: @combined_asks,
@@ -527,6 +550,35 @@
             can_remove: (target_el) ->
                 target_el.type == "bid" || target_el.type == "short" || target_el.type == "short_wall" || target_el.type == "margin_order"
             }
+
+        @combined_asks.sort (a,b) ->
+            if a.interest_rate and b.interest_rate
+                if a.price - b.price == 0
+                    if b.interest_rate - a.interest_rate == 0 then b.quantity - a.quantity else b.interest_rate - a.interest_rate
+                else
+                    a.price - b.price
+            else if a.interest_rate and !b.interest_rate
+                if a.price - b.price == 0 then -1 else a.price - b.price
+            else if b.interest_rate and !a.interest_rate
+                if a.price - b.price == 0 then 1 else a.price - b.price
+            else
+                if a.price - b.price == 0 then b.quantity - a.quantity else a.price - b.price
+
+        @combined_bids.sort (a,b) ->
+            if a.interest_rate and b.interest_rate
+                if b.price - a.price == 0
+                    if b.interest_rate - a.interest_rate == 0 then b.quantity - a.quantity else b.interest_rate - a.interest_rate
+                else
+                    b.price - a.price
+            else if a.interest_rate and !b.interest_rate
+                if b.price - a.price == 0 then -1 else b.price - a.price
+            else if b.interest_rate and !a.interest_rate
+                if b.price - a.price == 0 then 1 else b.price - a.price
+            else
+                if b.price - a.price == 0 then b.quantity - a.quantity else b.price - a.price
+
+        #@combined_bids.sort (a,b) ->
+        #    if b.price - a.price == 0 then a.quantity - b.quantity else b.price - a.price

         deferred.resolve(true)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/emitterable.coffee
+++ src/emitterable.coffee
@@ -33,7 +33,7 @@
     I.particles.invoke "draw", canvas

   self.bind 'overlay', (canvas) ->
-
+    I.particles.invoke "trigger", "over"

   self.bind 'update', (dt) ->
     I.batchSize.times ->


update student average

update student average

--- src/flux/scores.coffee
+++ src/flux/scores.coffee
@@ -30,19 +30,29 @@
 adjustTaskAverages = (data, taskInfo) ->
   {task} = taskInfo
   oldScore = task.score
+  course = data[taskInfo.courseId][0]
+  student = course.students[taskInfo.studentIndex]
+
+  # Calculate score for the task
   task.score = Math.round((
     (task.correct_on_time_exercise_count + task.correct_accepted_late_exercise_count ) /
       task.exercise_count
   ) * 100 ) / 100

-  course = data[taskInfo.courseId][0]
-
+  # Student's course average
+  assignmentCount = student.data.length
+  student.average_score =
+    ( student.average_score - ( oldScore / assignmentCount ) ) +
+      ( task.score / assignmentCount )
+
+  # Assignment averages
   studentCount = course.students.length
   heading = course.data_headings[taskInfo.periodIndex]
   heading.average_score =
     ( heading.average_score - ( oldScore / studentCount ) ) +
       ( task.score / studentCount )

+  # Overall course averages
   taskCount = 0
   for student in course.students
     taskCount += 1 for studentTask in student.data when studentTask.is_included_in_averages


ADD: confirm url option for message dialog

--- frontend/javascripts/dialog/message.coffee
+++ frontend/javascripts/dialog/message.coffee
@@ -1,15 +1,20 @@
 import DialogElement from './element'
+import Turbolinks from 'turbolinks'
+

 class MessageDialogElement extends DialogElement

   @tag 'tao-message-dialog'

+  @attribute 'confirmUrl'
+
   _bind: ->
     super()

     @on 'click', '.button-confirm', (e) =>
+      @active = false
       @namespacedTrigger 'confirm'
-      @active = false
+      Turbolinks.visit(@confirmUrl) if @confirmUrl
       null

 export default MessageDialogElement.register()


fix(app generator): allow different sets of `jshintOptions` per target whilst maintaining a common base

--- generators/app/templates/_Gruntfile.coffee
+++ generators/app/templates/_Gruntfile.coffee
@@ -376,7 +376,8 @@

             options:

-                ##  NOTE:   Current grunt-coffee-jshint@0.2.1 uses coffee-jshint@0.0.14, which uses jshint@2.1.11, which does not yet support the 'browserify' option and others.
+                ##  NOTE:   Current grunt-coffee-jshint@0.2.1 uses coffee-jshint@0.0.14, which uses jshint@2.1.11, which does not yet support the 'browserify'
+                ##          option and others.
                 ##          The use of browserify and the UMD (Universal Module Definition) pattern implies the legimate use of the globals below.
                 ##
                 ##  I would have liked to specify these globals and other jshint options through a '.jshintrc' file instead but have been unsuccessful so far.
@@ -389,7 +390,11 @@
                                         'require'
                 ]

-                jshintOptions: [
+                ##  Caveat: Using the extra variable `jshintOptions` to share a common set between the different targets below. Afaict this can't be done any
+                ##  other way. (Duplicating doesn't count).
+                ##
+                jshintOptions: ( jshintOptions = [
+
                     ##                  Enforcing options:
                                         'eqeqeq'
                                         'forin'
@@ -401,15 +406,31 @@
                     ##                  Relaxing options:
                                         'debug'
                                         'loopfunc'
-
-                    ##                  Environment options:
+                ])
+
+            app:
+                options:
+                    jshintOptions:      jshintOptions.concat( [
+                        ##              Environment options:
                                         'browser'
                                         'devel'
-                ]
-
-            app:                        '<%= coffeelint.app %>'
-            gruntfile:                  '<%= coffeelint.gruntfile %>'
-            test:                       '<%= coffeelint.test %>'
+                    ] )
+
+                files:                  '<%= coffeelint.app.files %>'
+
+            gruntfile:
+                options:
+                    jshintOptions:      jshintOptions.concat( [
+                        ##              Environment options:
+                                        'node'
+                    ] )
+
+                files:                  '<%= coffeelint.gruntfile.files %>'
+
+            test:
+                options:                '<%= coffee_jshint.gruntfile.options %>'
+
+                files:                  '<%= coffeelint.test.files %>'

         ##


Implement loadScript() to dynamically load and use external javascript modules from within cells.

--- h2o-web/src/main/steam/scripts/flow.coffee
+++ h2o-web/src/main/steam/scripts/flow.coffee
@@ -1716,6 +1716,14 @@

   dataflow = Flow.Dataflow

+  loadScript = (path, go) ->
+    onDone = (script, status) -> go null, script:script, status:status
+    onFail = (jqxhr, settings, error) -> go error #TODO use framework error
+
+    $.getScript path
+      .done onDone
+      .fail onFail
+
   fork: fork
   join: (args..., go) -> _join args, dataflow.applicate go
   call: (go, args...) -> _join args, dataflow.applicate go
@@ -1740,6 +1748,7 @@
   getModels: getModels
   getModel: getModel
   gui: gui
+  loadScript: loadScript
   help: help

 do ->


(feature) Add different types of API calls.

--- src/javascript_sandbox.coffee
+++ src/javascript_sandbox.coffee
@@ -10,6 +10,8 @@
   createApi: () ->
     []
   generateApiMethod: (name) ->
+    # generate random name
+    id = uuid.v4()
     process: (code) -> "var #{name}=function(){application.remote.#{name}.apply(null,arguments)}\n#{code}"
     name: name
   createRunner: (customApi = [], cbs = {}) ->
@@ -18,21 +20,36 @@
   run: (code, customApi) ->
     id = uuid.v4()

-    # generate api link similar to jailed
-    api = remote: customApi
+    internals = _.reduce customApi.internal, ((acc, i, fname) ->
+      "#{acc}\nvar #{fname} = #{i}"), ""

-    evalCode = "var fn = function(application){#{code}};fn";
+    remoteApi = _(customApi.remote).chain()
+      .keys()
+      .reject (v) -> (v=="finished" or v == "failed")
+      .value()
+    remotes = (_.map remoteApi, (api) -> "var #{api} = application.remote.#{api}").join ";"
+    internalApi = _.keys customApi.internal
+
+    apiArgs = (_.union remoteApi, internalApi).join ","
+
+    evalCode = """
+      var runTests = function(#{apiArgs}){#{code}};
+      var start = function(application){ #{remotes};#{internals};runTests(#{apiArgs})};
+      start
+    """
+
+    console.log evalCode

     # create "sandboxed" function
     try
       runner = eval evalCode
       # call it!
-      runner api
+      runner customApi

       # is synchronous so.. its finished here
-      customApi.finished?()
+      customApi.remote.finished?()
     catch e
-      customApi.failed? e
+      customApi.remote.failed? e

   debug: (code, customApi, cbs) ->
     debugApi = _.union [(code) -> "debugger;\n#{code}"], customApi


mixpanel

mixpanel

--- lib/analytics.coffee
+++ lib/analytics.coffee
@@ -102,6 +102,8 @@
   mixpanel.push 'register_once', hash

 module.exports.abTest = (key) ->
+  return if typeof mixpanel is 'undefined'
+  return if Object.prototype.toString.call(mixpanel) == '[object Array]'
   property = module.exports.getProperty key
   if property is 'enabled'
     true


remove redundant

remove redundant

--- app/scripts/editor.coffee
+++ app/scripts/editor.coffee
@@ -1,8 +1,4 @@
 @Editor = class Editor
-	articleCreateURL: ''
-	articleSaveURL: ''
-	articleDeleteURL: ''
-	id: ''
 	promptMessage: 'Type your article here'
 	status:
 		cmd: false


use mouseenter / mouseleave

use mouseenter / mouseleave

--- app/scripts/editor.coffee
+++ app/scripts/editor.coffee
@@ -134,7 +134,7 @@
 		$('#insertion').css 'top', top
 	delegateEvents: ()->
 		self = @
-		$('#editor').delegate 'h1,h2,p,pre,code,figure', 'mousemove', (e)->
+		$('#editor').delegate 'h1,h2,p,pre,code,figure', 'mouseenter', (e)->
 			return if self.status.empty || self.status.new
 			$(@).addClass('hovered').siblings().removeClass('hovered')
 			parentOffset = $(@).parent().offset()
@@ -142,16 +142,10 @@
 			height = $(@).outerHeight()
 			# showInsertion = true
 			# showInsertion = false if height >= 30 && ( thisOffset.top + height - e.pageY >= 30 || thisOffset.top + height - e.pageY <= 0 )
-			showTooltip = true
-			showTooltip = false if height >= 30 && ( thisOffset.top + height - e.pageY >= 30 || thisOffset.top + height - e.pageY <= 0 )
-			if showTooltip
-				self.showTooltip()
-				self.setTooltipTop thisOffset.top - parentOffset.top
-				# self.showInsertion()
-				# self.setInsertionTop thisOffset.top - parentOffset.top + height
-			else
-				self.hideTooltip()
-				# self.hideInsertion()
+			self.showTooltip()
+			self.setTooltipTop thisOffset.top - parentOffset.top
+		.delegate 'h1,h2,p,pre,code,figure', 'mouseleave', (e)->
+			self.hideTooltip()

 		# insertion add btn event
 		$('#insertion').delegate '.btn-add', 'click', (e)->


UNLICENSE -> Unlicense

UNLICENSE -> Unlicense

--- src/legal-eagle.coffee
+++ src/legal-eagle.coffee
@@ -98,7 +98,7 @@
   unless licenseText?
     for licenseFileName in ['UNLICENSE', 'UNLICENSE.md', 'UNLICENSE.txt', 'UNLICENCE', 'UNLICENCE.md', 'UNLICENCE.txt']
       if licenseText = readIfExists(join(path, licenseFileName))
-        license = 'UNLICENSE'
+        license = 'Unlicense'
         break

   return unless licenseText?


Adiciona mais chamadas

Adiciona mais chamadas

--- Terminal/server/cluster-config.coffee
+++ Terminal/server/cluster-config.coffee
@@ -13,3 +13,5 @@
 agencia.call 'conta:cadastrar', {cpf: '01700873016', conta: '1'}

 agencia.call 'deposito', {conta: '1', valor: 120}
+agencia.call 'deposito', {conta: '1', valor: 20}
+agencia.call 'deposito', {conta: '1', valor: 100}


Mark network as stopped also when it was finished and stop() is called

--- src/lib/Network.coffee
+++ src/lib/Network.coffee
@@ -652,7 +652,9 @@

     @abortDebounce = true if @debouncedEnd

-    return callback null unless @started
+    unless @started
+      @stopped = true
+      return callback null

     # Disconnect all connections
     for connection in @connections


Unnecessary fat arrow

Unnecessary fat arrow

--- lib/appHelper.coffee
+++ lib/appHelper.coffee
@@ -114,7 +114,7 @@
    * @param  {String}   dist Destination path
    * @param  {Function} cb   Optional Callback
   ###
-  @link: (orig, dist, cb) =>
+  @link: (orig, dist, cb) ->
     args = Args([
       {orig: Args.STRING   | Args.Required                     }
       {dist: Args.STRING   | Args.Required                     }


:frowning::custard: Updated in browser at strd6.github.io/editor

--- pixie.cson
+++ pixie.cson
@@ -1,6 +1,5 @@
-version: "0.2.9"
+version: "0.3.0"
 remoteDependencies: [
   "//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js"
   "http://strd6.github.io/tempest/javascripts/envweb.js"
-  "http://strd6.github.io/require/v0.2.2.js"
 ]


fixed indentation

fixed indentation

--- src/models/InitialView.coffee
+++ src/models/InitialView.coffee
@@ -1,36 +1,36 @@
 class InitialView

-	dismiss: (options={}, callbacks={})->
-		console.log "InitialView.dismiss called"
-		parameters = {}
+  dismiss: (options={}, callbacks={})->
+    console.log "InitialView.dismiss called"
+    parameters = {}

-		if options.animation?
-			parameters.animation = options.animation;
-		else
-			parameters.animation = new steroids.Animation
-				transition: "fade"
+    if options.animation?
+      parameters.animation = options.animation;
+    else
+      parameters.animation = new steroids.Animation
+        transition: "fade"

-		steroids.nativeBridge.nativeCall
-          method: "dismissInitialView"
-          parameters: parameters
-          successCallbacks: [callbacks.onSuccess]
-          failureCallbacks: [callbacks.onFailure]
+    steroids.nativeBridge.nativeCall
+      method: "dismissInitialView"
+      parameters: parameters
+      successCallbacks: [callbacks.onSuccess]
+      failureCallbacks: [callbacks.onFailure]

-	show: (options={}, callbacks={})->
-		console.log "InitialView.show called"
-		parameters = {}
+  show: (options={}, callbacks={})->
+    console.log "InitialView.show called"
+    parameters = {}

-		if options.animation?
-			parameters.animation = options.animation;
-		else
-			parameters.animation = new steroids.Animation
-				transition: "fade"
+    if options.animation?
+      parameters.animation = options.animation;
+    else
+      parameters.animation = new steroids.Animation
+        transition: "fade"

-		steroids.nativeBridge.nativeCall
-		  method: "showInitialView"
-		  parameters: parameters
-		  successCallbacks: [callbacks.onSuccess]
-		  failureCallbacks: [callbacks.onFailure]
+    steroids.nativeBridge.nativeCall
+      method: "showInitialView"
+      parameters: parameters
+      successCallbacks: [callbacks.onSuccess]
+      failureCallbacks: [callbacks.onFailure]


Add mocha task

Add mocha task

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -7,7 +7,7 @@

   grunt.registerTask 'default', ['clean', '6to5:dist', 'amd_tamer', 'uglify']

-  grunt.registerTask 'test', ['jshint', 'consolidate', '6to5:test', 'amd_tamer:test']
+  grunt.registerTask 'test', ['jshint', 'consolidate', '6to5:test', 'amd_tamer:test', 'mocha']

   grunt.registerTask 'doc', ['groc']


Remove the closure function from generated JS files. Add support for generating source maps

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -17,6 +17,10 @@
       build: 'gruntfile.coffee'

     coffee:
+      options:
+        bare: true
+        sourceMap: true
+        sourceMapDir: './target/.sourcemap'
       main:
         expand: true
         cwd: 'src'


Changes in gruntfile

Changes in gruntfile

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -52,6 +52,12 @@
           'extensions/app/style/organism.*.styl'
           'extensions/app/style/template.*.styl'
           'extensions/app/style/app.styl']
+        ide: [
+          'extensions/ide/style/reset.styl'
+          'extensions/ide/style/atom.*.styl'
+          'extensions/ide/style/molecule.*.styl'
+          'extensions/ide/style/organism.*.styl'
+          'extensions/ide/style/ide.styl']
         theme: [
           'extensions/app/style/theme/reset.styl'
           'extensions/app/style/theme/atom.*.styl'
@@ -106,6 +112,9 @@
       app:
         options: compress: true, import: [ '__init']
         files: '<%=meta.bower%>/<%=pkg.name%>.app.css': '<%=source.stylus.app%>'
+      ide:
+        options: compress: true, import: [ '__init']
+        files: '<%=meta.bower%>/<%=pkg.name%>.ide.css': '<%=source.stylus.ide%>'
       theme:
         options: compress: false, import: [ '__init']
         files: '<%=meta.bower%>/<%=pkg.name%>.app.theme.css': '<%=source.stylus.theme%>'
@@ -134,6 +143,9 @@
       stylus_app:
         files: ['<%= source.stylus.app %>']
         tasks: ['stylus:app']
+      stylus_ide:
+        files: ['<%= source.stylus.ide %>']
+        tasks: ['stylus:ide']
       stylus_theme:
         files: ['<%= source.stylus.theme %>']
         tasks: ['stylus:theme']


Added grunt prod task, and now default's grunt task generates a single JS file without minified for debugging

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -58,13 +58,15 @@
           ext   : ".min.css"]

     coffee:
-      compile:
+      all:
         files: [
           expand: true
           cwd   : "assets",
           src   :  "**/*.coffee"
           dest  : "public/js"
           ext   :  ".js"]
+      single:
+        files: "public/gertu.js": ["assets/**/*.coffee"]

     uglify:
       options: mangle: false, compress: true, banner: "<%= meta.banner %>"
@@ -80,10 +82,11 @@
         no_throwing_strings: level: "warn"

     clean:
-      js    : ["public/js",  "public/<%=pkg.name%>.min.js"]
+      js    : ["public/js",  "public/<%=pkg.name%>.js", "public/<%=pkg.name%>.min.js"]
       css   : ["public/css", "public/<%=pkg.name%>.min.css"]
       views : ["public/views"]
       images: ["public/img"]
+      prod  : ["public/js",  "public/<%=pkg.name%>.js"]

     copy:
       images:
@@ -126,10 +129,13 @@
     "clean", "coffeelint:app", "jade", "copy", "scripts", "stylesheets"]

   grunt.registerTask "stylesheets", "Compiles the stylesheets.",       [
-        "stylus", "cssmin"]
+    "stylus", "cssmin"]

   grunt.registerTask "scripts",     "Compiles the JavaScript files.",  [
-        "coffee", "uglify"]
+    "coffee", "uglify"]
+
+  grunt.registerTask "prod",        "Cleans dev JS files for prod.",   [
+    "build", "clean:prod"]

   grunt.registerTask "test",        "Executes the tests of project.",  [
     "env:test", "mochaTest"]


DEMO: Buttons: add vertically stacked non-flat button example.

--- demo/src/demos/ButtonDemo.coffee
+++ demo/src/demos/ButtonDemo.coffee
@@ -679,8 +679,19 @@
 				new CUI.Button(text: "groupA", group: "groupA")
 			]
 		)
-
 		@__demo_table.addExample("example 6", [ CUI.dom.append(CUI.dom.append(CUI.dom.append(CUI.dom.div(), CUI.dom.div("cui-buttonbar-demo-margin-testers")), bb.DOM), CUI.dom.div("cui-buttonbar-demo-margin-testers"))] )
+
+
+		bb = new CUI.Buttonbar(
+			stack: "vertical"
+			buttons: [
+				new CUI.Button(text: "groupA")
+				new CUI.Button(text: "groupA")
+				new CUI.Button(text: "groupA")
+			]
+		)
+		@__demo_table.addExample("example 7, vertical stack", [ CUI.dom.append(CUI.dom.append(CUI.dom.append(CUI.dom.div(), CUI.dom.div("cui-buttonbar-demo-margin-testers")), bb.DOM), CUI.dom.div("cui-buttonbar-demo-margin-testers"))] )
+

 		bb = new CUI.Buttonbar(
 			tooltip:


Always use getBoundingClientRect for getViewportTopLeft

--- lib/dom_utils.coffee
+++ lib/dom_utils.coffee
@@ -291,12 +291,16 @@
   getViewportTopLeft: ->
     box = document.documentElement
     style = getComputedStyle box
+    rect = box.getBoundingClientRect()
+    {clientTop, clientLeft} = box
     if style.position == "static" and not /content|paint|strict/.test(style.contain or "")
-      zoom = +style.zoom || 1
-      top: Math.ceil(window.scrollY / zoom), left: Math.ceil(window.scrollX / zoom)
-    else
-      rect = box.getBoundingClientRect()
-      top: -rect.top - box.clientTop, left: -rect.left - box.clientLeft
+      # The margin is included in the client rect, so we need to subtract it back out.
+      marginTop = parseInt style.marginTop
+      marginLeft = parseInt style.marginLeft
+      top: -rect.top + marginTop, left: -rect.left + marginLeft
+    else
+      top: -rect.top - clientTop, left: -rect.left - clientLeft
+

   suppressPropagation: (event) ->
     event.stopImmediatePropagation()


Use correct symlink class

Use correct symlink class

--- lib/file-view.coffee
+++ lib/file-view.coffee
@@ -11,7 +11,7 @@

   initialize: (@archivePath, @entry) ->
     if @entry.isSymbolicLink()
-      @name.addClass('icon-file-symlink')
+      @name.addClass('icon-file-symlink-file')
     else
       @name.addClass('icon-file-text')


Fixed missing shuffle function bug in Random.

--- src/math/random.coffee
+++ src/math/random.coffee
@@ -7,6 +7,7 @@
 ###

 mersenne = require 'mersenne'
+util = require '../util'

 class Random
   constructor: (initialSeed=0) ->
@@ -16,8 +17,7 @@
     @seedValue = value
     mersenne.seed @seedValue

-  toString: ->
-    "Random(#{@seedValue})"
+  toString: -> "Random(#{@seedValue})"

   # returns a random float value between [0..1]
@@ -45,7 +45,7 @@
       when 0 then null
       when 1 then list[0]
       else
-      # return single entry
+        # return single entry
         if count == 1
           list[ @randi(0, list.length) ]

@@ -55,7 +55,7 @@
           for i in [0...list.length]
             indices.push i

-          exports.shuffle(indices)
+          util.shuffle(indices)
           result = []
           for i in [0...count]
             result.push list[ indices[i] ]


Fix animate along path test

Fix animate along path test


naming; actually parse mixinghooks

naming; actually parse mixinghooks

--- src/mixer/index.coffee
+++ src/mixer/index.coffee
@@ -30,10 +30,13 @@

   _parse_mixinghooks: (mixin, mixinghooks) ->
     for own mixinghook_key, hook of mixinghooks
-      if hook? && !_.isFunction hook
-        throw new TypeError "Expected a function for #{mixinghook_key}"
+      if hook?
+        unless _.isFunction hook
+          throw new TypeError "Expected a function for #{mixinghook_key}"
+      else
+        mixinghooks[mixinghook_key] = null

-    {premix: mixinghooks.premixing_hook, postmix: mixinghooks.postmixing_hook}
+    {pre: mixinghooks.premixing_hook, post: mixinghooks.postmixing_hook}

   _parse_omits: (mixin, omits) ->
     if omits != undefined
@@ -51,14 +54,14 @@

     omits = @_parse_omits(mixin, omits)
     {before, after} = @_parse_methodhooks(mixin, {hook_before, hook_after})
-    {premix, postmix} = @_parse_mixinghooks(mixin, {premixing_hook, postmixing_hook})
+    {pre, post} = @_parse_mixinghooks(mixin, {premixing_hook, postmixing_hook})

-    {omits, methodhooks: {before, after}, mixinghooks: {premix, postmix}}
+    {omits, methodhooks: {before, after}, mixinghooks: {pre, post}}

 MIXER =

-  _with_hook: ({mixtarget, mixinprop, mixinvalue}, before = false) ->
+  _mix_with_hook: ({mixtarget, mixinprop, mixinvalue}, before = false) ->
     hookname = (before && "before_#{mixinprop}") || "after_#{mixinprop}"

     unless _.isFunction(mixtarget[hookname])
@@ -69,7 +72,7 @@
       hooked_mixinfunc = _.compose mixtarget[hookname], mixinvalue
     mixtarget[mixinprop] = hooked_mixinfunc

-  _without_hook: ({mixtarget, mixinprop, mixinvalue}) ->
+  _mix_without_hook: ({mixtarget, mixinprop, mixinvalue}) ->
     mixtarget[mixinprop] = mixinvalue

   mix: (mixtarget, mixin, options = {}) ->
@@ -79,7 +82,7 @@
     {premixing_hook, postmixing_hook} = mixin
     [__, __, __, mixinghook_args] = arguments

-    mixinghooks.premix?.call(mixtarget, mixinghook_args)
+    mixinghooks.pre?.call(mixtarget, mixinghook_args)
     premixing_hook?.call(mixtarget, mixinghook_args)

     mixing_in = _.object(
@@ -91,11 +94,11 @@
       mixcontent = {mixtarget, mixinprop, mixinvalue}

       if not (mixinprop in _.union(methodhooks.before, methodhooks.after))
-        @_without_hook mixcontent
+        @_mix_without_hook mixcontent
       else
-        @_with_hook mixcontent, (mixinprop in methodhooks.before)
+        @_mix_with_hook mixcontent, (mixinprop in methodhooks.before)

-    mixinghooks.postmix?.call(mixtarget, mixinghook_args)
+    mixinghooks.post?.call(mixtarget, mixinghook_args)
     postmixing_hook?.call(mixtarget, mixinghook_args)

     mixtarget


avoid unnecessary spell checks on session change

--- services/web/public/coffee/ide/editor/directives/aceEditor/spell-check/SpellCheckManager.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor/spell-check/SpellCheckManager.coffee
@@ -22,7 +22,8 @@
 				@closeContextMenu()

 			@editor.on "changeSession", (e) =>
-				@runSpellCheckSoon(200)
+				if @$scope.spellCheckEnabled and @$scope.spellCheckLanguage and @$scope.spellCheckLanguage != ""
+					@runSpellCheckSoon(200)

 				e.oldSession?.getDocument().off "change", onChange
 				e.session.getDocument().on "change", onChange


sgAdding css

sgAdding css

--- lib/languages.coffee
+++ lib/languages.coffee
@@ -17,6 +17,14 @@
     pygmentsLexer:     'csharp'
     multiLineComment:  ['/*', '*', '*/']
     singleLineComment: ['//']
+    ignorePrefix:      '}'
+    foldPrefix:        '^'
+
+  CSS:
+    nameMatchers:      ['.css']
+    pygmentsLexer:     'css'
+    multiLineComment:  ['/*', '*', '*/']
+    singleLineComment: ['/*']
     ignorePrefix:      '}'
     foldPrefix:        '^'


Clean up.

Clean up.

--- lib/linter/gc.coffee
+++ lib/linter/gc.coffee
@@ -1,28 +1,21 @@
 {Range, Point} = require 'atom'
 CommandRunner = require '../command-runner'
 Violation = require '../violation'
-os = require 'os'
 path = require 'path'
 fs = require 'fs'
+errorPattern = /^\/+([^:]+):(\d+)(:(\d+))?: (.*)$/

 module.exports =
-class GoLint
+class Gc
   constructor: (@filePath) ->
-    @pattern = /^\/+([^:]+):(\d+)(:(\d+))?: (.*)$/
-    @gotEnv = false

   run: (callback) ->
-    f = () =>
+    @getEnv =>
       @runGoLint (error, violations) ->
         if error?
           callback(error)
         else
           callback(null, violations)
-
-    if @gotEnv
-      f()
-    else
-      @getEnv f

   runGoLint: (callback) ->
     runner = new CommandRunner(@buildCommand())
@@ -42,10 +35,10 @@

         if item[0] is '\t' and violations.length > 0
           # stuff like interfaces not being satisfied, etc will be indented
-          if not skippingIndented
+          unless skippingIndented
             violations[violations.length-1].message += '\n' + item
         else
-          [_, filePath, line, _, col, msg] = item.match(@pattern)
+          [_, filePath, line, _, col, msg] = item.match(errorPattern)
           filePath = '/' + filePath
           if filePath isnt @filePath
             skippingIndented = true
@@ -78,30 +71,27 @@
       "GOPATH"
       "GOCHAR"
     ])).run (error, result) =>
-      if not error?
+      unless error?
         [@GOARCH, @GOOS, @GOPATH, @GOCHAR] = result.stdout.split('\n')
         @importpath = "#{@GOPATH}/pkg/#{@GOOS}_#{@GOARCH}"
-        @gotEnv = true
         callback()

   # Build the compile command to be run, including relevant flags and setting
   # the import path to GOPATH/pkg (the go tool *g needs to be told explicitly,
-  # normally `go build` does this all for us)
+  # normally `go build` does this all for us).
+  # Opting for *g instead of go build because the linking would take up
+  # unnecessary real and cpu time for no gain.
   buildCommand: () ->
     # compile with all the other files in the same directory so it doesn't
     # complain about missing identifiers, etc
     here = path.dirname(@filePath)
     files = fs.readdirSync(here).filter (file) ->
-      path.extname(file) in ['.go', '.c', '.h', '.s', '.cpp', '.cc', '.m']
+      path.extname(file) is '.go'
     .map (file) ->
       path.join(here, file)

-    gotool = atom.config.get('atom-lint.gc.path')
-    if not gotool?
-      gotool = "go"
-
     [
-      gotool
+      atom.config.get('atom-lint.gc.path') || 'go'
       "tool"
       @GOCHAR + 'g'
       "-L" # use full file paths


Use safer .style() instead of direct .before

--- lib/processor.coffee
+++ lib/processor.coffee
@@ -72,7 +72,7 @@
         notHack = @prefixes.group(decl).down (other) -> other.prop == unprefixed

         if notHack and not @withHackValue(decl)
-          @reduceSpaces(decl) if decl.before and decl.before.indexOf("\n") > -1
+          @reduceSpaces(decl) if decl.style('before').indexOf("\n") > -1
           rule.remove(i)
           return

@@ -126,12 +126,12 @@
     @prefixes.group(decl).up (other) -> stop = true
     return if stop

-    parts   = decl.before.split("\n")
+    parts   = decl.style('before').split("\n")
     prevMin = parts[parts.length - 1].length
     diff    = false

     @prefixes.group(decl).down (other) ->
-      parts = other.before.split("\n")
+      parts = other.style('before').split("\n")
       last  = parts.length - 1

       if parts[last].length > prevMin


Added rich-text.coffee

Added rich-text.coffee

--- lib/rich-text.coffee
+++ lib/rich-text.coffee
@@ -61,28 +61,31 @@
     for event in events
       switch event.name
         when "cursors"
-          cursorPos = @characters.indexOf event.object.char
-          @editor.cursors.setCursor event.object.author,
-            cursorPos,
-            event.object.author,
-            'grey' #FIXME
+          id = event.object.author
+          index = @characters.indexOf event.object.char
+          text = event.object.author
+          color = "grey" # FIXME
+
+          if event.type == "update" or event.type == "add"
+            @editor.cursors.setCursor id, index, text, color
+

         when "characters"
           charPos = @characters.indexOf event.object
+          delta = {ops: [{retain: charPos}]}
+          del = {delete: 1}
+          ins = {insert: event.object.char, attributes: event.object.attributes}
           if event.type == "update"
             #TODO: inherit attributes
-            delta = {ops: [{retain: charPos},
-              {delete: 1},
-              {insert: event.object.char, attributes: event.object.attributes}
-            ]}
+            delta.ops.push del
+            delta.ops.push ins
+
           else if event.type == "add"
-            delta = {ops: [{retain:charPos},
-              {insert: event.object.char, attributes: event.object.attributes}
-            ]}
+            delta.ops.push ins
+
           else if event.type == "delete"
-            delta = {ops: [{retain:charPos},
-              {delete: 1}
-            ]}
+            delta.ops.push del
+
           @editor.setContents delta

         when "selections"


Show credentials dialog

Show credentials dialog

--- lib/spark-ide.coffee
+++ lib/spark-ide.coffee
@@ -362,3 +362,5 @@

   enterWifiCredentials: (port, ssid, security) -> @loginRequired =>
     @initView 'wifi-credentials'
+    @wifiCredentialsView.port = port
+    @wifiCredentialsView(ssid, security)


style conventions do not permit whitespace around parameter defaults

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -767,7 +767,7 @@
     else
       @element.scrollTop + @element.offsetHeight

-  scrollToEntry: (entry, center = true) ->
+  scrollToEntry: (entry, center=true) ->
     element = if entry?.classList.contains('directory') then entry.header else entry
     element?.scrollIntoViewIfNeeded(center)


clear selection when entering command mode

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -224,6 +224,7 @@
     @submode = null
     @editorView.removeClass('insert-mode visual-mode')
     @editorView.addClass('command-mode')
+    @editor.clearSelections()

     @editorView.on 'cursor:position-changed', @moveCursorBeforeNewline


Rename callback

Rename callback

It's sending an image, we're setting that header.

--- lib/webserver.coffee
+++ lib/webserver.coffee
@@ -45,7 +45,7 @@
 # -----------------

 # Helper function for the stream callback
-sendFile = (err, stdout, req, res, next) ->
+sendImage = (err, stdout, req, res, next) ->
   res.setHeader('Expires', new Date(Date.now() + 604800000))
   res.setHeader('Content-Type', 'image/png')
   stdout.pipe(res)
@@ -68,12 +68,12 @@
 # Avatars: Basic Route
 router.get '/avatar/:name', (req, res, next) ->
   imager.combine req.faceParts, (err, stdout) ->
-    sendFile(err, stdout, req, res, next)
+    sendImage(err, stdout, req, res, next)

 # Avatars: Route with custom Size
 router.get '/avatar/:size/:name', (req, res, next) ->
   imager.resize req.faceParts, req.params.size, (err, stdout) ->
-    sendFile(err, stdout, req, res, next)
+    sendImage(err, stdout, req, res, next)

 app.use('/', router)


Prepopulate lists on setup

Prepopulate lists on setup

--- src/setup/setup.coffee
+++ src/setup/setup.coffee
@@ -22,3 +22,5 @@
 Installation complete.
 Open new console and run '#{PACKAGE.mingzi} ?' to start.
 """
+
+try run 0, file(install2, 'cli.bat'), 'l', 'r'


Debug

Debug

--- scripts/mailin.coffee
+++ scripts/mailin.coffee
@@ -24,6 +24,7 @@

     messageId = data.headers['Message-ID']
     host = messageId.split(/@/)[1]
+    console.log "ID: #{messageId}, host: #{host}"

     switch host
       when "disqus.net"


removed visibility restriction.

removed visibility restriction.

--- server/publish.coffee
+++ server/publish.coffee
@@ -28,7 +28,7 @@
   saved = {}
   visible = {}
   authored = ( ids.length - authored )
-  visible[ id ] = true for id in ids[authored..]
+  visible[ id ] = true for id in ids #ids[authored..]

   send = ( doc ) =>
     client_post = new ClientPost( doc, user_id )


Updating the description

Updating the description

--- src/scripts/ruby_rules.coffee
+++ src/scripts/ruby_rules.coffee
@@ -2,7 +2,7 @@
 #   Make sure that hubot shares the ruby rules.
 #
 # Commands:
-#   hubot the ruby rules - Make sure hubot still knows the rules.
+#   hubot the ruby rules - Make hubot explain how to write practical ruby.
 #
 # Notes:
 #   These rules were provided by Sandy Metz in the Ruby Rouges podcast:


allow for blank first name or last name when deleting account

--- src/smc-webapp/r_account.cjsx
+++ src/smc-webapp/r_account.cjsx
@@ -495,7 +495,7 @@
                         initial_click = {()=>@setState(show_delete_confirmation:true)}
                         confirm_click = {=>@actions('account').delete_account()}
                         cancel_click  = {()=>@setState(show_delete_confirmation:false)}
-                        user_name     = {@props.first_name + ' ' + @props.last_name}
+                        user_name     = {(@props.first_name + ' ' + @props.last_name).trim()}
                         show_confirmation={@state.show_delete_confirmation}
                         />
                 </Col>


Properly introduced `@options` helper

Properly introduced `@options` helper

--- js-library/src/js/views/notification.coffee
+++ js-library/src/js/views/notification.coffee
@@ -9,14 +9,14 @@
     in_screen_time: 2000
     fade_time: 'slow'

-  constructor: (message) ->
-    @message = message
+  constructor: (options) ->
+    @options = $.extend {}, @default_options, options

   render: ->
     @$el = $("<#{@tagName}/>")
               .addClass(@className)
               .hide()
-              .text(@message)
+              .text(@options.message)

     $(@appendTo).append(@$el)


#323 - disallow deletion of the last option in a multi option question

--- jsapp/xlform_model_view/view.choices.coffee
+++ jsapp/xlform_model_view/view.choices.coffee
@@ -58,6 +58,9 @@
       emptyOpt = new $choices.Option(label: label)
       @model.options.add(emptyOpt)
       new OptionView(model: emptyOpt, cl: @model).render().$el.appendTo @ul
+      lis = @ul.find('li')
+      if lis.length == 2
+        lis.find('.js-remove-option').removeClass('hidden')

     reordered: (evt, ui)->
       ids = []
@@ -135,8 +138,14 @@
       else
         ifield.removeClass("empty")
     remove: ()->
+      $parent = @$el.parent()
+
       @$el.remove()
       @model.destroy()
+
+      lis = $parent.find('li')
+      if lis.length == 1
+        lis.find('.js-remove-option').addClass('hidden')
     saveValue: (ick, nval, oval, ctxt)->
       if nval is ""
         @remove()


Explicit argument passing in controller#compose.

--- src/chaplin/controllers/controller.coffee
+++ src/chaplin/controllers/controller.coffee
@@ -33,7 +33,7 @@

   # Convenience method to publish the `!composer:compose` event. See the
   # composer for information on parameters, etc.
-  compose: (name) ->
+  compose: (name, second, third) ->
     if arguments.length is 1
       # Retrieve an active composition using the retrieve event.
       item = null
@@ -42,7 +42,7 @@
       item
     else
       # Compose the arguments using the compose method.
-      @publishEvent '!composer:compose', name, arguments...
+      @publishEvent '!composer:compose', name, second, third

   # Redirection
   # -----------


--- client/activity/lib/components/test.coffee
+++ client/activity/lib/components/test.coffee
@@ -1,5 +1,8 @@
 require './browseprivatechannelsmodal/test'
 require './datemarker/test'
 require './appcomponent/test'
+<<<<<<< HEAD

+=======
+>>>>>>> Client: Activity: Tests: Deleted unnecessary line


Client: Authority is ordered descending, not ascending.

--- client/app/js/collections/user_list.coffee
+++ client/app/js/collections/user_list.coffee
@@ -6,8 +6,8 @@
     bAuthority = b.get('authority')
     aName = a.id.toLowerCase()
     bName = b.id.toLowerCase()
-    if aAuthority < bAuthority      then -1
-    else if aAuthority > bAuthority then  1
+    if aAuthority < bAuthority      then  1
+    else if aAuthority > bAuthority then -1
     else if aName < bName           then -1
     else if aName > bName           then  1
     else                                  0


do not throw when there are no startup logs

--- client/views/instances/instanceView.coffee
+++ client/views/instances/instanceView.coffee
@@ -19,7 +19,7 @@
     "http"

 Template.instanceView.helpers
-  hasLogs: -> @logs.startup? or @logs.teardown?
+  hasLogs: -> @logs?.startup? or @logs?.teardown?
   logs: -> _.union @logs?.startup, @logs?.teardown
   ansiToHtml: -> if @.split then ansi_up.ansi_to_html @ else ''
   activityIcon: ->


Cache hasAstral calls, invalidating any time the text changes

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -170,6 +170,7 @@
     @nextMarkerId = 1
     @outstandingSaveCount = 0
     @loadCount = 0
+    @cachedHasAstral = null
     @_emittedWillChangeEvent = false

     @setEncoding(params?.encoding)
@@ -732,7 +733,12 @@
   # are encoded as surrogate pairs.
   #
   # Returns a {Boolean}.
-  hasAstral: -> @buffer.hasAstral()
+  hasAstral: ->
+    if @cachedHasAstral isnt null
+      @cachedHasAstral
+    else
+      @cachedHasAstral = @buffer.hasAstral()
+      @cachedHasAstral

   ###
   Section: Mutating Text
@@ -2072,6 +2078,7 @@
       @_emittedWillChangeEvent = true

   emitDidChangeTextEvent: ->
+    @cachedHasAstral = null
     if @transactCallDepth is 0
       if @changesSinceLastDidChangeTextEvent.length > 0
         compactedChanges = patchFromChanges(@changesSinceLastDidChangeTextEvent).getChanges()


:memo: fix ::observeCursors doc

:memo: fix ::observeCursors doc

Change selection -> cursor

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -354,7 +354,7 @@
   # Immediately calls your callback for each existing cursor.
   #
   # * `callback` {Function}
-  #   * `selection` {Selection} that was added
+  #   * `cursor` {Cursor} that was added
   #
   # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
   observeCursors: (callback) ->


removed old sitemap.plugin.coffee in root folder

--- sitemap.plugin.coffee
+++ sitemap.plugin.coffee
@@ -1,132 +0,0 @@
-# Export Plugin
-module.exports = (BasePlugin) ->
-  # Requires
-  # Node modules
-  path = require('path')
-  fs = require('fs')
-  url = require('url')
-  util = require('util')
-
-  # DocPad
-  balUtil = require('bal-util')
-
-  # External
-  _ = require('underscore')
-  sm = require('sitemap')
-
-  # Define Plugin
-  class SitemapPlugin extends BasePlugin
-    # Plugin name
-    name: 'sitemap'
-
-    # --------------
-    # Configuration values
-
-    # default values
-    config:
-      sitemapPath: "" # populated in the constructor
-      # Default values
-      # the following defaults are for the whole sitemap.xml file
-      defaultsGlobal:
-        hostname: "http://example.com" # Obviously need to be changed
-        cachetime: 600000 # 600 seconds cache period
-      # the following defaults are per-document
-      defaultsPerDoc:
-        changefreq: 'weekly'
-        priority: 0.5
-
-    # The sitemap being built, to be passed to sitemap.js
-    sitemap:
-      hostname: null
-      cachetime: null
-      urls: []
-
-
-    # --------------
-    # Constructor
-
-    constructor: ->
-      super
-      # filesPaths[1] points to 'src/public', which we want here
-      @config.sitemapPath = path.normalize "#{@docpad.config.filesPaths[1]}/sitemap.xml"
-
-
-    # --------------
-    # Docpad events
-
-    # Create the sitemap.xml site-wide data at the very beginning,
-    # so that DocPad copies it to the `out` directory
-    generateBefore: ({}, next) ->
-      # Prepare
-      config = @config
-      sitemap = @sitemap
-      logger = @docpad.logger
-
-      if path.existsSync config.sitemapPath
-        logger.log "debug", "The sitemap.xml file already exists"
-
-        # Done, let DocPad proceed
-        next?()
-      else
-        logger.log "debug", "Creating the sitemap.xml file..."
-        balUtil.writeFile config.sitemapPath, '', (err) ->
-          return next?(err) if err
-          logger.log "debug", "Created the sitemap.xml file."
-
-          # Creates the site-wide sitemap data
-          _.extend sitemap, config.defaultsGlobal
-
-          # Done, let DocPad proceed
-          next?()
-
-    # Populate the sitemap.xml data for each document
-    renderDocument: (opts, next) ->
-      # Prepare
-      {extension,templateData,file} = opts
-      config = @config
-      logger = @docpad.logger
-      sitemap = @sitemap
-
-      # Only HTML documents are of interest for a sitemap
-      # todo: figure out how to reliably differenciate between document from other files (layouts, assets etc.)
-      if extension in ['html']
-        # Merge document's sitemap data and default values into document's metadata
-        templateData = _.extend templateData, config.defaultsPerDoc, templateData
-
-        # Create document's data
-        docMap =
-          url: file.get 'url'
-          changefreq: templateData.changefreq
-          priority: templateData.priority
-
-        logger.log "debug", "sitemap.xml data => url: #{docMap.url} - changefreq: #{docMap.changefreq} - priority: #{docMap.priority}"
-
-        # Add document data to site-wide map
-        sitemap.urls.push docMap
-
-      # Done, let DocPad proceed
-      next?()
-
-    # Write the sitemap.xml file in 'src/public' before DocPad moves it to 'out'
-    writeBefore: ({}, next) ->
-      # Prepare
-      config = @config
-      logger = @docpad.logger
-      sitemap = @sitemap
-
-      logger.log "debug", "Sitemap data :\n#{JSON.stringify sitemap, null, 4}"
-
-      # Create a sitemap.js object
-      sitemap = sm.createSitemap(sitemap);
-
-      logger.log "debug", "sitemap.xml file content :\n#{sitemap.toString()}"
-
-      # Fill the sitemap.xml file with data
-      balUtil.writeFile config.sitemapPath, sitemap.toString(), (err) ->
-        # Check
-        return next?(err)  if err
-
-        logger.log 'debug', "Wrote the sitemap.xml file to: #{config.sitemapPath}"
-
-        # Done, let DocPad proceed
-        next?()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/text_screen.coffee
+++ src/text_screen.coffee
@@ -22,8 +22,7 @@

     @name centerText
     @methodOf TextScreen#
-    @param {PixieCanvas} canvas The canvas to draw on
-
+    @param {PixieCanvas} canvas The canvas to draw on
     ###
     centerText: (canvas, text, options={}) ->
       font = options.font || I.font


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/text_screen.coffee
+++ src/text_screen.coffee
@@ -13,7 +13,7 @@

   self = GameState(I).extend
     ###*
-    Draw tex
+    Draw text in the center of th

     enemy.follow(player)


code cleanup: remove an unused class

code cleanup: remove an unused class

--- workers/social/lib/social/models/userkite.coffee
+++ workers/social/lib/social/models/userkite.coffee
@@ -1,99 +0,0 @@
-jraphical   = require 'jraphical'
-CActivity   = require './activity'
-JAccount    = require './account'
-KodingError = require '../error'
-
-class JUserKite extends jraphical.Module
-
-  {Relationship} = jraphical
-
-  {Base, secure, race} = require 'bongo'
-
-  @share()
-
-  @set
-    softDelete        : yes
-    sharedMethods     :
-      instance        : ['revoke']
-      static          : ['create', 'fetchAll', 'fetchByKey']
-    schema            :
-      latest_s3url    : String
-      kitename        : String
-      owner           : String
-      hash:String
-      latest_version  :
-        type: String # ???
-        default:1
-      createdAt   :
-        type      : Date
-        default   : -> new Date
-
-  newVersion: (cb)=>
-    kiteversion = new JUserKiteVersion
-      s3url: @latest_s3url
-      kitename: @kitename
-      owner: @owner
-      version: @latest_version
-      hash: @hash
-    kiteversion.save (err)->
-      console.log "error", err
-      cb(err)
-
-  @fetchOrCreate: (data, callback)=>
-    if not data.account_id
-      throw new Error("no account id - sorry")
-    JUserKite.one
-      kitename: data.kitename
-      owner: data.account_id #or data.account?._id
-    , (err, userkite)->
-      console.log "the error: ", err
-      if err
-        callback err
-      else if not userkite
-        userkite = new JUserKite
-          kitename      : data.kitename
-          latest_s3url  : data.latest_s3url
-          owner         : data.account_id #or data.account?._id
-          hash: data.hash
-        userkite.save (err)->
-          if err
-            callback err
-          else
-            callback null, userkite
-      else
-        JUserKite.update {_id: userkite.data._id},
-                            {$set:
-                                latest_version: parseInt(userkite.data.latest_version) + 1,
-                                latest_s3url: data.latest_s3url,
-                                hash: data.hash
-                            }
-                          , (err)->
-          if err
-            return callback(err)
-          callback(null, userkite)
-
-
-class JUserKiteVersion extends jraphical.Module
-
-  {Relationship} = jraphical
-
-  {Base, secure, race} = require 'bongo'
-
-  @share()
-
-  @set
-    softDelete        : yes
-    sharedMethods     :
-      instance        : ['revoke']
-      static          : ['create', 'fetchAll', 'fetchByKey']
-    schema            :
-      s3url           : String
-      kitename        : String
-      owner           : String
-      version         : String
-      hash : String
-      createdAt   :
-        type      : Date
-        default   : -> new Date
-
-module.exports = {JUserKite, JUserKiteVersion}


change color function

change color function

--- graphics/003-colorful-wahlque/viewer.coffee
+++ graphics/003-colorful-wahlque/viewer.coffee
@@ -13,15 +13,19 @@
     canvas.width = 512
     canvas.height = 512

-    color = (height) ->
+    color = (height, factor) ->
         r = 128
         b = 192
-        r = Math.floor(height / 48) if height > 8192
+        r = Math.floor(height / 48 ) if height > 8192
         r = 255 if r > 255
         b = Math.floor(320 - height / 64) if height < 8192
         b = 255 if b > 255
         g = Math.floor((r + b) / 1.5)
         g = 255 if g > 255
+
+        r = Math.floor(r * factor)
+        g = Math.floor(g * factor)
+        b = Math.floor(b * factor)

         hexR = Math.round(r).toString(16)
         hexR = '0' + hexR if hexR.length == 1
@@ -32,19 +36,20 @@

         "#" + hexR + hexG + hexB

-    viewer.paint = (positioning, data) ->
+    viewer.paint = (params, data) ->
         context.clearRect(0, 0, 512, 512)
         [num, len, heights] = data
         for row in [0...num]
             for col in [0...num]
                 lng = 2 * Math.PI * col / num
                 lat = Math.PI * (0.5 - row / num)
-                [x, y] = positioning(lng, lat)
+                [position, factor] = params(lng, lat)
+                [x, y] = postion
                 if x != -1 && y != -1
                     pos = row * num + row + col
                     height = heights[pos] / 64

-                    context.fillStyle = color(height)
+                    context.fillStyle = color(height, factor)
                     context.fillRect(Math.floor(256 + 250 * x), Math.floor(256 + 250 * y), 5, 5)

     viewer


adjusted initial position of bullets fired from ship in dronewar

--- games/dronewar/src/objects/root.coffee
+++ games/dronewar/src/objects/root.coffee
@@ -38,7 +38,7 @@
     x           = Math.cos(@angle - Math.PI * 0.5)
     y           = Math.sin(@angle - Math.PI * 0.5)
     bullet.r.x  = @r.x + x * (@size / 3 + bullet.size)
-    bullet.r.y  = @r.y + y * (@size / 6)
+    bullet.r.y  = @r.y + y * 14
     bullet.v.x  = @bullet_speed * x
     bullet.v.y  = @bullet_speed * y
     bullet.stroke(@bullet_stroke)


Fix typo

Fix typo

--- assets/data/constant.cson
+++ assets/data/constant.cson
@@ -114,7 +114,7 @@
     name: 'Kensuke Tanaka'
     link: 'https://www.facebook.com/kensuke.tanaka.790'
     avatar: 'http://www.famitsu.com/images/000/039/658/l_522b1c633860a.jpg'
-    description: 'For providing development difficulties (which we enjoy a lot, sincerely).'
+    description: 'For providing development difficulties (which we enjoyed a lot, sincerely).'
     extraCSS:
         maxHeight: "200%"
         marginLeft: "-65%"


auto pin mode

auto pin mode

--- grovepi.coffee
+++ grovepi.coffee
@@ -40,9 +40,11 @@

 	mode: (port, mode, callback) ->
-		unless modes[port]? is mode
+		if modes[port] isnt mode
 			modes[port] = mode
 			@send CMD.mode, port, [mode, 0], -> callback()
+		else
+			callback()

 	send: ( cmd, port, args... ) ->


fix broken test

fix broken test

--- test/functional/switch.coffee
+++ test/functional/switch.coffee
@@ -35,7 +35,7 @@
     fiveS = sayNum.loadConstant fiveP
     sixS = sayNum.loadConstant sixP

-    sayIt = sayNum.select sayNum.parameter(0)
+    sayIt = sayNum.choose sayNum.parameter(0)
     oneB = sayIt.addCase 1
     twoB = sayIt.addCase 2
     threeB = sayIt.addCase 3


add browser name getter in utils

add browser name getter in utils

--- src/utils/Utils.coffee
+++ src/utils/Utils.coffee
@@ -65,3 +65,10 @@
     # @return {int} Screen height
     @screenHeight: ->
         Rotten.Utils.screenSize().height
+
+
+    # Get your browser app name (not bulletproof at all, fell free to do better)
+    #
+    # @return {String} Browser app name
+    @getBrowserName: ->
+        navigator.appName


Add overlay test.

Add overlay test.

--- test/unit/modelingSpec.coffee
+++ test/unit/modelingSpec.coffee
@@ -1,7 +1,6 @@
-# The Modeling unit test.
-#
-# Note: image load cannot be unit-tested, since it requires an
-# active browser.
+# The Modeling unit test. This test suite tests the subject
+# modelings built when Subject.extend calls Modeling.collect.
+# The session modeling objects are tested in sessionSpec.
 define ['ngmocks', 'lodash', 'expect', 'moment', 'modeling', 'helpers'],
   (ng, _, expect, moment, modeling) ->
     describe 'Unit Testing the Modeling Service', ->
@@ -90,18 +89,6 @@
                  " registration protocol")
             .to.equal(mock.protocol.registration._id)

-      describe 'Overlay', ->
-        it 'should have one modeling overlay', ->
-          expect(modeling.overlays, "Modeling is missing overlays")
-            .to.exist
-          expect(modeling.overlays.length, "Modeling overlays count" +
-                                           " is incorrect")
-            .to.equal(1)
-
-        it 'should recapitulate the modeling result delta Ktrans overlay', ->
-          expect(modeling.overlays[0], "Modeling result overlays is incorrect")
-            .to.equal(modeling.result.deltaKTrans.overlay)
-
       describe 'Result', ->
         result = null


Upload .rpm file when available

Upload .rpm file when available

--- build/tasks/publish-build-task.coffee
+++ build/tasks/publish-build-task.coffee
@@ -69,8 +69,14 @@
       else
         arch = 'amd64'
       {version} = grunt.file.readJSON('package.json')
+
       sourcePath = "#{buildDir}/atom-#{version}-#{arch}.deb"
       assetName = "atom-#{arch}.deb"
+
+      unless fs.isFileSync(sourcePath)
+        rpmName = fs.readdirSync("#{buildDir}/rpm")[0]
+        sourcePath = "#{buildDir}/rpm/#{rpmName}"
+        assetName = "atom.#{arch}.rpm"

       {cp} = require('./task-helpers')(grunt)
       cp sourcePath, path.join(buildDir, assetName)


Change blobs to use group instead of parent

--- src/Blob.coffee
+++ src/Blob.coffee
@@ -1,11 +1,15 @@
 Action = require('ecstasy').Action
 QuadTree = require('simple-quadtree')

+_groupId = 0
+groups = {}
+
 class BlobComponent
-  constructor: ({@velX, @velY, @parent, @parentTime, @weight, @weightCap, @invincible}) ->
+  constructor: ({@velX, @velY, @group, @parent, @weight, @weightCap, @invincible}) ->
     @velX ?= 0
     @velY ?= 0
     @weight ?= 0.15
+    @group ?= _groupId++

 square = (x) -> x * x

@@ -52,11 +56,10 @@
       if entBlob.invincible
         entBlob.invincible -= delta
         entBlob.invincible = null if entBlob.invincible < 0
-      if entBlob.parentTime
-        entBlob.parentTime -= delta
-        if entBlob.parentTime < 0
-          entBlob.parentTime = null
-          entBlob.parent = null
+      if groups[entBlob.group]
+        groups[entBlob.group] -= delta
+        if groups[entBlob.group] < 0
+          groups[entBlob.group] = null
       entObj =
         x: entPos.x - entPos.radius
         y: entPos.y - entPos.radius
@@ -68,12 +71,10 @@
         return if other == entity
         [otherPos, otherBlob] = [other.c('pos'), other.c('blob')]
         return if otherBlob.weightCap
-        if entBlob.parent == other.id
-          pushOther entity, other if entPos.collides otherPos
-          return
-        if otherBlob.parent == entity.id
-          pushOther entity, other if entPos.collides otherPos
-          return
+        if entBlob.group == otherBlob.group
+          if groups[entBlob.group]
+            pushOther entity, other if entPos.collides otherPos
+            return
         return if otherBlob.weight <= 0.1
         return if entBlob.invincible
         return if otherBlob.invincible
@@ -126,14 +127,15 @@
   .c 'blob',
     velX: velX + entBlob.velX
     velY: velY + entBlob.velY
-    parent: @entity.id
-    parentTime: 20000
+    group: @entity.c('blob').group
+    parent: parent.id
     weightCap: weight
     weight: 1
   .c 'render', @entity.c 'render'
   entBlob.weightCap = weight
   entBlob.velX = -velX
   entBlob.velY = -velY
+  groups[@entity.c('blob').group] = 20000
   @result = newEntity.id

 module.exports =


Remove <noscript> tags

Remove <noscript> tags

--- lib/assets/javascripts/turbolinks.js.coffee
+++ lib/assets/javascripts/turbolinks.js.coffee
@@ -79,6 +79,7 @@
 changePage = (title, body, runScripts) ->
   document.title = title
   document.documentElement.replaceChild body, document.body
+  removeNoscriptTags()
   executeScriptTags() if runScripts
   currentState = window.history.state
   triggerEvent 'page:change'
@@ -92,6 +93,8 @@
     parentNode.removeChild script
     parentNode.insertBefore copy, nextSibling

+removeNoscriptTags = ->
+  noscript.parentNode.removeChild noscript for noscript in document.body.getElementsByTagName 'noscript'

 reflectNewUrl = (url) ->
   if url isnt document.location.href


Add TODOs about smells.

Add TODOs about smells.


Bug fix in assertion

Bug fix in assertion

--- src/knockback-core/knockback-store.coffee
+++ src/knockback-core/knockback-store.coffee
@@ -145,7 +145,7 @@

   # @private
   findOrReplace: (obj, creator, observable) ->
-    obj or raiseUnexpected('obj missing')
+    obj or _throwUnexpected(@, 'obj missing')
     if (index = @findIndex(obj, creator)) < 0
       return @register(obj, observable, {creator: creator})
     else


Fix copy/paste failure.

Fix copy/paste failure.