Add specs to test folder git status colour after file delete

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -2165,6 +2165,26 @@
     describe "when a file is ignored", ->
       it "adds a custom style", ->
         expect(treeView.find('.file:contains(ignored.txt)')).toHaveClass 'status-ignored'
+
+    describe "when a file is selected in a directory", ->
+      beforeEach ->
+        jasmine.attachToDOM(workspaceElement)
+        treeView.focus()
+        element.expand() for element in treeView.find('.directory')
+        fileView = treeView.find('.file:contains(new2)')
+        expect(fileView).not.toBeNull()
+        fileView.click()
+
+      describe "when the file is deleted", ->
+        it "updates the style of the directory", ->
+          expect(treeView.selectedEntry().getPath()).toContain(path.join('dir2', 'new2'))
+          dirView = $(treeView.roots[0].entries).find('.directory:contains(dir2)')
+          expect(dirView).not.toBeNull()
+          spyOn(dirView[0].directory, 'updateStatus')
+          spyOn(atom, 'confirm').andCallFake (dialog) ->
+            dialog.buttons["Move to Trash"]()
+          atom.commands.dispatch(treeView.element, 'tree-view:remove')
+          expect(dirView[0].directory.updateStatus).toHaveBeenCalled()

     describe "when the project is a symbolic link to the repository root", ->
       beforeEach ->


handle mlkshk cdn

handle mlkshk cdn

--- scripts/mlkshk-observer.coffee
+++ scripts/mlkshk-observer.coffee
@@ -18,8 +18,8 @@

 module.exports = (robot) ->

-  robot.hear /http:\/\/mlkshk.com\/r\/(\w+)(\.(png|jpg|jpeg|gif))?/i, (msg) ->
-    imageId = msg.match[1]
+  robot.hear /http:\/\/(s\.)?mlkshk.com\/r\/(\w+)(\.(png|jpg|jpeg|gif))?/i, (msg) ->
+    imageId = msg.match[2]

     jsdom.env(
       html: "http://mlkshk.com/p/#{imageId}"
@@ -33,4 +33,4 @@

           if title
             msg.send title
-     )+     )


Provide a callback func

Provide a callback func

--- workers/social/lib/social/models/referral/index.coffee
+++ workers/social/lib/social/models/referral/index.coffee
@@ -324,7 +324,7 @@
       return callback null, OLD_DISK_SIZE_IN_MB if err or not status
       return callback null, CAMPAIGN_DISK_SIZE_IN_MB

-  decreaseLeftSpace = (size, callback)->
+  decreaseLeftSpace = (size, callback = ->)->
     isCampaingValid (err, status)->
       return callback err if err
       return callback null unless status
@@ -338,7 +338,7 @@
       , callback

-  decreaseLeftSpaceInTimeout =(options, callback)->
+  decreaseLeftSpaceInTimeout = (options, callback = ->)->
     oneDayInMs = 86400000
     sevenDayInMs = oneDayInMs*7
     totalTimeInMs = sevenDayInMs


TextShell: read val on every keyup

TextShell: read val on every keyup

--- coffee/src/shells/text.shell.coffee
+++ coffee/src/shells/text.shell.coffee
@@ -38,6 +38,9 @@
     <textarea class="span12"><%= text %></textarea>
     '''

+  events: => _.extend super,
+    'keyup textarea': => @model.text @$('textarea').val()
+
   render: =>
     super
     @$el.empty()


Guard against 0 bidder positions

Guard against 0 bidder positions

--- collections/bidder_positions.coffee
+++ collections/bidder_positions.coffee
@@ -35,7 +35,7 @@

   minBidCents: ->
     Math.max(
-      @first().get('suggested_next_bid_cents')
+      @first()?.get('suggested_next_bid_cents') or 0
       @saleArtwork.get('minimum_next_bid_cents')
     )


Use varargs in integration-test helper fn

--- spec/integration/startup-spec.coffee
+++ spec/integration/startup-spec.coffee
@@ -28,7 +28,7 @@
   afterEach ->
     waitsForPromise -> driver.quit().thenFinally(-> chromeDriver.kill())

-  startAtom = (args=[]) ->
+  startAtom = (args...) ->
     driver = new Builder()
       .usingServer("http://localhost:#{ChromeDriverPort}")
       .withCapabilities(
@@ -50,7 +50,7 @@
       driver.wait ->
         driver.getTitle().then (title) -> title.indexOf("Atom") >= 0

-  startAnotherAtom = (args=[]) ->
+  startAnotherAtom = (args...) ->
     spawnSync(AtomPath, args.concat([
       "--dev",
       "--safe",
@@ -63,7 +63,7 @@
     beforeEach ->
       tempFilePath = path.join(tempDirPath, "an-existing-file")
       fs.writeFileSync(tempFilePath, "This was already here.")
-      startAtom([path.join(tempDirPath, "new-file")])
+      startAtom(path.join(tempDirPath, "new-file"))

     it "opens a new window with an empty text editor", ->
       waitsForPromise ->
@@ -80,7 +80,7 @@
       # Opening another existing file in the same directory reuses the window,
       # and opens a new tab for the file.
       waitsForPromise ->
-        startAnotherAtom([tempFilePath])
+        startAnotherAtom(tempFilePath)
         driver.wait ->
           driver.executeScript(-> atom.workspace.getActivePane().getItems().length).then (length) ->
             length is 2
@@ -89,14 +89,14 @@

       # Opening a different directory creates a new window.
       waitsForPromise ->
-        startAnotherAtom([temp.mkdirSync("another-empty-dir")])
+        startAnotherAtom(temp.mkdirSync("another-empty-dir"))
         driver.wait ->
           driver.getAllWindowHandles().then (handles) ->
             handles.length is 2

   describe "when given the name of a directory that exists", ->
     beforeEach ->
-      startAtom([tempDirPath])
+      startAtom(tempDirPath)

     it "opens a new window no text editors open", ->
       waitsForPromise ->


Fix for difftool testing

Fix for difftool testing

--- spec/models/git-difftool-spec.coffee
+++ spec/models/git-difftool-spec.coffee
@@ -5,18 +5,32 @@
 GitDiffTool = require '../../lib/models/git-difftool'

 describe "GitDiffTool", ->
-  beforeEach ->
-    atom.config.set 'git-plus.includeStagedDiff', true
-    spyOn(git, 'cmd').andReturn Promise.resolve('diffs')
-    spyOn(git, 'getConfig').andReturn Promise.resolve('some-tool')
-    waitsForPromise ->
-      GitDiffTool repo, file: pathToSampleDir
-      GitDiffTool repo, file: pathToRepoFile
+  describe "Using includeStagedDiff", ->
+    beforeEach ->
+      atom.config.set 'git-plus.includeStagedDiff', true
+      # git.cmd ['config', 'diff.tool', 'meld']
+      spyOn(git, 'cmd').andReturn Promise.resolve('diffs')
+      spyOn(git, 'getConfig').andReturn Promise.resolve('some-tool')
+      waitsForPromise ->
+        GitDiffTool repo, file: pathToRepoFile

-  describe "when git-plus.includeStagedDiff config is true", ->
-    it "calls git.cmd with 'diff-index HEAD -z' and optional '-d' for directories", ->
-      expect(git.cmd).toHaveBeenCalledWith ['diff-index', 'HEAD', '-z'], cwd: repo.getWorkingDirectory()
-      expect(git.cmd).toHaveBeenCalledWith ['diff-index', 'HEAD', '-z', '-d'], cwd: repo.getWorkingDirectory()
+    describe "when git-plus.includeStagedDiff config is true", ->
+      it "calls git.cmd with 'diff-index HEAD -z'", ->
+        expect(git.cmd).toHaveBeenCalledWith ['diff-index', 'HEAD', '-z'], cwd: repo.getWorkingDirectory()

-    it "calls `git.getConfig` to check if a a difftool is set", ->
-      expect(git.getConfig).toHaveBeenCalledWith 'diff.tool', repo.getWorkingDirectory()
+      it "calls `git.getConfig` to check if a a difftool is set", ->
+        expect(git.getConfig).toHaveBeenCalledWith 'diff.tool', repo.getWorkingDirectory()
+
+  describe "Usage on dirs", ->
+    beforeEach ->
+      spyOn(git, 'cmd').andReturn Promise.resolve('diffs')
+      spyOn(git, 'getConfig').andReturn Promise.resolve('some-tool')
+      waitsForPromise ->
+        GitDiffTool repo, file: pathToSampleDir
+
+    describe "when file points to a directory", ->
+      it "calls git.cmd with 'difftool --no-prompt -d'", ->
+        expect(git.cmd.calls[1].args).toEqual([['difftool', '-d', '--no-prompt', pathToSampleDir], {cwd: repo.getWorkingDirectory()}]);
+
+      it "calls `git.getConfig` to check if a a difftool is set", ->
+        expect(git.getConfig).toHaveBeenCalledWith 'diff.tool', repo.getWorkingDirectory()


Update seal.coffee

Update seal.coffee

--- modules/seal.coffee
+++ modules/seal.coffee
@@ -5,10 +5,10 @@
   init: ->
     @registerCommand 'seal', { allowDM: true }, ({ msg, args })=>
       chance = new Chance()
-      # Get a seal from randomse.al
+      # Get a seal from random-seal
       seal = chance.integer { min: 1, max: 83 }
       seal = ('0000' + seal).substring(seal.toString().length)
-      msg.channel.send "https://randomse.al/seals/#{seal}.jpg"
+      msg.channel.send "https://focabot.github.io/random-seal/seals/#{seal}.jpg"

     # coffeelint: disable=max_line_length
     # PRAISE THE SEAL!


Debugging error-free code is fuckin difficult man

--- src-public/controllers/signup.coffee
+++ src-public/controllers/signup.coffee
@@ -8,4 +8,3 @@
     .catch((response) ->
         $window.location.href = '/'
     )
-z


extracting some logic out to new property `newRecord`

--- app/assets/javascripts/classes/model_backed_view.js.coffee
+++ app/assets/javascripts/classes/model_backed_view.js.coffee
@@ -17,13 +17,17 @@
     messages
   ).property("raw_errors")

-  actionText: (->
-    if not @get("model") or @get("model").get("isNew") then "Nuevo" else "Editar"
+  newRecord: (->
+    not @get("model") or @get("model").get("isNew")
   ).property("model")

+  actionText: (->
+    if @get("newRecord") then "Nuevo" else "Editar"
+  ).property("newRecord")
+
   submitButtonText: (->
-    if not @get("model") or @get("model").get("isNew") then "Crear" else "Actualizar"
-  ).property("model")
+    if @get("newRecord") then "Crear" else "Actualizar"
+  ).property("newRecord")

   getFormData: ->
     @getProperties @get("attributes")


Delete unused file

Delete unused file

--- app/assets/javascripts/lib/category_autocomplete.js.coffee
+++ app/assets/javascripts/lib/category_autocomplete.js.coffee
@@ -1,49 +0,0 @@
-$.widget( "custom.catcomplete",  $.ui.autocomplete,
-  _create: ->
-    @_super();
-    @widget().menu("option", "items", "> :not(.ui-autocomplete-category)")
-
-  _renderMenu: (ul, items) ->
-    currentCategory = ''
-    $.each items, (index, item) =>
-      if item.category isnt currentCategory
-        ul.append("
#{item.category}
")
-        currentCategory = item.category
-
-      li = @_renderItemData(ul, item)
-
-      if item.category?
-        li.attr('aria-label', item.category + " : " + item.label)
-
-  _renderItem: (ul, item) ->
-    # Highlight occurrences
-    item.label = item.label.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + $.ui.autocomplete.escapeRegex(this.term) + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "$1");
-
-    return $( "

" )
-        .data( "item.autocomplete", item )
-        .append( "#{item.label}" )
-        .appendTo( ul );
-
-  _resizeMenu: ->
-    if (isNaN(this.options.maxShowItems))
-      return
-
-    ul = this.menu.element.css(overflowX: '', overflowY: '', width: '', maxHeight: '')
-
-    lis = ul.children('li').css('whiteSpace', 'nowrap');
-
-    if (lis.length > this.options.maxShowItems)
-      ulW = ul.prop('clientWidth')
-
-      ul.css(
-            overflowX: 'hidden'
-            overflowY: 'auto'
-            maxHeight: lis.eq(0).outerHeight() * this.options.maxShowItems + 1
-          )
-
-      barW = ulW - ul.prop('clientWidth');
-      ul.width('+=' + barW);
-
-    # Original code from jquery.ui.autocomplete.js _resizeMenu()
-    ul.outerWidth(Math.max(ul.outerWidth() + 1, this.element.outerWidth()));
-  )


Fix doble submit specs

Fix doble submit specs

We have a custom implementation to avoid doble submissions which causes
some custom problems :relieved:

This commit should fix the following three specs
spec/features/comments/legislation_annotations_spec.rb:367
spec/features/comments/legislation_questions_spec.rb:333
spec/features/custom/probe_option_comments_spec.rb:298

One of the custom problems comes from ajax:success[1] not being
triggered, instead only ajax:complete seems to be triggered

We should remove this custom implementation and use the standard
solution provided in rails[2]

[1]
https://github.com/AyuntamientoMadrid/consul/blob/master/app/assets/java
scripts/prevent_double_submission.js.coffee#L28

[2]
https://stackoverflow.com/questions/9570912/how-to-disable-a-form-submit
-button-a-l%C3%A0-ruby-on-rails-way/9572893#9572893

--- app/assets/javascripts/prevent_double_submission.js.coffee
+++ app/assets/javascripts/prevent_double_submission.js.coffee
@@ -22,11 +22,15 @@

   initialize: ->
     $('form').on('submit', (event) ->
-      unless event.target.id == "new_officing_voter" || "admin_download_emails"
+      unless event.target.id == "new_officing_voter" ||
+             event.target.id == "admin_download_emails"
+
         buttons = $(this).find(':button, :submit')
         App.PreventDoubleSubmission.disable_buttons(buttons)
     ).on('ajax:success', (event) ->
-      unless event.target.id == "new_officing_voter"
+      unless event.target.id == "new_officing_voter" ||
+             event.target.id == "admin_download_emails"
+
         buttons = $(this).find(':button, :submit')
         App.PreventDoubleSubmission.reset_buttons(buttons)
     )


Updated Checkout service to persist selected_shipping_rate_id

--- app/assets/javascripts/sprangular/services/checkout.coffee
+++ app/assets/javascripts/sprangular/services/checkout.coffee
@@ -22,10 +22,8 @@
         'order[payments_attributes][][payment_method_id]': paymentMethodId
         payment_source: {}

-      if order.shippingMethod
-        params.order.shipping_method_id = order.shippingMethodId
-
       if order.shippingRate
+        params['order[shipments_attributes][][id]'] = order.shipment.id
         params['order[shipments_attributes][][selected_shipping_rate_id]'] = order.shippingRate.id

       @put(params)


change log message

change log message

--- app/templates/application/assets/coffee/application.coffee
+++ app/templates/application/assets/coffee/application.coffee
@@ -1,3 +1,3 @@
 require ['jquery'], ($) ->
   $ ->
-    console.log 'test'
+    console.log 'hello!'


minor bug fix

minor bug fix

--- app/annotation/step.cjsx
+++ app/annotation/step.cjsx
@@ -19,7 +19,7 @@

   onButtonClick: ({currentTarget}) ->
     button = currentTarget
-    human = steps[2][0].animal.options[8]
+    human = steps[2][0].animal.options[9]
     notAChimp = @animalCheck(button.value, chimp)

     switch


add pagination controls

add pagination controls

--- table-renderer.coffee
+++ table-renderer.coffee
@@ -29,6 +29,7 @@
             'partials'
             'data'
             'hasFooter'
+            'pagination_template'
         ]

         for option in validOptions
@@ -56,6 +57,11 @@

             else if data instanceof Array
                 new_data = {}
+                data = for item in data
+                    if Backbone and item instanceof Backbone.Model
+                        item.toJSON()
+                    else
+                        item
                 new_data[@key] = data
                 @data = new_data

@@ -140,3 +146,28 @@

         @row_template_compiled = Handlebars.compile(@row_template)
         @table_template_compiled = Handlebars.compile(@table_template)
+
+
+    pagination_template_compiled: null
+    pagination_template: """
+

+

+
Previous

+                {{#each pages}}
+
{{label}}

+                {{/each}}
+
Next

+

+

+    """
+
+    render_pagination: (options) =>
+        if not @pagination_template_compiled
+            @pagination_template_compiled = Handlebars.compile(@pagination_template)
+
+        return @pagination_template_compiled(options)
+
+
+
+
+


Tests: Fixed editPost test.

Tests: Fixed editPost test.

--- tests/src/activity/activity.coffee
+++ tests/src/activity/activity.coffee
@@ -49,8 +49,10 @@
       .clearValue                 selector + ' .edit-widget [testpath=ActivityInputView]'
       .setValue                   selector + ' .edit-widget [testpath=ActivityInputView]', post + '\n'
       .pause                      3000
-      .waitForElementVisible      selector + post, 10000 # Assertion
-      .end()
+
+    browser.assert.containsText selector, post # Assertion
+
+    browser.end()

   deletePost: (browser) ->


Test: Use helper for followTopic test.

Test: Use helper for followTopic test.

--- tests/src/activity/activity.coffee
+++ tests/src/activity/activity.coffee
@@ -205,15 +205,12 @@

   followTopic: (browser) ->

-    hashtag = helpers.sendHashtagActivity(browser)
-    selector = activitySelector + ' .has-markdown p a:first-child'
-
-    browser
-      .waitForElementVisible   selector, 5000
-      .click                   selector
-      .pause                   3000 # really required
-      .assert.containsText     '[testpath=channel-title]', hashtag # Assertion
-      .end()
+    helpers.doFollowTopic(browser)
+    browser.end()
+
+
+
+    browser

   postLongMessage: (browser) ->


Removed unused config options

Removed unused config options

--- lib/assets/javascripts/pixie/editor/pixel/tools.coffee
+++ lib/assets/javascripts/pixie/editor/pixel/tools.coffee
@@ -1,13 +1,6 @@
 # All the default editor tools for the pixel editor

 namespace "Pixie.Editor.Pixel", (Pixel) ->
-  # Import tools and actions from other files
-  {
-    config: {
-      IMAGE_DIR
-      DEBUG
-    }
-  } = Pixel

   colorNeighbors = (color) ->
     @color(color)


hideLegend

hideLegend

--- app/graph_drawing.coffee
+++ app/graph_drawing.coffee
@@ -73,6 +73,7 @@
   brushColor: '#efefef'
   valueFormat: d3.format(',.4g')
   hideAxes: false
+  hideLegend: false
   #lineWidth: 1

 fgColorParams = ['axisLineColor', 'axisTextColor', 'crosshairLineColor', 'crosshairTextColor']
@@ -500,6 +501,11 @@
       addCircles target, false
       addCircles target, true

+    if params.hideLegend
+      legend.style(display: 'none')
+    else
+      legend.style(display: 'block')
+
     legendTarget = legend.selectAll('li')
       .data(targets)


Refactors RightPanel.coffee to get rid of the ref.

--- src/View/RightPanel.coffee
+++ src/View/RightPanel.coffee
@@ -19,9 +19,6 @@
         startX = moveEvent.clientX
         layout.resizeRightPanel(dx)

-  componentDidMount: ->
-    @refs.resize.getDOMNode().addEventListener("mousedown", @_onResizeMouseDown)
-
   render: ->
     layout = @context.editor.layout

@@ -36,8 +33,7 @@
       },
       R.div {
         className: "RightResize"
-        ref: "resize"
-        #onClick: layout.dragRightPanel.bind(layout, 10)
+        onMouseDown: @_onResizeMouseDown
       }
       R.Outline {}
       R.Inspector {}


Touch events fix WIP

Touch events fix WIP

--- engine/events.coffee
+++ engine/events.coffee
@@ -39,7 +39,8 @@

         @touch =
             #touch events is a list of the captured touch events
-            touch_events:[]
+            touch_events:{}
+            first_touch_event: null
             #number of current touches
             touches:0

@@ -96,6 +97,8 @@
                 touch.movement_since_touch = 0
                 touch.touch_target = touch.target = t.target
                 @touch.touch_events[touch.id] = touch
+                if not @touch.first_touch_event?
+                    @touch.first_touch_event = touch
             @touch.touches = event.targetTouches.length

         if enable_touch
@@ -103,8 +106,8 @@

         touch_end = (event)=>
             event.preventDefault()
-            for touch in @touch.touch_events
-                touch.touching = 0
+            for id,touch of @touch.touch_events
+                touch.touching = false
             for t in event.targetTouches
                 touch = @touch.touch_events[t.identifier]
                 touch.touching = true
@@ -113,6 +116,10 @@
                 touch.radius_y = 0
                 touch.rel_x = 0
                 touch.rel_y = 0
+            for id,touch of @touch.touch_events when not touch.touching
+                delete @touch.touch_events[id]
+            if event.targetTouches.length == 0
+                @touch.first_touch_event = null
             @touch.touches = event.targetTouches.length

         if enable_touch


fix BridleNSIS file extensions

fix BridleNSIS file extensions

--- grammars/bridlensis.cson
+++ grammars/bridlensis.cson
@@ -1,6 +1,4 @@
 fileTypes: [
-  "bnsi"
-  "bnsh"
   "bridle-nsi"
   "bridle-nsis"
   "bridle-nsh"


remove debug flag again...

remove debug flag again...

--- app/assets/javascripts/wizard.coffee
+++ app/assets/javascripts/wizard.coffee
@@ -498,8 +498,6 @@
     $('#new_suggestion').submit()
     return false

-  $('.step-gps').view()
-

 # for animations
 $(window).load ->


Change spreadsheet editing blank record messages

--- app/assets/volume/spreadsheet.coffee
+++ app/assets/volume/spreadsheet.coffee
@@ -308,10 +308,10 @@
           else
             td.className = 'null'
             if !n || n == t
-              if !n
+              if editing && cols > 1
+                td.appendChild(document.createTextNode("\u2190 add " + cat.name))
+              else if !n
                 td.appendChild(document.createTextNode(cat.not))
-              else if editing
-                td.appendChild(document.createTextNode("add " + cat.name))
               if editing
                 if 'metrics' of col && col.metrics[0].id != 'id'
                   generateCell(row, undefined, undefined, id+'-rec_'+i+(if n then '_'+n else '')+'_'+col.start)


wip - rewrote to eliminate mousetrap dep

--- _assets/js/_keyboard.coffee
+++ _assets/js/_keyboard.coffee
@@ -1,38 +1,107 @@
-$(document).ready ->
-  search = $("#search-results")
+hasResults = if $('#search-results a').length > 0 then true else false

-  # search.on 'mouseover', 'li', ->
-  #   search.children('a').removeClass('selected')
-  #   $(this).parent('a').addClass 'selected'
-  #   $(this).mouseout -> $(this).parent('a').removeClass 'selected'
+currentSelection = $("#search-results a.selected")

-  Mousetrap.bind 'down', (e) ->
-    if e.preventDefault
-      e.preventDefault()
-    else
-      e.returnValue = false
-    console.log 'next'
-    if $("#search-results a.selected").index() + 1 == $("#search-results").children().length
-      console.log 'end of list'
-    else if !$('#search-results a').hasClass('selected')
-      $("#search-results a").first().addClass("selected")
-      console.log 'first first first'
-    else
-      $("#search-results a.selected").removeClass("selected").next().addClass('selected')
-      console.log 'down down down'
+results = $('#search-results')

-  Mousetrap.bind 'up', (e) ->
-    if e.preventDefault
+traverseUp = ->
+  console.log 'up arrow pressed'
+  return
+
+
+traverseDown = ->
+  console.log 'down arrow pressed'
+  $("#search-results a.selected").removeClass('selected').next().addClass('selected')
+  # return
+
+
+hoverSelection = ->
+  # TODO Refactor, currently it's very redundant
+  query = $('#search-results a')
+  $(query).on 'mouseenter', () -> $(this).addClass 'selected'
+  $(query).on 'mouseleave', () -> $(this).removeClass 'selected'
+  return
+
+arrayEqual = (a, b) ->
+  a.length is b.length and a.every (elem, i) -> elem is b[i]
+
+
+highlightSelection = ->
+  target = document.querySelector('#search-results')
+
+  # create an observer instance
+  observer = new MutationObserver((mutations) ->
+
+    oldResults = []
+
+    if $('#search-results a').index() != -1
+      $('#search-results a').each (index, element) ->
+        url = $(this).attr('href')
+        oldResults.push url
+        return
+
+    console.log 'old results: ' + oldResults
+    console.log 'oldResults count: ' + $(oldResults).length
+
+    i = 0
+    len = mutations.length
+    newResults = []
+    while i < len
+      resultsList = mutations[0].target
+
+      if i < mutations[0].length - 1
+        url = resultsList.querySelectorAll('a')[i].pathname
+        newResults.push url
+
+        $('#search-results a').each (index, element) ->
+          $(this).addClass 'selected' if index == 0
+      i++
+
+    $('#search-results a').each (index, element) ->
+      # FIXME
+      # this is "reseting" the selector to the top each time
+      if index == 0
+        console.log 'index = 0'
+        $(this).addClass 'selected'
+      return
+  )
+
+  # configuration of the observer:
+  config =
+    childList: true
+    subtree: true
+
+  # pass in the target node, as well as the observer options
+  observer.observe target, config
+
+
+  $('#search-posts').keydown (e) ->
+    if e.which == 40 # if the down arrow is pressed . . .
       e.preventDefault()
-    else
-      e.returnValue = false
-    console.log 'previous'

-    if $("#search-results a.selected").index() == 0
-      console.log 'end of list'
-    else
-      $("#search-results a.selected").removeClass("selected").prev().addClass('selected')
+      endOfList =
+        $("#search-results a.selected").index() + 1 == $("#search-results").children().length
+
+      observer.disconnect()
+      traverseDown() unless endOfList
+
+    else if e.which == 38 # if the up arrow is pressed . . .
+      e.preventDefault()
+
+      observer.disconnect()
+      traverseUp()
+
+  $('#search-posts').keyup (e) ->
+    # once the up or down arrow is released . . .
+    if e.which == 40 or e.which == 38
+      observer.observe target, config
+  return
+
+
+$(document).ready ->
+  highlightSelection()
+  hoverSelection()

   Mousetrap.bind 'enter', (e) ->
     if e.preventDefault
@@ -41,8 +110,6 @@
       e.returnValue = false
     window.open $('#search-results a.selected').attr('href'), '_self'

-  Mousetrap.prototype.stopCallback = ->
-     return false

   Mousetrap.bind 'o h', ->
     console.log 'io'
@@ -62,4 +129,15 @@
   Mousetrap.bind 'e m a c s', ->
     console.log 'did you mean: vim'

+
+  #Work in Progress
+
+  # Mousetrap.bind 'r o o t', ->
+  #   $('body').children().toggle()
+  #   $('body').toggleClass('term')
+  #   a = "
The future ain't what it used to be
"
+  #   b = "
It's so crowded, nobody comes here anymore
"
+  #   c = "
I really didn't say everything I said
"
+  #   $('.term').append(a,b,c)
+
   return


Make sure empty export responses are cached

--- app/partials/data-export-button.cjsx
+++ app/partials/data-export-button.cjsx
@@ -12,15 +12,13 @@
     exportError: null

   exportGet: ->
-    @props.project.get @props.exportType
-      .catch ->
-        []
+    @_exportsGet or= @props.project.get(@props.exportType).catch( -> [])

   requestDataExport: ->
     @setState exportError: null
     apiClient.post @props.project._getURL(@props.exportType), media: content_type: 'text/csv'
       .then =>
-        @classificationsExportGet = null
+        @_exportsGet = null
         @setState exportRequested: true
       .catch (error) =>
         @setState exportError: error


Change code style

Change code style

--- siteshot.coffee
+++ siteshot.coffee
@@ -51,14 +51,14 @@
                               console.log "Finish loading #{route} and save it in #{snapPath}"
                               callback()

-                      if (typeof config.pageModifier == 'function')
+                      if (typeof config.pageModifier is 'function')
                         config.pageModifier(page, ->
-                          generateHTML();
+                          generateHTML()
                         );
                       else
-                        generateHTML();
-                    ), config.delay || 0
-
+                        generateHTML()
+                    ), config.delay or 0
+
             # Async page load
             async.eachSeries routes, pageLoad, (err) =>
               throw err if err?


add tests for view class

add tests for view class

--- spec/atom-sfdc-ba-view-spec.coffee
+++ spec/atom-sfdc-ba-view-spec.coffee
@@ -1,5 +1,30 @@
-AtomSfdcBaView = require '../lib/atom-sfdc-ba-view'
+{WorkspaceView} = require 'atom'
+AtomSfdcBa = require '../lib/atom-sfdc-ba'
+
+# Use the command `window:run-package-specs` (cmd-alt-ctrl-p) to run specs.
+#
+# To run a specific `it` or `describe` block add an `f` to the front (e.g. `fit`
+# or `fdescribe`). Remove the `f` to unfocus the block.

 describe "AtomSfdcBaView", ->
-  it "has one valid test", ->
-    expect("life").toBe "easy"
+  activationPromise = null
+
+  beforeEach ->
+    atom.workspaceView = new WorkspaceView
+    activationPromise = atom.packages.activatePackage('atom-sfdc-ba')
+
+  describe "when the atom-sfdc-ba:toggle event is triggered", ->
+    it "attaches and then detaches the view", ->
+      expect(atom.workspaceView.find('.message')).not.toExist()
+
+      # This is an activation event, triggering it will cause the package to be
+      # activated.
+      atom.workspaceView.trigger 'atom-sfdc-ba:toggle'
+
+      waitsForPromise ->
+        activationPromise
+
+      runs ->
+        expect(atom.workspaceView.find('.message')).toExist()
+        atom.workspaceView.trigger 'atom-sfdc-ba:toggle'
+        expect(atom.workspaceView.find('.message')).not.toExist()


added .slug to active tab id

added .slug to active tab id

--- templates:tabs.coffee
+++ templates:tabs.coffee
@@ -41,7 +41,7 @@
         self.autorun ->
           activeTab = self._activeTab.get()
           ($ self.findAll('.tabs-content')).hide()
-          ($ self.find("[data-tab='#{activeTab}']")).show()
+          ($ self.find("[data-tab='#{activeTab.slug}']")).show()
           if options?.onChange?
             options.onChange(activeTab.slug)
           if activeTab?.onRender?


:dog::telephone_receiver: Updated at https://danielx.net/editor/


Fixed drawing bug.

Fixed drawing bug.

--- iced/content_script.iced
+++ iced/content_script.iced
@@ -230,6 +230,7 @@
 exitDrawMode = ->
     _state = STATE.INTERACT
     $('#js-gifics-draw').html """<i class="icon-pencil"></i> Draw"""
+    $('#js-gifics-canvas').remove()
     getPanel().animate {"left": 0}, 250

 enterDrawMode = ->


Updated faction adjectives

Updated faction adjectives

--- src/code/content/smash-up-decks.cjsx
+++ src/code/content/smash-up-decks.cjsx
@@ -70,7 +70,7 @@
 	title: "Robots"
 	name:
 		noun: "Robots"
-		adjective: "Robot"
+		adjective: "Robotic"
 	description: "Play lots of weak minions at once that compound eachother."
 	set: setNames.core
 ,
@@ -252,7 +252,7 @@
 	title: "Clerics"
 	name:
 		noun: "Clerics"
-		adjective: "Cleric"
+		adjective: "Clerical"
 	description: "Return cards from the discard pile and prevent them from going to the discard pile"
 	set: setNames.munchkin
 ,


Fixes sorting for Title and User

Fixes sorting for Title and User

--- views/coffeescripts/showbot.coffee
+++ views/coffeescripts/showbot.coffee
@@ -34,7 +34,7 @@
   # Extract vote count value if this is a vote column
   # Note: This is also required for sorting to continue working via the
   #   trigger('update') after a vote is cast
-  else if $element.find('.vote_count')
+  else if $element.find('.vote_count').length
     text = $element.find('.vote_count').html()

   text


Check metaKey and ctrlKey presence for multi platform support

Fixes #388

--- src/components/multiselect-list.cjsx
+++ src/components/multiselect-list.cjsx
@@ -122,7 +122,7 @@

   _onClickItem: (item, event) =>
     return unless @state.handler
-    if event.metaKey
+    if event.metaKey || event.ctrlKey
       @state.handler.onMetaClick(item)
     else if event.shiftKey
       @state.handler.onShiftClick(item)


fix(scroll): better check for scrollTo in multiselect list

--- src/components/multiselect-list.cjsx
+++ src/components/multiselect-list.cjsx
@@ -57,7 +57,7 @@

       item = React.findDOMNode(@).querySelector(".focused")
       item ?= React.findDOMNode(@).querySelector(".keyboard-cursor")
-      return unless item
+      return unless item instanceof Node
       @refs.list.scrollTo(item)

   componentWillUnmount: =>


alpha version of collab working - not fully implemented, still bugs - in developement

--- lib/GUI/atox-collab.coffee
+++ lib/GUI/atox-collab.coffee
@@ -1,6 +1,5 @@
 CollabGroupProtocol = require '../atox-collabGroupProtocol'
-
-newlineRegex: /\r\n|\n|\r/g
+{Range, Point} = require 'atom'

 module.exports =
 class Collab extends CollabGroupProtocol
@@ -14,7 +13,7 @@
     @pmutex = true
     @icb = []

-    @disposables.push @editor.onDidInsertText (e) => @internalChange e
+    @disposables.push @editor.getBuffer().onDidChange (e) => @internalChange e
     @disposables.push @editor.onDidChangeSelectionRange (e) =>
       @changedSelection e

@@ -38,83 +37,100 @@
     if @pmutex and @icb.length > 0
       @internalchanges = @icb.concat(@internalchanges)
       @icb = []
-    for pos in @editor.getCursorBufferPositions()
-      if @pmutex
-        @internalchanges.push {'pos': pos, 'text': e.text}
-      else
-        @icb.push {'pos': pos, 'text': e.text}
+
+    if (e.originFlag? and e.originFlag) or (e.newRange.originFlag? and e.newRange.originFlag)
+      console.log "ignore change"
+      return
+
+    {oldRange, newRange, oldText, newText} = e
+    oldRange = Range(oldRange.start, oldRange.end)
+    newRange = Range(newRange.start, newRange.end)
+    newRange.originFlag = true #dirty stuff - don't think about
+    normalizeLineEndings = true
+    e = {oldRange, newRange, oldText, newText, normalizeLineEndings}
+    if @pmutex
+      @internalchanges.push e
+    else
+      @icb.push e
+      console.log @icb

   externalChange: (e) ->
-    lineStartIndex = 0
-    while result = newlineRegex.exec(e.text)
-      line = e.text[lineStartIndex...result.index]
-      ending = result[0]
-      @externalchanges.push {'pos': e.pos, 'text': line, 'nline': ending}
-      lineStartIndex = newlineRegex.lastIndex
-    @externalchanges.push {'pos': e.pos, 'text': line, 'nline': false}
+    return unless e?
+    console.log e
+
+    {oldRange, newRange, oldText, newText} = e
+    oldRange = Range(oldRange.start, oldRange.end)
+    newRange = Range(newRange.start, newRange.end)
+    newRange.originFlag = true #dirty stuff - don't think about
+    normalizeLineEndings = true
+    e = {oldRange, newRange, oldText, newText, normalizeLineEndings}
+    @externalchanges.push e
     return
-
-    #Alternate (old) code
-    #for i in [0 .. e.text.length]
-    for c, i in e.text
-      #c = e.text[i]
-      if c is '\n' or (c is '\r' and e.text[i+1] != '\n')
-        @externalchanges.push {'pos': e.pos, 'text': e.text.slice(0, c)
-          'nline': c}
-        @externalChange {'pos': e.pos, 'text': e.text.slice(c + 1)}
-        return
-      else if c is '\r'
-        @externalchanges.push {'pos': e.pos, 'text': e.text.slice(0, c)
-          'nline': e.text.slice(c, c + 2)}
-        @externalChange {'pos': e.pos, 'text': e.text.slice(c + 2)}
-        return
-
-    @externalchanges.push {'pos': e.pos, 'text': e.text}

   changedSelection: (e) ->

   patchLines: ->
-    #Fix pos of external changes
-    rshift = 0
+    ###Fix pos of external changes
     for ec, i in @externalchanges #Shift
-      @externalchanges[i].pos[0] += rshift
-      if ec.nline? and ec.nline is not false
-        rshift++
+      for e in @externalchanges
+        if ec != e and ec.oldRange.compare(e.oldRange) == 1
+          @externalchanges[i].oldRange.translate(Point(e.newRange.getRowCount() - e.oldRange.getRowCount(), 0))
+          @externalchanges[i].newRange.translate(Point(e.newRange.getRowCount() - e.oldRange.getRowCount(), 0))###

     #Fix pos of internal changes
     for ec, i in @externalchanges
       for ic, j in @internalchanges
-        if ec.nline? and ec.nline is not false and ic.pos.row() >= ec.pos[0]
-          @internalchanges[j].pos.translate([1, 0]) #Shift row by 1
-        else if ic.pos.row() is ec.pos[0] and ic.pos.column() > ec.pos[1]
-          @internalchanges[j].pos.translate([0, ic.pos.column() - ec.pos[1]])
+        if ic.oldRange.start.row() > ec.oldRange.start.row()
+          @internalchanges[j].newRange.translate([ec.newRange.getRowCount() - ec.oldRange.getRowCount(), 0])
+          @internalchanges[j].oldRange.translate([ec.newRange.getRowCount() - ec.oldRange.getRowCount(), 0]) #Shift row by 1
+        else if ic.oldRange.start.row() is ec.oldRange.start.row() and ic.oldRange.start.column() > ec.oldRange.start.column()
+          @internalchanges[j].newRange.translate([ec.newRange.getRowCount() - ec.oldRange.getRowCount(), ec.newRange.end.column() - ec.oldRange.end.column()])
+          @internalchanges[j].oldRange.translate([ec.newRange.getRowCount() - ec.oldRange.getRowCount(), ec.newRange.end.column() - ec.oldRange.end.column()])

   applyExternal: ->
     #Concat both and patch local buffer
     for ec in @externalchanges
-      if ec.nline? and ec.nline is not false
-        @sLines[ec.pos[0]] = @sLines[ec.pos[0]].slice(0, ec.pos[1]) + ec.text
-        @sLines.splice(ec.pos[0] + 1, 0, @sLines[ec.pos[0]].slice(ec.pos[1]))
-        @sLineEndings.splice(ec.pos[0], 0, ec.nline)
-      else
-        @sLines[ec.pos[0]] = @sLines[ec.pos[0]].slice(0, ec.pos[1]) + ec.text +
-          @sLines[ec.pos[0]].slice(ec.pos[1])
+      console.log ec
+      @editor.getBuffer().applyChange(ec, false)
+      return

   swap: ->
+    oldText = @editor.getText()
+
+    #@editor.displayBuffer.updateAllLines() #doesn't work
+
+    #Combine the two arrays and use @editor.getBuffer().setText()
+    newText = ''
+    for row in [0 .. @sLines.length - 1]
+      newText += (@sLines[row] + @sLineEndings[row])
+    #@editor.setText(text) #use function of editor instead
+
+    oldRange = @editor.getBuffer().getRange()
+    oldRange.freeze()
+    newRange = Range.fromText(oldRange.start, newText)
+    newRange.freeze()
+    originFlag = true
+    changeEvent = Object.freeze({oldRange, newRange, oldText, newText, originFlag})
+
+    @editor.getBuffer().emitter.emit 'will-change', changeEvent
+
     @editor.getBuffer().lines = @sLines
     @editor.getBuffer().lineEndings = @sLineEndings

+    @editor.getBuffer().emitter.emit 'did-change', changeEvent
+    @editor.displayBuffer.updateAllScreenLines()
+
   applyInternal: ->
     #Apply internal changes
-    for ic in @internalchanges
-      @editor.getBuffer().insert(ic.pos, ic.text)
+    for ic in @internalchanges #maybe have to fix array -> normalizeLineEndings
+      @editor.getBuffer().applyChange(ic, true)

   process: ->
     @pmutex = false

     @patchLines()
+    @swap()
     @applyExternal()
-    @swap()
     @applyInternal()

     changes = @internalchanges.slice(0)
@@ -125,9 +141,6 @@
     @sLines = @editor.getBuffer().getLines()
     @sLineEndings = @editor.getBuffer().lineEndings
     @pmutex = true
-
-    for c, i in changes
-      changes[i].pos = changes[i].pos.toArray()

     return changes

@@ -149,6 +162,10 @@
     return {"lines": @sLines, "lineEndings": @sLineEndings}

   CMD_process: (changes) ->
+    @externalchanges = []
+    console.log "External Changes:"
+    console.log changes
     if changes?
-      @externalChange(c) for c in changes
+      for pchanges in changes
+        @externalChange(c) for c in pchanges
     @process()


Calculate pixel position of both bracket highlights before adding them

Before, we were causing a layout to occur twice because we needed to
read the DOM for the pixel position of the second highlight view after
invalidating the DOM when adding the first highlight view.

--- lib/bracket-matcher.coffee
+++ lib/bracket-matcher.coffee
@@ -50,12 +50,23 @@
     else if previousPosition.isEqual(endPosition)
       editor.setCursorBufferPosition(startPosition)

-  createView: (editor, bufferPosition) ->
-    pixelPosition = editor.pixelPositionForBufferPosition(bufferPosition)
+  addHighlightViews: (editor, bufferRange) ->
+    { start, end } = Range.fromObject(bufferRange)
+    startPixelPosition = editor.pixelPositionForBufferPosition(start)
+    endPixelPosition = editor.pixelPositionForBufferPosition(end)
+    @addHighlightViewAtPixelPosition(editor, bufferPosition: start, pixelPosition: startPixelPosition)
+    @addHighlightViewAtPixelPosition(editor, bufferPosition: end, pixelPosition: endPixelPosition)
+
+  addHighlightViewAtPixelPosition: (editor, {bufferPosition, pixelPosition}) ->
     view = $$ -> @div class: 'bracket-matcher'
     view.data('bufferPosition', bufferPosition)
-    view.css('top', pixelPosition.top).css('left', pixelPosition.left)
-    view.width(editor.charWidth).height(editor.lineHeight)
+    view.css(
+      top: pixelPosition.top
+      left: pixelPosition.left
+      width: editor.charWidth
+      height: editor.lineHeight
+    )
+    editor.underlayer.append(view)

   findCurrentPair: (editor, buffer, matches) ->
     position = editor.getCursorBufferPosition()
@@ -117,12 +128,7 @@
         matchPosition = @findMatchingStartPair(buffer, position, matchingPair, currentPair)

     if position? and matchPosition?
-      if position.isLessThan(matchPosition)
-        underlayer.append(@createView(editor, position))
-        underlayer.append(@createView(editor, matchPosition))
-      else
-        underlayer.append(@createView(editor, matchPosition))
-        underlayer.append(@createView(editor, position))
+      @addHighlightViews(editor, [position, matchPosition])
       @pairHighlighted = true

   subscribeToEditSession: (editSession) ->


fix unmounting of graphs

fix unmounting of graphs

--- app/graphing.coffee
+++ app/graphing.coffee
@@ -213,7 +213,7 @@
         @setState {error}
         graph.draw(data, params)
     componentWillUnmount: ->
-      if @state
+      if @state.graph?
         @state.graph.destroy()
         @state.unsubscribe()


Removing debug console.log call. Changing fetcher instantiation to CJS signature following POLVO schema. Removing unnecessary Factory`s namespace assignment.

--- www/src/theoricus/mvc/model.coffee
+++ www/src/theoricus/mvc/model.coffee
@@ -94,9 +94,9 @@
   @param [Object] data  Data to be send
   ###
   @_request = ( method, url, data='' ) ->
-    console.log "[Model] request", method, url, data
+    # console.log "[Model] request", method, url, data

-    fetcher = new theoricus.mvc.lib.Fetcher
+    fetcher = new Fetcher

     req =
       url  : url
@@ -120,7 +120,7 @@
       if fetcher.onerror?
         fetcher.onerror error
       else
-          console.log error
+          console.error error

     fetcher
@@ -169,7 +169,6 @@
   @param [Object] data  Data to be parsed
   ###
   @_instantiate = ( data ) ->
-    Factory = theoricus.core.Factory
     classname = ("#{@}".match /function\s(\w+)/)[1]
     records = []
     for record in [].concat data


Fix setting value

Fix setting value

--- packages/rocketchat-lib/server/startup/settings.coffee
+++ packages/rocketchat-lib/server/startup/settings.coffee
@@ -89,8 +89,8 @@
 		@add 'Accounts_OAuth_Twitter_callback_url', '_oauth/twitter', { type: 'relativeUrl', readonly: true, force: true, enableQuery: enableQuery }

 	@section 'Proxy', ->
-		@add 'Accounts_OAuth_Proxy_host', '', { type: 'string', public: true }
-		@add 'Accounts_OAuth_Proxy_services', 'https://oauth-proxy.rocket.chat', { type: 'string', public: true }
+		@add 'Accounts_OAuth_Proxy_host', 'https://oauth-proxy.rocket.chat', { type: 'string', public: true }
+		@add 'Accounts_OAuth_Proxy_services', '', { type: 'string', public: true }

 RocketChat.settings.addGroup 'General', ->


remove comment

remove comment

--- packages/rocketchat-ui-sidenav/side-nav/sideNav.coffee
+++ packages/rocketchat-ui-sidenav/side-nav/sideNav.coffee
@@ -95,7 +95,6 @@
 	wrapper = $('.rooms-list .wrapper').get(0)
 	lastLink = $('.rooms-list h3.history-div').get(0)

-	# @TODO validate role/permission inside roomTypes object
 	RocketChat.roomTypes.getTypes().forEach (roomType) ->
 		if Template[roomType.template]?
 			Blaze.render Template[roomType.template], wrapper, lastLink


Prune language list

Prune language list

--- app/lib/util.coffee
+++ app/lib/util.coffee
@@ -8,273 +8,273 @@

   locales: [
     { code: 'af', name: 'Afrikaans' }
-    { code: 'af-za', name: 'Afrikaans (South Africa)' }
+    # { code: 'af-za', name: 'Afrikaans (South Africa)' }
     { code: 'sq', name: 'Albanian' }
-    { code: 'sq-al', name: 'Albanian (Albania)' }
+    # { code: 'sq-al', name: 'Albanian (Albania)' }
     { code: 'gsw-fr', name: 'Alsatian (France)' }
     { code: 'am-et', name: 'Amharic (Ethiopia)' }
     { code: 'ar', name: 'Arabic' }
-    { code: 'ar-dz', name: 'Arabic (Algeria)' }
-    { code: 'ar-bh', name: 'Arabic (Bahrain)' }
-    { code: 'ar-eg', name: 'Arabic (Egypt)' }
-    { code: 'ar-iq', name: 'Arabic (Iraq)' }
-    { code: 'ar-jo', name: 'Arabic (Jordan)' }
-    { code: 'ar-kw', name: 'Arabic (Kuwait)' }
-    { code: 'ar-lb', name: 'Arabic (Lebanon)' }
-    { code: 'ar-ly', name: 'Arabic (Libya)' }
-    { code: 'ar-ma', name: 'Arabic (Morocco)' }
-    { code: 'ar-om', name: 'Arabic (Oman)' }
-    { code: 'ar-qa', name: 'Arabic (Qatar)' }
-    { code: 'ar-sa', name: 'Arabic (Saudi Arabia)' }
-    { code: 'ar-sy', name: 'Arabic (Syria)' }
-    { code: 'ar-tn', name: 'Arabic (Tunisia)' }
-    { code: 'ar-ae', name: 'Arabic (U.A.E.)' }
-    { code: 'ar-ye', name: 'Arabic (Yemen)' }
+    # { code: 'ar-dz', name: 'Arabic (Algeria)' }
+    # { code: 'ar-bh', name: 'Arabic (Bahrain)' }
+    # { code: 'ar-eg', name: 'Arabic (Egypt)' }
+    # { code: 'ar-iq', name: 'Arabic (Iraq)' }
+    # { code: 'ar-jo', name: 'Arabic (Jordan)' }
+    # { code: 'ar-kw', name: 'Arabic (Kuwait)' }
+    # { code: 'ar-lb', name: 'Arabic (Lebanon)' }
+    # { code: 'ar-ly', name: 'Arabic (Libya)' }
+    # { code: 'ar-ma', name: 'Arabic (Morocco)' }
+    # { code: 'ar-om', name: 'Arabic (Oman)' }
+    # { code: 'ar-qa', name: 'Arabic (Qatar)' }
+    # { code: 'ar-sa', name: 'Arabic (Saudi Arabia)' }
+    # { code: 'ar-sy', name: 'Arabic (Syria)' }
+    # { code: 'ar-tn', name: 'Arabic (Tunisia)' }
+    # { code: 'ar-ae', name: 'Arabic (U.A.E.)' }
+    # { code: 'ar-ye', name: 'Arabic (Yemen)' }
     { code: 'hy', name: 'Armenian' }
-    { code: 'hy-am', name: 'Armenian (Armenia)' }
+    # { code: 'hy-am', name: 'Armenian (Armenia)' }
     { code: 'as-in', name: 'Assamese (India)' }
     { code: 'az', name: 'Azeri' }
-    { code: 'az-cyrl-az', name: 'Azeri (Cyrillic, Azerbaijan)' }
-    { code: 'az-latn-az', name: 'Azeri (Latin, Azerbaijan)' }
+    # { code: 'az-cyrl-az', name: 'Azeri (Cyrillic, Azerbaijan)' }
+    # { code: 'az-latn-az', name: 'Azeri (Latin, Azerbaijan)' }
     { code: 'ba-ru', name: 'Bashkir (Russia)' }
     { code: 'eu', name: 'Basque' }
-    { code: 'eu-es', name: 'Basque (Basque)' }
+    # { code: 'eu-es', name: 'Basque (Basque)' }
     { code: 'be', name: 'Belarusian' }
-    { code: 'be-by', name: 'Belarusian (Belarus)' }
+    # { code: 'be-by', name: 'Belarusian (Belarus)' }
     { code: 'bn-bd', name: 'Bengali (Bangladesh)' }
     { code: 'bn-in', name: 'Bengali (India)' }
     { code: 'bs-cyrl-ba', name: 'Bosnian (Cyrillic, Bosnia and Herzegovina)' }
     { code: 'bs-latn-ba', name: 'Bosnian (Latin, Bosnia and Herzegovina)' }
     { code: 'br-fr', name: 'Breton (France)' }
     { code: 'bg', name: 'Bulgarian' }
-    { code: 'bg-bg', name: 'Bulgarian (Bulgaria)' }
+    # { code: 'bg-bg', name: 'Bulgarian (Bulgaria)' }
     { code: 'ca', name: 'Catalan' }
-    { code: 'ca-es', name: 'Catalan (Catalan)' }
-    { code: 'zh-hk', name: 'Chinese (Hong Kong S.A.R.)' }
-    { code: 'zh-mo', name: 'Chinese (Macao S.A.R.)' }
-    { code: 'zh-cn', name: 'Chinese (Peoples Republic of China)s Republic of China)' }
+    # { code: 'ca-es', name: 'Catalan (Catalan)' }
+    # { code: 'zh-hk', name: 'Chinese (Hong Kong S.A.R.)' }
+    # { code: 'zh-mo', name: 'Chinese (Macao S.A.R.)' }
+    # { code: 'zh-cn', name: 'Chinese (Peoples Republic of China)s Republic of China)' }
     { code: 'zh-hans', name: 'Chinese (Simplified)' }
-    { code: 'zh-sg', name: 'Chinese (Singapore)' }
-    { code: 'zh-tw', name: 'Chinese (Taiwan)' }
+    # { code: 'zh-sg', name: 'Chinese (Singapore)' }
+    # { code: 'zh-tw', name: 'Chinese (Taiwan)' }
     { code: 'zh-hant', name: 'Chinese (Traditional)' }
-    { code: 'co-fr', name: 'Corsican (France)' }
+    # { code: 'co-fr', name: 'Corsican (France)' }
     { code: 'hr', name: 'Croatian' }
-    { code: 'hr-hr', name: 'Croatian (Croatia)' }
-    { code: 'hr-ba', name: 'Croatian (Latin, Bosnia and Herzegovina)' }
+    # { code: 'hr-hr', name: 'Croatian (Croatia)' }
+    # { code: 'hr-ba', name: 'Croatian (Latin, Bosnia and Herzegovina)' }
     { code: 'cs', name: 'Czech' }
-    { code: 'cs-cz', name: 'Czech (Czech Republic)' }
+    # { code: 'cs-cz', name: 'Czech (Czech Republic)' }
     { code: 'da', name: 'Danish' }
-    { code: 'da-dk', name: 'Danish (Denmark)' }
+    # { code: 'da-dk', name: 'Danish (Denmark)' }
     { code: 'prs-af', name: 'Dari (Afghanistan)' }
     { code: 'div', name: 'Divehi' }
-    { code: 'div-mv', name: 'Divehi (Maldives)' }
+    # { code: 'div-mv', name: 'Divehi (Maldives)' }
     { code: 'nl', name: 'Dutch' }
-    { code: 'nl-be', name: 'Dutch (Belgium)' }
-    { code: 'nl-nl', name: 'Dutch (Netherlands)' }
+    # { code: 'nl-be', name: 'Dutch (Belgium)' }
+    # { code: 'nl-nl', name: 'Dutch (Netherlands)' }
     { code: 'en', name: 'English' }
-    { code: 'en-au', name: 'English (Australia)' }
-    { code: 'en-bz', name: 'English (Belize)' }
-    { code: 'en-ca', name: 'English (Canada)' }
-    { code: 'en-029', name: 'English (Caribbean)' }
-    { code: 'en-in', name: 'English (India)' }
-    { code: 'en-ie', name: 'English (Ireland)' }
-    { code: 'en-jm', name: 'English (Jamaica)' }
-    { code: 'en-my', name: 'English (Malaysia)' }
-    { code: 'en-nz', name: 'English (New Zealand)' }
-    { code: 'en-ph', name: 'English (Republic of the Philippines)' }
-    { code: 'en-sg', name: 'English (Singapore)' }
-    { code: 'en-za', name: 'English (South Africa)' }
-    { code: 'en-tt', name: 'English (Trinidad and Tobago)' }
-    { code: 'en-gb', name: 'English (United Kingdom)' }
-    { code: 'en-us', name: 'English (United States)' }
-    { code: 'en-zw', name: 'English (Zimbabwe)' }
+    # { code: 'en-au', name: 'English (Australia)' }
+    # { code: 'en-bz', name: 'English (Belize)' }
+    # { code: 'en-ca', name: 'English (Canada)' }
+    # { code: 'en-029', name: 'English (Caribbean)' }
+    # { code: 'en-in', name: 'English (India)' }
+    # { code: 'en-ie', name: 'English (Ireland)' }
+    # { code: 'en-jm', name: 'English (Jamaica)' }
+    # { code: 'en-my', name: 'English (Malaysia)' }
+    # { code: 'en-nz', name: 'English (New Zealand)' }
+    # { code: 'en-ph', name: 'English (Republic of the Philippines)' }
+    # { code: 'en-sg', name: 'English (Singapore)' }
+    # { code: 'en-za', name: 'English (South Africa)' }
+    # { code: 'en-tt', name: 'English (Trinidad and Tobago)' }
+    # { code: 'en-gb', name: 'English (United Kingdom)' }
+    # { code: 'en-us', name: 'English (United States)' }
+    # { code: 'en-zw', name: 'English (Zimbabwe)' }
     { code: 'et', name: 'Estonian' }
-    { code: 'et-ee', name: 'Estonian (Estonia)' }
+    # { code: 'et-ee', name: 'Estonian (Estonia)' }
     { code: 'fo', name: 'Faroese' }
-    { code: 'fo-fo', name: 'Faroese (Faroe Islands)' }
+    # { code: 'fo-fo', name: 'Faroese (Faroe Islands)' }
     { code: 'fil-ph', name: 'Filipino (Philippines)' }
     { code: 'fi', name: 'Finnish' }
-    { code: 'fi-fi', name: 'Finnish (Finland)' }
+    # { code: 'fi-fi', name: 'Finnish (Finland)' }
     { code: 'fr', name: 'French' }
-    { code: 'fr-be', name: 'French (Belgium)' }
-    { code: 'fr-ca', name: 'French (Canada)' }
-    { code: 'fr-fr', name: 'French (France)' }
-    { code: 'fr-lu', name: 'French (Luxembourg)' }
-    { code: 'fr-mc', name: 'French (Principality of Monaco)' }
-    { code: 'fr-ch', name: 'French (Switzerland)' }
-    { code: 'fy-nl', name: 'Frisian (Netherlands)' }
+    # { code: 'fr-be', name: 'French (Belgium)' }
+    # { code: 'fr-ca', name: 'French (Canada)' }
+    # { code: 'fr-fr', name: 'French (France)' }
+    # { code: 'fr-lu', name: 'French (Luxembourg)' }
+    # { code: 'fr-mc', name: 'French (Principality of Monaco)' }
+    # { code: 'fr-ch', name: 'French (Switzerland)' }
+    # { code: 'fy-nl', name: 'Frisian (Netherlands)' }
     { code: 'gl', name: 'Galician' }
-    { code: 'gl-es', name: 'Galician (Galician)' }
+    # { code: 'gl-es', name: 'Galician (Galician)' }
     { code: 'ka', name: 'Georgian' }
-    { code: 'ka-ge', name: 'Georgian (Georgia)' }
+    # { code: 'ka-ge', name: 'Georgian (Georgia)' }
     { code: 'de', name: 'German' }
-    { code: 'de-at', name: 'German (Austria)' }
-    { code: 'de-de', name: 'German (Germany)' }
-    { code: 'de-li', name: 'German (Liechtenstein)' }
-    { code: 'de-lu', name: 'German (Luxembourg)' }
-    { code: 'de-ch', name: 'German (Switzerland)' }
+    # { code: 'de-at', name: 'German (Austria)' }
+    # { code: 'de-de', name: 'German (Germany)' }
+    # { code: 'de-li', name: 'German (Liechtenstein)' }
+    # { code: 'de-lu', name: 'German (Luxembourg)' }
+    # { code: 'de-ch', name: 'German (Switzerland)' }
     { code: 'el', name: 'Greek' }
-    { code: 'el-gr', name: 'Greek (Greece)' }
+    # { code: 'el-gr', name: 'Greek (Greece)' }
     { code: 'kl-gl', name: 'Greenlandic (Greenland)' }
     { code: 'gu', name: 'Gujarati' }
-    { code: 'gu-in', name: 'Gujarati (India)' }
+    # { code: 'gu-in', name: 'Gujarati (India)' }
     { code: 'ha-latn-ng', name: 'Hausa (Latin, Nigeria)' }
     { code: 'he', name: 'Hebrew' }
-    { code: 'he-il', name: 'Hebrew (Israel)' }
+    # { code: 'he-il', name: 'Hebrew (Israel)' }
     { code: 'hi', name: 'Hindi' }
-    { code: 'hi-in', name: 'Hindi (India)' }
+    # { code: 'hi-in', name: 'Hindi (India)' }
     { code: 'hu', name: 'Hungarian' }
-    { code: 'hu-hu', name: 'Hungarian (Hungary)' }
+    # { code: 'hu-hu', name: 'Hungarian (Hungary)' }
     { code: 'is', name: 'Icelandic' }
-    { code: 'is-is', name: 'Icelandic (Iceland)' }
+    # { code: 'is-is', name: 'Icelandic (Iceland)' }
     { code: 'ig-ng', name: 'Igbo (Nigeria)' }
     { code: 'id', name: 'Indonesian' }
-    { code: 'id-id', name: 'Indonesian (Indonesia)' }
+    # { code: 'id-id', name: 'Indonesian (Indonesia)' }
     { code: 'iu-latn-ca', name: 'Inuktitut (Latin, Canada)' }
     { code: 'iu-cans-ca', name: 'Inuktitut (Syllabics, Canada)' }
     { code: 'ga-ie', name: 'Irish (Ireland)' }
     { code: 'xh-za', name: 'isiXhosa (South Africa)' }
     { code: 'zu-za', name: 'isiZulu (South Africa)' }
     { code: 'it', name: 'Italian' }
-    { code: 'it-it', name: 'Italian (Italy)' }
-    { code: 'it-ch', name: 'Italian (Switzerland)' }
+    # { code: 'it-it', name: 'Italian (Italy)' }
+    # { code: 'it-ch', name: 'Italian (Switzerland)' }
     { code: 'ja', name: 'Japanese' }
-    { code: 'ja-jp', name: 'Japanese (Japan)' }
+    # { code: 'ja-jp', name: 'Japanese (Japan)' }
     { code: 'kn', name: 'Kannada' }
-    { code: 'kn-in', name: 'Kannada (India)' }
+    # { code: 'kn-in', name: 'Kannada (India)' }
     { code: 'kk', name: 'Kazakh' }
-    { code: 'kk-kz', name: 'Kazakh (Kazakhstan)' }
+    # { code: 'kk-kz', name: 'Kazakh (Kazakhstan)' }
     { code: 'km-kh', name: 'Khmer (Cambodia)' }
     { code: 'qut-gt', name: 'K' }
     { code: 'rw-rw', name: 'Kinyarwanda (Rwanda)' }
     { code: 'sw', name: 'Kiswahili' }
-    { code: 'sw-ke', name: 'Kiswahili (Kenya)' }
+    # { code: 'sw-ke', name: 'Kiswahili (Kenya)' }
     { code: 'kok', name: 'Konkani' }
-    { code: 'kok-in', name: 'Konkani (India)' }
+    # { code: 'kok-in', name: 'Konkani (India)' }
     { code: 'ko', name: 'Korean' }
-    { code: 'ko-kr', name: 'Korean (Korea)' }
+    # { code: 'ko-kr', name: 'Korean (Korea)' }
     { code: 'ky', name: 'Kyrgyz' }
-    { code: 'ky-kg', name: 'Kyrgyz (Kyrgyzstan)' }
+    # { code: 'ky-kg', name: 'Kyrgyz (Kyrgyzstan)' }
     { code: 'lo-la', name: 'Lao (Lao P.D.R.)' }
     { code: 'lv', name: 'Latvian' }
-    { code: 'lv-lv', name: 'Latvian (Latvia)' }
+    # { code: 'lv-lv', name: 'Latvian (Latvia)' }
     { code: 'lt', name: 'Lithuanian' }
-    { code: 'lt-lt', name: 'Lithuanian (Lithuania)' }
+    # { code: 'lt-lt', name: 'Lithuanian (Lithuania)' }
     { code: 'wee-de', name: 'Lower Sorbian (Germany)' }
     { code: 'lb-lu', name: 'Luxembourgish (Luxembourg)' }
     { code: 'mk', name: 'Macedonian' }
-    { code: 'mk-mk', name: 'Macedonian (Former Yugoslav Republic of Macedonia)' }
+    # { code: 'mk-mk', name: 'Macedonian (Former Yugoslav Republic of Macedonia)' }
     { code: 'ms', name: 'Malay' }
-    { code: 'ms-bn', name: 'Malay (Brunei Darussalam)' }
-    { code: 'ms-my', name: 'Malay (Malaysia)' }
+    # { code: 'ms-bn', name: 'Malay (Brunei Darussalam)' }
+    # { code: 'ms-my', name: 'Malay (Malaysia)' }
     { code: 'ml-in', name: 'Malayalam (India)' }
     { code: 'mt-mt', name: 'Maltese (Malta)' }
     { code: 'mi-nz', name: 'Maori (New Zealand)' }
     { code: 'arn-cl', name: 'Mapudungun (Chile)' }
     { code: 'mr', name: 'Marathi' }
-    { code: 'mr-in', name: 'Marathi (India)' }
+    # { code: 'mr-in', name: 'Marathi (India)' }
     { code: 'moh-ca', name: 'Mohawk (Mohawk)' }
     { code: 'mn', name: 'Mongolian' }
-    { code: 'mn-mn', name: 'Mongolian (Cyrillic, Mongolia)' }
-    { code: 'mn-mong-cn', name: 'Mongolian (Traditional Mongolian, PRC)' }
+    # { code: 'mn-mn', name: 'Mongolian (Cyrillic, Mongolia)' }
+    # { code: 'mn-mong-cn', name: 'Mongolian (Traditional Mongolian, PRC)' }
     { code: 'ne-np', name: 'Nepali (Nepal)' }
     { code: 'no', name: 'Norwegian' }
-    { code: 'nb-no', name: 'Norwegian' }
-    { code: 'nn-no', name: 'Norwegian' }
+    # { code: 'nb-no', name: 'Norwegian' }
+    # { code: 'nn-no', name: 'Norwegian' }
     { code: 'oc-fr', name: 'Occitan (France)' }
     { code: 'or-in', name: 'Oriya (India)' }
     { code: 'ps-af', name: 'Pashto (Afghanistan)' }
     { code: 'fa', name: 'Persian' }
-    { code: 'fa-ir', name: 'Persian' }
+    # { code: 'fa-ir', name: 'Persian' }
     { code: 'pl', name: 'Polish' }
-    { code: 'pl-pl', name: 'Polish (Poland)' }
+    # { code: 'pl-pl', name: 'Polish (Poland)' }
     { code: 'pt', name: 'Portuguese' }
-    { code: 'pt-br', name: 'Portuguese (Brazil)' }
-    { code: 'pt-pt', name: 'Portuguese (Portugal)' }
+    # { code: 'pt-br', name: 'Portuguese (Brazil)' }
+    # { code: 'pt-pt', name: 'Portuguese (Portugal)' }
     { code: 'pa', name: 'Punjabi' }
-    { code: 'pa-in', name: 'Punjabi (India)' }
+    # { code: 'pa-in', name: 'Punjabi (India)' }
     { code: 'quz-bo', name: 'Quechua (Bolivia)' }
     { code: 'quz-ec', name: 'Quechua (Ecuador)' }
     { code: 'quz-pe', name: 'Quechua (Peru)' }
     { code: 'ro', name: 'Romanian' }
-    { code: 'ro-ro', name: 'Romanian (Romania)' }
+    # { code: 'ro-ro', name: 'Romanian (Romania)' }
     { code: 'rm-ch', name: 'Romansh (Switzerland)' }
     { code: 'ru', name: 'Russian' }
-    { code: 'ru-ru', name: 'Russian (Russia)' }
-    { code: 'smn-fi', name: 'Sami' }
-    { code: 'smj-no', name: 'Sami' }
-    { code: 'smj-se', name: 'Sami' }
-    { code: 'se-fi', name: 'Sami' }
-    { code: 'se-no', name: 'Sami' }
-    { code: 'se-se', name: 'Sami' }
-    { code: 'sms-fi', name: 'Sami' }
-    { code: 'sma-no', name: 'Sami' }
-    { code: 'sma-se', name: 'Sami' }
+    # { code: 'ru-ru', name: 'Russian (Russia)' }
+    # { code: 'smn-fi', name: 'Sami' }
+    # { code: 'smj-no', name: 'Sami' }
+    # { code: 'smj-se', name: 'Sami' }
+    # { code: 'se-fi', name: 'Sami' }
+    # { code: 'se-no', name: 'Sami' }
+    # { code: 'se-se', name: 'Sami' }
+    # { code: 'sms-fi', name: 'Sami' }
+    # { code: 'sma-no', name: 'Sami' }
+    # { code: 'sma-se', name: 'Sami' }
     { code: 'sa', name: 'Sanskrit' }
-    { code: 'sa-in', name: 'Sanskrit (India)' }
+    # { code: 'sa-in', name: 'Sanskrit (India)' }
     { code: 'sr', name: 'Serbian' }
-    { code: 'sr-cyrl-ba', name: 'Serbian (Cyrillic, Bosnia and Herzegovina)' }
-    { code: 'sr-cyrl-sp', name: 'Serbian (Cyrillic, Serbia)' }
-    { code: 'sr-latn-ba', name: 'Serbian (Latin, Bosnia and Herzegovina)' }
-    { code: 'sr-latn-sp', name: 'Serbian (Latin, Serbia)' }
+    # { code: 'sr-cyrl-ba', name: 'Serbian (Cyrillic, Bosnia and Herzegovina)' }
+    # { code: 'sr-cyrl-sp', name: 'Serbian (Cyrillic, Serbia)' }
+    # { code: 'sr-latn-ba', name: 'Serbian (Latin, Bosnia and Herzegovina)' }
+    # { code: 'sr-latn-sp', name: 'Serbian (Latin, Serbia)' }
     { code: 'nso-za', name: 'Sesotho sa Leboa (South Africa)' }
     { code: 'tn-za', name: 'Setswana (South Africa)' }
     { code: 'si-lk', name: 'Sinhala (Sri Lanka)' }
     { code: 'sk', name: 'Slovak' }
-    { code: 'sk-sk', name: 'Slovak (Slovakia)' }
+    # { code: 'sk-sk', name: 'Slovak (Slovakia)' }
     { code: 'sl', name: 'Slovenian' }
-    { code: 'sl-si', name: 'Slovenian (Slovenia)' }
+    # { code: 'sl-si', name: 'Slovenian (Slovenia)' }
     { code: 'es', name: 'Spanish' }
-    { code: 'es-ar', name: 'Spanish (Argentina)' }
-    { code: 'es-bo', name: 'Spanish (Bolivia)' }
-    { code: 'es-cl', name: 'Spanish (Chile)' }
-    { code: 'es-co', name: 'Spanish (Colombia)' }
-    { code: 'es-cr', name: 'Spanish (Costa Rica)' }
-    { code: 'es-do', name: 'Spanish (Dominican Republic)' }
-    { code: 'es-ec', name: 'Spanish (Ecuador)' }
-    { code: 'es-sv', name: 'Spanish (El Salvador)' }
-    { code: 'es-gt', name: 'Spanish (Guatemala)' }
-    { code: 'es-hn', name: 'Spanish (Honduras)' }
-    { code: 'es-mx', name: 'Spanish (Mexico)' }
-    { code: 'es-ni', name: 'Spanish (Nicaragua)' }
-    { code: 'es-pa', name: 'Spanish (Panama)' }
-    { code: 'es-py', name: 'Spanish (Paraguay)' }
-    { code: 'es-pe', name: 'Spanish (Peru)' }
-    { code: 'es-pr', name: 'Spanish (Puerto Rico)' }
-    { code: 'es-es', name: 'Spanish (Spain)' }
-    { code: 'es-us', name: 'Spanish (United States)' }
-    { code: 'es-uy', name: 'Spanish (Uruguay)' }
-    { code: 'es-ve', name: 'Spanish (Venezuela)' }
+    # { code: 'es-ar', name: 'Spanish (Argentina)' }
+    # { code: 'es-bo', name: 'Spanish (Bolivia)' }
+    # { code: 'es-cl', name: 'Spanish (Chile)' }
+    # { code: 'es-co', name: 'Spanish (Colombia)' }
+    # { code: 'es-cr', name: 'Spanish (Costa Rica)' }
+    # { code: 'es-do', name: 'Spanish (Dominican Republic)' }
+    # { code: 'es-ec', name: 'Spanish (Ecuador)' }
+    # { code: 'es-sv', name: 'Spanish (El Salvador)' }
+    # { code: 'es-gt', name: 'Spanish (Guatemala)' }
+    # { code: 'es-hn', name: 'Spanish (Honduras)' }
+    # { code: 'es-mx', name: 'Spanish (Mexico)' }
+    # { code: 'es-ni', name: 'Spanish (Nicaragua)' }
+    # { code: 'es-pa', name: 'Spanish (Panama)' }
+    # { code: 'es-py', name: 'Spanish (Paraguay)' }
+    # { code: 'es-pe', name: 'Spanish (Peru)' }
+    # { code: 'es-pr', name: 'Spanish (Puerto Rico)' }
+    # { code: 'es-es', name: 'Spanish (Spain)' }
+    # { code: 'es-us', name: 'Spanish (United States)' }
+    # { code: 'es-uy', name: 'Spanish (Uruguay)' }
+    # { code: 'es-ve', name: 'Spanish (Venezuela)' }
     { code: 'sv', name: 'Swedish' }
-    { code: 'sv-fi', name: 'Swedish (Finland)' }
-    { code: 'sv-se', name: 'Swedish (Sweden)' }
+    # { code: 'sv-fi', name: 'Swedish (Finland)' }
+    # { code: 'sv-se', name: 'Swedish (Sweden)' }
     { code: 'syr', name: 'Syriac' }
-    { code: 'syr-sy', name: 'Syriac (Syria)' }
+    # { code: 'syr-sy', name: 'Syriac (Syria)' }
     { code: 'tg-cyrl-tj', name: 'Tajik (Cyrillic, Tajikistan)' }
     { code: 'tzm-latn-dz', name: 'Tamazight (Latin, Algeria)' }
     { code: 'ta', name: 'Tamil' }
-    { code: 'ta-in', name: 'Tamil (India)' }
+    # { code: 'ta-in', name: 'Tamil (India)' }
     { code: 'tt', name: 'Tatar' }
-    { code: 'tt-ru', name: 'Tatar (Russia)' }
+    # { code: 'tt-ru', name: 'Tatar (Russia)' }
     { code: 'te', name: 'Telugu' }
-    { code: 'te-in', name: 'Telugu (India)' }
+    # { code: 'te-in', name: 'Telugu (India)' }
     { code: 'th', name: 'Thai' }
-    { code: 'th-th', name: 'Thai (Thailand)' }
+    # { code: 'th-th', name: 'Thai (Thailand)' }
     { code: 'bo-cn', name: 'Tibetan (PRC)' }
     { code: 'tr', name: 'Turkish' }
-    { code: 'tr-tr', name: 'Turkish (Turkey)' }
+    # { code: 'tr-tr', name: 'Turkish (Turkey)' }
     { code: 'tk-tm', name: 'Turkmen (Turkmenistan)' }
     { code: 'ug-cn', name: 'Uighur (PRC)' }
     { code: 'uk', name: 'Ukrainian' }
-    { code: 'uk-ua', name: 'Ukrainian (Ukraine)' }
+    # { code: 'uk-ua', name: 'Ukrainian (Ukraine)' }
     { code: 'wen-de', name: 'Upper Sorbian (Germany)' }
     { code: 'ur', name: 'Urdu' }
-    { code: 'ur-pk', name: 'Urdu (Islamic Republic of Pakistan)' }
+    # { code: 'ur-pk', name: 'Urdu (Islamic Republic of Pakistan)' }
     { code: 'uz', name: 'Uzbek' }
-    { code: 'uz-cyrl-uz', name: 'Uzbek (Cyrillic, Uzbekistan)' }
-    { code: 'uz-latn-uz', name: 'Uzbek (Latin, Uzbekistan)' }
+    # { code: 'uz-cyrl-uz', name: 'Uzbek (Cyrillic, Uzbekistan)' }
+    # { code: 'uz-latn-uz', name: 'Uzbek (Latin, Uzbekistan)' }
     { code: 'vi', name: 'Vietnamese' }
-    { code: 'vi-vn', name: 'Vietnamese (Vietnam)' }
+    # { code: 'vi-vn', name: 'Vietnamese (Vietnam)' }
     { code: 'cy-gb', name: 'Welsh (United Kingdom)' }
     { code: 'wo-sn', name: 'Wolof (Senegal)' }
     { code: 'sah-ru', name: 'Yakut (Russia)' }


:shirt::pray:

:shirt::pray:

--- spec/updates-panel-spec.coffee
+++ spec/updates-panel-spec.coffee
@@ -2,7 +2,7 @@
 PackageManager = require '../lib/package-manager'
 SettingsView = require '../lib/settings-view'

-describe 'UpdatesPanel', ->
+fdescribe 'UpdatesPanel', ->
   panel = null
   settingsView = null
   packageManager = null
@@ -111,7 +111,7 @@
       resolveC()

       waitsFor ->
-        panel.refs.updateAllButton.style.display === 'none'
+        panel.refs.updateAllButton.style.display is 'none'

     it 'becomes hidden if all updates succeed', ->
       expect(panel.refs.updateAllButton).not.toBeDisabled()
@@ -125,7 +125,7 @@
       resolveC()

       waitsFor ->
-        panel.refs.updateAllButton.style.display === 'none'
+        panel.refs.updateAllButton.style.display is 'none'

     it 'remains enabled and visible if not all updates succeed', ->
       panel.updateAll()


Write "one project" when only one project is tracked

It's actually writing "projects" regardless if it's only one project tracked

--- lib/views/dashboard.coffee
+++ lib/views/dashboard.coffee
@@ -35,10 +35,12 @@
             if currentProjects.length is 0
                 # Set the content to no time tracking
                 @message.text( "You are currently not tracking time for any projects! :(" )
-            else
+            else if currentProjects.length is 1
+                # We have one project, show the message
+                @message.text( "You are currently tracking time for one project" )
+            else
                 # We have some projects, show the message
                 @message.text( "You are currently tracking time for #{currentProjects.length} projects" )
-
             # Add the available projects to the summary list
             if currentProjects.length > 0
                 # Display Recent Projects


There is extra whitespace here.

There is extra whitespace here.

--- test/unit/mock.coffee
+++ test/unit/mock.coffee
@@ -37,7 +37,6 @@

     stub.reset()

-
   it "uses a sinon spy", ->
     called = false
     obj = method: ->


getSelectedObject as method call set

getSelectedObject as method call set

--- src/controller.main.coffee
+++ src/controller.main.coffee
@@ -52,6 +52,7 @@
   c.log.addListener mode, (txt) ->
     c.addLog "#{c.GUILogs.length+1}| #{txt}"

+
 vueSettings = Vue.extend({
   name: 'settings'
   template: '#settings-template'
@@ -86,7 +87,6 @@
       this.logs = c.GUILogs
   })

-
 Vue.component "settings", vueSettings
 Vue.component "logs", vueLogs

@@ -96,17 +96,17 @@
   data:
     t: t
     folders: dfc.getObjects()
-    active: dfc.getSelectedObject
+    active: dfc.getSelectedObject()
     activeIndex: dfc.selectedObjectIndex
     activeTab: 'files'
   events:
     'active': ->
       this.$broadcast 'active'
-      this.active = dfc.getSelectedObject
+      this.active = dfc.getSelectedObject()
       c.log.debug "active event in root"
     'refreshRoot': ->
       @folders = dfc.getObjects()
-      @active = dfc.getSelectedObject
+      @active = dfc.getSelectedObject()
       @activeIndex = dfc.selectedObjectIndex
   methods:
     'tabClick': (val) ->


Remove now-unused volume summary categories generation

--- web/volume/overview.coffee
+++ web/volume/overview.coffee
@@ -12,7 +12,6 @@
         agesum: 0
         ages: 0
         participants: 0
-        categories: {condition:[], group:[], task:[]}

       Participant = constants.categoryName.participant
       Gender = Participant.metricName.gender.id
@@ -38,10 +37,6 @@
               summary.genders[g]++
             else
               summary.genders[g] = 1
-        else
-          cat = constants.category[r.category]
-          if (l = volume.summary.categories[cat.name]) && (i = r.measures[cat.metricName.name.id])
-            l.push(i)

     {
     restrict: 'E'


Fixed task names to be 'T1' instead of 'survey'

--- app/stores/classification-store.coffee
+++ app/stores/classification-store.coffee
@@ -38,8 +38,8 @@

   finish: ->
     annotations = _.map annotationsStore.data, (annotation) ->
-      task: 'survey'
-      value: annotation
+      task: 'T1'
+      value: [annotation]

     upsert =
       annotations: annotations


Updating default atom

Updating default atom

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -1,7 +1,7 @@
 "*":
   "atom-beautify":
+    _analyticsUserId: "XXX"
     analytics: false
-    _analyticsUserId: "XXX"
   core:
     disabledPackages: [
       "background-tips"
@@ -44,5 +44,6 @@
     ]
   tabs:
     usePreviewTabs: true
+  "tree-view": {}
   welcome:
     showOnStartup: false


add a thing about blade language comments

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -61,6 +61,10 @@
     themeColor: "Purple"
   welcome:
     showOnStartup: false
+".blade.html.php.text":
+  editor:
+    commentEnd: " --}}"
+    commentStart: "{{-- "
 ".hyperlink.text":
   editor:
     softWrapHangingIndent: 4


remove horrible prompt

remove horrible prompt

--- atom.symlink/keymap.cson
+++ atom.symlink/keymap.cson
@@ -1,6 +1,7 @@
 'body':
   # reveal current file in tree view
   'cmd-i': 'tree-view:reveal-active-file'
+

   # go to line prompt
   'cmd-m': 'go-to-line:toggle'
@@ -13,6 +14,9 @@
   'alt-shift-k': 'editor:select-to-next-word-boundary'

   'cmd-o': 'rails-transporter:open-view'
+
+  # remove horrible cmd-shift-o window prompt
+  'cmd-shift-o': 'unset!'

 'atom-workspace atom-text-editor:not([mini])':
   'cmd-j': 'unset!'


jupyter_editor -- I noticed a traceback live that resulted in a blanked screen

--- src/smc-webapp/editor_jupyter.coffee
+++ src/smc-webapp/editor_jupyter.coffee
@@ -660,6 +660,8 @@
         return stringify(obj)

     save_blob: (blob, to_db) =>
+        if not @blobs?
+            return
         id = misc.uuidsha1(blob)
         if @blobs[id]
             return id
@@ -680,6 +682,8 @@
         return id

     load_blob: (id) =>
+        if not @blobs?
+            return
         blob = @blobs[id]
         if blob?
             return blob
@@ -692,7 +696,7 @@
                         id   : id
                         blob : null
                 cb: (err, resp) =>
-                    if @state == 'closed'
+                    if @state == 'closed' or not @blobs?
                         return
                     delete @blobs_pending[id]
                     if err


A (kind of sucky) first implementation of votes importing.

--- jobs/import-votes.coffee
+++ jobs/import-votes.coffee
@@ -22,8 +22,66 @@

 votesSvcUri = "./wsdl/Votes.svc.xml"

-module.exports = (jobs) ->
+mapVoteMemberIds = (session, votesCast, db, callback) ->
+  db.collection("members").find({sessions: session._id}).toArray (err, results) -> ifSuccessful err, callback, ->
+    for voteType, assemblyMemberIds of votesCast
+      votesCast[voteType] = assemblyMemberIds.map (assembyMemberId) ->
+        matchingMember = results.filter (candidateMember) -> candidateMember.assemblyId == assembyMemberId
+        matchingMember[0]?._id
+
+    callback(votesCast)
+
+persistVote = (session, vote, db, callback) ->
+  assemblyIdForVote = vote.VoteId
+
+  votesCast =
+    yea: [],
+    nay: [],
+    notvoting: [],
+    excused: [],
+    unknown: []
+
+  vote.Votes?.MemberVote.forEach (memberVote) ->
+    voteName = memberVote.MemberVoted.toLowerCase()
+    votesCast[voteName].push(memberVote.Member.Id)
+
+  mapVoteMemberIds session, votesCast, db, (votes) ->
+    voteDetails =
+      sessionId: session._id,
+      voteNumber: Number(vote.Number),
+      dateTime: vote.Date,
+      description: vote.Description,
+      chamber: vote.Branch.toLowerCase(),
+      votes: votes
+
+    db.collection("votes").update
+      assemblyId: assemblyIdForVote
+    ,
+      "$set": voteDetails
+    ,
+      upsert: true,
+      safe: true
+    , (err, doc) ->
+      ifSuccessful err, callback, ->
+        callback()
+
+module.exports = (jobs, db) ->
   jobs.process 'import votes', (job, callback) ->
     soap.createClient votesSvcUri, (err, client) -> ifSuccessful err, callback, ->
-      console.log client.describe()
-      callback()
+      db.collection("sessions").find().toArray (err, results) -> ifSuccessful err, callback, ->
+        results.forEach (session) ->
+          getVotesArgs =
+            SessionId: session.assemblyId
+
+          client.VoteService.BasicHttpBinding_VoteFinder.GetVotes getVotesArgs, (err, result, raw) -> ifSuccessful err, callback, ->
+            result.GetVotesResult.VoteListing?.forEach? (assemblyVoteSummary) ->
+              if assemblyVoteSummary.Caption != "ATTENDANCE"
+                getVoteArgs =
+                  VoteId: assemblyVoteSummary.VoteId
+
+                client.VoteService.BasicHttpBinding_VoteFinder.GetVote getVoteArgs, (err, result, raw) -> ifSuccessful err, callback, ->
+                  persistVote(session, result.GetVoteResult, db, callback)
+              else
+                persistVote(session, assemblyVoteSummary, db, callback)
+
+            callback()


Mark a collection as changed when adding content to it

--- src/scripts/models/contents/collection.coffee
+++ src/scripts/models/contents/collection.coffee
@@ -54,7 +54,12 @@
     # Proxy Backbone.Collection methods to make this model also work like a Collection
     #

-    add: () -> @get('contents').add(arguments...)
+    add: () ->
+      results = @get('contents').add(arguments...)
+      @set('changed', true)
+
+      return results
+

     create: (models, options = {}) ->
       options = _.extend({


Clicking anywhere on welcome hero triggers modal

--- apps/home/client/hero_unit_view.coffee
+++ apps/home/client/hero_unit_view.coffee
@@ -10,7 +10,7 @@
     'click #home-hero-units-left-arrow': 'onLeftArrow'
     'click #home-hero-units-right-arrow': 'onRightArrow'
     'click .hhu-dot': 'onDot'
-    'click .home-hero-unit-welcome .avant-garde-button': 'signUp'
+    'click .home-hero-unit-welcome': 'signUp'

   initialize: ->
     @$window = $(window)


apply QuestionService to st-app element

apply QuestionService to st-app element

--- src/elements/st-app.coffee
+++ src/elements/st-app.coffee
@@ -1,5 +1,5 @@
 {EventService} = require '../services/event-service'
-{Question} = require '../models/question'
+{QuestionService} = require '../services/question-service'
 {SentenceService} = require '../services/sentence-service'

 class Controller
@@ -10,6 +10,9 @@
   constructor: (@$timeout) ->
     event = EventService.getInstance()
     service = SentenceService.getInstance()
+
+    event.on 'question-service:changed', (questions) =>
+      @$timeout => @questions = questions

     event.on 'sentence-service:changed', (sentences) =>
       @$timeout => @sentences = sentences
@@ -30,8 +33,8 @@
     @translated = null

   generateQuestions: (sentence) ->
-    @questions = @sentences.map (i) ->
-      new Question(i)
+    service = QuestionService.getInstance()
+    service.generateFromSentences @sentences

 module.exports = ->
   bindToController: true


Revert "change"

Revert "change"

This reverts commit 104ce81ebdf41d88acd7fb6f2abf99fbc4eb91df.

--- app.coffee
+++ app.coffee
@@ -1,4 +1,3 @@
-
 CompileController = require "./app/js/CompileController"
 Settings = require "settings-sharelatex"
 logger = require "logger-sharelatex"


Include a+b in srt.

Include a+b in srt.

--- app.coffee
+++ app.coffee
@@ -76,7 +76,9 @@
   transform: (a, b) ->
     @srt = {
       events: ({ts: a*e.ts + b, text: e.text} for e in @rawSrt.events),
-      duration: a*@rawSrt.duration
+      duration: a*@rawSrt.duration,
+      a: a,
+      b: b
     }

   load: ->


new port 5000

new port 5000


Compile comparator as JavaScript instead of CoffeeScript

--- app.coffee
+++ app.coffee
@@ -39,7 +39,6 @@
 crypto = require 'crypto'
 basicAuth = require 'basic-auth'
 escapeRegExp = require 'escape-string-regexp'
-coffeescript = require 'coffee-script'
 debug = (require 'debug')('PackageServer:debug')
 warn = (require 'debug')('PackageServer:warn')
 warn.log = console.warn.bind console
@@ -144,7 +143,7 @@

 	comparison = comparison.replace /\$v(==|<=|>=|<|>)([0-9]+\.[0-9]+\.[0-9]+.-?[0-9]+.[0-9]+)/g, (comparison, operator, v2) -> """(comparatorHelper($v, "#{v2}") #{operator} 0)"""

-	comparator = new Function '$v', 'comparatorHelper', 'return ' + coffeescript.compile comparison, bare: yes
+	comparator = new Function '$v', 'comparatorHelper', 'return ' + comparison

 	($v) ->	comparator $v, comparatorHelper


Fixed wrong path

Fixed wrong path

--- app.coffee
+++ app.coffee
@@ -39,7 +39,7 @@
   res.render "play",
     title: "Play"
     env: env
-    socketServer: req.headers['uri'] or '/' + "socket.io"
+    socketServer: (req.headers['uri'] or '/') + "socket.io"

 # catch 404 and forward to error handler
 app.use (req, res, next) ->


Remove dead code

Remove dead code

--- src/scripts/models/content.coffee
+++ src/scripts/models/content.coffee
@@ -49,7 +49,7 @@
       depth = 0
       page = 1

-      traverse = (o = {}, sub) =>
+      traverse = (o = {}) =>
         for item, index in o.contents
           item.book = @
           item.depth = depth
@@ -64,7 +64,7 @@
             item.subcollection = true
             delete item.id
             depth++
-            traverse(item, true)
+            traverse(item)
           else
             item.page = page++


Remove comments

Remove comments

--- src/scripts/models/content.coffee
+++ src/scripts/models/content.coffee
@@ -193,11 +193,6 @@
       return super(null, options)

     toJSON: (options = {}) ->
-      # FIX: Only calculate tree if specific option is set
-      # FIX: Refactor code, move as much as possible into collection.coffee and node.coffee
-      # FIX: id@version is not being set properly on loaded modules in subcollections
-      # FIX: Saves are currently going to cnxarchive instead of cnxauthoring
-
       results = super(arguments...)

       if options.includeTree


Don't use WorkspaceView in VimCommandModeInputView

--- lib/view-models/vim-command-mode-input-view.coffee
+++ lib/view-models/vim-command-mode-input-view.coffee
@@ -8,8 +8,6 @@
         @subview 'editor', new TextEditorView(mini: true)

   initialize: (@viewModel, opts = {})->
-    @editor.setFontSize(atom.config.get('vim-mode.commandModeInputViewFontSize'))
-
     if opts.class?
       @editorContainer.addClass opts.class

@@ -17,21 +15,11 @@
       @editorContainer.addClass 'hidden-input'

     @singleChar = opts.singleChar
-
     @defaultText = opts.defaultText ? ''

-    unless atom.workspaceView?
-      # We're in test mode. Don't append to anything, just initialize.
-      @focus()
-      @handleEvents()
-      return
-
-    statusBar = atom.workspaceView.find('.status-bar')
-
-    if statusBar.length > 0
-      @insertBefore(statusBar)
-    else
-      atom.workspace.getActivePane().append(@)
+    # If in test mode, don't append to anything, just initialize.
+    if atom.workspace?
+      atom.workspace.addBottomPanel(item: this, priority: 100)

     @focus()
     @handleEvents()
@@ -68,5 +56,4 @@

   remove: =>
     @stopHandlingEvents()
-    atom.workspaceView.focus() if atom.workspaceView?
     super()


:memo: Doc other atom.open options

:memo: Doc other atom.open options

--- src/atom.coffee
+++ src/atom.coffee
@@ -363,7 +363,15 @@
   # a file/folder to open in the new window.
   #
   # options - An {Object} with the following keys:
-  #   :pathsToOpen -  An {Array} of {String} paths to open.
+  #   :pathsToOpen - An {Array} of {String} paths to open.
+  #   :newWindow   - A {Boolean}, true to always open a new window instead of
+  #                  reusing existing windows depending on the paths to open.
+  #   :devMode     - A {Boolean}, true to open the window in development mode.
+  #                  Development mode loads the Atom source from the locally
+  #                  cloned repository and also loads all the packages in
+  #                  ~/.atom/dev/packages
+  #   :safeMode    - A {Boolean}, true to open the window in safe mode. Safe
+  #                  mode prevents installed packages from loading
   open: (options) ->
     ipc.send('open', options)


style change

style change

before:
  => @ switchToLargeView()
after:
  @bound 'switchToLargeView'

--- client/app/Applications/Activity.kdapplication/widgets/statuswidget.coffee
+++ client/app/Applications/Activity.kdapplication/widgets/statuswidget.coffee
@@ -11,7 +11,7 @@
       placeholder   : "What's new #{Encoder.htmlDecode profile.firstName}?"
       name          : 'dummy'
       style         : 'input-with-extras'
-      focus         : => @switchToLargeView()
+      focus         : @bound 'switchToLargeView'
       validate      :
         rules       :
           maxLength : 2000


Open js-links in new tab/window when Ctrl modifier or middle-click (fix #66).

--- app/scripts/directives.coffee
+++ app/scripts/directives.coffee
@@ -52,12 +52,15 @@
   # TODO: create a directive which allows a link on any element.

   # TODO: Mutualize code with `xoClick`.
-  .directive 'xoSref', ($state) ->
+  .directive 'xoSref', ($state, $window) ->
     ($scope, $element, attrs) ->
       current = $element.get(0)
       current.addEventListener(
-        'click'
+        'mouseup'
         (event) ->
+
+          {which: button} = event
+          return unless button is 1 or button is 2

           # Browse all parent elements of the element the event
           # happened to and abort if one of them should handle the
@@ -86,8 +89,12 @@
           state = match[1]
           params = if match[2] then $scope.$eval match[2] else {}

-          # Go to this state.
-          $state.go state, params
+           # Ctrl modifier or middle-button.
+          if event.ctrlKey or button is 2
+            url = $state.href state, params
+            $window.open url
+          else
+            $state.go state, params
         true
       )


get random subjects by default Set subjectGroup to true to use the groups/subjects API endpoint.

--- project.coffee
+++ project.coffee
@@ -87,7 +87,7 @@
 module.exports =
   id: 'illustratedlife'
   background: 'background.jpg'
-  subjectGroup: '54dcebd07b9f99360b000001'
+  subjectGroup: true

   title: 'Science Gossip'
   summary: 'Uncover the history of citizen science'


fix-130

--- app/annotation/summary.cjsx
+++ app/annotation/summary.cjsx
@@ -7,7 +7,7 @@
 steps = require '../lib/steps'

 sites =
-  siteA: ["guinea_foutahdjalon", "guinea_sangaredi", "guineabissau_boe_part1", "guineabissau_boe_part2", "ivorycoast_azagny", "ivorycoast_djouroutou", "ivorycoast_taie", "ivorycoast_tair", "liberia_eastnimba", "liberia_grebo_sala_part1", "liberia_grebo_sala_part2", "liberia_grebo_tasla", "liberia_sapo", "senegal_kayan", "liberia_grebo_part1"]
+  siteA: ["guinea_foutahdjalon", "guinea_sangaredi", "guineabissau_boe_part1", "guineabissau_boe_part2", "ivorycoast_azagny", "ivorycoast_djouroutou", "ivorycoast_taie", "ivorycoast_tair", "liberia_eastnimba", "liberia_grebo_sala_part1", "liberia_grebo_sala_part2", "liberia_grebo_tasla", "liberia_sapo", "senegal_kayan", "liberia_grebo_part1", "taieast_marielyne"]
   siteB: ["nigeria_gashaka"]
   siteC: ["equatorialguinea_nationwide"]
   siteD: ["drc_biliuere", "uganda_budongo", "uganda_bwindi", "uganda_ngogo", "tanzania_ugalla"]


SocialWorker: get redisClient as argument

--- workers/usertracker.coffee
+++ workers/usertracker.coffee
@@ -36,13 +36,8 @@

   allUsers[username] = ''

-module.exports.start = ->
-  redis = require 'redis'
-  redisClient = redis.createClient(
-    KONFIG.monitoringRedis.split(':')[1]
-    KONFIG.monitoringRedis.split(':')[0]
-    {}
-  )
+module.exports.start = (redisClientInstance) ->
+  redisClient = redisClientInstance

   redisClient.on 'error', (err) -> console.log 'redis err', err
   redisClient.on 'connect', -> console.log 'connected to redis'


show correct last answer text

show correct last answer text

--- app/client/game/quiz.coffee
+++ app/client/game/quiz.coffee
@@ -46,9 +46,6 @@

 # methods
 answerQuestion = (idx) ->
-  # save last answer
-  Session.set 'lastAnswer', idx
-
   # pause asset
   audioPlayer().pause()
   $audioPlayer().unbind('timeupdate')
@@ -174,6 +171,13 @@
   # answer question with predefined alternative
   'click .alternative-predefined': (event) ->
     $('.alternative').prop 'disabled', true
+
+    # save last answer
+    answer = _.find(currentQuestion().alternatives,
+        (a) -> a.name == event.target.id)
+
+    Session.set 'lastAnswer', answer.text
+
     Meteor.call 'stopQuestion',
       currentGameId(), event.target.id, (err, result) ->
         if err?


remove web socket references

remove web socket references

--- assets/front.coffee
+++ assets/front.coffee
@@ -202,13 +202,4 @@
       window.ping = start_time - data.time
   window.getPing = getPing
   setInterval getPing, TIMING_INTERVAL
-  getPing()
-
-  s = io.connect ":8193/info.websocket"
-  s.on 'message', (data) ->
-    if typeof data is "string"
-      data = JSON.parse(data)
-    else if data.listener_count?
-      window._listeners = data.listener_count
-
-  window._socket = s
+  getPing()


try returning some son

try returning some son

--- app/controllers/home.coffee
+++ app/controllers/home.coffee
@@ -12,3 +12,8 @@
       $global: locals: req.app.locals
       title: 'Air Quality Now for LaMetric'
     , res
+
+  router.get '/current', (req, res) ->
+    res.json
+      a : 1
+


Support email env variables

Support email env variables

--- app/controllers/mail.coffee
+++ app/controllers/mail.coffee
@@ -23,8 +23,8 @@
   secureConnection: yes
   port: 465
   auth:
-    user: keychain 'email_user'
-    pass: keychain 'email_pass'
+    user: process.env.EMAIL_USER || keychain 'email_user'
+    pass: process.env.EMAIL_PASS || keychain 'email_pass'

 ###
 mailOptions =


Add shortcut to retrieve current editor's contents

--- .config/init.coffee
+++ .config/init.coffee
@@ -73,3 +73,8 @@
 	ed = atom.workspace.getActiveTextEditor()
 	el = ed.getElement()?.shadowRoot.querySelector(".scroll-view")
 	el?.classList.toggle("show-bracket-matcher")
+
+
+# Retrieve the contents of the current editor
+Object.defineProperty global, "text",
+	get: -> atom.workspace.getActiveTextEditor().buffer.getText()


Redirect to webview if a single course CC student

--- src/components/course-listing.cjsx
+++ src/components/course-listing.cjsx
@@ -18,7 +18,10 @@
     conceptCoach = courses[0].is_concept_coach

     if roleType is 'student'
-      if conceptCoach then callback() else view = 'viewStudentDashboard'
+      if conceptCoach
+        window.location.replace(course.webview_url)
+      else
+        view = 'viewStudentDashboard'
     else if roleType is 'teacher'
       view = if conceptCoach then 'cc-dashboard' else 'taskplans'
     else


shift around mapped part, lint some long lines

--- src/components/exercise/index.cjsx
+++ src/components/exercise/index.cjsx
@@ -37,7 +37,12 @@
   renderPart: (part, partProps) ->
     props = _.omit(@props, 'part', 'canOnlyContinue', 'footer', 'setScrollState', 'goToStep')

-    <ExercisePart {...partProps} {...props} step={part} id={part.id} taskId={part.task_id}/>
+    <ExercisePart
+      {...partProps}
+      {...props}
+      step={part}
+      id={part.id}
+      taskId={part.task_id}/>

   renderSinglePart: ->
     {parts, footer} = @props
@@ -99,7 +104,9 @@
 ExerciseWithScroll = React.createClass
   displayName: 'ExerciseWithScroll'
   mixins: [ScrollListenerMixin, ScrollTrackerParentMixin, ExerciseMixin]
-  wrapPartWithScrollTracker: (exerciseParts, part, index) ->
+  wrapPartWithScroll: (parts, exercisePart, index) ->
+    part = parts[index]
+
     scrollState =
       key: part.stepIndex
       questionNumber: part.questionNumber
@@ -110,7 +117,7 @@
       scrollState={scrollState}
       setScrollPoint={@setScrollPoint}
       unsetScrollPoint={@unsetScrollPoint}>
-      {exerciseParts[index]}
+      {exercisePart}
     </ScrollTracker>

   render: ->
@@ -120,7 +127,7 @@
       return @renderSinglePart()

     exerciseParts = @renderMultiParts()
-    exercisePartsWithScroll = _.map parts, _.partial @wrapPartWithScrollTracker, exerciseParts
+    exercisePartsWithScroll = _.map exerciseParts, _.partial @wrapPartWithScroll, parts
     exerciseGroup = @renderGroup()
     footer ?= @renderFooter()


ui tweaks for reload/validate options

ui tweaks for reload/validate options

--- BaragonUI/app/models/Service.coffee
+++ BaragonUI/app/models/Service.coffee
@@ -13,12 +13,13 @@
     removeUpstreamsSuccessTemplate: require '../templates/vex/removeUpstreamsSuccess'

     noReloadNoValidateInput: """
-            <input name="noValidate" type="checkbox" >Don't validate configs for this request</input>
-            <input name="noReload" type="checkbox" >Don't reload configs for this request</input>
+            <input name="validate" type="checkbox" checked> Validate new configuration after applying changes</input>
+            <br>
+            <input name="reload" type="checkbox" checked> Reload configuration after applying changes</input>
         """

     noValidateInput: """
-            <input name="noValidate" type="checkbox" >Don't validate configs for this request</input>
+            <input name="validate" type="checkbox" checked> Validate configuration before reloading</input>
         """

     initialize: ({ @serviceId }) ->
@@ -114,7 +115,9 @@
             ]
             callback: (data) =>
                 return if data is false
-                @delete(data.noValidate, data.noReload).done callback
+                noValidate = (!data.validate or data.validate != 'on')
+                noReload = (!data.reload or data.reload != 'on')
+                @delete(noValidate, noReload).done callback

     promptDeleteSuccess: (callback) =>
         vex.dialog.confirm
@@ -139,7 +142,8 @@
             ]
             callback: (data) =>
                 return if data is false
-                @reload(data.noValidate).done callback
+                noValidate = (!data.validate or data.validate != 'on')
+                @reload(noValidate).done callback

     promptReloadConfigsSuccess: (callback) =>
         vex.dialog.confirm
@@ -164,7 +168,9 @@
             ]
             callback: (data) =>
                 return if data is false
-                @undo(data.noValidate, data.noReload).done callback
+                noValidate = (!data.validate or data.validate != 'on')
+                noReload = (!data.reload or data.reload != 'on')
+                @undo(noValidate, noReload).done callback

     promptRemoveUpstreamsSuccess: (callback) =>
         vex.dialog.confirm
@@ -189,6 +195,8 @@
             ]
             callback: (data) =>
                 return if data is false
-                @remove(upstream, data.noValidate, data.noReload).done callback
+                noValidate = (!data.validate or data.validate != 'on')
+                noReload = (!data.reload or data.reload != 'on')
+                @remove(upstream, noValidate, noReload).done callback

 module.exports = Service


fix(run-package-specs): Return if the user cancels out of the dialog (Sentry 812)

--- src/browser/application.coffee
+++ src/browser/application.coffee
@@ -238,7 +238,7 @@
         defaultPath: configDirPath,
         properties: ['openDirectory']
       }, (filenames) =>
-        return if filenames.length is 0
+        return if not filenames or filenames.length is 0
         @runSpecs
           exitWhenDone: false
           resourcePath: @resourcePath


[#18] Fix empty `ALIAS` bug in KiCad generator

--- src/kicad-generator.coffee
+++ src/kicad-generator.coffee
@@ -155,7 +155,7 @@
         fs.writeSync fd, "F#{4 + i} \"#{attrObj.text}\" #{attrObj.x} #{attrObj.y} #{attrObj.fontSize} #{attrObj.orientation} V #{attrObj.halign} #{attrObj.valign}NN \"#{attrObj.name}\"\n"
         ++i

-    if element.aliases? then fs.writeSync fd, "ALIAS #{element.aliases.join(' ')}\n"
+    if element.aliases? and element.aliases.length then fs.writeSync fd, "ALIAS #{element.aliases.join(' ')}\n"
     if element.pattern?
       fs.writeSync fd, "$FPLIST\n"
       fs.writeSync fd, "  #{element.pattern.name}\n"


xUnitReporter writes asynchronously, and uses logger instead of cli

--- src/reporters/x-unit-reporter.coffee
+++ src/reporters/x-unit-reporter.coffee
@@ -1,5 +1,5 @@
 fs = require 'fs'
-cli = require 'cli'
+logger = require './../logger'
 htmlencode = require 'htmlencode'

 class XUnitReporter
@@ -13,7 +13,7 @@
   sanitizedPath: (path) =>
     filePath = process.cwd() + "/report.xml" unless path?
     if fs.existsSync(filePath)
-      cli.info "File exists at #{filePath}, deleting..."
+      logger.info "File exists at #{filePath}, deleting..."
       fs.unlinkSync(filePath)
     filePath

@@ -75,15 +75,16 @@

           fs.writeFile path, newStats + '\n' + restOfFile, (err) ->
             if err
-              cli.error err
+              logger.error err
       else
-        cli.error err
+        logger.error err

   cdata = (str) ->
     return '<![CDATA[' + str + ']]>'

   appendLine = (path, line) ->
-    fs.appendFileSync(path, line + "\n")
+    fs.appendFile path, line + "\n", (err) ->
+      throw err if err

   toTag = (name, attrs, close, content) ->
     end = (if close then "/>" else ">")


Disable annotation if there's a factlink button

--- app/js/util/annotate.coffee
+++ app/js/util/annotate.coffee
@@ -23,6 +23,9 @@
     window.clearTimeout timeout
     Factlink.createButton.hide()

+    if $('.factlink-create-button').length
+      return
+
     # We execute the showing of the prepare menu inside of a setTimeout
     # because of selection change only activating after mouseup event call.
     # Without this hack there are moments when the prepare menu will show


Don't toggle comment if clicking on a link

--- app/js/views/comment.coffee
+++ app/js/views/comment.coffee
@@ -28,6 +28,7 @@

   # Toggle the expanded or collapsed state of the comment
   toggle: (e) ->
+    return if $(e.target).is('a')
     e.preventDefault()
     @$el.toggleClass('collapsed expanded')


Use Vector(x, y) for Java object literal code generation

--- app/lib/aether_utils.coffee
+++ app/lib/aether_utils.coffee
@@ -200,7 +200,11 @@
     while quotesReg.test(s)
       s = s.replace quotesReg, '$1"$2"'
     # first replace ' to " then replace object
-    s = s.replace /\{\s*"?x"?\s*:\s*([^,]+),\s*"?y"?\s*:\s*([^\}]*)\}/g, '{$1, $2}'  # {x:1, y:1} -> {1, 1}
+    if language is 'cpp'
+      s = s.replace /\{\s*"?x"?\s*:\s*([^,]+),\s*"?y"?\s*:\s*([^\}]*)\}/g, '{$1, $2}'  # {x:1, y:1} -> {1, 1}
+    else if language is 'java'
+      # Let's use Vectors instead of object literals in this case
+      s = s.replace /\{\s*"?x"?\s*:\s*([^,]+),\s*"?y"?\s*:\s*([^\}]*)\}/g, 'new Vector($1, $2)'  # {x:1, y:1} -> new Vector(1, 1)
     jsCodes[i] = s

   unless fullCode


align previous marks (higlighted) given the (annoying) left padding

--- app/lib/canvas-graph.coffee
+++ app/lib/canvas-graph.coffee
@@ -173,11 +173,11 @@
   highlightCurve: (xLeft,xRight) ->
     for i in [0...@dataLength]
       if @data.x[i] >= xLeft and @data.x[i] <= xRight
-        x = ((+@data.x[i]-@xMin)/(@xMax-@xMin)) * @canvas.width
+        x = ((+@data.x[i]+@toDataXCoord(@leftPadding)-@xMin)/(@xMax-@xMin)) * (@canvas.width-@leftPadding)
         y = ((+@data.y[i]-@yMin)/(@yMax-@yMin)) * @canvas.height
         y = -y + @canvas.height # flip y-values
         @ctx.fillStyle = "rgba(252, 69, 65, 0.65)" #"#fc4541"
-        @ctx.fillRect(x+@leftPadding,y,2,2)
+        @ctx.fillRect(x,y,2,2)
     return

   plotPoints: (xMin = @smallestX, xMax = @largestX, yMin = @smallestY, yMax = @largestY) ->


alternate version with logos

alternate version with logos

--- app/translations/en_us.coffee
+++ app/translations/en_us.coffee
@@ -25,7 +25,7 @@
         If you’d like to try a similar project, how about:

Chicago Wildlife Watch

-

Chimp & See

+

Chimp & See


kontrol.coffee: make it coffee style

kontrol.coffee: make it coffee style

--- client/app/MainApp/kite/kontrol.coffee
+++ client/app/MainApp/kite/kontrol.coffee
@@ -36,8 +36,7 @@
     query = @_sanitizeQuery query

     @kite.tell "getKites", [query], (res)=>
-      err = res.withArgs[0]
-      kites = res.withArgs[1]
+      [err, kites] = res.withArgs
       return callback err, null  if err

       kiteInstances = []
@@ -61,8 +60,7 @@
     args = [query, onEvent]

     @kite.tell "getKites", args, (res)=>
-      err = res.withArgs[0]
-      kites = res.withArgs[1]
+      [err, kites] = res.withArgs
       return callback err, null  if err

       for k in kites


router: referral route

router: referral route

--- client/app/MainApp/kodingrouter.coffee
+++ client/app/MainApp/kodingrouter.coffee
@@ -268,6 +268,7 @@
       '/About' : createSectionHandler 'Activity'

       '/landing/:page': noop
+      '/R/:username'  : noop

       # verbs
       '/:name?/Login'        : ({params:{name}})->


Try harder to remove lockfile on exit.

Try harder to remove lockfile on exit.

--- app/lib/lock.coffee
+++ app/lib/lock.coffee
@@ -7,4 +7,4 @@

 fs.writeFileSync lockFile, "#{process.pid}\n"

-process.on 'exit', -> fs.unlink lockFile
+process.on 'cleanup', -> fs.unlinkSync lockFile


change width

change width

--- graphics/001-terrain-gen/viewer.coffee
+++ graphics/001-terrain-gen/viewer.coffee
@@ -36,7 +36,7 @@
         context.clearRect(0, 0, 4096, 2048)

         [num, len, heights] = data
-        width = 512 / num
+        width = 2048 / num
         for row in [0...num]
             for col in [0...num]
                 pos = row * num + row + col


modified redis config

modified redis config

--- src/lib/cache/index.coffee
+++ src/lib/cache/index.coffee
@@ -5,6 +5,6 @@
 { hostname, port, auth, pathname } = URL.parse(config.redis)
 redis = Redis.createClient(port, hostname, no_ready_check: true)
 redis.auth(auth.split(':').pop()) if auth
-redis.select(db) if db = pathname.split('/').pop()
+redis.select(db) if pathname and (db = pathname.split('/').pop())

 module.exports = redis


jupyter classic: fix bug with properly hiding the iframe

--- src/smc-webapp/editor_jupyter.coffee
+++ src/smc-webapp/editor_jupyter.coffee
@@ -928,7 +928,6 @@
                         @syncstring.live(live)
                         @syncstring.sync()
             @emit(@state)
-            @show()
             cb?(err)

     init_syncstring: (cb) =>
@@ -1167,6 +1166,8 @@
         return @syncstring._syncstring.last_changed() - 0

     show: =>
+        # This should ONLY be called externally from
+        # ./project_actions.ts!
         @element.show()
         @dom?.refresh()