OK remove it

OK remove it

--- lib/settings.coffee
+++ lib/settings.coffee
@@ -67,12 +67,6 @@
     default: 'smartcase'
     enum: ['smartcase', 'sensitive', 'insensitive']
     description: "Case sensitivity of your query in narrow-editor"
-  # vmpAutoChangeModeInUI:
-  #   default: true
-  #   description: "Automatically change mode on narrow-editor, insert-mode(=prompt), normal-mode(=item-area)"
-  # vmpStartInInsertModeForUI:
-  #   default: true
-  #   description: "Start in insert-mode on initial open"

   # Per providers settings
   # -------------------------


Fix syntax error

Fix syntax error

--- Learn-server/handleImage.coffee
+++ Learn-server/handleImage.coffee
@@ -9,8 +9,8 @@
 module.exports = (file, callback) ->
   return callback("No `image` file. Make sure the `image` field is being set.") unless file

-  path = path.normalize "#{global.image_path}#{createHashID()}_#{file.name}"
-  fs.rename file.path, path, (error) ->
+  p = path.normalize "#{global.image_path}#{createHashID()}_#{file.name}"
+  fs.rename file.path, p, (error) ->
     return callback(error) if error
     callback null, path


fix coffee script bug

fix coffee script bug

Signed-off-by: Sakchai Siripanyawuth <50d2926f6b388f0d7169db5beff2dc87e1537d54@artellectual.com>

--- vendor/assets/javascripts/simple_datatables.js.coffee
+++ vendor/assets/javascripts/simple_datatables.js.coffee
@@ -75,5 +75,4 @@
       "_sw"
     data.push({name: "search["+searchcolumns.join("_or_")+op+"]", value: sSearch});

-  $.ajax( { "dataType": 'json', "type": "GET", "url": sSource, "data": data, "success": fnCallback } );
-  +  $.ajax( { "dataType": 'json', "type": "GET", "url": sSource, "data": data, "success": fnCallback } );


Fix feed pagination

Fix feed pagination

--- lib/assets/javascripts/views/posts_feed.js.coffee
+++ lib/assets/javascripts/views/posts_feed.js.coffee
@@ -66,7 +66,7 @@
       complete: @hideLoading
     ) is false

-  fetchSuccess: (posts, xhr, params, options) =>
+  fetchSuccess: (posts, res, xhr, params, options) =>
     if options.append
       @appendRender(posts)
     else


index a square cropped thumbnail image in search

--- api/apps/articles/model/save.coffee
+++ api/apps/articles/model/save.coffee
@@ -172,7 +172,7 @@
       featured: article.featured
       tags: article.tags
       body: sections and stripHtmlTags(sections.join(' ')) or ''
-      image_url: article.thumbnail_image
+      image_url: crop(article.thumbnail_image, 70, 70)
     , (error, response) ->
       console.log('ElasticsearchIndexingError: Article ' + article.id + ' : ' + error) if error
   )


fix ko status fake stone display

fix ko status fake stone display

--- app/assets/javascripts/board.coffee
+++ app/assets/javascripts/board.coffee
@@ -521,7 +521,7 @@
   name = e.target.id.split('-')[2]
   dot = player.board.find_stone_by_name name
   # TODO: solve ko status fake stone
-  if dot.owner isnt 'e'
+  if dot.owner isnt 'e' or window.player.board.ko_dot == dot.name
     return

   player.board.draw_fake_stone dot


fix: correct the select_menu URL, must use the full link as for edit case, URL: http://[server]/meals/8/edit so it makes the correct URL for select_menu: http://[server]/meals/8/select_menu, but not for the new case, URL: http://[server]/meals/new then the link for select_menu is http://[server]/select_menu which is not right

--- app/assets/javascripts/meals.coffee
+++ app/assets/javascripts/meals.coffee
@@ -1,8 +1,9 @@
 $ ->
   @selectMenu = (source) ->
+    mealId = $(source).data('meal')
     menuId = $(source).data('menu')
     $.ajax
-      url: 'select_menu'
+      url: '/meals/' + mealId + '/select_menu'
       type: 'PUT'
       data:
         menu_id: menuId


Show correct error message.

Show correct error message.

--- app/assets/javascripts/nodes.coffee
+++ app/assets/javascripts/nodes.coffee
@@ -223,7 +223,7 @@

     error: (file, message) ->
       $previewElement = $(file.previewElement);
-      $errorElement = addFlashMessage('error', '' + file.name + ': ' + message)
+      $errorElement = addFlashMessage('error', '' + file.name + ': ' + message.error)

       if $.support.transition
         $errorElement.on $.support.transition.end, ()->


Fix reply post referencing

Fix reply post referencing

--- app/assets/javascripts/reply.coffee
+++ app/assets/javascripts/reply.coffee
@@ -9,38 +9,40 @@
     e.preventDefault()

     topicId = $(e.delegateTarget).data('id')
-    postId = $(e.target).parents('.post').data('id')
+    pos = $(e.target).parents('.post').data('pos')

     replyContainer = $('#reply-container')

     if replyContainer.length
       textArea = replyContainer.find('textarea')
       if currentTopicId == topicId
-        if ! firstPostFromTopic(topicId, postId)
-          insertPostId(textArea, postId)
-      else
-        if textArea.value == '' || confirm('You are about to switch thread which would cause currently unsaved reply to be cleared. Continue?')
-          # clear current form
-          replyContainer.remove()
-          replyContainer = [] # empty
+        if pos == 1
+          textArea.focus()
+        else
+          insertRef(textArea, pos)
+      else if textArea.val() == '' || confirm('You are about to switch thread which would cause currently unsaved reply to be cleared. Continue?')
+        # clear current form
+        replyContainer.remove()
+        replyContainer = [] # empty

     # load form if none exists
     if ! replyContainer.length
-      loadReplyForm(prepareUrl(e.target.getAttribute('href')), topicId, postId)
+      loadReplyForm(prepareUrl(e.target.getAttribute('href')), topicId, pos)

-insertPostId = (textarea, postId)->
+# quote reference to post in textarea
+insertRef = (textarea, ref)->
   t = textarea.val()
   if t.length > 0
     t = t + "\n"
-  t = t + "> #{postId}\n"
+  t = t + "> #{ref}\n"
   textarea.val(t).focus()
   textarea[0].scrollTop = textarea[0].scrollHeight

-loadReplyForm = (url, topicId, postId)->
+loadReplyForm = (url, topicId, pos)->
   $.get(url, topic_id:topicId).done (html)->
     $('body>footer').before("
" + html + "
")
-    if ! firstPostFromTopic(topicId, postId)
-      insertPostId($('#reply-container').find('textarea'), postId)
+    if pos != 1
+      insertRef($('#reply-container').find('textarea'), pos)
     currentTopicId = topicId

     new ImageUpload($('#reply-container .file-section'))
@@ -48,11 +50,6 @@
 prepareUrl = (path)->
   path.replace(/topics.*/, 'posts/new')

-firstPostFromTopic = (topicId, postId)->
-  topicEl = $(".topic[data-id=#{topicId}]")
-  firstPost = topicEl.find(".post:first-of-type")
-  firstPost.data('id') == postId
-
 PostHandle.register ($postEl)->
   return if $postEl.index() != 1
   url = $postEl.find('.actions .reply').attr('href')


Fixed bad indentation level in fitalyzer.js.coffee.

--- app/javascripts/fitalyzer.js.coffee
+++ app/javascripts/fitalyzer.js.coffee
@@ -80,14 +80,14 @@
     if $scope.set is $scope.sets.local
       $scope.reloadLocalFits()

-      $scope.fits = $firebase(ref).$child('fits').$child($scope.set.$id)
-
-      $scope.fits.$on 'loaded', ->
-        log "$scope.fits loaded from firebase for #{$scope.set.name}", 'debug'
-        initial_fit_id = if window.fit_id then window.fit_id else $scope.fits.$getIndex()[0]
-        window.fit_id = null
-        $scope.fits.$child(initial_fit_id).$on 'loaded', ->
-          $scope.fit = $scope.fits[initial_fit_id]
+    $scope.fits = $firebase(ref).$child('fits').$child($scope.set.$id)
+
+    $scope.fits.$on 'loaded', ->
+      log "$scope.fits loaded from firebase for #{$scope.set.name}", 'debug'
+      initial_fit_id = if window.fit_id then window.fit_id else $scope.fits.$getIndex()[0]
+      window.fit_id = null
+      $scope.fits.$child(initial_fit_id).$on 'loaded', ->
+        $scope.fit = $scope.fits[initial_fit_id]

   $scope.$watch 'fit', ->
     if not $scope.fit then return


Fix big damages on boss through mine

Fix big damages on boss through mine

--- app/scripts/components/Enemy.coffee
+++ app/scripts/components/Enemy.coffee
@@ -33,9 +33,11 @@
         return if @invincible
         for c in e
           splosion = c.obj
-          @trigger('Hit', entity: this, projectile: splosion)
-          @attr hitFlash: { _red: 255, _green: 255, _blue: 128 }
-          @absorbDamage(splosion.damage)
+          if splosion.damage > 0
+            @trigger('Hit', entity: this, projectile: splosion)
+            @attr hitFlash: { _red: 255, _green: 255, _blue: 128 }
+            @absorbDamage(splosion.damage)
+            splosion.damage = 0
       ->
         @attr hitFlash: no
     this


CHG: format value

CHG: format value

--- frontend/javascripts/shared/fields/date_range_picker.coffee
+++ frontend/javascripts/shared/fields/date_range_picker.coffee
@@ -6,6 +6,10 @@
 class DateRangePickerElement extends Component

   @tag 'tao-date-range-picker'
+
+  @attribute 'prefix'
+  @attribute 'suffix'
+  @attribute 'separator', default: ','

   @attribute 'dateValueFormat', default: 'YYYY-MM-DD'

@@ -35,7 +39,7 @@
       else
         @startDatePicker.disableAfter datePicker.moment
       @_syncMoment()
-      @namespacedTrigger('change', [@value]) if @value
+      @namespacedTrigger('change', [@value])

     startDatePickerDeferred = $.Deferred()
     endDatePickerDeferred = $.Deferred()
@@ -50,33 +54,47 @@
       @_initMoment()
       @namespacedTrigger 'ready'

+  _parseValue: ->
+    prefix = if @prefix then "\\#{@prefix}" else ''
+    suffix = if @suffix then "\\#{@suffix}" else ''
+    separator = if @separator then "\\#{@separator}" else ''
+
+    regexp = new RegExp "^#{prefix}(.*)#{separator}(.*)#{suffix}$"
+    if matches = @field.val().match(regexp)
+      [matches[1], matches[2]]
+    else
+      []
+
   _initMoment: ->
-    dates = @field.val().split(',')
+    dates = @_parseValue()
+    @startDate = moment _.trim(dates[0]), @dateValueFormat
+    @endDate = moment _.trim(dates[1]), @dateValueFormat

-    if dates.length > 0
-      @startDate = moment _.trim(dates[0]), @dateValueFormat
-      @endDate = moment _.trim(dates[1]), @dateValueFormat
+    if @startDate.isValid()
       @startDatePicker.setMoment @startDate.clone()
-      @endDatePicker.setMoment @endDate.clone()
-      @startDatePicker.disableAfter @endDate.clone()
       @endDatePicker.disableBefore @startDate.clone()
     else
       @startDatePicker.setMoment ''
+      @startDate = null
+
+    if @endDate.isValid()
+      @endDatePicker.setMoment @endDate.clone()
+      @startDatePicker.disableAfter @endDate.clone()
+    else
       @endDatePicker.setMoment ''
-      @startDate = null
       @endDate = null

   _syncMoment: ->
-    if @startDatePicker.moment && @endDatePicker.moment
-      startDateValue = @startDatePicker.moment.format(@dateValueFormat)
-      endDateValue = @endDatePicker.moment.format(@dateValueFormat)
+    startDateValue = @startDatePicker.moment?.format(@dateValueFormat)
+    endDateValue = @endDatePicker.moment?.format(@dateValueFormat)

-      @field.val "#{startDateValue},#{endDateValue}"
-      @startDate = @startDatePicker.moment.clone()
-      @endDate = @endDatePicker.moment.clone()
+    if startDateValue || endDateValue
+      @field.val ["#{@prefix}#{startDateValue || ''}", "#{endDateValue || ''}#{@suffix}"].join(@separator)
     else
       @field.val ''
-      @startDate = null
-      @endDate = null
+
+    @startDate = @startDatePicker.moment?.clone()
+    @endDate = @endDatePicker.moment?.clone()
+

 export default DateRangePickerElement.register()


Added components for competition checks/readiness.

--- picoCTF-web/web/coffee/management-competition-checks.coffee
+++ picoCTF-web/web/coffee/management-competition-checks.coffee
@@ -7,6 +7,80 @@
 Col = ReactBootstrap.Col
 Badge = ReactBootstrap.Badge

+update = React.addons.update
+
+TestGroupItem = React.createClass
+  render: ->
+    glyphName = "asterik"
+    glyphStyle = ""
+
+    switch @props.status
+      when "waiting"
+        glyphName = "refresh"
+        glyphStyle = "spin"
+      when "failing"
+        glyphName = "remove"
+      when "passing"
+        glyphName = "ok"
+
+    <ListGroupItem>{@props.name} <Glyphicon glyph={glyphName} className={glyphStyle}/></ListGroupItem>
+
+TestGroup = React.createClass
+  getInitialState: ->
+    state = {}
+    _.map @props.tests, (test) ->
+      state[test.name] = test
+      state[test.name].status = "waiting"
+    state
+
+  updateTestState: (testName, status) ->
+    updateObject = {}
+    updateObject[testName] =
+      status: $set: status
+    newState = update @state, updateObject
+
+    totalStatus = "passing"
+    if _.any(newState, (test) -> test.status == "waiting")
+      totalStatus = "waiting"
+    else if _.any(newState, (test) -> test.status == "failing")
+      totalStatus = "failing"
+
+    @setState newState
+    @props.onStatusChange totalStatus
+
+  componentWillMount: ->
+    #Initiate all the tests with the updateTestState callback
+    _.each @state, ((test, testName) ->
+      test.func (@updateTestState.bind null, testName)
+    ).bind this
+
+  render: ->
+    <ListGroup fill>
+      {_.map @state, (test, i) ->
+        <TestGroupItem key={i} {...test}/>}
+    </ListGroup>
+
 CompetitionCheck = React.createClass
-    render: ->
-

+
+  getInitialState: ->
+    competitionReadiness: "waiting"
+
+  alwaysTrue: (t, setStatus) ->
+    setTimeout (setStatus.bind null, t), (Math.random() * 3000)
+
+  onStatusChange: (status) ->
+    @setState update @state,
+      competitionReadiness: $set: status
+
+  render: ->
+    sanityChecks = [
+      {name: "Just Checking 1", func: @alwaysTrue.bind null, "passing"}
+      {name: "Just Checking 2", func: @alwaysTrue.bind null, "passing"}
+      {name: "Just Checking 3", func: @alwaysTrue.bind null, "passing"}
+
+    ]
+
+

+
Competition Status {@state.competitionReadiness}

+      <TestGroup tests={sanityChecks} onStatusChange={@onStatusChange}/>
+


adding check for existing properties defined on Number.prototype

--- src/number_extensions.coffee
+++ src/number_extensions.coffee
@@ -415,12 +415,15 @@
 @propertyOf Number#
 @returns {Number} This number as a duration of seconds
 ###
-Object.defineProperty Number::, 'seconds',
-  get: ->
-    @ * 1000
-Object.defineProperty Number::, 'second',
-  get: ->
-    @ * 1000
+unless 5.seconds
+  Object.defineProperty Number::, 'seconds',
+    get: ->
+      this * 1000
+
+unless 1.second
+  Object.defineProperty Number::, 'second',
+    get: ->
+      this * 1000

 ###*
 Utility method to convert a number to an amount of rotations.
@@ -436,12 +439,15 @@
 @propertyOf Number#
 @returns {Number} This number as an amount of rotations
 ###
-Object.defineProperty Number::, 'rotations',
-  get: ->
-    @ * Math.TAU
-Object.defineProperty Number::, 'rotation',
-  get: ->
-    @ * Math.TAU
+unless 5.rotations
+  Object.defineProperty Number::, 'rotations',
+    get: ->
+      this * Math.TAU
+
+unless 1.rotation
+  Object.defineProperty Number::, 'rotation',
+    get: ->
+      this * Math.TAU

 ###*
 Utility method to convert a number to an amount of degrees.
@@ -457,12 +463,15 @@
 @propertyOf Number#
 @returns {Number} This number as an amount of degrees
 ###
-Object.defineProperty Number::, 'degrees',
-  get: ->
-    @ * Math.TAU / 360
-Object.defineProperty Number::, 'degree',
-  get: ->
-    @ * Math.TAU / 360
+unless 2.degrees
+  Object.defineProperty Number::, 'degrees',
+    get: ->
+      this * Math.TAU / 360
+
+unless 1.degree
+  Object.defineProperty Number::, 'degree',
+    get: ->
+      this * Math.TAU / 360

 ###*
 The mathematical circle constant of 1 turn.


fix test long strings on windows

fix test long strings on windows

We expected `'Some long\nstring'` but got `'Some long\r\nstring'` on
windows because of different end-of-line markers.

--- test/parse.coffee
+++ test/parse.coffee
@@ -1,6 +1,7 @@

 CSON = require '../'
 assert = require 'assertive'
+os = require 'os'

 compilesTo = (source, expected) ->
   assert.deepEqual expected, CSON.parse(source)
@@ -64,7 +65,7 @@
       string
       \"""
       """
-      'Some long\nstring'
+      "Some long#{os.EOL}string"
     )

   it 'does not allow using assignments', ->


hotfix

hotfix

--- test/setup.coffee
+++ test/setup.coffee
@@ -1,8 +1,8 @@
+assert = chai.assert
+
 mp3 = '/base/doc/mp3/rain.mp3'
 aac = '/base/doc/mp3/coins.mp4a'
 empty_mp3 = '/base/doc/mp3/empty.mp3'
-
-assert = chai.assert

 p = new _mu.Player({
     mute: true


render_svg has defaults

render_svg has defaults

--- src/render/render_svg.coffee
+++ src/render/render_svg.coffee
@@ -1,8 +1,8 @@
 transform = (world) ->
   center = world.get('scale') / 2
-  translate = "translate(#{world.get('x')},#{world.get('y')})"
-  rotate = "rotate(#{world.get('angle')} #{center} #{center})"
-  "#{translate} #{rotate}"
+  translate = "translate(#{world.get('x') || 0},#{world.get('y') || 0})"
+  rotate = world.get('angle') && center? && "rotate(#{world.get('angle')} #{center} #{center})"
+  "#{translate} #{rotate || ''}"

 exports.render_svg = (world) ->
   SVG = world.SVG()


compensate for when schedule is returned as an Array instead of a String

--- src/scripts/pagerduty.coffee
+++ src/scripts/pagerduty.coffee
@@ -739,12 +739,15 @@
     oneHour = moment().add(1, 'hours').format()
     now = moment().format()

+    scheduleId = schedule.id
+    if (schedule instanceof Array)
+      scheduleId = schedule[0].id
     query = {
       since: now,
       until: oneHour,
       overflow: 'true'
     }
-    pagerduty.get "/schedules/#{schedule.id}/entries", query, (err, json) ->
+    pagerduty.get "/schedules/#{scheduleId}/entries", query, (err, json) ->
       if err?
         robot.emit 'error', err, msg
         return


Add a test about data emission with newline

--- test/write.coffee
+++ test/write.coffee
@@ -53,4 +53,13 @@
     # buff = new Buffer [0xE2, 0x82, 0xAC]
     # parser.write buff
     parser.end()
-
+
+  it 'instantly emits data once a newline is retrieved', (next) ->
+    data = []
+    parser = parse()
+    parser.write 'A,B,C\n'
+    parser.on 'data', (data) ->
+      data.should.eql ['A', 'B', 'C']
+      parser.end()
+    parser.on 'finish', ->
+      next()


disable new project creation upgrades and pay in place as they are somewhat broken: https://github.com/sagemathinc/smc/issues/1047

--- src/smc-webapp/projects.cjsx
+++ src/smc-webapp/projects.cjsx
@@ -1014,6 +1014,7 @@
         $('html, body').animate({ scrollTop: $('#upgrade_before_creation').offset().top }, 0)

     render_upgrade_before_create : (subs) ->
+        return
         <Col sm=12>

Upgrade to give your project internet access and more resources

@@ -1036,7 +1037,7 @@

     render_no_title_alert : ->
         if @state.title_text == '' and @state.state != 'saving'
-            <Alert bsStyle='danger'>No project title specified. Please enter title at the top.</Alert>
+            <Alert bsStyle='danger' style={marginTop:'15px'}>No project title specified. Please enter title at the top.</Alert>

     render_create_with_upgrades_button : (create_btn_disabled) ->
         <ButtonToolbar>
@@ -1099,7 +1100,7 @@
                             disabled = {@state.title_text == '' or @state.state == 'saving'}
                             bsStyle  = 'success'
                             onClick  = {=>@create_project(false)} >
-                            Create project without upgrades
+                            Create project{#without upgrades}
                         </Button>
                         <Button
                             disabled = {@state.state is 'saving'}
@@ -1118,7 +1119,7 @@
                 <Col sm=12 style={color:'#555'}>

                         A project is your own private computational workspace that you can
-                        share with others and upgrade.
+                        share with others and upgrade. {#upgrade.}

                 </Col>
             </Row>
@@ -1127,8 +1128,8 @@
             </Row>
             <Row>
                 <Col sm=12>
-                    {@render_no_title_alert() if subs > 0}
-                    {@render_create_with_upgrades_button(create_btn_disabled) if subs > 0}
+                    {@render_no_title_alert()}
+                    {#@render_create_with_upgrades_button(create_btn_disabled) if subs > 0}
                     {@render_error()}
                 </Col>
             </Row>


hopefully address issue with chat blanking traceback

--- src/smc-webapp/smc_chat.cjsx
+++ src/smc-webapp/smc_chat.cjsx
@@ -536,7 +536,8 @@
     save_scroll_position: ->
         @props.actions.set_use_saved_position(true)
         node = ReactDOM.findDOMNode(@refs.log_container)
-        @props.actions.save_scroll_state(node.scrollTop, node.scrollHeight, node.offsetHeight)
+        if node?
+            @props.actions.save_scroll_state(node.scrollTop, node.scrollHeight, node.offsetHeight)

     button_send_chat: (e) ->
         send_chat(e, @refs.log_container, ReactDOM.findDOMNode(@refs.input).value, @props.actions)


adding support to RTL in simple line!

adding support to RTL in simple line!

adding this line making the program full compatible with RTL requirements!
this fix all problems in ?!//.. etc

--- src/ui/views/messages.coffee
+++ src/ui/views/messages.coffee
@@ -246,7 +246,8 @@
     mclz = ['message']
     mclz.push c for c in MESSAGE_CLASSES when e[c]?
     title = if e.timestamp then moment(e.timestamp / 1000).calendar() else null
-    div id:e.event_id, key:e.event_id, class:mclz.join(' '), title:title, ->
+    div id:e.event_id, key:e.event_id, class:mclz.join(' '), title:title,
+        dir: 'auto' ->
         if e.chat_message
             content = e.chat_message?.message_content
             format content


Replaced path seperator with path.sep

Replaced path seperator with path.sep

--- tests/unit.coffee
+++ tests/unit.coffee
@@ -98,7 +98,9 @@
       it("file path should be replaced with .gulp-scss-cache/source.css",
           (done) ->
             func_promise.then(
-              -> expect(file.path).equal "/tests/data/source.css"
+              -> expect(file.path).equal(
+                path.join path.sep + "tests", "data", "source.css"
+              )
             ).done (-> done()), done
         )

@@ -128,7 +130,9 @@
         ).done (-> done()), done

       it "Path should have css extension", ->
-        expect(file.path).is.equal "/tests/data/source.css"
+        expect(file.path).is.equal(
+          path.join path.sep + "tests", "data", "source.css"
+        )

     describe "When path is specified", ->
       func_promise = undefined
@@ -194,7 +198,9 @@
       ).done (-> done()), done

     it "Path should have css extension", ->
-      expect(file.path).is.equal "/tests/data/source.css"
+      expect(file.path).is.equal(
+        path.join path.sep + "tests", "data", "source.css"
+      )

 describe "For non-testing mode", ->


Update welcome message, now uses #presentaciones channel

--- scripts/welcome-to-zihub.coffee
+++ scripts/welcome-to-zihub.coffee
@@ -18,7 +18,7 @@
     msg = """
 Hola {nick}, soy Robotico...un bot, un amigo, un sirviente

-Te recominedo que antes de nada revises tu perfil y te pongas un avatar (nos gusta ver la cara a la gente para conocernos cuando coincidamos en algún sarao), luego puedes presentarte en el canal #general para que sepan que has llegado.
+Te recominedo que antes de nada revises tu perfil y te pongas un avatar (nos gusta ver la cara a la gente para conocernos cuando coincidamos en algún sarao), luego puedes presentarte en el canal #presentaciones para que sepan que has llegado.

 Por aquí usamos la regla de los dos pies, así que te recomiendo que eches un vistazo a los canales y te unas a los de tu interés. En el momento que veas que no te aportan nada o que tu no puedes aportar nada puedes (debes) abandonar el canal y aquí no ha pasado nada.

@@ -51,4 +51,5 @@
         return
       add_nicks user.name
       robot.messageRoom user.name, welcomeMsg(user.name)
-      welcomeGif(user, res.channel)
+      robot.messageRoom '#presentaciones', "Hola #{user.name}"
+      welcomeGif(user, '#presentaciones')


fix in epmc use

fix in epmc use

--- server/api/use/europepmc.coffee
+++ server/api/use/europepmc.coffee
@@ -178,7 +178,7 @@

     if pmcid and not noui and API.service?.lantern?.licence?
       url = 'https://europepmc.org/articles/PMC' + pmcid.toLowerCase().replace('pmc','')
-      pg = API.job.limit (API.settings.use?.europepmc?.limit ? 3000), 'API.http.phantom', [url,1000,refresh], "EPMCUI", limitcache
+      pg = API.job.limit (API.settings.use?.europepmc?.limit ? 3000), 'API.http.phantom', [url,1000,refresh], "EPMCUI"
       if typeof pg is 'string'
         licsplash = API.service.lantern.licence url, false, pg
         if licsplash.licence?


Remove test code.

Remove test code.

--- server/code/lib/throttle.coffee
+++ server/code/lib/throttle.coffee
@@ -1,7 +1,7 @@
 _ = require 'underscore'

 exports.throttle = (getIdentifier, timeout = 1000) ->
-  cache = {tom: 'cool'}
+  cache = {}
   throttleInner = (req, resp, next) ->
     identifier = getIdentifier(arguments)


Allow access to treasure-game without a course for easy tournament play

--- server/middleware/levels.coffee
+++ server/middleware/levels.coffee
@@ -84,8 +84,9 @@
     if code
       attrs.code = code

-    if req.user.isAdmin()
-      null  # Admins can play any level, even if it's not in a course yet (example: level editor)
+    if not req.user.isAnonymous() and level.get('slug') in ['treasure-game']
+      console.log "Allowing session creation for #{level.get('slug')} outside of any course"
+      attrs.isForClassroom = true
     else if level.get('type') in ['course', 'course-ladder'] or req.query.course?

       # Find the course and classroom that has assigned this level, verify access
@@ -155,7 +156,7 @@
       if requiresSubscription and not canPlayAnyway
         throw new errors.PaymentRequired('This level requires a subscription to play')

-    attrs.isForClassroom = course?
+    attrs.isForClassroom ?= course?
     session = new LevelSession(attrs)
     if classroom # Potentially set intercom trigger flag on teacher
       teacher = yield User.findOne({ _id: classroom.get('ownerID') })


watch field

watch field

--- src/coffee/directives/field.coffee
+++ src/coffee/directives/field.coffee
@@ -9,18 +9,20 @@
 angular.module('CoolFormDirectives').
   directive('coolformField', ->

-    l = (scope, elem, attr) ->
-      scope.data =
-        error:
-          ok: true
-          msg: null
-
+    l = (scope) ->
+      scope.error = false
+      scope.service.watchFieldValidation(scope.field.name,
+        () -> scope.error = false,
+        (e) -> scope.error = e)
+
+
     return {
       restrict: 'E'
       scope:
         field: '='
+        service: '='
       template: templates.field
       link: l
     }

-  )+  )


実はこうなのでは？

実はこうなのでは？

--- scripts/line-beacon-listener.coffee
+++ scripts/line-beacon-listener.coffee
@@ -1,8 +1,6 @@
 module.exports = (robot) ->

   robot.router.post "/line-becon", (req, res) ->
-    message = JSON.parse req.body.payload
-    message.token = process.env.HUBOT_SLACK_TOKEN;
-    robot.logger.error "＊＊＊＊＊＊＊＊＊＊＊＊＊＊#{message}"
+    robot.logger.error "＊＊＊＊＊＊＊＊＊＊＊＊＊＊#{req.type}"
     robot.send {room: "C4LEAQHPW"}, "ビーコンに反応あり"
     res.end ""


Changing Stack Overflow Poller to 180 minutes

--- scripts/stackoverflow-poller.coffee
+++ scripts/stackoverflow-poller.coffee
@@ -8,7 +8,7 @@
 Poller = require '../support/poller'

 PROD_MODE        = process.env.NODE_ENV is 'production'
-POLL_INTERVAL    = 1000 * 60 * 74
+POLL_INTERVAL    = 1000 * 60 * 180
 ANNOUNCE_ROOMS   = []

 do =>


Style

Style

--- server/packages/config/index.coffee
+++ server/packages/config/index.coffee
@@ -58,6 +58,4 @@

 	]

-exports.$replContext = (context) ->
-
-	context.config = nconf
+exports.$replContext = (context) -> context.config = nconf


KDView: If containment provided, keep the widget with-in given boundries.

--- client/Framework/core/KDView.coffee
+++ client/Framework/core/KDView.coffee
@@ -687,10 +687,18 @@
       @dragIsAllowed = yes
       @setEmptyDragState()

-      dragState             = @dragState
+      dragState = @dragState
+
+      if options.containment
+        dragState.containment = {}
+        {view} = options.containment
+        if 'string' is typeof view
+          dragState.containment.viewBounds = @[view].getBounds()
+        dragState.containment.viewBounds or= @parent.getBounds()
+        dragState.containment.padding = \
+          options.containment.padding or x: 0, y: 0

       # TODO: should move these lines
-      dragState.containment = options.containment
       dragState.handle      = options.handle
       dragState.axis        = options.axis

@@ -713,7 +721,7 @@

   drag:(event, delta)->

-    {directionX, directionY, axis} = @dragState
+    {directionX, directionY, axis, containment} = @dragState

     {x, y}       = delta
     dragPos      = @dragState.position
@@ -755,6 +763,15 @@

       newX = if targetPosX is 'left' then dragMeta.left + dragRelPos.x else dragMeta.right  - dragRelPos.x
       newY = if targetPosY is 'top'  then dragMeta.top  + dragRelPos.y else dragMeta.bottom - dragRelPos.y
+
+      if containment
+        m  = w: @getWidth(), h: @getHeight()
+        p  = containment.viewBounds
+        cp = containment.padding
+        if newX <= 0 + cp.x then newX = 0 + cp.x
+        if newY <= 0 + cp.y then newY = 0 + cp.y
+        if newX + m.w + cp.x >= p.w - cp.x then newX = p.w - m.w - cp.x
+        if newY + m.h + cp.y >= p.h - cp.y then newY = p.h - m.h - cp.y

       el.css targetPosX, newX unless axis is 'y'
       el.css targetPosY, newY unless axis is 'x'


app: exec order typo

app: exec order typo

--- client/app/lib/appcontroller.coffee
+++ client/app/lib/appcontroller.coffee
@@ -12,15 +12,11 @@

     super

+    { name, version } = @getOptions()
     @canonicalName = _.uniqueId name.toLowerCase() + '$' + version.toLowerCase()

-    { mainController } = kd.singletons
-    { name, version } = @getOptions()
-
-
-    mainController.ready =>
-      # defer should be removed
-      # this should be listening to a different event - SY
+    kd.singleton('mainController').ready =>
+      # defer should be removed, this should be listening to a different event - SY
       kd.utils.defer  =>
         { appStorageController } = kd.singletons
         @appStorage = appStorageController.storage name, version or "1.0.1"


Home: Actions: fix cyclomatic_complexity

--- client/home/lib/flux/actions.coffee
+++ client/home/lib/flux/actions.coffee
@@ -37,6 +37,18 @@
     markAsDone 'installKd'

+checkIntegrationSteps = ->
+  return  unless hasIntegration 'gitlab'
+  whoami().fetchOAuthInfo (err, oauth) ->
+    markAsDone 'gitlabIntegration'  if oauth?.gitlab?
+
+
+checkCredentialSteps = ->
+  remote.api.JCredential.some {}, { limit: 1 }, (err, res) ->
+    return  if err
+    markAsDone 'enterCredentials'  if res?.length
+
+
 checkFinishedSteps = ->

   { appStorageController, groupsController
@@ -62,14 +74,9 @@
   unobserve = reactor.observe EnvironmentGetters.stacks, (_stacks) ->
     checkStacksForBuild _stacks, unobserve

-  remote.api.JCredential.some {}, { limit: 1 }, (err, res) ->
-    return  if err
-    markAsDone 'enterCredentials'  if res?.length
+  checkCredentialSteps()

-  if hasIntegration 'gitlab'
-    whoami().fetchOAuthInfo (err, oauth) ->
-      markAsDone 'gitlabIntegration'  if oauth?.gitlab?
-
+  checkIntegrationSteps()

   if reactor.evaluate(HomeGetters.welcomeSteps).getIn [ 'common', 'installKd', 'isDone' ]
     if reactor.evaluate HomeGetters.areStepsFinished


Remove spec for addPane. Oops.

Remove spec for addPane. Oops.

--- spec/atom/root-view-spec.coffee
+++ spec/atom/root-view-spec.coffee
@@ -252,12 +252,6 @@

         expect(rootView.find('.editor').outerWidth()).toBe rootView.width()

-  describe ".addPane(view)", ->
-    it "adds the given view to the rootView (at the bottom by default)", ->
-      expect(rootView.children().length).toBe 1
-      rootView.addPane $('<div id="foo">')
-      expect(rootView.children().length).toBe 2
-
   describe "the file finder", ->
     describe "when the toggle-file-finder event is triggered", ->
       describe "when there is a project", ->


Add spec for plus escaping

Add spec for plus escaping

--- spec/scope-selector-spec.coffee
+++ spec/scope-selector-spec.coffee
@@ -82,3 +82,4 @@
       expect(new ScopeSelector('a | b').toCssSelector()).toBe '.a, .b'
       expect(new ScopeSelector('a - (b.c d)').toCssSelector()).toBe '.a:not(.b.c .d)'
       expect(new ScopeSelector('a, b').toCssSelector()).toBe '.a, .b'
+      expect(new ScopeSelector('c++').toCssSelector()).toBe '.c\\+\\+'


:fire: Remove use of buffer in specs

:fire: Remove use of buffer in specs

--- spec/tabs-to-spaces-spec.coffee
+++ spec/tabs-to-spaces-spec.coffee
@@ -7,7 +7,7 @@
 helper = require './spec-helper'

 describe 'Tabs to Spaces', ->
-  [buffer, directory, editor, filePath, workspaceElement] = []
+  [directory, editor, filePath, workspaceElement] = []

   beforeEach ->
     directory = temp.mkdirSync()
@@ -18,10 +18,8 @@
     atom.config.set('editor.tabLength', 4)

     waitsForPromise ->
-      atom.workspace.open(filePath).then (e) -> editor = e
-
-    runs ->
-      buffer = editor.getBuffer()
+      atom.workspace.open(filePath).then (e) ->
+        editor = e

     waitsForPromise ->
       atom.packages.activatePackage('tabs-to-spaces')
@@ -53,42 +51,42 @@
       expect(editor.getText()).toBe ''

     it 'does not change spaces at the end of a line', ->
-      buffer.setText('foobarbaz     ')
+      editor.setText('foobarbaz     ')
       TabsToSpaces.tabify(editor)
       expect(editor.getText()).toBe 'foobarbaz     '

     it 'does not change spaces in the middle of a line', ->
-      buffer.setText('foo  bar  baz')
+      editor.setText('foo  bar  baz')
       TabsToSpaces.tabify(editor)
       expect(editor.getText()).toBe 'foo  bar  baz'

     it 'converts one tab worth of spaces to a tab', ->
       editor.setTabLength(2)
-      buffer.setText('  foo')
+      editor.setText('  foo')
       TabsToSpaces.tabify(editor)
       expect(editor.getText()).toBe '\tfoo'

     it 'converts almost two tabs worth of spaces to one tab and some spaces', ->
       editor.setTabLength(4)
-      buffer.setText('       foo')
+      editor.setText('       foo')
       TabsToSpaces.tabify(editor)
       expect(editor.getText()).toBe '\t   foo'

     it 'changes multiple lines of leading spaces to tabs', ->
       editor.setTabLength(4)
-      buffer.setText('    foo\n       bar')
+      editor.setText('    foo\n       bar')
       TabsToSpaces.tabify(editor)
       expect(editor.getText()).toBe '\tfoo\n\t   bar'

     it 'leaves successive newlines alone', ->
       editor.setTabLength(2)
-      buffer.setText('  foo\n\n  bar\n\n  baz\n\n')
+      editor.setText('  foo\n\n  bar\n\n  baz\n\n')
       TabsToSpaces.tabify(editor)
       expect(editor.getText()).toBe '\tfoo\n\n\tbar\n\n\tbaz\n\n'

     it 'changes mixed spaces and tabs to uniform whitespace', ->
       editor.setTabLength(2)
-      buffer.setText('\t \tfoo\n')
+      editor.setText('\t \tfoo\n')
       TabsToSpaces.tabify(editor)
       expect(editor.getText()).toBe '\t\t foo\n'

@@ -101,36 +99,36 @@
       expect(editor.getText()).toBe ''

     it 'does not change tabs at the end of a string', ->
-      buffer.setText('foobarbaz\t')
+      editor.setText('foobarbaz\t')
       TabsToSpaces.untabify(editor)
       expect(editor.getText()).toBe 'foobarbaz\t'

     it 'does not change tabs in the middle of a string', ->
-      buffer.setText('foo\tbar\tbaz')
+      editor.setText('foo\tbar\tbaz')
       TabsToSpaces.untabify(editor)
       expect(editor.getText()).toBe 'foo\tbar\tbaz'

     it 'changes one tab to the correct number of spaces', ->
       editor.setTabLength(2)
-      buffer.setText('\tfoo')
+      editor.setText('\tfoo')
       TabsToSpaces.untabify(editor)
       expect(editor.getText()).toBe '  foo'

     it 'changes two tabs to the correct number of spaces', ->
       editor.setTabLength(2)
-      buffer.setText('\t\tfoo')
+      editor.setText('\t\tfoo')
       TabsToSpaces.untabify(editor)
       expect(editor.getText()).toBe '    foo'

     it 'changes multiple lines of leading tabs to spaces', ->
       editor.setTabLength(2)
-      buffer.setText('\t\tfoo\n\t\tbar\n\n')
+      editor.setText('\t\tfoo\n\t\tbar\n\n')
       TabsToSpaces.untabify(editor)
       expect(editor.getText()).toBe '    foo\n    bar\n\n'

     it 'changes mixed spaces and tabs to uniform whitespace', ->
       editor.setTabLength(2)
-      buffer.setText(' \t foo\n')
+      editor.setText(' \t foo\n')
       TabsToSpaces.untabify(editor)
       expect(editor.getText()).toBe '    foo\n'

@@ -143,36 +141,36 @@
       expect(editor.getText()).toBe ''

     it 'does change tabs at the end of a string', ->
-      buffer.setText('foobarbaz\t')
+      editor.setText('foobarbaz\t')
       TabsToSpaces.untabifyAll(editor)
       expect(editor.getText()).toBe 'foobarbaz   '

     it 'does change tabs in the middle of a string', ->
-      buffer.setText('foo\tbar\tbaz')
+      editor.setText('foo\tbar\tbaz')
       TabsToSpaces.untabifyAll(editor)
       expect(editor.getText()).toBe 'foo   bar   baz'

     it 'changes one tab to the correct number of spaces', ->
       editor.setTabLength(2)
-      buffer.setText('\tfoo')
+      editor.setText('\tfoo')
       TabsToSpaces.untabifyAll(editor)
       expect(editor.getText()).toBe '  foo'

     it 'changes two tabs to the correct number of spaces', ->
       editor.setTabLength(2)
-      buffer.setText('\t\tfoo')
+      editor.setText('\t\tfoo')
       TabsToSpaces.untabifyAll(editor)
       expect(editor.getText()).toBe '    foo'

     it 'changes multiple lines of leading tabs to spaces', ->
       editor.setTabLength(2)
-      buffer.setText('\t\tfoo\n\t\tbar\n\n')
+      editor.setText('\t\tfoo\n\t\tbar\n\n')
       TabsToSpaces.untabifyAll(editor)
       expect(editor.getText()).toBe '    foo\n    bar\n\n'

     it 'changes mixed spaces and tabs to uniform whitespace', ->
       editor.setTabLength(2)
-      buffer.setText(' \t foo\n')
+      editor.setText(' \t foo\n')
       TabsToSpaces.untabifyAll(editor)
       expect(editor.getText()).toBe '    foo\n'

@@ -182,13 +180,13 @@

     it 'will untabify before an editor saves a buffer', ->
       atom.config.set('tabs-to-spaces.onSave', 'untabify')
-      buffer.setText('\t\tfoo\n\t\tbar\n\n')
+      editor.setText('\t\tfoo\n\t\tbar\n\n')
       editor.save()
       expect(editor.getText()).toBe '        foo\n        bar\n\n'

     it 'will tabify before an editor saves a buffer', ->
       atom.config.set('tabs-to-spaces.onSave', 'tabify')
-      buffer.setText('        foo\n        bar\n\n')
+      editor.setText('        foo\n        bar\n\n')
       editor.save()
       expect(editor.getText()).toBe '\t\tfoo\n\t\tbar\n\n'

@@ -206,7 +204,7 @@
           buffer = editor.getBuffer()

       it 'respects the overridden configuration', ->
-        buffer.setText('    foo\n    bar\n\n')
+        editor.setText('    foo\n    bar\n\n')
         editor.save()
         expect(editor.getText()).toBe '\t\tfoo\n\t\tbar\n\n'

@@ -214,6 +212,6 @@
         spyOn(atom.config, 'getUserConfigPath').andReturn(filePath)
         spyOn(editor, 'getPath').andReturn(filePath)

-        buffer.setText('    foo\n    bar\n\n')
+        editor.setText('    foo\n    bar\n\n')
         editor.save()
         expect(editor.getText()).toBe '    foo\n    bar\n\n'


add ga to pagination

add ga to pagination

--- coffee/controllers/issueCtrl.coffee
+++ coffee/controllers/issueCtrl.coffee
@@ -1,4 +1,4 @@
-timeTracker.controller 'IssueCtrl', ($scope, $window, Redmine, Ticket, Message, State, Resource) ->
+timeTracker.controller 'IssueCtrl', ($scope, $window, Redmine, Ticket, Message, State, Resource, Analytics) ->

   SHOW = { DEFAULT: 0, NOT: 1, SHOW: 2 }
   STATUS_CANCEL = 0
@@ -99,6 +99,7 @@
    on currentPage change, load issue on the project.
   ###
   $scope.$watch 'currentPage', ->
+    Analytics.sendEvent 'user', 'clicked', 'pagination'
     loadIssues($scope.currentPage)


Remove binding of showHideFallback to collection, causes trouble. The visibilityChange handler should be called anyway on addAtomic or reset.

--- coffee/views/collection_view.coffee
+++ coffee/views/collection_view.coffee
@@ -47,7 +47,7 @@

     initialize: (options = {}) ->
       super
-      console.debug 'CollectionView#initialize', @, @collection, options
+      #console.debug 'CollectionView#initialize', @, @collection, options

       # Default options
       _(options).defaults
@@ -136,6 +136,8 @@
     initFallback: ->
       return unless @fallbackSelector

+      #console.debug 'CollectionView#initFallback', @, @el, @el.parentNode
+
       # Set the $fallback property
       @$fallback = @$(@fallbackSelector)

@@ -148,14 +150,10 @@
       # Listen for visible items changes
       @bind 'visibilityChange', @showHideFallback

-      # Register a done handler on the collection
-      # TODO: How to dispose this reference?
-      @collection.done @showHideFallback
-
     # Show or hide the fallback when the visible items change

     showHideFallback: =>
-      console.debug 'CollectionView#showHideFallback', @, @visibleItems, @collection
+      #console.debug 'CollectionView#showHideFallback', @, 'visibleItems', @visibleItems, 'collection', @collection
       # Show fallback message if no item is visible and
       # the collection Deferred has been resolved
       empty = @visibleItems.length is 0 and @collection.state() is 'resolved'
@@ -349,11 +347,6 @@
     removeView: (cid, view) ->
       #console.debug 'CollectionView#removeView', cid, view

-      # TODO: How can that happen?
-      unless view
-        #console.error 'CollectionView#removeView called without a view', @, cid, view
-        return
-
       # Dispose the view
       view.dispose()


fixing tests.

fixing tests.

--- test/get-last-tweetTest.coffee
+++ test/get-last-tweetTest.coffee
@@ -4,9 +4,16 @@

 app = require('./../app/server').app

-describe 'GET /joshfinnie/last', ->
+describe 'GET /joshfinnie/status/last?withReplies=true', ->
     it 'returns get-last-tweet page', (done) ->
         request(app)
-            .get('/joshfinnie/last')
+            .get('/joshfinnie/status/last?withReplies=true')
             .expect("Content-Type", "text/html; charset=utf-8")
             .expect 200, done
+
+describe 'GET /joshfinnie/status/last?withReplies=false', ->
+    it 'returns get-last-tweet page', (done) ->
+        request(app)
+            .get('/joshfinnie/status/last?withReplies=false')
+            .expect("Content-Type", "text/html; charset=utf-8")
+            .expect 200, done


Fix tests

Fix tests

--- test/hubot-nextbus_test.coffee
+++ test/hubot-nextbus_test.coffee
@@ -16,7 +16,7 @@
     expect(@robot.respond).to.have.been.calledWith(/(?:bus|nextbus)(?: me)?$/i)

   it 'registers a stop respond listener', ->
-    expect(@robot.respond).to.have.been.calledWith(/(?:bus|nextbus) stop ([A-Z0-9]+)$/i)
+    expect(@robot.respond).to.have.been.calledWith(/(?:bus|nextbus) stop ([A-Z0-9_]+)$/i)

   it 'registers a stops respond listener', ->
     expect(@robot.respond).to.have.been.calledWith(/(?:bus|nextbus) stops$/i)


Bump timeout for User model as well

Bump timeout for User model as well

--- test/server/models/user.coffee
+++ test/server/models/user.coffee
@@ -5,6 +5,8 @@
 {assert} = require 'chai'

 describe 'Model#User', ->
+  @timeout 3000
+
   bucket = null
   user = null


Added an option to the runner for listing the loaded classes after running.

--- console/runner.coffee
+++ console/runner.coffee
@@ -83,7 +83,7 @@

 if require.main == module
   optimist = require('optimist')
-    .boolean(['count-logs','h'])
+    .boolean(['count-logs','h','list-class-cache'])
     .alias({h: 'help'})
     .describe({
       classpath: 'JVM classpath, "path1:...:pathn"',
@@ -94,6 +94,7 @@
       jar: 'add JAR to classpath and run its Main-Class (if found)',
       'count-logs': 'count log messages instead of printing them',
       'skip-logs': 'number of log messages to skip before printing',
+      'list-class-cache': 'list all of the loaded classes after execution',
       h: 'Show this usage'})
     .usage 'Usage: $0 /path/to/classfile [flags]'
   argv = optimist.argv
@@ -152,3 +153,7 @@
     stub console, 'log', (-> if --count == 0 then console.log = old_fn), run
   else
     run()
+
+  if argv['list-class-cache']
+    for k in Object.keys rs.loaded_classes
+      console.log k


Bugfix

Bugfix

--- cs_app/sabnzbd.coffee
+++ cs_app/sabnzbd.coffee
@@ -239,7 +239,7 @@
       if settings.noPostProcess
         p = '/api?mode=addurl&name=' + url + '&nzbname=' + nzbname + '&cat=' + username + '&pp=3&apikey=' + settings.sabApiKey
       else
-        p = '/api?mode=addurl&name=' + url + '&nzbname=' + nzbname + '&pp=3&script=sabre_postprocess.py&apikey=' + settings.sabApiKey
+        p = '/api?mode=addurl&name=' + url + '&nzbname=' + nzbname + '&pp=1&script=sabre_postprocess.py&apikey=' + settings.sabApiKey

       sabReq = http.request {host: settings.sabHost, port: settings.sabPort, path: encodeURI(p)}, (response) ->
         str = ''


Add spec for resizing window larger than screen.

--- spec/api-browser-window-spec.coffee
+++ spec/api-browser-window-spec.coffee
@@ -79,6 +79,18 @@
       assert.equal after[0], size[0]
       assert.equal after[1], size[1]

+    it 'can set the window larger than screen', ->
+      # No way to reliably set size when window has not been shown on Linux.
+      return if process.platform is 'linux'
+
+      size = require('screen').getPrimaryDisplay().size
+      size.width += 100
+      size.height += 100
+      w.setSize size.width, size.height
+      after = w.getSize()
+      assert.equal after[0], size.width
+      assert.equal after[1], size.height
+
   describe 'BrowserWindow.setContentSize(width, height)', ->
     it 'sets the content size', ->
       # No way to reliably set size when window has not been shown on Linux.


Fix test name in feedback_spec

Fix test name in feedback_spec

--- spec/unpoly/feedback_spec.js.coffee
+++ spec/unpoly/feedback_spec.js.coffee
@@ -11,7 +11,7 @@
   describe 'unobtrusive behavior', ->

     describe '[up-nav]', ->
-
+
       replaceURL = (url) ->
         # Don't use up.history.replace() since that fires up:location:changed
         # which up.feedback listens to.
@@ -182,7 +182,7 @@
         up.hello(container)
         expect(link).toHaveClass('up-current')

-      it 'marks a link as .up-current if it links to a #hash and the current URL only has a matching path without a #hash (bugfixxx)', ->
+      it 'marks a link as .up-current if it links to a #hash and the current URL only has a matching path without a #hash (bugfix)', ->
         replaceURL('/bar')
         container = fixture('.container')
         nav = e.affix(container, 'div[up-nav]')


Dobule check editor has path before resolving path

Fixes #48

--- lib/providers/coffee-provider.coffee
+++ lib/providers/coffee-provider.coffee
@@ -2,10 +2,13 @@
 resolve        = require 'resolve'
 path           = require 'path'

-scopedRequire = (basedir, modName) ->
+# Check if there is a local coffee-script package, use that package if it exists
+scopedRequire = (filepath) ->
+  basedir = path.dirname(filepath)
+
   rst = null
-  try rst = path.dirname(resolve.sync(modName, { basedir }))
-  if rst then require "#{rst}" else require modName
+  try rst = path.dirname(resolve.sync('coffee-script', { basedir }))
+  if rst then require rst else require 'coffee-script'

 module.exports =
   id: 'coffee-compile'
@@ -20,7 +23,9 @@
     return code

   compile: (code, editor) ->
-    coffee = scopedRequire path.dirname(editor.getPath()), 'coffee-script'
+    filepath = editor.getPath()
+
+    coffee = if filepath then scopedRequire(filepath) else require('coffee-script')
     literate = editor.getGrammar().scopeName is "source.litcoffee"

     bare  = configManager.get('noTopLevelFunctionWrapper')


use url.resolve to adding https:// part

use url.resolve to adding https:// part

--- services/web/app/coffee/infrastructure/ExpressLocals.coffee
+++ services/web/app/coffee/infrastructure/ExpressLocals.coffee
@@ -7,10 +7,10 @@
 SystemMessageManager = require("../Features/SystemMessages/SystemMessageManager")
 _ = require("underscore")
 Modules = require "./Modules"
+url = require "url"

 fingerprints = {}
 Path = require 'path'
-

 jsPath =
@@ -71,14 +71,17 @@
 		res.locals.buildJsPath = (jsFile, fingerprint)->
 			if !fingerprint?
 				fingerprint = getFingerprint(jsPath + jsFile)
-			p = Path.join(staticFilesBase, jsPath, jsFile) + "?fingerprint=" + fingerprint
+			p = Path.join(jsPath, jsFile)
+			return url.resolve(staticFilesBase, p) + "?fingerprint=" + fingerprint

 		res.locals.buildCssPath = (cssFile)->
-			return Path.join(staticFilesBase, cssPath, cssFile) + "?fingerprint=" + getFingerprint(cssPath + cssFile)
+			p = Path.join(cssPath, cssFile)
+			return url.resolve(staticFilesBase, p) + "?fingerprint=" + getFingerprint(cssPath + cssFile)

 		res.locals.buildImgPath = (imgFile)->
-			return Path.join(staticFilesBase, imgPath, imgFile)
+			p = Path.join(imgPath, imgFile)
+			return url.resolve(staticFilesBase, p)

 		next()


test(provider): fix provider suffix test

Needed due to changed default Provider suffix

--- test/ng-classify.spec.coffee
+++ test/ng-classify.spec.coffee
@@ -379,7 +379,7 @@
 					@name = name

 		angular.module('app')
-		.provider('greetingsProvider', ['$log', Greetings])
+		.provider('greetings', ['$log', Greetings])
 		'''

 		expect(result).toEqual(expectation)


Remove debug logging.

Remove debug logging.

--- src/chaplin/views/layout.coffee
+++ src/chaplin/views/layout.coffee
@@ -222,7 +222,6 @@
         if $
           $(region.instance.container).find region.selector
         else
-          console.log 123, region.instance.container
           region.instance.container.querySelector region.selector
       else
         region.instance[if $ then '$' else 'find'] region.selector


Update Gruntfile.coffee

Update Gruntfile.coffee

--- docs/Gruntfile.coffee
+++ docs/Gruntfile.coffee
@@ -72,7 +72,7 @@
                         'js/**'
                         'resources/**'
                     ]
-                    dest: 'dist/'
+                    dest: '../docs/'
                 },{
                     expand: true
                     src: ['index.html']


fix selectors

fix selectors

--- extension/chrome/js/src/content.coffee
+++ extension/chrome/js/src/content.coffee
@@ -39,14 +39,24 @@

 			if navs.length > 0
 				navs.append @composeButton MailButler.TYPE_NAV
+			else
+				log 'Could not find compose button bar'
 			return

 		injectThread: ->
-			threads = ($ '.iH > div').filter (index) ->
+			sels = [
+				'.iH > div,[gh="mtb"] > div'
+				"[gh='tm'] > div:first-child > div"
+				'.aeH > div > div:first-child > div'
+			]
+
+			threads = ($ sels.join ',').filter (index) ->
 				($ ".#{MailButler.MB_CLASS_THREAD}", @).length is 0

 			if threads.length > 0
 				threads.append @composeButton MailButler.TYPE_THREAD
+			else
+				log 'Could not find thread button bar'
 			return

 		copyAttrs: (attrs, source, target) ->
@@ -561,6 +571,8 @@
 				inbox:		!!props.inbox
 				#callback:	'alert'

+
+
 			#jQuery.getJSON @url, data, @onScheduleSuccess
 			log 'scheduling mail...', data

@@ -617,13 +629,15 @@
 			return

 		onMessage: (request, sender, sendResponse) =>
-			# log request
+			log request
 			switch request.type
 				when "fragment"
 					@injectButtons()
 				when "loaded"
 					@injectButtons()
-			return
+			return

 	mb = new MailButler
-) jQuery, window+	return
+
+) jQuery, window if top.document is document


No point in listening to the collection directly

--- SingularityUI/app/components/aggregateTail/InterleavedTail.cjsx
+++ SingularityUI/app/components/aggregateTail/InterleavedTail.cjsx
@@ -30,7 +30,7 @@

     Backbone.React.Component.mixin.on(@, {
       models: models
-      collections: collections
+      # collections: collections
     });

   componentDidMount: ->
@@ -150,7 +150,7 @@
     map = {}
     taskIds = _.uniq(logLines.map((line) =>
       line.taskId
-    ))
+    )).sort()
     if taskIds.length is 1
       map[taskIds[0]] = 'hsla(0, 0, 0, 0)'
     else


Implement "it compiles fine" step.

Implement "it compiles fine" step.

--- features/step_definitions/steps.coffee
+++ features/step_definitions/steps.coffee
@@ -1,4 +1,5 @@
 Finitio     = require '../../lib/finitio'
+System      = require '../../lib/system'
 should      = require 'should'

 # Global variables for steps below
@@ -24,6 +25,13 @@

   @Given /^the type under test is (.*?)$/, (typeName, callback) ->
     type = system.fetch(typeName)
+    callback()
+
+  # Language
+
+  @Then /^it compiles fine$/, (callback) ->
+    unless system instanceof System
+      callback.fail new Error("#{result} is not an finitio system")
     callback()

   # Dressing


Fixed a typo in capitalize

Fixed a typo in capitalize

--- pystr.coffee
+++ pystr.coffee
@@ -21,7 +21,7 @@
 String::endswith = (s) -> return false if s.charAt(s.length-i) isnt @charAt(@length-i) for i in [s.length...0]; return true

 # Capitalize the first character in the string.
-String::capitalize = -> l = @lower(); (l.charAt(c) or '').upper()+(l.charAt(c) for c in [1...l.length]).join('')
+String::capitalize = -> l = @lower(); (l.charAt(0) or '').upper()+(l.charAt(c) for c in [1...l.length]).join('')

 # Capitalize every word in the string.
 String::title = -> (word.capitalize() for word in @split(' ')).join(' ')


NaN fix for average row

NaN fix for average row

--- src/components/performance.cjsx
+++ src/components/performance.cjsx
@@ -18,7 +18,10 @@
     {heading.title}

   renderAverageCell: (heading) ->
-    classAverage = Math.round(heading.class_average)
+    if heading.class_average
+      classAverage = Math.round(heading.class_average)
+    else
+      classAverage = heading.class_average
     {classAverage}

   renderStudentRow: (student_data) ->
@@ -56,7 +59,7 @@
       <BS.Panel className='-course-performance-container'>

-
Performance Book

+
Performance Report

           <BS.Table className='-course-performance-table'>


don't let s3 errors stop archive worker

don't let s3 errors stop archive worker

--- services/track-changes/app/coffee/PackWorker.coffee
+++ services/track-changes/app/coffee/PackWorker.coffee
@@ -79,6 +79,10 @@
 			logger.log {project_id, doc_id}, "skipping pack, missing project/doc id"
 			return callback()
 		handler = (err, result) ->
+			if err? and err.code is "InternalError" and err.retryable
+				logger.warn {err, result}, "ignoring S3 error in pack archive worker"
+				# Ignore any s3 errors due to random problems
+				err = null
 			if err?
 				logger.error {err, result}, "error in pack archive worker"
 				return callback(err)


small fix

small fix

--- app/assets/javascripts/bookings/calendar/calendar_app.js.coffee
+++ app/assets/javascripts/bookings/calendar/calendar_app.js.coffee
@@ -57,10 +57,9 @@

     showMonthCalendar: (year, n)->
-      d = moment().year(year).month(n)
-      console.log d.toString()
-      d_start = (moment().year year).month n
-      d_end = (moment().year year).month n
+      d = (moment().year year).month n
+      d_start = d.clone()
+      d_end = d.clone()
       start = (d_start.startOf 'month').unix()
       end = (d_end.endOf 'month').unix()
       month_start = (d_start.startOf 'month').startOf 'week'


removed comment

removed comment

--- lib/atom-ternjs-view.coffee
+++ lib/atom-ternjs-view.coffee
@@ -60,8 +60,7 @@
     false

   getFilterKey: -> 'name'
-
-  # Returns an object that can be retrieved when package is activated
+
   setPosition: ->
     { left, top } = @editorView.pixelPositionForScreenPosition(@editor.getCursorScreenPosition())
     height = @outerHeight()


Wait longer for browser in history spec

Wait longer for browser in history spec

--- spec_app/spec/javascripts/up/history_spec.js.coffee
+++ spec_app/spec/javascripts/up/history_spec.js.coffee
@@ -300,20 +300,20 @@

             safeHistory.back()

-          next.after 100, =>
-            respond()
-
-          next =>
-            expect(events).toEqual [
-              ['up:history:pushed', normalize('/foo')]
-              ['up:history:pushed', normalize('/bar')]
-              ['up:history:pushed', normalize('/baz')]
-              ['up:history:restored', normalize('/bar')]
-            ]
-
-            safeHistory.back()
-
-          next.after 100, =>
+          next.after 150, =>
+            respond()
+
+          next =>
+            expect(events).toEqual [
+              ['up:history:pushed', normalize('/foo')]
+              ['up:history:pushed', normalize('/bar')]
+              ['up:history:pushed', normalize('/baz')]
+              ['up:history:restored', normalize('/bar')]
+            ]
+
+            safeHistory.back()
+
+          next.after 150, =>
             respond()

           next =>
@@ -327,7 +327,7 @@

             safeHistory.forward()

-          next.after 100, =>
+          next.after 150, =>
             respond()

           next =>
@@ -342,7 +342,7 @@

             safeHistory.forward()

-          next.after 100, =>
+          next.after 150, =>
             respond() # we need to respond since we've never requested /baz with the popTarget

           next =>


fix: change IP property

fix: change IP property

--- vendor/scripts/set-ip.coffee
+++ vendor/scripts/set-ip.coffee
@@ -1,2 +1,4 @@
+# I KNOW THIS IS BAD
+# HOW DO I DO IT BETTER?
 $.get 'http://ip-api.com/json', (res) ->
-  window.IP = res.query
+  window.USER_IP = res.query


SidebarMachineSharePopup: Align requires.

--- client/app/lib/activity/sidebar/sidebarmachinesharepopup.coffee
+++ client/app/lib/activity/sidebar/sidebarmachinesharepopup.coffee
@@ -1,13 +1,13 @@
-kd = require 'kd'
-KDModalView = kd.ModalView
+kd               = require 'kd'
+KDModalView      = kd.ModalView
 KDCustomHTMLView = kd.CustomHTMLView
-KDView = kd.View
-KDButtonView = kd.ButtonView
-KDOverlayView = kd.OverlayView
-AvatarView = require 'app/commonviews/avatarviews/avatarview'
-showError = require 'app/util/showError'
-remote = require('app/remote').getInstance()
-envDataProvider = require 'app/userenvironmentdataprovider'
+KDView           = kd.View
+KDButtonView     = kd.ButtonView
+KDOverlayView    = kd.OverlayView
+AvatarView       = require 'app/commonviews/avatarviews/avatarview'
+showError        = require 'app/util/showError'
+remote           = require('app/remote').getInstance()
+envDataProvider  = require 'app/userenvironmentdataprovider'

 module.exports = class SidebarMachineSharePopup extends KDModalView


bugfix: fix the double-reload issue

bugfix: fix the double-reload issue

--- client/landing/app/MainApp/maincontroller/maincontroller.coffee
+++ client/landing/app/MainApp/maincontroller/maincontroller.coffee
@@ -133,9 +133,10 @@
     # async clientId change checking procedures causes
     # race conditions between window reloading and post-login callbacks
     @utils.repeat 3000, do (cookie = $.cookie 'clientId') => =>
+      cookieExists = cookie?
       cookieMatches = cookie is ($.cookie 'clientId')
       cookie = $.cookie 'clientId'
-      if cookie? and not cookieMatches
+      if cookieExists and not cookieMatches
         return @isLoggingIn off  if @isLoggingIn() is on

         window.removeEventListener 'beforeunload', wc.bound 'beforeUnload'


Fix export plan viz to PNG in IE.

Fix export plan viz to PNG in IE.

- Substitute regular space + space="preserve" attribute
with unicode non breaking space (\u00A0) as a workaround for
IE bug in XMLSerializer: https://connect.microsoft.com/IE/feedback/details/816615/ie9-using-xmlserializer-on-svgsvgelement-with-custom-namespaces-is-broken

--- community/browser/lib/visualization/components/queryPlan.coffee
+++ community/browser/lib/visualization/components/queryPlan.coffee
@@ -327,7 +327,7 @@
                     else
                       ['EstimatedRows', 'estimated row']
                     [
-                      { x: x, y: y, text: formatNumber(source[key]) + ' ', anchor: 'end' }
+                      { x: x, y: y, text: formatNumber(source[key]) + '\u00A0', anchor: 'end' }
                       { x: x, y: y, text: plural(caption, source[key]), anchor: 'start' }
                     ]
                   else
@@ -343,7 +343,6 @@
                   .attr('x', (d) -> d.x)
                   .attr('y', (d) -> d.y)
                   .attr('text-anchor', (d) -> d.anchor)
-                  .attr('xml:space', 'preserve')
                   .text((d) -> d.text)

       'g.layer.operators':
@@ -480,7 +479,7 @@
                     data: (d) ->
                       if d.key
                         [
-                          { text: d.value + ' ', anchor: 'end', x: operatorWidth / 2 }
+                          { text: d.value + '\u00A0', anchor: 'end', x: operatorWidth / 2 }
                           { text: d.key, anchor: 'start', x: operatorWidth / 2 }
                         ]
                       else
@@ -495,7 +494,6 @@
                       update
                       .attr('x', (d) -> d.x)
                       .attr('text-anchor', (d) -> d.anchor)
-                      .attr('xml:space', 'preserve')
                       .attr('fill', 'black')
                       .transition()
                       .each('end', ->
@@ -568,7 +566,7 @@
                   if d.alwaysShowCost
                     y = d.height - d.costHeight + operatorDetailHeight
                     [
-                      { text: formatNumber(d.DbHits) + ' ', anchor: 'end', y: y }
+                      { text: formatNumber(d.DbHits) + '\u00A0', anchor: 'end', y: y }
                       { text: 'db hits', anchor: 'start', y: y }
                     ]
                   else
@@ -584,7 +582,6 @@
                   update
                   .attr('x', operatorWidth / 2)
                   .attr('text-anchor', (d) -> d.anchor)
-                  .attr('xml:space', 'preserve')
                   .transition()
                   .attr('y', (d) -> d.y)
                   .each('end', ->


:public: Document new ContextMenuManager::add API

--- src/context-menu-manager.coffee
+++ src/context-menu-manager.coffee
@@ -39,14 +39,50 @@
     map = CSON.readFileSync(platformMenuPath)
     atom.contextMenu.add(platformMenuPath, map['context-menu'])

-  # Public: Creates menu definitions from the object specified by the menu
-  # JSON API.
+  # Public: Add context menu items scoped by CSS selectors.
   #
-  # * `name` The path of the file that contains the menu definitions.
-  # * `object` The 'context-menu' object specified in the menu JSON API.
-  # * `options` An optional {Object} with the following keys:
-  #   * `devMode` Determines whether the entries should only be shown when
-  #     the window is in dev mode.
+  # ## Examples
+  #
+  # To add a context menu, pass a selector matching the elements to which you
+  # want the menu to apply as the top level key, followed by a menu descriptor.
+  # The invocation below adds a global 'Help' context menu item and a 'History'
+  # submenu on the editor supporting undo/redo. This is just for example
+  # purposes and not the way the menu is actually configured in Atom by default.
+  #
+  # ```coffee
+  # atom.contextMenu.add {
+  #   '.workspace': [{label: 'Help', command: 'application:open-documentation'}]
+  #   '.editor':    [{
+  #     label: 'History',
+  #     submenu: [
+  #       {label: 'Undo': command:'core:undo'}
+  #       {label: 'Redo': command:'core:redo'}
+  #     ]
+  #   }]
+  # }
+  # ```
+  #
+  # ## Arguments
+  #
+  # * `items` An {Object} whose keys are CSS selectors and whose values are
+  #   {Array}s of item {Object}s containing the following keys:
+  #   * `label` (Optional) A {String} containing the menu item's label.
+  #   * `command` (Optional) A {String} containing the command to invoke on the
+  #     target of the right click that invoked the context menu.
+  #   * `submenu` (Optional) An {Array} of additional items.
+  #   * `type` (Optional) If you want to create a separator, provide an item
+  #      with `type: 'separator'` and no other keys.
+  #   * `created` (Optional) A {Function} that is called on the item each time a
+  #     context menu is created via a right click. You can assign properties to
+  #    `this` to dynamically compute the command, label, etc. This method is
+  #    actually called on a clone of the original item template to prevent state
+  #    from leaking across context menu deployments. Called with the following
+  #    argument:
+  #     * `event` The click event that deployed the context menu.
+  #   * `shouldDisplay` (Optional) A {Function} that is called to determine
+  #     whether to display this item on a given context menu deployment. Called
+  #     with the following argument:
+  #     * `event` The click event that deployed the context menu.
   add: (items) ->
     unless typeof arguments[0] is 'object'
       legacyItems = arguments[1]


add create method to buckets store

add create method to buckets store

--- src/node_modules/buckets-store/buckets-store.coffee
+++ src/node_modules/buckets-store/buckets-store.coffee
@@ -2,6 +2,11 @@

   get: (bucketId)->
     Restangular.one('buckets', bucketId).get()
+    .then (bucket) ->
+      new BucketModel(bucket.plain())
+
+  create: (bucket) ->
+    Restangular.all('buckets').post(bucket)
     .then (bucket) ->
       new BucketModel(bucket.plain())


* [bug] _getPerms should pass @[rolesFieldName]

--- src/mixins/none-cache-roles.coffee
+++ src/mixins/none-cache-roles.coffee
@@ -105,7 +105,7 @@

   Model::getPerms = (lastVisited = 0)->
     if isFunc Model._getPerms
-      Model._getPerms(@, aRoles, lastVisited)
+      Model._getPerms(@, @[rolesFieldName], lastVisited)
     else
       Model.getPerms @[rolesFieldName], lastVisited


refactor on DrawerCollection to meet the new API

--- src/models/DrawerCollection.coffee
+++ src/models/DrawerCollection.coffee
@@ -39,9 +39,9 @@
       parameters.fullChange = options.fullChange

     # when fullChange = true specify a webview to be replaced in the center
-    if options.centerView? and options.centerView.id?
-      parameters.centerView = {
-        id: options.centerView.id
+    if options.center? and options.center.id?
+      parameters.center = {
+        id: options.center.id
       }

     steroids.nativeBridge.nativeCall
@@ -53,18 +53,24 @@
   show: (options={}, callbacks={}) ->
     steroids.debug "steroids.drawers.show called"

-    parameters = {}
+    parameters = {
+      edge: steroids.screen.edges.LEFT
+      left:{}
+      right:{}
+      options:{}
+    }

-    if options.keepLoading == true
-      steroids.debug "steroids.drawers.show using keepLoading"
-      parameters.keepTransitionHelper = true
+    if options.left?
+      @applyViewOptions options.left parameters.left
+
+    if options.right?
+      @applyViewOptions options.right parameters.right
+
+    if options.options?
+      @applyDrawerSettings options.options, parameters.options

     if options.edge?
       parameters.edge = options.edge
-    else
-      parameters.edge = steroids.screen.edges.LEFT
-
-    @applyDrawerSettings options, parameters

     steroids.nativeBridge.nativeCall
       method: "openDrawer"
@@ -76,27 +82,19 @@
     steroids.debug "steroids.drawers.enableGesture called"

     parameters = {
-      leftView:{}
-      rightView:{}
+      left:{}
+      right:{}
+      options:{}
     }

-    if options.leftView?
-      if options.leftView.id?
-        parameters.leftView.id = options.leftView.id
-      else
-        parameters.leftView.url = options.leftView.location
+    if options.left?
+      @applyViewOptions options.left parameters.left

-    if options.rightView?
-      if options.rightView.id?
-        parameters.rightView.id = options.rightView.id
-      else
-        parameters.rightView.url = options.rightView.location
+    if options.right?
+      @applyViewOptions options.right parameters.right

-    if options.keepLoading == true
-      steroids.debug "steroids.drawers.enableGesture using keepLoading"
-      parameters.keepTransitionHelper = true
-
-    @applyDrawerSettings options, parameters
+    if options.options?
+      @applyDrawerSettings options.options, parameters.options

     steroids.nativeBridge.nativeCall
       method: "updateDrawer"
@@ -118,8 +116,19 @@
       successCallbacks: [callbacks.onSuccess]
       failureCallbacks: [callbacks.onFailure]

-  # TODO: make it private.. ne need to expose in the object
-  applyDrawerSettings: (options={}, parameters={}) ->
+  @applyViewOptions: (view={}, parameters={}) ->
+    if view.id?
+      parameters.id = view.id
+    else
+      parameters.url = view.location
+
+    if view.keepLoading == true
+      steroids.debug "steroids.drawers using keepLoading"
+      parameters.keepTransitionHelper = true
+
+    parameters
+
+  @applyDrawerSettings: (options={}, parameters={}) ->

     if options.showShadow?
       parameters.showShadow = options.showShadow


Workspace: Fixed a Firebase bug on our implementation.

https://github.com/firebase/firepad/issues/30

--- client/landing/app/CommonViews/workspace/panes/collaborativeeditorpane.coffee
+++ client/landing/app/CommonViews/workspace/panes/collaborativeeditorpane.coffee
@@ -12,11 +12,10 @@
       @firepad    = Firepad.fromCodeMirror @ref, @codeMirrorEditor

       @firepad.on "ready", =>
+        @firepad.setText " " # fix for a firepad bug
+        @codeMirrorEditor.scrollTo 0, 0
         {file, content} = @getOptions()
         return @openFile file, content  if file
-        if @firepad.isHistoryEmpty()
-          @firepad.setText "" # fix for a firepad bug
-          @codeMirrorEditor.scrollTo 0, 0

       @ref.on "value", (snapshot) =>
         value = snapshot.val()


Fixes #37: correct strings highlighting.

--- tools/syntax_highlighting/atom/language-do/grammars/do.cson
+++ tools/syntax_highlighting/atom/language-do/grammars/do.cson
@@ -46,15 +46,25 @@
 	},
 	{
 		"begin": "\"",
-		"end": "(?<!\\\\)\"",
+		"end": "\"",
 		"name": "string.quoted.double.do",
-		"comment": "Strings."
+		"comment": "Strings.",
+		"patterns": [
+			{
+				"include": "#escaped_symbol"
+			}
+		]
 	},
 	{
 		"begin": "\'",
-		"end": "(?<!\\\\)\'",
+		"end": "\'",
 		"name": "string.quoted.single.do",
-		"comment": "Symbols."
+		"comment": "Symbols.",
+		"patterns": [
+			{
+				"include": "#escaped_symbol"
+			}
+		]
 	},
 	{
 		"match": "^\\s*note\\b.*$",
@@ -67,4 +77,11 @@
 		"name": "comment.block.do",
 		"comment": "Multiline comments."
 	}
-]
+],
+"repository": {
+	"escaped_symbol": {
+		"match": "\\\\.",
+		"name": "constant.character.escape.do",
+		"comment": "Escaped symbol."
+	}
+}


refactoring links helper

refactoring links helper

--- src/scripts/helpers/links.coffee
+++ src/scripts/helpers/links.coffee
@@ -36,7 +36,7 @@
       title = trim(model.get?('title') or model.title)

       if model.isBook?()
-        title = trim(model.get('currentPage').get('title')) if model.get('currentPage')?
+        title = trim(model.get('currentPage')?.get('title'))
         page = ":#{model.getPageNumber()}"

       return "#{settings.root}contents/#{id}#{page}/#{title}"


fix whenever account settings are updated, pro/con labels cease to work

Former-commit-id: 873779fbf2f06b1000b3f604fbf6b527c1b30644 [formerly 04c5c7cbdb3df73fd41566e657c1e2118e58aa60] [formerly 30d48e88e4e465f983b310e7f41bfeedaffa3b6e [formerly 30d48e88e4e465f983b310e7f41bfeedaffa3b6e [formerly 59cb4ca9f82b31ead1983e89f6215cbaf2c4ab01]]]
Former-commit-id: 6eff27aa612f87b834f5c26b7d5be5a02f5d17e9 [formerly ed933f37845b824acff2a7ef282017cfde7d3853]
Former-commit-id: 3d12b37d341e8843312de51ed3746575bfe29188
Former-commit-id: eff85adf38e7142672d8d528eb0f4c5af00ec040

--- app/assets/javascripts/backbone/entities/tenant.coffee
+++ app/assets/javascripts/backbone/entities/tenant.coffee
@@ -21,14 +21,17 @@

     initialize : (options = {}) ->
       super options
-
-      # Overwrite null and blank values for default fields
-      _.each _.keys(@defaults), (key) =>
-        @set(key, @defaults[key]) if !!!@get(key)
+      @updateAttrs()

       #TODO: create HTML version of each of these fields and use those to render when appropriate
       @attributes.header_text = htmlFormat(@attributes.header_text)
       @attributes.header_details_text = htmlFormat(@attributes.header_details_text)
+
+
+    updateAttrs : ->
+      # Overwrite null and blank values for default fields
+      _.each _.keys(@defaults), (key) =>
+        @set(key, @defaults[key]) if !!!@get(key)

     classesToModerate : ->
@@ -91,6 +94,7 @@

     updateTenant : (attrs) ->
       @current_tenant.set attrs
+      @current_tenant.updateAttrs()
       App.vent.trigger "tenant:updated"

   App.reqres.setHandler "tenant:get", ->


Fix excessive bottom margin on CO2 biomass chart

--- app/assets/javascripts/d3/co2_emissions_biomass.coffee
+++ app/assets/javascripts/d3/co2_emissions_biomass.coffee
@@ -22,7 +22,7 @@

     margins:
       top: 20
-      bottom: 45
+      bottom: 20
       left: 30
       right: 40


Enter submits comments. Ctrl-enter creates newlines. Clear textarea on submit.

--- app/assets/javascripts/views/discussion_view.js.coffee
+++ app/assets/javascripts/views/discussion_view.js.coffee
@@ -5,7 +5,7 @@
         buildItem: (comment) -> new Views.CommentItem(comment)
       )
       @div class: 'text-entry', =>
-        @textarea rows: 2, outlet: 'commentTextarea'
+        @textarea rows: 2, outlet: 'textarea'
         @button "Submit Comment", class: 'btn pull-right', click: 'createComment'

   autoScroll: true
@@ -15,12 +15,18 @@
     @commentsList.onInsert = => @scrollToBottom() if @autoScroll
     @commentsList.setRelation(@comments)

+    @textarea.keydown (e) =>
+      if e.keyCode == 13 && !e.ctrlKey # enter (not ctrl-enter though)
+        e.preventDefault()
+        @createComment()
+
   afterAttach: ->
     @scrollToBottom()

   createComment: ->
-    body = @commentTextarea.val()
+    body = @textarea.val()
     if /\S/.test(body)
+      @textarea.val('')
       @comments.create({body})

   scrollToBottom: ->


lint and fix

lint and fix

--- src/client/querystring/index.coffee
+++ src/client/querystring/index.coffee
@@ -1,13 +1,9 @@
-_    = require '../utils/lodash'
+_    = require '../../utils/lodash'
 qs   = require '../../utils/query-string-encoder'

-pick = (prefix, obj) => _.reduce obj, (m, v, k) =>
+pick = (prefix, obj) -> _.reduce obj, (m, v, k) ->
   m[k.replace(prefix,'')] = k if k.indexOf(prefix) == 0
   m
-
-# https://go.pardot.com/tracker/httpsRedirect?pi_email_id=6174525&request_uri_path=%2Fe%2F384582%2Fcomplete-guide-pqls-chapter1-%2F2gbql%2F6174525&https_redirect=1&tracker_id=1169747&account_id=384582&visitor_id=12046559&link_type=1
-
-

 # Segment QueryString API compatibility layer
 class QueryString
@@ -22,12 +18,12 @@

   getCurrentUserId: -> @currentUser.get('id')

-  pardot: () =>
+  pardot: () =>
     ref = window.location.referrer
     return unless ref.indexOf("https://go.pardot.com")
     q = qs.decode(window.location.referrer.split('httpsRedirect?')[1])
     return unless _.size(q)
-    @alias(`pardot:#{q.visitor_id}`) if q.visitor_id
+    @alias("pardot:#{q.visitor_id}") if q.visitor_id

   parse: () =>
     q = qs.decode()


fix deprecations in old functions

fix deprecations in old functions

--- lib/idris-controller.coffee
+++ lib/idris-controller.coffee
@@ -155,7 +155,7 @@
         editor.setTextInBufferRange lineRange, split

   doAddClause: ({target}) =>
-    editor = atom.workspace.getActiveEditor()
+    editor = atom.workspace.getActiveTextEditor()
     line = editor.getCursor(0).getBufferRow()
     word = @getWordUnderCursor target
     @model.addClause line + 1, word, (err, clause) =>
@@ -181,7 +181,7 @@
         @messages.add new MetavariablesView metavariables

   doProofSearch: ({target}) =>
-    editor = atom.workspace.getActiveEditor()
+    editor = atom.workspace.getActiveTextEditor()
     line = editor.getCursor(0).getBufferRow()
     word = @getWordUnderCursor target
     @model.proofSearch line + 1, word, (err, res) =>
@@ -190,11 +190,11 @@
       else
         editor.transact ->
           # Move the cursor to the beginning of the word
-          editor.moveCursorToBeginningOfWord()
+          editor.moveToBeginningOfWord()
           # Because the ? in the metavariable isn't part of
           # the word, we move left once, and then select two
           # words
-          editor.moveCursorLeft()
+          editor.moveLeft()
           editor.selectToEndOfWord()
           editor.selectToEndOfWord()
           # And then replace the replacement with the guess..


Now close is red (classic) and copy is green to match the pop-up generated by using it

--- lib/line-diff-worker.coffee
+++ lib/line-diff-worker.coffee
@@ -141,11 +141,11 @@
         @div class: "bubble", =>
             @div class: "action-buttons", =>
                 @button click: "removeView", class: "btn diff-button", title: "Close", =>
-                    @span class: "text-success icon icon-x"
+                    @span class: "text-error icon icon-x"
                 @button click: "revertAndClose", class: "btn diff-button", title: "Revert", =>
                     @span class: "text-warning icon icon-history"
                 unless isRemoval
                     @button click: "copyToClipboard", class: "btn diff-button", title: "Copy", =>
-                        @span class: "text-primary icon icon-clippy"
+                        @span class: "text-success icon icon-clippy"
             @div class: "bubble-code", => @span message
 module.exports = LineDiffWorker


collab -- make it possible for a project viewer or collab to remove him/her self.

--- salvus/page/project.coffee
+++ salvus/page/project.coffee
@@ -1553,24 +1553,32 @@
                         already_collab = {}
                         for x in users
                             already_collab[x.account_id] = true
-                            if x.account_id != salvus_client.account_id
-                                c = template_project_collab.clone()
-                                c.find(".project-collab-first-name").text(x.first_name)
-                                c.find(".project-collab-last-name").text(x.last_name)
-                                if x.mode == 'owner'
-                                    c.find(".project-close-button").hide()
-                                    c.css('background-color', '#51a351')
-                                    c.tooltip(title:"Owner", delay: { show: 500, hide: 100 })
+                            c = template_project_collab.clone()
+                            c.find(".project-collab-first-name").text(x.first_name)
+                            c.find(".project-collab-last-name").text(x.last_name)
+                            if x.mode == 'owner'
+                                c.find(".project-close-button").hide()
+                                c.css('background-color', '#51a351')
+                                c.tooltip(title:"Owner", delay: { show: 500, hide: 100 })
+                            else
+                                c.find(".project-close-button").data('collab', x).click () ->
+                                    remove_collaborator($(@).data('collab'))
+                                    return false
+
+                                if x.account_id == salvus_client.account_id
+                                    extra_tip = " (delete to remove your own access to this project)"
+                                    c.css("background-color","#bd362f")
                                 else
-                                    c.find(".project-close-button").data('collab', x).click () ->
-                                        remove_collaborator($(@).data('collab'))
-                                        return false
-                                    if x.mode == 'collaborator'
-                                        c.tooltip(title:"Collaborator", delay: { show: 500, hide: 100 })
-                                    else if x.mode == 'viewer'
+                                    extra_tip = ""
+
+
+                                if x.mode == 'collaborator'
+                                    c.tooltip(title:"Collaborator"+extra_tip, delay: { show: 500, hide: 100 })
+                                else if x.mode == 'viewer'
+                                    if extra_tip == ""
                                         c.css('background-color', '#f89406')
-                                        c.tooltip(title:"Viewer", delay: { show: 500, hide: 100 })
-                                collabs.append(c)
+                                    c.tooltip(title:"Viewer"+extra_tip, delay: { show: 500, hide: 100 })
+                            collabs.append(c)

         update_collaborators()


ui sync fix

ui sync fix

--- salvus/page/syncdoc.coffee
+++ salvus/page/syncdoc.coffee
@@ -435,7 +435,7 @@
             show_spinner = () =>
                 @element.find(".salvus-editor-codemirror-not-synced").show()
                 #@element.find(".salvus-editor-codemirror-synced").hide()
-            @_ui_synced_timer = setTimeout(show_spinner, 1500)
+            @_ui_synced_timer = setTimeout(show_spinner, 2*@opts.sync_interval)

     sync: (cb) =>    # cb(false if a sync occured; true-ish if anything prevented a sync from happening)
         if @_syncing? and @_syncing


More spec for process function

More spec for process function

--- src/spec/services/base.spec.coffee
+++ src/spec/services/base.spec.coffee
@@ -239,6 +239,11 @@
           .fail (err) ->
             done err

+        it 'should throw error if function is missing', ->
+          spyOn(@restMock, 'GET')
+          expect(=> @service.process()).toThrow new Error 'Please provide a function to process the elements'
+          expect(@restMock.GET).not.toHaveBeenCalled()
+
       describe ':: fetch', ->

         it 'should return promise on fetch', ->


change to use steroids project creator

change to use steroids project creator

--- src/steroids/ProjectCreator.coffee
+++ src/steroids/ProjectCreator.coffee
@@ -14,8 +14,7 @@

     env.plugins "node_modules", paths.npm
     env.lookup '*:*'
-    #env.run "steroids:app #{targetDirectory}", {"skip-install": "nobodychecksthisvalue"}, deferred.resolve
-    env.run "devroids:app #{targetDirectory}", {"skip-install": "nobodychecksthisvalue"}, deferred.resolve
+    env.run "steroids:app #{targetDirectory}", {"skip-install": "nobodychecksthisvalue"}, deferred.resolve

     return deferred.promise


update path to startup file

update path to startup file

--- lib/package/commands.coffee
+++ lib/package/commands.coffee
@@ -97,7 +97,7 @@
       'julia-debug:step-into-function': => juno.runtime.debugger.stepin()
       'julia-debug:finish-function': => juno.runtime.debugger.finish()

-      'julia:open-startup-file': -> atom.workspace.open juno.misc.paths.home '.juliarc.jl'
+      'julia:open-startup-file': -> atom.workspace.open(juno.misc.paths.home('.julia', 'config', 'startup.jl'))
       'julia:open-julia-home': -> shell.openItem juno.misc.paths.juliaHome()
       'julia:open-package-in-new-window': -> juno.misc.paths.openPackage()
       'julia:standard-layout': -> juno.ui.layout.standard()


Fix #12528

Fix #12528

--- src/text-editor-component.coffee
+++ src/text-editor-component.coffee
@@ -909,7 +909,7 @@

   screenRowForNode: (node) ->
     while node?
-      if screenRow = node.dataset.screenRow
+      if screenRow = node.dataset?.screenRow
         return parseInt(screenRow)
       node = node.parentElement
     null


BaseStackTemplateListItem: Change handling events

--- client/app/lib/stacks/basestacktemplatelistitem.coffee
+++ client/app/lib/stacks/basestacktemplatelistitem.coffee
@@ -37,9 +37,9 @@
     listView  = @getDelegate()
     @menu    ?= {}

-    @addMenuItem 'Show', listView.lazyBound 'showItemContent', this
-    @addMenuItem 'Edit', @bound 'editStackTemplate'
+    @addMenuItem 'Show', => listView.emit 'ItemAction', { action : 'ShowItem', item : this }
+    @addMenuItem 'Edit', => listView.emit 'ItemAction', { action : 'EditItem', item : this }
     @addMenuItem 'Generate Stack', @bound 'generateStackFromTemplate'
-    @addMenuItem 'Delete', listView.lazyBound 'deleteItem', this
+    @addMenuItem 'Delete', => listView.emit 'ItemAction', { action : 'RemoveItem', item : this }

     return @menu


home: utilities: kdcli: minor change

home: utilities: kdcli: minor change

--- client/home/lib/utilities/components/kdcli/view.coffee
+++ client/home/lib/utilities/components/kdcli/view.coffee
@@ -17,7 +17,7 @@

         {@props.copyKey}

-
Once installed, you can use kd list to list your Koding VMs and
+
Once installed, you can use kd list to list your Koding VMs and
         kd mount to mount your VM to a local folder in your computer.
         For detailed instructions:


cleanup

cleanup

--- lib/text-object-pair.coffee
+++ lib/text-object-pair.coffee
@@ -408,10 +408,9 @@

 getTagState = (event) ->
   backslash = event.match[1]
-  tagName = event.match[2]
   {
     state: if (backslash is '') then 'open' else 'close'
-    name: tagName
+    name: event.match[2]
     event: event
   }

@@ -456,13 +455,10 @@
     filters = @getFilters()
     options = {@editor, from, filters, @allowNextLine, @allowForwarding}

-    closeRange = findCloseTagRangeForward(options)
-    if closeRange?
-      options.from = closeRange.end
-      openRange = findOpenTagRangeBackward(options)
-
-    unless (openRange? and closeRange?)
-      return null
+    return unless closeRange = findCloseTagRangeForward(options)
+
+    options.from = closeRange.end
+    return unless openRange = findOpenTagRangeBackward(options)

     aRange = new Range(openRange.start, closeRange.end)
     innerRange = new Range(openRange.end, closeRange.start)


round score after calculating averages

round score after calculating averages

--- tutor/src/flux/scores.coffee
+++ tutor/src/flux/scores.coffee
@@ -43,11 +43,10 @@
   course = data[taskInfo.courseId][0]
   student = course.students[taskInfo.studentIndex]

-  # Calculate score for the task
-  task.score = Math.round((
+  # Update score for the task without rounding so the calculations below will use it's full precision
+  task.score =
     (task.correct_on_time_exercise_count + task.correct_accepted_late_exercise_count ) /
       task.exercise_count
-  ) * 100 ) / 100

   # Student's course average
   numTasksStudent = 0
@@ -78,6 +77,10 @@
   course.overall_average_score =
     (course.overall_average_score - ( oldScore / taskCount ) ) +
       ( task.score / taskCount )
+
+  # Now round the score
+  task.score = Math.round(task.score * 100 ) / 100
+

 ScoresConfig = {


Restate block breakout condition

Restate block breakout condition

--- src/trix/models/composition.coffee
+++ src/trix/models/composition.coffee
@@ -67,7 +67,8 @@
           @removeCurrentAttribute(key) for key of block.getAttributes()
         # Break out of block after a newline (and remove the newline)
         when text.endsWithString("\n")
-          @selectionDelegate?.expandSelectionInDirectionWithGranularity("backward", "character")
+          @deleteBackward()
+          @setPosition(@getPosition() + 1)
           @insertDocument(Trix.Document.fromString(""))
         # Stay in the block, add a newline
         else


Fix select all on inbox

Fix select all on inbox

--- static/coffee/controllers.coffee
+++ static/coffee/controllers.coffee
@@ -184,7 +184,7 @@
       $scope.oldItemsLoading = false

       if forSelectAll
-        for item in items
+        for item in $scope.items
           item.selected = true
         $scope.updateItems(false)
         if $scope.oldItemsMore and $scope.items.length < INFINITE_SCROLL_MAX_ITEMS


Browser.Resource filters should be bound to browser object

--- lib/zombie/resources.coffee
+++ lib/zombie/resources.coffee
@@ -71,6 +71,9 @@
 #     console.log("Response body: " + response.body);
 #     next();
 #   });
+#
+# These filters are added to every new browser.resources and immidiately bound
+# to the browser object.

 File        = require("fs")
@@ -88,7 +91,7 @@
 class Resources extends Array
   constructor: (browser)->
     @browser = browser
-    @filters = Resources.filters.slice()
+    @filters = Resources.filters.map((fn)-> fn.bind(browser))

   # Make an HTTP request (also supports file: protocol).
@@ -116,7 +119,6 @@
       [options, callback] = [{}, options]

     request =
-      browser: @browser
       method:  method.toUpperCase()
       url:     url
       headers: options.headers || {}
@@ -129,7 +131,7 @@
     @push(resource)
     @browser.emit("request", resource)

-    Resources.runFilters request, @filters, (error, response)=>
+    this.runFilters request, (error, response)=>
       if error
         resource.error = error
         callback(error)
@@ -190,6 +192,50 @@
     assert filter.length == 2 || filter.length == 3, "Filter function takes 2 (before filter) or 3 (after filter) arguments"
     @filters.push(filter)

+  # Processes the request using a chain of filters.
+  runFilters: (request, callback)->
+    beforeFilters = @filters.filter((fn)-> fn.length == 2)
+    beforeFilters.push(Resources.httpRequest.bind(@browser))
+    afterFilters = @filters.filter((fn)-> fn.length == 3)
+    response = null
+
+    # Called to execute the next 'before' filter.
+    beforeFilterCallback = (error, responseFromFilter)->
+      if error
+        callback(error)
+      else if responseFromFilter
+        # Received response, switch to processing request
+        response = responseFromFilter
+        afterFilterCallback()
+      else
+        # Use the next before filter.
+        filter = beforeFilters.shift()
+        try
+          filter(request, beforeFilterCallback)
+        catch error
+          callback(error)
+
+    # Called to execute the next 'after' filter.
+    afterFilterCallback = (error)->
+      if error
+        callback(error)
+      else
+        filter = afterFilters.shift()
+        if filter
+          # Use the next after filter.
+          try
+            filter(request, response, afterFilterCallback)
+          catch error
+            console.log error.stack
+            callback(error)
+        else
+          # No more filters, callback with response.
+          callback(null, response)
+
+    # Start with first before filter
+    beforeFilterCallback()
+    return
+

 # -- Filters

@@ -197,6 +243,8 @@
 #
 # Filters used before the request take two arguments.  Filters used with the
 # response take three arguments.
+#
+# These filters are bound to the browser object.
 Resources.addFilter = (filter)->
   assert filter.call, "Filter must be a function"
   assert filter.length == 2 || filter.length == 3, "Filter function takes 2 (before filter) or 3 (after filter) arguments"
@@ -211,7 +259,6 @@
 # Also handles file: URLs and creates query string from request.params for
 # GET/HEAD/DELETE requests.
 Resources.normalizeURL = (request, next)->
-  browser = request.browser
   if /^file:/.test(request.url)
     # File URLs are special, need to handle missing slashes and not attempt
     # to parse (downcases path)
@@ -219,10 +266,10 @@
   else
     # Resolve URL relative to document URL/base, or for new browser, using
     # Browser.site
-    if browser.document
-      request.url = HTML.resourceLoader.resolve(browser.document, request.url)
+    if @document
+      request.url = HTML.resourceLoader.resolve(@document, request.url)
     else
-      request.url = URL.resolve(browser.site || "http://localhost", request.url)
+      request.url = URL.resolve(@site || "http://localhost", request.url)

   if request.params
     method = request.method
@@ -243,14 +290,13 @@
 #
 # It also normalizes all headers by down-casing the header names.
 Resources.mergeHeaders = (request, next)->
-  browser = request.browser
   # Header names are down-cased and over-ride default
   headers =
-    "user-agent":       browser.userAgent
+    "user-agent":       @userAgent
     "accept-encoding":  "identity" # No gzip/deflate support yet

   # Merge custom headers from browser first, followed by request.
-  for name, value of browser.headers
+  for name, value of @headers
     headers[name.toLowerCase()] = value
   if request.headers
     for name, value of request.headers
@@ -262,7 +308,7 @@
   headers.host = host

   # Apply authentication credentials
-  if credentials = browser.authenticate(host, false)
+  if credentials = @authenticate(host, false)
     credentials.apply(headers)

   request.headers = headers
@@ -376,10 +422,8 @@

   else

-    browser = request.browser
-
     # We're going to use cookies later when recieving response.
-    cookies = browser.cookies(hostname, pathname)
+    cookies = @cookies(hostname, pathname)
     cookies.addHeader(request.headers)

     httpRequest =
@@ -388,11 +432,11 @@
       headers:        request.headers
       body:           request.body
       multipart:      request.multipart
-      proxy:          browser.proxy
+      proxy:          @proxy
       jar:            false
       followRedirect: false

-    Request httpRequest, (error, response)->
+    Request httpRequest, (error, response)=>
       if error
         callback(error)
         return
@@ -422,8 +466,8 @@
       if redirectURL
         # Handle redirection, make sure we're not caught in an infinite loop
         ++redirects
-        if redirects > browser.maxRedirects
-          callback(new Error("More than #{browser.maxRedirects} redirects, giving up"))
+        if redirects > @maxRedirects
+          callback(new Error("More than #{@maxRedirects} redirects, giving up"))
           return

         redirectHeaders = {}
@@ -437,12 +481,11 @@
         delete redirectHeaders["content-transfer-encoding"]

         redirectRequest =
-          browser:    browser
           method:     "GET"
           url:        redirectURL
           headers:    redirectHeaders
           redirects:  redirects
-        Resources.httpRequest(redirectRequest, callback)
+        Resources.httpRequest.call(this, redirectRequest, callback)

       else

@@ -453,50 +496,6 @@
           body:         response.body
           redirects:    redirects
         callback(null, response)
-  return
-
-
-# Processes the request using a chain of filters.
-Resources.runFilters = (request, filters, callback)->
-  beforeFilters = filters.filter((fn)-> fn.length == 2)
-  beforeFilters.push(Resources.httpRequest)
-  afterFilters = filters.filter((fn)-> fn.length == 3)
-  response = null
-
-  # Called to execute the next 'before' filter.
-  beforeFilterCallback = (error, responseFromFilter)->
-    if error
-      callback(error)
-    else if responseFromFilter
-      # Received response, switch to processing request
-      response = responseFromFilter
-      afterFilterCallback()
-    else
-      # Use the next before filter.
-      filter = beforeFilters.shift()
-      try
-        filter(request, beforeFilterCallback)
-      catch error
-        callback(error)
-
-  # Called to execute the next 'after' filter.
-  afterFilterCallback = (error)->
-    if error
-      callback(error)
-    else
-      filter = afterFilters.shift()
-      if filter
-        # Use the next after filter.
-        try
-          filter(request, response, afterFilterCallback)
-        catch error
-          callback(error)
-      else
-        # No more filters, callback with response.
-        callback(null, response)
-
-  # Start with first before filter
-  beforeFilterCallback()
   return


Add back path change subscription

Add back path change subscription

--- lib/git-diff-view.coffee
+++ lib/git-diff-view.coffee
@@ -8,6 +8,7 @@
     @markers = null

     @subscriptions.add(@editor.onDidStopChanging(@updateDiffs))
+    @subscriptions.add(@editor.onDidChangePath(@updateDiffs))

     atom.project.getRepositories().forEach (repository) =>
       @subscriptions.add repository.onDidChangeStatuses =>


Only scroll when already below message

Only scroll when already below message

--- lib/package-panel.coffee
+++ lib/package-panel.coffee
@@ -123,7 +123,8 @@
     console.error(error.stack ? error)
     errorView = @createErrorView(error.message, error.stderr)
     @errors.append(errorView)
-    @parent().scrollTop(errorView.offset().top - @offset().top)
+    top = errorView.offset().top - @offset().top
+    @parent().scrollTop(top) if @parent().scrollTop() > top

   createLoadingView: (text) ->
     $$ ->


search for existing tags in boolean condition

--- BackofficeBundle/Resources/public/coffee/orchestraLib.coffee
+++ BackofficeBundle/Resources/public/coffee/orchestraLib.coffee
@@ -199,6 +199,7 @@
     propertyToSearch: 'value'
     theme: 'facebook'
     tokenFormatter: (item) ->
+      item = _.findWhere(tags, value: item.value) or item
       item.type = item.type or 'new'
       '
' + item.value + '
'
     tokenDelimiter: ' '
@@ -222,7 +223,7 @@
         return
     )(operator[i])
     $('
').html(operator[i]).on('click', click).appendTo ul
-  element.before ul
+  element.parent().append ul
   return

 #NODE CHOICE ENABLED


groups invitations: shorten already invited email validation error message

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -794,7 +794,7 @@
       JInvitationRequest.one params, (err, invitationRequest)=>
         if invitationRequest
           callback new KodingError """
-            You've already invited #{email} to join this group before.
+            You've already invited #{email}.
             """
         else
           params.invitationType = 'invitation'


 Merge branch 'master.groups.removeGroupBot' into 'master.groups'

Master.Groups.Remove Group Bot

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -463,9 +463,6 @@
             else
               console.log 'roles are added'
               queue.next()
-        ->
-          group.createGroupBotAndPostMessage client, ->
-            queue.next()
       ]

       if 'private' is group.privacy
@@ -1529,28 +1526,6 @@
         subscription.plan = plan
         callback null, subscription

-  createGroupBotAndPostMessage: (client, callback) ->
-    # get groupbot account
-    JAccount = require '../account'
-    JAccount.one "profile.nickname" : "bot", (err, account) =>
-      return callback err if err
-      return callback new KodingError "Can't find bot account" if not account
-
-      @addMember account, "member", (err, member)=>
-        return callback err if err
-        JNewStatusUpdate = require '../messages/newstatusupdate'
-        # set client's delegate to bot account
-        # CtF this really is a hack
-        client.context.group = @slug
-        client.context.user = "bot"
-        client.connection.delegate = account
-        client.groupName = @slug
-
-        data =
-          body  : "Welcome to your group"
-          group : @slug
-        JNewStatusUpdate.create client, data, callback
-
   getPermissionSet : (callback)->
     @fetchPermissionSet (err, permissionSet) =>
       callback err, null if err