Worked around node bug in cluster.js, now our UDP audit server will not be clustered. (See https://github.com/joyent/node/issues/9261)

--- src/server.coffee
+++ src/server.coffee
@@ -325,7 +325,18 @@

       auditing.processAudit msg, -> logger.info "[Auditing UDP] Processed audit"

-    auditUDPServer.bind auditUDPPort, bindAddress
+    auditUDPServer.on 'error', (err) ->
+      if err.code is 'EADDRINUSE'
+        # ignore to allow only 1 worker to bind (workaround for: https://github.com/joyent/node/issues/9261)
+        defer.resolve()
+      else
+        logger.error "UDP Audit server error: #{err}", err
+        defer.reject err
+
+    auditUDPServer.bind
+      port: auditUDPPort
+      address: bindAddress
+      exclusive: true # workaround for: https://github.com/joyent/node/issues/9261

     return defer.promise


Updated secure API setup to NOT use mutualTLS

--- src/server.coffee
+++ src/server.coffee
@@ -105,7 +105,8 @@
 startApiServer = (apiPort, app) ->
 	deferred = Q.defer()

-	mutualTLS = config.authentication.enableMutualTLSAuthentication
+  # mutualTLS not applicable for the API - set false
+	mutualTLS = false
 	tlsAuthentication.getServerOptions mutualTLS, (err, options) ->
 		return done err if err


Adapt test

Adapt test

--- test/openfisca/mapping/reverse.coffee
+++ test/openfisca/mapping/reverse.coffee
@@ -28,9 +28,14 @@
     bourse_lycee: '2014-11': 1
     paris_logement_familles: '2014-11': 1

-  DATE_DE_VALEUR = new Date '2014-11'
+  OPENFISCA_RESPONSE =
+    value: [familles: [OPENFISCA_FAMILLE]]

-  actual = reverseMap OPENFISCA_FAMILLE, DATE_DE_VALEUR
+  SITUATION =
+    dateDeValeur: new Date '2014-11'
+    individus: [ressources:[]]
+
+  actual = reverseMap OPENFISCA_RESPONSE, SITUATION

   it 'should remove unused properties', ->
     actual.should.not.have.property 'id'
@@ -50,7 +55,9 @@
     REASON = 'tns'
     familleWithUncomputableRSA = _.clone OPENFISCA_FAMILLE
     familleWithUncomputableRSA.rsa_non_calculable = '2014-11': REASON
+    openfiscaResponseWithUncomputableRSA =
+      value: [familles: [familleWithUncomputableRSA]]

     it 'should set the value to the identifier of the uncomputability', ->
-      reverseMap(familleWithUncomputableRSA, DATE_DE_VALEUR).rsa.should.equal REASON
+      reverseMap(openfiscaResponseWithUncomputableRSA, SITUATION).rsa.should.equal REASON


use new extend capabilities to name space apps

--- source/application.coffee
+++ source/application.coffee
@@ -51,4 +51,4 @@

   @define: (appName, prototype) ->
     prototype.toString = -> appName # For better debugging
-    return @extend prototype
+    return @extend appName, prototype


If bindings fix

If bindings fix

--- source/core/output.coffee
+++ source/core/output.coffee
@@ -56,8 +56,8 @@

   # Private Methods
   _createIfBindings: ->
+    @attributes.if = {}
     for key of @attributes when key not in Atoms.Core.Constants.EXCLUDED_IF_KEYS
-      @attributes.if = {} unless @attributes.if
       @attributes.if[key] = true if @attributes[key]?

 ###


Fix for broken `sockjs.listen`.

Fix for broken `sockjs.listen`.

--- src/sockjs.coffee
+++ src/sockjs.coffee
@@ -142,7 +142,7 @@
     return new Server(options)

 exports.listen = (http_server, options) ->
-    srv = createServer(options)
+    srv = exports.createServer(options)
     if http_server
         srv.installHandlers(http_server)
     return srv


Fix for 404 test

Fix for 404 test

--- test/server/routes/api/install.coffee
+++ test/server/routes/api/install.coffee
@@ -82,5 +82,5 @@
     it 'starts serving a 200 at /', (done) ->
       request(app).get('/').expect(200, done)

-    it 'still serves a 404 at /404', (done) ->
-      request(app).get('/404').expect(404, done)
+    it 'serves a 404 at /bad/url', (done) ->
+      request(app).get('/bad/url').expect(404, done)


use fs instead of path

use fs instead of path

--- src/stitch.coffee
+++ src/stitch.coffee
@@ -14,7 +14,7 @@
   # Private

   walk: (path, parent = path, result = []) ->
-    return unless npath.existsSync(path)
+    return unless fs.existsSync(path)
     for child in fs.readdirSync(path)
       child = npath.join(path, child)
       stat  = fs.statSync(child)
@@ -36,4 +36,4 @@
   valid: ->
     !!compilers[@ext]

-module.exports = Stitch+module.exports = Stitch


Pass along search term

Pass along search term

--- app/assets/javascripts/add-playlist-option.js.coffee
+++ app/assets/javascripts/add-playlist-option.js.coffee
@@ -1,6 +1,11 @@
 addnew = 'Add new playlist'

 $('#post_playlist_id').append('<option>Add new playlist</option>')
+
+getSearchTerm = () ->
+  if $('.select2-search__field')[0]
+    return $('.select2-search__field')[0].value
+  return null

 matchWithNew = (params, data) ->
   params.term = params.term || ''
@@ -9,14 +14,16 @@
   return null;

 formatAddNew = (data) ->
-  if ($('.select2-search__field')[0])
-    term = $('.select2-search__field')[0].value || ''
-  term = addnew + term
+  term = getSearchTerm() || ''
+  term = addnew + ' ' + term
   if (data.text.indexOf('Add new playlist') != -1 )
     return '
                '+term+'
             '
   return data.text
+
+showNewPlaylistModal = (playlistName) ->
+  console.log(playlistName)

 $("#post_playlist_id").select2({
   templateResult: formatAddNew
@@ -24,9 +31,9 @@
     (markup) -> return markup
   matcher: matchWithNew
   })
-  .on('select2:select',
+  .on('select2:selecting',
     (evt) ->
-      choice = evt.params.data.text
+      choice = evt.params.args.data.text
       if (choice.indexOf(addnew) != -1)
-        alert('boop')
+        showNewPlaylistModal(getSearchTerm())
   )


If any query in a transaction fails, rollback

--- db/index.coffee
+++ db/index.coffee
@@ -29,7 +29,7 @@
     statement = if typeof statement.toQuery is 'function' then statement.toQuery() else statement
     @client.query statement, (err, rows) =>
       if err
-        @emit 'error', err
+        @rollback()
       else
         cb rows


#1188 Show Input catcher before resize

#1188 Show Input catcher before resize

--- app/assets/javascripts/oxalis/view/plane_view.coffee
+++ app/assets/javascripts/oxalis/view/plane_view.coffee
@@ -251,8 +251,8 @@

     @running = true

+    $(".inputcatcher").show()
     @scaleTrianglesPlane(@model.user.get("scale"))
-    $(".inputcatcher").show()

     @animate()


jquery and jquery_ujs should be included in all.js.coffee

--- app/assets/javascripts/rails_admin_cms/all.js.coffee
+++ app/assets/javascripts/rails_admin_cms/all.js.coffee
@@ -1,3 +1,5 @@
+#= require jquery
+#= require jquery_ujs
 #= require jquery.form-validator
 #= require jquery-ui/sortable
 #= require ./cms


updates documentation comment

updates documentation comment

--- app/assets/javascripts/visualization/paths.js.coffee
+++ app/assets/javascripts/visualization/paths.js.coffee
@@ -18,9 +18,8 @@
 #                                               # :name being the @layer parameter the constructor requires.
 #
 #           nature:       crumb.nature,         # proper to the crumb. May be one of the natures enumerated in Crumb model.
-#                                               # this option is used to display particular points bigger and to change the
-#                                               # path opacity for parts that correspond to actual works (points between a hard_start)
-#                                               # and a hard_stop crumb
+#                                               # this option is used to display particular points differently. Crumbs between
+#                                               # 'hard_start' and 'hard_stop' appear in black instead of white.
 #
 #           shape:        crumb.geolocation,    # proper to the crumb. contains the actual crumb location as a Charta::Geometry object.
 #                                               # This is what is used to draw the crumb on the map


Fixed tests to use document.addEventListener instead of window.addEventListener

--- testApp/app/controllers/plugin.coffee
+++ testApp/app/controllers/plugin.coffee
@@ -5,6 +5,8 @@

     # Make Navigation Bar to appear with a custom title text
     steroids.navigationBar.show { title: "Plugin" }
+
+    alert "deviceready !"

   # ACCELEROMETER TEST

@@ -235,20 +237,29 @@
     alert "Event listener added: batterystatus"

   @addBackButton = () ->
-    window.addEventListener "backbutton", () ->
+
+    handler = () ->
       alert "Device's back button pressed !"

+    document.addEventListener "backbutton", handler, false
+
     alert "Event listener added: backbutton"

   @addMenuButton = () ->
-    window.addEventListener "menubutton", () ->
+
+    handler = () ->
       alert "Menu button pressed !"

+    document.addEventListener "menubutton", handler, false
+
     alert "Event listener added: menubutton"

   @addSearchButton = () ->
-    window.addEventListener "searchbutton", () ->
+
+    handler = () ->
       alert "searchbutton button pressed !"
+
+    document.addEventListener "searchbutton", handler, false

     alert "Event listener added: searchbutton"


Removed look...

Removed look...

--- web.coffee
+++ web.coffee
@@ -1,5 +1,3 @@
-require("look").start()
-
 require "./import-env"
 core = require "./core"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -26,7 +26,7 @@

   window.keydown.left = false

-  equals player.I.x,
+  equals player.I.x, 8

 test "should move player right while key is pressed", ->
   window.keydown.right = true


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -74,6 +74,15 @@

   equals player.I.velocity.x, Point(-1, -1).norm().x * player.I.speed
   equals player.I.velocity.y, Point(-1, -1).norm().y * player.I.speed
+
+test "should handle tracking ", ->
+  window.keydown.left = true
+  window.keydown.up = true
+
+  player.update()
+
+  equals player.I.velocity.x, Point(-1, -1).norm().x * player.I.speed
+  equals player.I.velocity.y, Point(-1, -1).norm().y * player.I.speed

 test "should correctly track facing direction", ->
   ok player.I.facing.equal(Point(1, 0))


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -9,6 +9,6 @@

   player.include(Controllable)

-  e
+  equ

 module()


Fix Homepage Code

Fix Homepage Code

--- docs/components/home/HomeBody.cjsx
+++ docs/components/home/HomeBody.cjsx
@@ -34,37 +34,37 @@
   render: ->

     afterCode = """
-                ``` javascript
+                ```javascript
                 var ReactCSS = require('reactcss');

                 class Button extends ReactCSS.Component {

-                  classes: function(){
+                  classes(){
                     return {
                       'default': {
                         button: {
                           background: '#4A90E2'
                         },
                         Icon: {
-                          fill: '#fff'
+                          fill: '#fff',
                           name: this.props.icon
                         }
                       },
                       'disabled-true': {
                         button: {
                           background: '#bbb'
-                        }
+                        },
                         span: {
                           color: '#999'
-                        }
+                        },
                         Icon: {
                           fill: '#999'
                         }
                       }
                     }
-                  };
+                  }

-                  render: function(){
+                  render(){
                     return (
                       <div is\="button">
                         <Icon is\="Icon" />
@@ -73,25 +73,25 @@
                         </span>
                       </div>
                     )
-                  };
-                };
+                  }
+                }
                 ```
                 """

     beforeCode =  """
-                  ``` javascript
+                  ```javascript
                   var merge = require('merge');

                   class Button extends React.Component {

-                    render: function(){
+                    render(){
                       var styles = {
                         button: {
                           background: '#4A90E2'
                         },
                         Icon: {
                           fill: '#fff'
-                        }
+                        },
                         disabledButton: {
                           background: '#bbb'
                         },
@@ -119,8 +119,8 @@
                           </span>
                         </div>
                       )
-                    };
-                  };
+                    }
+                  }
                   ```
                   """


Guard against no cwd when resolving

Guard against no cwd when resolving

Refs #5074

--- src/browser/main.coffee
+++ src/browser/main.coffee
@@ -52,7 +52,10 @@

     cwd = args.executedFrom?.toString() or process.cwd()
     args.pathsToOpen = args.pathsToOpen.map (pathToOpen) ->
-      path.resolve(cwd, pathToOpen.toString())
+      if cwd
+        path.resolve(cwd, pathToOpen.toString())
+      else
+        path.resolve(pathToOpen.toString())

     setupCoffeeScript()
     if args.devMode


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/clamp_bounds.coffee
+++ src/clamp_bounds.coffee
@@ -1,5 +1,6 @@
 ClampBounds = (I={}, self) ->
   Object.reverseMerge I,
+    x: 0

   self.bind "update", ->


deploy takes attrs from querystring

deploy takes attrs from querystring

--- controllers/deploys.coffee
+++ controllers/deploys.coffee
@@ -10,7 +10,7 @@
   util.log "#{'DEPLOY'.magenta} #{req.team.name} (#{req.team.id})"
   req.session.destroy()

-  attr = _.clone req.body || {}
+  attr = _.clone req.query || {}
   attr.teamId = req.team.id
   attr.remoteAddress = req.socket.remoteAddress


KDEventEmitter: removed unsubscribe method

- it was unused, redundant and confusing

--- core/KDEventEmitter.coffee
+++ core/KDEventEmitter.coffee
@@ -20,7 +20,7 @@
   _unregisterEvent = (registry, eventName, callback)->
     if eventName is "*"
       registry = {}
-    # reset the listener container so no event
+    # reset the listener container so no event3
     # will be propagated to previously registered
     # listener callbacks.
     else if callback and registry[eventName]
@@ -70,7 +70,7 @@

   constructor:->
     @KDEventEmitterEvents  = {}
-    @_e = @KDEventEmitterEvents[@constructor.name] = {}
+    @_e = {}

   emit:(eventName, args...)->
     @_e[eventName] ?= []
@@ -90,7 +90,6 @@

   on  :(eventName, callback) -> _on  @_e, eventName, callback
   off :(eventName, callback) -> _off @_e, eventName, callback
-  unsubscribe:(eventName, callback) -> _off @_e, eventName, callback

   once:(eventName, callback) ->
     _callback = =>


now clicking on a plot in CDXPlotContextView opens that plot in its own tab

--- src/coffee/views.coffee
+++ src/coffee/views.coffee
@@ -126,17 +126,28 @@
       return null
     return callback

+  make_click_handler: (model, plot_num) ->
+    ->
+      s_pc = model
+      s_pc.set('render_loop', true)
+      plotcontextview = new s_pc.default_view(
+        model: s_pc, render_loop:true,
+        el: $CDX.main_tab_set.add_tab_el(
+          tab_name:"plot#{plot_num}",  view: {}, route:"plot#{plot_num}"))
+
   build_children : () ->
     @mainlist = $("

")
     @$el.append(@mainlist)
     view_specific_options = []
-    for spec, counter in @mget('children')
+    for spec, plot_num in @mget('children')
       model = @model.resolve_ref(spec)
       model.set({'usedialog' : false})
-      plotelem = $("

")
+      plotelem = $("

")
+      plotelem.click(@make_click_handler(model, plot_num))
       @mainlist.append(plotelem)
       view_specific_options.push({'el' : plotelem})
-    created_views = build_views(@model, @views, @mget('children'), {}, view_specific_options)
+    created_views = build_views(
+      @model, @views, @mget('children'), {}, view_specific_options)
     window.pc_created_views = created_views
     window.pc_views = @views
     for view in created_views


Add test case about the chunks of multiple chars without quotes

--- test/rowDelimiter.coffee
+++ test/rowDelimiter.coffee
@@ -52,7 +52,27 @@
     parser.write ':"GHI","94"::'
     parser.write '"JKL","02"'
     parser.end()
-
+
+  it 'handle chuncks of multiple chars without quotes', (next) ->
+    data = []
+    parser = parse rowDelimiter: '::'
+    parser.on 'readable', ->
+      while d = parser.read()
+        data.push d
+    parser.on 'finish', ->
+      data.should.eql [
+        [ 'ABC','45' ]
+        [ 'DEF','23' ]
+        [ 'GHI','94' ]
+        [ 'JKL','02' ]
+      ]
+      next()
+    parser.write 'ABC,45'
+    parser.write '::DEF,23:'
+    parser.write ':GHI,94::'
+    parser.write 'JKL,02'
+    parser.end()
+
   it 'handle chuncks in autodiscovery', (next) ->
     data = []
     parser = parse()


add parameters methods to row model

add parameters methods to row model

--- jsapp/xlform/src/model.row.coffee
+++ jsapp/xlform/src/model.row.coffee
@@ -12,6 +12,8 @@
 $choices = require './model.choices'
 $skipLogicHelpers = require './mv.skipLogicHelpers'
 _t = require('utils').t
+readParameters = require('utils').readParameters
+writeParameters = require('utils').writeParameters

 module.exports = do ->
   row = {}
@@ -418,6 +420,15 @@
       # TODO [ald]: pull this from $aliases
       @get('type').get('typeId') in ['select_one', 'select_multiple']

+    getParameters: ->
+      console.log('getParameters', @)
+      return readParameters()
+
+    setParameters: (obj) ->
+      console.log('setParameters', @, obj)
+      params = writeParameters(obj)
+      return
+
     getList: ->
       _list = @get('type')?.get('list')
       if (not _list) and @_isSelectQuestion()


format

format

--- core/auth.coffee
+++ core/auth.coffee
@@ -1,6 +1,4 @@
 crypto = require 'crypto'
-assert = require 'assert'
-User = require './model/account'

 exports.sha256 = (data) ->
   if not data


add terminal menu option

add terminal menu option

--- menus/julia-client.cson
+++ menus/julia-client.cson
@@ -7,6 +7,7 @@
         {label: 'Start Julia', command: 'julia-client:start-julia'}
         {label: 'Stop Julia', command: 'julia-client:kill-julia'}
         {label: 'Interrupt Julia', command: 'julia-client:interrupt-julia'}
+        {label: 'Open Terminal', command: 'julia-client:open-a-repl'}
         {
           label: 'Working Directory'
           submenu: [


Fix day click

Fix day click

--- app/assets/javascripts/deals.js.coffee
+++ app/assets/javascripts/deals.js.coffee
@@ -209,8 +209,8 @@
   # 日ナビゲーション

   $('.for_deal_editor').on('click', '#day_navigator td.day a', (event)->
+    $(".body_tab_link[data=monthly]").click()
     $('input#date_day').val($(@).data('day'))
-    event.preventDefault()
   )

   # 記入パターンのロード（リターンキーが押されたとき）


Fix favorite link generation.

Fix favorite link generation.

--- app/assets/javascripts/favorite.coffee
+++ app/assets/javascripts/favorite.coffee
@@ -1,16 +1,30 @@
-(($) ->
-  window.Favorite = link_to_users: (users) ->
-    html = ''
-    if users.size() == 0
-      I18n.t 'js.noone'
-    else
-      html = users.slice(0, 6).map((x) ->
-        '' + x.name + ''
-      ).join(', ')
-      if users.size() > 6
-        html += '' + users.slice(6, -1).map((x) ->
-          '' + x.name + ''
-        ).join(', ') + ' (' + users.size() - 6 + ' more)'
-      html
-  return
-) jQuery
+$ = jQuery
+
+usersLinks = (users) ->
+  users.map (user) ->
+    userLink(user.id, user.name)
+  .join(", ")
+
+
+userLink = (id, name) ->
+  "#{name}"
+
+
+$(document).on "click", "#remaining-favs-link a", (e) ->
+  e.preventDefault()
+
+  $(e.target).closest("span").hide()
+  $("#remaining-favs").show()
+
+
+window.Favorite = link_to_users: (users) ->
+  return I18n.t "js.noone" if users.length == 0
+
+  html = usersLinks users.slice(0, 6)
+
+  window.x = users
+  if users.length > 6
+    html += ", #{usersLinks users.slice(6)}"
+    html += " (#{users.length - 6} more)"
+
+  html


change form selector in modal.js

change form selector in modal.js

--- app/assets/javascripts/modal.js.coffee
+++ app/assets/javascripts/modal.js.coffee
@@ -14,8 +14,10 @@

   @init= () ->
     $(document).on 'click', 'a[data-modal=true]', -> MoModal.load(this)
-    $(document).on 'ajax:beforeSend','.modal form', (event, xhr, settings) -> settings.data += "&modal=true"
-    $(document).on 'ajax:success', '.modal form', handleAjaxSuccess
+    $(document).on 'ajax:beforeSend',"#{modal_holder_selector} form", (event, xhr, settings) ->
+      console.log 'ajax:beforeSend'
+      settings.data += "&modal=true"
+    $(document).on 'ajax:success', "#{modal_holder_selector} form", handleAjaxSuccess

   @load= (anker)->
     if jQuery.type(anker) == "string"
@@ -53,6 +55,7 @@
     $(document).trigger(type: 'modal:contentUpdated', target: target)

   handleAjaxSuccess= (event, data, status, xhr)->
+    console.log 'ajax:success'
     url = xhr.getResponseHeader('Location')
     response_type = (xhr.getResponseHeader("content-type") || "")


Add changing of input fields value

Add changing of input fields value

--- app/assets/javascripts/projects.coffee
+++ app/assets/javascripts/projects.coffee
@@ -45,8 +45,16 @@
   return

 inviteInitUser = ->
+  count = 0
   $('body').on 'click', '#AddOneMoreUser', ->
-    alert 'got it'
+    count += 1
+    value = $('#project_invitations_attributes_0_email').val()
+    $('#project_invitations_attributes_0_email').val('')
+    id = '#project_invitations_attributes_' + count + '_email'
+    setTimeout (->
+      $(id).val( value )
+      return
+    ), 5

 ready = ->


fixed deprecations

fixed deprecations

--- app/assets/javascripts/s3_relay.coffee
+++ app/assets/javascripts/s3_relay.coffee
@@ -72,8 +72,8 @@
       progressMeter = $(".s3r-meter", progressColumn)

       xhr.upload.addEventListener "progress", (ev) ->
-        if ev.position
-          percentage = ((ev.position / ev.totalSize) * 100.0).toFixed(0)
+        if ev.loaded
+          percentage = ((ev.loaded / ev.total) * 100.0).toFixed(0)
           progressBar.show()
           progressMeter.css "width", "#{percentage}%"
         else


move user_id out of nested user_info in JSON

--- app/assets/javascripts/users.js.coffee
+++ app/assets/javascripts/users.js.coffee
@@ -26,8 +26,8 @@
     $.ajax '/update',
       method: 'POST'
       data:
+        user_id: user_id
         user_info:
-          id: user_id
           screen_resolution: "#{screen.width} x #{screen.height}"
           window_size: "#{$window.width()} x #{$window.height()}"
           html5_support: "#{Modernizr.canvas}"


textarea size on post edit

textarea size on post edit

--- app/assets/javascripts/views.js.coffee
+++ app/assets/javascripts/views.js.coffee
@@ -223,11 +223,9 @@
     template: ->
       ich.post_edit @post

-    events: ->
-      @selector.find('textarea').bind 'focus', @expand_text_area
-
     yield: ->
       @selector.find('.bubble').html @template()
+      @selector.find('textarea').css 'height', '100px'

   class window.TopicNewView extends CommonView
     constructor: ->


Support media entities (simply linked for now)

--- app/assets/javascripts/views.js.coffee
+++ app/assets/javascripts/views.js.coffee
@@ -66,10 +66,10 @@
       characters.splice(index, howMany, newText, ('' for i in [1...howMany])...)
     for hashtag in @getPath('content.entities.hashtags') ? []
       replaceCharacters(hashtag.indices, "##{hashtag.text}")
-    for url in @getPath('content.entities.urls') ? []
-      replaceCharacters(url.indices, "#{url.displayUrl}")
     for userMention in @getPath('content.entities.userMentions') ? []
       replaceCharacters(userMention.indices, "@#{userMention.screenName}")
+    for url in (@getPath('content.entities.urls') ? []).concat(@getPath('content.entities.media') ? [])
+      replaceCharacters(url.indices, "#{url.displayUrl}")
     characters.join ''
   ).property('content.text', 'content.entities')


Document the available notify types on the directive definition.

--- app/coffee/modules/base/confirm.coffee
+++ app/coffee/modules/base/confirm.coffee
@@ -77,6 +77,8 @@
         # TODO: at this momment the message is ignored
         # because the notification message not permits
         # custom messages.
+        #
+        # Types: error, success

         selector = ".notification-message-#{type}"


output replacement of variables (not sure if keeping it as an adapter hack)

--- src/scripts/hubot-variables.coffee
+++ src/scripts/hubot-variables.coffee
@@ -27,7 +27,14 @@
   robot.adapter._oldsend = robot.adapter.send
   robot.adapter.send = (envelope, strings...) ->
     for i in [0...strings.length]
-      strings[i] = strings[i].replace('$blah', 'whatever')
+      strings[i] = strings[i].replace variableRE, ($0, $1, $2, $3) ->
+        if $1
+          return $0
+        varname = $2 || $3
+        if !robot.brain.data.variables[varname]
+          return $0
+        index = robot.brain.data.variables.values[Math.floor(Math.random() * robot.brain.data.variables.values.length)]
+        return robot.brain.data.variables[varname].values[index]

     @_oldsend envelope, strings


sending all events to particles

sending all events to particles

--- test/timed_events.coffee
+++ test/timed_events.coffee
@@ -6,7 +6,7 @@
   gameObject.every 1, ->
     ok true

-  gameObject
+  4.times ->

 # Clear out the module
 module()


don't send messages with empty text

don't send messages with empty text

--- zechat/static/conversation.coffee
+++ zechat/static/conversation.coffee
@@ -40,7 +40,8 @@
       evt.preventDefault()
       message = @ui.message.val()
       @ui.message.val("")
-      this.trigger('send', message)
+      if message
+        this.trigger('send', message)

 class zc.Compose extends Backbone.Marionette.Controller


TODO: Use /ajax/semilattice to create a new namespace

--- admin/static/coffee/namespaces/index.coffee
+++ admin/static/coffee/namespaces/index.coffee
@@ -207,6 +207,23 @@
                 $('.alert_modal').alert()
                 @reset_buttons()
             else
+                ### TODO use this call so we are sure to be consistent when we update the namespace
+                data = {}
+                data['memcached_namespaces'] = {}
+                data['memcached_namespaces']['new'] =
+                    name: formdata.name
+                    primary_uuid: formdata.primary_datacenter
+                    port : parseInt(formdata.port)
+
+                $.ajax
+                    processData: false
+                    url: '/ajax/semilattice'
+                    type: 'POST'
+                    contentType: 'application/json'
+                    data: JSON.stringify data
+                    success: @on_success
+                    error: @on_error
+                ###
                 $.ajax
                     processData: false
                     url: '/ajax/semilattice/memcached_namespaces/new'


:memo: Wrap types in curlies

:memo: Wrap types in curlies

--- src/window.coffee
+++ src/window.coffee
@@ -1,9 +1,9 @@
 # Public: Measure how long a function takes to run.
 #
 # * description:
-#   A String description that will be logged to the console.
+#   A {String} description that will be logged to the console.
 # * fn:
-#   A Function to measure the duration of.
+#   A {Function} to measure the duration of.
 #
 # Returns the value returned by the given function.
 window.measure = (description, fn) ->
@@ -16,10 +16,10 @@
 # Public: Create a dev tools profile for a function.
 #
 # * description:
-#   A String descrption that will be available in the Profiles tab of the dev
+#   A {String} descrption that will be available in the Profiles tab of the dev
 #   tools.
 # * fn:
-#   A Function to profile.
+#   A {Function} to profile.
 #
 # Return the value returned by the given function.
 window.profile = (description, fn) ->


Use "[#{I18n.locale}] #{message}" as default when translation is missing in js I18n.screw

--- app/assets/javascrips/i18n_screwdriver/screw.coffee
+++ app/assets/javascrips/i18n_screwdriver/screw.coffee
@@ -6,5 +6,5 @@
 I18n.screw = (message, data) ->
   md5 = window.md5(message)
   translation = I18n.translations[I18n.locale][md5]
-  translation ?= ""
+  translation ?= "[#{I18n.locale}] #{message}"
   interpolate(translation, data)


Only flush select elemIds: flushElem

Only flush select elemIds: flushElem

--- js/core/elem/elem.coffee
+++ js/core/elem/elem.coffee
@@ -435,6 +435,13 @@
   flush: ->
     @flushLoop()

+  flushElem: (_elemIds)->
+    _elemIds = [_elemIds] if typeof _elemIds == 'number'
+    for _id in _elemIds
+      continue unless @_elemTodoH[_id]
+      @_elemTodoH[_id] = false
+      @_flushStyleCache(_id)
+      @_flushAttrCache(_id)
   ###
   Performs the flush of flushLoop
   ###
@@ -932,7 +939,7 @@
       @_init()
     else
       ELEM._domLoadTimer = setTimeout( =>
-        ELEM._domWaiter()
+        @._domWaiter()
       , 10 )
     null
 ELEM.reset()


Style names are decamelized automatically. One practical application to list styles keys in camelcase when used in view/control construction; no quotes needed in most cases. Before: { 'background-color': '#000000' }, after: { backgroundColor: '#000000' }

--- js/core/elem/elem.coffee
+++ js/core/elem/elem.coffee
@@ -530,8 +530,15 @@
     _str.replace( /((-)([a-z])(\w))/g, ($0, $1, $2, $3, $4)->
       $3.toUpperCase()+$4
     )
-
-
+
+  ###
+  Decamelizes string (used for js property to css property conversion)
+  ###
+  _deCamelize: (_str)->
+    _str.replace( /(([A-Z])(\w))/g, ($0, $1, $2, $3)->
+      '-'+$2.toLowerCase()+$3
+    )
+
   ###
   IE version of _setElementStyle
   ###
@@ -557,6 +564,7 @@
     if _cached == undefined
       @_initCache( _id )
       _cached = @_styleCache[_id]
+    _key = @_deCamelize( _key )
     unless _value == _cached[_key]
       _cached[_key] = _value
       if _noCache
@@ -621,6 +629,7 @@
   ###
   getStyle: (_id, _key, _noCache)->
     _cached = @_styleCache[_id]
+    _key = @_deCamelize(_key)
     if _cached[_key] == undefined or _noCache
       if _key == 'opacity'
         _value = @getOpacity(_id)


Use path from Router

Use path from Router

Otherwise it uses the window's location.  That works fine normally but
not with phantomjs, since that's set to /context.html

--- src/components/app.cjsx
+++ src/components/app.cjsx
@@ -13,16 +13,17 @@

   componentDidMount: ->
     @storeInitial()
+    Analytics.setTracker(window.ga)
     HistoryLocation.addChangeListener(@storeHistory)

   componentWillUnmount: ->
     HistoryLocation.removeChangeListener(@storeHistory)

   storeInitial: ->
-    @storeHistory(path: HistoryLocation.getCurrentPath())
+    @storeHistory(path: @context.router.getCurrentPath())

   storeHistory: (locationChangeEvent) ->
-    Analytics.onNavigation(@context.router)
+    Analytics.onNavigation(locationChangeEvent, @context.router)
     TransitionActions.load(locationChangeEvent, @context.router)

   getInitialState: ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/controllable.coffee
+++ src/controllable.coffee
@@ -10,7 +10,7 @@
     height: 17
     x: 15
     y: 30
-    speed:
+    speed:  2

   # hold the left arrow key, then
   # update the player


Use Map instead of object

Use Map instead of object

--- src/core/Pointer.coffee
+++ src/core/Pointer.coffee
@@ -1,11 +1,12 @@
 'use strict'

-dirTable =
-	'^': { x:  0, y: -1 }
-	'<': { x: -1, y:  0 }
-	'v': { x:  0, y:  1 }
-	'>': { x:  1, y:  0 }
+dirTable = new Map [
+	['^', { x:  0, y: -1 }]
+	['<', { x: -1, y:  0 }]
+	['v', { x:  0, y:  1 }]
+	['>', { x:  1, y:  0 }]
+]

 Pointer = (@x, @y, dir, @space) ->
@@ -15,12 +16,13 @@

 Pointer::_updateDir = (dir) ->
 	@dir = dir
-	@ax = dirTable[dir].x
-	@ay = dirTable[dir].y
+	entry = dirTable.get dir
+	@ax = entry.x
+	@ay = entry.y

 Pointer::turn = (dir) ->
-	if dirTable[dir]? and (dir != @dir)
+	if (dirTable.has dir) and (dir != @dir)
 		@_updateDir dir
 	@


test syncType in RequestParser

test syncType in RequestParser

--- services/clsi/test/unit/coffee/RequestParserTests.coffee
+++ services/clsi/test/unit/coffee/RequestParserTests.coffee
@@ -242,3 +242,13 @@
 		it "should return an error", ->
 			@callback.calledWith("relative path in root resource")
 				.should.equal true
+
+	describe "with an unknown syncType", ->
+		beforeEach ->
+			@validRequest.compile.options.syncType = "unexpected"
+			@RequestParser.parse @validRequest, @callback
+			@data = @callback.args[0][1]
+
+		it "should return an error", ->
+			@callback.calledWith("syncType attribute should be one of: full, incremental")
+				.should.equal true


don't log docLines when document removed from redis

they can now be binary gzipped data which messes up the logs

--- services/document-updater/app/coffee/RedisManager.coffee
+++ services/document-updater/app/coffee/RedisManager.coffee
@@ -32,7 +32,6 @@
 	removeDocFromMemory : (project_id, doc_id, callback)->
 		logger.log project_id:project_id, doc_id:doc_id, "removing doc from redis"
 		multi = rclient.multi()
-		multi.get keys.docLines(doc_id:doc_id)
 		multi.del keys.docLines(doc_id:doc_id)
 		multi.del keys.projectKey(doc_id:doc_id)
 		multi.del keys.docVersion(doc_id:doc_id)
@@ -43,8 +42,7 @@
 				logger.err project_id:project_id, doc_id:doc_id, err:err, "error removing doc from redis"
 				callback(err, null)
 			else
-				docLines = replys[0]
-				logger.log project_id:project_id, doc_id:doc_id, docLines:docLines, "removed doc from redis"
+				logger.log project_id:project_id, doc_id:doc_id, "removed doc from redis"
 				callback()

 	getDoc : (doc_id, callback = (error, lines, version) ->)->


minor grammar edits to volunteering page

made a few grammar suggested edits to the volunteering page

--- app/pages/about/volunteering-page.cjsx
+++ app/pages/about/volunteering-page.cjsx
@@ -17,10 +17,10 @@
     ### Volunteer as a Beta Tester
     Volunteers also help us test projects before they are launched to check that they work properly. This involves working through some classifications on the beta project to check that it works, looking for any bugs, and filling out a questionnaire at the end. This helps us find any issues in the project that need resolving and also assess how suitable the project is for the Zooniverse. You can read some guidelines on what makes a project suitable on the [Policies](/lab-policies) page, under 'Rules and Regulations'.

-    To sign up as a beta tester, go to [www.zooniverse.org/settings/email](/settings/email) and tick the box next relating to beta testing. We'll then send you emails when a project is ready to be tested. You can change your email settings any time you want at [same email page](/settings/email) and untick the box.
+    To sign up as a beta tester, go to [www.zooniverse.org/settings/email](/settings/email) and tick the box relating to beta testing. We'll then send you emails when a project is ready to be tested. You can change your email settings any time you want using the [same email page](/settings/email) and unticking the box.

     ### Volunteer as a Project Moderator
-    Volunteer moderators have extra permissions in the Talk boards for a particular project. They help moderate discussions and act as a point of contact for the project and are selected by the project owner. If you're interested in becoming a moderator on a project your'e taking part in, go to the About page and get in touch with the researcher.
+    Volunteer moderators have extra permissions in the Talk discussion tool for a particular project. They help moderate discussions and act as a point of contact for the project. Modeators are selected by the project owner. If you're interested in becoming a moderator on a project your'e taking part in, go to the project's About page and get in touch with the researcher.

     ### Further Information
     If you'd like any more information on any of these different roles, contact us via the [Contact Us](/about/contact) page.


realy small code clean up

realy small code clean up

--- src/buffer-stream.coffee
+++ src/buffer-stream.coffee
@@ -62,7 +62,8 @@
         @flush() unless @paused

     flush: () =>
-        @emit('data', @buffer) if @buffer.length
+        return unless @buffer.length
+        @emit('data', @buffer)
         @buffer = new Buffer(0)

     split: (args...) =>


Set default fees level to "fast"

Set default fees level to "fast"

--- app/src/preferences/preferences.coffee
+++ app/src/preferences/preferences.coffee
@@ -20,7 +20,7 @@
     default: 'USD'

   miningFee:
-    default: ledger.preferences.defaults.Bitcoin.fees.normal.value
+    default: ledger.preferences.defaults.Bitcoin.fees.fast.value

   blockchainExplorer:
     default: _.keys(ledger.preferences.defaults.Bitcoin.explorers)[0]


Fix coffeelint

Fix coffeelint

https://travis-ci.org/atom/atom-keymap/builds/168703887

--- src/command-event.coffee
+++ src/command-event.coffee
@@ -13,13 +13,13 @@
   propagationStopped: false

   abortKeyBinding: ->
-    @stopImmediatePropagation()
-    @keyBindingAborted = true
+    this.stopImmediatePropagation()
+    this.keyBindingAborted = true

   stopPropagation: ->
-    @propagationStopped = true
+    this.propagationStopped = true
     super

   stopImmediatePropagation: ->
-    @propagationStopped = true
+    this.propagationStopped = true
     super


Speling

Speling

--- src/config-schema.coffee
+++ src/config-schema.coffee
@@ -139,7 +139,7 @@
       softWrap:
         type: 'boolean'
         default: false
-        description: 'Wraps lines that exceeed the width of the window. When `Soft Wrap At Preferred Line Length` is set, it will wrap to the number of characters defined by the `Preferred Line Length` setting.'
+        description: 'Wraps lines that exceed the width of the window. When `Soft Wrap At Preferred Line Length` is set, it will wrap to the number of characters defined by the `Preferred Line Length` setting.'
       softTabs:
         type: 'boolean'
         default: true


FormPopover: Don't auto position after data-change to not reset current scroll bars.

--- src/elements/FormPopover/FormPopover.coffee
+++ src/elements/FormPopover/FormPopover.coffee
@@ -268,12 +268,6 @@
 				@__renderDisplay()
 				@__dataChanged = info

-
-				CUI.setTimeout
-					ms: 0
-					call: =>
-						@__popover?.position()
-
 				if @_trigger_data_changed_while_open
 					@__triggerDataChanged()
 				return


<log /> rendering fixed

<log /> rendering fixed


removed old code and fixed the old, stupid way of handling resizing

--- app/assets/javascripts/facilities.js.coffee
+++ app/assets/javascripts/facilities.js.coffee
@@ -15,8 +15,8 @@
         right:  10
     }

-getWidth  = (elem) -> $(elem).width()
-getHeight = (elem) -> Math.floor(getWidth(elem)/3)
+getWidth  = () -> 500
+getHeight = () -> Math.floor(getWidth()/3)

 # Get the submission data for the given facility_id
 # returns an array of objects:
@@ -44,9 +44,12 @@

 # Get an SVG object with the dimensions given
 getSVG = (elem, width, height, margin) ->
+    width = width + margin.left + margin.right
+    height = height + margin.top + margin.bottom
     d3.select(elem).append("svg")
-          .attr("width", width + margin.left + margin.right)
-          .attr("height", height + margin.top + margin.bottom)
+          .attr("viewBox", "0 0 " + width + " " + height)
+          .attr("width", "100%")
+          .attr("height","100%")
         .append("g")
           .attr("transform", "translate(" + margin.left + "," + margin.top + ")")

@@ -56,48 +59,22 @@
 #   figures: An array of objects, each object being
 #       class: "<html class>"
 #       getFig: (x, y, width, height) -> figure
-# returns:
-#   the svg object, so it can be further manipulated
 drawGraph = (elem, figures) ->
     $_elem = $(elem)
     $_elem.empty()
     facility_id = parseInt($_elem.attr("facility-id"))
-    data = $_elem.data("data")
-    if not data
-        data = getData(facility_id)
-        $_elem.data({data: data})
+    data = getData(facility_id)
     # dimensions for the graph
     margin = getMargins()
-    width  = getWidth(elem)
-    height = getHeight(elem)
+    width  = getWidth()
+    height = getHeight()

     x = d3.time.scale().range([0, width])
     y = d3.scale.linear().range([height, 0])
     xAxis = d3.svg.axis().scale(x).orient("bottom")
     yAxis = d3.svg.axis().scale(y).orient("left")
-
+
     svg = getSVG(elem, width, height, margin)
-
-    washersLine = d3.svg.line()
-        .x( (d) -> x(d.date) )
-        .y( (d) -> y(d.washers) )
-        .interpolate("monotone")
-    washersArea = d3.svg.area()
-        .x( (d) -> x(d.date) )
-        .y0( height )
-        .y1( (d) -> y(d.washers) )
-        .interpolate("monotone")
-
-    driersLine = d3.svg.line()
-        .x( (d) -> x(d.date)  )
-        .y( (d) -> y(d.washers + d.driers))
-        .interpolate("monotone")
-    driersArea = d3.svg.area()
-        .x( (d) -> x(d.date) )
-        .y0( (d) -> y(d.washers) )
-        .y1( (d) -> y(d.washers + d.driers) )
-        .interpolate("monotone")
-

     x.domain(d3.extent(data, (d) -> d.date))
     y.domain([0, 100])
@@ -108,8 +85,6 @@
             .datum(data)
             .attr("class", fig.class)
             .attr("d", fig.getFig(x, y, width, height))
-    # Redraw this if the window is resized
-    $(window).resize(() -> drawGraph(elem, figures))

 # ######### GRAPHING FUNCTIONS ######### #


Add feature properties to set a feature removable or keep track of its level (if any). Add selector on popup to change its level if option is enabled. Set trigger on each deleted layer. Add an accessor to update properties of a feature.

--- app/assets/javascripts/map_editor.js.coffee
+++ app/assets/javascripts/map_editor.js.coffee
@@ -103,6 +103,8 @@
         try
           feature = e.layer.toGeoJSON()
           feature.properties['internal_id'] = new Date().getTime()
+          feature.properties['removable'] = true
+          feature.properties['level'] = 0
           widget.edition.addData feature
         catch
           widget.edition.addLayer e.layer
@@ -115,6 +117,10 @@
         widget.element.trigger "mapchange"

       this.map.on "draw:deleted", (e) ->
+        layers = e.layers
+        layers.eachLayer (layer) =>
+          widget.element.trigger 'mapeditor:feature_delete', layer.feature
+
         widget._saveUpdates()
         widget.element.trigger "mapchange"

@@ -136,6 +142,11 @@
         featureId = $(e.currentTarget).closest('.leaflet-popup-content').find('*[data-internal-id]').data('internal-id')
         newName = $(e.currentTarget).closest('.popup-content').find('input[type="text"]').val()

+        if this.options.multiLevels?
+          level = $(e.currentTarget).closest('.popup-content').find('select').val()
+          this.updateFeatureProperties(featureId, 'level', level)
+
+
         this.updateFeatureProperties(featureId, 'name', newName)

         layer = this.findLayer(featureId)
@@ -150,6 +161,14 @@
         false

       widget.element.trigger "mapeditor:loaded"
+
+    updateFeature: (feature_id, attributeName, attributeValue) ->
+      this.updateFeatureProperties(feature_id, attributeName, attributeValue)
+      layer = this.findLayer(feature_id)
+
+      #update popup
+      this.popupize layer.feature, layer
+

     updateFeatureProperties: (feature_id, attributeName, attributeValue) ->
       layer = this.findLayer(feature_id)
@@ -183,6 +202,18 @@
       popup += "
"
       popup += "
"
       popup += "<input type='text' value='#{feature.properties.name}'/>"
+
+      if this.options.multiLevels?
+        popup += "<select>"
+        for level in [parseInt(this.options.multiLevels.minLevel)..parseInt(this.options.multiLevels.maxLevel)]
+          selected = ""
+          if level == parseInt(feature.properties.level)
+            selected = "selected"
+
+          popup += "<option value='#{level}' #{selected}>#{level}</option>"
+
+        popup += "</select>"
+
       popup += "<input class='updateAttributesInPopup' type='button' value='ok'/>"
       popup += "
"

@@ -271,7 +302,13 @@
         else
           this.edition = L.GeoJSON.geometryToLayer(this.options.edit)
       else
-        this.edition = new L.GeoJSON()
+        this.edition = L.geoJson(this.options.edit, {
+          onEachFeature: (feature, layer) =>
+            $(this.element).trigger('mapeditor:feature_add', feature)
+
+            if feature.properties?
+              this.popupize(feature, layer)
+        })

       this.edition.setStyle this.options.editStyle
       this.edition.addTo this.map


:white_check_mark: increase/decrease for visual-mode

--- spec/operator-spec.coffee
+++ spec/operator-spec.coffee
@@ -1514,98 +1514,218 @@

   describe "the ctrl-a/ctrl-x keybindings", ->
     beforeEach ->
-      settings.set 'numberRegex', settings.config.numberRegex.default
       set
         text: '123\nab45\ncd-67ef\nab-5\na-bcdef'
         cursorBuffer: [[0, 0], [1, 0], [2, 0], [3, 3], [4, 0]]

     describe "increasing numbers", ->
-      it "increases the next number", ->
-        ensure [ctrl: 'a'],
-          text: '124\nab46\ncd-66ef\nab-4\na-bcdef'
-          cursorBuffer: [[0, 2], [1, 3], [2, 4], [3, 3], [4, 0]]
-
-      it "repeats with .", ->
-        ensure [ctrl: 'a', '.'],
-          text: '125\nab47\ncd-65ef\nab-3\na-bcdef'
-          cursorBuffer: [[0, 2], [1, 3], [2, 4], [3, 3], [4, 0]]
-
-      it "can have a count", ->
-        ensure ['5', ctrl: 'a'],
-          cursorBuffer: [[0, 2], [1, 3], [2, 4], [3, 2], [4, 0]]
-          text: '128\nab50\ncd-62ef\nab0\na-bcdef'
-
-      it "can make a negative number positive, change number of digits", ->
-        ensure ['99', ctrl: 'a'],
-          text: '222\nab144\ncd32ef\nab94\na-bcdef'
-          cursorBuffer: [[0, 2], [1, 4], [2, 3], [3, 3], [4, 0]]
-
-      it "does nothing when cursor is after the number", ->
-        set cursorBuffer: [2, 5]
-        ensure [ctrl: 'a'],
-          text: '123\nab45\ncd-67ef\nab-5\na-bcdef'
-          cursorBuffer: [[2, 5]]
-
-      it "does nothing on an empty line", ->
-        set
-          text: '\n'
-          cursorBuffer: [[0, 0], [1, 0]]
-        ensure [ctrl: 'a'],
-          cursorBuffer: [[0, 0], [1, 0]]
-          text: '\n'
-
-      it "honours the vim-mode-plus.numberRegex setting", ->
-        set
-          text: '123\nab45\ncd -67ef\nab-5\na-bcdef'
-          cursorBuffer: [[0, 0], [1, 0], [2, 0], [3, 3], [4, 0]]
-        settings.set('numberRegex', '(?:\\B-)?[0-9]+')
-        ensure [ctrl: 'a'],
-          cursorBuffer: [[0, 2], [1, 3], [2, 5], [3, 3], [4, 0]]
-          text: '124\nab46\ncd -66ef\nab-6\na-bcdef'
-
+      describe "normal-mode", ->
+        it "increases the next number", ->
+          ensure {ctrl: 'a'},
+            text: '124\nab46\ncd-66ef\nab-4\na-bcdef'
+            cursorBuffer: [[0, 2], [1, 3], [2, 4], [3, 3], [4, 0]]
+
+        it "repeats with .", ->
+          ensure [{ctrl: 'a'}, '.'],
+            text: '125\nab47\ncd-65ef\nab-3\na-bcdef'
+            cursorBuffer: [[0, 2], [1, 3], [2, 4], [3, 3], [4, 0]]
+
+        it "can have a count", ->
+          ensure ['5', {ctrl: 'a'}],
+            cursorBuffer: [[0, 2], [1, 3], [2, 4], [3, 2], [4, 0]]
+            text: '128\nab50\ncd-62ef\nab0\na-bcdef'
+
+        it "can make a negative number positive, change number of digits", ->
+          ensure ['99', {ctrl: 'a'}],
+            text: '222\nab144\ncd32ef\nab94\na-bcdef'
+            cursorBuffer: [[0, 2], [1, 4], [2, 3], [3, 3], [4, 0]]
+
+        it "does nothing when cursor is after the number", ->
+          set cursorBuffer: [2, 5]
+          ensure {ctrl: 'a'},
+            text: '123\nab45\ncd-67ef\nab-5\na-bcdef'
+            cursorBuffer: [[2, 5]]
+
+        it "does nothing on an empty line", ->
+          set
+            text: '\n'
+            cursorBuffer: [[0, 0], [1, 0]]
+          ensure {ctrl: 'a'},
+            text: '\n'
+            cursorBuffer: [[0, 0], [1, 0]]
+
+        it "honours the vim-mode-plus.numberRegex setting", ->
+          set
+            text: '123\nab45\ncd -67ef\nab-5\na-bcdef'
+            cursorBuffer: [[0, 0], [1, 0], [2, 0], [3, 3], [4, 0]]
+          settings.set('numberRegex', '(?:\\B-)?[0-9]+')
+          ensure {ctrl: 'a'},
+            cursorBuffer: [[0, 2], [1, 3], [2, 5], [3, 3], [4, 0]]
+            text: '124\nab46\ncd -66ef\nab-6\na-bcdef'
+      describe "visual-mode", ->
+        beforeEach ->
+          set
+            text: """
+              1 2 3
+              1 2 3
+              1 2 3
+              1 2 3
+              """
+        it "increase number in characterwise selected range", ->
+          set cursor: [0, 2]
+          ensure ["v2j", {ctrl: 'a'}],
+            text: """
+              1 3 4
+              2 3 4
+              2 3 3
+              1 2 3
+              """
+            selectedText: "3 4\n2 3 4\n2 3"
+            cursor: [2, 3]
+        it "increase number in characterwise selected range when multiple cursors", ->
+          set cursor: [0, 2], addCursor: [2, 2]
+          ensure ["v10", {ctrl: 'a'}],
+            text: """
+              1 12 3
+              1 2 3
+              1 12 3
+              1 2 3
+              """
+            selectedTextOrdered: ["12", "12"]
+            selectedBufferRangeOrdered: [
+                [[0, 2], [0, 4]]
+                [[2, 2], [2, 4]]
+              ]
+        it "increase number in linewise selected range", ->
+          set cursor: [0, 0]
+          ensure ["V2j", {ctrl: 'a'}],
+            text: """
+              2 3 4
+              2 3 4
+              2 3 4
+              1 2 3
+              """
+            selectedText: "2 3 4\n2 3 4\n2 3 4\n"
+            cursor: [3, 0]
+        it "increase number in blockwise selected range", ->
+          set cursor: [1, 2]
+          ensure [{ctrl: 'v'}, '2l2j', {ctrl: 'a'}],
+            text: """
+              1 2 3
+              1 3 4
+              1 3 4
+              1 3 4
+              """
+            selectedTextOrdered: ["3 4", "3 4", "3 4"]
+            selectedBufferRangeOrdered: [
+                [[1, 2], [1, 5]],
+                [[2, 2], [2, 5]],
+                [[3, 2], [3, 5]],
+              ]
     describe "decreasing numbers", ->
-      it "decreases the next number", ->
-        ensure [ctrl: 'x'],
-          text: '122\nab44\ncd-68ef\nab-6\na-bcdef'
-          cursorBuffer: [[0, 2], [1, 3], [2, 4], [3, 3], [4, 0]]
-
-      it "repeats with .", ->
-        ensure [ctrl: 'x', '.'],
-          text: '121\nab43\ncd-69ef\nab-7\na-bcdef'
-          cursorBuffer: [[0, 2], [1, 3], [2, 4], [3, 3], [4, 0]]
-
-      it "can have a count", ->
-        ensure ['5', ctrl: 'x'],
-          text: '118\nab40\ncd-72ef\nab-10\na-bcdef'
-          cursorBuffer: [[0, 2], [1, 3], [2, 4], [3, 4], [4, 0]]
-
-      it "can make a positive number negative, change number of digits", ->
-        ensure ['99', ctrl: 'x'],
-          text: '24\nab-54\ncd-166ef\nab-104\na-bcdef'
-          cursorBuffer: [[0, 1], [1, 4], [2, 5], [3, 5], [4, 0]]
-
-      it "does nothing when cursor is after the number", ->
-        set cursorBuffer: [2, 5]
-        ensure [ctrl: 'x'],
-          text: '123\nab45\ncd-67ef\nab-5\na-bcdef'
-          cursorBuffer: [[2, 5]]
-
-      it "does nothing on an empty line", ->
-        set
-          text: '\n'
-          cursorBuffer: [[0, 0], [1, 0]]
-        ensure [ctrl: 'x'],
-          text: '\n'
-          cursorBuffer: [[0, 0], [1, 0]],
-
-      it "honours the vim-mode-plus.numberRegex setting", ->
-        set
-          text: '123\nab45\ncd -67ef\nab-5\na-bcdef'
-          cursorBuffer: [[0, 0], [1, 0], [2, 0], [3, 3], [4, 0]]
-        settings.set('numberRegex', '(?:\\B-)?[0-9]+')
-        ensure [ctrl: 'x'],
-          text: '122\nab44\ncd -68ef\nab-4\na-bcdef'
-          cursorBuffer: [[0, 2], [1, 3], [2, 5], [3, 3], [4, 0]]
+      describe "normal-mode", ->
+        it "decreases the next number", ->
+          ensure {ctrl: 'x'},
+            text: '122\nab44\ncd-68ef\nab-6\na-bcdef'
+            cursorBuffer: [[0, 2], [1, 3], [2, 4], [3, 3], [4, 0]]
+
+        it "repeats with .", ->
+          ensure [{ctrl: 'x'}, '.'],
+            text: '121\nab43\ncd-69ef\nab-7\na-bcdef'
+            cursorBuffer: [[0, 2], [1, 3], [2, 4], [3, 3], [4, 0]]
+
+        it "can have a count", ->
+          ensure ['5', {ctrl: 'x'}],
+            text: '118\nab40\ncd-72ef\nab-10\na-bcdef'
+            cursorBuffer: [[0, 2], [1, 3], [2, 4], [3, 4], [4, 0]]
+
+        it "can make a positive number negative, change number of digits", ->
+          ensure ['99', {ctrl: 'x'}],
+            text: '24\nab-54\ncd-166ef\nab-104\na-bcdef'
+            cursorBuffer: [[0, 1], [1, 4], [2, 5], [3, 5], [4, 0]]
+
+        it "does nothing when cursor is after the number", ->
+          set cursorBuffer: [2, 5]
+          ensure {ctrl: 'x'},
+            text: '123\nab45\ncd-67ef\nab-5\na-bcdef'
+            cursorBuffer: [[2, 5]]
+
+        it "does nothing on an empty line", ->
+          set
+            text: '\n'
+            cursorBuffer: [[0, 0], [1, 0]]
+          ensure {ctrl: 'x'},
+            text: '\n'
+            cursorBuffer: [[0, 0], [1, 0]],
+
+        it "honours the vim-mode-plus.numberRegex setting", ->
+          set
+            text: '123\nab45\ncd -67ef\nab-5\na-bcdef'
+            cursorBuffer: [[0, 0], [1, 0], [2, 0], [3, 3], [4, 0]]
+          settings.set('numberRegex', '(?:\\B-)?[0-9]+')
+          ensure {ctrl: 'x'},
+            text: '122\nab44\ncd -68ef\nab-4\na-bcdef'
+            cursorBuffer: [[0, 2], [1, 3], [2, 5], [3, 3], [4, 0]]
+      describe "visual-mode", ->
+        beforeEach ->
+          set
+            text: """
+              1 2 3
+              1 2 3
+              1 2 3
+              1 2 3
+              """
+        it "decrease number in characterwise selected range", ->
+          set cursor: [0, 2]
+          ensure ["v2j", {ctrl: 'x'}],
+            text: """
+              1 1 2
+              0 1 2
+              0 1 3
+              1 2 3
+              """
+            selectedText: "1 2\n0 1 2\n0 1"
+            cursor: [2, 3]
+        it "decrease number in characterwise selected range when multiple cursors", ->
+          set cursor: [0, 2], addCursor: [2, 2]
+          ensure ["v5", {ctrl: 'x'}],
+            text: """
+              1 -3 3
+              1 2 3
+              1 -3 3
+              1 2 3
+              """
+            selectedTextOrdered: ["-3", "-3"]
+            selectedBufferRangeOrdered: [
+                [[0, 2], [0, 4]]
+                [[2, 2], [2, 4]]
+              ]
+        it "decrease number in linewise selected range", ->
+          set cursor: [0, 0]
+          ensure ["V2j", {ctrl: 'x'}],
+            text: """
+              0 1 2
+              0 1 2
+              0 1 2
+              1 2 3
+              """
+            selectedText: "0 1 2\n0 1 2\n0 1 2\n"
+            cursor: [3, 0]
+        it "decrease number in blockwise selected rage", ->
+          set cursor: [1, 2]
+          ensure [{ctrl: 'v'}, '2l2j', {ctrl: 'x'}],
+            text: """
+              1 2 3
+              1 1 2
+              1 1 2
+              1 1 2
+              """
+            selectedTextOrdered: ["1 2", "1 2", "1 2"]
+            selectedBufferRangeOrdered: [
+                [[1, 2], [1, 5]],
+                [[2, 2], [2, 5]],
+                [[3, 2], [3, 5]],
+              ]

   describe 'the R keybinding', ->
     beforeEach ->


automatically alias the $root to @root

automatically alias the $root to @root

--- library/membrane/visual.coffee
+++ library/membrane/visual.coffee
@@ -246,6 +246,7 @@
             assert @deployContext context, symbol
             assert @inlineAutocalls context, symbol
             context.inline -> @emit "installed", this
+            context.inline -> assert try @root = $root
             context = @compressContext context if asm
             compiled = @compileContext context if asm
             return receive context, compiled or null


no need to pass in contex twice

no need to pass in contex twice

--- library/membrane/visual.coffee
+++ library/membrane/visual.coffee
@@ -89,7 +89,7 @@
         prepared = util.inspect context
         entrypoint = @entrypoint.remote.source
         installer = "var context = #{prepared}"
-        invoker = "context.entrypoint(context)"
+        invoker = "context.entrypoint()"
         _.forIn this, (value, key, object) ->
             return unless _.isObject value.remote
             return unless src = value.remote.source


update datasystem compatibility 2

update datasystem compatibility 2

--- app/controllers/templates_controller.coffee
+++ app/controllers/templates_controller.coffee
@@ -64,7 +64,6 @@

 #search
 action 'search', ->
-<<<<<<< HEAD
     if req.query?
         if req.query.doctype? and req.query.range? and req.query.page? and req.query.nbperpage?

@@ -141,84 +140,6 @@
             #     res.send(ds.pageCountMatrix[newKey].slice(nbPerPage*(page-1), limit))
         else
             res.send([{ 'no_result' : 'No result for now.' }])
-
-=======
-    if req.query? and req.query.doctype? and req.query.range? and \
-       req.query.page? and req.query.nbperpage?
-
-        #prepare params
-        pageParams = {}
-        page = parseInt(req.query.page, 10)
-        nbPerPage = parseInt(req.query.nbperpage, 10)
-
-
-        #newKey = req.query.doctype.join('_')
-        # if not ds.pageCountMatrix[newKey]?
-        #     ds.pageCountMatrix[newKey] = []
-
-        #skip & limit + deleted lines
-        nbDeleted = if req.query.deleted? then parseInt(req.query.deleted, 10) else 0
-        pageParams['limit'] = nbPerPage
-        if page > 1
-            pageParams['skip'] = (nbPerPage * (page - 1)) - nbDeleted
-
-        #add query for plain text search
-        if req.query.query? and req.query.query isnt ""
-            pageParams['query'] = req.query.query
-
-        #if page is 1
-        requests = []
-        requests.push (callback) -> #0 -> metadoctypes
-            ds.getView callback, ds.getPATH().metadoctype.getallbyrelated
-
-        #one request per doctype
-        #reqCount = 0
-        #for dt in req.query.doctype
-        requests.push (callback) -> #1 to n -> requests
-            if pageParams['query']?
-                ds.getView callback, ds.getPATH().search + req.query.doctype[0], pageParams
-            else
-                ds.getView callback, ds.getPATH().request + req.query.doctype[0] + ds.getPATH().all, pageParams
-            #reqCount++
-
-
-        async.parallel requests, (error, results) ->
-            jsonRes = []
-            if error
-                res.send('no_result', 'No result : Server error occurred while retrieving data.')
-                console.log error
-            else
-                console.log results
-                idField = []
-                descField = []
-                #for dt in req.query.doctype
-                for md in results[0]
-                    if md.key? and md.value.identificationField? and md.key.toLowerCase() is req.query.doctype[0].toLowerCase()
-                        idField[req.query.doctype[0].toLowerCase()] = md.value.identificationField
-                        if md.value.fields[0]?
-                            descField[req.query.doctype[0].toLowerCase()] = md.value.fields[0]
-                #for result, index in results
-                    #if index > 0
-                for doc in results[1]
-                    if doc.key? and doc.value?
-                        doc.value['idField'] = idField[doc.value['docType'].toLowerCase()]
-                        doc.value['descField'] = descField[doc.value['docType'].toLowerCase()]
-                        jsonRes.push doc.value
-                res.send(jsonRes)
-
-                #page count matrix knows how many results must be skipped for each doctypes
-                #ds.pageCountMatrix[newKey] = jsonRes
-
-                #prepare limit
-                #limit = if ds.pageCountMatrix[newKey].length? and ds.pageCountMatrix[newKey].length <= nbPerPage then ds.pageCountMatrix[newKey].length else nbPerPage
-                #res.send(ds.pageCountMatrix[newKey].slice(0, limit))
-        # else if ds.pageCountMatrix[newKey] and ds.pageCountMatrix[newKey].length > nbPerPage
-        #     #send json
-        #     limit = if ds.pageCountMatrix[newKey].length? and ds.pageCountMatrix[newKey].length <= nbPerPage*page then ds.pageCountMatrix[newKey].length else nbPerPage*page
-        #     res.send(ds.pageCountMatrix[newKey].slice(nbPerPage*(page-1), limit))
->>>>>>> c8ba91e036e7543d8d1f15fafb4e5b2a056cf6b1
-    else
-        res.send([{ 'no_result' : 'No result for now.' }])

 #delete
 action 'delete', ->


Worked around issue with docs tab polluting supermodel for components to save. Another instance of #1108. Actually, same one, but the fix eralier got undone.

--- app/views/editor/level/LevelEditView.coffee
+++ app/views/editor/level/LevelEditView.coffee
@@ -79,7 +79,7 @@
     @insertSubView new ComponentsTabView supermodel: @supermodel
     @insertSubView new SystemsTabView supermodel: @supermodel
     @insertSubView new RelatedAchievementsView supermodel: @supermodel, level: @level
-    @insertSubView new ComponentDocsView supermodel: @supermodel
+    @insertSubView new ComponentDocsView  # Don't give it the supermodel, it'll pollute it!

     Backbone.Mediator.publish 'level-loaded', level: @level
     @showReadOnly() if me.get('anonymous')


Make projection rebuilder log warnings instead of throwing errors if there is no data to insert after rebuilding projected collection

--- source/server/infrastructure/projection-rebuilder.coffee
+++ source/server/infrastructure/projection-rebuilder.coffee
@@ -4,8 +4,10 @@

   dependencies: {
     commitStore: 'Space.eventSourcing.CommitStore'
+    configuration: 'configuration'
     injector: 'Injector'
     mongo: 'Mongo'
+    log: 'log'
   }

   rebuild: (projections, options) ->
@@ -42,7 +44,7 @@
       if inMemoryData.length
         realCollection.batchInsert inMemoryData
       else
-        throw new Error "No data to insert after replaying events for #{collectionId}"
+        @log.warning(@_logMsg("No data to insert after replaying events for #{collectionId}"))
       # Restore original collections
       @injector.override(collectionId).to realCollection

@@ -54,3 +56,6 @@
     for property, id of projection.collections
       collectionIds.push(id) if projection[property]
     return collectionIds
+
+  _logMsg: (message) ->
+    "#{@configuration.appId}: #{this}: #{message}"


posts - video - vix resizing

posts - video - vix resizing

--- _assets/javascripts/sections/articles.coffee
+++ _assets/javascripts/sections/articles.coffee
@@ -28,6 +28,7 @@
       # grab/create the embed
       embed_box = document.getElementById('video_embed')
       window.embetter.utils.buildPlayerFromServiceURL(embed_box, video_link, curServices)
+      $embed_box.removeAttr('style')

 $(document).ready ->
   if $("body.article").length


Remove unimplemented command-viewer

Remove unimplemented command-viewer


AlerterWorker: remove alerter worker

AlerterWorker: remove alerter worker

--- workers/alerter/index.coffee
+++ workers/alerter/index.coffee
@@ -1,17 +0,0 @@
-amqp = require 'amqp'
-{argv} = require 'optimist'
-
-if argv.c
-  config = require argv.c
-else
-  console.log "Please provide a valid config file with -c arg. Exiting."
-  process.exit()
-
-connection = amqp.createConnection config.mq
-connection.on 'ready',->
-  connection.exchange 'public-status',{autoDelete:no},(exchange)->
-    exchange.publish 'exit','sharedhosting is dead'
-    exchange.close()
-    connection.end()
-
-


Tweak compiler

Tweak compiler

--- src/gulp/compile.coffee
+++ src/gulp/compile.coffee
@@ -1,35 +1,34 @@
 ###

-# Compile bin folder
+# Compile cookbook

-`gulp compile-bin`
+`gulp compile`

 ---
 ###

-# del = require "del"
 gulp   = require "gulp"
 coffee = require "gulp-coffee"
 plumber = require "gulp-plumber"
 inject = require "gulp-inject-string"
-cson = require "gulp-cson"
+#cson = require "gulp-cson"
 # paths

 #binPath = ["./bin/**/*.*"]
 watchPath = ["./src/**/*.coffee","./src/recipes/**/*"]
-sourcePath = ["./src/bin/**/index.coffee"]
-csonPath = ["./src/bin/**/*.cson", "!./src/bin/**/*.template.cson"]
-recipePath = ["./src/bin/recipes/**/*", "!./src/bin/recipes/**/*.gitkeep"]
+indexPath = ["./src/index.coffee"]
+sourcePath = ["./src/**/*.coffee","!./src/index.coffee"]
+csonPath = ["./src/**/*.cson", "!./src/**/*.template.cson"]
+recipePath = ["./src/recipes/**/*", "!./src/recipes/**/*.gitkeep", "!./src/recipes/*.cson"]
+targetPath = "./"

-
-targetPath = "./"

 module.exports = ()->

-  #del.sync binPath
- # gulp.src(sourcePath).pipe(plumber()).pipe(coffee({bare:true})).pipe(gulp.dest(targetPath))
-  gulp.src("./src/bin/index.coffee").pipe(plumber()).pipe(coffee({bare:true})).pipe(inject.prepend("#!/usr/bin/env node\n")).pipe(gulp.dest(targetPath))
+
+  gulp.src(sourcePath).pipe(plumber()).pipe(coffee({bare:true})).pipe(gulp.dest(targetPath))
+  gulp.src(indexPath).pipe(plumber()).pipe(coffee({bare:true})).pipe(inject.prepend("#!/usr/bin/env node\n")).pipe(gulp.dest(targetPath))
   #gulp.src(csonPath).pipe(cson()).pipe(gulp.dest(targetPath))
   #gulp.src(recipePath).pipe(gulp.dest("#{targetPath}/recipes"))


Fixed output format for else statements

Fixed output format for else statements

--- source/generator.coffee
+++ source/generator.coffee
@@ -6,6 +6,7 @@
   indent = -> i += '  '
   dedent = -> i = i[0..-3]

+  self = this

   @File::js = ->
     i = ''
@@ -45,15 +46,15 @@
     return @op.value;

   @IfStatement::js = ->
-    rv = "if (#{@conditional.js()}) {\n#{@true_block.js()}\n}"
+    rv = "if (#{@conditional.js()}) {\n#{@true_block.js()}\n#{i}}"
     rv += @else_block.js() if @else_block?
     return rv

   @ElseStatement::js = ->
-    if @false_block instanceof @IfStatement
-      return "else #{@false_block.js()}"
+    if @false_block instanceof self.Statement and @false_block.statement instanceof self.IfStatement
+      return " else #{@false_block.js()}"
     else
-      return "else {\n#{@false_block.js()}\n}\n"
+      return " else {\n#{@false_block.js()}\n#{i}}"

   @BlankStatment::js = ->
     return ''


fix(lint): trailing whitespace

fix(lint): trailing whitespace

--- tasks/i18n.coffee
+++ tasks/i18n.coffee
@@ -97,12 +97,12 @@
       ext = '.' + ext
     ext

-  s = (file) ->
+  s = (file) ->
     path.basename(file.src[0]).split('.').shift()

   addLocaleExtensionDest = (file, locale, outputExt) ->
     locale = locale.toLowerCase()
-
+
     if ext = getExtension file.dest
       dest = path.join path.dirname(file.dest), path.basename(file.dest, ext) + ".#{locale}"
     else
@@ -117,7 +117,7 @@

   addLocaleDirnameDest = (file, locale, outputExt) ->
     throw new TypeError 'Missing the template destination path' unless file.dest
-
+
     if ext = getExtension file.dest
       dest = path.join path.dirname(file.dest), locale, path.basename(file.dest, ext) + setExtension ext
     else


removed un-used function call

removed un-used function call

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -108,6 +108,9 @@
     setTimeout shiftWindow, 100

 $ ->
+  breakpoints = new Breakpoints()
+  bootstrapBreakpoint = breakpoints.getBreakpointSize()
+
   $(".nicescroll").niceScroll(cursoropacitymax: '0.4', cursorcolor: '#FFF', cursorborder: "1px solid #FFF")

   # Click a .js-select-on-focus field, select the contents
@@ -258,7 +261,6 @@

   fitSidebarForSize = ->
     oldBootstrapBreakpoint = bootstrapBreakpoint
-    checkBootstrapBreakpoints()
     bootstrapBreakpoint = breakpoints.getBreakpointSize()
     if bootstrapBreakpoint != oldBootstrapBreakpoint
       $(document).trigger('breakpoint:change', [bootstrapBreakpoint])
@@ -274,6 +276,4 @@
       fitSidebarForSize()

   checkInitialSidebarSize()
-  breakpoints = new Breakpoints()
-  bootstrapBreakpoint = breakpoints.getBreakpointSize()
   new Aside()


Changes to discussions js

Changes to discussions js

--- app/assets/javascripts/discussions.js.coffee
+++ app/assets/javascripts/discussions.js.coffee
@@ -22,7 +22,13 @@
         console.log("result: ")
         console.log(result)
         #names = $.parseJSON(result)
-        names = result.name
+        names = Array::slice.call(result)
+        names = $.map(result, (value, key) ->
+          id: key
+          name: value
+          email: value + "@email.com"
+        )
+        console.log(names)
         callback names
     ), "json"


update list of file extensions accepted by the js validations

--- app/assets/javascripts/file_upload.js.coffee
+++ app/assets/javascripts/file_upload.js.coffee
@@ -9,7 +9,9 @@
     dataType: 'script'
     add: (e, data) ->
       console.log "inside second call"
-      types = /(\.|\/)(gif|jpe?g|png|mov|mpeg|mpeg4|avi)$/i
+
+      types = /(\.|\/)(xls|xlt|xla|xlsx|xlsm|xltx|xltm|xlsb|xlam|csv|tsv|docx|doc|dotx|docm|dotm|pptx|ppt|potx|pot|ppsx|pps|pptm|potm|ppsm|ppam|mp3|mp4|jpg|png|gif|pdf|3gpp|txt)$/i
+
       file = data.files[0]
       if types.test(file.type) || types.test(file.name)
         data.context = $(tmpl("template-upload", file))


fixes: Datepicker returns one day earlier than picked in IE (#7048) (#7050)

--- bokehjs/src/coffee/models/widgets/date_picker.coffee
+++ bokehjs/src/coffee/models/widgets/date_picker.coffee
@@ -70,7 +70,10 @@
     return @

   _on_select: (date) =>
-    @model.value = date.toISOString()
+    # Always use toDateString()!
+    # toString() breaks the websocket #4965.
+    # toISOString() returns the wrong day (IE on day earlier) #7048
+    @model.value = date.toDateString()
     @change_input()

 export class DatePicker extends InputWidget


Don't display back button for WizardView with only one step.

--- lib/ST/WizardView.coffee
+++ lib/ST/WizardView.coffee
@@ -7,6 +7,7 @@
     @_stepIndex = -1
     @_data = {}

+  @property 'data'
   @property 'steps'
   @property 'stepIndex'

@@ -27,7 +28,8 @@
   @method 'dialogButtons', (dialog, buttonbar) ->
     @_dialog = dialog
     @_buttonBar = buttonbar
-    @_backButton = buttonbar.button '< Back', @method('lastStep')
+    if @_steps.length > 1
+      @_backButton = buttonbar.button '< Back', @method('lastStep')
     @_nextButton = buttonbar.button 'Next >', @method('nextStep')
     @_cancelButton = buttonbar.button 'Cancel', @method('cancel')
     dialog.cancelFunction @method('cancel')


Reload when clear mission

Reload when clear mission

--- view/coffee/game/clear.coffee
+++ view/coffee/game/clear.coffee
@@ -14,7 +14,7 @@
         location.href = '/game/index.html'
       @getGame()

-  new Vue
+  ranking = new Vue
     el: '#ranking'
     mixins: [formatter, missionParam]
     data:
@@ -31,12 +31,14 @@
       getDistances: ->
         API.getJSON "/api/game/#{@missionId}/ranking/distance", (json) =>
           @distances = json
+      reload: ->
+        @getTimes()
+        @getMoneys()
+        @getDistances()
     ready: ->
       @setMission ->
         location.href = '/game/index.html'
-      @getTimes()
-      @getMoneys()
-      @getDistances()
+      @reload()

   modalId = '#complete'
   new Vue
@@ -49,7 +51,8 @@
           data:
             rate: rate
           success: ->
-            $(modalID).modal('hide')
+            $(modalId).modal('hide')
+            ranking.reload()
     ready: ->
       @setMission ->
         location.href = '/game/index.html'


:memo: optional not option

:memo: optional not option

--- src/menu-manager.coffee
+++ src/menu-manager.coffee
@@ -31,7 +31,8 @@
   # items - An {Array} of menu item {Object}s containing the keys:
   #   :label   - The {String} menu label.
   #   :submenu - An optional {Array} of sub menu items.
-  #   :command - An option {String} command to trigger when the item is clicked.
+  #   :command - An optional {String} command to trigger when the item is
+  #              clicked.
   #
   # Returns nothing.
   add: (items) ->


AccountCredentialListItem: Emit events according to KodingListController style

--- client/account/lib/accountcredentiallistitem.coffee
+++ client/account/lib/accountcredentiallistitem.coffee
@@ -29,12 +29,13 @@
     delegate            = @getDelegate()
     @menu               = {}

-    @addMenuItem 'Show', delegate.lazyBound 'showItemContent', this  if owner
+    if owner
+      @addMenuItem 'Show', => delegate.emit 'ItemAction', { action : 'ShowItem', item : this }

-    # Don't show the edit button for aws credentials in list. Gokmen'll on it.
-    @addMenuItem 'Edit', delegate.lazyBound 'editItem', this  unless provider is 'aws' or @getData().fields?
+    if not provider is 'aws' or @getData().fields?
+      @addMenuItem 'Edit', => delegate.emit 'ItemAction', { action : 'EditItem', item : this }

-    @addMenuItem 'Delete', delegate.lazyBound 'deleteItem', this
+    @addMenuItem 'Delete', => delegate.emit 'ItemAction', { action : 'RemoveItem', item : this }

     return @menu


AddManagedVM: make the install step one liner

--- client/app/lib/providers/managed/viewhelpers.coffee
+++ client/app/lib/providers/managed/viewhelpers.coffee
@@ -1,5 +1,6 @@

 kd    = require 'kd'
+nick  = require 'app/util/nick'

 addTo = (parent, views)->
   map = {}
@@ -13,16 +14,14 @@

   return map

-globals = require 'globals'
+globals    = require 'globals'
 kontrolUrl = if globals.config.environment in ['dev', 'sandbox'] \
              then "\n$ export KONTROLURL=#{globals.config.newkontrol.url}" else ''

 contents  =
   install : """#{kontrolUrl}
-    $ curl -sO https://s3.amazonaws.com/koding-klient/install.sh
-    $ bash ./install.sh
+    $ curl -sSL kodi.ng/s/klient.sh | bash -s %%TOKEN%% %%USERNAME%%
   """
-  # Add %%TOKEN%% to install instructions to enable tokens

 module.exports   = view =
   message        : ({text, cssClass}) -> new kd.View
@@ -48,7 +47,8 @@
   instructions   : (content) ->
     content      = contents[content] ? content
     if view._otatoken
-      content    = content.replace '%%TOKEN%%', view._otatoken
+      content    = content.replace '%%TOKEN%%',    view._otatoken
+      content    = content.replace '%%USERNAME%%', nick()
     container    = new kd.View
     addTo container,
       header     : 'Instructions'


Hide auto complete dropdown when the event is dispatched

--- client/ide/lib/views/chat/idechatmessagepane.coffee
+++ client/ide/lib/views/chat/idechatmessagepane.coffee
@@ -43,6 +43,10 @@

     @once 'NewParticipantButtonClicked', @bound 'removeOnboarding'

+    ideApp = kd.singletons.appManager.get 'IDE'
+    ideApp.on 'UserCouldNotStartVideo', =>
+      @autoComplete?.hideDropdown()
+

   createVideoActionButton: (action) ->


minor

minor

--- client/landing/app/MainApp/login/requestform.coffee
+++ client/landing/app/MainApp/login/requestform.coffee
@@ -33,7 +33,6 @@
         but in the meantime, you can help us out by sharing Koding with your friends.

         Tweet
-
         Follow @koding

         """


Tests: Collaboration: Fixed inviteUser test.

--- client/test/lib/helpers/collaborationhelpers.coffee
+++ client/test/lib/helpers/collaborationhelpers.coffee
@@ -82,18 +82,7 @@

   inviteUser: (browser, username) ->

-    @enterUserNameToInvite(browser, username)
-    browser
-      .element 'css selector', "span.profile[href='/#{username}']", (result) ->
-        if result.status is 0
-          browser.click        "span.profile[href='/#{username}']"
-        else
-          @enterUserNameToInvite(browser, username)
-          browser
-            .click        "span.profile[href='/#{username}']"
-
-
-  enterUserNameToInvite: (browser, username) ->
+    chatSelecor = "span.profile[href='/#{username}']"

     browser
       .waitForElementVisible   '.ParticipantHeads-button--new', 20000
@@ -101,3 +90,13 @@
       .waitForElementVisible   '.kdautocompletewrapper input', 20000
       .setValue                '.kdautocompletewrapper input', username
       .pause                   5000
+      .element                 'css selector', chatSelecor, (result) ->
+        if result.status is 0
+          browser.click        chatSelecor
+        else
+          browser
+            .click             '.ParticipantHeads-button--new'
+            .pause             500
+            .click             '.ParticipantHeads-button--new'
+            .pause             500
+            .click             chatSelecor


Use fs.listSync

Use fs.listSync

--- src/module-cache.coffee
+++ src/module-cache.coffee
@@ -7,28 +7,21 @@
 originalResolveFilename = Module._resolveFilename

 loadDependencies = (modulePath, rootPath, rootMetadata, moduleCache) ->
-  return unless fs.isDirectorySync(modulePath)
+  nodeModulesPath = path.join(modulePath, 'node_modules')
+  for childPath in fs.listSync(nodeModulesPath)
+    continue if path.basename(childPath) is '.bin'
+    continue if rootPath is modulePath and rootMetadata.packageDependencies?.hasOwnProperty(path.basename(childPath))

-  nodeModulesPath = path.join(modulePath, 'node_modules')
-  return unless fs.isDirectorySync(nodeModulesPath)
+    childMetadataPath = path.join(childPath, 'package.json')
+    continue unless fs.isFileSync(childMetadataPath)

-  for child in fs.readdirSync(nodeModulesPath)
-    continue if child is '.bin'
-    continue if rootPath is modulePath and rootMetadata.packageDependencies?.hasOwnProperty(child)
-
-    childPath = path.join(nodeModulesPath, child)
-    childMetadatapath = path.join(nodeModulesPath, child, 'package.json')
-    continue unless fs.isFileSync(childMetadatapath)
-
-    childMetadata = JSON.parse(fs.readFileSync(childMetadatapath))
+    childMetadata = JSON.parse(fs.readFileSync(childMetadataPath))
     if childMetadata?.version
       relativePath = path.relative(rootPath, childPath)
       moduleCache.dependencies[relativePath] = childMetadata.version
       loadDependencies(childPath, rootPath, rootMetadata, moduleCache)

 loadFolderCompatibility = (modulePath, rootPath, rootMetadata, moduleCache) ->
-  return unless fs.isDirectorySync(modulePath)
-
   metadataPath = path.join(modulePath, 'package.json')
   return unless fs.isFileSync(metadataPath)

@@ -51,12 +44,11 @@
   if paths.length > 0 and Object.keys(dependencies).length > 0
     moduleCache.folders.push({paths, dependencies})

-  if fs.isDirectorySync(nodeModulesPath)
-    for child in fs.readdirSync(nodeModulesPath)
-      continue if child is '.bin'
-      continue if rootPath is modulePath and rootMetadata.packageDependencies?.hasOwnProperty(child)
+  for childPath in fs.listSync(nodeModulesPath)
+    continue if path.basename(childPath) is '.bin'
+    continue if rootPath is modulePath and rootMetadata.packageDependencies?.hasOwnProperty(path.basename(childPath))

-      loadFolderCompatibility(path.join(nodeModulesPath, child), rootPath, rootMetadata, moduleCache)
+    loadFolderCompatibility(childPath, rootPath, rootMetadata, moduleCache)

 # Precompute versions of all modules in node_modules
 # Precompute the version each file is compatible


split related channel specific stuff from 'add model to collection' logic

--- core/app/backbone/views/related_channel_view.coffee
+++ core/app/backbone/views/related_channel_view.coffee
@@ -1,17 +1,32 @@
+AddModelToCollectionMixin =
+  addModel: ->
+    console.info('adding', @model.get('title'), 'to collection', @options.addToCollection)
+    if @wrapNewModel
+      model = @wrapNewModel(@model)
+    else
+      model = @model
+    @options.addToCollection.add(model)
+    model.save({},{
+      success: =>
+        if @addModelSuccess
+          @addModelSuccess(model)
+      error: =>
+        @options.addToCollection.remove(model)
+        if addModelError
+          @addModelError(model)
+    })
+
 class window.RelatedChannelView extends Backbone.Marionette.ItemView
   template: "channels/_related_channel"
   tagName: "li"

   events:
-    'click a.btn' : 'addChannel'
+    'click a.btn' : 'addModel'

-  addChannel: ->
-    console.info('adding', @model.get('title'), 'to collection', @options.addToCollection)
-    ch = new Channel(@model.toJSON())
-    @options.addToCollection.add(ch)
-    ch.save({},{
-      success: => @$('a.follow').hide()
-      error: =>
-        @options.addToCollection.remove(ch)
-        alert('something went wrong while adding this channel')
-    })+  addModelSuccess: (model)-> @$('a.follow').hide()
+
+  addModelError: (model)-> alert('something went wrong while adding this channel')
+
+  wrapNewModel: (model) -> new Channel(@model.toJSON())
+
+_.extend(window.RelatedChannelView.prototype, AddModelToCollectionMixin)


PreferencesStore pull current account from FocusedPerspective

--- src/flux/stores/preferences-ui-store.coffee
+++ src/flux/stores/preferences-ui-store.coffee
@@ -1,6 +1,6 @@
 _ = require 'underscore'
 NylasStore = require 'nylas-store'
-AccountStore = require './account-store'
+FocusedPerspectiveStore = require './focused-perspective-store'
 Actions = require '../actions'
 Immutable = require 'immutable'

@@ -13,10 +13,12 @@

 class PreferencesUIStore extends NylasStore
   constructor: ->
+    perspective = FocusedPerspectiveStore.current()
+
     @_tabs = Immutable.List()
     @_selection = Immutable.Map({
       tabId: null
-      accountId: AccountStore.accounts()[0].accountId
+      accountId: perspective?.account?.id
     })

     @_triggerDebounced ?= _.debounce(( => @trigger()), 20)


Let spaces works with Apps

Let spaces works with Apps

--- client/app/MainApp/kodingappscontroller.coffee
+++ client/app/MainApp/kodingappscontroller.coffee
@@ -479,7 +479,7 @@
     unless @constructor.manifests[name]
       @fetchApps (err, apps)=> kallback apps[name]
     else
-      @kiteController.run "stat #{getAppPath @constructor.manifests[name]}", (err)=>
+      @kiteController.run "stat #{escapeFilePath getAppPath @constructor.manifests[name]}", (err)=>
         if err
           new KDNotificationView
             title    : "App list is out-dated, refreshing apps..."


add editable classes

add editable classes

--- apps/channel/client/channel_path_view.coffee
+++ apps/channel/client/channel_path_view.coffee
@@ -78,6 +78,10 @@
       block: @model

   setupEditableViews: ->
+    @$('#metadata--actions').addClass 'is-editable'
+    @$('#metadata__column-manage').addClass 'is-editable'
+    @$('#metadata--collaborators').addClass 'is-editable'
+
     # title
     new EditableAttributeView
       el: @$("#attribute-title_#{@model.id}")


Define mixins in Plot only once

Define mixins in Plot only once

--- bokehjs/src/coffee/models/plots/plot.coffee
+++ bokehjs/src/coffee/models/plots/plot.coffee
@@ -617,8 +617,6 @@
   default_view: PlotView
   type: 'Plot'

-  @mixins ['line:outline_', 'text:title_', 'fill:background_', 'fill:border_']
-
   initialize: (attrs, options) ->
     super(attrs, options)

@@ -730,12 +728,7 @@
       attrs['renderers'] = _.filter(attrs['renderers'], (r) -> r.serializable_in_document())
     attrs

-  mixins: [
-    'text:title_',
-    'line:outline_',
-    'fill:border_',
-    'fill:background_'
-  ]
+  @mixins ['line:outline_', 'text:title_', 'fill:background_', 'fill:border_']

   @define {
       title:             [ p.String,   ''                     ]


ignore errors from our shitty unsupported experimental router

--- client/shared/helpers/exception_handler.coffee
+++ client/shared/helpers/exception_handler.coffee
@@ -21,6 +21,7 @@
       'Script error.'
       'miscellaneous_bindings'
       'canonicalUrl'
+      'TypeError: s.outlets[Object.keys(...)[0]]'
     ]
     ignoreUrls: [
       /graph\.facebook\.com/i


WIP: problem with loading code from cache, leads to error on playing first frame

--- client/src/app/game/undo/undoController.coffee
+++ client/src/app/game/undo/undoController.coffee
@@ -43,10 +43,27 @@
   onUpdateCurrentGame = ->
     if not currentGame.version then return

-    # If this the first time the code is loaded (ie. the controller just started)
-    if not currentLocalVersion
+    handleUpdate = ->
+      # Check that we're not already updated
+      if currentLocalVersion isnt currentGame.localVersion
+        # Indicate unsaved changes only if we already have some code in the undo stack
+        if not undo.isEmpty() then currentGame.setHasUnpublishedChanges()
+
+        # Store the change in the undo stack
+        undo.changeValue(currentGame.localVersion, currentGame.version)
+        currentLocalVersion = currentGame.localVersion
+
+        cache.save(currentGame.info.id, currentGame.version).then ->
+          currentGame.setStatusMessage("Saved offline at #{formatDate()}")
+        .catch (error) ->
+          currentGame.setStatusMessage("Offline saving unavailable")
+          console.error("Error saving offline:", error)
+
+    # Check if this is the first time the code is loaded (ie. the controller just started)
+    if currentLocalVersion
+      handleUpdate()
+    else
       # Check if code exists in offline cache
-
       cache.load(currentGame.info.id).then (cachedCode) ->
         if not cachedCode then return

@@ -67,21 +84,7 @@
       .catch (error) ->
         currentGame.setStatusMessage("Offline saving unavailable")
         console.error("Error saving offline:", error)
-
-    # Check that we're not already updated
-    if currentLocalVersion isnt currentGame.localVersion
-      # Indicate unsaved changes only if we already have some code in the undo stack
-      if not undo.isEmpty() then currentGame.setHasUnpublishedChanges()
-
-      # Store the change in the undo stack
-      undo.changeValue(currentGame.localVersion, currentGame.version)
-      currentLocalVersion = currentGame.localVersion
-
-      cache.save(currentGame.info.id, currentGame.version).then ->
-        currentGame.setStatusMessage("Saved offline at #{formatDate()}")
-      .catch (error) ->
-        currentGame.setStatusMessage("Offline saving unavailable")
-        console.error("Error saving offline:", error)
+      .then(handleUpdate)

   $scope.currentGame = currentGame
   $scope.$watch("currentGame.localVersion", onUpdateCurrentGame, true)


:bug: Prevent registering a decoration if it couldn't be created

Fixes #12

--- lib/minimap-find-and-replace-binding.coffee
+++ lib/minimap-find-and-replace-binding.coffee
@@ -50,6 +50,8 @@
       type: 'highlight'
       scope: ".minimap .search-result"
     })
+    return unless decoration?
+
     @decorationsByMarkerId[marker.id] = decoration
     @subscriptionsByMarkerId[marker.id] = decoration.onDidDestroy =>
       @subscriptionsByMarkerId[marker.id].dispose()


Remove default argument value

Remove default argument value

--- extension/packages/commands.coffee
+++ extension/packages/commands.coffee
@@ -368,7 +368,7 @@
   new Command('misc',   'Esc',                    command_Esc,                    ['Esc'])
 ]

-searchForMatchingCommand = (keys = []) ->
+searchForMatchingCommand = (keys) ->
   for index in [0...keys.length] by 1
     str = keys[index..].join(',')
     for command in commands


Remove editor class, fixes autosave error

--- lib/git-log-class.coffee
+++ lib/git-log-class.coffee
@@ -4,7 +4,7 @@

 class GitLogView extends ScrollView
     @content: ->
-        @div class: 'git-log  editor editor-colors native-key-bindings', tabindex: -1, =>
+        @div class: 'git-log native-key-bindings', tabindex: -1, =>
             @div class: 'panels', =>
                 @subview 'graph', new ColumnView('Graph', 'graph')
                 @subview 'comments', new ColumnView('Description', 'comments')


Fix import XRegExp

Fix import XRegExp

--- lib/linter-pylama.coffee
+++ lib/linter-pylama.coffee
@@ -3,7 +3,7 @@
 temp = require 'temp'
 {BufferedProcess} = require 'atom'
 {CompositeDisposable} = require 'atom'
-XRegExp = require('xregexp').XRegExp
+XRegExp = require 'xregexp'

 regex = XRegExp '(?<file>.+):' +
   '(?<line>\\d+):' +


 Merge branch 'master.activity.groupchannel' into 'master.activity'

Master.Activity.Groupchannel

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -3,6 +3,33 @@
   constructor: (options = {}, data) ->
     @openedChannels = {}
     super options, data
+    @utils.defer @bound 'init'
+
+  init:->
+    mainController    = KD.getSingleton 'mainController'
+    mainController.ready => @openGroupChannel()
+
+  openGroupChannel:->
+    # to - do refactor this part to use same functions with other parts
+    api = this
+    groupsController = KD.getSingleton "groupsController"
+    groupsController.ready =>
+      group = KD.getSingleton("groupsController").getCurrentGroup()
+
+      subscriptionData =
+        serviceType: 'socialapi'
+        group      : group.slug
+        channelType: "group"
+        channelName: group.slug
+        isExclusive: yes
+
+      name = "socialapi.#{group.slug}-group-#{group.slug}"
+      KD.remote.subscribe name, subscriptionData, (brokerChannel)=>
+        @forwardMessageEvents brokerChannel, api, [
+          "MessageAdded",
+          "MessageRemoved"
+        ]
+

   mapActivity = (data) ->

@@ -130,6 +157,8 @@
       source.on event, (message, rest...) ->
         message = mapActivity message
         target.emit event, message, rest...
+
+  forwardMessageEvents : forwardMessageEvents

   registerAndOpenChannels = (socialApiChannels)->
     getCurrentGroup (group)->


Social: add  bySlug function

Social: add  bySlug function

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -278,16 +278,21 @@

     return switch type
       when 'topic'                     then @channel.byName {name: id}, kallback
+      when 'activity'                  then @message.bySlug {slug: id}, kallback
       when 'channel', 'privatemessage' then @channel.byId {id}, kallback
       when 'post', 'message'           then @message.byId {id}, kallback
       else callback { message: 'not implemented in revive' }

-
   message:
     byId                 : messageRequesterFn
       fnName             : 'byId'
       validateOptionsWith: ['id']
+      mapperFn           : mapActivity
+
+    bySlug               : messageRequesterFn
+      fnName             : 'bySlug'
+      validateOptionsWith: ['slug']
       mapperFn           : mapActivity

     edit                 : messageRequesterFn


account: renamed function name and variables

--- client/account/lib/routehandler.coffee
+++ client/account/lib/routehandler.coffee
@@ -1,9 +1,9 @@
 kd                 = require 'kd'
 lazyrouter         = require 'app/lazyrouter'
-issoloproductlite  = require 'app/util/issoloproductlite'
+isSoloProductLite  = require 'app/util/issoloproductlite'
 KDNotificationView = kd.NotificationView

-routeHandle = [
+hiddenSectionsForSolo = [
   'Referral'
   'Billing'
 ]
@@ -22,9 +22,9 @@

 module.exports = -> lazyrouter.bind 'account', (type, info, state, path, ctx) ->

-  if info?.params?.section in routeHandle
-    if issoloproductlite()
-      kd.singletons.router.handleRoute '/Account/Profile'
+  if info?.params?.section in hiddenSectionsForSolo
+    if isSoloProductLite()
+      return kd.singletons.router.handleRoute '/Account/Profile'

   switch type
     when 'profile'


Newkite should be agree with the new changes

--- client/app/MainApp/kite/newkite.coffee
+++ client/app/MainApp/kite/newkite.coffee
@@ -12,7 +12,7 @@

   getKiteAddr:()->
     requestData =
-      name       : "#{KD.nick()}"
+      username   : "#{KD.nick()}"
       remoteKite : @kiteName
       token      : KD.remote.getSessionToken()

@@ -24,7 +24,9 @@
        if response.status is 200
          data = JSON.parse data

-         console.log "Remote Kite is #{data[0].name}"
+         console.log "DATA", data
+
+         console.log "Remote Kite belongs to: #{data[0].username}, type: #{data[0].kitename}"
          console.log "Addr to be connected is #{data[0].addr}"
          console.log "Token to use is #{data[0].token}"


Logs: Use native OS newlines.

Logs: Use native OS newlines.

--- client/app/js/views/battle_view.coffee
+++ client/app/js/views/battle_view.coffee
@@ -392,10 +392,10 @@
       log.push ""  if isHeader
       log.push $this.text()
       log.push ""  if isHeader
-    log = log.join('\n')
+    log = [ log.join('\n') ]
     fileName = (team.owner  for team in @model.teams).join(" vs ")
     fileName += ".txt"
-    blob = new Blob([ log ], type: "text/plain;charset=utf-8")
+    blob = new Blob(log, type: "text/plain;charset=utf-8", endings: "native")
     saveAs(blob, fileName)

   $pokemon: (player, slot) =>


[typo] Remove remaining console.debug

[typo] Remove remaining console.debug

--- client/app/views/steps/accounts.coffee
+++ client/app/views/steps/accounts.coffee
@@ -33,6 +33,5 @@

     renderError: (error) ->
-        console.debug 'renderError', error
         @$errorContainer.html(t(error))
         @$errorContainer.show()


Fixed: new task form didn't empty when adding a task without the tags of the current selection

--- client/app/views/task_form_view.coffee
+++ client/app/views/task_form_view.coffee
@@ -57,6 +57,7 @@
     onSubmit: ->
         inputVal = @$('input').val()
         @trigger 'new-task-submitted', content: inputVal if inputVal.length > 0
+        @$('input').val ''

     getRenderData: ->
         formPlaceholder: @getFormPlaceholder()


Allow local requests and attach X-Otter header to requests

This is the start of Otter being able to be used as a proxy to other servers.

--- lib/otter/browser.coffee
+++ lib/otter/browser.coffee
@@ -6,9 +6,16 @@

 module.exports = class OtterBrowser extends Browser
   constructor: (options = {}) ->
+    # Attach a magic header to all requests from otter
+    options.headers ?= {}
+    options.headers["X-Otter"] ?= "true"
+
     super

     @allowedHosts = options.allowedHosts ? []
+    # Always allow local requests
+    if @site
+      @allowedHosts.push(URL.parse(@site).host)
     @path = options.path

     @on 'opened', (window) =>


Remove unused FormBindings

Remove unused FormBindings

--- client/lanes/react/FormBindings.coffee
+++ client/lanes/react/FormBindings.coffee
@@ -1,103 +0,0 @@
-class Lanes.Views.FormBindings
-
-    constructor: (@view, config={})->
-        _.bindAll(this,'onFieldChange')
-        @selector = if config.selector then "#{config.selector} " else ""
-        @view.on( "change:$el",    this.rebindForm,    this )
-        @view.on( "change:model",  this.rebindModel,   this )
-        @view.on( "remove",        this.teardown,      this )
-        this.rebindModel( @view, @view.model )
-
-    onFieldChange: (ev)->
-        el = Lanes.$(ev.target)
-        name = el.attr('name')
-        return unless @view.model?.hasAttribute(name)
-        val = el.val()
-        unless false == this.view.trigger("update:#{val}", val)
-            el[0].binding_is_setting=true
-            @view.model.setFromView(name, val)
-            delete el[0].binding_is_setting
-        msg = @view.model.checkValid(name,val)
-        this.setError( el, msg )
-        return true
-
-    onAttributeUpdate: (model, opts={})->
-        attributes = if opts.all then this.attributesFor(model) else model.changedAttributes()
-        if attributes['errors'] && ! _.isEmpty(attributes['errors'])
-            for name, message of attributes['errors']
-                this.setError(Lanes.$(el),message) if el = this.getElement(name)
-        for name, _val of attributes
-            el = this.getElement(name)
-            this.setElValue(Lanes.$(el),model.get(name)) if el && ! el.binding_is_setting
-        return true
-
-    setElValue:  (el, value)->
-        if el.attr('type')
-            switch el.attr('type')
-                when 'radio'
-                    selector="#{@selector}input[type=radio][name=#{el.attr('name')}]"
-                    for input in Lanes.$(this.view.el).find(selector)
-                        input.checked = input.value == value
-                when 'checkbox'
-                     el.prop('checked', !!value)
-                when 'break'
-                    file
-                else
-                    el.val(value)
-        else if el.is('input') || el.is('select') || el.is('textarea')
-            el.val(value ||  ( if value == 0 then '0' else '') )
-        else
-            el.text(value || ( if value == 0 then '0' else '') )
-
-    clearErrors: ->
-        Lanes.$(this.view.el).find(
-            this.selector + "input ~ .feedback.error, " +
-            this.selector + ".ro-input.update ~ .feedback.error"
-        ).removeClass('error')
-
-    setError: (el, message)->
-        return unless placeholder = el.siblings('.feedback')
-        if message
-            placeholder.addClass('error')
-            placeholder.attr('data-tooltip-message', message)
-        else
-            placeholder.removeClass('error')
-
-    attributesFor: (model)->
-        attrs = {}
-        for name,val of model._definition
-            attrs[name]=val #if ! val.session
-        attrs
-
-    fields: ->
-        @cached_fields ||= @view.el?.querySelectorAll("#{@selector}input, #{@selector}.ro-input.update")
-
-    getElement: (name)->
-        return unless fields = this.fields()
-        for field in fields
-            return field if field.getAttribute('name') == name
-        null
-
-    setAllFields: ->
-        this.onAttributeUpdate(@view.model, all: true)
-
-    rebindForm: (view, el)->
-        input_selector = "#{@selector}input, #{@selector}select"
-        if old_el = @view.changedAttributes()['el']
-            Lanes.$(el).off("change", input_selector, this.onFieldChange )
-        @cached_fields = null
-        if @view.model
-            this.setAllFields()
-        @view.$el.on("change", input_selector, this.onFieldChange)
-
-    rebindModel: (view, model)->
-        this.clearErrors()
-        if old_model = @view.changedAttributes()['model']
-            old_model.off( "change", this.onAttributeUpdate )
-        if model
-            this.onAttributeUpdate(model, { all: true } )
-            @cached_model = null
-            model.on('change', this.onAttributeUpdate, this )
-
-    teardown: ->
-        @view.model?.off( "change", this.onAttributeUpdate )


Social: add activity id

Social: add activity id

--- client/Social/Activity/activitylistcontroller.coffee
+++ client/Social/Activity/activitylistcontroller.coffee
@@ -108,7 +108,7 @@
           likeView._currentState = yes

   addItem:(activity, index, animation) ->
-    dataId = activity.getId?() or activity._id
+    dataId = activity.getId?() or activity._id or activity.id
     if dataId?
       if @itemsIndexed[dataId]
         log "duplicate entry", activity.bongo_?.constructorName, dataId