:bug: Refactor put, putMulti and get Also fix #11 along the way

--- Source/SSH.coffee
+++ Source/SSH.coffee
@@ -50,54 +50,60 @@
         )
   put:(LocalFile, RemoteFile, SFTP, Retry = true)->
     throw new Error("Please connect before doing anything else") unless @Connected
-    return new Promise (Resolve, Reject)=>
-      try
-        FS.accessSync(LocalFile, FS.R_OK)
-      catch
-        return Reject("Local File '#{LocalFile}' doesn't exist")
-      if SFTP
-        ThePromise = Promise.resolve(SFTP)
-      else
-        ThePromise = @requestSFTP()
-      ThePromise.then (SFTP)=>
-        SFTP.fastPut LocalFile, RemoteFile, (Error)=>
-          return Resolve() unless Error
-          return Reject(Error) if Error.message isnt 'No such file' or not Retry
-          @mkdir(RemoteFile.split("/").slice(0,-1).join('/')).then =>
-            @put(LocalFile, RemoteFile, SFTP, false)
+    try
+      FS.accessSync(LocalFile, FS.R_OK)
+    catch
+      throw new Error("Local File '#{LocalFile}' doesn't exist")
+    if SFTP
+      ThePromise = Promise.resolve(SFTP)
+    else
+      ThePromise = @requestSFTP()
+    return ThePromise.then (SFTP)=>
+      Deferred = Promise.defer()
+      SFTP.fastPut LocalFile, RemoteFile, (Error)=>
+        if Error
+          if Error.message is 'No such file' or not Retry
+            Deferred.reject(Error)
+          else
+            Deferred.resolve(@mkdir(RemoteFile.split("/").slice(0,-1).join('/')).then =>
+              @put(LocalFile, RemoteFile, SFTP, false)
+            )
+        else
+          Deferred.resolve()
+      return Deferred.promise
   putMulti:(Files, SFTP)->
     throw new Error("Please connect before doing anything else") unless @Connected
-    return new Promise (Resolve, Reject)=>
-      if SFTP
-        ThePromise = Promise.resolve(SFTP)
-      else
-        ThePromise = @requestSFTP()
-      Promises = []
-      ThePromise.then (SFTP)=>
-        Files.forEach (File)=>
-          return unless File.Local or File.Remote
-          return unless FS.existsSync File.Local
-          Promises.push @put(File.Local, File.Remote, SFTP)
-        Promise.all(Promises).then(Resolve,Reject)
+    if SFTP
+      ThePromise = Promise.resolve(SFTP)
+    else
+      ThePromise = @requestSFTP()
+    Promises = []
+    return ThePromise.then (SFTP)=>
+      Files.forEach (File)=>
+        return unless File.Local or File.Remote
+        return unless FS.existsSync File.Local
+        Promises.push @put(File.Local, File.Remote, SFTP)
+      return Promise.all(Promises)
   get:(RemoteFile, LocalFile, SFTP)->
     throw new Error("Please connect before doing anything else") unless @Connected
-    return new Promise (Resolve, Reject)=>
-      if SFTP
-        ThePromise = Promise.resolve(SFTP)
-      else
-        ThePromise = @requestSFTP()
-      ThePromise.then (SFTP)=>
-        Stream = SFTP.createReadStream RemoteFile
-        Contents = []
-        Stream.on('data', (Data)->
-          Contents.push Data.toString()
-        ).on('close', ->
-          Contents = Contents.join('')
-          return Resolve(Contents) unless LocalFile
-          FS.writeFile LocalFile, Contents, (error)->
-            return Reject(error) if error
-            Resolve()
-        )
+    if SFTP
+      ThePromise = Promise.resolve(SFTP)
+    else
+      ThePromise = @requestSFTP()
+    return ThePromise.then (SFTP)=>
+      Deferred = Promise.defer()
+      Stream = SFTP.createReadStream(RemoteFile)
+      Contents = []
+      Stream.on('data', (Data)->
+        Contents.push Data.toString()
+      ).on('close', ->
+        Contents = Contents.join('')
+        return Deferred.resolve(Contents) unless LocalFile
+        FS.writeFile LocalFile, Contents, (error)->
+          return Reject(error) if error
+          Deferred.resolve()
+      )
+      return Deferred.promise
   requestSFTP:->
     throw new Error("Please connect before doing anything else") unless @Connected
     return new Promise (Resolve, Reject)=>


simplified tracking individual document downloads

--- app/assets/javascripts/species/views/documents/document_result_component.js.coffee
+++ app/assets/javascripts/species/views/documents/document_result_component.js.coffee
@@ -48,21 +48,13 @@

   actions:
     startDownload: () ->
-      document_id = @get('doc.id')
-      url = "/api/v1/documents/#{document_id}"
-      $.ajax({
-        type: 'GET'
-        dataType: 'json'
-        url: url
-      }).done((data) =>
-        doc = data.document_download
-        ga('send', {
-          hitType: 'event',
-          eventCategory: "Downloads: #{doc.event_type}",
-          eventAction: doc.event_name,
-          label: doc.document_type,
-          value: doc.id
-        })
-      )
+      url = "/api/v1/documents/#{@get('documentId')}"
+      ga('send', {
+        hitType: 'event',
+        eventCategory: "Downloads: #{@get('doc.event_type')}",
+        eventAction: @get('doc.event_name') + ': ' + @get('doc.document_type'),
+        label: 'Context: TODO',
+        value: @get('documentId')
+      })
       window.location = url


Use click to open user in new task.

Use click to open user in new task.

--- app/assets/javascripts/app/controllers/_application_controller.js.coffee
+++ app/assets/javascripts/app/controllers/_application_controller.js.coffee
@@ -311,6 +311,14 @@
     # remove old popovers
     $('.popover').remove()

+    # open user in new task if user isn't customer
+    if !@isRole('Customer')
+      @el.find('.user-popover').bind('click', (e) =>
+        user_id = $(e.target).data('id')
+        @navigate "#user/zoom/#{user_id}"
+        $('.popover').remove()
+      );
+
     # show user popup
     @el.find('.user-popover').popover(
       trigger:    'hover'


fix notification remove overflow bug

fix notification remove overflow bug

--- app/assets/javascripts/app/controllers/widget/online_notification.coffee
+++ app/assets/javascripts/app/controllers/widget/online_notification.coffee
@@ -113,9 +113,7 @@
       processData: true
     )

-  onShow: =>
-    @updateContent()
-
+  updateHeight: =>
     # set height of notification popover
     notificationsContainer  = $('.js-notificationsContainer')
     heightApp               = $('#app').height()
@@ -130,6 +128,10 @@
       notificationsContainer.removeClass('is-overflowing')

     notificationsContainer.find('.popover-content').css('height', "#{heightPopoverContentNew}px")
+
+  onShow: =>
+    @updateContent()
+    @updateHeight()

     # mark all notifications as read
     notificationsContainer.find('.js-markAllAsRead').on('click', (e) =>
@@ -221,7 +223,7 @@
       row = $(e.target).closest('.activity-entry')
       id = row.data('id')
       App.OnlineNotification.destroy(id)
-      @resetHeight()
+      @updateHeight()
     )

   createContainer: =>
@@ -254,7 +256,3 @@
     @counterUpdate(0)
     @toggle.popover('destroy')

-  resetHeight: ->
-    notificationsContainer = $('.js-notificationsContainer')
-    notificationsContainer.find('.popover-content').css('height', 'auto')
-


no actions for disacarded polls

no actions for disacarded polls

--- vue/src/shared/services/poll_service.coffee
+++ vue/src/shared/services/poll_service.coffee
@@ -13,14 +13,14 @@
     show_results:
       name: 'poll_common_card.show_results'
       canPerform: ->
-        poll.closingAt && !poll.hideResultsUntilClosed && !poll.showResults()
+        !poll.discardedAt && poll.closingAt && !poll.hideResultsUntilClosed && !poll.showResults()
       perform: ->
         poll.pleaseShowResults = true

     hide_results:
       name: 'poll_common_card.hide_results'
       canPerform: ->
-        poll.showResults() && !poll.closedAt && !poll.iHaveVoted()
+        !poll.discardedAt && poll.showResults() && !poll.closedAt && !poll.iHaveVoted()
       perform: ->
         poll.pleaseShowResults = false

@@ -44,7 +44,7 @@
           component: 'AnnouncementHistory'
           props:
             model: poll
-      canPerform: -> true
+      canPerform: -> !poll.discardedAt

     announce_poll:
       icon: 'mdi-send'
@@ -105,7 +105,7 @@
     show_history:
       icon: 'mdi-history'
       name: 'action_dock.show_edits'
-      canPerform: -> poll.edited()
+      canPerform: -> !poll.discardedAt && poll.edited()
       perform: ->
         openModal
           component: 'RevisionHistoryModal'


add support for firefox for outputting the CSS

--- assets/js/views/_css_code_area.coffee
+++ assets/js/views/_css_code_area.coffee
@@ -13,4 +13,5 @@
     getCode: ->
       selectedStyleName = App.request('supportedStyles').findWhere(selected: true).get('name')
       node = _.find document.styleSheets, (v) -> v.ownerNode.id is selectedStyleName
-      _.map(node.rules, (v) -> v.cssText).reverse().join('')
+      rules = if node.rules? then node.rules else node.cssRules
+      _.map(rules, (v) -> v.cssText).reverse().join('')


My own snippet region filtering

My own snippet region filtering

--- app/assets/javascripts/ckeditor/plugins/effective_regions/plugin.js.coffee
+++ app/assets/javascripts/ckeditor/plugins/effective_regions/plugin.js.coffee
@@ -15,7 +15,14 @@
     content = $('
' + instance.getData() + '
')
     content.find("div.#{snippet_id}").replaceWith("[#{snippet_id}]") for snippet_id in snippet_ids

-    {content: content.html(), snippets: snippets}
+    # Process Snippet Only Regions
+    if instance.config.toolbar == 'snippets'
+      content = content.text()
+      content = content.replace(/\]\s+\[/gi, '') if content.indexOf('[snippet_') > -1
+    else
+      content = content.html()
+
+    {content: content, snippets: snippets}

   exec: (editor) ->
     data = {}
@@ -49,8 +56,6 @@
     editor.on 'afterPaste', (evt) -> editor.setData(editor.getData().replace( /<[^<|>]+?>/gi,''))

   initSnippetsRegion: (editor) ->
-    console.log 'snippet'
-
     # Disable wrapping content with

.  This could break plugins.
     editor.config.autoParagraph = false
     editor.setActiveEnterMode(CKEDITOR.ENTER_BR, CKEDITOR.ENTER_BR)
@@ -60,7 +65,7 @@

     # Paste as plain text, but this doesn't work all the way
     editor.config.forcePasteAsPlainText = true
-    editor.on 'afterPaste', (evt) -> editor.setData(editor.getData().replace( /<[^<|>]+?>/gi,''))
+    editor.on 'afterPaste', (evt) -> editor.setData(editor.getData().replace(/<[^<|>]+?>/gi,''))
 }

 Snippets = {


Add Spec T005-A002-B001

Add Spec T005-A002-B001

--- spec/src/coffee/label_category/views/t005_label_category_view_spec.coffee
+++ spec/src/coffee/label_category/views/t005_label_category_view_spec.coffee
@@ -32,4 +32,29 @@
         @root.parse_labels_recursive_func @label.get("name"), @label
         @category_view.$(".childrens .childrens .childrens .name").text().should.equal "C"

+  context "A002: destroy", ->
+    context "B001: has child", ->
+      beforeEach ->
+        @label = new @Label.Models.Label(
+          name: "A/B/C"
+          color: "999999"
+        )
+        @root = new @LabelCategory.Models.LabelCategory(
+          name: "root"
+        )
+        @view = new @LabelCategory.Views.LabelCategoryView(
+          model: @root
+        )
+        @root.parse_labels_recursive_func "1/1", @label
+        @root.parse_labels_recursive_func "1/1/1", @label
+        @root.parse_labels_recursive_func "1/1/2", @label
+        @root.parse_labels_recursive_func "1/1/3", @label
+        @root.parse_labels_recursive_func "1/2", @label

+      it "C001: destroy 1/1", ->
+        @root.children("1").children("1").destroy()
+        @root.children("1").get("name").should.equal "1"
+        @root.children("1").children("1").children("1").get("name").should.equal "1"
+        @root.children("1").children("1").children("2").get("name").should.equal "2"
+        @root.children("1").children("1").children("3").get("name").should.equal "3"
+


Revert "rollbar log first & last entry in feed to debug duplicate entires in feed"

This reverts commit 664986d188edc2dedd70ba9af57194f9efd2e40a.

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -169,47 +169,21 @@
         cache.overview.reverse()  if cache?.overview
         callback null, cache

-  getTimeFromItem: (item) ->
-    # memberbucket data has no serverside model it comes from cache
-    # so it has no meta, that's why we check its date by its overview
-    lastDate = if item.createdAtTimestamps
-      new Date item.createdAtTimestamps.first
-    else
-      new Date item.meta.createdAt
-
-    return lastDate
-
   continueLoadingTeasers:->

     unless isLoading
       if @listController.itemsOrdered.last
-
-        # DEBUG: duplicate entries in activity feed
         lastItemData = @listController.itemsOrdered.last.getData()
-        lastDate = @getTimeFromItem(lastItemData)
-        lastId = lastItemData._id
-
-        firstItemData = @listController.itemsOrdered.first.getData()
-        firstDate = @getTimeFromItem(firstItemData)
-        firstId = firstItemData._id
-
-        timeStamps =
-          top:
-            date:firstDate
-            id:lastId
-          bottom:
-            date:lastDate
-            id:lastId
-          correct:firstDate>lastDate
-
-        _rollbar.push(msg:"duplicate entries debug", timeStamps:timeStamps, level:"debug")
-        # DEBUG: duplicate entries in activity feed
+        # memberbucket data has no serverside model it comes from cache
+        # so it has no meta, that's why we check its date by its overview
+        lastDate = if lastItemData.createdAtTimestamps
+          new Date lastItemData.createdAtTimestamps.first
+        else
+          new Date lastItemData.meta.createdAt
       else
         lastDate = new Date
-        _rollbar.push(msg:"could not find date", date:lastDate)

       lastTimeStamp = lastDate.getTime()
-
       @populateActivity {slug : "before/#{lastTimeStamp}", to: lastTimeStamp}

   teasersLoaded:->


Dirty hack to fix first request error temporarily

--- client/landing/app/MainApp/filetree/controllers/findertreecontroller.coffee
+++ client/landing/app/MainApp/filetree/controllers/findertreecontroller.coffee
@@ -698,7 +698,8 @@
   HELPERS
   ###

-  notification = null
+  notification  = null
+  autoTriedOnce = no

   hideNotification: ->
     notification.destroy() if notification
@@ -714,6 +715,13 @@

     style or= 'error' if details
     duration = if reconnect then 0 else if details then 5000 else 2500
+
+    if not autoTriedOnce and reconnect
+      KD.utils.wait 200, =>
+        @emit 'fs.retry.scheduled'
+        @getSingleton('kiteController')?.channels?.sharedHosting?.cycleChannel?()
+      autoTriedOnce = yes
+      return

     notification = new KDNotificationView
       title     : msg or "Something went wrong"


scale by page height

scale by page height

--- client/apps/edit/components/content/sections/image_collection/index.coffee
+++ client/apps/edit/components/content/sections/image_collection/index.coffee
@@ -43,12 +43,11 @@

   getFillWidthSizes: ->
     containerSize = 860
-    targetHeight = 450
+    targetHeight = window.innerHeight * .7
     if @props.section.get('type') is 'image_set' and @props.section.get('images').length > 3
-      targetHeight = 300
+      targetHeight = 400
     else if @props.section.get('layout') is 'column_width'
       containerSize = 580
-      targetHeight = 550
     return {containerSize: containerSize, targetHeight: targetHeight}

   setProgress: (progress) ->


Removes some JS logging

Removes some JS logging

--- app/assets/javascripts/background_jobs.coffee
+++ app/assets/javascripts/background_jobs.coffee
@@ -5,8 +5,6 @@

   onReady: =>
     this.onTick()
-
-    # @interval ||= window.setInterval this.onTick.bind(this), 5000

   onTick: =>
     clearTimeout(@timeout) if @timeout
@@ -14,7 +12,6 @@

     url = $(@el).parent().data('refresh-url')
     delay = $(@el).data('refresh-delay')
-    console.log "refreshing from #{url}"
     $.ajax(
       url:    url
       global: false
@@ -25,13 +22,10 @@

     delay = $(@el).data('refresh-delay')

-    console.log "scheduling refresh after #{delay}"
     clearTimeout(@timeout) if @timeout
     @timeout = setTimeout(this.onTick.bind(this), delay)
     true

-  element: ->
-
 $(document).ready (new BackgroundJobsUpdater).onReady


Generalize user association for upcoming user instance implementations

--- packages/shrub-user/login.coffee
+++ packages/shrub-user/login.coffee
@@ -41,72 +41,79 @@
         (req, res, next) ->

           # Authorize a user instance.
-          req.authorize(req.body.method, res).bind({}).then((@instance, info) ->
+          promise = req.authorize(
+            req.body.method, res
+          )

-            # Get any associated user.
-            @instance.associatedUser()
+          exports.loginWithInstance(promise, req).then((user) ->

-          ).then((associatedUser) ->
-            self = this
-
-            if associatedUser?
-
-              if req.user?
-
-                # If the user is already logged in and the instance has an
-                # associated user, they're either already logged in with
-                # the instance, or the instance belongs to another user. Throw
-                # a relevant error either way.
-                if req.user.id is associatedUser.id
-
-                  throw errors.instantiate('shrub-user-login-redundant')
-
-                else
-
-                  throw errors.instantiate('shrub-user-login-conflict')
-
-              # If the user isn't already logged in, just return the user
-              # associated with the instance.
-              else
-
-                return associatedUser
-
-            else
-
-              # If the instance isn't already associated with a user, either
-              # target the logged-in user, or if the user isn't already
-              # logged in, target a new user.
-              promise = if req.user?
-
-                Promise.resolve req.user
-
-              else
-
-                orm.collection('shrub-user').create()
-
-              # Associate the instance with the user targeted just above.
-              promise.then (user) ->
-
-                user.instances.add(
-                  model: self.instance.model
-                  modelId: self.instance.id
-                )
-
-                return user
-
-          # Log in the user if not already logged in.
-          ).then((@user) ->
-
-            req.logIn @user unless req.user?
-
-          # Redact the user object for delivery to the client.
-          ).then(->
-
-            @user.redactObject 'shrub-user', @user
+            # Redact the user for sending over the wire.
+            user.redactObject 'shrub-user', user

           # End the request, sending the redacted user.
           ).then((redactedUser) -> res.end redactedUser).catch next

+
       ]

     return routes
+
+exports.loginWithInstance = (promise, req) ->
+
+  promise.then((instance, info) ->
+
+    # Get any associated user.
+    instance.associatedUser()
+
+  ).then((associatedUser) ->
+
+    if associatedUser?
+
+      if req.user?
+
+        # If the user is already logged in and the instance has an
+        # associated user, they're either already logged in with
+        # the instance, or the instance belongs to another user. Throw
+        # a relevant error either way.
+        if req.user.id is associatedUser.id
+
+          throw errors.instantiate('shrub-user-login-redundant')
+
+        else
+
+          throw errors.instantiate('shrub-user-login-conflict')
+
+      # If the user isn't already logged in, just return the user
+      # associated with the instance.
+      else
+
+        return associatedUser
+
+    else
+
+      # If the instance isn't already associated with a user, either
+      # target the logged-in user, or if the user isn't already
+      # logged in, target a new user.
+      promise = if req.user?
+
+        Promise.resolve req.user
+
+      else
+
+        orm.collection('shrub-user').create()
+
+      # Associate the instance with the user targeted just above.
+      promise.then (user) ->
+
+        user.instances.add(
+          model: instance.model
+          modelId: instance.id
+        )
+
+        user.save().then -> return user
+
+  # Log in the user if not already logged in.
+  ).then (user) ->
+
+    req.logIn user unless req.user?
+    return user


landing: minor: unused partial cleaned

landing: minor: unused partial cleaned

--- client/landing/site.landing/coffee/team/forms/teamjoinbyloginform.coffee
+++ client/landing/site.landing/coffee/team/forms/teamjoinbyloginform.coffee
@@ -66,9 +66,6 @@
     {{> @username}}
     {{> @password}}
     {{> @tfcode}}
-

-      Forgot your password?
-

     {{> @buttonLink}}
     {{> @button}}


since this view now is dedicated to showing the popover, if no popover is defined, this is an error

--- core/app/backbone/factlink/popover_view.coffee
+++ core/app/backbone/factlink/popover_view.coffee
@@ -1,7 +1,7 @@
 class Backbone.Factlink.PopoverView extends Backbone.Marionette.ItemView
   constructor: (options) ->
     super(options)
-    return  unless _.isArray(@popover)
+    throw "PopoverView needs a popover property"  unless _.isArray(@popover)
     _.each @popover, @bindPopover, this
     @on "render", @realOnRender, this

@@ -14,7 +14,6 @@
     @$el.on "click.popover_menu", obj.popoverSelector, blockMethod

   realOnRender: ->
-    return  unless _.isArray(@popover)
     _.each @popover, @onRenderPopover, this

   onRenderPopover: (obj, key) ->


bugfix, call the function, don't return it

--- core/app/backbone/views/activities_view.coffee
+++ core/app/backbone/views/activities_view.coffee
@@ -41,7 +41,7 @@
     appendTo.collection.add(model);

   beforeClose: ->
-    childView.close for childView in @childViews
+    childView.close() for childView in @childViews

   appendHtml: (collectionView, childView) ->
     childView.render()


parentheses, they matter

parentheses, they matter

--- core/app/backbone/views/activities_view.coffee
+++ core/app/backbone/views/activities_view.coffee
@@ -13,7 +13,7 @@

   render: () ->
     super arguments
-    this.renderChildren
+    this.renderChildren()

   renderChildren: () ->
     this.$('.list').html('')


bokehjs: plot can be resized with more control over whether to use width and/or height

--- bokehjs/src/coffee/common/plot.coffee
+++ bokehjs/src/coffee/common/plot.coffee
@@ -359,6 +359,10 @@
     # @$el.find('canvas').attr('data-hash', ctx.hash());

   resize: () =>
+    @resize_width_height(true, false)
+
+  resize_width_height: (use_width, use_height) =>
+    # Resize plot based on available width and/or height
     canvas_height = @canvas.get('height')
     canvas_width = @canvas.get('width')
     # Calculating this each time means that we play nicely with resize tool
@@ -369,14 +373,29 @@
     # user-configurable in the future, as it may not be the right number
     # if people set a large border on their plots, for example.
     min_size = @mget('min_size')
-    new_width = Math.max(@.el.clientWidth, min_size)
-    new_height = parseInt(new_width / aspect_ratio)
-
-    if new_height < min_size
-      new_height = 100
-      new_width = new_height * aspect_ratio  # Preserves the aspect ratio
-
-    @canvas._set_dims([new_width, new_height])
+
+    if use_width
+      new_width1 = Math.max(@.el.clientWidth, min_size)
+      new_height1 = parseInt(new_width1 / aspect_ratio)
+    if use_height
+      new_height2 = Math.max(@.el.parentNode.clientHeight - 50, min_size)  # -50 for x ticks
+      new_width2 = parseInt(new_height2 * aspect_ratio)
+
+    if (not use_height) and (not use_width)
+      # remain same size
+    else if use_height and use_width
+      if new_width2 < new_width1 and new_width2 > min_size
+        @canvas._set_dims([new_width2, new_height2])
+      else
+        @canvas._set_dims([new_width1, new_height1])
+    else if use_height and new_width2 > min_size
+      @canvas._set_dims([new_width2, new_height2])
+    else if use_width and new_width1 > min_size
+      @canvas._set_dims([new_width1, new_height1])
+    else if aspect_ratio < 1
+      @canvas._set_dims([min_size, min_size / aspect_ratio])
+    else
+      @canvas._set_dims([min_size * aspect_ratio, min_size])
     return null

   _render_levels: (ctx, levels, clip_region) ->


Changed the auto save to 5seconds

Changed the auto save to 5seconds

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -293,7 +293,7 @@
     #TODO: indicators, error handlers?

   triggerAutosave = (e) ->
-    window.autosave_timer ||= setTimeout( autosave, 500 )
+    window.autosave_timer ||= setTimeout( autosave, 5000 )

   $(document).debounce "change", ".js-trigger-autosave", triggerAutosave, 50
   $(document).debounce "keyup", "input[type='text'].js-trigger-autosave", triggerAutosave, 50


Fix indenting.

Fix indenting.

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -17,6 +17,6 @@
 #= require dot_ledger

 _.mixin compactObject: (object) ->
-    _.each object, (v, k) ->
-      delete object[k] if _.isEmpty(object[k])
-    object
+  _.each object, (v, k) ->
+    delete object[k] if _.isEmpty(object[k])
+  object


removing explicit require for web-animations

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -1,3 +1,2 @@
 #= require jquery
 #= require_self
-#= web-animations-js/web-animations-next-lite.min.js


remove debug

remove debug

--- app/assets/javascripts/s3_form_for.js.coffee
+++ app/assets/javascripts/s3_form_for.js.coffee
@@ -69,9 +69,7 @@
       # Fallback; just use a placeholder
       $('#upload_thumbnail').show().attr('src', assetPath('media/mrx-placeholder-120x90.png'))

-  console.log('1111111111111111111111111111111111111111')
   if settings.click_submit_target
-    console.log('22222222222222222222222222222222222222')
     settings.click_submit_target.click =>
       validation_form = true
       $.each $uploadForm.find(settings.disable_fields_after_submit), (index, item) =>
@@ -91,18 +89,12 @@
         $.each $uploadForm.find(settings.disable_fields_after_submit), (index, item) =>
           $(item).removeAttr('disabled')

-      console.log('ccccccccccccccccccccccccc')
-      console.log(validation_form)
       if  validation_form
-#        form.submit() for form in forms_for_submit
         $(forms_for_submit).submit()
-        #$('#form_submit').click()

       if settings.allow_send_form_without_file && $uploadForm.find('#file_name_for_upload').text().length == 0
-        console.log('444444444444444444444444444444444444444444')
         true
       else
-        console.log('55555555555555555555555555555555555555')
         false

   setUploadForm = ->
@@ -124,11 +116,9 @@
             else
               forms_for_submit = [data]
           else
-            console.log('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz')
             data.submit()

       send: (e, data) =>
-        console.log('send')
         $uploadForm.trigger('show_cancel_button')
         $(selectors.cancel_upload).on 'click', (e) =>
           data.xhr().abort()
@@ -138,13 +128,12 @@
           return false

       start: (e) ->
-        console.log('start')
         $($uploadForm).find(".upload_picking").hide()
-        $($uploadForm).find(".upload_uploading").show().removeClass "hidden"
+        $($uploadForm).find(".upload_uploading").removeClass("hidden")
+        $($uploadForm).find(".upload_uploading").show()
         $("#upload_cancel_link").addClass "hidden"

       progress: (e, data) ->
-        console.log('progress')
         if data.context
           progress = parseInt(data.loaded / data.total * 100, 10)
           data.context.find('.bar').css('width', progress + '%')
@@ -186,7 +175,6 @@
         $uploadForm.trigger("s3_uploads_complete", [content]) unless current_files.length

       fail: (e, data) ->
-        console.log('fail')
         content = build_content_object $uploadForm, data.files[0], data.result
         content.error_thrown = data.errorThrown

@@ -194,32 +182,23 @@
         $uploadForm.trigger("s3_upload_failed", [content])

       formData: (form) ->
-        console.log('formData 1')

         data = form.serializeArray()
-        console.log('form data 1.2')
-        console.log(data[0])
         valid_data = []
         for i in data
-          console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
           if $.inArray(i.name, settings.used_fields) == -1
             console.log('1.2.1')
           else
-            console.log('1.2.2')
             valid_data.push(i)

-        console.log('1.2.3')
         data = valid_data

-        console.log('form data 1.3')
-        console.log(data)
         fileType = ""
         if "type" of @files[0]
           fileType = @files[0].type
         data.push
           name: "content-type"
           value: fileType
-        console.log('form datat 2')
         key = $uploadForm.data("key")
           .replace('{timestamp}', new Date().getTime())
           .replace('{unique_id}', @files[0].unique_id)
@@ -236,21 +215,17 @@
         # replace 'key' field to submit form
         unless 'FormData' of window
           $uploadForm.find("input[name='key']").val(settings.path + key)
-        console.log(data)
         data

     $uploadForm.on 'show_cancel_button', ->
-      console.log('show_cancel_button')
       $(selectors.submit).hide()
       $(selectors.cancel_upload).removeClass('hide')

     $uploadForm.on 'enable_all_field', ->
-      console.log('enable_all_field')
       $(selectors.s3_form).find(selectors.disable_fields).removeAttr('disabled')

     $uploadForm.on 'reset_s3_uploder', ->
-      console.log('reset_s3_uploder')
       $('.upload_uploading').hide()
       $('.upload_picking').show()
       $('.progress-bar').removeAttr('style')
@@ -261,37 +236,25 @@
       $(selectors.cancel_upload).addClass('hide')

     $uploadForm.on 'show_cancel_button', ->
-      console.log('show_cancel_button')
       $(selectors.submit).hide()
       $(selectors.cancel_upload).removeClass('hide')

     $uploadForm.on 's3_upload_complete', (e, content) =>
-      console.log('s3_upload_complete')
       $(selectors.s3_form).find(selectors.disable_fields).removeAttr('disabled')
-      console.log('s3_upload_complete 1')
       $uploadForm.find(".upload_uploading").hide()
-      console.log('s3_upload_complete 2')
       $uploadForm.find(".upload_finished").show().removeClass "hidden"
-      console.log('s3_upload_complete 3')
       $(selectors.submit).removeClass("disabled").prop "disabled", false
-      console.log('s3_upload_complete 4')
       $("#upload_skip_link").removeClass "hidden"
-      console.log('s3_upload_complete 5')
       $("#upload_s3_path").val(content.filepath)
-      console.log('s3_upload_complete 6')
-      console.log('start submit')
-      console.log(selectors.s3_form)
-      console.log($("#{selectors.s3_form} #form_submit").length)
-      $("#{selectors.s3_form} #form_submit").submit()
+#      $("#{selectors.s3_form} #form_submit").submit()
 #      Hack for IE9
-#      $("#{selectors.s3_form} #form_submit").submit (e) ->
-#        e.preventDefault();
-#        $(this).closest("form").submit()
+      $("#{selectors.s3_form} #form_submit").submit (e) ->
+        e.preventDefault();
+        $(this).closest("form").submit()

       $(selectors.s3_form).trigger('submit')

   build_content_object = ($uploadForm, file, result) ->
-    console.log('zzzzzzzzzzzzzzzzzzzz')
     content = {}
     if result # Use the S3 response to set the URL to avoid character encodings bugs
       content.url            = $(result).find("Location").text()
@@ -311,16 +274,13 @@
     content

   has_relativePath = (file) ->
-    console.log('xxxxxxxxxxxxxxxxxxxxx')
     file.relativePath || file.webkitRelativePath

   build_relativePath = (file) ->
-    console.log('cccccccccccccccccccccc')
     file.relativePath || (file.webkitRelativePath.split("/")[0..-2].join("/") + "/" if file.webkitRelativePath)

   #public methods
   @initialize = ->
-    console.log('dddddddddddddddddddddddddddddddddd')
     # Save key for IE9 Fix
     $uploadForm.data("key", $uploadForm.find("input[name='key']").val())


Add version information to docs.

Add version information to docs.

--- angular-inview.coffee
+++ angular-inview.coffee
@@ -1,6 +1,8 @@
 # #Angular-Inview
 # - Author: [Nicola Peduzzi](https://github.com/thenikso)
 # - Repository: https://github.com/thenikso/angular-inview
+# - Install with: `bower install angular-inview`
+# - Version: **1.3.4**

 'use strict'


useHtml5routing

useHtml5routing

Add AngularJS configuration so that, whenever it is allowed by browsers, the # (hashtag) in URL is avoided, without losing capability of client-side routing.

--- app/assets/javascripts/app.coffee
+++ app/assets/javascripts/app.coffee
@@ -22,6 +22,8 @@
                 templateUrl: '/assets/partials/create.html'
             })
             .otherwise({redirectTo: '/'})
+    .config ($locationProvider) ->
+        $locationProvider.html5Mode(true)

 @commonModule = angular.module('myApp.common', [])
 @controllersModule = angular.module('myApp.controllers', [])


Add support for Clean and Idris in fenced code blocks

--- grammars/fixtures/fenced-code.cson
+++ grammars/fixtures/fenced-code.cson
@@ -23,6 +23,7 @@
   { pattern:'ass' }
   { pattern:'coffee|coffeescript|coffee-script', include:'source.coffee' }
   { pattern:'c' }
+  { pattern:'clean' }
   { pattern:'clojure' }
   { pattern:'cpp|c\\+\\+', include:'source.cpp' }
   { pattern:'cr|crystal', include:'source.crystal'}
@@ -39,6 +40,7 @@
   { pattern:'go|golang', include:'source.go' }
   { pattern:'haskell' }
   { pattern:'html', include:'text.html.basic' }
+  { pattern:'idris' }
   { pattern:'java' }
   { pattern:'javascript|js|jsx', include:'source.js' }
   { pattern:'json|har', include:'source.json' }


Write electron-packager config

Write electron-packager config

--- gulp_config/common/electron.coffee
+++ gulp_config/common/electron.coffee
@@ -10,7 +10,7 @@
     version     : "0.33.4"

     out                 : "publish/"
-    icon                : null
+    icon                : "icon/nco.icns"
     "app-bundle-id"     : null
     "app-version"       : pjson.version
     "helper-bundle-id"  : null
@@ -18,12 +18,12 @@
     prune               : true
     overwrite           : true
     asar                : true
-    "sign"              : null
+    "sign"              : false
     "version-string"    :
         CompanyName         : pjson.author
-        LegalCopyright      : null
-        FileDescription     : null
-        OriginalFilename    : null
+        LegalCopyright      : "Copyright (c) 2015 Ragg"
+        FileDescription     : "Nsen用コメントビューワ Nco"
+        OriginalFilename    : "nco.exe"
         FileVersion         : pjson.version
         ProductVersion      : pjson.version
         ProductName         : pjson.productName


Adds 3 seconds (block times) to expiration time #296

--- dl/src/chain/config.coffee
+++ dl/src/chain/config.coffee
@@ -1,3 +1,3 @@
 module.exports =
     address_prefix: "GPH"
-    expire_in_secs: 12
+    expire_in_secs: 15


Log requests that don't have a route property

The v1 history service has its routes set up via swagger-tools, which
doesn't write a route property on the request. This prevents us to send
request metrics based on the route, but we can still log the request.

--- http.coffee
+++ http.coffee
@@ -18,36 +18,36 @@
 				Metrics.timing("http_request", responseTimeMs, null, {method:req.method, status_code: res.statusCode, path:routePath})
 				if requestSize
 					Metrics.summary("http_request_size_bytes", requestSize, {method:req.method, status_code: res.statusCode, path:routePath})
-				remoteIp = req.ip || req.socket?.socket?.remoteAddress || req.socket?.remoteAddress
-				reqUrl = req.originalUrl || req.url
-				referrer = req.headers['referer'] || req.headers['referrer']
-				if STACKDRIVER_LOGGING
-					info =
-						httpRequest:
-							requestMethod: req.method
-							requestUrl: reqUrl
-							requestSize: requestSize
-							status: res.statusCode
-							responseSize: res._headers?["content-length"]
-							userAgent: req.headers["user-agent"]
-							remoteIp: remoteIp
-							referer: referrer
-							latency:
-								seconds: responseTime[0]
-								nanos: responseTime[1]
-							protocol: req.protocol
-				else
-					info =
-						req:
-							url: reqUrl
-							method: req.method
-							referrer: referrer
-							"remote-addr": remoteIp
-							"user-agent": req.headers["user-agent"]
-							"content-length": req.headers["content-length"]
-						res:
-							"content-length": res._headers?["content-length"]
-							statusCode: res.statusCode
-						"response-time": responseTimeMs
-				logger.info(info, "%s %s", req.method, reqUrl)
+			remoteIp = req.ip || req.socket?.socket?.remoteAddress || req.socket?.remoteAddress
+			reqUrl = req.originalUrl || req.url
+			referrer = req.headers['referer'] || req.headers['referrer']
+			if STACKDRIVER_LOGGING
+				info =
+					httpRequest:
+						requestMethod: req.method
+						requestUrl: reqUrl
+						requestSize: requestSize
+						status: res.statusCode
+						responseSize: res._headers?["content-length"]
+						userAgent: req.headers["user-agent"]
+						remoteIp: remoteIp
+						referer: referrer
+						latency:
+							seconds: responseTime[0]
+							nanos: responseTime[1]
+						protocol: req.protocol
+			else
+				info =
+					req:
+						url: reqUrl
+						method: req.method
+						referrer: referrer
+						"remote-addr": remoteIp
+						"user-agent": req.headers["user-agent"]
+						"content-length": req.headers["content-length"]
+					res:
+						"content-length": res._headers?["content-length"]
+						statusCode: res.statusCode
+					"response-time": responseTimeMs
+			logger.info(info, "%s %s", req.method, reqUrl)
 		next()


AppManager: minor, removed unnecessary logs

--- client/Main/ApplicationManager.coffee
+++ client/Main/ApplicationManager.coffee
@@ -30,18 +30,6 @@
         safeToUnload = no
         break
       return safeToUnload ? yes
-
-    # @on 'AppIsBeingShown', @bound "setMissingRoute"
-
-  # setMissingRoute:(appController, appView, appOptions)->
-  #   router       = KD.getSingleton('router')
-  #   {entryPoint} = KD.config
-
-  #   route = appOptions.route.slug.replace /:\w+.?\//g, ''
-  #   comparedRoute = router.getCurrentPath().split('?').first
-  #   if route isnt comparedRoute
-  #     missingRoute = appController.getOption('initialRoute') or route
-  #     router.handleRoute missingRoute, { suppressListeners : yes, entryPoint }

   isAppInternal : (name='')->
     return KD.config.apps[name] and (name not in Object.keys KD.appClasses)
@@ -109,21 +97,6 @@
         switch appOptions.openWith
           when "lastActive" then do defaultCallback
           when "prompt"     then do defaultCallback
-          # when "prompt"
-          #   log "prompting"
-          #   if @appControllers[name]?.instances.length > 1
-          #     log "more than one, namely", @appControllers[name].instances.length
-          #     @createPromptModal appOptions, (appInstanceIndex, openNew)=>
-          #       if typeof appInstanceIndex is "number"
-          #         appInstance = @appControllers[name].instances[appInstanceIndex]
-          #         # user selected appInstance to open
-          #         @show appInstance, callback
-          #       else if openNew
-          #         # user wants to open a fresh instance
-          #         @create name, appParams, callback
-          #       else
-          #         warn "user cancelled app to open"
-          #  else do defaultCallback

       else do defaultCallback


client/app: remove selectText util

client/app: remove selectText util

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

--- client/app/lib/util/selectText.coffee
+++ client/app/lib/util/selectText.coffee
@@ -1,12 +0,0 @@
-module.exports = (element) ->
-
-  if document.body.createTextRange
-    range = document.body.createTextRange()
-    range.moveToElementText element
-    range.select()
-  else if window.getSelection
-    selection = window.getSelection()
-    range = document.createRange()
-    range.selectNodeContents element
-    selection.removeAllRanges()
-    selection.addRange range


Fix display of all day events in list, ref #391

--- client/app/models/scheduleitem.coffee
+++ client/app/models/scheduleitem.coffee
@@ -235,6 +235,10 @@

             # Compute the difference to know how many events to create.
             difference = endDate.diff(startDate, 'days')
+
+            # If event is all day, the end date is next day
+            if @isAllDay()
+                difference--

             # Create one all-day event for each day.
             fakeEvents = []


display current week by default

display current week by default

--- client/app/views/calendar_view.coffee
+++ client/app/views/calendar_view.coffee
@@ -32,12 +32,11 @@

         @cal = @$ '#alarms'
         @view = @options.view
-
         # set default date
         currDate = moment()
         currDate.year(@options.year)   if @options.year?
         currDate.month(@options.month) if @options.month?
-        currDate.date(@options.date)   if @options.date?
+        currDate.date(@options.date)   if @options.date? and @view isnt 'month'

         @cal.fullCalendar
             lang: window.locale


fix wrong assignment in calendar view

fix wrong assignment in calendar view

--- client/app/views/calendar_view.coffee
+++ client/app/views/calendar_view.coffee
@@ -14,7 +14,7 @@
     id: 'view-container'
     template: require('./templates/calendarview')

-    initialize: (options) ->
+    initialize: (@options) ->
         @alarmCollection = @model.alarms
         @listenTo @alarmCollection, 'add'  , @refresh
         @listenTo @alarmCollection, 'reset', @refresh


Refactored: proper coffee syntax

Refactored: proper coffee syntax

--- client/app/views/new_task_form.coffee
+++ client/app/views/new_task_form.coffee
@@ -55,8 +55,7 @@
             @taskCreationHandler(event) if(keyCode is 13) # enter key

         @newTaskFormInput.focus (event) =>
-            if !@hasUserTyped
-                @newTaskFormInput.val("")
+            @newTaskFormInput.val("") unless @hasUserTyped

         @newTaskFormInput.focusout (event) =>
             if @newTaskFormInput.val() is ""


ChatView: Handle add people link on settings pane.

--- client/ide/views/chat/chatview.coffee
+++ client/ide/views/chat/chatview.coffee
@@ -81,6 +81,11 @@
     ]

     @settingsPane.on 'SessionStarted', @bound 'showChatPane'
+    @settingsPane.on 'AddNewParticipantRequested', =>
+      @showChatPane()
+
+      KD.utils.wait 500, =>
+        @chatPane.showAutoCompleteInput()

     @emit 'ready'


do not clean docs dir on clean either

do not clean docs dir on clean either

--- gruntConfig.coffee
+++ gruntConfig.coffee
@@ -29,10 +29,6 @@
     clean:
       distrib: ["dist/#{pkg.name}.*"]

-      # these are the things created by bumble-docs/scripts
-      docs: ["docs/**/*"]
-
-
     cssmin:
       options:
         shorthandCompacting: false,


feat: even better error logging for RSVP

--- app/config/app.coffee
+++ app/config/app.coffee
@@ -10,9 +10,13 @@
     LOG_ACTIVE_GENERATION:          true

   Ember.RSVP.configure 'onerror', (error) ->
-    Ember.Logger.error(error?.message or 'No error message for Promise')
-    Ember.Logger.error(error?.stack)
-    Ember.Logger.error(error?.object)
+    if Ember.typeOf(error) is 'object'
+      message = error?.message or 'No error message'
+      Ember.Logger.error("RSVP Error: #{message}")
+      Ember.Logger.error(error?.stack)
+      Ember.Logger.error(error?.object)
+    else
+      Ember.Logger.debug 'RSVP Error', error

   # Log view render times to the console
   Ember.STRUCTURED_PROFILE = true


fix vessel editing

fix vessel editing

--- imports/api/vessels/methods.coffee
+++ imports/api/vessels/methods.coffee
@@ -119,7 +119,10 @@

 			newVessel.harborGroup = project.harborGroup

-			Vessels.schema.clean newVessel, mutate: true
+			Vessels.schema.clean newVessel,
+				mutate: true
+				removeEmptyStrings: false
+
 			Vessels.schema.validate newVessel

 			Meteor.call 'validateVesselInput',


auth fix when logging in

auth fix when logging in

--- app/lib/router.coffee
+++ app/lib/router.coffee
@@ -67,9 +67,16 @@
         @render @params.action

   Router.onBeforeAction( (pause) ->
-    if !Meteor.user()
+    if Meteor.loggingIn()
+      console.log("Logging in")
+
+      pause()
+    else if not Meteor.userId()
       console.log("Not logged in")
       #@render 'login'
       Router.go 'login'
       pause()
+    else
+      console.log("Logged in as:")
+      console.log(Meteor.user())
   , {except: 'login'})


Send the project ID, duh

Send the project ID, duh

--- app/lib/tutorial.cjsx
+++ app/lib/tutorial.cjsx
@@ -56,7 +56,7 @@
                       user.get('project_preferences', project_id: project.id).then ([projectPreferences]) =>
                         projectPreferences ?= apiClient.type('project_preferences').create({
                           links: {
-                            project: '231'
+                            project: project.id
                           },
                           preferences: {}
                         })


Extracts keyify fn

Extracts keyify fn

--- collections/recent_connections.coffee
+++ collections/recent_connections.coffee
@@ -11,6 +11,9 @@
     -(model.get '__timestamp__')

   localStorage: new Backbone.LocalStorage RecentConnections
+
+  @keyify: (model) ->
+    "selected:#{model.get 'base_class'}:#{model.id}"

   shove: (model) ->
     timestamp = __timestamp__: new Date().getTime()


no ms notifications in summary mode - closes #95

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -70,7 +70,7 @@
       @notify translate 'classifier.rightPanel.artifactDoneScreen' if @state is 'artifactTool' and @doneButton.prop('disabled')

     'click .marking-surface': ->
-      @notify translate 'classifier.rightPanel.whatKindScreen' if @state is 'whatKind'
+      @notify translate 'classifier.rightPanel.whatKindScreen' if @state is 'whatKind' and !@summaryImageContainer.is(':visible')

     'click .channel-cycler': ->
       if @cycling and !@tutorial.el.hasClass "open"


calculate intersection-over-union of light curve regions

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -545,20 +545,47 @@

   evaluateMarks: ->
     for mark in @canvasGraph.marks.all
-      # console.log 'mark: ', mark
+      console.log "mark: [#{mark.dataXMinRel}, #{mark.dataXMaxRel}]"
       boundL = mark.dataXMinRel
       boundR = mark.dataXMaxRel
       for transit in @known_transits
         transitL = transit[0]
         transitR = transit[1]
-        @intersectionOverUnion(boundL, boundR, transitL, transitR)
-        # console.log 'transit: ', transit[0], transit[1]
+        console.log "     transit: [#{transit[0]}, #{transit[1]}]"
+        console.log "INTERSECTION OVER UNION: ", @intersectionOverUnion(boundL, boundR, transitL, transitR)

   intersectionOverUnion: (aL, aR, bL, bR) ->
-    return
-
-  segmentOverlap: ->
-    return
+
+    console.log """
+      aL = #{aL}
+      aR = #{aR}
+      bL = #{bL}
+      bR = #{aR}
+    """
+
+    d = 0.001 # threshold
+    cL = Math.max(aL, bL)
+    cR = Math.min(aR, bR)
+    lengthA = aR-aL
+    lengthB = bR-bL
+
+    if cL > cR - d
+      console.log 'NO OVERLAP'
+      lengthC = 0 # no overlap
+    else
+      lengthC = cR-cL
+
+
+    # console.log """
+    #   OVERLAP = [#{cL},#{cR}]
+    #   lengthA = #{lengthA}
+    #   lengthB = #{lengthB}
+    #   lengthC = #{lengthC}
+    #   """
+    return lengthC/(lengthA+lengthB-lengthC)
+
+  # segmentOverlap: ->
+  #   return

   displayKnownTransits: ->


display tutorial on first visit

display tutorial on first visit

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -211,10 +211,10 @@
         @courseOptionIsChecked = true
         # supplementalOption = false

-    # # handle first-time users
-    # if +User.current?.preferences.planet_hunter.count is 0 or not User.current?
-    #   console.log 'First-time user. Loading tutorial...'
-    #   @onClickTutorial()
+    # handle first-time users
+    if +User.current?.preferences.planet_hunter.count is 0 or not User.current?
+      console.log 'First-time user. Loading tutorial...'
+      @onClickTutorial()

     User.current?.setPreference 'supplemental_option', supplementalOption


Silly bugfix

Silly bugfix

--- app/controllers/navigation.coffee
+++ app/controllers/navigation.coffee
@@ -9,7 +9,7 @@
     super

   navTo: ({ originalEvent: e }) ->
-    path = $(e.target).data 'nav'
+    path = $(e.target).closest('a').data 'nav'
     e.preventDefault()
     @navigate path


Partial completion of typeahead

Partial completion of typeahead

--- app/js/controllers/console.coffee
+++ app/js/controllers/console.coffee
@@ -1,5 +1,8 @@
 angular.module("app").controller "ConsoleController", ($scope, $location, Wallet) ->
 	$scope.outputs=[]
+	#  here for testing typeahead. remove later
+	$scope.states = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Dakota', 'North Carolina', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming']
+
 	$scope.submit = ->
 		Wallet.execute_command_line($scope.command).then (output) ->
 			$scope.outputs.unshift(output)


fix scroll_end bug

fix scroll_end bug

--- app/plugins/list/scrollend.coffee
+++ app/plugins/list/scrollend.coffee
@@ -20,7 +20,7 @@
     @_prev_top = @scroll_object.scrollTop()

     @enable() unless @list.options.scroll_end is false
-    @list.on 'update', @scroll_listener(), @, (e) => (e.data.type is 'item_removed' or e.data.type is 'load')
+    @list.on 'update', @scroll_listener(), @, (e) => (@enabled and (e.data.type is 'item_removed' or e.data.type is 'load'))
     @

   enable: () ->


Adding `@` symbol as not part of username anymore.

--- app/routers/get-last-tweet.coffee
+++ app/routers/get-last-tweet.coffee
@@ -14,7 +14,7 @@
       data = $last.data()
       tweet = $last.find('.js-tweet-text').html().trim()
       fullName = data.name
-      userName = data.screenName
+      userName = '@' + data.screenName
       timeStamp = $last.find('.js-short-timestamp').html().trim()
       avatarURL = $(".js-action-profile-avatar").attr("src")
       isRetweeted = data.retweeter ? $last.find('.js-retweet-text').text().trim()


fixes follower count

fixes follower count

--- apps/channel/client/channel_path_view.coffee
+++ apps/channel/client/channel_path_view.coffee
@@ -34,7 +34,7 @@
     super

   updateFollowerCount: ->
-    @$('.js-channel-follower-count').html " #{@channel.get('follower_count')} Followers"
+    @$('.js-channel-follower-count').html " #{@model.get('follower_count')} Followers"

   toggleSettings: (e)->
     @$('.metadata--container').toggleClass 'settings-is-active'


update specs

update specs

--- apps/fair/components/browse/test/view.coffee
+++ apps/fair/components/browse/test/view.coffee
@@ -89,6 +89,6 @@

     it 'renders the gene name title and capitalized without trailing numbers', ->
       @view.artworkParams.set related_gene: 'contemporary-abstract-photography-1'
-      @view.$el.html().should.containEql 'Contemporary Abstract Photography'
+      @view.$el.html().should.containEql 'Contemporary abstract photography'
       @view.artworkParams.set related_gene: '20th-century-design'
-      @view.$el.html().should.containEql '20th Century Design'
+      @view.$el.html().should.containEql '20th century design'


prevent the shutdown method from unnecessary calls

--- platforms/node/lib/Platform.coffee
+++ platforms/node/lib/Platform.coffee
@@ -227,8 +227,7 @@
             process.on 'SIGINT', ()=> shutdown()
             process.on 'SIGTERM', ()=> shutdown()
             process.on 'exit', ()=>
-                exited = true
-                shutdown()
+                shutdown() if !exited && exited = true

             ##
             ## async return! ctx is not booted yet...


fixing error concerning already set headers

--- app/middleware.coffee
+++ app/middleware.coffee
@@ -180,7 +180,7 @@
                 return renderError req, res, 500, { err } if err
                 req.setter_boulders = setter_boulders; next()
     else
-        res.redirect "/login"; next()
+        res.redirect "/login"

 exports.createBoulder = (req, res, next) ->


removing console.log commands from tagging system

--- app/coffee/tag.coffee
+++ app/coffee/tag.coffee
@@ -146,8 +146,6 @@
   removeTag: (tagId) ->
     #log tagId
     tagViewToRemove = @findByTagId(tagId)
-    console.log(tagViewToRemove)
-    console.log(@tagViews.length)
     tagViewToRemove.remove()
     @tagViews.remove(tagViewToRemove)

@@ -173,15 +171,10 @@

   removeTag: ($tag) ->
     tagName = $tag.find('.tagContent').text()
-    console.log(tagName)

     new_tags = (tag for tag in @tagViews when tag.tag.name != tagName)
-    console.log(new_tags.length)
-    console.log(@tagViews.length)
-    console.log(@tagViews)
-    console.log(new_tags)
-    #@tagViews.remove(tagViewToRemove)
+
     @tagViews = new_tags
     $tag.css('backgroundColor', '#ff0000')
     $tag.fadeOut('fast', =>
@@ -239,7 +232,6 @@
   # ask for ajax stuff
   constructor: (tag) ->
     super(tag)
-    console.log("SharingTagView Init")
   create: ->
     @tag.create().then (response)=>
       @setId(response.what_id)


Site - Update Calbug intro text.

Site - Update Calbug intro text.

--- app/lib/groups.coffee
+++ app/lib/groups.coffee
@@ -34,7 +34,7 @@
     "name":"Calbug"
     "categories":["Insects","Spiders"]
     "metadata":
-      "description":"Calbug is an effort to digitize terrestrial arthropod specimen records with a focus on California. Using data from specimens the impacts of climate change and habitat modification will be examined. The project is a collaboration of eight entomological collections in California to digitize museum specimens and thus allow assessment of how species distributions have responded to environmental change. It makes use of a long history of entomological collecting in the region to develop a database of approximately 1 million georeferenced specimens collected over the last century."
+      "description":"Calbug is an effort to digitize terrestrial arthropod-specimen records, with a focus on California. The project represents a collaboration among eight entomological collections in California to capture data from specimen labels. The Calbug Science Team will then use the collection data to assess how arthropods have responded to climate change and habitat modification. Drawing from over a century of insect collecting in the region, our goal is to develop a database of over 1 million geographically referenced specimens."
       "snippet": "Calbug digitizes collections of pinned insect specimens drawn from eight of California's major institutions, showcasing the stunning diversity of insect life in our world."
       "extra_template": require "views/archives/calbug"
       "difficulty":"Easy"


Update webgrabplus-siteini.cson

Update webgrabplus-siteini.cson


fixed default refreshFrequency

fixed default refreshFrequency

--- reminders.widget/reminders.coffee
+++ reminders.widget/reminders.coffee
@@ -14,7 +14,7 @@
 # For American's and such. Either true or false
   monthBeforeDay: false
 # Set the refresh frequency (milliseconds).
-  refreshFrequency: '60s'
+  refreshFrequency: '6000'
 #############################

 #############################
@@ -176,4 +176,3 @@
 #############################
 # CSS styling for the widget
 # style: null
-


Fixed broken track pages for guests.

Fixed broken track pages for guests.

--- resources/assets/scripts/app/services/playlists.coffee
+++ resources/assets/scripts/app/services/playlists.coffee
@@ -182,7 +182,7 @@
             isPlaylistPinned: (id) ->
                 _.find(self.pinnedPlaylists, (p) -> `p.id == id`) != undefined

-            refreshOwned: (force = false, slug = window.pfm.auth.user.slug) ->
+            refreshOwned: (force = false, slug = window.pfm.auth.user?.slug) ->
                 return playlistDef if !force && playlistDef

                 playlistDef = new $.Deferred()


:checkered_flag: Use f3/shift-f3 for find next/previous

Closes atom/go-to-line#8

--- keymaps/find-and-replace.cson
+++ keymaps/find-and-replace.cson
@@ -15,17 +15,10 @@
   'cmd-d': 'find-and-replace:select-next'
   'cmd-e': 'find-and-replace:use-selection-as-find-pattern'

-'.platform-win32 .editor':
-  'ctrl-g': 'find-and-replace:find-next'
-  'ctrl-G': 'find-and-replace:find-previous'
-  'ctrl-d': 'find-and-replace:select-next'
-  'ctrl-e': 'find-and-replace:use-selection-as-find-pattern'
-
-'.platform-linux .editor':
+'.platform-win32 .editor', '.platform-linux .editor':
   'f3': 'find-and-replace:find-next'
   'shift-f3': 'find-and-replace:find-previous'
   'ctrl-d': 'find-and-replace:select-next'
-
   'ctrl-e': 'find-and-replace:use-selection-as-find-pattern'

 '.platform-darwin .find-and-replace':


bench map fn

bench map fn

--- bench/immutable_value_iter.coffee
+++ bench/immutable_value_iter.coffee
@@ -50,6 +50,10 @@
   ctx.list.forEach (x) ->
     ctx.doWork(x)

+bench.add "list.map()", (ctx) ->
+  ctx.list.map (x) ->
+    ctx.doWork(x)
+
 bench.add "listSeq iter", (ctx) ->
   seq = ctx.listSeq
   i = 0
@@ -76,6 +80,10 @@
   ctx.seq.forEach (x) ->
     ctx.doWork(x)

+bench.add "seq.map()", (ctx) ->
+  ctx.seq.map (x) ->
+    ctx.doWork(x)
+

 bench.add "set.values() iter", (ctx) ->
   iter = ctx.set.values()
@@ -93,6 +101,10 @@

 bench.add "set.forEach() iter", (ctx) ->
   ctx.set.forEach (x) ->
+    ctx.doWork(x)
+
+bench.add "set.map() iter", (ctx) ->
+  ctx.set.map (x) ->
     ctx.doWork(x)

 bench.add "setSeq iter", (ctx) ->
@@ -121,6 +133,10 @@
   ctx.map.forEach (x) ->
     ctx.doWork(x)

+bench.add "map.map()", (ctx) ->
+  ctx.map.map (x) ->
+    ctx.doWork(x)
+
 bench.add "mapSeq iter", (ctx) ->
   seq = ctx.setSeq
   i = 0


Fix height bug

Fix height bug

--- app/lib/3w.coffee
+++ app/lib/3w.coffee
@@ -21,7 +21,7 @@
     @height = options.height or 350
     @colors = colorbrewer[colorScheme][numColors]

-  calcProjection: (projection, width, height) =>
+  calcProjection: (projection, width) =>
     # http://stackoverflow.com/a/14691788/408556
     path = d3.geo.path().projection(projection)
     b = path.bounds(@geom)
@@ -29,12 +29,12 @@
     bwidth = Math.abs(b[1][0] - b[0][0] )
     s = .9 / Math.max(bwidth / width, bheight / @height)
     t0 = (width - s * (b[1][0] + b[0][0])) / 2
-    t1 = (height - s * (b[1][1] + b[0][1])) / 2
+    t1 = (@height - s * (b[1][1] + b[0][1])) / 2

     result =
       scale: s
       width: (width - s * (b[1][0] + b[0][0])) / 2
-      height: (height - s * (b[1][1] + b[0][1])) / 2
+      height: (@height - s * (b[1][1] + b[0][1])) / 2

     result

@@ -95,7 +95,7 @@
       .ticks(5)

     projection = d3.geo.mercator().scale(1).translate([0, 0])
-    r = @calcProjection projection, whereWidth, @height
+    r = @calcProjection projection, whereWidth
     @projection = projection.scale(r.scale).translate([r.width, r.height])

     @whereChart
@@ -142,7 +142,7 @@
     @whatChart.width $(@whatSelector).width()
     dc.redrawAll()

-    r = @calcProjection @projection, $(@whereSelector).width(), @height
+    r = @calcProjection @projection, $(@whereSelector).width()
     scale = r.scale
     translate = "#{r.width}, #{r.height}"


ebook pdf will download by itself

ebook pdf will download by itself

--- lib/markdown-preview-enhanced-view.coffee
+++ lib/markdown-preview-enhanced-view.coffee
@@ -1222,12 +1222,13 @@
       div.innerHTML = outputHTML
       @renderViz(div)

-      # download images
+      # download images for .epub and .mobi
       imagesToDownload = []
-      for img in div.getElementsByTagName('img')
-        src = img.getAttribute('src')
-        if src.startsWith('http://') or src.startsWith('https://')
-          imagesToDownload.push(img)
+      if path.extname(dest) in ['.epub', '.mobi']
+        for img in div.getElementsByTagName('img')
+          src = img.getAttribute('src')
+          if src.startsWith('http://') or src.startsWith('https://')
+            imagesToDownload.push(img)

       request = require('request')
       async = require('async')


don't throw JS errors on the homepage

don't throw JS errors on the homepage

--- public/javascripts/tetris.coffee
+++ public/javascripts/tetris.coffee
@@ -1,5 +1,6 @@
-window.pusher = new Pusher(pusher_key)
-window.channel = pusher.subscribe(gameID)
+if window.gameID
+  window.pusher = new Pusher(pusher_key)
+  window.channel = pusher.subscribe(gameID)

 window.makeGuid = ->
     S4 = () -> (((1+Math.random())*0x10000)|0).toString(16).substring(1)
@@ -64,6 +65,8 @@
       @gs.addEntity(@blocks)
       @map = new Map(0, Tetris.viewport.y*Tetris.gridSize+1, @blocks.blocks, @shapes)
       @gs.addEntity(@map)
+      bindPusherEvents()
+      bindPageGameEvents()
       channel.trigger 'ready'
       @gs.launch()

@@ -106,7 +109,6 @@

     c.beginPath()
     count = (Tetris.viewport.x)
-    console.log(count)

     for num in [0..Tetris.viewport.y]
       c.moveTo(0, Tetris.gridSize * num)
@@ -153,85 +155,84 @@
     c.fillStyle = Tetris.abandonedBlockColor
     c.fillRect(vs.x, vs.y, Tetris.blockSize, Tetris.blockSize)

-
-channel.bind 'created', (data) ->
-  return if data.playerID is Tetris.playerID
-  shapeClass = Tetris.Shape.types[data.type]
-  console.log(data.avatar)
-  Tetris.gs.addEntity( new shapeClass(id: data.id, x:data.x, y:data.y, rotation: data.rotation, color: data.color, avatar: data.avatar) )
-
-channel.bind 'moved', (data) ->
-  return if data.playerID is Tetris.playerID
-  shape = Tetris.shapes[data.id]
-  return unless shape?
-  shape.x = data.x
-  shape.y = data.y
-  shape.rotation = data.rotation
-
-pusher.back_channel.bind 'start', (info) ->
-  for id, data of info.shapes
+bindPusherEvents = ->
+  channel.bind 'created', (data) ->
+    return if data.playerID is Tetris.playerID
     shapeClass = Tetris.Shape.types[data.type]
     Tetris.gs.addEntity( new shapeClass(id: data.id, x:data.x, y:data.y, rotation: data.rotation, color: data.color, avatar: data.avatar) )
-  for block in info.blocks
-    Tetris.blocks.add(new Tetris.Block(x:block.x, y:block.y), false)
-  Tetris.gs.addEntity(Tetris.Shape.randomShape(x:Tetris.initialShapeOffset(), y:0, color: Tetris.playerBlockColor, owned: true))
-
-channel.bind 'refreshLines', (blocks) ->
-  Tetris.blocks.reset()
-  Tetris.am.play 'line-completed' unless Tetris.sfxMuted
-  for block in blocks
-    Tetris.blocks.add(new Tetris.Block(x:block.x, y:block.y), false)
-
-channel.bind 'purge', (data) ->
-  shape = Tetris.shapes[data.id]
-  shape.remove()
-
-channel.bind 'inFinalPosition', (data) ->
-  return if data.playerID is Tetris.playerID
-  shape = Tetris.shapes[data.id]
-  shape.x = data.x
-  shape.y = data.y
-  shape.rotation = data.rotation
-  shape.shapeInFinalPosition()
-
-channel.bind 'drop', ->
-  for id, data of Tetris.shapes
-    Tetris.shapes[id].drop()
-
-channel.bind 'gameover', ->
-  Tetris.am.play 'gameover' unless Tetris.sfxMuted
-  score = $('#score').html()
-  $('#wrapper').append('

Game Over

You scored: '+
-                        score +
-                        '

Tweet it or '+
-                        'start over

')
-
-channel.bind 'scoreUpdate', (data) ->
-  $('#score').html(data.score)
-
-channel.bind 'players', (data) ->
-  if (data.number == 1)
-    text = " player online"
-  else
-    text = " players online"
-  $('#playerNumber').html(data.number+text)
-
-channel.bind 'blockAdded', (data) ->
-  return if data.playerID is Tetris.playerID
-  Tetris.blocks.add(new Tetris.Block(x:data.x, y:data.y), false)
-
-$('.control#left').click ->   Tetris.gs.entitiesCall('keyDown_37')
-$('.control#right').click ->  Tetris.gs.entitiesCall('keyDown_39')
-$('.control#down').click ->   Tetris.gs.entitiesCall('keyDown_40')
-$('.control#rotate').click -> Tetris.gs.entitiesCall('keyDown_38')
-
-$('#sound-fx-toggle').click ->
-  toggle = $('#sound-fx-toggle')
-  if toggle.hasClass('toggle-off') then toggle.removeClass('toggle-off') else toggle.addClass('toggle-off')
-  Tetris.toggleSfxMute()
-  console.log(Tetris.sfxMuted)
+
+  channel.bind 'moved', (data) ->
+    return if data.playerID is Tetris.playerID
+    shape = Tetris.shapes[data.id]
+    return unless shape?
+    shape.x = data.x
+    shape.y = data.y
+    shape.rotation = data.rotation
+
+  pusher.back_channel.bind 'start', (info) ->
+    for id, data of info.shapes
+      shapeClass = Tetris.Shape.types[data.type]
+      Tetris.gs.addEntity( new shapeClass(id: data.id, x:data.x, y:data.y, rotation: data.rotation, color: data.color, avatar: data.avatar) )
+    for block in info.blocks
+      Tetris.blocks.add(new Tetris.Block(x:block.x, y:block.y), false)
+    Tetris.gs.addEntity(Tetris.Shape.randomShape(x:Tetris.initialShapeOffset(), y:0, color: Tetris.playerBlockColor, owned: true))
+
+  channel.bind 'refreshLines', (blocks) ->
+    Tetris.blocks.reset()
+    Tetris.am.play 'line-completed' unless Tetris.sfxMuted
+    for block in blocks
+      Tetris.blocks.add(new Tetris.Block(x:block.x, y:block.y), false)
+
+  channel.bind 'purge', (data) ->
+    shape = Tetris.shapes[data.id]
+    shape.remove()
+
+  channel.bind 'inFinalPosition', (data) ->
+    return if data.playerID is Tetris.playerID
+    shape = Tetris.shapes[data.id]
+    shape.x = data.x
+    shape.y = data.y
+    shape.rotation = data.rotation
+    shape.shapeInFinalPosition()
+
+  channel.bind 'drop', ->
+    for id, data of Tetris.shapes
+      Tetris.shapes[id].drop()
+
+  channel.bind 'gameover', ->
+    Tetris.am.play 'gameover' unless Tetris.sfxMuted
+    score = $('#score').html()
+    $('#wrapper').append('

Game Over

You scored: '+
+                          score +
+                          '

Tweet it or '+
+                          'start over

')
+
+  channel.bind 'scoreUpdate', (data) ->
+    $('#score').html(data.score)
+
+  channel.bind 'players', (data) ->
+    if (data.number == 1)
+      text = " player online"
+    else
+      text = " players online"
+    $('#playerNumber').html(data.number+text)
+
+  channel.bind 'blockAdded', (data) ->
+    return if data.playerID is Tetris.playerID
+    Tetris.blocks.add(new Tetris.Block(x:data.x, y:data.y), false)
+
+bindPageGameEvents = ->
+  $('.control#left').click ->   Tetris.gs.entitiesCall('keyDown_37')
+  $('.control#right').click ->  Tetris.gs.entitiesCall('keyDown_39')
+  $('.control#down').click ->   Tetris.gs.entitiesCall('keyDown_40')
+  $('.control#rotate').click -> Tetris.gs.entitiesCall('keyDown_38')
+
+  $('#sound-fx-toggle').click ->
+    toggle = $('#sound-fx-toggle')
+    if toggle.hasClass('toggle-off') then toggle.removeClass('toggle-off') else toggle.addClass('toggle-off')
+    Tetris.toggleSfxMute()

 gameStart = ->
   $('#score').show()


:art: refactor getFullExtension function

--- lib/helpers.coffee
+++ lib/helpers.coffee
@@ -17,11 +17,9 @@
     styleObject

   getFullExtension: (filePath) ->
-    fullExtension = ''
-    while extension = path.extname(filePath)
-      fullExtension = extension + fullExtension
-      filePath = path.basename(filePath, extension)
-    fullExtension
+    basename = path.basename(filePath)
+    position = basename.indexOf('.')
+    if position > 0 then basename[position..] else ''

   updateEditorsForPath: (oldPath, newPath) ->
     editors = atom.workspace.getTextEditors()


Fixes deprivation

Fixes deprivation

--- lib/latexer.coffee
+++ lib/latexer.coffee
@@ -1,7 +1,7 @@
 LabelView = require './label-view'
 CiteView = require './cite-view'
 LatexerHook = require './latexer-hook'
-{TextEditor, CompositeDisposable} = require 'atom'
+{CompositeDisposable} = require 'atom'

 module.exports = Latexer =


Replace String.substring calls with String.slice calls for a tiny performance boost.

--- lib/message.coffee
+++ lib/message.coffee
@@ -13,10 +13,10 @@
     position = 0
     nextspace = 0

-    if line[0] is "@"
+    if line.charAt(0) is "@"
       nextspace = line.indexOf " "
       throw new Error "Expected prefix; malformed IRC message." if nextspace is -1
-      rawTags = line.substring(1, nextspace).split ";"
+      rawTags = line.slice(1, nextspace).split ";"

       for tag in rawTags
         pair = tag.split "="
@@ -29,7 +29,7 @@
     if line.charAt(position) is ":"
       nextspace = line.indexOf " ", position
       throw new Error "Expected command; malformed IRC message." if nextspace is -1
-      @prefix = line.substring position + 1, nextspace
+      @prefix = line.slice position + 1, nextspace
       position = nextspace + 1
       position++ while line.charAt(position) is " "

@@ -37,10 +37,10 @@

     if nextspace is -1
       if line.length > position
-        @command = line.substring(position).toUpperCase()
+        @command = line.slice(position)
       else return

-    @command = line.substring(position, nextspace).toUpperCase()
+    @command = line.slice(position, nextspace).toUpperCase()

     position = nextspace + 1
     position++ while line.charAt(position) is " "
@@ -48,17 +48,17 @@
     while position < line.length
       nextspace = line.indexOf " ", position
       if line.charAt(position) is ":"
-        @args.push line.substring position + 1
+        @args.push line.slice position + 1
         break

       if nextspace isnt -1
-        @args.push line.substring position, nextspace
+        @args.push line.slice position, nextspace
         position = nextspace + 1
         position++ while line.charAt(position) is " "
         continue

       if nextspace is -1
-        @args.push line.substring position
+        @args.push line.slice position
         break
     return


random number to generate_id()

random number to generate_id()

--- lib/orpheus.coffee
+++ lib/orpheus.coffee
@@ -63,23 +63,25 @@
 				]

 				# Create a simple schema for all fields
-				for f in @fields
-					do (f) =>
-						@[f] = (field, options = {}) ->
-							throw new Error("Field name already taken") if @model[field]
-
-							@model[field] =
-								type: f
-								options: options
-
-							return field
+				@create_field f for f in @fields

 				# Create the model
 				super()

+			create_field: (f) ->
+				@[f] = (field, options = {}) ->
+					throw new Error("Field name already taken") if @model[field]
+
+					@model[field] =
+						type: f
+						options: options
+
+					return field
+
 			# Add mapping
 			map: (field) ->
-				throw new Error("Map only works on strings") if @model[field].type isnt 'str'
+				if @model[field].type isnt 'str'
+					throw new Error("Map only works on strings")
 				@model[field].options.map = true

 			# Add relations
@@ -354,7 +356,8 @@
 		pid = process.pid
 		host = 0; (host += s.charCodeAt(0) for s in os.hostname())
 		counter = Orpheus.unique_id()
-		"#{host}#{pid}#{time}#{counter}"
+		random = Math.round(Math.random() * 1000)
+		"#{host}#{pid}#{time}#{counter}#{random}"

 	_get_key: (key) ->


handling for no nodepool status available

--- plugins/kubernetes/app/assets/javascripts/kubernetes/components/clusters/item.coffee
+++ plugins/kubernetes/app/assets/javascripts/kubernetes/components/clusters/item.coffee
@@ -82,13 +82,18 @@
         for nodePoolStatus in cluster.status.nodePools
           specSize = @nodePoolSpecSize(cluster, nodePoolStatus.name)
           div className: 'nodepool-info', key: "status-#{nodePoolStatus.name}",
+            statusAvailable = false
             for k,v of nodePoolStatus
               unless k == 'name' || k == 'size'
+                statusAvailable = true
                 div key: k,
                   strong null, "#{k}: "
                   "#{v}/#{specSize}"
                   if v != specSize
                     span className: 'spinner'
+
+            unless statusAvailable
+              span className: 'spinner'


limit parameter nullcheck

limit parameter nullcheck

--- lib/request.coffee
+++ lib/request.coffee
@@ -86,7 +86,7 @@
 		data = req.query

 		result =
-			perPage:parseInt(data.perPage, 10)
+			perPage: data.perPage && parseInt(data.perPage, 10)
 			page:data.page
 			sortField:data.sortField
 			sortDirection:data.sortDirection


DiaScene: getBezierCurves for getting position for given t as described

--- lib/components/dia/diascene.coffee
+++ lib/components/dia/diascene.coffee
@@ -258,6 +258,38 @@

     @cleanup @realCanvas

+  getBezierCurves = (t, pos) ->
+
+    # Applying Cubic Bézier Curve formula to provided
+    # positions to get specific position in given t
+    # where t is 0 <= t <= 1.
+    #
+    # https://en.wikipedia.org/wiki/Bézier_curve
+
+    mt = 1 - t
+    b  = [
+      mt ** 3          # P0 * (1 - t)^3
+      3 * t * mt ** 2  # P1 * 3 * (1 - t)^2 * t
+      3 * mt * t ** 2  # P2 * 3 * (1 - t) * t^2
+      t ** 3           # P3 * t^3
+    ]
+
+    # Above formula will be applied below with following data;
+    #
+    # Where P0 is s  -> source joint position
+    # Where P1 is c1 -> first control point
+    # Where P2 is c2 -> second control point
+    # Where P3 is t  -> target joint position
+    #
+    # Then sum of Ps will get the curve point
+
+    { s, t, c1, c2 } = pos
+
+    # Applying for both x and y coordinates to get pin point
+    return {
+      x: (s.x * b[0]) + (c1.x * b[1]) + (c2.x * b[2]) + (t.x * b[3])
+      y: (s.y * b[0]) + (c1.y * b[1]) + (c2.y * b[2]) + (t.y * b[3])
+    }

   drawConnectionLine: ({ source, target, options = {} }) ->


cleanup

cleanup

--- lib/autohide-tree-view.coffee
+++ lib/autohide-tree-view.coffee
@@ -161,9 +161,6 @@
       'tree-view:reveal-active-file': => @show 0, true
       'tree-view:remove': => @resize()
       'tree-view:paste': => @resize()
-
-    # these tree-view commands should trigger a resize
-    @disposables.add atom.commands.add 'atom-workspace',
       'tree-view:expand-directory': => @resize()
       'tree-view:recursive-expand-directory': => @resize()
       'tree-view:collapse-directory': => @resize()


remove own commands

remove own commands

--- lib/autohide-tree-view.coffee
+++ lib/autohide-tree-view.coffee
@@ -121,12 +121,6 @@
     @disposables.add atom.config.onDidChange 'tree-view.hideIgnoredNames', => @update()
     @disposables.add atom.config.onDidChange 'tree-view.hideVcsIgnoredFiles', => @update()
     @disposables.add atom.config.onDidChange 'core.ignoredNames', => @update()
-
-    # add listeners for commands
-    @disposables.add atom.commands.add 'atom-workspace',
-      'autohide-tree-view:show': => @show 0, true
-      'autohide-tree-view:hide': => @hide 0
-      'autohide-tree-view:toggle': => @toggle()

     # add listeners for mouse events
     mouseEventDisposables = null


removed unused line

removed unused line

--- lib/autohide-tree-view.coffee
+++ lib/autohide-tree-view.coffee
@@ -151,7 +151,6 @@
     treeViewEl.classList.remove 'autohide-unfolded'
     treeViewEl.style.transitionDelay = "#{transitionDelay}s"
     @applyHiddenWidth()
-    # treeViewEl.blur()
     treeView.unfocus()
     @enableHoverEvents()


Speed up utils.removeNote

Speed up utils.removeNote

--- lib/autoprefixer/utils.coffee
+++ lib/autoprefixer/utils.coffee
@@ -40,7 +40,10 @@

   # Return "-webkit-" on "-webkit-~old"
   removeNote: (string) ->
-    string.split('~')[0]
+    if string.indexOf('~') == -1
+      string
+    else
+      string.split('~')[0]

   # Escape RegExp symbols
   escapeRegexp: (string) ->


I fixed a bug involving counting files, only to have that no having that bug leads to another bug, namely considing the empty string a modified file...

--- salvus/snap.coffee
+++ salvus/snap.coffee
@@ -812,6 +812,7 @@
                             cb(err)
                         else
                             modified_files = (x.slice(2) for x in output.stdout.trim().split('\n'))
+                            modified_files = (x for x in modified_files when x != "")
                             n = modified_files.length
                             winston.debug("#{n} modified files for #{project_id}")
                             winston.debug("modified files = #{misc.trunc(misc.to_json(modified_files),512)}")


Fix coffescript error

Fix coffescript error

--- Email/EmailNotifier.coffee
+++ Email/EmailNotifier.coffee
@@ -35,7 +35,9 @@
                 triggerText: @triggerText

             for line in @event.exceptions[0].stacktrace
-                context.inProjectStack = line and break if line.inProject = true
+                if line.inProject = true
+                    context.inProjectStack = line
+                    break

             @emailAddresses (err, emails) =>
                 return callback "Error when retrieving emails! Contents: #{err}" if err?


Update Checkins.coffee

Update Checkins.coffee

--- client/components/Checkins.coffee
+++ client/components/Checkins.coffee
@@ -62,7 +62,7 @@
             </p>

             <Alert bsStyle="danger">
-                На 12 декабря запись есть только на одно занятие, см. новости.
+                На 15 декабря запись есть только на одно занятие, см. новости.
             </Alert>

             {


StaticPage: Regress data fetch operations from helper

--- server/lib/crawler/helpers.coffee
+++ server/lib/crawler/helpers.coffee
@@ -1,5 +1,6 @@
 {argv}  = require 'optimist'
 {uri}   = require('koding-config-manager').load("main.#{argv.c}")
+{daisy} = require('bongo')
 encoder = require 'htmlencode'

 getProfile = (account) ->
@@ -76,117 +77,156 @@
       """
     return tagContent

-createActivityContent = (JAccount, model, comments, createFullHTML=no, putBody=yes, callback) ->
-  {Relationship} = require 'jraphical'
+createLinkContent = (href, title, text) ->
+  marked = require 'marked'
+  renderer = new marked.Renderer()
+  renderer.link= (href, title, text)->
+    linkHTML = "#{text}" \
+    else " rel=\"nofollow\">#{text}"
+
+    return linkHTML
+
+createProfile = (models, activity, callback)->
+  {JAccount} = models
+  {accountOldId} = activity
+  JAccount.one "_id" : accountOldId, (err, acc) =>
+    return callback err  if err
+    return callback "account not found"  unless acc
+
+    renderedProfile = getProfile acc
+    callback null, renderedProfile
+
+renderCreatedAt = (activity) ->
+  {message:{createdAt}} = activity  #if model
+  return formatDate new Date(createdAt)
+
+renderBody = (activity) ->
+  marked = require 'marked'
+  # If href goes to outside of koding, add rel=nofollow.
+  # this is necessary to prevent link abusers.
+  renderer = new marked.Renderer()
+  renderer.link= (href, title, text)->
+    linkHTML = "#{text}"
+    else
+      linkHTML += " rel=\"nofollow\">#{text}"
+    return linkHTML
+
+  {message:{body}} = activity
+
+  body = marked body,
+    renderer  : renderer
+    gfm       : true
+    pedantic  : false
+    sanitize  : true
+
+  return body
+
+
+prepareComments = (models, activity, callback) ->
+  {JAccount} = models
+  {replies} = activity
+  return callback null  unless replies.length
+
+  queue = []
+  queue = replies.map (reply) ->->
+    JAccount.one _id: reply.accountOldId, (err, account) ->
+      if err
+        console.error "Could not fetch replier information #{reply.accountOldId}"
+        return queue.next()
+
+      reply.replier = getProfile account
+      queue.next()
+
+  queue.push ->
+    callback null, replies
+
+  daisy queue
+
+prepareLikes = (models, activity, callback) ->
+  {JAccount} = models
+  {interactions:{like}} = activity
+  return callback null  unless like.actorsPreview.length
+
+  queue = []
+  actors = []
+  queue = like.actorsPreview.map (actor)->->
+    JAccount.one _id: actor, (err, account) ->
+      if err
+        console.error "Could not fetch interactor information #{actor}"
+        return queue.next()
+
+      actors.push getProfile account
+
+      queue.next()
+
+  queue.push ->
+    callback null, actors
+
+  daisy queue
+
+
+prepareActivity = (models, {activity, profile}, callback) ->
+  {message, repliesCount, interactions} = activity
+  {slug, body, createdAt} = message
+  {like:{actorsCount}} = interactions
+
+  activityContent =
+    slug             : slug
+    fullName         : profile.fullName
+    nickname         : profile.nickname
+    hash             : profile.hash
+    avatar           : profile.avatar
+    body             : body
+    createdAt        : renderCreatedAt activity
+    commentCount     : repliesCount
+    likeCount        : actorsCount
+
+  queue = [
+    ->
+      prepareComments models, activity, (err, replies) ->
+        return callback err  if err
+        activityContent.replies = replies  if replies
+        queue.next()
+    ,
+    ->
+      prepareLikes models, activity, (err, likers) ->
+        return callback err  if err
+        activityContent.likers = likers  if likers
+        queue.next()
+    ,
+    ->
+      callback null, activityContent
+    ]
+
+  daisy queue
+
+createActivityContent = (models, activity, callback) ->
   {htmlEncode}   = require 'htmlencode'
-  marked         = require 'marked'
   {getSingleActivityPage, getSingleActivityContent} = require './staticpages/activity'

-  statusUpdateId = model.getId()
-  jAccountId = model.originId
-  selector =
-    "sourceId" : statusUpdateId,
-    "as"       : "author"
-
-  return callback new Error "Cannot call fetchTeaser function.", null unless typeof model.fetchTeaser is "function"
-  model.fetchTeaser (error, teaser)=>
-    tags = []
-    tags = teaser.tags  if teaser?.tags?
-
-    sel =
-      "_id" : model.originId
-
-    JAccount.one sel, (err, acc) =>
-      if err
-        console.error err
-        return callback err, null
-
-      profile = getProfile acc
-      slug    = teaser?.slug or "#"
-
-      {meta:{createdAt}} = model  if model
-      createdAt          = if createdAt then formatDate createdAt else ""
-
-      # If href goes to outside of koding, add rel=nofollow.
-      # this is necessary to prevent link abusers.
-      renderer = new marked.Renderer()
-      renderer.link= (href, title, text)->
-        linkHTML = "#{text}"
-        else
-          linkHTML += " rel=\"nofollow\">#{text}"
-        return linkHTML
-
-      if model?.body? and putBody
-        body = marked model.body,
-          renderer  : renderer
-          gfm       : true
-          pedantic  : false
-          sanitize  : true
-        body = normalizeActivityBody model, body
-      else
-        body = ""
-      activityContent =
-        slug             : teaser.slug
-        fullName         : profile.fullName
-        nickname         : profile.nickname
-        hash             : profile.hash
-        avatar           : profile.avatar
-        title            : if model?.title then model.title else model.body or ""
-        body             : body
-        createdAt        : createdAt
-        numberOfComments : teaser.repliesCount or 0
-        numberOfLikes    : model?.meta?.likes or 0
-        comments         : comments
-        tags             : tags
-        type             : model?.bongo_?.constructorName
-      activityContent.title = encoder.XSSEncode activityContent.title
-      if createFullHTML
-        content = getSingleActivityPage {activityContent, model}
-      else
-        content = getSingleActivityContent activityContent, model
-      return callback null, content
-
-decorateComment = (JAccount, comment, callback) ->
-  { Relationship } = require 'jraphical'
-  createdAt = formatDate(new Date(comment.timestamp_))
-  commentSummary = {body: comment.body, createdAt: createdAt}
-  selector =
-  {
-    "targetId" : comment.getId(),
-    "sourceName": "JAccount"
-  }
-  Relationship.one selector, (err, rel) =>
-    if err
-      console.error err
-      callback err, null
-    return callback err, null  unless rel?.sourceId?
-    sel = { "_id" : rel.sourceId}
-
-    JAccount.one sel, (err, acc) =>
-      if err
-        console.error err
-        callback err, null
-
-      profile = getProfile acc
-
-      commentSummary.authorName     = profile.fullName
-      commentSummary.authorNickname = profile.nickname
-      commentSummary.authorHash     = profile.hash or ''
-      commentSummary.authorAvatar   = profile.avatar
-
-      callback null, commentSummary
+  createProfile models, activity, (err, profile) ->
+    return callback err, null  if err
+
+    slug    = teaser?.slug or "#"
+    createdAt = renderCreatedAt activity
+    body = renderBody activity
+    prepareActivity models, {activity, profile}, (err, activityContent) ->
+      return callback err  if err
+      content = getSingleActivityContent activityContent
+      result = {activityContent, profile}
+      return callback null, content, activityContent

 module.exports = {
   getProfile
-  getAvatarImageUrl
-  forceTwoDigits
-  formatDate
   createActivityContent
-  decorateComment
 }


async identity access bugfix

async identity access bugfix

--- lib/data/PubSubContext.coffee
+++ lib/data/PubSubContext.coffee
@@ -41,6 +41,8 @@
                 data = topic
                 topic = '_LEGACY_'

+            origin = (@publish.identity || @identity).id
+
             threads = []
             @_process @_pool,
                 ##
@@ -48,8 +50,9 @@
                     if topic.match handler.topic
                         threads.push (fn)=>
                             try
+                                console.log data, @identity
                                 handler null,
-                                    origin: (@publish.identity || @identity).id
+                                    origin: origin
                                     data: data

                                 fn()


removed console.log

removed console.log

--- bankController.coffee
+++ bankController.coffee
@@ -9,6 +9,5 @@
     app.get '/words', 'words', (req, res) ->
         appConf.db.getWords()
             .then (words) ->
-                    console.log words
                     res.json
                         words: words


Use lazy requires in the main package file to speed up load times

--- lib/game-data-explorer.coffee
+++ lib/game-data-explorer.coffee
@@ -1,27 +1,16 @@
-path = require 'path'
-fs = require 'fs-plus'
-
-TGAEditor = require './tga-editor'
-WAVEditor = require './wav-editor'
-MDLEditor = require './mdl-editor'
-GLTFEditor = require './gltf-editor'
-HEXEditor = require './hex-editor'
-
-textOrBin = require 'istextorbinary'
-
 module.exports =
 class GameDataExplorer
   @editors:
     '.key': (filePath) ->
       try return new (require './aurora-keyv1-editor')(path: filePath) catch
       return null
-    '.mdl': (filePath) -> return new MDLEditor(path: filePath)
-    '.tga': (filePath) -> return new TGAEditor(path: filePath)
-    '.wav': (filePath) -> return new WAVEditor(path: filePath)
-    '.wad': (filePath) -> return new WAVEditor(path: filePath)
-    '.waa': (filePath) -> return new WAVEditor(path: filePath)
-    '.wam': (filePath) -> return new WAVEditor(path: filePath)
-    '.wac': (filePath) -> return new WAVEditor(path: filePath)
+    '.mdl': (filePath) -> return new (require './mdl-editor')(path: filePath)
+    '.tga': (filePath) -> return new (require './tga-editor')(path: filePath)
+    '.wav': (filePath) -> return new (require './wav-editor')(path: filePath)
+    '.wad': (filePath) -> return new (require './wav-editor')(path: filePath)
+    '.waa': (filePath) -> return new (require './wav-editor')(path: filePath)
+    '.wam': (filePath) -> return new (require './wav-editor')(path: filePath)
+    '.wac': (filePath) -> return new (require './wav-editor')(path: filePath)
     '.dir': (filePath) ->
       try return new (require './commandos1-dir-editor')(path: filePath) catch
       return null
@@ -39,9 +28,11 @@
     '.bf': (filePath) ->
       try return new (require './beyond-good-and-evil-bf-editor')(path: filePath) catch
       return null
-    '.gltf': (filePath) -> return new GLTFEditor(path: filePath)
+    '.gltf': (filePath) -> return new (require './gltf-editor')(path: filePath)

   @activate: ->
+    fs = require 'fs-plus'
+    path = require 'path'
     @opener = atom.workspace.addOpener (filePath = '') =>
       if fs.isFileSync(filePath)
         extName = path.extname(filePath).toLowerCase()
@@ -53,8 +44,9 @@
         buffer = new Buffer 256
         file = fs.openSync filePath, 'r'
         bytes = fs.readSync file, buffer, 0, 256, 0
+        textOrBin = require 'istextorbinary'
         if textOrBin.isBinarySync filePath, buffer.slice(0, bytes)
-          return new HEXEditor(path: filePath)
+          return new (require './hex-editor')(path: filePath)

   @deactivate: ->
     @opener.dispose()


Don't ignore the whole project.

Don't ignore the whole project.

--- lib/load-paths-handler.coffee
+++ lib/load-paths-handler.coffee
@@ -46,7 +46,7 @@
     @paths = []

   loadPath: (pathToLoad, done) ->
-    return done() if @isIgnored(pathToLoad)
+    return done() if @isIgnored(pathToLoad) and pathToLoad isnt @rootPath
     fs.lstat pathToLoad, (error, stats) =>
       return done() if error?
       if stats.isSymbolicLink()


:art: cleanup

:art: cleanup

--- lib/move-selected-text.coffee
+++ lib/move-selected-text.coffee
@@ -310,66 +310,10 @@
         switch @direction
           when 'right', 'left'
             for selection in selections
-              newText = selection.getText().repeat(@getCount())
-              width = newText.length
-              {start, end} = selection.getBufferRange()
-              range = switch @direction
-                when 'left'
-                  if @isOverwrite()
-                    [start.translate([0, -width]), start]
-                  else
-                    [start, start]
-                when 'right'
-                  if @isOverwrite()
-                    [end, end.translate([0, +width])]
-                  else
-                    [end, end]
-              @setTextInRangeAndSelect(range, newText, selection)
-
+              @duplicateCharacterwise(selection)
           when 'up', 'down'
-            getBaseTexts = (blockwiseSelection) ->
-              blockwiseSelection.selections.map (selection) ->
-                numberOfSpace = selection.getBufferRange().start.column
-                ' '.repeat(numberOfSpace) + selection.getText()
-
             for blockwiseSelection in @vimState.getBlockwiseSelections()
-              baseTexts = getBaseTexts(blockwiseSelection)
-              {
-                start: {row: startRow, column: startColumn}
-                end: {row: endRow, column: endColumn}
-              } = blockwiseSelection.getBufferRange()
-
-              lineTexts = _.flatten([1..@getCount()].map -> baseTexts)
-              height = lineTexts.length
-              newText = lineTexts.join("\n")
-              if @isOverwrite()
-                @duplicateBlockwiseSelection(blockwiseSelection, @direction)
-              else
-                switch @direction
-                  when 'up'
-                    point = @insertTextAtPoint([startRow - 1, Infinity], "\n").end
-                    {start, end} = @insertTextAtPoint(point, newText)
-                    newRange = [[start.row, startColumn], [end.row, endColumn]]
-                    blockwiseSelection.setBufferRange(newRange)
-
-                  when 'down'
-                    range = @insertTextAtPoint([endRow + 1, 0], newText)
-                    @insertTextAtPoint(range.end, "\n")
-                    {start, end} = range
-                    newRange = [[start.row, startColumn], [end.row, endColumn]]
-                    blockwiseSelection.setBufferRange(newRange)
-
-  duplicateBlockwiseSelection: (blockwiseSelection, direction) ->
-    {selections} = blockwiseSelection
-    height = selections.length
-    translation = switch direction
-      when 'up' then [-height, 0]
-      when 'down' then [+height, 0]
-
-    selections.forEach (selection) =>
-      range = selection.getBufferRange().translate(translation)
-      @complementSpacesToPoint(range.start)
-      @setTextInRangeAndSelect(range, selection.getText(), selection)
+              @duplicateCharacterwise(blockwiseSelection)

   setTextInRangeAndSelect: (range, text, selection) ->
     newRange = @editor.setTextInBufferRange(range, text)
@@ -430,14 +374,52 @@

 class DuplicateSelectedTextUp extends DuplicateSelectedText
   direction: 'up'
-
-class DuplicateSelectedTextDown extends DuplicateSelectedText
+  duplicateBlockwiseSelection: (blockwiseSelection, direction) ->
+    {selections} = blockwiseSelection
+    height = selections.length
+
+    unless @isOverwrite() # Insert Blank row
+      [startRow, endRow] = blockwiseSelection.getBufferRowRange()
+      point = switch direction
+        when 'up' then [startRow - 1, Infinity]
+        when 'down' then [endRow + 1, 0]
+      @insertTextAtPoint(point, "\n".repeat(height))
+
+    translation = switch direction
+      when 'up' then [-height, 0]
+      when 'down' then [+height, 0]
+
+    selections.forEach (selection) =>
+      range = selection.getBufferRange().translate(translation)
+      @complementSpacesToPoint(range.start)
+      @setTextInRangeAndSelect(range, selection.getText(), selection)
+
+  duplicateCharacterwise: (blockwiseSelection) ->
+    @duplicateBlockwiseSelection(blockwiseSelection, @direction)
+
+class DuplicateSelectedTextDown extends DuplicateSelectedTextUp
   direction: 'down'

 class DuplicateSelectedTextRight extends DuplicateSelectedText
   direction: 'right'
-
-class DuplicateSelectedTextLeft extends DuplicateSelectedText
+  duplicateCharacterwise: (selection) ->
+    newText = selection.getText().repeat(@getCount())
+    width = newText.length
+    {start, end} = selection.getBufferRange()
+    range = switch @direction
+      when 'left'
+        if @isOverwrite()
+          [start.translate([0, -width]), start]
+        else
+          [start, start]
+      when 'right'
+        if @isOverwrite()
+          [end, end.translate([0, +width])]
+        else
+          [end, end]
+    @setTextInRangeAndSelect(range, newText, selection)
+
+class DuplicateSelectedTextLeft extends DuplicateSelectedTextRight
   direction: 'left'

 module.exports = {


up teams

up teams

--- models/team.coffee
+++ models/team.coffee
@@ -37,7 +37,7 @@
 TeamSchema.static 'canRegister', (next) ->
   Team.count null, (err, count) ->
     return next err if err
-    max = 295 + 1 # +1 because team fortnight labs doesn't count
+    max = 300 + 1 # +1 because team fortnight labs doesn't count
     next null, count < max, max - count

 TeamSchema.static 'uniqueName', (name, next) ->


Don't update the badge issued_count

Don't update the badge issued_count

--- models/user.coffee
+++ models/user.coffee
@@ -113,12 +113,12 @@
     b.badge_id = badge.id
     b.image = badge.imageUrl
     b.imageObj = badge.image
-    b.issued_count = undefined
+    delete b.issued_count
     @badges.push b
     @save (err, user)=>
       if err
         callback(err, null)
-      @model("Badge").findByIdAndUpdate badge.id, {$inc:{issued_count: 1}},->
+      else
         callback null, {
           message: 'successfully added badge'
           earned: true


adds is premium method

adds is premium method

--- models/user.coffee
+++ models/user.coffee
@@ -18,6 +18,9 @@

   slugOrId: ->
     @get('slug') || @id
+
+  isPremium: ->
+    @get('is_pro') is true or @get('is_pro') is 'true'

   startPrivateChannel: ->
     $.ajax


execute 'open' for mac and update error msg

--- lib/native-app-starter.coffee
+++ lib/native-app-starter.coffee
@@ -15,7 +15,7 @@
     if settings.nativeApps[name]?
       @execute settings.nativeApps[name]
     else
-      window.eventbus.fire "Notifications","error","#{name} kann nicht geöffnet werden."
+      window.eventbus.fire "Notifications","error","Kein Pfard für #{name} hinterlegt."
     # switch name
     #   when 'visio:' then @exec '/test/visio'
     #   when 'adobe:' then @exec '/adobe/path'
@@ -26,6 +26,7 @@

   execute: (path) ->
     # console.log path
+    path = "open #{path}" if process.platform == 'darwin'
     exec path, (error, stdout, stderr) ->
       console.log "stdout: #{stdout}"
       console.log "stderr: #{stderr}"


fix a bug

fix a bug

--- lib/emulator/emulator-view.coffee
+++ lib/emulator/emulator-view.coffee
@@ -59,7 +59,10 @@
   onClickRefresh: ->
     console.log "refresh"
     @setTargetUrl(@url.text())
-    # @webview[0].reload()
+    try
+      @webview[0].reload()
+    catch
+      console.log "webview is not open"

   onClickDebug: ->
     webview = document.querySelector('webview')


Fix added backslash if there's \ at the end of namespace in composer.json

--- lib/services/namespace.coffee
+++ lib/services/namespace.coffee
@@ -19,6 +19,9 @@
     # Get elements from composer.json
     for psr, autoload of composer.autoload
         for namespace, src of autoload
+            if namespace.endsWith("\\")
+              namespace = namespace.substr(0, namespace.length-1)
+
             autoloaders[src] = namespace

     # Get the current path of the file