Attempt to simplify mysql server sock fix

--- actions/mysql_server.coffee
+++ actions/mysql_server.coffee
@@ -36,7 +36,6 @@
 Install the Mysql database server. Secure the temporary directory.
 ###
 module.exports.push name: 'Mysql Server # Package', timeout: -1, callback: (ctx, next) ->
-  {sql_on_install} = ctx.config.mysql_server
   modified = false
   do_install = ->
     ctx.service
@@ -66,48 +65,75 @@
       , (err, updated) ->
         return next err if err
         modified = true if updated
-        do_socket()
-  do_socket = ->
-    # Create the socket inside "/tmp" instead of default "/var/lib/mysql/mysql.sock". That
-    # way, on restart, there won't be any existing file preventing the mysql server to start
-    ctx.ini
-      destination: '/etc/my.cnf'
-      content: mysqld: socket: '/tmp/mysql/mysql.sock'
-      merge: true
-      backup: true
-    , (err, updated) ->
-      return next err if err
-      modified = true if updated
-      ctx.link
-        source: '/tmp/mysql/mysql.sock'
-        destination: '/var/lib/mysql/mysql.sock'
-      , (err, linked) ->
-        modified = true if linked
-        do_start()
+        do_end()
+  # do_socket = ->
+  #   # Create the socket inside "/tmp" instead of default "/var/lib/mysql/mysql.sock". That
+  #   # way, on restart, there won't be any existing file preventing the mysql server to start
+  #   ctx.ini
+  #     destination: '/etc/my.cnf'
+  #     content: mysqld: socket: '/tmp/mysql/mysql.sock'
+  #     merge: true
+  #     backup: true
+  #   , (err, updated) ->
+  #     return next err if err
+  #     modified = true if updated
+  #     ctx.link
+  #       source: '/tmp/mysql/mysql.sock'
+  #       destination: '/var/lib/mysql/mysql.sock'
+  #     , (err, linked) ->
+  #       modified = true if linked
+  #       do_start()
+  do_end = ->
+    next null, if modified then ctx.OK else ctx.PASS
+  do_install()
+
+module.exports.push name: 'Mysql Server # Start', callback: (ctx, next) ->
+  modified = false
   do_start = ->
     ctx.service
       name: 'mysql-server'
       srv_name: 'mysqld'
       action: 'start'
     , (err, started) ->
-      return next err if err
+      # return next err if err
+      return do_clean_sock() if err
       modified = true if started
-      do_sql()
-  do_sql = ->
-    each(sql_on_install)
-    .on 'item', (sql, next) ->
-      cmd = "mysql -uroot -e \"#{escape sql}\""
-      ctx.log "Execute: #{cmd}"
-      ctx.execute
-        cmd: cmd
-        code_skipped: 1
-      , (err, executed) ->
+      do_end()
+  do_clean_sock = ->
+    console.log 'do_clean_sock'
+    ctx.remove
+      # destination: "/tmp/mysql/mysql.sock"
+      destination: "/var/lib/mysql/mysql.sock"
+    , (err, removed) ->
+      return next err if err
+      return next new Error 'Failed to install mysqld' unless removed
+      ctx.service
+        name: 'mysql-server'
+        srv_name: 'mysqld'
+        action: 'start'
+      , (err, started) ->
         return next err if err
-        modified = true if executed
-        next()
-    .on 'both', (err) ->
-      next err, ctx.OK
-  do_install()
+        modified = true if started
+        do_end()
+  do_end = ->
+    next null, if modified then ctx.OK else ctx.PASS
+  do_start()
+
+module.exports.push name: 'Mysql Server # Populate', callback: (ctx, next) ->
+  {sql_on_install} = ctx.config.mysql_server
+  each(sql_on_install)
+  .on 'item', (sql, next) ->
+    cmd = "mysql -uroot -e \"#{escape sql}\""
+    ctx.log "Execute: #{cmd}"
+    ctx.execute
+      cmd: cmd
+      code_skipped: 1
+    , (err, executed) ->
+      return next err if err
+      modified = true if executed
+      next()
+  .on 'both', (err) ->
+    next err, ctx.PASS

 ###
 Secure Installation


Fix AWS.S3.ManagedUpload tests to run async

--- test/s3/managed_upload.spec.coffee
+++ test/s3/managed_upload.spec.coffee
@@ -3,7 +3,7 @@

 body = (size) ->
   try
-    return new Blob((0 for i in [0..size]))
+    return new Blob((0 for i in [0...size]))
   catch e
     return new AWS.util.Buffer(size)

@@ -54,7 +54,7 @@
     it 'uses a default service object if none provided', ->
       expect(-> new AWS.S3.ManagedUpload()).to.throw('params.Body is required')

-    it 'uploads a single part if size is less than min multipart size', ->
+    it 'uploads a single part if size is less than min multipart size', (done) ->
       reqs = helpers.mockResponses [
         data: ETag: 'ETAG'
       ]
@@ -65,6 +65,7 @@
         expect(data.Location).to.equal('https://bucket.s3.mock-region.amazonaws.com/key')
         expect(helpers.operationsForRequests(reqs)).to.eql ['s3.putObject']
         expect(reqs[0].params.ContentEncoding).to.equal('encoding')
+        done()

     it 'can fail a single part', ->
       reqs = helpers.mockResponses [
@@ -75,7 +76,7 @@
       expect(data).not.to.exist
       expect(err.message).to.equal('ERROR')

-    it 'uploads multipart if size is greater than min multipart size', ->
+    it 'uploads multipart if size is greater than min multipart size', (done) ->
       reqs = helpers.mockResponses [
         { data: UploadId: 'uploadId' }
         { data: ETag: 'ETAG1' }
@@ -113,8 +114,9 @@
           { ETag: 'ETAG3', PartNumber: 3 }
           { ETag: 'ETAG4', PartNumber: 4 }
         ]
-
-    it 'aborts if ETag is not in response', ->
+        done()
+
+    it 'aborts if ETag is not in response', (done) ->
       helpers.spyOn(AWS.util, 'isBrowser').andReturn true
       reqs = helpers.mockResponses [
         { data: UploadId: 'uploadId' }
@@ -130,8 +132,9 @@
         ]
         expect(err).to.exist
         expect(err.message).to.equal('No access to ETag property on response. Check CORS configuration to expose ETag header.')
-
-    it 'allows changing part size', ->
+        done()
+
+    it 'allows changing part size', (done) ->
       reqs = helpers.mockResponses [
         { data: UploadId: 'uploadId' }
         { data: ETag: 'ETAG1' }
@@ -154,19 +157,21 @@
         expect(data.Location).to.equal('FINAL_LOCATION')
         expect(reqs[1].params.ContentLength).to.equal(size)
         expect(reqs[2].params.ContentLength).to.equal(size)
-
-    it 'supports zero-byte body buffers', ->
+        done()
+
+    it 'supports zero-byte body buffers', (done) ->
       reqs = helpers.mockResponses [data: ETag: 'ETAG']
       upload = new AWS.S3.ManagedUpload params: { Body: zerobody }
       upload.send ->
         expect(helpers.operationsForRequests(reqs)).to.eql ['s3.putObject']
         expect(err).not.to.exist
+        done()

     it 'errors if partSize is smaller than minPartSize', ->
       expect(-> new AWS.S3.ManagedUpload(partSize: 5)).to.throw(
         'partSize must be greater than 10')

-    it 'aborts if uploadPart fails', ->
+    it 'aborts if uploadPart fails', (done) ->
       reqs = helpers.mockResponses [
         { data: UploadId: 'uploadId' }
         { data: ETag: 'ETAG1' }
@@ -185,8 +190,9 @@
         expect(err).to.exist
         expect(data).not.to.exist
         expect(reqs[3].params.UploadId).to.equal('uploadId')
-
-    it 'aborts if complete call fails', ->
+        done()
+
+    it 'aborts if complete call fails', (done) ->
       reqs = helpers.mockResponses [
         { data: UploadId: 'uploadId' }
         { data: ETag: 'ETAG1' }
@@ -209,8 +215,9 @@
         expect(err).to.exist
         expect(err.code).to.equal('CompleteFailed')
         expect(data).not.to.exist
-
-    it 'leaves parts if leavePartsOnError is set', ->
+        done()
+
+    it 'leaves parts if leavePartsOnError is set', (done) ->
       reqs = helpers.mockResponses [
         { data: UploadId: 'uploadId' }
         { data: ETag: 'ETAG1' }
@@ -231,6 +238,7 @@
         expect(err).to.exist
         expect(err.code).to.equal('UploadPartFailed')
         expect(data).not.to.exist
+        done()

     if AWS.util.isNode()
       describe 'streaming', ->


Scenario 40. added

Scenario 40. added

Scenario 40. Two tags with two attribute, a space at the beginning of
the second attribute

--- test/scenario-based-testing.coffee
+++ test/scenario-based-testing.coffee
@@ -414,4 +414,14 @@
     # console.log output
     expectedOutput = fs.readFileSync './test/scenarios/scenario-39-expected.html', 'utf8'
     # console.log expectedOutput
-    expect(output).to.equal(expectedOutput)+    expect(output).to.equal(expectedOutput)
+
+  it 'Scenario 40. Two tags with two attribute, a space at the beginning of the second attribute', ->
+
+    input = fs.readFileSync './test/scenarios/scenario-40-input.cohtml', 'utf8'
+    # console.log input
+    output = toHtml5 input
+    # console.log output
+    expectedOutput = fs.readFileSync './test/scenarios/scenario-40-expected.html', 'utf8'
+    # console.log expectedOutput
+    expect(output).to.equal(expectedOutput)


added request logging

added request logging

--- test/unit/wd-by-method-test.coffee
+++ test/unit/wd-by-method-test.coffee
@@ -12,6 +12,10 @@

   it "wd.remote", (done) ->
     browser = wd.remote(mode:'sync')
+    browser.on "status", (info) ->
+      console.log "\u001b[36m%s\u001b[0m", info
+    browser.on "command", (meth, path) ->
+      console.log " > \u001b[33m%s\u001b[0m: %s", meth, path
     Wd = Wd with:browser
     done()


Files utils specs now use fs.existsSync

Files utils specs now use fs.existsSync

--- test/units/utils/files.spec.coffee
+++ test/units/utils/files.spec.coffee
@@ -1,6 +1,6 @@
 require '../../test_helper'
 fs = require 'fs'
-{resolve, existsSync} = require "path"
+{resolve} = require "path"

 root = resolve __dirname, '../../../'

@@ -419,13 +419,13 @@
   deep = "#{base}/bar"

   afterEach ->
-    fs.rmdirSync deep if existsSync deep
-    fs.rmdirSync base if existsSync base
+    fs.rmdirSync deep if fs.existsSync deep
+    fs.rmdirSync base if fs.existsSync base

   describe 'when called with a valid path to the dir to create', ->
     it 'should create the directory', ->
       fu.ensureSync base
-      expect(existsSync base).toBeTruthy()
+      expect(fs.existsSync base).toBeTruthy()

     it 'should return true', ->
       res = fu.ensureSync base
@@ -446,14 +446,14 @@
   deep = "#{base}/bar"

   afterEach ->
-    fs.rmdirSync deep if existsSync deep
-    fs.rmdirSync base if existsSync base
+    fs.rmdirSync deep if fs.existsSync deep
+    fs.rmdirSync base if fs.existsSync base

   describe 'when called with a valid path to the dir to create', ->

     it 'should create the directory', (done) ->
       fu.ensure base, (err, created) ->
-        expect(existsSync base).toBeTruthy()
+        expect(fs.existsSync base).toBeTruthy()
         expect(created).toBeTruthy()
         done()

@@ -469,26 +469,26 @@
   deep = "#{base}/bar"

   afterEach ->
-    fs.rmdirSync deep if existsSync deep
-    fs.rmdirSync base if existsSync base
+    fs.rmdirSync deep if fs.existsSync deep
+    fs.rmdirSync base if fs.existsSync base

   describe 'when called with a valid path to the dir to create', ->
     it 'should create the directory', ->
       fu.ensurePathSync deep
-      expect(existsSync deep).toBeTruthy()
+      expect(fs.existsSync deep).toBeTruthy()

 describe 'ensurePath', ->
   base = "#{root}/test/fixtures/utils/files/foo"
   deep = "#{base}/bar"

   afterEach ->
-    fs.rmdirSync deep if existsSync deep
-    fs.rmdirSync base if existsSync base
+    fs.rmdirSync deep if fs.existsSync deep
+    fs.rmdirSync base if fs.existsSync base

   describe 'when called with a valid path to the dir to create', ->
     it 'should create the directory', (done) ->
       fu.ensurePath deep, (err, created) ->
-        expect(existsSync deep).toBeTruthy()
+        expect(fs.existsSync deep).toBeTruthy()
         expect(err).toBeNull()
         expect(created).toBeTruthy()
         done()
@@ -498,13 +498,13 @@
   deep = "#{root}/test/fixtures/utils/files/foo/bar.coffee"

   afterEach ->
-    fs.unlinkSync deep if existsSync deep
-    fs.unlinkSync base if existsSync base
+    fs.unlinkSync deep if fs.existsSync deep
+    fs.unlinkSync base if fs.existsSync base

   describe 'when called with a valid path to the file to create', ->
     it 'should create the directory', ->
       res = fu.touchSync base
-      expect(existsSync base).toBeTruthy()
+      expect(fs.existsSync base).toBeTruthy()

     it 'should create return true', ->
       res = fu.touchSync base
@@ -526,14 +526,14 @@
   deep = "#{root}/test/fixtures/utils/files/foo/bar.coffee"

   afterEach ->
-    fs.unlinkSync deep if existsSync deep
-    fs.unlinkSync base if existsSync base
+    fs.unlinkSync deep if fs.existsSync deep
+    fs.unlinkSync base if fs.existsSync base

   describe 'when called with a valid path to the file to create', ->

     it 'should create the directory', (done) ->
       fu.touch base, (err, created) ->
-        expect(existsSync base).toBeTruthy()
+        expect(fs.existsSync base).toBeTruthy()
         expect(created).toBeTruthy()
         done()

@@ -555,7 +555,7 @@
   it 'should remove all the files and directories recursively', ->
     fu.rmSync "#{root}/test/fixtures/utils/files/rm"

-    expect(existsSync "#{root}/test/fixtures/utils/files/rm").toBeFalsy()
+    expect(fs.existsSync "#{root}/test/fixtures/utils/files/rm").toBeFalsy()

 describe 'rm', ->
   beforeEach ->
@@ -568,7 +568,7 @@
   it 'should remove all the files and directories recursively', (done) ->
     fu.rm "#{root}/test/fixtures/utils/files/rm", (err) ->

-      expect(existsSync "#{root}/test/fixtures/utils/files/rm").toBeFalsy()
+      expect(fs.existsSync "#{root}/test/fixtures/utils/files/rm").toBeFalsy()
       done()

 describe 'readFiles', ->


Add support for FMA, not that any browser supports it... (but my special build does!\)

--- src/longdouble.coffee
+++ src/longdouble.coffee
@@ -57,9 +57,7 @@
 #                p = fl(a × b)
 #                e = err(a × b).

-FMA = false # No detection for FMA / FMS suport yet.
-
-if (!FMA)
+if (!Math.fma) # Only my special build of Firefox supports FMA yet.
 	two_prod = (a, b) ->
 		p = a * b


[fix] Make the timer dependant on the number of unique digits of the login. Fixes #713 (#715)

--- server/konnectors/freemobile.coffee
+++ server/konnectors/freemobile.coffee
@@ -92,7 +92,7 @@
             .use(filterExisting log, PhoneBill)
             .use(saveDataAndFile log, PhoneBill, {
                 vendor: 'freemobile',
-                others: ['phonenumber']
+                others: ['titulaire', 'phonenumber']
             }, ['facture'])
             .use(linkBankOperation
                 log: log
@@ -142,7 +142,7 @@
     request options, (err, res, body) ->
         if err?
             log.error "Cannot connect to Free Mobile : #{homeUrl}"
-            next err
+            return next err
         loginPageData = body
         data.imageUrlAndPosition = []
         $ = cheerio.load loginPageData
@@ -180,11 +180,15 @@
     # do)
     uniqueLogin = unifyLogin transcodedLogin

+
+    # Ensure the download of the small image takes at least 4s
+    timerDownload = Math.round(4 / uniqueLogin.length * 1000)
+
     # Each small image is downloaded. The small image is the image downloaded
     # when the user clicks on the image keyboard
-    async.eachSeries uniqueLogin, getSmallImage, (err) ->
-        if err?
-            next err
+    async.eachSeries uniqueLogin, getSmallImage(timerDownload), (err) ->
+        if err?
+            return next err
         #As trancodedLogin is an array, it is changed into a string
         login =""
         for i in transcodedLogin
@@ -213,7 +217,7 @@
                 log.error "No 302" if res.statusCode isnt 302
                 log.error "No password" if not requiredFields.password?
                 log.error "No login" if not requiredFields.login?
-                next 'bad credentials'
+                return next 'bad credentials'

             options =
                 method: 'GET'
@@ -223,14 +227,14 @@
                     referer : homeUrl
             request options, (err, res, body) ->
                 if err?
-                    next err
+                    return next err
 		# We check that there is no connection form (the statusCode is
                 # always 302 even if the credential are wrong)
                 $ = cheerio.load body
                 connectionForm = $('#form_connect')
                 if connectionForm.length isnt 0
                     log.error "Authentification error"
-                    next 'bad credentials'
+                    return next 'bad credentials'
                 next()

@@ -242,7 +246,7 @@
         jar: true
     request options, (err, res, body) ->
         if err?
-            next err
+            return next err
         data.html = body
         next()

@@ -264,6 +268,7 @@
     # - Import overall pdf with name YYYYMM_freemobile.pdf

     isMultiline = $('div.infosConso').length > 1
+    log.info 'Multi line detected' if isMultiline
     $('div.factLigne.is-hidden').each ->
         amount = $($(this).find('.montant')).text()
         amount = amount.replace '€', ''
@@ -282,8 +287,14 @@
             vendor: 'Free Mobile'
             type: 'phone'

+        number = $(this).find('div.titulaire > span.numero').text()
+        $(this).find('div.titulaire > span.numero').remove()
+        titulaire = $(this).find('div.titulaire').text().replace /(\n|\r)/g, ''
+        titulaire = titulaire.trim()
+
         if isMultiline and not data_fact_multi
-            bill.phonenumber = data_fact_ligne
+            bill.phonenumber = number
+            bill.titulaire = titulaire

         bill.pdfurl = pdfUrl if date.year() > 2011

@@ -297,7 +308,7 @@
     # download all the sounds, like a browser would do
     getSound imageInfo.position, (err) ->
         if err?
-            callback err, null
+            return callback err, null
         options =
             method: 'GET'
             jar: true
@@ -306,7 +317,7 @@
         # We dowload the image located at imageInfo.imagePath
         request options, (err, res, body) ->
             if err?
-                callback err, null
+                return callback err, null
             pngjs.loadImage body, (err, resultImage) ->
                 if resultImage.getWidth() < 24 or resultImage.getHeight() < 28
                     callback 'Wrong image size', null
@@ -339,7 +350,7 @@
             referer: baseUrl+"sound/soundmanager2_flash9.swf"
     request options, (err, res, body) ->
         if err?
-            callback err
+            return callback err
         callback null

@@ -402,15 +413,16 @@

 # Small images are downloaded like a browser woulds do.
-getSmallImage = (digit, callback) ->
-    baseUrl = "https://mobile.free.fr/moncompte/"
-    options =
-        method: 'GET'
-        jar: true
-        url: "#{baseUrl}chiffre.php?pos=#{digit}&small=1"
-
-    request options, (err, res, body) ->
-        if err?
-            callback err
-        #Timer is necessary otherwise the connection is not possible
-        setTimeout callback, 600, null
+getSmallImage = (timer) ->
+    (digit, callback) ->
+        baseUrl = "https://mobile.free.fr/moncompte/"
+        options =
+            method: 'GET'
+            jar: true
+            url: "#{baseUrl}chiffre.php?pos=#{digit}&small=1"
+
+        request options, (err, res, body) ->
+            if err?
+                return callback err
+            #Timer is necessary otherwise the connection is not possible
+            setTimeout callback, timer, null


Remove .coffee ext from a require

Remove .coffee ext from a require

--- src/Meteor.coffee
+++ src/Meteor.coffee
@@ -3,7 +3,7 @@
 _ = require("underscore")
 ChildProcess = require './ChildProcess'
 EventEmitter = require('events').EventEmitter
-MeteorMongodb = require("./MeteorMongodb.coffee")
+MeteorMongodb = require("./MeteorMongodb")
 glob = require("glob")
 fs = require("fs")
 path = require "path"


Terminal: TerminalStartTab: renaming cont. add fixme for notifications

--- client/Terminal/terminalstarttab.coffee
+++ client/Terminal/terminalstarttab.coffee
@@ -9,10 +9,10 @@
     @message = new KDCustomHTMLView cssClass : 'terminal-bottom-message'
     @message.hide()

-    KD.singletons.notificationController.on 'NotificationHasArrived', ({event}) =>
-      if event in ["VMCreated", "VMRemoved"]
-
-        @viewAppended yes
+    # FIXME GG
+    # KD.singletons.notificationController.on 'NotificationHasArrived', ({event}) =>
+    #   if event in ["VMCreated", "VMRemoved"]
+    #     @viewAppended yes

   viewAppended: (force = no) ->

@@ -62,9 +62,10 @@

Terminal allows you to interact directly with your VM.

Your VMs

-    {{> @vmWrapper}}
+    {{> @machineWrapper}}
     {{> @message}}
     """
+

   prepareMessage: ->


TerminalStartTab: check status from state object

--- client/Terminal/terminalstarttab.coffee
+++ client/Terminal/terminalstarttab.coffee
@@ -29,7 +29,7 @@
         ErrorLog.create "terminal: Couldn't fetch machines", reason:err
         return new KDNotificationView title : "Couldn't fetch your Machines"

-      machines = machines.filter (machine)-> machine.state is "READY"
+      machines = machines.filter (machine)-> machine.status.state is "Running"

       @listMachines        machines
       @listMachineSessions machines


Ask confirmation when deleting aru

Ask confirmation when deleting aru

--- app/assets/javascripts/components/annual_report_upload.js.coffee
+++ app/assets/javascripts/components/annual_report_upload.js.coffee
@@ -52,6 +52,7 @@
             className: 'delete-upload',
             href: "/annual_report_uploads/#{@state.annualReportUpload.id}",
             "data-method": 'delete'
+            "data-confirm": 'Are you sure you want to delete this report?'
           }
           i({className: 'fa fa-times'})
           I18n.t('delete')


activity: filter out group channel from channel threads

--- client/activity/lib/flux/getters.coffee
+++ client/activity/lib/flux/getters.coffee
@@ -47,6 +47,13 @@
     ids.filter (id) -> id isnt groupChannelId
 ]

+ChannelThreadsStore = [
+  ChannelThreadsStore
+  (threads) ->
+    groupChannelId = getGroup().socialApiChannelId
+    threads.filter (thread) -> thread.get('channelId') isnt groupChannelId
+]
+

 allChannels = [
   ChannelsStore, (channels) ->


Hide empty Grading section for non-instructors/admins

Resolves #360

--- app/assets/javascripts/components/timeline/timeline_handler.cjsx
+++ app/assets/javascripts/components/timeline/timeline_handler.cjsx
@@ -69,6 +69,16 @@

     outlet = React.cloneElement(@props.children, {key: 'wizard_handler', course: @props.course, weeks: @props.weeks, week_meetings: weekMeetings, meetings: meetings}) if @props.children

+    show_grading =
+      if @state?.reorderable == true
+        false
+      else if @props.current_user.admin || @props.current_user.role > 0
+        true
+      else if @props.gradeables.length == 0
+        false
+      else
+        true
+
     <div>
       <TransitionGroup
         transitionName="wizard"
@@ -95,7 +105,7 @@
         all_training_modules={@props.all_training_modules}
         edit_permissions={@props.current_user.admin || @props.current_user.role > 0}
       />
-      { if @state?.reorderable != true then <Grading {...@props} /> else null }
+      { if show_grading then <Grading {...@props} /> else null }
     </div>
 )


destroyPointer fixed

destroyPointer fixed

--- client/app/MainApp/book/bookview.coffee
+++ client/app/MainApp/book/bookview.coffee
@@ -75,8 +75,8 @@
     @pagerWrapper.addSubView @pageNav

     @once "OverlayAdded", => @$overlay.css zIndex : 999
-    @once "OverlayWillBeRemoved", ->
-      @destroyPointer()
+    @once "OverlayWillBeRemoved", =>
+      if @pointer then @destroyPointer()
       @unsetClass "in"
       @utils.wait 1000, =>
         $spanElement = KD.singletons.mainView.sidebar.footerMenu.items[0].$('span')
@@ -547,10 +547,10 @@
       @unsetClass 'aside'
       @destroyPointer()

-  destroyPointer:->
+  destroyPointer:()=>
     @unsetClass('aside')
     @setKeyView()
-    @utils.wait 500, =>
+    @utils.wait 500, ->
       @pointer.destroy()

   clickAnimation:->


Fix navigateContinue on onboarding

Fix navigateContinue on onboarding

--- app/src/controllers/onboarding/onboarding_view_controller.coffee
+++ app/src/controllers/onboarding/onboarding_view_controller.coffee
@@ -2,14 +2,13 @@

   view:
     continueButton: '#continue_button'
+  navigation:
+    continueUrl: undefined

   onAfterRender: ->
     super
     do @unbindWindow
     do @bindWindow
-
-  navigation:
-    continueUrl: undefined

   navigationBackParams: ->
     undefined
@@ -45,8 +44,9 @@
   navigateBack: ->
     ledger.app.router.go @params.back, @_finalNavigationBackParams()

-  navigateContinue: ->
-    ledger.app.router.go @navigation.continueUrl, @_finalNavigationContinueParams()
+  navigateContinue: (url, params) ->
+    params = _.extend(@_defaultNavigationContinueParams(), params) if params?
+    ledger.app.router.go (url || @navigation.continueUrl), (params || @_finalNavigationContinueParams())

   unbindWindow: ->
     $(window).unbind 'keyup', null


Fix single slide init

Fix single slide init

--- src/jquery.superslides.coffee
+++ src/jquery.superslides.coffee
@@ -27,13 +27,10 @@
   setupChildren()

 setupContainers = ->
-  if size > 1
-    $control.css
-      width: width * multiplier
-      height: height
-      left: -width
-
-    $container.hide()
+  $control.css
+    width: width * multiplier
+    height: height
+    left: -width if size > 1

 setupChildren = ->
   if $.fn.superslides.options.scrollable
@@ -45,13 +42,14 @@
       $scrollable.find('img').not('.keep-original')
                   .insertBefore($scrollable)

-  $children.not('.current').css
-    display: 'none'
-    position: 'absolute'
-    overflow: 'hidden'
-    top: 0
-    left: width
-    zIndex: 0
+  if size > 1
+    $children.not('.current').css
+      display: 'none'
+      position: 'absolute'
+      overflow: 'hidden'
+      top: 0
+      left: width
+      zIndex: 0

   adjustSlidesSize $children

@@ -125,6 +123,9 @@

     animate index
     play()
+  else
+    $container.fadeIn('fast')
+    $(".#{$.fn.superslides.options.nav_class}").hide()

 stop = ->
   clearInterval play_interval


:bug: Fix count logic in Bottom

:bug: Fix count logic in Bottom

--- lib/bottom.coffee
+++ lib/bottom.coffee
@@ -22,8 +22,8 @@
       priority: -999
   update: (Messages)->
     CountTotal = Messages.length
-    CountFile = 0
-    CountProject = 0
+    CountFile = Messages.filter( (Message) -> Message.CurrentFile).length
+    CountProject = CountTotal - CountFile
     @BarCurrent.Child.textContent = CountFile.toString()
     @BarProject.Child.textContent = CountProject.toString()
     if CountTotal


use package name from variable

use package name from variable

--- lib/browse.coffee
+++ lib/browse.coffee
@@ -28,16 +28,16 @@
     @subscriptions.add atom.commands.add "atom-workspace", "#{name}:reveal-file-from-treeview": => @revealFileFromTreeview()
     @subscriptions.add atom.commands.add "atom-workspace", "#{name}:application-folder": => @appFolder()

-    require("./ga").sendEvent("browse", "activate")
+    require("./ga").sendEvent(name, "activate")

   deactivate: ->
-    require("./ga").sendEvent("browse", "deactivate")
+    require("./ga").sendEvent(name, "deactivate")

     @subscriptions?.dispose()
     @subscriptions = null

   appFolder: ->
-    require("./ga").sendEvent("browse", "application-folder")
+    require("./ga").sendEvent(name, "application-folder")

     { platform } = require "os"
     { dirname, join, resolve } = require "path"
@@ -54,7 +54,7 @@
     @openFolder(appFolder)

   browsePackages: ->
-    require("./ga").sendEvent("browse", "packages-folder")
+    require("./ga").sendEvent(name, "packages-folder")

     { accessSync, F_OK } = require "fs"

@@ -71,7 +71,7 @@
       @openFolder(packageDir)

   revealFile: ->
-    require("./ga").sendEvent("browse", "reveal-file")
+    require("./ga").sendEvent(name, "reveal-file")

     editor = atom.workspace.getActivePaneItem()

@@ -85,7 +85,7 @@
     atom.notifications.addWarning("**#{name}**: No active file", dismissable: false)

   revealFiles: ->
-    require("./ga").sendEvent("browse", "reveal-all-open-files")
+    require("./ga").sendEvent(name, "reveal-all-open-files")

     editors = atom.workspace.getPaneItems()

@@ -105,7 +105,7 @@
     atom.notifications.addWarning("**#{name}**: No open files", dismissable: false)

   revealFileFromTreeview: ->
-    require("./ga").sendEvent("browse", "reveal-file-from-treeview")
+    require("./ga").sendEvent(name, "reveal-file-from-treeview")

     panes = atom.workspace.getPaneItems()

@@ -125,7 +125,7 @@
     atom.notifications.addWarning("**#{name}**: No selected files", dismissable: false)

   browseProjects: ->
-    require("./ga").sendEvent("browse", "project-folders")
+    require("./ga").sendEvent(name, "project-folders")

     { accessSync, F_OK } = require "fs"

@@ -148,7 +148,7 @@
       @openFolder(project)

   browseConfig: ->
-    require("./ga").sendEvent("browse", "configuration-folder")
+    require("./ga").sendEvent(name, "configuration-folder")

     { accessSync, F_OK } = require "fs"
     { dirname } = require "path"
@@ -168,7 +168,7 @@
       @openFolder(configPath)

   selectFile: (path) ->
-    require("./ga").sendEvent("browse", "configuration-folder")
+    require("./ga").sendEvent(name, "configuration-folder")

     { basename } = require "path"


add notifications for errors and warnings

--- lib/browse.coffee
+++ lib/browse.coffee
@@ -31,12 +31,12 @@
       # Does packages folder exist?
       try
         fs.accessSync(@packageDir, fs.F_OK)
-      catch e
-        console.log e
+      catch error
+        atom.notifications.addError("atom-browse", detail: error, dismissable: true)
         return

       # Open packages folder
-      exec "#{@fileManager} #{packageDir}"
+      exec "#{@fileManager} #{@packageDir}"

   getFileManager: ->

@@ -46,5 +46,5 @@
       when "win32"
         return "explorer"
       when "linux"
-        console.log "browse: Linux is not yet supported."
+        atom.notifications.addWarning("atom-browse", detail: "Hold tight. Linux is not yet supported, but we're working on it!", dismissable: true)
         return null


fix require

fix require

--- lib/browse.coffee
+++ lib/browse.coffee
@@ -163,8 +163,8 @@
       when "win32"
         @spawnCmd "explorer", [ "/select,#{path}" ], basename(path), "Explorer"
       when "linux"
-        { showItemInFolder } = require "electron"
-        showItemInFolder(path)
+        { shell } = require "electron"
+        shell.showItemInFolder(path)
         atom.notifications.addInfo("**#{name}**: Opening `#{basename(path)}` in file manager", dismissable: false) if atom.config.get("#{name}.notify")

   openFolder: (path) ->
@@ -185,8 +185,8 @@
         when "win32"
           Browse.spawnCmd "explorer", [ path ], basename(path), "Explorer"
         when "linux"
-          { openItem } = require "electron"
-          openItem(path)
+          { shell } = require "electron"
+          shell.openItem(path)
           atom.notifications.addInfo("**#{name}**: Opening `#{basename(path)}` in file manager", dismissable: false) if atom.config.get("#{name}.notify")

   spawnCmd: (cmd, args, baseName, fileManager) ->


:lipstick: Rename get as match and create an alias

--- src/net/router.coffee
+++ src/net/router.coffee
@@ -15,6 +15,7 @@
 #
 #
 class agt.net.Router
+  @extend agt.mixins.Aliasable

   constructor: (block) ->
     @routes = {}
@@ -24,10 +25,12 @@
     block.call(this)
     @buildRoutesHandlers()

-  get: (path, options={}, handle) ->
+  match: (path, options={}, handle) ->
     [options, handle] = [{}, options] if typeof options is 'function'
     path = path.replace /^\/|\/$/g, ''
     @routes[path] = {handle, options}
+
+  @alias 'match', 'get'

   beforeFilter: (filter) -> @beforeFilters.push filter
   afterFilter: (filter) -> @afterFilters.push filter


fixed aux output format bug (fixes #29)

fixed aux output format bug (fixes #29)

--- src/lib/renderer/ninja.coffee
+++ src/lib/renderer/ninja.coffee
@@ -39,7 +39,6 @@
     out.push [
       'build'
       target.outputs
-      if target.auxOutputs.length then '|' else ''
       target.auxOutputs
       ':'
       target.command.name


Use upcoming event instead if global one

--- client/app/Applications/Demos.kdapplication/AppController.coffee
+++ client/app/Applications/Demos.kdapplication/AppController.coffee
@@ -112,7 +112,7 @@
         @setClass 'ondrag'
     , 300

-    @on 'DragFinished', (e)->
+    @on 'DragFinished', (event)->

       @unsetClass 'ondrag'
       @_dragStarted = no


added space to trigger semaphore

added space to trigger semaphore

--- client/apps/edit/components/layout/components/yoast/index.coffee
+++ client/apps/edit/components/layout/components/yoast/index.coffee
@@ -46,3 +46,4 @@

   onKeyup: =>
     @snippetPreview.changedInput()
+


fixing shake

fixing shake

--- src/camera.coffee
+++ src/camera.coffee
@@ -78,14 +78,8 @@
   self.bind "afterUpdate", ->
     if currentObject
       followTypes[currentType](currentObject)
-
-      if I.shakeCooldown > 0
-        I.shakeCooldown = I.shakeCooldown.approach(0, 1)
-
-        I.transform.tx += signedRand(I.shakeIntensity)
-        I.transform.ty += signedRand(I.shakeIntensity)
-      else
-        followTypes[currentType](currentObject)
+    else
+      followTypes[currentType](currentObject)

   self.bind "draw", (canvas, objects) ->
     canvas.withTransform Matrix.translate(I.screen.x, I.screen.y), (canvas) ->


forward the close event to the client api

--- src/client.coffee
+++ src/client.coffee
@@ -43,6 +43,9 @@
 		client.on 'data', @dataHandler
 		client.on 'data', (data)=>
 			@emit 'tcpMessage', data
+
+		client.on 'close', ()=>
+			@emit 'close'

 		# events to forward
 		client.on 'connection', ()=>


commiting changes made before the demo

commiting changes made before the demo

--- src/client.coffee
+++ src/client.coffee
@@ -28,7 +28,7 @@
             a.css width: percentOfWidth(100) - 100, height: percentOfHeight(100) - 100
         when "youtube"
             time = if data.time then data.time else ""
-            a = $("")
+            a = $("")
             a.css width: percentOfWidth(100) - 100, height: percentOfHeight(100) - 100

         when "image"


Fix fetching channels

Fix fetching channels

fixes finding a channel by name

--- src/client.coffee
+++ src/client.coffee
@@ -95,8 +95,7 @@

     _onChannels: (data, headers) =>
         if data && not data.error
-            @channels = data.members
-            @channel_details = data.channels
+            @channels = data
         else
             @logger.error 'Failed to get subscribed channels list from server.'
             @emit 'error', { msg: 'failed to get channel list'}
@@ -270,9 +269,9 @@
             return data

     findChannelByName: (name) ->
-        for c of @channel_details
-            if @channel_details[c].name == name or @channel_details[c].display_name == name
-                return @channel_details[c]
+        for c of @channels
+            if @channels[c].name == name or @channels[c].display_name == name
+                return @channels[c]
         return null

     postMessage: (msg, channelID) ->


You should be able to use a callback with @ syntax or with its regular syntax.

--- src/client.coffee
+++ src/client.coffee
@@ -47,7 +47,7 @@

           apply_helpers ctx

-          action.apply ctx
+          action.apply ctx, data

     context.connect = ->
       context.socket = io.connect.apply io, arguments


Added logging to the client session.

Added logging to the client session.

--- src/runtime.js.coffee
+++ src/runtime.js.coffee
@@ -411,6 +411,7 @@
     expiration = new Date()
     expiration.setTime(expiration.getTime() + 2000)
     document.cookie = "mvcoffee_session=#{params}; expires=#{expiration}"
+    @log "Sending client session " + params

   # There is no "get" method.  Instead the functionality of "get" is provided by two


Compact minors list

Compact minors list

--- src/cmd/ls.coffee
+++ src/cmd/ls.coffee
@@ -29,7 +29,7 @@
     else
       list.push last = z
   for z in list
-    echo "#{z.dist} #{z.src.version}{#{z.minors.join ','}}"
+    echo "#{z.dist} #{z.src.version}#{r2s ranges z.minors}"
   return

 # Split version to major.minor
@@ -39,3 +39,21 @@
     major = major[0] = major[0].slice()
     z.minors = [z.minor = major.pop()]
   list
+
+# Combine list of minors into list of ranges
+ranges = (list)->
+  res = []
+  for n in list
+    if last and last.b + 1 == n
+      last.b = n
+    else
+      res.push last =
+        a: n
+        b: n
+  res
+
+# Convert ranges list to string
+r2s = (ranges)->
+  r = for r, i in ranges
+    "#{if i then r.a else ''}#{if r.b > r.a then ' - ' + r.b else ''}"
+  r.join ', '


:memo: new config APIs

:memo: new config APIs

--- src/config.coffee
+++ src/config.coffee
@@ -338,12 +338,13 @@
   #   # do stuff with value
   # ```
   #
-  # * `scopeDescriptor` (optional) {ScopeDescriptor} describing a path from
-  #   the root of the syntax tree to a token. Get one by calling
-  #   {editor.getLastCursor().getScopeDescriptor()}. See {::get} for examples.
-  #   See [the scopes docs](https://atom.io/docs/latest/advanced/scopes-and-scope-descriptors)
-  #   for more information.
   # * `keyPath` {String} name of the key to observe
+  # * `options` {Object}
+  #   * `scopeDescriptor` (optional) {ScopeDescriptor} describing a path from
+  #     the root of the syntax tree to a token. Get one by calling
+  #     {editor.getLastCursor().getScopeDescriptor()}. See {::get} for examples.
+  #     See [the scopes docs](https://atom.io/docs/latest/advanced/scopes-and-scope-descriptors)
+  #     for more information.
   # * `callback` {Function} to call when the value of the key changes.
   #   * `value` the new value of the key
   #
@@ -379,13 +380,14 @@
   # Essential: Add a listener for changes to a given key path. If `keyPath` is
   # not specified, your callback will be called on changes to any key.
   #
-  # * `scopeDescriptor` (optional) {ScopeDescriptor} describing a path from
-  #   the root of the syntax tree to a token. Get one by calling
-  #   {editor.getLastCursor().getScopeDescriptor()}. See {::get} for examples.
-  #   See [the scopes docs](https://atom.io/docs/latest/advanced/scopes-and-scope-descriptors)
-  #   for more information.
   # * `keyPath` (optional) {String} name of the key to observe. Must be
   #   specified if `scopeDescriptor` is specified.
+  # * `optional` (optional) {Object}
+  #   * `scopeDescriptor` (optional) {ScopeDescriptor} describing a path from
+  #     the root of the syntax tree to a token. Get one by calling
+  #     {editor.getLastCursor().getScopeDescriptor()}. See {::get} for examples.
+  #     See [the scopes docs](https://atom.io/docs/latest/advanced/scopes-and-scope-descriptors)
+  #     for more information.
   # * `callback` {Function} to call when the value of the key changes.
   #   * `event` {Object}
   #     * `newValue` the new value of the key
@@ -458,30 +460,34 @@
   # atom.config.get(scopeDescriptor, 'editor.tabLength') # => 2
   # ```
   #
-  # * `scopeDescriptor` (optional) {ScopeDescriptor} describing a path from
-  #   the root of the syntax tree to a token. Get one by calling
-  #   {editor.getLastCursor().getScopeDescriptor()}
-  #   See [the scopes docs](https://atom.io/docs/latest/advanced/scopes-and-scope-descriptors)
-  #   for more information.
   # * `keyPath` The {String} name of the key to retrieve.
+  # * `options` (optional) {Object}
+  #   * `sources` (optional) {Array} of {String} source names. If provided, only
+  #     values that were associated with these sources during {::set} will be used.
+  #   * `excludeSources` (optional) {Array} of {String} source names. If provided,
+  #     values that  were associated with these sources during {::set} will not
+  #     be used.
+  #   * `scopeDescriptor` (optional) {ScopeDescriptor} describing a path from
+  #     the root of the syntax tree to a token. Get one by calling
+  #     {editor.getLastCursor().getScopeDescriptor()}
+  #     See [the scopes docs](https://atom.io/docs/latest/advanced/scopes-and-scope-descriptors)
+  #     for more information.
   #
   # Returns the value from Atom's default settings, the user's configuration
   # file in the type specified by the configuration schema.
   get: ->
     if arguments.length > 1
       if typeof arguments[0] is 'string' or not arguments[0]?
-        keyPath = arguments[0]
-        options = arguments[1]
+        [keyPath, options] = arguments
         {scope} = options
       else
         Grim.deprecate """
           Passing a scope descriptor as the first argument to Config::get is deprecated.
           Pass a `scope` in an options hash as the final argument instead.
         """
-        scope = arguments[0]
-        keyPath = arguments[1]
-    else
-      keyPath = arguments[0]
+        [scope, keyPath] = arguments
+    else
+      [keyPath] = arguments

     if scope?
       value = @getRawScopedValue(scope, keyPath, options)
@@ -518,12 +524,15 @@
   # atom.config.get(['source.js'], 'editor.tabLength') # => 4
   # ```
   #
-  # * `scopeSelector` (optional) {String}. eg. '.source.ruby'
-  #   See [the scopes docs](https://atom.io/docs/latest/advanced/scopes-and-scope-descriptors)
-  #   for more information.
   # * `keyPath` The {String} name of the key.
   # * `value` The value of the setting. Passing `undefined` will revert the
   #   setting to the default value.
+  # * `options` (optional) {Object}
+  #   * `scopeSelector` (optional) {String}. eg. '.source.ruby'
+  #     See [the scopes docs](https://atom.io/docs/latest/advanced/scopes-and-scope-descriptors)
+  #     for more information.
+  #   * `source` (optional) {String} The name of a file with which the setting
+  #     is associated. Defaults to the user's config file.
   #
   # Returns a {Boolean}
   # * `true` if the value was set.
@@ -561,6 +570,12 @@
     @save() unless @configFileHasErrors
     true

+  # Essential: Restore the setting at `keyPath` to its default value.
+  #
+  # * `keyPath` The {String} name of the key.
+  # * `options` (optional) {Object}
+  #   * `scopeSelector` (optional) {String}. See {::set}
+  #   * `source` (optional) {String}. See {::set}
   unset: (keyPath, options) ->
     if typeof options is 'string'
       Grim.deprecate """
@@ -593,15 +608,11 @@

   # Deprecated: Restore the global setting at `keyPath` to its default value.
   #
-  # * `scopeSelector` (optional) {String}. eg. '.source.ruby'
-  #   See [the scopes docs](https://atom.io/docs/latest/advanced/scopes-and-scope-descriptors)
-  #   for more information.
-  # * `keyPath` The {String} name of the key.
-  #
   # Returns the new value.
   restoreDefault: (scopeSelector, keyPath) ->
     Grim.deprecate("Use ::unset instead.")
     @unset(scopeSelector, keyPath)
+    @get(keyPath)

   # Extended: Get the global default value of the key path. _Please note_ that in most
   # cases calling this is not necessary! {::get} returns the default value when


Added ability to define basePath in options

--- src/tasks/dust.coffee
+++ src/tasks/dust.coffee
@@ -63,7 +63,8 @@

 			for source in file.src
 				# relative path to
-				tplRelativePath = if file.orig.cwd? and options.relative then path.relative file.orig.cwd, source else source
+				tplRelativePath = if file.orig.cwd? and options.relative then path.relative file.orig.cwd, source else
+													if options.basePath? then path.relative options.basePath, source else source

 				# remove extension from template name
 				tplName = tplRelativePath.replace new RegExp( "\\#{ path.extname tplRelativePath }$" ), ""
@@ -87,7 +88,7 @@
 						when 1
 							returning = _.last tplNames
 						else
-							returning = tplNames
+							returning = tplNames

 					joined = amdHelper joined, options.wrapperOptions.deps, options.wrapperOptions.packageName, returning
 				else if options.wrapper is "commonjs"


Fixed the error log for when the revision file cannot be created. It was looking for the variable in the wrong scope.

--- src/deploy.coffee
+++ src/deploy.coffee
@@ -222,7 +222,7 @@
 			# If the file was not found, we need to create one with HEAD hash
 			if error
 				fs.writeFile @revisionPath, @local_hash, (error) =>
-					return console.log "Error creating revision file at:".red, "#{revisionPath}".red.bold.underline, error  if error
+					return console.log "Error creating revision file at:".red, "#{@revisionPath}".red.bold.underline, error if error

 					# Since this is our first upload, we will upload our entire local tree
 					@addAll()


:memo: Indent comment correctly

:memo: Indent comment correctly

--- src/editor.coffee
+++ src/editor.coffee
@@ -320,7 +320,7 @@
   # {Delegates to: TextBuffer.setText}
   setText: (text) -> @buffer.setText(text)

-   # {Delegates to: TextBuffer.getTextInRange}
+  # {Delegates to: TextBuffer.getTextInRange}
   getTextInRange: (range) -> @buffer.getTextInRange(range)

   # {Delegates to: TextBuffer.getLineCount}


check if p is really closed

check if p is really closed

--- src/test/jsdom.coffee
+++ src/test/jsdom.coffee
@@ -168,6 +168,7 @@

         'after end': (æ) ->
             @æ = æ ; { $, api } = this
+            p = null
             tpl = @tpl = jqueryify {$}, new Template schema:5, ->
                 @$div class:'content', ->
                     p = @$p("bla")
@@ -175,12 +176,13 @@
                     p.ready(next_step)

             next_step = ->
+                æ.equal "closed:#{p.closed and 'yes' or 'no'}", "closed:yes"
                 next_step.called = yes
                 setTimeout ->
                     api.emit 'view', t = new Template schema:5, ->
                         @$p ->
-                            @text "foo"
-                            @$b "bar"
+                            @text "hack"
+                            @$b "hack"
                     æ.equal "7#{tpl is t.xml?.parent?.builder?.template}", "7true"
                 , 7

@@ -190,7 +192,7 @@

             @results = [
                 '
'
-                "
foobar
"
+                "
hackhack
"
                 "
"
             ].join("")


removing objectAt method

removing objectAt method

--- src/engine.coffee
+++ src/engine.coffee
@@ -230,19 +230,6 @@

         return object

-      objectAt: (x, y) ->
-        targetObject = null
-        bounds =
-          x: x
-          y: y
-          width: 1
-          height: 1
-
-        self.eachObject (object) ->
-          targetObject = object if object.collides(bounds)
-
-        return targetObject
-
       eachObject: (iterator) ->
         I.objects.each iterator


engine updating keys

engine updating keys

--- src/engine.coffee
+++ src/engine.coffee
@@ -121,6 +121,8 @@
         window.requestAnimationFrame(animLoop)

     update = ->
+      window.updateKeys()
+
       self.trigger "update"

       [I.objects, toRemove] = I.objects.partition (object) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine.coffee
+++ src/engine.coffee
@@ -295,6 +295,7 @@
     "Selector"
     "Collision"
     "Tilemap"
+    "Levels"
   ]

   (exports ? this)["Engine"] = Engine


change insert mode `mkts` to `mktscript`; prepend `<document/>` tag (temporary hotfix)

--- src/tex-writer.coffee
+++ src/tex-writer.coffee
@@ -764,8 +764,8 @@
       switch Q.mode ? 'literal'
         when 'literal'
           send [ '.', 'text', content, ( copy meta ), ]
-        when 'mkts'
-          send [ '.', 'mktscript', content, ( copy meta ), ]
+        when 'mktscript'
+          send [ '.', 'mktscript', '<document/>' + content, ( copy meta ), ]
         when 'raw'
           send [ 'tex', content, ]
         else


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/follow.coffee
+++ src/follow.coffee
@@ -1,6 +1,5 @@
 ###*
-The Cooldown module provides a declarative way to manage cooldowns on
-GameObject's properties.
+The C

 # Health regeneration


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/follow.coffee
+++ src/follow.coffee
@@ -2,10 +2,8 @@
   Object.reverseMerge I,
     velocity: Point(0, 0)

-  # Inherit from game object
   self = GameObject(I)

-  # Add events and methods here
   self.bind "update", ->
     ; # Add update method behavior


removes debuging

removes debuging

--- src/ui/version.coffee
+++ src/ui/version.coffee
@@ -18,8 +18,6 @@
         releasedVersion = tag?.substr(1) # remove first "v" char
         localVersion = require('electron').remote.require('electron').app.getVersion()
         versionAdvertised = window.localStorage.versionAdvertised or null
-        console.log('released', releasedVersion, releasedVersion.split('.'), versionToInt(releasedVersion))
-        console.log('local', localVersion, localVersion.split('.'), versionToInt(localVersion))
         if releasedVersion? && localVersion?
             higherVersionAvailable = versionToInt(releasedVersion) > versionToInt(localVersion)
             if higherVersionAvailable and (releasedVersion isnt versionAdvertised)


'label.text' objects support

'label.text' objects support

--- style.coffee
+++ style.coffee
@@ -121,10 +121,12 @@
 		return

 	updateText: ->
-		if 'text' of @item
+		if @item.$? and 'text' of @item.$
+			obj = @item.$
+		else if 'text' of @item
 			obj = @item
-		else if @item.$ isnt null and 'text' of @item.$
-			obj = @item.$
+		else if @item._label? and 'text' of @item._label
+			obj = @item._label

 		if obj
 			text = @node.stringifyChildren()


Finding indexes for Text elements fixed

Finding indexes for Text elements fixed

--- style.coffee
+++ style.coffee
@@ -597,7 +597,7 @@
 		while tmpIndexNode
 			# by previous sibling
 			while tmpSiblingNode
-				if tmpSiblingNode isnt node and tmpSiblingNode instanceof Tag
+				if tmpSiblingNode isnt node
 					# get sibling item
 					if tmpSiblingNode._documentStyle?.parentSet and (tmpSiblingItem = tmpSiblingNode._documentStyle.item)
 						if tmpSiblingTargetItem = findItemWithParent(tmpSiblingItem, parent)


tiny coffee return type mistake

tiny coffee return type mistake

--- src/utils/file.coffee
+++ src/utils/file.coffee
@@ -15,7 +15,7 @@
   guessFileFromText: (text) ->
     unless text?
       console.warn "invalid file format"
-      return [, "error"]
+      return ["", "error"]
     if text.substring(0,7) is "CLUSTAL"
       reader = ClustalReader
       type = "seqs"


refactored vocabulary.coffee

refactored vocabulary.coffee

--- src/vocabulary.coffee
+++ src/vocabulary.coffee
@@ -2,24 +2,54 @@
 {APLArray} = require './array'
 {Complex} = require './complex'

+lazyRequires =
+  'arithmetic':  '+−×÷⋆⍟∣'
+  'floorceil':   '⌊⌈'
+  'question':    '?'
+  'exclamation': '!'
+  'circle':      '○'
+  'comparisons': '=≠<>≤≥≡≢'
+  'logic':       '∼∨∧⍱⍲'
+  'rho':         '⍴'
+  'iota':        '⍳'
+  'rotate':      '⌽⊖'
+  'transpose':   '⍉'
+  'epsilon':     '∈'
+  'zilde':       ['get_⍬', 'set_⍬']
+  'comma':       ',⍪'
+  'grade':       '⍋⍒'
+  'take':        '↑'
+  'squish':      '⌷'
+  'quad':        ['get_⎕', 'set_⎕']
+  'format':      '⍕'
+  'forkhook':    ['⎕fork', '⎕hook']
+  'each':        '¨'
+  'commute':     '⍨'
+
+createLazyRequire = (obj, name, fromModule) ->
+  obj[name] = (args...) ->
+    obj[name] = f = require(fromModule)[name]
+    f.aplName = name
+    f.aplMetaInfo = arguments.callee.aplMetaInfo
+    f args...
+
+for fromModule, names of lazyRequires
+  for name in names
+    createLazyRequire @, name, './vocabulary/' + fromModule
+
+for name in '⍨¨' then (@[name].aplMetaInfo ?= {}).isPostfixAdverb = true
+
 @['⎕aplify'] = (x) ->
   assert x?
-  if typeof x is 'string'
-    if x.length is 1
-      APLArray.scalar x
-    else
-      new APLArray x
-  else if typeof x is 'number'
-    APLArray.scalar x
+  if typeof x is 'string' then (if x.length is 1 then APLArray.scalar x else new APLArray x)
+  else if typeof x is 'number' then APLArray.scalar x
   else if x instanceof Array
     new APLArray(
       for y in x
         if y instanceof APLArray and y.shape.length is 0 then y.unbox() else y
     )
-  else if x instanceof APLArray
-    x
-  else
-    throw Error 'Cannot aplify object ' + x
+  else if x instanceof APLArray then x
+  else throw Error 'Cannot aplify object ' + x

 @['⎕complex'] = (re, im) ->
   APLArray.scalar new Complex re, im
@@ -31,44 +61,6 @@
   if r not in [0, 1] then throw Error 'DOMAIN ERROR: cannot convert to boolean: ' + r
   r

-
-lazyRequires =
-  './vocabulary/arithmetic':  '+−×÷⋆⍟∣'
-  './vocabulary/floorceil':   '⌊⌈'
-  './vocabulary/question':    '?'
-  './vocabulary/exclamation': '!'
-  './vocabulary/circle':      '○'
-  './vocabulary/comparisons': '=≠<>≤≥≡≢'
-  './vocabulary/logic':       '∼∨∧⍱⍲'
-  './vocabulary/rho':         '⍴'
-  './vocabulary/iota':        '⍳'
-  './vocabulary/rotate':      '⌽⊖'
-  './vocabulary/transpose':   '⍉'
-  './vocabulary/epsilon':     '∈'
-  './vocabulary/zilde':       ['get_⍬', 'set_⍬']
-  './vocabulary/comma':       ',⍪'
-  './vocabulary/grade':       '⍋⍒'
-  './vocabulary/take':        '↑'
-  './vocabulary/squish':      '⌷'
-  './vocabulary/quad':        ['get_⎕', 'set_⎕']
-  './vocabulary/format':      '⍕'
-  './vocabulary/forkhook':    ['⎕fork', '⎕hook']
-  './vocabulary/each':        '¨'
-  './vocabulary/commute':     '⍨'
-
-createLazyRequire = (obj, name, fromModule) ->
-  obj[name] = (args...) ->
-    obj[name] = f = require(fromModule)[name]
-    f.aplName = name
-    f.aplMetaInfo = arguments.callee.aplMetaInfo
-    f args...
-
-for fromModule, names of lazyRequires
-  for name in names
-    createLazyRequire @, name, fromModule
-
-for name in '⍨¨' then (@[name].aplMetaInfo ?= {}).isPostfixAdverb = true
-
 do =>
   for k, v of @ when typeof v is 'function'
     v.aplName = k


Fix typo in settings

Fix typo in settings

--- lib/config.coffee
+++ lib/config.coffee
@@ -39,8 +39,8 @@
             title: 'Kernel Specs'
             description: 'This field is populated on every launch or by invoking
             the command `hydrogen:update-kernels`. It contains the JSON string
-            resulting from running `jupyter kernelspecs list --json` or
-            `ipython kernelspecs list --json`. You can also edit this field
+            resulting from running `jupyter kernelspec list --json` or
+            `ipython kernelspec list --json`. You can also edit this field
             and specify custom kernel specs , like this:
             ```
             {


https to http

https to http

--- lib/config.coffee
+++ lib/config.coffee
@@ -13,5 +13,5 @@
   commitLive: process.env['IDE_COMMIT_LIVE']
   commitLiveApi: process.env['IDE_COMMIT_LIVE_API']
 ,
-  commitLive: 'https://app.commit.live'
+  commitLive: 'http://app.commit.live'
   commitLiveApi: 'https://api.commit.live'


Log outgoing msg to debug log

Log outgoing msg to debug log

--- src/webRequest.coffee
+++ src/webRequest.coffee
@@ -48,13 +48,13 @@

     [reqOptions, body]

-  post: (path, body, callback, headers = {}) ->
+  post: (path, jsonBody, callback, headers = {}) ->
     @logger.debug 'Sending new POST request'

-    [reqOptions, body] = this.prepareReqOptions path, body, headers
+    [reqOptions, body] = this.prepareReqOptions path, jsonBody, headers

-    @logger.debug 'Request options:'
-    @logger.debug reqOptions
+    @logger.debug 'Request options: ' + JSON.stringify reqOptions
+    @logger.debug 'Request body: ' + JSON.stringify jsonBody

     # Send the request
     request = https.request reqOptions, (response) =>


KodingProvider: if fetchPlan fails use provided default plan

This was breaking the buildservices task since its using standalone script
which is not starting Social API while working. So, it was breaking the
user-importer to create developer machines for local development.

--- workers/social/lib/social/models/computeproviders/koding.coffee
+++ workers/social/lib/social/models/computeproviders/koding.coffee
@@ -142,7 +142,9 @@

     @fetchUserPlan client, (err, userPlan)=>

-      return callback err  if err?
+      # Commented-out this since if its failing to fetch plan
+      # its falling back to 'free' as default. ~ GG
+      # return callback err  if err?

       @fetchUsage client, options, (err, usage)->


ComputeController: checkMachinePermissions: ignore managed vms

we are not able to change user data on a managed vm and for that reason we are not moving ownership of kicked user managed stack, but we are moving ownership of all machines without checking it's provider. and once we kick a user the ui side is fetching all machines and stacks, ignores managed stacks of kicked user but still processes managed vms and with that process we should ignore managed ones.

--- client/app/lib/providers/computecontroller.coffee
+++ client/app/lib/providers/computecontroller.coffee
@@ -1120,6 +1120,7 @@

       return  unless oldOwner
       return  if not machine.isRunning()
+      return  if machine.isManaged()

       @storage.fetchValue 'ignoredMachines', (ignoredMachines) =>
         ignoredMachines ?= {}


fixed some new buggyness.

fixed some new buggyness.

--- client/coffeescript/templates/post_wrapper.coffee
+++ client/coffeescript/templates/post_wrapper.coffee
@@ -123,7 +123,7 @@
         event.stopPropagation()

     'click': (event) ->
-      unless Session.get('showing_post').parent_id is @parent_id
+      if Session.get('showing_post')._id is @parent_id
         Session.set('showing_post', @)
   }
 )


Much better to extend from FSItem instead FSFolder

--- client/landing/app/MainApp/fs/fsbrokenlink.coffee
+++ client/landing/app/MainApp/fs/fsbrokenlink.coffee
@@ -1 +1 @@
-class FSBrokenLink extends FSFolder+class FSBrokenLink extends FSItem


add testing for custom formatter invocation

--- spec/javascripts/plugin.test.coffee
+++ spec/javascripts/plugin.test.coffee
@@ -4,16 +4,20 @@
   initFlexirails = (data = [], view = {}) ->
     $(document.body).append $("

")

-    $("#flexirails").empty()
     elem = $("#flexirails").flexirails { 'datasource':data, 'view': view }
     instance = $(elem).getFlexirails()

   destroyFlexirails = ->
     instance.destroy()
+    $("#flexirails").remove()

   it "should export $.flexirails", ->
-    expect(typeof $.flexirails).toEqual "function"
+    expect(typeof $.flexirails).toEqual 'function'

+  it "should export $.getFlexirails", ->
+    expect(typeof $().getFlexirails).toEqual 'function'
+
+  #
   describe "flexirails-instanciation", ->
     beforeEach ->
       initFlexirails()
@@ -26,6 +30,10 @@

     it "should add a header row to the table", ->
       expect( $(".fr-header").length ).toBeGreaterThan 0
+
+    it "should return the plugin instance when calling $.getFlexirails", ->
+      plugin = $("#flexirails").getFlexirails()
+      expect(plugin instanceof $.flexirails).toBeTruthy

   # mock data
   data = [
@@ -41,6 +49,7 @@
       { title: 'Country', attribute: 'country' }
     ]

+  #
   describe "flexirails-header", ->
     beforeEach ->
       initFlexirails data, view
@@ -55,6 +64,7 @@
       expect( $(".city").length ).toBeGreaterThan 1
       expect( $(".country").length ).toBeGreaterThan 1

+  #
   describe "flexirails-column", ->
     beforeEach ->
       initFlexirails data, view
@@ -64,17 +74,28 @@

     it "should add the attribute as td selector", ->
       expect( $(".fr-row .city").length ).toBeGreaterThan 0
-
-  describe "flexirails-cell", ->
-    beforeEach ->
-      initFlexirails data, view
-
+
+  #
+  describe "flexirails-cell", ->
     afterEach ->
       destroyFlexirails()

     it "should be able to register formatter for cells", ->
+      initFlexirails data, view
       expect( typeof instance.registerFormatter).toBe 'function'

+    it "should use a custom formatter on matching cells", ->
+      called = 0
+      cityFormatter = (td, col, obj, attr) ->
+        called = called + 1
+
+      $(document.body).append $("

")
+      elem = $("#flexirails").flexirails { 'datasource':data, 'view': view, 'formatters': {'city':cityFormatter} }
+      instance = $(elem).getFlexirails()
+
+      expect(called).toBeGreaterThan 0
+
+  #
   describe "flexirails-navigation", ->
     beforeEach ->
       initFlexirails data, view


Bug fix.

Bug fix.

--- client/templates/user_profile/user_profile.coffee
+++ client/templates/user_profile/user_profile.coffee
@@ -9,6 +9,6 @@
     skype_id = $(e.target).find('[name=skype_id]').val()

     Meteor.users.update this._id, {$set: {telephone_nr: telephone_nr, skype_id: skype_id}}
-    Router.go 'membersPage', {_id: Meteor.userId()}
+    Router.go 'memberPage', {_id: Meteor.userId()}

-  'click .cancel': (e) -> Router.go 'membersPage', {_id: Meteor.userId()}
+  'click .cancel': (e) -> Router.go 'memberPage', {_id: Meteor.userId()}


Cosmetic changes, whitespace added/removed

--- client/test/lib/helpers/environmenthelpers.coffee
+++ client/test/lib/helpers/environmenthelpers.coffee
@@ -286,6 +286,7 @@
       .clearValue             nicknameInput
       .setValue               nicknameInput, name + '\n'

+
   reinitVM: (browser) ->

     reinitSelector  = '.kdmodal.AppModal .advanced .advanced.reinit'
@@ -331,5 +332,4 @@
   	  .waitForElementVisible  addKodingVmButton, 20000
   	  .click                  addKodingVmButton
   	  .waitForElementVisible  vmSelector, 20000
-
   	  .assert.containsText    vmSelector, 'koding-vm-0'


Fix issue where you could enter letters

Fix issue where you could enter letters

When the input was empty it didn't match the regex, now we just set the value to nothing until it does

--- inputLimit.coffee
+++ inputLimit.coffee
@@ -16,6 +16,7 @@

       @decimalLimit = @$el.attr('data-limitdecimal') || @options.decimalLimit
       @numberLimit = @$el.attr('data-limitnumber') || @options.numberLimit
+      @min = @$el.attr('min') || 1

       @bind()
       @options.onInit( @$el, @getVal() )
@@ -28,7 +29,7 @@
       @$el.off 'input propertychange'

     getVal: =>
-      @val = @$el.val()
+      @$el.val()

     setVal: (value) =>
       @$el.val( value )
@@ -59,15 +60,16 @@

       return regex

-    extractNumbers: =>
-      if @val != null
-        return @val.match( @buildRegex() )
+    extractNumbers: (val) =>
+      matched = val.match( @buildRegex() )
+      if matched != "" && matched != null
+        return matched

     process: =>
       # Set the current value so we can access it anywhere
       # without having to read it all the time
-      @getVal()
-      extr = @extractNumbers()
+      extr = @extractNumbers( @getVal() )
+      console.log extr

       # If there are captured numbers
       if extr
@@ -77,8 +79,13 @@
         # - the capture is different to the value
         # - AND there is no '.' present after the capture
         # - OR there is no more than one '.' present
-        if ((@val != num) && (@val != num + '.')) || ( @occurrances(@val) > @allowedDecimals)
+        val = @getVal()
+        if ((val != num) && (val != num + '.')) || ( @occurrances(val) > @allowedDecimals)
           @setVal( num )
+
+      # Assume the input is empty and so set the value if it doesn't match the regex
+      else
+        @setVal( "" )

     # Simple function to check how many times
     # a decimal appears in a string


fix smtp-testing CLI

fix smtp-testing CLI

--- tests/smtp-testing/index.coffee
+++ tests/smtp-testing/index.coffee
@@ -33,8 +33,9 @@
         else
             SMTPTesting.onSecondMessage envelope, ->
                 callback null, "ABC" + queueID++
-    smtpServer.listen port, done
+
+    smtpServer.listen parseInt(port), done

 unless module.parent
     port = process.argv[1] or 587
-    SMTPTesting.init port, -> console.log arguments+    SMTPTesting.init port, -> console.log arguments


map_over_array just does what check and call does

--- lib/loofah.coffee
+++ lib/loofah.coffee
@@ -16,10 +16,10 @@
   key_value_pairs: key_value_pairs = (keywords = ['user', 'username', 'password', 'email'], delims = "\\s:=") ->
     (object) -> _check_and_call _key_value_pairs, object, {keywords, delims}

-_check_and_call = (func, object, keywords) ->
-  return func object, keywords if _.isString object
-  return _map_over_array(func, object, keywords) if _.isArray object
-  return _map_over_object(func, object, keywords) if _.isObject object
+_check_and_call = (func, object, keywords, key) ->
+  return func object, keywords, key if _.isString object
+  return _map_over_array(func, object, keywords, key) if _.isArray object
+  return _map_over_object(func, object, keywords, key) if _.isObject object
   object

 _map_over_object = (func, object, keywords, base_key='') ->
@@ -30,10 +30,7 @@

 _map_over_array = (func, object, keywords, key) ->
   _.map object, (item) ->
-    return _map_over_array(func, item, keywords, key) if _.isArray item
-    return _map_over_object(func, item, keywords, key) if _.isObject item
-    return item unless _.isString item
-    func item, keywords, key
+    _check_and_call(func, item, keywords, key)

 _object_keys = (val, b_keys, key) ->
   return val unless key?


fix(debug): reenable setting debug levels

--- src/logger.coffee
+++ src/logger.coffee
@@ -1,4 +1,14 @@
 debug = require('debug')('create-readme')
 levels = ['error', 'warn', 'info', 'verbose', 'debug', 'silly']
+
 for level in levels
   module.exports[level] = debug.extend(level)
+
+Object.defineProperty module.exports, 'level', set: (newLevel) ->
+  index = levels.indexOf(newLevel)
+  if index is -1
+    throw new Error "Level #{newLevel} is not one of the permissible levels #{levels.join(', ')}"
+  for i in [0..index]
+    logger(levels[i]).enable()
+  for i in [index + 1..levels.length - 1]
+    logger(levels[i]).disable()


No need to advanceReadiness (automatic with visit now).

--- assets/scripts/spec/integration/routes_spec.coffee
+++ assets/scripts/spec/integration/routes_spec.coffee
@@ -1,6 +1,4 @@
 module "Router",
-  setup: ->
-    Ember.run -> Travis.advanceReadiness()
   teardown: ->
     Ember.run -> Travis.reset()


go back to using onload, etc handlers

go back to using onload, etc handlers

it is cleaner to read, and is supported in all
browsers for which this library is relavant

--- src/math3d.coffee
+++ src/math3d.coffee
@@ -910,15 +910,18 @@
         when 'string'
             xhr = new XMLHttpRequest()
             xhr.timeout = opts.timeout
-            xhr.onreadystatechange = ->
-                if @readyState is @DONE
-                    if @status is 200 # success
-                        try
-                            create_scene JSON.parse @responseText
-                        catch error
-                            opts.callback? error
-                    else if @status
-                        opts.callback? "error #{@status} when downloading #{opts.scene}"
+            xhr.onload = ->
+                if @status is 200 # success
+                    try
+                        create_scene JSON.parse @responseText
+                    catch error
+                        opts.callback? error
+                else
+                    opts.callback? "errno #{@status} when trying to download #{opts.scene}"
+            xhr.onerror = ->
+                opts.callback? "error when trying to download #{opts.scene}"
+            xhr.onabort = ->
+                opts.callback? "downloading #{opts.scene} aborted"
             xhr.ontimeout = ->
                 opts.callback? "downloading #{opts.scene} timed out"

@@ -926,4 +929,3 @@
             xhr.send()
         else
             opts.callback? "bad scene type #{typeof opts.scene}"
-            obj = new Math3dThreeJS opts.element, opts.scene.opts


Add callbacks for on_clear and on_finish

--- lib/redeye.coffee
+++ lib/redeye.coffee
@@ -120,6 +120,16 @@
   # Alias for `Worker.mixin`
   mixin: (mixins) ->
     Worker.mixin mixins
+
+  # Provide a callback to be executed in the context
+  # of a worker whenever it has finished running, but before
+  # saving its resutlts
+  on_finish: (callback) ->
+    Worker.finish_callback = callback
+
+  # Provide a callback to be called every time the worker begings running
+  on_clear: (callback) ->
+    Worker.clear_callback = callback

 # The worker class is the context under which runner functions are run.
@@ -277,6 +287,7 @@
     @emitted = false
     @search = null
     @is_async = false
+    Worker.clear_callback?.apply this

   # If the caught error is from a `@for_reals`, then try to resolve
   # dependencies.
@@ -304,6 +315,7 @@

   # We're done!
   finish: (result) ->
+    Worker.finish_callback?.apply this
     num_workers--
     @queue.finish @key
     if result? && !@emitted
@@ -358,6 +370,10 @@
   # Set the default wrapper class, which is overridden by `as: `
   wrapper: (klass) ->
     @wrapper_class = klass
+
+  # Return the current worker
+  worker: ->
+    Worker.current

 # Extend the blessed methods with the given ones, so that


Removed a console log

Removed a console log

--- lib/router.coffee
+++ lib/router.coffee
@@ -18,7 +18,6 @@
             if Meteor.loggingIn()
                 @render @loadingTemplate
             else if not Roles.userIsInRole Meteor.user(), ["admin"]
-                console.log "Redirecting"
                 @redirect "/"
     @route "listPatients",
         path: "/patients"


removed logging statement.

removed logging statement.

--- lib/server.coffee
+++ lib/server.coffee
@@ -66,7 +66,6 @@
         options: swaggerOptions
       })
     .then =>
-      console.log 'HALP', Web
       @server.route
         method: 'GET'
         path: '/{param*}'


Silenced annoying Simlish for announced actions until we can get shorter, more legit audio in there.

--- app/lib/surface/Lank.coffee
+++ app/lib/surface/Lank.coffee
@@ -680,6 +680,12 @@
     @lastGold = gold
     Backbone.Mediator.publish 'surface:gold-changed', {team: @thang.team, gold: gold, goldEarned: Math.floor(@thang.goldEarned ? 0)}

+  shouldMuteMessage: (m) ->
+    return true if m in ['moveRight', 'moveUp', 'moveDown', 'moveLeft']
+    return true if /^attack /.test m
+    return true if /^Repeating loop/.test m
+    false
+
   playSounds: (withDelay=true, volume=1.0) ->
     for event in @thang.currentEvents ? []
       @playSound event, withDelay, volume
@@ -687,7 +693,7 @@
         AudioPlayer.playInterfaceSound 'coin_1', 0.25
     if @thang.actionActivated and (action = @thang.getActionName()) isnt 'say'
       @playSound action, withDelay, volume
-    if @thang.sayMessage and withDelay and not @thang.silent  # don't play sayMessages while scrubbing, annoying
+    if @thang.sayMessage and withDelay and not @thang.silent and not @shouldMuteMessage @thang.sayMessage  # don't play sayMessages while scrubbing, annoying
       offsetFrames = Math.abs(@thang.sayStartTime - @thang.world.age) / @thang.world.dt
       if offsetFrames <= 2  # or (not withDelay and offsetFrames < 30)
         sound = AudioPlayer.soundForDialogue @thang.sayMessage, @thangType.get 'soundTriggers'


Set DDP_DEFAULT_CONNECTION_URL to the same value of ROOT_URL

--- packages/rocketchat-lib/lib/startup/settingsOnLoadSiteUrl.coffee
+++ packages/rocketchat-lib/lib/startup/settingsOnLoadSiteUrl.coffee
@@ -4,6 +4,7 @@
 RocketChat.settings.get 'Site_Url', (key, value) ->
 	if value?.trim() isnt ''
 		__meteor_runtime_config__.ROOT_URL = value
+		__meteor_runtime_config__.DDP_DEFAULT_CONNECTION_URL = value
 		if Meteor.absoluteUrl.defaultOptions?.rootUrl?
 			Meteor.absoluteUrl.defaultOptions.rootUrl = value


make the socket path to xmcil-sock

make the socket path to xmcil-sock

--- src/browser/atom-application.coffee
+++ src/browser/atom-application.coffee
@@ -19,9 +19,9 @@

 DefaultSocketPath =
   if process.platform is 'win32'
-    '\\\\.\\pipe\\atom-sock'
+    '\\\\.\\pipe\\xmail-sock'
   else
-    path.join(os.tmpdir(), "atom-#{process.env.USER}.sock")
+    path.join(os.tmpdir(), "xmail-#{process.env.USER}.sock")

 # The application's singleton class.
 #


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color.coffee
+++ test/color.coffee
@@ -458,5 +458,13 @@
   equalEnough piPink.b, 153
   equalEnough piPink.a, 1.0

+test "#transparentize", ->
+  color = Color(0, 0, 0, 1)
+
+  transparentColor = color.transparentize(0.5)
+
+  equal color.a, 1
+  equal transparentColor.a, 0.5
+
 module()


Shorten function names

Shorten function names

--- src/objects/file.proto.coffee
+++ src/objects/file.proto.coffee
@@ -41,18 +41,18 @@
   s = ""
   for a, i in args
     s += ",a[#{i}]"
-  (new Function 'x,f,a', "return x.#{name}(f#{s})") fs, path, args
+  (new Function 'x,f,a', "return x.#{name}TextFile(f#{s})") fs, path, args

 # fs.OpenTextFile
 # args: IOMode, Create, Format
 exports.open = ->
-  varargs 'OpenTextFile', @, arguments
+  varargs 'Open', @, arguments

 # fs.CreateTextFile
 # args: Overwrite, Unicode
 exports.create = ->
-  varargs 'CreateTextFile', @, arguments
+  varargs 'Create', @, arguments

 # Read file
 exports.load = ->


"protocolVersion" added to "parseResponse()" return value

--- src/parser.coffee
+++ src/parser.coffee
@@ -23,6 +23,7 @@
   lines = responseString.split('\r\n')

   parsedStatusLine = parseStatusLine lines.shift()
+  response['protocolVersion'] = parsedStatusLine['protocol']
   response['statusCode'] = parsedStatusLine['statusCode']
   response['statusMessage'] = parsedStatusLine['statusMessage']


Adds support for the VASTAdTagURL-Tag

Adds support for the VASTAdTagURL-Tag

This tag was part of an older version of the VAST2.0 standard (when it was still in draft, presumably), but it is used on some systems out there. It was replaced by the already supported VASTAdTagURI-Tag. For more information see here: http://support.brightcove.com/en/video-cloud/docs/vast-wrapper-ads

--- src/parser.coffee
+++ src/parser.coffee
@@ -159,6 +159,10 @@
         wrapperURLElement = @childByName wrapperElement, "VASTAdTagURI"
         if wrapperURLElement?
             ad.nextWrapperURL = @parseNodeText wrapperURLElement
+        else
+            wrapperURLElement = @childByName wrapperElement, "VASTAdTagURL"
+            if wrapperURLElement?
+                ad.nextWrapperURL = @parseNodeText @childByName wrapperURLElement, "URL"

         wrapperCreativeElement = null
         for creative in ad.creatives


Removed extra whitespace

Removed extra whitespace

--- test/saml2.coffee
+++ test/saml2.coffee
@@ -495,13 +495,11 @@
           type: 'authn_response'
           user:
             name_id: undefined
-            session_index: null,
+            session_index: null
             attributes: {}

         assert.deepEqual response, expected_response
         done()
-
-

   describe 'redirect assert', ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/state.coffee
+++ test/state.coffee
@@ -1,3 +1,4 @@
+###
 test "States transition method should change the current state", ->
   animation = GameObject
     initialState: 'stand'
@@ -32,4 +33,4 @@
     animation.update()

   equals animation.currentState(), animation.defaultState()
-
+###


Fix various test framework issues

Fix various test framework issues

--- test/suite.coffee
+++ test/suite.coffee
@@ -5,12 +5,24 @@
 exports.app = app = require './../coffee/server.coffee'
 exports.data = data = require('./util/data-gen.coffee')(100)

+# @TODO ok, so we had to hack this
+data[50].status = "Offentlig"
+data[51].status = "Offentlig"
+data[52].status = "Offentlig"
+data[53].status = "Offentlig"
+data[53].status = "Offentlig"
+
+exports.cache = redis = null
+
 before (done) -> app.once 'ready', done
 beforeEach (done) ->
   db = app.get 'db'
+  cache = app.get 'cache'
+
+  cache.flushall()
   db.collection('turer').drop (err) ->
     #throw err if err
-    db.collection('turer').insert data, (err) ->
+    db.collection('turer').insert data, {safe: true}, (err) ->
       throw err if err
       done()


Fix issue if no sprite on ship

Fix issue if no sprite on ship

--- src/components/ShipSpawnable.coffee
+++ src/components/ShipSpawnable.coffee
@@ -109,6 +109,7 @@
     this

   _updateShipSprite: ->
+    return unless @ship.has('Sprite')
     sprite = 0
     healthPerc = @health / @maxHealth
     sprite = 2 if healthPerc < .3


Hide setting collector unless collector complexity

If we are at a complexity other than "collector", we hide the checkbox
that allows the user to turn a node into a collector.

--- src/code/views/node-value-inspector-view.coffee
+++ src/code/views/node-value-inspector-view.coffee
@@ -1,13 +1,14 @@
 {div, h2, label, span, input, p, i} = React.DOM

-SimulationStore = require '../stores/simulation-store'
+SimulationStore  = require '../stores/simulation-store'
+AppSettingsStore = require '../stores/app-settings-store'
 tr = require "../utils/translate"

 module.exports = React.createClass

   displayName: 'NodeValueInspectorView'

-  mixins: [ SimulationStore.mixin ]
+  mixins: [ SimulationStore.mixin, AppSettingsStore.mixin ]

   propTypes:
     max: React.PropTypes.number
@@ -97,6 +98,46 @@
         @renderEditableProperty("max", "right")
       )

+  renderCollectorOptions: (node) ->
+    unless @state.complexity is AppSettingsStore.store.Complexity.collectors
+      return null
+
+    isChecked = not @state.capNodeValues and node.allowNegativeValues
+    tooltip = if @state.capNodeValues \
+                then tr "~NODE-VALUE-EDIT.RESTRICT_POSITIVE_DISABLED_TOOLTIP" \
+                else (if isChecked \
+                        then tr "~NODE-VALUE-EDIT.RESTRICT_POSITIVE_CHECKED_TOOLTIP" \
+                        else tr "~NODE-VALUE-EDIT.RESTRICT_POSITIVE_UNCHECKED_TOOLTIP")
+    positiveCheckbox = (
+      label {
+        className: if @state.capNodeValues then 'disabled' else ''
+        title: tooltip
+        key: 'positive-label'
+      }, [
+        input {
+          key: 'positive-checkbox'
+          type: 'checkbox'
+          checked: isChecked
+          disabled: @state.capNodeValues
+          onChange: if @state.capNodeValues then null else @updateNegativeValuesAllowed
+        }
+        tr "~NODE-VALUE-EDIT.RESTRICT_POSITIVE"
+      ]
+    )
+
+    return (span {className: "checkbox group full"},
+      (label {key: 'accumulator-label'}, [
+        input {
+          key: 'accumulator-checkbox'
+          type: 'checkbox'
+          checked: node.isAccumulator
+          onChange: @updateAccumulatorChecked
+        }
+        tr "~NODE-VALUE-EDIT.IS_ACCUMULATOR"
+      ])
+      if node.isAccumulator then positiveCheckbox else null
+    )
+
   render: ->
     node = @props.node
     (div {className: 'value-inspector'},
@@ -128,41 +169,7 @@
           )
         )
         if not node.isTransfer
-          isChecked = not @state.capNodeValues and node.allowNegativeValues
-          tooltip = if @state.capNodeValues \
-                      then tr "~NODE-VALUE-EDIT.RESTRICT_POSITIVE_DISABLED_TOOLTIP" \
-                      else (if isChecked \
-                              then tr "~NODE-VALUE-EDIT.RESTRICT_POSITIVE_CHECKED_TOOLTIP" \
-                              else tr "~NODE-VALUE-EDIT.RESTRICT_POSITIVE_UNCHECKED_TOOLTIP")
-          positiveCheckbox = (
-            label {
-              className: if @state.capNodeValues then 'disabled' else ''
-              title: tooltip
-              key: 'positive-label'
-            }, [
-              input {
-                key: 'positive-checkbox'
-                type: 'checkbox'
-                checked: isChecked
-                disabled: @state.capNodeValues
-                onChange: if @state.capNodeValues then null else @updateNegativeValuesAllowed
-              }
-              tr "~NODE-VALUE-EDIT.RESTRICT_POSITIVE"
-            ]
-          )
-
-          (span {className: "checkbox group full"},
-            (label {key: 'accumulator-label'}, [
-              input {
-                key: 'accumulator-checkbox'
-                type: 'checkbox'
-                checked: node.isAccumulator
-                onChange: @updateAccumulatorChecked
-              }
-              tr "~NODE-VALUE-EDIT.IS_ACCUMULATOR"
-            ])
-            if node.isAccumulator then positiveCheckbox else null
-          )
+          @renderCollectorOptions(node)
       )

       (div {className: "bottom-pane"},


Remove empty arguments head

Remove empty arguments head

--- src/coffee/cilantro/ui/workflows/results.coffee
+++ src/coffee/cilantro/ui/workflows/results.coffee
@@ -82,7 +82,7 @@
                 if c.isSerranoOutdated()
                     $('.serrano-version-warning').show()

-        onPageScroll: () ->
+        onPageScroll: ->
             scrollPos = $(document).scrollTop()

             if @ui.navbar.hasClass('navbar-fixed-top')


Bug fix: double quotes around string values in heap

--- views/coffee/application.coffee
+++ views/coffee/application.coffee
@@ -27,7 +27,7 @@
     s = this
     sNode = s.childNodes[0]
     if sNode
-      sNode.nodeValue = sNode.nodeValue.replace(/^'([\s\S]*)'$/, '$1')
+      sNode.nodeValue = sNode.nodeValue.replace(/^"([\s\S]*)"$/, '$1')
       s.innerHTML = sNode.nodeValue.replace(/\\'/g, '\'')
       s.innerHTML = sNode.nodeValue.replace(/\n/g, '<br>')


Rename attribute to ecosystem_title

Rename attribute to ecosystem_title

--- src/components/task-step/index.cjsx
+++ src/components/task-step/index.cjsx
@@ -38,13 +38,13 @@
   render: ->
     {id, taskId} = @props
     {type} = TaskStepStore.get(id)
-    {ecosystem_info} = TaskStore.get(taskId)
+    {ecosystem_title} = TaskStore.get(taskId)

     Type = getStepType(type)

       <Type {...@props}/>

-        TaskId: {taskId}, StepId: {id}, Ecosystem: {ecosystem_info}
+        TaskId: {taskId}, StepId: {id}, Ecosystem: {ecosystem_title}


app-settings-menu: Added a notification for not previewable files.

--- client/landing/app/Applications/Ace.kdapplication/AppView.coffee
+++ client/landing/app/Applications/Ace.kdapplication/AppView.coffee
@@ -69,9 +69,12 @@

   preview: ->
     publicUrlCheck = /.*\/(.*\.koding.com)\/website\/(.*)/
-    publicPath = @getData().path.replace publicUrlCheck, 'http://$1/$2'
-    return if publicPath is @getData().path
-    KD.getSingleton("appManager").open "Viewer", (appInstance)->
+    publicPath     = @getData().path.replace publicUrlCheck, 'http://$1/$2'
+    appManager     = KD.getSingleton "appManager"
+    if publicPath is @getData().path
+      {nickname} = KD.whoami().profile
+      return appManager.notify "File must be under: /#{nickname}/Sites/#{nickname}.#{location.hostname}/website/"
+    appManager.open "Viewer", (appInstance)->
       appInstance.open publicPath

   compileAndRun: ->


remove useless scope.$apply in pdf scrollhandler

--- services/web/public/coffee/ide/pdfng/directives/pdfViewer.coffee
+++ services/web/public/coffee/ide/pdfng/directives/pdfViewer.coffee
@@ -364,11 +364,11 @@

 				scrollHandler = () ->
 					renderVisiblePages()
-					scope.$apply()
+					#scope.$apply()
 					newPosition = ctrl.getPdfPosition()
 					if newPosition?
 						scope.position = newPosition
-					scope.$apply()
+					#scope.$apply()
 					scope.scrollHandlerTimeout = null

 				scope.$watch 'pdfSrc', (newVal, oldVal) ->


test(utils): add test for `min` function.

--- test/utils.coffee
+++ test/utils.coffee
@@ -14,8 +14,3 @@
     utils.min(['a', 1]).should.be.equal(1)
     utils.min([1, 1]).should.be.equal(1)
     done()
-
-describe 'min', ->
-  it 'should work', (done) ->
-    utils.min([1, 10, 100]).should.be.equal(1)
-    done()


write: fix diff after latest package updates

--- test/write.coffee
+++ test/write.coffee
@@ -1004,13 +1004,13 @@
             diffcalled = true
             diff.should.eql [
               { value: 'Testing diff\n', count: 1 }
+              { value: 'original text', count: 1, added: undefined, removed: true }
               { value: 'new text', count: 1, added: true, removed: undefined }
-              { value: 'original text', count: 1, added: undefined, removed: true }
             ]
           stdout: write: (d) -> data.push d
         , (err) ->
           diffcalled.should.be.ok
-          data.should.eql ['2 + new text\n', '2 - original text\n']
+          data.should.eql ['2 - original text\n', '2 + new text\n']
           next()

     they 'write a buffer', (ssh, next) ->
@@ -1037,7 +1037,7 @@
         content: 'Testing diff\nnew text1'
         stdout: write: (d) -> data.push d
       , (err) ->
-        data.should.eql ['2 + new text1\n', '2 - original text1\n', '3 - original text2\n']
+        data.should.eql ['2 - original text1\n', '3 - original text2\n', '2 + new text1\n']
         next()

     they 'empty source on empty file', (ssh, next) ->


Fixed: Parser writing before assigning listeners

--- test/write.coffee
+++ test/write.coffee
@@ -57,9 +57,9 @@
   it 'instantly emits data once a newline is retrieved', (next) ->
     data = []
     parser = parse()
-    parser.write 'A,B,C\n'
     parser.on 'data', (data) ->
       data.should.eql ['A', 'B', 'C']
       parser.end()
     parser.on 'finish', ->
       next()
+    parser.write 'A,B,C\n'


removing double line

removing double line

--- src/report.coffee
+++ src/report.coffee
@@ -17,7 +17,6 @@

       logger.warn "output file extention '#{@options.output}'
                   does not match reporter '#{reporter}'"
-

     switch reporter
       when 'xml' then reporter = './reporters/xml-pmd'


Clean Up Scoring of path. Apply folder and filename penalty, even if whole path captured by query folder. Some refactor because we always use score as verb elsewhere doScore -> scoreMain, pathScore -> scorePath.

--- src/scorer.coffee
+++ src/scorer.coffee
@@ -47,8 +47,8 @@
 exports.score = (string, query, prepQuery, allowErrors, isPath) ->
   return 0 unless allowErrors or isMatch(string, prepQuery.core_lw, prepQuery.core_up)
   string_lw = string.toLowerCase()
-  score = doScore(string, string_lw, prepQuery)
-  if isPath then score = pathScore(string, string_lw, prepQuery, score)
+  score = scoreMain(string, string_lw, prepQuery)
+  if isPath then score = scorePath(string, string_lw, prepQuery, score)
   return Math.ceil(score)

@@ -115,7 +115,7 @@
 # Main scoring algorithm
 #

-doScore = (subject, subject_lw, prepQuery) ->
+scoreMain = (subject, subject_lw, prepQuery) ->
   query = prepQuery.query
   query_lw = prepQuery.query_lw

@@ -490,13 +490,12 @@
 # Score adjustment for path
 #

-pathScore = (subject, subject_lw, prepQuery, fullPathScore) ->
+scorePath = (subject, subject_lw, prepQuery, fullPathScore) ->
   return 0 if fullPathScore is 0
-

   # Skip trailing slashes
   end = subject.length - 1
-  end-- while subject[end] is PathSeparator
+  while subject[end] is PathSeparator then end--

   # Get position of basePath of subject.
   basePos = subject.lastIndexOf(PathSeparator, end)
@@ -512,15 +511,13 @@
   depth = prepQuery.depth

   # Get that many folder from subject
-  while(depth-- > 0)
+  while basePos > -1 and depth-- > 0
     basePos = subject.lastIndexOf(PathSeparator, basePos - 1)
-    if (basePos is -1) then return fullPathScore #consumed whole subject ?
-
-  # Get basePath score
-  basePos++
-  end++
-  basePathScore = doScore(subject[basePos...end], subject_lw[basePos...end], prepQuery)
-  basePathScore *= extAdjust
+
+  # Get basePath score, if BaseName is the whole string, no need to recompute
+  # We still need to apply the folder depth and filename penalty.
+  basePathScore = if (basePos is -1) then fullPathScore else
+    extAdjust * scoreMain(subject.slice(basePos + 1, end + 1), subject_lw.slice(basePos + 1, end + 1), prepQuery)

   # Final score is linear interpolation between base score and full path score.
   # For low directory depth, interpolation favor base Path then include more of full path as depth increase


extends fixes

extends fixes

--- src/sentai.coffee
+++ src/sentai.coffee
@@ -50,6 +50,7 @@
               set: (val)->
                 @[__v] = val
                 to.call(@_entity, val)
+              enumerable:true
             }
         else if type =='string'
           config = (__v, v, to) ->
@@ -60,6 +61,7 @@
               set: (val)->
                 @[__v] = val
                 @_entity[to] = val
+              enumerable:true
             }
       else
         config = (__v, v) ->
@@ -70,6 +72,7 @@
             set: (val)->
               @[__v] = val
               @_entity[v] = val
+            enumerable:true
           }
       Object.defineProperty(prototype, v, config('__' + v, v, to))
   return @
@@ -99,9 +102,6 @@
       component.prototype[name] = extension

   component.prototype._entity = null
-  component.prototype._sync = null
-  component.prototype._listensTo = null
-  component.prototype._observes = null

   component.sync = addSyncs
   component.listensTo = addListensTo
@@ -199,6 +199,7 @@
               for arg in getSetConfig.vars
                 args.push(@[arg])
               getSetConfig.cb.apply(@_components[getSetConfig.ctx], args)
+        enumerable:true
       }
     Object.defineProperty(NewClass.prototype, v, config('__' + v, getSet))


JS scoping ftw

JS scoping ftw

--- src/server.coffee
+++ src/server.coffee
@@ -30,7 +30,7 @@
     @app.use '/vendor/', express.static("#{__dirname}/../bower_components/")
     @app.use '/nota.js', express.static("#{__dirname}/client-config.js")

-    @app.get '/data.json', ( req, res ) ->
+    @app.get '/data.json', ( req, res ) =>
       res.send JSON.stringify(@data)

     @server.listen(serverPort)


Bad indentation. Bad. Go to your room.

Bad indentation. Bad. Go to your room.

--- src/server.coffee
+++ src/server.coffee
@@ -226,7 +226,7 @@
       if user is @name
         user += " (you)"
       @send " * #{user}"
-      @send "end of list."
+    @send "end of list."

   ###
   Callback for listing all channels


fix bug when message isn't a JSON string

--- src/server.coffee
+++ src/server.coffee
@@ -28,7 +28,14 @@
 handleMessage = ( e ) ->
     if e.origin not in trustedDomains
         throw new Error "untrusted origin"
-    data = JSON.parse e.data
+    data =
+        try
+            if typeof e.data is 'string'
+                JSON.parse e.data
+            else
+                e.data
+        catch
+            e.data
     if data.storage
         if data.storage.loaded
             e.source.postMessage JSON.stringify({ storage: { loaded: true }}), e.origin


Use correct member model

Use correct member model

--- src/member/javascripts/services/login.js.coffee
+++ src/member/javascripts/services/login.js.coffee
@@ -8,7 +8,7 @@
     halClient.$post(url, options, form).then (login) ->
       if login.$has('member')
         login.$get('member').then (member) ->
-          member = new BBModel.Client(member)
+          member = new BBModel.Member.Member(member)
           defer.resolve(member)
       else if login.$has('members')
         defer.resolve(login)


Disable need to be logged in to get room infos

--- src/server/Application.coffee
+++ src/server/Application.coffee
@@ -106,7 +106,6 @@
 		return RoomManager.getList()

 	onRoomRequest: (request, response) =>
-		session = @getAndTestSession(request)
 		room = RoomManager.get(request.params.room);
 		if (!room?)
 			throw HttpErrors.invalidRoomName()


bug fix

bug fix

--- src/components/widgets/accounts-assets-liability-summary/accounts-assets-liability-summary.directive.coffee
+++ src/components/widgets/accounts-assets-liability-summary/accounts-assets-liability-summary.directive.coffee
@@ -21,7 +21,7 @@
   w.initContext = ->
     $scope.isDataFound = angular.isDefined(w.content) && !_.isEmpty(w.content.summary)
     $scope.multiEntity = w.metadata.organization_ids.length > 1
-    if $scope.multiEntity
+    if $scope.multiEntity && w.content.account_list
       w.selectedAccount = w.content.account_list[0]
       $scope.applyAccountSelection()
     if !$scope.selectedAccountsOption


jupyter slideshow -- import metadata

jupyter slideshow -- import metadata

--- src/smc-webapp/jupyter/import-from-ipynb.coffee
+++ src/smc-webapp/jupyter/import-from-ipynb.coffee
@@ -190,8 +190,11 @@
             exec_count : @_get_exec_count(cell.execution_count, cell.prompt_number)

         for k in ['collapsed', 'scrolled']
-            if cell.metadata[k]
-                obj[k] = !!cell.metadata[k]
+            if cell.metadata?[k]
+                obj[k] = !!cell.metadata?[k]
+
+        if cell.metadata?.slideshow?
+            obj.slide = cell.metadata.slideshow.slide_type

         return obj


Cleaning up event loop timing.

Cleaning up event loop timing.

--- src/timber.coffee
+++ src/timber.coffee
@@ -234,6 +234,7 @@
     add : (element) ->
         @elements.push(element)

+    # Run the engine's event loop.
     loop : () ->
         # Stop, if so desired.
         return if not @continue
@@ -241,7 +242,7 @@
         # Update the state of the world.
         now = new Date()
         elapsed = now - @timestamp
-        @environment.elapse(@elements, 1)
+        @environment.elapse(@elements, elapsed)
         @canvas.clear()
         @canvas.render(@elements)
         @timestamp = now
@@ -249,4 +250,4 @@
         # Loop again, when the stack clear.
         setTimeout () =>
             @.loop()
-        , 10
+        , 0


Don't respond to elements moving apart.

Don't respond to elements moving apart.

--- src/timber.coffee
+++ src/timber.coffee
@@ -529,7 +529,7 @@
         # Calcule the seperating velocity of the elements.
         seperatingVelocity = collision.getSeperatingVelocity()

-        #return if 0 > seperatingVelocity
+        return if 0 > seperatingVelocity

         # Change the direction and scale by the elasticity of the elements.
         velocity = -seperatingVelocity * collision.getRestitutionCoefficient()


Changed regex so "Hubot tucker <Firstname Lastname>" works and

doesn't cut off the second word.

--- src/tucker.coffee
+++ src/tucker.coffee
@@ -78,7 +78,7 @@
 allQuotes = secondPerson.concat thirdPerson, restOfQuotes

 module.exports = (robot) ->
-  robot.respond /tucker (\w+)/i, (res) ->
+  robot.respond /tucker (.+)/i, (res) ->
     person = res.match[1]
     if person is "me"
       quote = res.random thirdPerson


fix error on some yakyak program exits #451

--- src/ui/app.coffee
+++ src/ui/app.coffee
@@ -97,8 +97,8 @@

 #
 window.addEventListener 'unload', (ev) ->
-    if process.platform == 'darwin'
-        if window?.isFullScreen()
+    if process.platform == 'darwin' && window?
+        if window.isFullScreen()
             window.setFullScreen false
         if not remote.getGlobal('forceClose')
             ev.preventDefault()


Fix bug on WSServer on add a client in the rooms list

--- src/ws/webSocketServer.coffee
+++ src/ws/webSocketServer.coffee
@@ -82,7 +82,7 @@
         clientToSendId  = args[2]

         console.log(eventName)
-        sock = room[clientToSendId]
+        sock = room[roomId][clientToSendId]
         if sock?
           args[1] = socket.clientId

@@ -91,19 +91,26 @@
               console.log(error))
       else
         if eventName == 'authentification'
-          clientId  = args[2]
-
           console.log "Authentification"

+          clientId  = args[2]
+
           if roomValidation?
             console.log "Room" + roomId + " is valid"
             datas = getClientDatas(clientId)
             if datas?
               socket.roomId   = roomId
               socket.clientId = clientId
+              socket.enable         = true

-              rooms[roomId] = socket
-              socket.enable = true
+              if not rooms[roomId]
+                roomSockets             = {}
+                roomSockets[clientId]   = socket
+                rooms[roomId]           = roomSockets
+              else
+                rooms[roomId][clientId] = socket
+
+              console.log rooms
             else
               socket.close()
           else
@@ -116,7 +123,7 @@
   socket.on('close', () ->
     if socket.enable == true
       if socket.roomId != -1 && rooms[socket.roomId]
-        rooms[socket.roomId] = null
+        rooms[socket.roomId][socket.clientId] = null
     sockets.splice(sockets.indexOf(socket), 1)
     console.log('close')
     )


groups: link wip, target blank

groups: link wip, target blank

--- client/landing/app/Applications/Groups.kdapplication/Views/GroupsListItemView.coffee
+++ client/landing/app/Applications/Groups.kdapplication/Views/GroupsListItemView.coffee
@@ -104,7 +104,7 @@
       tagName : 'a'
       attributes :
         href : '/'+@getData().slug+"/Activity"
-        target : @getData().slug
+        target : '_blank'
       partial : 'Open Group'


Reduce labels on opinions to allow longer categories

--- static/coffee/v2_polls.coffee
+++ static/coffee/v2_polls.coffee
@@ -353,7 +353,7 @@
               style: {
                 color: '#333'
                 fontWeight: 'bold'
-                fontSize: '1.25rem'
+                fontSize: '0.75rem'
                 textOutline: false
               }
               formatter: ->


Update blog link on classify page

Update blog link on classify page

--- src/scripts/views/Classifier.coffee
+++ src/scripts/views/Classifier.coffee
@@ -1,7 +1,7 @@
 define (require, exports, module) ->
   module.exports = """

-
This audio clip was recorded in Zambia. Click here to learn more about Zambian bats and the Bat Detective World Tour.

+
This audio clip was recorded in Zambia. Click here to learn more about Zambian bats and the Bat Detective World Tour.