Fixed wrong regex

Fixed wrong regex

--- scripts/idobata-talk.coffee
+++ scripts/idobata-talk.coffee
@@ -18,7 +18,7 @@
     msg.send msg.random words

   # respond
-  robot.respond /^嫌い？$/i, (msg) ->
+  robot.respond /嫌い？$/i, (msg) ->
     words = [
       "別に嫌いじゃないわよ"
       "だいっきらい！"
@@ -26,7 +26,7 @@
     ]
     msg.send msg.random words

-  robot.respond /^好き？$/i, (msg) ->
+  robot.respond /好き？$/i, (msg) ->
     words = [
       "べっ別に、あんたのことがすきなんかじゃないからね！"
       "…は？す、好きなんて言ってないじゃない!ち、調子に乗らないでよね、バカ!"
@@ -35,7 +35,7 @@
     ]
     msg.send msg.random words

-  robot.respond /^好き$/i, (msg) ->
+  robot.respond /好き$/i, (msg) ->
     words = [
       "てっ、照れてないもんっ。"
       "べべべべべ、べつに嬉しくなんかないからね！"


Add missing "in" to "Please type in a Text Editor" message

Thanks @meaningofshape for comment https://github.com/Glavin001/atom-preview/issues/41#issuecomment-112099589

--- lib/preview-view.coffee
+++ lib/preview-view.coffee
@@ -223,7 +223,7 @@
       cEditor = @lastEditor
     if not cEditor?
       # cEditor not defined
-      @showError({message:"Please type a Text Editor to render preview"})
+      @showError({message:"Please type in a Text Editor to render preview"})
     else
       # Source Code text
       text = cEditor.getText()


Ignore fixable eslint rules while typing

--- atom/config.cson
+++ atom/config.cson
@@ -59,6 +59,7 @@
   "linter-elm-make": {}
   "linter-eslint":
     fixOnSave: true
+    ignoreFixableRulesWhileTyping: true
   "linter-rubocop":
     disableWhenNoConfigFile: true
   "linter-sass-lint":


Instruct pigments to ignore string scope

--- atom/config.cson
+++ atom/config.cson
@@ -123,6 +123,7 @@
   pigments:
     ignoredScopes: [
       "\\.comment"
+      "\\.string"
       "\\.source\\.gfm"
     ]
     markerType: "dot"


Use bigger :hammer: in 'close-all-panes' command

Prior to this change, the command would close all tabs in each pane, but
it would leave all the panes intact. With this change, we destroy the
panes, getting us back to a clean slate.

--- atom/init.coffee
+++ atom/init.coffee
@@ -34,7 +34,7 @@
 # Hurry!"
 atom.commands.add 'atom-workspace', 'dot-atom:close-all-panes', ->
   atom.workspace.getPanes().forEach (pane) ->
-    pane.destroyItems()
+    pane.destroy()

 # Define commands that should only be available in an editor view (and should
 # not be available in a tree view, for example).


add keymap to atom

add keymap to atom

--- atom/keymap.cson
+++ atom/keymap.cson
@@ -30,6 +30,8 @@
 # If you are unfamiliar with CSON, you can read more about it in the
 # Atom Flight Manual:
 # https://atom.io/docs/latest/using-atom-basic-customization#cson
+'atom-workspace':
+  'cmd-k cmd-u': 'change-case:upper'
 'atom-workspace atom-text-editor:not([mini])':
   'shift-alt-up': 'editor:add-selection-above'
   'shift-alt-down': 'editor:add-selection-below'


Update for Dataporten closing #3580 (#3608)

* Update for Dataporten closing #3580

* Update custom_oauth_server.coffee

* Added support for email-fetching with dataporten

* Added general option for userid instead of id

* Codeprotecting

--- packages/rocketchat-custom-oauth/custom_oauth_server.coffee
+++ packages/rocketchat-custom-oauth/custom_oauth_server.coffee
@@ -122,7 +122,16 @@

 			if identity?.CharacterID and not identity.id
 				identity.id = identity.CharacterID
-
+
+			# Fix Dataporten having 'user.userid' instead of 'id'
+			if identity?.user?.userid and not identity.id
+				identity.id = identity.user.userid
+				identity.email = identity.user.email
+
+			# Fix general 'userid' instead of 'id' from provider
+			if identity?.userid and not identity.id
+				identity.id = identity.userid
+
 			# console.log 'id:', JSON.stringify identity, null, '  '

 			serviceData =
@@ -135,7 +144,7 @@
 				serviceData: serviceData
 				options:
 					profile:
-						name: identity.name or identity.username or identity.nickname or identity.CharacterName
+						name: identity.name or identity.username or identity.nickname or identity.CharacterName or identity.user?.name

 			# console.log data


Fix oembed

Fix oembed

--- packages/rocketchat-lib/server/functions/sendMessage.coffee
+++ packages/rocketchat-lib/server/functions/sendMessage.coffee
@@ -10,7 +10,7 @@

 	message.rid = room._id

-	if not message.parseUrls is false
+	if message.parseUrls isnt false
 		if urls = message.msg.match /([A-Za-z]{3,9}):\/\/([-;:&=\+\$,\w]+@{1})?([-A-Za-z0-9\.]+)+:?(\d+)?((\/[-\+=!:~%\/\.@\,\w]+)?\??([-\+=&!:;%@\/\.\,\w]+)?#?([\w]+)?)?/g
 			message.urls = urls.map (url) -> url: url


Add deprecated shim for atom.services global

--- src/atom.coffee
+++ src/atom.coffee
@@ -42,6 +42,12 @@
       which returns an HTMLElement.
     """

+    serviceHubDeprecationMessage = """
+      atom.services is no longer available. To register service providers and
+      consumers, use the `providedServices` and `consumedServices` fields in
+      your package's package.json.
+    """
+
     Object.defineProperty atom, 'workspaceView',
       get: ->
         deprecate(workspaceViewDeprecationMessage)
@@ -49,6 +55,14 @@
       set: (newValue) ->
         deprecate(workspaceViewDeprecationMessage)
         atom.__workspaceView = newValue
+
+    Object.defineProperty atom, 'services',
+      get: ->
+        deprecate(serviceHubDeprecationMessage)
+        atom.packages.serviceHub
+      set: (newValue) ->
+        deprecate(serviceHubDeprecationMessage)
+        atom.packages.serviceHub = newValue

     atom


Handle both forms of output

Handle both forms of output

Apparently there are at least two different formats for an error message to take, handle both of them.

--- lib/init.coffee
+++ lib/init.coffee
@@ -76,20 +76,33 @@
     parseError = (toParse, textEditor) ->
       ret = []
       re = ///
-        \*\*.*
-        (\((.*)\)         # 1 - Full message + 2 - (TypeOfError)
-        \s(.+))           # 3 - Message
-        [\r\n]{1,2}(?:.+) # Internal elixir code
-        \s+(.+)           # 4 - File
-        :(\d+):           # 5 - Line
+        \*\*[\ ]+
+        \((\w+)\)                  # 1 - (TypeOfError)
+        [\ ](?: # Two message formats.... mode one
+          ([\w\ ]+)                # 2 - Message
+          [\r\n]{1,2}.+[\r\n]{1,2} # Internal elixir code
+          [\ ]+(.+)                # 3 - File
+          :(\d+):                  # 4 - Line
+        |      # Or... mode two
+          (.+)                     # 5 - File
+          :(\d+):                  # 6 - Line
+          [\ ](.+)                 # 7 - Message
+        )
         ///g
       reResult = re.exec(toParse)
       while reResult?
-        ret.push
-          type: "Error"
-          text: reResult[1]
-          filePath: path.join(projectPath(textEditor), reResult[4])
-          range: helpers.rangeFromLineNumber(textEditor, reResult[5] - 1)
+        if (reResult[2]?)
+          ret.push
+            type: "Error"
+            text: '(' + reResult[1] + ') ' + reResult[2]
+            filePath: path.join(projectPath(textEditor), reResult[3])
+            range: helpers.rangeFromLineNumber(textEditor, reResult[4] - 1)
+        else
+          ret.push
+            type: "Error"
+            text: '(' + reResult[1] + ') ' + reResult[7]
+            filePath: path.join(projectPath(textEditor), reResult[5])
+            range: helpers.rangeFromLineNumber(textEditor, reResult[6] - 1)
         reResult = re.exec(toParse)
       ret


Fix incorrect test

Fix incorrect test

--- test/test.cmd.coffee
+++ test/test.cmd.coffee
@@ -24,13 +24,13 @@
 describe "json-concat command", () ->
     this.timeout 0

-    it "exit with an error if only one arg is passed", (done) ->
+    it "exit with an error code 1 if only one arg is passed", (done) ->
         outputFile = path.join process.cwd(), ".test/ignore.json"
         fs.unlinkSync(outputFile) if fs.existsSync(outputFile)
-        ret = shelljs.exec cmd + outputFile, (code, output) ->
+        ret = shelljs.exec cmd + outputFile, (code, output, stderr) ->
             should(code).eql(1)
             should(fs.existsSync(outputFile)).eql(false)
-            should(output).containEql("No need")
+            should(stderr).containEql("No need")
             done()

     it "assumes last arg is the output file", (done) ->


Re-enable gap.

Re-enable gap.

Closes #34.

--- src/draw.coffee
+++ src/draw.coffee
@@ -52,13 +52,15 @@
             c.fillStyle = @color
             c.arc @point..., @defaults.radius * @ratio, 0, 2 * Math.PI
             c.fill()
-            #c.strokeStyle = 'rgba(0,0,0,1.0)'
+            c.strokeStyle = 'rgba(0,0,0,1.0)'
+            old_composite = c.globalCompositeOperation
+            c.globalCompositeOperation = "destination-out"
+            c.lineWidth = 2
+            c.stroke()
+            c.globalCompositeOperation = old_composite
             c.strokeStyle = '#fcf7f5'
-            #old_composite = c.globalCompositeOperation
-            #c.globalCompositeOperation = "destination-out"
             c.lineWidth = 1
             c.stroke()
-            #c.globalCompositeOperation = old_composite
             c.closePath()
         defaults:
             radius: 1000


update pathing

update pathing

--- lib/main.coffee
+++ lib/main.coffee
@@ -10,6 +10,7 @@
     fusker.config.silent = true
     server = fusker.http.createServer port, username, password
     io = fusker.socket.listen server
+    breakf = path.join __dirname, "./break"
     io.sockets.on 'connection', (socket) ->
       socket.cwd ?= process.cwd()
       socket.emit 'cwd', socket.cwd
@@ -17,7 +18,7 @@
       socket.on 'command', (msg) ->
         return socket.broken = true if msg is 'breakout'
         return socket.broken = false if msg is 'breakin'
-        msg = "./break \"#{msg}\"" if socket.broken
+        msg = "#{breakf} \"#{msg}\"" if socket.broken
         exec msg, {cwd: socket.cwd}, (err, stdout, stderr) ->
           if !err and !stderr and msg.indexOf('cd ') is 0
             socket.cwd = path.join socket.cwd, msg.replace 'cd ', ''


:art: refactoring.

:art: refactoring.

--- lib/main.coffee
+++ lib/main.coffee
@@ -56,26 +56,20 @@

   observeMouse: ->
     locationStack = []
-    shouldSaveLocation = (target) ->
-      if (editor = target.getModel?()) and editor.getURI?()
-        true
-      else
-        false
-
     handleCapture = ({target}) ->
-      return unless shouldSaveLocation(target)
+      return unless target.getModel?()?.getURI?()?
       return unless editor = atom.workspace.getActiveTextEditor()
       locationStack.push getLocation('mousedown', editor)

     handleBubble = ({target}) =>
+      return unless target.getModel?()?.getURI?()?
       delay 100, =>
-        return unless shouldSaveLocation(target)
         @checkLocationChange(locationStack.pop()) if locationStack.length

     # Mouse handling is not primal purpose of this package
     # I dont' use mouse basically while coding.
     # So keep codebase minimal and simple,
-    #  I won't use editor::onDidChangeCursorPosition() to track
+    #  I don't use editor::onDidChangeCursorPosition() to track
     #  cursor position change caused by mouse click.

     # When mouse clicked, cursor position is updated by atom core using setCursorScreenPosition()
@@ -99,10 +93,7 @@
       'cursor-history:clear',
     ]
     shouldSaveLocation = (type, target) ->
-      if (':' in type) and (editor = target.getModel?()) and editor.getURI?()
-        true
-      else
-        false
+      (':' in type) and target.getModel?()?.getURI?()?

     locationStack = []
     saveLocation = _.debounce (type, target) ->
@@ -116,8 +107,8 @@
       saveLocation(type, target)

     @subscriptions.add atom.commands.onDidDispatch ({type, target}) =>
+      return if type in ignoreCommands
       return if locationStack.length is 0
-      return if type in ignoreCommands
       return unless shouldSaveLocation(type, target)
       # debug "DidDispatch: #{type}"
       delay 100, =>
@@ -136,7 +127,7 @@
   jump: (direction, withinEditor=false) ->
     return unless editor = atom.workspace.getActiveTextEditor()
     forURI = if withinEditor then editor.getURI() else null
-    needToSave = direction is 'prev' and @history.isIndexAtHead()
+    needToSave = (direction is 'prev') and @history.isIndexAtHead()
     unless entry = @history.get(direction, URI: forURI)
       return


Removed unnecessary space

Removed unnecessary space

--- lib/main.coffee
+++ lib/main.coffee
@@ -133,5 +133,5 @@
     activePane = atom.workspace.getActivePaneItem()

     if isMarkdownPreviewView(activePane)
-      activePane.saveAs(). then ->
+      activePane.saveAs().then ->
         previousActivePane.activate()


:art: Further improve regex

:art: Further improve regex

--- lib/main.coffee
+++ lib/main.coffee
@@ -64,14 +64,14 @@

         return helpers.exec(atom.config.get('linter-flake8.executablePath'), parameters, stdin: fileText).then (result) ->
           toReturn = []
-          regex = /(\d+):(\d+):\s*(.*?) (.*)/g
+          regex = /(\d+):(\d+):\s((E|W|F|C|N)\d{2,3})\s(.*)/g

           while (match = regex.exec(result)) isnt null
             line = parseInt(match[1]) or 0
             col = parseInt(match[2]) or 0
             toReturn.push({
-              type: "Error"
-              text: match[3] + '- ' + match[4]
+              type: match[4] is 'E' then 'Error' else 'Warning'
+              text: match[3] + '- ' + match[5]
               filePath
               range: [[line - 1, col - 1], [line - 1, col]]
             })


Use jQuery's .attr() function instead of .data()

--- tooltips.coffee
+++ tooltips.coffee
@@ -37,12 +37,12 @@
 	if getTooltip().text then hideTooltip() else showTooltip null, $(this)

 positionTooltip = ($el) ->
-	direction = $el.data('tooltip-direction') or 'n'
+	direction = $el.attr('data-tooltip-direction') or 'n'
 	$tooltip = $(".tooltip")

 	position = $el.offset()
-	offLeft = $el.data('tooltip-left')
-	offTop = $el.data('tooltip-top')
+	offLeft = $el.attr('data-tooltip-left')
+	offTop = $el.attr('data-tooltip-top')

 	if _.isUndefined(offLeft)
 		offLeft = 0
@@ -68,17 +68,17 @@

 showTooltip = (evt, $el) ->
 	$el = $el or $(this)
-	viewport = $el.data 'tooltip-disable'
+	viewport = $el.attr 'data-tooltip-disable'

 	if viewport and _.isString(viewport)
 		mq = window.matchMedia(viewport)
 		return false if mq.matches

-	content = if selector = $el.data 'tooltip-element'
+	content = if selector = $el.attr 'data-tooltip-element'
 		$target = $(selector)
 		$target.length and $target.html()
 	else
-		$el.data('tooltip')
+		$el.attr('data-tooltip')

 	setTooltip(content)
 	setPosition(top: 0, left: 0)


plumb up the select to boundary to editor commands

--- src/app/editor.coffee
+++ src/app/editor.coffee
@@ -148,6 +148,8 @@
       'editor:select-to-end-of-word': @selectToEndOfWord
       'editor:select-to-beginning-of-word': @selectToBeginningOfWord
       'editor:select-to-beginning-of-next-word': @selectToBeginningOfNextWord
+      'editor:select-to-next-word-boundary': @selectToNextWordBoundary
+      'editor:select-to-previous-word-boundary': @selectToPreviousWordBoundary
       'editor:add-selection-below': @addSelectionBelow
       'editor:add-selection-above': @addSelectionAbove
       'editor:select-line': @selectLine
@@ -338,6 +340,12 @@
   # {Delegates to: EditSession.selectToEndOfLine}
   selectToEndOfLine: -> @activeEditSession.selectToEndOfLine()

+  # {Delegates to: EditSession.selectToPreviousWordBoundary}
+  selectToPreviousWordBoundary: -> @activeEditSession.selectToPreviousWordBoundary()
+
+  # {Delegates to: EditSession.selectToNextWordBoundary}
+  selectToNextWordBoundary: -> @activeEditSession.selectToNextWordBoundary()
+
   # {Delegates to: EditSession.addSelectionBelow}
   addSelectionBelow: -> @activeEditSession.addSelectionBelow()


Now that we remove binding sets, track binding set indices explicitly

Previously we relied on the length of the binding sets, but now that
we remove them the length is not a strictly increasing number. This
could cause problem when using the index to break specificity ties.

--- src/app/keymap.coffee
+++ src/app/keymap.coffee
@@ -8,6 +8,7 @@
 module.exports =
 class Keymap
   bindingSets: null
+  nextBindingSetIndex: 0
   bindingSetsByFirstKeystroke: null
   queuedKeystrokes: null

@@ -58,7 +59,7 @@
   bindKeys: (args...) ->
     name = args.shift() if args.length > 2
     [selector, bindings] = args
-    bindingSet = new BindingSet(selector, bindings, @bindingSets.length, name)
+    bindingSet = new BindingSet(selector, bindings, @nextBindingSetIndex++, name)
     @bindingSets.unshift(bindingSet)
     for keystrokes of bindingSet.commandsByKeystrokes
       keystroke = keystrokes.split(' ')[0] # only index by first keystroke


Increase FPS

Increase FPS

--- src/game.coffee
+++ src/game.coffee
@@ -55,6 +55,7 @@
     Crafty.init(1024, 576, stage) # PAL+
     #Crafty.pixelart(true)
     Crafty.background('#000000')
+    Crafty.timer.FPS(62.5)

     Crafty.e('Player, Color')
       .attr(name: 'Player 1', z: 0, playerNumber: 1)


update

update

--- options.coffee
+++ options.coffee
@@ -281,6 +281,8 @@
       'u-punct':                'latin'
       'u-grek':                 'latin'
       'u-cdm':                  'latin' # combining diacritical marks
+      'u-cyrl':                 'latin'
+      'u-cyrl-s':               'latin'
       'u-cjk':                  'cn'
       'u-halfull':              'cn'
       'u-dingb':                'cn'


moved getPanelType method

moved getPanelType method

--- oriDomi.coffee
+++ oriDomi.coffee
@@ -396,6 +396,13 @@
         'top'

+  _getPanelType: (anchor) ->
+    if anchor is 'left' or anchor is 'right'
+      @vPanels
+    else
+      @hPanels
+
+
   _getLonghandAnchor: (shorthand) ->
     switch shorthand
       when 'left', 'l', 4


exit bootstrap process early when browser lacks support to avoid unnecessary work

--- oridomi.coffee
+++ oridomi.coffee
@@ -145,87 +145,10 @@
 testEl = document.createElement 'div'

 # Set a list of browser prefixes for testing CSS3 properties.
-prefixList = ['Webkit', 'Moz', 'ms']
-
-# A map of the CSS3 properties needed to support oriDomi, with shorthand names as keys.
-css = new ->
-  @[key] = key for key in [
-    'transform'
-    'transformOrigin'
-    'transformStyle'
-    'transitionProperty'
-    'transitionDuration'
-    'transitionDelay'
-    'transitionTimingFunction'
-    'perspective'
-    'perspectiveOrigin'
-    'backfaceVisibility'
-    'boxSizing'
-  ]
-  @
-
-# Loop through the CSS hash and replace each value with the result of `testProp()`.
-for key, value of css
-  css[key] = testProp value
-  # If the returned value is false, warn the user that the browser doesn't support
-  # oriDomi, set `isSupported` to false and break out of the loop.
-  unless css[key]
-    supportWarning value
-    break
-
-p3d = 'preserve-3d'
-if isSupported and css.transformStyle
-  testEl.style[css.transformStyle] = p3d
-  unless testEl.style[css.transformStyle] is p3d
-    isSupported = false
-    supportWarning p3d
-
-# CSS3 gradients are used for shading.
-# Testing for them is different because they are prefixed values, not properties.
-# This invokes an anonymous function to loop through vendor-prefixed linear-gradients.
-css.gradientProp = do ->
-  for prefix in prefixList
-    hyphenated = "-#{ prefix.toLowerCase() }-linear-gradient"
-    testEl.style.backgroundImage = "#{ hyphenated }(left, #000, #fff)"
-    # After setting a gradient background on the test div, attempt to retrieve it.
-    return hyphenated unless testEl.style.backgroundImage.indexOf('gradient') is -1
-  # If none of the hyphenated values worked, return the un-prefixed version.
-  'linear-gradient'
-
-# The default cursor style is set to `grab` to prompt the user to interact with the element.
-[css.grab, css.grabbing] = do ->
-  for prefix in prefixList
-    plainGrab = 'grab'
-    testEl.style.cursor = (grabValue = "-#{ prefix.toLowerCase() }-#{ plainGrab }")
-    # If the cursor was set correctly, return the prefixed pair.
-    return [grabValue, "-#{ prefix.toLowerCase() }-grabbing"] if testEl.style.cursor is grabValue
-  # Otherwise try the unprefixed version.
-  testEl.style.cursor = plainGrab
-  if testEl.style.cursor is plainGrab
-    [plainGrab, 'grabbing']
-  else
-    # Fallback to `move`.
-    ['move', 'move']
-
-# Invoke a functional scope to set a hyphenated version of the transform property.
-css.transformProp = do ->
-  # Use a regex to pluck the prefix `testProp` found.
-  if prefix = css.transform.match /(\w+)Transform/i
-    "-#{ prefix[1].toLowerCase() }-transform"
-  else
-    'transform'
-
-# Set a `transitionEnd` property based on the browser's prefix for `transitionProperty`.
-css.transitionEnd = do ->
-  switch css.transitionProperty.toLowerCase()
-    when 'transitionproperty'       then 'transitionEnd'
-    when 'webkittransitionproperty' then 'webkitTransitionEnd'
-    when 'moztransitionproperty'    then 'transitionend'
-    when 'mstransitionproperty'     then 'msTransitionEnd'
-
-
-baseName  = 'oridomi'
-elClasses =
+prefixList  = ['Webkit', 'Moz', 'ms']
+styleBuffer = ''
+baseName    = 'oridomi'
+elClasses   =
   active:       'active'
   clone:        'clone'
   holder:       'holder'
@@ -247,91 +170,169 @@
   shaderTop:    'shader-top'
   shaderBottom: 'shader-bottom'

-
 elClasses[k] = "#{ baseName }-#{ v }" for k, v of elClasses
-styleBuffer  = ''
-
-addStyle elClasses.active,
-  backgroundColor: 'transparent'
-  backgroundImage: 'none'
-  padding:         '0'
-  boxSizing:       'border-box'
-  border:          'none'
-  outline:         'none'
-  position:        'relative'
-
-addStyle elClasses.clone, margin: '0'
-
-addStyle elClasses.holder,
-  width:     '100%'
-  height:    '100%'
-  position:  'absolute'
-  transform: 'translateY(-100%)'
-
-addStyle elClasses.stage,
-  width:          '100%'
-  height:         '100%'
-  position:       'absolute'
-  transform:      'translate3d(-9999px, 0, 0)'
-  margin:         '0'
-  padding:        '0'
-  transformStyle: p3d
-
-for k, v of {Left: '0% 50%', Right: '100% 50%', Top: '50% 0%', Bottom: '50% 100%'}
-  addStyle elClasses['stage' + k], perspectiveOrigin: v
-
-addStyle elClasses.shader,
-  width:              '100%'
-  height:             '100%'
-  position:           'absolute'
-  opacity:            '0'
-  top:                '0'
-  left:               '0'
-  pointerEvents:      'none'
-  transitionProperty: 'opacity'
-
-for anchor in anchorList
-  addStyle elClasses['shader' + capitalize anchor], background: getGradient anchor
-
-addStyle elClasses.content,
-  width:     '100%'
-  height:    '100%'
-  margin:    '0'
-  position:  'relative'
-  float:     'none'
-  boxSizing: 'border-box'
-
-addStyle elClasses.mask,
-  width:     '100%'
-  height:    '100%'
-  position:  'absolute'
-  overflow:  'hidden'
-  transform: 'translate3d(0, 0, 0)'
-  backfaceVisibility: 'hidden'
-
-addStyle elClasses.panel,
-  width:              '100%'
-  height:             '100%'
-  padding:            '0'
-  position:           'relative'
-  transitionProperty: css.transformProp
-  transformOrigin:    'left'
-  transformStyle:     p3d
-  backfaceVisibility: 'hidden'
-
-addStyle elClasses.panelH, transformOrigin: 'top'
-addStyle "#{ elClasses.stageRight } .#{ elClasses.panel }", transformOrigin: 'right'
-addStyle "#{ elClasses.stageBottom } .#{ elClasses.panel }", transformOrigin: 'bottom'
-
-styleEl      = document.createElement 'style'
-styleEl.type = 'text/css'
-
-if styleEl.styleSheet
-  styleEl.styleSheet.cssText = styleBuffer
-else
-  styleEl.appendChild document.createTextNode styleBuffer
-
-document.head.appendChild styleEl
+
+# A map of the CSS3 properties needed to support oriDomi, with shorthand names as keys.
+css = new ->
+  @[key] = key for key in [
+    'transform'
+    'transformOrigin'
+    'transformStyle'
+    'transitionProperty'
+    'transitionDuration'
+    'transitionDelay'
+    'transitionTimingFunction'
+    'perspective'
+    'perspectiveOrigin'
+    'backfaceVisibility'
+    'boxSizing'
+  ]
+  @
+
+
+do ->
+  # Loop through the CSS hash and replace each value with the result of `testProp()`.
+  for key, value of css
+    css[key] = testProp value
+    # If the returned value is false, warn the user that the browser doesn't support
+    # oriDomi, set `isSupported` to false and break out of the loop.
+    unless css[key]
+      supportWarning value
+      return
+
+  p3d = 'preserve-3d'
+  if isSupported and css.transformStyle
+    testEl.style[css.transformStyle] = p3d
+    unless testEl.style[css.transformStyle] is p3d
+      isSupported = false
+      supportWarning p3d
+      return
+
+  # CSS3 gradients are used for shading.
+  # Testing for them is different because they are prefixed values, not properties.
+  # This invokes an anonymous function to loop through vendor-prefixed linear-gradients.
+  css.gradientProp = do ->
+    for prefix in prefixList
+      hyphenated = "-#{ prefix.toLowerCase() }-linear-gradient"
+      testEl.style.backgroundImage = "#{ hyphenated }(left, #000, #fff)"
+      # After setting a gradient background on the test div, attempt to retrieve it.
+      return hyphenated unless testEl.style.backgroundImage.indexOf('gradient') is -1
+    # If none of the hyphenated values worked, return the un-prefixed version.
+    'linear-gradient'
+
+  # The default cursor style is set to `grab` to prompt the user to interact with the element.
+  [css.grab, css.grabbing] = do ->
+    for prefix in prefixList
+      plainGrab = 'grab'
+      testEl.style.cursor = (grabValue = "-#{ prefix.toLowerCase() }-#{ plainGrab }")
+      # If the cursor was set correctly, return the prefixed pair.
+      return [grabValue, "-#{ prefix.toLowerCase() }-grabbing"] if testEl.style.cursor is grabValue
+    # Otherwise try the unprefixed version.
+    testEl.style.cursor = plainGrab
+    if testEl.style.cursor is plainGrab
+      [plainGrab, 'grabbing']
+    else
+      # Fallback to `move`.
+      ['move', 'move']
+
+  # Invoke a functional scope to set a hyphenated version of the transform property.
+  css.transformProp = do ->
+    # Use a regular expression to pluck the prefix `testProp` found.
+    if prefix = css.transform.match /(\w+)Transform/i
+      "-#{ prefix[1].toLowerCase() }-transform"
+    else
+      'transform'
+
+  # Set a `transitionEnd` property based on the browser's prefix for `transitionProperty`.
+  css.transitionEnd = do ->
+    switch css.transitionProperty.toLowerCase()
+      when 'transitionproperty'       then 'transitionEnd'
+      when 'webkittransitionproperty' then 'webkitTransitionEnd'
+      when 'moztransitionproperty'    then 'transitionend'
+      when 'mstransitionproperty'     then 'msTransitionEnd'
+
+
+  addStyle elClasses.active,
+    backgroundColor: 'transparent'
+    backgroundImage: 'none'
+    padding:         '0'
+    boxSizing:       'border-box'
+    border:          'none'
+    outline:         'none'
+    position:        'relative'
+
+  addStyle elClasses.clone, margin: '0'
+
+  addStyle elClasses.holder,
+    width:     '100%'
+    height:    '100%'
+    position:  'absolute'
+    transform: 'translateY(-100%)'
+
+  addStyle elClasses.stage,
+    width:          '100%'
+    height:         '100%'
+    position:       'absolute'
+    transform:      'translate3d(-9999px, 0, 0)'
+    margin:         '0'
+    padding:        '0'
+    transformStyle: p3d
+
+  for k, v of {Left: '0% 50%', Right: '100% 50%', Top: '50% 0%', Bottom: '50% 100%'}
+    addStyle elClasses['stage' + k], perspectiveOrigin: v
+
+  addStyle elClasses.shader,
+    width:              '100%'
+    height:             '100%'
+    position:           'absolute'
+    opacity:            '0'
+    top:                '0'
+    left:               '0'
+    pointerEvents:      'none'
+    transitionProperty: 'opacity'
+
+  for anchor in anchorList
+    addStyle elClasses['shader' + capitalize anchor], background: getGradient anchor
+
+  addStyle elClasses.content,
+    width:     '100%'
+    height:    '100%'
+    margin:    '0'
+    position:  'relative'
+    float:     'none'
+    boxSizing: 'border-box'
+
+  addStyle elClasses.mask,
+    width:     '100%'
+    height:    '100%'
+    position:  'absolute'
+    overflow:  'hidden'
+    transform: 'translate3d(0, 0, 0)'
+    backfaceVisibility: 'hidden'
+
+  addStyle elClasses.panel,
+    width:              '100%'
+    height:             '100%'
+    padding:            '0'
+    position:           'relative'
+    transitionProperty: css.transformProp
+    transformOrigin:    'left'
+    transformStyle:     p3d
+    backfaceVisibility: 'hidden'
+
+  addStyle elClasses.panelH, transformOrigin: 'top'
+  addStyle "#{ elClasses.stageRight } .#{ elClasses.panel }", transformOrigin: 'right'
+  addStyle "#{ elClasses.stageBottom } .#{ elClasses.panel }", transformOrigin: 'bottom'
+
+  styleEl      = document.createElement 'style'
+  styleEl.type = 'text/css'
+
+  if styleEl.styleSheet
+    styleEl.styleSheet.cssText = styleBuffer
+  else
+    styleEl.appendChild document.createTextNode styleBuffer
+
+  document.head.appendChild styleEl

 # Defaults


Add sourceType - room

Add sourceType - room

1. Add sourceType - room

--- src/line.coffee
+++ src/line.coffee
@@ -85,11 +85,21 @@

         @robot.router.post @PATH, (req, res) =>
             @robot.logger.debug 'GET LINE MSG'
-            replyToken = req.body.events[0].replyToken;
-            eventType = req.body.events[0].type;
-            text = req.body.events[0].message.text;
-            id = req.body.events[0].message.id;
-            userId = req.body.events[0].source.userId;
+
+            # Event
+            event = events[0];
+            replyToken = event.replyToken;
+            eventType = event.type;
+
+            # Message
+            message = @getMessage event
+            text = message.text
+            id = message.id
+
+            # Source
+            source = @getSource event
+            userId = source.userId or source.roomId
+
             # Validate signature
             headerSignature = req.headers['x-line-signature'];
             isValid = @validateSignature req.body, headerSignature
@@ -97,12 +107,28 @@
                 @robot.logger.debug "Failed validate, result: #{isValid}"
                 res.send 'Auth Failed'
                 return;
+
             # Pass Validate
             # Can't handle other event now, discards them
             if eventType is 'message'
                 @emit 'message', userId, replyToken, text, id

             res.send 'OK'
+
+    # Message_Type = ['text']
+    getMessage: (event)->
+        message =
+            text: event.message.text;
+            id: event.message.id;
+        return message
+
+    # Source_Type = ['room', 'user']
+    getSource: (event)->
+        source =
+            sourceType: event.source.type
+            roomId: event.source.userId
+            userId: event.source.roomId
+        return source

     validateSignature: (content, headerSignature)->
         genSign = @generateSignature content


display current version number in footer

--- src/main.coffee
+++ src/main.coffee
@@ -17,7 +17,6 @@
 sys = require 'sys'
 queryString = require 'query-string'
 package_json = require '../package.json'
-console.log "package_json", package_json

 _.mixin(_.str.exports())
 rx = require 'reactive-coffee'
@@ -42,7 +41,7 @@
   $('#contents').css(height: my.page_dimensions[1]).append(
     render(world) if my.online
   )
-  $('#version').append "1.0"
+  $('#version').append package_json.version

 # Instantiate our main view
 $(main)


Splitting up moloFunc into moloDefine and moloRequire

--- src/molo.coffee
+++ src/molo.coffee
@@ -45,6 +45,17 @@

       firstScriptElem = root.document.getElementsByTagName('script')[0]
       firstScriptElem.parentNode.insertBefore scriptElem, firstScriptElem
+
+  moloDefine = (name, body) ->
+    # If body is a function, add it to the cache directly, else queue it
+    if typeof body is 'function' then return cache[name] = body
+
+  moloRequire = (name, callback) ->
+    if Object.hasOwnProperty.call cache, name then return callback cache[name]
+
+    if Object.hasOwnProperty.call queue, name
+      name
+

   moloFunc = (name, defines) ->
     # TODO: Add module name always to queue and remove it if all dependencies have been met
@@ -53,13 +64,18 @@

     # Skip flag to skip the rest of the function
     skipFunc = false
+
+    defObject = {}

     # If name is not a string, it's an anonymous module (Put defines parameter in the correct order)
     if typeof name isnt 'string'
       # If name is an array, just load dependencies
       if Array.isArray(name)
-        defines =
-          require: name
+        if Object.keys(defines).length > 0
+          defines.require = name
+        else
+          defines =
+            require: name

         skipFunc = true
       else
@@ -194,7 +210,7 @@
   root.module or= moloFunc

   # Compability to official definition (https://github.com/amdjs/amdjs-api/wiki/AMD)
-  root.define = (name, deps, defines) ->
+  root.define = (name, deps, defines) ->
     unless Array.isArray deps
       defines = deps
       deps = []


MQTT: Use a single 'message' event handler

Makes it a bit easier to manage subscriptions correclty

--- src/mqtt.coffee
+++ src/mqtt.coffee
@@ -6,16 +6,24 @@
 class Client
   constructor: (@address) ->
     @client = null
+    @subscribers = {} # queueName -> [handler1, ...]

   ## Broker connection management
   connect: (callback) ->
     @client = mqtt.connect @address
-    @client.once 'connect', (err) ->
-      console.log 'mqtt connected'
-      return callback null
+    onConnected = (err) =>
+      @client.on 'message', (topic, message) =>
+        @_onMessage topic, message
+      return callback err
+    @client.once 'connect', onConnected

   disconnect: (callback) ->
-    @client.end callback
+    @client.removeAllListeners 'message'
+    @client.removeAllListeners 'connect'
+    @subscribers = {}
+    @client.end (err) =>
+      @client = null
+      return callback err

   ## Manipulating queues
   createQueue: (queueName, callback) ->
@@ -37,24 +45,12 @@
     @client.publish queueName, data, published

   subscribeToQueue: (queueName, handler, callback) ->
-    onMessage = (topic, message) =>
-      return if topic != queueName
-      data = null
-      try
-        data = JSON.parse message.toString()
-      catch e
-        console.log 'JSON parse exception:', e
-      out =
-        data: data
-        mqtt: message
-      return handler out if data
-    options = {}
-
     console.log 'mqtt subscribing', queueName
     @client.subscribe queueName, (err) =>
       console.log 'mqtt DONE subscribing', queueName, err
       return callback err if err
-      @client.on 'message', onMessage
+      subs = @subscribers[queueName]
+      if subs then subs.push handler else @subscribers[queueName] = [ handler ]
       return callback null

   ## ACK/NACK messages
@@ -63,5 +59,26 @@
   nackMessage: (message) ->
     return

+  _onMessage: (topic, message) ->
+    console.log 'MQTT message', Object.keys(@subscribers).length > 0, @client != null
+    return if not @client
+    return if not Object.keys(@subscribers).length > 0
+
+    msg = null
+    try
+      msg = JSON.parse message.toString()
+    catch e
+      console.log 'JSON parse exception:', e
+    handlers = @subscribers[topic]
+
+    console.log 'MQTT MES', handlers.length, msg != null
+    return if not msg or not handlers
+    out =
+      data: msg
+      mqtt: message
+    for handler in handlers
+      handler out
+
+
 exports.Client = Client
 exports.MessageBroker = Client


InvitedItemView: Update timeAgoView in the client.

--- client/admin/lib/views/invitations/inviteditemview.coffee
+++ client/admin/lib/views/invitations/inviteditemview.coffee
@@ -59,6 +59,7 @@
       if err
         title  = 'Unable to resend the invitation. Please try again.'

+      @timeAgoView.setData new Date
       return new KDNotificationView { title, duration }


InvitedItemView: Remove back-end logic

InvitedItemView: Remove back-end logic

--- client/admin/lib/views/invitations/inviteditemview.coffee
+++ client/admin/lib/views/invitations/inviteditemview.coffee
@@ -42,13 +42,7 @@

     @revokeButton.showLoader()

-    @getData().remove (err) =>
-      return @destroy()  unless err
-
-      @revokeButton.hideLoader()
-      new KDNotificationView
-        title    : 'Unable to revoke invitation. Please try again.'
-        duration : 5000
+    @getDelegate().emit 'ItemAction', { action : 'RemoveItem', item : this }

   resend: ->
@@ -57,16 +51,7 @@

     @resendButton.showLoader()

-    remote.api.JInvitation.sendInvitationByCode @getData().code, (err) =>
-      @resendButton.hideLoader()
-      title    = 'Invitation is resent.'
-      duration = 5000
-
-      if err
-        title  = 'Unable to resend the invitation. Please try again.'
-
-      @timeAgoView.setData new Date
-      return new KDNotificationView { title, duration }
+    @getDelegate().emit 'ItemAction', { action : 'Resend', item : this }

   isPendingView: ->


fixing homepage

fixing homepage

--- client/app/Applications/Home.kdapplication/AppView.coffee
+++ client/app/Applications/Home.kdapplication/AppView.coffee
@@ -279,6 +279,22 @@
       Websites  :
         count   : "00000"

+    @moreActivity = new KDCustomHTMLView
+      tagName : 'a'
+      partial : "Want to See More Activity"
+      click   : => @notify()
+    @moreTopics = new KDCustomHTMLView
+      tagName : 'a'
+      partial : "View More Topics?"
+      click   : => @notify()
+    @moreMembers = new KDCustomHTMLView
+      tagName : 'a'
+      partial : "View More Members"
+      click   : => @notify()
+
+  notify:->
+    new KDNotificationView
+      title : 'Please login/register!'

   showLoaders:->
     @membersLoader.show()
@@ -293,7 +309,7 @@
       {{> @membersLoader}}
       {{> @members}}

-        View More Members
+        {{> @moreMembers}}

@@ -304,7 +320,7 @@
       {{> @activityLoader}}
       {{> @activity}}

-        Want to See More Activity?
+        {{> @moreActivity}}

@@ -312,7 +328,7 @@
       {{> @topicsLoader}}
       {{> @topics}}

-        View More Topics?
+        {{> @moreTopics}}

     """
@@ -396,15 +412,15 @@

   followerText:(count)->
     if count < 2
-      "#{count} Follower"
+      "#{count} Post"
     else
-      "#{count} Followers"
+      "#{count} Posts"

   pistachio:->
     """
     {span.fl.ttag{ #(title)}}

-       {{ @followerText #(counts.followers)}}
+       {{ @followerText #(counts.tagged) or 0}}

     """

@@ -422,13 +438,27 @@
     options.type = "activity"
     super options,data

-    @user = new ProfileTextView
-      tagName           : "strong"
-      origin            :
-        id              : data.originId or data.anchor.id
-        constructorName : data.originType or data.anchor.constructorName
+
+    if data.anchor?.constructorName is "JTag"
+      @user = new ProfileTextView
+        tagName           : "strong"
+        origin            :
+          id              : data.group[0].id
+          constructorName : data.group[0].constructorName
+        click             : -> no
+    else
+      @user = new ProfileTextView
+        tagName           : "strong"
+        origin            :
+          id              : data.originId or data.anchor.id
+          constructorName : data.originType or data.anchor.constructorName
+

   getActivityPhrase:(constructorName)->
+    data = @getData()
+    if data.anchor?.constructorName is "JTag"
+      return "has followed a topic."
+
     switch constructorName
       when "JStatusUpdate"    then "posted a status update."
       when "JCodeSnip"        then "shared some code."


removed populatePendingGroups method from avatarpopupgroupswitcher class

--- client/app/lib/avatararea/avatarpopupgroupswitcher.coffee
+++ client/app/lib/avatararea/avatarpopupgroupswitcher.coffee
@@ -40,11 +40,6 @@
     @_popupListPending = new PopupList
       itemClass  : PopupGroupListItemPending

-    # does not work yet
-    # @_popupListPending.on 'PendingCountDecreased', @bound 'decreasePendingCount'
-    # @_popupListPending.on 'UpdateGroupList',       @bound 'populateGroups'
-    # whoami().on        'NewPendingInvitation',  @bound 'populatePendingGroups'
-
     @listControllerPending = new KDListViewController
       lazyLoaderOptions   :
         partial           : ''
@@ -259,22 +254,6 @@
         router.handleRoute '/Logout'
         @hide()

-  populatePendingGroups:->
-    @listControllerPending.removeAllItems()
-    @listControllerPending.hideLazyLoader()
-
-    return  unless isLoggedIn()
-
-    whoami().fetchGroupsWithPendingInvitations (err, groups)=>
-      if err then kd.warn err
-      else if groups?
-        @pending = 0
-        for group in groups when group
-          @listControllerPending.addItem {group, roles:[], admin:no}
-          @pending++
-        @updatePendingCount()
-        @notPopulatedPending = no
-

   populateGroups:->
     return  if not isLoggedIn() or @isLoading
@@ -327,7 +306,6 @@
     # Commenting out these lines because of
     # removal of the groups links from avatar popup. ~Umut
     # @populateGroups() if @notPopulated
-    # @populatePendingGroups() if @notPopulatedPending

   hide:->
     super


flux: stacks to use a helper for both in init and update

--- client/app/lib/flux/environment/stores/stacksstore.coffee
+++ client/app/lib/flux/environment/stores/stacksstore.coffee
@@ -7,7 +7,16 @@

   @getterPath = 'StacksStore'

+  _convertMachinesToIds = (jStack) ->
+    stack      = toImmutable jStack
+    machines   = stack.get('machines').toJS()
+    machineIds = machines.map (m) -> m._id
+    stack      = stack.set 'machines', machineIds
+    return stack
+
+
   getInitialState: -> immutable.Map()
+

   initialize: ->

@@ -18,12 +27,10 @@
   load: (stacks, jstacks) ->

     stacks.withMutations (stacks) ->
-      jstacks.forEach (stack) ->
-        stack.machines = stack.machines.map (m) -> m._id
-        stacks.set stack._id, toImmutable stack
+      jstacks.forEach _convertMachinesToIds

   updateStack: (stacks, stack) ->

     stacks.withMutations (stacks) ->
-      stacks.set stack._id, toImmutable stack+      stacks.set stack._id, _convertMachinesToIds stack


opentok: sendSignal accepts a SocialChannel now

--- client/app/lib/videocollaboration/services/opentok.coffee
+++ client/app/lib/videocollaboration/services/opentok.coffee
@@ -125,14 +125,14 @@
    * Sends a signal with type to given subscriber
    * through given session.
    *
-   * @param {OT.Session} session
+   * @param {SocialChannel} channel
    * @param {String} type
-   * @param {Object=} data
+   * @param {function(err: object)} callback
   ###
-  sendSignal: (session, type, data = {})->
+  sendSignal: (channel, type, callback) ->

-    signalData = { type, data: JSON.stringify data }
-    session.signal signalData, helper._errorSignal
+    @fetchChannelSession channel, (session) =>
+      session.signal { type }, callback

   ###*


Fix spacewarp viewer to wait for FITS library to load

--- src/ubret/spacewarp_viewer/initialize.coffee
+++ src/ubret/spacewarp_viewer/initialize.coffee
@@ -1,8 +1,3 @@
-
-#
-# Define collection and model used in viewer
-#
-
 # Collection of layers where each layer corresponds to a FITS image
 class Layers extends Backbone.Collection
   hasExtent: false
@@ -71,12 +66,14 @@

     # Set various deferred objects to handle asynchronous requests.
     @dfs =
-      webfits: new $.Deferred()
       u: new $.Deferred()
       g: new $.Deferred()
       r: new $.Deferred()
       i: new $.Deferred()
       z: new $.Deferred()
+
+
+    @dfWebfits = new $.Deferred()

     @getApi()

@@ -94,7 +91,7 @@
     lib = if context? then 'gl' else 'canvas'
     url = "javascripts/webfits-#{lib}.js"
     $.getScript(url, =>
-      @dfs.webfits.resolve()
+      @dfWebfits.resolve()
     )

   getNextSubject: =>
@@ -102,52 +99,53 @@

   # Request FITS files for each channel
   requestChannels: ->
-    @initWebFITS()
-
-    # Set various deferred objects to handle asynchronous requests.
-    @dfs =
-      u: new $.Deferred()
-      g: new $.Deferred()
-      r: new $.Deferred()
-      i: new $.Deferred()
-      z: new $.Deferred()
+    @dfWebfits.then =>
+      @initWebFITS()
+
+      # Set various deferred objects to handle asynchronous requests.
+      @dfs =
+        u: new $.Deferred()
+        g: new $.Deferred()
+        r: new $.Deferred()
+        i: new $.Deferred()
+        z: new $.Deferred()

-    subject = @currentPageData()[0]
-    prefix  = subject.metadata.id
-
-    # Set callback for when all channels and WebFITS Api received
-    $.when.apply(null, _.values(@dfs))
-      .done(@allChannelsReceived)
-
-    for band, index in @bands
-      do (band, index) =>
-        path = "#{@source}#{prefix}_#{band}.fits.fz"
+      subject = @currentPageData()[0]
+      prefix  = subject.metadata.id
+
+      # Set callback for when all channels and WebFITS Api received
+      $.when.apply(null, _.values(@dfs))
+        .done(@allChannelsReceived)
+
+      for band, index in @bands
+        do (band, index) =>
+          path = "#{@source}#{prefix}_#{band}.fits.fz"

-        new astro.FITS.File(path, (fits) =>
-          hdu = fits.getHDU()
-          header = hdu.header
-          dataunit = hdu.data
+          new astro.FITS.File(path, (fits) =>
+            hdu = fits.getHDU()
+            header = hdu.header
+            dataunit = hdu.data

-          # Get image data
-          dataunit.getFrameAsync(0, (arr) =>
+            # Get image data
+            dataunit.getFrameAsync(0, (arr) =>

-            # Compute extent
-            [min, max] = dataunit.getExtent(arr)
-
-            # Initialize a model and push to collection
-            layer = new Layer({band: band, fits: fits, minimum: min, maximum: max})
-            @collection.add(layer)
-
-            # Load texture
-            @wfits.loadImage(band, arr, dataunit.width, dataunit.height)
-            @dfs[band].resolve()
+              # Compute extent
+              [min, max] = dataunit.getExtent(arr)
+
+              # Initialize a model and push to collection
+              layer = new Layer({band: band, fits: fits, minimum: min, maximum: max})
+              @collection.add(layer)
+
+              # Load texture
+              @wfits.loadImage(band, arr, dataunit.width, dataunit.height)
+              @dfs[band].resolve()
+            )
           )
-        )

   # Initialize a WebFITS object
   initWebFITS: =>
     @wfits?.teardown()
-
+
     @wfits = new astro.WebFITS(@el, @dimension)

     unless @wfits.ctx?


Code refactoring

Code refactoring

--- client/admin/lib/views/members/teammemberscommonview.coffee
+++ client/admin/lib/views/members/teammemberscommonview.coffee
@@ -195,7 +195,7 @@

   handleSearchResult: (accounts) ->

-    usernames = (profile.nickname for { profile: profile } in accounts)
+    usernames = (profile.nickname for { profile } in accounts)

     # Send a request to back-end for user emails.
     remote.api.JAccount.fetchEmailsByUsername usernames, (err, emails) =>


Nicer fading of voting popover, plus hoverintent

--- core/app/backbone/views/evidence/vote_up_down_view.coffee
+++ core/app/backbone/views/evidence/vote_up_down_view.coffee
@@ -17,6 +17,7 @@

   onRender: ->
     @_updateButtons()
+    @_setHoverIntent()

   _updateButtons: ->
     @ui.upButton.toggleClass 'active', @model.get('current_user_opinion') == 'believes'
@@ -49,7 +50,7 @@
     @popoverAdd selector,
       side: @_side()
       align: 'top'
-      fadeTime: 40
+      fadeTime: 200
       contentView: new view_klass model: @model

   _side: ->
@@ -57,3 +58,11 @@
       'left'
     else
       'right'
+
+  _setHoverIntent: ->
+    @$el.hoverIntent
+      timeout: 500
+      over: ->
+      out: =>
+        @popoverRemove '.js-up'
+        @popoverRemove '.js-down'


Add fadeTime to popover

Add fadeTime to popover

--- core/app/backbone/views/evidence/vote_up_down_view.coffee
+++ core/app/backbone/views/evidence/vote_up_down_view.coffee
@@ -64,6 +64,7 @@
   default_popover_options: ->
     side: 'right'
     align: 'top'
+    fadeTime: 100

   open_vote_up_popup: ->
     return if @_up_is_opened


Removed useless class

Removed useless class

--- core/app/backbone/views/facts/react_fact_vote_area.coffee
+++ core/app/backbone/views/facts/react_fact_vote_area.coffee
@@ -1,7 +1,7 @@
 ReactOpinionatorsAvatar = React.createBackboneClass
   render: ->
     _span ['opinionators-avatar'],
-      _a ["popover-link js-user-link opinionators-avatar-link"
+      _a ["popover-link opinionators-avatar-link"
           href: @model().get('username')
           rel:"backbone"],
         _img ["image-24px", "opinionators-avatar-image",


Do not rely on VM metrics for memory when not running.

--- src/spec.coffee
+++ src/spec.coffee
@@ -283,16 +283,21 @@
           address: -> null # TODO

           # TODO: `0` should not be used when the value is unknown.
-          memory: @dynamic {usage: null, size: null}, {
+          memory: @dynamic {
+            usage: null
+            size: get 'memory_dynamic_min'
+          }, {
             VM_metrics: {
               update: (metrics, UUID) ->
                 return if UUID isnt refsToUUIDs[@generator.metrics]

+                # Do not trust the metrics if the VM is not running.
+                {power_state: state} = @value
+                return unless state in ['Paused', 'Running']
+
                 @field.size = +metrics.memory_actual
             }
           }
-
-          memory_dynamic: get('memory_dynamic_min')

           power_state: get('power_state')


Updates hosts and VMs with their metrics.

--- src/spec.coffee
+++ src/spec.coffee
@@ -69,8 +69,10 @@
   #     console.log event
   #     emit.call collection, event, items

-  $link = (keyFn, valFn = -> @val) ->
+  $link = (keyFn, valFn = (-> @val), once = false) ->
     valuePerItem = Object.create null
+    updating = false
+
     ->
       {key} = this

@@ -91,17 +93,23 @@
       # If not found, listens for its apparition.
       eventName = "key=#{remoteKey}"
       listener = (event, item) ->
-        # We only care if the item is entering.
-        return unless event is 'enter'
+        # If the events are due to an update of this link or if the item is
+        # exiting, just returns.
+        return if updating or event isnt 'enter'

         # Register its value.
         valuePerItem[key] = valFn.call item

-        # Removes the now unnecessary listener.
-        collection.removeListener eventName, listener
+        if once
+          # Removes the now unnecessary listener.
+          collection.removeListener eventName, listener

         # Force the object to update.
-        collection.touch key
+        try
+          updating = true
+          collection.touch key
+        finally
+          updating = false
       collection.on eventName, listener

       # Returns `null` for now.
@@ -133,9 +141,11 @@
           map[val[0]] = val[1]

       if changed
-        updating = true
-        collection.touch subscribers
-        updating = false
+        try
+          updating = true
+          collection.touch subscribers
+        finally
+          updating = false

     generator = ->
       subscribers[@key] = true


Don't show placeholder when element has focus

--- src/trix.coffee
+++ src/trix.coffee
@@ -55,6 +55,11 @@

   config:
     editorCSS: """
+      .trix-editor[contenteditable=true]:empty:not(.focused)::before {
+        content: attr(data-placeholder);
+        color: graytext;
+      }
+
       .trix-editor a[contenteditable=false] {
         cursor: text;
       }
@@ -176,6 +181,9 @@

     if placeholder = textarea.getAttribute("placeholder")
       element.setAttribute("data-placeholder", placeholder)
+      {handleEvent} = Trix.DOM
+      handleEvent "focus", onElement: element, withCallback: -> element.classList.add("focused")
+      handleEvent "blur",  onElement: element, withCallback: -> element.classList.remove("focused")

     element.className = textarea.className
     element.classList.add("trix-editor")


cleanup

cleanup

--- src/view.coffee
+++ src/view.coffee
@@ -1,7 +1,6 @@
 angular.module 'poi.view', []

 .directive 'poiView', ['$injector', ($injector) ->
-    $rootScope = $injector.get '$rootScope'
     $router = $injector.get '$router'
     $compile = $injector.get '$compile'
     $controller = $injector.get '$controller'


fix prompt.coffee error

fix prompt.coffee error

--- prompts.coffee
+++ prompts.coffee
@@ -80,5 +80,5 @@
   }, {
     type: 'confirm',
     name: '__continue',
-    message: 'Continue?'}
+    message: 'Continue?'
 }]


Remove trailing whitespace

Remove trailing whitespace

--- test/gyp.coffee
+++ test/gyp.coffee
@@ -91,7 +91,7 @@

 			execGruntTask 'configure', (err) ->
 				return done(err) if err
-
+
 				execGruntTask 'build', (err) ->
 					return done(err) if err


chore: more configs

chore: more configs

--- init/config.cson
+++ init/config.cson
@@ -15,6 +15,7 @@
   "circle-ci":
     apiToken: "CIRCLE_CI_API_TOKEN"
   core:
+    closeDeletedFileTabs: true
     disabledPackages: [
       "svg-preview"
       "relative-numbers"
@@ -27,10 +28,10 @@
       "dracula-ui"
       "dracula-theme"
     ]
-  docblockr: {}
+  docblockr:
+    deep_indent: true
   editor:
     fontFamily: "SourceCodePro-Light"
-    fontSize: 15
     invisibles: {}
     preferredLineLength: 1000
     showIndentGuide: true
@@ -51,6 +52,9 @@
     lowContrastTooltip: true
     spaciousMode: true
   linter: {}
+  "linter-eslint":
+    disableFSCache: true
+    fixOnSave: true
   "linter-jshint":
     disableWhenNoJshintrcFileInPath: true
   "linter-sass-lint":
@@ -59,20 +63,33 @@
     disableWhenNoConfigFileInPath: true
     executablePath: "path_to_linter"
   "linter-stylelint": {}
+  "linter-ui-default":
+    showPanel: true
+    showTooltip: false
+    useBusySignal: false
   pigments:
     groupPaletteColors: "by file"
     markerType: "dot"
     mergeColorDuplicates: true
     sortPaletteColors: "by color"
     traverseIntoSymlinkDirectories: true
+  "split-diff":
+    diffWords: true
+    ignoreWhitespace: true
+    leftEditorColor: "red"
+    rightEditorColor: "green"
+    scrollSyncType: "Vertical + Horizontal"
   "travis-ci-status": {}
   "tree-view":
+    focusOnReveal: false
     hideIgnoredNames: true
-    hideVcsIgnoredFiles: true
     showOnRightSide: true
     squashDirectoryNames: true
   "vim-mode":
     useClipboardAsDefaultRegister: false
+  "vim-mode-plus":
+    automaticallyEscapeInsertModeOnActivePaneItemChange: true
+    highlightSearch: true
   welcome:
     showOnStartup: false
   whitespace:


save and typecheck file before showing holes

It feels more normal since most commands now save the file
and typecheck before executing.

--- lib/idris-controller.coffee
+++ lib/idris-controller.coffee
@@ -252,6 +252,10 @@
       .subscribe successHandler, @displayErrors

   showHoles: ({target}) =>
+    editor = target.model
+    @saveFile editor
+    uri = editor.getURI()
+
     successHandler = ({responseType, msg}) =>
       [holes] = msg
       @messages.show()
@@ -262,7 +266,9 @@
       @messages.add holesView

     @model
-      .holes 80
+      .load uri
+      .filter ({responseType}) -> responseType == 'return'
+      .flatMap => @model.holes 80
       .subscribe successHandler, @displayErrors

   doProofSearch: ({target}) =>


Handle asynchronous save

Handle asynchronous save

In atom 1.19 save and a bunch of other Atom will become asynchronous
and return promises. Wait for the promise to be fulfilled.

Fixes #173

--- lib/idris-controller.coffee
+++ lib/idris-controller.coffee
@@ -107,10 +107,11 @@
         null

   saveFile: (editor) ->
-    if editor.getURI()
-      editor.save()
-    else
-      atom.workspace.saveActivePaneItemAs()
+    waitsForPromise ->
+      if editor.getURI()
+        editor.save()
+      else
+        atom.workspace.saveActivePaneItemAs()

   typecheckFile: (event) =>
     editor = @getEditor()


Bug fix

Bug fix

--- lib/model_extensions.coffee
+++ lib/model_extensions.coffee
@@ -252,6 +252,7 @@
       (attributes = {})[key] = value;

     options = Utils.bbCallback(options) if _.isFunction(options) # node style callback
+    options = {} unless options
     original_success = options.success
     original_error = options.error
     options = _.clone(options)
@@ -286,6 +287,7 @@
     return _original_destroy.apply(@, arguments) unless model_type.schema and (schema = model_type.schema())

     options = Utils.bbCallback(options) if _.isFunction(options) # node style callback
+    options = {} unless options
     original_success = options.success
     original_error = options.error
     options = _.clone(options)


forgot to respect stay for occurrence cursor restore

--- lib/mutation-manager.coffee
+++ lib/mutation-manager.coffee
@@ -97,7 +97,7 @@
             selection.destroy()
             continue

-          if isOccurrence
+          if isOccurrence and stay
             point = @vimState.getOriginalCursorPosition()
             selection.cursor.setBufferPosition(point)
           else if point = mutation.getRestorePoint({stay})


client check

client check

--- lib/package/commands.coffee
+++ lib/package/commands.coffee
@@ -63,7 +63,7 @@
       'julia-client:kill-julia': -> juno.connection.client.kill()
       'julia-client:interrupt-julia': => requireClient 'interrupt Julia', -> juno.connection.client.interrupt()
       'julia-client:reset-julia-server': -> juno.connection.local.server.reset()
-      'julia-client:connnect-external-process': -> juno.connection.messages.connectExternal()
+      'julia-client:connnect-external-process': -> disrequireClient -> juno.connection.messages.connectExternal()
       'julia-client:open-console': => @withInk -> juno.runtime.console.open()
       "julia-client:clear-console": => juno.runtime.console.reset()
       'julia-client:open-plot-pane': => @withInk -> juno.runtime.plots.open()


Update player_internals

Update player_internals

--- lib/player_internals.coffee
+++ lib/player_internals.coffee
@@ -32,17 +32,22 @@
       # Allows us to add a native error handler in Atom
       window.yieldWorker(@worker)

+  requestFrame: ->
+    @worker.postMessage(["generate"])
+
   pop: ->
-    @worker.postMessage(["generate"])
+    @requestFrame()
     if @_buffer?
       buffer = @_buffer
       @_buffer = null
-      buffer
     else
-      console.log("Dropped an audio frame. If you don't see any other error messages you may be processing too much data. This is normal when system audio is initializing.");
+      if @showDroppedFrameWarning
+        console.log("Dropped an audio frame. If you don't see any other error messages you may be processing too much data.");
       buffer = new Float64Array(AUDIO_BUFFER_SIZE * 2)
       buffer.fill 0
-      buffer
+    # Our worker lags behind the system by one frame so skip warning on the first pop()
+    @showDroppedFrameWarning = true
+    buffer

   terminate: ->
     @worker.terminate()
@@ -160,7 +165,7 @@
   adjustTime: (sec) ->
     framesPerSec = 1 / (AUDIO_BUFFER_SIZE / SAMPLE_RATE)
     for i in [0..(framesPerSec * sec)]
-      @_worker.pop()
+      @_worker.requestFrame()
       @_elapsedTime += AUDIO_BUFFER_SIZE / SAMPLE_RATE

 class @EditorBuilder


debugger tooltips

debugger tooltips

--- lib/runtime/debugger.coffee
+++ lib/runtime/debugger.coffee
@@ -43,7 +43,7 @@
   consumeInk: (ink) ->
     @stepper = new ink.Stepper
       buttons: [
-        {icon: 'arrow-down', command: 'julia-debug:step-to-next-line'}
-        {icon: 'link-external', command: 'julia-debug:finish-function'}
-        {icon: 'chevron-right', command: 'julia-debug:step-into-function'}
+        {icon: 'arrow-down', command: 'julia-debug:step-to-next-line', tooltip: 'Step to the next line.'}
+        {icon: 'link-external', command: 'julia-debug:finish-function', tooltip: 'Finish function.'}
+        {icon: 'chevron-right', command: 'julia-debug:step-into-function', tooltip: 'Step into function.'}
       ]


make single-line comment capturing, so that we can know its length

--- lib/text-buffer/pure.coffee
+++ lib/text-buffer/pure.coffee
@@ -6,7 +6,7 @@

 # regular expressions
 commentRegex =
-    /// (?:
+    /// (
         # single line comment
         --[^\r\n]*[\r\n]
     )|(


Fix #136 with no longer working Tab key in login

--- lib/views/login-view.coffee
+++ lib/views/login-view.coffee
@@ -22,10 +22,10 @@
       @subview 'passwordEditor', new MiniEditorView('and a password?')
       @div class: 'text-error block', outlet: 'errorLabel'
       @div class: 'block', =>
-        @button click: 'login', id: 'loginButton', class: 'btn btn-primary', outlet: 'loginButton', 'Log in'
-        @button click: 'cancel', id: 'cancelButton', class: 'btn', outlet: 'cancelButton', 'Cancel'
+        @button click: 'login', id: 'loginButton', class: 'btn btn-primary', outlet: 'loginButton', tabindex: 3, 'Log in'
+        @button click: 'cancel', id: 'cancelButton', class: 'btn', outlet: 'cancelButton', tabindex: 4, 'Cancel'
         @span class: 'three-quarters inline-block hidden', outlet: 'spinner'
-        @a href: 'https://build.particle.io/forgot-password', class: 'pull-right', 'Forgot password?'
+        @a href: 'https://build.particle.io/forgot-password', class: 'pull-right', tabindex: 5, 'Forgot password?'

   initialize: (serializeState) ->
     {CompositeDisposable} = require 'atom'
@@ -46,7 +46,9 @@
     @loginPromise = null

     @emailModel = @emailEditor.editor.getModel()
+    @emailEditor.editor.element.setAttribute('tabindex', 1)
     @passwordModel = @passwordEditor.editor.getModel()
+    @passwordEditor.editor.element.setAttribute('tabindex', 2)
     passwordElement = $(@passwordEditor.editor.element.rootElement)
     passwordElement.find('div.lines').addClass('password-lines')
     @passwordModel.onDidChange =>
@@ -73,7 +75,7 @@

   show: =>
     @panel.show()
-    @emailEditor.editor.click()
+    @emailEditor.editor.focus()

   hide: ->
     @panel.hide()


fix broken fr locale file

fix broken fr locale file

--- client/app/locales/fr.coffee
+++ client/app/locales/fr.coffee
@@ -69,13 +69,10 @@
     'konnector description sosh': "Téléchargez toutes vos factures Sosh. Pour pouvoir stocker les factures au format PDF, ce connecteur requiert que l'application Files soit installée sur votre Cozy."
     'konnector description electrabel': "Téléchargez toutes vos factures Electrabel. Pour pouvoir stocker les factures au format PDF, ce connecteur requiert que l'application Files soit installée sur votre Cozy."
     'konnector description orange': "Téléchargez toutes vos factures Orange. Pour pouvoir stocker les factures au format PDF, ce connecteur requiert que l'application Files soit installée sur votre Cozy."
-<<<<<<< HEAD
     'konnector description numericable': "Téléchargez toutes vos factures Numéricable. Pour pouvoir stocker les factures au format PDF, ce connecteur requiert que l'application Files soit installée sur votre Cozy."
     'konnector description virgimobile': "Téléchargez toutes vos factures Virgin Mobile. Pour pouvoir stocker les factures au format PDF, ce connecteur requiert que l'application Files soit installée sur votre Cozy."
     'konnector description nest': "Enregistrez la température actuelle mesurée par votre Nest."
-=======
     'konnector description isen': "Les étudiants de l'école d'ingénieur ISEN peuvent importer leurs supports de cours et leur agenda."
->>>>>>> 54aabd3004d88f202484ca6901539acad21d5e4d

     # Konnectors' notifications
     'notification prefix': "Konnector %{name} :"


Add note about aggregation for survey tasks

--- app/pages/lab/data-dumps.cjsx
+++ app/pages/lab/data-dumps.cjsx
@@ -62,7 +62,7 @@
               exportType="aggregations_export"
               newFeature=true
             />
-            Text tasks cannot be aggregated at this time.
+            Text tasks and survey tasks cannot be aggregated at this time.


transitionTo lab index page if signed out of project builder.

--- app/partials/account-bar.cjsx
+++ app/partials/account-bar.cjsx
@@ -6,6 +6,7 @@
 Translate = require 'react-translate-component'
 Avatar = require '../partials/avatar'
 TriggeredModalForm = require 'modal-form/triggered'
+Router = require '@edpaget/react-router'

 UP = 38
 DOWN = 40
@@ -20,6 +21,7 @@

 module.exports = React.createClass
   displayName: 'AccountBar'
+  mixins: [Router.Navigation, Router.State]

   propTypes:
     user: React.PropTypes.object.isRequired
@@ -110,3 +112,6 @@

   handleSignOutClick: ->
     auth.signOut()
+
+    if @getPathname().indexOf("lab") > -1 and @getParams()?
+      @transitionTo "lab"


call handle Section if no action and identifier and has function

--- client/home/lib/index.coffee
+++ client/home/lib/index.coffee
@@ -64,7 +64,9 @@
     else
       @doOnboarding targetPane

-    return  unless identifier and action
+    unless identifier and action
+      targetPaneView.handleSection()? unless action
+      return

     targetPaneView.emit 'SubTabRequested', action, identifier
     { parentTabTitle } = targetPane.getOptions()


permalinks to arbitrary commands

permalinks to arbitrary commands

--- ops.coffee
+++ ops.coffee
@@ -255,10 +255,11 @@
       @success()
   lead._finished

-cmd 'permalink', 'Create a link to the previously run statement', ->
+cmd 'permalink', 'Create a link to the previously run statement', (code) ->
   a = document.createElement 'a'
   a.href = location.href
-  a.search = '?' + encodeURIComponent btoa @previously_run if @previously_run?
+  code ?= @previously_run
+  a.search = '?' + encodeURIComponent btoa code
   a.innerText = a.href
   @output a
   @success()


Fix gulp-open

Fix gulp-open

--- app/templates/gulpfile.coffee
+++ app/templates/gulpfile.coffee
@@ -124,11 +124,9 @@

 # site launcher
 gulp.task 'open', ->
-	return
-	# gulp.src(config.startpage)
-	# 	.pipe open(config.startpage,
-	# 	url: 'http://localhost:' + config.http_port
-	# )
+	gulp.src('app/index.js') # dummy source, but must match a real file to run
+		.pipe open '',
+			url: 'http://localhost:' + config.http_port

 # default task -- run 'gulp' from cli


update grunt start

update grunt start

--- grunt.coffee
+++ grunt.coffee
@@ -95,7 +95,7 @@
   grunt.initConfig(config)

   #grunt.loadNpmTasks 'grunt-coffee'
-  grunt.registerTask 'default', 'coffee:all copy dist:client'
+  grunt.registerTask 'default', 'coffee:all copy build:client'
   grunt.registerTask 'start', 'default watch'
   grunt.registerTask 'dist', 'build uploadToGithub'


:racehorse: Don't require browser ipc until used

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -1,4 +1,4 @@
-BrowserIpc = require('remote').require('ipc')
+BrowserIpc = null # Defer require until actually used
 RendererIpc = require('ipc')

 {$, View} = require 'atom'
@@ -239,6 +239,7 @@

           if not isNaN(fromProcessId) and not isNaN(fromProcessId)
             # Let the window where the drag started know that the tab was dropped
+            BrowserIpc ?= require('remote').require('ipc')
             BrowserIpc.sendChannel(fromProcessId, fromRoutingId, 'tab:dropped', fromPaneId, fromIndex)

         atom.focus()


handle better error for thumb creation

handle better error for thumb creation

Returned error on file opening was not handled.

--- server/helpers/thumb.coffee
+++ server/helpers/thumb.coffee
@@ -23,18 +23,24 @@

 module.exports.create = (file, callback) ->
     return callback new Error('no binary') unless file.binary?
+
     if file.binary?.thumb?
         console.log "createThumb #{file.id} : already done"
         callback()
+
     else
         rawFile = "/tmp/#{file.name}"
         fs.open rawFile, 'w', (err) ->
-            stream = file.getBinary 'file', (err) ->
-                return callback err if err
-            stream.pipe fs.createWriteStream rawFile
-            stream.on 'error', callback
-            stream.on 'end', =>
-                resize rawFile, file, 'thumb', (err) =>
-                    fs.unlink rawFile, ->
-                        console.log "createThumb #{file.id} : done"
-                        callback(err)+            if err
+                callback err
+
+            else
+                stream = file.getBinary 'file', (err) ->
+                    return callback err if err
+                stream.pipe fs.createWriteStream rawFile
+                stream.on 'error', callback
+                stream.on 'end', =>
+                    resize rawFile, file, 'thumb', (err) =>
+                        fs.unlink rawFile, ->
+                            console.log "createThumb #{file.id} : done"
+                            callback err


Tweak working about package time

Tweak working about package time

--- lib/timecop-view.coffee
+++ lib/timecop-view.coffee
@@ -47,7 +47,7 @@
           @span class: "inline-block #{highlightClass}", "#{pack.loadTime}ms"
     @loadingSummary.text """
       Loaded #{packageCount} packages in #{totalLoadTime}ms.
-      #{loadedPackages.length} packages took >= 5ms to load.
+      #{loadedPackages.length} packages took longer than 5ms to load.
     """

   showActivePackages: ->
@@ -68,7 +68,7 @@
           @span class: "inline-block #{highlightClass}", "#{pack.activateTime}ms"
     @activateSummary.text """
       Activated #{packageCount} packages in #{totalActivateTime}ms.
-      #{activePackages.length} packages took >= 5ms to activate.
+      #{activePackages.length} packages took longer than 5ms to activate.
     """

   serialize: ->


<debug> Solving problem with super - Praise the fat arrows.

--- server/models/sensor.coffee
+++ server/models/sensor.coffee
@@ -103,7 +103,8 @@
 	###
 	@create: (data, callback) ->
 		thisSensor = @
-		superCreate = (data, callback) -> super data, callback
+		superCreate = (data, callback) => super data, callback
+
 		if sensorsDrivers[data.driverId] # If this kind of device is supported:
 			# Check if this sensor isn't already added (the combination driverId + customId should be unique):
 			params = key: [data.accountID, data.driverId]
@@ -114,12 +115,10 @@
 					callback 'Device already added', sensors[0]
 				else
 					superCreate data, (err, sensor) ->
-						if err
-							callback err, null
-							return
-
+						return callback err, null if err
+
 						# Let the driver handle the integration of the device to the system:
-						sensorsDrivers[driverId].add customId, sensor.id, (err) ->
+						sensorsDrivers[data.driverId].add data.customId, sensor.id, (err) ->
 							if err
 								# Cancelling modif:
 								thisSensor.requestDestroy "all", {key: sensor.id}, (err) ->


Overwrite new submits

Overwrite new submits

--- server/models/submit.coffee
+++ server/models/submit.coffee
@@ -14,7 +14,7 @@
     quality: { type: Number, default: 0 },

 submitsSchema.methods.upsert = () ->
-    @update(this, {upsert: true})
+    @update(this, {upsert: true, overwrite: true})

 submitsSchema.statics.findByUser = (userId) ->
     Submit.find


clearer language

clearer language

--- lib/view_helpers.coffee
+++ lib/view_helpers.coffee
@@ -1,6 +1,6 @@
 #
 # Export isomorphic libraries and functions you would like to use in templates.
-# This will injected in res.locals & exposed globally on the client for Jadeify.
+# This will be injected in res.locals & exposed globally client-side for Jadeify.
 #

 module.exports =


remove unused require()

remove unused require()

--- lib/view_manager.coffee
+++ lib/view_manager.coffee
@@ -3,7 +3,6 @@
 { Panel } = require('sdk/panel')
 tabs = require('sdk/tabs')
 preferences = require('sdk/simple-prefs').prefs
-windowUtils = require('sdk/window/utils')
 { identify } = require('sdk/ui/id')
 _ = require('sdk/l10n').get


As it turned out the one I'm looking for is .toFixed.

And I've even used it before.

--- resources/assets/coffee/react/profile-page/top-ranks.coffee
+++ resources/assets/coffee/react/profile-page/top-ranks.coffee
@@ -50,7 +50,7 @@
     if (i + 1) > limit
       topClasses += ' hidden'

-    title = "#{score.beatmapSet.data.title} [#{score.beatmap.data.version}]#{modsText}(#{_.round(score.accuracy * 100, 2)}%)"
+    title = "#{score.beatmapSet.data.title} [#{score.beatmap.data.version}]#{modsText}(#{(score.accuracy * 100).toFixed(2)}%)"

     li
       key: score.id


Adjust usage, log file name and allow to set API host.

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -5,6 +5,13 @@

 argv = require('optimist')
   .usage('Usage: $0 --projectKey key --clientId id --clientSecret secret --logDir dir --logLevel level --timeout timeout')
+  .describe('projectKey', 'your SPHERE.IO project-key')
+  .describe('clientId', 'your SPHERE.IO OAuth client id')
+  .describe('clientSecret', 'your SPHERE.IO OAuth client secret')
+  .describe('timeout', 'timeout for requests')
+  .describe('sphereHost', 'SPHERE.IO API host to connecto to')
+  .describe('logLevel', 'log level for file logging')
+  .describe('logDir', 'directory to store logs')
   .default('logLevel', 'info')
   .default('logDir', '.')
   .default('timeout', 60000)
@@ -14,7 +21,7 @@
 logger = new Logger
   streams: [
     { level: 'error', stream: process.stderr }
-    { level: argv.logLevel, path: "#{argv.logDir}/sphere-stock-sync-#{argv.projectKey}.log" }
+    { level: argv.logLevel, path: "#{argv.logDir}/sphere-stock-sync_#{argv.projectKey}.log" }
   ]

 process.on 'SIGUSR2', ->
@@ -32,6 +39,8 @@
     client_id: argv.clientId
     client_secret: argv.clientSecret

+options.baseConfig.host = argv.sphereHost if argv.sphereHost?
+
 updater = new MarketPlaceStockUpdater options
 updater.run (msg) ->
   exitCode = 0


cached value should be verified before sinking

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -213,16 +213,17 @@
         .then (lastProcessedSN) =>
           @stats.awaitOrderingRemoved out.message

-          if out.message.sequenceNumber is (lastProcessedSN + 1)
-            out.sink.onNext out.message
-          else if out.message.sequenceNumber <= lastProcessedSN
+          if out.message.sequenceNumber is (lastProcessedSN.sequenceNumber + 1)
+            @_doSinkMessage lastProcessedSN, out
+          else if out.message.sequenceNumber <= lastProcessedSN.sequenceNumber
             @_messageHasLowSequenceNumber out.message
             out.recycleBin.onNext out.message
+            out.sink.onCompleted()
+            out.errors.onCompleted()
           else
             out.recycleBin.onNext out.message
-
-          out.sink.onCompleted()
-          out.errors.onCompleted()
+            out.sink.onCompleted()
+            out.errors.onCompleted()
         .fail (error) =>
           @stats.awaitOrderingRemoved out.message

@@ -239,10 +240,7 @@

     _.each toSink, (s) =>
       @stats.awaitOrderingRemoved s.message
-
-      s.sink.onNext s.message
-      s.sink.onCompleted()
-      s.errors.onCompleted()
+      @_doSinkMessage {sequenceNumber: justProcessedMsg.sequenceNumber, cached: true}, s

   checkAvaialbleForProcessingAndLockLocally: (msg) ->
     @isProcessed msg
@@ -299,17 +297,42 @@
   _messageHasLowSequenceNumber: (msg) ->
     console.info "WARN: message has appeared twice for processing (something wrong in the pipeline)", msg

+  _doSinkMessage: (sn, box) ->
+    doSink = () ->
+      box.sink.onNext box.message
+      box.sink.onCompleted()
+      box.errors.onCompleted()
+
+    if sn.cached
+      @sphere.getLastProcessedSequenceNumber box.message.resource
+      .then (upToDateSN) ->
+        if upToDateSN.sequenceNumber is sn.sequenceNumber
+          doSink()
+        else
+          # looks like already processed somewhere else
+          box.recycleBin.onNext box.message
+          box.errors.onCompleted()
+          box.sink.onCompleted()
+      .fail (error) ->
+        box.errors.onNext {message: box.message, error: error, processor: "Get last processed sequence number during sinking"}
+        box.errors.onCompleted()
+        box.sink.onCompleted()
+    else
+      doSink()
+
   orderBySequenceNumber: (msg, lock, recycleBin) ->
     sink = new Rx.Subject()
     errors = new Rx.Subject()

     @_getLastProcessedSequenceNumber msg
     .then (lastProcessedSN) =>
-      if msg.sequenceNumber is (lastProcessedSN + 1)
-        sink.onNext msg
-        sink.onCompleted()
-        errors.onCompleted()
-      else if msg.sequenceNumber <= lastProcessedSN
+      if msg.sequenceNumber is (lastProcessedSN.sequenceNumber + 1)
+        @_doSinkMessage lastProcessedSN,
+          message: msg
+          sink: sink
+          errors: errors
+          recycleBin: recycleBin
+      else if msg.sequenceNumber <= lastProcessedSN.sequenceNumber
         @_messageHasLowSequenceNumber msg
         recycleBin.onNext msg
         sink.onCompleted()
@@ -338,7 +361,7 @@
     cached = @sequenceNumberCache.get @_snCacheKey(msg.resource)

     if cached?
-      Q(cached)
+      Q({sequenceNumber: cached, cached: true})
     else
       @sphere.getLastProcessedSequenceNumber msg.resource
       .then (sn) =>
@@ -347,7 +370,7 @@
         if not alreadyInCache? or alreadyInCache < sn
           @sequenceNumberCache.set @_snCacheKey(msg.resource), sn

-        sn
+        {sequenceNumber: sn, cached: false}

 class MessageProcessor
   constructor: (@stats, @messageService, @persistenceService, options) ->


unnoticed dep

unnoticed dep

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -3,7 +3,6 @@

 util = require '../lib/util'
 package_json = require '../package.json'
-{ProjectCredentialsConfig} = require 'sphere-node-utils'

 optimist = require('optimist')
 .usage('Usage: $0 --mapping [mapping.json]')


increment coffeelint.VERSION to 0.5.5

increment coffeelint.VERSION to 0.5.5

--- src/coffeelint.coffee
+++ src/coffeelint.coffee
@@ -23,7 +23,7 @@

 # The current version of Coffeelint.
-coffeelint.VERSION = "0.5.4"
+coffeelint.VERSION = "0.5.5"

 # CoffeeLint error levels.


'System' command added for grey box testing.

--- cakes.coffee
+++ cakes.coffee
@@ -152,6 +152,16 @@

 exports.Describe = exports.Spec = createDescribe(['label', 'color'])

+createSystem = (options)->
+	return (callback)->
+		label = 'System'
+		if 'style' in options
+			label = label.yellow.inverse.italic
+
+		mocha.describe label, callback
+
+exports.System = createSystem(['style'])
+

 ###
 gwt = (label, args, options)->


Do not send stats if developer_mode is enabled.

--- app/assets/javascripts/app/lib/app_init/track.js.coffee
+++ app/assets/javascripts/app/lib/app_init/track.js.coffee
@@ -102,7 +102,7 @@
     )

   log: ( facility, level, args ) ->
-    return if !App.Config.get('developer_mode')
+    return if App.Config.get('developer_mode')
     return if !App.Config.get('ui_send_client_stats')
     info =
       time:     Math.round( new Date().getTime() / 1000 )
@@ -113,7 +113,7 @@
     @data.push info

   send: (async = true) =>
-    return if !App.Config.get('developer_mode')
+    return if App.Config.get('developer_mode')
     return if !App.Config.get('ui_send_client_stats')
     return if _.isEmpty @data
     newData = _.clone( @data )


remove logs

remove logs

--- client/vue/components/poll_common_directive/poll_common_directive.coffee
+++ client/vue/components/poll_common_directive/poll_common_directive.coffee
@@ -10,10 +10,6 @@
     componentName: ->
       pollType = (@stance or @outcome or @stanceChoice or @poll).poll().pollType

-      # console.log "poll-#{@poll.pollType}-#{@name}"
-      # console.log 'camelcase', _.camelCase("poll-#{@poll.pollType}-#{@name}")
-      # console.log 'Vue.options.components', Vue.options.components
-
       if Vue.options.components[_.upperFirst(_.camelCase("poll-#{pollType}-#{@name}"))]
         "poll-#{_.kebabCase(pollType)}-#{@name}"
       else


add close with esc key

add close with esc key

--- extension/content.coffee
+++ extension/content.coffee
@@ -1,6 +1,8 @@
 port = chrome.runtime.connect()
 overflow = undefined
 tabsView = undefined
+controlInput = undefined
+tabsterActive = false

 baseKeys = ['a', 's', 'd', 'f', 'h', 'j', 'k', 'l']
 keys = []
@@ -20,6 +22,8 @@
 		overflow.appendChild tabsView
 		document.body.appendChild overflow

+		addEvents(document)
+
 	tabsView.innerHTML = ''

 	for tab in tabs
@@ -34,6 +38,7 @@
 		tabView.addEventListener('click', tabsterSwitchTab.bind(this, tab.id), false)

 	overflow.className += " visible"
+	tabsterActive = true

 tabsterSwitchTab = (id) ->
 	tabsterClose()
@@ -44,3 +49,13 @@

 tabsterClose = () ->
 	overflow.className = 'tabster-overflow'
+	tabsterActive = false
+
+handleKeyEvents = (e) ->
+	return e unless (tabsterActive)
+	e.preventDefault()
+	if 27 == e.keyCode
+		tabsterClose()
+
+addEvents = () ->
+	document.addEventListener('keyup', handleKeyEvents, false)


Modify to display pop-out error when adding user to a room returns an error.

--- client/views/app/room.coffee
+++ client/views/app/room.coffee
@@ -486,7 +486,7 @@
 		else if roomData.t in ['c', 'p']
 			Meteor.call 'addUserToRoom', { rid: roomData._id, username: doc.username }, (error, result) ->
 				if error
-					return Errors.throw error.reason
+					return toastr.error error.reason

 				$('#user-add-search').val('')
 				toggleAddUser()


Removed debug statement

Removed debug statement

--- client/views/app/room.coffee
+++ client/views/app/room.coffee
@@ -244,7 +244,6 @@
 			if Meteor.user()?.admin is true
 				userData = _.extend userData, Meteor.users.findOne { username: String(username) }

-			console.log 'userdata: ', userData
 		return userData

 	seeAll: ->


Improve scroll in all devices

Improve scroll in all devices

--- client/views/app/room.coffee
+++ client/views/app/room.coffee
@@ -528,8 +528,8 @@
 	newMessage = this.find(".new-message")

 	template = this
+
 	onscroll = _.throttle ->
-		console.log wrapper.scrollTop
 		template.atBottom = wrapper.scrollTop >= wrapper.scrollHeight - wrapper.clientHeight
 	, 200

@@ -540,11 +540,13 @@
 	, 100

 	wrapper.addEventListener 'touchstart', ->
-		console.log 'touchstart'
 		template.atBottom = false

 	wrapper.addEventListener 'touchend', ->
-		console.log 'touchend'
+		onscroll()
+
+	wrapper.addEventListener 'scroll', ->
+		template.atBottom = false
 		onscroll()

 	wrapper.addEventListener 'mousewheel', ->


Make the first 4 genes longer.

Make the first 4 genes longer.


Re-order examples

Re-order examples

--- apps/examples/examples.coffee
+++ apps/examples/examples.coffee
@@ -1,4 +1,17 @@
 module.exports = [
+  {
+    id: 'mood'
+    title: 'Mood'
+    description: 'Vibes, patterns, atmospheres, colors, and more'
+    # channel_ids: [
+    #   "sublime",
+    #   "color-schemes",
+    #   "hotel-concepts",
+    #   "color-files",
+    #   "life-luxury",
+    # ]
+    channel_ids: [13771, 28613, 7469, 158542, 46371]
+  },
   {
     id: 'collections'
     title: 'Collections'
@@ -59,19 +72,6 @@
     channel_ids: [191639, 103614, 10956, 25149, 70109, 13958, 122061]
   },
   {
-    id: 'mood'
-    title: 'Mood'
-    description: 'Vibes, patterns, atmospheres, colors, and more'
-    # channel_ids: [
-    #   "sublime",
-    #   "color-schemes",
-    #   "hotel-concepts",
-    #   "color-files",
-    #   "life-luxury",
-    # ]
-    channel_ids: [28613, 7469, 158542, 46371, 13771]
-  },
-  {
     id: 'places'
     title: 'Place'
     description: 'Close observations of cities and landscapes'


Refactor fix_reply regexp, apply fix to moinmoin

--- coffee/lib/formatters.coffee
+++ coffee/lib/formatters.coffee
@@ -55,7 +55,16 @@
       any escaping.
       ###
       f = if format == "moinmoin" then @moinmoin else @markdown
-      f.call this, raw, reply
+      raw = raw.replace @FIX_REPLY_START[0], @FIX_REPLY_START[1] if reply
+      f.call this, raw
+
+    # Fix formatting at the first line of reply comment by deleting
+    # reply nickname.
+    # Actually API shouldn't do any actions with user text
+    # (applying reply nickname is template work undoubtedly)
+    # but fuck that legacy.
+    FIX_REPLY_START:
+      [/^[^ ]+ /, ""]

     USER_LINK_FORMATTER:
       [/(^|\s)@([-0-9A-Za-z_]+)/g
@@ -73,31 +82,22 @@
         "#{space}[##{link}](/p/#{link.replace '/', '#'})"
       ]

-    markdown: (raw, reply = false) ->
+    markdown: (raw) ->
       ###Markdown with some additional BnW-specific rules.###

       FIX_LINE_ENDINGS =
         [/\r/g, ""]
-
-      # Fix formatting at the first line of reply comment by deleting
-      # reply nickname.
-      # Actually API shouldn't do any actions with user text
-      # (applying reply nickname is template work undoubtedly)
-      # but fuck that legacy.
-      FIX_REPLY_START =
-        [/^(@[-0-9A-Za-z_]+ )/g, ""]

       # Do the wakabic blockquotes
       WAKABAMARK_BLOCKQUOTE =
         [/(^|\n)(>.+)(?=\n[^>])/g, "$1$2\n"]

-      format = (text) =>
-        ###Apply BnW additional formatters.###
-        text = text.replace FIX_LINE_ENDINGS[0], FIX_LINE_ENDINGS[1]
-        text = text.replace FIX_REPLY_START[0], FIX_REPLY_START[1] if reply
-        text = text.replace WAKABAMARK_BLOCKQUOTE[0], WAKABAMARK_BLOCKQUOTE[1]
-        text = text.replace @USER_LINK_FORMATTER[0], @USER_LINK_FORMATTER[2]
-        text = text.replace @POST_LINK_FORMATTER[0], @POST_LINK_FORMATTER[2]
+      format = (rawb) =>
+        ###Apply BnW additional formatters to the block of raw text.###
+        rawb = rawb.replace FIX_LINE_ENDINGS[0], FIX_LINE_ENDINGS[1]
+        rawb = rawb.replace WAKABAMARK_BLOCKQUOTE[0], WAKABAMARK_BLOCKQUOTE[1]
+        rawb = rawb.replace @USER_LINK_FORMATTER[0], @USER_LINK_FORMATTER[2]
+        rawb = rawb.replace @POST_LINK_FORMATTER[0], @POST_LINK_FORMATTER[2]

       # Walk through raw input text and apply bnw formatters only
       # for non-code blocks.


stub steroids-configure task

stub steroids-configure task

--- tasks/steroids-make.coffee
+++ tasks/steroids-make.coffee
@@ -1,7 +1,8 @@
 chalk = require "chalk"
+wrench = require "wrench"
+xml2js = require "xml2js"

 fs = require "fs"
-wrench = require "wrench"
 path = require "path"

 module.exports = (grunt)->
@@ -217,3 +218,8 @@
         fs.writeFileSync filePathInDist, fs.readFileSync(filePath)

     grunt.log.writeln "#{chalk.green("OK")}"
+
+  grunt.registerTask "steroids-configure", "Read XML configuration files from www/ and output a JSON to dist/", ->
+
+    configXml = grunt.file.read "www/config.xml"
+    grunt.file.write "dist/config.json", configXml


removed extra log call

removed extra log call

--- public/dreamcatcher/controllers/entries/newedit_entry_controller.coffee
+++ public/dreamcatcher/controllers/entries/newedit_entry_controller.coffee
@@ -117,8 +117,7 @@
     $("#entryAttach .attach[name=#{name}]").show()

   '.imagesHeader click': ->
-    log 'test'
-    $(".entryImages").slideUp()
+    $('.entryImages').slideUp()

   ##entryAttach
   '.attach click': (el) ->


:art:

:art:

--- src/file.coffee
+++ src/file.coffee
@@ -388,6 +388,7 @@
 if Grim.includeDeprecatedAPIs
   EmitterMixin = require('emissary').Emitter
   EmitterMixin.includeInto(File)
+
   File::on = (eventName) ->
     switch eventName
       when 'contents-changed'


Add File.prototype.safeWriteSync(contents)

--- src/file.coffee
+++ src/file.coffee
@@ -325,6 +325,36 @@
     @setDigest(text)
     @subscribeToNativeChangeEvents() if not previouslyExisted and @hasSubscriptions()
     undefined
+
+  safeWriteSync: (text) ->
+    @writeSync(text)
+
+    try
+      # Ensure file contents are really on disk before proceeding
+      fd = fs.openSync(@getPath(), 'r+')
+      fs.fdatasyncSync(fd)
+      fs.closeSync(fd)
+
+      # Ensure file directory entry is really on disk before proceeding
+      #
+      # Windows doesn't support syncing on directories so we'll just have to live
+      # with less safety on that platform.
+      unless process.platform is 'win32'
+        try
+          directoryFD = fs.openSync(path.dirname(@getPath()), 'r')
+          fs.fdatasyncSync(directoryFD)
+          fs.closeSync(directoryFD)
+        catch error
+          console.warn("Non-fatal error syncing parent directory of #{@getPath()}")
+      return
+    catch error
+      if error.code is 'EACCES' and process.platform is 'darwin'
+        runas ?= require 'runas'
+        # Use sync to force completion of pending disk writes.
+        if runas('/bin/sync', [], admin: true) isnt 0
+          throw error
+      else
+        throw error

   writeFile: (filePath, contents) ->
     encoding = @getEncoding()


add missing require

add missing require

--- services/web/test/unit/coffee/Templates/TemplatesControllerTests.coffee
+++ services/web/test/unit/coffee/Templates/TemplatesControllerTests.coffee
@@ -24,6 +24,9 @@
 		@dumpFolder = "dump/path"
 		@ProjectOptionsHandler = {setCompiler:sinon.stub().callsArgWith(2)}
 		@uuid = "1234"
+		@ProjectRootDocManager = {
+			setRootDocFromName: sinon.stub().callsArgWith(2)
+		}
 		@ProjectDetailsHandler =
 			getProjectDescription:sinon.stub()
 		@Project =
@@ -31,6 +34,7 @@
 		@controller = SandboxedModule.require modulePath, requires:
 			'../../../js/Features/Uploads/ProjectUploadManager':@ProjectUploadManager
 			'../../../js/Features/Project/ProjectOptionsHandler':@ProjectOptionsHandler
+			'../../../js/Features/Project/ProjectRootDocManager':@ProjectRootDocManager
 			'../../../js/Features/Authentication/AuthenticationController': @AuthenticationController = {getLoggedInUserId: sinon.stub()}
 			'./TemplatesPublisher':@TemplatesPublisher
 			"logger-sharelatex":


Activity: privatemessage scrolldown done by wondow not kdlistview

--- client/Social/Activity/views/privatemessage/pane.coffee
+++ client/Social/Activity/views/privatemessage/pane.coffee
@@ -51,15 +51,6 @@
       @listPreviousReplies()  if current < 20 and current < previous
       previous = current
     , 200
-
-
-  scrollDown: (item) ->
-
-    super item
-
-    KD.utils.defer =>
-
-      @listController.getListView().scrollDown()

   createInputWidget: ->


 Merge branch 'master.activity.minorFixes' into 'master.activity'

Master.Activity.Minor Fixes

--- client/Social/Activity/views/privatemessagemodal.coffee
+++ client/Social/Activity/views/privatemessagemodal.coffee
@@ -50,6 +50,7 @@
     @modalTabs.forms.Message.inputs.recipient.addSubView @chatHeads

     @createUserAutoComplete()
+    @setFocus()

     if @getOption 'arrowTop'
       @addSubView (new KDCustomHTMLView
@@ -58,6 +59,7 @@
           top    : @getOption 'arrowTop'
       ), 'kdmodal-inner'

+  setFocus : -> @autoComplete.getView().setFocus()

   submitMessage : ->

@@ -153,4 +155,4 @@

     # if event.which is 8 and val is ''
     #   log 'sil bi eleman'
-    #   debugger
+    #   debugger


account/shortcuts: fix a bug where cancelling record session restores the default binding

--- client/account/lib/views/accounteditshortcutsrow.coffee
+++ client/account/lib/views/accounteditshortcutsrow.coffee
@@ -3,6 +3,12 @@
 facade = require('./accounteditshortcutsfacade')
 EventType = require './accounteditshortcutseventtype'
 globals = require 'globals'
+
+# Class name to set for currently recording item.
+ENABLED_CLASS_NAME = 'enabled'
+
+# Class name to set for currently recording item.
+ACTIVE_CLASS_NAME = 'active'

 # Class name to set for duplicate/colliding items.
 DUP_CLASS_NAME = 'collides'
@@ -64,15 +70,16 @@

     @model = data
     @_enabled = @model.enabled
+    @_binding = _.first @model.binding
     @_handlers = null
     @_active = no

-    dup = options.dup
+    @_dup = options.dup
     delete options.dup

     super _.extend cssClass: 'row', options

-    if dup then @setClass DUP_CLASS_NAME
+    if @_dup then @setClass DUP_CLASS_NAME

   viewAppended: ->
@@ -82,7 +89,7 @@
       tagName  : 'div'
       partial: '
'

-    if @model.enabled then toggle.setClass 'enabled'
+    if @model.enabled then toggle.setClass ENABLED_CLASS_NAME

     descriptionText = _.escape @model.description

@@ -95,7 +102,7 @@

     binding = new kd.View
       cssClass : 'col'
-      partial  : presentBinding _.first @model.binding
+      partial  : presentBinding @_binding

     # Handles checkbox clicks.
     #
@@ -105,7 +112,7 @@
       e.preventDefault?()
       e.stopPropagation?()
       @_enabled = if _.isBoolean e then e else not @_enabled
-      if @_enabled then toggle.setClass 'enabled' else toggle.unsetClass 'enabled'
+      if @_enabled then toggle.setClass ENABLED_CLASS_NAME else toggle.unsetClass ENABLED_CLASS_NAME
       unless silent
         @emit EventType.Item.TOGGLED, @_enabled

@@ -114,7 +121,8 @@
     clickHandler = =>
       return  if @_active

-      @setClass 'active'
+      @setClass ACTIVE_CLASS_NAME
+      @unsetClass DUP_CLASS_NAME
       @_active = yes
       binding.updatePartial ''

@@ -126,9 +134,13 @@
         # Otherwise we set the previous binding.
         (res) =>
           @_active = no
-          partial = if res then presentBinding res else presentBinding _.first @model.binding
+          if res
+            partial = presentBinding res
+          else
+            partial = presentBinding @_binding
+            dupHandler @_dup
           binding.updatePartial partial
-          @unsetClass 'active'
+          @unsetClass ACTIVE_CLASS_NAME

     # Handles Facade.CHANGED, which is only fired upon app storage is synced-up
     # with internal representation.
@@ -136,17 +148,18 @@
     # Beware that returned model is not a json representation, but the keyconfig.Model itself.
     #
     changeHandler = (model) ->
+      { shortcuts } = kd.singletons
+      @_binding = _.first shortcuts.getPlatformBinding model
       enabled = if model.options?.enabled is yes then yes else no
       toggleClickHandler enabled, yes
-      # XXX: do not update binding while recording
-      { shortcuts } = kd.singletons
-      binding.updatePartial presentBinding _.first shortcuts.getPlatformBinding model
+      binding.updatePartial presentBinding @_binding

     # Handles Facade.DUP, which is fired on every change made.
     #
     # Passed value determines if this row's binding is colliding with some other binding or not.
     #
     dupHandler = (dup) =>
+      @_dup = dup
       if dup
       then @setClass   DUP_CLASS_NAME
       else @unsetClass DUP_CLASS_NAME


removing unused onCreated event

removing unused onCreated event

--- client/controllers/incidentReports/incidentTable.coffee
+++ client/controllers/incidentReports/incidentTable.coffee
@@ -1,5 +1,3 @@
-Template.incidentTable.onCreated ->
-
 changeIncidentStatus = (status, instance) ->
   $.each instance.data.incidents.find({selected: true}).fetch(), (index, incident) ->
     incident = _id: incident._id


Remove extraneous FunctionDeclaration generator.

--- js2coffee.coffee
+++ js2coffee.coffee
@@ -756,12 +756,6 @@
         line + "\n"
     [ "###", lines, "###\n" ]

-  FunctionDeclaration: (node) ->
-    params = @makeParams(node.params)
-
-    @indent (indent) =>
-      [ @walk(node.id), ' = ', params, "->\n", @walk(node.body) ]
-
   ReturnStatement: (node) ->
     if node.argument
       if node.argument.type is 'ObjectExpression'


Fixed escape button behaviour

Fixed escape button behaviour

--- core/app/backbone/factlink/react_text_input_view.coffee
+++ core/app/backbone/factlink/react_text_input_view.coffee
@@ -14,16 +14,18 @@
       when 13 then @props.onReturn?()
       when 40 then @props.onDown?()
       when 38 then @props.onUp?()
-      when 27 then @props.onEscape?()
+      when 27 then @_setText ''
       else eventHandled = false

     if eventHandled
       e.preventDefault()
       e.stopPropagation()

-  _onChange: (e) ->
-    @setState text: e.target.value
-    @props.onChange?(e.target.value)
+  _onChange: (e) -> @_setText e.target.value
+
+  _setText: (text) ->
+    @setState text: text
+    @props.onChange?(text)

   render: ->
     _input


parser test: better assertion messages

parser test: better assertion messages

--- test/parser/classes.coffee
+++ test/parser/classes.coffee
@@ -13,9 +13,9 @@

 # visibility

-assert parses('public class Foo {}'), 'simple classes should parse fine'
-assert parses('private class Foo {}'), 'simple classes should parse fine'
-assert parses('global class Foo {}'), 'simple classes should parse fine'
+assert parses('public class Foo {}'), 'public classes should parse fine'
+assert parses('private class Foo {}'), 'private classes should parse fine'
+assert parses('global class Foo {}'), 'global classes should parse fine'
 assert not parses('class Foo {}'), 'visibility keyword is required'
 assert not parses('public private class Foo {}'), 'there can be only one visibility keyword'


service.action: remove only filter

service.action: remove only filter

--- test/service/action.coffee
+++ test/service/action.coffee
@@ -9,7 +9,7 @@
   config = test.config()
   return if config.disable_service_systemctl

-  they.only 'should start', (ssh) ->
+  they 'should start', (ssh) ->
     nikita
       ssh: ssh
     .service.remove


Fix crash on empty blocks represented as "Identifier" nodes.

--- src/javascript.coffee
+++ src/javascript.coffee
@@ -91,6 +91,7 @@
     'TryStatement': 'returns'
     'CallExpression': 'command'
     'SequenceExpression': 'command'
+    'Identifier': 'value'
   }

   # See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence


Use transChoice instead

Use transChoice instead

--- resources/assets/coffee/react/contest/voting/entry.coffee
+++ resources/assets/coffee/react/contest/voting/entry.coffee
@@ -65,9 +65,9 @@
       if @props.contest.show_votes
         if @props.contest.best_of
           div className:'contest__vote-count contest__vote-count--no-percentages',
-            "#{@props.entry.results.votes}", osu.trans("contest.vote.points")
+            osu.transChoice 'contest.vote.points', @props.entry.results.votes
         else
           div className:'contest__vote-count',
-            "#{@props.entry.results.votes}", osu.trans("contest.vote.votes")
+            osu.transChoice 'contest.vote.count', @props.entry.results.votes
             if isFinite(votePercentage)
               " (#{votePercentage}%)"


Disable emailConfirmationCheckerWorker

Disable emailConfirmationCheckerWorker

--- config/main.vagrant.coffee
+++ config/main.vagrant.coffee
@@ -76,7 +76,7 @@
     numberOfWorkers: 1
     watch       : yes
   emailConfirmationCheckerWorker :
-    enabled              : yes
+    enabled              : no
     login                : 'prod-social'
     queueName            : socialQueueName+'emailConfirmationCheckerWorker'
     numberOfWorkers      : 1


correcting counter value service

correcting counter value service

--- app/src/app.coffee
+++ app/src/app.coffee
@@ -198,6 +198,8 @@
           @dongle = null
         else if reroute
           @dongle?.lock()
+        else
+          ledger.tasks.TickerTask.instance.startIfNeccessary()
       ledger.dialogs.manager.dismissAll(no)
       @router.go '/onboarding/device/plug' if @isInWalletMode() and reroute


Removed unecessary helper

Removed unecessary helper

--- tests/cucumber/features/support/hooks.coffee
+++ tests/cucumber/features/support/hooks.coffee
@@ -1,16 +1,15 @@
 module.exports = ->
-    helper = @

     size =
         width: 800
         height: 600

-    @.Before ->
+    @.Before =>
         next = arguments[arguments.length - 1]
-        helper.world.browser.init()
+        @.world.browser.init()
             .setViewportSize(size)
             .call next

-    @.After ->
+    @.After =>
         next = arguments[arguments.length - 1]
-        helper.world.browser.end().call next+        @.world.browser.end().call next