fixed display of guidance/params in suggestions

--- lib/provider.coffee
+++ lib/provider.coffee
@@ -71,7 +71,7 @@
         return suggestions

     values = Object.keys(thisLevel)
-    for value in values when validPrefix(prefix, value)
+    for value in values when isValidPrefixOrValue(prefix, value)
       if (equalStringStarts(prefix, value) or (prefix == ""))
         try
           desc = thisLevel[value]['guidance']
@@ -111,9 +111,9 @@
       return false
   return true

-validPrefix = (prefix, value) ->
-  isGuidance = (prefix == "guidance")
-  isParams = (prefix == "params")
+isValidPrefixOrValue = (prefix, value) ->
+  isGuidance = (prefix == "guidance") || (value == "guidance")
+  isParams = (prefix == "params") || (value == "params")
   isValue = (prefix == value)
   if isGuidance or isParams or isValue
     return false


Also remove diacritics from the key

Also remove diacritics from the key

--- lib/provider.coffee
+++ lib/provider.coffee
@@ -50,6 +50,7 @@
         new Promise (resolve) ->
           if prefix[0] == "@"
             p = prefix.normalize().replace(/^@/, '')
+            p = removeDiacritics(p)
             suggestions = []
             hits = fuzzaldrin.filter allwords, p, { key: 'author' }
             for h in hits


Add Way to Enumerate Qualities

Add Way to Enumerate Qualities

This is probably temporary.

What I wouldn't give for an ES6 generator.

--- src/coffee/quality.coffee
+++ src/coffee/quality.coffee
@@ -14,4 +14,6 @@
           q
         else
           library[q.toString()]
+      getAll: () ->
+        quality for quality of library
     return Object.freeze api


Added support for multiple asset paths

Added support for multiple asset paths

--- src/commands/watch.coffee
+++ src/commands/watch.coffee
@@ -86,12 +86,16 @@
   compile: =>
     paths = @config.paths
     fs_utils.write @fileList, @config, @plugins, (error, result) =>
-      fs_utils.copyIfExists paths.assets, paths.public, yes, (error) =>
-        logger.error "Asset compilation failed: #{error}" if error?
-        logger.info "compiled."
-        logger.debug "compilation time: #{Date.now() - @start}ms"
-        @watcher.close() unless @persistent
-        @onCompile null, result
+      assetError = null
+      assetPaths = if paths.assets instanceof Array then paths.assets else [paths.assets]
+      assetPaths.map (assetPath) ->
+        fs_utils.copyIfExists assetPath, paths.public, yes, (error) => assetError = error if assetError?
+
+      logger.error "Asset compilation failed: #{assetError}" if assetError?
+      logger.info "compiled."
+      logger.debug "compilation time: #{Date.now() - @start}ms"
+      @watcher.close() unless @persistent
+      @onCompile null, result

   watch: ->
     @initServer()


Fix cannot rename in atom 1.9.9

Fix cannot rename in atom 1.9.9

--- lib/refactor.coffee
+++ lib/refactor.coffee
@@ -56,7 +56,7 @@

   onRename: (e) =>
     isExecuted = false
-    for watcher in @watchers
+    for watcher in Array.from @watchers
       isExecuted or= watcher.rename()
     d 'rename', isExecuted
     return if isExecuted
@@ -64,7 +64,7 @@

   onDone: (e) =>
     isExecuted = false
-    for watcher in @watchers
+    for watcher in Array.from @watchers
       isExecuted or= watcher.done()
     return if isExecuted
     e.abortKeyBinding()


Delay mounting panel

Delay mounting panel

--- src/components/panel.cjsx
+++ src/components/panel.cjsx
@@ -9,10 +9,9 @@

   getInitialState: ->
     selectedIndex = @props.factory.getSelectedIndex()
-    {
-      selected: selectedIndex is @props.index
-      childComponent: null
-    }
+    state = selected: selectedIndex is @props.index
+    state.childComponent = <@props.handler /> if state.selected
+    state

   componentDidMount: ->
     @props.factory.addListener @_onFactoryUpdate
@@ -21,24 +20,18 @@
     @props.factory.removeListener @_onFactoryUpdate

   _onFactoryUpdate: (index) ->
-    selected = index is @props.index
-    @setState selected: selected
+    state = selected: index is @props.index
+    state.childComponent = <@props.handler /> if state.selected and !@state.childComponent?
+    @setState state

   _getStyle: ->
     display = "none"
     display = "block" if @state.selected
     {display: display}

-  _getChildComponent: ->
-    return @state.childComponent if @state.childComponent?
-    return null if !@state.selected
-    element = <@props.handler />
-    #@setState childComponent: element
-    element
-
   render: ->
     <div className={@props.className} style={@_getStyle()}>
-      {@_getChildComponent()}
+      {@state.childComponent}
     </div>

 module.exports = Panel


resurrection.activity: remove non-existing variable reference in error title

--- client/Social/Activity/views/inputwidget.coffee
+++ client/Social/Activity/views/inputwidget.coffee
@@ -107,7 +107,7 @@

       KD.showError err,
         AccessDenied :
-          title      : "You are not allowed to #{action} activities"
+          title      : "You are not allowed to post activities"
           content    : 'This activity will only be visible to you'
           duration   : 5000
         KodingError  : 'Something went wrong while creating activity'


activity: subscribe to message events when channel is ready

--- client/Social/Activity/views/messagepane.coffee
+++ client/Social/Activity/views/messagepane.coffee
@@ -19,14 +19,10 @@

   bindChannelEvents: ->

-    {channel} = @getData()
-    channel  ?= KD.singleton("socialapi").openedChannels[@getOption "name"]
+    KD.singletons.socialapi.onChannelReady @getOption('name'), (channel) =>

-    return  unless channel
-
-    channel.on "MessageAdded", @bound "addMessage"
-    channel.on "MessageRemoved", @bound "removeMessage"
-
+      channel.on "MessageAdded", @bound "addMessage"
+      channel.on "MessageRemoved", @bound "removeMessage"

   addMessage: (message) ->


activity: comment out procedure to add activity on input submit event

--- client/Social/Activity/views/messagepane.coffee
+++ client/Social/Activity/views/messagepane.coffee
@@ -86,4 +86,4 @@
     channel = @getData()

     @input = new ActivityInputWidget {channel}
-      .on 'Submit', @listController.bound 'addItem'
+      # .on 'Submit', @listController.bound 'addItem'


Account Settings: Credentials: Use double quote instead of thin

--- client/account/lib/accountcredentiallist.coffee
+++ client/account/lib/accountcredentiallist.coffee
@@ -88,7 +88,7 @@

     # Don't show the edit button for aws credentials in list. Gokmen'll on it.
     if provider is 'aws'
-      return showError 'This AWS credential can\'t be edited for now.'
+      return showError "This AWS credential can't be edited for now."

     credential.fetchData (err, data) ->
       return if showError err


added retarded existential chek

added retarded existential chek

--- client/landing/app/CommonViews/LinkViews.coffee
+++ client/landing/app/CommonViews/LinkViews.coffee
@@ -34,7 +34,7 @@
     bongo.cacheable origin.constructorName, origin.id, (err, origin)=>
       @setData origin
       @render()
-      @$().attr "href","/#!/#{origin.profile.nickname}"
+      @$().attr "href","/#!/#{origin.profile?.nickname}"
       # @$().twipsy title : "@#{origin.profile.nickname}", placement : "left"

   viewAppended:->


#1 fix undefined error

#1 fix undefined error

--- lib/selector.coffee
+++ lib/selector.coffee
@@ -48,8 +48,7 @@
       if end?
         return {start: start,end:end};
         # selection.setBufferRange([start, end])
-    else
-      return {start:null, end:null}
+    return {start:null, end:null}

   @select: (event, type, args...) ->
     editor = event.target.getModel?()


Started converting Sequence to a dual class/instance method scheme.

--- lib/sequence.coffee
+++ lib/sequence.coffee
@@ -1,3 +1,10 @@
+# --------------------------------------------------------------------
+# A sequence class vaguely inspired by Clojure's sequences.
+#
+# Copyright (c) 2010 Olaf Delgado-Friedrichs (odf@github.com)
+# --------------------------------------------------------------------
+
+
 if typeof(require) != 'undefined'
   require.paths.unshift __dirname
   { recur, resolve } = require('trampoline')
@@ -21,27 +28,70 @@
       @first = source.first
       @rest  = source.rest

-  take: (n) ->
-    if n > 0
-      Sequence.conj @first(), => @rest()?.take n-1
+  @conj: (first, rest, mode) ->
+    r = rest() if mode == 'forced'
+    new Sequence {
+      first: -> first
+      rest:
+        switch mode
+          when 'stored' then -> val = rest(); (@rest = -> val)()
+          when 'forced' then -> r
+          else               -> rest()
+    }
+
+  @from: (start) -> Sequence.conj start, => @from start+1
+
+  @range: (start, end) -> @take__ @from(start), end - start + 1
+
+  @::S = this
+
+  @memo: (name, f) ->
+    @[name]        = (seq) -> f.call(this, new Sequence(seq))
+    @["#{name}__"] = (seq) -> f.call(this, seq)
+    @::[name]      = -> x = f.call(@S, this); (@[name] = -> x)()
+
+  @method: (name, f) ->
+    @[name]        = (seq, args...) -> f.call(this, new Sequence(seq), args...)
+    @["#{name}__"] = (seq, args...) -> f.call(this, seq, args...)
+    @::[name]      = (args...) -> f.call(@S, this, args...)
+
+  @operator: (name, f) ->
+    @[name]        = (seq, other, args...) ->
+      f(new Sequence(seq), new Sequence(other), args...)
+    @["#{name}__"] = (seq, other, args...) ->
+      f.call(this, seq, other, args...)
+    @::[name]      = (other, args...) ->
+      f.call(@S, this, new Sequence(other), args...)
+
+  @memo 'size', (seq) ->
+    step = (s, n) -> if s then recur -> step s.rest(), n + 1 else n
+    resolve step seq, 0
+
+  @memo 'last', (seq) ->
+    step = (s) -> if s.rest() then recur -> step s.rest() else s.first()
+    resolve step seq
+
+  @method 'take', (seq, n) ->
+    if seq and n > 0
+      Sequence.conj seq.first(), => @take__ seq.rest(), n-1
     else
       null

-  takeWhile: (pred) ->
-    if pred(@first())
-      Sequence.conj @first(), => @rest()?.takeWhile pred
+  @method 'takeWhile', (seq, pred) ->
+    if seq and pred(seq.first())
+      Sequence.conj seq.first(), => @takeWhile__ seq.rest(), pred
     else
       null

-  drop: (n) ->
+  @method 'drop', (seq, n) ->
     step = (s, n) -> if s and n > 0 then recur -> step(s.rest(), n - 1) else s
-    resolve step this, n
+    resolve step seq, n

-  dropWhile: (pred) ->
+  @method 'dropWhile', (seq, pred) ->
     step = (s) -> if s and pred s.first() then recur -> step s.rest() else s
-    resolve step this
+    resolve step seq

-  get: (n) -> @drop(n)?.first() if n >= 0
+  @method 'get', (seq, n) => @drop__(seq, n)?.first() if n >= 0

   select: (pred) ->
     if pred @first()
@@ -122,31 +172,6 @@

   forced: -> Sequence.conj @first(), (=> @rest()?.forced()), 'forced'

-  @memo: (name, f) -> @::[name] = -> x = f.apply(this); (@[name] = -> x)()
-
-  @memo 'size', ->
-    step = (s, n) -> if s then recur -> step s.rest(), n + 1 else n
-    resolve step this, 0
-
-  @memo 'last', ->
-    step = (s) -> if s.rest() then recur -> step s.rest() else s.first()
-    resolve step this
-
-  @conj: (first, rest, mode) ->
-    r = rest() if mode == 'forced'
-    new Sequence {
-      first: -> first
-      rest:
-        switch mode
-          when 'stored' then -> val = rest(); (@rest = -> val)()
-          when 'forced' then -> r
-          else               -> rest()
-    }
-
-  @from: (start) -> Sequence.conj start, => @from start+1
-
-  @range: (start, end) -> @from(start).take end - start + 1
-

 # --------------------------------------------------------------------
 # Exporting.


improve description

improve description

--- lib/settings.coffee
+++ lib/settings.coffee
@@ -52,7 +52,7 @@
   wrapLeftRightMotion: false
   numberRegex:
     default: '-?[0-9]+'
-    description: 'Used to find number in ctrl-a/ctrl-x. To ignore "-"(minus) char in string like "identifier-1" use "(?:\\B-)?[0-9]+"'
+    description: 'Used to find number in ctrl-a/ctrl-x.
To ignore "-"(minus) char in string like "identifier-1" use "(?:\\B-)?[0-9]+"'
   clearHighlightSearchOnResetNormalMode:
     default: false
     description: 'Clear highlightSearch on `escape` in normal-mode'
@@ -62,7 +62,7 @@
   charactersToAddSpaceOnSurround:
     default: []
     items: type: 'string'
-    description: 'Comma separated list of character, which add additional space inside when surround.'
+    description: 'Comma separated list of character, which add space around surrounded text.
For vim-surround compatible behavior, set `(, {, [, <`.'
   showCursorInVisualMode: true
   ignoreCaseForSearch:
     default: false


Removed comments

Removed comments

--- client/test/lib/environments/paidaccount.coffee
+++ client/test/lib/environments/paidaccount.coffee
@@ -6,7 +6,7 @@

 module.exports =
-  ###
+
   seeUpgradeModalForNotPaidUser: (browser) ->

     helpers.beginTest(browser)
@@ -123,7 +123,7 @@
             browser
               .waitForElementVisible   '.AppModal-form.with-fields .alwayson .koding-on-off.on', 20000
               .end()
-        ###
+

   createSnapshotForNonPaidUser: (browser) ->


fix typo

fix typo

--- components/header/data.coffee
+++ components/header/data.coffee
@@ -9,6 +9,6 @@
       traditional: '我們視Artsy為資源，讓人能在此學習藝術，探索藝術，收藏藝術，取之不盡，用之不竭。'
     },
     navLinks: {
-      simplified: {'artworld': '艺术世界在线', 'collecting': '收藏在 Artsy', 'genome': '艺术基因鉴定', 'ios': 'Artsy的iOS', 'partnership': '加盟 Artsy'},
+      simplified: {'artworld': '艺术世界在线', 'collecting': '收藏在 Artsy', 'genome': '艺术基因鉴定', 'ios': 'Artsy的iOS', 'partnerships': '加盟 Artsy'},
       traditional: {'artworld': 'Artsy藝術世界', 'collecting': 'Artsy收藏藝術', 'genome': '藝術基因鑑定', 'ios': 'Artsy的iOS', 'partnerships': 'Artsy夥伴加盟'}
     }


Detect Python3 before Python2 on Windows OS

--- lib/wakatime.coffee
+++ lib/wakatime.coffee
@@ -286,11 +286,11 @@
         '/usr/bin/python3',
         '/usr/bin/python',
       ]
-      i = 26
-      while i < 50
+      i = 39
+      while i >= 26
         locations.push '\\python' + i + '\\pythonw'
         locations.push '\\Python' + i + '\\pythonw'
-        i++
+        i--
     args = ['--version']
     if locations.length is 0
       callback(null)


Add required activate method to avoid logging during specs

--- spec/fixtures/packages/package-with-config-defaults/index.coffee
+++ spec/fixtures/packages/package-with-config-defaults/index.coffee
@@ -1,3 +1,5 @@
 module.exports =
   configDefaults:
     numbers: { one: 1, two: 2 }
+
+  activate: -> # no-op


client: don't show disabled headers to members

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

--- client/app/lib/components/headermessage/container.coffee
+++ client/app/lib/components/headermessage/container.coffee
@@ -44,13 +44,6 @@
       expire. You have only #{pluralize 'day', daysLeft, yes} left. Have an
       admin enter a credit card to avoid any suspension.
     "
-
-  member[Status.PAST_DUE] =
-    type: 'danger'
-    title: 'We couldn’t charge your credit card.'
-    description: '
-      Have an admin to enter a credit card to avoid any suspension.
-    '

   return { admin, member }


Adds atom snippets

Adds atom snippets

--- .atom/snippets.cson
+++ .atom/snippets.cson
@@ -71,6 +71,32 @@

           export default $1;
         """
+    'Statefull React Component':
+        'prefix': 'streact'
+        'body': """
+          import React, { PropTypes, Component } from 'react';
+
+          class $1 extends Component {
+            constructor(props) {
+              super(props);
+            }
+            render() {
+              return (
+                <div>
+                </div>
+              )
+            }
+          };
+
+          $1.propTypes = {
+
+          };
+
+          export default $1;
+        """
     'React component tags':
         'prefix': 'rc'
         'body': '<$1 />'
+    'DOM element':
+        'prefix': 'dom'
+        'body': '<$1>$2</$1>'


Change shortcut to reload current window

--- .config/keymap.cson
+++ .config/keymap.cson
@@ -27,48 +27,51 @@

 # Display
 'body':
-	'cmd-alt-w':    'window:toggle-invisibles'
-	'shift-cmd-W':  'editor:toggle-soft-wrap'
-	'cmd-alt-0':    'window:reset-font-size'
-	'cmd-k up':     'unset!'
-	'cmd-k right':  'unset!'
-	'cmd-k left':   'unset!'
-	'cmd-k down':   'unset!'
+	'cmd-alt-w':      'window:toggle-invisibles'
+	'shift-cmd-W':    'editor:toggle-soft-wrap'
+	'cmd-alt-0':      'window:reset-font-size'
+	'cmd-k up':       'unset!'
+	'cmd-k right':    'unset!'
+	'cmd-k left':     'unset!'
+	'cmd-k down':     'unset!'

 # Navigation
 'atom-workspace atom-text-editor:not(.mini)':
-	'cmd-g':        'go-to-line:toggle'
-	'cmd-enter':    'minimap:toggle'
-	'cmd-|':        'bookmarks:toggle-bookmark'
-	'cmd-{':        'bookmarks:jump-to-previous-bookmark'
-	'cmd-}':        'bookmarks:jump-to-next-bookmark'
+	'cmd-g':          'go-to-line:toggle'
+	'cmd-enter':      'minimap:toggle'
+	'cmd-|':          'bookmarks:toggle-bookmark'
+	'cmd-{':          'bookmarks:jump-to-previous-bookmark'
+	'cmd-}':          'bookmarks:jump-to-next-bookmark'

 '.platform-darwin':
-	'cmd-0':        'tree-view:toggle'
-	'shift-cmd-O':  'application:open-dev'
+	'cmd-0':          'tree-view:toggle'
+	'shift-cmd-O':    'application:open-dev'
+	'shift-cmd-R':    'window:reload'
+	'ctrl-alt-cmd-l': 'unset!'
+

 'atom-text-editor':
-	'cmd-l':        'grammar-selector:show'
+	'cmd-l':          'grammar-selector:show'

 # Editing
 'atom-text-editor:not([mini])':
-	'cmd-d':        'editor:delete-line'
-	'alt-up':       'editor:move-line-up'
-	'alt-down':     'editor:move-line-down'
-	'cmd-alt-up':   'editor:duplicate-lines'
-	'ctrl-w':       'whitespace:convert-spaces-to-tabs'
-	'cmd-b':        'atom-beautify:beautify-editor'
+	'cmd-d':          'editor:delete-line'
+	'alt-up':         'editor:move-line-up'
+	'alt-down':       'editor:move-line-down'
+	'cmd-alt-up':     'editor:duplicate-lines'
+	'ctrl-w':         'whitespace:convert-spaces-to-tabs'
+	'cmd-b':          'atom-beautify:beautify-editor'

 # Character transformation
 '.platform-darwin atom-text-editor':
-	'cmd-shift-U': 'editor:upper-case'
-	'cmd-u':       'editor:lower-case'
-	'cmd-r':       'unset!'
+	'cmd-shift-U':    'editor:upper-case'
+	'cmd-u':          'editor:lower-case'
+	'cmd-r':          'unset!'

 # Find and replace
 '.platform-darwin .find-and-replace':
-	'cmd-r':       'find-and-replace:toggle-regex-option'
+	'cmd-r':          'find-and-replace:toggle-regex-option'


Use Vue level data attr not dom data attr in check

--- coffee/game.coffee
+++ coffee/game.coffee
@@ -426,24 +426,6 @@
 visibleWave = () ->
   waveID  = visibleWaveID()
   GLOBAL_WAVES[waveID]['wave']
-
-#
-# Begin gameover countdown timer
-#
-# When the user has lost all their lives beginCountdown(t) will populate the
-# `GAME OVER ... {1:d}` with an integer counting down from t. At the end of
-# the countdown the function will trigger the `submit` action if the button
-# has not already been pressed.
-#
-# @param  {int} t Starting countdown value
-# @return {undefined}
-#
-beginCountdown = (t) ->
-  $('.counter').text t
-  if t==0
-    GLOBAL_ACTIONS['submit']()
-  else
-    _.delay beginCountdown, 1000, t-1

 #
 # Carousel
@@ -644,7 +626,7 @@
       $('#wave-players').removeClass().addClass('dim-100-20')
       $('#check-preloader').removeClass().addClass('light-0-100')

-      _.delay( (waveID) ->
+      _.delay( (waveID) =>
         # Check if visible data contains the signal
         if $('#'+waveID).data('signal') is 1
           # Show continue
@@ -667,22 +649,20 @@

         # If not reduce the number of lives
         else
-          dialCol = $('#dial-col')
-          lives = dialCol.data('lives')

           # With one life lost of one life remaining remove abbility to check
-          if lives == 1
+          if @lives == 1
             $('#tick-noclick').hide()
             $('#lost').show()
-            beginCountdown(5)
+            @beginCountdown(5)
           else
             # Toggle tick
             $('#tick-noclick').hide()
             $('#tick').show()

           # Render new number of lives
-          dialCol.data('lives', (lives-1))
-          renderLives(lives-1)
+          @lives -= 1
+          renderLives(@lives)

           # Show incorrect preloader
           $('#check-preloader').removeClass().addClass('dim-100-0')
@@ -732,3 +712,21 @@
       # Form needs to be a part of document in to be able to submit.
       $(document.body).append form
       form.submit()
+
+    #
+    # Begin gameover countdown timer
+    #
+    # When the user has lost all their lives beginCountdown(t) will populate the
+    # `GAME OVER ... {1:d}` with an integer counting down from t. At the end of
+    # the countdown the function will trigger the `submit` action if the button
+    # has not already been pressed.
+    #
+    # @param  {int} t Starting countdown value
+    # @return {undefined}
+    #
+    beginCountdown: (t) ->
+      $('.counter').text t
+      if t==0
+        @submit()
+      else
+        _.delay @beginCountdown, 1000, t-1


Remove keyboard shortcuts

Remove keyboard shortcuts

--- coffee/main.coffee
+++ coffee/main.coffee
@@ -17,16 +17,6 @@
     return

   do bindEvents = ->
-    $(document).on 'keydown', (e) ->
-      NUM_1_KEY = 49
-      NUM_2_KEY = 50
-
-      if e.ctrlKey and (e.which is NUM_1_KEY)
-        $dicIframe.contentWindow.focus()
-
-      if e.ctrlKey and (e.which is NUM_2_KEY)
-        $imagesIframe.contentWindow.focus()
-
     $input.on 'keydown', (e) ->
       search() if e.which is 13


Removed extra blank line

Removed extra blank line

--- coffee/main.coffee
+++ coffee/main.coffee
@@ -443,7 +443,6 @@
               return false if field.isValid && !field.isValid()
             return true

-
       doAjaxSave: (payload) ->
         $.ajax
           url: Formbuilder.options.HTTP_ENDPOINT


deployment: enable ProxyProtocol for production

--- deployment/nginx.coffee
+++ deployment/nginx.coffee
@@ -407,14 +407,10 @@
 createListenDirective = (KONFIG, env) ->
   return "listen #{KONFIG.publicPort};" if not isProxy KONFIG.ebEnvName

-  # TODO(rjeczalik): remove after ProxyProtocol policy is enabled for prodproxy
-  return "listen 79;" if not env is 'sandbox'
-
   return """
     listen 79 proxy_protocol;
     real_ip_header proxy_protocol;
-    set_real_ip_from 10.0.0.0/24;
-    set_real_ip_from 10.0.1.0/24;
+    set_real_ip_from 10.0.0.0/16;
   """

 createHttpsRedirector = (KONFIG) ->


Revert "rollbar log first & last entry in feed to debug duplicate entires in
feed"; Cherrypicked this commit into a new branch to be deployed.

This reverts commit 121caa2c89dc37733b5f90ee5da701f9bfc17225.

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

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

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

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

   teasersLoaded:->


Fixed bug when no people data returned for getMembersFromDropbox

--- routes/api.coffee
+++ routes/api.coffee
@@ -175,16 +175,19 @@
         API.dropbox.get req.params.folder, "#{req.params.user}.json", (data) ->
           try
             JSONdata = JSON.parse data
+            res.json data: JSONdata
           catch error
             next error
-          res.json data: JSONdata
       else
         API.dropbox.get req.params.folder, 'list.json', (data) ->
           try
             JSONdata = JSON.parse data
+            if JSONdata.people?
+              res.json data: JSONdata.people.values
+            else
+              res.json error: "No people: #{JSONdata}"
           catch error
             next error
-          res.json data: JSONdata.people.values
     members: (req, res, next) ->
       if req.params.user?
         authUser = req.params.user


Update find-view specs

Update find-view specs

--- spec/find-view-spec.coffee
+++ spec/find-view-spec.coffee
@@ -280,18 +280,49 @@

       expect(findView.descriptionLabel.text()).toEqual "No results found for 'notinthefilebro'"

-    it "properly handles the info message when there are no results", ->
-      findView.findEditor.setText 'item'
-      findView.findEditor.trigger 'core:confirm'
-      expect(findView.descriptionLabel.text()).toEqual "6 results found for 'item'"
-
-      findView.findEditor.setText 'notinthefilenope'
-      findView.findEditor.trigger 'core:confirm'
-      expect(findView.descriptionLabel.text()).toEqual "No results found for 'notinthefilenope'"
-
-      findView.findEditor.setText 'item'
-      findView.findEditor.trigger 'core:confirm'
-      expect(findView.descriptionLabel.text()).toEqual "6 results found for 'item'"
+    describe "updating the descriptionLabel", ->
+      it "properly updates the info message", ->
+        findView.findEditor.setText 'item'
+        findView.findEditor.trigger 'core:confirm'
+        expect(findView.descriptionLabel.text()).toEqual "6 results found for 'item'"
+
+        findView.findEditor.setText 'notinthefilenope'
+        findView.findEditor.trigger 'core:confirm'
+        expect(findView.descriptionLabel.text()).toEqual "No results found for 'notinthefilenope'"
+
+        findView.findEditor.setText 'item'
+        findView.findEditor.trigger 'core:confirm'
+        expect(findView.descriptionLabel.text()).toEqual "6 results found for 'item'"
+
+        findView.findEditor.setText ''
+        findView.findEditor.trigger 'core:confirm'
+        expect(findView.descriptionLabel.text()).toEqual "Find in Current Buffer"
+
+      describe "when there is a find-error", ->
+        beforeEach ->
+          editor.setCursorBufferPosition([2,0])
+          findView.findEditor.trigger 'find-and-replace:toggle-regex-option'
+
+        it "displays the error", ->
+          findView.findEditor.setText 'i[t'
+          findView.findEditor.trigger 'core:confirm'
+          expect(findView.descriptionLabel).toHaveClass 'text-error'
+          expect(findView.descriptionLabel.text()).toContain 'Invalid regular expression'
+
+        it "will be reset when there is no longer an error", ->
+          findView.findEditor.setText 'i[t'
+          findView.findEditor.trigger 'core:confirm'
+          expect(findView.descriptionLabel).toHaveClass 'text-error'
+
+          findView.findEditor.setText ''
+          findView.findEditor.trigger 'core:confirm'
+          expect(findView.descriptionLabel).not.toHaveClass 'text-error'
+          expect(findView.descriptionLabel.text()).toEqual "Find in Current Buffer"
+
+          findView.findEditor.setText 'item'
+          findView.findEditor.trigger 'core:confirm'
+          expect(findView.descriptionLabel).not.toHaveClass 'text-error'
+          expect(findView.descriptionLabel.text()).toContain "6 results"

     it "selects the first match following the cursor", ->
       expect(findView.resultCounter.text()).toEqual('2 of 6')
@@ -512,7 +543,7 @@
           findView.findEditor.trigger 'find-and-replace:toggle-regex-option'
           findView.findEditor.setText 'i[t'
           findView.findEditor.trigger 'core:confirm'
-          expect(findView.errorMessages.children()).toHaveLength 1
+          expect(findView.descriptionLabel).toHaveClass 'text-error'

     describe "when case sensitivity is toggled", ->
       beforeEach ->
@@ -566,7 +597,7 @@

         findView.findEditor.setText ''
         advance()
-        expect(findView.descriptionLabel.text()).toContain "No results"
+        expect(findView.descriptionLabel.text()).toContain "Find in Current Buffer"
         expect(findView).toHaveFocus()

         findView.findEditor.setText 'sort'
@@ -815,7 +846,10 @@
         it "does not add live searches to the history", ->
           expect(findView.descriptionLabel.text()).toContain "1 result"

-          findView.findEditor.setText 'FIXME: necessary first search for some reason'
+          # I really do not understand why these are necessary...
+          findView.findEditor.setText 'FIXME: necessary first search for some reason??'
+          advance()
+          findView.findEditor.setText 'FIXME: necessary second search for some reason??'
           advance()

           findView.findEditor.setText 'nope'


Add failing tests for awaiting (#642)

Add failing tests for awaiting (#642)

--- spec/specs/awaiting.coffee
+++ spec/specs/awaiting.coffee
@@ -1,9 +1,10 @@
+# build-dependencies: flatmap
 describe "EventStream.awaiting(other)", ->
   describe "indicates whether s1 has produced output after s2 (or only the former has output so far)", ->
     expectPropertyEvents(
       -> series(2, [1, 1]).awaiting(series(3, [2]))
       [false, true, false, true], semiunstable)
-  describe "supports Properties", ->
+  describe "supports awaiting Properties", ->
     expectPropertyEvents(
       -> series(2, [1, 1]).awaiting(series(3, [2]).toProperty())
       [false, true, false, true], semiunstable)
@@ -22,8 +23,25 @@
     expect(Bacon.never().awaiting(Bacon.never()).toString()).to.equal("Bacon.never().awaiting(Bacon.never())")

 describe "Property.awaiting(other)", ->
+  describe "works for awaiting self", ->
+    expectPropertyEvents(
+      ->
+        p = Bacon.constant(1)
+        p.awaiting(p)
+      [false], unstable)
   describe "indicates whether p1 has produced output after p2 (or only the former has output so far)", ->
     expectPropertyEvents(
       -> series(2, [1, 1]).toProperty().awaiting(series(3, [2]))
       [false, true, false, true], semiunstable)
-
+  describe "works for awaiting self.map", ->
+    expectPropertyEvents(
+      ->
+        p = Bacon.constant(1)
+        p.awaiting(p.map())
+      [false], unstable)
+  describe "works for awaiting self.flatMap", ->
+    expectPropertyEvents(
+      ->
+        p = Bacon.constant(1)
+        p.awaiting(p.flatMap(->))
+      [false], unstable)


Reorganize and combine autoReveal option tests

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -711,6 +711,21 @@
         runs ->
           dirView = root1.find('.directory:contains(dir1)')
           expect(dirView).toHaveClass 'selected'
+
+      describe "when the tree-view.autoReveal config setting is true", ->
+        beforeEach ->
+          atom.config.set "tree-view.autoReveal", true
+
+        it "selects the active item's entry in the tree view, expanding parent directories if needed", ->
+          waitsForPromise ->
+            atom.workspace.open(path.join('dir1', 'sub-dir1', 'sub-file1'))
+
+          runs ->
+            dirView = root1.find('.directory:contains(dir1)')
+            fileView = root1.find('.file:contains(sub-file1)')
+            expect(dirView).not.toHaveClass 'selected'
+            expect(fileView).toHaveClass 'selected'
+            expect(treeView.find('.selected').length).toBe 1

     describe "when the item has no path", ->
       it "deselects the previously selected entry", ->
@@ -2711,33 +2726,6 @@
         element.innerText

       expect(gammaEntries).toEqual(["delta.txt", "epsilon.txt", "theta"])
-
-  describe "the autoReveal config option", ->
-    beforeEach ->
-      atom.config.set "tree-view.autoReveal", true
-
-    describe "when the item has a path", ->
-      it "selects the entry with that path in the tree view if it is visible", ->
-        waitsForFileToOpen ->
-          sampleJs.click()
-
-        waitsForPromise ->
-          atom.workspace.open(atom.project.getDirectories()[0].resolve('tree-view.txt'))
-
-        runs ->
-          expect(sampleTxt).toHaveClass 'selected'
-          expect(treeView.find('.selected').length).toBe 1
-
-      it "selects the entry with that path in the tree view if it is not visible", ->
-        waitsForPromise ->
-          atom.workspace.open(path.join('dir1', 'sub-dir1', 'sub-file1'))
-
-        runs ->
-          dirView = root1.find('.directory:contains(dir1)')
-          fileView = root1.find('.file:contains(sub-file1)')
-          expect(dirView).not.toHaveClass 'selected'
-          expect(fileView).toHaveClass 'selected'
-          expect(treeView.find('.selected').length).toBe 1

   describe "showSelectedEntryInFileManager()", ->
     beforeEach ->


Use new temp folder on each spec run

Use new temp folder on each spec run

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -980,13 +980,8 @@
     temporaryFilePath = null

     beforeEach ->
+      atom.project.setPath(fs.absolute(temp.mkdirSync('tree-view')))
       temporaryFilePath = path.join(atom.project.getPath(), 'temporary')
-      if fs.existsSync(temporaryFilePath)
-        fs.removeSync(temporaryFilePath)
-        waits(20)
-
-    afterEach ->
-      fs.removeSync(temporaryFilePath)

     describe "when a file is added or removed in an expanded directory", ->
       it "updates the directory view to display the directory's new contents", ->


add test for <escape> from reversed visual-mode

--- spec/vim-state-spec.coffee
+++ spec/vim-state-spec.coffee
@@ -230,6 +230,16 @@
       expect(editor.getCursorBufferPositions()).toEqual [[0, 4]]
       expect(editorElement.classList.contains('command-mode')).toBe(true)
       expect(editorElement.classList.contains('visual-mode')).toBe(false)
+
+    it "puts the editor into command mode when <escape> is pressed on selection is reversed", ->
+      expect(editor.getSelectedText()).toBe("t")
+      keydown("h")
+      keydown("h")
+      expect(editor.getSelectedText()).toBe("e t")
+      expect(editor.getLastSelection().isReversed()).toBe(true)
+      keydown('escape')
+      expect(editorElement.classList.contains('command-mode')).toBe(true)
+      expect(editor.getCursorBufferPositions()).toEqual [[0, 2]]

     describe "motions", ->
       it "transforms the selection", ->


Coerced equality check on data-show

Coerced equality check on data-show

--- src/cuffs/bindings.coffee
+++ src/cuffs/bindings.coffee
@@ -23,16 +23,21 @@
                     return false
                 if attrValue == "true"
                     return true
+
                 return attrValue

             if notEqual.test @attr
                 [@attrName, @attrValue] = @attr.split("!=")
                 @test = (value)=>
-                    return booleanWrapper(@attrValue) != value
+                    # We don't want strict comparison - coercion is
+                    # fine - hence the backticks.
+                    return `booleanWrapper(this.attrValue) != value`
             else if equal.test @attr
                 [@attrName, @attrValue] = @attr.split("==")
                 @test = (value)=>
-                    return booleanWrapper(@attrValue) == value
+                    # We don't want strict comparison - coercion is
+                    # fine - hence the backticks.
+                    return `booleanWrapper(this.attrValue) == value`
             else
                 @attrName = @attr
                 @test = (value)->


:lipstick:

:lipstick:

--- src/directory-view.coffee
+++ src/directory-view.coffee
@@ -23,17 +23,15 @@
     @disclosureArrow.on 'click', => @toggleExpansion()

     repo = @project.repo
+    iconClass = 'directory-icon'
     if repo?
       path = @directory.getPath()
-      @directoryName.addClass('ignored') if parent and repo.isPathIgnored(path)
-      if not parent and path is repo.getWorkingDirectory()
-        @directoryName.addClass('repository-icon')
-      else if repo.isSubmodule(path)
-        @directoryName.addClass('submodule-icon')
+      if parent
+        @directoryName.addClass('ignored') if repo.isPathIgnored(path)
+        iconClass = 'submodule-icon' if repo.isSubmodule(path)
       else
-        @directoryName.addClass('directory-icon')
-    else
-      @directoryName.addClass('directory-icon')
+        iconClass = 'repository-icon' if path is repo.getWorkingDirectory()
+    @directoryName.addClass(iconClass)

   getPath: ->
     @directory.path


Pass invisibles configuration into DisplayLayer

--- src/display-buffer.coffee
+++ src/display-buffer.coffee
@@ -51,7 +51,7 @@
       @grammarRegistry, @packageManager, @assert
     })
     @buffer = @tokenizedBuffer.buffer
-    @displayLayer = @buffer.addDisplayLayer({tabLength: @getTabLength()})
+    @displayLayer = @buffer.addDisplayLayer({tabLength: @getTabLength(), invisibles: @config.get('editor.invisibles')})
     @displayLayer.setTextDecorationLayer(@tokenizedBuffer)
     @charWidthsByScope = {}
     @defaultMarkerLayer = @displayLayer.addMarkerLayer()


Splice in new DisplayBuffer lines in chunks to avoid stack overflows

--- src/display-buffer.coffee
+++ src/display-buffer.coffee
@@ -1120,7 +1120,7 @@
     {screenLines, regions} = @buildScreenLines(startBufferRow, endBufferRow + bufferDelta)
     screenDelta = screenLines.length - (endScreenRow - startScreenRow)

-    @screenLines[startScreenRow...endScreenRow] = screenLines
+    _.spliceWithArray(@screenLines, startScreenRow, endScreenRow - startScreenRow, screenLines, 10000)
     @rowMap.spliceRegions(startBufferRow, endBufferRow - startBufferRow, regions)
     @findMaxLineLength(startScreenRow, endScreenRow, screenLines, screenDelta)


fix for passport auth

fix for passport auth

--- salvus/hub.coffee
+++ salvus/hub.coffee
@@ -380,7 +380,7 @@
         opts.last_name = "User"

     if opts.emails?
-        opts.emails = (x.toLowerCase() for x in opts.emails)
+        opts.emails = (x.toLowerCase() for x in opts.emails when (x? and x.toLowerCase?))

     opts.id = "#{opts.id}"  # convert to string (id is often a number)


public raw -- snapshot of work in progress

--- salvus/hub.coffee
+++ salvus/hub.coffee
@@ -449,7 +449,7 @@
                 f = () ->
                     delete _remember_me_cache[key]
                 if has_access
-                    setTimeout(f, 1000*60*6)    # access lasts 6 minutes (i.e., if you revoke privs to a user they could still hit the port for 5 minutes)
+                    setTimeout(f, 1000*60*6)    # access lasts 6 minutes (i.e., if you revoke privs to a user they could still hit the port for this long)
                 else
                     setTimeout(f, 1000*60*2)    # not having access lasts 2 minute
                 opts.cb(err, has_access)
@@ -579,13 +579,19 @@
         #buffer = httpProxy.buffer(req)  # see http://stackoverflow.com/questions/11672294/invoking-an-asynchronous-method-inside-a-middleware-in-node-http-proxy

         dbg = (m) -> winston.debug("http_proxy_server(#{req_url}): #{m}")
+        dbg()

         cookies = new Cookies(req, res)
         remember_me = cookies.get(program.base_url + 'remember_me')

         if not remember_me?
-            res.writeHead(500, {'Content-Type':'text/html'})
-            res.end("Please login to https://cloud.sagemath.com with cookies enabled, then refresh this page.")
+
+            # before giving an error, check on possibility that file is public
+            public_raw req_url, res, (err, is_public) ->
+                if not is_public
+                    res.writeHead(500, {'Content-Type':'text/html'})
+                    res.end("Please login to https://cloud.sagemath.com with cookies enabled, then refresh this page.")
+
             return

         target remember_me, req_url, (err, location) ->
@@ -641,6 +647,34 @@
                     dbg("websocket upgrade -- using cache")
                 proxy.ws(req, socket, head)

+    public_raw = (req_url, res, cb) ->
+        # Determine if the requested path is public (and not too big).
+        # If so, send content to the client and cb(undefined, true)
+        # If not, cb(undefined, false)
+        # req_url = /9627b34f-fefd-44d3-88ba-5b1fc1affef1/raw/a.html
+        v = req_url.split('/')
+        if v[2] != 'raw'
+            cb(undefined, false)
+            return
+        project_id = v[1]
+        if not misc.is_valid_uuid_string(project_id)
+            cb(undefined, false)
+            return
+        path = v.slice(3).join('/')
+        winston.debug("public_raw: project_id=#{project_id}, path=#{path}")
+        project = bup_server.get_project(project_id)
+        project.read_file
+            path    : path
+            maxsize : 10000000
+            cb      : (err, data) ->
+                winston.debug("read_file; err='#{err}', data='#{data}'")
+                if err
+                    cb(err)
+                else
+                    res.writeHead(200, "text/html")
+                    res.write(data)
+                    res.end()
+                    cb(undefined, true)

 #############################################################
 # Client = a client that is connected via a persistent connection to the hub


hub -- add public_access property to project info

--- salvus/hub.coffee
+++ salvus/hub.coffee
@@ -1527,6 +1527,7 @@
                     if info.hide_from_accounts?
                         info.hidden = @account_id in info.hide_from_accounts
                         delete info.hide_from_accounts
+                    info.public_access = false
                     return info

                 project.get_info (err, info) =>
@@ -2214,7 +2215,7 @@
                     if err
                         @error_to_client(id:mesg.id, error:"no project with id #{mesg.project_id} available")
                     else
-                        info.read_only = true
+                        info.public_access = true
                         @push_to_client(message.public_project_info(id:mesg.id, info:info))

     mesg_public_get_directory_listing: (mesg) =>


refactor AutoCompleteManager

refactor AutoCompleteManager

--- services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee
@@ -44,13 +44,11 @@

 			GraphicsCompleter =
 				getCompletions: (editor, session, pos, prefix, callback) ->
-					context = Helpers.getContext(editor, pos)
-					{commandFragment, closingBrace} = context
+					{commandFragment, closingBrace} = Helpers.getContext(editor, pos)
 					if commandFragment
 						match = commandFragment.match(/^~?\\(includegraphics(?:\[.*])?){([^}]*, *)?(\w*)/)
 						if match
-							commandName = match[1]
-							currentArg = match[3]
+							[commandName, _, currentArg] = match
 							graphicsPaths = Preamble.getGraphicsPaths()
 							result = []
 							for graphic in Graphics.getGraphicsFiles()
@@ -70,13 +68,11 @@
 			metadataManager = @metadataManager
 			FilesCompleter =
 				getCompletions: (editor, session, pos, prefix, callback) =>
-					context = Helpers.getContext(editor, pos)
-					{commandFragment, closingBrace} = context
+					{commandFragment, closingBrace} = Helpers.getContext(editor, pos)
 					if commandFragment
 						match = commandFragment.match(/^\\(input|include){(\w*)/)
 						if match
-							commandName = match[1]
-							currentArg = match[2]
+							[commandName, currentArg] = match
 							result = []
 							for file in Files.getTeXFiles()
 								if file.id != @$scope.docId
@@ -91,13 +87,11 @@

 			LabelsCompleter =
 				getCompletions: (editor, session, pos, prefix, callback) ->
-					context = Helpers.getContext(editor, pos)
-					{commandFragment, closingBrace} = context
+					{commandFragment, closingBrace} = Helpers.getContext(editor, pos)
 					if commandFragment
 						refMatch = commandFragment.match(/^~?\\([a-z]*ref){([^}]*, *)?(\w*)/)
 						if refMatch
-							commandName = refMatch[1]
-							currentArg = refMatch[2]
+							[commandName, currentArg] = refMatch
 							result = []
 							if commandName != 'ref' # ref is in top 100 commands
 								result.push {
@@ -118,17 +112,14 @@
 			references = @$scope.$root._references
 			ReferencesCompleter =
 				getCompletions: (editor, session, pos, prefix, callback) ->
-					context = Helpers.getContext(editor, pos)
-					{commandFragment, closingBrace} = context
+					{commandFragment, closingBrace} = Helpers.getContext(editor, pos)
 					if commandFragment
 						citeMatch = commandFragment.match(
 							/^~?\\([a-z]*cite[a-z]*(?:\[.*])?){([^}]*, *)?(\w*)/
 						)
 						if citeMatch
-							commandName = citeMatch[1]
-							previousArgs = citeMatch[2]
-							currentArg = citeMatch[3]
-							if previousArgs == undefined
+							[commandName, previousArgs, currentArg] = citeMatch
+							if !previousArgs?
 								previousArgs = ""
 							previousArgsCaption = if previousArgs.length > 8 then "…," else previousArgs
 							result = []
@@ -140,7 +131,7 @@
 							}
 							if references.keys and references.keys.length > 0
 								references.keys.forEach (key) ->
-									if !(key in [null, undefined])
+									if key?
 										result.push({
 											caption: "\\#{commandName}{#{previousArgsCaption}#{key}#{closingBrace}"
 											value: "\\#{commandName}{#{previousArgs}#{key}#{closingBrace}"
@@ -170,8 +161,7 @@
 		onChange: (change) ->
 			cursorPosition = @editor.getCursorPosition()
 			end = change.end
-			context = Helpers.getContext(@editor, end)
-			{lineUpToCursor, commandFragment} = context
+			{lineUpToCursor, commandFragment} = Helpers.getContext(@editor, end)
 			if lineUpToCursor.match(/.*%.*/)
 				return
 			lastCharIsBackslash = lineUpToCursor.slice(-1) == "\\"
@@ -194,7 +184,7 @@
 					, 0
 			if (
 				change.action == "insert" and
-				change.lines[0] in ["\\begin{}", "\\ref{}", "\\usepackage{}", "\\cite{}"]
+				change.lines[0].match(/\\(\w+){}/)?[1].match(/(begin|[a-z]*ref|usepackage|[a-z]*cite[a-z]*)/)
 			)
 				setTimeout () =>
 					@editor.execCommand("startAutocomplete")


Fixed sorting

Fixed sorting

--- src/emoticon-stats.coffee
+++ src/emoticon-stats.coffee
@@ -52,7 +52,7 @@
         for emoticon, count of @cache.emoticonCounts
             tops.push(emoticon: emoticon, count: count)

-        return tops.sort((a,b) -> b.score - a.score).slice(0, n)
+        return tops.sort((a,b) -> b.count - a.count).slice(0, n)

     bottom: (n) ->
         all = @top(@cache.emoticonCounts.length)


geda: fix arc drawing (#83)

geda: fix arc drawing (#83)

--- src/geda-generator.coffee
+++ src/geda-generator.coffee
@@ -102,7 +102,7 @@
           when 'circle'
             fs.writeSync fd, "V #{shape.x} #{shape.y} #{shape.radius} 3 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n"
           when 'arc'
-            fs.writeSync fd, "A #{shape.x} #{shape.y} #{shape.radius} #{shape.start} #{shape.end-shape.start} 3 0 0 0 -1 -1\n"
+            fs.writeSync fd, "A #{shape.x} #{shape.y} #{shape.radius} #{Math.round(shape.start)} #{Math.round(shape.end-shape.start)} 3 0 0 0 -1 -1\n"
           when 'pin'
             x1 = shape.x
             y1 = shape.y


Conditionally include deprecations in GitRepository

--- src/git-repository.coffee
+++ src/git-repository.coffee
@@ -5,7 +5,7 @@
 {Emitter, Disposable, CompositeDisposable} = require 'event-kit'
 fs = require 'fs-plus'
 GitUtils = require 'git-utils'
-{deprecate} = require 'grim'
+{includeDeprecations, deprecate} = require 'grim'

 Task = require './task'

@@ -155,16 +155,6 @@
   onDidChangeStatuses: (callback) ->
     @emitter.on 'did-change-statuses', callback

-  on: (eventName) ->
-    switch eventName
-      when 'status-changed'
-        deprecate 'Use GitRepository::onDidChangeStatus instead'
-      when 'statuses-changed'
-        deprecate 'Use GitRepository::onDidChangeStatuses instead'
-      else
-        deprecate 'GitRepository::on is deprecated. Use event subscription methods instead.'
-    EmitterMixin::on.apply(this, arguments)
-
   ###
   Section: Repository Details
   ###
@@ -252,9 +242,6 @@
   # * `path` (optional) {String} path in the repository to get this information
   #   for, only needed if the repository has submodules.
   getOriginURL: (path) -> @getConfigValue('remote.origin.url', path)
-  getOriginUrl: (path) ->
-    deprecate 'Use ::getOriginURL instead.'
-    @getOriginURL(path)

   # Public: Returns the upstream branch for the current HEAD, or null if there
   # is no upstream branch for the current HEAD.
@@ -492,3 +479,18 @@
       unless statusesUnchanged
         @emit 'statuses-changed'
         @emitter.emit 'did-change-statuses'
+
+if includeDeprecations
+  GitRepository::on = (eventName) ->
+    switch eventName
+      when 'status-changed'
+        deprecate 'Use GitRepository::onDidChangeStatus instead'
+      when 'statuses-changed'
+        deprecate 'Use GitRepository::onDidChangeStatuses instead'
+      else
+        deprecate 'GitRepository::on is deprecated. Use event subscription methods instead.'
+    EmitterMixin::on.apply(this, arguments)
+
+  GitRepository::getOriginUrl = (path) ->
+    deprecate 'Use ::getOriginURL instead.'
+    @getOriginURL(path)


paramsView renders params list

paramsView renders params list

--- jsapp/xlform/src/view.params.coffee
+++ jsapp/xlform/src/view.params.coffee
@@ -7,31 +7,30 @@
 module.exports = do ->
   class ParamsView extends $baseView
     initialize: ({@rowView, @parameters})->
-      console.log('paramsView init', @rowView, @parameters, $configs.paramTypes)
-      @row = @rowView.model
+      @questionType = @rowView.model.attributes.type.attributes.typeId
       $($.parseHTML $viewTemplates.row.paramsQuestionContent()).insertAfter @rowView.$('.card__header')
       @$el = @rowView.$('.params-view')
+      console.log('paramsView init', @questionType, @parameters)

     render: ->
-      for param, value in @parameters
-        new ParamOption(@attributes.type.attributes.typeId, param, value).render().$el.appendTo(@$el)
+      for param, value of @parameters
+        console.log(new ParamOption(@questionType, param, value).render())
+        new ParamOption(@questionType, param, value).render().$el.appendTo(@$el)
       return @

   class ParamOption extends $baseView
-    className: "param-option"
-    events:
+    className: "param-option js-cancel-select-row"
+    events: {
       "input": "oninput"
       "click .js-clear-option": "onclear"
+    }

-    initialize: (@rowType, @paramName, @paramValue) ->
+    initialize: (@questionType, @paramName, @paramValue) ->

     render: ->
-      @inputEl = $('<input>')
-      @container = $('<div>')
-
-      @container.append(@inputEl)
-
-      @$el.html(@d)
+      templateName = $configs.paramTypes[@questionType][@paramName] + 'Param'
+      template = $($viewTemplates.$$render("ParamsView.#{templateName}", @paramName, @paramValue))
+      @$el.html(template)
       return @

     oninput: (evt) ->


Limit the size of the HTML wrapper to the intrinsic size

Fixes motif/company#5000.

--- framer/Layer.coffee
+++ framer/Layer.coffee
@@ -817,6 +817,8 @@
 		else
 			@_elementHTML.style.transformOrigin = "0 0"
 			@_elementHTML.style.transform = "scale(#{@context.scale * @width / @htmlIntrinsicSize.width}, #{@context.scale * @height / @htmlIntrinsicSize.height})"
+			@_elementHTML.style.width = "#{@htmlIntrinsicSize.width}px"
+			@_elementHTML.style.height = "#{@htmlIntrinsicSize.height}px"

 	querySelector: (query) -> @_element.querySelector(query)
 	querySelectorAll: (query) -> @_element.querySelectorAll(query)


Add call after count utility

Add call after count utility

--- framer/Utils.coffee
+++ framer/Utils.coffee
@@ -292,6 +292,12 @@
 # Backwards compatibility
 Utils.toggle = Utils.cycle

+Utils.callAfterCount = (total, callback) ->
+	# This calls a function after this method is called total times
+	count = 0
+	return callAfterCount = ->
+		count += 1
+		callback?() if count is total

 ######################################################
 # ENVIROMENT FUNCTIONS


Use Animator in Utils

Use Animator in Utils


update unit test to startup http on random unused port (port 0 yay)

--- src/test/hubot-jenkins-notifier_test.coffee
+++ src/test/hubot-jenkins-notifier_test.coffee
@@ -1,12 +1,12 @@
 'use strict'
-
+process.env.PORT = 0 # pick a random port for this test
 Hubot = require('hubot')
 Path = require('path')
 request = require('supertest')
 sinon = require('sinon')

 adapterPath = Path.join Path.dirname(require.resolve 'hubot'), "src", "adapters"
-robot = Hubot.loadBot adapterPath, "shell", "true", "MochaHubot"
+robot = Hubot.loadBot adapterPath, "shell", true, "MochaHubot"

 hubot_jenkins_notifier = require('../scripts/hubot-jenkins-notifier')(robot)


Remove unnecessary fat arrows

Remove unnecessary fat arrows

--- src/jquery.payment.coffee
+++ src/jquery.payment.coffee
@@ -124,7 +124,7 @@
 # Format Card Number

 reFormatCardNumber = (e) ->
-  setTimeout =>
+  setTimeout ->
     $target = $(e.currentTarget)
     value   = $target.val()
     value   = $.payment.formatCardNumber(value)
@@ -376,7 +376,7 @@
   num.length in card.length and
     (card.luhn is false or luhnCheck(num))

-$.payment.validateCardExpiry = (month, year) =>
+$.payment.validateCardExpiry = (month, year) ->
   # Allow passing an object
   if typeof month is 'object' and 'month' of month
     {month, year} = month


Update atom configuration

Update atom configuration

--- atom/atom/config.cson
+++ atom/atom/config.cson
@@ -7,6 +7,8 @@
   "atom-ctags":
     autoBuildTagsWhenActive: true
     cmdArgs: "--exclude=node_modules --exclude=bower_components"
+  "atom-jest-snippets":
+    toggleSemicolons: false
   "atom-material-ui":
     compactTreeView: true
     depth: true
@@ -58,7 +60,6 @@
       "save-session"
       "file-type-icons"
       "autocomplete-bibtex"
-      "wrap-guide"
       "tabs"
     ]
     ignoredNames: [
@@ -85,12 +86,16 @@
   docblockr: {}
   editor:
     fontFamily: "FuraCode Nerd Font"
+    fontSize: 12
     invisibles:
       space: "•"
       tab: "›"
     lineHeight: 1.3
     showInvisibles: true
     useHardwareAcceleration: true
+  "ex-mode":
+    splitbelow: true
+    splitright: true
   "exception-reporting":
     userId: "7652af63-0a80-ab67-85f4-9d3eebd7294e"
   "file-icons":
@@ -119,6 +124,7 @@
     showInfoMessages: true
   "linter-csslint":
     csslintExecutablePath: "/usr/local/bin/csslint"
+  "linter-eslint": {}
   "linter-jscs":
     esnext: true
     executablePath: "/usr/local/bin/jscs"
@@ -131,7 +137,7 @@
   "linter-tidy":
     tidyExecutablePath: "/usr/local/bin/tidy5"
   "linter-ui-default":
-    panelHeight: 300
+    panelHeight: 99
     showProviderName: true
     statusBarRepresents: "Current File"
   "markdown-preview":
@@ -159,11 +165,13 @@
   pigments: {}
   "prettier-atom":
     formatOnSaveOptions:
+      enabled: true
       isDisabledIfNotInPackageJson: true
     prettierOptions:
       semi: false
       singleQuote: true
     useEslint: true
+    useStylelint: true
   "pristine-ui": {}
   "repl-term":
     focusTerminal: true
@@ -206,6 +214,9 @@
     surroundKey: "S"
   welcome:
     showOnStartup: false
+  zentabs:
+    maximumOpenedTabs: 1
+    neverCloseUnsaved: true
 ".angular.html.text":
   editor:
     preferredLineLength: 80


heartbeat cleanup

heartbeat cleanup

we dont need to respond to heartbeats, we just need to track them and send them ( or some data ) at the correct interval

--- src/lib/Connection.coffee
+++ src/lib/Connection.coffee
@@ -245,8 +245,7 @@
   # on heartbeat recieved we should expect another
   _receivedHeartbeat: ()=>
     debug 4, ()=> return "♥ heartbeat"
-    @connection.write HeartbeatFrame
-    @_resetAllHeartbeatTimers()
+    @_resetHeartbeatTimer()

   _resetAllHeartbeatTimers: ()=>
     @_resetSendHeartbeatTimer()


Add tests

Add tests

--- karma/flows/test_controllers.coffee
+++ karma/flows/test_controllers.coffee
@@ -426,7 +426,9 @@
         scope.ruleset.ruleset_type = 'webhook'
         scope.formData.webhook = 'http://www.nyaruka.com'
         scope.formData.webhook_action = 'POST'
-        scope.formData.webhook_header = {"Authorization": "Token 12345"}
+        scope.formData.webhook_headers = [{name: '', key: ''}]
+        scope.webhook_headers_name[0] = 'Authorization'
+        scope.webhook_headers_value[0] = 'Token 12345'

       ruleset = flowService.flow.rule_sets[0]
       expect(ruleset.ruleset_type).toBe('webhook')
@@ -437,7 +439,8 @@
       # our config should have a url
       expect(ruleset.config.webhook).toBe('http://www.nyaruka.com')
       expect(ruleset.config.webhook_action).toBe('POST')
-      expect(ruleset.config.webhook_header['Authorization']).toBe('Token 12345')
+      expect(ruleset.config.webhook_headers[0]['name']).toBe('Authorization')
+      expect(ruleset.config.webhook_headers[0]['value']).toBe('Token 12345')

       # do it again, make sure we have the right number of rules
       editRules ruleset, (scope) ->


paper isn’t async anymore

paper isn’t async anymore

--- engines/standard_tasks/app/assets/javascripts/standard_tasks/controllers/funder_controller.js.coffee
+++ engines/standard_tasks/app/assets/javascripts/standard_tasks/controllers/funder_controller.js.coffee
@@ -13,7 +13,7 @@
       @send('saveModel')

     startAddingAuthor: ->
-      author = @store.createRecord('author', paper: @get('task.paper.content'), position: 0)
+      author = @store.createRecord('author', paper: @get('task.paper'), position: 0)
       @set('addingAuthor', author)

     finishAddingAuthor: ->


getVariableType - Fixed closures not being scanned for type hints. Fixes #178.

--- lib/services/php-file-parser.coffee
+++ lib/services/php-file-parser.coffee
@@ -455,7 +455,7 @@
         lineNumber = bufferPosition.row - 1

         while lineNumber > 0
-            line = editor.getTextInBufferRange([[lineNumber, 0], bufferPosition])
+            line = editor.lineTextForBufferRow(lineNumber)

             if not bestMatch
                 # Check for $x = new XXXXX()
@@ -490,6 +490,26 @@
                     # don't recognize the class name is irrelevant.
                     bestMatchRow = lineNumber
                     bestMatch = @parseElements(editor, newPosition, elements)
+
+            if not bestMatch
+                # Check for function or closure parameter type hints and the docblock.
+                regexFunction = new RegExp("function(?:[\\s]+([a-zA-Z]+))?[\\s]*[\\(](?:(?![a-zA-Z\\_\\\\]*[\\s]*\\#{element}).)*[,\\s]?([a-zA-Z\\_\\\\]*)[\\s]*\\#{element}[a-zA-Z0-9\\s\\$\\\\,=\\\"\\\'\(\)]*[\\s]*[\\)]", "g")
+                matches = regexFunction.exec(line)
+
+                if null != matches
+                    typeHint = matches[2]
+
+                    if typeHint.length > 0
+                        return @getFullClassName(editor, typeHint)
+
+                    funcName = matches[1]
+
+                    # Can be empty for closures.
+                    if funcName.length > 0
+                        params = proxy.docParams(@getFullClassName(editor), func)
+
+                        if params.params? and params.params[element]?
+                            return @getFullClassName(editor, params.params[element])

             chain = editor.scopeDescriptorForBufferPosition([lineNumber, line.length]).getScopeChain()

@@ -519,25 +539,7 @@
                     return @getFullClassName(editor, matches[1])

             # We've reached the function definition, check for type hints and/or the docblock.
-            if not bestMatch and chain.indexOf("function") != -1
-                regexFunction = new RegExp("function[\\s]+([a-zA-Z]+)[\\s]*[\\(](?:(?![a-zA-Z\\_\\\\]*[\\s]*\\#{element}).)*[,\\s]?([a-zA-Z\\_\\\\]*)[\\s]*\\#{element}[a-zA-Z0-9\\s\\$,=\\\"\\\'\(\)]*[\\s]*[\\)]", "g")
-                matches = regexFunction.exec(line)
-
-                if null == matches
-                    return null
-
-                func = matches[1]
-                value = matches[2]
-
-                # If we have a type hint
-                if value != ""
-                    return @getFullClassName(editor, value)
-
-                # otherwise, we are parsing PHPdoc (@param)
-                params = proxy.docParams(@getFullClassName(editor), func)
-                if params.params? and params.params[element]?
-                    return @getFullClassName(editor, params.params[element])
-
+            if chain.indexOf("function") != -1
                 break

             --lineNumber


Moved minted (again?)

Moved minted (again?)

--- grammars/latex.cson
+++ grammars/latex.cson
@@ -1120,239 +1120,239 @@
   }

 # Unfortunate necessity (mainly making a unique grammar for each coding language)
-mintGeneric: {
-  comment: 'A catch all for any other languages.'
-  name: 'meta.environment.embedded.$11.latex'
-  contentName: 'markup.raw.verbatim.latex'
-  begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)([a-zA-Z]*)(\\})'
-  beginCaptures:
-    1: name: 'support.function.environment.begin.latex'
-    2: name: 'punctuation.definition.function.begin.latex'
-    3: name: 'punctuation.definition.arguments.begin.latex'
-    4: name: 'variable.parameter.environment.$5.latex'
-    # 5: Used for meta scope name (basically just non starred version)
-    6: name: 'punctuation.definition.arguments.end.latex'
-    7: name: 'punctuation.definition.arguments.optional.begin.latex'
-    8: name: 'variable.parameter.optional.function.environment.latex'
-    9: name: 'punctuation.definition.arguments.optional.end.latex'
-    10: name: 'punctuation.definition.arguments.begin.latex'
-    11: name: 'variable.parameter.language.$11.latex'
-    12: name: 'punctuation.definition.arguments.end.latex'
-  end: '((\\\\)end)(\\{)(\\4)(\\})'
-  endCaptures:
-    1: name: 'support.function.environment.end.latex'
-    2: name: 'punctuation.definition.function.latex'
-    3: name: 'punctuation.definition.arguments.begin.latex'
-    4: name: 'variable.parameter.environment.latex'
-    5: name: 'punctuation.definition.arguments.end.latex'
-}
-mintPython: {
-  name: 'meta.environment.embedded.python.latex'
-  contentName: 'source.embedded.python'
-  begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(python3?|py3?)(\\})'
-  beginCaptures:
-    1: name: 'support.function.environment.begin.latex'
-    2: name: 'punctuation.definition.function.begin.latex'
-    3: name: 'punctuation.definition.arguments.begin.latex'
-    4: name: 'variable.parameter.environment.minted.latex'
-    # 5: Used for meta scope name (basically just non starred version)
-    6: name: 'punctuation.definition.arguments.end.latex'
-    7: name: 'punctuation.definition.arguments.optional.begin.latex'
-    8: name: 'variable.parameter.optional.function.environment.latex'
-    9: name: 'punctuation.definition.arguments.optional.end.latex'
-    10: name: 'punctuation.definition.arguments.begin.latex'
-    11: name: 'variable.parameter.language.python.latex'
-    12: name: 'punctuation.definition.arguments.end.latex'
-  end: '((\\\\)end)(\\{)(minted)(\\})'
-  endCaptures:
-    1: name: 'support.function.environment.end.latex'
-    2: name: 'punctuation.definition.function.latex'
-    3: name: 'punctuation.definition.arguments.begin.latex'
-    4: name: 'variable.parameter.environment.latex'
-    5: name: 'punctuation.definition.arguments.end.latex'
-  patterns: [{ include: 'source.python' }]
-}
-mintJavascript: {
-  name: 'meta.environment.embedded.js.latex'
-  contentName: 'source.embedded.js'
-  begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(js)(\\})'
-  beginCaptures:
-    1: name: 'support.function.environment.begin.latex'
-    2: name: 'punctuation.definition.function.begin.latex'
-    3: name: 'punctuation.definition.arguments.begin.latex'
-    4: name: 'variable.parameter.environment.minted.latex'
-    # 5: Used for meta scope name (basically just non starred version)
-    6: name: 'punctuation.definition.arguments.end.latex'
-    7: name: 'punctuation.definition.arguments.optional.begin.latex'
-    8: name: 'variable.parameter.optional.function.environment.latex'
-    9: name: 'punctuation.definition.arguments.optional.end.latex'
-    10: name: 'punctuation.definition.arguments.begin.latex'
-    11: name: 'variable.parameter.language.js.latex'
-    12: name: 'punctuation.definition.arguments.end.latex'
-  end: '((\\\\)end)(\\{)(minted)(\\})'
-  endCaptures:
-    1: name: 'support.function.environment.end.latex'
-    2: name: 'punctuation.definition.function.latex'
-    3: name: 'punctuation.definition.arguments.begin.latex'
-    4: name: 'variable.parameter.environment.latex'
-    5: name: 'punctuation.definition.arguments.end.latex'
-  patterns: [{ include: 'source.js' }]
-}
-mintJSON: {
-  name: 'meta.environment.embedded.json.latex'
-  contentName: 'source.embedded.json'
-  begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(json)(\\})'
-  beginCaptures:
-    1: name: 'support.function.environment.begin.latex'
-    2: name: 'punctuation.definition.function.begin.latex'
-    3: name: 'punctuation.definition.arguments.begin.latex'
-    4: name: 'variable.parameter.environment.minted.latex'
-    # 5: Used for meta scope name (basically just non starred version)
-    6: name: 'punctuation.definition.arguments.end.latex'
-    7: name: 'punctuation.definition.arguments.optional.begin.latex'
-    8: name: 'variable.parameter.optional.function.environment.latex'
-    9: name: 'punctuation.definition.arguments.optional.end.latex'
-    10: name: 'punctuation.definition.arguments.begin.latex'
-    11: name: 'variable.parameter.language.json.latex'
-    12: name: 'punctuation.definition.arguments.end.latex'
-  end: '((\\\\)end)(\\{)(minted)(\\})'
-  endCaptures:
-    1: name: 'support.function.environment.end.latex'
-    2: name: 'punctuation.definition.function.latex'
-    3: name: 'punctuation.definition.arguments.begin.latex'
-    4: name: 'variable.parameter.environment.latex'
-    5: name: 'punctuation.definition.arguments.end.latex'
-  patterns: [{ include: 'source.json' }]
-}
-mintCoffeescript: {
-  name: 'meta.environment.embedded.coffee.latex'
-  contentName: 'source.embedded.coffee'
-  begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(coffee(?:\\-?script)?)(\\})'
-  beginCaptures:
-    1: name: 'support.function.environment.begin.latex'
-    2: name: 'punctuation.definition.function.begin.latex'
-    3: name: 'punctuation.definition.arguments.begin.latex'
-    4: name: 'variable.parameter.environment.minted.latex'
-    # 5: Used for meta scope name (basically just non starred version)
-    6: name: 'punctuation.definition.arguments.end.latex'
-    7: name: 'punctuation.definition.arguments.optional.begin.latex'
-    8: name: 'variable.parameter.optional.function.environment.latex'
-    9: name: 'punctuation.definition.arguments.optional.end.latex'
-    10: name: 'punctuation.definition.arguments.begin.latex'
-    11: name: 'variable.parameter.language.coffee.latex'
-    12: name: 'punctuation.definition.arguments.end.latex'
-  end: '((\\\\)end)(\\{)(minted)(\\})'
-  endCaptures:
-    1: name: 'support.function.environment.end.latex'
-    2: name: 'punctuation.definition.function.latex'
-    3: name: 'punctuation.definition.arguments.begin.latex'
-    4: name: 'variable.parameter.environment.latex'
-    5: name: 'punctuation.definition.arguments.end.latex'
-  patterns: [{ include: 'source.coffee' }]
-}
-mintCSS: {
-  name: 'meta.environment.embedded.css.latex'
-  contentName: 'source.embedded.css'
-  begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(css)(\\})'
-  beginCaptures:
-    1: name: 'support.function.environment.begin.latex'
-    2: name: 'punctuation.definition.function.begin.latex'
-    3: name: 'punctuation.definition.arguments.begin.latex'
-    4: name: 'variable.parameter.environment.minted.latex'
-    # 5: Used for meta scope name (basically just non starred version)
-    6: name: 'punctuation.definition.arguments.end.latex'
-    7: name: 'punctuation.definition.arguments.optional.begin.latex'
-    8: name: 'variable.parameter.optional.function.environment.latex'
-    9: name: 'punctuation.definition.arguments.optional.end.latex'
-    10: name: 'punctuation.definition.arguments.begin.latex'
-    11: name: 'variable.parameter.language.css.latex'
-    12: name: 'punctuation.definition.arguments.end.latex'
-  end: '((\\\\)end)(\\{)(minted)(\\})'
-  endCaptures:
-    1: name: 'support.function.environment.end.latex'
-    2: name: 'punctuation.definition.function.latex'
-    3: name: 'punctuation.definition.arguments.begin.latex'
-    4: name: 'variable.parameter.environment.latex'
-    5: name: 'punctuation.definition.arguments.end.latex'
-  patterns: [{ include: 'source.css' }]
-}
-mintLess: {
-  name: 'meta.environment.embedded.less.latex'
-  contentName: 'source.embedded.less'
-  begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(less)(\\})'
-  beginCaptures:
-    1: name: 'support.function.environment.begin.latex'
-    2: name: 'punctuation.definition.function.begin.latex'
-    3: name: 'punctuation.definition.arguments.begin.latex'
-    4: name: 'variable.parameter.environment.minted.latex'
-    # 5: Used for meta scope name (basically just non starred version)
-    6: name: 'punctuation.definition.arguments.end.latex'
-    7: name: 'punctuation.definition.arguments.optional.begin.latex'
-    8: name: 'variable.parameter.optional.function.environment.latex'
-    9: name: 'punctuation.definition.arguments.optional.end.latex'
-    10: name: 'punctuation.definition.arguments.begin.latex'
-    11: name: 'variable.parameter.language.less.latex'
-    12: name: 'punctuation.definition.arguments.end.latex'
-  end: '((\\\\)end)(\\{)(minted)(\\})'
-  endCaptures:
-    1: name: 'support.function.environment.end.latex'
-    2: name: 'punctuation.definition.function.latex'
-    3: name: 'punctuation.definition.arguments.begin.latex'
-    4: name: 'variable.parameter.environment.latex'
-    5: name: 'punctuation.definition.arguments.end.latex'
-  patterns: [{ include: 'source.less' }]
-}
-mintXML: {
-  name: 'meta.environment.embedded.xml.latex'
-  contentName: 'source.embedded.xml'
-  begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(xml)(\\})'
-  beginCaptures:
-    1: name: 'support.function.environment.begin.latex'
-    2: name: 'punctuation.definition.function.begin.latex'
-    3: name: 'punctuation.definition.arguments.begin.latex'
-    4: name: 'variable.parameter.environment.minted.latex'
-    # 5: Used for meta scope name (basically just non starred version)
-    6: name: 'punctuation.definition.arguments.end.latex'
-    7: name: 'punctuation.definition.arguments.optional.begin.latex'
-    8: name: 'variable.parameter.optional.function.environment.latex'
-    9: name: 'punctuation.definition.arguments.optional.end.latex'
-    10: name: 'punctuation.definition.arguments.begin.latex'
-    11: name: 'variable.parameter.language.xml.latex'
-    12: name: 'punctuation.definition.arguments.end.latex'
-  end: '((\\\\)end)(\\{)(minted)(\\})'
-  endCaptures:
-    1: name: 'support.function.environment.end.latex'
-    2: name: 'punctuation.definition.function.latex'
-    3: name: 'punctuation.definition.arguments.begin.latex'
-    4: name: 'variable.parameter.environment.latex'
-    5: name: 'punctuation.definition.arguments.end.latex'
-  patterns: [{ include: 'source.xml' }]
-}
-mintRuby: {
-  name: 'meta.environment.embedded.ruby.latex'
-  contentName: 'source.embedded.ruby'
-  begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(ruby|rb)(\\})'
-  beginCaptures:
-    1: name: 'support.function.environment.begin.latex'
-    2: name: 'punctuation.definition.function.begin.latex'
-    3: name: 'punctuation.definition.arguments.begin.latex'
-    4: name: 'variable.parameter.environment.minted.latex'
-    # 5: Used for meta scope name (basically just non starred version)
-    6: name: 'punctuation.definition.arguments.end.latex'
-    7: name: 'punctuation.definition.arguments.optional.begin.latex'
-    8: name: 'variable.parameter.optional.function.environment.latex'
-    9: name: 'punctuation.definition.arguments.optional.end.latex'
-    10: name: 'punctuation.definition.arguments.begin.latex'
-    11: name: 'variable.parameter.language.ruby.latex'
-    12: name: 'punctuation.definition.arguments.end.latex'
-  end: '((\\\\)end)(\\{)(minted)(\\})'
-  endCaptures:
-    1: name: 'support.function.environment.end.latex'
-    2: name: 'punctuation.definition.function.latex'
-    3: name: 'punctuation.definition.arguments.begin.latex'
-    4: name: 'variable.parameter.environment.latex'
-    5: name: 'punctuation.definition.arguments.end.latex'
-  patterns: [{ include: 'source.ruby' }]
-}
+  mintGeneric: {
+    comment: 'A catch all for any other languages.'
+    name: 'meta.environment.embedded.$11.latex'
+    contentName: 'markup.raw.verbatim.latex'
+    begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)([a-zA-Z]*)(\\})'
+    beginCaptures:
+      1: name: 'support.function.environment.begin.latex'
+      2: name: 'punctuation.definition.function.begin.latex'
+      3: name: 'punctuation.definition.arguments.begin.latex'
+      4: name: 'variable.parameter.environment.$5.latex'
+      # 5: Used for meta scope name (basically just non starred version)
+      6: name: 'punctuation.definition.arguments.end.latex'
+      7: name: 'punctuation.definition.arguments.optional.begin.latex'
+      8: name: 'variable.parameter.optional.function.environment.latex'
+      9: name: 'punctuation.definition.arguments.optional.end.latex'
+      10: name: 'punctuation.definition.arguments.begin.latex'
+      11: name: 'variable.parameter.language.$11.latex'
+      12: name: 'punctuation.definition.arguments.end.latex'
+    end: '((\\\\)end)(\\{)(\\4)(\\})'
+    endCaptures:
+      1: name: 'support.function.environment.end.latex'
+      2: name: 'punctuation.definition.function.latex'
+      3: name: 'punctuation.definition.arguments.begin.latex'
+      4: name: 'variable.parameter.environment.latex'
+      5: name: 'punctuation.definition.arguments.end.latex'
+  }
+  mintPython: {
+    name: 'meta.environment.embedded.python.latex'
+    contentName: 'source.embedded.python'
+    begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(python3?|py3?)(\\})'
+    beginCaptures:
+      1: name: 'support.function.environment.begin.latex'
+      2: name: 'punctuation.definition.function.begin.latex'
+      3: name: 'punctuation.definition.arguments.begin.latex'
+      4: name: 'variable.parameter.environment.minted.latex'
+      # 5: Used for meta scope name (basically just non starred version)
+      6: name: 'punctuation.definition.arguments.end.latex'
+      7: name: 'punctuation.definition.arguments.optional.begin.latex'
+      8: name: 'variable.parameter.optional.function.environment.latex'
+      9: name: 'punctuation.definition.arguments.optional.end.latex'
+      10: name: 'punctuation.definition.arguments.begin.latex'
+      11: name: 'variable.parameter.language.python.latex'
+      12: name: 'punctuation.definition.arguments.end.latex'
+    end: '((\\\\)end)(\\{)(minted)(\\})'
+    endCaptures:
+      1: name: 'support.function.environment.end.latex'
+      2: name: 'punctuation.definition.function.latex'
+      3: name: 'punctuation.definition.arguments.begin.latex'
+      4: name: 'variable.parameter.environment.latex'
+      5: name: 'punctuation.definition.arguments.end.latex'
+    patterns: [{ include: 'source.python' }]
+  }
+  mintJavascript: {
+    name: 'meta.environment.embedded.js.latex'
+    contentName: 'source.embedded.js'
+    begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(js)(\\})'
+    beginCaptures:
+      1: name: 'support.function.environment.begin.latex'
+      2: name: 'punctuation.definition.function.begin.latex'
+      3: name: 'punctuation.definition.arguments.begin.latex'
+      4: name: 'variable.parameter.environment.minted.latex'
+      # 5: Used for meta scope name (basically just non starred version)
+      6: name: 'punctuation.definition.arguments.end.latex'
+      7: name: 'punctuation.definition.arguments.optional.begin.latex'
+      8: name: 'variable.parameter.optional.function.environment.latex'
+      9: name: 'punctuation.definition.arguments.optional.end.latex'
+      10: name: 'punctuation.definition.arguments.begin.latex'
+      11: name: 'variable.parameter.language.js.latex'
+      12: name: 'punctuation.definition.arguments.end.latex'
+    end: '((\\\\)end)(\\{)(minted)(\\})'
+    endCaptures:
+      1: name: 'support.function.environment.end.latex'
+      2: name: 'punctuation.definition.function.latex'
+      3: name: 'punctuation.definition.arguments.begin.latex'
+      4: name: 'variable.parameter.environment.latex'
+      5: name: 'punctuation.definition.arguments.end.latex'
+    patterns: [{ include: 'source.js' }]
+  }
+  mintJSON: {
+    name: 'meta.environment.embedded.json.latex'
+    contentName: 'source.embedded.json'
+    begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(json)(\\})'
+    beginCaptures:
+      1: name: 'support.function.environment.begin.latex'
+      2: name: 'punctuation.definition.function.begin.latex'
+      3: name: 'punctuation.definition.arguments.begin.latex'
+      4: name: 'variable.parameter.environment.minted.latex'
+      # 5: Used for meta scope name (basically just non starred version)
+      6: name: 'punctuation.definition.arguments.end.latex'
+      7: name: 'punctuation.definition.arguments.optional.begin.latex'
+      8: name: 'variable.parameter.optional.function.environment.latex'
+      9: name: 'punctuation.definition.arguments.optional.end.latex'
+      10: name: 'punctuation.definition.arguments.begin.latex'
+      11: name: 'variable.parameter.language.json.latex'
+      12: name: 'punctuation.definition.arguments.end.latex'
+    end: '((\\\\)end)(\\{)(minted)(\\})'
+    endCaptures:
+      1: name: 'support.function.environment.end.latex'
+      2: name: 'punctuation.definition.function.latex'
+      3: name: 'punctuation.definition.arguments.begin.latex'
+      4: name: 'variable.parameter.environment.latex'
+      5: name: 'punctuation.definition.arguments.end.latex'
+    patterns: [{ include: 'source.json' }]
+  }
+  mintCoffeescript: {
+    name: 'meta.environment.embedded.coffee.latex'
+    contentName: 'source.embedded.coffee'
+    begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(coffee(?:\\-?script)?)(\\})'
+    beginCaptures:
+      1: name: 'support.function.environment.begin.latex'
+      2: name: 'punctuation.definition.function.begin.latex'
+      3: name: 'punctuation.definition.arguments.begin.latex'
+      4: name: 'variable.parameter.environment.minted.latex'
+      # 5: Used for meta scope name (basically just non starred version)
+      6: name: 'punctuation.definition.arguments.end.latex'
+      7: name: 'punctuation.definition.arguments.optional.begin.latex'
+      8: name: 'variable.parameter.optional.function.environment.latex'
+      9: name: 'punctuation.definition.arguments.optional.end.latex'
+      10: name: 'punctuation.definition.arguments.begin.latex'
+      11: name: 'variable.parameter.language.coffee.latex'
+      12: name: 'punctuation.definition.arguments.end.latex'
+    end: '((\\\\)end)(\\{)(minted)(\\})'
+    endCaptures:
+      1: name: 'support.function.environment.end.latex'
+      2: name: 'punctuation.definition.function.latex'
+      3: name: 'punctuation.definition.arguments.begin.latex'
+      4: name: 'variable.parameter.environment.latex'
+      5: name: 'punctuation.definition.arguments.end.latex'
+    patterns: [{ include: 'source.coffee' }]
+  }
+  mintCSS: {
+    name: 'meta.environment.embedded.css.latex'
+    contentName: 'source.embedded.css'
+    begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(css)(\\})'
+    beginCaptures:
+      1: name: 'support.function.environment.begin.latex'
+      2: name: 'punctuation.definition.function.begin.latex'
+      3: name: 'punctuation.definition.arguments.begin.latex'
+      4: name: 'variable.parameter.environment.minted.latex'
+      # 5: Used for meta scope name (basically just non starred version)
+      6: name: 'punctuation.definition.arguments.end.latex'
+      7: name: 'punctuation.definition.arguments.optional.begin.latex'
+      8: name: 'variable.parameter.optional.function.environment.latex'
+      9: name: 'punctuation.definition.arguments.optional.end.latex'
+      10: name: 'punctuation.definition.arguments.begin.latex'
+      11: name: 'variable.parameter.language.css.latex'
+      12: name: 'punctuation.definition.arguments.end.latex'
+    end: '((\\\\)end)(\\{)(minted)(\\})'
+    endCaptures:
+      1: name: 'support.function.environment.end.latex'
+      2: name: 'punctuation.definition.function.latex'
+      3: name: 'punctuation.definition.arguments.begin.latex'
+      4: name: 'variable.parameter.environment.latex'
+      5: name: 'punctuation.definition.arguments.end.latex'
+    patterns: [{ include: 'source.css' }]
+  }
+  mintLess: {
+    name: 'meta.environment.embedded.less.latex'
+    contentName: 'source.embedded.less'
+    begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(less)(\\})'
+    beginCaptures:
+      1: name: 'support.function.environment.begin.latex'
+      2: name: 'punctuation.definition.function.begin.latex'
+      3: name: 'punctuation.definition.arguments.begin.latex'
+      4: name: 'variable.parameter.environment.minted.latex'
+      # 5: Used for meta scope name (basically just non starred version)
+      6: name: 'punctuation.definition.arguments.end.latex'
+      7: name: 'punctuation.definition.arguments.optional.begin.latex'
+      8: name: 'variable.parameter.optional.function.environment.latex'
+      9: name: 'punctuation.definition.arguments.optional.end.latex'
+      10: name: 'punctuation.definition.arguments.begin.latex'
+      11: name: 'variable.parameter.language.less.latex'
+      12: name: 'punctuation.definition.arguments.end.latex'
+    end: '((\\\\)end)(\\{)(minted)(\\})'
+    endCaptures:
+      1: name: 'support.function.environment.end.latex'
+      2: name: 'punctuation.definition.function.latex'
+      3: name: 'punctuation.definition.arguments.begin.latex'
+      4: name: 'variable.parameter.environment.latex'
+      5: name: 'punctuation.definition.arguments.end.latex'
+    patterns: [{ include: 'source.less' }]
+  }
+  mintXML: {
+    name: 'meta.environment.embedded.xml.latex'
+    contentName: 'source.embedded.xml'
+    begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(xml)(\\})'
+    beginCaptures:
+      1: name: 'support.function.environment.begin.latex'
+      2: name: 'punctuation.definition.function.begin.latex'
+      3: name: 'punctuation.definition.arguments.begin.latex'
+      4: name: 'variable.parameter.environment.minted.latex'
+      # 5: Used for meta scope name (basically just non starred version)
+      6: name: 'punctuation.definition.arguments.end.latex'
+      7: name: 'punctuation.definition.arguments.optional.begin.latex'
+      8: name: 'variable.parameter.optional.function.environment.latex'
+      9: name: 'punctuation.definition.arguments.optional.end.latex'
+      10: name: 'punctuation.definition.arguments.begin.latex'
+      11: name: 'variable.parameter.language.xml.latex'
+      12: name: 'punctuation.definition.arguments.end.latex'
+    end: '((\\\\)end)(\\{)(minted)(\\})'
+    endCaptures:
+      1: name: 'support.function.environment.end.latex'
+      2: name: 'punctuation.definition.function.latex'
+      3: name: 'punctuation.definition.arguments.begin.latex'
+      4: name: 'variable.parameter.environment.latex'
+      5: name: 'punctuation.definition.arguments.end.latex'
+    patterns: [{ include: 'source.xml' }]
+  }
+  mintRuby: {
+    name: 'meta.environment.embedded.ruby.latex'
+    contentName: 'source.embedded.ruby'
+    begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(ruby|rb)(\\})'
+    beginCaptures:
+      1: name: 'support.function.environment.begin.latex'
+      2: name: 'punctuation.definition.function.begin.latex'
+      3: name: 'punctuation.definition.arguments.begin.latex'
+      4: name: 'variable.parameter.environment.minted.latex'
+      # 5: Used for meta scope name (basically just non starred version)
+      6: name: 'punctuation.definition.arguments.end.latex'
+      7: name: 'punctuation.definition.arguments.optional.begin.latex'
+      8: name: 'variable.parameter.optional.function.environment.latex'
+      9: name: 'punctuation.definition.arguments.optional.end.latex'
+      10: name: 'punctuation.definition.arguments.begin.latex'
+      11: name: 'variable.parameter.language.ruby.latex'
+      12: name: 'punctuation.definition.arguments.end.latex'
+    end: '((\\\\)end)(\\{)(minted)(\\})'
+    endCaptures:
+      1: name: 'support.function.environment.end.latex'
+      2: name: 'punctuation.definition.function.latex'
+      3: name: 'punctuation.definition.arguments.begin.latex'
+      4: name: 'variable.parameter.environment.latex'
+      5: name: 'punctuation.definition.arguments.end.latex'
+    patterns: [{ include: 'source.ruby' }]
+  }

 # Removed


Restore quidoc handling of blank lines in comments, which should produce \n\n.

--- grunt/quidoc.coffee
+++ grunt/quidoc.coffee
@@ -40,7 +40,7 @@
     className: @controlClassName source
     comments: @controlComments source

-  # Return the name for the control class defined in the CoffeeScript source text.
+  # Return the name for the control class defined in the source text.
   controlClassName: ( source ) ->
     match = @regexClassName.exec source
     if match?
@@ -48,7 +48,7 @@
     else
       null

-  # Return the comments found for members defined in the CoffeeScript source text.
+  # Return the comments found for members defined in the source text.
   controlComments: ( source ) ->
     comments = null
     match = @regexComments.exec source
@@ -61,12 +61,16 @@
       match = @regexComments.exec source
     comments

-  # Remove the "  # " found at the start of a CoffeeScript block comment.
+  # Remove the comment indicator (e.g., "*") on interior block comment lines.
   commentText: ( commentBlock ) ->
     text = ""
     match = @regexCommentText.exec commentBlock
     while match != null
       [ full, lineText ] = match
+      if text.length > 0
+        text += "\n"
+      text += lineText
+      ###
       lineText = lineText.trim()
       if lineText.length == 0
         text += "\n\n"  # Line of pure whitespace was intended as a break.
@@ -74,6 +78,7 @@
         if text.length > 0 and text[ text.length - 1 ] != "\n"
           text += " "
         text += lineText
+      ###
       match = @regexCommentText.exec commentBlock
     text

@@ -102,7 +107,7 @@
     )
     :                         # Colon terminates identifier
   ///g
-  commentText: /\r?\n  # (.*)/g
+  commentText: /^\s*#[ ]?(.*)/gm

 # Extracts docs from QuickUI markup controls
@@ -140,7 +145,7 @@
     )
     :                         # Colon terminates identifier
   ///g
-  commentText: /\s*\*\s(.*)/g
+  commentText: /^\s*\*[ ]?(.*)/gm

 extractors =


Removed some outdated lines.

Removed some outdated lines.

--- lib/formats/jsonschema-v4.coffee
+++ lib/formats/jsonschema-v4.coffee
@@ -13,12 +13,6 @@
     return cb err if err

     valueType = typeSpec.name  # ignoring nested types for now
-    unless valueType
-      if value.valueDefinition?.values?.length > 1
-        valueType = 'array'
-      else
-        valueType = 'string'
-
     switch valueType
       when 'object'
         return handleObject value, options, cb


hansel and gretel

hansel and gretel

--- src/notice/capsule.coffee
+++ src/notice/capsule.coffee
@@ -5,7 +5,7 @@
     testable = class Capsule

         #
-        # ./message  - creates on of these
+        # ./message  - creates one of these
         # ./notifier - pushes it down the middleware pipeline
         #


Revert "Make window.FACTLINK equal to the Factlink object in the iframe, no funny proxy methods. Use other explicit names for the other things window.FACTLINK was used for"

This reverts commit 669fbd077e18f2122d1552b60cbab09cf97183b7.

--- app/js/loader.coffee
+++ app/js/loader.coffee
@@ -1,5 +1,4 @@
-return if window.FACTLINK_LOADED?
-window.FACTLINK_LOADED = true
+return if window.FACTLINK?

 #### Create iframe

@@ -45,12 +44,23 @@

   coreScriptTag.onload = coreScriptTag.onreadystatechange = null

-  window.FACTLINK = iframe.contentWindow.Factlink
+  proxy = (func) ->
+    window.FACTLINK[func] = ->
+      iframe.contentWindow.Factlink[func].apply iframe.contentWindow.Factlink, arguments
+
+  proxy 'on'
+  proxy 'off'
+  proxy 'hideDimmer'
+  proxy 'triggerClick'
+  proxy 'stopAnnotating'
+  proxy 'getSelectionInfo'
+
   jQuery?(window).trigger 'factlink.libraryLoaded'

 #### Load iframe with script tag

-window.FACTLINK_ON_IFRAME_LOAD = ->
+window.FACTLINK = {}
+window.FACTLINK.iframeLoaded = ->
   iframe.contentWindow.document.head.appendChild coreScriptTag

 iframeDoc = iframe.contentWindow.document
@@ -59,7 +69,7 @@
   <!DOCTYPE html>

 """


add "confused with" data

add "confused with" data

--- app/lib/en-us.coffee
+++ app/lib/en-us.coffee
@@ -84,8 +84,117 @@
     identify: 'Identify'

   differences:
-    bearBlack:
-      amphibiansAndReptiles: 'Really hard to confuse these two, but somehow people manage it.'
+    aardvark:
+      pangolin: 'A pangolin is covered in scales while an aardvark has brown fur. It also has a shorter, pointier snout than an aardvark.'
+    baboon:
+      samangoMonkey: 'A samango monkey has bluish-gray fur while a baboon has yellow-brown fur. It also has a dark face with prominent fur on the cheeks.'
+    baboon:
+      vervetMonkey: 'A vervet monkey is much smaller than a baboon with a black face and light gray fur.'
+    buffalo:
+      wildebeest: 'A wildebeest is more slender than a buffalo with a long, narrow face and a beard.'
+    bushbuck:
+      reedbuck: 'A reedbuck has solid tan fur without white spots or stripes. Its horns are ringed and it has a large, black spot below the ears.'
+    bushbuck:
+      nyala: 'A nyala female has prominent vertical white stripes and white markings between the eyes, while a bushbuck female has lighter stripes and more prominent spots.'
+    bushpig:
+      warthog: 'A warthog is a slate gray color with prominent warts on the sides of its face and larger tusks than a bushpig.'
+    caracal:
+      lionFemale: 'A lion is larger than a caracal with rounded ears, no white or black markings on the face or ears, and its fur is a lighter golden-yellow color.'
+    civet:
+      genet: 'A genet is more slender than a civet with a much longer, bushier tail. '
+    duiker:
+      oribi: 'An oribi is more slender and taller than a duiker with a flat back, white underbelly, and prominent black spots below the ears. '
+    eland:
+      hartebeest: 'A hartebeest is a taller and more slender than an eland with reddish fur, narrow face, and short, spiral horns.'
+    eland:
+      kudu: 'A kudu is taller and less ox-like than an eland, it has massive spiral horns and lacks a dewlap on the throat.'
+    genet:
+      civet: 'A civet is stockier than a genet with a much shorter tail and a dark mask on the face.'
+    hartebeest:
+      eland: 'An eland is more stocky and ox-like than a hartebeest with long, straight horns that twist. It has a dewlap below the neck.'
+    hartebeest:
+      wildebeest: 'A wildebeest is dark gray-brown with a mane and beard. Its horns are longer than a hartebeest.'
+    hartebeest:
+      kudu: 'A kudu is stockier than a hartebeest with tan-gray fur, vertical stripes, and the males have manes and massive spiral horns.'
+    hippopotamus:
+      warthog: 'A warthog is much smaller than a hippopotamus with horns and prominent warts on the face.'
+    hyena:
+      wildDog: 'A wild dog is more slender than a hyena with patchy black, orange, and white fur and a flat back.'
+    impala:
+      bushbuck: 'A bushbuck has vertical white stripes and spots on the flanks as well as a rounded back.'
+    impala:
+      reedbuck: 'A reedbuck has solid tan fur without horizontal bands. Its horns are curved and ringed and it has large, black spots below the ears.'
+    jackal:
+      wildDog: 'A wild dog is more larger than a jackal with patchy black, orange, and white fur.'
+    kudu:
+      eland: 'An eland is more stocky and ox-like than a kudu with long, straight horns that twist. It has a dewlap below the neck.'
+    kudu:
+      hartebeest: 'A hartebeest is more slender than a kudu with reddish fur, narrow face, and short, spiral horns.'
+    kudu:
+      nyala: 'A nyala is smaller than a kudu and females are more reddish-brown while males are chestnut with orange legs.'
+    leopard:
+      lionFemale: 'A lion has solid golden-tan fur without dark spots. '
+    leopard:
+      lionCub: 'A lion cub is small with solid golden-tan fur without dark spots. '
+    leopard:
+      serval: 'A serval is much smaller and lankier than a leopard with pointy ears, a small face, and a bushy tail.'
+    lionCub:
+      lionFemale: 'A female lion is much larger than a cub.'
+    lionCub:
+      leopard: 'A leopard is much larger than a lion cub and it has dark spots.'
+    lionFemale:
+      lionCub: 'A lion cub is much smaller than an adult female lion.'
+    lionFemale:
+      caracal: 'A caracal has pointy ears, reddish-tan fur, and white and black markings on the face.'
+    lionFemale:
+      leopard: 'A leopard has dark black spots.'
+    mongoose:
+      weasel: 'The weasel species in Gorongosa are skunklike with black and white fur.'
+    nyala:
+      kudu: 'A kudu is larger than a nyala with grayish-tan fur. Males have shaggy beards and massive, spiral horns.'
+    oribi:
+      duiker: 'A duiker is stockier than an oribi with a rounded back. The common duiker has a dark stripe on its nose, while the red duiker has reddish fur and the blue duiker has bluish-gray fur.'
+    otter:
+      honeyBadger: 'A honey badger has a distinct white patch on its back and is stockier than an otter'
+    otter:
+      mongoose: 'A mongoose is smaller and more slender than an otter, with a long thin tail.'
+    otter:
+      weasel: 'The weasel species in Gorongosa are skunklike with black and white fur.'
+    pangolin:
+      aardvark: 'An aardvark has brown fur and a long, flat snout.'
+    reedbuck:
+      bushbuck: 'A bushbuck has vertical white stripes and spots on the flanks as well as a rounded back.'
+    reedbuck:
+      impala: 'An impala has horizontal bands and males have large, spiral horns.'
+    samangoMonkey:
+      baboon: 'A baboon has yellow-brown fur and a light colored face.'
+    samangoMonkey:
+      vervetMonkey: 'A vervet monkey is much smaller than a samango monkey with a black face and light gray fur.'
+    serval:
+      leopard: 'A leopard is much larger and stockier than a serval and it has a round face and a smooth tail.'
+    serval:
+      wildCat: 'A wildcat is smaller and stockier than a serval and resembles a house cat.'
+    vervetMonkey:
+      baboon: 'A baboon is larger than a vervet monkey with yellow-brown fur and a light colored face.'
+    vervetMonkey:
+      samangoMonkey: 'A samango monkey is larger than a vervet monkey with bluish-gray fur and prominent fur on the cheeks.'
+    warthog:
+      bushpig: 'A bushpig has thick reddish-silvery fur and smaller tusks than a warthog.'
+    warthog:
+      hippopotamus: 'A hippopotamus is much larger than a warthog with a round body, face, and tiny pink ears. '
+    weasel:
+      mongoose: 'The mongoose species in Gorongosa are various shades of brown while the weasels are black and white and skunklike.'
+    wildDog:
+      hyena: 'A hyena is stockier than a wild dog with yellowish fur and light brown spots. Its back is sloped downward. '
+    wildDog:
+      jackal: 'A jackal is smaller than a wild dog with light brown and silver-black fur.'
+    wildcat:
+      serval: 'A serval is larger, taller, and more slender than a wildcat with yellow fur and black spots.'
+    wildebeest:
+      buffalo: 'A buffalo is stockier and more cattlelike than a wildebeest and lacks a beard and mane.'
+    wildebeest:
+      hartebeest: 'A hartebeest has reddish-brown fur, very short horns, and lacks a beard and mane.'
+

   animals:
     aardvark: