Fetch sources from runtime

Fetch sources from runtime

--- components/RuntimeToProject.coffee
+++ components/RuntimeToProject.coffee
@@ -1,5 +1,33 @@
 noflo = require 'noflo'
 uuid = require 'uuid'
+
+fetchSources = (components, runtime, sources, callback) ->
+  return callback null, sources unless components.length
+  handleMessage = (msg) ->
+    if msg.command is 'error'
+      callback msg.payload
+      return
+    if msg.command is 'source'
+      sources.push msg.payload
+      fetchSources components, runtime, sources, callback
+      return
+    # We got unrelated message, subscribe again
+    runtime.once 'component', handleMessage
+  runtime.once 'component', handleMessage
+  component = components.shift()
+  runtime.sendComponent 'getsource',
+    name: component
+
+fetchFromLibrary = (runtime, callback) ->
+  return callback null, [] unless runtime.isConnected()
+  return callback null, [] unless runtime.canDo 'component:getsource'
+  return callback null, [] unless runtime.definition?.namespace
+  return callback null, [] unless runtime.definition.components
+  components = Object.keys(runtime.definition.components).filter (componentName) ->
+    return true if componentName.indexOf('/') is -1
+    [library, component] = componentName.split '/'
+    return library is runtime.definition.namespace
+  fetchSources components, runtime, [], callback

 exports.getComponent = ->
   c = new noflo.Component
@@ -28,49 +56,54 @@
       components: []
       specs: []

+    # Start with the data we already have
     graphs = data.payload.graphs.slice 0
     components = []
     components.push data.payload.component if data.payload.component

-    for graph in graphs
-      graph.name = graph.name.split('/').pop()
-      graph.setProperties
-        id: "#{project.id}/#{graph.properties?.id or graph.name}"
-        project: project.id
-      project.main = graph.properties.id unless project.main
-      project.name = graph.name unless project.name
-      if graph.properties?.environment?.type and not project.type
-        project.type = graph.properties.environment.type
-      project.graphs.push graph
-      out.graph.send graph
+    # Add components and graphs from library
+    fetchFromLibrary data.payload.runtime, (err, sources) ->
+      return callback err if err
+      graphs = graphs.concat sources.filter (c) -> c.language is 'json'
+      components = components.concat sources.filter (c) -> c.language isnt 'json'

-    for component in components
-      component.project = project.id
-      project.components.push component
-      out.component.send component
+      for graph in graphs
+        graph.name = graph.name.split('/').pop()
+        graph.setProperties
+          id: "#{project.id}/#{graph.properties?.id or graph.name}"
+          project: project.id
+        project.main = graph.properties.id unless project.main
+        project.name = graph.name unless project.name
+        if graph.properties?.environment?.type and not project.type
+          project.type = graph.properties.environment.type
+        project.graphs.push graph
+        out.graph.send graph

-    # TODO: Discover components with project's namespace from runtime
+      for component in components
+        component.project = project.id
+        project.components.push component
+        out.component.send component

-    # Associate runtime with project for auto-connecting
-    data.payload.runtime.definition.project = project.id
-    out.runtime.send data.payload.runtime.definition
+      # Associate runtime with project for auto-connecting
+      data.payload.runtime.definition.project = project.id
+      out.runtime.send data.payload.runtime.definition

-    out.project.send
-      id: project.id
-      name: project.name
-      type: project.type
-      main: project.main
+      out.project.send
+        id: project.id
+        name: project.name
+        type: project.type
+        main: project.main

-    # FIXME: Do in reducer
-    data.state.projects.push project
-    foundRuntime = data.state.runtimes.filter (rt) ->
-      rt.id is data.payload.runtime.definition.id
-    if foundRuntime.length
-      foundRuntime[0].project = project.id
-    else
-      data.state.runtimes.push data.payload.runtime.definition
+      # FIXME: Do in reducer
+      data.state.projects.push project
+      foundRuntime = data.state.runtimes.filter (rt) ->
+        rt.id is data.payload.runtime.definition.id
+      if foundRuntime.length
+        foundRuntime[0].project = project.id
+      else
+        data.state.runtimes.push data.payload.runtime.definition

-    # FIXME: Make an action
-    window.location.hash = "#project/#{encodeURIComponent(project.id)}/#{encodeURIComponent(project.main)}"
+      # FIXME: Make an action
+      window.location.hash = "#project/#{encodeURIComponent(project.id)}/#{encodeURIComponent(project.main)}"

-    do callback
+      do callback


staging: set "latest.koding.com" as email host

--- config/main.staging.coffee
+++ config/main.staging.coffee
@@ -236,7 +236,7 @@
     disconnectTimeout: 3e3
     vhost       : 'kite'
   email         :
-    host        : "koding.com"
+    host        : "latest.koding.com"
     protocol    : 'https:'
     defaultFromAddress: 'hello@koding.com'
   emailWorker   :


Fix stupid error

Fix stupid error

--- packages/rocketchat-external/app/client/views/room.coffee
+++ packages/rocketchat-external/app/client/views/room.coffee
@@ -11,8 +11,8 @@
 		Template.instance().chatMessages.keydown(@_id, event, Template.instance())

 Template.room.onCreated ->
-	this.subscribe 'visitorRoom'visitorId.get()
-	console.log 'visitor ->',visitorId.get()
+	this.subscribe 'visitorRoom', visitorId.get()
+	console.log 'visitor ->', visitorId.get()

 Template.room.onRendered ->


fix nested_select

fix nested_select

--- app/plugins/list/nested_select.coffee
+++ app/plugins/list/nested_select.coffee
@@ -11,13 +11,16 @@

 _null = ->

-class pi.List.NestedSelect extends pi.Plugin
+class pi.List.NestedSelect extends pi.List.Selectable
   id: 'nested_select'
   initialize: (@list) ->
-    super
+    pi.Plugin::initialize.apply @, arguments

     @selectable = @list.selectable || {select_all: _null, clear_selection: _null, type: _null, _selected_item: null}
     @list.delegate_to @, 'clear_selection', 'select_all', 'selected'
+
+    unless @list.has_selectable is true
+      @list.delegate_to @, 'selected_records', 'selected_record', 'selected_item', 'selected_size'

     @type @list.options.nested_select_type||""

@@ -32,8 +35,6 @@
         e.cancel()
         @_check_selected()
     return
-
-  _check_selected: pi.List.Selectable::_check_selected

   type: (value) ->
     @is_radio = !!value.match('radio')
@@ -52,7 +53,7 @@
   update_radio_selection: (item) ->
     return if not item or (@_prev_selected_list is item.host)
     @_prev_selected_list = item.host
-    if @list.selected_size()>1
+    if @list.selected().length > 1
       @list.clear_selection(true)
       item.host.select_item item
       return


Clear undo stack after reloading with new encoding

Being able to undo the encoding change can lead to file
corruption if the re-encoding is reverted and then saved
using the new encoding.

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -336,7 +336,9 @@
     @emitter.emit 'did-change-encoding', encoding

     unless @isModified()
-      @updateCachedDiskContents true, => @reload()
+      @updateCachedDiskContents true, =>
+        @reload()
+        @clearUndoStack()

   # Public: Returns the {String} encoding of this buffer.
   getEncoding: -> @encoding ? @file?.getEncoding()


Set firstVisibleScreenRow property before emitting event

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -3200,8 +3200,8 @@
       screenRow = Math.max(Math.min(screenRow, maxScreenRow), 0)

     unless screenRow is @firstVisibleScreenRow
+      @firstVisibleScreenRow = screenRow
       @emitter.emit 'did-change-first-visible-screen-row', screenRow unless fromView
-      @firstVisibleScreenRow = screenRow

   getFirstVisibleScreenRow: -> @firstVisibleScreenRow


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/text_effect.coffee
+++ src/text_effect.coffee
@@ -5,7 +5,7 @@
 returned immediately. Even though it has a draw method it will not
 draw anything to the screen until the image has been loaded.

-@name Sprite
+@name Text
 @constructor
 ###
 TextEffect = (I={}) ->


re-enable sourcemaps

re-enable sourcemaps

--- app/templates/gulp/tasks/css.coffee
+++ app/templates/gulp/tasks/css.coffee
@@ -14,8 +14,10 @@
 #*------------------------------------*/
 gulp.task 'css', () ->
   gulp.src(["#{conf.path.dev.scss}/**/*.{scss,sass}"])
-    .pipe(sass().on('error', sass.logError))
+    .pipe sourcemaps.init()
+    .pipe sass().on('error', sass.logError)
     .pipe autoprefixer({browsers: ['last 2 versions']})
+    .pipe sourcemaps.write()
     .pipe gulp.dest(conf.path.dev.css)


Fixed listenners cleanup

Fixed listenners cleanup

--- src/wrapper.coffee
+++ src/wrapper.coffee
@@ -22,7 +22,7 @@
       'pipe' # stderr
     ]
     proc = spawn @bin, args, {stdio}
-    proc.once 'error', (err) ->
+    proc.once 'error', onError = (err) ->
       reject err unless err.code is 'OK' is err.errno
     proc.once 'close', onClose = (code, signal) ->
       if code isnt 0 or signal isnt null
@@ -32,13 +32,13 @@
         reject err
       else
         resolve()
-    proc.stderr.on 'data', onErr = (data) ->
+    proc.stderr.on 'data', onErrData = (data) ->
       stderr += data
     promise = promise.ensure ->
       # Cleanup
-      proc.removeListener 'error', reject
+      proc.removeListener 'error', onError
       proc.removeListener 'close', onClose
-      proc.stderr.removeListener 'close', onErr
+      proc.stderr.removeListener 'data', onErrData
     res = {promise}
     res.stdin = proc.stdin if stdin
     res.stdout = proc.stdout if stdout


header - search - fake some things for the experience

--- _assets/javascripts/shared/shared.coffee
+++ _assets/javascripts/shared/shared.coffee
@@ -2,6 +2,7 @@
 root.ElloWTFShared =
   init: () ->
     ElloWTFShared.watchSearchHeader()
+    ElloWTFShared.watchSearchTerms()

   # initAjax: () ->
   #   ElloWTFShared.doAThing()
@@ -28,6 +29,25 @@
       $search_box.val("")
       $search_form.addClass("inactive")

+  ## temp to fake search expereience
+  watchSearchTerms: ->
+    console.log ElloWTFShared.getURLParameter('search')
+    search_term = ElloWTFShared.getURLParameter('search')
+    unless typeof search_term == 'undefined' || search_term == ""
+      $(".search_holder .form input").val("#{search_term}")
+      $(".search_holder .form").removeClass("inactive")
+
+  getURLParameter: (parameter) ->
+    page_url = window.location.search.substring(1)
+    url_variables = page_url.split("&")
+    i = 0
+    while i < url_variables.length
+      parameter_name = url_variables[i].split("=")
+      if parameter_name[0] == parameter
+        return parameter_name[1]
+      i++
+    return
+

 $(document).ready ->


JPermissionSet: flush mainGroupData on each updateInstance event

--- workers/social/lib/social/models/group/permissionset.coffee
+++ workers/social/lib/social/models/group/permissionset.coffee
@@ -45,6 +45,10 @@

     super data

+    # Flush mainGroupData cache on update
+    @on 'updateInstance', =>
+      @constructor.mainGroupData = null
+
     return  if @isCustom

     # initialize the permission set with some sane defaults:
@@ -87,6 +91,8 @@

   @fetchMainGroupAndPermissionSet = (callback) ->

+    # We do have a in memory cache here for `MAIN_GROUP` which will be
+    # flushed once a JPermissionSet is getting updated ~ GG
     return callback null, @mainGroupData  if @mainGroupData

     fetchGroupAndPermissionSet MAIN_GROUP, (err, res) =>


Remove unused function.

Remove unused function.

--- src/validateSSH.coffee
+++ src/validateSSH.coffee
@@ -6,9 +6,6 @@
 	return 0 if len == 0

 	return arr[len-1] + (uint8ArrayToInt(arr[...len-1]) << 8)
-
-uint8ArrayToString = (arr) ->
-	(String.fromCharCode(i) for i in arr).join('')

 validate = (key) ->
 	# See http://crypto.stackexchange.com/a/5948.


Make sure layout math is done on load.

Make sure layout math is done on load.

--- services/web/public/coffee/main/project-list/project-list.coffee
+++ services/web/public/coffee/main/project-list/project-list.coffee
@@ -14,10 +14,9 @@
 		$scope.searchText =
 			value : ""

-		if $scope.projects.length == 0
-			$timeout () ->
-				recalculateProjectListHeight()
-			, 10
+		$timeout () ->
+			recalculateProjectListHeight()
+		, 10

 		recalculateProjectListHeight = () ->
 			topOffset = $(".project-list-card")?.offset()?.top


Also don't try to link the team

Also don't try to link the team

--- scripts/govuk_app_owners.coffee
+++ scripts/govuk_app_owners.coffee
@@ -10,4 +10,4 @@
       .get() (err, response, body) ->
         if response.statusCode == 200
           data = JSON.parse(body)
-          res.reply "#{data.app_name} is owned by <#{data.team}>"
+          res.reply "#{data.app_name} is owned by #{data.team}"


added matchers

added matchers

--- lib/ricksum-provider.coffee
+++ lib/ricksum-provider.coffee
@@ -1,16 +1,19 @@
 {fetchText} = require('./request')

-makeSuggestion = (text, prefix) ->
+matchers = ['ricksum', 'lorem']
+
+makeSuggestion = (text, matcher) ->
   suggestion =
     text: text
-    displayText: prefix
+    displayText: matcher
     rightLabel: 'Lorem Ricksum'

 module.exports =
   selector: '.source.coffee'
   getSuggestions: ({editor, bufferPosition, scopeDescriptor, prefix}) ->
     new Promise (resolve) ->
-      if 'ricksum'.indexOf(prefix) >= 0 or 'lorem'.indexOf(prefix) >= 0
-        resolve([makeSuggestion "WUBBA LUBBA DUB DUB", prefix])
+      matcher = matchers.find (val) -> val.indexOf(prefix) >= 0
+      if prefix != '' and matcher
+        resolve([makeSuggestion "WUBBA LUBBA DUB DUB", matcher])
       else
         resolve([])


Also escape < and >

Also escape < and >


flag status

flag status

--- app/assets/javascripts/dash.js.coffee
+++ app/assets/javascripts/dash.js.coffee
@@ -22,7 +22,11 @@
   record

 exg_toggle = (event) ->
-  name = $(event.target).attr('name')
+  el = $(event.target)
+  name = el.attr('name')
   exchange = exchanges[name]
   exchange.active = !exchange.active
-  console.log('exchange '+name+' active '+exchange.active)+  if exchange.active
+    el.addClass('btn-success')
+  if !exchange.active
+    el.removeClass('btn-success')


:racehorse: Speedup screen rows update by removing calls to indexOf

--- lib/display-table.coffee
+++ lib/display-table.coffee
@@ -504,20 +504,43 @@
     rows = @table.getRows()
     @screenRows = rows.concat()

+    @screenToModelRowsMap = []
+    @modelToScreenRowsMap = []
+
     if @order?
       if typeof @order is 'function'
         @screenRows.sort(@order)
+        for row,i in rows
+          @modelToScreenRowsMap[i] = index = @screenRows.indexOf(row)
+          @screenToModelRowsMap[index] = i
+
       else
-        orderFunction = @compareRows(@order, @direction)
-        @screenRows.sort(orderFunction)
-
-    @screenToModelRowsMap = (rows.indexOf(row) for row in @screenRows)
-    @modelToScreenRowsMap = (@screenRows.indexOf(row) for row in rows)
+        orderArray = @screenRows.map (row, i) =>
+          originalIndex: i
+          value: row[@order]
+
+        orderArray.sort(@compareScreenRows(@direction))
+
+        for {originalIndex},i in orderArray
+          @screenRows[i] = rows[originalIndex]
+          @modelToScreenRowsMap[originalIndex] = i
+          @screenToModelRowsMap[i] = originalIndex
+
+    else
+      for row,i in rows
+        @modelToScreenRowsMap[i] = @screenToModelRowsMap[i] = i
+
     @computeRowOffsets()

-  compareRows: (order, direction=1) ->
-    collator = new Intl.Collator("en-US", numeric: true)
-    return (a,b) -> collator.compare(a[order],b[order]) * direction
+  compareScreenRows: (direction=1) ->
+    collator = @getCollator()
+    return (a,b) -> collator.compare(a.value,b.value) * direction
+
+  compareModelRows: (order, direction=1) ->
+    collator = @getCollator()
+    return (a,b) -> collator.compare(a[order], b[order]) * direction
+
+  getCollator: -> @collator ?= new Intl.Collator("en-US", numeric: true)

   ##     ######  ######## ##       ##        ######
   ##    ##    ## ##       ##       ##       ##    ##
@@ -624,7 +647,7 @@
       if typeof @order is 'function'
         orderFunction = @order
       else
-        orderFunction = @compareRows(@order, @direction)
+        orderFunction = @compareModelRows(@order, @direction)

       order = @order


fixes and improvements

fixes and improvements

--- app/assets/javascripts/game.js.coffee
+++ app/assets/javascripts/game.js.coffee
@@ -15,9 +15,6 @@
   p1_cards_obj = {}
   p2_cards_obj = {}

-  p1_cards_jquery = []
-  p2_cards_jquery = []
-
   p1_card_selector = undefined
   p2_card_selector = undefined

@@ -53,16 +50,18 @@
         $.each(
           $(p1_card_selector),
           ->
-            p1_cards_jquery.push($(@))
             key = $(@).attr('card_id')
-            p1_cards_obj[key] = game_faces().faces[Number(key)]
+            p1_cards_obj[key] = {
+              attrs: game_faces().faces[Number(key)]
+              obj: $(@)
+            }
         )

 #        while(Object.keys(comp_cards).length < 24)
 #          comp_cards[Math.floor(Math.random() * 82)] = true

-        for key in Object.keys(comp_cards)
-          p1_cards_obj[key] = game_faces().faces[Number(key)]
+#        for key in Object.keys(comp_cards)
+#          p1_cards_obj[key] = game_faces().faces[Number(key)]

         p1_face_index = p1_cards_obj[Math.floor(Math.random() * 24)]
       else
@@ -71,9 +70,11 @@
       $.each(
         $(p2_card_selector),
         ->
-          p2_cards_jquery.push($(@))
           key = $(@).attr('card_id')
-          p2_cards_obj[key] = game_faces().faces[Number(key)]
+          p2_cards_obj[key] = {
+            attrs: game_faces().faces[Number(key)]
+            obj: $(@)
+          }
       )

       state = states['inited']
@@ -101,7 +102,7 @@
     sort_measures = []
     measures = {}
     for k,v of p2_cards_obj
-      for n,val of v
+      for n,val of v.attrs
         key = "#{n}|#{val}"
         measures[key] = (Number(measures[key])||0) + 1
     for key, val of measures
@@ -213,7 +214,7 @@
     rel_val = question_panel.find('.relation').val()
     property_val = question_panel.find('.property').val()

-    questions_list = state = states['player1'] ? p1_questions : p2_questions
+    questions_list = state == states['player1'] ? p1_questions : p2_questions

     if (questions_list[rel_val + property_val])
       finished(if state == states['player1'] then 'player2' else 'player1')
@@ -228,25 +229,20 @@
     is_first_player = state == states['player1']

     obj_list = if is_first_player then p2_cards_obj else p1_cards_obj
-    selector_list = if is_first_player then p2_cards_jquery else p1_cards_jquery

     negative = question[0] == '-'

     [hkey, hvalue] = question.substring(1).split('|')

-    index = 0
     for key, value of obj_list
-      predictable = value[hkey] != hvalue
+      predictable = value.attrs[hkey] != hvalue
       if (negative) then  predictable = !predictable

       if (predictable)
-        selector_list[index].addClass('back')
-        selector_list.splice(index, 1)
+        value.obj.addClass('back')
         delete obj_list[key]
-      else
-        index++
-
-    if selector_list.length == 0
+
+    if Object.keys(obj_list).length == 0
       finished(if is_first_player then 'player1' else 'player2')
     else
       change_turn()


通信量の低減

通信量の低減

--- app/assets/javascripts/home.js.coffee
+++ app/assets/javascripts/home.js.coffee
@@ -188,7 +188,7 @@
   message.from = _.find(instances, (obj) -> obj.id is message.from_id)

 drawObjects = (instances, messages) ->
-  canvas = document.getElementById("diagram")
+  canvas = $('#diagram')[0]
   return unless canvas
   if instances.length is 0
     canvas.wodth = 0
@@ -394,11 +394,11 @@
 # ------------------------------

 disableOpenImageButton = (flag) ->
-  button = document.getElementById("open_button")
+  button = $("input#open_button")[0]
   button.disabled = flag

 this.openImage = ->
-  canvas = document.getElementById("diagram")
+  canvas = $('#diagram')[0]
   return unless canvas
   window.open(canvas.toDataURL("image/png"))
   return
@@ -414,15 +414,20 @@
   this.timerID = setTimeout("this.postRawText()", 1000)

 this.postRawText = () ->
+  text = "source_text=" + $("#source_text").val()
+  if this.lastRawText is text
+    setInfoText("")
+    return
   disableOpenImageButton(true)
-  data = "source_text=" + $("#source_text").val()
+
   $.ajax({
     type: "POST",
     url: "home/parse_text",
-    data: data,
+    data: text,
     success: (data) ->
       fetchRestObjects("instances.json", instancesDidFetched)
     })
+  this.lastRawText = text

 setInfoText = (text) ->
   $("label#info_label").text(text)


Add width:resolved option to select2

Add width:resolved option to select2

--- app/assets/javascripts/schemas.coffee
+++ app/assets/javascripts/schemas.coffee
@@ -2,6 +2,7 @@
   $('select').select2
     theme: 'bootstrap'
     containerCssClass: ':all:'
+    width: 'resolve'
   $('pre code').each (i, block) ->
     hljs.highlightBlock(block)
   $('#categories-list li:first').addClass("active")


bug fix

bug fix

--- app/assets/javascripts/view.js.coffee
+++ app/assets/javascripts/view.js.coffee
@@ -944,7 +944,7 @@
   BWUtil.initialize()
   BWClient.initialize()

-  unless UINotice.isEmpty()
+  unless $DOM.notice().length is 0
     UINotice.initialize()

   # TL이 없다면 패스할것.


Use underscore node module

Use underscore node module

--- lib/spell-check-view.coffee
+++ lib/spell-check-view.coffee
@@ -1,5 +1,5 @@
 {View} = require 'space-pen'
-_ = require 'underscore'
+_ = nodeRequire 'underscore'
 SpellCheckTask = require './spell-check-task'
 MisspellingView = require './misspelling-view'


updating select topics to use chapter_section so numbers are displayed

--- src/components/task-plan/select-topics.cjsx
+++ src/components/task-plan/select-topics.cjsx
@@ -5,6 +5,7 @@
 LoadableItem = require '../loadable-item'
 {TocStore, TocActions} = require '../../flux/toc'
 {TaskPlanStore, TaskPlanActions} = require '../../flux/task-plan'
+ChapterSection = require './chapter-section'

 SectionTopic = React.createClass
   displayName: 'SectionTopic'
@@ -24,8 +25,8 @@

         <input type='checkbox' checked={isChecked}/>

-      {@props.section.number}
-      {@props.section.title}
+      <ChapterSection section={@props.section.chapter_section}/>.
+       {@props.section.title}

   toggleSection: ->
@@ -78,8 +79,10 @@
           <input type='checkbox' id="chapter-checkbox-#{chapter.id}"
             onChange={@toggleAllSections} checked={allChecked}/>

-        {chapter.number}
-        {chapter.title}
+
+          Chapter <ChapterSection section={chapter.chapter_section}/> -
+
+         {chapter.title}

     <BS.Accordion activeKey={activeKey}>


Scroll status list into view

Scroll status list into view

--- lib/status-list-view.coffee
+++ lib/status-list-view.coffee
@@ -134,6 +134,7 @@
     @git.getPatch(entry.path, entry.status).then (patch) =>
       @base.trigger('render-patch', [entry, patch])

+    @scrollIntoView(entry)
     entry

   stageSelection: ->
@@ -163,6 +164,15 @@

   focusCommitMessage: =>
     @commitMessageView.focusTextArea()
+
+  scrollIntoView: (entry) ->
+    container    = $(entry).closest('.files')[0]
+    scrollBottom = container.offsetHeight + container.scrollTop
+    entryTop     = entry.offsetTop
+    if scrollBottom - entryTop < 0
+      entry.scrollIntoView(false)
+    else if entry.offsetTop - entry.offsetHeight < container.scrollTop
+      entry.scrollIntoView()

   moveSelectionUp: =>
     selected = @selectedEntry()


fix notification config

fix notification config

--- lib/ui/notifications.coffee
+++ lib/ui/notifications.coffee
@@ -13,7 +13,7 @@
   enabled: () -> atom.config.get('julia-client.notifications')

   show: (msg) ->
-    return unless @enabled and not document.hasFocus()
+    return unless @enabled() and not document.hasFocus()
     n = new Notification "Julia Client",
       body: msg
       icon: @iconPath


all error have location in the first line, factor them out

--- lib/view/body/parser.coffee
+++ lib/view/body/parser.coffee
@@ -75,84 +75,78 @@
 parseNotInScope = (str) ->
     result = str.match regexNotInScope
     if result
-        location: parseLocation str
+        errorType: 'not in scope'
         term: result[1]
-        errorType: 'not in scope'

-regexTypeMismatch = /\s+((?:\n|.)*)\s+\!\=\<?\s+((?:\n|.)*)\s+of type\s+((?:\n|.)*)\s+when checking that the expression\s+((?:\n|.)*)\s+has type\s+((?:\n|.)*)/
+regexTypeMismatch = /((?:\n|.)*)\s+\!\=\<?\s+((?:\n|.)*)\s+of type\s+((?:\n|.)*)\s+when checking that the expression\s+((?:\n|.)*)\s+has type\s+((?:\n|.)*)/
 parseTypeMismatch = (str) ->
     result = str.match regexTypeMismatch
     if result
-        location: parseLocation str
+        errorType: 'type mismatch'
         expected: result[2]
         actual: result[1]
         type: result[3]
         term: result[4]
         termType: result[5]
-        errorType: 'type mismatch'

 regexWrongConstructor = /The constructor\s+((?:\n|.)*)\s+does not construct an element of\s+((?:\n|.)*)\s+when checking that the expression\s+((?:\n|.)*)\s+has type\s+((?:\n|.)*)/
 parseWrongConstructor = (str) ->
     result = str.match regexWrongConstructor
     if result
-        location: parseLocation str
+        errorType: 'wrong constructor'
         constructor: result[1]
         constructorType: result[2]
         term: result[3]
         termType: result[4]
-        errorType: 'wrong constructor'

-regexApplicationParseError = /\s*Could not parse the application\s+((?:\n|.)*)\s+when scope checking\s+((?:\n|.)*)/
+regexApplicationParseError = /Could not parse the application\s+((?:\n|.)*)\s+when scope checking\s+((?:\n|.)*)/
 parseApplicationParseError = (str) ->
     result = str.match regexApplicationParseError
     if result
-        location: parseLocation str
+        errorType: 'application parse error'
         term: result[1]
-        errorType: 'application parse error'

-regexTerminationError = /\s*Termination checking failed for the following functions:\s+((?:\n|.)*)\s+Problematic calls:\s+((?:\n|.)*)\s+\(at (.*)\)/
+regexTerminationError = /Termination checking failed for the following functions:\s+((?:\n|.)*)\s+Problematic calls:\s+((?:\n|.)*)\s+\(at (.*)\)/
 parseTerminationError = (str) ->
     result = str.match regexTerminationError
     if result
-        location: parseLocation str
+        errorType: 'termination error'
         term: result[1]
         call: result[2]
         callLocation: parseLocation result[3]
-        errorType: 'termination error'

-regexMissingDefinition = /\s*Missing definition for\s+((?:\n|.)*)/
+regexMissingDefinition = /Missing definition for\s+((?:\n|.)*)/
 parseMissingDefinition = (str) ->
     result = str.match regexMissingDefinition
     if result
-        location: parseLocation str
+        errorType: 'missing definition'
         term: result[1]
-        errorType: 'missing definition'

-regexRhsOmitted = /\s*The right-hand side can only be omitted if there is an absurd\s*pattern\, \(\) or \{\}\, in the left-hand side\.\s*when checking that the clause\s+((?:\n|.)*)\s+has type\s+((?:\n|.)*)/
+regexRhsOmitted = /The right-hand side can only be omitted if there is an absurd\s*pattern\, \(\) or \{\}\, in the left-hand side\.\s*when checking that the clause\s+((?:\n|.)*)\s+has type\s+((?:\n|.)*)/
 parseRhsOmitted = (str) ->
     result = str.match regexRhsOmitted
     if result
-        location: parseLocation str
+        errorType: 'rhs omitted'
         term: result[1]
         type: result[2]
-        errorType: 'rhs omitted'

-parseUnknownError = (strings) ->
-    location: parseLocation strings[0]
-    raw: _.rest(strings).join('\n')
+parseUnknownError = (str) ->
     errorType: 'unknown'
+    raw: str

-parseError = (str) ->
-    bulk = str.join('\n')
+parseError = (strings) ->
+    bulk = _.rest(strings).join('\n')

-    parseNotInScope(bulk) ||
-    parseTypeMismatch(bulk) ||
-    parseWrongConstructor(bulk) ||
-    parseApplicationParseError(bulk) ||
-    parseTerminationError(bulk) ||
-    parseMissingDefinition(bulk) ||
-    parseRhsOmitted(bulk) ||
-    parseUnknownError(str)
+    result = parseNotInScope(bulk) ||
+        parseTypeMismatch(bulk) ||
+        parseWrongConstructor(bulk) ||
+        parseApplicationParseError(bulk) ||
+        parseTerminationError(bulk) ||
+        parseMissingDefinition(bulk) ||
+        parseRhsOmitted(bulk) ||
+        parseUnknownError(bulk)
+    result.location = parseLocation strings[0]
+    return result

 module.exports =
     parseHeader: parseHeader


minor

minor

--- lib/viewmodel-checks.coffee
+++ lib/viewmodel-checks.coffee
@@ -5,7 +5,7 @@
 parentTemplate = (templateInstance) ->
   view = templateInstance.view?.parentView
   while view
-    if view.name.substring(0, 9) == 'Template.'
+    if view.name.substring(0, 9) is 'Template.' or view.name is 'body'
       return view.templateInstance()
     view = view.parentView
   return


Add experimental RFC5424 levels

Add experimental RFC5424 levels

```
 Numerical         Severity
   Code

    0       Emergency: system is unusable
    1       Alert: action must be taken immediately
    2       Critical: critical conditions
    3       Error: error conditions
    4       Warning: warning conditions
    5       Notice: normal but significant condition
    6       Informational: informational messages
    7       Debug: debug-level messages
``

--- lib/Default.coffee
+++ lib/Default.coffee
@@ -124,37 +124,57 @@
     line:
       color : 'white dim'

-    error:
+    emerg:
       level : 0
       color : 'red'
       symbol: figure.error

-    warn:
+    alert:
       level : 1
       color : 'yellow'
       symbol: figure.warning

+    crit:
+      level : 2
+      color : 'yellow'
+      symbol: figure.warning
+
+    error:
+      level : 3
+      color : 'red'
+      symbol: figure.error
+
+    warn:
+      level : 4
+      color : 'yellow'
+      symbol: figure.warning
+
+    notice:
+      level : 5
+      color : 'cyan bold'
+      symbol: figure.success
+
     success:
-      level : 2
+      level : 6
       color : 'green'
       symbol: figure.success

     info:
-      level : 3
+      level : 7
       color : 'white'
       symbol: figure.info

     verbose:
-      level : 4
+      level : 8
       color : 'cyan'
       symbol: figure.info

     debug:
-      level : 5
+      level : 9
       color : 'blue'
       symbol: figure.info

     silly:
-      level : 6
+      level : 10
       color : 'magenta'
       symbol: figure.info


NEW: Rollover the unit from dates selector

--- src/app/units/states/rollover/directives/unit-dates-selector/unit-dates-selector.coffee
+++ src/app/units/states/rollover/directives/unit-dates-selector/unit-dates-selector.coffee
@@ -8,7 +8,7 @@
   replace: true
   restrict: 'E'
   templateUrl: 'units/states/rollover/directives/unit-dates-selector/unit-dates-selector.tpl.html'
-  controller: ($scope, $state, $rootScope, ExternalName, Unit, alertService, unitService, TeachingPeriod) ->
+  controller: ($scope, $state, $rootScope, ExternalName, Unit, RolloverUnit, alertService, analyticsService, unitService, TeachingPeriod) ->
     $scope.calOptions = {
       startOpened: false
       endOpened: false
@@ -45,4 +45,25 @@
       if $scope.unit.end_date && $scope.unit.end_date.getMonth
         $scope.unit.end_date = "#{$scope.unit.end_date.getFullYear()}-#{$scope.unit.end_date.getMonth() + 1}-#{$scope.unit.end_date.getDate()}"

+      if $scope.unit.teaching_period_id
+        saveData = {
+          id: $scope.unit.id
+          teaching_period_id: $scope.unit.teaching_period_id
+        }
+      else
+        saveData = {
+          id: $scope.unit.id
+          start_date: $scope.unit.start_date
+          end_date: $scope.unit.end_date
+        }
+
+      RolloverUnit.create(
+        saveData
+        (response) ->
+          alertService.add("success", "Unit created.", 2000)
+          analyticsService.event 'Unit Admin', 'Saved New Unit'
+        (response) ->
+          alertService.add 'danger', "Error creating unit - #{response.data.error}"
+      )
+
 )


Run server tests first

Run server tests first

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -7,7 +7,7 @@
         port: 9998
       ci:
         singleRun: true
-        reporters: ['dots']
+        reporters: ['progress']
       dev:
         background: true

@@ -41,7 +41,7 @@
   # Register Tasks
   grunt.registerTask 'test:browser', ['server', 'karma:ci']
   grunt.registerTask 'test:server', ['simplemocha:server']
-  grunt.registerTask 'test', ['test:browser', 'test:server']
+  grunt.registerTask 'test', ['test:server', 'test:browser']

   grunt.registerTask 'server', 'Start a server to test clients', ->
     done = this.async()


- [grunt] run all shell targets for production build


Code snippet displays correctly in followed feed

Issues:
* After registering, if user follows someone, it won't create e2e binding.
* Relationship between feed and activity has targetId as string instead
of ObjectId.

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -155,28 +155,41 @@
         @activityListController.isLoading = no
         @activityListController.hideLazyLoader()

+  performFetchingTeasers:(type, selector, options, callback) ->
+    if type is 'public'
+      appManager.fetchStorage 'Activity', '1.0', (err, storage) =>
+        if err
+          log '>> error fetching app storage', err
+        else
+          options.collection = 'activities'
+          flags = KD.whoami().globalFlags
+          exempt = flags?.indexOf 'exempt'
+          exempt = (exempt? and ~exempt) or storage.getAt 'bucket.showLowQualityContent'
+          $.ajax KD.apiUri+'/1.0'
+            data      :
+              t       : if exempt then 1 else undefined
+              data    : JSON.stringify(_.extend options, selector)
+              env     : KD.config.env
+            dataType  : 'jsonp'
+            success   : (data) -> callback null, data
+
+    else if type is 'private'
+      KD.whoami().fetchFeeds (err, feeds) =>
+        for feed in feeds
+          continue unless feed.title is 'followed'
+          feed.fetchActivities selector, options, (err, data) =>
+            if err
+              callback err
+            else
+              for datum in data
+                datum.snapshot = datum.snapshot.replace /"/g, '"'
+              callback null, data
+
   fetchTeasers:(selector,options,callback)->
-    appManager.fetchStorage 'Activity', '1.0', (err, storage) =>
-      if err
-        log '>> error fetching app storage', err
-      else
-        options.collection = 'activities'
-        flags = KD.whoami().globalFlags
-        exempt = flags?.indexOf 'exempt'
-        exempt = (exempt? and ~exempt) or storage.getAt 'bucket.showLowQualityContent'
-        $.ajax KD.apiUri+'/1.0'
-          data      :
-            t       : if exempt then 1 else undefined
-            data    : JSON.stringify(_.extend options, selector)
-            env     : KD.config.env
-          dataType  : 'jsonp'
-          success   : (data)->
-            KD.remote.reviveFromSnapshots data, (err, instances)->
-              # console.log instances
-              callback instances
-
-    # KD.remote.api.CActivity.some selector, options, (err, activities) ->
-    #   callback activities
+    type = @activityListController._state
+    @performFetchingTeasers type, selector, options, (err, data) ->
+      KD.remote.reviveFromSnapshots data, (err, instances)->
+        callback instances
     #
     # KD.remote.api.CActivity.teasers selector, options, (err, activities) =>
     #   if not err and activities?
@@ -227,27 +240,13 @@
         createdAt : -1

     if not options.skip < options.limit
-      if controller._state is 'public'
-        @fetchTeasers selector, options, (activities)=>
-          if activities
-            for activity in activities
-              controller.addItem activity
-            callback? activities
-          else
-            callback?()
-
-      else if controller._state is 'private'
-        KD.whoami().fetchFeeds (err, feeds) =>
-          for feed in feeds
-            continue unless feed.title is 'followed'
-            feed.fetchActivities selector, options, (err, activities) =>
-              if not err and activities?
-                for activity in activities
-                  controller.addItem activity
-                callback? activities
-              else
-                callback?()
-
+      @fetchTeasers selector, options, (activities)=>
+        if activities
+          for activity in activities
+            controller.addItem activity
+          callback? activities
+        else
+          callback?()

   loadSomeTeasersIn:(sourceIds, options, callback)->
     KD.remote.api.Relationship.within sourceIds, options, (err, rels)->


steward: support for a target _blank, _self, _parent, _top, window name

--- lib/web/views/router.coffee
+++ lib/web/views/router.coffee
@@ -78,7 +78,7 @@
   attachClickListener: =>
     $(document).on "click", "a", (event) =>
       target = event.target
-      if !(event.shiftKey || event.ctrlKey || event.altKey || event.metaKey)
+      if !(event.shiftKey || event.ctrlKey || event.altKey || event.metaKey) && @targetAttributeIsCurrentWindow(target)
         href = target.attributes.href.nodeValue
         @log("Processing click", href)
         if @exec(href)
@@ -86,6 +86,18 @@
           event.preventDefault();
           @previousView = href
           @updateUrl(href)
+
+  targetAttributeIsCurrentWindow: (target) =>
+    if !target.attributes.target
+      return true
+    val = target.attributes.target.nodeValue
+    if ["_blank", "_parent"].indexOf(val) != -1
+      return false
+    if val == "_self"
+      return true
+    if val == "_top"
+      return window.self == window.top
+    return val == window.name

   attachLocationChangeListener: =>
     if @pushStateSupport


Copy edit

Copy edit

--- examples/examples.cjsx
+++ examples/examples.cjsx
@@ -31,8 +31,8 @@
           The header on this site is a living example. When you scroll down, it
           slides out of view and slides back in when scrolling up.

-
Fixed headers are nice when you want persistent navigation but they
-          can also get in the way especially on mobile where screen space is scarce.
+
Fixed headers are nice for persistent navigation but they
+          can also get in the way by taking up valuable vertical screen space.
           Using this component let's you have your persistent navigation while
           preserving screen space when the navigation is not needed.


payment: delegate to handleSuccess method

--- client/Main/payment/modal.coffee
+++ client/Main/payment/modal.coffee
@@ -66,8 +66,9 @@

       # return KD.showError err  if err

-      @form.showSuccess yes
+      @state = KD.utils.extend @state, subscription

+      @handleSuccess()

   handleStripeFail: (error) ->


update tests

update tests

--- services/clsi/test/unit/coffee/CompileManagerTests.coffee
+++ services/clsi/test/unit/coffee/CompileManagerTests.coffee
@@ -202,8 +202,8 @@

 	describe "wordcount", ->
 		beforeEach ->
-			@CommandRunner.run = sinon.stub().callsArg(5)
-			@fs.readFileSync = sinon.stub().returns @stdout = "Encoding: ascii\nWords in text: 2"
+			@CommandRunner.run = sinon.stub().callsArg(6)
+			@fs.readFile = sinon.stub().callsArgWith(2, null, @stdout = "Encoding: ascii\nWords in text: 2")
 			@callback  = sinon.stub()

 			@project_id = "project-id-123"
@@ -217,10 +217,10 @@
 		it "should run the texcount command", ->
 			@directory = "#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}"
 			@file_path = "$COMPILE_DIR/#{@file_name}"
-			@command =[ "texcount", "-inc", @file_path, "-out=" + @file_path + ".wc"]
+			@command =[ "texcount", "-nocol", "-inc", @file_path, "-out=" + @file_path + ".wc"]

 			@CommandRunner.run
-				.calledWith("#{@project_id}-#{@user_id}", @command, @directory, @image, @timeout)
+				.calledWith("#{@project_id}-#{@user_id}", @command, @directory, @image, @timeout, {})
 				.should.equal true

 		it "should call the callback with the parsed output", ->
@@ -234,5 +234,7 @@
 					elements: 0
 					mathInline: 0
 					mathDisplay: 0
+					errors: 0
+					messages: ""
 				})
 				.should.equal true


Fixes task clean:dist:darwin64

Fixes task clean:dist:darwin64

--- tasks/clean.coffee
+++ tasks/clean.coffee
@@ -26,7 +26,7 @@

 # Clean all the dist files for darwin64 and make sure the dir exists
 gulp.task 'clean:dist:darwin64', ->
-  del './dist/' + manifest.productName + '.dmg'
+  del './dist/' + manifest.productName + '-' + manifest.version + '-osx.dmg'
     .then -> fs.ensureDirAsync './dist'

 # Just ensure the dir exists (dist files are overwritten)


reformat a bit and remove a console call

--- client/navigator-location.coffee
+++ client/navigator-location.coffee
@@ -1,18 +1,23 @@
 # Nav is the main object for all cosmos:navigator packages
 # this package is the base package all others are built from.
 Nav =
+
   # tells if Nav is currently running
   running: false
+
   # (non-reactive) a string containing the browser location's path/query/hash
   location: null
   # reactive version of the above value
   _location: new ReactiveVar()
+
   # convenience function to set both location values
   _setLocations: (location) -> @location = location ; @_location.set location
+
   # all reactive getters
   get:
     # returns reactive value of location
     location: -> Nav._location.get()
+
   # all setters
   set:
     # changes the current location to the specified one
@@ -90,7 +95,6 @@
   # triggering actions
   _handlePopstate: (event) ->
     unless document.readyState is 'complete' then return
-    console.log 'event.state:',event?.state
     @_setLocations @_buildLocation()
     return


Prefetch: fix extra indentation

Prefetch: fix extra indentation

--- server/lib/server/client.coffee
+++ server/lib/server/client.coffee
@@ -48,7 +48,7 @@

   # if client id is not set, check for pendingCookies
   if not clientId and req.pendingCookies?.clientId
-      clientId = req.pendingCookies.clientId
+    clientId = req.pendingCookies.clientId

   return callback null, fakeClient unless clientId?


Only restart if tmp/restart.txt exists (not if it is deleted)

* Try to add resolved directory to campfire notices

--- lifeguard.coffee
+++ lifeguard.coffee
@@ -1,6 +1,7 @@
 forever = require "forever-monitor"
 campfire = (require "campfire").Campfire
 fs = require "fs"
+path = require "path"

 module.exports = class Lifeguard
   constructor: (@dir,@cmd) ->
@@ -40,7 +41,9 @@
     # set up watcher on tmp/restart.txt
     # it needs to exist for us to watch it...

-    @watcher = fs.watchFile "#{@dir}/tmp/restart.txt", => @_restartInstance()
+    @watcher = fs.watchFile "#{@dir}/tmp/restart.txt", =>
+      # only restart on a touch, not on a deletion (such as when current/ is unlinked)
+      @_restartInstance() if fs.existsSync("#{@dir}/tmp/restart.txt")

   #----------

@@ -49,16 +52,17 @@
       # INT will gracefully shut down workers and immediately kill the manager
       process.kill @instance.child.pid, "SIGINT"
       @instance.forceStop = true
-
+
+    rdir = path.resolve @dir
     @instance = new (forever.Monitor) @cmd.split(" "), cwd: @dir
     @instance.start()

-    @instance.on "start", => @_notifyRestart()
-    @instance.on "restart", => @_notifyRestart()
+    @instance.on "start", => @_notifyRestart rdir
+    @instance.on "restart", => @_notifyRestart rdir

-  _notifyRestart: ->
+  _notifyRestart: (rdir) ->
     if @campfire
-      msg = "Lifeguard: restarted resque-pool in #{@dir} at #{new Date}"
+      msg = "Lifeguard: restarted resque-pool in #{rdir} at #{new Date}"

       if @campfire_room
         # send directly


preemptive fix

preemptive fix

--- lib/command.coffee
+++ lib/command.coffee
@@ -43,8 +43,8 @@
             res = if Array.isArray res then res else [res]
             for val in res
               log.info val unless val? and val.type?
-              log.error val.message if val.type is 'stderr'
-              log.info val.message if val.type is 'stdout'
+              log.error val.message if val? and val.type is 'stderr'
+              log.info val.message if val? and val.type is 'stdout'
             cb null, res

         async.mapSeries tasks, runTask, -> log.info "Tasks completed!"


comment out rewire test

comment out rewire test

--- src/api/apps/articles/test/model/index/index.test.coffee
+++ src/api/apps/articles/test/model/index/index.test.coffee
@@ -25,8 +25,8 @@
       index: 'articles_' + process.env.NODE_ENV

   beforeEach (done) ->
-    @deleteArticleFromSailthru = sinon.stub().yields()
-    Article.__set__ 'deleteArticleFromSailthru', @deleteArticleFromSailthru
+    # @deleteArticleFromSailthru = sinon.stub().yields()
+    # Article.__set__ 'deleteArticleFromSailthru', @deleteArticleFromSailthru

     empty ->
       fabricate 'articles', _.times(10, -> {}), ->
@@ -94,15 +94,15 @@
         err.message.should.equal 'Article not found.'
         done()

-    it 'removes the article from sailthru', (done) ->
-      fabricate 'articles', {
-        _id: ObjectId('5086df098523e60002000018')
-        layout: 'video'
-        slugs: ['article-slug']
-      }, =>
-        Article.destroy '5086df098523e60002000018', (err) =>
-          @deleteArticleFromSailthru.args[0][0].should.containEql '/video/article-slug'
-          done()
+    # it 'removes the article from sailthru', (done) ->
+    #   fabricate 'articles', {
+    #     _id: ObjectId('5086df098523e60002000018')
+    #     layout: 'video'
+    #     slugs: ['article-slug']
+    #   }, =>
+    #     Article.destroy '5086df098523e60002000018', (err) =>
+    #       @deleteArticleFromSailthru.args[0][0].should.containEql '/video/article-slug'
+    #       done()

     it 'removes the article from elasticsearch', (done) ->
       fabricate 'articles', { _id: ObjectId('5086df098523e60002000019'), title: 'quux' }, ->


Fixes the flaky test for SuperArticle count

The missing indent caused CoffeeScript to interpret this code as:

  function_call(arg1, arg2, function() { })
  do_stuff

Instead of:

  function_call(arg1, arg2, function() { do_stuff })

So the assertion was not guaranteed to be called after the fabricate
function completes the execution.

--- src/api/apps/articles/test/model/index/index.test.coffee
+++ src/api/apps/articles/test/model/index/index.test.coffee
@@ -155,10 +155,10 @@
           related_articles: [ObjectId('5086df098523e60002000018')]
         }
       , ->
-      id = '5086df098523e60002000018'
-      Article.getSuperArticleCount(id)
-      .then (count) =>
-        count.should.equal 1
+        id = '5086df098523e60002000018'
+        Article.getSuperArticleCount(id)
+        .then (count) =>
+          count.should.equal 1

   describe '#promisedMongoFetch', ->
     it 'returns results, counts, and totals', ->


FIX: Fix breaking create ticket without task

--- src/app/helpdesk/modals/ticket-modal/ticket-modal.coffee
+++ src/app/helpdesk/modals/ticket-modal/ticket-modal.coffee
@@ -68,7 +68,7 @@
     HelpdeskTicket.submitTicket(
       $scope.ticket.project.project_id,
       $scope.ticket.description,
-      $scope.ticket.task_definition.id,
+      $scope.ticket.task_definition?.id,
       openTicketCallback
     )


FIX: Fix logic for loading task definition on ticket modal

--- src/app/helpdesk/modals/ticket-modal/ticket-modal.coffee
+++ src/app/helpdesk/modals/ticket-modal/ticket-modal.coffee
@@ -48,8 +48,8 @@
     unitService.getUnit $scope.selectedProject.unit_id, false, false, (response) ->
       $scope.selectedUnit = response
       # If ticket was provided, we need to look up the task def now from the
-      # unit loaded
-      unless $scope.isNew
+      # unit loaded if it has one
+      if not $scope.isNew and $scope.ticket.task_definition_id?
         taskDef = $scope.selectedUnit.taskDef($scope.ticket.task_definition_id)
         $scope.taskDefSelected taskDef


JCredential: fetchUsers implemented, relationship logic simplified. there is one-to-one relationship between user and the credential now, and user can be a JAccount or a JGroup

--- workers/social/lib/social/models/computeproviders/credential.coffee
+++ workers/social/lib/social/models/computeproviders/credential.coffee
@@ -41,10 +41,10 @@
       instance        :
         delete        :
           (signature Function)
-        share         :
+        shareWith     :
           (signature Object, Function)
-        # withold       :
-        #   (signature Object, Function)
+        fetchUsers    :
+          (signature Function)
         # update        :
         #   (signature Object, Function)

@@ -104,7 +104,6 @@
         credential.save (err)->
           return  if failed err, callback, credData

-          delegate.addCredential credential, as: "user"
           delegate.addCredential credential, as: "owner", (err)->
             return  if failed err, callback, credential, credData

@@ -136,17 +135,43 @@
       options ?= { limit : 10 }

       {delegate} = client.connection
-      delegate.fetchCredentials as: "user",
+      delegate.fetchCredentials {},
         targetOptions : {
           selector, options
         }, callback

+  fetchUsers: permit
+
+    advanced: [
+      { permission: 'update credential', validateWith: Validators.own }
+    ]
+
+    success: (client, callback)->
+
+      Relationship.someData targetId : @getId(), {
+        as:1, sourceId:1, sourceName:1
+      }, (err, cursor)->
+
+        return callback err  if err?
+
+        cursor.toArray (err, arr)->
+          return callback err  if err?
+
+          if arr.length > 0
+            callback null, ({
+              constructorName : u.sourceName
+              _id : u.sourceId
+              as  : u.as
+            } for u in arr)
+          else
+            callback null, []

   # .share can be used like this:
   #
   # JCredentialInstance.share { user: yes, owner: no, target: "gokmen"}, cb
-
-  share: permit
+  #                                      group or user slug -> ^^^^^^
+
+  shareWith: permit

     advanced: [
       { permission: 'update credential', validateWith: Validators.own }
@@ -154,27 +179,21 @@

     success: (client, options, callback)->

-      {target, owner, user} = options
+      {target, user, owner} = options
+      user ?= yes

       setPermissionFor = (target, callback)=>

-        method = (x)-> if x then 'assureCredential' else 'removeCredential'
-
-        daisy queue = [
-          =>
-            if user?
-              target[method(user)]  this, as: 'user', queue.next
-            else
-              queue.next()
-        ,
-          =>
-            if owner?
-              target[method(owner)] this, as: 'owner', queue.next
-            else
-              queue.next()
-        ,
-          -> callback null
-        ]
+        Relationship.remove {
+          targetId : @getId()
+          sourceId : target.getId()
+        }, (err)=>
+
+          if user
+            as = if owner then 'owner' else 'user'
+            target.addCredential this, { as }, (err)-> callback err
+          else
+            callback err

       JName.fetchModels target, (err, result)=>

@@ -182,7 +201,7 @@
           return callback new KodingError "Target not found."

         { models } = result
-        target = models[0]
+        [ target ] = models

         if target instanceof JUser
           target.fetchOwnAccount (err, account)=>


changed number of decimal points for tissue expression graph

--- static/js/controllers/designer.js.coffee
+++ static/js/controllers/designer.js.coffee
@@ -179,20 +179,20 @@
       guides_data.push guides_count
     angular.forEach $scope.gene.exons, (exon, key) ->
       if guidesFactory.data.tissues_disabled # don't use median
-        expression_data[0].push exon.expression.overall / max_expression
-        expression_data[1].push exon.expression.brain / max_expression
-        expression_data[2].push exon.expression.heart / max_expression
-        expression_data[3].push exon.expression.kidney / max_expression
-        expression_data[4].push exon.expression.liver / max_expression
-        expression_data[5].push exon.expression.skin / max_expression
+        expression_data[0].push (exon.expression.overall / max_expression).toFixed(2)
+        expression_data[1].push (exon.expression.brain / max_expression).toFixed(2)
+        expression_data[2].push (exon.expression.heart / max_expression).toFixed(2)
+        expression_data[3].push (exon.expression.kidney / max_expression).toFixed(2)
+        expression_data[4].push (exon.expression.liver / max_expression).toFixed(2)
+        expression_data[5].push (exon.expression.skin / max_expression).toFixed(2)
       else
-        expression_data[0].push exon.expression.overall / max_expression
-        expression_data[1].push exon.expression.median / max_expression
-        expression_data[2].push exon.expression.brain / max_expression
-        expression_data[3].push exon.expression.heart / max_expression
-        expression_data[4].push exon.expression.kidney / max_expression
-        expression_data[5].push exon.expression.liver / max_expression
-        expression_data[6].push exon.expression.skin / max_expression
+        expression_data[0].push (exon.expression.overall / max_expression).toFixed(2)
+        expression_data[1].push (exon.expression.median / max_expression).toFixed(2)
+        expression_data[2].push (exon.expression.brain / max_expression).toFixed(2)
+        expression_data[3].push (exon.expression.heart / max_expression).toFixed(2)
+        expression_data[4].push (exon.expression.kidney / max_expression).toFixed(2)
+        expression_data[5].push (exon.expression.liver / max_expression).toFixed(2)
+        expression_data[6].push (exon.expression.skin / max_expression).toFixed(2)
       expression_labels.push('Exon ' + (key+1))
       guide_labels.push('') # empty labels
     $scope.chart_config.expression.labels = expression_labels


Add support for \protect (from import)

Add support for \protect (from import)


:bug: Return undefined explicitly in Helpers::validateMessages

--- lib/helpers.coffee
+++ lib/helpers.coffee
@@ -10,6 +10,7 @@
       result.range = Range.fromObject result.range if result.range?
       result.class = result.type.toLowerCase().replace(' ', '-')
       Helpers.validateMessages(result.trace) if result.trace
+    return # Explicit return to return undefined
   validateLinter: (linter) ->
     unless linter.grammarScopes instanceof Array
       message = "grammarScopes is not an Array. (see console for more info)"


Ensure modal is active

Ensure modal is active

--- resources/assets/coffee/_classes/user-verification.coffee
+++ resources/assets/coffee/_classes/user-verification.coffee
@@ -76,6 +76,10 @@
       modal.style.paddingTop = "#{referenceBottom}px"

+  isActive: =>
+    @modal?.classList.contains('js-user-verification--active')
+
+
   prepareForRequest: (type) =>
     @request?.abort()
     @setMessage osu.trans("user_verification.box.#{type}"), true
@@ -94,7 +98,7 @@

   reposition: =>
-    return unless @modal?.classList.contains('js-user-verification--active')
+    return unless @isActive()

     if osu.isMobile()
       @float(true, @modal)
@@ -119,6 +123,8 @@

   success: =>
+    return unless @isActive()
+
     @$modal().modal 'hide'
     @modal.classList.remove('js-user-verification--active')


setEncoding doesn't seem to work with my own streams, it also seems unnecessary

--- lib/massage.coffee
+++ lib/massage.coffee
@@ -82,7 +82,6 @@
   # piping buffer transformations
   pipe: (stream_in, stream_out, encoding = "utf8") ->
     self = this
-    stream_in.setEncoding encoding
     stream_in.on "data", (chunk) ->
       stream_out.write self.transform(chunk), encoding


backup changing default style in asset_attachment.js.coffee in case responsive styles haven't been generated

--- app/assets/javascripts/tenon/features/tenon_content/asset_attachment.js.coffee
+++ app/assets/javascripts/tenon/features/tenon_content/asset_attachment.js.coffee
@@ -3,7 +3,7 @@
     @$assetField.find('.asset-id-field').val($li.data('record-id'))
     @$assetField.find('.asset-crop').attr('data-asset-id', $li.data('record-id'))
     $img = $li.find('.thumbnail img').clone()
-    $img.attr('src', $li.data('style-urls')['twelve'])
+    $img.attr('src', $li.data('style-urls')['medium'])
     @$assetField.find('.image a.add-image, .image img').remove()
     @$assetField.find('.image').prepend($img)


remove log statement

remove log statement

--- lib/motions.coffee
+++ lib/motions.coffee
@@ -344,7 +344,6 @@

   execute: (count) ->
     lastScreenRow = @editorView.getLastVisibleScreenRow()
-    console.log(lastScreenRow)
     lastRow = @editor.getBuffer().getLastRow()
     destRow = if lastRow != lastScreenRow then lastScreenRow - 1 else lastRow + 1
     super(destRow)


Remove unused variable

Remove unused variable

--- src/packages/command-panel/spec/command-panel-spec.coffee
+++ src/packages/command-panel/spec/command-panel-spec.coffee
@@ -3,7 +3,7 @@
 _ = require 'underscore'

 describe "CommandPanel", ->
-  [editor, buffer, commandPanel, CommandPanel] = []
+  [editor, buffer, commandPanel] = []

   beforeEach ->
     window.rootView = new RootView


prepare more snooze options

prepare more snooze options

--- lib/pagerv2.coffee
+++ lib/pagerv2.coffee
@@ -239,35 +239,36 @@
   getIncident: (incident) ->
     @request('GET', "/incidents/#{incident}")

-  listIncidents: (statuses = 'triggered,acknowledged') ->
-    query = {
-      date_range: 'all',
-      time_zone: 'UTC'
-    }
-    if statuses?
-      query.statuses = statuses.split(/,/)
-    @request('GET', '/incidents', query)
+  listIncidents: (incidents = '', statuses = 'triggered,acknowledged') ->
+    if incidents isnt ''
+      new Promise (res, err) ->
+        res {
+          incidents: incidents.split(/, ?/).map (inc) ->
+            { id: inc }
+          }
+    else
+      query = {
+        date_range: 'all',
+        time_zone: 'UTC'
+      }
+      if statuses?
+        query.statuses = statuses.split /,/
+      @request('GET', '/incidents', query)

   updateIncidents: (user, incidents = '', which = 'triggered', status = 'acknowledged') ->
     @getUserEmail(user)
     .bind({ from: null })
     .then (email) =>
       @from = email
-      if incidents isnt ''
-        new Promise (res, err) ->
-          res incidents.split /, ?/
-      else
-        @listIncidents(which)
-        .then (data) ->
-          (inc.id for inc in data.incidents)
-    .then (incidents) =>
-      if incidents.length > 0
+      @listIncidents incidents, which
+    .then (data) =>
+      if data.incidents.length > 0
         payload = {
           incidents: []
         }
-        for inc in incidents
+        for inc in data.incidents
           payload.incidents.push {
-            id: inc,
+            id: inc.id,
             type: 'incident_reference',
             status: status
           }
@@ -275,16 +276,22 @@
       else
         "There is no #{which} incidents at the moment."

-  snoozeIncidents: (user, incidents, duration = 120) ->
+  snoozeIncidents: (user, incidents = '', duration = 120) ->
     @getUserEmail(user)
+    .bind({ from: null })
     .then (email) =>
-      incidents = incidents.split /, ?/
-      incidentsDone = Promise.map incidents, (inc) =>
-        payload = {
-          duration: duration
-        }
-        @request('POST', "/incidents/#{inc}/snooze", payload, email)
-      Promise.all incidentsDone
+      @from = email
+      @listIncidents incidents
+    .then (data) =>
+      if data.incidents.length > 0
+        incidentsDone = Promise.map data.incidents, (inc) =>
+          payload = {
+            duration: duration
+          }
+          @request('POST', "/incidents/#{inc.id}/snooze", payload, @from)
+        Promise.all incidentsDone
+      else
+        "There is no #{which} incidents at the moment."


Change settings order

Change settings order

--- lib/pg-hoff.coffee
+++ lib/pg-hoff.coffee
@@ -22,30 +22,37 @@
         host:
             type: 'string'
             default: 'http://localhost:5000'
+            order: 1
         pollInterval:
             type: 'integer',
             description: 'Poll interval in milliseconds.'
             minimum: 10
             maximum: 10000
             default: 100
-        autocompletionEnabled:
-            type: 'boolean'
-            default: true
-        locale:
-            type: 'string'
-            default: 'sv-SE'
+            order: 2
         displayQueryExecutionTime:
             type: 'boolean'
             description: 'Display query execution time after the query is finished.'
             default: true
+            order: 3
+        autocompletionEnabled:
+            type: 'boolean'
+            default: true
+            order: 4
         pascaliseAutocompletions:
             type: 'boolean'
             default: true
             description: 'user_name becomes User_Name'
+            order: 5
         unQuoteFunctionNames:
             type: 'boolean'
             default: true
             description: '"sum"() becomes sum()'
+            order: 6
+        locale:
+            type: 'string'
+            default: 'sv-SE'
+            order: 7

     activate: (state) ->
         console.debug 'Activating the greatest plugin ever..'


focus output panel on error when there is only one resultset with less than 2 rows and notices

--- lib/pg-hoff.coffee
+++ lib/pg-hoff.coffee
@@ -218,14 +218,17 @@

         allCompleted = true
         gotResults = false
-        gotOutput = false
+        gotNotices = false
+        gotError = false
         for resultset in resultsets
           if !resultset.complete
             allCompleted = false
           if resultset.columns
             gotResults = true
-          if resultset.error or resultset.notices?[0]
-            gotOutput = true
+          if resultset.notices?[0]
+            gotNotices = true
+          if resultset.error
+            gotError = true

         #recreate panes if previously removed
         if !@outputPane or !@resultsPane
@@ -259,13 +262,18 @@
             @removeHoffPane(@resultsPane)
             @bottomDock.deletePane(@resultsPane.getId())
             @resultsPane = null
-        if allCompleted and !gotOutput
+        if allCompleted and !gotNotices and !gotError
             @removeHoffPane(@outputPane)
             @bottomDock.deletePane(@outputPane.getId())
             @outputPane = null

         if not @bottomDock?.isActive()
             @bottomDock.toggle()
+
+        if gotError or (gotResults and resultsets?.length = 1 and resultsets?[0].rows.length < 2)
+            @bottomDock.changePane(@outputPane.getId())
+        else if gotResults
+            @bottomDock.changePane(@resultsPane.getId())

     removeHoffPane: (pane) ->
         index = @hoffPanes.indexOf(pane);


Removed debug log.

Removed debug log.

--- app/assets/javascripts/app/controllers/ticket_zoom.js.coffee
+++ app/assets/javascripts/app/controllers/ticket_zoom.js.coffee
@@ -462,7 +462,6 @@

     # rerender whole sidebar if customer or organization has changed
     if @ticketLastAttributes.customer_id isnt @ticket.customer_id || @ticketLastAttributes.organization_id isnt @ticket.organization_id
-      console.log('rerender sidebar')
       new App.WidgetAvatar(
         el:       @$('.page-header .js-avatar')
         user_id:  @ticket.customer_id
@@ -836,7 +835,6 @@
     @render()

   render: (overview) =>
-    console.log('RENDER OverviewNavigator', @overview_id)
     if !@overview_id
       @html('')
       return


Revert "Don't show topic popover"

Revert "Don't show topic popover"

This reverts commit 0190d9a6405a0e5a9cd0ab1a42c538821fcff13a.

--- core/app/backbone/views/activities/activity_item_view.coffee
+++ core/app/backbone/views/activities/activity_item_view.coffee
@@ -59,14 +59,14 @@
     topic: @topic().toJSON()

   onRender: ->
-    # Backbone.Factlink.makeTooltipForView @,
-    #   positioning:
-    #     align: 'left'
-    #     side: 'bottom'
-    #   selector: '.js-link'
-    #   $offsetParent: @options.$offsetParent
-    #   tooltipViewFactory: =>
-    #     new TopicPopoverContentView model: @topic()
+    Backbone.Factlink.makeTooltipForView @,
+      positioning:
+        align: 'left'
+        side: 'bottom'
+      selector: '.js-link'
+      $offsetParent: @options.$offsetParent
+      tooltipViewFactory: =>
+        new TopicPopoverContentView model: @topic()

   topic: -> @_topic ?= new Topic(@model.get('activity').topic)


Social: add channel fetcher functions (byId and byName)

--- workers/social/lib/social/models/socialapi/channel.coffee
+++ workers/social/lib/social/models/socialapi/channel.coffee
@@ -12,7 +12,11 @@
   @set
     sharedMethods :
       static      :
-        fetchActivities     :
+        byId              :
+          (signature Object, Function)
+        byName            :
+          (signature Object, Function)
+        fetchActivities   :
           (signature Object, Function)
         fetchChannels     :
           (signature Object, Function)
@@ -105,6 +109,16 @@

     @constructor.cycleChannel options, callback

+  # byId - fetch channel by id
+  @byId = secureRequest
+    fnName  : 'channelById'
+    validate: ["id"]
+
+  # byName - fetch channel by name
+  @byName = secureRequest
+    fnName  : 'channelByName'
+    validate: ["name"]
+
   # searchTopics - search topics for autocompletion
   @searchTopics          = secureRequest fnName: 'searchTopics'


bugfix for re-wrapped remote dnode proxies

--- tools/objects.coffee
+++ tools/objects.coffee
@@ -19,7 +19,9 @@
             objects.process obj,
                 each: (key, value, next)->
                     if typeof value is 'function'
-                        proxy[key] = objects.promisify value, obj
+                        wrapper = (args...)->
+                            obj[key].apply obj, args
+                        proxy[key] = objects.promisify wrapper, obj
                     else
                         proxy[key] = value

@@ -595,7 +597,7 @@
                     target.push item

                 next()
-
+
     else
         objects.process source,
             each: (key, item, next)=>


Remove unused dependency

Remove unused dependency

--- src/scripts/modules/media/tabs/contents/tree/tree.coffee
+++ src/scripts/modules/media/tabs/contents/tree/tree.coffee
@@ -1,5 +1,4 @@
 define (require) ->
-  $ = require('jquery')
   _ = require('underscore')
   BaseView = require('cs!helpers/backbone/views/base')
   TocLeafView = require('cs!./leaf')


[➠] Improved “formy”.

[➠] Improved “formy”.

--- vendor/assets/javascripts/jquery/plugins/formify/formy.js.coffee
+++ vendor/assets/javascripts/jquery/plugins/formify/formy.js.coffee
@@ -32,96 +32,20 @@

     return

-  hideFieldsets: ->
-    @fieldsets.velocity @options.fieldsets.hide.effect,
-      duration: @options.fieldsets.hide.duration
-      complete: @showSuccess
-
-    return
-
-  showSuccess: =>
-    @success.velocity @options.success.show.effect,
-      duration: @options.success.show.duration
-
-    return
-
-  showErrors: (errors) ->
-    if errors
-      messages = @translateErrors errors
-
-      output = @errorMessagesTemplate.render messages
-
-      @errorMessages.append output
-
-      do @error.show
-
-    return
-
-  translateErrors: (errors) ->
-    result   = {}
-    messages = []
-
-    $.each errors, (key, message) ->
-      messages.push {
-        key: $.i18n._ key
-        message: $.i18n._ message
-      }
-
-      return
-
-    result.messages = messages
-
-    result
-
-  lock: ->
-    do @disabledElements
-
-    @form.addClass 'locked'
-
-    return
-
-  unlock: ->
-    do @enabledElements
-
-    @form.removeClass 'locked'
-
-    return
-
   isLocked: ->
     @form.hasClass 'locked'

-  enabledElements: ->
-    @elements.prop 'disabled', false
-
-    return
-
-  disabledElements: ->
-    @elements.prop 'disabled', true
-
-    return
-
-  cleanup: ->
-    do @success.hide
-
-    do @failure.hide
-
-    do @errorMessages.empty
-
-    do @error.hide
-
-    return
-
   # === Events ===

   onSuccess: =>
-    do @hideFieldsets
+    hideFieldsets.call @

     return

   beforeRequestSend: (xhr, settings) =>
-    do @cleanup
-
-    do @lock
+    cleanup.call @
+
+    lock.call @

     return

@@ -138,11 +62,11 @@
         response = xhr.responseJSON
         errors   = response.errors

-        @showErrors errors
+        showErrors.call @, errors
       else
-        do @failure.show
-
-    do @unlock
+        showFailure.call @
+
+    unlock.call @

     return

@@ -195,4 +119,85 @@
   setupSubmit = ->
     @submit.on 'click', @onSubmitClick

+    return
+
+  hideFieldsets = ->
+    @fieldsets.velocity @options.fieldsets.hide.effect,
+      duration: @options.fieldsets.hide.duration
+      complete: showSuccess.call @
+
+    return
+
+  showSuccess = ->
+    @success.velocity @options.success.show.effect,
+      duration: @options.success.show.duration
+
+    return
+
+  showErrors = (errors) ->
+    if errors
+      messages = translateErrors.call @, errors
+
+      output = @errorMessagesTemplate.render messages
+
+      @errorMessages.append output
+
+      do @error.show
+
+    return
+
+  translateErrors = (errors) ->
+    result   = {}
+    messages = []
+
+    $.each errors, (key, message) ->
+      messages.push {
+        key: $.i18n._ key
+        message: $.i18n._ message
+      }
+
+      return
+
+    result.messages = messages
+
+    result
+
+  showFailure = ->
+    do @failure.show
+
+    return
+
+  lock = ->
+    disabledElements.call @
+
+    @form.addClass 'locked'
+
+    return
+
+  unlock = ->
+    enabledElements.call @
+
+    @form.removeClass 'locked'
+
+    return
+
+  enabledElements = ->
+    @elements.prop 'disabled', false
+
+    return
+
+  disabledElements = ->
+    @elements.prop 'disabled', true
+
+    return
+
+  cleanup = ->
+    do @success.hide
+
+    do @failure.hide
+
+    do @errorMessages.empty
+
+    do @error.hide
+
     return


fix #78

fix #78

--- src/server/models/ComputePersonal.coffee
+++ src/server/models/ComputePersonal.coffee
@@ -62,7 +62,7 @@
             delta = if result == GAME_RESULT.MAFIA_WIN then 1 else -1
           profile.efficiency.push({
             gameID: games[i].id
-            date: games[i].date
+            date: games[i].date.getTime()
             winsMinusLosses: delta
           })
           profile.rating += rating(playerGame, result)


NL@EOF

NL@EOF

--- BackofficeBundle/Resources/public/coffee/widget/widgetChannel.coffee
+++ BackofficeBundle/Resources/public/coffee/widget/widgetChannel.coffee
@@ -2,4 +2,4 @@

 widgetChannel.bind 'ready', (view) ->
   view.onOrchestraViewReady()
-  view.onViewReady()+  view.onViewReady()


two boxes for video navigation

two boxes for video navigation

--- client/app/Applications/Activity.kdapplication/ContentDisplays/tutorial.coffee
+++ client/app/Applications/Activity.kdapplication/ContentDisplays/tutorial.coffee
@@ -198,12 +198,10 @@
           item.hide()
           item.destroy()

-    # @listInfo = new KDView
-
-    @listAnchor = new KDView
+    @listAnchorNext = new KDView
+    @listAnchorPrevious = new KDView

     KD.remote.api.JTutorialList.fetchForTutorialId @getData().getId(), (listData)=>
-      log "list",listData
       if listData
         # @listInfo.updatePartial "In list '#{listData.title}'"
         for tutorial,i in listData.tutorials
@@ -211,13 +209,12 @@
             @position = i
             @before = listData.tutorials[0...i]
             @after = listData.tutorials[i+1..]
-        log "at",@position,"before",@before,"after",@after
         if @after.length >0
-          @listAnchor.addSubView new TutorialListSwitchBox
+          @listAnchorNext.addSubView new TutorialListSwitchBox
             direction:"next"
           , @after[0]
         if @before.length >0
-          @listAnchor.addSubView new TutorialListSwitchBox
+          @listAnchorPrevious.addSubView new TutorialListSwitchBox
             direction:"previous"
           , @before[0]

@@ -294,7 +291,6 @@
             AUTHOR

-            {{> @listAnchor}}

{{@utils.expandUsernames @utils.applyMarkdown #(title)}}

@@ -306,7 +302,9 @@

             {{> @editDiscussionLink}}
             {{> @deleteDiscussionLink}}
+            {{> @listAnchorPrevious}}
             {{> @embedBox}}
+            {{> @listAnchorNext}}

{{@utils.expandUsernames(@utils.applyMarkdown(#(body)),"pre")}}

@@ -333,13 +331,11 @@

     @outgoingContainer = new KDView
     if data.link?
-      log data.link
       image = new KDCustomHTMLView
         cssClass : "image-preview"
         tagName : "img"
         attributes :
           src : @utils.proxifyUrl data.link.link_embed.images[0].url
-      log image
       @outgoingContainer.addSubView image

     @outgoingLink = new KDButtonView


wrong arrow

wrong arrow

--- src/smc-hub/kucalc/compute-client.coffee
+++ src/smc-hub/kucalc/compute-client.coffee
@@ -234,7 +234,7 @@
         dbg = @dbg("address")
         dbg('first ensure is running')
         @ensure_running
-            cb : (err) ->
+            cb : (err) =>
                 if err
                     dbg('error starting it up')
                     opts.cb(err)


tracker errors are NOT fatal

tracker errors are NOT fatal

--- src/smc-hub/postgres-user-queries.coffee
+++ src/smc-hub/postgres-user-queries.coffee
@@ -1319,7 +1319,7 @@
                         # Any tracker error means this changefeed is now broken and
                         # has to be recreated.
                         tracker?.on 'error', (err) ->
-                            changes.cb("FATAL: tracker error - #{err}")
+                            changes.cb("tracker error - #{err}")
                         cb()
         ], cb)


runtime: Wait a bit longer for Arduino reset

--- lib/runtime.coffee
+++ lib/runtime.coffee
@@ -334,7 +334,7 @@
     setTimeout () ->
         runtime.device.open () ->
             send()
-    , 1000 # wait for Arduino reset
+    , 2000 # HACK: wait for Arduino reset

 subscribeEdges = (runtime, edges, callback) ->
     graph = runtime.graph


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color2.coffee
+++ test/color2.coffee
@@ -71,4 +71,13 @@
   equal color.b.round(), 89
   equal color.a, 0.47

+test "accepts hex value of length 3 as first argument and alpha value as second argument", ->
+  color = Color('#ff0', 0.5)
+
+  equal color.r, 255
+  equal color.g, 255
+  equal color.b, 0
+  equal color.a, 0.5
+
+
 module()


Guard against null endpoints in SelectionManager#preserveSelection

Cut or delete operations picked up by the mutation observer can result in getSelectionEndPoints returning null.

--- src/trix/models/selection_manager.coffee
+++ src/trix/models/selection_manager.coffee
@@ -69,7 +69,8 @@
       @setLocationRange(lockedLocationRange) if lockedLocationRange?

   preserveSelection: (block) ->
-    point = @getSelectionEndPoints()[0]
+    return block() unless endPoints = @getSelectionEndPoints()
+    point = endPoints[0]
     locationRange = @getLocationRange()
     block()
     locationRangeAtPoint = @getLocationRangeAtPoint(point)


Follow mixin

Follow mixin

--- test/follow.coffee
+++ test/follow.coffee
@@ -39,6 +39,6 @@
   rightEnemy.include(Follow)
   rightEnemy.follow(player)

-
+  ok rightEn

 module()


Follow mixin

Follow mixin

--- test/follow.coffee
+++ test/follow.coffee
@@ -4,6 +4,6 @@
   player = GameObject
     x: 50
     y: 50
-
+    width: 10

 module()


Updating Americano to latest

Updating Americano to latest

--- scripts/Americano.coffee
+++ scripts/Americano.coffee
@@ -40,9 +40,15 @@
     # The method to call to put the processing gears into action and
     # bind all the events
     bind: () ->
+        @bound = true
         @onBind()
-        @bound = true
-
+
+    #####ensureBound#####
+    # Makes sure the presenter is bound, if not it will call bind for you
+    ensureBound: () ->
+        if not @bound
+          @bind()
+
     #####onBind#####
     # Shoddy interface. `:P`
     onBind: () ->


Use x64_64 as arch in rpm name

Use x64_64 as arch in rpm name

--- build/tasks/publish-build-task.coffee
+++ build/tasks/publish-build-task.coffee
@@ -79,6 +79,10 @@
       unless fs.isFileSync(sourcePath)
         rpmName = fs.readdirSync("#{buildDir}/rpm")[0]
         sourcePath = "#{buildDir}/rpm/#{rpmName}"
+        if process.arch is 'ia32'
+          arch = 'i386'
+        else
+          arch = 'x64_64'
         assetName = "atom.#{arch}.rpm"

       {cp} = require('./task-helpers')(grunt)


Remove unused publish release step

Remove unused publish release step

--- build/tasks/publish-build-task.coffee
+++ build/tasks/publish-build-task.coffee
@@ -31,16 +31,12 @@

     zipApps buildDir, assets, (error) ->
       return done(error) if error?
-      uploadAssets release, buildDir, assets, (error) ->
+      getAtomDraftRelease (error, release) ->
         return done(error) if error?
-        publishRelease release, (error) ->
+        assetNames = (asset.assetName for asset in assets)
+        deleteExistingAssets release, assetNames, (error) ->
           return done(error) if error?
-          getAtomDraftRelease (error, release) ->
-            return done(error) if error?
-            assetNames = (asset.assetName for asset in assets)
-            deleteExistingAssets release, assetNames, (error) ->
-              return done(error) if error?
-              uploadAssets(release, buildDir, assets, done)
+          uploadAssets(release, buildDir, assets, done)

 logError = (message, error, details) ->
   grunt.log.error(message)
@@ -127,17 +123,3 @@
     assetPath = path.join(buildDir, assetName)
     tasks.push(upload.bind(this, release, assetName, assetPath))
   async.parallel(tasks, callback)
-
-publishRelease = (release, callback) ->
-  options =
-    uri: release.url
-    method: 'POST'
-    headers: defaultHeaders
-    json:
-      draft: false
-  request options, (error, response, body={}) ->
-    if error? or response.statusCode isnt 200
-      logError('Creating release failed', error, body)
-      callback(error ? new Error(response.statusCode))
-    else
-      callback()


fixed hear text

fixed hear text

--- scripts/TM-Github.coffee
+++ scripts/TM-Github.coffee
@@ -54,8 +54,8 @@
     request.post options, (error, response, data)->
       res.reply 'Request sent, take a look at https://ci.appveyor.com/project/DinisCruz/tm-ci-deploy where a new build will have just started'
       res.reply 'In a bit, the deployed website will be at https://tm-qa-6.azurewebsites.net'
-
-  robot.hear /TM_4_0_Design(.*)Dev(.*)commits/i, (res)->
+
+  robot.hear /TM_4_0_Design(.*)commit/i,(res)->
     res.reply 'New commit to TM_4_0_Design/Dev detected ...'
     res.reply '...time to deploy to azure'


put cursor back in box

put cursor back in box

--- app/assets/javascripts/app/controllers/messages/article.js.coffee
+++ app/assets/javascripts/app/controllers/messages/article.js.coffee
@@ -53,7 +53,7 @@
     message.from_user(App.user)
     message.conversation(@record)
     if message.body
-      @input.text('')
+      @render()
       message.save()
       @record.open()

@@ -99,5 +99,4 @@

   scrollToBottom: ->
     @delay ->
-      @items.scrollTop(@items[0].scrollHeight)
-      @compose.find('.input').select()+      @items.scrollTop(@items[0].scrollHeight)


activity: channelByName getter accepts types

--- client/activity/lib/flux/getters.coffee
+++ client/activity/lib/flux/getters.coffee
@@ -1,6 +1,5 @@
 kd                         = require 'kd'
 immutable                  = require 'immutable'
-isPublicChatChannel        = require 'activity/util/isPublicChatChannel'
 whoami                     = require 'app/util/whoami'
 isPublicChannel            = require 'app/util/isPublicChannel'
 calculateListSelectedIndex = require 'activity/util/calculateListSelectedIndex'
@@ -202,10 +201,10 @@
     return thread.set 'channel', channel
 ]

-channelByName = (name) ->
+channelByName = (name, types = ['topic', 'group']) ->
   channels = kd.singletons.reactor.evaluateToJS allChannels
-  channel = _channel for id, _channel of channels when _channel.name is name
-  return channel
+  for id, _channel of channels when _channel.name is name and _channel.typeConstant in types
+    return _channel

 # Returns followed public channel threads mapped with relevant channel
 # instances.


Tweak Google search.

Tweak Google search.

--- config/search/jkn2-google.coffee
+++ config/search/jkn2-google.coffee
@@ -8,6 +8,7 @@
     "div#search li.g"
     # "div.srg div.g"
     "div.g > div.rc"
+    "div.g > div > div.rc"
   ]
   style:
     "border-color": "#0266C8"


add skeleton for Facebook login tests

add skeleton for Facebook login tests


rollback broken test changes

rollback broken test changes

--- test/mvstar.coffee
+++ test/mvstar.coffee
@@ -3,48 +3,6 @@

 Model = mvstar.Model
 View = mvstar.View
-
-Form = Model
-  defaults:
-    inputs: []
-
-Input = Model
-  defaults:
-    name:  ''
-    value: ''
-  validators:
-    name: (name) -> name != ''
-
-FormView = View
-  model: Form
-  pull:
-    'inputs': (inputs) ->
-      @el.appendChild input.el for input in inputs
-    'inputs:add': (inputs, added) ->
-      @el.appendChild added
-    'inputs:delete': (inputs, deleted) ->
-      @el.removeChild deleted
-  template: -> '<form></form>'
-
-InputView = View
-  model: Input
-  pull:
-    name:  (name)  -> @el.name  = name
-    value: (value) -> @el.value = value
-  push:
-    'name:changed':  (name)  -> @model.name  = name
-    'value:changed': (value) -> @model.value = value
-  template: -> '<input></input>'
-
-# Usage
-form = FormView
-  inputs: [
-    InputView name: 'bar', value: 1
-    InputView name: 'baz', value: 2
-  ]
-
-form.render()
-document.body.appendChild form.el

 describe 'mvstar', ->
   it 'should expose Model and View constructors', ->


Social: style fix

Social: style fix

--- client/Social/Activity/views/privatemessagerecipientmodal.coffee
+++ client/Social/Activity/views/privatemessagerecipientmodal.coffee
@@ -44,7 +44,7 @@

     return KD.showError message : 'Please add a recipient.'  unless recipients.length

-    options  = {channelId: @getData().id, accountIds:recipients}
+    options  = { channelId: @getData().id, accountIds: recipients }

     KD.singletons.socialapi.channel.addParticipants options, (err, result) ->


Added dummy test cases for StockContentProvider


update prefix of example block to fix merge conflict with upstream


changed to final bot name

changed to final bot name

--- scripts/transport.coffee
+++ scripts/transport.coffee
@@ -1,7 +1,7 @@
 #https://github.com/github/hubot/blob/master/docs/scripting.md

 module.exports = (robot) ->
-  robot.hear /@test (.*)/i, (msg) -> #capture everything and forward it to API.AI
+  robot.hear /@st-bot (.*)/i, (msg) -> #capture everything and forward it to API.AI
     url = "https://api.api.ai/api/query?v=20150910&lang=en&sessionId="+createUID()+"&query="+encodeURI(msg.match[1])
     robot.logger.debug('url: '+url)
     robot.http(url)


test($filter):add test for `contains` build-in filter operation.

--- test/odata.query.filter.functions.coffee
+++ test/odata.query.filter.functions.coffee
@@ -26,6 +26,26 @@
       s = server.listen PORT, ->
         PORT = s.address().port
         done()
+
+  describe '[contains]', ->
+    it 'should filter items', (done) ->
+      request("http://localhost:#{PORT}")
+        .get("/book?$filter=contains(title,'i')")
+        .expect(200)
+        .end (err, res) ->
+          return done(err)  if(err)
+          res.body.value.length.should.greaterThan(0)
+          res.body.value.should.matchEach((item) -> item.title.indexOf('i') >= 1)
+          done()
+    it 'should filter items when it has extra spaces in query string', (done) ->
+      request("http://localhost:#{PORT}")
+        .get("/book?$filter=contains(title,'Visual Studio')")
+        .expect(200)
+        .end (err, res) ->
+          return done(err)  if(err)
+          res.body.value.length.should.greaterThan(0)
+          res.body.value.should.matchEach((item) -> item.title.indexOf('Visual Studio') >= 0)
+          done()

   describe '[indexof]', ->
     it 'should filter items', (done) ->


Kloud::info: instead rejecting promises in queue send them back the current state of machine

--- client/Main/kite/kites/kloud.coffee
+++ client/Main/kite/kites/kloud.coffee
@@ -26,7 +26,7 @@
   # subsequent info requests while the first request is pending
   # will be queued up and resolved by the pending request

-  info: ({ machineId }) ->
+  info: ({ machineId, currentState }) ->
     if @needsRequest[machineId] in [undefined, yes]
       @needsRequest[machineId] = no
       @tell 'info', { machineId }
@@ -35,7 +35,9 @@
           @requestingInfo[machineId] = null
         .timeout ComputeController.timeout
         .catch (err) =>
-          @requestingInfo[machineId].forEach ({ reject }) -> reject err
+          warn "kloud.info failed, sending current state back:", { currentState, err }
+          @requestingInfo[machineId].forEach ({ resolve }) ->
+            resolve State: currentState
           @requestingInfo[machineId] = null
         .catch(require('kite').Error.codeIs "107", (err) => ) # SILENCE THIS ERROR!
         .finally => @needsRequest[machineId] = yes


payment: emit an event when workflow starts

--- client/Main/payment/workflow.coffee
+++ client/Main/payment/workflow.coffee
@@ -79,6 +79,8 @@
       when DOWNGRADE                then @startDowngradeFlow()
       when UPGRADE, INTERVAL_CHANGE then @startRegularFlow()

+    @emit 'WorkflowStarted'
+

   startRegularFlow: ->


fix spec

fix spec

--- spec/cursor-history-spec.coffee
+++ spec/cursor-history-spec.coffee
@@ -129,26 +129,30 @@
           expect(getEntries('last')).toBeEqualEntry point: [2, 5], URI: pathSample1

   describe "go/back history with next/prev commands", ->
-    isInitialState = ->
+    isInitialStateOfBeforeHistoryInitialized = ->
       expect(main.history).not.toBeTruthy()
       expect(editor.getCursorBufferPosition()).toEqual [0, 0]

+    isInitialStateOfAfterHistoryInitialized = ->
+      expect(getEntries()).toHaveLength(0)
+      expect(editor.getCursorBufferPosition()).toEqual [0, 0]
+
     beforeEach ->
-      isInitialState()
+      isInitialStateOfBeforeHistoryInitialized()

     describe "when history is empty", ->
       it "do nothing with next", ->
         dispatchCommand(editorElement, 'cursor-history:next')
-        isInitialState()
+        isInitialStateOfAfterHistoryInitialized()
       it "do nothing with prev", ->
         dispatchCommand(editorElement, 'cursor-history:prev')
-        isInitialState()
+        isInitialStateOfAfterHistoryInitialized()
       it "do nothing with next-within-editor", ->
         dispatchCommand(editorElement, 'cursor-history:next-within-editor')
-        isInitialState()
+        isInitialStateOfAfterHistoryInitialized()
       it "do nothing with prev-within-editor", ->
         dispatchCommand(editorElement, 'cursor-history:prev-within-editor')
-        isInitialState()
+        isInitialStateOfAfterHistoryInitialized()

     describe "when history is not empty", ->
       [e0, e1, e2, e3, editor2, editorElement2] = []


:lipstick:

:lipstick:

--- src/packages/markdown-preview/lib/markdown-preview.coffee
+++ src/packages/markdown-preview/lib/markdown-preview.coffee
@@ -1,10 +1,10 @@
 EditSession = require 'edit-session'
-MarkdownPreviewView = require 'markdown-preview/lib/markdown-preview-view'
+MarkdownPreviewView = require './markdown-preview-view'

 module.exports =
   activate: ->
     rootView.command 'markdown-preview:show', '.editor', => @show()
-    rootView.on 'core:save', ".pane", => @show() if @previewExists()
+    rootView.on 'core:save', '.pane', => @show() if @previewExists()

   show: ->
     activePane = rootView.getActivePane()


project: injecting terminal's filename into environment when starting up xterm

--- src/smc-project/console_server_child.coffee
+++ src/smc-project/console_server_child.coffee
@@ -36,10 +36,14 @@
         path     : undefined
         filename : undefined

+    fn = misc.path_split(opts.filename).tail
+    env = misc.merge({SMC_TERM_FILENAME: fn}, process.env)
+
     term_opts =
         name : 'xterm'
         rows : opts.rows
         cols : opts.cols
+        env  : env

     if opts.path?
         term_opts.cwd = opts.path


removed console.logs

removed console.logs

--- workers/social/lib/social/models/group/index.test.coffee
+++ workers/social/lib/social/models/group/index.test.coffee
@@ -163,7 +163,6 @@
           ->
             # making sure client is a member of the group before trying to kick
             group.searchMembers adminClient, userFormData.username, {}, (err, members) ->
-              console.log err
               expect(err).to.not.exist
               expect(members.length).to.be.equal 1
               queue.next()
@@ -189,7 +188,6 @@
           ->
             # admin client search for kicked member within group member
             group.searchMembers adminClient, userFormData.username, {}, (err, members) ->
-              console.log err
               expect(err).to.not.exist
               expect(members.length).to.be.equal 0
               queue.next()
@@ -554,7 +552,6 @@
           # expecting to return false when there are no allowedDomains
           JGroup.create client, groupData, account, (err, group) ->
             expect(err).to.not.exist
-            console.log userFormData.email
             expect(group.isInAllowedDomain userFormData.email).to.be.ok
             queue.next()


doc (Modules) Add the obvious next step to a missing module name argument error

--- src/steroids/module/commands/install.coffee
+++ src/steroids/module/commands/install.coffee
@@ -20,6 +20,8 @@
       Module name required.

       Please run again with the target module name as an argument.
+
+      Find installable Modules at: https://composer2.appgyver.com/web_components
     """

   moduleApi.repository


Remove comment

Remove comment

--- server/code/index.coffee
+++ server/code/index.coffee
@@ -368,7 +368,6 @@
       return resp.send err.statusCode, error: "Error creating box: #{err.body}"
     console.log "POST dataset boxName=#{box.name}"
     # Save dataset
-    # TODO: save box
     body = req.body
     dataset = new Dataset
       box: box.name


Initial complete renderer

Initial complete renderer

--- lib/BehaviorTree.coffee
+++ lib/BehaviorTree.coffee
@@ -300,7 +300,6 @@

   toGraph: () ->
     graphs = {}
-    subgraphs = {}
     unless graph
       graph = new graphlib.Graph
         compound: true
@@ -334,9 +333,8 @@
         style: 'dotted'
       if n.id is 'root'
         node.shape = 'Mdiamond'
-      if c
-        node.style = 'solid' if c.state in [2, 3]
-        node.color = 'red' if c.state is 3
+      node.style = 'solid' if c?.state in [2, 3]
+      node.color = 'red' if c?.state is 3
       node

     # Handler for registering node and edges based on a single behavior tree node
@@ -346,37 +344,30 @@

       for path, choice of node.choices
         if choice.subtrees?.length
-          subgraphs[nodeId] = true
           # This node has subtrees, handle accordingly
           graph.setNode "cluster_#{nodeId}", choiceToNode t, node, choice
           graph.setParent "cluster_#{nodeId}", parent if parent
           toGraph st, "cluster_#{nodeId}" for st in choice.subtrees
-          continue

         # Register node
         graph.setNode nodeId, choiceToNode t, node, choice
         # Register parent if in subgraph
         graph.setParent nodeId, parent if parent

-        ###
         if choice.parentSource
           fromId = "t#{choice.parentSource.treeId}_#{choice.parentSource.id}"
-          fromId = "t#{choice.parentSource.treeId}_#{choice.parentSource.source.id}" if choice.parentSource.source
-        ###
         if choice.source
           fromId = "t#{t.id}_#{choice.source.id}"
-          ###
           if choice.source.subLeaves and choice.source.subLeaves.length
             for l in choice.source.subLeaves
               graph.setEdge "t#{l.choice.treeId}_#{l.choice.id}", nodeId,
                 style: if l.accepted then 'solid' else 'dotted'
             continue
-          ###

         continue unless fromId
         graph.setEdge fromId, nodeId, choiceToEdge(t, node, choice), node.name or node.type

-      unless node.id is 'root' and choices
+      if node.id isnt 'root' and not choices
         # This node was never reached, mark with ignored sources
         graph.setNode nodeId, choiceToNode t, node, choice
         graph.setParent nodeId, parent if parent


Support deeper contest loops

Support deeper contest loops


Fix some issues with indentation in the line wrapper

--- lib/line_wrapper.coffee
+++ lib/line_wrapper.coffee
@@ -7,19 +7,21 @@
         @on 'firstLine', (options) =>
             indent = options.indent or 0
             @document.x += indent
-            options.lineWidth -= indent
+            @lineWidth -= indent

             @once 'line', =>
                 @document.x -= indent
-                options.lineWidth += indent
+                @lineWidth += indent

         @on 'lastLine', (options) =>
             align = options.align
             options.align = 'left' if align is 'justify'
+            @lastLine = true

             @once 'line', =>
                 @document.y += options.paragraphGap or 0
                 options.align = align
+                @lastLine = false

     wrap: (paragraphs, options) ->
         width = @document.widthOfString.bind(@document)