Change to throw the error when a value in range is error

--- src/formula/evaluator.coffee
+++ src/formula/evaluator.coffee
@@ -54,9 +54,13 @@
           if Utils.isBoolean(x)
             res &&= !!x
             flag = true
+          else if x instanceof FormulaError
+            throw x

       else
         x = @getValue(arg)
+        if x instanceof FormulaError
+          throw x
         unless Utils.isBoolean(x)
           @error(FormulaError.VALUE)
         res &&= !!x
@@ -756,9 +760,13 @@
           if Utils.isBoolean(x)
             res ||= !!x
             flag = true
+          else if x instanceof FormulaError
+            throw x

       else
         x = @getValue(arg)
+        if x instanceof FormulaError
+          throw x
         unless Utils.isBoolean(x)
           @error(FormulaError.VALUE)
         res ||= !!x
@@ -1239,9 +1247,13 @@
           if Utils.isBoolean(x)
             true_count++ if !!x
             flag = true
+          else if x instanceof FormulaError
+            throw x

       else
         x = @getValue(arg)
+        if x instanceof FormulaError
+          throw x
         unless Utils.isBoolean(x)
           @error(FormulaError.VALUE)
         true_count++ if !!x


Support TRIM

Support TRIM

--- src/formula_evaluator.coffee
+++ src/formula_evaluator.coffee
@@ -959,6 +959,10 @@
         str.length > 0
     strings.join(delim)

+  TRIM: (args)->
+    @checkArgumentSize(args, 1)
+    str = @expectString(args[0])
+    str.replace(/\s+/g, " ").trim()

   SUM: (args)->


TAP5-2089: Don't return a value from the source function for Typeahead

--- tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/autocomplete.coffee
+++ tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/autocomplete.coffee
@@ -34,7 +34,9 @@

       $field.typeahead
         minLength: spec.minChars
-        source: (query, process) -> doLookup $field, spec.url, query, process
+        source: (query, process) ->
+          doLookup $field, spec.url, query, process
+          return

-    exports = init+    exports = init


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color2.coffee
+++ test/color2.coffee
@@ -31,6 +31,6 @@
   equal color.b, 3
   equal color.a, 0.4

-test "accepts an array of length 4 as the first argument and an alpha value. The alpha value overrides the alpha passed in by the array"
+test "accepts an array of length 4 as the first argument and an alpha value. The alpha value passed as the second argument overrides the alpha in the array"

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color2.coffee
+++ test/color2.coffee
@@ -155,11 +155,11 @@
   raises ->
     Color2("A Fake Color")

-test "#equals", ->
+test "#equal", ->
   color1 = Color2(255, 255, 255, 1)
   color2 = Color2([255, 255, 255])

-  color1.equal(color2)
+  log color1

 module()


[] => {} I know, very descriptive

[] => {}
I know, very descriptive

--- lib/ui/progress.coffee
+++ lib/ui/progress.coffee
@@ -1,16 +1,17 @@
 {formatTimePeriod} = require '../misc'

 module.exports =
-  progs: []
+  progs: {}

   add: (p) ->
     pr = @ink.progress.create p
     pr.register()
     pr.t0 = Date.now()
-    @progs.push pr
+    @progs[pr.id] = pr

   update: (p) ->
-    pr = @progs.find (pr) => pr.id is p.id
+    pr = @progs[p.id]
+    return unless pr?
     pr.setProgress if p.progress >= 0 then p.progress else null
     pr.setLeftText p.leftText
     if p.rightText?.length
@@ -20,12 +21,12 @@
     pr.setMessage p.msg

   delete: (p) ->
-    i = @progs.findIndex (pr) => pr.id is p.id
-    return if i < 0
-    @progs[i].destroy()
-    @progs.splice i, 1
+    pr = @progs[p.id]
+    return unless pr?
+    pr.destroy()
+    delete @progs[p.id]

   clear: ->
-    for p in @progs
-      p.destroy()
-    @progs = []
+    for _, p of @progs
+      p?.destroy()
+    @progs = {}


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine.coffee
+++ test/engine.coffee
@@ -187,6 +187,8 @@

   engine.setState nextState

-  engine.update
+  engine.update()
+
+

 module()


ignore more pusher warnings

ignore more pusher warnings

--- assets/javascripts/app/pusher.coffee
+++ assets/javascripts/app/pusher.coffee
@@ -1,7 +1,7 @@
 Travis.Pusher = ->
   @active_channels = []
   if Travis.config.pusher?.key?
-    Pusher.warn = @warn
+    Pusher.warn = @warn.bind(this)
     @pusher = new Pusher(Travis.config.pusher.key)
     @subscribe(channel) for channel in Travis.Pusher.CHANNELS
   this
@@ -41,5 +41,9 @@
       when 'worker:added', 'worker:updated', 'worker:removed'
         { worker: data }

-  warn: (type, error) ->
-    console.warn(error) unless error.data?.message && error.data.message.indexOf('No current subscription') > -1
+  warn: (type, warning) ->
+    console.warn(warning) unless @ignoreWarning(warning)
+
+  ignoreWarning: (warning) ->
+    if message = warning.data?.message
+      message.indexOf('Existing subscription') == 0 || message.indexOf('No current subscription') == 0


Add dropItem in battle-env

Add dropItem in battle-env

--- views/battle-env.coffee
+++ views/battle-env.coffee
@@ -197,6 +197,7 @@
             enemy: body.api_enemy_info.api_deck_name
             combined: combined
             mvp: if combined then [body.api_mvp - 1, body.api_mvp_combined - 1] else [body.api_mvp - 1, body.api_mvp - 1]
+            dropItem: body.api_get_useitem
             dropShipId: if body.api_get_ship? then body.api_get_ship.api_ship_id else -1
             deckShipId: if combined then _decks[0].api_ship.concat(_decks[1].api_ship) else Object.clone _decks[deckId].api_ship
             deckHp: if combined then _sortieHp.concat(_combinedHp) else _sortieHp


adding analytics track

adding analytics track

--- apps/articles/client/analytics.coffee
+++ apps/articles/client/analytics.coffee
@@ -8,4 +8,7 @@
 $('.articles-social:eq(1) > a').click ->
   analytics.track.click 'Clicked Article Share',
     position: 'bottom'
-    service: $(this).attr 'data-service'+    service: $(this).attr 'data-service'
+
+$ ->
+  analytics.track.click 'Article page load'


Restore matcher tests in indeed

Restore matcher tests in indeed

--- test/indeed.coffee
+++ test/indeed.coffee
@@ -507,6 +507,101 @@
       ( ->
         indeed(true).And.noneOf(true).and(false).anyOf(false)
       ).should.throw('IllegalMethodException: anyOf cannot be called with noneOf')
+
+  describe '#is', ->
+    context 'with a string', ->
+      it 'should be true with the same string', ->
+        indeed('string').is('string').test().should.be.true
+
+      it 'should be false with different strings', ->
+        indeed('string').is('nope').test().should.be.false
+
+    context 'with numbers', ->
+      it 'should be true for the same number', ->
+        indeed(2).is(2).test().should.be.true
+
+      it 'should be false for different numbers', ->
+        indeed(2).is(4).test().should.be.false
+
+    context 'with an array', ->
+      it 'should be true for reference equality', ->
+        @arr = [1,2,3]
+        indeed(@arr).is(@arr).test().should.be.true
+
+      it 'should be false for comparison equality', ->
+        indeed([1,2,3]).is([1,2,3]).test().should.be.false
+
+    context 'with object literals', ->
+      it 'should be true for reference equality', ->
+        @obj = key: 'value'
+        indeed(@obj).is(@obj).test().should.be.true
+
+      it 'should be false for comparison equality', ->
+        indeed(key: 'value').is(key: 'value').test().should.be.false
+
+    context 'with new-able objects', ->
+      it 'should be true for reference equality', ->
+        @d = new Date()
+        indeed(@d).is(@d).test().should.be.true
+
+      it 'should be false for comparison equality', ->
+        indeed(new Date(2000, 9, 9)).is(new Date(2000, 9, 9)).test().should.be.false
+
+    context 'with null', ->
+      it 'should be true when null', ->
+        indeed(null).is(null).test().should.be.true
+
+    context 'with undefined', ->
+      it 'should be true when undefined', ->
+        indeed(undefined).is(undefined).test().should.be.true
+
+  describe '#equal', ->
+    context 'with a string', ->
+      it 'should be true with the same string', ->
+        indeed('string').equals('string').test().should.be.true
+
+      it 'should be false with different strings', ->
+        indeed('string').equals('nope').test().should.be.false
+
+    context 'with numbers', ->
+      it 'should be true for the same number', ->
+        indeed(2).equals(2).test().should.be.true
+
+      it 'should be false for different numbers', ->
+        indeed(2).equals(4).test().should.be.false
+
+    context 'with an array', ->
+      it 'should be true for reference equality', ->
+        @arr = [1,2,3]
+        indeed(@arr).equals(@arr).test().should.be.true
+
+      it 'should be true for comparison equality', ->
+        indeed([1,2,3]).equals([1,2,3]).test().should.be.true
+
+      it 'should be false for different arrays', ->
+        indeed([1,2,3]).equals([4,5,6]).test().should.be.false
+
+    context 'with object literals', ->
+      it 'should be true for reference equality', ->
+        @obj = key: 'value'
+        indeed(@obj).equals(@obj).test().should.be.true
+
+      it 'should be true for comparison equality', ->
+        indeed(key: 'value').equals(key: 'value').test().should.be.true
+
+      it 'should  be false for differnt objects', ->
+        indeed(key: 'value').equals(hello: 'world').test().should.be.false
+
+    context 'with new-able objects', ->
+      it 'should be true for reference equality', ->
+        @d = new Date()
+        indeed(@d).equals(@d).test().should.be.true
+
+      it 'should be true for comparison equality', ->
+        indeed(new Date(2000, 9, 9)).equals(new Date(2000, 9, 9)).test().should.be.true
+
+      it 'should be false for different objects', ->
+        indeed(new Date(2000, 9, 9)).equals(new Date(1999, 3, 8)).test().should.be.false

   describe '#isA', ->
     it 'should return true if constructor name matches', ->


Added generate tile functions

Added generate tile functions

--- js/main.coffee
+++ js/main.coffee
@@ -1 +1,44 @@
+randomInt = (x) ->
+  Math.floor(Math.random() * x)
+
+randomCellIndicies = ->
+  [randomInt(4), randomInt(4)]
+
+randomValue = ->
+  values = [2,2,2,4]
+  values[randomInt(4)]
+
+buildBoard = ->
+  [0..3].map -> [0..3].map -> 0
+
+# buildBoardSlow = ->
+#   board = []
+#   for row in [0..3]
+#     board[row] = []
+#     for col in [0..3]
+#       board[row][col] = 0
+#   board
+
+generateTile = (board) ->
+  value = randomValue()
+  console.log "randomInt: #{randomCellIndicies()}"
+  [row, column] = randomCellIndicies()
+  if board[row][column] is 0
+    board[row][column] = value
+  else
+    generateTile(board)
+  console.log ("Generate tile!")
+
+printArray = (array) ->
+  console.log "--- Start ---"
+  for row in array
+    console.log row
+  console.log "--- End ---"
+
+
+
 $ ->
+  newBoard = buildBoard()
+  generateTile(newBoard)
+  generateTile(newBoard)
+  printArray(newBoard)


More simplification/refactoring.

More simplification/refactoring.

--- background_scripts/completion.coffee
+++ background_scripts/completion.coffee
@@ -205,12 +205,12 @@
       chrome.history.onVisitRemoved.addListener(@onVisitRemoved.bind(this))
       onComplete()

-  # We want each key in our domains hash to point to the most recent History entry for that domain.
   onPageVisited: (newPage) ->
     domain = @parseDomain(newPage.url)
     if domain
       @domains[domain] ||= { entry: newPage, referenceCount: 0 }
       slot = @domains[domain]
+      # We want each entry in our domains hash to point to the most recent History entry for that domain.
       slot.entry = newPage if slot.entry.lastVisitTime < newPage.lastVisitTime
       slot.referenceCount += 1

@@ -220,12 +220,8 @@
     else
       toRemove.urls.forEach (url) =>
         domain = @parseDomain(url)
-        if domain
-          previousEntry = @domains[domain]
-          if previousEntry
-            previousEntry.referenceCount -= 1
-            if previousEntry.referenceCount == 0
-              delete @domains[domain]
+        if domain and @domains[domain] and ( @domains[domain].referenceCount -= 1 ) == 0
+          delete @domains[domain]

   parseDomain: (url) -> url.split("/")[2] || ""


consolidated then statesments

consolidated then statesments

--- addon/models/dataview.coffee
+++ addon/models/dataview.coffee
@@ -33,8 +33,8 @@
     .mapValues bind(resolveAp, @, params)
     .thru RSVP.hash
     .value()
-    .then (hash) => @setProperties hash
-    .then =>
+    .then (hash) =>
+      @setProperties hash
       @set "dataviewLoadStatus", "loaded"
       @
     .catch (error) ->


ButtonRenderer can now decorate any tag, resolves #111

--- wzk/ui/ButtonRenderer.coffee
+++ wzk/ui/ButtonRenderer.coffee
@@ -23,4 +23,15 @@
     goog.dom.classes.add el, wzk.ui.ButtonRenderer.CLASSES.BUTTON
     el

+  ###*
+    Allows decorating any element.
+    @override
+    @param {Element} el
+  ###
+  canDecorate: (el) ->
+    if el.tagName is 'DIV' or el.tagName is 'SPAN'
+      return true
+    else
+      return super el
+
 goog.addSingletonGetter wzk.ui.ButtonRenderer


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/sprite.coffee
+++ test/sprite.coffee
@@ -9,7 +9,7 @@

   callback = ->
     sprite2 = Sprite.load spriteUrl, (s) ->
-      ok s
+      ok s.width isnt n

   sprite = Sprite.load spriteUrl, callback


Always use the bools.

Always use the bools.

--- lib/about.coffee
+++ lib/about.coffee
@@ -16,6 +16,7 @@
 module.exports = About =
   activate: ->
     @subscriptions = new CompositeDisposable
+    @updateAvailable = false

     @subscriptions.add atom.workspace.addOpener (uriToOpen) ->
       createAboutView(uri: uriToOpen) if uriToOpen is aboutURI


Fixed a bug that prevented radio buttons from being unselected

--- app/assets/javascripts/components/surveys.coffee
+++ app/assets/javascripts/components/surveys.coffee
@@ -115,6 +115,10 @@
     false
   )
   .on('click', '.survey-choices input', ->
+    if $(this).closest("label").hasClass("active")
+      $(this).prop("checked", false)
+    else
+      $(this).prop("checked", true)
     toggleMutuallyExclusive($(this))
     $(".survey-choices input").closest("label").removeClass("active")
     $(".survey-choices input:checked").closest("label").addClass("active")


Let the DOM finish any current rendering operations before positioning the tour

--- app/assets/javascripts/modules/landing.js.coffee
+++ app/assets/javascripts/modules/landing.js.coffee
@@ -65,7 +65,9 @@
     $(window).on 'statechange anchorchange', updateLandingPageState

     preferences.onload ->
-      help.startTour() if uiModel.isLandingPage() && preferences.showTour()
+      if preferences.showTour()
+        # Let the DOM finish any refresh operations before showing the tour
+        setTimeout((-> help.startTour() if uiModel.isLandingPage()), 0)

     $('.landing-area').on 'keypress', '#keywords', (e) ->
       urlUtil.pushState('/search') if e.which == 13


Add caption numbers to text positioner boxes

--- app/assets/javascripts/text_positioner.js.coffee
+++ app/assets/javascripts/text_positioner.js.coffee
@@ -18,16 +18,9 @@

   add_rect: (name, x_input, y_input, width_input, height_input) ->
     rect = new fabric.Rect
-      name: name
-      top: @scale_height(y_input.val())
-      left: @scale_width(x_input.val())
       width: @scale_width(width_input.val())
       height: @scale_height(height_input.val())
       fill: '#808080'
-      cornerSize: 20
-      transparentCorners: false
-      lockRotation: true
-      hasRotatingPoint: false
       opacity: 0.9

     rect.setGradient 'fill',
@@ -40,13 +33,30 @@
         0: 'black'
         1: 'white'

-    rect.setShadow
+    text = new fabric.Text "#{name + 1}",
+      left: rect.getWidth() / 2
+      top: rect.getHeight() / 2
+      originX: 'center'
+      originY: 'center'
+
+    group = new fabric.Group [rect, text],
+      name: name
+      top: @scale_height(y_input.val())
+      left: @scale_width(x_input.val())
+      width: @scale_width(width_input.val())
+      height: @scale_height(height_input.val())
+      cornerSize: 20
+      transparentCorners: false
+      lockRotation: true
+      hasRotatingPoint: false
+
+    group.setShadow
       color: 'rgba(0,0,0,0.3)'
       blur: 10
       offsetX: 10
       offsetY: 10

-    @fabric_canvas.add rect
+    @fabric_canvas.add group

   remove_rect: (name) ->
     toRemove = (
@@ -120,6 +130,8 @@
     target.bound_scale_y()

     target.fire 'object:moving'
+
+    target.preserve_item_scale(1)

   object_modified: (o) ->
     target = new Target(o)
@@ -199,6 +211,12 @@
   bottom_side: ->
     @target.getTop() + @target.getHeight()

+  preserve_item_scale: (index) ->
+    text = @target.item(index)
+    text.setScaleX(1 / @target.getScaleX())
+    text.setScaleY(1 / @target.getScaleY())
+    @target.dirty = true
+
 @text_positioner_init = ->
   $('.text-positioner').each (index, element) ->
     tp = new TextPositioner


we're not big idiots

we're not big idiots

--- app/assets/javascripts/views/mapping_view.coffee
+++ app/assets/javascripts/views/mapping_view.coffee
@@ -67,9 +67,10 @@

   deleteLastPoint: =>
     @farm.removePoint()
+    @renderPath()
+    @renderFinishButton()

   finish: =>
-    @farm.setLastPointToFirstPoint()
     @renderPolygon()
     @renderSubmitButton()

@@ -81,6 +82,7 @@
     $("#mark-point").hide()

   renderFinishButton: ->
+    return if $('#finish-mapping').length
     finishButton = $("""FINISH""")
     $('#undo-point').before(finishButton)
     $('#submit-button').remove()


Remove append flag

Remove append flag

--- lib/index.coffee
+++ lib/index.coffee
@@ -28,8 +28,6 @@

   if isFirstTime
     opts['release-count'] = 0
-  else
-    opts.append = true

   flags = parseOptions opts
   ps = spawn(changelog, flags, stdio: 'inherit')


Added UpdateOnSignInOrOutMixin mixin for  can_destroy and can_edit

--- app/backbone/views/comments/react_comment.coffee
+++ app/backbone/views/comments/react_comment.coffee
@@ -2,7 +2,7 @@

 window.ReactComment = React.createBackboneClass
   displayName: 'ReactComment'
-  mixins: [React.BackboneMixin('tally')]
+  mixins: [React.BackboneMixin('tally'), UpdateOnSignInOrOutMixin]
   propTypes:
     fact_opinionators: React.PropTypes.instanceOf(InterestedUsers).isRequired
     model: React.PropTypes.instanceOf(Comment).isRequired


gradebook: keep submission details in sync

fixes CNVS-20683

Test plan:
  * in the gradebook, click the "submission details" link on a cell
  * close the dialog
  * change the grade in the gradebook cell
  * go back to submission details
  * the grade in the dialog should be properly updated

Change-Id: Ib0439cbdc75494e332fa30b88306c41d51741e6e
Reviewed-on: https://gerrit.instructure.com/55954
Tested-by: Jenkins
Reviewed-by: Dylan Ross <7374864f039b76a5b01d8407f92828e3f173309e@instructure.com>
QA-Review: Adrian Foong <84c1094366ed084f9dbd0e92a3de1886a73fdc57@instructure.com>
Product-Review: Cameron Matheson <1d572acbfa68c7c6e541c7b840d6b622e5c0dc91@instructure.com>

--- app/coffeescripts/SubmissionDetailsDialog.coffee
+++ app/coffeescripts/SubmissionDetailsDialog.coffee
@@ -88,8 +88,6 @@
       @dialog.find('select').trigger('change')
       @scrollCommentsToBottom()

-    @cachedDialogs: {}
+    @open: (assignment, student, options) ->
+      new SubmissionDetailsDialog(assignment, student, options).open()

-    @open: (assignment, student, options) ->
-      (SubmissionDetailsDialog.cachedDialogs["#{assignment.id}-#{student.id}"] ||= new SubmissionDetailsDialog(assignment, student, options)).open()
-


minor code reformatting

minor code reformatting

--- lib/model.coffee
+++ lib/model.coffee
@@ -3,8 +3,7 @@
 class Model
   constructor : (@sources, @filters) ->
     @feeds = flatten(source.getFeeds() for source in @sources)
-    for feed in @feeds
-      feed.on("updated", @onFeedUpdated)
+    feed.on("updated", @onFeedUpdated) for feed in @feeds
     @requestUpdate()

   onFeedUpdated : (feed) =>
@@ -13,7 +12,6 @@
     # TODO

   requestUpdate : =>
-    for feed in @feeds
-      feed.requestUpdate()
+    feed.requestUpdate() for feed in @feeds

 exports.Model = Model


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/bounded.coffee
+++ src/bounded.coffee
@@ -57,6 +57,7 @@

   @name distance
   @methodOf Bounded#
+  @see Point.distance
   @returns {Number} Distance between the two objects
   ###
   distance: (otherObj) ->


add vars

add vars

--- src/browser.coffee
+++ src/browser.coffee
@@ -1,6 +1,6 @@
 # Browser.js

-window.device = {}
+window.browser = {}
 _user_agent = window.navigator.userAgent.toLowerCase()

 browser.isOpera = ->


fixed kinda

fixed kinda

--- site/app/docs/js/coffee/docs.coffee
+++ site/app/docs/js/coffee/docs.coffee
@@ -1,29 +1,29 @@
 $ ->
-	for section in $("#sidebar").children()
-		href = $(section).children(".level-1").attr("where-to")
-		sectionContent = $("##{href}").children("h1, h2, h3, h4, h5")
-		$(section).append("
")
-		nextLvl = $("#lvl-2-#{href}")
-		for header in sectionContent
-			id = $(header).prop("id")
-			title = id.replace(/-/g, " ")
-			nextLvl.append("#{title}")
-		$("#sidebar").find(".level-2").addClass("hidden")
+    for section in $("#sidebar").children()
+        href = $(section).children(".level-1").attr("where-to")
+        sectionContent = $("##{href}").children("h1, h2, h3, h4, h5")
+        $(section).append("
")
+        nextLvl = $("#lvl-2-#{href}")
+        for header in sectionContent
+            id = $(header).prop("id")
+            title = id.replace(/-/g, " ")
+            nextLvl.append("#{title}")
+        $("#sidebar").find(".level-2").addClass("hidden")

-	$(".section-bar>a")
-		.on 'click', () ->
-			console.log $(@).offset()
-			console.log $(@).position()
-			console.log $(@).parent().position()
-			href = $(@).attr("where-to")
-			console.log $("##{href}").offset()
-			console.log $("##{href}").position()
-			absTargetY = $("##{href}").offset().top
-			relTargetY = $("##{href}".position().top
-			relHeaderY = $(@).parent().position().top
-			y = absTargetY - relHeaderY
-			$("#sidebar").attr("style", "transform: translate(0, #{y}px)")
-			$(@).parent().toggleClass("active")
-			$(@).parent().siblings().removeClass("active")
-			$(@).parent().find(".level-2").removeClass("hidden")
-			$(@).parent().siblings().find(".level-2").addClass("hidden")+    $(".section-bar>a")
+        .on 'click', () ->
+            # console.log $(@).offset()
+            # console.log $(@).position()
+            # console.log $(@).parent().position()
+            href = $(@).attr("where-to")
+            console.log $("##{href}").offset()
+            console.log $("##{href}").position()
+            absTargetY = $("##{href}").offset().top
+            relTargetY = $("##{href}").position().top
+            relHeaderY = $(@).parent().position().top
+            y = absTargetY
+            $("#sidebar").attr("style", "transform: translate(0, #{y}px)")
+            $(@).parent().toggleClass("active")
+            $(@).parent().siblings().removeClass("active")
+            $(@).parent().find(".level-2").removeClass("hidden")
+            $(@).parent().siblings().find(".level-2").addClass("hidden")


Refactor updateState

Refactor updateState

--- src/context.coffee
+++ src/context.coffee
@@ -5,15 +5,12 @@
   # (State => State) => Promise<void>
   updateState: (stateFn) -> new Promise (done) =>
     Promise.resolve(
+      # Call initState if state is null/undefined
       if !@state? and @props
-        new Promise (d) =>
-          Promise.resolve(@initState(@props))
-          .then (@state) =>
-            @state = stateFn(@state) # TODO: merge other update
-            d()
-      else
-        @state = stateFn(@state)
+        Promise.resolve(@initState(@props))
+        .then (@state) => Promise.resolve()
     ).then =>
+      @state = stateFn(@state)
       @emit 'internal:state-updated', @state
       Promise.resolve(@expandTemplate(@props, @state))
       .then (template) =>


rename Creator.mkdirDashP to Creator.mkdir since it doesn't do dash p

--- src/creator.coffee
+++ src/creator.coffee
@@ -14,7 +14,7 @@
   # Create a folder if it doesn't already exist.
   #
   # Returns nothing.
-  mkdirDashP: (path) ->
+  mkdir: (path) ->
     Fs.exists path, (exists) ->
       unless exists
         Fs.mkdirSync path, 0o0755
@@ -53,9 +53,9 @@
   run: ->
     console.log "Creating a hubot install at #{@path}"

-    @mkdirDashP(@path)
-    @mkdirDashP("#{@path}/bin")
-    @mkdirDashP("#{@path}/scripts")
+    @mkdir(@path)
+    @mkdir("#{@path}/bin")
+    @mkdir("#{@path}/scripts")

     @copyDefaultScripts("#{@path}/scripts")


Minor bug squashing

Minor bug squashing

--- src/drafter.coffee
+++ src/drafter.coffee
@@ -35,7 +35,7 @@
 # @param contentType [Object] Payload content type
 generateBody = (payload, attributes, contentType, callback) ->
   if not attributes? or not contentType? or payload.body
-    return callback null
+    return callback null, payload, attributes, contentType

   boutique.represent
     ast: attributes,
@@ -60,7 +60,7 @@
 # @param contentType [Object] Payload content type
 generateSchema = (payload, attributes, contentType, callback) ->
   if not attributes? or payload.schema or contentType.indexOf('json') is -1
-    return callback null
+    return callback null, payload, attributes, contentType

   boutique.represent
     ast: attributes,
@@ -73,7 +73,7 @@
           role: 'bodySchema'
         content: body

-      payload.content.push resolved
+      payload.content.push resolved

     # For waterfall
     callback null, payload, attributes, contentType
@@ -115,7 +115,7 @@
   # @param callback [(Error, ParseResult)]
   make: (source, callback) ->
     protagonist.parse source, @config, (error, result) =>
-      callback error if error
+      return callback error if error

       ruleList = ['mson-inheritance', 'mson-mixin', 'mson-member-type-name']
       rules = (require './rules/' + rule for rule in ruleList)
@@ -128,7 +128,7 @@

       async.each payloads, @resolvePayload, (error) =>
         @reconstructResourceGroups result.ast
-        callback error, result
+        callback error or null, result

   # Resolve assets of a payload
   resolvePayload: ({payload, actionAttributes}, callback) ->
@@ -144,12 +144,12 @@
     attributes ?= actionAttributes

     async.waterfall [
-      (callback) ->
-        callback null, payload, attributes, contentType
+      (cb) ->
+        cb null, payload, attributes, contentType
       , generateBody
       , generateSchema
-    ], (err) ->
-      callback null
+    ], (error) ->
+      callback error or null

   # Expand a certain node with the given rules
   #


durable

durable

--- src/durable.coffee
+++ src/durable.coffee
@@ -14,7 +14,7 @@
    enemy.I.active
 => true

-   6.times ->
+   5.times ->
      enemy.update()

    enemy.I.active
@@ -33,6 +33,8 @@

   before:
     update: () ->
-      if I.duration != -1 && I.age >= I.duration
+      # adding in I.age + 1 because this
+      # check occurs before I.age is updated
+      if I.duration != -1 && I.age + 1 >= I.duration
         I.active = false


Fix issue that was corrupting binary files like PNGs

--- src/enfield.coffee
+++ src/enfield.coffee
@@ -483,6 +483,7 @@
   for file in fs.readdirSync layoutDir
     name = path.basename file, path.extname file
     { data, content } = getDataAndContent path.join(layoutDir, file)
+    data or= {}
     fileData[name] = data
     fileContents[name] = content

@@ -553,6 +554,7 @@

       {data, content, ext} = getDataAndContent path.join postDir, filename

+      data or= {}
       post = { raw_content: content, ext }
       post[key] = data[key] for key of data

@@ -645,8 +647,8 @@

 # Get the frontmatter plus content of a file
 getDataAndContent = (filepath) ->
-  lines = fs.readFileSync(filepath).toString().split(/\r\n|\n|\r/)
-  data = {}
+  content = fs.readFileSync(filepath).toString()
+  lines = content.split(/\r\n|\n|\r/)
   if /^---\s?$/.test lines[0]
     lines.shift()
     frontMatter = []
@@ -663,7 +665,7 @@
         data = { value: data }

   data: data
-  content: lines.join "\n"
+  content: if data then lines.join("\n") else content
   ext: path.extname filepath

 getPermalink = (post, style) ->


Fixes #27.  Remaps numpad keys to scan codes.

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -75,21 +75,21 @@
     unshifted: 48  # "0"

 NumPadToASCII =
-  111: 47 # "/"
-  106: 42 # "*"
-  109: 45 # "-"
-  107: 43 # "+"
-  110: 46 # "."
+  79: 47 # "/"
+  74: 42 # "*"
+  77: 45 # "-"
+  75: 43 # "+"
+  78: 46 # "."
   96: 48 # "0"
-  97: 49 # "1"
-  98: 50 # "2"
-  99: 51 # "3"
-  100: 52 # "4"
-  101: 53 # "5"
-  102: 54 # "6"
-  103: 55 # "7"
-  104: 56 # "8"
-  105: 57 # "9"
+  65: 49 # "1"
+  66: 50 # "2"
+  67: 51 # "3"
+  68: 52 # "4"
+  69: 53 # "5"
+  70: 54 # "6"
+  71: 55 # "7"
+  72: 56 # "8"
+  73: 57 # "9"

 exports.normalizeKeystrokes = (keystrokes) ->
   normalizedKeystrokes = []
@@ -104,17 +104,16 @@
   unless KeyboardEventModifiers.has(event.keyIdentifier)
     keyIdentifierIsHexCharCode = event.keyIdentifier.indexOf('U+') is 0

-    if event.location is KeyboardEvent.DOM_KEY_LOCATION_NUMPAD
-      # This is a numpad number
-      keyCode = numpadToASCII(event.keyCode, event.shiftKey)
-    else
-      keyCode = event.keyCode
-
-    if isASCII(keyCode) and keyIdentifierIsHexCharCode
-      key = keyFromCharCode(keyCode, event.shiftKey)
+    if event.location isnt KeyboardEvent.DOM_KEY_LOCATION_NUMPAD and isASCII(event.keyCode) and keyIdentifierIsHexCharCode
+      key = keyFromCharCode(event.keyCode, event.shiftKey)
     else if keyIdentifierIsHexCharCode
       hexCharCode = event.keyIdentifier[2..]
       charCode = charCodeFromHexCharCode(hexCharCode)
+
+      if event.location is KeyboardEvent.DOM_KEY_LOCATION_NUMPAD
+        # This is a numpad number
+        charCode = numpadToASCII(charCode, event.shiftKey)
+
       key = keyFromCharCode(charCode, event.shiftKey)
     else
       key = event.keyIdentifier.toLowerCase()
@@ -248,7 +247,14 @@
     else String.fromCharCode(charCode)

 isASCII = (charCode) ->
-  0 <= charCode <= 127
+  return (
+    (charCode > 47 and charCode < 58)   || # number keys
+    (charCode is 32 or charCode is 13)  || # spacebar & return keys
+    (charCode > 64 and charCode < 91)   || # Uppercase letter keys
+    (charCode > 96 and charCode < 123)  || # Lowercase letter keys
+    (charCode > 185 and charCode < 193) || # ;=,-./`
+    (charCode > 218 and charCode < 223);   # [\]'
+  )

 numpadToASCII = (charCode, shifted) ->
   trans = NumPadToASCII[charCode]


Rename KEYNAMES -> KEY_NAMES

Rename KEYNAMES -> KEY_NAMES

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -16,7 +16,7 @@
   'ArrowLeft': 'left',
   'ArrowRight': 'right'
 }
-NUMPAD_KEYNAMES_BY_KEYBOARD_EVENT_CODE = {
+NUMPAD_KEY_NAMES_BY_KEYBOARD_EVENT_CODE = {
   'Numpad0': 'numpad0',
   'Numpad1': 'numpad1',
   'Numpad2': 'numpad2',
@@ -135,8 +135,8 @@
       else if characters.unmodified?
         key = characters.unmodified

-  if NUMPAD_KEYNAMES_BY_KEYBOARD_EVENT_CODE[code]? and event.getModifierState('NumLock')
-    key = NUMPAD_KEYNAMES_BY_KEYBOARD_EVENT_CODE[code]
+  if NUMPAD_KEY_NAMES_BY_KEYBOARD_EVENT_CODE[code]? and event.getModifierState('NumLock')
+    key = NUMPAD_KEY_NAMES_BY_KEYBOARD_EVENT_CODE[code]

   if KEY_NAMES_BY_KEYBOARD_EVENT_CODE[code]?
     key = KEY_NAMES_BY_KEYBOARD_EVENT_CODE[code]


Scroll to comment form when reply clicked

--- lineman/app/components/thread_page/comment_form/comment_form.coffee
+++ lineman/app/components/thread_page/comment_form/comment_form.coffee
@@ -3,7 +3,7 @@
   restrict: 'E'
   templateUrl: 'generated/components/thread_page/comment_form/comment_form.html'
   replace: true
-  controller: ($scope, $rootScope, FlashService, Records, CurrentUser, KeyEventService, AbilityService) ->
+  controller: ($scope, $rootScope, FlashService, Records, CurrentUser, KeyEventService, AbilityService, ScrollService) ->
     discussion = $scope.comment.discussion()
     group = $scope.comment.group()

@@ -33,6 +33,7 @@

     $scope.$on 'replyToCommentClicked', (event, parentComment) ->
       $scope.comment.parentId = parentComment.id
+      ScrollService.scrollTo('.comment-form__comment-field')

     $scope.removeAttachment = (attachment) ->
       ids = $scope.comment.newAttachmentIds


:lipstick:

:lipstick:

--- spec/app/buffer-spec.coffee
+++ spec/app/buffer-spec.coffee
@@ -343,7 +343,7 @@
         saveBuffer.save()
         expect(fs.read(filePath)).toEqual 'Buffer contents!'

-      it "fires beforeSave and afterSave events around the call to fs.write", ->
+      it "fires before-save and after-save events around the call to fs.write", ->
         events = []
         beforeSave1 = -> events.push('beforeSave1')
         beforeSave2 = -> events.push('beforeSave2')


Add test for hexadecimal integer constants.

--- spec/powershell-spec.coffee
+++ spec/powershell-spec.coffee
@@ -228,6 +228,15 @@
           expect(tokens[0].value).toEqual constant
           expect(tokens[0]).toHaveScope "constant.numeric.float.powershell"

+    describe "Constant hexadecimal values", ->
+      constants = [ "0x1234", "0x1FF2", "0xff2e" ]
+
+      it "tokenizes constant hexadecimal integer values", ->
+        for constant in constants
+          {tokens} = grammar.tokenizeLine constant
+          expect(tokens[0].value).toEqual constant
+          expect(tokens[0]).toHaveScope "constant.numeric.integer.hexadecimal.powershell"
+
   describe "Highlighting types", ->
     types = [ "[string]", "[Int32]", "[System.Diagnostics.Process]"]


Tests for the packageWatcher

Tests for the packageWatcher

--- spec/ui-watcher-spec.coffee
+++ spec/ui-watcher-spec.coffee
@@ -3,19 +3,43 @@
 ThemeManager = require 'theme-manager'

 UIWatcher = require '../lib/ui-watcher.coffee'
+PackageWatcher = require '../lib/package-watcher.coffee'

 describe "UIWatcher", ->
   themeManager = null
   uiWatcher = null

-  beforeEach ->
-    themeManager = new ThemeManager()
-    uiWatcher = new UIWatcher({ themeManager })
+  describe "when a base theme's file changes", ->
+    beforeEach ->
+      themeManager = new ThemeManager()
+      uiWatcher = new UIWatcher({ themeManager })

-  describe "when a base theme's file changes", ->
     it "reloads all the base styles", ->
       spyOn(atom, 'reloadBaseStylesheets')

-      uiWatcher.baseTheme.entities[0].trigger('contents-changed')
+      uiWatcher.baseTheme.entities[1].trigger('contents-changed')

       expect(atom.reloadBaseStylesheets).toHaveBeenCalled()
+
+  describe "when a package stylesheet file changes", ->
+    beforeEach ->
+      atom.activatePackage("package-with-stylesheets-manifest")
+      themeManager = new ThemeManager()
+      uiWatcher = new UIWatcher({ themeManager })
+
+    it "reloads all package styles", ->
+      pack = atom.getActivePackages()[0]
+      spyOn(pack, 'reloadStylesheets')
+
+      _.last(uiWatcher.watchers).entities[1].trigger('contents-changed')
+
+      expect(pack.reloadStylesheets).toHaveBeenCalled()
+
+  describe "when a package does not have a stylesheet", ->
+    beforeEach ->
+      atom.activatePackage("package-with-index")
+      themeManager = new ThemeManager()
+      uiWatcher = new UIWatcher({ themeManager })
+
+    it "does not create a PackageWatcher", ->
+      expect(_.last(uiWatcher.watchers)).not.toBeInstanceOf PackageWatcher


Troubleshoot: webtermPing method is added to new terminal kite

--- client/Main/kite/kites/terminalkite.coffee
+++ client/Main/kite/kites/terminalkite.coffee
@@ -6,3 +6,4 @@
     webtermGetSessions: 'webterm.getSessions'
     webtermConnect    : 'webterm.connect'
     webtermKillSession: 'webterm.killSession'
+    webtermPing       : 'webterm.ping'


Remove unused, and make all sockets join $global channel

--- server/packages/socket/SocketIo.coffee
+++ server/packages/socket/SocketIo.coffee
@@ -12,8 +12,6 @@
 	constructor: ->
 		super

-	channelsSocketIsIn: (socket) -> @io.sockets.manager.roomClients[socket.id]
-
 	socketsInChannel: (channel) -> @io.sockets.clients channel

 	listen: (http) ->
@@ -40,6 +38,8 @@
 		@io.sockets.on 'connection', (socket) =>
 			req = socket.handshake

+			socket.join '$global'
+
 			req.http = http
 			req.socket = socket


docstring: Provide better informtion, in context.

--- client/app/lib/terminal/webtermview.coffee
+++ client/app/lib/terminal/webtermview.coffee
@@ -98,14 +98,8 @@
         if event.status is Stopped
           @messagePane.handleError message: "ErrNoSession"

-    # If the machine is resizing, it will have a status of Stopping
-    # but never Stopped. For resizing, status progress goes:
-    #
-    #   Stopping -> Pending -> Starting -> Pending -> Running
-    #
-    # As such, we need to listen specifically for the resize-
-    # event to know if this is a resizing Terminal and needs to
-    # show ErrNoSession.
+    # Listen for resize events to show an error after a resize has
+    # finished. This provides same UX as a stopped machine (above).
     computeController.on "resize-#{machineId}", (event) =>
       if event.percentage is 100
         @messagePane.handleError message: "ErrNoSession"


:watermelon::arrow_up_small: Updated in browser at strd6.github.io/editor

--- load-sound-font.coffee
+++ load-sound-font.coffee
@@ -68,6 +68,7 @@

   while(i <= il)
     if (preset[i])
+      i += 1
       continue

     sampleId = getModGenAmount(generator, 'sampleID');


added comments

added comments

--- app/coffee/UrlFetcher.coffee
+++ app/coffee/UrlFetcher.coffee
@@ -20,8 +20,9 @@

 		logger.log url:url, filePath: filePath, "started downloading url to cache"
 		urlStream = request.get({url: url, timeout: oneMinute})
-		urlStream.pause()
+		urlStream.pause() # stop data flowing until we are ready

+		# attach handlers before setting up pipes
 		urlStream.on "error", (error) ->
 			logger.error err: error, url:url, filePath: filePath, "error downloading url"
 			callbackOnce(error or new Error("Something went wrong downloading the URL #{url}"))
@@ -33,6 +34,7 @@
 			if res.statusCode >= 200 and res.statusCode < 300
 				fileStream = fs.createWriteStream(filePath)

+				# attach handlers before setting up pipes
 				fileStream.on 'error', (error) ->
 					logger.error err: error, url:url, filePath: filePath, "error writing file into cache"
 					fs.unlink filePath, (err) ->
@@ -48,7 +50,7 @@
 					logger.log url:url, filePath: filePath, "piping into filestream"

 				urlStream.pipe(fileStream)
-				urlStream.resume()
+				urlStream.resume() # now we are ready to handle the data
 			else
 				logger.error statusCode: res.statusCode, url:url, filePath: filePath, "unexpected status code downloading url to cache"
 				# https://nodejs.org/api/http.html#http_class_http_clientrequest


Add a flag to disable login URL signing.

--- lib/saml2.coffee
+++ lib/saml2.coffee
@@ -383,8 +383,12 @@
       zlib.deflateRaw xml, (err, deflated) =>
         return cb err if err?
         uri = url.parse identity_provider.sso_login_url
-        uri.query =
-          sign_get_request deflated.toString('base64'), @private_key, relay_state
+        if options?.no_signature
+          uri.query = SAMLRequest: deflated.toString 'base64'
+          uri.query.RelayState = relay_state if relay_state?
+        else
+          uri.query =
+            sign_get_request deflated.toString('base64'), @private_key, relay_state
         cb null, url.format(uri), id

     # Returns an object containing the parsed response.


Provide more information on parser errors

--- src/lib/Fbp.coffee
+++ src/lib/Fbp.coffee
@@ -40,25 +40,25 @@

             connection = @matchConnection.exec currentString
             if connection
-                throw "Port or initial expected" unless @lastElement is "initial" or @lastElement is "port"
+                throw "Port or initial expected, got #{currentString}" unless @lastElement is "initial" or @lastElement is "port"
                 @lastElement = "connection"
                 @handleConnection connection
                 currentString = ""
             initial = @matchInitial currentString
             if initial
-                throw "Newline expected" unless @lastElement is null
+                throw "Newline expected, gor #{currentString}" unless @lastElement is null
                 @lastElement = "initial"
                 @handleInitial initial
                 currentString = ""
             component = @matchComponent currentString
             if component
-                throw "Port or newline expected" unless @lastElement is "port" or @lastElement is null
+                throw "Port or newline expected, got #{currentString}" unless @lastElement is "port" or @lastElement is null
                 @lastElement = "component"
                 @handleComponent component
                 currentString = ""
             port = @matchPort currentString
             if port
-                throw "Connection or component expected" unless @lastElement is "connection" or @lastElement is "component"
+                throw "Connection or component expected, got #{currentString}" unless @lastElement is "connection" or @lastElement is "component"
                 @lastElement = "port"
                 @handlePort port
                 currentString = ""


delete dead code, fixes #126

delete dead code, fixes #126

--- src/lib/dns.coffee
+++ src/lib/dns.coffee
@@ -258,10 +258,3 @@
                 @log.error gLineInfo('exception sending error back!'), e.stack
             cb()
             false # helps other functions pass back an error value
-
-        resolve: (path, options, cb) ->
-            req = new Packet()
-            req.question.push dns2.Question {name: path, type: options.type if options?.type? and _.has NAME_QTYPE,options.type}
-            @oldDNSLookup req, (packet, code) ->
-                code = {code:code, name:RCODE_NAME[code]} if code
-                cb code, packet


Spec for resolving

Spec for resolving

--- spec/ioc_spec.coffee
+++ spec/ioc_spec.coffee
@@ -2,7 +2,7 @@
   uuid = -> require('uuid').v4()
   createSpyObj = (args...) -> jasmine.createSpyObj args...

-  spyObjects =
+  mocks =
     component: (name = "component_#{uuid()}") -> createSpyObj name, ["resolve"]
     promise: (name = "promise_#{uuid()}") -> createSpyObj name, ["then"]

@@ -24,8 +24,8 @@

       beforeEach ->
         componentName = uuid()
-        resolution = spyObjects.promise()
-        component = spyObjects.component()
+        resolution = mocks.promise()
+        component = mocks.component()

         components.set componentName, component
         spyOn(resolutions, 'set').and.callThrough()
@@ -52,3 +52,31 @@
         it "returns the stored resolution", ->
           result = container.resolveComponent componentName
           expect(result).toEqual resolution
+
+    describe "resolving", ->
+      [componentName, resolution] = []
+
+      beforeEach ->
+        componentName = uuid()
+        resolution = mocks.promise()
+        resolution.then.and.callFake (success) -> success()
+        spyOn(container, "resolveComponent").and.returnValue resolution
+
+      it "resolves the component", (done) ->
+        container.resolve componentName, (error, result) ->
+          expect(container.resolveComponent).toHaveBeenCalledWith componentName
+          done(error)
+
+      it "proxies the resolution result on succes", (done) ->
+        mockResult = uuid()
+        resolution.then.and.callFake (success) -> success mockResult
+        container.resolve componentName, (error, result) ->
+          expect(result).toEqual mockResult
+          done(error)
+
+      it "proxies the resolution error on failure", (done) ->
+        mockError = uuid()
+        resolution.then.and.callFake (success, failure) -> failure mockError
+        container.resolve componentName, (error, result) ->
+          expect(error).toEqual mockError
+          done()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -10,7 +10,7 @@
 enemy = GameObject
   health: 500

-player.meter 'heal'
+player.meter 'health'
 # => Uncaught TypeError: Object has no method 'position'

 player.include(Bounded)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -98,11 +98,8 @@
   ###*
   Configures a meter to be drawn each overlay event.

-  The position of this game object. By default it is the top left point.
-  Redefining the center method will change the relative position.
-

-  player = Core
+  player = GameObj
     x: 50
     y: 40


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -1,6 +1,7 @@
 Metered = (I={}, self) ->
   Object.reverseMerge I,
     meters: []
+

   self.bind 'overlay', (canvas) ->
     I.meters.each (meterData) ->


More mucking around for graphics.

More mucking around for graphics.

--- src/methods.coffee
+++ src/methods.coffee
@@ -63,8 +63,25 @@
       ]
   java:
     awt:
+      Component: [
+        o '<clinit>()V', ->
+        o 'setBoundsOp(I)V', (rs, _this, op) -> _this.fields.boundsOp = op
+        o 'getBoundsOp()I', (rs, _this) -> _this.fields.boundsOp
+        o 'reshape(IIII)V', -> # TODO
+      ]
+      Container: [
+        o '<clinit>()V', ->
+      ]
       Frame: [
         o '<clinit>()V', ->
+      ]
+      Window: [
+        o '<clinit>()V', ->
+        o 'show()V', (rs, _this) ->
+            # XXX fails: _this does not even seem to be a Window
+            rs.push _this, 200
+            rs.method_lookup({class:'java/awt/Window',sig:{name:'postWindowEvent',type:'(I)V'}}).run(rs)
+            console.log 'showing window!'
       ]
     lang:
       ref:
@@ -335,6 +352,7 @@
               rs.field_lookup({class: 'java/lang/Thread', sig: {name:'threadSeqNumber'}}).static_value = 0
             rs.main_thread
         o 'setPriority0(I)V', (rs) -> # NOP
+        o 'holdsLock(L!/!/Object;)Z', -> true
         o 'isAlive()Z', (rs) -> false
         o 'start0()V', (rs) -> # NOP
         o 'sleep(J)V', (rs, millis) ->
@@ -396,6 +414,13 @@
             stats = fs.statSync rs.jvm2js_str rs.get_obj file.fields.path
             return 0 unless stats?
             if stats.isFile() then 3 else if stats.isDirectory() then 5 else 1
+        o 'canonicalize0(L!/lang/String;)L!/lang/String;', (rs, _this, jvm_path_str) ->
+            try
+              path = require 'path'
+            catch e
+              util.java_throw 'java/lang/UnsupportedOperationException', 'Filesystem ops are not supported in the browser'
+            js_str = rs.jvm2js_str jvm_path_str
+            rs.init_string path.resolve path.normalize js_str
       ]
   sun:
     misc:


feat(commands): display open pull request total

First message displays the total number of open pull requests

--- src/octocat.coffee
+++ src/octocat.coffee
@@ -54,7 +54,7 @@

     # Short circuit if there is no GitHub connection
     if !github_conn
-      msg.send "Unable to obtain GitHub connection, please verify your credentials"
+      msg.send "Unable to obtain GitHub connection, please verify your credentials."
       return

     repo_name = msg.match[4]
@@ -62,24 +62,43 @@
       getPullRequests(repo_name)
         .then (results) ->
           if results.length is 0
-            msg.send "There are no open pull requests for #{ repo_name }"
+            msg.send "There are no open pull requests for #{ repo_name }."
           else
-            postRepoHeader results[0].head.repo.full_name if results.length > 0
+            is_singular = results.length is 1
+            msg.send "There #{ if is_singular then 'is' else 'are' } #{ results.length } open pull request" +
+              "#{ if !is_singular then 's' else '' } for #{ repo_name }."
+            postRepoHeader(results[0].head.repo.full_name) if results.length > 0
             postPullRequests results
         .catch (error) ->
           handlePRError error, repo_name
     else
       github_conn.reposAsync()
         .then (repos) ->
-          for repo in repos
-            getPullRequests(repo.full_name)
-              .then (results) ->
-                postRepoHeader results[0].head.repo.full_name if results.length > 0
-                postPullRequests results
-              .catch (error) ->
-                handlePRError error, repo.full_name
+          Promise.reduce(repos, pullRequestReducer, {}).then (pull_requests) ->
+            keys = Object.keys pull_requests
+            if keys.length is 0
+              msg.send "There are no open pull requests, get to work!"
+            else
+              is_singular = keys.length is 1
+              msg.send "There #{ if is_singular then 'is' else 'are' } #{ keys.length } open pull request" +
+                "#{ if !is_singular then 's' else }."
+              keys.sort()
+              for k in keys
+                postRepoHeader k
+                postPullRequests pull_requests[k]
         .catch (error) ->
-          msg.send "Unable to retreive repositories, please verify your credentials"
+          msg.send "Unable to retreive repositories, please verify your credentials."
+
+    # Maps pull requests by repository and counts total
+    pullRequestReducer = (pull_requests, repo) ->
+      return getPullRequests(repo.full_name)
+        .then (results) ->
+          if results.length > 0
+            pull_requests[results[0].head.repo.full_name] = results
+          return pull_requests
+        .catch (error) ->
+          handlePRError error, repo.full_name
+          return pull_requests

     # Post a list of pull requests
     postPullRequests = (requests) ->


Fixed NavItemLink to send the href to the underlying NavItem

--- src/NavItemLink.cjsx
+++ src/NavItemLink.cjsx
@@ -21,6 +21,7 @@
   render: ->
     @transferPropsTo(
       <NavItem
+        href={@getHref()}
         active={@state.isActive}
         onClick={@handleClick}>
         {@props.children}


Massage message a bit

Massage message a bit

--- src/project.coffee
+++ src/project.coffee
@@ -329,9 +329,9 @@

     if fileSize >= 20 * 1048576 # 20MB
       choice = atom.confirm
-        message: 'Atom can have issues with files over 20MB.'
-        detailedMessage: "Do you still want to try to load this?"
-        buttons: ["Open", "Cancel"]
+        message: 'Atom currently freezes during the loading of very large files.'
+        detailedMessage: "Do you still want to load this file?"
+        buttons: ["Proceed", "Cancel"]
       if choice is 1
         error = new Error
         error.code = 'CANCELLED'


address reviewer comments

address reviewer comments

--- bokehjs/src/coffee/common/plot.coffee
+++ bokehjs/src/coffee/common/plot.coffee
@@ -457,12 +457,13 @@

     # We need the parent node, because the el node itself collapses to zero
     # height. It might not be available though, if the initial resize
-    # happened before Bokeh got properly initialized. If that happens, we
-    # will try again, but only once.
-    if not @.el.parentNode and not @_re_resized
-      that = this
-      setTimeout( (-> that.resize_width_height(use_width, use_height, maintain_ar)), 10)
-      @_re_resized = true
+    # happened before the plot was added to the DOM. If that happens, we
+    # try again in increasingly larger intervals (the first try should just
+    # work, but lets play it safe).
+    @_re_resized = @_re_resized or 0
+    if not @.el.parentNode and @_re_resized < 14  # 2**14 ~ 16s
+      setTimeout( (=> this.resize_width_height(use_width, use_height, maintain_ar)), 2**@_re_resized)
+      @_re_resized += 1
       return

     avail_width = @.el.clientWidth


funcion recibirBombas, entraba al for cuando no existian barcos

--- proyecto/public/js/controllers/tablero-controller.coffee
+++ proyecto/public/js/controllers/tablero-controller.coffee
@@ -56,11 +56,12 @@
     # actualiza array bombasTablero
     # la directiva ng-repeat utiliza este array p/dibujar las bombas
     $scope.recibirBombas=(x,y)->
-       for i in [0..$scope.barcos.length-1]
-         $scope.barcos[i].recibirBomba(x,y)
-       if $scope.checkearBarcosHundidos()
-         alert 'flota hundida'
-       actualizarArregloBombasTablero(x,y)
+      if $scope.barcos.length >0
+          for i in [0..$scope.barcos.length-1]
+             $scope.barcos[i].recibirBomba(x,y)
+          if $scope.checkearBarcosHundidos()
+              alert 'flota hundida'
+          actualizarArregloBombasTablero(x,y)

     #esta funcion actualiza el arreglo de sitios bombardeados, para que
     # la directiva bomba, dibuje las bombas


Switched the 'Tags:' heading to use h2 here too.

--- src/Views/TagBarView.coffee
+++ src/Views/TagBarView.coffee
@@ -4,7 +4,7 @@
 	el: $("#TagBar")

 	render: =>
-		pre = "Tags:
"
+		pre = "
Tags
"
 		ret =  ((@make("a", {"class": "tag", "name": t.get("name")}, t.get("name"))).outerHTML for t in @options.models)
 		ret = "#{pre}#{ret.join(", ")}"
 		$(this.el).html(ret)


Wait for nextTick after presenter updates

This gives any nextTick callbacks in the model a chance to complete
before proceeding with tests.

--- spec/text-editor-presenter-spec.coffee
+++ spec/text-editor-presenter-spec.coffee
@@ -67,7 +67,7 @@
         fn?()
         disposable = presenter.onDidUpdateState ->
           disposable.dispose()
-          done()
+          process.nextTick(done)

     tiledContentContract = (stateFn) ->
       it "contains states for tiles that are visible on screen", ->


Make project access more explicit

Make project access more explicit

--- app/pages/project/index.cjsx
+++ app/pages/project/index.cjsx
@@ -135,10 +135,15 @@
       query =
         owner: props.params.owner
         slug: props.params.name
+
       @promiseToSetState project: auth.checkCurrent().then ->
-        # TODO: This refresh is a little annoying. Can get the complete resource somehow?
-        apiClient.type('projects').get(query).index(0).refresh().catch ->
-          throw new Error "Couldn't find project #{props.params.owner}/#{props.params.name}"
+        apiClient.type('projects').get query
+          .catch ->
+            []
+          .then ([project]) ->
+            unless project?
+              throw new Error "Couldn't find project #{props.params.owner}/#{props.params.name}"
+            project

   render: ->
     if @state.project?


Text edit

Text edit

--- web/scripts/ref.coffee
+++ web/scripts/ref.coffee
@@ -1,7 +1,7 @@
 window.inject = (d) ->

 	d.innerHTML = '
A short introduction to Java (Draft Version!)

-
    A gift from the undergrad CS students at the University of Illinois at Urbana Champaign (UIUC).

+
    By undergraduate CS students at the University of Illinois at Urbana Champaign (UIUC).

    When first looking at <tt>code</tt>, it can be very confusing and disorienting because it is so different from conventional human language.  Once you understand your first language, picking up another will be considerably easier, and something you may have to do quickly and often if you program professionally.  In these regards, <tt>Java</tt> is no different.


recursive, safe

recursive, safe

--- src/scripts/ui-scroll.coffee
+++ src/scripts/ui-scroll.coffee
@@ -47,14 +47,11 @@
 							angular.isObject(datasource) and datasource.get and angular.isFunction(datasource.get)

 						getValueChain = (targetScope, target) ->
-							return targetScope[target] if target.indexOf('.') is -1
-							chain = target.split('.')
-							src = targetScope
-							for ring in chain
-								if !src.hasOwnProperty(ring)
-									throw new Error "Chain walk error: can't find \"" + ring + "\" property within \"" + target + "\" chain"
-								src = src[ring]
-							return src
+							chain = target.match(/^([\w]+)\.(.+)$/)
+							if not chain or chain.length isnt 3
+								return if not targetScope.hasOwnProperty(target) then null else targetScope[target]
+							return null if not targetScope.hasOwnProperty(chain[1])
+							getValueChain(targetScope[chain[1]], chain[2])

 						datasource = getValueChain($scope, datasourceName)


Use tags to filter specs

Use tags to filter specs

--- spec/pathwatcher-spec.coffee
+++ spec/pathwatcher-spec.coffee
@@ -49,11 +49,8 @@
         expect(eventType).toBe 'change'
         expect(eventPath).toBe ''

-  describe 'when a watched path is renamed', ->
+  describe 'when a watched path is renamed #darwin #win32', ->
     it 'fires the callback with the event type and new path and watches the new path', ->
-      if process.platform is 'linux'
-        return
-
       eventType = null
       eventPath = null
       watcher = pathWatcher.watch tempFile, (type, path) ->
@@ -68,11 +65,8 @@
         expect(eventPath).toBe fs.realpathSync(tempRenamed)
         expect(pathWatcher.getWatchedPaths()).toEqual [watcher.handleWatcher.path]

-  describe 'when a watched path is deleted', ->
+  describe 'when a watched path is deleted #win32 #darwin', ->
     it 'fires the callback with the event type and null path', ->
-      # FIXME Fails on Travis CI
-      return if process.platform is 'linux'
-
       deleted = false
       watcher = pathWatcher.watch tempFile, (type, path) ->
         deleted = true if type is 'delete' and path is null


Wait for delete event explicitly

Wait for delete event explicitly

On Linux a change event followed by a delete event is fired on deletes
causing specs to intermittently fail depending on when the waitsFor
block check runs.

--- spec/pathwatcher-spec.coffee
+++ spec/pathwatcher-spec.coffee
@@ -70,17 +70,12 @@

   describe 'when a watched path is deleted', ->
     it 'fires the callback with the event type and null path', ->
-      eventType = null
-      eventPath = null
+      deleted = false
       watcher = pathWatcher.watch tempFile, (type, path) ->
-        eventType = type
-        eventPath = path
+        deleted = true if type is 'delete' and path is null

       fs.unlinkSync(tempFile)
-      waitsFor -> eventType?
-      runs ->
-        expect(eventType).toBe 'delete'
-        expect(eventPath).toBe null
+      waitsFor -> deleted

   describe 'when a file under watched directory is deleted', ->
     it 'fires the callback with the change event and empty path', (done) ->


:fire: old comment

:fire: old comment

--- spec/text-object-spec.coffee
+++ spec/text-object-spec.coffee
@@ -653,7 +653,7 @@
             mode: ['visual', 'linewise']
           ensure 'i B',
             selectedText: textSelected
-            mode: ['visual', 'characterwise'] # FIXME to 'characterwise'
+            mode: ['visual', 'characterwise']

         it "from vB, final-mode is 'characterwise'", ->
           ensure 'ctrl-v',
@@ -661,7 +661,7 @@
             mode: ['visual', 'blockwise']
           ensure 'i B',
             selectedText: textSelected
-            mode: ['visual', 'characterwise'] # FIXME to 'characterwise'
+            mode: ['visual', 'characterwise']

         describe "as operator target", ->
           it "change inner-pair", ->
@@ -747,7 +747,7 @@
             mode: ['visual', 'linewise']
           ensure 'a B',
             selectedText: textSelected
-            mode: ['visual', 'characterwise'] # FIXME to 'characterwise'
+            mode: ['visual', 'characterwise']

         it "from vB, final-mode is 'characterwise'", ->
           ensure 'ctrl-v',
@@ -755,7 +755,7 @@
             mode: ['visual', 'blockwise']
           ensure 'a B',
             selectedText: textSelected
-            mode: ['visual', 'characterwise'] # FIXME to 'characterwise'
+            mode: ['visual', 'characterwise']

         describe "as operator target", ->
           it "change inner-pair", ->


Unload a-theme in an afterEach

Unload a-theme in an afterEach

--- spec/theme-panel-spec.coffee
+++ spec/theme-panel-spec.coffee
@@ -25,6 +25,7 @@
     panel = new ThemePanel(packageManager)

   afterEach ->
+    atom.packages.unloadPackage('a-theme') if atom.packages.isPackageLoaded('a-theme')
     atom.themes.deactivateThemes()

   it "selects the active syntax and UI themes", ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/string_extensions.coffee
+++ src/string_extensions.coffee
@@ -191,3 +191,32 @@
 String::withoutExtension = ->
   this.replace(/\.[^\.]*$/, '')

+String::parseHex = ->
+  hexString = @replace(/#/, '')
+
+  switch hexString.length
+    when 3, 4
+      if hexString.length == 4
+        alpha = ((parseInt(hexString.substr(3, 1), 16) * 0x11) / 255)
+      else
+        alpha = 1
+
+      rgb = (parseInt(hexString.substr(i, 1), 16) * 0x11 for i in [0..2])
+      rgb.push(alpha)
+
+      return rgb
+
+    when 6, 8
+      if hexString.length == 8
+        alpha = (parseInt(hexString.substr(6, 2), 16) / 255)
+      else
+        alpha = 1
+
+      rgb = (parseInt(hexString.substr(2 * i, 2), 16) for i in [0..2])
+      rgb.push(alpha)
+
+      return rgb
+
+    else
+      return undefined
+


refactor($tests): change core specs descriptions

--- specs/units/core.spec.coffee
+++ specs/units/core.spec.coffee
@@ -1,10 +1,10 @@
 # examples groups

-runningSpecs('error raised in spec file')
+runningSpecs('an error raised in spec file')
 .shouldStopWith /message/, ->
   throw new Error 'message'

-runningSpecs('error raised in describe')
+runningSpecs('an error raised in describe')
 .shouldStopWith /message/, ->
   describe 'failing declaration', ->
     throw new Error 'message'
@@ -94,17 +94,17 @@
 .shouldSucceedWith /0 success, 0 assertions, (.*), 1 pending/, ->
   describe 'foo'

-runningSpecs('async example timing out')
+runningSpecs('an async example timing out')
 .shouldFailWith /1 failure/, ->
   it (async) ->
     async.rejectAfter 100, 'Timed out'

-runningSpecs('unhandled exception raised in example')
+runningSpecs('an unhandled exception raised in example')
 .shouldFailWith /0 assertions, 0 failures, 1 error/, ->
   describe 'failing example', ->
     it -> throw new Error 'message'

-runningSpecs('unhandled exception raised in example with expectations')
+runningSpecs('an unhandled exception raised in example with expectations')
 .shouldFailWith /1 assertion, 0 failures, 1 error/, ->
   describe 'failing example with expectation', ->
     it 'should have been stopped', ->
@@ -113,13 +113,13 @@

 # before

-runningSpecs('unhandled exception raised in before')
+runningSpecs('an unhandled exception raised in before')
 .shouldFailWith /0 assertions, 0 failures, 1 error/, ->
   describe 'with successful example', ->
     before -> throw new Error 'message'
     it -> true.should be true

-runningSpecs('async before hook timing out')
+runningSpecs('an async before hook timing out')
 .shouldFailWith /0 assertions, (.*), 1 error/, ->
   describe 'with successful example', ->
     before (async) ->
@@ -127,7 +127,7 @@

     it -> true.should be true

-runningSpecs('async before hook rejected')
+runningSpecs('a rejected async before hook')
 .shouldFailWith /0 assertions, (.*), 1 error/, ->
   describe 'with successful example', ->
     before (async) ->
@@ -137,19 +137,19 @@

 # after

-runningSpecs('unhandled exception raised in after')
+runningSpecs('an unhandled exception raised in after')
 .shouldFailWith /0 success, 1 assertion, 0 failures, 1 error/, ->
   describe 'with successful example', ->
     after -> throw new Error 'message'
     it -> true.should be true

-runningSpecs('unhandled exception raised in after')
+runningSpecs('an unhandled exception raised in after')
 .shouldFailWith /0 success, 1 assertion, 0 failures, 1 error/, ->
   describe 'with failing example', ->
     after -> throw new Error 'message'
     it -> true.should be false

-runningSpecs('async after hook timing out')
+runningSpecs('an async after hook timing out')
 .shouldFailWith /1 assertion, (.*), 1 error/, ->
   describe 'with successful example', ->
     after (async) ->
@@ -157,7 +157,7 @@

     it -> true.should be true

-runningSpecs('async after hook rejected')
+runningSpecs('a rejected async after hook')
 .shouldFailWith /1 assertion, (.*), 1 error/, ->
   describe 'with successful example', ->
     after (async) ->
@@ -166,7 +166,7 @@
     it -> true.should be true

 # matchers
-runningSpecs('unhandled exception raised in matcher')
+runningSpecs('an unhandled exception raised in matcher')
 .shouldFailWith /1 assertion, 0 failures, 1 error/, ->
   describe 'failing example', ->
     it -> {}.should throwing


Skip rendering the XML if it would be a 304 anyway

--- app.coffee
+++ app.coffee
@@ -210,6 +210,11 @@
 			res.sendStatus 406
 			return

+		res.set 'ETag', "#{if config.deterministic then '' else 'W/'}\"#{if username isnt '' then username + '-' else ''}#{lastUpdate.getTime()}\""
+		if req.fresh
+			res.sendStatus 304
+			return
+
 		res.type 'xml'

 		# build the xml structure of the package list
@@ -320,7 +325,6 @@
 			"""
 		do writer.endElement
 		do writer.endDocument
-		res.set 'ETag', "#{if config.deterministic then '' else 'W/'}\"#{if username isnt '' then username + '-' else ''}#{lastUpdate.getTime()}\""
 		res.status(200).send writer.toString()

 	checkAuth req, res, callback


Compat fix after CUI.DOM -> CUI.DOMElement change.

--- src/base/Layer/Layer.coffee
+++ src/base/Layer/Layer.coffee
@@ -309,7 +309,7 @@
 	knownPlacements: ["s", "e", "w", "ws", "wn", "n", "se", "ne", "es", "en", "nw", "sw", "c"]

 	__setElement: (element) ->
-		if element instanceof CUI.DOM
+		if element instanceof CUI.DOMElement
 			@__element = element.getElementForLayer()
 		else if element.DOM
 			@__element = element.DOM


add subscribeOnce alias in mediator

add subscribeOnce alias in mediator

--- src/chaplin/mediator.coffee
+++ src/chaplin/mediator.coffee
@@ -27,9 +27,10 @@

 # Mixin event methods from Backbone.Events,
 # create Publish/Subscribe aliases.
-mediator.subscribe   = Backbone.Events.on
-mediator.unsubscribe = Backbone.Events.off
-mediator.publish     = Backbone.Events.trigger
+mediator.subscribe     = Backbone.Events.on
+mediator.subscribeOnce = Backbone.Events.once
+mediator.unsubscribe   = Backbone.Events.off
+mediator.publish       = Backbone.Events.trigger

 # Initialize an empty callback list so we might seal the mediator later.
 mediator._callbacks = null


shuffle

shuffle

--- src/startup.coffee
+++ src/startup.coffee
@@ -12,20 +12,19 @@
 Settings = require 'settings'
 Storage = require 'storage'

-atom.native = new Native
-atom.storage = new Storage
 atom.event = new Event
-
 # atom.on, atom.off, etc.
 for name, method of atom.event
   atom[name] = atom.event[name]

+atom.native = new Native
+atom.storage = new Storage
 atom.keybinder = new KeyBinder
 atom.settings = new Settings
 atom.extensions = {}
 atom.extensionManager = new ExtensionManager
+
 atom.app = new App
-
 # atom.open, atom.close, etc.
 for name, method of atom.app
   atom[name] = atom.app[name]


Use a more modern snippet to insert for loops

--- user/snippets.cson
+++ user/snippets.cson
@@ -82,6 +82,9 @@
 	Require:
 		prefix: "req"
 		body: 'const ${1:module} = require("${1:module}");'
+	For:
+		prefix: "for"
+		body: "for(let i = 0, l = ${1:array}.length; i < l; ++i){\n\t$2\n}"

 # CoffeeScript/CSON


Break a long line.

Break a long line.

--- test/e2e/volumeSpec.coffee
+++ test/e2e/volumeSpec.coffee
@@ -64,7 +64,8 @@
       panel = page.overlaySelector()

     it 'should display the overlay selection buttons', ->
-      expect(panel, 'The overlay selection buttons are missing').to.eventually.exist
+      expect(panel, 'The overlay selection buttons are missing')
+        .to.eventually.exist

   describe 'Image Control Panel', ->
     panel = null


Use in query instead of or.

Use in query instead of or.

--- src/coffee/apiclient.coffee
+++ src/coffee/apiclient.coffee
@@ -14,10 +14,10 @@
     @dryRun = false

   getByExternalIds: (externalIds) ->
-    get = @client.categories.all().whereOperator('or')
-    _.each externalIds, (eId) ->
-      get.where("externalId = \"#{eId}\"")
-    get.fetch()
+    @client.categories
+    .all()
+    .query("externalId in #{externalIds.join(', ')}")
+    .fetch()

   update: (category, existingCategory, actionsToIgnore = [], context = {}) ->
     @logger.debug "performing update"


no do

no do

--- src/coffee/messenger.coffee
+++ src/coffee/messenger.coffee
@@ -204,7 +204,7 @@

             $cancel.click =>
               do @cancel
-              do @options.onClickClose?()
+              @options.onClickClose?()

               true


Remove obsolete jQuery reference

Remove obsolete jQuery reference

Noticed that "soon" is now

--- src/command-registry.coffee
+++ src/command-registry.coffee
@@ -35,9 +35,7 @@
 # ```coffee
 # atom.commands.add 'atom-text-editor',
 #   'user:insert-date': (event) ->
-#     editor = $(this).view().getModel()
-#     # soon the above above line will be:
-#     # editor = @getModel()
+#     editor = @getModel()
 #     editor.insertText(new Date().toLocaleString())
 # ```
 module.exports =


fix content migration file select for IE

test plan:
* using IE, use the new content migration ui
to select a file and import it

fixes #CNVS-7105

Change-Id: I5a3ecc50f5ba71a0398f2c150cf38be849eef969
Reviewed-on: https://gerrit.instructure.com/22637
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Bracken Mosbacker <48b846001bca4166dc3fb7711563afc17525921e@instructure.com>
Product-Review: Bracken Mosbacker <48b846001bca4166dc3fb7711563afc17525921e@instructure.com>
QA-Review: August Thornton <9adc7a1161ddf32ff608de792a7e50179545f026@instructure.com>

--- app/coffeescripts/views/content_migrations/subviews/ChooseMigrationFileView.coffee
+++ app/coffeescripts/views/content_migrations/subviews/ChooseMigrationFileView.coffee
@@ -23,7 +23,7 @@
     #   Handle cases for file size from IE browsers
     # @api private

-    fileSize: (fileElement) -> fileElement.files[0].size
+    fileSize: (fileElement) -> fileElement.files?[0].size

     # Validates this form element. This validates method is a convention used
     # for all sub views.
@@ -45,7 +45,7 @@
                     type: "required"
                     message: "You must select a file to import content from"

-      if fileElement?.files[0].size > @fileSizeLimit
+      if @fileSize(fileElement) > @fileSizeLimit
         fileErrors.push
                     type: "upload_limit_exceeded"
                     message: "You're migration cannot exceed #{@humanReadableSize(@fileSizeLimit)}"


Only disconnect the merge socket if all inbound sockets are closed

--- src/components/Merge.coffee
+++ src/components/Merge.coffee
@@ -18,6 +18,9 @@
         @inPorts.in.on "endgroup", =>
             @outPorts.out.endGroup()
         @inPorts.in.on "disconnect", =>
+            # Check that all ports have disconnected before emitting
+            for socket in @inPorts.in.sockets
+              return if socket.connected
             @outPorts.out.disconnect()

 exports.getComponent = ->


Make COMMAND its own dict

Make COMMAND its own dict

--- src/config/inspector.coffee
+++ src/config/inspector.coffee
@@ -25,12 +25,16 @@

 COMMAND_SIZE = my.button.size * 0.8
 COMMAND_SPACE = COMMAND_SIZE + my.margin / 2
-COMMAND_AUTHORITY = $.extend {}, BUTTON_AUTHORITY
-COMMAND_AUTHORITY['height'] = COMMAND_SIZE
-COMMAND_AUTHORITY['x'] = (world) -> world.index * COMMAND_SPACE + my.margin
-COMMAND_AUTHORITY['selected'] = (world) ->
+COMMAND_AUTHORITY = {
+  height: COMMAND_SIZE
+  width: (world) ->  world.get('height')
+  fill: my.color.button
+
+  x:(world) -> world.index * COMMAND_SPACE + my.margin
+  selected: (world) ->
     return false unless world.up.get('selected')
     world.index == world.get('current').get('programs').get('counter') + 1
+}

 display_commands = (name, programs) ->
   signals = programs.get('signals')


add extend function

add extend function

--- src/uapanfw.coffee
+++ src/uapanfw.coffee
@@ -137,7 +137,6 @@
   list_name = String(msg.match.shift())
   list_name = 'whitelist' if list_name.indexOf('w') == 0
   list_name = 'blacklist' if list_name.indexOf('b') == 0
-  #console.log list_name

   l_val = String(msg.match.shift())
   if extra = l_val.match /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?:\/\d{1,2}|))$/
@@ -184,11 +183,8 @@
       notifyAdmins "#{logmsg}\nReason: #{usermsg}"
       return

-  #console.log "#{l_val}: #{entry.type} => #{entry.val}"
-
   expires = String(msg.match.shift())
   if expires isnt 'undefined'
-    #console.log "processing expiration: #{expires}"
     extra = expires.match /\+(\d)([hdwMQy])/
     if extra?
       n = extra[1]
@@ -219,6 +215,81 @@
   notifySubscribers usermsg, who

+extendListEntry = (robot, msg) ->
+  fullcmd = String(msg.match.shift())
+  who = msg.envelope.user.name
+
+  list_name = String(msg.match.shift())
+  list_name = 'whitelist' if list_name.indexOf('w') == 0
+  list_name = 'blacklist' if list_name.indexOf('b') == 0
+
+  l_search = String(msg.match.shift())
+
+  if l_search.toLowerCase().indexOf('https://') == 0
+    usermsg = "#{list_name}ing of https links not supported."
+    return msg.reply usermsg
+
+  if l_search.toLowerCase().indexOf('http://') == 0
+    l_search = l_search.replace(/http:\/\//i,'')
+
+  logmsg = "#{modulename}: #{who} requested: #{list_name} extend #{l_search}"
+  robot.logger.info logmsg
+
+  # check for matches
+  matches = 0
+  entry = undefined
+  for obj in fwdata[list_name] when obj.val.indexOf(l_search) > -1
+    #console.log "obj.type: [#{obj.type}] l_search: [#{l_search}] obj.val: [#{obj.val}]"
+    if obj.val.indexOf(l_search) > -1
+      #console.log "l_search: [#{l_search}] MATCHED obj.val: [#{obj.val}]"
+      matches++
+      entry = obj
+  if matches != 1
+    usermsg = "search matched zero or more than a single entry; " +
+      "improve search term and try again"
+    return msg.reply usermsg
+  #console.log entry
+
+  expires = String(msg.match.shift())
+  if expires is 'undefined'
+    usermsg = "you must provide a new absolute or relative expiration"
+    return msg.reply usermsg
+
+  extra = expires.match /(-|\+|)(\d+)([hdwMQy])/
+  if extra?
+    direction = extra.shift()
+    n = extra.shift()
+    unit = extra.shift()
+    if direction == '+'
+      entry.expires = moment(entry.expires).add(n,unit).format()
+    else if direction == '-'
+      entry.expires = moment(entry.expires).subtract(n,unit).format()
+    else
+      entry.expires = moment().add(n,unit).format()
+  else if moment(expires).isValid()
+    entry.expires = moment(expires).format()
+  else
+    usermsg = "invalid expiration date: #{expires}"
+    return msg.reply usermsg
+
+  logmsg = "#{modulename}: #{who} requested: " +
+    "#{list_name} #{entry.type} #{entry.val} new expiration #{moment(entry.expires).format(timefmt)}"
+  robot.logger.info logmsg
+
+  fs.writeFileSync fwdata_file, JSON.stringify(fwdata), 'utf-8'
+
+  usermsg = "#{who} updated expiration for `#{entry.val}` #{list_name}ing. " +
+    "New expiration `#{entry.expires}`."
+  msg.reply usermsg
+
+  logmsg = "#{modulename}: robot responded to #{who}: " +
+    "updated entry in #{list_name}"
+  robot.logger.info logmsg
+
+  #usermsg = usermsg.replace(/  Change will be applied in \< 5 minutes\./, '')
+  notifySubscribers usermsg, who
+
+
 deleteListEntry = (robot, msg) ->
   fullcmd = String(msg.match.shift())
   who = msg.envelope.user.name
@@ -248,17 +319,12 @@
   for obj in fwdata[list_name]
     expires = moment(obj.expires)
     if l_type and l_type != obj.type
-      errfmt = 'type: [%s] != [%s]'
-      #console.log sprintf errfmt, l_type, obj.type
       new_entry.push obj
       continue
     if l_search and obj.val.indexOf(l_search) == -1
-      errfmt = 'search: indexOf[%s] not found in [%s]'
-      #console.log sprintf errfmt, l_search, obj.val
       new_entry.push obj
       continue
     if expires.isBefore() # now
-      #console.log 'expires: ['+ expires +'] is before now'
       new_entry.push obj
       continue
     deleted.push sprintf displayfmt, obj.type, obj.val,
@@ -302,16 +368,11 @@
   for obj in fwdata[list_name]
     expires = moment(obj.expires)
     if l_type and l_type != obj.type
-      #console.log 'skipping type: '+ obj.type
       continue
     if l_search and obj.val.indexOf(l_search) == -1
-      #console.log 'skipping search: '+ obj.val
       continue
     if expires.isBefore() # now
-      #console.log 'skipping expires: '+ obj.expires
       continue
-    #console.log 'adding to array: '
-    #console.log obj
     arr.push sprintf displayfmt, obj.type, obj.val, expires.fromNow(), obj.creator

   msg.reply "#{list_name} items and expirations\n```\n"+ arr.join("\n") + "\n```"
@@ -462,8 +523,9 @@
     cmds = ['```']
     arr = [
       modulename + " show (white|black)list [searchterm]"
-      modulename + " add (white|black)list <weburl.tld/etc|x.x.x.x>"
+      modulename + " add (white|black)list <weburl.tld/etc|x.x.x.x> [+7d]"
       modulename + " del (white|black)list <weburl.tld/etc|x.x.x.x>"
+      modulename + " extend (white|black)list <weburl.tld/etc|x.x.x.x> [[+-]20d]"
       modulename + " subscribe [username] - subscribe to change notifications"
       modulename + " unsubscribe [username]"
       modulename + " show subscribers"
@@ -507,6 +569,12 @@

     return deleteListEntry robot, msg

+  robot.respond /(?:firewall|fw) (?:extend|ext|e) (whitelist|wl|w|blacklist|bl|b) ([^ ]+)(?: ([^ ]+)|)$/i, (msg) ->
+    return unless isAuthorized msg
+    return unless is2fa msg
+
+    return extendListEntry robot, msg
+
   robot.respond /(?:firewall|fw) (?:subscribe|sub)(?: ([^ ]+))$/i, (msg) ->
     return subscribe robot, msg


improve safety fail user text; log.info as well

--- src/uapanfw.coffee
+++ src/uapanfw.coffee
@@ -22,6 +22,7 @@

 modulename = 'fw'
 fwdata_file = modulename + ".json"
+safety_fail_note = 'If this is time critical ask a human; otherwise please open a ticket.'
 displayfmt = '%-4s %-50s %-15s %s'
 timefmt = 'YYYY-MM-DD HH:mm:ss ZZ'
 svcQueueIntervalMs = 300 * 1000
@@ -41,6 +42,8 @@
   '10.9.192.252': 'Juneau-1'
   '10.9.192.253': 'Juneau-2'

+# borrowed from
+# http://stackoverflow.com/questions/9796764/how-do-i-sort-an-array-with-coffeescript
 sortBy = (key, a, b, r) ->
   r = if r then 1 else -1
   return -1*r if a[key] > b[key]
@@ -118,10 +121,12 @@

     # safety check !!
     if entry.val.match /^(?:137\.229\.|199\.165\.)/
-      usermsg = "Blocking CIDRs begining with 137.229. or 199.165. not allowed. "+
-        " If this is time critical ask a human for help; otherwise open a ticket."
-      notifySubscribers "Request by #{who} failed safety check: #{fullcmd}\nReason: #{usermsg}"
-      return msg.reply usermsg
+      usermsg = "Blocking CIDRs begining with 137.229. or 199.165. is not allowed. #{safety_fail_note}"
+      logmsg = "#{modulename}: Request by #{who} failed safety check: #{fullcmd}"
+      robot.logger.info logmsg
+      notifySubscribers "#{logmsg}\nReason: #{usermsg}"
+      #msg.reply usermsg
+      return

   else if extra = l_val.match /^([a-zA-Z][a-zA-Z0-9\.]+)$/
     entry.type = 'domain'
@@ -129,10 +134,12 @@

     # safety check !!
     if entry.val.toLowerCase().match /(?:alaska|uaf)\.edu$/
-      usermsg = "Blocking alaska.edu or uaf.edu not allowed. "+
-        " If this is time critical ask a human for help; otherwise open a ticket."
-      notifySubscribers "Request by #{who} failed safety check: #{fullcmd}\nReason: #{usermsg}"
-      return msg.reply usermsg
+      usermsg = "Blocking alaska.edu or uaf.edu is not allowed. #{safety_fail_note}"
+      logmsg = "#{modulename}: Request by #{who} failed safety check: #{fullcmd}"
+      robot.logger.info logmsg
+      notifySubscribers "#{logmsg}\nReason: #{usermsg}"
+      #msg.reply usermsg
+      return

   else
     entry.type = 'url'
@@ -145,10 +152,13 @@

     # safety check !!
     if entry.val.toLowerCase().match /[^\/]+(?:alaska|uaf)\.edu/
-      usermsg = "Blocking alaska.edu or uaf.edu not allowed. "+
-        " If this is time critical ask a human for help; otherwise open a ticket."
-      notifySubscribers "Request by #{who} failed safety check: #{fullcmd}\nReason: #{usermsg}"
-      return msg.reply usermsg
+      usermsg = "Blocking alaska.edu or uaf.edu is not allowed. #{safety_fail_note}"
+      logmsg = "#{modulename}: Request by #{who} failed safety check: #{fullcmd}"
+      robot.logger.info logmsg
+      notifySubscribers "#{logmsg}\nReason: #{usermsg}"
+      #msg.reply usermsg
+
+      return

   #console.log "#{l_val}: #{entry.type} => #{entry.val}"


Updated udefine to coffeelint configuration

--- src/udefine.coffee
+++ src/udefine.coffee
@@ -47,7 +47,7 @@
     result

   # Helper function to inject function/object into any object
-  root.udefine.inject = (obj, name) -> (res) ->
+  root.udefine.inject = (obj, name) -> (res) ->
     return unless obj? and name?
     obj[name] = res


Fixes #2280 -- add enumerable prototypal properties to autocompletion.

--- repl.coffee
+++ repl.coffee
@@ -47,7 +47,7 @@
       val = Script.runInThisContext obj
     catch error
       return
-    completions = getCompletions prefix, Object.getOwnPropertyNames Object val
+    completions = getCompletions prefix, (key for key of Object(val))
     [completions, prefix]

 # Attempt to autocomplete an in-scope free variable: `one`.


handle case where two people are using the same pic

--- @client/avatar.coffee
+++ @client/avatar.coffee
@@ -511,7 +511,7 @@
             @loading_cnt -= 1
             if @loading_cnt == 0
               loading.loading = false
-              loading.loaded = md5 JSON.stringify Object.keys(loaded_images)
+              loading.loaded = md5 "#{JSON.stringify(Object.keys(loaded_images))}-#{JSON.stringify((u.name for u in avatars_to_load))}"
               save loading
           pic.onerror = do(user) => =>
             @loading_cnt -= 1


Removed wrong line

Removed wrong line

--- src/core/environment.coffee
+++ src/core/environment.coffee
@@ -269,6 +269,7 @@
     @mode = 'preview'
     # set url to dev url in preview mode
     if @locals.dev? then @locals.url = @locals.dev
+    server = require './server'
     server.run this, callback

   build: (outputDir, callback) ->


Fix issue when queryStore is set to null

--- src/datadog-snapshot.coffee
+++ src/datadog-snapshot.coffee
@@ -19,7 +19,7 @@
 dogapi = require("dogapi")

 userGraphs = {}
-queryStore = null
+queryStore = {}
 saveQueryStore = null
 graphResponseDelay = 0

@@ -125,4 +125,4 @@
   robot.respond /(?:datadog|dd)\s+save(?:\s+that)?\s+as\s+(.*)/i, saveCommand
   robot.respond /(?:datadog|dd)\s+list/i, listCommand
   robot.respond /(?:datadog|dd)\s+delete\s+(.*)/i, deleteCommand
-  robot.respond /(?:datadog|dd)\s+describe\s+(.*)/i, describeCommand+  robot.respond /(?:datadog|dd)\s+describe\s+(.*)/i, describeCommand


CollaborativeWorkspace: Proper variable to get state of is old session.

--- client/CollaborativeWorkspace/collaborativeworkspace.coffee
+++ client/CollaborativeWorkspace/collaborativeworkspace.coffee
@@ -47,7 +47,8 @@
       cb = =>
         isOldSession = keys = snapshot.val()?.keys
         if isOldSession
-          @sessionData = keys
+          @isOldSession = yes
+          @sessionData  = keys
           @createPanel()
         else
           @createPanel()


teams: minor code styling

teams: minor code styling

--- client/activity/lib/components/privatechatpane/index.coffee
+++ client/activity/lib/components/privatechatpane/index.coffee
@@ -35,12 +35,12 @@

     <ChatPane
       thread     = { @props.thread }
-      className  = "PrivateChatPane"
+      className  = 'PrivateChatPane'
       messages   = { @props.messages }
       onSubmit   = { @bound 'onSubmit' }
       onLoadMore = { @bound 'onLoadMore' }
     >
-      <footer className="PrivateChatPane-footer">
+      <footer className='PrivateChatPane-footer'>
         <ChatInputWidget
           onSubmit     = { @bound 'onSubmit' }
           enableSearch = no


activity: minor

activity: minor

--- client/activity/lib/components/privatechatpane/index.coffee
+++ client/activity/lib/components/privatechatpane/index.coffee
@@ -22,7 +22,7 @@
         thread         = { @props.thread }
         className      = 'PrivateChatPane'
         onSubmit       = { @bound 'onSubmit' }
-        onInviteOthers = {@bound 'onInviteOthers'}
+        onInviteOthers = { @bound 'onInviteOthers'}
         onLoadMore     = { @bound 'onLoadMore' }
       />
       <footer className='PrivateChatPane-footer ChatPaneFooter'>


guest vms: don't initialize them from the client side

since guest accounts feature is somewhat "disabled", we should waste resources having the os kite prepare vms for guests.  Just don't call reset initially for guest accounts, as a dead-simple solution.

--- client/landing/app/MainApp/sidebar/sidebarcontroller.coffee
+++ client/landing/app/MainApp/sidebar/sidebarcontroller.coffee
@@ -34,7 +34,9 @@

     avatarAreaIconMenu.accountChanged account

-    finderController.reset()
+    # get registered users vms all ready to go (don't for guests right now):
+    finderController.reset()      if "registered" is account.type
+
     resourcesController.reset()

   resetAdminNavItems:->


updates jasmine tests to correspond to new capa template HTML

--- common/lib/xmodule/xmodule/js/spec/capa/display_spec.coffee
+++ common/lib/xmodule/xmodule/js/spec/capa/display_spec.coffee
@@ -16,7 +16,7 @@

     # note that the fixturesPath is set in spec/helper.coffee
     loadFixtures 'problem.html'
-
+
     spyOn Logger, 'log'
     spyOn($.fn, 'load').andCallFake (url, callback) ->
       $(@).html readFixtures('problem_content.html')
@@ -27,13 +27,13 @@
     it 'set the element from html', ->
       @problem999 = new Problem ("

-
@@ -62,7 +62,7 @@
       expect($('section.action input.reset')).toHandleWith 'click', @problem.reset

     it 'bind the show button', ->
-      expect($('section.action input.show')).toHandleWith 'click', @problem.show
+      expect($('section.action button.show')).toHandleWith 'click', @problem.show

     it 'bind the save button', ->
       expect($('section.action input.save')).toHandleWith 'click', @problem.save
@@ -126,14 +126,14 @@

     describe 'when the response is correct', ->
       it 'call render with returned content', ->
-        spyOn($, 'postWithPrefix').andCallFake (url, answers, callback) ->
+        spyOn($, 'postWithPrefix').andCallFake (url, answers, callback) ->
           callback(success: 'correct', contents: 'Correct!')
         @problem.check()
         expect(@problem.el.html()).toEqual 'Correct!'

     describe 'when the response is incorrect', ->
       it 'call render with returned content', ->
-        spyOn($, 'postWithPrefix').andCallFake (url, answers, callback) ->
+        spyOn($, 'postWithPrefix').andCallFake (url, answers, callback) ->
           callback(success: 'incorrect', contents: 'Incorrect!')
         @problem.check()
         expect(@problem.el.html()).toEqual 'Incorrect!'
@@ -159,7 +159,7 @@
     it 'POST to the problem reset page', ->
       spyOn $, 'postWithPrefix'
       @problem.reset()
-      expect($.postWithPrefix).toHaveBeenCalledWith '/problem/Problem1/problem_reset',
+      expect($.postWithPrefix).toHaveBeenCalledWith '/problem/Problem1/problem_reset',
           { id: 'i4x://edX/101/problem/Problem1' }, jasmine.any(Function)

     it 'render the returned content', ->
@@ -179,7 +179,7 @@

       it 'log the problem_show event', ->
         @problem.show()
-        expect(Logger.log).toHaveBeenCalledWith 'problem_show',
+        expect(Logger.log).toHaveBeenCalledWith 'problem_show',
             problem: 'i4x://edX/101/problem/Problem1'

       it 'fetch the answers', ->
@@ -198,7 +198,7 @@
       it 'toggle the show answer button', ->
         spyOn($, 'postWithPrefix').andCallFake (url, callback) -> callback(answers: {})
         @problem.show()
-        expect($('.show')).toHaveValue 'Hide Answer'
+        expect($('.show .show-label')).toHaveText 'Hide Answer(s)'

       it 'add the showed class to element', ->
         spyOn($, 'postWithPrefix').andCallFake (url, callback) -> callback(answers: {})
@@ -223,7 +223,7 @@
           expect($('label[for="input_1_1_3"]')).toHaveAttr 'correct_answer', 'true'
           expect($('label[for="input_1_2_1"]')).not.toHaveAttr 'correct_answer', 'true'

-    describe 'when the answers are alreay shown', ->
+    describe 'when the answers are already shown', ->
       beforeEach ->
         @problem.el.addClass 'showed'
         @problem.el.prepend '''
@@ -243,7 +243,7 @@

       it 'toggle the show answer button', ->
         @problem.show()
-        expect($('.show')).toHaveValue 'Show Answer'
+        expect($('.show .show-label')).toHaveText 'Show Answer(s)'

       it 'remove the showed class from element', ->
         @problem.show()
@@ -261,7 +261,7 @@
     it 'POST to save problem', ->
       spyOn $, 'postWithPrefix'
       @problem.save()
-      expect($.postWithPrefix).toHaveBeenCalledWith '/problem/Problem1/problem_save',
+      expect($.postWithPrefix).toHaveBeenCalledWith '/problem/Problem1/problem_save',
           'foo=1&bar=2', jasmine.any(Function)

     # TODO: figure out why failing


extra check in ui

extra check in ui

--- BaragonUI/app/models/Group.coffee
+++ BaragonUI/app/models/Group.coffee
@@ -12,8 +12,14 @@
     initialize: ({ @groupId }) ->

     parse: (data) =>
-        data.splitSources = utils.splitArray(data.sources.sort(), Math.ceil(data.sources.length/4))
-        data.splitDomains = utils.splitArray(data.domainsServed.sort(), Math.ceil(data.domainsServed.length/4))
+        if data.sources
+            data.splitSources = utils.splitArray(data.sources.sort(), Math.ceil(data.sources.length/4))
+        else
+            data.splitSources = []
+        if data.domainsServed
+            data.splitDomains = utils.splitArray(data.domainsServed.sort(), Math.ceil(data.domainsServed.length/4))
+        else
+            data.splitDomains = []
         data

     deleteSource: (source) =>


converted spaces to tabs

converted spaces to tabs

--- src/middleware/stats.coffee
+++ src/middleware/stats.coffee
@@ -10,32 +10,31 @@
 sdc = new statsd_client statsd_server

 exports.incrementTransactionCount = (ctx, done) ->
-  logger.info 'sending counts to statsd for ' + domain + '.' + ctx.authorisedChannel._id
-  transactionStatus = ctx.transactionStatus
-  try
-    sdc.increment domain + '.Channels' # Overall Counter
-    sdc.increment domain + '.Channels.' + transactionStatus #Overall Transaction Status
-    sdc.increment domain + '.Channels.' + ctx.authorisedChannel._id # Per channel
-    sdc.increment domain + '.Channels.' + ctx.authorisedChannel._id + '.' + transactionStatus # Per Channel Status
-  catch error
-    logger.error error
+	logger.info 'sending counts to statsd for ' + domain + '.' + ctx.authorisedChannel._id
+	transactionStatus = ctx.transactionStatus
+	try
+		sdc.increment domain + '.Channels' # Overall Counter
+		sdc.increment domain + '.Channels.' + transactionStatus #Overall Transaction Status
+		sdc.increment domain + '.Channels.' + ctx.authorisedChannel._id # Per channel
+		sdc.increment domain + '.Channels.' + ctx.authorisedChannel._id + '.' + transactionStatus # Per Channel Status
+	catch error
+		logger.error error

 exports.measureTransactionDuration = (ctx, done) ->
-  logger.info 'sending durations to statsd for ' + domain + '.' + ctx.authorisedChannel._id
-  transactionStatus = ctx.transactionStatus
-  try
-    sdc.timing domain + '.Channels'  , timer # Overall Timer
-    sdc.timing domain + '.Channels.' + transactionStatus, timer # Overall Transaction Status
-    sdc.timing domain + '.Channels.' + ctx.authorisedChannel._id, timer # Per Channel
-    sdc.timing domain + '.Channels.' + ctx.authorisedChannel._id + '.' + transactionStatus, timer #Per Channel Status
-  catch error
-    logger.error error
+	logger.info 'sending durations to statsd for ' + domain + '.' + ctx.authorisedChannel._id
+	transactionStatus = ctx.transactionStatus
+	try
+		sdc.timing domain + '.Channels'	, timer # Overall Timer
+		sdc.timing domain + '.Channels.' + transactionStatus, timer # Overall Transaction Status
+		sdc.timing domain + '.Channels.' + ctx.authorisedChannel._id, timer # Per Channel
+		sdc.timing domain + '.Channels.' + ctx.authorisedChannel._id + '.' + transactionStatus, timer #Per Channel Status
+	catch error
+		logger.error error

 exports.koaMiddleware = `function *statsMiddleware(next) {
-
-      yield next;
-      exports.incrementTransactionCount(this)
-      exports.measureTransactionDuration(this)
-      sdc.close();
-}`+		yield next;
+		exports.incrementTransactionCount(this)
+		exports.measureTransactionDuration(this)
+		sdc.close();
+}`


Fix: 終了時にエラーが出ていたのを修正

Fix: 終了時にエラーが出ていたのを修正

--- src/core/core.coffee
+++ src/core/core.coffee
@@ -26,8 +26,6 @@
   )
   mainWindow.loadURL("file://#{app.getAppPath()}/gui/index.html")
   mainWindow.on("closed", ->
-    # キャッシュ削除
-    session.defaultSession.clearCache()
     # guiの終了
     mainWindow = null
     return
@@ -47,6 +45,8 @@
 )

 app.on("window-all-closed", ->
+  # キャッシュ削除
+  session.defaultSession.clearCache(->)
   # アプリ終了
   app.quit()
   return


Fix: バージョン取得に失敗していたのを修正 close #43

Fix: バージョン取得に失敗していたのを修正 close #43

--- src/core/util.coffee
+++ src/core/util.coffee
@@ -79,23 +79,19 @@
     ver = _parseVersion(str)
     if ver is ""
       throw new Error("Error: Version Regexp Error")
-    _version = ver
-    return ver
   try
     text = await fs.readFile(path.join(config.get("blitzPath"), "Data", "version.txt"), "utf-8")
     ver = _parseVersion(text)
     if ver is ""
       throw new Error("Error: Version Regexp Error (#{text})")
-    _version = ver
-    return ver
   catch
     text = await fs.readFile(path.join(config.get("blitzPath"), "Data/version", "resources.txt"), "utf-8")
     reg = /^(\d+\.\d+\.\d+)$/.exec(text)
     if !reg?
       throw new Error("Error: Version Regexp Error (#{text})")
-    _version = ver
-    return reg[1]
-  return
+    ver = reg[1]
+  _version = ver
+  return ver

 ###*
  * 利用している端末を取得します


uses projectId argument in case it was passed to the showFile function

--- src/Models/ProjectRepository.coffee
+++ src/Models/ProjectRepository.coffee
@@ -60,7 +60,7 @@
   showFile: (projectId, params, fn = null) =>
     @debug "Projects::showFile()", params
     if params.file_path and params.ref
-      @post "projects/#{Utils.parseProjectId params.projectId}/repository/files", params, (data) => fn data if fn
+      @post "projects/#{Utils.parseProjectId(projectId || params.projectId)}/repository/files", params, (data) => fn data if fn

   createFile: (params = {}, fn = null) =>
     @debug "Projects::createFile()", params


data-type validation should validate 'Invalid Date'

--- src/data-type.coffee
+++ src/data-type.coffee
@@ -125,6 +125,7 @@
         buffer.writeUInt8(0)
     validate: (value) ->
       if not value? then return null
+      if `value == "Invalid Date"` then return new TypeError "Invalid date."
       if value instanceof Date then return value
       value = Date.parse value
       if isNaN value then return new TypeError "Invalid date."
@@ -207,6 +208,7 @@
         buffer.writeUInt8(0)
     validate: (value) ->
       if not value? then return null
+      if `value == "Invalid Date"` then return new TypeError "Invalid date."
       if value instanceof Date then return value
       value = Date.parse value
       if isNaN value then return new TypeError "Invalid date."
@@ -911,6 +913,7 @@
         buffer.writeUInt8 0
     validate: (value) ->
       if not value? then return null
+      if `value == "Invalid Date"` then return new TypeError "Invalid date."
       if value instanceof Date then return value
       value = Date.parse value
       if isNaN value then return new TypeError "Invalid date."
@@ -973,6 +976,7 @@
         buffer.writeUInt8 0
     validate: (value) ->
       if not value? then return null
+      if `value == "Invalid Date"` then return new TypeError "Invalid date."
       if value instanceof Date then return value
       value = Date.parse value
       if isNaN value then return new TypeError "Invalid date."
@@ -1032,6 +1036,7 @@
         buffer.writeUInt8 0
     validate: (value) ->
       if not value? then return null
+      if `value == "Invalid Date"` then return new TypeError "Invalid date."
       if value instanceof Date then return value
       value = Date.parse value
       if isNaN value then return new TypeError "Invalid date."


Anchor the rxTsLine regexp.

Anchor the rxTsLine regexp.

--- src/parsers/slackapp.coffee
+++ src/parsers/slackapp.coffee
@@ -9,7 +9,7 @@
 # RegExps
 rxWs = /^\s*$/
 rxNickLine = new RegExp("^\\s*(\\S+)\\s+#{TS}\\s*$")
-rxTsLine = new RegExp("^\\s*#{TS}\\s*")
+rxTsLine = new RegExp("^\\s*#{TS}\\s*$")
 rxNewMessagesLine = new RegExp("^\\snew messages*\\s*$")

 parseTs = (ts) ->


delay attr actions when needed as well

delay attr actions when needed as well

--- src/dt-jquery.coffee
+++ src/dt-jquery.coffee
@@ -3,6 +3,15 @@
 # TODO use requestAnimationFrame to update dom
 # TODO listen on data and use innerHTML to create all dom elems at once
 #       http://blog.stevenlevithan.com/archives/faster-than-innerhtml
+
+# delay or invoke job immediately
+delay = (el, job) ->
+    # only when tag is ready
+    if el._jquery?
+        do job
+    else
+        el._jquery_delay ?= []
+        el._jquery_delay.push(job)

 jqueryify = (tpl) ->
     tpl.xml._jquery = $()
@@ -11,21 +20,15 @@
         el._jquery = $(el.toString())
         # invoke all delayed jquery work
         if el._jquery_delay?
-            for delay in el._jquery_delay
-                delay?()
+            for job in el._jquery_delay
+                do job
             delete el._jquery_delay
         # insert into parent
-        append = ->
+        delay el.parent, ->
             if el.parent is tpl.xml
                 el.parent._jquery = el.parent._jquery.add(el._jquery)
             else
                 el.parent._jquery.append(el._jquery)
-        # only when parent is ready
-        if el.parent._jquery?
-            append()
-        else
-            el.parent._jquery_delay ?= []
-            el.parent._jquery_delay.push(append)

         el.on 'newListener', (type) ->
             return if el._events?[type]?.length # dont bind two times for the same type
@@ -34,21 +37,17 @@
                 return # dont return emit result (which is either true or false)

     tpl.on 'text', (el, text) ->
-        settext = ->
+        delay el, ->
             el._jquery.text(text)
-        # only when tag is ready
-        if el._jquery?
-            settext()
-        else
-            el._jquery_delay ?= []
-            el._jquery_delay.push(settext)

     tpl.on 'attr', (el, key, value) ->
-        el._jquery?.attr key, value
+        delay el, ->
+            el._jquery.attr(key, value)

     tpl.on 'attr:remove', (el, key) ->
-        el._jquery?.removeAttr key
+        delay el, ->
+            el._jquery.removeAttr(key)

     tpl.on 'remove', (el) ->
         el._jquery?.remove()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/game_over.coffee
+++ src/game_over.coffee
@@ -7,7 +7,7 @@
 ###

 ###*
-Trans
+Transitions to the title state

 @name update
 @methodOf GameOver#