removed duplicated code

removed duplicated code

--- app/assets/javascripts/pages/websites.coffee
+++ app/assets/javascripts/pages/websites.coffee
@@ -8,90 +8,6 @@
         Routes.api_v1_website_path(this.id, {format: 'js'})
   }
 }
-
-PubSub.subscribe('assigned.website', (ev, website)->
-  console.log gon.action
-  switch gon.action
-    when "new"
-      addNewWebsite('#formWebsite', 'new')
-    when "index"
-      $.getJSON Routes.api_v1_websites_url(), {member_id: $.auth.user.id}, (data) ->
-        $('#websites-container').render data, directive
-        $('#myModal').modal('show') if data.websites.length == 0
-        console.log 'data loaded'
-
-      $.getJSON Routes.api_v1_website_path(website.id), (data) ->
-        $('#current-website').render data
-
-      reset_platforms()
-      $('.tab2, .tab3').addClass('disabled')
-      addNewWebsite('#modalWebsite', 'index')
-
-)
-addNewWebsite = (form, action) ->
-  $(form).submit (e) ->
-      e.preventDefault()
-      $.ajax
-        url: Routes.api_v1_websites_url()
-        type: 'post'
-        dataType: 'json'
-        data: { website: { domain: $(form).find('#domain').val(), title: $(form).find('#title').val() } }
-        success: (data) ->
-          EpicLogger.setMemberDetails(data.id)
-          if action == 'index'
-            manipulateWizard(2)
-            $('.tab1').addClass('disabled')
-            $('.tab2').removeClass('disabled')
-          else
-            alert 'Website added!'
-            location.href = '/installations'
-        error: (error) ->
-          alert "Website exists!" if error.status == 401
-      return
-    return
-
-reset_platforms = () ->
-  $('#javascript, #node_js, #rails, #ruby, #python, #ios, #php, #java, #django').hide()
-
-$('#retry').on 'click', () ->
-  manipulateWizard(2)
-  reset_platforms()
-
-$('#finish').on 'click', () ->
-  location.href = '/errors'
-
-$('#platform').on 'click', (platform) ->
-  reset_platforms()
-  switch platform.target.parentElement.name
-    when 'javascript'
-      $('#javascript').show()
-    when 'node_js'
-      $('#node_js').show()
-    when 'rails'
-      $('#rails').show()
-    when 'ruby'
-      $('#ruby').show()
-    when 'python'
-      $('#python').show()
-    when 'ios'
-      $('#ios').show()
-    when 'php'
-      $('#php').show()
-    when 'java'
-      $('#java').show()
-    when 'django'
-      $('#django').show()
-  manipulateWizard(3)
-
-manipulateWizard = (n) ->
-  if n != 0
-    $('.stepwizard-row a').removeClass('btn-primary')
-    $('.stepwizard-row a').addClass('btn-default')
-    $('.stepwizard a[href="#step-' + n + '"]').tab 'show'
-    $('.stepwizard-row a[href="#step-' + n + '"]').removeClass 'btn-default'
-    $('.stepwizard-row a[href="#step-' + n + '"]').addClass 'btn-primary'
-  return
-

 PubSub.subscribe('assigned.website', (ev, website)->
   console.log gon.action


checkpoint

checkpoint

--- app/assets/javascripts/s3_form_for.js.coffee
+++ app/assets/javascripts/s3_form_for.js.coffee
@@ -242,8 +242,13 @@
         $(forms_for_submit).submit()

       if settings.allow_send_form_without_file && file_name_for_upload_text == 'No file selected'
+        console.log('s3 submit')
+        $($uploadForm).off 'submit'
+        console.log('1111111111111111111111111111')
+        console.log($($uploadForm))
         $($uploadForm).submit()
       else
+        console.log('s3 false')
         false

     $uploadForm.on 'enable_all_field', ->
@@ -271,11 +276,12 @@
       $uploadForm.find("#upload_s3_path").val(content.filepath)
 #      $("#{selectors.s3_form} #form_submit").submit()
 #      Hack for IE9
-      $("#{selectors.s3_form} #{selectors.submit}").submit (e) ->
-        e.preventDefault();
-        $(this).closest("form").submit()
-
-      $(selectors.s3_form).trigger('submit')
+#      $("#{selectors.s3_form} #{selectors.submit}").submit (e) ->
+#        e.preventDefault();
+#        $(this).closest("form").submit()
+      console.log('s3_upload_complete')
+      $($uploadForm).off 'submit'
+      $($uploadForm).submit()#rigger('submit')

   build_content_object = ($uploadForm, file, result) ->
     content = {}


put cursor in username field

put cursor in username field

--- app/assets/javascripts/views/login.js.coffee
+++ app/assets/javascripts/views/login.js.coffee
@@ -4,6 +4,11 @@

     template: "login"
     el: "#body"
+
+    app_events: {
+      "state:activate": ->
+        $("input.username").putCursorAtEnd();
+    }

     events: {
       "submit form": (e) ->


Fix previous commit

Fix previous commit

--- app/coffee/modules/userstories/detail.coffee
+++ app/coffee/modules/userstories/detail.coffee
@@ -240,7 +240,6 @@
     """)
     selectionPointsTemplate = _.template("""

-

         <% _.each(points, function(point) { %>

<a href="" class="point" title="<%- point.name %>"
                data-point-id="<%- point.id %>"><%- point.name %>


Firewall: Draw connections between rule and domain dia.

--- client/Environments/views/scene/environmentsceneview.coffee
+++ client/Environments/views/scene/environmentsceneview.coffee
@@ -117,14 +117,25 @@
   updateConnections:->
     @reset no

-    vmDias     = @boxes.vms.dias
-    domainDias = @boxes.domains.dias
+    vmDias       = @boxes.vms.dias
+    domainDias   = @boxes.domains.dias
+    domainsByDia = {}
+    rulesById    = {}
+
+    domainsByDia[domain.data.title] = domain for key, domain of domainDias
+    rulesById[rule.data._id] = rule for key, rule of @boxes.rules.dias

     for _mkey, vm of vmDias
       for _dkey, domain of domainDias
         domainAliases = domain.getData().aliases
         if domainAliases and vm.getData().title in domainAliases
           @connect {dia : domain , joint : 'right'}, {dia : vm, joint : 'left' }, yes
+
+    for restriction in EnvironmentRuleContainer.restrictions
+      domainDia = domainsByDia[restriction.domainName]
+      for filterId in restriction.filters
+        ruleDia = rulesById[filterId]
+        @connect {dia : domainDia, joint : 'left'}, {dia : ruleDia, joint : 'right' }, yes

   createApproveModal:(items, action)->
     return unless KD.isLoggedIn()


Fix: "encoding issue in comment edits"

Fix: "encoding issue in comment edits"

--- client/Social/Activity/views/comments/newcommentform.coffee
+++ client/Social/Activity/views/comments/newcommentform.coffee
@@ -91,7 +91,7 @@
       cssClass  : "cancel-description"
       pistachio : "Press Esc to cancel"

-    @commentInput.setValue data.body
+    @commentInput.setValue Encoder.htmlDecode data.body
     @commentInput.on "EscapePerformed", @bound "cancel"
     @commentInput.setFocus()


admin: minor: code cleanup

admin: minor: code cleanup

--- client/admin/lib/views/invitations/invitesomeoneview.coffee
+++ client/admin/lib/views/invitations/invitesomeoneview.coffee
@@ -1,15 +1,15 @@
 _                   = require 'lodash'
 kd                  = require 'kd'
+async               = require 'async'
+Promise             = require 'bluebird'
 remote              = require('app/remote').getInstance()
 KDView              = kd.View
 KDButtonView        = kd.ButtonView
 KDCustomScrollView  = kd.CustomScrollView
 KDCustomHTMLView    = kd.CustomHTMLView
 KDNotificationView  = kd.NotificationView
+showError           = require 'app/util/showError'
 InvitationInputView = require './invitationinputview'
-showError           = require 'app/util/showError'
-Promise             = require 'bluebird'
-async               = require 'async'

 module.exports = class InviteSomeoneView extends KDView


Remove some logs

Remove some logs

--- app/views/courses/ChooseLanguageModal.coffee
+++ app/views/courses/ChooseLanguageModal.coffee
@@ -13,14 +13,12 @@

   onClickLanguageChoiceButton: (e) ->
     @chosenLanguage = $(e.target).data('language')
-    console.log 'click language choice button'
     if @logoutFirst
       @logoutUser()
     else
       @saveLanguageSetting()

   logoutUser: ->
-    console.log 'logout'
     $.ajax({
       method: 'POST'
       url: '/auth/logout'
@@ -29,7 +27,6 @@
     })

   onUserLoggedOut: ->
-    console.log 'login new user'
     me.clear()
     me.fetch({
       url: '/auth/whoami'


make activities in stream persisted (from tour)

--- core/app/backbone/views/user_channel_suggestion_view.coffee
+++ core/app/backbone/views/user_channel_suggestion_view.coffee
@@ -15,7 +15,7 @@
       aantal_callbacks++
       activityCollection = @options.addToActivities
       if (aantal_callbacks == 2 )
-        channel.on 'destroy', -> activityCollection.remove(activity)
+        channel.on 'destroy', -> activity.destroy()

     @addActivityToStream
       success: (act)->
@@ -33,6 +33,7 @@
     last_fact_activity.fetch
       success: =>
         @options.addToActivities.unshift(last_fact_activity)
+        last_fact_activity.save()
         options.success(last_fact_activity)
       error: => options.error() if options.error


show legend by default

show legend by default

--- Tank/Plugins/charts.coffee
+++ Tank/Plugins/charts.coffee
@@ -145,7 +145,6 @@
         borderWidth: 0

       series: @data
-    @chart.legendHide()

 $(document).ready ->
   $('.tank-charts').each ->


Override tool controls render to do nothing Don't reset the details task values on render. Closes #8.

--- drawing-tools/tool-controls.coffee
+++ drawing-tools/tool-controls.coffee
@@ -59,6 +59,8 @@
   addFieldset: (legend) ->
     form = @el.querySelector 'form'
     form.insertAdjacentHTML 'afterbegin', "<fieldset><legend>#{legend}</legend></fieldset>"
+
+  render: ->

 module.exports = AccessibleControls


Removed bindonce dependency

Removed bindonce dependency

--- src/angular-mighty-datepicker.coffee
+++ src/angular-mighty-datepicker.coffee
@@ -6,18 +6,18 @@
         <<
       </button>

+        ng-repeat="month in months track by $index">

-            <th ng-repeat="day in ::month.weeks[1]"
+
               {{:: day.date.format('dd') }}

                 <div class="mighty-picker-calendar__day-wrapper"
-                  {{:: day.date.date() }}
+                  {{ day.date.date() }}

                   <div class="mighty-picker-calendar__day-marker"


Fix collection.

Fix collection.

--- src/chaplin/models/collection.coffee
+++ src/chaplin/models/collection.coffee
@@ -4,6 +4,7 @@
 Backbone = require 'backbone'
 EventBroker = require 'chaplin/lib/event_broker'
 Model = require 'chaplin/models/model'
+utils = require 'chaplin/lib/utils'

 # Abstract class which extends the standard Backbone collection
 # in order to add some functionality


:art: :memo:

:art: :memo:

--- lib/root-drag-and-drop.coffee
+++ lib/root-drag-and-drop.coffee
@@ -2,7 +2,8 @@

 {ipcRenderer, remote} = require 'electron'

-_ = require 'underscore-plus'
+# TODO: Support dragging external folders and using the drag-and-drop indicators for them
+# Currently they're handled in TreeView's drag listeners

 module.exports =
 class RootDragAndDropHandler
@@ -120,7 +121,6 @@

     {dataTransfer} = e

-    # TODO: support dragging folders from the filesystem -- electron needs to add support first
     return unless @isAtomTreeViewEvent(e)

     fromWindowId = parseInt(dataTransfer.getData('from-window-id'))


Randomly committing more untested shit.

Randomly committing more untested shit.

--- lib/simple-flake8-view.coffee
+++ lib/simple-flake8-view.coffee
@@ -103,15 +103,23 @@
     # return unless editor?
     # return unless editor.getGrammar().name == 'Python'

-    filePath = editor.getPath()

     @panel ?= atom.workspace.addModalPanel(item: this)
     @panel.show()

+    @storeFocusedElement()
+
+    if @previouslyFocusedElement[0] and @previouslyFocusedElement[0] isnt document.body
+      @eventElement = @previouslyFocusedElement[0]
+    else
+      @eventElement = atom.views.getView(atom.workspace)
+    @keyBindings = atom.keymap.findKeyBindings(target: @eventElement)
+
     @setLoading('Running Flake8 Linter...')

     # atom.workspaceView.append(this)
-    @focusFilterEditor()
+    # @focusFilterEditor()
+    filePath = editor.getPath()

     flake filePath, (errors) =>
       error_list = []
@@ -126,6 +134,8 @@
           message = error.message
         error_list.push(error)
       @setItems(error_list)
+
+      @focusFilterEditor()

   hide: ->
     @panel?.hide()


Unify the syntax in the kernel module

Unify the syntax in the kernel module

--- library/nucleus/kernel.coffee
+++ library/nucleus/kernel.coffee
@@ -187,4 +187,4 @@
         @connect.use plumbs.sender this
         @connect.use plumbs.logger this
         @connect.use m for m in middlewares
-        @connect.use(@middleware)
+        @connect.use @middleware


minor code reordering

minor code reordering

--- library/nucleus/kernel.coffee
+++ library/nucleus/kernel.coffee
@@ -296,22 +296,6 @@
         monitor paths.resolve __dirname, "../semantic"
         monitor directory for directory in subjects
         monitor library; monitor configs; this
-
-    # The utilitary method that is being called by either the kernel
-    # or scope implementation to establish the desirable facade for
-    # logging. The options from the config may be used to configure
-    # various options of the logger, such as output format, etc.
-    setupLoggingFacade: ->
-        assert _.isObject @logging = logger
-        assert format = "DD/MM/YYYY @ HH:mm:ss"
-        stamp = -> return moment().format format
-        options = timestamp: stamp, colorize: yes
-        options.level = nconf.get "log:level" or 0
-        noLevel = "No logging level is specified"
-        throw new Error noLevel unless options.level
-        assert console = logger.transports.Console
-        try do -> logger.remove console catch error
-        logger.add console, options; return this

     # This routine takes care of resolving all the necessary details
     # for successfully creating and running an HTTPS (SSL) server.
@@ -478,6 +462,22 @@
         @serveStaticDirectory established, Object()
         @serveStaticDirectory pub(); return this

+    # The utilitary method that is being called by either the kernel
+    # or scope implementation to establish the desirable facade for
+    # logging. The options from the config may be used to configure
+    # various options of the logger, such as output format, etc.
+    setupLoggingFacade: ->
+        assert _.isObject @logging = logger
+        assert format = "DD/MM/YYYY @ HH:mm:ss"
+        stamp = -> return moment().format format
+        options = timestamp: stamp, colorize: yes
+        options.level = nconf.get "log:level" or 0
+        noLevel = "No logging level is specified"
+        throw new Error noLevel unless options.level
+        assert console = logger.transports.Console
+        try do -> logger.remove console catch error
+        logger.add console, options; return this
+
     # Create and wire in an appropriate Connext middleware that will
     # serve the specified directory as the directory with a static
     # content. That is, it will expose it to the world (not list it).


Refactor the proxy selection into a separate algorithm

--- library/nucleus/scaled.coffee
+++ library/nucleus/scaled.coffee
@@ -82,15 +82,16 @@
     # refer to the `node-http-proxy` library for info on the proxy.
     # Also see `makeForward` method for details on load balancing!
     startupHttpsMaster: ->
-        assert _.isArray @queueOfHttps ?= new Array
+        assert _.isArray q = @queueOfHttps ?= Array()
         assert _.isObject options = @resolveSslDetails()
         assert _.isString host = nconf.get "master:host"
         assert _.isNumber port = nconf.get "master:https"
         cmp = (exm) -> (srv) -> exm.uuid is srv.uuid or 0
         remove = (srv) => _.remove @queueOfHttps, cmp(srv)
-        assert upgrade = @makeUpgraders @queueOfHttp, "https"
-        assert registr = @makeRegistrar @queueOfHttps, "https"
-        assert forward = @makeForwarder @queueOfHttps, "https"
+        assert registr = @makeRegistrar q, "http", null
+        assert selectr = @makeSelectors q, "http", null
+        assert forward = @makeForwarder q, "http", selectr
+        assert upgrade = @makeUpgraders q, "http", selectr
         @secureProxy = https.createServer options, forward
         assert @secureProxy; @secureProxy.listen port, host
         @secureProxy.on "upgrade", -> upgrade arguments...
@@ -106,14 +107,15 @@
     # refer to the `node-http-proxy` library for info on the proxy.
     # Also see `makeForward` method for details on load balancing!
     startupHttpMaster: ->
-        assert _.isArray @queueOfHttp ?= new Array
+        assert _.isArray q = @queueOfHttp ?= Array()
         assert _.isString host = nconf.get "master:host"
         assert _.isNumber port = nconf.get "master:http"
         cmp = (exm) -> (srv) -> exm.uuid is srv.uuid or 0
         remove = (srv) => _.remove @queueOfHttp, cmp(srv)
-        assert upgrade = @makeUpgraders @queueOfHttp, "http"
-        assert registr = @makeRegistrar @queueOfHttp, "http"
-        assert forward = @makeForwarder @queueOfHttp, "http"
+        assert registr = @makeRegistrar q, "http", null
+        assert selectr = @makeSelectors q, "http", null
+        assert forward = @makeForwarder q, "http", selectr
+        assert upgrade = @makeUpgraders q, "http", selectr
         assert @serverProxy = http.createServer forward
         assert @serverProxy; @serverProxy.listen port, host
         @serverProxy.on "upgrade", -> upgrade arguments...
@@ -147,12 +149,18 @@
             res.writeHead 500, "a proxy backend error"
             res.end format msg, err.toString(); this

+    makeSelectors: (queue, kind) -> (request, response) =>
+        proxy = queue.shift()
+        assert _.isObject proxy
+        num = queue.push proxy
+        assert num > 0; proxy
+
     # This is a factory method that produces request forwarders.
     # These are directly responsible for proxying an HTTP request
     # from the master server (frontend) to actual server (backend)
     # that does the job of handling the request. The forwarder is
     # also responsible for rotating (round-robin) servers queue!
-    makeForwarder: (queue, kind) -> (request, response) =>
+    makeForwarder: (queue, kind, select) -> (request, response) =>
         encrypted = request.connection.encrypted
         assert u = "#{request.url}".underline.yellow
         assert x = (encrypted and "HTTPS" or "HTTP").bold
@@ -161,7 +169,7 @@
         assert _.isArray(queue), "got invalid proxy queue"
         response.writeHead 504, reason if _.isEmpty queue
         return response.end(msg) and no if _.isEmpty queue
-        assert proxy = queue.shift(); queue.push proxy
+        assert proxy = select.apply this, arguments
         a = "#{proxy.target.host}:#{proxy.target.port}"
         assert a = "#{a.toLowerCase().underline.yellow}"
         logger.debug "Proxy %s request %s to %s", x, u, a
@@ -172,7 +180,7 @@
     # on one of the master servers. This is the functionality that
     # is required for WebSockets and other similar transports to
     # perform its operation correctly in a distributed environment.
-    makeUpgraders: (queue, kind) -> (request, response) =>
+    makeUpgraders: (queue, kind, select) -> (request, response) =>
         encrypted = request.connection.encrypted
         assert u = "#{request.url}".underline.yellow
         assert x = (encrypted and "HTTPS" or "HTTP").bold
@@ -181,7 +189,7 @@
         assert _.isArray(queue), "got invalid proxy queue"
         response.writeHead 504, reason if _.isEmpty queue
         return response.end(msg) and no if _.isEmpty queue
-        assert proxy = queue.shift(); queue.push proxy
+        assert proxy = select.apply this, arguments
         a = "#{proxy.target.host}:#{proxy.target.port}"
         assert a = "#{a.toLowerCase().underline.yellow}"
         logger.debug "Proxy %s upgrade %s to %s", x, u, a


update mark color, set as var

update mark color, set as var

--- app/lib/canvas-graph.coffee
+++ app/lib/canvas-graph.coffee
@@ -122,15 +122,16 @@
     @element = document.createElement('div')
     @element.className = "mark"

+    bgColor = "#fc4541"
     @element.innerHTML = """
-

+

-

-
III

+

+
III

-

-
III

+

+
III

     """

@@ -138,8 +139,8 @@
     @element.style.position = 'absolute'
     @element.style.top = e.target.offsetTop + "px"
     @element.style.height = (@canvas.height - 2) + 'px'
-    @element.style.backgroundColor = 'rgba(255,0,0,.5)'
-    @element.style.borderBottom = '2px solid red'
+    @element.style.backgroundColor = 'rgba(252,69,65,.6)'
+    @element.style.borderBottom = "2px solid #{bgColor}"
     @element.style.pointerEvents = 'auto'
     @element.style.textAlign = 'center'


Escape rootPath too

Escape rootPath too

--- kites/sharedHosting/api.coffee
+++ kites/sharedHosting/api.coffee
@@ -158,7 +158,7 @@

     {username, version, appName, userAppPath} = options

-    appRootPath   = "/opt/Apps/#{username}/#{appName}"
+    appRootPath   = escapePath "/opt/Apps/#{username}/#{appName}"
     latestPath    = escapePath "/opt/Apps/#{username}/#{appName}/latest"
     versionedPath = escapePath "/opt/Apps/#{username}/#{appName}/#{version}"


Add menu item

Add menu item


Client: Activity: Updated render method of ChannelThreadPane. Added props.children to render them

--- client/activity/lib/components/channelthreadpane/index.coffee
+++ client/activity/lib/components/channelthreadpane/index.coffee
@@ -91,6 +91,7 @@
       <aside className='ChannelThreadPane-sidebar'>
         {@renderSidebar()}
       </aside>
+      {@props.children}
     </div>


Search completion; implement <Enter> timer.

With suggested completions appearing in the vomnibar, the meaning of
<Enter> can change when the completion list is updated, which happens
asynchronously.  The user can end up selecting something they didn't
intend to ... which is bad.

This implements a temporary fix.  When the completion list updates (and
the update might affect what the user expects <Enter> to do), we block
<Enter> for a short time (250ms).  That's pretty bad too.

These UX issues around search completion are proving difficult to get
right.  I will open an issue to discuss them soon.

--- pages/vomnibar.coffee
+++ pages/vomnibar.coffee
@@ -69,6 +69,7 @@
     @completions = []
     @previousAutoSelect = null
     @previousInputValue = null
+    @lastUpdateTime = null
     @suppressedLeadingKeyword = null
     @selection = @initialSelectionValue
     @keywords = []
@@ -195,8 +196,13 @@
       @selection = @completions.length - 1 if @selection < @initialSelectionValue
       @updateSelection()
     else if (action == "enter")
+      # <Enter> immediately after new suggestions have been posted is ignored.  It's all too common that the
+      # user gets results they weren't intending.
+      return if @lastUpdateTime? and new Date() - @lastUpdateTime < 250 and @inputContainsASelectionRange()
+      @lastUpdateTime = null
       if @selection == -1
         query = @input.value.trim()
+        # <Enter> on an empty query is a no-op.
         return unless 0 < query.length
         # If the user types something and hits enter without selecting a completion from the list, then:
         #   - If a search URL has been provided, then use it.  This is custom search engine request.
@@ -241,6 +247,8 @@
     true

   onKeypress: (event) =>
+    # The user is typing. They know what they're doing.
+    @lastUpdateTime = null
     # Handle typing together with prompted text.
     unless event.altKey or event.ctrlKey or event.metaKey
       if @inputContainsASelectionRange()
@@ -284,6 +292,7 @@
         @previousAutoSelect = null if @completions[0]?.autoSelect and @completions[0]?.forceAutoSelect
         @updateSelection()
         @addPromptedText()
+        @lastUpdateTime = new Date()
         callback?()

   updateOnInput: =>


Fix vomnibar flicker after tabs mode.

Fix vomnibar flicker after tabs mode.

In tabs mode, the vomnibar is pre-populated when the query is empty.
If, as part of hiding the vomnibar, we reset it, it becomes populated
again, so the display style is reset from "none" to "block".  Therefore,
the completion list is briefly visible when the vomnibar is later
reactivated.

Solution:
- Do not run `@update()` from `@reset()`.

--- pages/vomnibar.coffee
+++ pages/vomnibar.coffee
@@ -51,6 +51,7 @@
   setCompleter: (completer) ->
     @completer = completer
     @reset()
+    @update(true)

   setRefreshInterval: (refreshInterval) -> @refreshInterval = refreshInterval

@@ -66,7 +67,6 @@
     @updateTimer = null
     @completions = []
     @selection = @initialSelectionValue
-    @update(true)

   updateSelection: ->
     # We have taken the option to add some global state here (previousCompletionType) to tell if a search


remove deferred

remove deferred

--- lib/client/javascripts/nali/deferred.js.coffee
+++ lib/client/javascripts/nali/deferred.js.coffee
@@ -1,16 +0,0 @@
-#Nali.extend Deferred:
-#
-#  new: ( callback ) ->
-#    obj         = Object.create @
-#    obj.defer   = callback
-#    obj.awaits  = []
-#    obj.results = []
-#    obj
-#
-#  await: ( callback ) ->
-#    wrapper = ( args... ) =>
-#      @results.push callback args...
-#      @awaits.splice @awaits.indexOf( wrapper ), 1
-#      @defer @results unless @awaits.length
-#    @awaits.push wrapper
-#    wrapper


Fix filter Regex (Fixes #1)

Fix filter Regex (Fixes #1)

--- lib/guard/jasmine/phantomjs/run-jasmine.coffee
+++ lib/guard/jasmine/phantomjs/run-jasmine.coffee
@@ -12,7 +12,7 @@
       condition = (if typeof testFx is 'string' then eval testFx else testFx())
     else
       if not condition
-        console.log "JasmineResult: #{ JSON.stringify { error: "Timeout requesting Jasmine test runner!" } }"
+        console.log JSON.stringify { error: "Timeout requesting Jasmine test runner!" }
         phantom.exit(1)
       else
         if typeof onReady is 'string' then eval onReady else onReady()
@@ -23,7 +23,7 @@
 # Check arguments of the script.
 #
 if phantom.args.length isnt 1
-  console.log "JasmineResult: #{ JSON.stringify { error: "Wrong usage of PhantomJS script!" } }"
+  console.log JSON.stringify { error: "Wrong usage of PhantomJS script!" }
   phantom.exit()
 else
   url = phantom.args[0]
@@ -33,7 +33,8 @@
 # Output the Jasmine test runner result as JSON object.
 # Ignore all other calls to console.log
 #
-page.onConsoleMessage = (msg) -> console.log(RegExp.$1) if /^JasmineResult: (.*)$/.test(msg)
+page.onConsoleMessage = (msg) ->
+  console.log(RegExp.$1) if /^JasmineResult: ([\s\S]*)$/.test(msg)

 # Open web page and run the Jasmine test runner
 #
@@ -46,7 +47,9 @@

   else
     # Wait until the Jasmine test is run
-    waitFor -> page.evaluate -> if document.body.querySelector '.finished-at' then true else false
+    waitFor ->
+      page.evaluate ->
+        if document.body.querySelector '.finished-at' then true else false
     , ->
         # Jasmine test runner has finished, extract the result from the DOM
         page.evaluate ->


separate function to create the package query by filtering an upstream repo for tests

Signed-off-by: Dominik Richter <b7323add517e187efe0283b76511b7ed762bfe13@gmail.com>

--- test/spec/controllers/main.coffee
+++ test/spec/controllers/main.coffee
@@ -273,18 +273,23 @@
     paginateResponse @httpBackend, '/v1/packages', allPackages1, () -> scope.fetchPackages()
     expect(scope.packages.length).toBe(allPackages1.length)

-  it 'should list /packages compared to upstream repositories (by ID)', () ->
+  query_packages_with_upstream_repo_id = (httpBackend)->
     # get repos
-    paginateResponse @httpBackend, '/v1/repositories', allRepos1, () -> scope.fetchRepos()
+    paginateResponse httpBackend, '/v1/repositories', allRepos1, () -> scope.fetchRepos()
     expect(scope.repos.length).toBe(allRepos1.length)
     # set packages list when selecting a repository
     ps = allPackages2
     scope.selectRepo(allRepos1[0])
     opts = {'query': [['repo',allRepos1[0].id],['outdated','true']]}
-    dontPaginateResponse @httpBackend, '/v1/packages', ps, (() -> scope.fetchPackages()), opts
+    dontPaginateResponse httpBackend, '/v1/packages', ps, (() -> scope.fetchPackages()), opts
     # results
     expect(scope.allPackages.length).toBe(ps.length)
     expect(scope.packages.length).toBe(ps.length)
+    # return the packages we returned from the query
+    return ps
+
+  it 'should list /packages compared to upstream repositories (by ID)', () ->
+    ps = query_packages_with_upstream_repo_id(@httpBackend)
     # test both packages
     for idx in [0,1]
       res_p = scope.packages[idx]


+ ExpressionStack: tested .addBinaryOperator

--- test/unit/expression-stack.coffee
+++ test/unit/expression-stack.coffee
@@ -30,6 +30,18 @@
     it ".evaluate() returns true", ->
       testedStack.evaluate().should.equal true

+    describe "after .addBinaryOperator((a, b) => a() && b())", ->
+      beforeEach ->
+        testedStack.addBinaryOperator (a, b) -> a() && b()
+
+      [true, false].forEach (value) ->
+        describe "after .addOperand(() => #{value})", ->
+          beforeEach ->
+            testedStack.addOperand () -> value
+
+          it ".evaluate() returns false", ->
+            testedStack.evaluate().should.equal value
+
   describe "after .push()", ->
     beforeEach ->
       testedStack.push()
@@ -53,13 +65,52 @@
       it ".stackName is 'bottom'", ->
         testedStack.stackName.should.equal "bottom"

-    describe "after .addOperand(() => false) and .pop()", ->
+    describe "after .addOperand(() => false)", ->
       beforeEach ->
         testedStack.addOperand () -> false
-        testedStack.pop()

-      it ".evaluate() returns false", ->
-        testedStack.evaluate().should.equal false
+      describe "after .pop()", ->
+        beforeEach ->
+          testedStack.pop()
+
+        it ".evaluate() returns false", ->
+          testedStack.evaluate().should.equal false
+
+      describe "after .addBinaryOperator((a, b) => a() && b())", ->
+        beforeEach ->
+          testedStack.addBinaryOperator (a, b) -> a() && b()
+
+        describe "after .addOperand(() => true) and .pop()", ->
+          beforeEach ->
+            testedStack.addOperand () -> true
+            testedStack.pop()
+
+          it ".evaluate() returns false", ->
+            testedStack.evaluate().should.equal false
+
+          it ".stackName is 'bottom'", ->
+            testedStack.stackName.should.equal "bottom"
+
+        describe "after .forcePop()", ->
+          beforeEach ->
+            testedStack.forcePop()
+
+          it ".evaluate() returns false", ->
+            testedStack.evaluate().should.equal false
+
+          it ".stackName is 'bottom'", ->
+            testedStack.stackName.should.equal "bottom"
+
+        describe "after .popWhenReady() and .addOperand(() => true)", ->
+          beforeEach ->
+            testedStack.popWhenReady()
+            testedStack.addOperand () -> true
+
+          it ".evaluate() returns false", ->
+            testedStack.evaluate().should.equal false
+
+          it ".stackName is 'bottom'", ->
+            testedStack.stackName.should.equal "bottom"

     describe "after .forcePop()", ->
       beforeEach ->


fix(unread): Mark as read twice without visiting another unread thread

--- src/flux/stores/message-store.coffee
+++ src/flux/stores/message-store.coffee
@@ -163,20 +163,21 @@
     # Override canBeUndone to return false so that we don't see undo
     # prompts (since this is a passive action vs. a user-triggered
     # action.)
-    return unless @_thread and @_thread.unread
+    return if not @_thread
     return if @_lastLoadedThreadId is @_thread.id
     @_lastLoadedThreadId = @_thread.id

-    markAsReadDelay = NylasEnv.config.get('core.reading.markAsReadDelay')
-    markAsReadId = @_thread.id
-    return if markAsReadDelay < 0
-
-    setTimeout =>
-      return unless markAsReadId is @_thread?.id and @_thread.unread
-      t = new ChangeUnreadTask(thread: @_thread, unread: false)
-      t.canBeUndone = => false
-      Actions.queueTask(t)
-    , markAsReadDelay
+    if @_thread.unread
+      markAsReadDelay = NylasEnv.config.get('core.reading.markAsReadDelay')
+      markAsReadId = @_thread.id
+      return if markAsReadDelay < 0
+
+      setTimeout =>
+        return unless markAsReadId is @_thread?.id and @_thread.unread
+        t = new ChangeUnreadTask(thread: @_thread, unread: false)
+        t.canBeUndone = => false
+        Actions.queueTask(t)
+      , markAsReadDelay

   _onToggleAllMessagesExpanded: =>
     if @hasCollapsedItems()


finalise fix for incorrect usage of provider in factory. fixes #6

--- src/lib/named-routes.coffee
+++ src/lib/named-routes.coffee
@@ -1,106 +1,116 @@
-angular.module "zj.namedRoutes", []
-  .provider "$NamedRouteService", [
-    "$locationProvider"
-    ($locationProvider) ->
-      prefix = $locationProvider.hashPrefix()
-
-      this.$get = [
-        '$rootScope'
-        '$route'
-        '$location'
-        '$log'
-        ($rootScope, $route, $location, $log) ->
-          type = (obj) ->
-            if obj == undefined or obj == null
-              return String obj
-            classToType =
-              '[object Boolean]': 'boolean'
-              '[object Number]': 'number'
-              '[object String]': 'string'
-              '[object Function]': 'function'
-              '[object Array]': 'array'
-              '[object Date]': 'date'
-              '[object RegExp]': 'regexp'
-              '[object Object]': 'object'
-            classToType[Object.prototype.toString.call(obj)]
-
-          routeService =
-            reverse: (routeName, options) ->
-              # /* Step through routes,
-              #     pick one with matching name,
-              #     replace placeholders with supplied arguments,
-              #     return built url.
-              # */
-              routes = routeService.match(routeName);
-              if routes.length == 1
-                  return routeService.resolve options, routes[0]
-              else if routes.length is 0
-                  throw new Error 'Route ' + routeName + ' not found'
-              throw new Error 'Multiple routes matching ' + routeName + ' were found'
-
-            match: (routeName) ->
-              routes = []
-              angular.forEach $route.routes, (config, route) ->
-                  if config.name is routeName
-                      routes.push route
-              return routes
-
-            resolve: (options, route) ->
-              pattern = /(\:\w+)/g
-
-              if route is undefined
-                throw new Error("Can't resolve undefined into a route")
-
-              count = 0
-              prefix + route.replace pattern, (match, ..., offset) ->
-                if type(options) is 'array'
-                  output = options[count]
-                  count++
-                  return output
-                else if type(options) is 'object'
-                  return options[match.slice(1)]
-
-      ]
-      return this
-    ]
-
-    .directive 'namedUrl', [
-      '$log'
-      '$NamedRouteService'
-      ($log, $NamedRouteService) ->
-        # /* Given that the following route exists :
-        #         .when('/products/:cat/:id', {
-        #             controller: 'OptionalController',
-        #             template: '/static/javascripts/application/templates/optional-template.html',
-        #             name: 'item-detail'
-        #         })
-
-        #    And that an element is present :
-        #      Salmon Info
-
-        #      return the following
-        #      Salmon Info
-        #  */
-        restrict: "AC"
-        link: (scope, element, attributes) ->
-            options = {}
-            for key in attributes
-              if key.indexOf('kwarg') is 0
-                newKey = key.splice 0, 5
-                newKey = newKey.charAt(0).toLowerCase() + newKey.slice(1)
-                options[newKey] = attributes[key]
-
-            url = $NamedRouteService.reverse attributes.namedUrl, options
-            element.attr('href', url)
-
-      ]
-
-    .filter 'url', [
-      '$route'
-      '$NamedRouteService'
-      ($route, $NamedRouteService) ->
-        (input, options) ->
-          $NamedRouteService.reverse input, options
-    ]
+angular.module "zj.namedRoutes", []
+
+  .provider "$NamedRouteService", [
+    "$locationProvider"
+    ($locationProvider) ->
+      # pull prefix as set by the project integrator :
+      # angular.module('yourmodule', ['zj.namedRoutes'])
+      #   .config([
+      #     '$locationProvider',
+      #     function($locationProvider){
+      #         $locationProvider.hashPrefix("#!")
+      #         $locationProvider.html5Mode(false)
+      #          ...
+
+      prefix = if $locationProvider.html5Mode() then $locationProvider.hashPrefix() else ""
+
+      this.$get = [
+        '$rootScope'
+        '$route'
+        '$location'
+        '$log'
+        ($rootScope, $route, $location, $log) ->
+          type = (obj) ->
+            if obj == undefined or obj == null
+              return String obj
+            classToType =
+              '[object Boolean]': 'boolean'
+              '[object Number]': 'number'
+              '[object String]': 'string'
+              '[object Function]': 'function'
+              '[object Array]': 'array'
+              '[object Date]': 'date'
+              '[object RegExp]': 'regexp'
+              '[object Object]': 'object'
+            classToType[Object.prototype.toString.call(obj)]
+
+          routeService =
+            reverse: (routeName, options) ->
+              # /* Step through routes,
+              #     pick one with matching name,
+              #     replace placeholders with supplied arguments,
+              #     return built url.
+              # */
+              routes = routeService.match(routeName);
+              if routes.length == 1
+                  return routeService.resolve options, routes[0]
+              else if routes.length is 0
+                  throw new Error 'Route ' + routeName + ' not found'
+              throw new Error 'Multiple routes matching ' + routeName + ' were found'
+
+            match: (routeName) ->
+              routes = []
+              angular.forEach $route.routes, (config, route) ->
+                  if config.name is routeName
+                      routes.push route
+              return routes
+
+            resolve: (options, route) ->
+              pattern = /(\:\w+)/g
+
+              if route is undefined
+                throw new Error("Can't resolve undefined into a route")
+
+              count = 0
+              prefix + route.replace pattern, (match, ..., offset) ->
+                if type(options) is 'array'
+                  output = options[count]
+                  count++
+                  return output
+                else if type(options) is 'object'
+                  return options[match.slice(1)]
+
+      ]
+      return this
+    ]
+
+    .directive 'namedUrl', [
+      '$log'
+      '$NamedRouteService'
+      ($log, $NamedRouteService) ->
+        # /* Given that the following route exists :
+        #         .when('/products/:cat/:id', {
+        #             controller: 'OptionalController',
+        #             template: '/static/javascripts/application/templates/optional-template.html',
+        #             name: 'item-detail'
+        #         })
+
+        #    And that an element is present :
+        #      Salmon Info
+
+        #      return the following
+        #      Salmon Info
+        #  */
+        restrict: "AC"
+        link: (scope, element, attributes) ->
+            options = {}
+            for key in attributes
+              if key.indexOf('kwarg') is 0
+                newKey = key.splice 0, 5
+                newKey = newKey.charAt(0).toLowerCase() + newKey.slice(1)
+                options[newKey] = attributes[key]
+
+            url = $NamedRouteService.reverse attributes.namedUrl, options
+            element.attr('href', url)
+
+      ]
+
+    .filter 'url', [
+      '$route'
+      '$NamedRouteService'
+      ($route, $NamedRouteService) ->
+        (input, options) ->
+          $NamedRouteService.reverse input, options
+    ]


(fix) {{ a contains false }} was always false

--- src/liquid/condition.coffee
+++ src/liquid/condition.coffee
@@ -19,7 +19,7 @@
     '<=': (cond, left, right) -> left <= right
     '>=': (cond, left, right) -> left >= right
     'contains': (cond, left, right) ->
-      if left and right
+      if typeof left?.indexOf is 'function'
         left.indexOf(right) >= 0
       else
         false


operations: s/geoloc/geo/

operations: s/geoloc/geo/

--- src/local/operations.coffee
+++ src/local/operations.coffee
@@ -23,19 +23,19 @@
         accessModel: "open"
         publishModel: "publishers"
         defaultAffiliation: "member"
-    'geoloc/previous':
+    'geo/previous':
         title: "#{user} Previous Location"
         description: "Where #{user} has been before"
         accessModel: "open"
         publishModel: "publishers"
         defaultAffiliation: "member"
-    'geoloc/current':
+    'geo/current':
         title: "#{user} Current Location"
         description: "Where #{user} is at now"
         accessModel: "open"
         publishModel: "publishers"
         defaultAffiliation: "member"
-    'geoloc/next':
+    'geo/next':
         title: "#{user} Next Location"
         description: "Where #{user} intends to go"
         accessModel: "open"


re-form config-json-generator

re-form config-json-generator

--- src/lib/config-json-generator.coffee
+++ src/lib/config-json-generator.coffee
@@ -105,8 +105,8 @@

         for own k, sub of dominant
             if merged[k]? and typeof merged[k] is 'object' and v?
-                    # merges subobject
-                    merged[k] = @merge sub, merged[k]
+                # merges subobject
+                merged[k] = @merge sub, merged[k]
             else
                 merged[k] = sub


Activity Ticker: Personalize ticker events

--- client/Social/Activity/views/activitytickeritem.coffee
+++ client/Social/Activity/views/activitytickeritem.coffee
@@ -10,6 +10,7 @@
     JApp               : AppLinkView
     JTag               : TagLinkView
     JGroup             : GroupLinkView
+    JStatusUpdate      : ActivityLinkView

 class ActivityTickerFollowItem extends ActivityTickerBaseItem
   constructor: (options = {}, data) ->
@@ -33,18 +34,41 @@
   constructor: (options = {}, data) ->
     super options, data

-    {source, target} = data
+    {source, target, subject} = data

     @avatar    = new AvatarView
       size     : width: 28, height: 28
       cssClass : "avatarview"
-    , target
+    , source

-    @actor    = new ProfileLinkView null, target
-    @object   = new @itemLinkViewClassMap[source.bongo_.constructorName] null, source
+    @liker    = new ProfileLinkView null, source
+    @origin   = new ProfileLinkView null, target
+    @subj     = new @itemLinkViewClassMap[subject.bongo_.constructorName] null, subject
+
+

   pistachio: ->
-    """{{> @avatar}} {{> @actor}} liked {{> @object}}"""
+    {source, target, subject} = @getData()
+    template = """{{> @avatar}} {{> @liker}} liked {{> @origin}} s {{> @subj}}"""
+
+    # i did something
+    if  source.getId() is KD.whoami().getId()
+      # if user liked his/her post
+      if source.getId() is target.getId() then \
+        return """{{> @avatar}} You liked your {{> @subj}}"""
+      else
+        return """{{> @avatar}} You liked {{> @origin}} s {{> @subj}}"""
+
+    # someone did something to you
+    if target.getId() is KD.whoami().getId() then \
+      return """{{> @avatar}} {{> @liker}} liked your {{> @subj}}"""
+
+    # if user liked his/her post
+    if source.getId() is target.getId() then \
+      return """{{> @avatar}} {{> @liker}} liked their {{> @subj}}"""
+
+    # rest
+    """{{> @avatar}} {{> @liker}} liked {{> @origin}} s {{> @subj}}"""

 class ActivityTickerMemberItem extends ActivityTickerBaseItem
   constructor: (options = {}, data) ->


Client: Activity: Updated setChannelParticipantsInputQuery action event name.

--- client/activity/lib/flux/chatinput/actions/user.coffee
+++ client/activity/lib/flux/chatinput/actions/user.coffee
@@ -100,8 +100,8 @@
 setChannelParticipantsInputQuery = (query) ->

   if query
-    { SET_PARTICIPANT_USERS_QUERY } = actionTypes
-    dispatch SET_PARTICIPANT_USERS_QUERY, { query }
+    { SET_CHANNEL_PARTICIPANTS_USERS_QUERY } = actionTypes
+    dispatch SET_CHANNEL_PARTICIPANTS_USERS_QUERY, { query }
     resetChannelParticipantsSelectedIndex()
     appActions.user.searchAccounts query


Activity: Sidebar: Added showManagedMachineAddedModal method.

--- client/app/lib/activity/sidebar/activitysidebar.coffee
+++ client/app/lib/activity/sidebar/activitysidebar.coffee
@@ -752,3 +752,9 @@
     for list in @machineLists
       for box in list.machineBoxes when expandedBoxUIds[box.data.machine.uid]
         box.expandList()
+
+
+  showManagedMachineAddedModal: (info, machine) ->
+
+    box = @getMachineBoxByMachineUId machine.uid
+    box.machineItem.showMachineConnectedPopup info


ActivitySidebar: Revive payload environment data.

--- client/app/lib/activity/sidebar/activitysidebar.coffee
+++ client/app/lib/activity/sidebar/activitysidebar.coffee
@@ -102,6 +102,8 @@
     # @on 'MoreWorkspaceModalRequested', @bound 'handleMoreWorkspacesClick'
     @on 'ReloadMessagesRequested',     @bound 'handleReloadMessages'

+    environmentDataProvider.revive()
+
     mainController.ready =>
       whoami().on 'NewWorkspaceCreated', @bound 'newWorkspaceCreated'


delay wording converted to followups

delay wording converted to followups

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -279,7 +279,7 @@
       loader        :
         color       : "#ffffff"
       title         : "Please wait while we are publishing your app..."
-      delay         :
+      followUps     :
         duration    : 10000
         title       : "We are still working on it. Your app will be published soon..."
       duration      : 120000


Change method 'resetAvatar' to use RocketFile

--- server/methods/setAvatarFromService.coffee
+++ server/methods/setAvatarFromService.coffee
@@ -21,6 +21,6 @@

 		user = Meteor.user()

-		Avatars.remove {'copies.avatars.name': user.username}
+		RocketFileAvatarInstance.deleteFile user.username

 		Meteor.users.update user._id, {$unset: {avatarOrigin: 1}}


Make epoch conversions parse date first

Make epoch conversions parse date first

--- server/mixins/update_indicator_data.coffee
+++ server/mixins/update_indicator_data.coffee
@@ -41,7 +41,9 @@
 CONVERSIONS =
   epoch:
     integer: (value) ->
-      new Date(value).getFullYear()
+      date = new Date(parseInt(value, 10)).getFullYear()
+      return date
+
   decimalPercentage:
     integer: (value)->
       value * 100


Fix button size scaling

Fix button size scaling

--- _assets/javascripts/sections/ello_button.coffee
+++ _assets/javascripts/sections/ello_button.coffee
@@ -73,16 +73,18 @@
     updateButtonIcon = (size) ->
       size = 'large' if (size == undefined || size == '')
       switch size
-        when 'small'  then size = "18"
-        when 'medium' then size = "24"
-        when 'large'  then size = "40"
+        when 'small'  then size = "18px"
+        when 'medium' then size = "24px"
+        when 'large'  then size = "40px"
         else null

       # grabbing all of the ello icons
       $elloIcons = $('span.button-holder').children().children()
       # iterating through the icons and changing their width
       for icon in $elloIcons
-        icon.style.width = size
+        icon.style.width                = size
+        icon.parentElement.style.width  = size
+        icon.parentElement.style.height = size

     $(document).on "change", "input[type=radio]", ->
       size     = $(this).val()


Temporary fix to set the speed of video player to 1.0 regardless of previous state

--- common/lib/xmodule/xmodule/js/src/video/display.coffee
+++ common/lib/xmodule/xmodule/js/src/video/display.coffee
@@ -33,11 +33,7 @@
     @speeds = ($.map @videos, (url, speed) -> speed).sort()

   setSpeed: (newSpeed) ->
-    if @videos[newSpeed] != undefined
-      @speed = newSpeed
-      $.cookie('video_speed', "#{newSpeed}", expires: 3650, path: '/')
-    else
-      @speed = '1.0'
+    @speed = '1.0'

   embed: ->
     @player = new VideoPlayer video: this


Workspace: Removed marking user as active in fetchUsers.

--- client/landing/CollaborativeWorkspace/collaborativeworkspace.coffee
+++ client/landing/CollaborativeWorkspace/collaborativeworkspace.coffee
@@ -139,8 +139,7 @@
       KD.remote.api.JAccount.some { "profile.nickname": { "$in": usernames } }, {}, (err, jAccounts) =>
         for user in jAccounts
           {nickname} = user.profile
-          @users[nickname]       = user
-          @activeUsers[nickname] = user
+          @users[nickname] = user
         @emit "WorkspaceUsersFetched"

   createPanel: (callback = noop) ->


Ace: Implemented AceApplicationTabView to handle tab close behavior.

--- client/landing/app/Applications/Ace.kdapplication/aceappview.coffee
+++ client/landing/app/Applications/Ace.kdapplication/aceappview.coffee
@@ -3,18 +3,15 @@

     super options, data

-    @aceViews   = {}
-    @timestamp  = Date.now()
-    @appManager = KD.getSingleton "appManager"
-
-    @tabHandleContainer = new ApplicationTabHandleHolder
-      delegate: @
-
-    @tabView = new ApplicationTabView
-      delegate             : @
-      tabHandleContainer   : @tabHandleContainer
-      saveSession          : yes
-      sessionName          : "AceTabHistory"
+    @aceViews            = {}
+    @timestamp           = Date.now()
+    @appManager          = KD.getSingleton "appManager"
+    @tabHandleContainer  = new ApplicationTabHandleHolder delegate: @
+    @tabView             = new AceApplicationTabView
+      delegate           : @
+      tabHandleContainer : @tabHandleContainer
+      saveSession        : yes
+      sessionName        : "AceTabHistory"

     @on "SessionDataCreated", (@sessionData) =>

@@ -119,7 +116,7 @@

   addNewTab: (file) ->
     file = file or FSHelper.createFileFromPath 'localfile:/Untitled.txt'
-    aceView = new AceView {}, file
+    aceView = new AceView delegate: @, file
     aceView.on 'KDObjectWillBeDestroyed', => @removeOpenDocument aceView
     @aceViews[file.path] = aceView
     @setViewListeners aceView
@@ -195,3 +192,42 @@
       {{> @tabHandleContainer}}
       {{> @tabView}}
     """
+
+class AceApplicationTabView extends ApplicationTabView
+
+  handleTabCloseAction: (pane) ->
+    {ace} = pane.getOptions().aceView
+    file  = ace.getData()
+
+    return @removePane pane  unless ace.isContentChanged()
+    modal = new KDModalView
+      cssClass      : "modal-with-text"
+      title         : "Do you want to save your changes?"
+      content       : "
Your changes will be lost if you don't save them.
"
+      overlay       : yes
+      buttons       :
+        "SaveClose" :
+          cssClass  : "modal-clean-gray"
+          title     : "Save and Close"
+          callback  : =>
+            if file.path.indexOf("localfile:") is 0
+              file.once "fs.saveAs.finished", => @removePane pane
+              ace.requestSaveAs()
+              modal.destroy()
+            else
+              ace.requestSave()
+              @closePaneAndModal pane, modal
+        "DontSave"  :
+          cssClass  : "modal-clean-gray"
+          title     : "Don't Save"
+          callback  : =>
+            @closePaneAndModal pane, modal
+        "Cancel"    :
+          cssClass  : "modal-cancel"
+          title     : "Cancel"
+          callback  : =>
+            modal.destroy()
+
+  closePaneAndModal: (pane, modal) ->
+    @removePane pane
+    modal.destroy()


do not show new link on unparented neurons

--- app/assets/javascripts/admin/moi_tree.js.coffee
+++ app/assets/javascripts/admin/moi_tree.js.coffee
@@ -127,8 +127,12 @@
     $popover = $(".popover")
     # format:
     $popover.find(".popover-title").html(node.title)
-    $popover.find(".new-child-link")
-            .attr("href", "/admin/neurons/new?parent_id=#{node.id}")
+    $newChildLink = $popover.find(".new-child-link")
+    if node.parent_id
+      $newChildLink.show()
+                   .attr("href", "/admin/neurons/new?parent_id=#{node.id}")
+    else
+      $newChildLink.hide()
     $popover.find(".edit-link")
             .attr("href", "/admin/neurons/#{node.id}/edit")
     $popover.removeClass("hidden")


intermediate commit before trying outlet method

--- app/assets/javascripts/hanuman/router.js.coffee
+++ app/assets/javascripts/hanuman/router.js.coffee
@@ -2,8 +2,13 @@

 App.Router.map ()->
   @resource 'survey_templates'
-  @resource('survey_template', { path: 'survey_templates/:survey_template_id' })
-  @resource 'questions'
+  @resource 'survey_template',
+    path: 'survey_templates/:survey_template_id'
+  , ->
+    @resource 'steps',
+    path: 'steps/:step'
+  return
+

 App.SurveyTemplatesRoute = Ember.Route.extend({
@@ -16,16 +21,6 @@
   model: (params) ->
     console.log("in SurveyTemplateRoute")
     console.log(params)
-    @store.find('surveyTemplate', params.survey_template_id)
+    @store.find('survey_template', params.survey_template_id)
 })

-# App.SurveyQuestionRoute = Ember.Route.extend({
-#   model: (params) ->
-#     @store.find('survey_question', params.survey_question_id)
-# })
-
-App.QuestionsRoute = Ember.Route.extend({
-  model: ->
-    console.log("in QuestionsRoute")
-    @store.find('question')
-})


Destroy confirm dialog when user has confirmed

--- app/assets/javascripts/videos/destroy.js.coffee
+++ app/assets/javascripts/videos/destroy.js.coffee
@@ -12,6 +12,7 @@
           $.rails.allowAction = () -> true
           element.trigger('click')
           $.rails.allowAction = allowActionOrig
+          dialog.destroy()
       dialog.onClose () ->
         element.focus()
       dialog.show()


Disable the UI for editing / voting on archived questions

--- app/assets/javascripts/views/answer-item.coffee
+++ app/assets/javascripts/views/answer-item.coffee
@@ -17,6 +17,7 @@
         connectToSortable: '.personal.answer-list'
         delay: 1
         helper: => @buildDragHelper()
+        start: (e) => false if @answer.question().archived()
       )
       @updateRankIndicator()

@@ -24,6 +25,8 @@
       @data('position', position)

     @answer.getField('body').onChange (body) => @body.html(markdown.toHTML(body))
+
+    @on 'mousedown', => false if @answer.question().archived()

   updateRankIndicator: ->
     if @answer.preferenceForCurrentUser()


Limit direction searches to Poland

Limit direction searches to Poland

--- app/assets/javascripts/widgets/direction.coffee
+++ app/assets/javascripts/widgets/direction.coffee
@@ -208,18 +208,19 @@

 searchRoute = (search_params, callback) ->
   return callback(FOUND_ROUTE) if FOUND_ROUTE?
-
+
   request =
-    origin: search_params.start
-    destination: search_params.end
+    origin: search_params.start + ", Polska"
+    destination: search_params.end + ", Polska"
     travelMode: google.maps.TravelMode.WALKING
+    region: 'pl'

   gmap.directions.route request, (result, status) ->
     if status == google.maps.DirectionsStatus.OK
       FOUND_ROUTE = route = result.routes[0]
       route.path = route.overview_path.map (o) ->
         latitude: o.Ya, longitude: o.Za
-
+
       gmap.directionsRenderer.setDirections(result)
       gmap.onNextMovement -> callback(route)
     else


Fix exceptions

Fix exceptions

--- framer/Extras/TouchEmulator.coffee
+++ framer/Extras/TouchEmulator.coffee
@@ -122,8 +122,9 @@

 		if event.keyCode is @keyPanCode
 			cancelEvent(event)
-			@centerPoint = Utils.pointCenter(@touchPoint, @point)
-			@isPanKeyDown = false
+			if @touchPoint and @point
+				@centerPoint = Utils.pointCenter(@touchPoint, @point)
+				@isPanKeyDown = false

 	mousedown: (event) =>
@@ -151,26 +152,30 @@
 	mousemove: (event) =>

 		cancelEvent(event)
-
-		@startPoint ?=
+
+		@point =
 			x: event.pageX
 			y: event.pageY
-
-		@centerPoint ?= @startPoint
+
+		@startPoint ?= @point
+		@centerPoint ?= @point

 		if @isPinchKeyDown and not @isPanKeyDown
-			@touchPoint = Utils.pointAdd(@touchPointerInitialOffset, @pinchPoint(@point, @centerPoint))
-			@touchPointDelta = Utils.pointSubtract(@point, @touchPoint)
+			if @touchPointerInitialOffset and @centerPoint
+				@touchPoint = Utils.pointAdd(@touchPointerInitialOffset, @pinchPoint(@point, @centerPoint))
+				@touchPointDelta = Utils.pointSubtract(@point, @touchPoint)

 		if @isPinchKeyDown and @isPanKeyDown
-			@touchPoint = @panPoint(@point, @touchPointDelta)
+			if @touchPoint and @touchPointDelta
+				@touchPoint = @panPoint(@point, @touchPointDelta)

 		if @isPinchKeyDown or @isPanKeyDown
-			@touchPointLayer.visible = true
-			@touchPointLayer.midX = @touchPoint.x
-			@touchPointLayer.midY = @touchPoint.y
-
-		if @isPinchKeyDown or @isPanKeyDown
+			if @touchPoint
+				@touchPointLayer.visible = true
+				@touchPointLayer.midX = @touchPoint.x
+				@touchPointLayer.midY = @touchPoint.y
+
+		if (@isPinchKeyDown or @isPanKeyDown) and @touchPointDelta
 			dispatchTouchEvent("touchmove", @target, event, @touchPointDelta)
 		else
 			dispatchTouchEvent("touchmove", @target, event)


Fix follow message

Fix follow message

--- apps/personalize/client/views/categories.coffee
+++ apps/personalize/client/views/categories.coffee
@@ -50,7 +50,7 @@
     @followButtonViews[key].remove() if @followButtonViews[key]?
     @followButtonViews[key] = new FollowButton
       analyticsUnfollowMessage : 'Unfollowed gene from personalize gene suggestions'
-      analyticsFollowMessage   : 'Unfollowed gene from personalize gene suggestions'
+      analyticsFollowMessage   : 'Followed gene from personalize gene suggestions'
       modelName                : 'Gene'
       model                    : model
       el                       : el


durationchange tests added

durationchange tests added

--- spec/javascripts/audio_element_spec.coffee
+++ spec/javascripts/audio_element_spec.coffee
@@ -44,3 +44,9 @@
     $(no1.audio).trigger 'timeupdate'
     expect(no1.updatePlayedProgress).toHaveBeenCalled()
     expect(no1.updateTimeStatuses).toHaveBeenCalled()
+
+  it 'handler `durationchange` works correctly', ->
+    spyOn no1, 'updateTimeStatuses'
+    expect(no1.updateTimeStatuses).not.toHaveBeenCalled()
+    $(no1.audio).trigger 'timeupdate'
+    expect(no1.updateTimeStatuses).toHaveBeenCalled()


:white_check_mark:  for Case insensitive sort #640

--- spec/operator-transform-string-spec.coffee
+++ spec/operator-transform-string-spec.coffee
@@ -1296,3 +1296,35 @@
           a
           \n
           """
+
+  describe "SortCaseInsensitively", ->
+    beforeEach ->
+      atom.keymaps.add "test",
+        'atom-text-editor.vim-mode-plus:not(.insert-mode)':
+          'g s': 'vim-mode-plus:sort-case-insensitively'
+    it "Sort rows case-insensitively", ->
+      set
+        textC: """
+        |apple
+        Beef
+        APPLE
+        DOG
+        beef
+        Apple
+        BEEF
+        Dog
+        dog
+        """
+
+      ensure "g s G", ->
+        text: """
+        apple
+        Apple
+        APPLE
+        beef
+        Beef
+        BEEF
+        dog
+        Dog
+        DOG
+        """


:bug: Fix missing decorations caused by shadow root

--- lib/minimap-render-view.coffee
+++ lib/minimap-render-view.coffee
@@ -386,7 +386,7 @@
   #
   # Returns a {String}.
   retrieveDecorationColorFromDom: (decoration) ->
-    @retrieveStyleFromDom(decoration.getProperties().scope.split(/\s+/), 'background-color')
+    @retrieveStyleFromDom(decoration.getProperties().scope.split(/\s+/), 'background-color', false)

   # Internal: This function insert a dummy element in the DOM to compute
   # its style, return the specified property, and remove the element
@@ -396,8 +396,8 @@
   # property - The property {String} name.
   #
   # Returns a {String} of the property value.
-  retrieveStyleFromDom: (scopes, property) ->
-    @ensureDummyNodeExistence()
+  retrieveStyleFromDom: (scopes, property, shadowRoot=true) ->
+    @ensureDummyNodeExistence(shadowRoot)

     parent = @dummyNode
     for scope in scopes
@@ -415,11 +415,14 @@

   # Internal: Creates a DOM node container for all the operations that
   # need to read styles properties from DOM.
-  ensureDummyNodeExistence: ->
+  ensureDummyNodeExistence: (shadowRoot) ->
     unless @dummyNode?
       @dummyNode = document.createElement('span')
       @dummyNode.style.visibility = 'hidden'
+    if shadowRoot
       @editorView.shadowRoot.appendChild(@dummyNode)
+    else
+      @editorView.appendChild(@dummyNode)

   # Internal: Converts a `rgb(...)` color into a `rgba(...)` color
   # with the specified opacity.


@jasonkarns showed me the light.

@jasonkarns showed me the light.

At some point non-objects were printing out with " marks, but no longer.

--- lib/pretty-prints-value.coffee
+++ lib/pretty-prints-value.coffee
@@ -5,8 +5,7 @@

 module.exports =
   prettyPrint: (value) ->
-    return value unless _(value).isObject()
     try
-      JSON.stringify(value, replacer, indentation = 2)
+      return JSON.stringify(value, replacer, indentation = 2)
     catch e
       value


Conform to linter requirements.

Conform to linter requirements.

--- lib/stagehand/stagehand.coffee
+++ lib/stagehand/stagehand.coffee
@@ -15,7 +15,7 @@
       process.stderr.on 'data', (data) =>
         errors = errors + data.toString()
       process.on 'exit', (code) =>
-        if code == 0
+        if code is 0
           resolve()
         else
           reject(errors)
@@ -33,7 +33,7 @@
       process.stderr.on 'data', (data) =>
         errors = errors + data.toString()
       process.on 'exit', (code) =>
-        if code == 0
+        if code is 0
           resolve(JSON.parse(json))
         else
           reject(errors)
@@ -64,11 +64,11 @@
       process.stderr.on 'data', (data) =>
         errors = errors + data.toString()
       process.on 'exit', (code) =>
-        if code == 0
+        if code is 0
           atom.notifications.addInfo "[dart-tools] Generated #{projectTemplate.label}!"
         else
           atom.notifications.addError "[dart-tools] Failed to generate #{projectTemplate.label}!",
-            detail: errors || output
+            detail: errors or output

     if projectPaths.length is 0
       atom.notifications.addError "[dart-tools] Cannot run Stagehand without a project directory open."
@@ -79,7 +79,7 @@
         {item: p, displayName: path.basename(p)}
       picker.selectFrom(items).then(
         (selectedProjectPath) => generateProject(selectedProjectPath)
-        () => 1
+        => 1
       )


MysqlMixin: fixed query method

MysqlMixin: fixed query method

The promisify method was not called properly.

--- src/mixin/MysqlMixin.coffee
+++ src/mixin/MysqlMixin.coffee
@@ -4,7 +4,7 @@
   (target) ->
     target._mysql =
       connection: connection
-      query: Promise.promisify connection.query
+      query: Promise.promisify connection.query, connection

     target.query = (query, args...) ->
       if args?


Translated inspect element

Translated inspect element

--- def/eo/context.cson
+++ def/eo/context.cson
@@ -1,6 +1,6 @@
 Context:
   "atom-workspace":
-    "application:inspect": "Inspect Element"
+    "application:inspect": "Inspekti Elementon"
   "atom-text-editor":
     "color-picker:open": "Color Picker"
     "minimap:toggle": "Toggle Minimap"


Fix entity persistence implementation

Fix entity persistence implementation

--- library/exposure/access.coffee
+++ library/exposure/access.coffee
@@ -80,7 +80,8 @@
         @ressurectEntity content, (error, entity) ->
             format = (e) -> "ressurection error: #{e}"
             return callback format error if error
-            p = get: -> return entity or undefined
+            assert p = new Object enumerable: yes
+            p.get = -> return entity or undefined
             Object.defineProperty container, key, p
             assert container[key]?; callback()


minor refactoring of the duplex guard

minor refactoring of the duplex guard

--- library/membrane/duplex.coffee
+++ library/membrane/duplex.coffee
@@ -124,18 +124,18 @@
     # the domains and error handling itself. This method is generally
     # used only once per the domain declaration. See `provider`.
     @guarded: (method, socket) ->
-        assert identify = @identify().underline
         assert guarded = require("domain").create()
+        assert identify = try @identify().underline
+        assert _.isFunction i = Marshal.deserialize
         assert _.isFunction o = Marshal.serialize
-        assert _.isFunction i = Marshal.deserialize
-        location = "Breakpoint at @#{method}#%s"
-        message = "Error running provider:\r\n%s"
-        guarded.on "error", (error, optional) ->
+        location = "Got breakpoint in the #{method}#%s"
+        message = "Error while running provider:\r\n%s"
+        fx = (blob) => blob.call this; return guarded
+        fx -> guarded.on "error", (error, optional) ->
             logger.error location.red, identify
             logger.error message.red, error.stack
             socket.emit "exception", o([error])...
             try socket.disconnect?() catch error
-        return guarded

     # A utility method to mark the certain function as the provider.
     # The method returns the original function back so it can be used


add some logging to the sock authorization

--- library/membrane/duplex.coffee
+++ library/membrane/duplex.coffee
@@ -111,15 +111,19 @@
     # contains correct session requsities and restores the session!
     # The session can normally be used as you would use req session.
     authorization: (context) => (socket, accept) ->
+        assert message = "Authorizing %s at %s"
         assert _.isObject handshake = socket.request
         assert _.isFunction session = @kernel.session
         assert _.isFunction cookies = @kernel.cookieParser
+        assert idc = try @constructor.identify().underline
         assert handshake.originalUrl = handshake.url or "/"
+        assert _.isString id = try socket.id.toString().bold
         assert Response = class RDummy extends EventEmitter2
         Response::setHeader = (name, value) -> undefined
         Response::end = (data, encoding) -> undefined
         cookies handshake, response = new Response, =>
             session handshake, response, (parameter) =>
+                logger.debug message.yellow, id, idc
                 session = handshake.session or null
                 ns = new Error "no session detected"
                 return accept ns, no unless session


throw in some docs

throw in some docs

--- library/nucleus/content.coffee
+++ library/nucleus/content.coffee
@@ -39,7 +39,8 @@
 # This class is a content negotiation broker. It is instantiated by
 # the kernel and then can be used either directly or via middleware
 # to negotiate the procedure of responding to a client with the data
-# using the correct protocol, meaning correct `Content-Type`, etc.
+# using the correct protocol, meaning correct `Content-Type` header.
+# This also tries to correctly set the `Content-Length` header too.
 module.exports.Broker = class Broker extends Archetype

     # Every broker has to have a public constructor that accepts
@@ -92,10 +93,11 @@
         response.setHeader args... unless sent
         return response.write "#{encoded}"

-# This class is a content negotiation broker. It is instantiated by
-# the kernel and then can be used either directly or via middleware
-# to negotiate the procedure of responding to a client with the data
-# using the correct protocol, meaning correct `Content-Type`, etc.
+# This class is a derived content negotiation broker that provides
+# the support for sending native data as JSON encoded blob. This does
+# support `Object` and `Array` as the root containers. If any other
+# root data type is attempted - that is an error. The broker and the
+# `sender` middleware are designed to work mostly with data objects.
 module.exports.JsonBroker = class JsonBroker extends Broker

     # The actual flusher that serializes the JSON root object and


port configuration system from kernel to service ABC

--- library/nucleus/service.coffee
+++ library/nucleus/service.coffee
@@ -58,6 +58,13 @@
     # mainly is used to exclude or account for abstract classes.
     # Once inherited from, the inheritee is not abstract anymore.
     @abstract yes
+
+    # Symbol declaration table, that states what keys, if those are
+    # vectors (arrays) should be exported and then merged with their
+    # counterparts in the destination, once the composition process
+    # takes place. See the `Archetype::composition` hook definition
+    # for more information. Keys are names, values can be anything.
+    @COMPOSITION_EXPORTS = configures: yes

     # A hook that will be called each time when the kernel beacon
     # is being fired. Please refer to this prototype signature for
@@ -152,10 +159,39 @@
         logger.debug message.grey, identify.toString()
         assert downstream = downstream.bind service
         assert instance = downstream instance: =>
-            logger.debug firings.grey, identify
-            callback.call this, service, kernel
-            try service.emit "instance", kernel
+            this.configure().call service, (results) =>
+                logger.debug firings.grey, identify
+                callback.call this, service, kernel
+                try service.emit "instance", kernel
         instance kernel, service; return service
+
+    # An embedded system for adding ad-hoc configuration routines.
+    # Supply the reasoning and the routine and this method will add
+    # that routine to the configuration stack, to be launched once
+    # the service spawned. With no arguments it returns the launcher.
+    # This is a convenient way of running additions config routines.
+    # The implementation is borrowed from the `GraniteKernel` class.
+    @configure: (xexplain, xroutine) ->
+        {series, apply} = async or require "async"
+        assert _.isString config = "Configuring: %s"
+        bareCall = (try arguments.length or 0) is 0
+        run = bareCall and _.isArray @configures or 0
+        gr = (o) -> try o.routine.apply.bind o.routine
+        lg = (o) -> logger.info config, o.explain.bold
+        fn = (t) -> (o) -> (a...) -> lg o; gr(o) t, a
+        explain = _.find(arguments, _.isString) or null
+        routine = _.find(arguments, _.isFunction) or 0
+        assert id = this.identify().toString().underline
+        explain = "instance of #{id} service" unless explain
+        assert _.isArray cs = configures = @configures or []
+        return ((nxt) -> series _.map(cs, fn @), nxt) if run
+        return ((c) -> c()) if not @configures and bareCall
+        invRoutine = "supplied invalid config routine"
+        invExplain = "no explanation has been supplied"
+        assert _.isFunction(routine or 0), invRoutine
+        assert _.isString(explain or 0), invExplain
+        return (@configures ?= []).push new Object
+            explain: explain, routine: routine

     # This method is used to obtain all the available dosposition
     # data of this services. Disposition data is basically the data


Initialize new course

Initialize new course

--- tutor/src/flux/new-course.coffee
+++ tutor/src/flux/new-course.coffee
@@ -1,7 +1,7 @@
 {makeStandardStore} = require './helpers'
 _ = require 'lodash'
-
-{CourseListingActions} = require './course-listing'
+{CourseStore} = require './course'
+{CourseListingActions, CourseListingStore} = require './course-listing'
 PeriodHelper = require '../helpers/period'

 DEFAULTS =
@@ -90,18 +90,34 @@
     _.extend(@_local, attrs)
     @emitChange()

+  initialize: ({sourceId}) ->
+    if sourceId
+      course = CourseStore.get(sourceId)
+      @setClone(course)
+    else
+      @setNew()
+
+  setNew: ->
+    @set(
+      course_type: CourseListingStore.typeOfAllCourses()
+    )
+
   setClone: (course) ->
-    newCourse =
+    @set(
       new_or_copy: 'copy'
-      course_type: if course.is_concept_coach then 'cc' else 'tutor'
       offering_id: course.offering_id
       cloned_from_id: course.id
       name: course.name
+      course_type: CourseListingStore.typeOfAllCourses()
       num_sections: PeriodHelper.activePeriods(course).length
-
-    @set(newCourse)
+    )

   exports:
+
+    canSelectCourseType: ->
+      # if the user has only a single type of course then they cannot change it
+      not CourseListingStore.typeOfAllCourses()
+
     isValid: (attr) ->
       switch attr
         when 'details' then @_local.name and @_local.num_sections


change log level -> debug

change log level -> debug

--- node_modules/hubot-twitter-egosearch/scripts/egosearch.coffee
+++ node_modules/hubot-twitter-egosearch/scripts/egosearch.coffee
@@ -67,7 +67,7 @@
   , eval(process.env.HUBOT_TWITTER_EGOSEARCH_SEARCH_INTERVAL) or 1000 * 60

 searchQueries = (robot) ->
-  robot.logger.info 'searchLoopQuery'
+  robot.logger.debug 'searchLoopQuery'
   for id, query of queries
     searchQueryAsync(id, query)
       .then (data) ->
@@ -109,11 +109,12 @@
   robot.brain.data.egosearch or= {}

   robot.brain.on 'loaded', =>
-    robot.logger.info 'loaded'
+    robot.logger.debug 'loaded'
     loadQueriesFromBrain robot
     loopSearching robot

   robot.respond /egosearch (?:add|new|start) (.+)/i, (msg) ->
+    robot.logger.debug 'add search'
     createQuery robot, msg, msg.match[1], msg.message.user

   robot.respond /egosearch (?:rm|remove|del|delete|stop) (.+)/i, (msg) ->


Adding an configurable geodata url on S3

--- config.coffee
+++ config.coffee
@@ -98,6 +98,7 @@
   PARSELY_KEY: null
   PARSELY_SECRET: null
   SEGMENT_WRITE_KEY_MICROGRAVITY: null
+  GEODATA_URL: 'http://artsy-geodata.s3-website-us-east-1.amazonaws.com'

 # Override any values with env variables if they exist.
 # You can set JSON-y values for env variables as well such as "true" or


changed it to say job finished

changed it to say job finished

--- src/public/javascripts/client.coffee
+++ src/public/javascripts/client.coffee
@@ -34,7 +34,7 @@
     urls = (s.trim() for s in $("#url_area").val().split(','))
     client.save_urls(urls)

-    $("#header_text").html("Job Running!")
+    $("#header_text").html("Job Running...")
     $("#url_div").slideUp 300

   firebase.JOB_STATUS_REF.child(client._id).on 'child_added', (snapshot) ->
@@ -42,6 +42,7 @@
       url = 'http://' + window.location.hostname + '/' + snapshot.val()
       $('#output_url_div').html('Click to download output!')
       client.finish_job()
+    $("#header_text").html("Job Finished!")

 trim_code = (text) ->
   lines = text.trim().split "\n"


i18n Inst Dash Analytics tab

i18n Inst Dash Analytics tab

LMS-2357

--- lms/static/coffee/src/instructor_dashboard/analytics.coffee
+++ lms/static/coffee/src/instructor_dashboard/analytics.coffee
@@ -34,7 +34,7 @@
     @reset_display()

     @get_profile_distributions @feature,
-      error: std_ajax_err => @show_error "Error fetching distribution."
+      error: std_ajax_err => @show_error gettext("Error fetching distribution.")
       success: (data) =>
         feature_res = data.feature_results
         if feature_res.type is 'EASY_CHOICE'
@@ -74,7 +74,7 @@
           ]
         else
           console.warn("unable to show distribution #{feature_res.type}")
-          @show_error 'Unavailable metric display.'
+          @show_error gettext('Unavailable metric display.')

   # fetch distribution data from server.
   # `handler` can be either a callback for success
@@ -110,9 +110,11 @@

   load: ->
     @get_grade_distributions
-      error: std_ajax_err => @show_error "Error fetching grade distributions."
+      error: std_ajax_err => @show_error gettext("Error fetching grade distributions.")
       success: (data) =>
-        @$container.find('.last-updated').text "Last Updated: #{data.time}"
+        time_updated = gettext("Last Updated: <%= timestamp %>")
+        full_time_updated = _.template(time_updated, {timestamp: data.time})
+        @$container.find('.last-updated').text full_time_updated

         # populate selector
         @$problem_selector.empty()
@@ -145,8 +147,10 @@
     total_students = _.reduce ([0].concat grade_info),
       (accum, {grade, max_grade, num_students}) -> accum + num_students

+    msg = gettext("<%= num_students %> students scored.")
+    full_msg = _.template(msg, {num_students: total_students})
     # show total students
-    @$container.find('.display-text').text "#{total_students} students scored."
+    @$container.find('.display-text').text full_msg

     # render to graph
     graph_placeholder = $ '<div/>', class: 'graph-placeholder'


Use splat instead of apply

Use splat instead of apply

--- lib/load-paths-task.coffee
+++ lib/load-paths-task.coffee
@@ -26,7 +26,7 @@
         @callback(paths)
         deferred.resolve()
     stdout = (data) ->
-      paths.push.apply(paths, _.compact(data.split('\n')))
+      paths.push(_.compact(data.split('\n'))...)

     @process = new BufferedProcess({command, args, stdout, exit})
     deferred


Token replacement now replaces all instances

Was previously only replacing the first instance of a dynamic token eg. `<% url[1] %>`

--- src/models/endpoints.coffee
+++ src/models/endpoints.coffee
@@ -67,7 +67,7 @@
 applyTemplating = (obj, captures) ->
   for key, value of obj
     if typeof value is 'string' or value instanceof Buffer
-      obj[key] = ejs.render value.toString().replace('<%', '<%='), captures
+      obj[key] = ejs.render value.toString().replace(/<%/g, '<%='), captures
     else
       applyTemplating value, captures


Troubleshoot: reset method should emit the given event

--- client/Main/troubleshoot/troubleshoot.coffee
+++ client/Main/troubleshoot/troubleshoot.coffee
@@ -44,7 +44,7 @@
     @status = PENDING
     clearTimeout @timeout
     @timeout = null
-    @emit "troubleshootCompleted"
+    @emit event

   isConnectionFailed: ->
     @items["connection"].status is "fail"


Remove duplicate command bindings for undo and redo

Co-authored-by: Nathan Sobo <2e8aa918660411855c6d44d5bb2da677aa033255@github.com>

--- src/register-default-commands.coffee
+++ src/register-default-commands.coffee
@@ -122,8 +122,6 @@
   commandRegistry.add(
     'atom-text-editor',
     stopEventPropagation({
-      'core:undo': -> @undo()
-      'core:redo': -> @redo()
       'core:move-left': -> @moveLeft()
       'core:move-right': -> @moveRight()
       'core:select-left': -> @selectLeft()


Removed commented-out code

Removed commented-out code

--- src/entities/creature.coffee
+++ src/entities/creature.coffee
@@ -119,7 +119,6 @@
 		@equipment
 		.map (item) => item.getEquipSlotUse slot, @
 		.reduce ((prev, curr) -> prev + curr), 0
-		# 0

 	maxSpacesInSlot: (slot) ->
 		@species.equipSlotNum[slot]
@@ -128,20 +127,16 @@
 		maxSpaces = @maxSpacesInSlot slot

 		((@equipSlotCount slot) + (item.getEquipSlotUse slot, @)) <= maxSpaces
-		# no

 	hasItemEquipped: (item) ->
 		item in @equipment
-		# no

 	hasItemInSlot: (slot, extraCheck) ->
 		_.any @equipment, (item) =>
 			(item.getEquipSlotUse slot, @) > 0 and (extraCheck?(item, slot) ? yes)
-		# no

 	getItemsForSlot: (slot) ->
 		@equipment.filter (item) => (item.getEquipSlotUse slot, @) > 0
-		# []

 	belongsToGroup: (other) ->
 		if _.isString other
@@ -212,9 +207,7 @@

 	equip: (item, silent = no) ->
 		notFit = (slot) =>
-			r = @equipSlotFits slot, item
-			# log.info "YO #{slot} = #{r}"
-			not r
+			not @equipSlotFits slot, item

 		if _.any creatureSpecies._equipSlots, notFit
 			return no


[master] Optimize consecutive days

[master] Optimize consecutive days

--- get_comment_count.coffee
+++ get_comment_count.coffee
@@ -53,7 +53,16 @@
         createDate = entry.createDate
         # Set correct year of entry
         createDate.year lastEntryDate.year()
-        if not createDate.isValid() or Math.abs(createDate.diff(lastEntryDate, 'days')) != 0
+        storePayload = (createDate) ->
+          payload =
+            readCount: readCount
+            commentCount: commentCount
+            createDate: createDate.format 'YYYY-MM-DD'
+          redis.set "#{executionDate.format('YYYY-MM-DD')}:#{symbol}:#{threadId}", JSON.stringify(payload)
+          callback null, createDate
+        if createDate.isValid() and -1 <= createDate.diff(lastEntryDate, 'days') <= 0
+          storePayload createDate
+        else
           request "http://guba.eastmoney.com/#{threadUrl}", (error, response, body) ->
             if error or response.statusCode != 200
               throw (error ? response)
@@ -62,22 +71,9 @@
               [match] = /\d{4}-\d{2}-\d{2}/.exec html('.zwfbtime').text()
               createDate = moment match, 'YYYY-MM-DD'
               debug "date change from #{lastEntryDate.format 'YYYY-MM-DD'} to #{createDate.format 'YYYY-MM-DD'}"
-              payload =
-                readCount: readCount
-                commentCount: commentCount
-                createDate: createDate.format 'YYYY-MM-DD'
-              redis.set "#{executionDate.format('YYYY-MM-DD')}:#{symbol}:#{threadId}", JSON.stringify(payload)
-              callback null, createDate
-        else
-          payload =
-            readCount: readCount
-            commentCount: commentCount
-            createDate: createDate.format 'YYYY-MM-DD'
-          redis.set "#{executionDate.format('YYYY-MM-DD')}:#{symbol}:#{threadId}", JSON.stringify(payload)
-          callback null, createDate
+              storePayload createDate
       , (err, lastEntryDate) ->
-        if err
-          throw err
+        throw err if err?
         # Process next page
         redis.hset 'progress', symbol, page
         if page < maxPageNum


Update addon/locales/zh-tw/translations.coffee

Co-Authored-By: Abhinav <4a00278ffa8e155fccc600506021c4b814330d82@kiprosh.com>

--- addon/locales/zh-tw/translations.coffee
+++ addon/locales/zh-tw/translations.coffee
@@ -59,7 +59,7 @@
     "rate_button_label": "好的"
     "yes_button_label": "是!"
     "no_button_label": "不是"
-    "app_rate_prompt_title":"Rate GoodCity! Thank you for your kind donation"
+    "app_rate_prompt_title":"Rate GoodCity! Thank you for your kind donation."
     "feedback_prompt_title":"請分享閣下的意見。"

   "menu":


We append just once suggestions['stream'] to suggestions['table']

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -41,7 +41,8 @@
             'click .change_size': 'toggle_size'

         displaying_full_view: false
-
+        has_been_initialized:
+            value: false #We use that boolean to track if suggestions['stream'] has been append to suggestions['table']
         map_state:
             '': ''
             'r': 'r'
@@ -632,9 +633,10 @@
             @.$('.input_query').val event.target.dataset.query

         initialize: =>
-            log_initial '(initializing) dataexplorer view'
-            for suggestion in @suggestions.stream
-                @suggestions.table.push suggestion
+            if @has_been_initialized.value is false
+                for suggestion in @suggestions.stream
+                    @suggestions.table.push suggestion
+                @has_been_initialized.value = true

             host = window.location.hostname
             port = window.location.port


Fix print booleans in the tree view

Fix print booleans in the tree view

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -850,7 +850,7 @@
             else if value_type is 'boolean'
                 return @template_json_tree.span
                     classname: 'jt_bool'
-                    value: value
+                    value: if value then 'true' else 'false'


avoid relying on window object to know if the event popover is expanded.

--- client/app/views/popover_screens/main.coffee
+++ client/app/views/popover_screens/main.coffee
@@ -166,7 +166,7 @@
                 @description.focus()

         # Apply the expanded status if it has been previously set.
-        if window.popoverExtended
+        if @context.popoverExtended
             @expandPopover()

         # If all the optional fields are shown by default (they all have a
@@ -451,7 +451,7 @@

         # Mark the popover has extended so the information is not lost when
         # screen is left.
-        window.popoverExtended = not window.popoverExtended
+        @context.popoverExtended = not @context.popoverExtended?

     # Show the optional fields of the screen (the ones hidden by default).


Fix regression. When hitting Enter with a suggestion being selected, we just select the suggestion, nothing more

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -609,6 +609,11 @@
                     return true
                 else if event.which is 13 and (event.shiftKey is false and event.ctrlKey is false and event.metaKey is false)
                     if event.type is 'keydown'
+                        if @current_highlighted_suggestion > -1
+                            event.preventDefault()
+                            @handle_keypress()
+                            return true
+
                         previous_char = @get_previous_char()
                         if previous_char of @matching_opening_bracket
                             next_char = @get_next_char()


Fix install to use compile:tmp task.

Fix install to use compile:tmp task.


update site intro content for #91

update site intro content for #91

--- app/translations/en_us.coffee
+++ app/translations/en_us.coffee
@@ -55,7 +55,7 @@
       content: "Occasionally it is difficult to determine which animal is in the photo. Just above the list of animals is a variety of methods to filter the animal list. Selecting different options reduces the number of choices, making your job easier."
     s4:
       title: "Made a mistake?"
-      content: "If you need to delete any of your existing marks while viewing an image, simply find your animal in the list below the image and click the 'X' icon"
+      content: "If you need to delete any of your existing marks while viewing an image, simply find your animal in the list below the image and click the 'X' icon. Don’t worry if you’re unsure about your choices, though—even a guess is more helpful than nothing!"
     s5:
       title: "That's it!"
       content: "That's it! You may catch up on project updates either on the blog or through speaking with other volunteers. If you need additional help using the interface, click Tutorial within the list of links below the Finish button."


Switch from port 9200 to port 80

Switch from port 9200 to port 80

--- src/scripts/control.search.js.coffee
+++ src/scripts/control.search.js.coffee
@@ -109,7 +109,7 @@

     $.ajax({
       method: 'get'
-      url: 'http://scholar.arcticconnect.org:9200/arctic/_search'
+      url: 'http://scholar.arcticconnect.org/arctic/_search'
       data:
         q: "GH:*#{query}*"
         size: 100


Fix destroyship

Fix destroyship

--- views/components/ship/index.cjsx
+++ views/components/ship/index.cjsx
@@ -205,12 +205,13 @@
               if shipId == removeId
                 [x, y] = [i, j]
                 break
-          if y == 5
-            decks[x].api_ship[y] = -1
-          else
-            for idx in [y..4]
-              decks[x].api_ship[idx] = decks[x].api_ship[idx + 1]
-            decks[x].api_ship[5] = -1
+          if x != -1 && y != -1
+            if y == 5
+              decks[x].api_ship[y] = -1
+            else
+              for idx in [y..4]
+                decks[x].api_ship[idx] = decks[x].api_ship[idx + 1]
+              decks[x].api_ship[5] = -1
         when '/kcsapi/api_req_map/next'
           {decks, states} = @state
           {$ships, _ships} = window


operations: fix PushInbox newNodes

operations: fix PushInbox newNodes


ManageDomainsView:domainStateChange implementation is done [completes #78676742]

--- client/Main/domains/managedomainsview.coffee
+++ client/Main/domains/managedomainsview.coffee
@@ -38,6 +38,7 @@

     @domainController.getListView()
       .on 'DeleteDomainRequested', @bound 'removeDomain'
+      .on 'DomainStateChanged',    @bound 'changeDomainState'

     @addSubView @loader = new KDLoaderView
       cssClass      : 'in-progress'
@@ -124,6 +125,98 @@
       .finally =>
         @loader.hide()

+
+  changeDomainState:(domainItem, state)->
+
+    {computeController} = KD.singletons
+    {stateToggle}       = domainItem
+    {domain}            = domainItem.getData()
+
+    machineId = @machine._id
+    action    = if state then 'setDomain' else 'unsetDomain'
+
+    @loader.show()
+    @warning.hide()
+
+    stateToggle.makeDisabled()
+
+    @askForPermission domainItem, state, (approved)=>
+
+      if approved
+        computeController.kloud[action] {domainName: domain, machineId}
+          .then ->
+            computeController.domains = []
+            domainItem.data.machineId = null  unless state
+
+          .catch (err)=>
+            warn "Failed to change domain state:", err
+            @warning.setTooltip title: err.message
+            @warning.show()
+
+            @revertToggle domainItem, state
+
+          .finally =>
+            @loader.hide()
+            stateToggle.makeEnabled()
+
+      else
+        @revertToggle domainItem, state
+        @loader.hide()
+        stateToggle.makeEnabled()
+
+
+  revertToggle:(domainItem, state)->
+
+    {stateToggle} = domainItem
+    if state
+    then stateToggle.setOff no
+    else stateToggle.setOn  no
+
+
+  askForPermission:(domainItem, state, callback)->
+
+    {domain, machineId} = domainItem.getData()
+
+    if not state or not machineId? then return callback yes
+
+    {computeController} = KD.singletons
+
+    modal = new KDModalView
+      title         : "Re-assign domain ?"
+      content       :
+        """
+

+

+            This domain (#{domain}) already assigned
+            to another machine, if you continue it will be re-assigned
+            to this machine. Do you want to continue?
+

+

+        """
+      overlay       : yes
+      cancel        : ->
+        modal.destroy()
+        callback no
+      buttons       :
+        OK          :
+          title     : "Yes, re-assign"
+          style     : 'modal-clean-red'
+          loader    :
+            color   : 'darkred'
+          callback  : ->
+            computeController.kloud
+              .unsetDomain {domainName: domain, machineId}
+              .then -> callback yes
+              .catch (err)->
+                warn err
+                callback no
+              .finally -> modal.destroy()
+        cancel      :
+          title     : "Cancel"
+          style     : 'modal-cancel'
+          callback  : -> modal.cancel()
+
+
   toggleInput:->

     @inputView.toggleClass 'hidden'


activity: reformat likeview.coffee

activity: reformat likeview.coffee

--- client/Social/Activity/views/likeview.coffee
+++ client/Social/Activity/views/likeview.coffee
@@ -1,6 +1,6 @@
 class ActivityLikeView extends JView

-  constructor:(options={}, data)->
+  constructor: (options = {}, data) ->

     options.tagName            or= 'span'
     options.cssClass           or= 'like-view'
@@ -45,7 +45,8 @@

         @_currentState = likedBefore

-  fetchLikeInfo:->
+
+  fetchLikeInfo: ->

     data = @getData()

@@ -117,7 +118,9 @@
         @likeCount.getTooltip().update { title: tooltip }
         @_lastUpdatedCount = data.meta.likes

-  click:(event)->
+
+  click: (event) ->
+
     event.preventDefault()

     if $(event.target).is("a.action-link")
@@ -142,14 +145,24 @@

           @_lastUpdatedCount = -1

-  pistachio:->
+
+  pistachio: ->
+
     """{{> @likeLink}}{{> @likeCount}}"""

 class LikeViewClean extends ActivityLikeView

-  constructor:->
+  constructor: ->
+
     super
+
     @likeLink.updatePartial "Like"

-  pistachio:->
-    """{{> @likeLink}}{{> @likeCount}}"""
+
+  pistachio: ->
+
+    """
+
+      {{> @likeLink}}{{> @likeCount}}
+
+    """


fixed GET/query params to vanish when facets are clicked. needed to remove setRouteSilently

--- client/app/MainApp/ApplicationManager.coffee
+++ client/app/MainApp/ApplicationManager.coffee
@@ -26,7 +26,6 @@
       image : "Viewer"
       sound : "Viewer"
     @on 'AppManagerWantsToShowAnApp', @bound "setFrontApp"
-    @on 'AppManagerWantsToShowAnApp', @bound "setRouteSilently"

   open: do ->

@@ -245,20 +244,6 @@
       @appControllers[name].instances.splice index, 1
       if @appControllers[name].instances.length is 0
         delete @appControllers[name]
-
-  setRouteSilently:(controller, view, options)->
-
-    {profileEntryPoint, groupEntryPoint} = KD.config
-
-    entryPoint = if profileEntryPoint or groupEntryPoint
-      "/#{profileEntryPoint or groupEntryPoint}"
-    else ''
-
-    return unless options.route
-
-    @getSingleton('router').handleRoute "#{entryPoint}/#{options.route}",
-      suppressListeners : yes
-

   createPromptModal:(appOptions, callback)->
     # show modal and wait for response


Client: Sidebar: Included new stores

Client: Sidebar: Included new stores

--- client/app/lib/flux/environment/index.coffee
+++ client/app/lib/flux/environment/index.coffee
@@ -8,6 +8,7 @@
     require './stores/workspacesstore'
     require './stores/ownmachinesstore'
     require './stores/sharedmachinesstore'
+    require './stores/sharedmachinelistitemsstore'
     require './stores/machinesworkspacesstore'
     require './stores/collaborationmachinesstore'
     require './stores/addworkspaceviewstore'
@@ -15,6 +16,8 @@
     require './stores/deleteworkspacewidgetstore'
     require './stores/connectedmanagedmachinestore'
     require './stores/activemachinestore'
+    require './stores/activeinvitationmachineidstore'
+    require './stores/activeleavingsharedmachineidstore.coffee'
   ]

   register: (reactor) -> reactor.registerStores @stores


Use KD.notify_ instead new KDnotif....

Use KD.notify_ instead new KDnotif....

--- client/finder/commonviews/DNDUploader.coffee
+++ client/finder/commonviews/DNDUploader.coffee
@@ -44,12 +44,6 @@

   viewAppended: ->
     super
-    # @finder = KD.getSingleton "finderController"
-    # tc      = @finder.treeController
-    @notify = (msg) ->
-      new KDNotificationView
-        title : msg
-

   reset: ->
     {uploadToVM, defaultPath, title} = @getOptions()
@@ -67,7 +61,7 @@
     {files, items}  = event.originalEvent.dataTransfer

     if files.length >= 20
-      @notify """
+      KD.notify_ """
       Too many files to transfer!<br>
       Archive your files and try again.
       """, "error", """
@@ -102,7 +96,7 @@
       for file, index in files
         sizeInMb = file.size/1024/1024
         if sizeInMb > 100 && @getOptions().uploadToVM
-          @notify "Too big file to upload.", "error", "Max 100MB allowed per file."
+          KD.notify_ "Too big file to upload.", "error", "Max 100MB allowed per file."
           continue
         reader = new FileReader
         reader.onloadend = do (file=files[index])=> (readEvent)=>


IDE: Added makeEditable/makeReadOnly methods to EditorPane.

--- client/ide/workspace/panes/editorpane.coffee
+++ client/ide/workspace/panes/editorpane.coffee
@@ -317,3 +317,13 @@
       @getEditorSession().remove range

     @dontEmitChangeEvent = no
+
+
+  makeReadOnly: ->
+
+    @getEditor().setReadOnly yes
+
+
+  makeEditable: ->
+
+    @getEditor().setReadOnly no


Added missing ' to pistachio.

Added missing ' to pistachio.

--- client/landing/app/MainApp/avatararea.coffee
+++ client/landing/app/MainApp/avatararea.coffee
@@ -348,7 +348,7 @@

{{> @participants}} {{@getActionPhrase #(dummy)}} {{@getActivityPlot #(dummy)}}

-          {{> @timeAgoView}
+          {{> @timeAgoView}}

     """


sort latex output files into order in dropdown

--- services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
+++ services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
@@ -195,11 +195,16 @@
 				qs.clsiserverid = response.clsiServerId
 			for file in response.outputFiles
 				if IGNORE_FILES.indexOf(file.path) == -1
+					isOutputFile = file.path.match(/^output\./)
 					$scope.pdf.outputFiles.push {
 						# Turn 'output.blg' into 'blg file'.
-						name: if file.path.match(/^output\./) then "#{file.path.replace(/^output\./, "")} file" else file.path
+						name: if isOutputFile then "#{file.path.replace(/^output\./, "")} file" else file.path
 						url: "/project/#{project_id}/output/#{file.path}" + createQueryString qs
+						main: if isOutputFile then true else false
 					}
+
+			# sort the output files into order, main files first, then others
+			$scope.pdf.outputFiles.sort (a,b) -> (b.main - a.main) || a.name.localeCompare(b.name)

 		fetchLogs = (fileByPath, options) ->


read protected storage from req.session.user

--- modules/http/lib/Cache.coffee
+++ modules/http/lib/Cache.coffee
@@ -18,7 +18,7 @@
                 if !@data.disabled

                     if req.session?.user
-                        storage = req.session.HttpCacheStorage ?= {__name: 'private'}
+                        storage = req.session.user.HttpCacheStorage ?= {__name: 'private'}

                     else
                         storage = _storage


Use serializeErr helper everywhere

Use serializeErr helper everywhere

--- src/protocol.coffee
+++ src/protocol.coffee
@@ -101,20 +101,20 @@
       # Regular component
       proto.coordinator.getComponentSource payload.name, (err, source) ->
         if err
-          proto.transport.send 'component', 'error', { message: "#{payload.name} #{err.message}", name: payload.name, error: err.message }, ctx
+          proto.transport.send 'component', 'error', serializeErr(err), ctx
           return
         proto.transport.send 'component', 'source', source, ctx

   else if sub == 'component' and cmd == 'source'
     p = payload
     proto.coordinator.addComponent p.name, p.language, p.code, (err) ->
-      return proto.transport.send 'component', 'error', err, ctx if err
+      return proto.transport.send 'component', 'error', serializeErr(err), ctx if err
       return proto.transport.sendAll 'component', 'source', payload

   # Network
   else if sub == 'network' and cmd == 'start'
     proto.coordinator.startNetwork payload.graph, (err) ->
-      return proto.transport.sendAll 'network', 'error', err if err
+      return proto.transport.sendAll 'network', 'error', serializeErr(err) if err
       proto.transport.sendAll 'network', 'started',
         running: true
         started: true
@@ -123,7 +123,7 @@

   else if sub == 'network' and cmd == 'stop'
     proto.coordinator.stopNetwork payload.graph, (err) ->
-      return proto.transport.sendAll 'network', 'error', err if err
+      return proto.transport.sendAll 'network', 'error', serializeErr(err) if err
       proto.transport.sendAll 'network', 'stopped',
         running: false
         started: true