cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -19,7 +19,7 @@

 player.cooldown "shootTimer"

-player.I.shootTimer =
+player.I.shootTimer = 10
 </pre></code>

 @name Cooldown


:fire: Remove return statements from linter-plus

--- lib/linter-plus.coffee
+++ lib/linter-plus.coffee
@@ -56,32 +56,32 @@
     @messages.deleteMessages(linter)

   getMessages: ->
-    return @messages.publicMessages
+    @messages.publicMessages

   onDidChangeMessages: (callback) ->
-    return @messages.onDidUpdateMessages(callback)
+    @messages.onDidUpdateMessages(callback)

   onDidChangeProjectMessages: (callback) ->
     deprecate("Linter::onDidChangeProjectMessages is deprecated, use Linter::onDidChangeMessages instead")
-    return @onDidChangeMessages(callback)
+    @onDidChangeMessages(callback)

   getProjectMessages: ->
     deprecate("Linter::getProjectMessages is deprecated, use Linter::getMessages instead")
-    return @getMessages()
+    @getMessages()

   setProjectMessages: (linter, messages) ->
     deprecate("Linter::setProjectMessages is deprecated, use Linter::setMessages instead")
-    return @setMessages(linter, messages)
+    @setMessages(linter, messages)

   deleteProjectMessages: (linter) ->
     deprecate("Linter::deleteProjectMessages is deprecated, use Linter::deleteMessages instead")
-    return @deleteMessages(linter)
+    @deleteMessages(linter)

   getActiveEditorLinter: ->
-    return @getEditorLinter atom.workspace.getActiveTextEditor()
+    @getEditorLinter atom.workspace.getActiveTextEditor()

   getEditorLinter: (editor) ->
-    return @editorLinters.get editor
+    @editorLinters.get editor

   eachEditorLinter: (callback) ->
     @editorLinters.forEach(callback)


turns out visitArray needs to both flatten and filter.  yuck

--- lib/nodevisitor.coffee
+++ lib/nodevisitor.coffee
@@ -15,17 +15,31 @@
                                 rv.push y
                 rv

+        # a rather disgusting in-place filter+flatten visitor
         visitArray: (arr) ->
                 i = 0
                 e = arr.length
                 while i < e
-                        arr[i] = @visit arr[i]
-                        if arr[i] is null
+                        tmp = @visit arr[i]
+                        if tmp is null
                                 arr.splice i, 1
                                 e = arr.length
+                        else if Array.isArray tmp
+                                tmplen = tmp.length
+                                if tmplen > 0
+                                        tmp.unshift 1
+                                        tmp.unshift i
+                                        arr.splice.apply arr, tmp
+                                        i += tmplen
+                                        e = arr.length
+                                else
+                                        arr.splice i, 1
+                                        e = arr.length
                         else
+                                arr[i] = tmp
                                 i += 1
                 arr
+

         visit: (n) ->
                 return null if not n?


Adjust loading indicator pulse

Adjust loading indicator pulse

--- assets/javascripts/views/loading_indicator.js.coffee
+++ assets/javascripts/views/loading_indicator.js.coffee
@@ -8,7 +8,7 @@
       @_pulseTimeout = setTimeout =>
         @$el.removeClass('pulse')
       , 1000
-    , 1200
+    , 1500

   hide: =>
     clearInterval @_pulseInterval


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/distance.coffee
+++ src/distance.coffee
@@ -1,4 +1,4 @@
-Flickerable = (I={}, self) ->
+Distance = (I={}, self) ->
   originalAlpha = I.alpha

   self.bind 'update', ->


fix help of linda-server  #2

fix help of linda-server  #2

--- bin/linda-server.coffee
+++ bin/linda-server.coffee
@@ -17,7 +17,7 @@
 parser.on 'help', ->
   package_json = require "#{__dirname}/../package.json"
   parser.banner = """
-  #{package_json.name} v#{package_json.version}
+  linda-server v#{package_json.version} - #{package_json.homepage}

   Usage:
     % linda-server


do not break blocks on lines with unchanged html

--- src/document.coffee
+++ src/document.coffee
@@ -107,13 +107,15 @@
       div = @root.ownerDocument.createElement('div')
       @root.appendChild(div)
     else
-      _.each(_.clone(@root.childNodes), (child) ->
+      _.each(_.clone(@root.childNodes), (child) =>
         if child.nodeType != child.ELEMENT_NODE
           child.parentNode.removeChild(child)
         else
-          Tandem.Utils.breakBlocks(child)
+          line = this.findLine(child)
+          if !line? || line.node.innerHTML != line.innerHTML
+            Tandem.Utils.breakBlocks(child)
       )
-    this.rebuildDirty()
+      this.rebuildDirty()

   printLines: ->
     lines = @lines.toArray()


Adiciona documentacao baseada no UIDoc

Adiciona documentacao baseada no UIDoc

--- src/coffee/vtex-message.coffee
+++ src/coffee/vtex-message.coffee
@@ -1,6 +1,11 @@
 root = exports ? window
 window.vtex or = {}

+###
+# Classe Message, que representa uma mensagem
+# @class Message
+# @constructor
+###
 class Message
 	constructor: (options = {}) ->
 		classes =
@@ -85,6 +90,12 @@

 		return

+	###
+	# Exibe a mensagem da tela
+	# @method show
+	# @param {Object|Number} fadeInObj caso preenchido, será passado como parametro para o método fadeIn do jQuery
+	# @return
+	###
 	show: (fadeInObj) =>
 		if @usingModal
 			visibleModal = $('.modal:visible')
@@ -110,6 +121,12 @@
 			@domElement.show()
 			@visible = true

+	###
+	# Esconde a mensagem da tela
+	# @method hide
+	# @param {Object|Number} fadeOutObj caso preenchido, será passado como parametro para o método fadeOut do jQuery
+	# @return
+	###
 	hide: (fadeOutObj) =>
 		if @usingModal
 			@domElement.modal('hide')
@@ -128,21 +145,44 @@
 		else
 			@domElement.hide()
 			@visible = false
-
+###
+# Classe Messages, que agrupa todas as mensagens
+# @class Messages
+# @constructor
+###
 class Messages
+	###
+	# Construtor
+	# @param {Object} options propriedades a ser extendida pelo plugin
+	# @return {Object} Messages
+	###
 	constructor: (options = {}) ->
-		defaultProperties = {}
+		defaultProperties =
+			ajaxError: false
+			messagesArray: []
 		_.extend(@, defaultProperties, options)
-		@messagesArray = []

 		@bindAjaxError() if @ajaxError

+	###
+	# Adiciona uma mensagem ao objeto Messages, exibe caso passado show como true
+	# @method addMessage
+	# @param {Object} message
+	# @param {Boolean} show boolean
+	# @returns {Object} retorna a instancia da Message criada
+	###
 	addMessage: (message, show = false) =>
 		messageObj = new Message(message)
 		@messagesArray.push messageObj
 		messageObj.show(show) if show
 		return messageObj

+	###
+	# Remove uma mensagem
+	# @method removeMessage
+	# @param {Object} messageProperty objeto Message ou objeto com alguma propriedade da mensagem a ser removida
+	# @returns
+	###
 	removeMessage: (messageProperty) =>
 		results = _.where(@messagesArray, messageProperty)
 		for message, i in @messagesArray
@@ -151,6 +191,12 @@
 					@messagesArray.splice(i,1)
 					return

+	###
+	# Bind erros de Ajax para exibir modal de erro
+	# @method bindAjaxError
+	# @param
+	# @returns
+	###
 	bindAjaxError: ->
 		$(document).ajaxError (event, xhr, ajaxOptions, thrownError) =>
 			return if xhr.status is 401 or xhr.status is 403
@@ -176,7 +222,7 @@
 					detail: errorMessage

 			if getCookie("ShowFullError") is "Value=1"
-				iframe = document.createElement('iframe')
+				iframe = document.createElement('iframe')
 				iframe.src = 'data:text/html;charset=utf-8,' + encodeURI(xhr.responseText)
 				$(iframe).css('width', '100%')
 				$(iframe).css('height', '900px')
@@ -184,6 +230,11 @@

 			@addMessage(messageObj, true)

+	###
+	# Get cookie
+	# @param {String} nome do cookie
+	# @returns {String|null} valor do cookie
+	###
 	getCookie = (name) ->
 		cookieValue = null
 		if document.cookie and document.cookie isnt ""


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/camera.coffee
+++ test/camera.coffee
@@ -3,8 +3,9 @@
 test "create", ->
   ok Camera()

-test "overlay", ->
-  object = GameObject
+test "overlay", 1, ->
+  object = GameObject()
+

 # Clear out the module
 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color2.coffee
+++ test/color2.coffee
@@ -87,4 +87,12 @@
   equal color.b, 51
   equal color.a, 0.3

+test "accepts hex value of length 6 as first argument and alpha value as second argument", ->
+  color = Color2('#de34ab', 0.27)
+
+  equal color.r, 222
+  equal color.g, 52
+  equal color.b, 171
+  equal color.a, 0.27
+
 module()


Small refactor to denote private methods.

--- lib/split-diff.coffee
+++ lib/split-diff.coffee
@@ -37,75 +37,82 @@
     @disable()
     @subscriptions.dispose()

-  # gets two visible editors
-  # auto opens new editors so there are two to diff with
-  getVisibleEditors: ->
-    editor1 = null
-    editor2 = null
-
-    panes = atom.workspace.getPanes()
-    for p in panes
-      activeItem = p.getActiveItem()
-      if atom.workspace.isTextEditor(activeItem)
-        if editor1 == null
-          editor1 = activeItem
-        else if editor2 == null
-          editor2 = activeItem
-          break
-
-    # auto open editor panes so we have two to diff with
-    if editor1 == null
-      editor1 = atom.workspace.buildTextEditor()
-      @wasEditor1Created = true
-      leftPane = atom.workspace.getActivePane()
-      leftPane.addItem(editor1)
-    if editor2 == null
-      editor2 = atom.workspace.buildTextEditor()
-      @wasEditor2Created = true
-      editor2.setGrammar(editor1.getGrammar())
-      rightPane = atom.workspace.getActivePane().splitRight()
-      rightPane.addItem(editor2)
-
-    editor1Path = editor1.getPath()
-    # only show git changes if the right editor is empty
-    if editor1Path? && (editor2.getLineCount() == 1 && editor2.lineTextForBufferRow(0) == '')
-      for directory, i in atom.project.getDirectories()
-        if editor1Path is directory.getPath() or directory.contains(editor1Path)
-          projectRepo = atom.project.getRepositories()[i]
-          if projectRepo?
-            relativeEditor1Path = projectRepo.relativize(editor1Path)
-            editor2.setText(projectRepo.repo.getHeadBlob(relativeEditor1Path))
-            @hasGitRepo = true
-            break
-
-    # unfold all lines so diffs properly align
-    editor1.unfoldAll()
-    editor2.unfoldAll()
-
-    # turn off soft wrap setting for these editors so diffs properly align
-    if editor1.isSoftWrapped()
-      @wasEditor1SoftWrapped = true
-      editor1.setSoftWrapped(false)
-    if editor2.isSoftWrapped()
-      @wasEditor2SoftWrapped = true
-      editor2.setSoftWrapped(false)
-
-    # want to scroll a newly created editor to the first editor's position
-    if @wasEditor2Created
-      atom.views.getView(editor1).focus()
-
-    editors =
-      editor1: editor1
-      editor2: editor2
-
-    return editors
+  # called by "toggle" command
+  # toggles split diff
+  toggle: ->
+    if @isEnabled
+      @disable(true)
+    else
+      @diffPanes()
+
+  # called by "Disable" command
+  # removes diff and sync scroll, disposes of subscriptions
+  disable: (displayMsg) ->
+    @isEnabled = false
+
+    if @editorSubscriptions?
+      @editorSubscriptions.dispose()
+      @editorSubscriptions = null
+
+    if @diffViewEditor1?
+      if @wasEditor1SoftWrapped
+        @diffViewEditor1.enableSoftWrap()
+      if @wasEditor1Created
+        @diffViewEditor1.cleanUp()
+
+    if @diffViewEditor2?
+      if @wasEditor2SoftWrapped
+        @diffViewEditor2.enableSoftWrap()
+      if @wasEditor2Created
+        @diffViewEditor2.cleanUp()
+
+    @_clearDiff()
+
+    @diffChunkPointer = 0
+    @isFirstChunkSelect = true
+    @wasEditor1SoftWrapped = false
+    @wasEditor1Created = false
+    @wasEditor2SoftWrapped = false
+    @wasEditor2Created = false
+    @hasGitRepo = false
+
+    if displayMsg
+      atom.notifications.addInfo('Split Diff Disabled', {dismissable: false})
+
+  # called by "toggle ignore whitespace" command
+  # toggles ignoring whitespace and refreshes the diff
+  toggleIgnoreWhitespace: ->
+    @_setConfig('ignoreWhitespace', !@isWhitespaceIgnored)
+    @isWhitespaceIgnored = @_getConfig('ignoreWhitespace')
+
+  # called by "Move to next diff" command
+  nextDiff: ->
+    if !@isFirstChunkSelect
+      @diffChunkPointer++
+      if @diffChunkPointer >= @linkedDiffChunks.length
+        @diffChunkPointer = 0
+    else
+      @isFirstChunkSelect = false
+
+    @_selectDiffs(@linkedDiffChunks[@diffChunkPointer])
+
+  # called by "Move to previous diff" command
+  prevDiff: ->
+    if !@isFirstChunkSelect
+      @diffChunkPointer--
+      if @diffChunkPointer < 0
+        @diffChunkPointer = @linkedDiffChunks.length - 1
+    else
+      @isFirstChunkSelect = false
+
+    @_selectDiffs(@linkedDiffChunks[@diffChunkPointer])

   # called by the command "enable" to do initial diff
   # sets up subscriptions for auto diff and disabling when a pane is destroyed
   diffPanes: ->
     @disable(false)

-    editors = @getVisibleEditors()
+    editors = @_getVisibleEditors()

     @editorSubscriptions = new CompositeDisposable()
     @editorSubscriptions.add editors.editor1.onDidStopChanging =>
@@ -151,15 +158,15 @@

     detailMsg = 'Ignore Whitespace: ' + @isWhitespaceIgnored
     detailMsg += '\nShow Word Diff: ' + @isWordDiffEnabled
-    detailMsg += '\nSync Horizontal Scroll: ' + @getConfig('syncHorizontalScroll')
+    detailMsg += '\nSync Horizontal Scroll: ' + @_getConfig('syncHorizontalScroll')
     atom.notifications.addInfo('Split Diff Enabled', {detail: detailMsg, dismissable: false})

   # called by both diffPanes and the editor subscription to update the diff
   # creates the scroll sync
   updateDiff: (editors) ->
     @isEnabled = true
-    @clearDiff()
-    @isWhitespaceIgnored = @getConfig('ignoreWhitespace')
+    @_clearDiff()
+    @isWhitespaceIgnored = @_getConfig('ignoreWhitespace')

     SplitDiffCompute = require './split-diff-compute'

@@ -176,83 +183,90 @@
       theOutput = err
     exit = (code) =>
       if code == 0
-        @resumeUpdateDiff(SplitDiffCompute, editors, computedDiff)
+        @_resumeUpdateDiff(SplitDiffCompute, editors, computedDiff)
       else
         console.log('BufferedNodeProcess code was ' + code)
         console.log(theOutput)
     process = new BufferedNodeProcess({command, args, stdout, stderr, exit})
     # --- kick off background process to compute diff ---

-  resumeUpdateDiff: (SplitDiffCompute, editors, computedDiff) ->
-    @linkedDiffChunks = @evaluateDiffOrder(computedDiff.chunks)
-
-    @displayDiff(editors, computedDiff)
-
-    @isWordDiffEnabled = @getConfig('diffWords')
+  # gets two visible editors
+  # auto opens new editors so there are two to diff with
+  _getVisibleEditors: ->
+    editor1 = null
+    editor2 = null
+
+    panes = atom.workspace.getPanes()
+    for p in panes
+      activeItem = p.getActiveItem()
+      if atom.workspace.isTextEditor(activeItem)
+        if editor1 == null
+          editor1 = activeItem
+        else if editor2 == null
+          editor2 = activeItem
+          break
+
+    # auto open editor panes so we have two to diff with
+    if editor1 == null
+      editor1 = atom.workspace.buildTextEditor()
+      @wasEditor1Created = true
+      leftPane = atom.workspace.getActivePane()
+      leftPane.addItem(editor1)
+    if editor2 == null
+      editor2 = atom.workspace.buildTextEditor()
+      @wasEditor2Created = true
+      editor2.setGrammar(editor1.getGrammar())
+      rightPane = atom.workspace.getActivePane().splitRight()
+      rightPane.addItem(editor2)
+
+    editor1Path = editor1.getPath()
+    # only show git changes if the right editor is empty
+    if editor1Path? && (editor2.getLineCount() == 1 && editor2.lineTextForBufferRow(0) == '')
+      for directory, i in atom.project.getDirectories()
+        if editor1Path is directory.getPath() or directory.contains(editor1Path)
+          projectRepo = atom.project.getRepositories()[i]
+          if projectRepo?
+            relativeEditor1Path = projectRepo.relativize(editor1Path)
+            editor2.setText(projectRepo.repo.getHeadBlob(relativeEditor1Path))
+            @hasGitRepo = true
+            break
+
+    # unfold all lines so diffs properly align
+    editor1.unfoldAll()
+    editor2.unfoldAll()
+
+    # turn off soft wrap setting for these editors so diffs properly align
+    if editor1.isSoftWrapped()
+      @wasEditor1SoftWrapped = true
+      editor1.setSoftWrapped(false)
+    if editor2.isSoftWrapped()
+      @wasEditor2SoftWrapped = true
+      editor2.setSoftWrapped(false)
+
+    # want to scroll a newly created editor to the first editor's position
+    if @wasEditor2Created
+      atom.views.getView(editor1).focus()
+
+    editors =
+      editor1: editor1
+      editor2: editor2
+
+    return editors
+
+  _resumeUpdateDiff: (SplitDiffCompute, editors, computedDiff) ->
+    @linkedDiffChunks = @_evaluateDiffOrder(computedDiff.chunks)
+
+    @_displayDiff(editors, computedDiff)
+
+    @isWordDiffEnabled = @_getConfig('diffWords')
     if @isWordDiffEnabled
-      @highlightWordDiff(SplitDiffCompute, @linkedDiffChunks)
-
-    syncHorizontalScroll = @getConfig('syncHorizontalScroll')
+      @_highlightWordDiff(SplitDiffCompute, @linkedDiffChunks)
+
+    syncHorizontalScroll = @_getConfig('syncHorizontalScroll')
     @syncScroll = new SyncScroll(editors.editor1, editors.editor2, syncHorizontalScroll)
     @syncScroll.syncPositions()

-  # called by "Disable" command
-  # removes diff and sync scroll, disposes of subscriptions
-  disable: (displayMsg) ->
-    @isEnabled = false
-
-    if @editorSubscriptions?
-      @editorSubscriptions.dispose()
-      @editorSubscriptions = null
-
-    if @diffViewEditor1?
-      if @wasEditor1SoftWrapped
-        @diffViewEditor1.enableSoftWrap()
-      if @wasEditor1Created
-        @diffViewEditor1.cleanUp()
-
-    if @diffViewEditor2?
-      if @wasEditor2SoftWrapped
-        @diffViewEditor2.enableSoftWrap()
-      if @wasEditor2Created
-        @diffViewEditor2.cleanUp()
-
-    @clearDiff()
-
-    @diffChunkPointer = 0
-    @isFirstChunkSelect = true
-    @wasEditor1SoftWrapped = false
-    @wasEditor1Created = false
-    @wasEditor2SoftWrapped = false
-    @wasEditor2Created = false
-    @hasGitRepo = false
-
-    if displayMsg
-      atom.notifications.addInfo('Split Diff Disabled', {dismissable: false})
-
-  # called by "Move to next diff" command
-  nextDiff: ->
-    if !@isFirstChunkSelect
-      @diffChunkPointer++
-      if @diffChunkPointer >= @linkedDiffChunks.length
-        @diffChunkPointer = 0
-    else
-      @isFirstChunkSelect = false
-
-    @selectDiffs(@linkedDiffChunks[@diffChunkPointer])
-
-  # called by "Move to previous diff" command
-  prevDiff: ->
-    if !@isFirstChunkSelect
-      @diffChunkPointer--
-      if @diffChunkPointer < 0
-        @diffChunkPointer = @linkedDiffChunks.length - 1
-    else
-      @isFirstChunkSelect = false
-
-    @selectDiffs(@linkedDiffChunks[@diffChunkPointer])
-
-  selectDiffs: (diffChunk) ->
+  _selectDiffs: (diffChunk) ->
     if diffChunk? && @diffViewEditor1? && @diffViewEditor2?
       @diffViewEditor1.deselectAllLines()
       @diffViewEditor2.deselectAllLines()
@@ -265,7 +279,7 @@
         @diffViewEditor2.scrollToLine(diffChunk.newLineStart)

   # removes diff and sync scroll
-  clearDiff: ->
+  _clearDiff: ->
     if @diffViewEditor1?
       @diffViewEditor1.destroyMarkers()
       @diffViewEditor1 = null
@@ -279,12 +293,12 @@
       @syncScroll = null

   # displays the diff visually in the editors
-  displayDiff: (editors, computedDiff) ->
+  _displayDiff: (editors, computedDiff) ->
     @diffViewEditor1 = new DiffViewEditor(editors.editor1)
     @diffViewEditor2 = new DiffViewEditor(editors.editor2)

-    leftColor = @getConfig('leftEditorColor')
-    rightColor = @getConfig('rightEditorColor')
+    leftColor = @_getConfig('leftEditorColor')
+    rightColor = @_getConfig('rightEditorColor')
     if leftColor == 'green'
       @diffViewEditor1.setLineHighlights(computedDiff.removedLines, 'added')
     else
@@ -297,7 +311,8 @@
     @diffViewEditor1.setLineOffsets(computedDiff.oldLineOffsets)
     @diffViewEditor2.setLineOffsets(computedDiff.newLineOffsets)

-  evaluateDiffOrder: (chunks) ->
+  # puts the chunks into order so nextDiff and prevDiff are in order
+  _evaluateDiffOrder: (chunks) ->
     oldLineNumber = 0
     newLineNumber = 0
     prevChunk = null
@@ -362,9 +377,9 @@
     return diffChunks

   # highlights the word differences between lines
-  highlightWordDiff: (SplitDiffCompute, chunks) ->
-    leftColor = @getConfig('leftEditorColor')
-    rightColor = @getConfig('rightEditorColor')
+  _highlightWordDiff: (SplitDiffCompute, chunks) ->
+    leftColor = @_getConfig('leftEditorColor')
+    rightColor = @_getConfig('rightEditorColor')
     for c in chunks
       # make sure this chunk matches to another
       if c.newLineStart? && c.oldLineStart?
@@ -417,23 +432,9 @@
           else
             @diffViewEditor1.setWordHighlights(c.oldLineStart + i, [{changed: true, value: @diffViewEditor1.getLineText(c.oldLineStart + i)}], 'removed', @isWhitespaceIgnored)

-  # called by "toggle ignore whitespace" command
-  # toggles ignoring whitespace and refreshes the diff
-  toggleIgnoreWhitespace: ->
-    @setConfig('ignoreWhitespace', !@isWhitespaceIgnored)
-    @isWhitespaceIgnored = @getConfig('ignoreWhitespace')
-
-  # called by "toggle" command
-  # toggles split diff
-  toggle: ->
-    if @isEnabled
-      @disable(true)
-    else
-      @diffPanes()
-
-
-  getConfig: (config) ->
+
+  _getConfig: (config) ->
     atom.config.get("split-diff.#{config}")

-  setConfig: (config, value) ->
+  _setConfig: (config, value) ->
     atom.config.set("split-diff.#{config}", value)


Save serialized text to input element

Save serialized text to input element

--- src/trix/controllers/editor_controller.coffee
+++ src/trix/controllers/editor_controller.coffee
@@ -3,23 +3,28 @@
 #= require trix/controllers/debug_controller

 class Trix.EditorController
-  constructor: (textElement, toolbarElement, inputElement, debugElement) ->
-    @textController = new Trix.TextController textElement, createText(inputElement)
+  constructor: (textElement, toolbarElement, @inputElement, debugElement) ->
+    @text = @createText()
+    @textController = new Trix.TextController textElement, @text
     @textController.delegate = this
     @toolbarController = new Trix.ToolbarController toolbarElement
     @toolbarController.delegate = this
     @debugController = new Trix.DebugController debugElement, @textController
     @debugController.render()

-  createText = (inputElement) ->
-    if inputElement?.value
-      Trix.Text.fromJSON(inputElement.value)
+  createText: ->
+    if @inputElement?.value
+      Trix.Text.fromJSON(@inputElement.value)
     else
       new Trix.Text
+
+  saveSerializedText: ->
+    @inputElement?.value = @text.asJSON()

   # Text controller delegate

   textControllerDidRender: ->
+    @saveSerializedText()
     @debugController.render()

   textControllerDidFocus: ->


updated theme manager event handler

updated theme manager event handler

--- lib/timekeeper.coffee
+++ lib/timekeeper.coffee
@@ -3,7 +3,7 @@
 url = require "url"

 # atom
-{$} = require "atom"
+{$} = require 'atom'

 # timerkeeper
 Timer = require "./timer.coffee"
@@ -77,7 +77,7 @@
                 @timer.autopause()

         # Watch for theme changes/reloads to reload our view
-        atom.themes.on "reloaded", =>
+        atom.themes.onDidChangeActiveThemes =>
             # Reset the view
             @timekeeperView = null


Code to help in possible directory traversal issues.

--- lib/transpiler.coffee
+++ lib/transpiler.coffee
@@ -200,6 +200,10 @@
     absTranspiledFile = path.join(absTranspileRoot, relSourceRootToSourceFile , parsedSourceFile.name  + '.js')
     absMapFile = path.join(absMapsRoot, relSourceRootToSourceFile , parsedSourceFile.name  + '.js.map')

+    # Code to assist when potential path traversal issues may arise
+    if sourceFile.indexOf absProjectPath != 0
+      console.log "LANGUAGE-BABEL\nProject Dir is %s\nSource File is %s", absProjectPath, sourceFile
+
     sourceFile: sourceFile
     sourceFileDir: parsedSourceFile.dir
     mapFile: absMapFile


fix log

fix log

--- lib/remote-atom.coffee
+++ lib/remote-atom.coffee
@@ -66,9 +66,10 @@
         socket.on "data", (chunk) =>
             @parse_chunk(chunk)
         socket.on "close", =>
-            @alive = false
-            console.log "[ratom] connection lost!"
-            status-message.display "Connection lost!", 5000
+            if @alive
+                @alive = false
+                console.log "[ratom] connection lost!"
+                status-message.display "Connection lost!", 5000

     parse_chunk: (chunk) ->
         if chunk
@@ -122,9 +123,8 @@

     close: ->
         @nconn -= 1
-        console.log @nconn
         if @alive and @nconn == 0
-            @online = false
+            @alive = false
             @send "close"
             @send ""
             @socket.end()
@@ -172,6 +172,7 @@
         @server.on 'listening', (e) =>
             @server_is_running = true
             console.log "[ratom] listening on port #{port}"
+
         @server.on 'error', (e) =>
             if not quiet
                 status-message.display "Unable to start server", 2000
@@ -183,6 +184,7 @@

         @server.on "close", () ->
             console.log "[ratom] stop server"
+
         @server.listen port, 'localhost'

     stop_server: ->


Follow mixin

Follow mixin

--- test/follow.coffee
+++ test/follow.coffee
@@ -7,6 +7,6 @@
     width: 10
     height: 10

-
+  enemy = G

 module()


Ensure result of indexOf >= 0 before using

Calling view.unbind() more that once will result in the callback not
being found (indexOf returns -1).

Calling splice with an index of -1 will remove the last item in the
list.

Instead, check the result if indexOf before using it and skip the
splice when the callback was not found.

--- src/adapters.coffee
+++ src/adapters.coffee
@@ -47,7 +47,9 @@
   unobserveMutations: (obj, ref, keypath) ->
     if Array.isArray obj and obj[@id]?
       if keypaths = @weakReference(obj).pointers?[ref]
-        keypaths.splice keypaths.indexOf(keypath), 1
+        idx = keypaths.indexOf(keypath)
+        if idx >= 0
+          keypaths.splice idx, 1

   subscribe: (obj, keypath, callback) ->
     callbacks = @weakReference(obj).callbacks
@@ -72,7 +74,10 @@

   unsubscribe: (obj, keypath, callback) ->
     callbacks = @weakmap[obj[@id]].callbacks[keypath]
-    callbacks.splice callbacks.indexOf(callback), 1
+
+    idx = callbacks.indexOf(callback);
+    if idx >= 0
+      callbacks.splice idx, 1
     @unobserveMutations obj[keypath], obj[@id], keypath

   read: (obj, keypath) ->


dependancies spec

dependancies spec

--- dev/specs/00-DependanciesAvailable-Spec.coffee
+++ dev/specs/00-DependanciesAvailable-Spec.coffee
@@ -1,39 +1,18 @@
 # http://jasmine.github.io/2.1/introduction.html

-describe "Checks something cool", ->
+describe "Checks required object are abailable", ->

-  beforeAll (done)->
-    alert 123456
-    done()
+  it "checks cordova FS is defined", ->
+    expect window.requestFileSystem
+    .toBeDefined()
+    return

-  it "checks window is an object", ->
-    expect window
+  it "checks CryptoJS object is defined", ->
+    expect CryptoJS
     .toBeDefined()
+    return

-describe "Checks something cool", ->
-  it "checks window is an object", ->
-    expect window
+  it "checks SI.cordovaCache is defined", ->
+    expect SI.cordovaCache
     .toBeDefined()
-
-
-describe "Checks something cool", ->
-  it "checks window is an object", ->
-    expect window
-    .toBeDefined()
-
-describe "Checks something cool", ->
-  it "checks window is an object", ->
-    expect window
-    .toBeDefined()
-
-
-###
-   document.addEventListener 'deviceready', ->
-  SI.cordovaCache 'SomeThing' , (Cache, Crypt)->
-
-
-  expect( typeof window.SI ).toBe("object");
-  expect( typeof CryptoJS.AES ).toBe("object");
-  expect( typeof window.requestFileSystem ).toBe("function");
-  expect( typeof Crypt ).toBe("object");
-###+    return


Fixes cancelAnimationFrame return value

Fixes cancelAnimationFrame return value

--- src/core/animationframe.coffee
+++ src/core/animationframe.coffee
@@ -31,4 +31,4 @@
     unless cancelAnimationFrame
       cancelAnimationFrame = (id) -> root.clearTimeout id

-    requestAnimationFrame+    cancelAnimationFrame


improved stack again

improved stack again

--- lib/unit_runner.coffee
+++ lib/unit_runner.coffee
@@ -49,7 +49,8 @@
       error = e

     promise.fail (err) ->
-      err.stack += "\n" + e.stack
+      stack = error.stack
+      err.stack += "\n" + stack.substring(stack.indexOf("\n") + 1)
       throw err

     promise.finally @taskDone


Snippet for Num implementation

Snippet for Num implementation

--- snippets/snippets.cson
+++ snippets/snippets.cson
@@ -28,6 +28,14 @@
     'body': """
       Monoid ${1:Type} where
         neutral = ?holeMonoid
+      """
+  'Num implementation':
+    'prefix': 'Num'
+    'body': """
+      Num ${1:Type} where
+        (+) a b = ?holePlus
+        (*) a b = ?holeMult
+        fromInteger i = ?holeFromInteger
       """
   'Functor implementation':
     'prefix': 'Functor'


update .ssf3-2 to .ssf_3-2

update .ssf3-2 to .ssf_3-2


Include ?ref in URLCleaner stripping.

Include ?ref in URLCleaner stripping.

--- client/URLCleaner.coffee
+++ client/URLCleaner.coffee
@@ -8,7 +8,7 @@
     # Remove any parameters that start with `utm_` using replaceState.
     _stripParameters = ->
         if window.history?.replaceState?
-            cleaned_search = window.location.search.replace(/utm_[^&]+&?/g, '').replace(/&$/, '').replace(/^\?$/, '')
+            cleaned_search = window.location.search.replace(/utm_[^&]+&?/g, '').replace(/ref=[^&]+&?/g, '').replace(/&$/, '').replace(/^\?$/, '')
             window.history.replaceState({}, '', window.location.pathname + cleaned_search)
         return


Take threshold into account for choosing weaker

--- src/flux/learning-guide.coffee
+++ src/flux/learning-guide.coffee
@@ -97,11 +97,14 @@

 Helpers = {

-  filterForecastedSections: (sections) ->
-    _.filter(sections, (s) -> s.clue.sample_size_interpretation isnt 'below')
+  canDisplayForecast: (clue, sampleSizeThreshold) ->
+    clue.sample_size >= sampleSizeThreshold or clue.sample_size_interpretation isnt 'below'

-  weakestSections: (sections, displayCount = 4) ->
-    validSections = @filterForecastedSections(sections)
+  filterForecastedSections: (sections, sampleSizeThreshold) ->
+    _.filter(sections, (s) -> Helpers.canDisplayForecast(s.clue, sampleSizeThreshold) )
+
+  weakestSections: (sections, sampleSizeThreshold, displayCount = 4) ->
+    validSections = @filterForecastedSections(sections, sampleSizeThreshold)
     displayCount = Math.min(Math.floor(validSections.length / 2), displayCount)

     # Sort by value and pick 'displayCount' of the weakest
@@ -110,10 +113,10 @@
       .first(displayCount)
       .value()

-  canPractice: ({sections, displayCount, minimumSectionCount}) ->
+  canPractice: ({sections, sampleSizeThreshold, displayCount, minimumSectionCount}) ->
     displayCount ||= 4
     minimumSectionCount ||= 2
-    @weakestSections(sections, displayCount).length >= minimumSectionCount
+    @weakestSections(sections, sampleSizeThreshold, displayCount).length >= minimumSectionCount

   pagesForSections: (sections) ->
     _.chain(sections).pluck('page_ids').flatten().uniq().value()


Update atom config.cson

Update atom config.cson

--- atom/.atom/config.cson
+++ atom/.atom/config.cson
@@ -50,7 +50,10 @@
     showInvisibles: true
     softWrap: true
     softWrapAtPreferredLineLength: true
-    tabLength: 4
+    tabLength: 2
+  emmet:
+    extensionsPath: "~/.emmet"
+    formatLineBreaks: false
   "exception-reporting":
     userId: "b9301e45-0712-4ee7-ba90-a7e3f3260eb9"
   "file-icons":
@@ -63,4 +66,4 @@
 ".ini.source":
   editor:
     preferredLineLength: 120
-    tabLength: 4
+    showIndentGuide: false


fixed merge conflict

fixed merge conflict


Update ignites and Magneto

Update ignites and Magneto

--- server/create-agenda.coffee
+++ server/create-agenda.coffee
@@ -27,14 +27,14 @@
   AgendaItem.create
     time: new Date("Mar 11 2015 12:00:00 GMT+0200")
     class1: "ZcN9QbmsfAfLknk5a"
-    class2: "b3f2SCm3fpu6TB37w"
+    class2: "G55eQaRNcbRPvJhSu"
   AgendaItem.create
     time: new Date("Mar 11 2015 12:40:00 GMT+0200")
     class1: "Lunch"
     icon1: "cutlery"
   AgendaItem.create
     time: new Date("Mar 11 2015 13:40:00 GMT+0200")
-    class1: "Ignites"
+    class1: "Ignites,9hftG3biQTENTeP9K,E8Mfns6GXTj6jf4J6,FB9ADuP6pDqcGf7tq,GehMDtzJSNFLna7A6,Ntkq6XYBcbiDCtfgb"
     icon1: "fire"
     class2: "GK8QNv6vhAb763iwZ"
   AgendaItem.create
@@ -107,7 +107,7 @@
     icon1: "cutlery"
   AgendaItem.create
     time: new Date("Mar 12 2015 13:40:00 GMT+0200")
-    class1: "Ignites"
+    class1: "Ignites,QAccw5PDNSqFXPX4M,XFjdSijm82Y4Afngm,fQ8Eg8MnimDJufg4f,AS8o7CgACMLh3qGdt,NrHZHCukNczgwpqQf,fy5n9vFiX7hjNQk63"
     icon1: "fire"
     class2: "jrueTTCGDcuk6vCtp"
   AgendaItem.create
@@ -120,7 +120,7 @@
     icon1: "coffee"
   AgendaItem.create
     time: new Date("Mar 12 2015 15:20:00 GMT+0200")
-    class1: "Open Source in Israel,yFpX9vJpPxWD9EsoG,YapN2NL5xCdJoNFkq,ev4ERxP7qPc7S9ai9,Dekx6XaujGpHCFmWu,G55eQaRNcbRPvJhSu"
+    class1: "Open Source in Israel,yFpX9vJpPxWD9EsoG,YapN2NL5xCdJoNFkq,ev4ERxP7qPc7S9ai9,Dekx6XaujGpHCFmWu"
     icon1: "code-fork"
     class2: "qbT7S6CaDdKADJgLL"
   AgendaItem.create


missing return  -- dumb callback hell error...

--- src/smc-hub/user-remember-me.coffee
+++ src/smc-hub/user-remember-me.coffee
@@ -24,6 +24,7 @@
         cb   : (err, signed_in_mesg) ->
             if err
                 cb(err)
+                return
             remember_me_cache.set(remember_me, signed_in_mesg?.account_id)
             if not signed_in_mesg?
                 cb('not signed in')


fix  https://github.com/sagemathinc/cocalc/issues/3506a - too many sage servers started at once

--- src/smc-project/sage_session.coffee
+++ src/smc-project/sage_session.coffee
@@ -62,7 +62,7 @@
 # Get a new connection to the Sage server.  If the server
 # isn't running, e.g., it was killed due to running out of memory,
 # attempt to restart it and try to connect.
-exports.get_sage_socket = (cb) ->   # cb(err, socket)
+get_sage_socket = (cb) ->   # cb(err, socket)
     socket = undefined
     try_to_connect = (cb) ->
         _get_sage_socket (err, _socket) ->
@@ -176,13 +176,24 @@
     is_running: () =>
         return @_socket?

+    # NOTE: There can be many simultaneous init_socket calls at the same time,
+    # if e.g., the socket doesn't exist and there are a bunch of calls to @call
+    # at the same time.
+    # See https://github.com/sagemathinc/cocalc/issues/3506
     init_socket: (cb) =>
         dbg = @dbg('init_socket()')
         dbg()
-        exports.get_sage_socket (err, socket) =>
+        if @_init_socket_cbs?
+            @_init_socket_cbs.push(cb)
+            return
+        @_init_socket_cbs = [cb]
+        get_sage_socket (err, socket) =>
             if err
                 dbg("fail -- #{err}.")
-                cb(err)
+                cbs = @_init_socket_cbs
+                delete @_init_socket_cbs
+                for c in cbs
+                    c(err)
                 return

             dbg("successfully opened a sage session")
@@ -199,7 +210,11 @@
                 dbg("sage session: received message #{type}")
                 @["_handle_mesg_#{type}"]?(mesg)

-            @_init_path(cb)
+            @_init_path (err) =>
+                cbs = @_init_socket_cbs
+                delete @_init_socket_cbs
+                for c in cbs
+                    c(err)

     _init_path: (cb) =>
         dbg = @dbg("_init_path()")


add common timezones to the top of the tz list

This list is displayed at register time

--- server/lib/timezones.coffee
+++ server/lib/timezones.coffee
@@ -1,4 +1,10 @@
 module.exports = [
+    "Europe/Paris"
+    "Europe/Berlin"
+    "Europe/Madrid"
+    "Europe/Rome"
+    "America/Los_Angeles"
+    "America/New_York"
     "Africa/Abidjan"
     "Africa/Accra"
     "Africa/Addis_Ababa"


activity: render load more marker after chat item

--- client/activity/lib/components/chatlist/index.coffee
+++ client/activity/lib/components/chatlist/index.coffee
@@ -82,8 +82,24 @@
     return markers

+  getAfterMarkers: (currentMessage, prevMessage, index) ->

+    { channelId, isMessagesLoading } = @props

+    markers = []
+
+    if loaderMarkers = currentMessage.get 'loaderMarkers'
+      if afterMarker = loaderMarkers.get 'after'
+        markers.push \
+          <LoadMoreMessagesMarker
+            channelId={channelId}
+            messageId={currentMessage.get 'id'}
+            position="after"
+            autoload={afterMarker.get 'autoload'}
+            timestamp={currentMessage.get 'createdAt'}
+            isLoading={isMessagesLoading} />
+
+    return markers

   renderChildren: ->
@@ -116,6 +132,8 @@
         children.push \
           <ChatListItem {...itemProps} />

+      children = children.concat @getAfterMarkers message, prevMessage, i
+
       prevMessage = message
       return children
     , []


activity: add missing key properties

activity: add missing key properties

--- client/activity/lib/components/chatlist/index.coffee
+++ client/activity/lib/components/chatlist/index.coffee
@@ -83,6 +83,7 @@
       if beforeMarker = loaderMarkers.get 'before'
         markers.push \
           <LoadMoreMessagesMarker
+            key="loadafter-#{currentMessage.get 'id'}"
             channelId={channelId}
             messageId={currentMessage.get 'id'}
             position="before"
@@ -92,12 +93,18 @@
     switch
       # if this is first message put a date marker no matter what.
       when not prevMessage
-        markers.push <DateMarker date={currentMessage.get 'createdAt'} />
+        markers.push \
+          <DateMarker
+            key={currentMessage.get 'createdAt'}
+            date={currentMessage.get 'createdAt'} />

       # if day of previous message is not the same with current one, put a date
       # marker.
       when not currentMessageMoment.isSame prevMessageMoment, 'day'
-        markers.push <DateMarker date={currentMessage.get 'createdAt'} />
+        markers.push \
+          <DateMarker
+            key={currentMessage.get 'createdAt'}
+            date={currentMessage.get 'createdAt'} />

     # put new message marker on top of other messages if unread count is
     # greater than currently loaded messages.
@@ -124,6 +131,7 @@
       if afterMarker = loaderMarkers.get 'after'
         markers.push \
           <LoadMoreMessagesMarker
+            key="loadafter-#{currentMessage.get 'id'}"
             channelId={channelId}
             messageId={currentMessage.get 'id'}
             position="after"


Client: handle acces denied errors

Client: handle acces denied errors

--- client/admin/lib/views/members/memberitemview.coffee
+++ client/admin/lib/views/members/memberitemview.coffee
@@ -116,7 +116,7 @@

       group.fetchUserRoles [ jAccount.getId() ], (err, roles) =>

-        return @handleError button  if err
+        return @handleError button, err  if err

         data        = @getData()
         data.roles  = (role.as for role in roles)
@@ -135,10 +135,12 @@
     @createSettingsView()

-  handleError: (button, message) ->
+  handleError: (button, err) ->
+    if err?.message is 'Access denied'
+      return global.location.href = '/Activity'

     button.hideLoader()
-    message or= 'Failed to change user role. Please try again.'
+    message = err?.message or 'Failed to change user role. Please try again.'
     return new KDNotificationView title: message, duration: 5000

@@ -146,7 +148,7 @@

     kd.singletons.groupsController.getCurrentGroup().kickMember @getData().getId(), (err) =>

-      return @handleError @actionButtons.kick, 'Failed to kick user. Please try again.'  if err
+      return @handleError @actionButtons.kick, new Error 'Failed to kick user. Please try again.'  if err

       @destroy()


Watch mode guide link

Watch mode guide link

--- client/ide/lib/views/chat/idechatsettingspane.coffee
+++ client/ide/lib/views/chat/idechatsettingspane.coffee
@@ -122,7 +122,7 @@
     @createMuteHostSettingElements()

-  GUIDE_LINK = 'http://learn.koding.com/guides/collaboration/#what-does-quot-watch-quot-mode-mean-'
+  WATCH_MODE_GUIDE_LINK = 'http://learn.koding.com/guides/collaboration/#watch_mode'

   createUnwatchSettingElements: ->

@@ -140,7 +140,7 @@
     @unwatchWrapper.addSubView new KDCustomHTMLView
       tagName    : 'a'
       attributes :
-        href     : GUIDE_LINK
+        href     : WATCH_MODE_GUIDE_LINK
         target   : '_blank'
       partial    : '(?)'

@@ -334,8 +334,11 @@
       {{> @everyone}}

-        Have sessions with people you trust, and remember, \
-        they can view and edit all your files!
+        Have sessions with people you trust, and remember,
+
+            they can view and edit all your files!
+
+

       {{> @settings}}


Go back to gear as default icon

Go back to gear as default icon

--- src/lib/ComponentLoader.coffee
+++ src/lib/ComponentLoader.coffee
@@ -193,7 +193,7 @@
       instance.setIcon 'sitemap'
       return

-    instance.setIcon 'square'
+    instance.setIcon 'gear'
     return

   getLibraryIcon: (prefix) ->


Cleaning tests.

Cleaning tests.

--- tests/tests.coffee
+++ tests/tests.coffee
@@ -30,13 +30,13 @@

 BlazeComponent.register 'MainComponent', MainComponent

-class @FooComponent extends BlazeComponent
+class FooComponent extends BlazeComponent
   template: ->
     'FooComponent'

 BlazeComponent.register 'FooComponent', FooComponent

-class @SelfRegisterComponent extends BlazeComponent
+class SelfRegisterComponent extends BlazeComponent
   # Alternative way of registering components.
   @register 'SelfRegisterComponent'

@@ -298,7 +298,7 @@
     @assertThrows =>
       BlazeComponent.register 'OtherMainComponent', MainComponent
     ,
-      /Component 'OtherMainComponent' already registered under the name 'MainComponent/
+      /Component 'OtherMainComponent' already registered under the name 'MainComponent'/

     class WithoutTemplateComponent extends BlazeComponent
       @componentName 'WithoutTemplateComponent'


[grunt] fix issue with pages, which doesn't have any props

--- tasks/templates.coffee
+++ tasks/templates.coffee
@@ -340,7 +340,7 @@
           pagepath     = humanReadableUrl(@src[0].replace(templatesDir, ''), taskConfig.humanReadableUrls.exclude)
           breadcrumb   = crumble(pagepath)
           matterData   = grunt.file.readJSON(taskConfig.files.matter)
-          pageProps    = _.get(matterData, breadcrumb).props
+          pageProps    = (_.get(matterData, breadcrumb) or {}).props

           _.set data, 'site.__matter__', matterData


parse clicktracking: object ids as int

parse clicktracking: object ids as int

--- code/import/clicktracking/parse_clicktracking.coffee
+++ code/import/clicktracking/parse_clicktracking.coffee
@@ -44,7 +44,7 @@
 parseCl = (input, type) ->
     if x = input.cl
         objects = x.slice(1, -1).split(', ').filter((z) -> z.charAt(0) == type)
-        objects[0].slice(1)
+        parseInt(objects[0].slice(1))
     else null

 parsePlatform = (input) ->


add jump button

add jump button

--- scrolling/input-controller.coffee
+++ scrolling/input-controller.coffee
@@ -17,3 +17,4 @@
         right: @keyCache['D']
         up: @keyCache['W']
         down: @keyCache['S']
+        jump: @keyCache[' ']


Update UI: Make info panel header clickable.

--- views/battle-info-area.cjsx
+++ views/battle-info-area.cjsx
@@ -166,8 +166,9 @@

         info.push

+    # Use

 as panel header to make the whole title clickable.

-      <Panel header={__ "Battle Information"} collapsible>
+      <Panel collapsible header={

{__ "Battle Information"}

} >
         {
           if info.length > 0
             info


Basic functional testing of jupyterPath

Basic functional testing of jupyterPath

--- spec/paths-spec.coffee
+++ spec/paths-spec.coffee
@@ -1,3 +1,5 @@
+path = require('path')
+
 describe "JupyterPath", ->
   # Because `paths` shells out to Python, we need a chance to mock it first
   [jupyterPath, jupyterConfigDir, jupyterDataDir, jupyterRuntimeDir] = []
@@ -8,4 +10,18 @@

   describe "when jupyterPath is called", ->
     it "it returns an array of path strings", ->
-      expect(2).toBe(2)
+      kernelspecs = jupyterPath()
+      for kernelspec in kernelspecs
+        expect(typeof(kernelspec)).toBe('string')
+
+        parsed = path.parse(kernelspec)
+
+        expect(parsed.root).toBe('/')
+        expect(parsed.dir)
+
+  describe "when jupyterPath is called with kernels", ->
+    it "has 'kernels' at the end of each path", ->
+      kernelspecs = jupyterPath('kernels')
+      for kernelspec in kernelspecs
+        parsed = path.parse(kernelspec)
+        expect(parsed.base).toBe('kernels')


the Document I was looking for

the Document I was looking for

--- services/data/schema.coffee
+++ services/data/schema.coffee
@@ -21,7 +21,7 @@
     @singular: 'measurement'
     @collection: 'measurements'
     @coerce: (item) ->
-        item = new Document item
+        item = new orm.Document item
         item.created_at = item.createdAt()
         item


Removed console.logs in selector.js most recent additions.

--- app/assets/javascripts/selector.js.coffee
+++ app/assets/javascripts/selector.js.coffee
@@ -343,13 +343,8 @@
         updateSource = $(this)
           .closest($(this).data("selector-use-closest"))
           .find("*[data-selector-id='"+$(this).data("selector-update")+"']")[0]
-        console.log updateSource
-        console.log changeEvent.target
         if updateSource == changeEvent.target
-          console.log(changeEvent)
           if value
-            console.log(value)
-            console.log($(value).data("item-"+$(this).data("selector-update-with")))
             $(this).html $(value).data("item-"+$(this).data("selector-update-with"))
   return
 ) ekylibre, jQuery


remove changes related to emacs-plus

remove changes related to emacs-plus

--- keymap.cson
+++ keymap.cson
@@ -1,10 +1,10 @@
-'.platform-win32 atom-text-editor, .platform-linux atom-text-editor':
-  'ctrl-e': 'editor:move-to-end-of-screen-line'
-  'ctrl-shift-e': 'editor:select-to-end-of-line'
-  'alt-shift-e': 'find-and-replace:use-selection-as-find-pattern'  # was ctrl-e
+# '.platform-win32 atom-text-editor, .platform-linux atom-text-editor':
+#   'ctrl-e': 'editor:move-to-end-of-screen-line'
+#   'ctrl-shift-e': 'editor:select-to-end-of-line'
+#   'alt-shift-e': 'find-and-replace:use-selection-as-find-pattern'  # was ctrl-e
 # emacs-plus redefines ctrl-d but doesn't override find-and-replace:select-next
 # for these platforms
-  'ctrl-d': 'core:delete'
+  # 'ctrl-d': 'core:delete'

 '.platform-darwin atom-text-editor:not([mini])':
   'cmd-shift-v': 'clipboard-plus:toggle'
@@ -16,10 +16,10 @@
   'ctrl-shift-v': 'clipboard-plus:toggle'

 'atom-workspace atom-text-editor':
-  'alt-shift-a': 'core:select-all'  # was ctrl-a
-  'ctrl-a': 'editor:move-to-first-character-of-line'
-  'ctrl-shift-a': 'editor:select-to-first-character-of-line'
-  'ctrl-/': 'core:undo'
+  # 'alt-shift-a': 'core:select-all'  # was ctrl-a
+  # 'ctrl-a': 'editor:move-to-first-character-of-line'
+  # 'ctrl-shift-a': 'editor:select-to-first-character-of-line'
+  # 'ctrl-/': 'core:undo'
   'alt-shift-i': 'window:toggle-invisibles'

 # great. ctrl-k is delete-to-end-of-line in emacs


Fix ctrl-/,ctrl-p,new key for fuzzy-finder

--- keymap.cson
+++ keymap.cson
@@ -7,6 +7,7 @@
   'alt-shift-a': 'core:select-all'  # was ctrl-a
   'ctrl-a': 'editor:move-to-first-character-of-line'
   'ctrl-shift-a': 'editor:select-to-first-character-of-line'
+  'ctrl-/': 'core:undo'

 # great. ctrl-k is delete-to-end-of-line in emacs
 # in atom it's a prefix key, so we'd have to remap a lot of keys
@@ -19,3 +20,11 @@
   'alt-m ctrl-b': 'atom-keyboard-macros:execute_macro_from_top_to_bottom'
   'alt-m ctrl-e': 'atom-keyboard-macros:repeat_last_kbd_macro'
   'alt-m e': 'atom-keyboard-macros:call_last_kbd_macro'
+
+# override fuzzy-finder
+'body':
+  'ctrl-p': 'core:move-up'
+
+# meanwhile emacs-plus package overrides other key for fuzzy-finder
+'platform-linux':
+  'ctrl-alt-x': 'fuzzy-finder:toggle-file-finder'


Only navigate to an href if it exists

Only navigate to an href if it exists

--- scripts/app.coffee
+++ scripts/app.coffee
@@ -25,7 +25,7 @@
         if external.test(href)
           window.open(href, '_blank')
         else
-          router.navigate(href, {trigger: true})
+          if href then router.navigate(href, {trigger: true})

       Backbone.history.start
         #pushState: true


AppView: split out logic for if to intercept click to own method, for overriding

--- shared/base/app_view.coffee
+++ shared/base/app_view.coffee
@@ -26,9 +26,12 @@
     # We want the actual value of the attribute, rather than the
     # full URL, so we use jQuery instead of just e.currentTarget.href
     href = $(e.currentTarget).attr('href')
-    if href
-      hashParts = href.split('#')
-      isHashClick = hashParts.length > 1 && hashParts[0] == window.location.pathname
-      if !isHashClick && href.slice(0, 1) == '/' && href.slice(0, 2) != '//'
-        e.preventDefault()
-        @app.router.navigate(href, true)
+    if @shouldInterceptClick(href, e.currentTarget)
+      e.preventDefault()
+      @app.router.redirectTo(href)
+
+  shouldInterceptClick: (href, el) ->
+    return false unless href
+    hashParts = href.split('#')
+    isHashClick = hashParts.length > 1 && hashParts[0] == window.location.pathname
+    !isHashClick && href.slice(0, 1) == '/' && href.slice(0, 2) != '//'


Update news.coffee

Update news.coffee

--- server/materials/data/news.coffee
+++ server/materials/data/news.coffee
@@ -3,8 +3,8 @@

 export default allNews = () ->
     return news([
-        newsItem("14 ноября занятие во ВШЭ пройдет с 11.00 до 14.00", String.raw"""
-            14 ноября занятие во ВШЭ пройдет с 11.00 до 14.00.
+        newsItem("Занятия для старших уровней", String.raw"""
+            С 21 ноября возобновляются занятия для старших уровней — по воскресеньям, параллельно с основным занятием во ВШЭ. подробнее.
         """),
         newsItem("Районная олимпиада", String.raw"""
             Районная олимпиада в Нижегородской области в этом году будет, предварительно, 16 декабря. На сайт добавлена подробная информация, также в архив районных олимпиад добавлены задачи прошлого года.


Added news

Added news

--- server/materials/data/news.coffee
+++ server/materials/data/news.coffee
@@ -3,6 +3,9 @@

 export default allNews = () ->
     return news([
+        newsItem("Очных занятий в этом учебном году больше не будет (ни в лицее 40, ни в ННГУ)", String.raw"""
+            В текущем учебном году занятий в лицее 40 и в ННГУ больше не будет. Занятия возобновятся в сентябре, следите за объявлениями на сайте.
+        """),
         newsItem("Летние алгопрог-сборы", String.raw"""

Я рассматриваю возможность провести дистанционные сборы летом 2021 года длительностью примерно 7-10 дней. Сборы будут бесплатными для нижегородских школьников, и платными для любых других желающих (школьников, студентов и т.д.), стоимость примерно 5-8 тыс. рублей. (Безотносительно оплаты основного алгопрога.) Сборы будут рассчитаны на учеников, чей уровень на алгопроге примерно 3 и выше. По формату будет примерно похоже на зимние сборы к региону.

Если вам будет интересно участвовать, заполните анкету.

@@ -16,11 +19,6 @@
         newsItem("Летняя компьютерная школа и прочие летние школы", String.raw"""

Добавлена информация про ЛКШ и про летние школы вообще. Обязательно прочитайте информацию про ЛКШ; я настоятельно рекомендую всем школьникам, кто может, туда поехать.

         """),
-        newsItem("Про занятия в ННГУ", String.raw"""
-
В текущем учебном году занятий в ННГУ точно не будет.

-
При этом я готов проводить онлайн-занятия через zoom или другие сервисы видеоконференций, если будут желающие участвовать в таких занятиях. Формат занятий будет как всегда — никаких лекций, вы сами решаете задачи, просто вам будет легче меня спросить о чем-нибудь. Пишите, если вы хотите участвовать в таком занятии, сразу указывайте время, когда вам удобнее. Думаю, если такие занятия будут, то участвовать в них смогут все желающие (не только нижегородские школьники), хотя я буду отдавать приоритет нижегородским школьникам (в первую очередь в плане выбора удобного времени).

-
Также я планирую возобновить онлайн-занятия для старших уровней. Занятия устроены аналогично тому, как были устроены такие занятия в прошлом году в ННГУ — я готовлю некоторый контест на codeforces, вы будете решаете (в виртуальном режиме, контест будет доступен несколько дней), после чего мы в онлайн-режиме обсуждаем и разбираем задачи и обсуждаем разные олимпиадные новости. Время такого обсуждения можно выбрать; пишите, если вы планируете участвовать и у вас есть пожелания по времени. Это занятия тоже будут доступны для всех желающих, хотя если ваш уровень ниже чем примерно 2В или 3А, то скорее всего вам будет сложно; и опять-таки приоритет будет отдаваться сильным нижегородским школьникам.

-        """),
         newsItem("Опрос про алгопрог", String.raw"""
             Ответьте, пожалуйста, на несколько вопросов. Тем, кто уже отвечал — я там добавил несколько вопросов, можете ответить еще раз.
         """),


omit phrases guide/kwic/v1/*, guide/kwic/v2/*


:handbag::blush: Updated in browser at strd6.github.io/editor

--- main.coffee
+++ main.coffee
@@ -56,7 +56,10 @@

 toAudioBuffer = (buffer, sampleRate) ->
   audioBuffer = context.createBuffer 1, buffer.length, sampleRate
-  audioBuffer.getChannelData(0).set(buffer)
+
+  audioData = audioBuffer.getChannelData(0)
+  buffer.forEach (n, i) ->
+    audioData[i] = n / 32768

   return audioBuffer


docker/rmi: refactor test

docker/rmi: refactor test

--- test/docker/rmi.coffee
+++ test/docker/rmi.coffee
@@ -3,31 +3,11 @@
 test = require '../test'
 they = require 'ssh2-they'
 fs = require 'ssh2-fs'
+docker = require '../../src/misc/docker'

 clean = (ssh, machine, callback) ->
-  mecano
-  .execute
-    cmd: """
-      export SHELL=/bin/bash
-      export PATH=/opt/local/bin/:/opt/local/sbin/:/usr/local/bin/:/usr/local/sbin/:$PATH
-      bin_boot2docker=$(command -v boot2docker)
-      bin_docker=$(command -v docker)
-      bin_machine=$(command -v docker-machine)
-      if [ -f $bin_machine ];
-        if [ \"#{machine}\" = \"--\" ];then exit 5;fi
-        then
-          eval $(${bin_machine} env #{machine}) && $bin_docker  rmi -f mecano/rmi_test:latest
-      elif [ -f $bin_boot2docker ];
-        then
-          eval $(${bin_boot2docker} shellinit) && $bin_docker rmi -f mecano/rmi_test:latest
-      else
-        $bin_docker rmi mecano/rmi_test:latest
-      fi
-      """
-    code_skipped: 1
-    , (err, executed, stdout, stderr) ->
-      return callback err, executed, stdout, stderr
-
+  docker.exec " rmi -f mecano/rmi_test:latest || true" , {  ssh: ssh, machine: machine }, null
+  , (err, executed, stdout, stderr) -> callback err, executed, stdout, stderr

 describe 'docker rmi', ->


Remove test until it's meaningful. Fixes #88

--- test/email_test.coffee
+++ test/email_test.coffee
@@ -1,12 +0,0 @@
-Robot = require '../src/robot'
-Path  = require 'path'
-
-Hubot = require("../src/hubot/email").Email
-
-scriptsPath = Path.resolve "./src/hubot/scripts"
-
-robot = new Hubot scriptsPath, 'Hewbot'
-
-robot.load scriptsPath
-
-robot.run()


Set a default value for @windowAckSize

Set a default value for @windowAckSize

--- rtmp.coffee
+++ rtmp.coffee
@@ -748,13 +748,16 @@
     @clientid = generateNewClientID()
     @useEncryption = false
     @receiveTimestamp = null
-    @windowAckSize = null
     @lastSentAckBytes = 0
     @receivedBytes = 0
     @stream = null # AVStream
     @seekedDuringPause = false
     @lastSentTimestamp = null
     @isResuming = false
+
+    # Some broadcaster software like Wirecast does not send Window Acknowledgement Size,
+    # so it seems we have to set a default value.
+    @windowAckSize = 2500000

   toString: ->
     return "#{@clientid}: addr=#{@socket.remoteAddress} port=#{@socket.remotePort}"


ahh stupid mistake

ahh stupid mistake

--- lib/assets/javascripts/appitude/facebook.js.coffee
+++ lib/assets/javascripts/appitude/facebook.js.coffee
@@ -40,8 +40,8 @@
   justiceServed = true

 copyFbRoot = ->
-  $('#fb-root').detach()
-
+  fb_root = $('#fb-root').detach()
+
 restoreFbRoot = ->
   if $('#fb-root').length > 0
     $('#fb-root').replaceWith fb_root


fix missing variable before method invocation!

--- lib/assets/javascripts/firehose/consumer.js.coffee
+++ lib/assets/javascripts/firehose/consumer.js.coffee
@@ -49,7 +49,7 @@
         if nextTransportType = supportedTransports.pop()
           nextTransport = new nextTransportType @config
           console?.log "Attempting to fail over to transport: #{nextTransport.name()}"
-          .connect delay
+          nextTransport.connect delay
         else originalFailFun?()
       new transport(@config)
     # Fire off the first connection attempt.


removing disconnect

removing disconnect

--- lib/assets/javascripts/firehose/consumer.js.coffee
+++ lib/assets/javascripts/firehose/consumer.js.coffee
@@ -60,11 +60,11 @@
         @config.connected = origConnected
         origConnected()

-    origdisconnected = @config.disconnected
-    @config.disconnected = =>
-      deferred.reject()
-      if origDisconnected
-        @config.disconnected = origDisconnected
-        origDisconnected()
+  #  origDisconnected = @config.disconnected
+  #  @config.disconnected = =>
+  #    deferred.reject()
+  #    if origDisconnected
+  #      @config.disconnected = origDisconnected
+  #      origDisconnected()

     deferred.promise()


loading from enterprise_scripts, update integration naming to support such scripts and give default name `script`

--- src/0_bootstrap.coffee
+++ src/0_bootstrap.coffee
@@ -15,8 +15,7 @@

 # Bootstraping hubot-enterprise
 # adding enterprise functions to robot object
-
-path = require('path')
+Path = require('path')
 Insight = require('insight')
 pkg = require('../package.json')

@@ -29,11 +28,15 @@
 insight.track 'HE', 'start'

 module.exports = (robot) ->
+  # create e (enterprise object on robot)
   robot.e = {}
+  # `mount` adapter object
   robot.e.adapter = new (require __dirname+
     '/../lib/adapter_core')(robot)
+  # create array for HE integrations to store calls for help
   robot.e.help = []

+  # create common strings object
   robot.e.commons = {
     no_such_integration: (product) ->
       return "there is no such integration #{product}"
@@ -41,6 +44,13 @@
       return "help for hubot enterprise:"+content
   }

+  # load scripts to robot
+  load_he_scripts = (path) ->
+    scriptsPath = Path.resolve ".", path
+    robot.load scriptsPath
+
+  # find integrations names: find by extracting integration name from integration folder
+  # hubot-integration will become: `integration`
   find_integration_name = ->
     myError = new Error
     trace = myError.stack.split('\n')
@@ -52,7 +62,11 @@
       fname = /\((.*):/i.exec(shift)[1].split(':')[0]
       unless fname == filename
         break
-    fname.match(/\/hubot-(.*?)\//ig).pop().replace(/hubot-|\//g, '')
+    fmatch = fname.match(/\/hubot-(.*?)\//ig)
+    if fmatch
+      return fmatch.pop().replace(/hubot-|\//g, '')
+    # if not matched- return default 'script'
+    return 'script'

   # register a listener function with hubot-enterprise
   #
@@ -66,7 +80,6 @@
   #
   # will register function with the following regex:
   # /#{info.product} #{info.action} (.*)/i
-
   robot.e.create = (info, callback) ->
     integration_name = find_integration_name()
     info.product = info.product || integration_name
@@ -97,3 +110,6 @@

   # robot.enterprise as alias to robot.e for backward compatibility
   robot.enterprise = robot.e
+
+  # load hubot enterprise scripts (not from integrations) after HE loaded
+  load_he_scripts('enterprise_scripts')


initial voodoo magic

initial voodoo magic

--- ink.coffee
+++ ink.coffee
@@ -0,0 +1,61 @@
+#
+# INK.js
+#
+ansiRegex = require 'ansi-regex'
+supportsColor = require 'supports-color'
+
+defineFnProp = (obj, name, thisArg, fn)->
+  if not fn and thisArg instanceof Function
+    fn = thisArg
+    thisArg = null
+  Object.defineProperty obj, name,
+    enumerable: yes
+    get: -> fn.call thisArg
+  return obj
+
+InkRender = (args...)->
+  console.log 'RENDER', @
+
+class InkFGBG
+  constructor: (@ink, @side)->
+    console.log 'FGBG:', @side
+
+  red: ->
+    console.log 'RED'
+
+class Ink
+  bold: ->
+    console.log 'BOLD'
+
+  reset: ->
+    console.log 'RESET'
+
+  fg: ->
+    new InkFGBG @, 'fg'
+
+  bg: ->
+    new InkFGBG @, 'bg'
+
+# ... and a teaspoon of Javascript black voodoo magic ...
+module.exports = {}
+
+mkGetter = (ink, fn, getter)->->
+  fgbg = fn.call ink
+  if fgbg instanceof InkFGBG
+    fgbggetter = InkRender.bind ink
+    for name, gfn of InkFGBG.prototype
+      defineFnProp fgbggetter, name, mkGetter ink, gfn, getter
+    fgbggetter
+  else
+    getter
+
+mkExport = (name, fn)->->
+  ink = new Ink
+  getter = InkRender.bind ink
+  for name, gfn of Ink.prototype
+    defineFnProp getter, name, mkGetter ink, gfn, getter
+  fgbg = fn.call ink
+  return if fgbg instanceof InkFGBG then fgbg else getter
+
+for name, fn of Ink.prototype
+  defineFnProp module.exports, name, mkExport name, fn


work of 1 min load and set server as up

work of 1 min load and set server as up

--- app.coffee
+++ app.coffee
@@ -131,11 +131,11 @@

 server = net.createServer (socket) ->
-	fiveMinLoad = os.loadavg()[1]
+	fiveMinLoad = os.loadavg()[0]
 	availableWorkingCpus = os.cpus().length - 1
 	freeLoad = availableWorkingCpus - fiveMinLoad
 	freeLoadPercentage = Math.round((freeLoad / availableWorkingCpus) * 100)
-	socket.write "#{freeLoadPercentage}%\n", "ASCII"
+	socket.write "up, #{freeLoadPercentage}%\n", "ASCII"
 	socket.pipe socket
 	return


String interpolation

String interpolation

--- app.coffee
+++ app.coffee
@@ -6,7 +6,7 @@
 app     = express()

 app.set 'port', process.env.PORT || 3000
-app.set 'views', __dirname + '/views'
+app.set 'views', "#{__dirname}/views"
 app.set 'view engine', 'jade'
 app.use express.favicon()
 app.use express.logger('dev')
@@ -23,4 +23,4 @@

 http.createServer(app).listen(app.get('port'), ->
-  console.log 'Express server listening on port ' + app.get('port'))
+  console.log "Express server listening on port #{app.get('port')}")


redis

redis

--- app.coffee
+++ app.coffee
@@ -49,7 +49,8 @@
 app.set 'view engine', 'ejs'
 app.set 'views', path.join(__dirname, 'views')
 app.set 'view options', { layout: 'layout.ejs' }
-app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
+# uncomment after placing your favicon in /public
+#app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
 app.use partials()
 app.use logger('dev')
 app.use bodyParser.json()


handle record sub elements

handle record sub elements

--- shared/ironTableCollection.coffee
+++ shared/ironTableCollection.coffee
@@ -12,7 +12,6 @@
             # Meteor Method to get all the records in collection
             meths = {}
             meths["ironTable_" + @_name + "_recordCount"] = (select = {})=>
-                console.log("recordCount", @_name, select, @find?(select)?.count?())
                 @find?(select)?.count?()

             Meteor.methods meths


👔 Remove unncessary trailing commas

👔 Remove unncessary trailing commas

/xref https://github.com/atom/sort-lines/commit/6b9148e#commitcomment-24844325

--- menus/sort-lines.cson
+++ menus/sort-lines.cson
@@ -8,8 +8,8 @@
         { 'label': 'Reverse Sort', 'command': 'sort-lines:reverse-sort' }
         { 'label': 'Unique', 'command': 'sort-lines:unique' }
         { 'label': 'Sort (Case Insensitive)', 'command': 'sort-lines:case-insensitive-sort' }
-        { 'label': 'Natural', 'command': 'sort-lines:natural' },
-        { 'label': 'By Length', 'command': 'sort-lines:by-length' },
+        { 'label': 'Natural', 'command': 'sort-lines:natural' }
+        { 'label': 'By Length', 'command': 'sort-lines:by-length' }
         { 'label': 'Shuffle', 'command': 'sort-lines:shuffle' }
       ]
     ]


Modifying ajax text

Modifying ajax text

--- test/js/totodoo.coffee
+++ test/js/totodoo.coffee
@@ -29,8 +29,9 @@

 		it 'is the name of the default list', (done) ->
 			drvr = @driver
-			@driver.wait(-> drvr.executeScript('return document.readyState'), 10000)
-			text = @driver.findElement(id: 'listName').getText()
-			expect(text).to.eventually.contain
-			'Todos'
-			@timeout 4000, done()
+			# wait for page to be loaded
+			@driver.wait((()-> drvr.executeScript('return document.readyState')), 10000).then ()->
+				text = drvr.findElement(id: 'listName').getText()
+				expect(text).to.eventually.contain
+				'Todos'
+				done()


Modify server.coffee to get *.map

Modify server.coffee to get *.map

--- samples/server.coffee
+++ samples/server.coffee
@@ -7,6 +7,7 @@
 app.use logger 'dev'

 cachedir = "#{__dirname}/.cache"
+libroot = "#{__dirname}/.."

 # view engine setup
 app.set 'views', "#{__dirname}/."
@@ -31,13 +32,15 @@

 app.use express.static cachedir

+
 app.get '/ga.js', (req, res) ->
-  res.sendFile "ga-#{version}.js",
-    root: "${__dirname}/.."
+  res.sendFile "ga-#{version}.js", root: libroot

 app.get '/ga.min.js', (req, res) ->
-  res.sendFile "ga-#{version}.min.js",
-    root: "${__dirname}/.."
+  res.sendFile "ga-#{version}.min.js", root: libroot
+
+app.get "/*.map", (req, res) ->
+  res.sendFile req.path, root: libroot

 app.get '/*', (req, res) ->
   res.render req.params[0]


Oscillator

Oscillator


Added default submit method.

Added default submit method.

--- app/assets/javascripts/app/controllers/_application_controller.js.coffee
+++ app/assets/javascripts/app/controllers/_application_controller.js.coffee
@@ -801,3 +801,7 @@
     if e
       e.preventDefault()
     @el.modal('hide')
+
+  submit: (e) =>
+    e.preventDefault()
+    @log 'You need to implement your own "submit" method!'


Add foreground / background and styles to full toolbar

--- app/assets/javascripts/effective_ckeditor_text_area/initialize.js.coffee
+++ app/assets/javascripts/effective_ckeditor_text_area/initialize.js.coffee
@@ -63,12 +63,15 @@
     filebrowserBrowseUrl: '/effective/assets'
     filebrowserImageBrowseUrl: '/effective/assets?only=images'
     toolbar_full: [
-      { name: 'definedstyles', items: ['Format'] },
       { name: 'html', items: ['ShowBlocks'] },
       { name: 'justify', items: ['JustifyLeft', 'JustifyCenter', 'JustifyRight']}
-      { name: 'basicstyles', items: ['Bold', 'Italic', 'Underline'] },
+      { name: 'basicstyles', items: ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript'] },
       { name: 'insert', items: ['Link', 'Table', '-', 'Image', 'oembed', 'EffectiveAssets'] },
       { name: 'lists', items: ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent'] },
+      '/'
+      { name: 'definedstyles', items: ['Format'] },
+      { name: 'styles', items: ['Styles', 'RemoveFormat'] },
+      { name: 'colors', items: ['TextColor', 'BGColor'] }
     ],
     toolbar_simple: [
       { name: 'definedstyles', items: ['Format'] },


fix for bug parsing checklist attributes in opinion views

--- @client/opinion_views.coffee
+++ @client/opinion_views.coffee
@@ -100,7 +100,7 @@
           attributes.push
             key: name
             name: tag.view_name or tag.name or tag.self_report?.question or name
-            pass: do(name, tag) -> (u, value) ->
+            pass: if (tag.self_report?.input or tag.input) != 'checklist' then do(name, tag) -> (u, value) ->
               result = tag.compute?(u) or fetch(u).tags[name]
               if value?
                 result == value
@@ -926,7 +926,10 @@
     return false if !user.tags

     val_for_user = user.tags[attr_key]
-    is_array = Array.isArray(val_for_user)
+
+    if attribute.input_type == 'checklist' && val_for_user
+      val_for_user = val_for_user.split(',')
+      is_array = true

     passing_vals = (val for val,enabled of opinion_views_ui.visible_attribute_values[attr_key] when enabled)

@@ -948,6 +951,8 @@
           passes ||= undefined == val

       else
+
+
         passes ||= val_for_user == passing_val || (is_array && passing_val in val_for_user)


Removed shortcut methods

Removed shortcut methods

--- src/octonode/repository.coffee
+++ src/octonode/repository.coffee
@@ -10,7 +10,7 @@
 class Repository

   constructor: (@name, @client) ->
-
+
   _invokeGet: (path,expectedStatus = 200,methodName,cb) ->
     @client.get path, (err, s, b)  ->
       return cb(err) if err
@@ -24,52 +24,65 @@
   # Get a repository
   # '/repos/pkumar/hub' GET
   info: (cb) ->
-    @_invokeGet "/repos/#{@name}",200,"info",cb
+    @client.get "/repos/#{@name}", (err, s, b) ->
+      return cb(err) if err
+      if s isnt 200 then cb(new Error("Repository info error")) else cb null, b

   # Get the commits for a repository
   # '/repos/pkumar/hub/commits' GET
-  getCommits: (cb) ->
-    @_invokeGet "/repos/#{@name}/commits",200,"getCommits",cb
+  commits: (cb) ->
+    @client.get "/repos/#{@name}/commits", (err, s, b) ->
+      return cb(err) if err
+      if s isnt 200 then cb(new Error("Repository commits error")) else cb null, b

   # Get the tags for a repository
-  # '/repos/pkumar/hub/commits' GET
-  getTags: (cb) ->
-    @_invokeGet "/repos/#{@name}/tags",200,"getTags",cb
+  # '/repos/pkumar/hub/tags' GET
+  tags: (cb) ->
+    @client.get "/repos/#{@name}/tags", (err, s, b) ->
+      return cb(err) if err
+      if s isnt 200 then cb(new Error("Repository tags error")) else cb null, b

   # Get the languages for a repository
   # '/repos/pkumar/hub/languages' GET
-  getLanguages: (cb) ->
-    @_invokeGet "/repos/#{@name}/languages",200,"getLanguages",cb
+  languages: (cb) ->
+    @client.get "/repos/#{@name}/languages", (err, s, b) ->
+      return cb(err) if err
+      if s isnt 200 then cb(new Error("Repository languages error")) else cb null, b

   # Get the contributors for a repository
   # '/repos/pkumar/hub/contributors' GET
-  getContributors: (cb) ->
-    @_invokeGet "/repos/#{@name}/contributors",200,"getContributors",cb
+  contributors: (cb) ->
+    @client.get "/repos/#{@name}/contributors", (err, s, b) ->
+      return cb(err) if err
+      if s isnt 200 then cb(new Error("Repository contributors error")) else cb null, b

   # Get the teams for a repository
   # '/repos/pkumar/hub/teams' GET
-  getTeams: (cb) ->
-    @_invokeGet "/repos/#{@name}/teams",200,"getTeams",cb
+  teams: (cb) ->
+    @client.get "/repos/#{@name}/teams", (err, s, b) ->
+      return cb(err) if err
+      if s isnt 200 then cb(new Error("Repository teams error")) else cb null, b

   # Get the branches for a repository
   # '/repos/pkumar/hub/branches' GET
-  getBranches: (cb) ->
-    @_invokeGet "/repos/#{@name}/branches",200,"getBranches",cb
+  branches: (cb) ->
+    @client.get "/repos/#{@name}/branches", (err, s, b) ->
+      return cb(err) if err
+      if s isnt 200 then cb(new Error("Repository branches error")) else cb null, b

   # Get the issues for a repository
   # '/repos/pkumar/hub/issues' GET
-  getIssues: (cb) ->
-    @_invokeGet "/repos/#{@name}/issues",200,"getIssues",cb
+  issues: (cb) ->
+    @client.get "/repos/#{@name}/issues", (err, s, b) ->
+      return cb(err) if err
+      if s isnt 200 then cb(new Error("Repository issues error")) else cb null, b

   # Get the forks for a repository
   # '/repos/pkumar/hub/forks' GET
-  getForks: (cb) ->
-    @_invokeGet "/repos/#{@name}/forks",200,"getForks",cb
-
-  # Create a fork for a repository
-  # '/repos/pkumar/hub/forks' POST
-  createFork: (cb) ->
-    @_invokePost "/repos/#{@name}/forks",{},202,"createFork",cb
+  forks: (cb) ->
+    @client.get "/repos/#{@name}/forks", (err, s, b) ->
+      return cb(err) if err
+      if s isnt 200 then cb(new Error("Repository forks error")) else cb null, b

 # Export module
 module.exports = Repository


Use of _.extend instead of custom extend

--- src/Enumeration.coffee
+++ src/Enumeration.coffee
@@ -1,8 +1,3 @@
-extend = (object, properties) ->
-  for key, val of properties
-    object[key] = val
-  object
-
 enumTypes=[]
 _=require("underscore")
 #Java like enum
@@ -29,7 +24,7 @@
       describe: -> "#{enumName}:#{identifier}#{if valueIsObject then "  {#{enumName+":"+prop for enumName,prop of extend(descriptor,valueProto) when !(_.isFunction(prop))}}" else ""}"
     testReserved=(object)-> throw "Reserved field #{field} cannot be passed as enum property" for field of object when field in Object.keys(methods)
     testReserved valueProto
-    prototype=extend methods, valueProto
+    prototype=_.extend methods, valueProto
     properties={}
     defineReadOnlyProperty= (key0,value0)->
       properties[key0]=
@@ -69,7 +64,9 @@
     #Define non-enumerable method that returns a concise, pretty string representing the Enumeration
     Object.defineProperty @, 'pretty', value:-> "#{enumType}:#{"\n\t"+enume.describe() for key,enume of instance}"
     #Define non-enumerable method that returns the enum instance which matches identifier (descriptor if string, descriptor._id if object)
-    Object.defineProperty @, 'from', value: (identifier) -> (instance[key] for key,enume of instance when enume.id() is identifier)[0] or throw "identifier #{identifier} does not match any"
+    Object.defineProperty @, 'from', value:
+      (identifier,throwOnFailure=false) ->
+        (instance[key] for key,enumVal of instance when enumVal.id() is identifier)[0] or (throw "identifier #{identifier} does not match any" if throwOnFailure )
     #inherit function prototype so that a class or constructor can inherit from the Enumeration instance
     this.__proto__=Function.prototype
     #Guaranties properties to be 'final', non writable


Update the docs about the default version

--- js/outer/versions.coffee
+++ js/outer/versions.coffee
@@ -1,5 +1,5 @@
 CI.Versions =
-  default_ruby: "1.9.3-p194"
+  default_ruby: "1.9.3-p448"
   old_ruby: "1.8.7-p358"
   default_node: "0.8.12"
   default_php: "5.3.10-1ubuntu3.5"


Improve resiliency of getSupportedLanguages() unit test

--- test/get-supported-languages.coffee
+++ test/get-supported-languages.coffee
@@ -7,11 +7,13 @@

 describe "getLanguageSupport", ->

-  definitionsPath = path.resolve(__dirname, "definitions")
+  it "lists all supported languages", ->

-  languages = []
-  for file in fs.readdirSync(definitionsPath)
-    if path.extname(file) is ".csv"
-      languages.push path.basename(file, ".csv")
+    definitionsPath = path.resolve(__dirname, "definitions")

-  assert.deepEqual(languages, getSupportedLanguages())
+    languages = []
+    for file in fs.readdirSync(definitionsPath)
+      if path.extname(file) is ".csv"
+        languages.push path.basename(file, ".csv")
+
+    assert.deepEqual(languages.sort(), getSupportedLanguages().sort())


Fix tests for exception catches on file stats.

--- test/integration/events.spec.coffee
+++ test/integration/events.spec.coffee
@@ -72,23 +72,25 @@
       fs.createDirSync './test/fixtures/new'

     it 'should register all create events for a git clone', (done) ->
-      @timeout 5000
+      @timeout 10000
       watcher = new Inotifyr 'test/fixtures', {recursive: yes, events: 'create'}
       files = []
-      watcher.on 'create', (filename, stats) ->
-        files.push filename
-        expect(stats.mtime).to.be.a.number
-
-      args = ['clone', 'https://github.com/codio/node-demo.git']
-      collect 'git', args, {cwd: './test/fixtures'}, 'test/fixtures/node-demo', (realFiles) ->
-        # Filter out lock files as they are generated multiple times in git
-        uniqFiles = _.filter files, (file) -> not file.match /\.lock$/
-        expect(_.uniq uniqFiles).to.be.eql uniqFiles
-        realFiles.forEach (file) ->
-          return if file.match /\.git/
-          expect(files).to.contain file
-        done()
-
+      _.delay ->
+        watcher.on 'create', (filename, stats) ->
+          files.push filename
+          expect(stats.mtime).to.be.a.number
+
+        args = ['clone', 'https://github.com/codio/node-demo.git']
+        collect 'git', args, {cwd: './test/fixtures'}, 'test/fixtures/node-demo', (realFiles) ->
+          # Filter out lock files as they are generated multiple times in git
+          uniqFiles = _.filter files, (file) -> not file.match /\.lock$/
+          expect(_.uniq uniqFiles).to.be.eql uniqFiles
+          realFiles.forEach (file) ->
+            return if file.match /\.git/
+            expect(files).to.contain file
+          done()
+      , 300
+
     it 'should register all create events for an unzip action', (done) ->
       watcher = new Inotifyr 'test/fixtures', {recursive: yes, events: 'create'}
       files = []
@@ -108,20 +110,21 @@
       fs.createFileSync 'test/fixtures/new/hello.txt'
       watcher = new Inotifyr 'test/fixtures/new', {recursive: yes, events: 'create'}
       files = []
-
-      watcher.on 'create', (filename, stats) -> files.push filename
-
-      fs.createDirSync 'test/fixtures/a/b/c/d/e/f/g/h/i/j/k/l'
-      fs.deleteFileSync 'test/fixtures/new/hello.txt'
-      fs.copyDirSync 'test/fixtures/a', 'test/fixtures/new/a'
-      _.delay ->
-        expect(files).to.contain path.resolve 'test/fixtures/new/a/b/c/d/e/f/g/h/i/j/k/l'
-        files = []
-        fs.deleteDirSync 'test/fixtures/new/a'
+      _.delay ->
+        watcher.on 'create', (filename, stats) -> files.push filename
+
+        fs.createDirSync 'test/fixtures/a/b/c/d/e/f/g/h/i/j/k/l'
+        fs.deleteFileSync 'test/fixtures/new/hello.txt'
         fs.copyDirSync 'test/fixtures/a', 'test/fixtures/new/a'
-        _.delay ->
+        _.delay ->
           expect(files).to.contain path.resolve 'test/fixtures/new/a/b/c/d/e/f/g/h/i/j/k/l'
-          done()
+          files = []
+          fs.deleteDirSync 'test/fixtures/new/a'
+          fs.copyDirSync 'test/fixtures/a', 'test/fixtures/new/a'
+          _.delay ->
+            expect(files).to.contain path.resolve 'test/fixtures/new/a/b/c/d/e/f/g/h/i/j/k/l'
+            done()
+          , 100
         , 100
       , 100

@@ -509,14 +512,16 @@
         recursive: yes
       }
       files = []
-      watcher.on 'create', (filename, stats) -> files.push filename
-
-      fs.createFileSync './test/fixtures/hello.txt', 'hello'
-      _.delay ->
-        [1..5].forEach (i) ->
-          expect(files).to.not.contain path.resolve "test/fixtures/#{i}.txt"
-          expect(files).to.not.contain path.resolve "test/fixtures/watchme/#{i}.txt"
-        expect(files).to.contain path.resolve 'test/fixtures/hello.txt'
-        watcher.close()
-        done()
-      , 10
+      _.delay ->
+        watcher.on 'create', (filename, stats) -> files.push filename
+
+        fs.createFileSync './test/fixtures/hello.txt', 'hello'
+        _.delay ->
+          [1..5].forEach (i) ->
+            expect(files).to.not.contain path.resolve "test/fixtures/#{i}.txt"
+            expect(files).to.not.contain path.resolve "test/fixtures/watchme/#{i}.txt"
+          expect(files).to.contain path.resolve 'test/fixtures/hello.txt'
+          watcher.close()
+          done()
+        , 10
+      , 100


tweak to checkNetwork

tweak to checkNetwork

--- scripts/bungie.coffee
+++ scripts/bungie.coffee
@@ -139,7 +139,7 @@

 checkNetwork = (network) ->
-  xbox = ['xbox', 'xb1', 'xbox1', 'xboxone', 'xbox360', 'xb360', 'xbone']
+  xbox = ['xbox', 'xb1', 'xbox1', 'xboxone', 'xbox360', 'xb360', 'xbone', 'xb']
   playstation = ['playstation', 'ps', 'ps3', 'ps4', 'playstation3', 'playstation4']
   if network in xbox
     return '1'


remove extraneous auth

remove extraneous auth

--- models/account.coffee
+++ models/account.coffee
@@ -8,12 +8,6 @@
 module.exports = class Account extends Backbone.Model

   url: -> "#{KERNAL_API_URL}/accounts/#{@idOr_id()}"
-
-  sync: (method, model, options = {})->
-    { authId, authToken } = authTokenPair()
-    options.data ?= {}
-    options.data = _.extend options.data, { auth_id: authId, auth_token: authToken }
-    super

   idOr_id: ->
     @id or @get('_id')


Added angular-resource.js

Added angular-resource.js

--- vendor/assets/javascripts/qrious-ui.js.coffee
+++ vendor/assets/javascripts/qrious-ui.js.coffee
@@ -2,6 +2,7 @@
 #= require spin.js/spin
 #= require jquery/jquery
 #= require angular/angular
+#= require angular-resource/angular-resource.js
 #= require angular-bootstrap/ui-bootstrap-tpls
 #= require angular-bootstrap-colorpicker/js/bootstrap-colorpicker-module
 #= require angular-file-upload/angular-file-upload


7pm not 9pm

7pm not 9pm

--- source/scripts/views/night.coffee
+++ source/scripts/views/night.coffee
@@ -28,7 +28,7 @@
         nextRun.setHours 7
       else
         # turn it off 7pm today
-        nextRun.setHours 21
+        nextRun.setHours 19

       setTimeout(=>
         @el.toggleClass 'dark'


Changed base URL to be relative

Changed base URL to be relative

--- scripts/config.coffee
+++ scripts/config.coffee
@@ -1,5 +1,5 @@
 require.config
-  baseUrl: '/scripts/'
+  baseUrl: 'scripts/'

   # # Configure Library Locations
   paths:


allowing multiple files to be uploaded at once

--- lib/assets/javascripts/attachinary.js.coffee
+++ lib/assets/javascripts/attachinary.js.coffee
@@ -57,6 +57,7 @@
       if @$input.attr('accept')
         options.acceptFileTypes = new RegExp("^#{@$input.attr('accept').split(",").join("|")}$", "i")

+      @$input.attr('multiple', true) if @options.maximum > 1
       @$input.fileupload(options)


Fix bug

Fix bug

--- src/6.array.coffee
+++ src/6.array.coffee
@@ -83,9 +83,9 @@
         if 0 < pos < 1
             pos = {Ratio: pos}
         else if -1 < pos < 0
-            pos = {Reverse: Ratio: pos}
+            pos = {Reverse: Ratio: -pos}
         else if pos < 0
-            pos = {Reverse: pos}
+            pos = {Reverse: -pos - 1}
     if typeof pos == "number"
         pos
     else if pos?.Reverse?.Ratio?


fixes #10

fixes #10

--- lib/atom-ternjs.coffee
+++ lib/atom-ternjs.coffee
@@ -70,7 +70,7 @@
     @provider.init(@client, @documentation)
     @reference = new Reference(@client)
     @registerEvents()
-    @registration = atom.services.provide('autocomplete.provider', '1.0.0', {provider: @provider})
+    @registration = atom.packages.serviceHub.provide('autocomplete.provider', '1.0.0', {provider: @provider})

   init: ->
     @activatePackage()


Little note :memo:

Little note :memo:

--- spec/editor-component-spec.coffee
+++ spec/editor-component-spec.coffee
@@ -289,6 +289,7 @@
         editor.getBuffer().insert([12, 0], '\n')
         nextTick()

+        # The newline and he tab need to be in two different operations to surface the bug
         editor.getBuffer().insert([13, 0], '  ')
         nextTick()


Update close on toggle spec

Update close on toggle spec

--- spec/markdown-preview-spec.coffee
+++ spec/markdown-preview-spec.coffee
@@ -151,18 +151,12 @@
         preview = previewPane.getActiveItem()
         MarkdownPreviewView::renderMarkdown.reset()

-    it "toggle closes the existing preview", ->
-      waitsForPromise ->
-        previewPane.focus()
-        atom.workspace.open()
-
-      runs ->
-        editorPane.focus()
-        atom.workspaceView.getActiveView().trigger 'markdown-preview:toggle'
-
-        [editorPane, previewPane] = atom.workspaceView.getPanes()
-        expect(editorPane).toHaveFocus()
-        expect(previewPane).toBeUndefined()
+    it "closes the existing preview when toggle is triggered a second time", ->
+      atom.workspaceView.getActiveView().trigger 'markdown-preview:toggle'
+
+      [editorPane, previewPane] = atom.workspaceView.getPanes()
+      expect(editorPane).toHaveFocus()
+      expect(previewPane?.activeItem).toBeUndefined()

     describe "when the editor is modified", ->
       describe "when the preview is in the active pane but is not the active item", ->


Add Amazon URL

Add Amazon URL

--- src/retrieve.coffee
+++ src/retrieve.coffee
@@ -35,6 +35,7 @@
       author: data.Author?[0]
       value: data.TradeInValue?[0]?.Amount?[0]
       image: image
+      url: item.DetailPageURL?[0]

   return deferred.promise


Fixed deprecation for paneItem

Fixed deprecation for paneItem

--- lib/bookmarklet.coffee
+++ lib/bookmarklet.coffee
@@ -49,7 +49,7 @@
     @create( { link: true, jquery: jquery } )

   create: (options) ->
-    editor = atom.workspace.activePaneItem
+    editor = atom.workspace.getActivePaneItem()
     grammar = editor.getGrammar()

     if grammar.name is 'JavaScript'


Test delete in ActiveRecord

Test delete in ActiveRecord

--- specs/access/active-record.coffee
+++ specs/access/active-record.coffee
@@ -42,3 +42,9 @@
             assert = (row) -> row.firstName.should.eql('The Greatest')
             assertIdOne assert, done, (cb) -> o.update(cb)

+    it 'Can delete a row', (done) ->
+        o = db.newObject('fighter')
+        db.fighters.findOne 4, (err, row) ->
+            return done(err) if err
+            o.load(row)
+            assertCount cntFighters - 1, done, (cb) -> o.delete(cb)


better configuration handling

better configuration handling

--- scripts/trello.coffee
+++ scripts/trello.coffee
@@ -1,5 +1,5 @@
 # Description:
-#   Get notifications from trello
+#   Get notifications from trello for a given board.
 #
 # Dependencies:
 #   "node-trello": "0.1.2"
@@ -19,7 +19,7 @@
 #   this can be changed
 #
 #   Get app token by heading to https://trello.com/1/connect?
-#       key=#{HUBOT_TRELLO_KEY}&name=#{HUBOT_TRELLO_APPNAME}&
+#       key=#{HUBOT_TRELLO_KEY}&name=<desired_app_name_here>&
 #       response_type=token&scope=read,write&expiration=never
 #
 # Author:
@@ -29,13 +29,11 @@

 check_interval_ms = process.env.HUBOT_TRELLO_INTERVAL ? 1000 * 60 * 5
 checker = undefined
-trello_key = process.env.HUBOT_TRELLO_KEY
-trello_token = process.env.HUBOT_TRELLO_TOKEN
-trello_board_id = process.env.HUBOT_TRELLO_BOARD_ID
-trello_notify_room = process.env.HUBOT_TRELLO_NOTIFY_ROOM
-
-last_notif_id = undefined
-
+config =
+  key: process.env.HUBOT_TRELLO_KEY
+  token: process.env.HUBOT_TRELLO_TOKEN
+  board_id: process.env.HUBOT_TRELLO_BOARD_ID
+  notify_room: process.env.HUBOT_TRELLO_NOTIFY_ROOM
 filter_actions =
   changeCard: (notif) ->
     if 'listAfter' not of notif.data
@@ -45,17 +43,18 @@
     "#{notif.memberCreator.username} commented on card `#{notif.data.card.name}`: #{notif.data.text} - #{cardUrl(notif.data.card)}"
   createdCard: (notif) ->
     "#{notif.memberCreator.username} created card `#{notif.data.card.name}` - #{cardUrl(notif.data.card)}"
+last_notif_id = undefined

 cardUrl = (card) ->
-  "https://trello.com/card/#{trello_board_id}/#{card.idShort}"
+  "https://trello.com/card/#{config.board_id}/#{card.idShort}"

 connect = (onConnected, onError) ->
-  console.error "missing HUBOT_TRELLO_KEY" if not trello_key
-  console.error "missing HUBOT_TRELLO_TOKEN" if not trello_token
-  console.error "missing HUBOT_TRELLO_BOARD_ID" if not trello_board_id
-  console.error "missing HUBOT_TRELLO_NOTIFY_ROOM" if not trello_notify_room
+  console.error "missing HUBOT_TRELLO_KEY" if not config.key
+  console.error "missing HUBOT_TRELLO_TOKEN" if not config.token
+  console.error "missing HUBOT_TRELLO_BOARD_ID" if not config.board_id
+  console.error "missing HUBOT_TRELLO_NOTIFY_ROOM" if not config.notify_room
   try
-    t = new Trello trello_key, trello_token
+    t = new Trello config.key, config.token
   catch e
     console.error e
     return onError?(e)
@@ -87,7 +86,7 @@
     get_notifs options, (notifs) ->
       console.info "#{notifs.length} new notifications received"
       for notif in notifs
-        robot.messageRoom(trello_notify_room, notif)
+        robot.messageRoom(config.notify_room, notif)
     , (err) ->
       msg.send "Error: #{err}"
   , check_interval_ms


added we should to trello

added we should to trello

--- scripts/trello.coffee
+++ scripts/trello.coffee
@@ -44,12 +44,29 @@
       msg.send "Please specify the Trello List ID in TRELLO_LIST_ID"
       return

-    Trello = require("node-trello");
-    t = new Trello(process.env.TRELLO_API_KEY, process.env.TRELLO_TOKEN);
-    args =
-      name: msg.match[1]
-      idList: process.env.TRELLO_LIST_ID
+    trelloIt msg

-    t.post "/1/cards", args, (err, data) ->
-      msg.send err if err
-      msg.send "Added card: " + args.name + " - " + data.url
+module.exports = (robot) ->
+  robot.hear /we should (.*)/i, (msg) ->
+    unless process.env.TRELLO_API_KEY?
+      msg.send "Please specify the Trello API key in TRELLO_API_KEY"
+      return
+    unless process.env.TRELLO_TOKEN?
+      msg.send "Please specify the Trello token in TRELLO_TOKEN"
+      return
+    unless process.env.TRELLO_LIST_ID?
+      msg.send "Please specify the Trello List ID in TRELLO_LIST_ID"
+      return
+
+    trelloIt msg
+
+trelloIt = (msg) ->
+  Trello = require("node-trello");
+  t = new Trello(process.env.TRELLO_API_KEY, process.env.TRELLO_TOKEN);
+  args =
+    name: msg.match[1]
+    idList: process.env.TRELLO_LIST_ID
+
+  t.post "/1/cards", args, (err, data) ->
+    msg.send err if err
+    msg.send "Added card: " + args.name + " - " + data.url


Oops, broke that test when I renamed path.

--- spec/stacktrace-spec.coffee
+++ spec/stacktrace-spec.coffee
@@ -26,7 +26,7 @@
         expect(trace.message).toBe('whoops (RuntimeError)')

       it 'parses file paths from each frame', ->
-        filePaths = (frame.path for frame in trace.frames)
+        filePaths = (frame.realPath for frame in trace.frames)
         expected = [
           '/home/smash/samples/tracer/otherdir/file2.rb'
           '/home/smash/samples/tracer/dir/file1.rb'


Removed some potential problems from Delaunay code.

--- examples/delaunay.coffee
+++ examples/delaunay.coffee
@@ -167,7 +167,7 @@
       else if x = seq([a, b], [b, c], [c, a]).find((e) => @third__.get(e)?)
         [f, g] = x
         h = @third__.get x
-        throw new Error "Orientation mismatch: #{[a,b,c]} <=> #{[f,g,h]}."
+        throw new Error "Orientation mismatch."
       else
         new Triangulation(
           @triangles__.plus(tri(a, b, c)),
@@ -340,7 +340,7 @@
       else
         [a, b] = stack.first()
         if T.mustFlip a, b
-          c = T.third b, a
+          c = T.third a, b
           recur -> doFlips flip(T, a, b), seq([a,c], [c,b]).concat stack.rest()
         else
           recur -> doFlips T, stack.rest()
@@ -353,10 +353,13 @@
       else
         t = @containingTriangle p
         [a, b, c] = t.vertices()
-        seq([a, b], [b, c], [c, a]).reduce subdivide(this, t, p), (T, [u, v]) ->
+        seq([b, a], [c, b], [a, c]).reduce subdivide(this, t, p), (T, [u, v]) ->
           if T.sideOf(u, v, p) == 0
-            w = T.third v, u
-            resolve doFlips flip(T, u, v), seq [u, w], [w, v]
+            w = T.third u, v
+            if w?
+              resolve doFlips flip(T, u, v), seq [u, w], [w, v]
+            else
+              T
           else
             resolve doFlips T, seq [u, v]

@@ -380,7 +383,7 @@

 test = ->
   rnd = -> Math.floor(Math.random() * 100)
-  t = Sequence.range(1, 1000).reduce delaunayTriangulation(),  (s, i) ->
+  t = Sequence.range(1, 200).reduce delaunayTriangulation(),  (s, i) ->
     s.plus new Point2d rnd(), rnd()

   Sequence.each t, (triangle) ->
@@ -394,4 +397,4 @@
         if t.mustFlip r, s
           console.log "Delaunay condition fails for #{u},#{v},#{w},#{x})", ->

-test()
+#test()


Fixed problem where text selection lost focus when mouse up happens out of textcontrol

--- js/foundation/eventmanager/eventmanager.coffee
+++ js/foundation/eventmanager/eventmanager.coffee
@@ -924,9 +924,9 @@
           _dropCtrl.endHover(_ctrl) if _dropCtrl.endHover?
           _dropCtrl.drop(_ctrl) if _dropCtrl.drop?
         _stop = true if _ctrl.endDrag( x, y, _leftClick )
-    for _viewId in _newActive
-      _ctrl = @_views[_viewId]
-      @changeActiveControl( _ctrl )
+    # for _viewId in _newActive
+    #   _ctrl = @_views[_viewId]
+    #   @changeActiveControl( _ctrl )
     @_listeners.hovered = []
     @_listeners.dragged = []
     @_cancelTextSelection() unless _endDragIds.length == 0 and _mouseUpIds.length == 0


Add Rdio

Add Rdio

--- packages/rocketchat-oembed/server/providers.coffee
+++ packages/rocketchat-oembed/server/providers.coffee
@@ -31,6 +31,9 @@
 providers.registerProvider
 	urls: [new RegExp('https?://www.youtube.com/\\S+'), new RegExp('https?://www.youtu.be/\\S+')]
 	endPoint: 'https://www.youtube.com/oembed?maxheight=200'
+providers.registerProvider
+	urls: [new RegExp('https?://www.rdio.com/\\S+'), new RegExp('https?://rd.io/\\S+')]
+	endPoint: 'http://www.rdio.com/api/oembed/?format=json&maxheight=150'

 RocketChat.oembed = {}
 RocketChat.oembed.providers = providers


Fix CPP # confusion

Fix CPP # confusion

Closes #70

--- src/haskell.coffee
+++ src/haskell.coffee
@@ -319,7 +319,7 @@
       match: /\b([0-9]+|0([xX][0-9a-fA-F]+|[oO][0-7]+))\b/
     ,
       name: 'meta.preprocessor.c'
-      begin: /{maybeBirdTrack}\s*(?=#)/
+      begin: /{maybeBirdTrack}(?=#)/
       end: '(?<!\\\\)(?=\\n)'
       patterns: [
         include: 'source.c'
@@ -374,6 +374,7 @@
         2: name: 'keyword.other.double-colon.haskell'
         3: {name: 'meta.type-signature.haskell', patterns: [include: '#type_signature']}
     ,
+      # match: '(::|∷)((?:(?:{className}|{functionName}|->|=>|[→⇒()\\[\\]]|\\s)(?!:<-|=))*)'
       match: '(::|∷)((?:{className}|{functionName}|\\->|=>|[→⇒()\\[\\]]|\\s)*)'
       captures:
         1: name: 'keyword.other.double-colon.haskell'


Fix for module_name

Fix for module_name

Module names with no dots weren't caught

--- src/haskell.coffee
+++ src/haskell.coffee
@@ -444,7 +444,7 @@
       ]
     module_name:
       name: 'support.other.module.haskell'
-      match: /(?:{className}\.)+{className}?/
+      match: /(?:{className}\.)*{className}\.?/
     pragma:
       name: 'meta.preprocessor.haskell'
       begin: /\{-#/


Ultimate fix for gh-693.

Ultimate fix for gh-693.

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -26,16 +26,16 @@

 applyOverrides = (config, options) ->
   options.env.forEach (override) ->
-    deepExtend config, config.overrides?[override] or {}, true
-  config
-
-deepExtend = (object, properties, isRoot) ->
+    deepExtend config, config.overrides?[override] or {}, config.files
+  config
+
+deepExtend = (object, properties, rootFiles = {}) ->
   Object.keys(properties).forEach (key) ->
     value = properties[key]
-    isIgnored = (isRoot and key is 'files')
-    if toString.call(value) is '[object Object]' and not isIgnored
+    # Special case for files[type]: don't merge properties.
+    if toString.call(value) is '[object Object]' and object isnt rootFiles
       object[key] ?= {}
-      deepExtend object[key], value
+      deepExtend object[key], value, rootFiles
     else
       object[key] = value
   object
@@ -75,6 +75,7 @@
   if isWindows then _.replace(/\\/g, '\/') else _

 exports.replaceConfigSlashes = replaceConfigSlashes = (config) ->
+  return config is isWindows
   files = config.files or {}
   Object.keys(files).forEach (language) ->
     lang = files[language] or {}
@@ -255,7 +256,7 @@

   config

-getConfigDeprecations = (config) ->
+warnAboutConfigDeprecations = (config) ->
   messages = []
   warnRemoved = (path) ->
     if config.paths[path]
@@ -281,7 +282,10 @@
   ensureNotArray 'assets'
   ensureNotArray 'test'
   ensureNotArray 'vendor'
-  messages
+
+  messages.forEach logger.warn
+
+  config

 normalizeConfig = (config) ->
   normalized = {}
@@ -315,15 +319,14 @@
     config = require(fullPath).config
   catch error
     throw new Error "couldn\'t load config #{fullPath}. #{error}"
+
   setConfigDefaults config, configPath
-
-  deprecations = getConfigDeprecations config
-  deprecations.forEach logger.warn if deprecations.length > 0
-
+  warnAboutConfigDeprecations config
   applyOverrides config, options
   deepExtend config, options
-  replaceConfigSlashes config if isWindows
+  replaceConfigSlashes config
   normalizeConfig config
+
   readComponents '.', 'bower', (error, bowerComponents) ->
     if error and not /ENOENT/.test(error.toString())
       logger.error error


Don't assign non-existent 'unmodified' character

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -240,7 +240,7 @@
     if characters
       if event.shiftKey
         key = characters.withShift
-      else
+      else if characters.unmodified?
         key = characters.unmodified

   # Work around https://bugs.chromium.org/p/chromium/issues/detail?id=766800


Preserve location range when rendering

Preserve location range when rendering

--- src/trix/controllers/editor_controller.coffee
+++ src/trix/controllers/editor_controller.coffee
@@ -35,7 +35,10 @@
   # Composition controller delegate

   compositionDidChangeDocument: (composition, document) ->
+    range = @selectionManager.getLocationRange()
     @documentController.render()
+    @selectionManager.setLocationRange(range) if range?
+
     @saveSerializedText()
     @toolbarController.updateActions()


Prevent toolbar dialogs from scrolling the page

Specifically fixes that hiding the link dialog would scroll to the top of the document because it focused the element before thawing the selection.

--- src/trix/controllers/editor_controller.coffee
+++ src/trix/controllers/editor_controller.coffee
@@ -225,19 +225,19 @@
     @recordFormattingUndoEntry()
     @composition.toggleCurrentAttribute(attributeName)
     @render()
-    @editorElement.focus()
+    @editorElement.focus() unless @selectionFrozen

   toolbarDidUpdateAttribute: (attributeName, value) ->
     @recordFormattingUndoEntry()
     @composition.setCurrentAttribute(attributeName, value)
     @render()
-    @editorElement.focus()
+    @editorElement.focus() unless @selectionFrozen

   toolbarDidRemoveAttribute: (attributeName) ->
     @recordFormattingUndoEntry()
     @composition.removeCurrentAttribute(attributeName)
     @render()
-    @editorElement.focus()
+    @editorElement.focus() unless @selectionFrozen

   toolbarWillShowDialog: (dialogElement) ->
     @composition.expandSelectionForEditing()
@@ -247,8 +247,8 @@
     @editorElement.notify("toolbar-dialog-show", {dialogName})

   toolbarDidHideDialog: (dialogName) ->
+    @thawSelection()
     @editorElement.focus()
-    @thawSelection()
     @editorElement.notify("toolbar-dialog-hide", {dialogName})

   # Selection


fixed replacing of allready present data object

--- modules/gui/platforms/node/lib/HttpContext.coffee
+++ modules/gui/platforms/node/lib/HttpContext.coffee
@@ -33,8 +33,9 @@
         ##
         start: (done)->

-            @_model.remote.data =
-                origin: @ID
+            if @_model.remote
+                @_model.remote.data ?= {}
+                @_model.remote.data.origin = @ID

             super done