Add tests for max_line_length

Add tests for max_line_length

--- test/brave-mouse/validate.coffee
+++ test/brave-mouse/validate.coffee
@@ -369,3 +369,41 @@
 						results.should.be.true

 						done()
+
+			describe 'max_line_length', ->
+				after ->
+					mockFs.restore()
+
+				it 'should validate if all lines in file have less than 80 characters', (done) ->
+					prepareFs 'max_line_length', 80, 'max_line_length/under-80.txt'
+
+					bmValidate 'file.txt', (err, results) ->
+						results.should.be.true
+
+						done()
+
+				it 'should validate if all lines in file have less than or equal to 80 characters', (done) ->
+					prepareFs 'max_line_length', 80, 'max_line_length/80.txt'
+
+					bmValidate 'file.txt', (err, results) ->
+						results.should.be.true
+
+						done()
+
+				it 'should validate if file contains lines having more than 80 characters', (done) ->
+						prepareFs 'max_line_length', 80, 'max_line_length/over-80.txt'
+
+						bmValidate 'file.txt', (err, results) ->
+							results.max_line_length.should.deep.equal
+								expected: 80
+								is: 212
+
+							done()
+
+				it 'should not validate if nothing is expected', (done) ->
+					prepareFs null, null, 'max_line_length/80.txt'
+
+					bmValidate 'file.txt', (err, results) ->
+						results.should.be.true
+
+						done()


OPCE19: minor changes in test and some cleanup

--- test/rte-test.coffee
+++ test/rte-test.coffee
@@ -97,17 +97,19 @@
     delta = { ops:[
       { insert: 'Gandalf', attributes: { bold: true } },
       { insert: ' the ' },
-      { insert: 'Grey', attributes: { color: '#ccc' } }
+      { insert: 'Grey', attributes: { color: '#ccc' } },
+      { insert: '.'}
       ] }
     rte1 = new Rte()

     rte1.delta delta

-    rte1.val().should.equal "Gandalf the Grey"
-    rte1._rte.words.length.should.equal 3
+    rte1.val().should.equal "Gandalf the Grey."
+    rte1._rte.words.length.should.equal 4
     rte1._rte.words[0].word.should.equal "Gandalf "
     rte1._rte.selections[0].should.have.property 'left', rte1.getWord(0)
     rte1._rte.selections.length.should.equal 2
+
   it 'accept styles [delta]', ->
     delta = { ops:[
       { insert: 'Gandalf', attributes: { bold: true } },
@@ -121,7 +123,7 @@
       {retain: 5, attributes: {bold: true}}
       ]}
     rte1.delta delta1
-    console.log rte1._rte.selections
+    rte1.getSelections().length.should.equal 1

   it 'should accept deltas (retain & delete) [delta]', ->
     delta = { ops:[
@@ -132,12 +134,6 @@
     rte1.delta delta
     rte1.val().should.equal "Gandalf Grey"

-  it 'should accept deltas (style) [delta]', ->
-    delta = {ops:[
-      { retain: 7, attributes: {bold: true } }]}
-    rte1 = new Rte "Gandalf the Grey"
-    rte1.delta delta
-
 describe 'Utilities', ->
   it 'should convert correctly [relativeFromAbsolute]', ->
     rte = new Rte "Zero One two three four five"


Added `parts` to data.

Added `parts` to data.

--- tasks/markdown_jade.coffee
+++ tasks/markdown_jade.coffee
@@ -224,6 +224,7 @@
         process: (contents, path) ->
           html = grunt.template.process contents, data: _.extend({
               section: _.last(leavePath)
+              ids: parts
               all: data
               root: leavePath[0]
             }, _options.data)


[runbooks] handle NPEs

[runbooks] handle NPEs

--- app/assets/javascripts/states/runbooks/views/_command_response.js.coffee
+++ app/assets/javascripts/states/runbooks/views/_command_response.js.coffee
@@ -15,13 +15,16 @@
     return (a == "{" && b == "}") || (a == "[" && b == "]")

   num_bytes: (str) ->
+    if !str || str.length == 0
+      return "0 bytes"
+
     s = "#{str.length} byte"
     if str.length != 1
       s += "s"
     return s

   num_lines: (str) ->
-    if str.length == 0
+    if !str || str.length == 0
       return "0 lines"

     lines = str.split("\n")


menu/debug: Add Spawn Shape.

menu/debug: Add Spawn Shape.

--- Oghma/menu/debug.coffee
+++ Oghma/menu/debug.coffee
@@ -45,6 +45,11 @@
             O.action.show_origin_marker()
           else
             O.action.hide_origin_marker()
+      },
+      {
+        text: "Spawn Shape"
+        handler: ->
+          O.tableverse.create( 'shape' )
       }
     ]
   )


Two new task states

Two new task states

--- SingularityUI/app/constants.coffee
+++ SingularityUI/app/constants.coffee
@@ -28,6 +28,12 @@
         TASK_LOST:
             isActive: false
             label: 'Lost'
+        TASK_CLEANING:
+            isActive: false
+            label: 'Cleaning' # Trying to be killed?
+        TASK_LOST_WHILE_DOWN:
+            isActive: false
+            label: 'Lost while down'

     requestStates:
         CREATED: 'Created'


views test: can use @.

views test: can use @.

The `room` tests are still not 100% working.

--- tests/sockets.coffee
+++ tests/sockets.coffee
@@ -92,12 +92,12 @@
     c.on 'joined', (data) ->
       t.reached 'joined1'
       t.equal 'room1', data.room, 'main'
-      c.emit said: {msg:'said2'}
+      @emit said: {msg:'said2'}

     c2.on 'joined', (data) ->
       t.reached 'joined2'
       t.equal 'room2', data.room, 'main'
-      c2.emit said: {msg:'said1'}
+      @emit said: {msg:'said1'}

     c.on 'said', (data) ->
       t.reached 'reached1'


Why did we disabled it? It breaks message list in avatararea

--- client/landing/app/Applications/Inbox.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Inbox.kdapplication/AppController.coffee
@@ -25,7 +25,7 @@
   fetchMessages:(options, callback)->
     # log "FETCH MESSAGES INTERNAL"
     {currentDelegate} = KD.getSingleton('mainController').getVisitor()
-    #currentDelegate.fetchMail? options, callback
+    currentDelegate.fetchMail? options, callback

   fetchAutoCompleteForToField:(inputValue,blacklist,callback)->
     bongo.api.JAccount.byRelevance inputValue,{blacklist},(err,accounts)->


TeamJoinByLoginForm: Listen FormValidationFailed event

--- client/landing/site.landing/coffee/team/forms/teamjoinbyloginform.coffee
+++ client/landing/site.landing/coffee/team/forms/teamjoinbyloginform.coffee
@@ -38,7 +38,7 @@
     @button     = @getButton @getOption 'buttonTitle'
     @buttonLink = @getButtonLink @createButtonLinkPartial(), callback

-    @on 'FormSubmitFailed', @button.bound 'hideLoader'
+    @on [ 'FormSubmitFailed', 'FormValidationFailed' ], @button.bound 'hideLoader'

   createButtonLinkPartial: ->


:lipstick: Use getVariables() instead of variables in tests

--- spec/color-project-spec.coffee
+++ spec/color-project-spec.coffee
@@ -56,8 +56,8 @@
       ])

     it 'scans the loaded paths to retrieve the variables', ->
-      expect(project.variables).toBeDefined()
-      expect(project.variables.length).toEqual(TOTAL_VARIABLES_IN_PROJECT)
+      expect(project.getVariables()).toBeDefined()
+      expect(project.getVariables().length).toEqual(TOTAL_VARIABLES_IN_PROJECT)

     it 'dispatches a did-initialize event', ->
       expect(eventSpy).toHaveBeenCalled()
@@ -147,7 +147,7 @@
             "#{rootPath}/styles/variables.styl"
           ]
           buffers: {}
-          variables: project.variables.map (v) -> v.serialize()
+          variables: project.getVariables().map (v) -> v.serialize()
         })

     describe '::getVariablesForPath', ->
@@ -192,7 +192,7 @@
           expect(project.deleteVariablesForPaths).toHaveBeenCalled()

         it 'scans again the file to find variables', ->
-          expect(project.variables.length).toEqual(TOTAL_VARIABLES_IN_PROJECT)
+          expect(project.getVariables().length).toEqual(TOTAL_VARIABLES_IN_PROJECT)

         it 'dispatches a did-reload-file-variables event', ->
           expect(eventSpy).toHaveBeenCalled()
@@ -212,7 +212,7 @@
           expect(project.deleteVariablesForPaths).toHaveBeenCalled()

         it 'scans again the file to find variables', ->
-          expect(project.variables.length).toEqual(TOTAL_VARIABLES_IN_PROJECT)
+          expect(project.getVariables().length).toEqual(TOTAL_VARIABLES_IN_PROJECT)

         it 'dispatches a did-reload-file-variables event', ->
           expect(eventSpy).toHaveBeenCalled()


Fixed the broken test case

Fixed the broken test case

--- spec/config-layout-spec.coffee
+++ spec/config-layout-spec.coffee
@@ -31,21 +31,21 @@
     attr = 'spacing'

     # Default value
-    expect(domattr(attr)).toBe '100'
+    expect(domattr(attr)).toBe '80'

     # Invalid value
     atom.config.set('reduced-dark-ui.layout.spacing', false)
-    expect(domattr(attr)).toBe '100'
+    expect(domattr(attr)).toBe '80'

     # Minimum boundary
     atom.config.set('reduced-dark-ui.layout.spacing', 10)
     console.log(document.documentElement.getAttribute('spacing'))
-    expect(domattr(attr)).toBe '50'
+    expect(domattr(attr)).toBe '40'

     # Maximum boundary
     atom.config.set('reduced-dark-ui.layout.spacing', 250)
     console.log(document.documentElement.getAttribute('spacing'))
-    expect(document.documentElement.getAttribute('spacing')).toBe '150'
+    expect(document.documentElement.getAttribute('spacing')).toBe '160'

     # Regular case
     atom.config.set('reduced-dark-ui.layout.spacing', 55)


Wrap app.isMobile in try/catch

Wrap app.isMobile in try/catch

--- assets/javascripts/app/app.coffee
+++ assets/javascripts/app/app.coffee
@@ -191,11 +191,14 @@
     !!(@DOC or @doc)

   isMobile: ->
-    # Need to sniff the user agent because some Android and Windows Phone devices don't take
-    # resolution (dpi) into account when reporting device width/height.
-    @_isMobile ?= (matchMedia('(max-device-width: 767px), (max-device-height: 767px)').matches) or
-                  (navigator.userAgent.indexOf('Android') isnt -1 and navigator.userAgent.indexOf('Mobile') isnt -1) or
-                  (navigator.userAgent.indexOf('IEMobile') isnt -1)
+    try
+      # Need to sniff the user agent because some Android and Windows Phone devices don't take
+      # resolution (dpi) into account when reporting device width/height.
+      @_isMobile ?= (window.matchMedia('(max-device-width: 767px), (max-device-height: 767px)').matches) or
+                    (navigator.userAgent.indexOf('Android') isnt -1 and navigator.userAgent.indexOf('Mobile') isnt -1) or
+                    (navigator.userAgent.indexOf('IEMobile') isnt -1)
+    catch
+      @_isMobile = false

   isInvalidLocation: ->
     @config.env is 'production' and location.host.indexOf(app.config.production_host) isnt 0


Change new playlist_item default title to include structure info

--- app/assets/javascripts/me-add-to-playlist.coffee
+++ app/assets/javascripts/me-add-to-playlist.coffee
@@ -19,7 +19,17 @@
       button.click (event) ->
         if $('#add_playlist_item_playlists_not_empty')[0].innerHTML == 'false'
           $('#playlist_item_description')[0].value = ''
-          $('#playlist_item_title').val(player.options.playlistItemDefaultTitle)
+          default_title = player.options.playlistItemDefaultTitle
+          current_span = $('li i.now-playing').parent().find('a[data-fragmentbegin]')
+          if current_span
+            re = /\s*\(.*\)$/ # duration notation at end of section title ' (2:00)'
+            structure_title = current_span.text().replace(re,'').trim()
+            parent = current_span.closest('ul').closest('li').prev()
+            while parent.length > 0
+              structure_title = parent.text().trim()+' - '+structure_title
+              parent = parent.closest('ul').closest('li').prev()
+            default_title = default_title+' - '+structure_title
+          $('#playlist_item_title').val(default_title)
           $('#playlist_item_start').val(mejs.Utility.secondsToTimeCode(player.getCurrentTime(), true))
           if $('a.current-stream').length && typeof($('a.current-stream')[0].dataset.fragmentend) != typeof(undefined)
             end_time = $('a.current-stream')[0].dataset.fragmentend


FSItem: latest version of oskite.exec is expecting an object instead string

--- client/ide/fs/fsitem.coffee
+++ client/ide/fs/fsitem.coffee
@@ -53,7 +53,7 @@

         command = "#{ commandPrefix } #{ escapeFilePath sourceItem.path } #{ escapeFilePath actualPath }"

-        kite.exec(command)
+        kite.exec({command})

         .then(handleStdErr())

@@ -103,7 +103,7 @@
         else
           "zip -r #{escapeFilePath actualPath} #{escapeFilePath file.path}"

-      kite.exec command
+      kite.exec {command}

     .then(handleStdErr())

@@ -143,7 +143,7 @@
         else
           "cd #{escapeFilePath file.parentPath};unzip -o #{escapeFilePath file.name} -d #{escapeFilePath actualPath}"

-      kite.exec(command)
+      kite.exec({command})

     .then(handleStdErr())


Fix share popup method invocation

Fix share popup method invocation

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -506,7 +506,7 @@
       if not machineItem.isMine() and not machineItem.isApproved()
         { activitySidebar } = kd.singletons.mainView
         box = activitySidebar.getMachineBoxByMachineUId machineItem.uid
-        box.machineItem.showSidebarSharePopup sticky: yes
+        box.machineItem.showSharePopup sticky: yes, workspaceId: @workspaceData.getId()
         withFakeViews = yes

       @setMountedMachine machineItem


Fixes slow loading of games side due to fetching all games played so far

--- client/routes/routes.coffee
+++ client/routes/routes.coffee
@@ -42,12 +42,12 @@
   template: 'games'
   waitOn: ->
     [
-      Meteor.subscribe 'playerGames'
+      cachedSubs.subscribe 'playerGames'
       cachedSubs.subscribe "playerGroups"
     ]

   games: ->
-    Games.find {}, {sort: {createdAt: -1, _id: -1}}
+    Games.find( {}, {sort: {createdAt: -1, _id: -1}}, {limit: 10})
   data: ->
     games: @games()


Kloud::info: if a managed vm doesn't have any klient instance force to recreate

--- client/app/lib/kite/kites/kloud.coffee
+++ client/app/lib/kite/kites/kloud.coffee
@@ -99,7 +99,7 @@

     unless klientKite?

-      if machine.status.state is Machine.State.Running
+      if machine.status.state is Machine.State.Running or managed

         klientKite = kontrol.getKite
           name            : 'klient'
@@ -107,9 +107,8 @@
           correlationName : machine.uid

       else
-        return callback if managed
-        then State: Machine.State.Stopped, via: 'klient'
-        else null
+        return callback null
+

     klientKite.ping()


hub/quota: fix test

hub/quota: fix test

--- src/smc-hub/test/kucalc/quota.coffee
+++ src/smc-hub/test/kucalc/quota.coffee
@@ -119,13 +119,13 @@
                     memory          : 20000 # max 16gb
                     mintime         : 24*3600*100 # max 90 days
                     memory_request  : 10000 # max 8gb
-                    cores           : 7  # max 2
-                    cpu_shares      : 1024 * 4 # max 3 requests
+                    cores           : 7  # max 3
+                    cpu_shares      : 1024 * 4 # max 2 requests

         maxedout = quota({}, over_max)
         exp =
-            cpu_limit: 2
-            cpu_request: 3 # set at the top of quota config
+            cpu_limit: 3
+            cpu_request: 2 # set at the top of quota config
             disk_quota: 20000
             idle_timeout: 24*3600*90
             member_host: true
@@ -193,7 +193,7 @@
             memory_request  : 4210
             memory_limit    : 1500 # 1500 mb free for members
             cpu_request     : .5
-            cpu_limit       : 2
+            cpu_limit       : 3
             privileged      : false
             idle_timeout    : 24*3600*90  # 1800 secs free
             disk_quota      : 23000


Update platform.coffee

Update platform.coffee

--- src/tasks/build/base/platform.coffee
+++ src/tasks/build/base/platform.coffee
@@ -8,7 +8,7 @@
     helpers.exec "phonegap remote build #{platform} #{helpers.setVerbosity()}", fn

   local = (platform, fn) ->
-    helpers.exec "phonegap local build #{platform} #{helpers.setVerbosity()}", fn
+    helpers.exec "phonegap build #{platform} #{helpers.setVerbosity()}", fn

   runAfter = (provider, platform, fn) ->
     adapter = path.join __dirname, '..', 'after', provider, "#{platform}.js"


fix error on space in internal link form (#1891)

--- BackofficeBundle/Resources/public/coffee/InternalLinkFormView.coffee
+++ BackofficeBundle/Resources/public/coffee/InternalLinkFormView.coffee
@@ -90,7 +90,7 @@
         if field.value != ''
           fieldName = field.name.replace(@formName, '').replace(/\]\[/g, '_').replace(/(\]|\[)/g, '')
           if fieldName != '_token'
-            fields[fieldName] = field.value
+            fields[fieldName] = $("<div/>").text(field.value).html()
       link = $('<a href="#">').html(inputText.val()).attr('data-options', JSON.stringify(fields))
       div = $('<div>').append(link)
       sourceNode = $(tinymce.get(@options.editor.id).selection.getNode())


Only show ASCII banner if not embedded

Only show ASCII banner if not embedded

--- scripts/global.coffee
+++ scripts/global.coffee
@@ -129,8 +129,8 @@
     , false) for link in document.querySelectorAll('.ad-opt-out')
 , false)

-
-console.log("""
+if not window.embedded
+    console.log("""
    -++++:--`
   -hddddddddhy+/.
  -hddddddddddddddhy/.


For all users, not just most recent checked.

--- scripts/points.coffee
+++ scripts/points.coffee
@@ -126,5 +126,5 @@
         if robot.auth.hasRole(msg.envelope.user, ['admin', 'moderator'])
             points[username] ?= 0

-            score = ["#{username} has #{points[username]}"]
+            score = ["#{username} has #{points[username]}" for username in people]
             msg.send "Stored scores: #{score}"


Try looping through users.

Try looping through users.

--- scripts/points.coffee
+++ scripts/points.coffee
@@ -127,5 +127,6 @@
             robot.brain.get points
             points[username] ?= 0

-            score = ["#{username} has #{points[username]}" .forEach username]
+            for username in people
+                score = ["#{username} has #{points[username]}"]
             msg.send "Stored scores: #{score}"


Flatten once more.

Flatten once more.

--- scripts/points.coffee
+++ scripts/points.coffee
@@ -14,6 +14,8 @@
 #   brettlangdon

 points = {}
+flatten = (array) ->
+    Array::concat.apply([], array)

 award_points = (msg, username, pts) ->
     points[username] ?= 0
@@ -85,9 +87,7 @@
     robot.http("https://tmi.twitch.tv/group/user/masonest/chatters")
         .get() (err, res, body) ->
             chat = JSON.parse(body)
-            people = [chat.chatters.moderators, chat.chatters.staff, chat.chatters.admins, chat.chatters.global_mods, chat.chatters.viewers]
-            flatten = (people) ->
-                people::concat.apply([], people)
+            people = flatten([chat.chatters.moderators, chat.chatters.staff, chat.chatters.admins, chat.chatters.global_mods, chat.chatters.viewers], array)

             setInterval(->
                 for username in people


Add filter to train

Add filter to train

--- scripts/yamada.coffee
+++ scripts/yamada.coffee
@@ -149,7 +149,8 @@
     cheerio-httpcli.fetch 'http://api.tetsudo.com/traffic/atom.xml?kanto', {}, (err, $, res)->
       trains = []
       $('entry > title').each ()->
-        trains.push($(this).text())
+        if /JR東日本|東京メトロ|都営地下鉄|東武鉄道|西武鉄道|京成電鉄|京王電鉄|小田急電鉄|東急電鉄|京急電鉄|横浜市営地下鉄|りんかい線|つくばエクスプレス|ゆりかもめ|東京モノレール|日暮里・舎人ライナー/.test($(this).text())
+          trains.push($(this).text())
       if trains.length > 0
         msg.send "遅延してる電車な。"
         for train in trains


fix bug when overriding subject

fix bug when overriding subject

--- lib/Spec/SeaweedDSL.coffee
+++ lib/Spec/SeaweedDSL.coffee
@@ -50,9 +50,9 @@
   #     its 'name', -> should equal('Fred')
   its: (attribute, definition) ->
     it "#{attribute} #{Spec.Util.descriptionize definition}", ->
-      value = @subject[attribute]
-      value = value.call @subject if typeof value is 'function'
-      @subject = value
+      value = @_subject[attribute]
+      value = value.call @_subject if typeof value is 'function'
+      @_subject = value
       definition.call this

   # Runs a test against @subject
@@ -61,7 +61,7 @@
   #     subject -> new Employee()
   #     it -> should beAnInstanceOf(Employee)
   should: (matcher) ->
-    expect(@subject).to matcher
+    expect(@_subject).to matcher

   # Runs a negative test against @subject
   #
@@ -69,7 +69,7 @@
   #     subject -> new Employee()
   #     it -> shouldNot be(null)
   shouldNot: (matcher) ->
-    expect(@subject).notTo matcher
+    expect(@_subject).notTo matcher

   # Creates a new mock object
   #
@@ -113,7 +113,7 @@
     definition = args.pop()
     name = args.pop()
     before ->
-      root.subject = @subject = definition.call this
+      root._subject = @subject = definition.call this
       @[name] = @subject if name

 window.Spec.SeaweedDSL.context = window.Spec.SeaweedDSL.describe


Fixed error when timbres are not ready.

Fixed error when timbres are not ready.

--- lib/atom-code-music.coffee
+++ lib/atom-code-music.coffee
@@ -1,48 +1,38 @@
 fs = require("fs")
 path = require("path")
 {CompositeDisposable} = require("atom")
+base64Binary = require(path.join(__dirname, "base64binary"))
 timbres =
   "Piano": require(path.join(__dirname, "timbres", "acoustic_grand_piano-ogg"))
   "Marimba": require(path.join(__dirname, "timbres", "marimba-ogg"))
 Converter = require(path.join(__dirname, "converter"))
+keysNotes = JSON.parse(fs.readFileSync(
+  path.join(__dirname, "keys-notes.json")
+))
+sheetsArray = fs.readdirSync(path.join(__dirname, "sheets"))

 class AtomCodeMusic
   constructor: () ->
+    @readyTimbres = 0
+    @timbresLength = 0
     @i = 0
-    @base64Binary = require(path.join(__dirname, "base64binary"))
-    @keysNotes = JSON.parse(fs.readFileSync(
-      path.join(__dirname, "keys-notes.json")
-    ))
     @subscriptions = new CompositeDisposable()
     @sheet = []
     @sheets= {}
-    sheetsArray = fs.readdirSync(path.join(__dirname, "sheets"))
-    for sheetName in sheetsArray
-      @sheets[sheetName] = JSON.parse(fs.readFileSync(
-        path.join(__dirname, "sheets", sheetName)
-      ))
-    # Use two closure wrappers to save the status.
-    # Because decodeAudioData() is an async method.
+    @loadSheets()
     @sources = []
     @context = new AudioContext()
     @gainNode = @context.createGain()
     @gainNode.connect(@context.destination)
     @gainNode.gain.value = 1
     @timbresAudio = {}
-    for timbre of timbres then do (timbre) =>
-      @timbresAudio[timbre] = []
-      for pitch of timbres[timbre] then do (pitch) =>
-        @context.decodeAudioData(@base64Binary.decodeArrayBuffer(
-          timbres[timbre][pitch].split("data:audio/ogg;base64,")[1]), \
-          (audioBuffer) =>
-            @timbresAudio[timbre][pitch] = audioBuffer
-        )
+    @loadTimbre()
     @switch = false
     @workMode = ""
     @timbre = ""
     @customSheets = ""

-  activate: =>
+  activate: () =>
     @subscriptions.add(atom.commands.add("atom-workspace", \
     "atom-code-music:toggle": @toggle))
     @subscriptions.add(atom.commands.add("atom-text-editor", \
@@ -59,6 +49,29 @@

   deactivate: () =>
     @subscriptions.dispose()
+
+  loadSheets: () =>
+    for sheetName in sheetsArray
+      @sheets[sheetName] = JSON.parse(fs.readFileSync(
+        path.join(__dirname, "sheets", sheetName)
+      ))
+
+  loadTimbre: () =>
+    # Use two closure wrappers to save the status.
+    # Because decodeAudioData() is an async method.
+    for timbre of timbres then do (timbre) =>
+      @timbresAudio[timbre] = []
+      for pitch of timbres[timbre] then do (pitch) =>
+        @context.decodeAudioData(base64Binary.decodeArrayBuffer(
+          timbres[timbre][pitch].split("data:audio/ogg;base64,")[1]), \
+          (audioBuffer) =>
+            @timbresAudio[timbre][pitch] = audioBuffer
+            ++@readyTimbres
+        )
+    for timbre of timbres
+      for pitch of timbres[timbre]
+        ++@timbresLength
+

   changeMode: () =>
     @workMode = atom.config.get("atom-code-music.workMode")
@@ -89,6 +102,8 @@
             continue

   noteOn: (event) =>
+    if @readyTimbres isnt @timbresLength
+      return
     # console.log(event.code)
     switch (@workMode)
       when "Music Box Mode"
@@ -112,11 +127,11 @@
               @sources.push(source)
           @i++
       when "Real Piano Mode"
-        if event.code of @keysNotes
+        if event.code of keysNotes
           @gainNode.gain.value = 1
           source = @context.createBufferSource()
           source.connect(@gainNode)
-          source.buffer = @timbresAudio[@timbre][@keysNotes[event.code]]
+          source.buffer = @timbresAudio[@timbre][keysNotes[event.code]]
           source.start(0)
           @sources.push(source)

@@ -127,8 +142,9 @@
           source?.stop(@context.currentTime + 0.5)
       when "Real Piano Mode"
         for source in @sources
-          if event.code of @keysNotes
+          if event.code of keysNotes
             source?.stop(@context.currentTime + 0.5)
+    @sources = []

   toggle: () =>
     if not @switch then @enable() else @disable()


Ignore bad text input

Ignore bad text input

Return early when the text is null/empty so the error occurs in the right
location and not in bracket matcher.

Closes #101

--- lib/bracket-matcher.coffee
+++ lib/bracket-matcher.coffee
@@ -46,6 +46,7 @@
     @subscriptions.add @editor.on 'destroyed', => @unsubscribe()

   insertText: (text, options) =>
+    return true unless text
     return true if options?.select or options?.undo is 'skip'
     return false if @wrapSelectionInBrackets(text)
     return true if @editor.hasMultipleCursors()


:art: Simplify LinterRegistry::lint

:art: Simplify LinterRegistry::lint

--- lib/linter-registry.coffee
+++ lib/linter-registry.coffee
@@ -40,19 +40,18 @@

     # Confusing code ahead, proceed with caution :P
     return @linters.reduce((promise, linter) =>
-      if helpers.shouldTriggerLinter(linter, true, onChange, scopes)
-        return promise.then(-> linter.lint(editor)).then((vals) =>
-          if vals then @emitter.emit('did-update-messages', {linter, messages: vals, editor})
-        ).catch (e) -> helpers.error(e)
-      else return promise
+      return promise unless helpers.shouldTriggerLinter(linter, true, onChange, scopes)
+      return promise.then(-> linter.lint(editor)).then((vals) =>
+        if vals then @emitter.emit('did-update-messages', {linter, messages: vals, editor})
+      ).catch (e) -> helpers.error(e)
     , Promise.resolve()).then( =>
       Promises = @linters.map (linter) =>
         return unless helpers.shouldTriggerLinter(linter, false, onChange, scopes)
-        try result = linter.lint(editor) catch e then return helpers.error(e)
-        unless result instanceof Promise
-          result = Promise.resolve(result)
-        result.then (vals) =>
-          if vals then @emitter.emit('did-update-messages', {linter, messages: vals, editor})
+        return new Promise((resolve) =>
+          resolve(linter.lint(@editor))
+        ).then((results) =>
+          if results then @emitter.emit('did-update-messages', {linter, messages: results, editor})
+        ).catch((e) -> helpers.error(e))
       return Promise.all(Promises)
     ).then =>
       @locks[lockKey].delete(editorLinter)


Fix git show command never dispose commands

--- lib/models/git-show.coffee
+++ lib/models/git-show.coffee
@@ -51,7 +51,8 @@
       @destroy()

   destroy: ->
-    @panel.destroy()
+    @disposables?.dispose()
+    @panel?.destroy()

 module.exports = (repo, objectHash, file) ->
   if not objectHash?


refactored request.coffee-s respondTo

refactored request.coffee-s respondTo

--- lib/nodelib/request.coffee
+++ lib/nodelib/request.coffee
@@ -24,21 +24,32 @@
   respondTo: (destination, callback) ->
     @consumer.consume destination, (message, msgHandler) =>
       properties = msgHandler.properties
-      if properties.headers?['message_with_ack']
-        callback(message, msgHandler)
-        response = {error: msgHandler.error()}
-      else if properties.correlationId
-        callback(message, msgHandler)
-        error = msgHandler.error()
-        if error
-          response = {error: error}
-        else
-          response = msgHandler.response
-      else
-        callback(message, msgHandler)
+      response =  @_responder(properties)(message, msgHandler, callback)
+      @producer.produce properties.replyTo, response, {correlationId: properties.correlationId} if response?

-      if response?
-        @producer.produce properties.replyTo, response, {correlationId: properties.correlationId}
+  _responder: (properties) ->
+    if properties.headers?['message_with_ack']
+      responder = @_respondToAck
+    else if properties.correlationId
+      responder = @_respondToRequest
+    else
+      responder = @_respondToSimpleDeliver
+
+  _respondToAck: (message, msgHandler, callback) ->
+    callback(message, msgHandler)
+    {error: msgHandler.error()}
+
+  _respondToRequest: (message, msgHandler, callback) ->
+    callback(message, msgHandler)
+    error = msgHandler.error()
+    if error
+      {error: error}
+    else
+      msgHandler.response
+
+  _respondToSimpleDeliver: (message, msgHandler, callback) ->
+    callback(message, msgHandler)
+    return null #avoid returning anything

   _uuid: ->
     'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) ->


Compress assignment of theme into one-liner

--- lib/package-manager.coffee
+++ lib/package-manager.coffee
@@ -224,7 +224,6 @@
   # pack - The package for which the event is being emitted.
   # error - Any error information to be included in the case of an error.
   emitPackageEvent: (eventName, pack, error) ->
-    {theme} = pack
-    theme ?= pack.metadata?.theme
+    theme = pack.theme ? pack.metadata?.theme
     eventName = if theme then "theme-#{eventName}" else "package-#{eventName}"
     @emit eventName, pack, error


Fixed issue concerning different line ending on Windows when using Synctex.

--- lib/pdf-editor-view.coffee
+++ lib/pdf-editor-view.coffee
@@ -119,6 +119,7 @@
         callback =
           (error, stdout, stderr) =>
             if not error
+              stdout = stdout.replace('\r\n', '\n')
               attrs = {}
               for line in stdout.split('\n')
                 m = line.match /^([a-zA-Z]*):(.*)$/


tasks: working on making draggable/re-orderable

--- salvus/page/tasks.coffee
+++ salvus/page/tasks.coffee
@@ -143,6 +143,7 @@
             t.find(".salvus-task-viewer-not-done").hide()
         if @current_task? and task.task_id == @current_task.task_id
             @set_current_task(task)
+        t.draggable(handle:t.find(".fa-reorder"))

     set_current_task: (task) =>
         if @current_task?


tasks: fix a bug when adding new tasks which disabled cursor movement from the keyboard

--- salvus/page/tasks.coffee
+++ salvus/page/tasks.coffee
@@ -711,6 +711,7 @@
             last_edited : new Date() - 0

         @tasks.unshift(task)
+        @_visible_tasks.unshift(task)
         task_id = uuid()
         @db.update(set:task, where:{task_id : task_id})
         task.task_id = task_id
@@ -897,44 +898,48 @@
     current_task_list = task_list

 $(window).keydown (evt) =>
-    if current_task_list?
-        if help_dialog_open
-            close_help_dialog()
-            return
-
-        if evt.shiftKey
-            return
-
-        if evt.ctrlKey or evt.metaKey
-            if evt.keyCode == 83 # s
-                current_task_list.save()
-                return false
-            else if evt.keyCode == 78 # n
-                current_task_list.create_task()
-                return false
-            else if evt.which == 74 or evt.which == 40  # j = down
-                current_task_list.move_current_task_down()
-                return false
-            else if evt.which == 75 or evt.which == 38  # k = down
-                current_task_list.move_current_task_up()
-                return false
-        else
-
-            if current_task_list.element?.find(".salvus-task-editing-title").length > 0
-                # currently editing some task
-                return
-
-            if evt.which == 13  # return
-                current_task_list.edit_title(current_task_list.current_task)
-                return false
-
-            else if evt.which == 74 or evt.which == 40  # j = down
-                current_task_list.set_current_task_next()
-                return false
-
-            else if evt.which == 75 or evt.which == 38  # k = down
-                current_task_list.set_current_task_prev()
-                return false
+    if not current_task_list?
+        #console.log("no task list")
+        return
+
+    if help_dialog_open
+        #console.log("help dialog open")
+        close_help_dialog()
+        return
+
+    if evt.shiftKey
+        return
+
+    if evt.ctrlKey or evt.metaKey
+        if evt.keyCode == 83 # s
+            current_task_list.save()
+            return false
+        else if evt.keyCode == 78 # n
+            current_task_list.create_task()
+            return false
+        else if evt.which == 74 or evt.which == 40  # j = down
+            current_task_list.move_current_task_down()
+            return false
+        else if evt.which == 75 or evt.which == 38  # k = down
+            current_task_list.move_current_task_up()
+            return false
+    else
+
+        if current_task_list.element?.find(".salvus-task-editing-title").length > 0
+            #console.log("currently editing some task")
+            return
+
+        if evt.which == 13  # return
+            current_task_list.edit_title(current_task_list.current_task)
+            return false
+
+        else if evt.which == 74 or evt.which == 40  # j = down
+            current_task_list.set_current_task_next()
+            return false
+
+        else if evt.which == 75 or evt.which == 38  # k = down
+            current_task_list.set_current_task_prev()
+            return false

 parse_hashtags = (t0) ->


gcloud: better disk control; vm creation

--- salvus/smc_gcloud.coffee
+++ salvus/smc_gcloud.coffee
@@ -88,6 +88,9 @@
     reset: (opts={}) =>
         @_action('reset', opts.cb)

+    delete: (opts={}) =>
+        @_action('delete', opts.cb)
+
     get_metadata: (opts) =>
         opts = defaults opts,
             cb : required
@@ -106,24 +109,35 @@

     attach_disk: (opts) =>
         opts = defaults opts,
+            disk      : required
+            read_only : false
+            cb        : required
+        dbg = @dbg("attach_disk")
+        if not (opts.disk instanceof Disk)
+            dbg("not Disk")
+            if typeof(opts.disk) == 'string'
+                dbg("is string so make disk")
+                opts.disk = @gcloud.disk(name:opts.disk, zone:@zone)
+            else
+                opts.cb("disk must be an instance of Disk")
+                return
+        dbg("starting...")
+        @_vm.attachDisk opts.disk._disk, {readOnly: opts.read_only}, (err, operation, apiResponse) =>
+            handle_operation(err, operation, (->dbg("done")), opts.cb)
+
+    detach_disk: (opts) =>
+        opts = defaults opts,
             disk : required
             cb   : required
-        if not opts.disk instanceof Disk
-            opts.cb("disk must be an instance of Disk")
-            return
-        dbg = @dbg("attach_disk")
-        dbg("starting...")
-        @_vm.attachDisk opts.disk._disk, (err, operation, apiResponse) =>
-            handle_operation(err, operation, (->dbg("done")), opts.cb)
-
-    detach_disk: (opts) =>
-        opts = defaults opts,
-            disk : required
-            cb   : required
-        if not opts.disk instanceof Disk
-            opts.cb("disk must be an instance of Disk")
-            return
         dbg = @dbg("detach_disk")
+        if not (opts.disk instanceof Disk)
+            dbg("not Disk")
+            if typeof(opts.disk) == 'string'
+                dbg("is string so make disk")
+                opts.disk = @gcloud.disk(name:opts.disk, zone:@zone)
+            else
+                opts.cb("disk must be an instance of Disk")
+                return
         dbg("starting...")
         vm_data = disk_data = undefined
         async.series([
@@ -145,7 +159,7 @@
                     if x.source == disk_data.selfLink
                         deviceName = x.deviceName
                         break
-                dbg("determining deviceName='#{deviceName}'")
+                dbg("determined that local deviceName is '#{deviceName}'")
                 if not deviceName
                     dbg("already done -- disk not connected to this machine")
                     cb()
@@ -156,7 +170,20 @@
                 disk = @gcloud._gce.zone(@zone).disk(deviceName)
                 @_vm.detachDisk disk, (err, operation, apiResponse) =>
                     handle_operation(err, operation, (->dbg("done")), cb)
-        ], opts.cb)
+        ], (err) => opts.cb(err))
+
+    get_serial_console: (opts) =>
+        opts = defaults opts,
+            cb   : required
+        @_vm.getSerialPortOutput (err, output) => opts.cb(err, output)
+
+    show_console: =>
+        @get_serial_console
+            cb : (err, output) =>
+                if err
+                    console.log("ERROR -- ", err)
+                else
+                    console.log(output)

 class Disk
     constructor: (@gcloud, @name, @zone=DEFAULT_ZONE) ->
@@ -221,14 +248,16 @@

     attach_to: (opts) =>
         opts = defaults opts,
-            vm : required
-            cb : required
-        if not opts.vm instanceof VM
+            vm        : required
+            read_only : false
+            cb        : required
+        if not (opts.vm instanceof VM)
             opts.cb("vm must be an instance of VM")
             return
         opts.vm.attach_disk
-            disk : @
-            cb   : opts.cb
+            disk      : @
+            read_only : opts.read_only
+            cb        : opts.cb

     detach: (opts) =>
         opts = defaults opts,
@@ -261,7 +290,6 @@

             ], opts.cb)

-
 class Snapshot
     constructor: (@gcloud, @name) ->
         @_snapshot = @gcloud._gce.snapshot(@name)
@@ -312,6 +340,48 @@

         @_gcloud = require('gcloud')(projectId: PROJECT)
         @_gce    = @_gcloud.compute()
+
+    create_vm: (opts) =>
+        opts = defaults opts,
+            name         : required
+            zone         : DEFAULT_ZONE
+            disks        : undefined      # see disks[] at https://cloud.google.com/compute/docs/reference/latest/instances
+                                          # can also pass in Disk objects in the array; or a single string which
+                                          # will refer to the disk with that name in same zone.
+            http         : undefined      # allow http
+            https        : undefined      # allow https
+            machine_type : undefined      # the instance type, e.g., 'n1-standard-1'
+            os           : undefined      # see https://github.com/stephenplusplus/gce-images#accepted-os-names
+            tags         : undefined      # array of strings
+            preemptible  : false
+            cb           : required
+        dbg = @dbg("create_vm(name=#{opts.name})")
+        config = {}
+        config.http        = opts.http if opts.http?
+        config.https       = opts.https if opts.https?
+        config.machineType = opts.machine_type if opts.machine_type?
+        config.os          = opts.os if opts.os?
+        config.tags        = opts.tags if opts.tags?
+        if opts.preemptible
+            config.scheduling = {preemptible : true}
+        if opts.disks?
+            config.disks = []
+            for disk in opts.disks
+                if typeof(disk) == 'string'
+                    disk = @disk(name:disk, zone:opts.zone)  # gets used immediately below!
+
+                if disk instanceof Disk
+                    # use existing disk read/write
+                    config.disks.push({source:disk._disk.formattedName})
+                else
+                    # use object as specified at https://cloud.google.com/compute/docs/reference/latest/instances
+                    config.disks.push(disk)
+            # ensure at least one disk is a boot disk
+            if config.disks.length > 0 and (x for x in config.disks when x.boot).length == 0
+                config.disks[0].boot = true
+        dbg("config=#{misc.to_json(config)}")
+        @_gce.zone(opts.zone).createVM opts.name, config, (err, vm, operation, apiResponse) =>
+            handle_operation(err, operation, (->dbg('done')), opts.cb)

     vm: (opts) =>
         opts = defaults opts,
@@ -350,6 +420,13 @@
                     if x.zone?
                         delete x.zone
                 opts.cb(undefined, vms)
+
+    # Get all outstanding global not-completed operations
+    get_operations: (opts) =>
+        opts = defaults opts,
+            cb  : required
+        @dbg("get_operations")()
+        @_gce.getOperations {filter:"status ne DONE", maxResults:500}, (err, operations) => opts.cb(err, operations)

     _check_db: (cb) =>
         if not @db


convenient URL method

convenient URL method

--- test/integration/tool_rpc.coffee
+++ test/integration/tool_rpc.coffee
@@ -16,6 +16,11 @@
 url = 'http://localhost:3001' # DRY DRY DRY
 login_url = "#{url}/login"
 browser = wd.remote()
+
+# Hacky way to extend browser.
+browser.trueURL = (cb) ->
+  browser.eval "window.location.href", cb
+

 describe 'Tool RPC', ->
   before (done) ->
@@ -56,7 +61,7 @@
                     browser.window handle, done

       it 'redirects the host to the specified URL', (done) ->
-        browser.eval "window.location.href", (err, url) ->
+        browser.trueURL (err, url) ->
           url.should.equal url
           done()


TEST update tests for changes in controller rendering (parseSelf)

--- test/spec/neck/controller.coffee
+++ test/spec/neck/controller.coffee
@@ -110,8 +110,9 @@
       spy = controller._parseNode = sinon.spy()
       controller.render()

-      assert.ok spy.calledOnce
-      assert.equal spy.args[0][0].outerHTML, "
This is text
"
+      assert.ok spy.calledTwice
+      assert.equal spy.args[0][0], controller.el
+      assert.equal spy.args[1][0], controller.$el.find('p')[0]

     it "should parse only children of root node when parseSelf is false", ->
       class Controller extends Neck.Controller


Minor fixes for the api#process

Minor fixes for the api#process

--- library/nucleus/api.coffee
+++ library/nucleus/api.coffee
@@ -93,11 +93,9 @@
     # defined in the subclass of this abstract base class. Default
     # implementation of each method will throw a not implemented.
     process: (request, response, next) ->
-        knowns = @constructor.SUPPORTED
-        parameters = [request, response, next]
-        tokens = super(parameters...)
         method = request?.method?.toUpperCase()?.trim()
-        return @unsupported parameters... unless method in knowns
+        [tokens, knowns] = [super, @constructor.SUPPORTED]
+        return @unsupported arguments... unless method in knowns
         missing = "Missing implementation for #{method} method"
         throw new Error missing unless method of this
         variables = [tokens.resource, tokens.domain]


Cosmetic

Cosmetic

--- app/assets/javascripts/main.coffee
+++ app/assets/javascripts/main.coffee
@@ -33,10 +33,6 @@

   window.setRoutes $routeSegmentProvider, $routeProvider

-# Initialize the application by storing some data and functions into the
-# root scope. Invoked when the app is defined, below.
-
-
 # The app itself.
 pwguardApp = angular.module('PWGuardApp', requiredModules)
 pwguardApp.config ['$routeSegmentProvider',
@@ -111,6 +107,7 @@
   $scope.flashAfterRouteChange = null

   pwgFlash.init() # initialize the flash service
+
   pwgAjax.on401 ->
     $scope.loggedInUser = null
     $scope.redirectToSegment "login"


you can now add headers through default params

--- src/octoblu-channel-request-formatter.coffee
+++ src/octoblu-channel-request-formatter.coffee
@@ -81,7 +81,11 @@
     hiddenBodyParams  = _.filter config.hiddenParams, style: 'body'
     hiddenQueryParams = _.filter config.hiddenParams, style: 'query'

-    urlParams = _.extend {}, config.defaultParams, config.urlParams
+
+    defaultHeaderParams = @_generateHeaderParams config.defaultParams
+    defaultUrlParams = _.filter config.defaultParams, style: 'url'
+
+    urlParams = _.extend {}, defaultUrlParams, config.urlParams
     headerParams = _.cloneDeep config.headerParams
     bodyParams = {}
     uri = @_replaceParams config.url, urlParams
@@ -102,7 +106,7 @@
     _.each hiddenQueryParams, (param) ->
       _.set requestParams.qs, param.name, param.value

-    requestParams.headers = _.extend {}, requestParams.headers, headerParams
+    requestParams.headers = _.extend {}, requestParams.headers, headerParams, defaultHeaderParams

     strategyMethod = @getStrategy config.oauth?.tokenMethod
     strategyParams = strategyMethod config, _.cloneDeep(requestParams)
@@ -153,6 +157,12 @@

     return path

+  _generateHeaderParams: (defaultParams) =>
+     params = _.filter defaultParams, style: 'header'
+     headers = {}
+     _.forEach params, (header) ->
+       headers[header.name] = header.value
+
   onEnvelope: ({config, message}) =>
     message = _.cloneDeep config unless message?.url?
     @buildRequestParameters message


Cookie the dismissal

Cookie the dismissal

--- components/logged_out_cta/index.coffee
+++ components/logged_out_cta/index.coffee
@@ -1,12 +1,20 @@
 { truncate } = require 'underscore.string'
 { CURRENT_USER, USER, CURRENT_ACTION, CHANNEL } = require('sharify').data
 { isPhoneLike } = require '../util/device.coffee'
+Dismisser = require '../has_seen/dismisser.coffee'
 template = -> require('./index.jade') arguments...

 module.exports = ->
   return if isPhoneLike()
   return if CURRENT_USER?
   return if CURRENT_ACTION not in ['channel', 'profile', 'block']
+
+  dismisser = new Dismisser
+    key: 'logged_out_cta'
+    limit: 1
+    expires: 2628000 # 1 month
+
+  return if dismisser.dismissed()

   call = switch CURRENT_ACTION
     when 'channel'
@@ -21,3 +29,4 @@
   $el.one 'click', '.js-close', (e) ->
     e.preventDefault()
     $el.remove()
+    dismisser.dismiss()


临时屏蔽Pager中的pageSize和goto

临时屏蔽Pager中的pageSize和goto

--- src/widget/list/table-support.coffee
+++ src/widget/list/table-support.coffee
@@ -15,7 +15,7 @@
 			readOnlyAfterCreate: true
 		caption: null
 		align:
-			enum: ["left", "center", "right"]
+			enum: [ "left", "center", "right" ]
 		visible:
 			type: "boolean"
 			defaultValue: true
@@ -94,7 +94,7 @@
 		width:
 			defaultValue: "100px"
 		valign:
-			enum: ["top", "center", "bottom"]
+			enum: [ "top", "center", "bottom" ]
 		footerTemplate: null

 	@events:
@@ -120,7 +120,7 @@
 class cola.TableSelectColumn extends cola.TableContentColumn
 	@events:
 		change: null
-		itemChange:null
+		itemChange: null
 	@attributes:
 		width:
 			defaultValue: "42px"
@@ -134,11 +134,11 @@
 				triState: true
 				change: (self, arg) =>
 					if typeof arg.value != "boolean"
-						@fire("change", this, {checkbox: self, oldValue: arg.oldValue, value: arg.value})
+						@fire("change", @, { checkbox: self, oldValue: arg.oldValue, value: arg.value })
 				click: (self) =>
 					checked = self.get("checked")
 					@selectAll(checked)
-					@fire("change", this, {checkbox: self, oldValue: not checked, value: checked})
+					@fire("change", @, { checkbox: self, oldValue: not checked, value: checked })
 					return
 			)
 			checkbox.appendTo(dom)
@@ -149,11 +149,11 @@
 			checkbox = new cola.Checkbox(
 				class: "in-cell"
 				bind: @_table._alias + "." + @_table._selectedProperty
-				change: (self,arg) =>
+				change: (self, arg) =>
 					if !@_ignoreCheckedChange
 						@refreshHeaderCheckbox()
-					arg.item=item;
-					@fire("itemChange",this,arg);
+					arg.item = item
+					@fire("itemChange", @, arg)
 					return
 			)
 			checkbox.appendTo(dom)


remove anti-spam

remove anti-spam

--- site/js/script.coffee
+++ site/js/script.coffee
@@ -56,7 +56,6 @@
   section: "new"
   sectiontitle: type + ga_to_hash(getGoogleAnalyticsId())
   text: text
-  ct_checkjs: "cb2b2fb6bee3251f032a965d4a36e99b"
   summary: "Input from website"
   bot: ""
   token: api_key


change btn-group to vertical

change btn-group to vertical

--- views/titlesList.cjsx
+++ views/titlesList.cjsx
@@ -70,7 +70,7 @@
                onChange={@handleKeyWordChange} />
       </div>
       <div>
-        <ButtonGroup bsSize='xsmall' className='titles-container'>
+        <ButtonGroup vertical bsSize='xsmall' className='titles-container'>
         {
           if @state.showData?
             if @state.showData.titles? and @state.showData.titles isnt []


Minor typo

Minor typo

--- app/components/project-list.coffee
+++ app/components/project-list.coffee
@@ -62,9 +62,9 @@
         keyObjectFull.key = keyObject.key
         keyObjectFull.text = keyObject.text
         if reverse
-          keyObjectFull.text += " in descending order"
+          keyObjectFull.text += " in Descending Order"
         else
-          keyObjectFull.text += " in ascending order"
+          keyObjectFull.text += " in Ascending Order"
         keyObjectsWithReverse.push keyObjectFull
     keyObjectsWithReverse
   ).property()


removing test_utils from demo/

removing test_utils from demo/

--- static/coffee/demo/test_utils.coffee
+++ static/coffee/demo/test_utils.coffee
@@ -1,99 +0,0 @@
-base = require("../../base")
-Collections = base.Collections
-
-class Rand
-  # if created without a seed, uses current time as seed
-  constructor: (@seed) ->
-    # Knuth and Lewis' improvements to Park and Miller's LCPRNG
-    @multiplier = 1664525
-    @modulo = 4294967296 # 2**32-1;
-    @offset = 1013904223
-    unless @seed? && 0 <= seed < @modulo
-      @seed = (new Date().valueOf() * new Date().getMilliseconds()) % @modulo
-
-  # sets new seed value
-  seed: (seed) ->
-    @seed = seed
-
-  # return a random integer 0 <= n < @modulo
-  randn: ->
-    # new_seed = (a * seed + c) % m
-    @seed = (@multiplier*@seed + @offset) % @modulo
-
- # return a random float 0 <= f < 1.0
-  randf: ->
-    this.randn() / @modulo
-
-  # return a random int 0 <= f < n
-  rand: (n) ->
-    Math.floor(this.randf() * n)
-
-  # return a random int min <= f < max
-  rand2: (min, max) ->
-    min + this.rand(max-min)
-
-
-
-make_glyph_test = (test_name, data_source, defaults, glyphspecs, xrange, yrange, tools=false, dims=[200, 200], axes=true) ->
-  return () ->
-    expect(0)
-    plot_tools = []
-    if tools
-      pantool = Collections('PanTool').create(
-        dataranges: [xrange.ref(), yrange.ref()]
-        dimensions: ['width', 'height']
-      )
-      zoomtool = Collections('ZoomTool').create(
-        dataranges: [xrange.ref(), yrange.ref()]
-        dimensions: ['width', 'height']
-      )
-      plot_tools = [pantool, zoomtool]
-    glyphs = []
-    for glyphspec in glyphspecs
-      glyph = Collections('GlyphRenderer').create({
-        data_source: data_source.ref()
-        xdata_range: xrange.ref()
-        ydata_range: yrange.ref()
-        glyphspec: glyphspec
-      })
-      glyph.set(defaults)
-      glyphs.push(glyph)
-    plot_model = Collections('Plot').create()
-    if axes
-      xaxis = Collections('LinearAxis').create(
-        orientation: 'bottom'
-        parent: plot_model.ref()
-        data_range: xrange.ref()
-      )
-      yaxis = Collections('LinearAxis').create(
-        orientation: 'left',
-        parent: plot_model.ref()
-        data_range: yrange.ref()
-      )
-      plot_model.set(
-        renderers: (g.ref() for g in glyphs)
-        axes: [xaxis.ref(), yaxis.ref()]
-        tools: plot_tools
-        width: dims[0]
-        height: dims[1]
-      )
-    else
-      plot_model.set(
-        renderers: (g.ref() for g in glyphs)
-        axes: []
-        tools: plot_tools
-        width: dims[0]
-        height: dims[1]
-      )
-    div = $('

')
-    $('body').append(div)
-    myrender  =  ->
-      view = new plot_model.default_view(model: plot_model)
-      div.append(view.$el)
-      view.render()
-      console.log('Test ' + test_name)
-    _.defer(myrender)
-
-
-exports.make_glyph_test = make_glyph_test
-exports.Rand = Rand


Stop sending stub calculationTime

Stop sending stub calculationTime

--- app/lib/simulator/Simulator.coffee
+++ app/lib/simulator/Simulator.coffee
@@ -365,7 +365,6 @@
       receiptHandle: @task.getReceiptHandle()
       originalSessionID: @task.getFirstSessionID()
       originalSessionRank: -1
-      calculationTime: 500
       sessions: []
       simulator: @simulator
       randomSeed: @task.world.randomSeed


server side decide which match returns (#6478)

Co-authored-by: smallst_wsl <6c42689ff77371401bf43b7d56b93ac1ca3e92d3@163.com>

--- app/lib/simulator/Simulator.coffee
+++ app/lib/simulator/Simulator.coffee
@@ -19,7 +19,6 @@
   constructor: (@options) ->
     @options ?= {}
     simulatorType = if @options.headlessClient then 'headless' else 'browser'
-    @simulateTournamentRatio = 0.5
     @simulator =
       type: simulatorType
       version: SIMULATOR_VERSION
@@ -41,11 +40,7 @@
     return if @destroyed
     url = '/queue/scoring/getTwoGames'
     if @options.singleLadder
-      url = "/db/level/#{@options.levelOriginal}/next-ladder-match"
-
-    simulateTournament = Math.random()
-    if simulateTournament < @simulateTournamentRatio
-      url = "/db/tournament/-/next-match"
+      url = "/db/level/#{@options.levelOriginal}/next-match"

     $.ajax
       url: url
@@ -72,16 +67,10 @@
       success: (taskData, textStatus, xhr) =>
         return if @destroyed
         unless taskData
-          if xhr.status is 205
-            @retryDelayInSeconds = 5
-            @simulateTournamentRatio = 0.1  # scale down the ratio of simulate tournament
-          else
-            @retryDelayInSeconds = 10
+          @retryDelayInSeconds = 5
           @trigger 'statusUpdate', "Waiting #{@retryDelayInSeconds} seconds before next game."
           @simulateAnotherTaskAfterDelay()
           return
-        if taskData.tournamentId
-          @simulateTournamentRatio = 0.9  # scale up the ratio of simulate tournament
         @simulatingPlayerStrings = {}
         if taskData.sessions[0].realTeam == taskData.sessions[1].realTeam # only random for ladder
           newTeam = _.shuffle ['humans', 'ogres']


Refactors List.MovieView

Refactors List.MovieView

--- app/assets/javascripts/backbone/apps/movies/list/list_view.js.coffee
+++ app/assets/javascripts/backbone/apps/movies/list/list_view.js.coffee
@@ -11,41 +11,42 @@

   class List.MovieView  extends Marionette.ItemView
     initialize: ->
-      if this.model.get('watched') is true
-        this.$el.addClass('watched')
-      if this.model.get('queued') is true
-        this.$el.addClass('queued')
+      attributes = ['watched', 'queued', 'trashed']
+      for attr in attributes
+        if this.model.get(attr) is true
+          this.$el.addClass('no-display')
     template: 'movies/list/templates/_movie'
     tagName: 'tr'
     events:
       'click .watched-btn' : 'toggleWatched'
-      'click .queued-btn' : 'toggleQueued'
-      'mouseenter' : 'showOverlay'
-      'mouseleave' : 'hideOverlay'
-
-    # getTemplate: ->
-    #   if this.model.get('watched') is true
-    #     return "movies/list/templates/_movie"
-    #   else
-    #     return "movies/list/templates/_watched"
-
+      'click .queued-btn'  : 'toggleQueued'
+      'click .trashed-btn'  : 'toggleTrashed'
+      'click .btn-group'   : 'hideMovie'
+      'mouseenter'         : 'showOverlay'
+      'mouseleave'         : 'hideOverlay'

     toggleWatched: ->
-      this.$el.toggleClass('watched')
       if this.model.get('watched') is true
         this.model.set(watched: false).save()
       else
         this.model.set(watched: true).save()

     toggleQueued: ->
-      this.$el.toggleClass('queued')
-      console.log this.model.get('queued')
       if this.model.get('queued') is true
         this.model.set(queued: false).save()
       else
         this.model.set(queued: true).save()
       console.log this.model.get('queued')

+    toggleTrashed: ->
+      if this.model.get('trashed') is true
+        this.model.set(trashed: false).save()
+      else
+        this.model.set(trashed: true).save()
+      console.log this.model.get('trashed')
+
+    hideMovie: ->
+      this.$el.fadeOut(500)

     showOverlay: ->
       this.$el.find('.overlay').fadeIn(500)


Fix necessary key for looped items

Fix necessary key for looped items

--- app/assets/javascripts/components/search/material_link.js.jsx.coffee
+++ app/assets/javascripts/components/search/material_link.js.jsx.coffee
@@ -2,7 +2,7 @@
   render: ->
     link = @props.link
     return `(
-
{link.text}

+
{link.text}

     )`

 MaterialLinks = React.createClass


Remove crap. Literally

Remove crap. Literally

--- example/public/javascripts/dom.coffee
+++ example/public/javascripts/dom.coffee
@@ -14,4 +14,4 @@
   console.log message

 app.bind "message.new.client", (message) ->
-  console.log message + 'crap'
+  console.log message


Fixed ticket attribute selection.

Fixed ticket attribute selection.

--- app/assets/javascripts/app/controllers/_application_controller_form.js.coffee
+++ app/assets/javascripts/app/controllers/_application_controller_form.js.coffee
@@ -987,7 +987,7 @@
             remove:     true
           }
         itemSub = @formGenItem( attribute_config )
-        itemSub.find('.icon-minus').bind('click', (e) ->
+        itemSub.find('.glyphicon-minus').bind('click', (e) ->
           e.preventDefault()
           $(@).parent().parent().parent().remove()
         )
@@ -1016,18 +1016,18 @@
             selected: false
             disable:  true
           },
-#          {
-#            value:    'tickets.number'
-#            name:     'Number'
-#            selected: true
-#            disable:  false
-#          },
-#          {
-#            value:    'tickets.title'
-#            name:     'Title'
-#            selected: true
-#            disable:  false
-#          },
+          {
+            value:    'tickets.number'
+            name:     'Number'
+            selected: true
+            disable:  false
+          },
+          {
+            value:    'tickets.title'
+            name:     'Title'
+            selected: true
+            disable:  false
+          },
           {
             value:    'tickets.group_id'
             name:     'Group'
@@ -1217,7 +1217,7 @@
       }
       list = @formGenItem( attribute_config )

-      list.find('.icon-plus').bind('click', (e) ->
+      list.find('.glyphicon-plus').bind('click', (e) ->
         e.preventDefault()

         value = $(e.target).parents().find('[name=ticket_attribute_list]').val()


tests...

tests...

--- examples/ble_flash/flash_tests.coffee
+++ examples/ble_flash/flash_tests.coffee
@@ -210,6 +210,10 @@
                     expect( error_callback.called ).to.be.true

     describe 'receiving progress', ->
+        describe 'fails when', ->
+
+            it 'checksum error is detected'
+            it 'unexpected consecutive number is detected'

     describe 'continously reveivin progress', ->


raise thresh a bit

raise thresh a bit

--- src/smc-hub/postgres-user-query-queue.coffee
+++ src/smc-hub/postgres-user-query-queue.coffee
@@ -28,7 +28,7 @@
 # many global outstanding concurent **user queries**.  The point is that
 # if there's very little load, we should get queries done as fast
 # as possible for users.
-GLOBAL_LIMIT = 75
+GLOBAL_LIMIT = 100
 #GLOBAL_LIMIT = 4

 # setup metrics


Allow to create search choice on fly with select2_bridge

--- app/assets/javascripts/ab_admin/components/select2_bridge.js.coffee
+++ app/assets/javascripts/ab_admin/components/select2_bridge.js.coffee
@@ -25,6 +25,10 @@
     if @el.data('tags')
       @options.tokenSeparators = [',']
       @options.tags = @el.data('tags')
+    if @el.data('data')
+      data = @el.data('data')
+      data = _.map(data, (el) -> {id: el, text: el.toString()} ) if data[0] && !_.isObject(data[0])
+      @options.data = data
     else if @el.data('collection')
       @options.data = @el.data('collection')
     else if @el.data('class')
@@ -32,6 +36,10 @@
     if @el.data('add')
       @initCreateChoiceOnce()
       @initCreateChoice()
+    if @el.data('create-search-choice')
+      @options.createSearchChoice = (term) ->
+        log 'createSearchChoice'
+        {id: term, text: term}
     @options

   runModal: ->
@@ -91,7 +99,7 @@
       placeholder: ' '
       allowClear: true
       escapeMarkup: (m) -> m
-    opts.minimumResultsForSearch = 10 unless fv.test || @el.data('add')
+    opts.minimumResultsForSearch = 10 unless fv.test || @el.data('add') || @el.data('create-search-choice')
     opts

   initAjaxInput: ->


Fix control+o

Fix control+o

--- src/trix/controllers/input_controller.coffee
+++ src/trix/controllers/input_controller.coffee
@@ -218,6 +218,7 @@
         event.preventDefault()
         @delegate?.inputControllerWillPerformTyping()
         @responder?.insertString("\n", updatePosition: false)
+        @requestRender()

     alt:
       backspace: (event) ->


Avoid unnecessary observer since model ID doesn't change

--- app/assets/javascripts/rglossa/controllers/search_controller.coffee
+++ app/assets/javascripts/rglossa/controllers/search_controller.coffee
@@ -4,8 +4,8 @@
   model: null
   modelBinding: 'controllers.searches.currentSearch'

-  corpusId: null
-  corpusIdBinding: 'controllers.corpus.model.id'
+  corpus: null
+  corpusBinding: 'controllers.corpus.model'

   modelIdDidChange: (->
     # When the search has been created and we have got an ID from the server,
@@ -18,7 +18,7 @@
     # until that is fixed, we observe the model ID.
     if @get('model.id')
       @get('target').send 'showResults',
-        corpusId: @get('corpusId')
+        corpusId: @get('corpus.id')
         searchId: @get('model.id')
         pageNo: 1
   ).observes('model.id')


Delegate setting value to input element

Delegate setting value to input element

--- src/trix/elements/trix_editor_element.coffee
+++ src/trix/elements/trix_editor_element.coffee
@@ -71,18 +71,15 @@
   value:
     get: ->
       @inputElement.value
+    set: (value) ->
+      @inputElement.value = value

   # Selection methods

   getClientRectAtPosition: (position) ->
     @editorController?.getClientRectAtPosition(position)

-
   # Controller delegate methods
-
-  save: ->
-    value = Trix.serializeToContentType(this, "text/html")
-    @inputElement.value = value

   notify: (message, data) ->
     switch message
@@ -93,7 +90,7 @@
           @documentChangedSinceLastRender = false
           @notify("change")
       when "change", "attachment-add", "attachment-edit", "attachment-remove"
-        @save()
+        @value = Trix.serializeToContentType(this, "text/html")

     triggerEvent("trix-#{message}", onElement: this, attributes: data)


Fix vertical size of graph widgets

Fix vertical size of graph widgets

--- templates/project/widgets/graph/graph.coffee
+++ templates/project/widgets/graph/graph.coffee
@@ -10,7 +10,7 @@
     container = $(@node).parent()
     # Gross hacks. Let's fix this.
     width = (Dashing.widget_base_dimensions[0] * container.data("sizex")) + Dashing.widget_margins[0] * 2 * (container.data("sizex") - 1)
-    height = (Dashing.widget_base_dimensions[1] * container.data("sizey"))
+    height = (Dashing.widget_base_dimensions[1] * container.data("sizey")) + Dashing.widget_margins[1] * 2 * (container.data("sizey") - 1)
     @graph = new Rickshaw.Graph(
       element: @node
       width: width


set a default wait time

set a default wait time

--- test-integration/helpers/test-element.coffee
+++ test-integration/helpers/test-element.coffee
@@ -34,21 +34,21 @@

 class TestHelper extends TestItemHelper
   constructor: (test, testElementLocator, commonElements = {}, options) ->
-    super(test, testElementLocator, 'parent')
-
     defaultOptions =
       loadingLocator:
         css: '.is-loading'
+      defaultWaitTime: 1000

-    @_options = _.assign {}, defaultOptions, options
+    @_options = _.assign {}, defaultOptions, options

+    super(test, testElementLocator, 'parent')
     _.each commonElements, @setCommonElement
     @

-  waitUntilLoaded: (ms) =>
+  waitUntilLoaded: (waitTime = @options.defaultWaitTime) =>
     @test.driver.wait =>
-      @test.driver.isElementPresent(@_options.loadingLocator).then (isPresent) -> not isPresent
-    , ms
+      @test.driver.isElementPresent(@options.loadingLocator).then (isPresent) -> not isPresent
+    , waitTime

   setCommonElement: (commonElementInfo, name) =>
     {locator, isSingle} = commonElementInfo
@@ -58,4 +58,10 @@
     @["get#{S.capitalize(name, false)}"] = @[name].get.bind(@[name])

+# Using defined properties for access eliminates the possibility
+# of accidental assignment
+Object.defineProperties TestHelper.prototype,
+  options:
+    get: -> @_options
+
 module.exports = {TestHelper}


Change test for equal output to fail

Change test for equal output to fail

--- spec/mixed-indent-warning-spec.coffee
+++ spec/mixed-indent-warning-spec.coffee
@@ -37,9 +37,9 @@
       input = "\tfoobar1\n\tfoobar2\n  foobar3\n\tfoobar4"
       expect( IndentChecker.getMostCommonIndentType(input) ).toBe( 'tabs' )

-    it "returns 'spaces' when the input contains equal tab and space indentation", ->
+    it "returns '' when the input contains equal tab and space indentation", ->
       input = "\tfoobar1\n  foobar2\n\tfoobar3\n  foobar4"
-      expect( IndentChecker.getMostCommonIndentType(input) ).toBe( 'spaces' )
+      expect( IndentChecker.getMostCommonIndentType(input) ).toBe( '' )

   describe ".getLinesWithLessCommonType()", ->
     it "returns an array of line numbers with the less-common indentation type from the input", ->


Add comments describing the focusInput behavior.

--- content_scripts/vimium_frontend.coffee
+++ content_scripts/vimium_frontend.coffee
@@ -301,6 +301,9 @@
     HUD.showForDuration("Yanked URL", 1000)

   focusInput: (count) ->
+    # Focus the first input element on the page, and create overlays to highlight all the input elements, with
+    # the currently-focused element highlighted specially. Tabbing will shift focus to the next input element.
+    # Pressing any other key will remove the overlays and the special tab behavior.
     resultSet = DomUtils.evaluateXPath(textInputXPath, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE)
     visibleInputs =
       for i in [0...resultSet.snapshotLength] by 1


Widgets now have a formatted 'updatedAtMessage'. For stuff like 'Last updated at 18:00'

--- javascripts/dashing.coffee
+++ javascripts/dashing.coffee
@@ -41,6 +41,10 @@
     type = Batman.Filters.dashize(@view)
     $(@node).addClass("widget widget-#{type} #{@id}")

+  @accessor 'updatedAtMessage', ->
+    if updatedAt = @get('updatedAt')
+      timestamp = updatedAt.toString().match(/\d*:\d*/)[0]
+      "Last updated at #{timestamp}"

   @::on 'ready', ->
     Dashing.Widget.fire 'ready'


Add comments in test-test.

Add comments in test-test.

--- test-test/main.coffee
+++ test-test/main.coffee
@@ -2,81 +2,93 @@

 This is to test the "test". The output should look something like this:

-2014-05-28T05:29:21.394Z OK: 8, Exception: 0, Pending: 1
-2014-05-28T05:29:22.392Z OK: 8, Exception: 0, Pending: 1
-2014-05-28T05:29:23.392Z OK: 8, Exception: 0, Pending: 1
-2014-05-28T05:29:24.394Z OK: 9, Exception: 0, Pending: 0
+2014-05-30T20:54:28.380Z OK: 9, Exception: 0, Pending: 1
+2014-05-30T20:54:29.375Z OK: 9, Exception: 0, Pending: 1
+2014-05-30T20:54:30.375Z OK: 9, Exception: 0, Pending: 1
+2014-05-30T20:54:31.376Z OK: 10, Exception: 0, Pending: 0

 ********** Failed Unit **********
     Test: root -->
     Unit: 1+2+3=7
-Expected: 7
+Expected: = 7
   Actual: 6

 ********** Failed Unit **********
     Test: root -->
     Unit: (obj.unit>1)=true
-Expected: true
+Expected: = true
   Actual: false

 ********** Failed Unit **********
     Test: root --> nested test --> test 2 in nested test
     Unit: simple boolean test
-Expected: true
+Expected: = true
   Actual: false

 ********** Failed Unit **********
     Test: root -->
     Unit: ("1"===2)=true
-Expected: true
+Expected: = true
   Actual: false

 ********** Failed Unit **********
     Test: root -->
     Unit: {} is {}
-Expected: {}
+Expected: is {}
   Actual: {}

 ********** Failed Unit **********
     Test: root -->
     Unit: [] is []
-Expected: []
+Expected: is []
   Actual: []

 ********** Failed Unit **********
     Test: root -->
+    Unit: null<>null
+Expected: ≠ null
+  Actual: null
+
+********** Failed Unit **********
+    Test: root -->
+    Unit: undefined<>undefined
+Expected: ≠ undefined
+  Actual: undefined
+
+********** Failed Unit **********
+    Test: root -->
     Unit: sampleNaN1=1
-Expected: 1
-  Actual: null
+Expected: = 1
+  Actual: NaN

 ********** Failed Unit **********
     Test: root -->
     Unit: NaN= 3
-Expected: 3
-  Actual: null
+Expected: = 3
+  Actual: NaN

 ********** Failed Unit **********
     Test: root -->
     Unit: '' =NaN
-Expected: null
+Expected: = NaN
   Actual: ""

 ********** Failed Unit **********
     Test: root -->
     Unit: 0=-0
-Expected: 0
+Expected: = 0
   Actual: 0

 ********** Failed Unit **********
     Test: root -->
     Unit: 0 is -0
-Expected: 0
+Expected: is 0
   Actual: 0

 ********** Failed Unit **********
     Test: root -->
     Unit: -0 is 0
-Expected: 0
+Expected: is 0
   Actual: 0

 ********** Failed Unit **********
@@ -103,7 +115,97 @@
 Expected: no exception
   Actual: exception

-Completed. 0 exceptional tests. 16 failed units.
+********** Failed Unit **********
+    Test: root -->
+    Unit: /=/.test("=")=false
+Expected: = false
+  Actual: true
+
+********** Failed Unit **********
+    Test: root -->
+    Unit: "\""="\"abc"
+Expected: = "\"abc"
+  Actual: "\""
+
+********** Failed Unit **********
+    Test: root -->
+    Unit: 123<>123
+Expected: ≠ 123
+  Actual: 123
+
+********** Failed Unit **********
+    Test: root -->
+    Unit: {a:1,b:2}<>{a:1,b:2}
+Expected: ≠ {a:1,b:2}
+  Actual: {a:1,b:2}
+
+********** Failed Unit **********
+    Test: root -->
+    Unit: NaN isnt NaN
+Expected: isn't NaN
+  Actual: NaN
+
+********** Failed Unit **********
+    Test: root -->
+    Unit: {a:1,b:2}={a:1,b:2,c:function(){}}
+Expected: = {a:1,b:2,c:[Function]}
+  Actual: {a:1,b:2}
+
+********** Failed Unit **********
+    Test: root -->
+    Unit: {a:{a:{a:{a:{}}}}}={a:{a:{a:{a:1}}}}
+Expected: = {a:{a:{a:[Object]}}}
+  Actual: {a:{a:{a:[Object]}}}
+
+********** Failed Unit **********
+    Test: root -->
+    Unit: circularObj isnt circularObj
+Expected: isn't {a:{a:{a:[Object],b:[Array]},b:[undefined,undefined]},b:[undefined,undefined]}
+  Actual: {a:{a:{a:[Object],b:[Array]},b:[undefined,undefined]},b:[undefined,undefined]}
+
+********** Failed Unit **********
+    Test: root -->
+    Unit: circularObj <> circularObj
+Expected: ≠ {a:{a:{a:[Object],b:[Array]},b:[undefined,undefined]},b:[undefined,undefined]}
+  Actual: {a:{a:{a:[Object],b:[Array]},b:[undefined,undefined]},b:[undefined,undefined]}
+
+********** Failed Unit **********
+    Test: root -->
+    Unit: circularObj is circularObj2
+Expected: is {a:{a:{a:[Object],b:[Array]},b:[undefined,undefined]},b:[undefined,undefined]}
+  Actual: {a:{a:{a:[Object],b:[Array]},b:[undefined,undefined]},b:[undefined,undefined]}
+
+********** Failed Unit **********
+    Test: root -->
+    Unit: circularObj = circularObj2
+Expected: = {a:{a:{a:[Object],b:[Array]},b:[undefined,undefined]},b:[undefined,undefined]}
+  Actual: {a:{a:{a:[Object],b:[Array]},b:[undefined,undefined]},b:[undefined,undefined]}
+
+********** Failed Unit **********
+    Test: root -->
+    Unit: circularWrapper is circularWrapper2
+Expected: is {content:{a:{a:[Object],b:[Array]},b:[undefined,undefined]},b:[undefined,undefined]}
+  Actual: {content:{a:{a:[Object],b:[Array]},b:[undefined,undefined]},b:[undefined,undefined]}
+
+********** Failed Unit **********
+    Test: root -->
+    Unit: circularWrapper <> circularWrapper2
+Expected: ≠ {content:{a:{a:[Object],b:[Array]},b:[undefined,undefined]},b:[undefined,undefined]}
+  Actual: {content:{a:{a:[Object],b:[Array]},b:[undefined,undefined]},b:[undefined,undefined]}
+
+********** Failed Unit **********
+    Test: root -->
+    Unit: veryLongCircularObj1=veryLongCircularObj2
+Expected: = {me:[Object],secondMe:[Object],thirdMe:[Object],meArray:[Array],a:"............................................................",b:"............................................................",c:"............................................................",d:"............................................................",e:"............................................................",f:"............................................................",g:"............................................................",h:"............................................................",i:"............................................................",j:"............................................................",k:"............................................................",l:"............................................................",m:"............................................................",n:"............................................................"}
+  Actual: {me:[Object],secondMe:[Object],thirdMe:[Object],meArray:[Array],a:"............................................................",b:"............................................................",c:"............................................................",d:"............................................................",e:"............................................................",f:"............................................................",g:"............................................................",h:"............................................................",i:"............................................................",j:"............................................................",k:"............................................................",l:"............................................................",m:"............................................................",n:"............................................................"}
+
+********** Failed Unit **********
+    Test: root -->
+    Unit: veryLongCircularObj1 is veryLongCircularObj2
+Expected: is {me:[Object],secondMe:[Object],thirdMe:[Object],meArray:[Array],a:"............................................................",b:"............................................................",c:"............................................................",d:"............................................................",e:"............................................................",f:"............................................................",g:"............................................................",h:"............................................................",i:"............................................................",j:"............................................................",k:"............................................................",l:"............................................................",m:"............................................................",n:"............................................................"}
+  Actual: {me:[Object],secondMe:[Object],thirdMe:[Object],meArray:[Array],a:"............................................................",b:"............................................................",c:"............................................................",d:"............................................................",e:"............................................................",f:"............................................................",g:"............................................................",h:"............................................................",i:"............................................................",j:"............................................................",k:"............................................................",l:"............................................................",m:"............................................................",n:"............................................................"}
+
+Completed. Exceptional Tests: 0, Failed Units: 33, Mark: 8a4f7

 ###


clampable

clampable

--- test/clampable.coffee
+++ test/clampable.coffee
@@ -2,6 +2,8 @@

 test "testing for equality", ->
   o = GameObject()
+
+  max = 100

   o.clamp
     x:


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/expirable.coffee
+++ test/expirable.coffee
@@ -27,6 +27,6 @@
   5.times ->
     obj.update()

-  equals obj.I.alpha, 0.8 * (obj.I.age / obj.I.duration) * 0.8
+  equals obj.I.alpha, 0.8 - ((obj.I.age / obj.I.duration) * 0.8)

 module()


remove unecessary style overides for list-view

--- addon/lib/eui-poplist/list-view.coffee
+++ addon/lib/eui-poplist/list-view.coffee
@@ -5,12 +5,6 @@
   attributeBindings: ['role', 'tabindex']
   role: 'menu'
   tabindex: '-1'
-
-  css:
-    position: 'relative'
-    '-webkit-overflow-scrolling': 'touch'
-    'overflow-scrolling': 'touch'
-    overflow: 'auto'

   classNames: ['eui-options']
   height: Ember.computed.alias 'controller.listHeight'


Remove unnecessary debug code

Remove unnecessary debug code


Remove exid tag when deleting it's book

Remove exid tag when deleting it's book

--- src/components/tags/books.cjsx
+++ src/components/tags/books.cjsx
@@ -23,8 +23,12 @@
     )
   onDelete: ->
     ExerciseActions.setPrefixedTag(@props.exerciseId,
-      prefix: 'book', tag: '', previous: @props.tag
+      prefix: 'book', tag: false, previous: @props.tag
     )
+    ExerciseActions.setPrefixedTag(@props.exerciseId,
+      prefix: "exid:#{@props.tag}", tag: false, replaceOthers: true
+    )
+
   render: ->
     <div className="tag">
       <select


fix some of the multipart problems where milestones dont open and the arrows dont work

--- src/components/task/index.cjsx
+++ src/components/task/index.cjsx
@@ -137,7 +137,6 @@
       unless @state.currentStep is nextState.currentStep
         nextStep = TaskStore.getStepByIndex(id, nextState.currentStep)
         TaskStepActions.load(nextStep.id)
-      return false

     # if we reach this point, assume that we should go ahead and do a normal component update
     true


Remove an unused attribute

Remove an unused attribute

--- lib/transient-emacs.coffee
+++ lib/transient-emacs.coffee
@@ -16,7 +16,6 @@
   event_listeners: []
   add_text_editor_listener: null
   is_user_command: true
-  auto_move: 0
   isearch_word: ""
   isearch_last: ""
   isearch_tile: null


set proper path when adding new task group

--- SingularityUI/app/reducers/log.coffee
+++ SingularityUI/app/reducers/log.coffee
@@ -29,6 +29,7 @@
     newState = Object.assign({}, state)
     for taskId in action.taskIds
       newState[taskId] = {
+        path: action.path.replace('$TASK_ID', taskId)
         minOffset: 0
         maxOffset: 0
         filesize: 0


Correctly teardown after test

Correctly teardown after test

--- test/spec/test.coffee
+++ test/spec/test.coffee
@@ -159,7 +159,7 @@
         @$el.on 'touchclick', @spy

       afterEach ->
-        window.navigator.msPointerEnabled = true
+        window.navigator.msPointerEnabled = false
         @spy.reset()
         @$el.off 'touchclick', @spy

@@ -254,7 +254,7 @@
         @$el.off 'touchclick', @spy

       afterEach ->
-        window.navigator.msPointerEnabled = true
+        window.navigator.msPointerEnabled = false
         @spy.reset()

       describe 'on MSPointerDown', ->


Fix typo

Fix typo

--- client/app/MainApp/filetree/controllers/findertreecontroller.coffee
+++ client/app/MainApp/filetree/controllers/findertreecontroller.coffee
@@ -16,7 +16,7 @@
       @getView().setClass "no-context-menu"

     @getSingleton('mainController').on "NewFileIsCreated", (newFile)=> @navigateToNewFile newFile
-    @getSingleton('mainController').on "SelectedFileChanged", (file)=> @higlightFile file
+    @getSingleton('mainController').on "SelectedFileChanged", (file)=> @highlightFile file

   addNode:(nodeData, index)->

@@ -47,7 +47,7 @@
           @selectNode @nodes[newFile.path]
         @listenedPaths[file.path] = file.path

-  higlightFile:(file)->
+  highlightFile:(file)->
     @selectNode @nodes[file.path]

   navigateToNewFile:(newFile)=>


Multiple values in addClass, removeClass, toggleClass

--- source/quo.css.coffee
+++ source/quo.css.coffee
@@ -18,8 +18,8 @@
   @method addClass
   @param  {string} Name of stylesheet class
   ###
-  $$.fn.addClass = (name) ->
-    @each -> @classList.add name
+  $$.fn.addClass = (values) ->
+    @each -> @classList.add value for value in values.split " "

   ###
@@ -27,8 +27,8 @@
   @method addClass
   @param  {string} Name of stylesheet class
   ###
-  $$.fn.removeClass = (name) ->
-    @each -> @classList.remove name
+  $$.fn.removeClass = (values) ->
+    @each -> @classList.remove value for value in values.split " "

   ###
@@ -36,10 +36,8 @@
   @method addClass
   @param  {string} Name of stylesheet class
   ###
-  $$.fn.toggleClass = (name) ->
-    @each ->
-      method = if @.classList.contains(name) then "remove" else "add"
-      @classList[method] name
+  $$.fn.toggleClass = (values) ->
+    @each -> @classList.toggle value for value in values.split " "

   ###


Social: add new bongo function for fetching profile feed

--- workers/social/lib/social/models/socialapi/channel.coffee
+++ workers/social/lib/social/models/socialapi/channel.coffee
@@ -33,6 +33,9 @@
         fetchFollowedChannels:
           (signature Object, Function)
         searchTopics:
+          (signature Object, Function)
+
+        fetchProfileFeed:
           (signature Object, Function)

     schema             :
@@ -101,6 +104,9 @@
   @searchTopics = secure (client, options = {}, callback)->
     @doRequest 'searchTopics', client, options, callback

+  @fetchProfileFeed = secure (client, options = {}, callback)->
+    @doRequest 'fetchProfileFeed', client, options, callback
+
   @fetchPopularTopics = secure (client, options = {}, callback)->
     @doRequest 'fetchPopularTopics', client, options, callback


prevent forwarding of content-disposition to couch : ref #238

--- server/lib/binary.coffee
+++ server/lib/binary.coffee
@@ -29,6 +29,8 @@
                 db.merge doc._id, binary: binList, (err) ->
                     log.error err if err?
                     cb()
+
+        delete readStream.headers['content-disposition']
         readStream.pipe stream

     # Update binary list set on given doc then save file to CouchDB


tests: account fix wrong selector

tests: account fix wrong selector

--- tests/src/helpers/helpers.coffee
+++ tests/src/helpers/helpers.coffee
@@ -442,7 +442,7 @@
     newName             = paragraph.split(' ')[0]
     avatarSelector      = '.avatar-area a.profile'
     accountPageSelector = '.AppModal--account'
-    saveButtonSelector  = accountPageSelector + ' .button-field .profile-save-changes'
+    saveButtonSelector  = accountPageSelector + ' .button-field'

     browser
       .waitForElementVisible   '.avatar-area [testpath=AvatarAreaIconLink]', 20000


activity: fix minor codestyling problem

activity: fix minor codestyling problem

--- client/activity/lib/components/createpublicchannelmodal/index.coffee
+++ client/activity/lib/components/createpublicchannelmodal/index.coffee
@@ -41,12 +41,13 @@
     { getters } = CreateChannelFlux

     return {
-      participants        : getters.createChannelParticipants
-      query               : getters.createChannelParticipantsSearchQuery
-      dropdownUsers       : getters.createChannelParticipantsInputUsers
-      selectedItem        : getters.createChannelParticipantsSelectedItem
-      selectedIndex       : getters.createChannelParticipantsSelectedIndex
-      dropdownVisibility  : getters.createChannelParticipantsDropdownVisibility
+      participants       : getters.createChannelParticipants
+      query              : getters.createChannelParticipantsSearchQuery
+      dropdownUsers      : getters.createChannelParticipantsInputUsers
+      selectedItem       : getters.createChannelParticipantsSelectedItem
+      selectedIndex      : getters.createChannelParticipantsSelectedIndex
+      dropdownVisibility : getters.createChannelParticipantsDropdownVisibility
+      selectedThread     : ActivityFlux.getters.selectedChannelThread
     }


Client: Activity: Added handleUnfollowChannelSuccess event handler to remove given channelId from followedChannelIds

--- client/activity/lib/flux/stores/followedpublicchannelidsstore.coffee
+++ client/activity/lib/flux/stores/followedpublicchannelidsstore.coffee
@@ -13,6 +13,7 @@

     @on actions.LOAD_FOLLOWED_PUBLIC_CHANNEL_SUCCESS, @handleLoadChannelSuccess
     @on actions.FOLLOW_CHANNEL_SUCCESS, @handleFollowChannelSuccess
+    @on actions.UNFOLLOW_CHANNEL_SUCCESS, @handleUnfollowChannelSuccess

   ###*
@@ -38,3 +39,15 @@

     followedChannelIds.set channelId, channelId

+
+  ###*
+   * Remove a channel from followedChannelIds by given channelId
+   *
+   * @param {Immutable.Map} followedChannelIds
+   * @param {object} payload
+   * @param {string} payload.channelId
+  ###
+  handleUnfollowChannelSuccess: (followedChannelIds, { channelId }) ->
+
+    followedChannelIds.remove channelId
+


fix empty grey box

fix empty grey box

--- client/app/Applications/Activity.kdapplication/views/embedbox.coffee
+++ client/app/Applications/Activity.kdapplication/views/embedbox.coffee
@@ -421,6 +421,7 @@

       callback data
     else
+      @hide()
       callback no

   embedUrl:(url,options={},callback=noop)=>
@@ -460,6 +461,7 @@

         callback data
       else
+        @hide()
         callback no
       @embedLoader.hide()
       @$("div.link-embed").removeClass "loading"


copy paste image to upload

copy paste image to upload

--- app/assets/javascripts/posts.js.coffee
+++ app/assets/javascripts/posts.js.coffee
@@ -12,7 +12,10 @@
     url: '/images.json',
     paramName: 'image[source]',
     add: (e, data) ->
-      data.orig_name = remove_ext data.files[0].name
+      if data.files[0].name
+        data.orig_name = remove_ext data.files[0].name
+      else
+        data.orig_name = 'image'
       data.progress_bar = $('<progress max="100" value="0" class="image-upload">').insertAfter(this)
       data.label = $('<label>').insertAfter(data.progress_bar)
       data.submit()


Fixing tests

Fixing tests

--- test/strawpoll_test.coffee
+++ test/strawpoll_test.coffee
@@ -13,4 +13,4 @@
     require('../src/strawpoll')(@robot)

   it 'registers a respond listener', ->
-    expect(@robot.respond).to.have.been.calledWith(/strawpoll 1 2 3/)
+    expect(@robot.respond).to.have.been.calledWith(/strawpoll/).withArgs(1, 2, 3)


Update search box and url on forward/back events

--- web/site/search.coffee
+++ web/site/search.coffee
@@ -123,9 +123,9 @@

       if $scope.searchBoxQuery? and $scope.searchBoxQuery.length > 0
         if $location.search()["debug"]?
-          $location.search("query=" + $scope.searchBoxQuery + "&debug=1").replace()
+          $location.search("query=" + $scope.searchBoxQuery + "&debug=1")
         else
-          $location.search("query=" + $scope.searchBoxQuery).replace()
+          $location.search("query=" + $scope.searchBoxQuery)
       # $location.replace($location.search, $scope.searchBoxQuery)

       # Handle user clearing search box and hitting enter
@@ -481,6 +481,12 @@
       $scope.search()
       return

+    $scope.$on('$routeUpdate',() ->
+      $scope.query = $location.search().query
+      $scope.searchBoxQuery = $scope.query
+      $scope.search()
+    )
+
     # Code for the initial loado
     params = $location.search()
     $scope.query = params.query


tidied up

tidied up

--- test/unit/test-main.coffee
+++ test/unit/test-main.coffee
@@ -18,9 +18,20 @@
     'lodash':
       exports: '_'

-require ['config', 'angular'], (cfg, A) ->
-  require ['angular-mocks', 'angular-resource', 'angular-route'], () ->
-    A.module cfg.ngApp, ['ngMock', 'ngResource', 'ngRoute']
+require [
+  'config'
+  'angular'
+], (cfg, A) ->
+  require [
+    'angular-mocks'
+    'angular-resource'
+    'angular-route'
+  ], () ->
+    A.module cfg.ngApp, [
+      'ngMock'
+      'ngResource'
+      'ngRoute'
+    ]
     A.bootstrap document, [cfg.ngApp]

     require tests, () ->


Add tests for getUTCDateString

Add tests for getUTCDateString

--- test/unit/util.spec.coffee
+++ test/unit/util.spec.coffee
@@ -16,7 +16,16 @@
       expect(util.getISODateString()).toEqual('19700101T001000Z')

     it 'should allow date parameter', ->
-      expect(util.getISODateString(new Date(600000))).toEqual('19700101T001000Z')
+      expect(util.getISODateString(new Date(660000))).toEqual('19700101T001100Z')
+
+  describe 'getUTCDateString', ->
+    it 'should return date formatted as YYYYMMDDTHHnnssZ', ->
+      spyOn(util, 'getDate').andCallFake -> new Date(600000)
+      expect(util.getUTCDateString()).toEqual('Thu, 01 Jan 1970 00:10:00 GMT')
+
+    it 'should allow date parameter', ->
+      expect(util.getUTCDateString(new Date(660000))).toEqual('Thu, 01 Jan 1970 00:11:00 GMT')
+

 describe 'AWS.util.crypto', ->
   util = AWS.util.crypto


Further Merge Error Fix

Further Merge Error Fix

--- app/router.cjsx
+++ app/router.cjsx
@@ -28,11 +28,7 @@
       <IndexRoute component={require './pages/about/about-home'} />
       <Route path="team" component={require './pages/about/team-page'} />
       <Route path="publications" component={require './pages/about/publications-page'} />
-<<<<<<< HEAD
-      <Route path="education" component={require './pages/about/education-page'} />
       <Route path="acknowledgements" component={require './pages/about/acknowledgements'} />
-=======
->>>>>>> 21fe72febe391b01057d8e98b4583d6271a45c92
       <Route path="contact" component={require './pages/about/contact-page'} />
     </Route>

@@ -215,4 +211,3 @@
     <Route path="dev/ribbon" component={require './components/classifications-ribbon'} />
     <Route path="*" component={require './pages/not-found'} />
   </Route>
-


react: fix little password state bug (so easy!)

--- salvus/page/r_account.cjsx
+++ salvus/page/r_account.cjsx
@@ -245,6 +245,7 @@
         @setState
             state    : 'edit'
             error    : ''
+            zxcvbn   : undefined
             old_password : ''
             new_password : ''
             strength     : 0
@@ -254,6 +255,7 @@
             state    : 'view'
             old_password : ''
             new_password : ''
+            zxcvbn   : undefined
             strength     : 0

     save_new_password: ->


chat gets rendered twice the first time it is displayed (leftover bug from when Vivek rewrote something to be async)

--- salvus/page/syncdoc.coffee
+++ salvus/page/syncdoc.coffee
@@ -46,7 +46,6 @@
 ###

-
 CLICK_TO_EDIT = "Edit text..."

 # seconds to wait for synchronized doc editing session, before reporting an error.
@@ -194,7 +193,6 @@

         @project_id = @opts.project_id   # must also be set by derived classes that don't call this constructor!
         @filename   = @opts.filename
-
         #@connect = @_connect
         @connect = misc.retry_until_success_wrapper
             f         : @_connect
@@ -914,9 +912,8 @@
             # done -- no need to render anything.
             return

-        # Clear the chat message area
+        # The chat message area
         chat_output = @element.find(".salvus-editor-codemirror-chat-output")
-        chat_output.empty()

         messages = messages.split('\n')

@@ -956,8 +953,10 @@
                 if err
                     console.log("Error getting user namees -- ", err)
                 else
-
-                    # Send each message to the correct handler
+                    # Clear the chat output
+                    chat_output.empty()
+
+                    # Use handler to render each message
                     last_mesg = undefined
                     for mesg in all_messages


Webterm: add to-do

Webterm: add to-do

--- client/Terminal/webtermview.coffee
+++ client/Terminal/webtermview.coffee
@@ -147,6 +147,7 @@
           console.error "Couldn't connect to your VM. Trying to reconnect...(err:oskite)"
           @webtermConnect("resume")

+      # todo do not leak events
       KD.kite.mq.on "broker.error", (err)=>
         console.error "Couldn't connect to your VM. Trying to reconnect...(err:broker)"
         if err.code is 404


Fix Panel.loading

Fix Panel.loading

--- client/app/components/panel.coffee
+++ client/app/components/panel.coffee
@@ -9,7 +9,6 @@

 # React Mixins
 RouterMixin          = require '../mixins/router_mixin'
-StoreWatchMixin      = require '../mixins/store_watch_mixin'
 TooltipRefesherMixin = require '../mixins/tooltip_refresher_mixin'

 # Flux stores
@@ -34,6 +33,12 @@
     # Build initial state from store values.
     getInitialState: ->
         @getStateFromStores()
+
+    componentDidMount: ->
+        MessageStore.addListener 'change', @fetchMessageComplete
+
+    componentWillUnmount: ->
+        MessageStore.removeListener 'change', @fetchMessageComplete

     render: ->
         # -- Generates a list of messages for a given account and mailbox
@@ -134,7 +139,6 @@
         # Generates the edit draft composition form.
         else if @props.action is 'edit' or
                 @props.action is 'compose.edit'
-
             component = Compose _.extend options,
                 key: options.key + '-' + @props.messageID
                 messageID: @props.messageID
@@ -162,18 +166,15 @@
     # Configure the component depending on the given action.
     # Returns a spinner if the message is not available.
     getReplyComponent: (options) ->
-        message = MessageStore.getByID @props.messageID
-
-        if not(@state.isLoadingReply) or message?
-            message = MessageStore.getByID @props.messageID
-            message.set 'id', @props.messageID
-            options.inReplyTo = message
-            component = Compose options
-        else
-            component = Spinner()
-
+        options.id = @props.messageID
+        options.inReplyTo = @props.messageID
+        component = Compose options
         return component

+    # Update state with store values.
+    fetchMessageComplete: (message) ->
+        return unless @isMounted()
+        @setState isLoadingReply: false

     getStateFromStores: ->
         return {


Add a test for falsy output

Add a test for falsy output

--- spec/Component.coffee
+++ spec/Component.coffee
@@ -469,6 +469,50 @@
       sout1.on 'ip', (ip) ->
       sout2.on 'ip', (ip) ->

+      sin1.post new noflo.IP 'data', 'first'
+
+    it 'should be able to send falsy IPs', (done) ->
+      expected = [
+        port: 'out1'
+        data: 1
+      ,
+        port: 'out2'
+        data: 0
+      ]
+      c = new noflo.Component
+        inPorts:
+          foo:
+            datatype: 'string'
+        outPorts:
+          out1:
+            datatype: 'int'
+          out2:
+            datatype: 'int'
+        process: (input, output) ->
+          return unless input.has 'foo'
+          packet = input.get 'foo'
+          output.sendDone
+            out1: 1
+            out2: 0
+
+      c.inPorts.foo.attach sin1
+      c.outPorts.out1.attach sout1, 1
+      c.outPorts.out2.attach sout2, 0
+
+      sout1.on 'ip', (ip) ->
+        exp = expected.shift()
+        received =
+          port: 'out1'
+          data: ip.data
+        chai.expect(received).to.eql exp
+        done() unless expected.length
+      sout2.on 'ip', (ip) ->
+        exp = expected.shift()
+        received =
+          port: 'out2'
+          data: ip.data
+        chai.expect(received).to.eql exp
+        done() unless expected.length
       sin1.post new noflo.IP 'data', 'first'

     it 'should not be triggered by non-triggering ports', (done) ->