Tweaks to Twilio authentication

Tweaks to Twilio authentication

--- app.coffee
+++ app.coffee
@@ -6,11 +6,11 @@
 Sightengine = new SightengineClient(process.env.SIGHTENGINE_USER, process.env.SIGHTENGINE_SECRET)

 app = Express()
-app.use BodyParser.urlencoded()
+app.use BodyParser.urlencoded extended:true

 app.get '/sms', (req, res) ->
     if !Twilio.validateExpressRequest(req, process.env.TWILIO_AUTHTOKEN)
-        res.send 403
+        res.status(403).send("Not a valid Twilio request")
         return

     console.log req.query


Everyone gets a new player message

Everyone gets a new player message

--- app.coffee
+++ app.coffee
@@ -110,7 +110,7 @@
     game.addPlayer(player)

     console.log "PLAYER #{player.name} #{player.uuid}"
-    socket.emit 'user joined', player.identifier()
+    io.sockets.emit 'user joined', player.identifier()

   # when the client emits 'new message', this listens and executes
   socket.on 'new word', (word, cb) ->


Add missing error handling in checkAuth

Add missing error handling in checkAuth

--- app.coffee
+++ app.coffee
@@ -194,6 +194,9 @@
 		if auth?.users?[req.auth.username]?
 			# hash first because Woltlab Community Framework uses double salted hashes
 			bcrypt.hash req.auth.password, auth.users[req.auth.username].passwd, (err, hash) ->
+				if err?
+					res.send 500, '500 Internal Server Error'
+					return
 				bcrypt.compare hash, auth.users[req.auth.username].passwd, (err, result) ->
 					if err?
 						res.send 500, '500 Internal Server Error'


allow reconciliation of container created property

--- imports/server/stateReconciler.coffee
+++ imports/server/stateReconciler.coffee
@@ -79,6 +79,8 @@

   updateContainerId: (contId, labels) -> setServiceField 'container.id', contId, labels

+  updateCreated: (created, labels) -> setServiceField 'container.created', created, labels
+
   updateServiceState: (mappedState, labels) ->
     unless mappedState in SERVICE_STATES
       throw "Service state '#{mappedState}' is not supported."


fix test

fix test

--- test/test_object_literal_assignment_spacing.coffee
+++ test/test_object_literal_assignment_spacing.coffee
@@ -7,25 +7,31 @@

     'Spacing around assignment' :

-        topic : -> '{spacing : true}'
+        topic : ->
+            '''
+            object = {spacing : true}
+            '''

         'will not return an error' : (source) ->
-            config = {'object_literal_assignment_spacing' : 'error'}
-            errors = coffeelint.link(source, config)
+            config = 'object_literal_assignment_spacing' : {'level' : 'error'}
+            errors = coffeelint.lint(source, config)
             assert.isEmpty(errors)

     'No spacing around assignment' :

-        topic : -> '{spacing: false}'
+        topic : ->
+            '''
+            object = {spacing: false}
+            '''

         'will return an error' : (source) ->
-            config = {'object_literal_assignment_spacing' : 'error'}
-            errors = coffeelint.link(source, config)
+            config = 'object_literal_assignment_spacing' : {'level' : 'error'}
+            errors = coffeelint.lint(source, config)
             assert.equal(errors.length, 1)

         'will ignore an error' : (source) ->
-            config = {'object_literal_assignment_spacing' : 'ignore'}
-            errors = coffeelint.link(source, config)
+            config = 'object_literal_assignment_spacing' : {'level' : 'ignore'}
+            errors = coffeelint.lint(source, config)
             assert.isEmpty(errors)

 }).export(module)


remove focused describes

remove focused describes

--- spec/editor-spec.coffee
+++ spec/editor-spec.coffee
@@ -863,7 +863,7 @@
         expect(selection1.getScreenRange()).toEqual [[3, 0], [4, 5]]
         expect(selection2.getScreenRange()).toEqual [[5, 6], [6, 2]]

-    fdescribe ".selectToBeginningOfNextParagraph()", ->
+    describe ".selectToBeginningOfNextParagraph()", ->
       it "selects from the cursor to first line of the next paragraph", ->
         editor.setSelectedBufferRange([[3, 0], [4, 5]])
         editor.addCursorAtScreenPosition([5, 6])
@@ -875,7 +875,7 @@
         expect(selections.length).toBe 1
         expect(selections[0].getScreenRange()).toEqual [[3, 0], [10, 0]]

-    fdescribe ".selectToBeginningOfPreviousParagraph()", ->
+    describe ".selectToBeginningOfPreviousParagraph()", ->
       it "selects from the cursor to the first line of the pevious paragraph", ->
         editor.setSelectedBufferRange([[3, 0], [4, 5]])
         editor.addCursorAtScreenPosition([5, 6])


Set unsubmitted for error case of editlinks.

--- app/assets/volume/editLinks.coffee
+++ app/assets/volume/editLinks.coffee
@@ -44,6 +44,7 @@
             form.$setPristine()
             form.$setUnsubmitted()
           , (res) ->
+            form.$setUnsubmitted()
             form.validator.server res
           )


make sure tests match graphHeight/Width functions

--- test/spec/configurationTests.coffee
+++ test/spec/configurationTests.coffee
@@ -44,8 +44,8 @@

         describe "graphHeight", ->
             it "should reassign default configuration", ->
-                runWithConf({dataSource:"sample_data/movies.json", graphHeight:200 })
-                alchemy.conf.graphHeight.should.equal(200)
+                runWithConf({dataSource:"sample_data/movies.json", graphHeight: () -> 200 })
+                alchemy.conf.graphHeight().should.equal(200)

         describe "alpha", ->
             it "should reassign default configuration", ->


fixed an indent

fixed an indent

--- scripts/maurice.coffee
+++ scripts/maurice.coffee
@@ -15,7 +15,7 @@
   robot.respond /maurice bomb( (\d+))?/i, (msg) ->
     count = msg.match[2] || 5
     maurice = "https://fiercemarkets.campfirenow.com/room/470540/uploads/3090344/maurice-2.gif"
-      msg.send maurice for count
+    msg.send maurice for count

   robot.hear /.*(dance|happy).*/i, (msg) ->
-    msg.send "https://fiercemarkets.campfirenow.com/room/470540/uploads/3090344/maurice-2.gif"+    msg.send "https://fiercemarkets.campfirenow.com/room/470540/uploads/3090344/maurice-2.gif"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/clamp_bounds.coffee
+++ src/clamp_bounds.coffee
@@ -6,6 +6,7 @@
     height: 32

   self.bind "update", ->
-    log App.
+    log App.width
+    log App
     I.x = I.x.clamp(I.width / 2, App.width - I.width / 2)
     I.y = I.y.clamp(I.height / 2, App.height - I.height / 2)


Remove overflow scroll bar

Remove overflow scroll bar

--- src/smc-webapp/widget-ssh-key-adder/main.cjsx
+++ src/smc-webapp/widget-ssh-key-adder/main.cjsx
@@ -161,7 +161,7 @@
     render: ->
         <Well style={@props.style}>
             <Row>
-                <Col md=9 style={overflowX:'scroll'}>
+                <Col md=9>

{@props.ssh_key.get('title')}

                     Fingerprint: {@props.ssh_key.get('fingerprint')}

                     Added on {new Date(@props.ssh_key.get('last_use_date')).toLocaleDateString()}


More damage to damage rocket

More damage to damage rocket

--- app/scripts/components/weapons/Freddie.coffee
+++ app/scripts/components/weapons/Freddie.coffee
@@ -95,7 +95,7 @@
           @attr hitFlash: { _red: 255, _green: 255, _blue: 0 }
           @hFlash = 5
           if enemy.absorbDamage?
-            enemy.absorbDamage(100)
+            enemy.absorbDamage(500)
           if enemy.has('Projectile')
             enemy.destroy()
           @eaten += 1


Filter out dropped students

Filter out dropped students

--- tutor/src/flux/scores.coffee
+++ tutor/src/flux/scores.coffee
@@ -23,7 +23,7 @@
 computeTaskCache = (data) ->
   for courseId, period of data
     for period, periodIndex in data[courseId]
-      for student, studentIndex in period.students
+      for student, studentIndex in period.students when student.is_dropped isnt true
         for task in student.data when task?
           TASK_ID_CACHE[task.id] = {task, courseId, period, periodIndex, studentIndex}

@@ -141,6 +141,12 @@

   exports:
+    getEnrolledScoresForPeriod: (courseId, periodId) ->
+      data = @_get(courseId)
+      scores = if periodId? then _.findWhere(data, period_id: periodId) else _.first(data)
+      if scores?
+        scores.students = _.reject(scores.students, 'is_dropped')
+      scores

     getTaskInfoById: (taskId) ->
       getTaskInfoById(taskId, @_local)


Set filename according to molecule name if available

--- src/coffee/main.coffee
+++ src/coffee/main.coffee
@@ -119,6 +119,7 @@
     url = buildUrl screenWidth, screenHeight, foregroundColor, backgroundColor, currentCompoundName
     if smilesMode
       url = buildSmilesUrl screenWidth, screenHeight, foregroundColor, backgroundColor, currentCompoundSmiles
+    downloadButton.attr 'download', if smilesMode then 'smiles_molecule' else currentCompoundName
     downloadButton.attr 'href', url

   # Updates the displayed preview.


FMC-460 - 'Error uploading patch file' occurs when no patch is specified for upload in Web Console

--- fabric/fmc-webui/src/main/webapp/app/controllers/patches_page.coffee
+++ fabric/fmc-webui/src/main/webapp/app/controllers/patches_page.coffee
@@ -31,6 +31,7 @@

     elements:
       "select[name=version-select]": "version_select"
+      "input[name=patch_file]": "file_input"
       ".patch-list": "patch_list"
       "form": "upload_form"
       "a.upload.btn": "upload_button"
@@ -89,7 +90,15 @@

       @apply_button.toggleClass 'disabled', @patch_files.length == 0

+      @upload_button.toggleClass 'disabled', @file_input.val() == ""
+
+      @file_input.change (event) =>
+        @upload_button.toggleClass 'disabled', @file_input.val() == ""
+
       @upload_button.click =>
+        if @upload_button.hasClass('disabled')
+          return false;
+
         app.flash
           kind: "info"
           title: "Uploading patch file to server"
@@ -101,7 +110,9 @@
               title: "Successfully uploaded patch file"
               hide_after: 2000

-          error: (data, textStatus, errorThrown) ->
+          error: (data, textStatus, errorThrown) =>
+            @file_input.val ""
+            @upload_button.addClass 'disabled'
             app.flash
               kind: "error"
               title: "Error uploading patch file: "


Test space preservation

Test space preservation

--- test/src/trix/text_view_test.coffee
+++ test/src/trix/text_view_test.coffee
@@ -19,6 +19,15 @@
   equal node.data, text.toString(), "child node contains text string"
   equal node.trixPosition, 0, "child node has a trixPosition property"
   equal node.trixLength, text.getLength(), "child node has a trixLength property"
+
+  elements = getElementsForText(createText(" !"))
+  equal elements[0].firstChild.data, "\u00a0!", "leading space replaced with non-breaking space"
+
+  elements = getElementsForText(createText("! "))
+  equal elements[0].firstChild.data, "!\u00a0", "trailing space replaced with non-breaking space"
+
+  elements = getElementsForText(createText("!   !"))
+  equal elements[0].firstChild.data, "! \u00a0 !", "multiple spaces preserved with non-breaking spaces"

   elements = getElementsForText(createText("Hello\n"))
   equal elements.length, 2, "two elements for string ending with a newline"


remove unhelpful unit tests

remove unhelpful unit tests

It may be useful to have unit tests for the methods that no longer do, but as
they were they only verified that they had a specific implementation.

--- test/test-transloadit-client.coffee
+++ test/test-transloadit-client.coffee
@@ -4,31 +4,6 @@
 TransloaditClient = require "../src/TransloaditClient"

 describe "TransloaditClient", ->
-  describe "constructor", ->
-    it "should set some default properties", ->
-      opts =
-        authKey    : "foo_key"
-        authSecret : "foo_secret"
-      client = new TransloaditClient opts
-      expect(client._authKey).to.equal "foo_key"
-      expect(client._authSecret).to.equal "foo_secret"
-      expect(client._service).to.equal "api2.transloadit.com"
-      expect(client._region).to.equal "us-east-1"
-      expect(client._protocol).to.equal "https://"
-
-    it "should allow overwriting some properties", ->
-      opts =
-        authKey    : "foo_key"
-        authSecret : "foo_secret"
-        service    : "foo_service"
-        region     : "foo_region"
-
-      client = new TransloaditClient opts
-      expect(client._authKey).to.equal "foo_key"
-      expect(client._authSecret).to.equal "foo_secret"
-      expect(client._service).to.equal "foo_service"
-      expect(client._region).to.equal "foo_region"
-
   describe "addStream", ->
     it "should properly add a stream", ->
       client = new TransloaditClient authKey: "foo_key", authSecret: "foo_secret"
@@ -58,304 +33,6 @@
         expect(stream).to.equal STREAM

       client.addFile NAME, PATH
-
-  describe "createAssembly", ->
-    it "should send a valid request", ->
-      client = new TransloaditClient authKey: "foo_key", authSecret: "foo_secret"
-      client._streams = "foo"
-
-      OPTS =
-        params  : "foo_params"
-        fields  : "foo_fields"
-
-      REQUEST_OPTS =
-        url     : "https://api2.transloadit.com/assemblies"
-        method  : "post"
-        timeout : 24 * 60 * 60 * 1000
-        params  : "foo_params"
-        fields  : "foo_fields"
-
-      ERR    = {}
-      RESULT =
-        ok: "foo_ok"
-
-      errCalls = 0
-      calls    = 0
-      CB = (err, result) ->
-        if err
-          errCalls++
-          expect(err).to.equal ERR
-        else
-          expect(result).to.equal RESULT
-
-        calls++
-
-      gently.expect client, "_remoteJson", (opts, cb2) ->
-        expect(opts).to.eql REQUEST_OPTS
-
-        cb2 ERR
-        expect(client._streams).to.eql {}
-
-        cb2 null, RESULT
-
-      client.createAssembly OPTS, CB
-      expect(errCalls).to.equal 1
-      expect(calls).to.equal 2
-
-      usedUrl = client.getLastUsedAssemblyUrl()
-      expect(usedUrl).to.equal "https://api2.transloadit.com/assemblies"
-
-  describe "deleteAssembly", ->
-    it "should find the assembly url, and then call DELETE on it", ->
-      client = new TransloaditClient authKey: "foo_key", authSecret: "foo_secret"
-
-      OPTS =
-        url     : "https://api2.transloadit.com/assemblies/foo_assembly_id"
-        timeout : 16000
-
-      ASSEMBLY_ID = "foo_assembly_id"
-      ERR    = {}
-      RESULT =
-        assembly_url: "foo_assembly_url"
-
-      errCalls = 0
-      calls    = 0
-      CB = (err, result) ->
-        if err
-          errCalls++
-          expect(err).to.equal ERR
-        else
-          expect(result).to.equal RESULT
-
-        calls++
-
-      gently.expect client, "_remoteJson", (opts, cb) ->
-        expect(opts).to.eql OPTS
-
-        cb ERR
-
-      client.deleteAssembly ASSEMBLY_ID, CB
-      expect(errCalls).to.equal 1
-      expect(calls).to.equal 1
-
-  describe "replayAssembly", ->
-    it "should send the proper request", ->
-      client = new TransloaditClient authKey: "foo_key", authSecret: "foo_secret"
-      ASSEMBLY_ID = "foo_assembly_id"
-      OPTS =
-        assembly_id: ASSEMBLY_ID
-        notify_url: "foo_notify_url"
-
-      REQUEST_OPTS =
-        url        : "https://api2.transloadit.com/assemblies/foo_assembly_id/replay"
-        method     : "post"
-        params     :
-          notify_url: "foo_notify_url"
-
-      CB = {}
-      gently.expect client, "_remoteJson", (opts, cb) ->
-        expect(opts).to.eql REQUEST_OPTS
-        expect(cb).to.eql CB
-
-      client.replayAssembly OPTS, CB
-
-  describe "replayAssemblyNotification", ->
-    it "should send the proper request", ->
-      client = new TransloaditClient authKey: "foo_key", authSecret: "foo_secret"
-
-      ASSEMBLY_ID = "foo_assembly_id"
-      url = "https://api2.transloadit.com/assembly_notifications/"
-      url += ASSEMBLY_ID + "/replay"
-
-      OPTS =
-        assembly_id: ASSEMBLY_ID
-        notify_url: "foo_notify_url"
-
-      REQUEST_OPTS =
-        url    : url
-        method : "post"
-        params :
-          notify_url: "foo_notify_url"
-
-      CB = {}
-      gently.expect client, "_remoteJson", (opts, cb) ->
-        expect(opts).to.eql REQUEST_OPTS
-        expect(cb).to.eql CB
-
-      client.replayAssemblyNotification OPTS, CB
-
-  describe "listAssemblies", ->
-    it "should send the proper request", ->
-      client = new TransloaditClient authKey: "foo_key", authSecret: "foo_secret"
-      url = "https://api2.transloadit.com/assemblies"
-
-      PARAMS =
-        foo: "bar"
-
-      REQUEST_OPTS =
-        url    : url
-        method : "get"
-        params : PARAMS
-
-      CB = {}
-      gently.expect client, "_remoteJson", (opts, cb) ->
-        expect(opts).to.eql REQUEST_OPTS
-        expect(cb).to.eql CB
-
-      client.listAssemblies PARAMS, CB
-
-  describe "listAssemblyNotifications", ->
-    it "should send the proper request", ->
-      client = new TransloaditClient authKey: "foo_key", authSecret: "foo_secret"
-      url = "https://api2.transloadit.com/assembly_notifications"
-
-      PARAMS =
-        foo: "bar"
-
-      REQUEST_OPTS =
-        url    : url
-        method : "get"
-        params : PARAMS
-
-      CB = {}
-      gently.expect client, "_remoteJson", (opts, cb) ->
-        expect(opts).to.eql REQUEST_OPTS
-        expect(cb).to.eql CB
-
-      client.listAssemblyNotifications PARAMS, CB
-
-  describe "createTemplate", ->
-    it "should send the proper request", ->
-      client = new TransloaditClient authKey: "foo_key", authSecret: "foo_secret"
-      url = "https://api2.transloadit.com/templates"
-
-      PARAMS =
-        foo: "bar"
-
-      REQUEST_OPTS =
-        url    : url
-        method : "post"
-        params : PARAMS
-
-      CB = {}
-      gently.expect client, "_remoteJson", (opts, cb) ->
-        expect(opts).to.eql REQUEST_OPTS
-
-      client.createTemplate PARAMS, CB
-
-  describe "editTemplate", ->
-    it "should send the proper request", ->
-      client = new TransloaditClient authKey: "foo_key", authSecret: "foo_secret"
-
-      TEMPLATE_ID = "foo_template_id"
-      url         = "https://api2.transloadit.com/templates/#{TEMPLATE_ID}"
-
-      PARAMS =
-        foo: "bar"
-
-      REQUEST_OPTS =
-        url    : url
-        method : "put"
-        params : PARAMS
-
-      CB = {}
-      gently.expect client, "_remoteJson", (opts, cb) ->
-        expect(opts).to.eql REQUEST_OPTS
-
-      client.editTemplate TEMPLATE_ID, PARAMS, CB
-
-  describe "deleteTemplate", ->
-    it "should send the proper request", ->
-      client = new TransloaditClient authKey: "foo_key", authSecret: "foo_secret"
-
-      TEMPLATE_ID = "foo_template_id"
-      url         = "https://api2.transloadit.com/templates/#{TEMPLATE_ID}"
-
-      REQUEST_OPTS =
-        url    : url
-        method : "del"
-        params : {}
-
-      CB = {}
-      gently.expect client, "_remoteJson", (opts, cb) ->
-        expect(opts).to.eql REQUEST_OPTS
-        expect(cb).to.eql CB
-
-      client.deleteTemplate TEMPLATE_ID, CB
-
-  describe "getTemplate", ->
-    it "should send the proper request", ->
-      client = new TransloaditClient authKey: "foo_key", authSecret: "foo_secret"
-
-      TEMPLATE_ID  = "foo_template_id"
-      url          = "https://api2.transloadit.com/templates/#{TEMPLATE_ID}"
-      REQUEST_OPTS =
-        url    : url
-        method : "get"
-        params : {}
-
-      CB = {}
-      gently.expect client, "_remoteJson", (opts, cb) ->
-        expect(opts).to.eql REQUEST_OPTS
-        expect(cb).to.eql CB
-
-      client.getTemplate TEMPLATE_ID, CB
-
-  describe "listTemplates", ->
-    it "should send the proper request", ->
-      client = new TransloaditClient authKey: "foo_key", authSecret: "foo_secret"
-      url = "https://api2.transloadit.com/templates"
-
-      PARAMS =
-        foo: "bar"
-
-      REQUEST_OPTS =
-        url    : url
-        method : "get"
-        params : PARAMS
-
-      CB = {}
-      gently.expect client, "_remoteJson", (opts, cb) ->
-        expect(opts).to.eql REQUEST_OPTS
-        expect(cb).to.eql CB
-
-      client.listTemplates PARAMS, CB
-
-  describe "getAssembly", ->
-    it "should find the assembly's URL and then send the request", ->
-      client = new TransloaditClient authKey: "foo_key", authSecret: "foo_secret"
-      OPTS =
-        url: "https://api2.transloadit.com/assemblies/foo_assembly_id"
-
-      ASSEMBLY_ID = "foo_assembly_id"
-      ERR    = {}
-      RESULT =
-        assembly_url: "foo_assembly_url"
-
-      errCalls = 0
-      calls    = 0
-      CB = (err, result) ->
-        if err
-          errCalls++
-          expect(err).to.equal ERR
-        else
-          expect(result).to.equal RESULT
-
-        calls++
-
-      gently.expect client, "_remoteJson", (opts, cb) ->
-        expect(opts).to.eql OPTS
-
-        cb ERR
-
-        gently.expect client, "_remoteJson", (opts2, cb2) ->
-          expect(opts2.url).to.eql RESULT.assembly_url
-
-        cb null, RESULT
-
-      client.getAssembly ASSEMBLY_ID, CB
-      expect(errCalls).to.equal 1
-      expect(calls).to.equal 1

   describe "_appendForm", ->
     it "should append all required fields to the request form", ->


Mesh: Throw a meaningful error if mesh data is too short.

--- engine/mesh.coffee
+++ engine/mesh.coffee
@@ -159,8 +159,12 @@
         vlen = @offsets[@offsets.length-2] # 4 byte units
         ilen = @offsets[@offsets.length-1] # 2 byte units
         offset = (@pack_offset or 0) + buffer_offset
-        va = new Float32Array data, offset, vlen
-        ia = new Uint16Array data, offset + vlen * 4, ilen
+        try
+            va = new Float32Array data, offset, vlen
+            ia = new Uint16Array data, offset + vlen * 4, ilen
+        catch e
+            e = Error "Mesh #{@name} is corrupt"
+            throw e
         @context.main_loop.add_frame_callback =>
             @load_from_va_ia va, ia


DS.ActiveModelSerializer already defines `serializeBelongsTo`

--- app/assets/javascripts/models/concerns/venta/venta_serializer.js.coffee
+++ app/assets/javascripts/models/concerns/venta/venta_serializer.js.coffee
@@ -1,8 +1,4 @@
 get = Ember.get
-  serializeBelongsTo: (record, json, relationship) ->
-    key = relationship.key
-    json["#{key}_id"] = get(record, key).get("id")
-
 Bandango.VentaSerializer = DS.ActiveModelSerializer.extend
   serializeHasMany: (record, json, relationship) ->
     key = relationship.key


More specs

More specs

--- spec/modx-spec.coffee
+++ spec/modx-spec.coffee
@@ -1,4 +1,6 @@
 path = require 'path'
+fs = require 'fs-plus'
+temp = require 'temp'
 {$} = require 'atom-space-pen-views'

 describe 'MODX Revolution', ->
@@ -56,3 +58,30 @@

         atom.commands.dispatch(modxGeneratorView.element, "core:cancel")
         expect(modxGeneratorView.panel.isVisible()).toBeFalsy()
+
+
+  describe "when a package is generated", ->
+    [packageName, packagePath, packageRoot] = []
+
+    beforeEach ->
+      spyOn(atom, "open")
+
+      packageRoot = temp.mkdirSync('atom')
+      packageName = "modx-transport-package"
+      packagePath = path.join(packageRoot, packageName)
+      fs.removeSync(packageRoot)
+
+    afterEach ->
+      fs.removeSync(packageRoot)
+
+    describe 'when creating a package', ->
+      it "scaffolds a package and opens it", ->
+        executeCommand ->
+          atom.commands.dispatch(workspaceElement, "package-generator:generate-package")
+          packageGeneratorView = $(workspaceElement).find(".modx-generator").view()
+          expect(packageGeneratorView.hasParent()).toBeTruthy()
+          packageGeneratorView.miniEditor.setText(packagePath)
+          atom.commands.dispatch(packageGeneratorView.element, "core:confirm")
+          waitsFor ->
+            atom.open.callCount is 1
+          expect(atom.open.callCount).toBe 1


Specced missing "dest" and missing "orig" cases

--- spec/ride_spec.coffee
+++ spec/ride_spec.coffee
@@ -44,3 +44,13 @@
         ride = {orig:{txitle:"hamburg"},dest:{title:"berlin"}}
         factory.createRide(ride)
         expect(builder.create).toHaveBeenCalledWith(ride)
+
+      it 'should call RideFromRideObjectBuilder with only orig', ->
+        ride = {orig:{txitle:"hamburg"}}
+        factory.createRide(ride)
+        expect(builder.create).toHaveBeenCalledWith(ride)
+
+      it 'should call RideFromRideObjectBuilder with only dest', ->
+        ride = {dest:{title:"berlin"}}
+        factory.createRide(ride)
+        expect(builder.create).toHaveBeenCalledWith(ride)


Check for duplicate entries.

Check for duplicate entries.

--- scripts/tickets.coffee
+++ scripts/tickets.coffee
@@ -51,13 +51,17 @@
     robot.respond /ticket$/i, (msg) ->
         username = "#{msg.envelope.user.name}"
         points[username] ?= 0
+        entry = bought.indexOf(username)

         if points[username] >= cost and raffle is on
-            points[username] -= cost
-            bought.push(username)
-            save(robot)
-            msg.send "Test: raffle entered."
-            return
+            if entry = -1
+                points[username] -= cost
+                bought.push(username)
+                save(robot)
+                msg.send "Test: raffle entered."
+            if entry >= 0
+                msg.send "Test: Seems you're already in the raffle."
+

     # Close raffle, drawn winner and reset.
     robot.respond /raffle draw$/i, (msg) ->


turn subscriptions off by default

turn subscriptions off by default

--- services/web/config/settings.defaults.coffee
+++ services/web/config/settings.defaults.coffee
@@ -166,6 +166,8 @@
 		price: 0
 		features: defaultFeatures
 	}]
+
+	enableSubscriptions:false

 	# i18n
 	# ------


revise spec text

revise spec text

--- spec/tabs-spec.coffee
+++ spec/tabs-spec.coffee
@@ -309,7 +309,7 @@
       expect(tabBar.getTabs().length).toBe 2
       expect($(tabBar).find('.tab:contains(sample.js)')).not.toExist()

-  describe "scrollToTab", ->
+  describe "when an item is activated", ->
     [item3] = []
     beforeEach ->
       item3 = new TestView("Item 3")
@@ -328,7 +328,7 @@
       # Expect there to be content to scroll
       expect(tabBar.scrollWidth).toBeGreaterThan tabBar.clientWidth

-    it "does not scroll new active pane item is completely visible", ->
+    it "does not scroll to the item when it is visible", ->
       pane.activateItem(item1)
       expect(tabBar.scrollLeft).toBe 0

@@ -341,7 +341,7 @@
       pane.activateItem(item3)
       expect(tabBar.scrollLeft).not.toBe 0

-    it "scrolls when the new active pane item is not completely visible", ->
+    it "scrolls to the item when it isn't completely visible", ->
       tabBar.scrollLeft = 5
       expect(tabBar.scrollLeft).toBe 5 # This can be 0 if there is no horizontal scrollbar


Support the new pending pane item API

Support the new pending pane item API

--- spec/tabs-spec.coffee
+++ spec/tabs-spec.coffee
@@ -839,7 +839,13 @@
         expect(pane.getItems().length).toBe 1
         expect(tabBar).toHaveClass 'hidden'

-  if atom.workspace.buildTextEditor().isPending?
+  if atom.workspace.buildTextEditor().isPending? or atom.workspace.getActivePane().getActiveItem?
+    isPending = (item) ->
+      if item.isPending?
+        item.isPending()
+      else
+        atom.workspace.getActivePane().getPendingItem() is item
+
     describe "when tab's pane item is pending", ->
       beforeEach ->
         pane.destroyItems()
@@ -863,9 +869,9 @@

           runs ->
             pane.activateItem(editor1)
-            expect(editor1.isPending()).toBe true
+            expect(isPending(editor1)).toBe true
             atom.commands.dispatch(atom.views.getView(atom.workspace.getActivePane()), 'tabs:keep-pending-tab')
-            expect(editor1.isPending()).toBe false
+            expect(isPending(editor1)).toBe false

       describe "when there is a temp tab already", ->
         it "it will replace an existing temporary tab", ->
@@ -932,11 +938,11 @@
           tabBar2 = new TabBarView
           tabBar2.initialize(pane2)
           newEditor = pane2.getActiveItem()
-          expect(newEditor.isPending()).toBe false
+          expect(isPending(newEditor)).toBe false
           expect($(tabBar2.tabForItem(newEditor)).find('.title')).not.toHaveClass 'temp'

         it "keeps the pending tab in the old pane", ->
-          expect(editor1.isPending()).toBe true
+          expect(isPending(editor1)).toBe true
           expect($(tabBar.tabForItem(editor1)).find('.title')).toHaveClass 'temp'

       describe "when dragging a pending tab to a different pane", ->


weather script

weather script

--- scripts/weather.coffee
+++ scripts/weather.coffee
@@ -17,7 +17,6 @@

     rp(locationOptions)
       .then( (data) ->
-        console.log(data)
         latitude = data.results[0].geometry.location.lat
         longitude = data.results[0].geometry.location.lng
         latlng = latitude + ',' + longitude
@@ -35,7 +34,7 @@
             summary = data.currently.summary
             icon = data.currently.icon
             feelsLike = data.currently.apparentTemperature
-            dailyData = data.daily[0]
+            dailyData = data.daily.data[0]
             maxTemp = dailyData.temperatureMax
             minTemp = dailyData.temperatureMin
             msg.send('Current Temp: ' + currentTemp + '\n' +


Simplified session

Simplified session

--- public/scripts/backbone-schedule/models/session.coffee
+++ public/scripts/backbone-schedule/models/session.coffee
@@ -7,21 +7,14 @@
     defaults:
       access_token: null
       user_id: null
+      user: null

     initalize: ->
       @load()

     authenticated: ->
-      !!@get("access_token")
+      !!@get("access_token") and !!@get("user")

-    save: (auth_hash)->
-      $.cookie('user_id', auth_hash.id)
-      $.cookie('access_token', auth_hash.access_token)
-
-    load: ->
-      @set
-        user_id: $.cookie('user_id')
-        access_token: $.cookie('access_token')

   Session
 )


save dirty

save dirty

--- lib/log-filter.coffee
+++ lib/log-filter.coffee
@@ -68,6 +68,27 @@
     else
       @results.text = for line, i in buffer.getLines()
         if regex.test(line) then else i
+
+    console.log (@results)
+
+    if 0 < @results.text.length
+      resultsTailLength = 2
+      if 0 < resultsTailLength
+        resultsWithTail = []
+        for lineNumber, lineIndex in @results.text
+          resultsWithTail.push(lineNumber)
+          numberOfTailedLine = 0
+          while ( numberOfTailedLine < resultsTailLength ) and lineNumber + numberOfTailedLine + 1 < @textEditor.getLineCount() and lineIndex + 1 < @results.text.length and lineNumber + numberOfTailedLine + 1 < @results.text[lineIndex+1]
+            resultsWithTail.push(lineNumber + numberOfTailedLine + 1)
+            numberOfTailedLine = numberOfTailedLine + 1
+        @results.text = resultsWithTail
+
+      # resultsHeadLength = 2
+      # if 0 < resultsHeadLength
+      #   for num in [@results.text.length..0]
+
+    console.log (@results)
+
     @filterLines()

   performLevelFilter: (scopes) ->


[grunt] avoid global gettext instance inside `Gettext` module

--- modules/gettext.coffee
+++ modules/gettext.coffee
@@ -1,7 +1,6 @@
 _           = require('lodash')
 path        = require('path')
 NodeGettext = require('node-gettext')
-gettext     = new NodeGettext()

 module.exports = (grunt) ->
   return class Gettext
@@ -18,6 +17,8 @@
      *       * https://github.com/LotusTM/Kotsu/issues/45
     ###
     constructor: (@locales, @localesDir) ->
+      @gettext = new NodeGettext()
+
       @locales.forEach (locale) =>

         grunt.file.expand({ cwd: @localesDir + '/' + locale, filter: 'isFile' }, '**/*.po').forEach (filepath) =>
@@ -27,9 +28,10 @@
           domain   = if domain == defaultDomain then locale else locale + ':' + domain
           messages = grunt.file.read(@localesDir + '/' + locale + '/' + filepath, { encoding: null })

-          gettext.addTextdomain(domain, messages)
+          @gettext.addTextdomain(domain, messages)

     installNunjucksGlobals: (env, currentLocale) ->
+      gettext = @gettext

       # --------------
       # i18n functions


Add approve button to AppView for super-admins to approve app

--- client/landing/app/Applications/Apps.kdapplication/Views/appview.coffee
+++ client/landing/app/Applications/Apps.kdapplication/Views/appview.coffee
@@ -31,6 +31,23 @@
             callback? err
       ]
     , app
+
+    if KD.checkFlag 'super-admin'
+      @approveButton = new KDToggleButton
+        style           : "kdwhitebtn"
+        dataPath        : "approved"
+        defaultState    : if app.approved then "Disapprove" else "Approve"
+        states          : [
+          "Approve", (callback)->
+            app.approve yes, (err)->
+              callback? err
+          "Disapprove", (callback)->
+            app.approve no, (err)->
+              callback? err
+        ]
+      , app
+    else
+      @approveButton = new KDView

     app.checkIfLikedBefore (err, likedBefore)=>
       if likedBefore
@@ -52,7 +69,7 @@
           parentId : null
           callback : (item)=>
             appsController.installApp app, app.versions[item.data.id], (err)=>
-              @hideLoader()
+              if err then warn err

       @installButton = new KDButtonViewWithMenu
         title     : "Install Now"
@@ -115,6 +132,7 @@

         {{ @putThumb #(manifest)}}

+      {{> @approveButton}}

{{#(title)}}by {{#(manifest.author)}}


Do not set the channel if setP2PKeys fails in broker-client

--- client/landing/app/Applications/Chat.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Chat.kdapplication/AppController.coffee
@@ -17,7 +17,7 @@
   handleChatOpen:({routingKey, bindingKey, publicName})->
     channel = KD.remote.mq.setP2PKeys \
       publicName, { routingKey, bindingKey }, 'secret'
-    @channels[publicName] = channel
+    @channels[publicName] = channel  if channel?

   create:(invitees, callback)->
     chatPanel = KD.getSingleton 'chatPanel'


No need to handle those manually

No need to handle those manually

--- client/landing/app/Applications/Chat.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Chat.kdapplication/AppController.coffee
@@ -24,13 +24,7 @@
     JChatConversation.create invitees, (err, conversation)=>
       return callback err  if err
       chatChannel = @subscribe conversation.publicName
-      chatPanel.createConversation {chatChannel, conversation, invitees}
       callback null, new ChannelWrapper chatChannel
-
-      @handleChatRequest {
-        invitee     : KD.whoami().profile.nickname
-        publicName  : chatConversation.publicName
-      }, callback

   subscribe:(publicName)->
     options = { serviceType: 'chat', isP2P: yes, exchange: 'chat' }


Fix scroll when PoiControl expended

Fix scroll when PoiControl expended

--- views/components/info/alert.cjsx
+++ views/components/info/alert.cjsx
@@ -10,22 +10,24 @@
     overflow: false
     messagewidth: 0

-  updateAlert: (e, overflow) ->
+  updateAlert: (e, overflow, alertChanged) ->
     displayMessage = @message
     @setState
       message: displayMessage
       overflowAnim: if overflow then 'overflow-anim' else ''
+    if alertChanged then @handleAlertChanged()

   alertWidthChange: (e) ->
+    @alertWidth = e.detail.alertWidth
     if @state.overflowAnim isnt ''
       @message = @messageOld
-    @updateAlert()
+    @needUpdate = true
+    @updateAlert(null, @state.overflowAnim)

   handleMessageScroll: (overflow) ->
     overflowed = @state.overflowAnim isnt ''
-    return if overflow is overflowed
+    return if overflow is overflowed && !@needUpdate
     if overflow
-      @messageOld = @message
       if React.isValidElement @message
         @message = <span>{@message}<span>　　　　　</span>{@message}<span>　　　　　</span></span>
       else
@@ -35,12 +37,8 @@
   handleAlertChanged: (e) ->
     @setState
       messageWidth: document.getElementById('alert-area').offsetWidth
-    if e?.detail?.alertWidth?
-      containerWidth = e.detail.alertWidth
-    else
-      containerWidth = document.getElementById('alert-container').offsetWidth
     contentWidth = document.getElementById('alert-area').offsetWidth
-    overflow = containerWidth < contentWidth
+    overflow = @alertWidth < contentWidth
     @handleMessageScroll(overflow)

   handleAlert: (e) ->
@@ -61,13 +59,17 @@
       else
         @stickyEnd = null
       @message = e.detail.message
+      @messageOld = @message
       @messageType = e.detail.type
       @updateAlert()

   componentDidMount: ->
     window.addEventListener 'poi.alert', @handleAlert
     window.addEventListener 'alert.change', @alertWidthChange
+    @alertWidth = document.getElementById('alert-container').offsetWidth
     @message = @state.message
+    @messageOld = @message
+    @needUpdate = false
     observer = new MutationObserver(@handleAlertChanged)
     target = document.getElementById('alert-area')
     options =
@@ -75,6 +77,12 @@
       attributes: true
       subtree: true
     observer.observe(target, options)
+  componentDidUpdate: ->
+    setTimeout =>
+      @alertWidth = document.getElementById('alert-container').offsetWidth
+    , 350
+    if @needUpdate then @handleAlertChanged()
+    @needUpdate = false
   componentWillUnmount: ->
     window.removeEventListener 'poi.alert', @handleAlert
     window.removeEventListener 'alert.change', @alertWidthChange


Take 1 ms off the fromTime operator

Take 1 ms off the fromTime operator

--- assets/javascripts/custom_rx_operators.coffee
+++ assets/javascripts/custom_rx_operators.coffee
@@ -5,7 +5,9 @@
   timers = R.keys(timesAndValues)
     .map (relativeTime) -> [parseInt(relativeTime), timesAndValues[relativeTime]]
     .map ([time, value]) ->
-      Rx.Observable.timer(time, scheduler).map R.I(value)
+      # 1 is substracted at the moment to not distract users
+      # with a 1ms delay due to test scheduler
+      Rx.Observable.timer(Math.max(time - 1, 0), scheduler).map R.I(value)

   Rx.Observable.merge(timers)


..nested interpolations, sigh

..nested interpolations, sigh

--- lib/generators/angularjs/scaffold/templates/plural_model_name.js.coffee
+++ lib/generators/angularjs/scaffold/templates/plural_model_name.js.coffee
@@ -1,7 +1,7 @@
 root = global ? window

-angular.module(<%= "#{'@plural_model_name'}" %>, ["ngResource"]).factory <%= "#{'@model_name'}" %>, ($resource) ->
-  <%= "#{@model_name}" %> = $resource(<%= "\'/#{@plural_model_name}/:id\'" %>,
+angular.module(<%= '"' + "#{@plural_model_name}" + '"' %>, ["ngResource"]).factory <%= '"' + "#{@model_name}" + '"' } %>, ($resource) ->
+  <%= "#{@model_name}" %> = $resource(<%= '"' + "/#{@plural_model_name}/:id" + '"'%>,
     id: "@id"
   ,
     update:


add ab_test customBinding

add ab_test customBinding

--- js/libs/circle/custom-bindings.coffee
+++ js/libs/circle/custom-bindings.coffee
@@ -7,6 +7,65 @@
   init: (el, valueAccessor, allBindingsAccessor, viewModel, bindingContext) =>
     options = valueAccessor()
     $(el).slider(options)
+
+# returns random integer in range [0, n)
+randInt = (n) ->
+  Math.floor(Math.random() * n)
+
+# The a/b tests should be an observable on viewModel called ab_tests()
+# If an ab_test hasn't been chosen, it will set a random value and send it to
+# Kissmetrics
+# Example usage:
+#   %p{href: "#", data-bind: "ab_test: {test: 'Show beta text', option: true}"}
+#     This is the text that will show up if option is set to true by KissMetrics!
+#   %p{href: "#", data-bind: "ab_test: {test: 'Show beta text', option: false}"}
+#     This is the text that will show up if option is set to false by KissMetrics!
+ko.bindingHandlers.ab_test =
+  init: (el, valueAccessor, allBindingsAccessor, viewModel, bindingContext) =>
+    test = valueAccessor().test
+    option = valueAccessor().option
+
+    if viewModel.ab_tests?
+      ab_tests = viewModel.ab_tests()
+    else
+      console.log 'ab_tests is not defined on the viewModel!'
+      ab_tests = {}
+
+
+    # set default option to first one if the a/b test isn't defined in
+    # ab-test-definitions.js
+    if not ab_tests[test]?
+      console.log "No A/B test definitions for '#{test}'!"
+
+      ab_tests[test] =
+        options: [option]
+        chosen_p: true
+        choice: option
+      viewModel.ab_tests(ab_tests)
+
+      console.log "Chose #{option} for test '#{test}', anyway"
+
+    if not ab_tests[test].chosen_p?
+      i = randInt(ab_tests[test].options.length)
+      choice = ab_tests[test].options[i]
+      ab_tests[test].chosen_p = true
+      ab_tests[test].choice = choice
+
+      viewModel.ab_tests(ab_tests)
+
+      console.log "Too late for test '#{test}' from KM, selected #{choice}"
+
+      # send the choice manually to KissMetrics
+      # I don't think this actually works, waiting for KM to email me (dwwoelfel)
+      kmq_choice = {}
+      kmq_choice[test] = choice
+      _kmq.push(["set", kmq_choice])
+
+    display_p = ab_tests[test].choice is option
+
+    console.log("Displaying #{option} for test '#{test}':", display_p)
+
+    if not display_p then $(el).css('display', 'none')

 # Takes any kind of jQueryExtension, e.g. popover, tooltip, etc.
 # Example usage:


special case for datepicker

special case for datepicker

--- bokehjs/test/test_common/test_defaults.coffee
+++ bokehjs/test/test_common/test_defaults.coffee
@@ -33,14 +33,25 @@
   python_missing = []
   coffee_missing = []
   for k, v of coffee_defaults
+
+    # special case for date picker, default is "now"
+    if name == 'DatePicker' and k == 'value'
+      continue
+
     if k == 'id'
       continue
+
     if k of python_defaults
       py_v = python_defaults[k]
       if not _.isEqual(py_v, v)
+
+        # these two conditionals compare 'foo' and {value: 'foo'}
         if _.isObject(v) and 'value' of v and _.isEqual(py_v, v['value'])
           continue
-
+        if _.isObject(py_v) and 'value' of py_v and _.isEqual(py_v['value'], v)
+          continue
+
+        # compare arrays of objects
         if _.isArray(v) and _.isArray(py_v)
           equal = true


wait before before creating krb5 host principal

--- core/krb5_server.coffee
+++ core/krb5_server.coffee
@@ -331,7 +331,12 @@
 module.exports.push name: 'Krb5 Server # Populate', timeout: -1, callback: (ctx, next) ->
   {realm, principals, kadmin_principal, kadmin_password, kadmin_server} = ctx.config.krb5_server
   modified = false
-  createMachinePrincipal = ->
+  do_wait = ->
+    # It takes time after Kerberos is started and before `kadmin` is really ready
+    ctx.waitForExecution "kadmin -p #{kadmin_principal} -w #{kadmin_password} -s #{kadmin_server} -q ?", (err) ->
+      return next err if err
+      do_createMachinePrincipal()
+  do_createMachinePrincipal = ->
     ctx.log "Create principal host/#{ctx.config.host}@#{realm}"
     ctx.krb5_addprinc
       principal: "host/#{ctx.config.host}@#{realm}"
@@ -342,8 +347,8 @@
     , (err, created) ->
       return next err if err
       modified = true if created
-      createConfigPrincipals()
-  createConfigPrincipals = ->
+      do_createConfigPrincipals()
+  do_createConfigPrincipals = ->
     each(principals)
     .on 'item', (principal, next) ->
       ctx.log "Create principal {principal}"
@@ -357,14 +362,14 @@
         modified = true if created
         next()
     .on 'both', (err) ->
-      done err
-  done = (err) ->
+      do_end err
+  do_end = (err) ->
     next err, if modified then ctx.OK else ctx.PASS
-  createMachinePrincipal()
-
-
-
-
-
-
-
+  do_wait()
+
+
+
+
+
+
+


fix rest of selection tests

fix rest of selection tests

--- tests/scripts/unit/selection.coffee
+++ tests/scripts/unit/selection.coffee
@@ -85,11 +85,11 @@
         'insert at beginning of selection':
           lines: ['
0123|45|6789
']
           fn: (editor) -> editor.insertAt(4, "A")
-          expected: ['
0123A|45|6789
']
+          expected: ['
0123|A45|6789
']
         'insert at end of selection':
           lines: ['
0123|45|6789
']
           fn: (editor) -> editor.insertAt(6, "A")
-          expected: ['
0123|45A|6789
'] # TODO the desired behavior is |45|A
+          expected: ['
0123|45|A6789
']
         'insert in middle of selection':
           lines: ['
0123|45|6789
']
           fn: (editor) -> editor.insertAt(5, "A")
@@ -97,11 +97,11 @@
         'insert newline at beginning of selection':
           lines: ['
0123|45|6789
']
           fn: (editor) -> editor.insertAt(4, "\n")
-          expected: ['
0123
', '
|45|6789
']
+          expected: ['
0123|
', '
45|6789
']
         'insert newline at end of selection':
           lines: ['
0123|45|6789
']
           fn: (editor) -> editor.insertAt(6, "\n")
-          expected: ['
0123|45
', '
|6789
']
+          expected: ['
0123|45|
', '
6789
']
         'insert newline at middle of selection':
           lines: ['
0123|45|6789
']
           fn: (editor) -> editor.insertAt(5, "\n")
@@ -110,6 +110,7 @@
           lines: ['

0123456789

', '0123|45|6789
']
           fn: (editor) -> editor.insertAt(10, "\n")
           expected: [0, '

', 1]
+
       'delete tests':
         'delete before':
           lines: ['
0123|45|6789
']


Get rid of more logging.

Get rid of more logging.

--- app/js/loader/loader_common.coffee
+++ app/js/loader/loader_common.coffee
@@ -91,7 +91,6 @@
     if document.body
       whenHasBody()
     else
-      console.log 'waiting #{i}...'
       setTimeout i, tryToInit(i+1)

   tryToInit(1)()


Remove stopAnnotating from public api

Remove stopAnnotating from public api

--- app/js/loader/loader_common.coffee
+++ app/js/loader/loader_common.coffee
@@ -12,7 +12,7 @@

   # Create proxy object that stores all calls
   # proxies calls from external content page to the js-library "jail" iframe's "FactlinkJailRoot"..
-  methods = 'on,triggerClick,highlightAdditionalFactlinks,stopAnnotating,showLoadedNotification,scrollTo,openFactlinkModal,initializeFactlinkButton,showProxyMessage'.split(',')
+  methods = 'on,triggerClick,highlightAdditionalFactlinks,showLoadedNotification,scrollTo,openFactlinkModal,initializeFactlinkButton,showProxyMessage'.split(',')

   storedMethodCalls = []


edit how stats page workflow list populated

--- app/pages/project/stats/index.cjsx
+++ app/pages/project/stats/index.cjsx
@@ -3,16 +3,18 @@
 PromiseToSetState = require '../../../lib/promise-to-set-state'
 qs = require 'qs'
 ProjectStatsPage = require './stats'
+getWorkflowsInOrder = require '../../../lib/get-workflows-in-order'

 ProjectStatsPageController = React.createClass
   mixins: [History, PromiseToSetState]

   getInitialState: ->
-    workflowList: (null for [1..@props.project.links.workflows.length])
+    workflowList: []

   componentDidMount: ->
-    for workflowID, idx in @props.project.links.workflows
-      @getWorkflow(workflowID, idx);
+    getWorkflowsInOrder(@props.project)
+      .then (workflows) =>
+        @setState workflowList: workflows

   handleGraphChange: (which, e) ->
     query = qs.parse location.search.slice 1
@@ -20,7 +22,7 @@
     query["#{which}Range"] = undefined
     {owner, name} = @props.params
     @history.replaceState(null, "/projects/#{owner}/#{name}/stats/", query)
-
+
   handleWorkflowChange: (which, e) ->
     query = qs.parse location.search.slice 1
     [name, value] = e.target.value.split('=')
@@ -41,13 +43,13 @@
   getQuery: (which) ->
     qs.parse(location.search.slice(1))[which]

-  getWorkflow: (workflowID, idx) ->
-    @props.project.get('workflows', id: workflowID).then ([workflow]) =>
-      unless workflow?
-        throw new Error "No workflow #{workflowID} for project #{@props.project.id}"
-      currentList = @state.workflowList
-      currentList[idx] = workflow
-      @setState({workflowList: currentList})
+  # getWorkflow: (workflowID, idx) ->
+  #   @props.project.get('workflows', id: workflowID).then ([workflow]) =>
+  #     unless workflow?
+  #       throw new Error "No workflow #{workflowID} for project #{@props.project.id}"
+  #     currentList = @state.workflowList
+  #     currentList[idx] = workflow
+  #     @setState({workflowList: currentList})

   render: ->
     queryProps =


buildItemPathTree without returning comprehension.

--- js/src/tree.coffee
+++ js/src/tree.coffee
@@ -45,6 +45,7 @@
   current = parentItem.add(source, selector)
   for node in filter(node.childNodes, (node)-> node.nodeType == 1)
     buildItemPathTree(source, node, current, idMasher)
+  return

 this.output = output = (write, nodeItem, sources, indent=2, level=0, parentRatio=0)->


social: remove unnecessary user instance from fetchGuestUser function

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -675,8 +675,6 @@

   @fetchGuestUser = (callback)->

-    user = new JUser()
-
     username      = @createGuestUsername()

     account = new JAccount()


stopParticipant(): Remove node/discovered participant

Fixes #124

--- src/coordinator.coffee
+++ src/coordinator.coffee
@@ -241,14 +241,30 @@
         return callback err, processes

   stopParticipant: (node, component, callback) ->
+    return callback new Error "stopParticipant(): Missing node argument" if not (node? and typeof node == 'string')
+
     processes = {}
     for k, v of @processes
       if k == node
         processes[k] = v
+    delete @nodes[node]
+
+    removeDiscoveredParticipants = (role) =>
+      keep = {}
+      for id, def of @participants
+        match = def.role == role
+        keep[id] = def if not match
+      @participants = keep
+
+    # we know it should stop sending discovery, pre-emptively remove
+    removeDiscoveredParticipants node
+    @emit 'graph-changed'
     setup.killProcesses processes, 'SIGTERM', (err) =>
       return callback err
       for k, v of processes
         delete @process[k]
+      # might have been discovered again during shutdown
+      removeDiscoveredParticipants node
       return callback null, processes

   updateNodeMetadata: (node, metadata, callback) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -148,7 +148,7 @@
           Module = Module.constantize()
         else

-        self.extend Module(I, self) unless I.incl
+        self.extend Module(I, self) unless I.included_modules

       return self


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -147,7 +147,7 @@
           moduleName = Module
           Module = Module.constantize()
         else
-          for key
+          for key, value of root

         unless I.included_modules.include moduleName
           I.included_modules.push moduleName


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -147,6 +147,7 @@
           moduleName = Module
           Module = Module.constantize()
         else if moduleName = Module._name
+          #
         else
           # Attempt to look up module in global namespace
           for key, value of root