Fixed incorrect variable

Fixed incorrect variable

--- js-library/app/js/jail_iframe/classes/highlight.coffee
+++ js-library/app/js/jail_iframe/classes/highlight.coffee
@@ -2,8 +2,8 @@
   constructor: (@id, @elements) ->
     @show_button = new FactlinkJailRoot.ShowButton @elements, @id

-  highlight:   -> @$elements.addClass('fl-core-highlight')
-  dehighlight: -> @$elements.removeClass('fl-core-highlight')
+  highlight:   -> $(@elements).addClass('fl-core-highlight')
+  dehighlight: -> $(@elements).removeClass('fl-core-highlight')

   destroy: ->
     for el in @elements


Enterprise form SaveBar becomes available when adding a new rule

--- app/assets/javascripts/admin/tag_rules/controllers/tag_rules_controller.js.coffee
+++ app/assets/javascripts/admin/tag_rules/controllers/tag_rules_controller.js.coffee
@@ -38,6 +38,7 @@
         newRule.peferred_exchange_tags = []
         newRule.preferred_matched_order_cycles_visibility = "visible"
     tagGroup.rules.push(newRule)
+    $scope.enterprise_form.$setDirty()
     $scope.updateRuleCounts()

   $scope.addNewTag = ->


Add permissions to collaboration data

Add permissions to collaboration data

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -391,7 +391,7 @@
     settings    = @getSettings()
     watchMap    = @myWatchMap?.values()

-    callback {@amIHost, settings, watchMap}
+    callback {@amIHost, settings, watchMap, @permissions}

   startHeartbeat: ->


Collaboration: Remove avatar of participant when invitation request is rejected by participant

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -348,6 +348,20 @@
       .on 'AddedToChannel', @bound 'participantAdded'
       .on 'MessageAdded',   @bound 'channelMessageAdded'
       .on 'ChannelDeleted', => @stopCollaborationSession()  # Don't pass any arguments.
+      .on 'RemovedFromChannel', @bound 'participantRemoved'
+
+
+  participantRemoved: (participant) ->
+
+    socialHelpers.fetchAccount participant, (err, account) =>
+
+      return throwError err  if err
+      return  unless account
+
+      { nickname } = account.profile
+
+      @statusBar.removeParticipantAvatar nickname
+      @unwatchParticipant nickname

   participantAdded: (participant) ->
@@ -1249,6 +1263,13 @@
     @permissions.set nickname, permission

+  removeParticipantPermissions: (nickname) ->
+
+    return  unless @permissions.get nickname
+
+    @permissions.delete nickname
+
+
   getMyWatchers: ->

     participants = []


repository: implement getIssues

repository: implement getIssues

--- src/octonode/repository.coffee
+++ src/octonode/repository.coffee
@@ -51,5 +51,10 @@
   getBranches: (cb) ->
     @_invokeGet "/repos/#{@name}/branches",200,"getBranches",cb

+  # Get the issues for a repository
+  # '/repos/pkumar/hub/issues' GET
+  getIssues: (cb) ->
+    @_invokeGet "/repos/#{@name}/issues",200,"getIssues",cb
+
 # Export module
 module.exports = Repository


IDEView: Don't close Untitled.txt file if has content.

--- client/ide/lib/views/tabview/ideview.coffee
+++ client/ide/lib/views/tabview/ideview.coffee
@@ -78,6 +78,7 @@

     @tabView.on 'FileNeedsToBeOpened', (file, contents, callback, emitChange) =>
       @closeUntitledFileIfNotChanged()
+      file.initialContents = contents
       @openFile file, contents, callback, emitChange

     @tabView.on 'FileNeedsToBeTailed', @bound 'tailFile'
@@ -528,10 +529,11 @@

       return unless isFsFile

-      isLocal  = pane.data.path.indexOf('localfile:/') > -1
-      isEmpty  = pane.view.getEditor()?.getSession()?.getValue() is '' # intentional `?` checks
-
-      @tabView.removePane pane  if isLocal and isEmpty
+      isLocal    = pane.data.path.indexOf('localfile:/') > -1
+      isEmpty    = pane.view.getEditor()?.getSession()?.getValue() is '' # intentional `?` checks
+      hasContent = pane.data.initialContents
+
+      @tabView.removePane pane  if isLocal and isEmpty and not hasContent

   getPlusMenuItems: ->


Support content and title or pure string error messages

--- client/landing/app/MainApp/KD.extend.coffee
+++ client/landing/app/MainApp/KD.extend.coffee
@@ -93,9 +93,18 @@
       err     = {message}

     err.name or= 'KodingError'
-    message    = messages[err.name] or err.message or 'Something went wrong'

-    new KDNotificationView
-      title : message
+    content    = ''
+    errMessage = messages[err.name]
+    if errMessage?
+      if 'string' is typeof errMessage
+        title = errMessage
+      else if errMessage.title? and errMessage.content?
+        {title, content} = errMessage
+
+    duration = errMessage.duration or 2500
+    title  or= err.message or 'Something went wrong'
+
+    new KDNotificationView {title, content, duration}

     warn "KodingError:", err.message  unless err.name is 'AccessDenied'


work on bongo-client for new topology

work on bongo-client for new topology

--- client/landing/app/MainApp/mq.config.coffee
+++ client/landing/app/MainApp/mq.config.coffee
@@ -3,6 +3,9 @@
   getUserArea:-> KD.getSingleton('mainController').getUserArea()

   getSessionToken:-> $.cookie('clientId')
+
+  createRoutingKey:(service, event)->
+    "client.#{Bongo.createId()}.#{KD.whoami().profile.nickname}.#{service}.#{event}"

   fetchName:do->
     cache = {}


UI Copy: improve copy of teamwork invite email

--- client/landing/CollaborativeWorkspace/views/collaborativeworkspaceuserlist.coffee
+++ client/landing/CollaborativeWorkspace/views/collaborativeworkspaceuserlist.coffee
@@ -145,10 +145,10 @@
       title    : "Invite"
       callback : =>
         accounts  = @userController.getSelectedItemData()
-        @sendInvite account for account in accounts
+        @sendInviteTo account for account in accounts
         @reset()

-  sendInvite: (account) ->
+  sendInviteTo: (account) ->
     return @emit "UserInviteFailed"  unless account

     to           = account.profile.nickname
@@ -162,15 +162,13 @@
     subject      = "Join my #{appName} session"
     body         =
       """
-        Hey #{account.profile.firstName},
-
-        #{fromFullName} (@#{profile.nickname}) invited you to #{appName} session.
-
-        You can use this key #{sessionKey} to join #{fromFullName}'s #{appName} session or you can click the link below:
-
-        http://koding.com/Develop/#{appName}?sessionKey=#{sessionKey}
-
-        If you don't have #{appName} installed, you can install it from the App Catalog.
+        Hi @#{account.profile.nickname},
+
+        @#{profile.nickname} has invited you to join #{KD.utils.formatIndefiniteArticle appName} session.
+
+        To join, click the link below:
+
+        https://koding.com/#{encodeURIComponent appName}?sessionKey=#{encodeURIComponent sessionKey}
       """

     return if to is nickname


Add delegator to model for PaneContainerView::saveAll

--- src/pane-container-view.coffee
+++ src/pane-container-view.coffee
@@ -10,6 +10,8 @@
   atom.deserializers.add(this)
   Serializable.includeInto(this)
   Delegator.includeInto(this)
+
+  @delegatesMethod 'saveAll', toProperty: 'model'

   @deserialize: (state) ->
     new this(PaneContainer.deserialize(state.model))


Updated tests to use should.js more consistently.

--- Src/coffeescript/cql-execution/test/datatypes/interval.coffee
+++ Src/coffeescript/cql-execution/test/datatypes/interval.coffee
@@ -645,26 +645,26 @@

   it 'should properly calculate before/after unions', ->
     [x, y] = xy @dIvl.before
-    (x.closed.union(y.closed) == null).should.be.true
-    (x.closed.union(y.open) == null).should.be.true
-    (x.open.union(y.closed) == null).should.be.true
-    (x.open.union(y.open) == null).should.be.true
-    (y.closed.union(x.closed) == null).should.be.true
-    (y.closed.union(x.open) == null).should.be.true
-    (y.open.union(x.closed) == null).should.be.true
-    (y.open.union(x.open) == null).should.be.true
+    should(x.closed.union(y.closed)).be.null
+    should(x.closed.union(y.open)).be.null
+    should(x.open.union(y.closed)).be.null
+    should(x.open.union(y.open)).be.null
+    should(y.closed.union(x.closed)).be.null
+    should(y.closed.union(x.open)).be.null
+    should(y.open.union(x.closed)).be.null
+    should(y.open.union(x.open)).be.null

   it 'should properly calculate meets unions', ->
     [x, y] = xy @dIvl.meets
     z = @all2012
     x.closed.union(y.closed).equals(z.closed).should.be.true
-    (x.closed.union(y.open) == null).should.be.true
-    (x.open.union(y.closed) == null).should.be.true
-    (x.open.union(y.open) == null).should.be.true
+    should(x.closed.union(y.open)).be.null
+    should(x.open.union(y.closed)).be.null
+    should(x.open.union(y.open)).be.null
     y.closed.union(x.closed).equals(z.closed).should.be.true
-    (y.closed.union(x.open) == null).should.be.true
-    (y.open.union(x.closed) == null).should.be.true
-    (y.open.union(x.open) == null).should.be.true
+    should(y.closed.union(x.open)).be.null
+    should(y.open.union(x.closed)).be.null
+    should(y.open.union(x.open)).be.null

   it 'should properly calculate left/right overlapping unions', ->
     [x, y] = xy @dIvl.overlaps
@@ -902,10 +902,10 @@
     y.toDay.intersect(x.toDay).high.should.eql x.toDay.high

     [x, y] = xy @dIvl.begins
-    x.toDay.intersect(y.toDay).low.should.eql  x.toDay.low
-    x.toDay.intersect(y.toDay).high.should.eql  x.toDay.high
-    y.toDay.intersect(x.toDay).low.should.eql  x.toDay.low
-    y.toDay.intersect(x.toDay).high.should.eql  x.toDay.high
+    x.toDay.intersect(y.toDay).low.should.eql x.toDay.low
+    x.toDay.intersect(y.toDay).high.should.eql x.toDay.high
+    y.toDay.intersect(x.toDay).low.should.eql x.toDay.low
+    y.toDay.intersect(x.toDay).high.should.eql x.toDay.high

   it 'should throw when the argument is a point', ->
     try
@@ -1007,10 +1007,10 @@

   it 'should properly handle imprecision', ->
     [x, y] = xy @dIvl.overlaps
-    (x.toDay.except(y.toDay).low  == x.toDay.low).should.be.true
-    (x.toDay.except(y.toDay).high == y.toDay.low).should.be.true
-    (y.toDay.except(x.toDay).low  == x.toDay.high).should.be.true
-    (y.toDay.except(x.toDay).high == y.toDay.high).should.be.true
+    x.toDay.except(y.toDay).low.should.eql x.toDay.low
+    x.toDay.except(y.toDay).high.should.eql y.toDay.low
+    y.toDay.except(x.toDay).low.should.eql x.toDay.high
+    y.toDay.except(x.toDay).high.should.eql y.toDay.high

     [x, y] = xy @dIvl.meets
     # [a,b].except([b,c]) (where b is uncertain) should result in [a,b) but spec says we don't know if they overlap
@@ -1025,14 +1025,14 @@
     [x, y] = xy @dIvl.ends
     should.not.exist x.toDay.except(y.toDay)
     should.not.exist x.toDay.except(y.toDay)
-    (y.toDay.except(x.toDay).low  == y.toDay.low).should.be.true
-    (y.toDay.except(x.toDay).high == x.toDay.low).should.be.true
+    y.toDay.except(x.toDay).low.should.eql y.toDay.low
+    y.toDay.except(x.toDay).high.should.eql x.toDay.low

     [x, y] = xy @dIvl.begins
     should.not.exist x.toDay.except(y.toDay)
     should.not.exist x.toDay.except(y.toDay)
-    (y.toDay.except(x.toDay).low  == x.toDay.high).should.be.true
-    (y.toDay.except(x.toDay).high == y.toDay.high).should.be.true
+    y.toDay.except(x.toDay).low.should.eql x.toDay.high
+    y.toDay.except(x.toDay).high.should.eql y.toDay.high

   it 'should throw when the argument is a point', ->
     try
@@ -2196,26 +2196,26 @@

   it 'should properly calculate before/after unions', ->
     [x, y] = xy @iIvl.before
-    (x.closed.union(y.closed) == null).should.be.true
-    (x.closed.union(y.open) == null).should.be.true
-    (x.open.union(y.closed) == null).should.be.true
-    (x.open.union(y.open) == null).should.be.true
-    (y.closed.union(x.closed) == null).should.be.true
-    (y.closed.union(x.open) == null).should.be.true
-    (y.open.union(x.closed) == null).should.be.true
-    (y.open.union(x.open) == null).should.be.true
+    should(x.closed.union(y.closed)).be.null
+    should(x.closed.union(y.open)).be.null
+    should(x.open.union(y.closed)).be.null
+    should(x.open.union(y.open)).be.null
+    should(y.closed.union(x.closed)).be.null
+    should(y.closed.union(x.open)).be.null
+    should(y.open.union(x.closed)).be.null
+    should(y.open.union(x.open)).be.null

   it 'should properly calculate meets unions', ->
     [x, y] = xy @iIvl.meets
     z = @zeroToHundred
     x.closed.union(y.closed).equals(z.closed).should.be.true
-    (x.closed.union(y.open) == null).should.be.true
-    (x.open.union(y.closed) == null).should.be.true
-    (x.open.union(y.open) == null).should.be.true
+    should(x.closed.union(y.open)).be.null
+    should(x.open.union(y.closed)).be.null
+    should(x.open.union(y.open)).be.null
     y.closed.union(x.closed).equals(z.closed).should.be.true
-    (y.closed.union(x.open) == null).should.be.true
-    (y.open.union(x.closed) == null).should.be.true
-    (y.open.union(x.open) == null).should.be.true
+    should(y.closed.union(x.open)).be.null
+    should(y.open.union(x.closed)).be.null
+    should(y.open.union(x.open)).be.null

   it 'should properly calculate left/right overlapping unions', ->
     [x, y] = xy @iIvl.overlaps
@@ -2270,8 +2270,8 @@
     uIvl.union(ivl).equals(ivl).should.be.true

     ivl = new Interval(-100, 0)
-    (ivl.union(uIvl) == null).should.be.true
-    (uIvl.union(ivl) == null).should.be.true
+    should(ivl.union(uIvl)).be.null
+    should(uIvl.union(ivl)).be.null

     ivl = new Interval(8, 17)
     i = ivl.union(uIvl)
@@ -2288,22 +2288,18 @@

     ivl = new Interval(10, 15)
     i = ivl.union(uIvl)
-    (i.low.low == uIvl.low.low).should.be.true
-    (i.low.high == uIvl.low.high).should.be.true
-    (i.high.low == uIvl.high.low).should.be.true
-    (i.high.high == uIvl.high.high).should.be.true
+    i.should.eql uIvl

     i = uIvl.union(ivl)
-    (i.low.low == uIvl.low.low).should.be.true
-    (i.low.high == uIvl.low.high).should.be.true
-    (i.high.low == uIvl.high.low).should.be.true
-    (i.high.high == uIvl.high.high).should.be.true
+    i.should.eql uIvl

     ivl = new Interval(15, 20)
-    (ivl.union(uIvl).low == uIvl.low).should.be.true
-    (uIvl.union(ivl).low == uIvl.low).should.be.true
-    (ivl.union(uIvl).high == ivl.high).should.be.true
-    (uIvl.union(ivl).high == ivl.high).should.be.true
+    i = ivl.union(uIvl)
+    i.low.should.eql uIvl.low
+    i.high.should.eql ivl.high
+    i = uIvl.union(ivl)
+    i.low.should.eql uIvl.low
+    i.high.should.eql ivl.high

     ivl = new Interval(20, 30)
     should.not.exist ivl.union(uIvl)
@@ -2411,10 +2407,8 @@

     x = new Interval(  b,  e)
     y = new Interval(a  ,c  )
-    (x.intersect(y).low  == b).should.be.true
-    (x.intersect(y).high == c).should.be.true
-    (y.intersect(x).low  == b).should.be.true
-    (y.intersect(x).high == c).should.be.true
+    x.intersect(y).should.eql new Interval(b, c)
+    y.intersect(x).should.eql new Interval(b, c)

     x = new Interval(a,b   )
     y = new Interval(  b,d )
@@ -2425,17 +2419,13 @@

     x = new Interval(a  ,  e)
     y = new Interval(  b,d  )
-    (x.intersect(y).low  == b).should.be.true
-    (x.intersect(y).high == d).should.be.true
-    (y.intersect(x).low  == b).should.be.true
-    (y.intersect(x).high == d).should.be.true
+    x.intersect(y).should.eql y
+    y.intersect(x).should.eql y

     x = new Interval(a,  d )
     y = new Interval(  b, e)
-    (x.intersect(y).low  == b).should.be.true
-    (x.intersect(y).high == d).should.be.true
-    (y.intersect(x).low  == b).should.be.true
-    (y.intersect(x).high == d).should.be.true
+    x.intersect(y).should.eql new Interval(b, d)
+    y.intersect(x).should.eql new Interval(b, d)

     x = new Interval(a,b,   )
     y = new Interval(    d,e)
@@ -2548,12 +2538,10 @@
     d = new Uncertainty(80,90)
     e = 100

-    x = new Interval(  b,  e)
-    y = new Interval(a  ,c  )
-    (x.except(y).low  == c).should.be.true
-    (x.except(y).high == e).should.be.true
-    (y.except(x).low  == a).should.be.true
-    (y.except(x).high == b).should.be.true
+    x = new Interval(  b,  e) #([10,20] , 100)
+    y = new Interval(a  ,c  ) #(   0    ,  50)
+    x.except(y).should.eql new Interval(c, e, false, true)
+    y.except(x).should.eql new Interval(a, b, true, false)

     x = new Interval(a,b   )
     y = new Interval(  b,d )
@@ -2569,10 +2557,8 @@

     x = new Interval(a,  d )
     y = new Interval(  b, e)
-    (x.except(y).low  == a).should.be.true
-    (x.except(y).high == b).should.be.true
-    (y.except(x).low  == d).should.be.true
-    (y.except(x).high == e).should.be.true
+    x.except(y).should.eql new Interval(a, b, true, false)
+    y.except(x).should.eql new Interval(d, e, false, true)

     x = new Interval(a,b,   )
     y = new Interval(    d,e)


Layer control now works with CartoDB.js

Layer control now works with CartoDB.js

I apologise for the use of settimeout to solve async issues.

--- app/assets/javascripts/analysis/analysis.js.coffee
+++ app/assets/javascripts/analysis/analysis.js.coffee
@@ -9,7 +9,6 @@
     minZoom: 2
     maxZoom: 18

-  # Layers
   baseMaps =
     'Map': baseMap
     'Satellite': baseSatellite
@@ -39,9 +38,17 @@
             polygon-opacity: 0.4
           }
         """
-    ).on('done', (layer) => map.addLayer(layer))
+    ).on('done', (layer) =>
+      map.addLayer(layer)

-  #L.control.layers(baseMaps, overlayMaps).addTo(map)
+      prettyName = habitat.replace("_", " ")
+      prettyName = prettyName.replace(/\w\S*/g, (t) -> t.charAt(0).toUpperCase() + t.substr(1).toLowerCase())
+      overlayMaps[prettyName] = layer
+    )
+
+  setTimeout(() ->
+    L.control.layers(baseMaps, overlayMaps).addTo(map)
+  ,200)

   attribution = L.control.attribution(
     position: 'bottomleft'


edit cred: fix error occured after save button clicked

--- client/account/lib/accountcredentialeditmodal.coffee
+++ client/account/lib/accountcredentialeditmodal.coffee
@@ -34,7 +34,7 @@
         credential.update {
           provider, title, meta: data
         }, (err, credential) =>
-          @form.buttons.Save.hideLoader()
+          @form.buttonField.buttons.Save.hideLoader()

           unless showError err
             @form.emit 'CredentialUpdated', credential


landing: remove payment check from default env team create

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- client/landing/site.landing/coffee/core/utils.coffee
+++ client/landing/site.landing/coffee/core/utils.coffee
@@ -245,7 +245,9 @@
       return _.assign {}, data, { payment: { stripeToken } }

     if team = kd.team
-      if token = getPayment()?.token
+      token = getPayment()?.token
+      isDefaultEnv = kd.config.environment is 'default'
+      if not isDefaultEnv and token
         team = attachPayment kd.team, token
       return team

@@ -283,10 +285,10 @@

   createTeam: (callbacks = {}) ->

-    # this part should never run actually because the signup flow in
-    # TeamAppController takes care of this. But i'll leave this here just in
-    # case.
-    unless token = utils.getPayment()?.token
+    isDefaultEnv = kd.config.environment is 'default'
+    token = utils.getPayment()?.token
+
+    if not isDefaultEnv and not token
       new kd.NotificationView { title : 'You need to enter your credit card!' }
       return kd.singletons.router.handleRoute '/Team/Payment'


When loading entries, redirection to the login page if the server responds with a 401 status is achieved with angularjs $http interceptors, instead of explicitly controlling this response in the error callback.

This will be extended to all $http calls in the angularjs application, reducing the amount of boilerplate code.

--- app/assets/javascripts/ng-modules/ng-app.js.coffee
+++ app/assets/javascripts/ng-modules/ng-app.js.coffee
@@ -7,10 +7,11 @@
 # Configure $http service to send the CSRF-prevention token, otherwise POST, DELETE etc requests will be rejected
 module.config ["$httpProvider", ($httpProvider)->
   $httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content')
-  $httpProvider.interceptors.push ['$window', ($window)->
-    'response': (response)->
-      if response.status == 401
+  $httpProvider.interceptors.push ['$window', '$q', ($window, $q)->
+    'responseError': (rejection)->
+      if rejection.status == 401
         $window.location.href = '/login'
-      return response
+      else
+        return $q.reject rejection
   ]
 ]


one more try fix redirect

one more try fix redirect

--- app/assets/javascripts/shared/mobile_sms.js.coffee
+++ app/assets/javascripts/shared/mobile_sms.js.coffee
@@ -1,9 +1,3 @@
-redirect = (uri) ->
-  if navigator.userAgent.match(/Android/i)
-    document.location = uri
-  else
-    window.location.replace uri
-
 sms_delimiter = ->
   ua = navigator.userAgent.toLowerCase()
   if ua.indexOf('iphone') > -1 or ua.indexOf('ipad') > -1
@@ -15,6 +9,6 @@
   $sms_field = $('.mobile-sms')
   if $sms_field.length > 0
     mobile_href = $sms_field.prop('href').replace('?', sms_delimiter())
-    redirect(mobile_href)
+    window.location.href = mobile_href


[fix] Improve sorting of the relics while printing

--- app/assets/javascripts/widgets/direction.js.coffee
+++ app/assets/javascripts/widgets/direction.js.coffee
@@ -257,10 +257,14 @@
   $('#relics-container').html ''
   $('#relics-loading-info').show()

+roundToSix = (num) ->
+  +(Math.round(num + "e+6")  + "e-6")
+
 closestRoutePoint = (relic) ->
   a = Infinity
   position = 0
-  distance = google.maps.geometry.spherical.computeDistanceBetween
+  distance = (p, q) ->
+    roundToSix(google.maps.geometry.spherical.computeDistanceBetween(p, q))
   relicPoint = new google.maps.LatLng(relic.latitude, relic.longitude)
   ROUTE.overview_path.each (point, index) ->
     if a > (b = distance(point, relicPoint))
@@ -272,14 +276,16 @@
   relics.each (relic) ->
     closest = closestRoutePoint(relic)
     SORTED_RELICS[closest.position] ||= []
-    SORTED_RELICS[closest.position].add({distance: closest.distance, relic})
+    SORTED_RELICS[closest.position][closest.distance] ||= []
+    SORTED_RELICS[closest.position][closest.distance].add(relic)

 getSortedRelics = ->
   sorted = []
   compare = (a, b) ->
-    a.distance - b.distance
-  Object.keys(SORTED_RELICS).each (key) ->
-    sorted.add SORTED_RELICS[key].sort(compare).map (a) -> a.relic
+    a - b
+  Object.keys(SORTED_RELICS).each (index) ->
+    Object.keys(SORTED_RELICS[index]).sort(compare).each (key) ->
+      sorted.add(SORTED_RELICS[index][key])
   sorted

 printRenderRelics = (relics, last) ->


more descriptive offset var names

more descriptive offset var names

--- oriDomi.coffee
+++ oriDomi.coffee
@@ -187,12 +187,12 @@
         panel.style[transformProp] = @_transform @_getXy i - 1, anchor

         if anchor is 'top'
-          y = -((i - 1) * @panelHeight)
+          yOffset = -((i - 1) * @panelHeight)
         else
           panel.style[transformOriginProp] = 'bottom'
-          y = -((@hPanels * @panelHeight) - (@panelHeight * i))
-
-        content.style[transformProp] = @_transform [0, y]
+          yOffset = -((@hPanels * @panelHeight) - (@panelHeight * i))
+
+        content.style[transformProp] = @_transform [0, yOffset]

         if @shading
           @shaders[anchor].top[i - 1] = panel.getElementsByClassName('oridomi-shader-top')[0]
@@ -233,12 +233,12 @@
         panel.style[transformProp] = @_transform @_getXy i - 1, anchor

         if anchor is 'left'
-          x = -((i - 1) * @panelWidth)
+          xOffset = -((i - 1) * @panelWidth)
         else
           panel.style[transformOriginProp] = 'right'
-          x = -((@vPanels * @panelWidth) - (@panelWidth * i))
-
-        content.style[transformProp] = @_transform [x, 0]
+          xOffset = -((@vPanels * @panelWidth) - (@panelWidth * i))
+
+        content.style[transformProp] = @_transform [xOffset, 0]

         if @shading
           @shaders[anchor].left[i - 1]  = panel.getElementsByClassName('oridomi-shader-left')[0]


fixed bug in topchannels, sry

fixed bug in topchannels, sry

--- core/app/backbone/collections/channels.coffee
+++ core/app/backbone/collections/channels.coffee
@@ -81,9 +81,9 @@
     if results.length == 1 then results[0] else `undefined`

   orderByAuthority: ->
-    topchannels.comparator = (channel) ->
+    @comparator = (channel) ->
       -parseFloat(channel.get("created_by_authority"))
-    topchannels.sort()
+    @sort()

   orderedByAuthority: ->


rethrow JS (native) exceptions

rethrow JS (native) exceptions

--- src/jvm.coffee
+++ src/jvm.coffee
@@ -66,4 +66,5 @@
         show_state(rs)
         console.error e.stack or e
         cb?()
+        throw e  # so we get the JS traceback
   run()


update movie after info saving

update movie after info saving

--- app/scripts/controllers/fragments/get-infos.coffee
+++ app/scripts/controllers/fragments/get-infos.coffee
@@ -9,14 +9,14 @@
 ###
 angular
     .module 'gummyApp'
-    .controller 'GetInfosCtrl', ($scope, $rootScope, $trakt, $db) ->
+    .controller 'GetInfosCtrl', ($scope, $rootScope, $trakt, $db, $timeout) ->

         ###*
          # Movie model
          # @attribute movie
         ###
-        $scope.movie = $rootScope.seeking
+        $scope.movie = $rootScope.seeking.movie

         ###*
@@ -53,7 +53,8 @@
          # @method close
         ###
         $scope.close = () ->
-            $rootScope.seeking = null
+            $timeout () ->
+                $rootScope.seeking = null

         ###*
@@ -77,6 +78,7 @@
         ###
         $scope.seek = () ->
             $scope.errors = []
+            $scope.movies = []
             $scope.loading.list = true

             # Call provider for infos
@@ -99,14 +101,15 @@

             # Sanitize
             _tmp = angular.copy movie
-            delete _tmp.$$hashkey
             _tmp.path = $scope.movie.path

             # Update movie to db
             $db.movies.update { path: $scope.movie.path }, _tmp, {}, (e, items) ->
                 if e then console.error "MovieUpdate", e
                 else if items <= 0 then console.error "MovieUpdate", "Nothing updated"
-                else $rootScope.$emit "refresh.movies", {}
+                else
+                    $rootScope.seeking.movie = _tmp
+                    $scope.close()

         # GOGO GADGETO SEEKING


embarrassing mistake

embarrassing mistake

--- packages/rocketchat-integrations/server/api/api.coffee
+++ packages/rocketchat-integrations/server/api/api.coffee
@@ -195,7 +195,7 @@
 				user_name: 'rocket.cat'
 				text: 'Sample text 1'
 				trigger_word: 'Sample'
-			],[
+			,
 				token: Random.id(24)
 				channel_id: Random.id()
 				channel_name: 'general'
@@ -204,7 +204,7 @@
 				user_name: 'rocket.cat'
 				text: 'Sample text 2'
 				trigger_word: 'Sample'
-			],[
+			,
 				token: Random.id(24)
 				channel_id: Random.id()
 				channel_name: 'general'


try to fix regular expression interpolation

--- app/locale/fr.coffee
+++ app/locale/fr.coffee
@@ -1707,7 +1707,7 @@
 #    teacher_quest_teach_methods_step2: "Print out the [Student Quick Start Guide](http://files.codecombat.com/docs/resources/StudentQuickStartGuide.pdf) in the Resource Hub."
 #    teacher_quest_teach_strings: "Don't string your students along, teach them `strings`."
 #    teacher_quest_teach_strings_step1: "Get 75% of at least one class through __True Names__"
-#    teacher_quest_teach_strings_step2: "Use the Teacher Level Selector on [Course Guides](/teachers/courses) page to preview __True Names__."
+    teacher_quest_teach_strings_step2: "Positionnez le sélecteur de niveaux des professeurs sur la page [Guide des cours](/teachers/"+"cou"+"rses)pour prévisualiser __True Names__."
 #    teacher_quest_teach_loops: "Keep your students in the loop about `loops`."
 #    teacher_quest_teach_loops_step1: "Get 75% of at least one class through __Fire Dancing__."
 #    teacher_quest_teach_loops_step2: "Use the __Loops Activity__ in the [CS1 Curriculum guide](/teachers/resources/cs1) to reinforce this concept."
@@ -1735,8 +1735,8 @@
 #    success: "Success"
 #    in_progress: "In Progress"
 #    not_started: "Not Started"
-#    mid_course: "Mid-Course"
-#    end_course: "End of Course"
+    mid_course: "Moitié du Cours"
+    end_course: "Fin du Cours"
 #    none: "None detected yet"
 #    explain_open_ended: "Note: Students are encouraged to solve this level creatively — one possible solution is provided below."
 #    level_label: "Level:"
@@ -1987,7 +1987,7 @@
     my_matches: "Mes matchs"
     simulate: "Simuler"
     simulation_explanation: "En simulant une partie, tu peux classer ton rang plus rapidement !"
-#    simulation_explanation_leagues: "You will mainly help simulate games for allied players in your clans and courses."
+    simulation_explanation_leagues: "Vous allez principalement simuler des jeux pour vos joueurs alliés au sein de vos clans et cours."
     simulate_games: "Simuler une partie !"
     games_simulated_by: "Parties que vous avez simulées :"
     games_simulated_for: "Parties simulées pour vous :"


Fixed last merge

Fixed last merge

--- app/locale/it.coffee
+++ app/locale/it.coffee
@@ -1358,11 +1358,11 @@
     input_handling: "Gestione dell'Input" #"Input Handling"
     math_operations: "Operazioni Matematiche" #"Math Operations"
 #    object_literals: "Object Literals"
-     parameters: "Parametri" #"Parameters"
-     strings: "Stringhe" #"Strings"
-     variables: "Variabili" #"Variables"
-     vectors: "Vettori" #"Vectors"
-     while_loops: "Cicli" #"Loops"
+    parameters: "Parametri" #"Parameters"
+    strings: "Stringhe" #"Strings"
+    variables: "Variabili" #"Variables"
+    vectors: "Vettori" #"Vectors"
+    while_loops: "Cicli" #"Loops"
     recursion: "Ricorsione" #"Recursion"

 #  delta:


refs #15 -- add preview message to markdown preview

--- app/assets/javascripts/builder/views/editor_buttons.js.coffee
+++ app/assets/javascripts/builder/views/editor_buttons.js.coffee
@@ -22,6 +22,9 @@
         new App.Views.MarkdownEditor
           markdownField: '#page_body_md'
           wysiwygField: '#page_body'
+        $('.right.half').find('.content').html """
+
Loading preview. Please wait ...

+          """

   launchWysiwygEditor: (e) ->
     e.preventDefault()


Fix enroll buttons inside Edit Details of Overview

I broke this when I reenabled the Enroll button on /students

--- app/assets/javascripts/components/students/enroll_button.cjsx
+++ app/assets/javascripts/components/students/enroll_button.cjsx
@@ -58,7 +58,9 @@

     ) if @props.role == 0

-    # This row allows permitted users to add students to the course by wiki_id
+    # This row allows permitted users to add usrs to the course by wiki_id
+    # @props.role controls its presence in the Enrollment popup on /students
+    # @props.allowed controls its presence in Edit Details mode on Overview
     edit_rows.push (

@@ -68,7 +70,7 @@
           </form>

-    ) if @props.role == 0
+    ) if @props.role == 0 || @props.allowed

     button_class = 'button'
     button_class += if @props.inline then ' border plus' else ' dark'


Removed dead code

Removed dead code

--- core/app/backbone/modules/notification_center.coffee
+++ core/app/backbone/modules/notification_center.coffee
@@ -41,6 +41,5 @@
     alertsRegion: ".js-notification-center-alerts"
   FactlinkApp.alertsRegion.show new NotificationCenter.AlertsView collection: alerts

-  NotificationCenter.info    = (message) -> alerts.add new NotificationCenter.Alert message: message, type: 'info'
   NotificationCenter.success = (message) -> alerts.add new NotificationCenter.Alert message: message, type: 'success'
   NotificationCenter.error   = (message) -> alerts.add new NotificationCenter.Alert message: message, type: 'error'


- Fallback default app icon if original icon doesnt exists.

--- client/landing/app/Applications/Apps.kdapplication/Views/AppsListItemView.coffee
+++ client/landing/app/Applications/Apps.kdapplication/Views/AppsListItemView.coffee
@@ -12,12 +12,13 @@
     else
       thumb = "#{KD.apiUri + '/images/default.app.listthumb.png'}"

-    thumbOptions =
-      tagName     : 'img'
+    @thumbnail = new KDCustomHTMLView
+      tagName     : "img"
+      bind        : "error"
+      error       : =>
+        @thumbnail.$().attr "src", "/images/default.app.listthumb.png"
       attributes  :
         src       : thumb
-
-    @thumbnail = new KDCustomHTMLView thumbOptions

   click:(event)->
     if $(event.target).is ".appdetails h3 a span"


minor: ellipsis converted to three dots.

--- client/landing/app/Applications/Groups.kdapplication/Views/permissionview.coffee
+++ client/landing/app/Applications/Groups.kdapplication/Views/permissionview.coffee
@@ -11,7 +11,7 @@
     @loader           = new KDLoaderView
       cssClass        : 'loader'
     @loaderText       = new KDView
-      partial         : 'Loading Permissions…'
+      partial         : 'Loading Permissions...'
       cssClass        : ' loader-text'

     addPermissionsView = (newPermissions)=>


Implement pagination for category pages.

--- app/assets/javascripts/application/pods_store.js.coffee
+++ app/assets/javascripts/application/pods_store.js.coffee
@@ -27,7 +27,14 @@
   readFromCategory: (category, sortBy, asc=true, offset=0, limit=50) ->
     logger.verbose 'PodsStore#readCategory'
     keyRange = ydn.db.KeyRange.only(category)
-    @db.values 'pod', 'category', keyRange
+    @db.values('pod', 'category', keyRange).then (allPods) ->
+      pods = []
+      i = offset
+      while i <= (offset + limit) && i < allPods.length
+        pod = allPods[i]
+        pods.push pod
+        i++
+      pods
   # this reads from _all_ pods and uses ydn internal capabilities for sorting
   # and pagination
   readFromAll: (sortBy, asc=true, offset=0, limit=50) ->


Normalize arrow angles to range [0,360) degrees.

--- community/browser/lib/visualization/utils/pairwiseArcsRelationshipRouting.coffee
+++ community/browser/lib/visualization/utils/pairwiseArcsRelationshipRouting.coffee
@@ -57,7 +57,7 @@
       if not nodePair.isLoop()
         dx = nodePair.nodeA.x - nodePair.nodeB.x
         dy = nodePair.nodeA.y - nodePair.nodeB.y
-        angle = Math.atan2(dy, dx) / Math.PI * 180
+        angle = ((Math.atan2(dy, dx) / Math.PI * 180) + 360) % 360
         centreDistance = Math.sqrt(square(dx) + square(dy))
         for relationship in nodePair.relationships
           relationship.naturalAngle = if relationship.target is nodePair.nodeA


Remove obsolete test

Remove obsolete test

--- bokehjs/test/models/formatters/func_tick_formatter.coffee
+++ bokehjs/test/models/formatters/func_tick_formatter.coffee
@@ -18,11 +18,3 @@

     labels = obj.doFormat(["a", "b", "c", "d", "e"])
     expect(labels).to.deep.equal(["a_lat", "b_lat", "c_lat", "d_lat", "e_lat"])
-
-  it "should compile coffeescript to javascript correctly", ->
-    obj = new formatter.Model
-      code: "(tick) -> return tick*10"
-      lang: "coffeescript"
-
-    labels = obj.doFormat([-10, -0.1, 0, 0.1, 10])
-    expect(labels).to.deep.equal([-100, -1.0, 0, 1, 100])


remove request for user score from server as it is not implemented in the backend yet

--- assets/js/app.coffee
+++ assets/js/app.coffee
@@ -432,7 +432,7 @@
         window.top.location=window.top.location

     #TODO: get info about leaders and add them in leaderboard
-    $.post '/user_score', {uid: uid, score: score}
+    #$.post '/user_score', {uid: uid, score: score}
   # If the initialization of VK JSapi wasn't successful then reload page and try gain
   #
   onVkInitFail = ->


Use cache select to get line_widths correctly in all cases

--- bokehjs/src/coffee/models/glyphs/multi_line.coffee
+++ bokehjs/src/coffee/models/glyphs/multi_line.coffee
@@ -47,10 +47,10 @@
       x: this.renderer.plot_view.canvas.vx_to_sx(geometry.vx)
       y: this.renderer.plot_view.canvas.vy_to_sy(geometry.vy)
     shortest = 9999
-    threshold = Math.max(2, @visuals.line.line_width.value() / 2)

     hits = {}
     for i in [0...@sxs.length]
+      threshold = Math.max(2, @visuals.line.cache_select('line_width', i) / 2)
       points = null
       for j in [0...@sxs[i].length-1]
         [p0, p1] = [{x: @sxs[i][j], y: @sys[i][j]}, {x: @sxs[i][j+1], y: @sys[i][j+1]}]


added tiletype and alias

added tiletype and alias

--- addon/components/map-grid-tile.coffee
+++ addon/components/map-grid-tile.coffee
@@ -8,12 +8,13 @@
   shapeType: "tile"
   tagName: "g"
   layout: layout
-  iconText: "x"
+  iconText: alias "model.iconText"
   classNames: ["map-grid-tile"]
-  classNameBindings: ["type", "selected"]
+  classNameBindings: ["type", "selected", "tileType"]
   selected: false
   hasTileImage: present "model.tileImage"
   type: alias "model.type"
+  tileType: alias "model.tileType"
   attributeBindings: ["transform"]
   pixelsPerLength: alias "parentView.pixelsPerLength"
   origin: alias "model.origin"


Move css setup to window.coffee

Move css setup to window.coffee

--- battle-detail.coffee
+++ battle-detail.coffee
@@ -26,5 +26,8 @@
 window.i18n.resources.setLocale ?= (str) -> return
 window.__r = window.i18n.resources.__.bind(window.i18n.resources)

+# css
+$('#font-awesome')?.setAttribute 'href', "#{ROOT}/components/font-awesome/css/font-awesome.min.css"
+

 require './views'


EnvironmentMachineItem: follow build- and destroy- events and when hit 100 percent invalidate the machine data

--- client/Environments/views/scene/environmentmachineitem.coffee
+++ client/Environments/views/scene/environmentmachineitem.coffee
@@ -31,6 +31,9 @@

     {computeController} = KD.singletons

+    computeController.on "build-#{machine._id}",   @bound 'invalidateMachine'
+    computeController.on "destroy-#{machine._id}", @bound 'invalidateMachine'
+
     computeController.on "public-#{machine._id}", (event)=>

       {percentage, status} = event
@@ -51,6 +54,16 @@

   contextMenuItems : ->
+  invalidateMachine:(event)->
+
+    if event.percentage is 100
+
+      machine = @getData()
+      KD.remote.api.JMachine.one machine._id, (err, newMachine)=>
+        if err then warn ".>", err
+        else @setData newMachine
+
+

     colorSelection = new ColorSelection selectedColor : @getOption 'colorTag'
     colorSelection.on "ColorChanged", @bound 'setColorTag'


Cleanup new App created event so we can call it anywhere

--- client/app/Applications/StartTab.kdapplication/AppView.coffee
+++ client/app/Applications/StartTab.kdapplication/AppView.coffee
@@ -14,6 +14,9 @@
     appsController.on "AppsRefreshed", (apps)=>
       @getSingleton("kodingAppsController").appStorage.fetchStorage (storage)=>
         @decorateApps apps
+
+    appsController.on "aNewAppCreated", =>
+      @aNewAppCreated()

     # mainView.sidebar.finderResizeHandle.on "DragInAction", =>
     #   log "DragInAction", mainView.contentPanel.getWidth()
@@ -44,17 +47,7 @@
       # iconClass   : "make-an-app"
       title       : "Make a new App"
       callback    : =>
-        appsController.makeNewApp (err)=>
-          if not err
-            new KDNotificationView
-              type     : "mini"
-              cssClass : "success"
-              title    : "App is created! Check your Applications folder!"
-
-            @removeAppIcons()
-            @showLoader()
-            appsController.refreshApps =>
-              @hideLoader()
+        appsController.makeNewApp()

     # appManager.fetchStorage "KodingApps", "1.0", (err, storage)=>
     #   storage.update $set : { "bucket.apps" : {} }, =>
@@ -96,6 +89,17 @@
       @appItemContainer.addSubView new StartTabOldAppThumbView
         tab : @
       , app
+
+  aNewAppCreated:->
+    new KDNotificationView
+      type     : "mini"
+      cssClass : "success"
+      title    : "App is created! Check your Applications folder!"
+
+    @removeAppIcons()
+    @showLoader()
+    @getSingleton("kodingAppsController").refreshApps =>
+      @hideLoader()

   pistachio:->
     """


Client: Activity: Updated SidebarMessagesSection component to add modal event handlers

--- client/app/lib/components/sidebarmessagessection/index.coffee
+++ client/app/lib/components/sidebarmessagessection/index.coffee
@@ -16,6 +16,22 @@
     <SidebarSection title="Messages" className="SidebarMessagesSection">
       <SidebarList
         itemComponent={SidebarMessagesListItem}
+  constructor: (props) ->
+
+    super
+
+    @state = { browsePrivateChannels: no }
+
+
+  onClose: ->
+
+    @setState browsePrivateChannels: no
+
+
+  showPrivateChannelsModal: ->
+
+    @setState browsePrivateChannels: yes
+
         threads={@props.threads}
         selectedId={@props.selectedId} />
     </SidebarSection>


billing/creditcard: update attribute name

--- client/home/lib/billing/components/creditcard/view.coffee
+++ client/home/lib/billing/components/creditcard/view.coffee
@@ -104,12 +104,12 @@
     node = findScrollableParent node, yes

     if lastKnownNode
-      lastKnownNode.dataset.scrollable = 'no-scroll'
+      lastKnownNode.dataset.innerItemWillScroll = 'no-scroll'
       return

     return  unless node

-    node.dataset.scrollable = 'no-scroll'
+    node.dataset.innerItemWillScroll = 'no-scroll'
     lastKnownNode = node

@@ -120,12 +120,11 @@
     node = findScrollableParent node, yes

     if lastKnownNode
-      lastKnownNode.dataset.scrollable = ''
+      delete lastKnownNode.dataset?.innerItemWillScroll
       return

     return  unless node
-
-    node.dataset?.scrollable = ''
+    delete node.dataset?.innerItemWillScroll
     lastKnownNode = node

@@ -138,7 +137,7 @@
       options={options[type]}
       placeholder={placeholders[type]}
       onChange={(e) ->
-        lastKnownNode.dataset.scrollable = ''
+        delete lastKnownNode.dataset?.innerItemWillScroll
         onChange?(e.value)}
       value={value} />
   </div>


Pass logger in specs

Pass logger in specs

--- src/spec/pricesync.spec.coffee
+++ src/spec/pricesync.spec.coffee
@@ -1,12 +1,20 @@
+_ = require 'underscore'
+Q = require 'q'
+Logger = require '../lib/logger'
 PriceSync = require '../lib/pricesync'
-_ = require('underscore')._
-Q = require 'q'

 describe 'PriceSync', ->
+
   beforeEach ->
+    @logger = new Logger
+      streams: [
+        { level: 'error', stream: process.stderr }
+      ]
+
     options =
       baseConfig:
-        logConfig: {}
+        logConfig:
+          logger: @logger
       master:
         project_key: 'x'
         client_id: 'y'
@@ -17,7 +25,7 @@
         client_secret: 'c'

     @priceSync = new PriceSync options
-
+
   describe '#_filterPrices', ->
     it 'should work for no variants', ->
       data = @priceSync._filterPrices [], [], {}, {}
@@ -44,7 +52,8 @@
       expect(data.masterPrices[1]).toEqual { customerGroup: { id: 'foo' }, channel: { id: 'retailerX' } }

   describe '#_updatePrices', ->
-    it 'should complain when no price at retailer', ->
+
+    it 'should complain when there are no prices at retailer', ->
       updates = @priceSync._updatePrices [], [], 'bla', 'sku1'
       expect(_.size updates).toBe 0


modifying object in one test was making another test fail

--- apps/artwork/components/commercial/test/template.coffee
+++ apps/artwork/components/commercial/test/template.coffee
@@ -16,7 +16,8 @@
   )

 renderArtwork = (artworkOptions = {}, sdOptions = {}) ->
-  artwork = _.extend(inquireableArtwork.data.artwork, artworkOptions)
+  artwork = _.clone inquireableArtwork.data.artwork
+  _.extend artwork, artworkOptions
   sd = _.extend { CLIENT: artwork: artwork }, sdOptions

   html = render('index')(


S52498 - Add test to project cfd jasmine spec, rename get to build for chart config assembly

--- charts/magic/cfd/CumulativeFlowChartAppSpec.coffee
+++ charts/magic/cfd/CumulativeFlowChartAppSpec.coffee
@@ -51,7 +51,7 @@

   it 'has key chartAppConfig elements in the right places', ->
     @createApp().then =>
-      config = @app._getChartAppConfig()
+      config = @app._buildChartAppConfig()
       expect(config).not.toBeNull()
       expect(config.storeConfig).not.toBeNull() # tested elsewhere
       expect(config.calculatorType).toBe 'ProjectCFDCalculator'
@@ -61,7 +61,7 @@

   it 'has correct chartStore', ->
     @createApp().then =>
-      storeConfig = @app._getChartStoreConfig()
+      storeConfig = @app._buildChartStoreConfig()
       expect(storeConfig).not.toBeNull()
       expect(storeConfig.find.Project).toBe @project.ObjectID

@@ -71,9 +71,22 @@
       stateFieldValues: 'val1,val2,val3'
     }
     @createApp(settings).then =>
-      calcConfig = @app._getChartCalculatorConfig()
+      calcConfig = @app._buildChartCalculatorConfig()
       expect(calcConfig.stateFieldName).toBe settings.stateFieldName
       expect(calcConfig.stateFieldValues).toBe settings.stateFieldValues
+
+  it 'has correct chartStoreConfig ValidFrom date', ->
+    settings = {
+      timeFrameQuantity: '30',
+      timeFrameUnit: 'day'
+    }
+
+    @createApp(settings).then =>
+      testToday = new Date(2013, 3, 1, 0, 0, 0, 0) # April 1
+      @app._getNow = () -> testToday
+      validFrom = @app._buildChartStoreConfigValidFrom()
+      fromDate = Rally.util.DateTime.add(testToday, settings.timeFrameUnit, -settings.timeFrameQuantity);
+      expect(validFrom).toBe(fromDate.toISOString())

   helpers
     getContext: (initialValues) ->


fix voting indicators when changing opininon

--- core/app/backbone/views/facts/react_fact_vote_area.coffee
+++ core/app/backbone/views/facts/react_fact_vote_area.coffee
@@ -57,6 +57,10 @@
              style: {width: "#{disbelieve_percentage}%"}]

 FactVoteStatsTable = React.createBackboneClass
+  componentDidMount: ->
+    # react.backbone.js doesn't listen to changing models
+    @model().on 'change', => @forceUpdate()
+
   render: ->
     votes = @model().countBy (vote) -> vote.get('type')
     _.defaults votes,


don't show coupon area for upgrades

don't show coupon area for upgrades

--- services/web/public/coffee/main/account-upgrade.coffee
+++ services/web/public/coffee/main/account-upgrade.coffee
@@ -12,6 +12,6 @@
 					ga?('send', 'event', 'subscription-funnel', 'upgraded-free-trial', source)
 					url = "/user/subscription/new?planCode=#{planName}_free_trial_7_days&ssp=#{planName == 'collaborator'}"
 					if couponCode?
-						url = "#{url}&cc=#{couponCode}&scf=true"
+						url = "#{url}&cc=#{couponCode}"
 					window.open(url)
 					$scope.startedFreeTrial = true


Update time zone on user when it changes

--- angular/core/services/session.coffee
+++ angular/core/services/session.coffee
@@ -9,11 +9,16 @@
       Records.polls.remote.defaultParams   = defaultParams

     return unless AppConfig.currentUserId?
+    user = @user()

-    $translate.use(@user().locale)
-    $rootScope.$broadcast 'loggedIn', @user()
+    $translate.use user.locale
+    $rootScope.$broadcast 'loggedIn', user

-    @user()
+    if user.timeZone != AppConfig.timeZone
+      user.timeZone = AppConfig.timeZone
+      Records.users.updateProfile(user)
+
+    user

   logout: ->
     AppConfig.loggingOut = true


video/opentok: add a method to send mute signal

--- client/app/lib/videocollaboration/services/opentok.coffee
+++ client/app/lib/videocollaboration/services/opentok.coffee
@@ -183,6 +183,22 @@

   ###*
+   * OpenTok doesn't have an easy mechanism to mute a particular user for all
+   * video session subscribers. Because of that we are sending a signal to be
+   * muted user. Other places can subscribe to OT.Session instance's
+   * `signal:mute` event to register a callback to this. Ideal situtation will be
+   * triggering the `publishAudio(false)` in to be muted user's client.
+   *
+   * @param {SocialChannel} channel
+   * @param {ParticipantType.Subscriber} to
+   * @param {function(err: object)} callback
+  ###
+  sendMuteSignal: (channel, to, callback) ->
+
+    @sendSignal { channel, to, type: 'mute' }, callback
+
+
+  ###*
    * It destroys the publisher for given social channel.
    *
    * @param {SocialChannel} channel


pulls in code sample from http://apneadiving.github.io/

--- app/assets/javascripts/map.js.coffee
+++ app/assets/javascripts/map.js.coffee
@@ -1,3 +1,39 @@
+class CustomMarkerBuilder extends Gmaps.Google.Builders.Marker
+  create_marker: ->
+    options = _.extend @marker_options(), @rich_marker_options()
+    @serviceObject = new RichMarker options
+
+  rich_marker_options: ->
+    marker = document.createElement("div")
+    marker.setAttribute('class', 'custom_marker_content')
+    marker.innerHTML = this.args.custom_marker
+    { content: marker }
+
+  create_infowindow: ->
+    return null unless _.isString @args.custom_infowindow
+
+    boxText = document.createElement("div")
+    boxText.setAttribute("class", 'custom_infowindow_content')
+    boxText.innerHTML = @args.custom_infowindow
+    @infowindow = new InfoBox(@infobox(boxText))
+
+  infobox: (boxText)->
+    content: boxText
+    pixelOffset: new google.maps.Size(-140, 0)
+    boxStyle:
+      width: "280px"
+
+# handler = Gmaps.build("Google", builders: { Marker: CustomMarkerBuilder } )
+# handler.buildMap { internal: id: "custom_builder" }, ->
+#   marker = handler.addMarker
+#     lat:               40.689167
+#     lng:               -74.044444
+#     custom_marker:     " Statue of Liberty"
+#     custom_infowindow: " The Statue of Liberty is a colossal neoclassical sculpture on Liberty Island in the middle of New York"
+#
+#   handler.map.centerOn marker
+#   handler.getMap().setZoom(15)
+
 LocalSupport.google_map =
   settings:
     id: "map_canvas"
@@ -17,7 +53,7 @@
     $("#marker_data").data().markers

   build: (settings, data) ->
-    handler = Gmaps.build("Google")
+    handler = Gmaps.build("Google", builders: { Marker: CustomMarkerBuilder })
     handler.buildMap settings, ->
       markers = handler.addMarkers(data)
       _.each markers, (marker) ->


Fix extensions listing for grep command

Fix extensions listing for grep command

--- client/ide/workspace/panes/settings/editorsettings.coffee
+++ client/ide/workspace/panes/settings/editorsettings.coffee
@@ -128,10 +128,10 @@
     objectivec  : ["Objective C"  , "__dummy__"]

   getAllExts : ->
-    exts = (IDE.settings.editor.syntaxAssociations[key] for key in Object.keys IDE.settings.editor.syntaxAssociations)
+    exts = (IDE.settings.editor.syntaxAssociations[key][1].split "|" for key in Object.keys IDE.settings.editor.syntaxAssociations)
     exts = [].concat exts...
-    exts = (v.split "|" for v in exts)
-    exts = [].concat exts...
+    exts = (v.toLowerCase() for v in exts)
+    exts = _.unique exts

   getSyntaxOptions : ->


minor

minor

--- client/landing/app/MainApp/maincontroller/mainview.coffee
+++ client/landing/app/MainApp/maincontroller/mainview.coffee
@@ -26,7 +26,6 @@
 class MainView extends KDView

   viewAppended:->
-    # @utils.wait 1000, =>

     # @putLoader()
     @addHeader()


raise timeout setting for video uploads

raise timeout setting for video uploads

--- app/assets/javascripts/upload.coffee
+++ app/assets/javascripts/upload.coffee
@@ -66,7 +66,7 @@
   uppy.use Uppy.XHRUpload,
     endpoint: '/videos/upload'
     fieldName: 'file'
-    timeout: 120 * 1000
+    timeout: 600 * 1000

   # give the user feedback after upload has started
   uppy.on 'upload', (data) ->


SidebarMachineBox: Handle WorkspaceDeleted event.

--- client/Social/Activity/sidebar/sidebarmachinebox.coffee
+++ client/Social/Activity/sidebar/sidebarmachinebox.coffee
@@ -25,6 +25,9 @@
       itemOptions   : { machine }

     @listWrapper = @listController.getView()
+
+    @listController.getListView().on 'ItemWasAdded', (item) =>
+      item.once 'WorkspaceDeleted', @bound 'handleWorkspaceDeleted'

     @addWorkspace ws  for ws in workspaces
     @addSubView @listWrapper
@@ -120,3 +123,11 @@
   forEachWorkspaceItem: (callback) ->

     callback item  for item in @listController.getItemsOrdered()
+
+
+  handleWorkspaceDeleted: (wsId) ->
+
+    { workspaces } = @getData()
+
+    for ws, index in workspaces when ws.getId() is wsId
+      workspaces.splice index, 1


chat: start using ParticipantSearchController

--- client/Social/Activity/views/privatemessage/pane.coffee
+++ client/Social/Activity/views/privatemessage/pane.coffee
@@ -345,17 +345,7 @@
       @participantMap[participant._id].destroy()
       delete @participantMap[participant._id]

-      @removeFromAutoCompleteSelectedList account
-
-
-  removeFromAutoCompleteSelectedList: (account) ->
-
-    return  unless account in @autoComplete.getSelectedItemData()
-
-    # remove the account from autocomplete controller's
-    # selected list. So that it can show up in other searchs.
-    @autoComplete.removeSelectedItemData account
-    @autoComplete.selectedItemCounter--
+      @autoComplete.removeSelectedParticipant account

   createPreviousLink: ->
@@ -414,11 +404,12 @@
           itemClass      : KDView
       submit             : (e) -> e.preventDefault()

-    @autoComplete = new KDAutoCompleteController
+    @autoComplete = new ParticipantSearchController
       name                : 'userController'
       placeholder         : 'Type a username...'
       itemClass           : ActivityAutoCompleteUserItemView
       itemDataPath        : 'profile.nickname'
+      fetchInterval       : 0
       outputWrapper       : new KDView cssClass: 'hidden'
       listWrapperCssClass : 'private-message hidden'
       submitValuesAsText  : yes


form workflow: implement buggy history

form workflow: implement buggy history

this is proving to be more difficult than i originally thought.  I'm going to commit it as is (read "buggy") and loop back around for a non-buggy implementation after I do payment nonces

--- client/app/CommonViews/formworkflow/formworkflow.coffee
+++ client/app/CommonViews/formworkflow/formworkflow.coffee
@@ -10,7 +10,29 @@
     @forms      = {}
     @providers  = {}

+    @active = null
+
+    @history = new FormWorkflow.History
+
+  isWorkflow: yes
+
   enter: -> @ready @bound 'nextForm'
+
+  go: (direction) ->
+
+    provider = do @history[direction]
+
+    if provider.isWorkflow
+      if (provider is @active and direction is 'back') or
+         (direction is 'next')
+
+        provider.go direction
+
+    @showForm provider, no
+
+  next: -> @go 'next'
+
+  back: -> @go 'back'

   requireData: (fields, mode = 'all') ->
     gate =
@@ -67,6 +89,7 @@
     form.hide()
     @forwardEvent form, 'Cancel'
     @provideData formName, provides
+
     return this

   removeForm: (form) ->
@@ -87,11 +110,15 @@

     return this

-  showForm: (form) ->
+  showForm: (form, shouldPushState = yes) ->
     @hideForms()
     form = @getForm form
     form.activate? this
     form.show()
+
+    @active = form
+
+    @history.push form  if shouldPushState

     return this


fix exception in error log

fix exception in error log

--- app/coffee/OutputCacheManager.coffee
+++ app/coffee/OutputCacheManager.coffee
@@ -85,7 +85,7 @@
 						# clean up the directory we just created
 						fse.remove cacheDir, (err) ->
 							if err?
-								logger.error err: err, dir: dir, "error removing cache dir after failure"
+								logger.error err: err, dir: cacheDir, "error removing cache dir after failure"
 					else
 						# pass back the list of new files in the cache
 						callback(err, results)


Removed debug output for map location and removed protocol from href

--- app/assets/javascripts/home.js.coffee
+++ app/assets/javascripts/home.js.coffee
@@ -1,7 +1,5 @@
 @homeReady = () ->
   loc_path = $("#home-map").data("path")
-
-  console.log loc_path

   $.getJSON(loc_path, null, (data) ->

@@ -16,7 +14,7 @@

     $.each(data.all_locations, (key, latlng) ->
       new google.maps.Marker({
-        icon: 'http://maps.google.com/mapfiles/ms/icons/red-dot.png'
+        icon: '//maps.google.com/mapfiles/ms/icons/red-dot.png'
         position: new google.maps.LatLng(latlng.latitude, latlng.longitude),
         map: document.map
       })


Bar Chart now uses integers in usage histogram

--- app/assets/javascripts/init.js.coffee
+++ app/assets/javascripts/init.js.coffee
@@ -84,13 +84,17 @@
     myLightboxChart = new Chart(ctx).Doughnut data.leader_usage_data
     moc = $('<canvas height=80>')
     ctx2 = moc.get(0).getContext('2d')
+    if data.song_frequency_data.datasets.length > 1
+      console.error "only handling one dataset when there are " + data.song_frequency_data.datasets.length
+      console.console.log "(must fix highestValue)"
+    highestValue = Math.max.apply(null, data.song_frequency_data.datasets[0].data)
     myOtherLightboxChart = new Chart(ctx2).Bar(data.song_frequency_data, {
       "showTooltips": false,
-      scaleIntegersOnly: true
-      # scaleOverride: true,
-      # scaleSteps: steps,
-      # scaleStepWidth: Math.ceil(max / steps),
-      # scaleStartValue: 0
+      scaleIntegersOnly: true,
+      scaleOverride: true,
+      scaleSteps: highestValue,
+      scaleStepWidth: 1,
+      scaleStartValue: 0
     })
     tabledata = ''
     for key of data.song_details


Fixed contenteditable condition to check model is correct across columns

--- app/assets/javascripts/init.js.coffee
+++ app/assets/javascripts/init.js.coffee
@@ -31,7 +31,7 @@
       if data.result
         ceAfterUpdate $(toupdate)
         $(toupdate).siblings('td').each ->
-          if $(this).text() != ""+data.object[$(this).attr('name')] and !($(this).text() or ""+data.object[$(this).attr('name')]) and typeof data.object[$(this).attr('name')] != 'undefined'
+          if $(this).text() != ""+data.object[$(this).attr('name')] and !!$(this).text() and !!data.object[$(this).attr('name')] and typeof data.object[$(this).attr('name')] != 'undefined'
             $(this).text data.object[$(this).attr('name')]
             ceAfterUpdate $(this)
           return


LAB-58: Remove OS details in "Pick a platform"

--- app/assets/javascripts/main.js.coffee
+++ app/assets/javascripts/main.js.coffee
@@ -127,7 +127,8 @@
         id = 'platform_version_platform_id_eq'
         $platformSelect = if $results.attr('id').indexOf('-form') > -1 then $(id) else $platformSelect = $('#q_' + id)
         $label = $('label[for="' + $platformSelect.attr('id') + '"]')
-        $label.append(" (Detected as " + os + ")").data('detected', true) unless $label.data('detected')
+        ##$label.append(" (Detected as " + os + ")").data('detected', true) unless $label.data('detected')
+        $label.append(" (Detected)").data('detected', true) unless $label.data('detected')
         $options = $('option', $platformSelect)
         os_set = false
         #console.log $options


fix turbolinks problems

fix turbolinks problems

--- app/assets/javascripts/reasons.coffee
+++ app/assets/javascripts/reasons.coffee
@@ -2,8 +2,8 @@
 # All this logic will automatically be available in application.js.
 # You can use CoffeeScript in this file: http://coffeescript.org/

-$ ->
-  $(".show-post-body").on 'click', (event) ->
+$ ->
+  $(document).on 'click', '.show-post-body', (event) ->
     $(this).parent().children(".post-body").toggle()

     if $(this).text() == "►"


Fixed typo

Fixed typo

--- app/coffee/modules/admin/roles.coffee
+++ app/coffee/modules/admin/roles.coffee
@@ -224,7 +224,7 @@
                 { key: "modify_milestone", description: "Modify milestone" }
                 { key: "delete_last_milestone", description: "Delete last milestone" }
                 { key: "delete_milestone", description: "Delete milestone" }
-                { key: "add_us_to_milestone", description: "Add use to milestone" }
+                { key: "add_us_to_milestone", description: "Add us to milestone" }
                 { key: "remove_us_from_milestone", description: "Remove us from milestone" }
                 { key: "reorder_us_on_milestone", description: "Reorder us on milestone" }
             ]


Use preventDefault instead of returning false (less implicit)

--- core/app/assets/javascripts/base/social-services.coffee
+++ core/app/assets/javascripts/base/social-services.coffee
@@ -6,8 +6,8 @@
 $("span.social-services-button a.popup").click((e) ->
   popupCenter($(this).attr("href"), $(this).attr("data-width"), $(this).attr("data-height"), "authPopup")
   e.stopPropagation()
-  false
 )
+  e.preventDefault()

 document.addEventListener('signed in', ()->
   window.location.reload(true)


Remvove unneccessary instruction

Remvove unneccessary instruction

--- app/scripts/controllers/domain.coffee
+++ app/scripts/controllers/domain.coffee
@@ -38,13 +38,6 @@
         pageCount: violation.count
         color: 'color' + (i + 1)
       data)
-
-    @selectedCategory =
-      title: @domainGroupedViolations[0].label
-      percentage: @domainGroupedViolations[0].value
-      pageCount: @domainGroupedViolations[0].pageCount
-      color: @domainGroupedViolations[0].color
-      violations: @violationData[@domainGroupedViolations[0].id]

   _fillReviews: (data) =>
     @reviews = data


Add opening/closing sounds to HintsView

Add opening/closing sounds to HintsView

--- app/views/play/level/HintsView.coffee
+++ app/views/play/level/HintsView.coffee
@@ -24,7 +24,7 @@
       hintsUsed: {}
     })
     @updateHint()
-
+
     debouncedRender = _.debounce(@render)
     @listenTo(@state, 'change', debouncedRender)
     @listenTo(@hintsState, 'change', debouncedRender)
@@ -38,6 +38,7 @@
   afterRender: ->
     @$el.toggleClass('hide', @hintsState.get('hidden'))
     super()
+    @playSound 'game-menu-open'

   getProcessedHint: ->
     language = @session.get('codeLanguage')
@@ -48,9 +49,9 @@
     translated = utils.i18n(hint, 'body')
     filtered = utils.filterMarkdownCodeLanguages(translated, language)
     markedUp = marked(filtered)
-
+
     return markedUp
-
+
   updateHint: ->
     index = @state.get('hintIndex')
     hintsTitle = $.i18n.t('play_level.hints_title').replace('{{number}}', index + 1)
@@ -69,7 +70,9 @@
     @playSound 'menu-button-click'
     @updateHintTimer()

-  hideView: -> @hintsState?.set('hidden', true)
+  hideView: ->
+    @hintsState?.set('hidden', true)
+    @playSound 'game-menu-close'

   visibilityChanged: (e) ->
     @updateHintTimer()
@@ -85,10 +88,9 @@
     hintsViewTime[hintIndex] ?= 0
     hintsViewTime[hintIndex]++
     hintsUsed = @state.get('hintsUsed')
-    if hintsViewTime[hintIndex] > @hintUsedThresholdSeconds and not hintsUsed[hintIndex]
+    if hintsViewTime[hintIndex] > @hintUsedThresholdSeconds and not hintsUsed[hintIndex]
       window.tracker?.trackEvent 'Hint Used', category: 'Students', levelSlug: @level.get('slug'), hintCount: @hintsState.get('hints')?.length ? 0, hintCurrent: hintIndex, ['Mixpanel']
       hintsUsed[hintIndex] = true
       @state.set('hintsUsed', hintsUsed)
       clearInterval(@timerIntervalID)
     @state.set('hintsViewTime', hintsViewTime)
-


[auth] Change a Google OAuth parameter

[auth] Change a Google OAuth parameter

Summary:
Some of our users who reauth are getting weird errors from Google — it seems that in some cases Google OAuth doesn’t pass us a `refresh_token`, even though we ask for one by passing `approval_prompt=force` (I’ve landed a commit to log more details about this in edgehill-server —- https://github.com/nylas/edgehill-server/commit/6e984ffa268d8523b6f98be8532c4ebfaa2d1536 — but this won’t fix the problem).

Apparently, Google recently « soft-deprecated » `approval_prompt` in favor of a new parameter called `prompt`, which does mostly the same thing (https://developers.google.com/identity/protocols/OAuth2WebServer#formingtheurl)

This diff replaces `approval_prompt` by `prompt`.

Test Plan: Tested by authing a Gmail account and checking that it worked.

Reviewers: bengotow

Reviewed By: bengotow

Subscribers: emfree

Differential Revision: https://phab.nylas.com/D2557

--- internal_packages/onboarding/lib/account-choose-page.cjsx
+++ internal_packages/onboarding/lib/account-choose-page.cjsx
@@ -100,7 +100,7 @@
             https://mail.google.com/ \
             https://www.google.com/m8/feeds \
             https://www.googleapis.com/auth/calendar'
-        approval_prompt: 'force'
+        prompt: 'consent'
     })
     {shell} = require 'electron'
     shell.openExternal(googleUrl)


Added path module.

Added path module.

--- lib/command.coffee
+++ lib/command.coffee
@@ -1,11 +1,11 @@
+path = require 'path'
 async = require 'async'
 logsmith = require 'logsmith'
 child_process = require 'child_process'
 stream_buffers = require 'stream-buffers'

 ###
-	* Executes VBoxManage command. Commands are queued in order to prevent race
-	* conditions within VirtualBox.
+	* Executes VBoxManage command. Commands are queued in order to prevent race conditions within VirtualBox.
 	*
 	* @param {string} command
 	* @param {array<string>} args


Updated to jquery 3

Updated to jquery 3

--- app/assets/javascripts/application.coffee
+++ app/assets/javascripts/application.coffee
@@ -13,7 +13,7 @@
 # (https://github.com/rails/sprockets#sprockets-directives)
 # for details about supported directives.
 #
-#= require jquery
+#= require jquery3
 #= require jquery_ujs
 #= require bootstrap-sprockets
 #= require turbolinks


--- app/assets/javascripts/application.coffee
+++ app/assets/javascripts/application.coffee
@@ -62,6 +62,14 @@
   ko: "Coréen"

 $("#form_links").nested_fields "news", "link", "lien", title: "text", url: "url", lang: langs
+
+# Mask the contributors if they are too many
+$("article.news .edited_by").each ->
+  nb = @find("a").length
+  if nb > 3
+    was = @html()
+    @html "<a>#{nb} contributeurs</a>"
+    @one "click", => @html was

 # Toolbar preferences
 $("#account_visible_toolbar")


Load more posts when anchor-post is not visible

--- app/assets/javascripts/channels.js.coffee
+++ app/assets/javascripts/channels.js.coffee
@@ -145,15 +145,27 @@
         dataType: "json"
         url: "/channels/#{channelId}/visit"

-  $(document).on 'click', ".post-loader a", ->
-    loader = $(this).parent(".post-loader")
+  loadMorePosts = (include_id=0, cb=null) ->
+    loader = $(".post-loader")
     loader.data("channel-id")
     last_id = $('.post:not(.preview)').first().attr("data-post-id")
     ourl = document.location.href.replace(/#.*$/, '')
     $.get "#{ourl}/posts?first_id=#{last_id}", (data) ->
       $(data).insertAfter loader
       loader.hide()
+      cb?()
+
+  $(document).on 'click', ".post-loader a", ->
+    loadMorePosts()
     return false
+
+  if window.location.hash != ""
+    post_id = parseInt(window.location.hash.replace(/^#post_/, ''))
+    console.log post_id
+    if $(".post-#{post_id}").length < 1
+      loadMorePosts post_id, () ->
+        $("body").scrollTop($(".post-#{post_id}").offset().top)
+        window.location.hash = "post_#{post_id}"

   if $('.comment-small').length
     $('.comment-small').on 'submit', 'form', ->


More code commentary

More code commentary

--- addon/lib/sparse-array.coffee
+++ addon/lib/sparse-array.coffee
@@ -12,6 +12,13 @@
 # SparseArrayItem uses snake case to try to avoid naming conflicts with
 # proxied content

+###
+  `SparseArrayItem` is an object proxy for managing individual items in
+  an `EllaSparseArray`.
+
+  @class SparseArrayItem
+###
+
 SparseArrayItem = ObjectProxy.extend

   ###
@@ -22,17 +29,54 @@
   ###
   isSparseArrayItem: true #quack like a duck

+  ###
+    The Javascript Unix timestamp representing the last time the content for
+    this item was provided.
+
+    @property last_fetched_at
+    @type Integer
+    @default 0
+  ###
   last_fetched_at: 0

+  ###
+    The number of ms until this item is automatically considered stale.
+    The default value is 10 minutes.
+
+    @property time_to_live
+    @type Integer
+    @default 36000000
+  ###
   time_to_live: DEFAULT_TTL

+  ###
+    Loading state of the item.
+
+    @property is_loading
+    @type Boolean
+    @default false
+  ###
+  is_loading: false
+
+  ###
+    Helps to determine if this item should be fetched or not.
+
+    @property is_stale
+    @type Boolean
+    @default true
+  ###
   is_stale: computed('last_fetched_at', 'time_to_live', {
     get: ->
       (get(@, 'last_fetched_at') + get(@, 'time_to_live')) <= Date.now()
   })

-  is_loading: false
-
+  ###
+    Provide fetched content this item should represent.
+
+    @method resolve
+    @param {Object} The content for this item
+    @chainable
+  ###
   resolve: (value) ->
     setProperties(@, {
       content: value
@@ -42,6 +86,12 @@

     @

+  ###
+    Clear the content from this item and mark it stale.
+
+    @method resetItem
+    @chainable
+  ###
   resetItem: ->
     setProperties(@, {
       content: null
@@ -49,10 +99,42 @@
     })
     @

+  ###
+    Given a timestamp, determines if the content should be fetched from the
+    persistence layer.
+
+    @method isExpiredAt
+    @param {Integer} A timestamp
+    @return {Boolean}
+  ###
   isExpiredAt: (timestamp = 0) ->
     return false if get(@, 'is_loading')
     !!(get(@, 'is_stale') or get(@, 'last_fetched_at') <= timestamp)

+
+###
+  `EllaSparseArray` provides an array-like structure that can fetch portions of
+  data from a persistence layer on demand.
+
+  @example
+    var sparseArray = EllaSparseArray.create({
+      didRequestLength: function() {
+        var _this = this;
+        store.query('cat', {page: {offset: 0, limit: 1}}).then(function(response) {
+          _this.provideLength(Ember.get(response, 'meta.page.total'));
+        });
+      },
+      didRequestRange: function(range) {
+        var _this = this;
+        store.query('cat', {page: {offset: range.start, limit: range.length}}).then(function(response) {
+          _this.provideLength(Ember.get(response, 'meta.page.total'));
+          _this.provideObjectsInRange(range, response.get('content').mapBy('record'));
+        });
+      }
+    });
+
+  @class EllaSparseArray
+###

 EllaSparseArray = Ember.Object.extend Ember.Array,
   init: ->


fix autocomplete when multiple autocomplete inputs in form

--- app/assets/javascripts/contacts.js.coffee
+++ app/assets/javascripts/contacts.js.coffee
@@ -3,9 +3,11 @@
 # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/

 jQuery ->
-  $('.jq_autocomplete').autocomplete
-    source: $('.jq_autocomplete').data('autocomplete-source')
-    delay: 1000
+  $('.jq_autocomplete').each (index,element) =>
+    typeaheadField = $(element)
+    typeaheadField.autocomplete
+      source: typeaheadField.data('autocomplete-source')
+      delay: 1000

   ###
   $('.typeah').typeahead


Trigger change event on rails <select> elements

--- app/assets/javascripts/date_picker.coffee
+++ app/assets/javascripts/date_picker.coffee
@@ -32,4 +32,7 @@
     date_parts = el.value.split('/')
     return unless date_parts.length is 3
     date_parts.reverse()
-    @find(el).siblings('select').each (i) -> @value = parseInt(date_parts[i], 10)
+    $selects = @find(el).siblings('select')
+    $selects.each (i) -> @value = parseInt(date_parts[i], 10)
+    $selects.trigger('change')
+


Bug fix - clear the autocomplete base field values

--- app/assets/javascripts/meetings.js.coffee
+++ app/assets/javascripts/meetings.js.coffee
@@ -15,11 +15,11 @@
             $fields.find(".select2-container").remove()

             $fields.find(".activity-students-autocomplete-field").each ->
-                $(this).data("data", null)
+                $(this).data("data", null).val("")
                 window.Digilys.Autocomplete.activityStudentsGroups(this)

             $fields.find(".user-autocomplete-field").each ->
-                $(this).data("data", null)
+                $(this).data("data", null).val("")
                 window.Digilys.autocomplete(
                     this,
                     data: (term, page) ->


Fix profile graph x-axis scaling

Fix profile graph x-axis scaling

--- app/assets/javascripts/profiles.js.coffee
+++ app/assets/javascripts/profiles.js.coffee
@@ -14,8 +14,8 @@
         tickFormatter: percentageFormatter
       },
       xaxis: {
-        min: 1000.0,
-        max: 1000.0
+        min: 500.0,
+        max: 3000.0
       }
     }
     plot_data = $.map(data, (rating, i) ->


removed comments from coffeescript file

removed comments from coffeescript file

--- app/assets/javascripts/projects.js.coffee
+++ app/assets/javascripts/projects.js.coffee
@@ -1,7 +1,3 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script
-
 shortlist_before_send = (event, data, xhr) ->
   xhr.type = $(event.currentTarget).attr('data-method').toUpperCase()


Remove meetup from homepage section

Remove meetup from homepage section

--- app/coffee/modules/common/sections.coffee
+++ app/coffee/modules/common/sections.coffee
@@ -33,8 +33,7 @@
     6: {id: 6, title: 'ISSUES', path:'issues'}
     7: {id: 7, title: 'WIKI', path:'wiki'}
     8: {id: 8, title: 'TEAM', path:'team'}
-    9: {id: 9, title: 'MEETUP', path:'meetup'}
-    10: {id: 10, title: 'ADMIN', path:'admin'}
+    10: {id: 9, title: 'ADMIN', path:'admin'}
 }

 class SectionsService extends taiga.Service


add Documentation's comments

add Documentation's comments

--- lib/gesture.coffee
+++ lib/gesture.coffee
@@ -1,4 +1,7 @@
 {settings} = require '../package.json'
+
+# Guesture,
+# fires events on gestures from left and right
 module.exports =
 class Guesture
   space: 20
@@ -8,16 +11,82 @@
     right : {}
   on : {}

+  ###
+  Section: Construction and other initialize Methode
+  ###
+
+  #define the options
+  # * ´options´ {Object}
+  #   * ´space´ {Number} check space betweene side and mouseDown
+  #   * ´minActivate´ {Number} the move Pixel's before call the on function
+  #   * ´on´ {Object}
+  #     * ´left´ {Function}
+  #       * ´event´ {Object} the transmitted event
+  #     * right {Function}
+  #       * ´event´ {Object} the transmitted event
   constructor: (options) ->
     {@on,@space,@minActivate} = options
     @addEventListener()

+  #register the Mouse Events to MainPage (Electron)
   addEventListener: ->
     addEventListener = document.body.addEventListener
     addEventListener 'mousedown', (e) => @fortifyEvent e,@mouseDown
     addEventListener 'mousemove', (e) => @fortifyEvent e,@mouseMove
     addEventListener 'mouseup', (e) => @fortifyEvent e,@mouseUp

+
+  ###
+  Section: Event Functions
+  ###
+
+  #On Mouse press down, check if on left/right side.
+  # * `e` {Object} the Event from MainPage (electron)
+  mouseDown: (e) =>
+    @checkDown 'left', e
+    @checkDown 'right', e
+  #summery of mouseDown check for every directions
+  # * ´direction´ {String} the direction left/right
+  # * ´event´ {Object} the transmitted event
+  checkDown: (direction,event) ->
+    if event[direction] <= @space and @on[direction]?
+      @move[direction].start = event[direction]
+
+  #On Mouse move, check it was pressed and apply @on[direction] function
+  # * ´event´ {Object} the transmitted event
+  mouseMove: (e) =>
+    @checkMove 'left', e
+    @checkMove 'right', e
+  #summery of mouseMove check for every directions
+  # * ´direction´ {String} the direction left/right
+  # * ´event´ {Object} the transmitted event
+  checkMove: (direction,event) ->
+    @move[direction].activate = true if event.diff[direction] >= @minActivate
+    if @on[direction]? and @move[direction].activate?
+      @on[direction] event
+
+  #On Mouse Up, send last event with event.end=true and uncheck left/right side press.
+  # * ´event´ {Object} the transmitted event
+  mouseUp: (e) =>
+    e.end = true
+    @checkUp 'left', e
+    @checkUp 'right', e
+  #summery of mouseUp check for every directions
+  # * ´direction´ {String} the direction left/right
+  # * ´event´ {Object} the transmitted event
+  checkUp: (direction,event) ->
+    if @move[direction].activate
+      @on[direction] event
+      @move[direction] = {}
+
+  ###
+  Section: Private
+  ###
+
+  #fortify the event, before call the Event Functions
+  # * ´event´ {Object} transmitted event
+  # * ´callFunction´ {Function} Executeted function after fortify the event Object
+  #   * ´event´ {Object} the fortifyed transmitted event Object
   fortifyEvent: (event,callFunction) ->
     event.left = event.clientX
     event.winWidth = window.innerWidth
@@ -26,27 +95,3 @@
       left : if @move.left.start? then (event.left-@move.left.start) else undefined
       right : if @move.right.start? then (event.right-@move.right.start) else undefined
     callFunction event
-
-  mouseDown: (e) =>
-    @checkDown 'left', e
-    @checkDown 'right', e
-  checkDown: (direction,event) ->
-    if event[direction] <= @space and @on[direction]?
-      @move[direction].start = event[direction]
-
-  mouseMove: (e) =>
-    @checkMove 'left', e
-    @checkMove 'right', e
-  checkMove: (direction,event) ->
-    @move[direction].activate = true if event.diff[direction] >= @minActivate
-    if @on[direction]? and @move[direction].activate?
-      @on[direction] event
-
-  mouseUp: (e) =>
-    e.end = true
-    @checkUp 'left', e
-    @checkUp 'right', e
-  checkUp: (direction,event) ->
-    if @move[direction].activate
-      @on[direction] event
-      @move[direction] = {}


event -> callbackName

event -> callbackName

--- lib/assets/javascripts/cable/subscriber_manager.js.coffee
+++ lib/assets/javascripts/cable/subscriber_manager.js.coffee
@@ -19,16 +19,16 @@
     @sendCommand("unsubscribe", identifier)
     delete @subscribers[identifier]

-  notifyAll: (event, args...) ->
+  notifyAll: (callbackName, args...) ->
     for identifier, subscriber of @subscribers
-      @notify(subscriber, event, args...)
+      @notify(subscriber, callbackName, args...)

-  notify: (subscriber, event, args...) ->
+  notify: (subscriber, callbackName, args...) ->
     if typeof subscriber is "string"
       subscriber = @subscribers[subscriber]

     if subscriber
-      subscriber[event]?(args...)
+      subscriber[callbackName]?(args...)

   sendCommand: (command, identifier) ->
     return true if identifier is Cable.PING_IDENTIFIER


update webdriver manager version

update webdriver manager version

--- angular/test/protractor.coffee
+++ angular/test/protractor.coffee
@@ -8,7 +8,7 @@
 paths         = require('../tasks/paths')

 exports.config =
-  seleniumServerJar: '../node_modules/webdriver-manager/selenium/selenium-server-standalone-3.5.1.jar'
+  seleniumServerJar: '../node_modules/webdriver-manager/selenium/selenium-server-standalone-3.5.3.jar'
   allScriptsTimeout: 60000
   capabilities:
     browserName:     'firefox'


Hashing the MAC address is not secure and does guarantee anonymity

This enforces the use of an UUID v4 which is a 128bit string of which
122bits are random which guarantees anonymity. For more details of why
not to use MAC address hashing see:
http://webpolicy.org/2014/03/19/questionable-crypto-in-retail-analytics/

--- lib/metrics.coffee
+++ lib/metrics.coffee
@@ -67,15 +67,7 @@
         callback()

   createUserId: (callback) ->
-    createUUID = -> callback require('node-uuid').v4()
-    try
-      require('getmac').getMac (error, macAddress) ->
-        if error?
-          createUUID()
-        else
-          callback crypto.createHash('sha256').update(macAddress, 'utf8').digest('hex')
-    catch e
-      createUUID()
+    callback require('node-uuid').v4()

   getUserId: ->
     userId = localStorage.getItem('metrics.userId')


Made setCoordinates private

Made setCoordinates private

--- app/js/jail_iframe/classes/buttons.coffee
+++ app/js/jail_iframe/classes/buttons.coffee
@@ -15,7 +15,7 @@
     @$el.addClass stateClass
     @frame.sizeFrameToFitContent()

-  setCoordinates: (top, left) =>
+  _setCoordinates: (top, left) =>
     return if @_visible

     @_top = top
@@ -61,7 +61,7 @@

   placeNearElement: (el) ->
     offset = $(el).offset()
-    @setCoordinates offset.top, offset.left
+    @_setCoordinates offset.top, offset.left
     @show()

@@ -101,4 +101,4 @@
     else
       left = selectionLeft + selectionBox.width/2

-    @setCoordinates selectionTop-2, left
+    @_setCoordinates selectionTop-2, left


Fix clicking directly on an icon span

Fix clicking directly on an icon span

--- app/js/jail_iframe/classes/buttons.coffee
+++ app/js/jail_iframe/classes/buttons.coffee
@@ -132,7 +132,7 @@

-
+

Loading...

@@ -150,7 +150,7 @@

   _onClick: (event) =>
     @startLoading()
-    current_user_opinion = $(event.target).data('opinion')
+    current_user_opinion = $(event.target).closest('.js-opinion').data('opinion')
     FactlinkJailRoot.createFactFromSelection(current_user_opinion)

   placeNearSelection: (mouseX=null) ->


Properly assign stroke-width to nodes

Properly assign stroke-width to nodes

Fixes #195

--- app/scripts/alchemy/core/drawnodes.coffee
+++ app/scripts/alchemy/core/drawnodes.coffee
@@ -71,7 +71,8 @@
         .attr('shape-rendering', 'optimizeSpeed')
         .attr('target-id', (d) -> d.id)
         .attr('style', (d) ->
-           "#{nodeColours(d)}; stroke-width: #{this.r / 3}")
+           radius = d3.select(this).attr('r')
+           "fill:#{nodeColours(d)}; stroke-width: #{ radius / 3 }")

     #append caption to the node
     nodeEnter


Add styles on watch

Add styles on watch

--- app/skeletons/bootstrap3/Gruntfile.coffee
+++ app/skeletons/bootstrap3/Gruntfile.coffee
@@ -179,7 +179,12 @@
         tasks: ['copy:assets']
         options:
           debounceDelay: 250
-      livereload:
+      styles:
+        files: ['app/styles/**/*']
+        tasks: ['styles']
+        options:
+          debounceDelay: 250
+      watchify:
         options:
           debounceDelay: 250
           livereload: true


fix store bug increasing and decreasing the wrong languages

--- both/methods/shift/shiftReport.coffee
+++ both/methods/shift/shiftReport.coffee
@@ -89,7 +89,7 @@
 				languages = item.languages
 				break

-			for language, index in languages when language == language
+			for language, index in languages when language.short == language
 				language.stock -= parseInt count
 				languages[index..index] = [language]
 				break
@@ -119,7 +119,7 @@
 				languages = item.languages
 				break

-			for language, index in languages when language == language
+			for language, index in languages when language.short == language
 				language.stock += parseInt count
 				languages[index..index] = [language]
 				break


fix bucket input cap when over contributing

--- src/node_modules/bucket-item/bucket-item-link.coffee
+++ src/node_modules/bucket-item/bucket-item-link.coffee
@@ -17,12 +17,15 @@
       # don't accept number
       amount = oldAmount

-    if round.myAllocationsLeftCents < 0
-      amount += (round.myAllocationsLeftCents * 100)
-
     # save amount to bucket contribution
     myContribution = bucket.myContribution
     myContribution.amount = amount

+    # if over contributed, cap at allocation
+    if round.myAllocationLeftCents < 0
+      myContribution.amount += (round.myAllocationLeftCents / 100)
+      # change view model too
+      scope.model.myContributionAmount = myContribution.amount
+
     if bucket.myContributionIndex == -1
       bucket.contributions.push(myContribution)


Declare null variables properly

Declare null variables properly

--- homedisplay/display/static/js/moment_auto_update.coffee
+++ homedisplay/display/static/js/moment_auto_update.coffee
@@ -27,7 +27,8 @@
   this.update = update
   return this

-moment_auto_update
+moment_auto_update = null
+
 jq ->
   moment_auto_update = new MomentAutoUpdate()
   moment_auto_update.startInterval()


Remove a console.log

Remove a console.log

--- app/coffee/modules/auth.coffee
+++ app/coffee/modules/auth.coffee
@@ -174,7 +174,6 @@

         $el.on "submit", (event) ->
             event.preventDefault()
-            console.log "kaka"
             submit()

         $el.on "click", "a.button-login", (event) ->


KodingKontrol: remove not used code

KodingKontrol: remove not used code

--- client/Main/kite/kodingkontrol.coffee
+++ client/Main/kite/kodingkontrol.coffee
@@ -60,13 +60,6 @@
   hasKite: (options = {}) ->
     { name, correlationName, region } = options
     return (kite = @getCachedKite name, correlationName)?
-
-  fetchVmInfo: (correlationName) ->
-    new Promise (resolve, reject) ->
-      KD.remote.api.JVM.fetchVmInfo correlationName, (err, info) =>
-        return reject err                              if err
-        return reject new Error "VM info not found!"   unless info?
-        resolve info

   getWhoParams: (kiteName, correlationName) ->
     if kiteName in ['oskite', 'terminal']


terminal: WIP sessions

terminal: WIP sessions

--- client/Terminal/webtermappview.coffee
+++ client/Terminal/webtermappview.coffee
@@ -197,6 +197,9 @@
       delegate    : this
       vmName      : vmName

+    webTermView.on 'WebTermConnected', (remote) ->
+      console.log remote.session
+
     @forwardEvents webTermView, ['KeyViewIsSet', 'command']

     pane          = new KDTabPaneView


split out numbers for time ago

split out numbers for time ago

--- client/app/modules/auxiliaries.coffee
+++ client/app/modules/auxiliaries.coffee
@@ -36,9 +36,9 @@
 # TODO move copy to content directory
 timeAgo = (str) ->
     diff = (new Date()).getTime() - (new Date(str)).getTime()
-    days = Math.floor(diff / 86400000)
-    hours = Math.floor(diff / 3600000)
-    minutes = Math.floor(diff / 60000)
+    days = Math.floor(diff / (24 * 60 * 60 * 1000))
+    hours = Math.floor(diff / (60 * 60 * 1000))
+    minutes = Math.floor(diff / (60 * 1000))
     return "#{days} days ago" if days > 1
     return 'Yesterday' if days is 1
     return "#{hours} hours ago" if hours > 1


fix typo in message utils

fix typo in message utils

--- client/app/utils/message_utils.coffee
+++ client/app/utils/message_utils.coffee
@@ -62,7 +62,7 @@
         text = text.trim()
         if match = text.match /"{0,1}(.*)"{0,1} <(.*)>/
             address =
-                optionsname: match[1]
+                name: match[1]
                 address: match[2]
         else
             address =


add history

add history

--- app/js/controllers/dice.coffee
+++ app/js/controllers/dice.coffee
@@ -29,6 +29,7 @@
                         tx.jackpot.payouts = tx.dice.payouts;
                     else
                         tx.jackpot.lucky_number/= 10
+                        tx.jackpot.jackpot_received/= $scope.precision
                     tx.jackpot.play_amount /= $scope.precision;
                     console.log(tx);


Untested subjects listing

Untested subjects listing

--- app/pages/lab/subject-set.cjsx
+++ app/pages/lab/subject-set.cjsx
@@ -16,6 +16,65 @@
 VALID_SUBJECT_EXTENSIONS = ['.jpg', '.png', '.gif', '.svg']
 INVALID_FILENAME_CHARS = ['/', '\\', ':']

+SubjectSetListingData = React.createClass
+  displayName: 'SubjectSetListing'
+
+  getDefaultProps: ->
+    subjects: []
+    onPreview: Function.prototype # No-op
+    onRemove: Function.prototype
+
+  render: ->
+

+      {for subject in @props.subjects
+

+
+            {subject.id}
+
+
+            {for location in subject.locations
+
{JSON.stringify location}
}
+
+
+            <button type="button" onClick={@props.onPreview}></button>
+            <button type="button" onClick={@props.onRemove}></button>
+
+
}
+

+
+SubjectSetListing = React.createClass
+  displayName: 'SubjectSetListing'
+
+  getDefaultProps: ->
+    subjectSet: {}
+
+  getInitialState: ->
+    page: 1
+
+  getSubjects: ->
+    getSetMemberSubjects = apiClient.get 'set_member_subjects',
+      subject_set_id: @props.subjectSet.id
+      include: 'subject'
+      page: @state.page
+
+    getSetMemberSubjects.get 'subject'
+
+  render: ->
+    pageCount = NaN
+
+

+      <PromiseRenderer promise={@getSubjects()} then={(subjects) =>
+        pageCount = subject[0]?.getMeta().page_count ? 1
+        <SubjectSetListingData subjects={subjects} />
+      } />
+
+        <select value={@state.page} onChange={(e) => @setState page: e.target.value}>
+          {for p in [1..pageCount]
+            <option value={p}>{p}</option>}
+        </select>
+
+

+
 EditSubjectSetPage = React.createClass
   displayName: 'EditSubjectSetPage'

@@ -48,6 +107,10 @@

+      <SubjectSetListing subjectSet={@props.subjectSet} />
+
+

+

         <UploadDropTarget accept="text/csv, text/tab-separated-values, image/*" onSelect={@handleFileSelection}>
           Drag-and-drop manifests and subject images here.


Add note.

Add note.

--- test/e2e/helpers/expect.coffee
+++ test/e2e/helpers/expect.coffee
@@ -3,6 +3,10 @@
 # Example:
 #   expect = require('../helpers/expect')()
 #   expect(promise).to.eventually.exist
+#
+# Note: this module is the Protractor version of the unit test
+# helpers/expect.coffee. It is necessary because Protractor
+# cannot import requirejs.
 module.exports = ->
   chai = require 'chai'
   chaiAsPromised = require 'chai-as-promised'


More concise transpose function

More concise transpose function

--- attitude/display/assets/test-plane.coffee
+++ attitude/display/assets/test-plane.coffee
@@ -4,14 +4,10 @@
 transpose = (array, length=null) ->
   unless length?
     length = array[0].length
-  newArray = [0..array.length].map -> []
-  i = 0
-  while i < array.length
-    j = 0
-    while j < length
+  newArray = [0...length].map -> []
+  for i in [0...array.length]
+    for j in [0...length]
       newArray[j].push array[i][j]
-      j++
-    i++
   newArray

 testData = ->


- fix: only remove when dropdown was initialized

--- coffee/angular-daterangepicker.coffee
+++ coffee/angular-daterangepicker.coffee
@@ -159,5 +159,5 @@
       )

     $scope.$on '$destroy', ->
-      _picker.remove()
+      _picker?.remove()
 )


Watching model value, if its a single date picker

--- coffee/angular-daterangepicker.coffee
+++ coffee/angular-daterangepicker.coffee
@@ -132,10 +132,15 @@

     # Watchers enable resetting of start and end dates
     # Update the date picker, and set a new viewValue of the model
-    $scope.$watch 'model.startDate', (n) ->
-      _setStartDate(n)
-    $scope.$watch 'model.endDate', (n) ->
-      _setEndDate(n)
+    if opts.singleDatePicker
+      $scope.$watch 'model', (n) ->
+        _setStartDate(n)
+        _setEndDate(n)
+    else
+      $scope.$watch 'model.startDate', (n) ->
+        _setStartDate(n)
+      $scope.$watch 'model.endDate', (n) ->
+        _setEndDate(n)

     # Add validation/watchers for our min/max fields
     _initBoundaryField = (field, validator, modelField, optName) ->


Fix label assignment

Fix label assignment

--- app/scripts/scenes/Game.coffee
+++ app/scripts/scenes/Game.coffee
@@ -19,8 +19,8 @@
     label = "Checkpoint #{data.checkpoint}"
     window.ga('send', 'event', 'Game', 'CheckpointStart', label)
   else
+    label = 'Begin'
     window.ga('send', 'event', 'Game', 'Start', label)
-    label = 'Begin'

   executeScript = (name, options) ->
     scriptName = name


Handle better fireing

Handle better fireing

--- app/scripts/states/fire.coffee
+++ app/scripts/states/fire.coffee
@@ -4,7 +4,7 @@
     @game.stage.disableVisibilityChange = true
     @marker = @game.add.sprite @game.layer1.getTileX(@game.input.x), @game.layer1.getTileX(@game.input.y), 'notready'

-    @currentCanon = 0
+    @canFire = true

     @myCanons = []
     for c in @game.canons
@@ -85,20 +85,28 @@
     for canon in @myCanons
       canon.rotation = @game.physics.arcade.angleBetween(canon, @game.input) + Math.PI / 2

+  findFreeCanon: () ->
+    for c in @myCanons
+      if not c.busy
+        return c
+    return null
+
   inputCallback: () ->
     if @started and @myCanons.length and @counter > 0
-      canon = @myCanons[@currentCanon]
-      if not canon.busy
+      canon = @findFreeCanon()
+      if canon?
         canon.busy = true
-
-        @currentCanon++
-        if @currentCanon >= @myCanons.length
-          @currentCanon = 0
-
-
-        @fire canon, {x: @game.input.x, y: @game.input.y}, ->
+        @fire canon, {x: @game.input.x, y: @game.input.y}, =>
+          if !@canFire
+            @marker.destroy()
+            @marker = @game.add.sprite @game.layer1.getTileX(@game.input.x), @game.layer1.getTileX(@game.input.y), 'crosshair'
           canon.busy = false
         @game.session.publish @game.prefix + 'fire', [{x: canon.x, y:canon.y}, {x: @game.input.x, y: @game.input.y}]
+
+        if !@findFreeCanon()?
+          @canFire = false
+          @marker.destroy()
+          @marker = @game.add.sprite @game.layer1.getTileX(@game.input.x), @game.layer1.getTileX(@game.input.y), 'notready'

   onFire: (args)->
     @fire(args[0], args[1])


Logging removed

Logging removed

--- src/packages/carpool-view-mui/views/MuiEditTrip.coffee
+++ src/packages/carpool-view-mui/views/MuiEditTrip.coffee
@@ -1,6 +1,5 @@
-console.log(Template.MuiEditTrip, Template)
 Template.MuiEditTrip.rendered = ->
-  d "MuiEditTrip rendered"
+  #d "MuiEditTrip rendered"

   tripFormInputFrom = document.getElementById("trip-form-input-from")
   mapView.addAutocomplete tripFormInputFrom, (err, latlng, address, place)->


using jQuery detach method to preserve events on grouped row elements; tp #1305

--- jsapp/xlform_model_view/view.surveyApp.coffee
+++ jsapp/xlform_model_view/view.surveyApp.coffee
@@ -548,7 +548,7 @@
     groupSelectedRows: ->
       rows = @selectedRows()
       $q = @$('.survey__row--selected')
-      $q.remove()
+      $q.detach()
       $q.removeClass('survey__row--selected')
       @activateGroupButton(false)
       if rows.length > 0


use null instead of err in callbacks on success

--- app/coffee/LockManager.coffee
+++ app/coffee/LockManager.coffee
@@ -46,11 +46,11 @@
 						return callback(err) if err? # error freeing lock
 						callback null, false # tell caller they didn't get the lock
 				else
-					callback err, true, lockValue
+					callback null, true, lockValue
 			else
 				metrics.inc "doc-blocking"
 				profile.log("doc is locked").end()
-				callback err, false
+				callback null, false

 	getLock: (doc_id, callback = (error, lockValue) ->) ->
 		startTime = Date.now()
@@ -81,10 +81,10 @@
 			exists = parseInt exists
 			if exists == 1
 				metrics.inc "doc-blocking"
-				callback err, false
+				callback null, false
 			else
 				metrics.inc "doc-not-blocking"
-				callback err, true
+				callback null, true

 	releaseLock: (doc_id, lockValue, callback)->
 		key = keys.blockingKey(doc_id:doc_id)
@@ -99,4 +99,4 @@
 				return callback(new Error("tried to release timed out lock"))
 			else
 				profile.log("unlockScript:ok").end()
-				callback(err,result)
+				callback(null,result)


Fix loop in tests

Fix loop in tests

--- test/no_explicit_parens.coffee
+++ test/no_explicit_parens.coffee
@@ -16,15 +16,16 @@
     }

     result = if succeed then 'succeeded' else 'failed'
+    i = 1

     for code in tests
-      it "#{title} #{result}", ->
-        errors = coffeelint.lint(code, config)
-        if succeed
-          expect(errors).to.be.empty
-        else
-          expect(errors).to.not.be.empty
-
+      do (code) ->
+        it "#{title} #{i++} #{result}", ->
+          errors = coffeelint.lint(code, config)
+          if succeed
+            expect(errors).to.be.empty
+          else
+            expect(errors).to.not.be.empty

   suite 'single line strict parens', true, false, ['myFunction(a1, a2)']


Fix show-more button

Fix show-more button

--- app/controllers/builds.coffee
+++ app/controllers/builds.coffee
@@ -16,7 +16,8 @@
   showMore: ->
     id     = @get('repo.id')
     number = @get('lastObject.number')
-    @get('content').load @olderThanNumber(id, number, @get('tab'))
+    type   = if @get('tab') == "builds" then 'push' else 'pull_request'
+    @get('content').load @olderThanNumber(id, number, type)

   displayShowMoreButton: (->
     @get('tab') != 'branches' and parseInt(@get('lastObject.number')) > 1


Update virtual_gamepad_hub.coffee

Update virtual_gamepad_hub.coffee

--- app/virtual_gamepad_hub.coffee
+++ app/virtual_gamepad_hub.coffee
@@ -8,6 +8,24 @@
 config = require '../config'

 num_gamepads = config.ledBitFieldSequence.length
+# ledBitFieldSequence should be an array of 'bit fields'.
+# the bit fields are numbers from 0-15, resulting in the following LED arrangements:
+#  0 - ....
+#  1 - *...
+#  2 - .*..
+#  3 - **..
+#  4 - ..*.
+#  5 - *.*.
+#  6 - .**.
+#  7 - ***.
+#  8 - ...*
+#  9 - *..*
+# 10 - .*.*
+# 11 - **.*
+# 12 - ..**
+# 13 - *.**
+# 14 - .***
+# 15 - ****

 class virtual_gamepad_hub


Flip hearts at 50% point

Flip hearts at 50% point

--- apps/about2/client/view.coffee
+++ apps/about2/client/view.coffee
@@ -122,7 +122,7 @@
     @$("#about2-section1-pull-blurb-3-artworks .about2-image-container").waypoint
       handler: (dir) ->
         $(this).find('.icon-heart')[if dir is 'down' then 'addClass' else 'removeClass'] 'is-active'
-      offset: '75%'
+      offset: '50%'

   submitPhoneLink: (e) ->
     e.preventDefault()


Insteon dimmer: incorporate command feedback as power/brightness state

--- lib/adapters/insteon/InsteonDimmerNode.coffee
+++ lib/adapters/insteon/InsteonDimmerNode.coffee
@@ -5,7 +5,13 @@
   aspects:
     powerOnOff:
       commands:
-        set: (node, value) -> node.adapter.toggleLight(node.id, value)
+        set: (node, value) ->
+          node.adapter.toggleLight(node.id, value).then ->
+            node.getAspect('powerOnOff').setData state: value
+            node.getAspect('brightness').setData state: if value then 100 else 0
     brightness:
       commands:
-        set: (node, value) -> node.adapter.setLightLevel(node.id, value)
+        set: (node, value) ->
+          node.adapter.setLightLevel(node.id, value).then ->
+            node.getAspect('powerOnOff').setData state: (value == 0)
+            node.getAspect('brightness').setData state: value


Trade Route Mat and Value added.

Trade Route Mat and Value added.

--- gameState.coffee
+++ gameState.coffee
@@ -642,7 +642,7 @@
         @prizes.remove(card)
       else
         @supply[card] -= 1
-      if @supply["Trade Route"]? and card.isVictory and not in @tradeRouteMat
+      if @supply["Trade Route"]? and card.isVictory and card not in @tradeRouteMat
         @tradeRouteMat.push(card)
         @tradeRouteValue = +1
     else


Machine: Changed property name of ip field.

--- client/Main/providers/machine.coffee
+++ client/Main/providers/machine.coffee
@@ -25,7 +25,7 @@
     delete options.machine
     super options, machine

-    { @label, @publicAddress, @_id, @provisioners, @provider
+    { @label, @ipAddress, @_id, @provisioners, @provider
       @status, @uid, @queryString } = @jMachine = @getData()

     @fs =
@@ -36,7 +36,7 @@

   getName: ->
-    @label or @publicAddress or @uid or "one of #{KD.nick()}'s machine"
+    @label or @ipAddress or @uid or "one of #{KD.nick()}'s machine"

   getBaseKite: (createIfNotExists = yes)->


Teamwork: resize trigger on app is shown

--- client/Teamwork/AppController.coffee
+++ client/Teamwork/AppController.coffee
@@ -16,6 +16,10 @@

     super options, data

+    KD.singletons.appManager.on 'AppIsBeingShown', (app)=>
+      if app.getId() is @getId()
+        KD.utils.defer -> $(window).trigger 'resize'
+

   handleQuery: (query) ->


Fix infinite scrolling, refs #175

Fix infinite scrolling, refs #175

--- client/app/components/message-list.coffee
+++ client/app/components/message-list.coffee
@@ -437,6 +437,7 @@
         setTimeout =>
             scrollable.removeEventListener 'scroll', @_loadNext
             scrollable.addEventListener 'scroll', @_loadNext
+            @_loadNext()
         , 0

     componentDidMount: ->