fix pre-release-bug button state for search-option was not properly updated

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -166,11 +166,13 @@
     @editor.buffer.emitModifiedStatusChanged(state)

   toggleSearchWholeWord: ->
-    @controlBar.updateStateElements(wholeWordButton: @provider.toggleSearchWholeWord())
+    @provider.toggleSearchWholeWord()
+    @controlBar.updateStateElements(wholeWordButton: @searchWholeWord)
     @refresh(force: true)

   toggleSearchIgnoreCase: ->
-    @controlBar.updateStateElements(ignoreCaseButton: @provider.toggleSearchIgnoreCase())
+    @provider.toggleSearchIgnoreCase()
+    @controlBar.updateStateElements(ignoreCaseButton: @searchIgnoreCase)
     @refresh(force: true)

   toggleProtected: ->


just change location within file

just change location within file

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -61,17 +61,6 @@
   readOnly: false
   protected: false
   excludedFiles: null
-
-  isModified: ->
-    @modifiedState
-
-  setModifiedState: (state) ->
-    if state isnt @modifiedState
-      # HACK: overwrite TextBuffer:isModified to return static state.
-      # This state is used for tabs package to show modified icon on tab.
-      @modifiedState = state
-      @editor.buffer.isModified = -> state
-      @editor.buffer.emitModifiedStatusChanged(state)

   onDidMoveToPrompt: (fn) -> @emitter.on('did-move-to-prompt', fn)
   emitDidMoveToPrompt: -> @emitter.emit('did-move-to-prompt')
@@ -124,6 +113,27 @@
       'narrow-ui:toggle-search-whole-word': => @toggleSearchWholeWord()
       'narrow-ui:toggle-search-ignore-case': => @toggleSearchIgnoreCase()

+  withIgnoreCursorMove: (fn) ->
+    @ignoreCursorMove = true
+    fn()
+    @ignoreCursorMove = false
+
+  withIgnoreChange: (fn) ->
+    @ignoreChange = true
+    fn()
+    @ignoreChange = false
+
+  isModified: ->
+    @modifiedState
+
+  setModifiedState: (state) ->
+    if state isnt @modifiedState
+      # HACK: overwrite TextBuffer:isModified to return static state.
+      # This state is used for tabs package to show modified icon on tab.
+      @modifiedState = state
+      @editor.buffer.isModified = -> state
+      @editor.buffer.emitModifiedStatusChanged(state)
+
   toggleSearchWholeWord: ->
     @provider.toggleSearchWholeWord()
     @refresh(force: true)
@@ -133,6 +143,30 @@
     @provider.toggleSearchIgnoreCase()
     @refresh(force: true)
     @updateProviderPanel(ignoreCaseButton: @provider.searchIgnoreCase)
+
+  toggleProtected: ->
+    @protected = not @protected
+    @itemIndicator.redraw()
+    @updateProviderPanel({@protected})
+
+  toggleAutoPreview: ->
+    @autoPreview = not @autoPreview
+    @updateProviderPanel({@autoPreview})
+    if @autoPreview
+      @preview()
+    else
+      @rowMarker?.destroy()
+
+  setReadOnly: (readOnly) ->
+    @readOnly = readOnly
+    if @readOnly
+      @editorElement.component?.setInputEnabled(false)
+      @editorElement.classList.add('read-only')
+      @vmpActivateNormalMode() if @vmpIsInsertMode()
+    else
+      @editorElement.component.setInputEnabled(true)
+      @editorElement.classList.remove('read-only')
+      @vmpActivateInsertMode() if @vmpIsNormalMode()

   constructor: (@provider, {@query}={}) ->
     @disposables = new CompositeDisposable
@@ -180,23 +214,35 @@
     @disposables.add new Disposable =>
       @constructor.unregister(this)

-  toggleProtected: ->
-    @setProtected(not @protected)
-
-  setProtected: (@protected) ->
-    @itemIndicator.redraw()
-    @updateProviderPanel({@protected})
-
-  setReadOnly: (readOnly) ->
-    @readOnly = readOnly
-    if @readOnly
-      @editorElement.component?.setInputEnabled(false)
-      @editorElement.classList.add('read-only')
-      @vmpActivateNormalMode() if @vmpIsInsertMode()
-    else
-      @editorElement.component.setInputEnabled(true)
-      @editorElement.classList.remove('read-only')
-      @vmpActivateInsertMode() if @vmpIsNormalMode()
+  start: ->
+    # When initial getItems() take very long time, it means refresh get delayed.
+    # In this case, user see modified icon(mark) on tab.
+    # Explicitly setting modified start here prevent this
+    @setModifiedState(false)
+    if @editorElement.component?
+      attachedPromise = Promise.resolve()
+    else
+      attachedPromise = new Promise (resolve) =>
+        disposable = @editorElement.onDidAttach ->
+          disposable.dispose()
+          resolve()
+
+    if isNarrowEditor(@provider.editor)
+      # if narrow is invoked from narrow-editor, open new narrow-editor
+      #  on same pane of existing narrow-editor
+      @provider.getPane().activateItem(@editor)
+    else
+      activatePaneItemInAdjacentPane(@editor, split: settings.get('directionToOpen'))
+
+    attachedPromise.then =>
+      @grammar.activate()
+      if @query
+        @insertQuery(@query)
+      else
+        @withIgnoreChange => @insertQuery()
+      @providerPanel.show()
+      @moveToPrompt()
+      @refresh()

   observeStopChangingActivePaneItem: ->
     needToSync = (item) =>
@@ -220,36 +266,6 @@
       else
         @startSyncToEditor(item) if @hasSomeNormalItemForFilePath(item.getPath())

-  start: ->
-    # When initial getItems() take very long time, it means refresh get delayed.
-    # In this case, user see modified icon(mark) on tab.
-    # Explicitly setting modified start here prevent this
-    @setModifiedState(false)
-    if @editorElement.component?
-      attachedPromise = Promise.resolve()
-    else
-      attachedPromise = new Promise (resolve) =>
-        disposable = @editorElement.onDidAttach ->
-          disposable.dispose()
-          resolve()
-
-    if isNarrowEditor(@provider.editor)
-      # if narrow is invoked from narrow-editor, open new narrow-editor
-      #  on same pane of existing narrow-editor
-      @provider.getPane().activateItem(@editor)
-    else
-      activatePaneItemInAdjacentPane(@editor, split: settings.get('directionToOpen'))
-
-    attachedPromise.then =>
-      @grammar.activate()
-      if @query
-        @insertQuery(@query)
-      else
-        @withIgnoreChange => @insertQuery()
-      @providerPanel.show()
-      @moveToPrompt()
-      @refresh()
-
   getPane: ->
     paneForItem(@editor)

@@ -294,40 +310,6 @@
     @provider?.destroy?()
     @itemIndicator?.destroy()
     @rowMarker?.destroy()
-
-  updateRealFile: ->
-    return unless @provider.supportDirectEdit
-    return unless @isModified()
-
-    if settings.get('confirmOnUpdateRealFile')
-      unless atom.confirm(message: 'Update real file?', buttons: ['Update', 'Cancel']) is 0
-        return
-
-    return unless @ensureNarrowEditorIsValidState()
-
-    changes = []
-    lines = @editor.buffer.getLines()
-    for line, row in lines when @isNormalItem(item = @items[row])
-      if item._lineHeader?
-        line = line[item._lineHeader.length...] # Strip lineHeader
-      if line isnt item.text
-        changes.push({newText: line, item})
-
-    return unless changes.length
-
-    unless @provider.boundToEditor
-      {success, message} = @ensureNoModifiedFileForChanges(changes)
-      unless success
-        atom.notifications.addWarning(message, dismissable: true)
-        return
-
-    {success, message} = @ensureNoConflictForChanges(changes)
-    unless success
-      atom.notifications.addWarning(message, dismissable: true)
-      return
-
-    @provider.updateRealFile(changes)
-    @setModifiedState(false)

   # This function is mapped from `narrow:close`
   # To differentiate `narrow:close` for protected narrow-editor.
@@ -397,16 +379,6 @@

   previewPreviousItem: ->
     @previewItemForDirection('previous')
-
-  toggleAutoPreview: ->
-    @setAutoPreview(not @autoPreview)
-
-  setAutoPreview: (@autoPreview) ->
-    @updateProviderPanel({@autoPreview})
-    if @autoPreview
-      @preview()
-    else
-      @rowMarker?.destroy()

   getQuery: ->
     @lastQuery = @editor.lineTextForBufferRow(0)
@@ -486,66 +458,6 @@
       itemArea = new Range(@itemAreaStart, @editor.getEofBufferPosition())
       range = @editor.setTextInBufferRange(itemArea, texts.join("\n"), undo: 'skip')
       @editorLastRow = range.end.row
-
-  ensureNarrowEditorIsValidState: ->
-    unless @editorLastRow is @editor.getLastBufferRow()
-      return false
-
-    # Ensure all item have valid line header
-    if @provider.showLineHeader
-      for line, row in @editor.buffer.getLines() when @isNormalItem(item = @items[row])
-        return false unless line.startsWith(item._lineHeader)
-
-    true
-
-  detectConflictForChanges: (changes) ->
-    conflictChanges = {}
-    changesByFilePath =  _.groupBy(changes, ({item}) -> item.filePath)
-    for filePath, changesInFile of changesByFilePath
-      changesByRow = _.groupBy(changesInFile, ({item}) -> item.point.row)
-      for row, changesInRow of changesByRow
-        newTexts = _.pluck(changesInRow, 'newText')
-        if _.uniq(newTexts).length > 1
-          conflictChanges[filePath] ?= []
-          conflictChanges[filePath].push(changesInRow...)
-    conflictChanges
-
-  ensureNoConflictForChanges: (changes) ->
-    message = []
-    conflictChanges = @detectConflictForChanges(changes)
-    success = _.isEmpty(conflictChanges)
-    unless success
-      message.push """
-        Cancelled `update-real-file`.
-        Detected **conflicting change to same line**.
-        """
-      for filePath, changesInFile of conflictChanges
-        message.push("- #{filePath}")
-        for {newText, item} in changesInFile
-          message.push("  - #{item.point.translate([1, 1]).toString()}, #{newText}")
-
-    return {success, message: message.join("\n")}
-
-  getModifiedFilePathsInChanges: (changes) ->
-    _.uniq(changes.map ({item}) -> item.filePath).filter (filePath) ->
-      atom.project.isPathModified(filePath)
-
-  ensureNoModifiedFileForChanges: (changes) ->
-    message = ''
-    modifiedFilePaths = @getModifiedFilePathsInChanges(changes)
-    success = modifiedFilePaths.length is 0
-    unless success
-      modifiedFilePathsAsString = modifiedFilePaths.map((filePath) -> " - `#{filePath}`").join("\n")
-      message = """
-        Cancelled `update-real-file`.
-        You are trying to update file which have **unsaved modification**.
-        But `narrow:#{@provider.getDashName()}` can not detect unsaved change.
-        To use `update-real-file`, you need to save these files.
-
-        #{modifiedFilePathsAsString}
-        """
-
-    return {success, message}

   observeStopChanging: ->
     @editor.onDidStopChanging =>
@@ -580,16 +492,6 @@
       else
         @setModifiedState(true)

-  withIgnoreCursorMove: (fn) ->
-    @ignoreCursorMove = true
-    fn()
-    @ignoreCursorMove = false
-
-  withIgnoreChange: (fn) ->
-    @ignoreChange = true
-    fn()
-    @ignoreChange = false
-
   observeCursorMove: ->
     @editor.onDidChangeCursorPosition (event) =>
       return if @ignoreCursorMove
@@ -776,8 +678,6 @@

   hasNormalItem: ->
     @items.some (item) -> (not item.skip)
-    #
-    # @getNormalItems().length > 0

   hasSomeNormalItemForFilePath: (filePath) ->
     @items.some (item) ->
@@ -879,3 +779,101 @@

   vmpIsEnabled: ->
     @editorElement.classList.contains('vim-mode-plus')
+
+  # Direct-edit related
+  # -------------------------
+  updateRealFile: ->
+    # action = require './ui-action/update-real-file'
+    # new action(this).run()
+    return unless @provider.supportDirectEdit
+    return unless @isModified()
+
+    if settings.get('confirmOnUpdateRealFile')
+      unless atom.confirm(message: 'Update real file?', buttons: ['Update', 'Cancel']) is 0
+        return
+
+    return unless @ensureNarrowEditorIsValidState()
+
+    changes = []
+    lines = @editor.buffer.getLines()
+    for line, row in lines when @isNormalItem(item = @items[row])
+      if item._lineHeader?
+        line = line[item._lineHeader.length...] # Strip lineHeader
+      if line isnt item.text
+        changes.push({newText: line, item})
+
+    return unless changes.length
+
+    unless @provider.boundToEditor
+      {success, message} = @ensureNoModifiedFileForChanges(changes)
+      unless success
+        atom.notifications.addWarning(message, dismissable: true)
+        return
+
+    {success, message} = @ensureNoConflictForChanges(changes)
+    unless success
+      atom.notifications.addWarning(message, dismissable: true)
+      return
+
+    @provider.updateRealFile(changes)
+    @setModifiedState(false)
+
+  ensureNarrowEditorIsValidState: ->
+    unless @editorLastRow is @editor.getLastBufferRow()
+      return false
+
+    # Ensure all item have valid line header
+    if @provider.showLineHeader
+      for line, row in @editor.buffer.getLines() when @isNormalItem(item = @items[row])
+        return false unless line.startsWith(item._lineHeader)
+
+    true
+
+  getModifiedFilePathsInChanges: (changes) ->
+    _.uniq(changes.map ({item}) -> item.filePath).filter (filePath) ->
+      atom.project.isPathModified(filePath)
+
+  ensureNoModifiedFileForChanges: (changes) ->
+    message = ''
+    modifiedFilePaths = @getModifiedFilePathsInChanges(changes)
+    success = modifiedFilePaths.length is 0
+    unless success
+      modifiedFilePathsAsString = modifiedFilePaths.map((filePath) -> " - `#{filePath}`").join("\n")
+      message = """
+        Cancelled `update-real-file`.
+        You are trying to update file which have **unsaved modification**.
+        But `narrow:#{@provider.getDashName()}` can not detect unsaved change.
+        To use `update-real-file`, you need to save these files.
+
+        #{modifiedFilePathsAsString}
+        """
+
+    return {success, message}
+
+  ensureNoConflictForChanges: (changes) ->
+    message = []
+    conflictChanges = @detectConflictForChanges(changes)
+    success = _.isEmpty(conflictChanges)
+    unless success
+      message.push """
+        Cancelled `update-real-file`.
+        Detected **conflicting change to same line**.
+        """
+      for filePath, changesInFile of conflictChanges
+        message.push("- #{filePath}")
+        for {newText, item} in changesInFile
+          message.push("  - #{item.point.translate([1, 1]).toString()}, #{newText}")
+
+    return {success, message: message.join("\n")}
+
+  detectConflictForChanges: (changes) ->
+    conflictChanges = {}
+    changesByFilePath =  _.groupBy(changes, ({item}) -> item.filePath)
+    for filePath, changesInFile of changesByFilePath
+      changesByRow = _.groupBy(changesInFile, ({item}) -> item.point.row)
+      for row, changesInRow of changesByRow
+        newTexts = _.pluck(changesInRow, 'newText')
+        if _.uniq(newTexts).length > 1
+          conflictChanges[filePath] ?= []
+          conflictChanges[filePath].push(changesInRow...)
+    conflictChanges


Re-order. Remove hist def.

Re-order. Remove hist def.

--- ruler.coffee
+++ ruler.coffee
@@ -1,4 +1,39 @@
- # Approx. inverse error function (pdf):
+ # The two plots show a "roof" function, and
+ # the corresponding transformed
+ # distribution. The code, and explanation,
+ # folows below ...
+
+# "roof" function
+fig1 = figure
+    xlabel: "x"
+    ylabel: "y=erfinv(x)"
+    yaxis: {min:-4, max:4}
+    height: 220
+    colors: ["green"]
+    series:
+        shadowSize: 0
+        lines: {lineWidth: 1, show:true}
+        points: {show: false}
+
+
+# histogram
+fig2 = figure
+    xlabel: "y"
+    ylabel: "prob(y)"
+    height: 220
+    colors: ["green", "black"]
+    series:
+        shadowSize: 0
+        lines: {lineWidth: 1, show:true}
+        points: {show: false}
+
+ # The roof function is the inverse error
+ # function (erfinv). That is, the integral
+ # of the Gaussian distribution when we
+ # rotate the screen 90 degrees.
+
+ # Actually, we cannot express errinv simply, so
+ # we use a polynomial approximation (pdf):

 erfinv = (u) -> #;
     c = [1.758, -2.257, 0.1661]
@@ -6,61 +41,26 @@
     t = sqrt( -log(0.5*(1+s*u)) )
     (c[0] + c[1]*t + c[2]*t*t)*s

-# Inverse error function plot
+x = linspace -1, 1, 1000 #;
+y = erfinv x #;
+plot x, y, fig: fig1

-fig1 = figure
-    xlabel: "u"
-    ylabel: "y=erfinv(u)"
-    yaxis: {min:-4, max:4}
-    height: 320
-    colors: ["green"]
-    series:
-        shadowSize: 0
-        lines: {lineWidth: 1, show:true}
-        points: {show: false}
+ # Now consider applying a uniform
+ # distribution to the erfinv roof
+ # function.

-u = linspace -1, 1, 1000 #;
-y = erfinv u #;
-plot u, y, fig: fig1
+X = 2*rand([10000])-1 #;
+Y = erfinv(X) #;

- # uniformly distributed input (-1,1)
+ # Applying the output to a histogram,
+ # we can compare the transformed
+ # uniform distribution with and ideal
+ # Gaussian distribution.

-U = 2*rand([10000])-1 #;
-
- # transform by erfinv
-
-Y = erfinv(U) #;
-
- # estimate dist with histogram
-
-histo = (U, N, l, r) -> #;
-    # (input, Nbins, min, max) ->
-    d = (r-l)/N # bin width
-    bin = [0...N]*d+l
-    count = bin*0
-    I = (floor((u-l)/d) for u in U)
-    count[i]+=1 for i in I
-    {bin:bin , prob:count/U.length/d}
-
-h = histo(erfinv(U), 200, -4, 4) #;
-
- # ideal distribution
-
+h = $blab.histo(Y, 200, -4, 4) #;
 gauss = (u) -> 1/sqrt(2*pi)*exp(-u*u/2) #;
-
-# approximate Gaussian distrution plot
-
-fig2 = figure
-    xlabel: "y"
-    ylabel: "prob(y)"
-    height: 250
-    colors: ["green", "black"]
-    series:
-        shadowSize: 0
-        lines: {lineWidth: 1, show:true}
-        points: {show: false}
-
 plot h.bin, [h.prob, gauss(h.bin)], fig:fig2

-
+ # NB: the histo function is imported
+ # from lib.coffee.


Projects: removed testapp

Projects: removed testapp

--- projects.coffee
+++ projects.coffee
@@ -28,10 +28,10 @@
     script    : "website/js/kdapp.#{KODING_VERSION}.js"
     sourceMapRoot : "Main/"

-  TestApp     :
-    files     : "client/Testapp/includes.coffee"
-    style     : "website/css/testapp.css"
-    script    : "website/js/testapp.js"
+  # TestApp     :
+  #   files     : "client/Testapp/includes.coffee"
+  #   style     : "website/css/testapp.css"
+  #   script    : "website/js/testapp.js"

   HomeIntro   :
     files     : "client/Intro/includes.coffee"


toggle whether or not defaults throws an exception

--- salvus/misc.coffee
+++ salvus/misc.coffee
@@ -130,6 +130,7 @@
 # obj1.  For each property P of obj2 not specified in obj1, the
 # corresponding value obj1[P] is set (all in a new copy of obj1) to
 # be obj2[P].
+DEBUG = false
 exports.defaults = (obj1, obj2, allow_extra) ->
     if not obj1?
         obj1 = {}
@@ -146,7 +147,10 @@
         err = "misc.defaults -- TypeError: function takes inputs as an object #{error()}"
         console.log(err)
         console.trace()
-        throw err
+        if DEBUG
+            throw err
+        else
+            return obj2
     r = {}
     for prop, val of obj2
         if obj1.hasOwnProperty(prop) and obj1[prop]?
@@ -154,14 +158,16 @@
                 err = "misc.defaults -- TypeError: property '#{prop}' must be specified: #{error()}"
                 console.log(err)
                 console.trace()
-                throw err
+                if DEBUG
+                    throw err
             r[prop] = obj1[prop]
         else if obj2[prop]?  # only record not undefined properties
             if obj2[prop] == exports.defaults.required
                 err = "misc.defaults -- TypeError: property '#{prop}' must be specified: #{error()}"
                 console.log(err)
                 console.trace()
-                throw err
+                if DEBUG
+                    throw err
             else
                 r[prop] = obj2[prop]
     if not allow_extra
@@ -170,7 +176,8 @@
                 err = "misc.defaults -- TypeError: got an unexpected argument '#{prop}' #{error()}"
                 console.log(err)
                 console.trace()
-                throw err
+                if DEBUG
+                    throw err
     return r

 # WARNING -- don't accidentally use this as a default:


revert estimate copy change

revert estimate copy change

--- js/inner/build.coffee
+++ js/inner/build.coffee
@@ -384,8 +384,7 @@
       estimated_millis = @previous_successful_build().build_time_millis

       if valid estimated_millis
-        estimated_duration = CI.time.as_estimated_duration(estimated_millis)
-        return " of an estimated #{estimated_duration}"
+        return " / ~" + CI.time.as_estimated_duration(estimated_millis)
     ""

   urlForBuildNum: (num) =>


ipa env: fix start condition

ipa env: fix start condition

--- packages/ipa/env/ipa/start.coffee
+++ packages/ipa/env/ipa/start.coffee
@@ -36,7 +36,7 @@
 .log.md filename: '/tmp/nikita_ipa_lxd_install'
 .lxd.cluster
   header: 'Container'
-  debug: true # params.debug
+  # debug: true # params.debug
   networks:
     lxdbr0public:
       'ipv4.address': '172.16.0.1/24'
@@ -102,13 +102,14 @@
     @lxd.exec
       header: 'Install FreeIPA'
       container: options.container
-      unless_exists: '/etc/ipa/default.conf' # Not enough
-      # unless_exec: 'echo > /dev/tcp/localhost/443'
-      # unless_exec: 'echo admin_pw | kinit admin'
+      code_skipped: 42
+      # Other possibilities to check ipa status:
+      # echo > /dev/tcp/localhost/443
+      # echo admin_pw | kinit admin
       cmd: """
+      [ -f /etc/ipa/default.conf ] && exit 42
       yum install -y freeipa-server
       hostnamectl set-hostname freeipa.nikita.local --static
-      # [ -f /etc/ipa/default.conf ] ||
       #{[
         'ipa-server-install', '-U'
         #  Basic options


Fix bug in generateRpc causing infinite GPN

--- lib/session.coffee
+++ lib/session.coffee
@@ -138,11 +138,11 @@
         overlap = common.pathOverlap(descendantTimestamps[j][0], descendantTimestamps[i][0], path.length)

         if overlap & 1 and descendantTimestamps[j][4]?
-          if descendantRefresh[i] and descendantTimestamps[j][4] > descendantTimestamps[i][2]
+          if descendantRefresh[i] and descendantTimestamps[j][4] >= descendantTimestamps[i][2]
             descendantRefresh[i] = false

         if overlap & 2 and descendantTimestamps[i][4]?
-          if descendantRefresh[j] and descendantTimestamps[i][4] > descendantTimestamps[j][2]
+          if descendantRefresh[j] and descendantTimestamps[i][4] >= descendantTimestamps[j][2]
             descendantRefresh[j] = false

     for d in descendantRefresh when d


Authorization is required for Google Drive

Fixes https://www.pivotaltracker.com/n/projects/1055240/stories/133639103

If the user is either

1. not logged into Drive, or
2. has not authorized the app

we will show the authorization dialog.

[#133639103]

--- src/code/providers/google-drive-provider.coffee
+++ src/code/providers/google-drive-provider.coffee
@@ -206,6 +206,9 @@
   getOpenSavedParams: (metadata) ->
     metadata.providerData.id

+  isAuthorizationRequired: ->
+    true
+
   _loadGAPI: ->
     if not window._LoadingGAPI
       window._LoadingGAPI = true


About/intro command added.

About/intro command added.

--- scripts/common.coffee
+++ scripts/common.coffee
@@ -5,6 +5,7 @@
 #   hubot schedule    — Return the current stream schedule
 #   hubot social      — Links to Twitter and Facebook
 #   hubot commands    - Shows a current list of commands
+#   hubot bot         - A quick bot about/introduction

 module.exports = (robot) ->
     robot.respond /schedule$/i, (msg) ->
@@ -14,4 +15,7 @@
         msg.send "You can find me on Twitter at http://twitter.com/masonest_ and on Facebook at http://facebook.com/masonest"

     robot.respond /commands$/i, (msg) ->
-        msg.send "I currently have a few commands you can use: !schedule, !social and !ctt/!tweet can be used by anyone."
+        msg.send "I currently have a few commands you can use: !schedule, !social, !ctt/!tweet and !bot."
+
+    robot.respond /bot$/i, (msg) ->
+        msg.send "I’m Awebot. I am Masonest’s custom bot and was built by him. I’m still young and learning new things all the time."


support https webservers

support https webservers

--- scripts/config.coffee
+++ scripts/config.coffee
@@ -35,7 +35,7 @@
     diffview: "#{BOWER}/jsdifflib/diffview"

-    mathjax: 'http://cdn.mathjax.org/mathjax/2.0-latest/MathJax.js?config=TeX-MML-AM_HTMLorMML-full&delayStartupUntil=configured'
+    mathjax: '//cdn.mathjax.org/mathjax/2.0-latest/MathJax.js?config=TeX-MML-AM_HTMLorMML-full&delayStartupUntil=configured'

     # ## Template path
     'templates/gh-book': '../templates/gh-book'


Fix memory script to send bytes not kB

Fix memory script to send bytes not kB

--- scripts/memory.coffee
+++ scripts/memory.coffee
@@ -10,12 +10,12 @@
     content = Fs.readFileSync memfile, 'ascii'
     mem = {}
     for line in content.split('\n')
-      mem.total       = parseInt line.split(/\s+/)[1] if line.match /^MemTotal/
-      mem.free        = parseInt line.split(/\s+/)[1] if line.match /^MemFree/
-      mem.buffers     = parseInt line.split(/\s+/)[1] if line.match /^Buffers/
-      mem.cached      = parseInt line.split(/\s+/)[1] if line.match /^Cached/
-      mem.swapTotal   = parseInt line.split(/\s+/)[1] if line.match /^SwapTotal/
-      mem.swapFree    = parseInt line.split(/\s+/)[1] if line.match /^SwapFree/
+      mem.total       = parseInt(line.split(/\s+/)[1]) * 1024 if line.match /^MemTotal/
+      mem.free        = parseInt(line.split(/\s+/)[1]) * 1024 if line.match /^MemFree/
+      mem.buffers     = parseInt(line.split(/\s+/)[1]) * 1024 if line.match /^Buffers/
+      mem.cached      = parseInt(line.split(/\s+/)[1]) * 1024 if line.match /^Cached/
+      mem.swapTotal   = parseInt(line.split(/\s+/)[1]) * 1024 if line.match /^SwapTotal/
+      mem.swapFree    = parseInt(line.split(/\s+/)[1]) * 1024 if line.match /^SwapFree/

     mem.swapUsed = mem.swapTotal - mem.swapFree
     mem.used = mem.total - mem.free


Don't minify python parser.

Don't minify python parser.

--- scripts/minify.coffee
+++ scripts/minify.coffee
@@ -29,7 +29,11 @@
 		fpath = path.join(root, file)
 		dpath = path.join(dest, file)
 		smArgs = []
-
+		if /aether-python\.js/.test fpath
+			console.log "Skipping #{fpath} due to blacklist"
+			fs.copy fpath, dpath, cb2
+			cb2(null)
+
 		if fs.existsSync fpath + '.map'
 			smArgs = [
 				'--in-source-map', fpath + '.map',


Further output checks.

Further output checks.

--- scripts/points.coffee
+++ scripts/points.coffee
@@ -130,10 +130,10 @@
             # Also only returns active users at the moment
             # score = ["#{username} has #{points[username]}" for points in robot.brain.data.points]

-            output1 = robot.brain.get('data')
+            output1 = robot.brain.get('data.points')
             output2 = robot.brain.get('points')
-            output3 = robot.brain.get('points.username')
-            output4 = robot.brain.get('data.points.username')
+            output3 = robot.brain.get('points[username]')
+            output4 = robot.brain.get('people.username')

             points[username] ?= 0


Hears to hear - oops

Hears to hear - oops

--- scripts/trains.coffee
+++ scripts/trains.coffee
@@ -19,7 +19,7 @@
 #  JamieMagee

 module.exports = (robot) ->
-  robot.hears /trains (\w{3})( (to)*(.*))*/i, (msg) ->
+  robot.hear /trains (\w{3})( (to)*(.*))*/i, (msg) ->
     trainFrom = if !!msg.match[4] then msg.match[1].toUpperCase() else process.env.HUBOT_DEFAULT_STATION
     trainTo =  if !!msg.match[4] then msg.match[4].toUpperCase() else msg.match[1].toUpperCase()
     msg.http('http://ojp.nationalrail.co.uk/service/ldb/liveTrainsJson')


Add cache worker settings

Add cache worker settings

--- config/main.autoscale.coffee
+++ config/main.autoscale.coffee
@@ -68,6 +68,11 @@
     numberOfWorkers: 1
     watch       : yes
     queueName   : socialQueueName
+  cacheWorker   :
+    login       : 'prod-social'
+    watch       : yes
+    queueName   : socialQueueName+'cache'
+    run         : yes
   feeder        :
     queueName   : "koding-feeder"
     exchangePrefix: "followable-"


request exercises with excluded as well

request exercises with excluded as well

--- src/components/questions/exercises-display.cjsx
+++ src/components/questions/exercises-display.cjsx
@@ -189,7 +189,7 @@
   render: ->
     return null if ExerciseStore.isLoading() or _.isEmpty(@props.sectionIds)

-    exercises = ExerciseStore.groupBySectionsAndTypes(@props.sectionIds)
+    exercises = ExerciseStore.groupBySectionsAndTypes(@props.sectionIds, withExcluded: true)


Use the new broadcast method

Use the new broadcast method

--- nodejs/palsu.coffee
+++ nodejs/palsu.coffee
@@ -488,10 +488,7 @@
         modelInstance.save()

         # Send the item back to everybody else
-        for clientId, clientObject of socket.clients
-            if clientObject isnt client
-                console.log "Forwarding data to #{clientId}"
-                clientObject.emit "update", data
+        client.broadcast.emit "update", data

     client.on "disconnect", ->
         return unless client.userInstance


Update Atom preferences

Update Atom preferences

--- .atom/config.cson
+++ .atom/config.cson
@@ -1,8 +1,12 @@
 "*":
   core:
     disabledPackages: [
+      "background-tips"
       "exception-reporting"
       "metrics"
+      "package-generator"
+      "styleguide"
+      "update-package-dependencies"
       "welcome"
       "wrap-guide"
     ]
@@ -20,6 +24,8 @@
     showErrorInline: false
     showErrorTabLine: false
     underlineIssues: false
+  "markdown-preview":
+    useGitHubStyle: true
   "tree-view":
     hideIgnoredNames: true
     hideVcsIgnoredFiles: true


Disabling some less often used atom plugins.

--- .atom/config.cson
+++ .atom/config.cson
@@ -61,6 +61,10 @@
       "linter-write-good"
       "markdown-preview"
       "language-gfm"
+      "python-yapf"
+      "atom-isort"
+      "atom-language-avro"
+      "linter-coffeelint"
     ]
     openEmptyEditorOnStart: false
     packagesWithKeymapsDisabled: [


oops, forgot to remove console.log :fired:

--- ext/user_heartbeat.coffee
+++ ext/user_heartbeat.coffee
@@ -33,7 +33,6 @@
           last_seen = 0 unless last_seen
           if Date.now() > last_seen
             deletePresenceKeys(user,client_id)
-            console.log "Offline!? - #{user.name}"
             msg = { status: "offline" }
             broadcastOnClouds(user,msg,user.cloud_ids)


Added yandex webmaster tag

Added yandex webmaster tag


added destroy method

added destroy method

--- maskew.coffee
+++ maskew.coffee
@@ -113,6 +113,13 @@
     @holder.style[css.transform] = transform 0, -angle

     @el.style[css.transform] = transform yOffset, 0 if @options.anchor is 'bottom'
+  destroy: =>
+    parent = @_outerMask.parentNode
+    parent.insertBefore @_el, @_outerMask
+    parent.removeChild @_outerMask
+    $.data @_el, 'maskew', null if $
+    @[k] = null for k of @
+    null

   _onTouchStart: (e) =>


added gokmmens copy mongo to dev-new config

--- config/dev-new.coffee
+++ config/dev-new.coffee
@@ -5,6 +5,7 @@

 version = "0.0.1" #fs.readFileSync nodePath.join(__dirname, '../.revision'), 'utf-8'

+# mongo = 'dev:GnDqQWt7iUQK4M@linus.mongohq.com:10048/koding_dev2_copy?auto_reconnect'
 mongo = 'dev:GnDqQWt7iUQK4M@rose.mongohq.com:10084/koding_dev2?auto_reconnect'
 # mongo = 'koding_stage_user:dkslkds84ddj@web0.beta.system.aws.koding.com:38017/koding_stage?auto_reconnect'


Updated config items to have descriptions

--- lib/atomatigit.coffee
+++ lib/atomatigit.coffee
@@ -3,9 +3,19 @@
 ErrorView = require './views/error-view'

 module.exports =
-  configDefaults:
-    debug: false
-    pre_commit_hook: ''
+  config:
+    debug:
+      title: 'Debug'
+      description: 'Toggle debugging tools'
+      type: 'boolean'
+      default: false
+      order: 1
+    pre_commit_hook:
+      title: 'Pre Commit Hook'
+      description: 'Command to run for the pre commit hook'
+      type: 'string'
+      default: ''
+      order: 2

   repo: null
   repoView: null


delta should work

delta should work

--- lib/characters.coffee
+++ lib/characters.coffee
@@ -104,35 +104,42 @@
       return char

   # Apply a delta and return the new position
+  # @param delta [Object] a delta (see ot-types for more info)
+  # @param position [Integer] the position where to start applying the delta, defaults to 0
+  #
+  # @return [Integer] the position of the cursor after parsing the delta
   delta: (delta, position) ->
     if delta?
+      if delta.attributes?
+        deltaSelection.attributes = delta.attributes
+
       if not position?
         position = 0
       if delta.insert?
         deltaSelection =
-          from: position
-          to: position + delta.insert.length
+          from: @val position
+          to: @val (position + delta.insert.length)
           action: "set"
+
         @insert position, delta.insert
+        state.push deltaSelection
         return position + delta.insert.length
+
       else if delta.delete?
         deltaSelection =
-          from: position
-          to: position + delta.delete
+          from: @val position
+          to: @val (position + delta.delete)
           action: "delete"
+
+        state.push deltaSelection
         @delete position, delta.delete
         return position
+
       else if delta.retain?
         deltaSelection =
-          from: position
-          to: position + delta.retain
+          from: @val position
+          to: @val (position + delta.retain)
           action: "set"

-        return position + delta.retain
-
-      # FIXME: their may be a problem here if selections are updated *after* the words are updated
-      # because the new indexes of the word may have changed
-      # do something in case there's a style
-      if delta.attributes?
-        # create the transformation to apply to the selections
-        deltaSelection.attributes = attributes+        state.push deltaSelection
+        return position+delta.retain


Now the cursor is not moving to the action area whe someone write new codes

--- lib/controller.coffee
+++ lib/controller.coffee
@@ -537,7 +537,7 @@
         @buffer.insert newRange.start, newText
         actionArea = newRange.start

-    @editor.scrollToBufferPosition(actionArea)
+    #@editor.scrollToBufferPosition(actionArea)
     @addMarker(actionArea.toArray()[0], data.colour)

     @triggerPush = true


controller-open-file: set marker, allow either charPos or line+col arguments

--- lib/controller.coffee
+++ lib/controller.coffee
@@ -11,6 +11,7 @@
     @projectRoot = directory.path
     @repls = {}
     @activeRepl = null
+    @markers = []

   start: ->
     @workspaceView.command "supercollider:open-post-window", =>
@@ -66,7 +67,8 @@
           $target = $target.parents('.open-file').eq(0)
           return unless $target.length
         link = $target.attr('open-file')
-        @openFileLink(link)
+        [path, charPos] = link.split(':')
+        @openFile(path, charPos)

       atom.workspace.open(uri, split: 'right', searchAllPanes: true)
         .then () =>
@@ -160,26 +162,40 @@
     if base
       @evalWithRepl("#{base}.openHelpFile")

-  openFileLink: (link)->
-    [uri, pos] = link.split(':')
-    line = 0
-    col = 0
+  openFile: (uri, charPos, row, col)->
     options =
-      initialLine: line
+      initialLine: row
       initialColumn: col
       split: 'left'
       activatePane: false
       searchAllPanes: true

     atom.workspace.open(uri, options)
-      .then (editor)->
+      .then (editor) =>
+
+        setMark = (point) =>
+          editor.setCursorBufferPosition(point)
+          expression = editor.lineForBufferRow(point[0])
+          range = [point, [point[0], expression.length - 1]]
+          marker = editor.markBufferRange(range, invalidate: 'touch')
+          decoration = editor.decorateMarker(marker,
+            type: 'line',
+            class: 'line-error')
+          @markers.push marker
+
+        if row isnt null
+          return setMark([row, col])
+
         text = editor.getText()
         cursor = 0
         li = 0
-        for line in text.split('\n')
-          cursor += (line.length + 1)
-          if cursor > pos
-            editor.setCursorBufferPosition([li, 0])
-            # editor.markBufferRange
-            return
+        for ll in text.split('\n')
+          cursor += (ll.length + 1)
+          if cursor > charPos
+            return setMark([li, cursor - charPos])
           li += 1
+
+  destroyMarkers: () ->
+    @markers.forEach (m) ->
+      m.destroy()
+    @markers = []


refactor test

refactor test

--- spec/BaconSpec.coffee
+++ spec/BaconSpec.coffee
@@ -1703,38 +1703,32 @@
     expect(values).to.deep.equal(["foo"])

 describe "independent observables created while dispatching", ->
-  verifyIndependent = (f, expected) ->
-    values = []
-    Bacon.once(1).onValue ->
-      f().onValue (value) ->
-        values.push(value)
+  verifyWhileDispatching = (name, f, expected) ->
+    it name, ->
+      values = []
+      Bacon.once(1).onValue ->
+        f().onValue (value) ->
+          values.push(value)
+        expect(values).to.deep.equal(expected)
       expect(values).to.deep.equal(expected)
-    expect(values).to.deep.equal(expected)
-  it "with combineAsArray", ->
-    verifyIndependent (-> Bacon.combineAsArray([Bacon.constant(1)])), [[1]]
-  it "with combineAsArray.startWith", ->
-    verifyIndependent (->
+
+  verifyWhileDispatching "with combineAsArray", (-> Bacon.combineAsArray([Bacon.constant(1)])), [[1]]
+  verifyWhileDispatching "with combineAsArray.startWith", (->
       a = Bacon.constant("lolbal")
       Bacon.combineAsArray([a, a]).map("right").startWith("wrong")), ["right"]
-  it "with stream.startWith", ->
-    verifyIndependent (-> Bacon.later(1).startWith(0)), [0]
-  it "with combineAsArray.changes.startWith", ->
-    verifyIndependent (->
+  verifyWhileDispatching "with stream.startWith", (-> Bacon.later(1).startWith(0)), [0]
+  verifyWhileDispatching "with combineAsArray.changes.startWith", (->
       a = Bacon.constant("lolbal")
       Bacon.combineAsArray([a, a]).changes().startWith("right")), ["right"]
-  it "with flatMap", ->
-    verifyIndependent (->
+  verifyWhileDispatching "with flatMap", (->
       a = Bacon.constant("lolbal")
       a.flatMap((x) -> Bacon.once(x))), ["lolbal"]
-  it "with awaiting", ->
-    verifyIndependent (->
+  verifyWhileDispatching "with awaiting", (->
       a = Bacon.constant(1)
       s = a.awaiting(a.map(->))), [true]
-  it "with concat", ->
-    verifyIndependent (->
+  verifyWhileDispatching "with concat", (->
       s = Bacon.once(1).concat(Bacon.once(2))), [1,2]
-  it "with Property.delay", ->
-    verifyIndependent (->
+  verifyWhileDispatching "with Property.delay", (->
       c = Bacon.constant(1)
       Bacon.combineAsArray([c, c]).delay(1).map(".0")), [1]


Add tests for proper errors when reading non-existing inPorts

--- spec/Component.coffee
+++ spec/Component.coffee
@@ -175,6 +175,91 @@
           inPorts:
             '$%^&*a': {}
       chai.expect(shorthand).to.throw()
+  describe 'with non-existing ports', ->
+    getComponent = ->
+      c = new noflo.Component
+        inPorts:
+          in: {}
+        outPorts:
+          out: {}
+    getAddressableComponent = ->
+      c = new noflo.Component
+        inPorts:
+          in:
+            addressable: true
+        outPorts:
+          out:
+            addressable: true
+    it 'should throw an error when checking attached for non-existing port', (done) ->
+      c = getComponent()
+      c.process (input, output) ->
+        try
+          input.attached 'foo'
+        catch e
+          chai.expect(e).to.be.an 'Error'
+          chai.expect(e.message).to.contain 'foo'
+          done()
+          return
+        done new Error 'Expected a throw'
+      sin1 = noflo.internalSocket.createSocket()
+      c.inPorts.in.attach sin1
+      sin1.send 'hello'
+    it 'should throw an error when checking IP for non-existing port', (done) ->
+      c = getComponent()
+      c.process (input, output) ->
+        try
+          input.has 'foo'
+        catch e
+          chai.expect(e).to.be.an 'Error'
+          chai.expect(e.message).to.contain 'foo'
+          done()
+          return
+        done new Error 'Expected a throw'
+      sin1 = noflo.internalSocket.createSocket()
+      c.inPorts.in.attach sin1
+      sin1.send 'hello'
+    it 'should throw an error when checking IP for non-existing addressable port', (done) ->
+      c = getComponent()
+      c.process (input, output) ->
+        try
+          input.has ['foo', 0]
+        catch e
+          chai.expect(e).to.be.an 'Error'
+          chai.expect(e.message).to.contain 'foo'
+          done()
+          return
+        done new Error 'Expected a throw'
+      sin1 = noflo.internalSocket.createSocket()
+      c.inPorts.in.attach sin1
+      sin1.send 'hello'
+    it 'should throw an error when checking data for non-existing port', (done) ->
+      c = getComponent()
+      c.process (input, output) ->
+        try
+          input.hasData 'foo'
+        catch e
+          chai.expect(e).to.be.an 'Error'
+          chai.expect(e.message).to.contain 'foo'
+          done()
+          return
+        done new Error 'Expected a throw'
+      sin1 = noflo.internalSocket.createSocket()
+      c.inPorts.in.attach sin1
+      sin1.send 'hello'
+    it 'should throw an error when checking stream for non-existing port', (done) ->
+      c = getComponent()
+      c.process (input, output) ->
+        try
+          input.hasStream 'foo'
+        catch e
+          chai.expect(e).to.be.an 'Error'
+          chai.expect(e.message).to.contain 'foo'
+          done()
+          return
+        done new Error 'Expected a throw'
+      sin1 = noflo.internalSocket.createSocket()
+      c.inPorts.in.attach sin1
+      sin1.send 'hello'

   describe 'starting a component', ->


Added pending test for previous commit

Added pending test for previous commit

--- spec/ModelSpec.coffee
+++ spec/ModelSpec.coffee
@@ -279,6 +279,8 @@
 			expect(serial.foo).toBe 1
 			expect(serial.bar[0]).toBe 1
 			expect(serial.bar[1]).toBe 2
+
+		it "should clone"

 		describe "persistence", ->
 			m1 = new Model id:1


Assume first available repo if no active pane.

--- lib/git-bridge.coffee
+++ lib/git-bridge.coffee
@@ -69,6 +69,8 @@
     if rootDir?
       rootDirIndex = atom.project.getPaths().indexOf(rootDir)
       repo = atom.project.getRepositories()[rootDirIndex]
+    else
+      repo = atom.project.getRepositories()[0]
     return repo

   @_repoWorkDir: -> @getActiveRepo().getWorkingDirectory()


Add code for ignoreWhitespaceOnlyLines as Issue #13

--- lib/whitespace.coffee
+++ lib/whitespace.coffee
@@ -27,6 +27,7 @@
   removeTrailingWhitespace: (editor, grammarScopeName) ->
     buffer = editor.getBuffer()
     ignoreCurrentLine = atom.config.get('whitespace.ignoreWhitespaceOnCurrentLine')
+    ignoreWhitespaceOnlyLines = atom.config.get('whitespace.ignoreWhitespaceOnlyLines')

     buffer.backwardsScan /[ \t]+$/g, ({lineText, match, replace}) ->
       whitespaceRow = buffer.positionForCharacterIndex(match.index).row
@@ -34,9 +35,11 @@

       return if ignoreCurrentLine and whitespaceRow in cursorRows

+      [whitespace] = match
+      return if ignoreWhitespaceOnlyLines and whitespace is lineText
+
       if grammarScopeName is 'source.gfm'
         # GitHub Flavored Markdown permits two spaces at the end of a line
-        [whitespace] = match
         replace('') unless whitespace is '  ' and whitespace isnt lineText
       else
         replace('')


Updating powershell resource snippet

Updating powershell resource snippet

--- snippets/powershell.cson
+++ snippets/powershell.cson
@@ -1,4 +1,4 @@
 '.source.inspec':
   'Describe powershell':
     'prefix': 'powershell'
-    'body': "# http://inspec.io/docs/reference/resources/powershell/\ndescribe powershell('${1:name}') do\n  it { should${2:_not} ${3:exist} }${4}\nend"
+    'body': "# http://inspec.io/docs/reference/resources/powershell/\ndescribe powershell(\"${1:script}\") do\n  its('stdout') { should${2:_not} eq '${2:value}' }\n  its('stderr') { should{3:_not} eq '${4:value}' }${5}\nend"


Fix empty json

Fix empty json

--- server/testSystems/informatics/InformaticsSubmitDownloader.coffee
+++ server/testSystems/informatics/InformaticsSubmitDownloader.coffee
@@ -152,6 +152,8 @@
         submitsUrl = @baseUrl(page)
         logger.debug "submitsUrl=", submitsUrl
         submits = await @adminUser.download submitsUrl
+        if submits == ""
+            return []
         submits = JSON.parse(submits)["result"]["text"]
         result = await @parseSubmits(submits)
         return result


namespaced unbind

namespaced unbind

--- src/bindable.coffee
+++ src/bindable.coffee
@@ -81,6 +81,7 @@
           eventCallbacks[event].remove(callback)
         else
           eventCallbacks[event] = []
+      else

   ###*
   Calls all listeners attached to the specified event.


FIX: requier was wrong

FIX: requier was wrong

--- test/tests/Art/Engine/Elements/Shapes/rectangle_element.coffee
+++ test/tests/Art/Engine/Elements/Shapes/rectangle_element.coffee
@@ -1,7 +1,7 @@
 Foundation = require 'art-foundation'
 Atomic = require 'art-atomic'
 Engine = require 'art-engine'
-Helper = require '../helper'
+Helper = require '../Helper'

 {drawAndTestElement} = require '../../TestHelper'


Refactoring delete function

Refactoring delete function

--- flow.coffee
+++ flow.coffee
@@ -48,10 +48,10 @@
     @list.find(@options.slidesSelector).eq(position).before(slideHtml).remove()
     @show(slide)

-  delete: (index) ->
-    if index is @slide.index()
+  delete: (position) ->
+    if position is @slide.index()
       @next()
-    @list.find(@options.slidesSelector).eq(index).remove()
+    @list.find(@options.slidesSelector).eq(position).remove()

   _size: ->
     @list.find(@options.slidesSelector).size()


ensure that model type is lower case to match analytics schema

--- src/mobile/components/follow_button/view.coffee
+++ src/mobile/components/follow_button/view.coffee
@@ -52,6 +52,6 @@
         setTimeout (=> @$el.removeClass 'is-clicked'), 1500
     else
       analyticsHooks.trigger 'follow:signup'
-      location.href = "/sign_up?action=follow&objectId=#{@followId}&kind=artist&redirect-to=#{window.location}&signupIntent=follow+#{@type}&intent=follow+#{@type}&trigger=click&contextModule=#{@context_module}"
+      location.href = "/sign_up?action=follow&objectId=#{@followId}&kind=artist&redirect-to=#{window.location}&signupIntent=follow+#{@type.toLowerCase()}&intent=follow+#{@type.toLowerCase()}&trigger=click&contextModule=#{@context_module}"

     false


add template for man page to snippets (#112)

--- snippets/language-asciidoc.cson
+++ snippets/language-asciidoc.cson
@@ -291,3 +291,50 @@
     descriptionMoreURL: 'http://asciidoctor.org/docs/user-manual/#checklist'
     prefix: '-x'
     body: '- [x] $0'
+
+  # Templates
+  #
+  'Man Page':
+    description: 'Man page template'
+    descriptionMoreURL: 'http://asciidoctor.org/docs/user-manual/#man-pages'
+    # See also http://docopt.org/ for how to describe commmand line options.
+    prefix: 'man'
+    body: '''
+      = {manname}(1)
+      ${1:Author Name}
+      v${2:1.0.0}
+      :doctype: manpage
+      :manname: ${3:command}
+      :manmanual: User Commands
+      :mansource: {revnumber}
+
+      == NAME
+
+      {manname} - ${4:description}
+
+      == SYNOPSIS
+
+      *{manname}* ['OPTION']... 'FILE'...
+
+      == OPTIONS
+
+      *-${5:short-option}, --${6:long-option}*::
+        ${7:Description of option.}
+
+      $0
+
+      == EXIT STATUS
+
+      *0*::
+        Success.
+
+      *1*::
+        Failure.
+
+      == COPYRIGHT
+
+      Copyright \\\\(C) {author}.
+
+      This is free software.
+      See the source for copying conditions.
+      '''


Added encodingURI to filepaths.

Added encodingURI to filepaths.

--- src/invalidate_cloudfront.coffee
+++ src/invalidate_cloudfront.coffee
@@ -13,7 +13,7 @@

         done = @async()
         cf = new AWS.CloudFront.Client(new AWS.Config({accessKeyId:options.key, secretAccessKey: options.secret, region:options.region}))
-        filelist = ('/' + grunt.template.process(items.dest) for items in this.files)
+        filelist = ('/' + encodeURI(grunt.template.process(items.dest)) for items in this.files)
         grunt.log.writeflags(filelist, 'Invalidating '+filelist.length+' files')

         # List Current Invalidations


Skip RNG being used if a move can never miss.

--- server/move.coffee
+++ server/move.coffee
@@ -105,6 +105,7 @@
     damage

   willMiss: (battle, user, target) =>
+    return false  if @accuracy == 0
     battle.rng.randInt(1, 100, "miss") > @chanceToHit(battle, user, target)

   chanceToHit: (battle, user, target) =>


Remove unused requires

Remove unused requires

--- src/packages/snippets/spec/snippets-spec.coffee
+++ src/packages/snippets/spec/snippets-spec.coffee
@@ -6,8 +6,6 @@
 Editor = require 'editor'
 _ = require 'underscore'
 fs = require 'fs'
-AtomPackage = require 'atom-package'
-TextMatePackage = require 'text-mate-package'

 describe "Snippets extension", ->
   [buffer, editor] = []


use finally to release the lock

use finally to release the lock

--- src/mailButler.appsscript.coffee
+++ src/mailButler.appsscript.coffee
@@ -326,25 +326,25 @@
     # Get a lock for the current user
     lock = LockService.getPrivateLock()
     if lock.tryLock 10000
-
-      # wait 10 seconds at most
-      Logger.log 'We have the lock...'
-
-      # Get the time this scheduled execution started
-      d = new Date()
-      if e
-        d.setUTCDate e["day-of-month"]
-        d.setUTCFullYear e.year
-        d.setUTCMonth (e.month - 1)
-        d.setUTCHours e.hour
-        d.setUTCMinutes e.minute
-        d.setUTCSeconds e.second
-
-      MailButler.processButlerMails d
-
-    # release our lock
-    lock.releaseLock()
-    Logger.log '...lock released'
+      try
+        # wait 10 seconds at most
+        Logger.log 'We have the lock...'
+
+        # Get the time this scheduled execution started
+        d = new Date()
+        if e
+          d.setUTCDate e["day-of-month"]
+          d.setUTCFullYear e.year
+          d.setUTCMonth (e.month - 1)
+          d.setUTCHours e.hour
+          d.setUTCMinutes e.minute
+          d.setUTCSeconds e.second
+
+        MailButler.processButlerMails d
+      finally
+        # release our lock
+        lock.releaseLock()
+        Logger.log '...lock released'
   else if MailButler.isOutdated()
     # Automatic uninstall
     MailButler.uninstall true


fix javascript

fix javascript

--- vendor/assets/javascripts/tusur_header_observer/init.js.coffee
+++ vendor/assets/javascripts/tusur_header_observer/init.js.coffee
@@ -1,8 +1,12 @@
 @init_tusur_header_observer = ->
+
   first_menu = $('#menu_first')
   second_menu = $('#menu_second')
   arrow = $('.arrow_box', first_menu)
-  site_menu = $('ul.nav.navbar-nav', first_menu)[1]
+  dropdown_menus = $('ul.nav.navbar-nav', first_menu).not('.navbar-right')
+  return true if dropdown_menus.length < 2
+
+  site_menu = dropdown_menus[1]
   site_menu_caret = $('.caret', site_menu)
   site_menu_toggle = $('.dropdown-toggle', site_menu)
   site_menu_toggle.click (e) ->


Notification: corrected fetchRelated signature parameter (fixes post is deleted bug when clicked on a comment notification)

--- workers/social/lib/social/models/messages/comment/index.coffee
+++ workers/social/lib/social/models/messages/comment/index.coffee
@@ -24,7 +24,7 @@
     sharedMethods  :
       static       :
         fetchRelated:
-          (signature ObjectId, Function)
+          (signature String, Function)
         one:
           (signature Object, Function)
       instance     :


WIP - Add permit to opinions

WIP - Add permit to opinions

--- workers/social/lib/social/models/messages/opinion/index.coffee
+++ workers/social/lib/social/models/messages/opinion/index.coffee
@@ -16,7 +16,9 @@

   {log} = console

+  {permit}    = require '../../group/permissionset'
   KodingError = require '../../../error'
+

   @trait __dirname, '../../../traits/grouprelated'

@@ -58,12 +60,13 @@
       kodingErr[prop] = err[prop]
     kodingErr

-  @create = secure (client, data, callback)->
-    codeSnip =
-      title       : data.title
-      body        : data.body
-      meta        : data.meta
-    JPost.create.call @, client, codeSnip, callback
+  @create = permit 'reply to posts',
+    success: (client, data, callback)->
+      codeSnip =
+        title       : data.title
+        body        : data.body
+        meta        : data.meta
+      JPost.create.call @, client, codeSnip, callback

   @fetchRelated = (targetId, callback)->
     {Relationship} = require 'jraphical'


Should be heroku now

Should be heroku now

--- python_congress_finder/node-congress.coffee
+++ python_congress_finder/node-congress.coffee
@@ -1,13 +1,15 @@
 sys = require 'sys'
 exec = require('child_process').exec

-congress = (command, args...) ->
+congress = (command, args..., callback) ->
   running = "python2 ./congress_finder.py #{command}"
   for a in args
     running += " "
     running += a
   exec running, (error, stdout, stderr) ->
     console.log error if error
-    return JSON.parse stdout
+    callback JSON.parse stdout

-congress "getAllReps"
+congress "getAllReps", (object) ->
+  console.log JSON.stringify object
+


preventSyncToProviderEditor while previewing

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -27,6 +27,7 @@
   @uiByNarrowEditor: new Map()
   autoPreview: false
   preventAutoPreview: false
+  preventSyncToProviderEditor: false
   ignoreChangeOnEditor: false
   destroyed: false
   items: []
@@ -300,6 +301,7 @@
       @preview() if @isAutoPreview()

   syncToProviderEditor: ->
+    return if @preventSyncToProviderEditor
     # Detect item
     # - cursor position is equal or greather than that item.
     cursorPosition = @providerEditor.getCursorBufferPosition()
@@ -325,10 +327,12 @@
     editor.decorateMarker(@rowMarker, type: 'line', class: 'narrow-result')

   preview: ->
+    @preventSyncToProviderEditor = true
     @confirm(keepOpen: true).then ({editor, point}) =>
       if editor.isAlive()
         @setRowMarker(editor, point)
         @focus()
+        @preventSyncToProviderEditor = false

   isNormalItem: (item) ->
     item? and not item.skip


Rename _add_mock_method to _install_mock_method

--- src/TinyMockJS.coffee
+++ src/TinyMockJS.coffee
@@ -20,7 +20,7 @@
     @called = false
     @_original_method = undefined
     Expectation._all_expectations.push(@)
-    _add_mock_method(@, object, method_name)
+    _install_mock_method(@, object, method_name)

   #
   # mock (my_mock) ->
@@ -122,7 +122,7 @@
 _save_throws = (expectation, error) ->
   expectation._throws = error

-_add_mock_method = (expectation, object, method_name) ->
+_install_mock_method = (expectation, object, method_name) ->
   if typeof object == 'function'
     expectation._original_method = object.prototype[ method_name ]
     object.prototype[ method_name ] = _build_mocked_method(object, method_name)


Retire unused method _set_return_for_current_expectation().

--- src/TinyMockJS.coffee
+++ src/TinyMockJS.coffee
@@ -238,9 +238,6 @@
 _save_args = (expectation, args) ->
   expectation._args = args

-_set_return_for_current_expectation = (mock, value) ->
-  _current_expectation(mock)._returns = value
-
 _save_returns = (expectation, value) ->
   expectation._returns = value


Fix internal url check

Fix internal url check

Apparently "#" stays so the check fails when location is something like "/home#".

--- resources/assets/coffee/_classes/url.coffee
+++ resources/assets/coffee/_classes/url.coffee
@@ -51,4 +51,4 @@

   @isInternal: (location) ->
-    RegExp("^/(?:#{internal})(?:$|/)").test location.getPath()
+    RegExp("^/(?:#{internal})(?:$|/|#)").test location.getPath()


Make scanInRange iterative instead of recursive

--- src/app/buffer.coffee
+++ src/app/buffer.coffee
@@ -167,7 +167,12 @@
     global = regex.global
     regex = new RegExp(regex.source, 'gm')

-    traverseRecursively = (text, startIndex, endIndex, lengthDelta) =>
+    text = @getText()
+    startIndex = @characterIndexForPosition(range.start)
+    endIndex = @characterIndexForPosition(range.end)
+    lengthDelta = 0
+
+    while true
       regex.lastIndex = startIndex
       return unless match = regex.exec(text)

@@ -186,9 +191,9 @@
       startPosition = @positionForCharacterIndex(matchStartIndex + lengthDelta)
       endPosition = @positionForCharacterIndex(matchEndIndex + lengthDelta)
       range = new Range(startPosition, endPosition)
-      recurse = true
+      keepLooping = true
       replacementText = null
-      stop = -> recurse = false
+      stop = -> keepLooping = false
       replace = (text) -> replacementText = text
       iterator(match, range, { stop, replace })

@@ -200,12 +205,8 @@
         matchStartIndex++
         matchEndIndex++

-      if global and recurse
-        traverseRecursively(text, matchEndIndex, endIndex, lengthDelta)
-
-    startIndex = @characterIndexForPosition(range.start)
-    endIndex = @characterIndexForPosition(range.end)
-    traverseRecursively(@getText(), startIndex, endIndex, 0)
+      break unless global and keepLooping
+      startIndex = matchEndIndex

   backwardsScanInRange: (regex, range, iterator) ->
     global = regex.global
@@ -217,8 +218,8 @@

     matches.reverse()

-    recurse = true
-    stop = -> recurse = false
+    keepLooping = true
+    stop = -> keepLooping = false
     replacementText = null
     replace = (text) -> replacementText = text

@@ -226,7 +227,7 @@
       replacementText = null
       iterator(match, matchRange, { stop, replace })
       @change(matchRange, replacementText) if replacementText
-      return unless global and recurse
+      return unless global and keepLooping

 _.extend(Buffer.prototype, EventEmitter)


Auto-scroll last to avoid forcing a reflow before we're done modifying

--- src/app/editor.coffee
+++ src/app/editor.coffee
@@ -737,11 +737,11 @@

   updateDisplay: (options={}) ->
     return unless @attached
+    @updateRenderedLines()
+    @highlightCursorLine()
     @updateCursorViews()
     @updateSelectionViews()
     @autoscroll(options)
-    @updateRenderedLines()
-    @highlightCursorLine()

   updateCursorViews: ->
     if @newCursors.length > 0


Fixes bug in benchmarks

Fixes bug in benchmarks

Need to investigate further

--- src/app/editor.coffee
+++ src/app/editor.coffee
@@ -231,6 +231,7 @@
   afterAttach: (onDom) ->
     return if @attached or not onDom
     @attached = true
+    @clearLines()
     @subscribeToFontSize()
     @calculateDimensions()
     @setMaxLineLength() if @softWrap


Add editor.selectLine

Add editor.selectLine

--- src/app/editor.coffee
+++ src/app/editor.coffee
@@ -122,6 +122,7 @@
       'editor:select-to-beginning-of-line': @selectToBeginningOfLine
       'editor:select-to-end-of-word': @selectToEndOfWord
       'editor:select-to-beginning-of-word': @selectToBeginningOfWord
+      'editor:select-line': @selectLine
       'editor:transpose': @transpose
       'editor:upper-case': @upperCase
       'editor:lower-case': @lowerCase
@@ -211,6 +212,7 @@
   selectToBeginningOfWord: -> @activeEditSession.selectToBeginningOfWord()
   selectToEndOfWord: -> @activeEditSession.selectToEndOfWord()
   selectWord: -> @activeEditSession.selectWord()
+  selectLine: -> @activeEditSession.selectLine()
   selectToScreenPosition: (position) -> @activeEditSession.selectToScreenPosition(position)
   transpose: -> @activeEditSession.transpose()
   upperCase: -> @activeEditSession.upperCase()


Eliminate another unnecessary call to `highlightCursorLine`

--- src/app/editor.coffee
+++ src/app/editor.coffee
@@ -345,7 +345,6 @@
       else
         @gutter.addClass('drop-shadow')

-    @on 'cursor-move', ({bufferChanged}) => @highlightCursorLine() unless bufferChanged
     @on 'selection-change', => @highlightCursorLine()

   selectOnMousemoveUntilMouseup: ->
@@ -962,6 +961,7 @@

   highlightCursorLine: ->
     return if @mini
+
     @highlightedLine?.removeClass('cursor-line')
     if @getSelection().isSingleScreenLine()
       @highlightedLine = @lineElementForScreenRow(@getCursorScreenRow())


Remove unneeded trailing if

Remove unneeded trailing if

--- src/app/window.coffee
+++ src/app/window.coffee
@@ -34,7 +34,7 @@
     return if location[0] is '#'

     if location.indexOf('https://') is 0 or location.indexOf('http://') is 0
-      require('child_process').spawn('open', [location]) if location
+      require('child_process').spawn('open', [location])
     false

   keymap.bindDefaultKeys()


KDRouter: remove dead code

KDRouter: remove dead code

--- core/KDRouter.coffee
+++ core/KDRouter.coffee
@@ -94,6 +94,7 @@
     @addRoute route, listener  for own route, listener of routes

   handleRoute:(userRoute, options={})->
+
     [frag, query...] = (userRoute ? @getDefaultRoute?() ? '/').split '?'

     query = @utils.parseQuery query.join '&'
@@ -105,8 +106,6 @@

     node = @tree
     params = {}
-
-    isRooted = '/' is frag[0]

     frag = frag.split '/'
     frag.shift() # first edge is garbage like '' or '#!'


fix in case VK not available

fix in case VK not available

--- src/Resources/public/js/article.coffee
+++ src/Resources/public/js/article.coffee
@@ -24,6 +24,8 @@
             (d.head || d.body).appendChild(s)
           )()
         when '#vk-comments'
+          return if not VK
+
           VK.Widgets.Comments(
             "vk-comments",
             { limit: 5, attach: "*", pageUrl: Blog.crate.get('article_url') },


Use request within messager component.

Use request within messager component.

--- src/nodeswork-component/messager.coffee
+++ src/nodeswork-component/messager.coffee
@@ -10,6 +10,11 @@
   constructor: (ctx) ->
     super ctx
     {@user, @applet} = ctx
+    @request = ctx.components.request
+    validator.isRequired @request, meta: {
+      path: 'ctx.components.request'
+      hints: 'Ensure request component is imported.'
+    }

   # Send message to user's message inbox.
   #
@@ -25,8 +30,7 @@

     url = "#{API_PREFIX}/applet-api/#{@applet._id}/users/#{@user._id}/messages"

-    @nodeswork.request {
-      method:            'POST'
+    @request.post {
       url:               url
       body:
         message:         message


Remove selection when added a discussion

--- js-library/app/js/jail_iframe/util/create.coffee
+++ js-library/app/js/jail_iframe/util/create.coffee
@@ -1,5 +1,6 @@
 FactlinkJailRoot.createFactFromSelection = (current_user_opinion) ->
   success = ->
+    window.document.getSelection().removeAllRanges()
     FactlinkJailRoot.createButton.hide()
     FactlinkJailRoot.off 'modalOpened', success


Fix minor bug.

Fix minor bug.


add new badges

add new badges


Database per UID

Database per UID

--- src/server/mongo_interface/index.coffee
+++ src/server/mongo_interface/index.coffee
@@ -7,6 +7,10 @@

 class MongoInterface
     constructor : (dbName, callback) ->
+        # Ensures unique database for every user of the system
+        # but will use the same database for multiple instances
+        # of cloudbrowser run by the same user
+        dbName = "UID#{process.getuid()}-#{dbName}"
         @dbClient = new Mongo.Db(dbName,
             new Mongo.Server("127.0.0.1", 27017, options:{auto_reconnect:true}))
         @dbClient.open (err, pClient) ->


Work around a require issue with atom

Work around a require issue with atom

--- src/coffeelint.coffee
+++ src/coffeelint.coffee
@@ -17,13 +17,18 @@
 if window?
     # If we're in the browser assume CoffeeScript is already loaded.
     CoffeeScript = window.CoffeeScript
-else
-    # By using nodeRequire it prevents browserify from finding this dependency.
-    # If it isn't hidden there is an error attempting to inline CoffeeScript.
-    # if browserify uses `-i` to ignore the dependency it creates an empty shim
-    # which breaks NodeJS
-    # https://github.com/substack/node-browserify/issues/471
-    CoffeeScript = nodeRequire 'coffee-script'
+# By using nodeRequire it prevents browserify from finding this dependency.
+# If it isn't hidden there is an error attempting to inline CoffeeScript.
+# if browserify uses `-i` to ignore the dependency it creates an empty shim
+# which breaks NodeJS
+# https://github.com/substack/node-browserify/issues/471
+#
+# Atom has a `window`, but not a `window.CoffeeScript`. Calling `nodeRequire`
+# here should fix Atom wihtout breaking anything else.
+CoffeeScript ?= nodeRequire 'coffee-script'
+
+unless CoffeeScript?
+    throw new Error('Unable to find CoffeeScript')

 # Browserify will inline the file at compile time.
 packageJSON = require('./../package.json')


Fixing coffeescript global name.

Fixing coffeescript global name.

--- src/coffeelint.coffee
+++ src/coffeelint.coffee
@@ -6,7 +6,7 @@
 ###

-coffeescript = require 'coffee-script'
+CoffeeScript = require 'coffee-script'

 coffeelint = if exports?
@@ -61,7 +61,7 @@
 # Return a list of errors found by performing "lex"
 # checks on the source
 checkTokens = (source, config) ->
-    tokens = coffeescript.tokens(source)
+    tokens = CoffeeScript.tokens(source)
     errors = []
     for token in tokens when not token.generated?
         [type, value, line] = token


implementation clear method

implementation clear method

--- src/collection.coffee
+++ src/collection.coffee
@@ -170,6 +170,11 @@
   all: () ->
     @_array

+  # Clear the collection
+  clear: () ->
+    @_array = []
+
+
   # @return [Numeric] size of collection
   size: () ->
     @_array.length


Change default isolationLevel to READ_COMMITED

--- src/connection.coffee
+++ src/connection.coffee
@@ -176,7 +176,7 @@
     @config.options.cancelTimeout ||= DEFAULT_CANCEL_TIMEOUT
     @config.options.packetSize ||= DEFAULT_PACKET_SIZE
     @config.options.tdsVersion ||= DEFAULT_TDS_VERSION
-    @config.options.isolationLevel ||= ISOLATION_LEVEL.READ_UNCOMMITTED
+    @config.options.isolationLevel ||= ISOLATION_LEVEL.READ_COMMITTED
     @config.options.encrypt ||= false
     @config.options.cryptoCredentialsDetails ||= {}


Fix getValue() bug (must delegate to ACE when active)

--- src/controller.coffee
+++ src/controller.coffee
@@ -2272,7 +2272,7 @@
     catch e
       return success: false, error: e

-  Editor::getValue = -> @tree.stringify()
+  Editor::getValue = -> if @currentlyUsingBlocks then @tree.stringify() else @aceEditor.getValue()

   # PUBLIC EVENT BINDING HOOKS
   # ===============================


Switching back to ‘default’ as initial state name

Signed-off-by: Niels van Hoorn <9639b9eee3fe09ce53e8dfede54f918fb4bd39be@nvh.io>

--- framer/LayerStates.coffee
+++ framer/LayerStates.coffee
@@ -36,9 +36,10 @@

 class exports.LayerStates
 	constructor: (stateMachine) ->
-		readOnlyProperty @, "initial", true, -> stateMachine.initial
 		readOnlyProperty @, "previous", false, -> stateMachine.previous
 		readOnlyProperty @, "current", false, -> stateMachine.current
+		readOnlyProperty @, "default", true, -> @initial
+		readOnlyProperty @, "initial", false, -> stateMachine.initial
 		readOnlyProperty @, "previousName", false, -> stateMachine.previousName
 		readOnlyProperty @, "currentName", false, -> stateMachine.currentName


Switched the level view to grab opponent code from submittedCode instead of code.

--- app/views/play/level_view.coffee
+++ app/views/play/level_view.coffee
@@ -134,7 +134,7 @@
       opponentSpells = opponentSpells.concat spells

     otherSession = @levelLoader.opponentSession
-    opponentCode = otherSession?.get('code') or {}
+    opponentCode = otherSession?.get('submittedCode') or {}
     myCode = @session.get('code') or {}
     for spell in opponentSpells
       c = opponentCode[spell]


easier options filling & switch handling

--- src/gavel2html.coffee
+++ src/gavel2html.coffee
@@ -1,4 +1,5 @@
 Converter = require './converter'
+
 JsonResultConverter = require './json-result-converter'
 TextResultConverter = require './text-result-converter'
 HeadersResultConverter = require './headers-result-converter'
@@ -59,32 +60,25 @@
   #@private
   getConverter: ->
     options = {
-      dataReal: @dataReal
-      dataExpected: @dataExpected
-      gavelResult: @gavelResult
-      usePointers: @usePointers
+      @dataReal
+      @dataExpected
+      @gavelResult
+      @usePointers
     }

     switch @gavelResult?.validator
       when 'TextDiff'
         return new TextResultConverter options
-      when 'JsonSchema'
+
+      when 'JsonSchema', 'JsonExample'
         transformedData = transformJsonData(options.dataReal, options.dataExpected)
         if transformedData
           options.dataReal = transformedData.real
           options.dataExpected = transformedData.expected
           return new JsonResultConverter options
         return new TextResultConverter options
-      when 'JsonExample'
-        transformedData = transformJsonData(options.dataReal, options.dataExpected)
-        if transformedData
-          options.dataReal = transformedData.real
-          options.dataExpected = transformedData.expected
-          return new JsonResultConverter options
-        return new TextResultConverter options
-      when 'HeadersJsonSchema'
-        return new HeadersResultConverter options
-      when 'HeadersJsonExample'
+
+      when 'HeadersJsonSchema', 'HeadersJsonExample'
         return new HeadersResultConverter options

     return new TextResultConverter options


Malformed instances for non-existent classes

--- src/compiler.coffee
+++ src/compiler.coffee
@@ -482,8 +482,11 @@
       instances: []
       declarations: declarations

+  maybeClassNamed: (name) ->
+    @_classNamed name
+
   classNamed: (name) ->
-    (@_classNamed name) or throw new Error "Class #{name} is not defined"
+    (@maybeClassNamed name) or throw new Error "Class #{name} is not defined"

   _classNamed: (name) ->
     for scope in (reverse @scopes)
@@ -707,7 +710,8 @@

   addAuxiliaryDependency: (dependent, dependency) ->
     aux = (lookupInMap @auxiliaries(), dependent)
-    aux.deps = unique join aux.deps, [dependency]
+    if aux # TODO: aux is missing if class is imported
+      aux.deps = unique join aux.deps, [dependency]

   deferredNames: ->
     @_definition().deferredBindings
@@ -2261,20 +2265,25 @@
     [instanceConstraint, defs...] = _validArguments call
     if not instanceConstraint or not isValidTypeConstraint ctx, instanceConstraint
       return malformed ctx, call, 'Requiring the instance type'
-    else
-      instanceType = typeConstraintCompile ctx, instanceConstraint
+
+    className = _symbol _operator instanceConstraint # TODO: do this check in typeConstraintCompile
+    classDefinition = ctx.maybeClassNamed className
+    # TODO: defer if class does not exist
+    if not classDefinition
+      return malformed ctx, (_operator instanceConstraint), 'Class doesn\'t exist'
+    else
+      # TODO: remove when deferring, this is pretty dangerous, we usually don't remove malformed once marked
+      delete (_operator instanceConstraint).malformed
+
+    instanceType = typeConstraintCompile ctx, instanceConstraint
+    # className = instanceType.className
+
     [constraintSeq, wheres...] = defs
     if not isSeq constraintSeq
       wheres = defs
       constraints = []
     else
       constraints = typeConstraintsCompile ctx, _terms constraintSeq
-
-    # TODO: defer if class does not exist
-    className = instanceType.className
-    classDefinition = ctx.classNamed className
-    if not classDefinition
-      return malformed ctx, (_operator instanceConstraint), 'Class doesn\'t exist'

     # TODO: defer if super class instances don't exist yet
     superClassInstances = findSuperClassInstances ctx, instanceType.types, classDefinition
@@ -3438,7 +3447,7 @@
         translation: (jsAccess '_', validSymbol)
     else
       # TODO: access from other module
-      throw "NOT SUPPORTED YET"
+      throw new Error "NOT SUPPORTED YET"

 isQuotedReference = (atom) ->
   atom.builtin or atom.builtInDefinition or atom.modulePath


special handling for long constants

special handling for long constants

--- src/compiler.coffee
+++ src/compiler.coffee
@@ -1,4 +1,5 @@
 _ = require '../third_party/_.js'
+gLong = require '../third_party/gLong.js'
 util = require './util'
 {Method} = require './methods'
 {c2t} = require './types'
@@ -272,6 +273,9 @@
      .replace(/[\n]/g, '\\n')
      .replace(/[\r]/g, '\\r')
      .replace(/[\t]/g, '\\t')
+
+# JS's maximum representable integer
+max_number = gLong.fromNumber(Math.pow(2,53))

 compile_class_handlers =
   PushOpcode: (b) -> b.push @value
@@ -294,7 +298,12 @@
       # runtime, but for now we can assume it is
       b.push "rs.class_lookup(c2t('#{@str_constant.value}')), true)"
     else if @name is 'ldc2_w'
-      b.push2 val
+      if val?.greaterThan?(max_number)
+        b.push2 "gLong.fromBits(#{val.getLowBits()},#{val.getHighBits()})"
+      else if val?.toNumber?
+        b.push2 "gLong.fromNumber(#{val})"
+      else
+        b.push2 val
     else
       b.push val
   ArrayLoadOpcode: (b) ->


Fix display spaces

Fix display spaces

--- views/components/settings/parts/plugin-config.cjsx
+++ views/components/settings/parts/plugin-config.cjsx
@@ -205,13 +205,14 @@
     isUpdateAvailable = updateData.length > 0
     if isfirst && updateData.length > 0
       title = __ 'Plugin update'
-      content = ""
+      outdatedPlugins = []
       for plugin, index in plugins
         if semver.lt(plugin.version, latest[plugin.packageName])
-          for child in plugin.displayName.props.children
+          displayItems = plugin.displayName.props.children || plugin.displayName
+          for child in displayItems
             if typeof child is "string"
-              content = "#{content} #{child}"
-      content = "#{content} #{__ "have newer version. Please update your plugins."}"
+              outdatedPlugins.push child
+      content = "#{outdatedPlugins.join(' ')} #{__ "have newer version. Please update your plugins."}"
       notify content,
         type: 'plugin update'
         title: title


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -9,6 +9,7 @@
       I[name] = I[name].approach(target, approachBy)

   cooldown: (name, options={target: 0, approachBy: 1, value: null}) ->
+
     unless I.cooldowns[name]
       I.cooldowns[name] = options


Add slider to spec index

Add slider to spec index

--- spec/index.cjsx
+++ spec/index.cjsx
@@ -8,6 +8,7 @@
 Dropdown      = require './components/dropdown'
 Form          = require './components/form'
 Progress      = require './components/progress'
+Slider        = require './components/slider'
 Switch        = require './components/switch'

 # React           = require('react/addons')
@@ -34,6 +35,7 @@
       <Dropdown />
       <Form />
       <Progress />
+      <Slider />
     </app>

 React.render <Test/>, document.body


Collaboration: Call "removeParticipantPermissions" method

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -362,6 +362,7 @@

       @statusBar.removeParticipantAvatar nickname
       @unwatchParticipant nickname
+      @removeParticipantPermissions nickname

   participantAdded: (participant) ->


CollaborationController: Handle 'Prepared' case in showChat.

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -767,7 +767,8 @@
   showChat: ->

     switch @stateMachine.state
-      when 'Active' then @showChatPane()
+      when 'Active'     then @showChatPane()
+      when 'Prepared'   then @chat.show()
       when 'NotStarted' then @stateMachine.transition 'Preparing'


Allow return outside function in JS.

Allow return outside function in JS.

Previously "return __" could not be parsed as part of the palette
because a top-level return is not allowed in the ECMAScript 5 spec.
It's a slightly odd rule because it adds context-dependence.
While most JS code looks the same everywhere, top-level code actually
has slightly different syntax - because return is not alllowed.

It's easier for the block editor to avoid this asymmetry and allow
return everywhere, at least from the point of view of parsing
blocks.  It turns out this is an issue that has been seen by other
tools, and that acorn has an option that lets us break this rule.

This change sets the allowReturnOutsideFunction option for acorn.

--- src/javascript.coffee
+++ src/javascript.coffee
@@ -196,6 +196,7 @@
       tree = acorn.parse(@text, {
         locations: true
         line: 0
+        allowReturnOutsideFunction: true
       })

       #console.log 'PROGRAM IS', JSON.stringify tree, null, 2


Add returns in InPort spec

Add returns in InPort spec

--- spec/InPort.coffee
+++ spec/InPort.coffee
@@ -9,12 +9,16 @@
     p = new noflo.InPort
     it 'should be of datatype "all"', ->
       chai.expect(p.getDataType()).to.equal 'all'
+      return
     it 'should not be required', ->
       chai.expect(p.isRequired()).to.equal false
+      return
     it 'should not be addressable', ->
       chai.expect(p.isAddressable()).to.equal false
+      return
     it 'should not be buffered', ->
       chai.expect(p.isBuffered()).to.equal false
+    return
   describe 'with custom type', ->
     p = new noflo.InPort
       datatype: 'string'
@@ -22,19 +26,24 @@
     it 'should retain the type', ->
       chai.expect(p.getDataType()).to.equal 'string'
       chai.expect(p.getSchema()).to.equal 'text/url'
-
+      return
+    return
   describe 'without attached sockets', ->
     p = new noflo.InPort
     it 'should not be attached', ->
       chai.expect(p.isAttached()).to.equal false
       chai.expect(p.listAttached()).to.eql []
+      return
     it 'should allow attaching', ->
       chai.expect(p.canAttach()).to.equal true
+      return
     it 'should not be connected initially', ->
       chai.expect(p.isConnected()).to.equal false
+      return
     it 'should not contain a socket initially', ->
       chai.expect(p.sockets.length).to.equal 0
-
+      return
+    return
   describe 'with processing function called with port as context', ->
     it 'should set context to port itself', (done) ->
       s = new noflo.internalSocket.InternalSocket
@@ -43,9 +52,11 @@
         chai.expect(@).to.equal p
         chai.expect(packet).to.equal 'some-data'
         done()
+        return
       p.attach s
       s.send 'some-data'
-
+      return
+    return
   describe 'with default value', ->
     p = s = null
     beforeEach ->
@@ -53,21 +64,27 @@
         default: 'default-value'
       s = new noflo.internalSocket.InternalSocket
       p.attach s
+      return
     it 'should send the default value as a packet, though on next tick after initialization', (done) ->
       p.on 'data', (data) ->
         chai.expect(data).to.equal 'default-value'
         done()
+        return
       s.send()
+      return
     it 'should send the default value before IIP', (done) ->
       received = ['default-value', 'some-iip']
       p.on 'data', (data) ->
         chai.expect(data).to.equal received.shift()
         done() if received.length is 0
+        return
       setTimeout ->
         s.send()
         s.send 'some-iip'
+        return
       , 0
-
+      return
+    return
   describe 'with options stored in port', ->
     it 'should store all provided options in port, whether we expect it or not', ->
       options =
@@ -79,29 +96,35 @@
       p = new noflo.InPort options
       for name, option of options
         chai.expect(p.options[name]).to.equal option
-
+      return
+    return
   describe 'with data type information', ->
     right = 'all string number int object array'.split ' '
     wrong = 'not valie data types'.split ' '
     f = (datatype) ->
-      new noflo.InPort
+      return new noflo.InPort
         datatype: datatype
     right.forEach (r) ->
       it "should accept a '#{r}' data type", =>
         chai.expect(-> f r).to.not.throw()
+        return
+      return
     wrong.forEach (w) ->
       it "should NOT accept a '#{w}' data type", =>
         chai.expect(-> f w).to.throw()
-
+        return
+      return
+    return
   describe 'with TYPE (i.e. ontology) information', ->
     f = (type) ->
-      new noflo.InPort
+      return new noflo.InPort
         type: type
     it 'should be a URL or MIME', ->
       chai.expect(-> f 'http://schema.org/Person').to.not.throw()
       chai.expect(-> f 'text/javascript').to.not.throw()
       chai.expect(-> f 'neither-a-url-nor-mime').to.throw()
-
+      return
+    return
   describe 'with accepted enumerated values', ->
     it 'should accept certain values', (done) ->
       p = new noflo.InPort
@@ -111,8 +134,10 @@
       p.on 'data', (data) ->
         chai.expect(data).to.equal 'awesome'
         done()
+        return
       s.send 'awesome'

+      return
     it 'should throw an error if value is not accepted', ->
       p = new noflo.InPort
         values: 'noflo is awesome'.split ' '
@@ -121,9 +146,10 @@
       p.on 'data', ->
         # Fail the test, we shouldn't have received anything
         chai.expect(true).to.be.equal false
-
+        return
       chai.expect(-> s.send('terrific')).to.throw
-
+      return
+    return
   describe 'with processing shorthand', ->
     it 'should also accept metadata (i.e. options) when provided', (done) ->
       s = new noflo.internalSocket.InternalSocket
@@ -143,11 +169,13 @@
         return unless ip.type is 'data'
         chai.expect(ip.data).to.equal 'some-data'
         done()
+        return
       ps.inPorts.in.attach s
       chai.expect(ps.inPorts.in.listAttached()).to.eql [0]
       s.send 'some-data'
       s.disconnect()

+      return
     it 'should translate IP objects to legacy events', (done) ->
       s = new noflo.internalSocket.InternalSocket
       expectedEvents = [
@@ -165,16 +193,20 @@
         required: true
       ps.inPorts.in.on 'connect', ->
         receivedEvents.push 'connect'
+        return
       ps.inPorts.in.on 'data', ->
         receivedEvents.push 'data'
+        return
       ps.inPorts.in.on 'disconnect', ->
         receivedEvents.push 'disconnect'
         chai.expect(receivedEvents).to.eql expectedEvents
         done()
+        return
       ps.inPorts.in.attach s
       chai.expect(ps.inPorts.in.listAttached()).to.eql [0]
       s.post new noflo.IP 'data', 'some-data'

+      return
     it 'should stamp an IP object with the port\'s datatype', (done) ->
       p = new noflo.InPort
         datatype: 'string'
@@ -184,7 +216,9 @@
         chai.expect(data.data).to.equal 'Hello'
         chai.expect(data.datatype).to.equal 'string'
         done()
+        return
       p.handleIP new noflo.IP 'data', 'Hello'
+      return
     it 'should keep an IP object\'s datatype as-is if already set', (done) ->
       p = new noflo.InPort
         datatype: 'string'
@@ -194,9 +228,11 @@
         chai.expect(data.data).to.equal 123
         chai.expect(data.datatype).to.equal 'integer'
         done()
+        return
       p.handleIP new noflo.IP 'data', 123,
         datatype: 'integer'

+      return
     it 'should stamp an IP object with the port\'s schema', (done) ->
       p = new noflo.InPort
         datatype: 'string'
@@ -208,7 +244,9 @@
         chai.expect(data.datatype).to.equal 'string'
         chai.expect(data.schema).to.equal 'text/markdown'
         done()
+        return
       p.handleIP new noflo.IP 'data', 'Hello'
+      return
     it 'should keep an IP object\'s schema as-is if already set', (done) ->
       p = new noflo.InPort
         datatype: 'string'
@@ -220,6 +258,10 @@
         chai.expect(data.datatype).to.equal 'string'
         chai.expect(data.schema).to.equal 'text/plain'
         done()
+        return
       p.handleIP new noflo.IP 'data', 'Hello',
         datatype: 'string'
         schema: 'text/plain'
+      return
+    return
+  return


use socket.end for tcp checks

use socket.end for tcp checks

--- app.coffee
+++ app.coffee
@@ -131,17 +131,19 @@
 server = net.createServer (socket) ->
 	socket.on "error", (err)->
 		logger.err err:err, "error with socket on load check"
+		socket.destroy()
+
 	currentLoad = os.loadavg()[0]
 	availableWorkingCpus = os.cpus().length - 1
 	freeLoad = availableWorkingCpus - currentLoad
 	freeLoadPercentage = Math.round((freeLoad / availableWorkingCpus) * 100)
-	if freeLoadPercentage < 0
+	if freeLoadPercentage <= 0
 		freeLoadPercentage = 1 # when its 0 the server is set to drain and will move projects to different servers
-	socket.write "up, #{freeLoadPercentage}%\n", "ASCII"
-	socket.destroy()
+	socket.write("up, #{freeLoadPercentage}%\n", "ASCII")
+	socket.end()

 server.listen port = (Settings.internal?.clsi?.load_port or 3044),  ->
-  console.log "listening on port #{port}"
-  # netcat 127.0.0.1 3044
+  logger.info "tcp load endpoint listening on port #{port}"
+  # telnet 127.0.0.1 3044


hello james

hello james

--- app.coffee
+++ app.coffee
@@ -96,7 +96,7 @@

 app.get '/status', (req, res)->
 	if appIsOk
-		res.send('filestore sharelatex up - v5')
+		res.send('filestore sharelatex up - hello james')
 	else
 		logger.log "app is not ok - shutting down"
 		res.send("server is being shut down", 500)


A connection can only join the game once

--- app.coffee
+++ app.coffee
@@ -98,6 +98,8 @@
     game.setTopic(topic)

   socket.on 'new player', (playerName) ->
+    if socket? and socket.player?
+      return
     console.log("PLAYER " + playerName)
     player = new Player(0, socket, game, playerName)


return 0 for server which is being hammered

socket.destroy when finished

--- app.coffee
+++ app.coffee
@@ -135,10 +135,12 @@
 	availableWorkingCpus = os.cpus().length - 1
 	freeLoad = availableWorkingCpus - fiveMinLoad
 	freeLoadPercentage = Math.round((freeLoad / availableWorkingCpus) * 100)
+	if freeLoadPercentage < 0
+		freeLoadPercentage = 1 # when its 0 the server is set to drain and will move projects about
 	socket.write "up, #{freeLoadPercentage}%\n", "ASCII"
 	socket.on "error", (err)->
 		console.log err, "error with socket"
-	socket.pipe socket
+	socket.destroy()
 	return

 port = 4080


removed app-level server access logging

removed app-level server access logging

--- app.coffee
+++ app.coffee
@@ -27,15 +27,6 @@

 app.set 'views', __dirname + '/views'
 app.set 'view options', layout: false
-
-accessLogStream = fs.createWriteStream './access.log',
-	flags: 'a'
-	encoding: 'utf8'
-	mode: 0o0644
-
-app.use express.logger
-	format: if debug then 'dev' else 'default'
-	stream: accessLogStream

 app.configure 'production', ->
 	app.use (req, res, next) ->
@@ -102,4 +93,3 @@
 	setTimeout((-> process.exit(128+15)), 1000)

 module.exports = app
-


Defensively handle new word from unknown player

--- app.coffee
+++ app.coffee
@@ -88,6 +88,10 @@
   # when the client emits 'new message', this listens and executes
   socket.on 'new word', (word) ->

+    if !socket.player
+      console.error("word " + word + " from unknown player")
+      return
+
     game.addWord(socket.player, word)

     if (game.start)


[fix] remove debug socket event

[fix] remove debug socket event

--- app.coffee
+++ app.coffee
@@ -63,11 +63,6 @@
   res.send 'def'
 ###

-#broadcast the new event
-io.sockets.on('connection', (socket) ->
-  socket.emit('livereport', { report: "connection start" });
-)
-
 server.listen(
   app.get('port')
   () ->


Added better handeling of socket file.

Added better handeling of socket file.

--- app.coffee
+++ app.coffee
@@ -1,3 +1,6 @@
+util = require 'util'
+fs = require 'fs'
+
 require './app/lib/process'
 require './app/lib/pid'
 config = require './app/config/app'
@@ -25,7 +28,13 @@
 io.sockets.on 'connection', (socket) ->
   require('./app/controllers/socket')(socket)

+# delete the socket file if it exists (from a previous crash)
+if config.socket? and fs.existsSync config.socket
+  fs.unlinkSync config.socket
+
 # start server
 server.listen app.settings.port, ->
+  if config.socket? and fs.existsSync config.socket
+    fs.chmodSync config.socket, '777'
   time = new Date - start
-  console.log "Express server started on port/socket #{app.settings.port} (#{env} mode) in #{time}ms"
+  util.log "Express server started on port/socket #{app.settings.port} (#{env} mode) in #{time}ms"


Update status to 'blocked' in tracking identifier

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -1941,6 +1941,8 @@
       # clear all of the cookies of the blocked user
       JSession.remove { username: @username }, callback

+      Tracker.identify @username, { status }
+

   unblock: (callback) ->


Initial JUser::killSessions method

Initial JUser::killSessions method

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -1975,3 +1975,14 @@

       return callback new KodingError 'Captcha not valid. Please try again.'

+
+  ###*
+   * Remove session documents matching selector object.
+   *
+   * @param {Object} [selector={}] - JSession query selector.
+   * @param {Function} - Callback.
+  ###
+  killSessions: (selector = {}, callback) ->
+
+    selector.username = @username
+    JSession.remove selector, callback


JUser:afterLogin: remove code comments related with obsolete code, tidy up

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -579,6 +579,7 @@
       callback null

   afterLogin = (user, clientId, session, callback)->
+
     user.fetchOwnAccount (err, account)->
       if err then return callback err
       checkLoginConstraints user, account, (err)->
@@ -594,25 +595,22 @@
             $unset:
               guestId       : 1
           }, (err)->
+            return callback err  if err
+
+            user.update { $set: lastLoginDate: new Date }, (err) ->
               return callback err  if err
-              user.update { $set: lastLoginDate: new Date }, (err) ->
-                return callback err  if err
-                JAccount.emit "AccountAuthenticated", account
-                # This should be called after login and this
-                # is not correct place to do it, FIXME GG
-                # p.s. we could do that in workers
-                JLog.log { type: "login", username: account.username, success: yes }, ->
-                account.updateCounts()
-                JUser.clearOauthFromSession session, ->
-                  callback null, {account, replacementToken}
-                  # options = targetOptions: selector: tags: $in: ["nosync"]
-                  # account.fetchSubscriptions {}, options, (err, subscriptions) ->
-                  #   console.warn err  if err
-                  #   if subscriptions.length is 0
-                  #     JPaymentSubscription.createFreeSubscription account, (err, subscription) ->
-                  #       console.warn err  if err
-                  #       subscription.debitPack tag: "vm", (err) ->
-                  #         console.warn "VM pack couldn't be debited from subscription: #{err}"  if err
+
+              # This should be called after login and this
+              # is not correct place to do it, FIXME GG
+              # p.s. we could do that in workers
+              account.updateCounts()
+
+              JLog.log
+                type: "login", username: account.username, success: yes
+
+              JUser.clearOauthFromSession session, ->
+                callback null, { account, replacementToken }
+

   @logout = secure (client, callback)->
     if 'string' is typeof client


Fix default sameBillingAddress on credit card

--- src/script/payment-group/credit-card/credit-card-vm.coffee
+++ src/script/payment-group/credit-card/credit-card-vm.coffee
@@ -38,8 +38,7 @@
     @oldFlag = ko.observable()
     @isUsingNewCard = ko.observable()
     @cardSafetyCodeHasFocus = ko.observable()
-    # TODO check if giftRegistryData is same a selected address id
-    @sameBillingAddress = ko.observable(!vtexjs.checkout.orderForm.giftRegistryData?.addressId is vtexjs.checkout.orderForm.shippingData?.address?.addressId)
+    @sameBillingAddress = ko.observable(not (vtexjs.checkout.orderForm.giftRegistryData?.addressId is vtexjs.checkout.orderForm.shippingData?.address?.addressId))
     @isCreditCardCustom = ko.observable(@paymentGroup.isCustom())
     @requiredProperties = [
       "cardNumber"


Using default hint text for select2 institution form

--- app/assets/javascripts/app/registrations/_signup_form.js.coffee
+++ app/assets/javascripts/app/registrations/_signup_form.js.coffee
@@ -29,8 +29,6 @@
   minimumInputLength: 1
   width: 'resolve'
   multiple: false
-  formatSearching: () -> I18n.t('invite_people.users.searching')
-  formatInputTooShort: () -> I18n.t('invite_people.users.hint')
   ajax:
     url: url
     dataType: "json"


add institution to the zip code help text

--- app/assets/javascripts/components/signup/teacher_form.js.coffee
+++ app/assets/javascripts/components/signup/teacher_form.js.coffee
@@ -10,7 +10,7 @@
   'You are adding a new school / institution. Please make sure that the ' +
   "#{zipOrPostal} and school / institution name are correct!"
 zipcodeHelp = (zipOrPostal) ->
-  "Not sure which #{zipOrPostal} to use? Please enter the #{zipOrPostal} of your school's address."
+  "Not sure which #{zipOrPostal} to use? Please enter the #{zipOrPostal} of your school or institution."
 invalidZipcode = (zipOrPostal) ->
   "Incorrect #{zipOrPostal}"


FIX: beforeAnimation and afterAnimation are now before and after

--- src/Moon.coffee
+++ src/Moon.coffee
@@ -87,8 +87,8 @@
                 duration: 0
                 delay: 0
                 easing: "ease"
-                beforeAnimation: undefined
-                afterAnimation: undefined
+                before: undefined
+                after: undefined

             for arg, value of args
                 animationProps[arg] = value
@@ -114,21 +114,21 @@
             anm = this._stack[step]
             if anm?
                 # before animation function
-                anm.beforeAnimation() if typeof anm.beforeAnimation == "function"
+                anm.before() if typeof anm.before == "function"

                 # apply animation for each element
                 for el in this._collection
                     el.style[this.getPrefix("transition")] = "#{anm.duration}ms all #{anm.easing} #{anm.delay}ms"

                     for key, value of anm
-                        if key == "duration" || key == "delay" || key == "easing" || key == "beforeAnimation" || key == "afterAnimation"
+                        if key == "duration" || key == "delay" || key == "easing" || key == "before" || key == "after"
                             continue
                         el.style[this.getPrefix(key)] = value

                 nextTimeout = setTimeout =>
                     # after animation function
-                    anm.afterAnimation() if typeof anm.afterAnimation == "function"
+                    anm.after() if typeof anm.after == "function"

                     # continue chained animations
                     @._play()
@@ -171,7 +171,7 @@
             for el in this._collection
                 el.style[this.getPrefix("transition")] = null
                 for key, prop of this._stack[this._step]
-                    if key == "duration" || key == "delay" || key == "easing" || key == "beforeAnimation" || key == "afterAnimation"
+                    if key == "duration" || key == "delay" || key == "easing" || key == "before" || key == "after"
                         continue
                     el.style[this.getPrefix(key)] = window.getComputedStyle(el, null).getPropertyValue(key)


Require tree

Require tree

--- actioncable/lib/rails/generators/channel/templates/assets/consumer.coffee
+++ actioncable/lib/rails/generators/channel/templates/assets/consumer.coffee
@@ -1,6 +1,6 @@
 #= require action_cable
 #= require_self
-#= require ./channels
+#= require_tree ./channels

 @App ||= {}
 App.cable = ActionCable.createConsumer()


disable recording when not supported

disable recording when not supported

--- app/assets/javascripts/uploadcare/widget/tabs/camera-tab.coffee
+++ app/assets/javascripts/uploadcare/widget/tabs/camera-tab.coffee
@@ -21,11 +21,14 @@
       @container.append(tpl('tab-camera'))
       @container.addClass('uploadcare-dialog-padding uploadcare-dialog-camera-requested')
       @container.find('.uploadcare-dialog-camera-capture').on('click', @__capture)
-      @container.find('.uploadcare-dialog-camera-start-record').on('click', @__startRecording)
+      startRecord = @container.find('.uploadcare-dialog-camera-start-record').on('click', @__startRecording)
       @container.find('.uploadcare-dialog-camera-stop-record').on('click', @__stopRecording)
       @container.find('.uploadcare-dialog-camera-cancel-record').on('click', @__cancelRecording)
       @container.find('.uploadcare-dialog-camera-mirror').on('click', @__mirror)
       @container.find('.uploadcare-dialog-camera-retry').on('click', @__requestCamera)
+
+      if not @MediaRecorder or @settings.imagesOnly
+        startRecord.hide()

       @video = @container.find('video')
       @video.on 'loadeddata', ->


Record full file path

Record full file path

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -364,7 +364,7 @@
         file.previewTemplate = file.previewElement # Backwards compatibility

         @previewsContainer.appendChild file.previewElement
-        node.textContent = file.name for node in file.previewElement.querySelectorAll(
+        node.textContent = file.fullPath for node in file.previewElement.querySelectorAll(
           "[data-dz-name]")
         node.innerHTML = @filesize file.size for node in file.previewElement.querySelectorAll(
           "[data-dz-size]")
@@ -402,7 +402,7 @@
       if file.previewElement
         file.previewElement.classList.remove "dz-file-preview"
         for thumbnailElement in file.previewElement.querySelectorAll("[data-dz-thumbnail]")
-          thumbnailElement.alt = file.name
+          thumbnailElement.alt = file.fullPath
           thumbnailElement.src = dataUrl

         setTimeout (=> file.previewElement.classList.add "dz-image-preview"), 1
@@ -881,8 +881,7 @@
         if entry.isFile
           entry.file (file) =>
             return if @options.ignoreHiddenFiles and file.name.substring(0, 1) is '.'
-            file.fullPath = "#{path}/#{file.name}"
-            @_addFile file
+            @_addFile file, path
         else if entry.isDirectory
           @_addFilesFromDirectory entry, "#{path}/#{entry.name}"
       return
@@ -907,10 +906,13 @@
     else
       @options.accept.call this, file, done

-  _addFile: (file) ->
-    matchingFiles = (f for f in @files when f.name == file.name)
+  _addFile: (file, path=null) ->
+    if !file.fullPath?
+      file.fullPath = if !S.isBlank(path) then "#{path}/#{file.name}" else file.name
+
+    matchingFiles = (f for f in @files when f.fullPath == file.fullPath)
     for matchingFile in matchingFiles
-      @emit "debug", "Removing duplicate file '#{matchingFile.name}'"
+      @emit "debug", "Removing duplicate file '#{matchingFile.fullPath}'"
       @removeFile(matchingFile)

     if file.status != Dropzone.EXISTING
@@ -1207,7 +1209,7 @@
     # Finally add the file
     # Has to be last because some servers (eg: S3) expect the file to be the
     # last parameter
-    formData.append @_getParamName(i), files[i], files[i].name for i in [0..files.length-1]
+    formData.append @_getParamName(i), files[i], files[i].fullPath for i in [0..files.length-1]

     xhr.send formData