Reorg Editor a bit based on conversations

Move heavier used sections near the top of the file. Make some of the
single extended methods public.

--- src/editor.coffee
+++ src/editor.coffee
@@ -545,7 +545,7 @@
       atom.clipboard.write(filePath)

   ###
-  Section: Saving
+  Section: File Operations
   ###

   # Essential: Saves the editor's text buffer.
@@ -553,14 +553,14 @@
   # See {TextBuffer::save} for more details.
   save: -> @buffer.save()

-  # Extended: Saves the editor's text buffer as the given path.
+  # Public: Saves the editor's text buffer as the given path.
   #
   # See {TextBuffer::saveAs} for more details.
   #
   # * `filePath` A {String} path.
   saveAs: (filePath) -> @buffer.saveAs(filePath)

-  # Extended: Determine whether the user should be prompted to save before closing
+  # Determine whether the user should be prompted to save before closing
   # this editor.
   shouldPromptToSave: -> @isModified() and not @buffer.hasMultipleEditors()

@@ -663,7 +663,7 @@
   # {Delegates to: TextBuffer.getEndPosition}
   getEofBufferPosition: -> @buffer.getEndPosition()

-  # Extended: Get the {Range} of the paragraph surrounding the most recently added
+  # Public: Get the {Range} of the paragraph surrounding the most recently added
   # cursor.
   #
   # Returns a {Range}.
@@ -685,6 +685,46 @@
   #
   # Returns the {Range} of the newly-inserted text.
   setTextInBufferRange: (range, text, normalizeLineEndings) -> @getBuffer().setTextInRange(range, text, normalizeLineEndings)
+
+  # Essential: For each selection, replace the selected text with the given text.
+  #
+  # * `text` A {String} representing the text to insert.
+  # * `options` (optional) See {Selection::insertText}.
+  #
+  # Returns a {Range} when the text has been inserted
+  # Returns a {Bool} false when the text has not been inserted
+  insertText: (text, options={}) ->
+    willInsert = true
+    cancel = -> willInsert = false
+    willInsertEvent = {cancel, text}
+    @emit('will-insert-text', willInsertEvent)
+    @emitter.emit 'will-insert-text', willInsertEvent
+
+    if willInsert
+      options.autoIndentNewline ?= @shouldAutoIndent()
+      options.autoDecreaseIndent ?= @shouldAutoIndent()
+      @mutateSelectedText (selection) =>
+        range = selection.insertText(text, options)
+        didInsertEvent = {text, range}
+        @emit('did-insert-text', didInsertEvent)
+        @emitter.emit 'did-insert-text', didInsertEvent
+        range
+    else
+      false
+
+  # Essential: For each selection, replace the selected text with a newline.
+  insertNewline: ->
+    @insertText('\n')
+
+  # Essential: For each selection, if the selection is empty, delete the character
+  # preceding the cursor. Otherwise delete the selected text.
+  delete: ->
+    @mutateSelectedText (selection) -> selection.delete()
+
+  # Essential: For each selection, if the selection is empty, delete the character
+  # preceding the cursor. Otherwise delete the selected text.
+  backspace: ->
+    @mutateSelectedText (selection) -> selection.backspace()

   # Extended: Mutate the text of all the selections in a single transaction.
   #
@@ -914,68 +954,6 @@
   joinLines: ->
     @mutateSelectedText (selection) -> selection.joinLines()

-  # Extended: Batch multiple operations as a single undo/redo step.
-  #
-  # Any group of operations that are logically grouped from the perspective of
-  # undoing and redoing should be performed in a transaction. If you want to
-  # abort the transaction, call {::abortTransaction} to terminate the function's
-  # execution and revert any changes performed up to the abortion.
-  #
-  # * `fn` A {Function} to call inside the transaction.
-  transact: (fn) -> @buffer.transact(fn)
-
-  # Extended: Start an open-ended transaction.
-  #
-  # Call {::commitTransaction} or {::abortTransaction} to terminate the
-  # transaction. If you nest calls to transactions, only the outermost
-  # transaction is considered. You must match every begin with a matching
-  # commit, but a single call to abort will cancel all nested transactions.
-  beginTransaction: -> @buffer.beginTransaction()
-
-  # Extended: Commit an open-ended transaction started with {::beginTransaction}
-  # and push it to the undo stack.
-  #
-  # If transactions are nested, only the outermost commit takes effect.
-  commitTransaction: -> @buffer.commitTransaction()
-
-  # Extended: Abort an open transaction, undoing any operations performed so far
-  # within the transaction.
-  abortTransaction: -> @buffer.abortTransaction()
-
-  ###
-  Section: Inserting Text
-  ###
-
-  # Essential: For each selection, replace the selected text with the given text.
-  #
-  # * `text` A {String} representing the text to insert.
-  # * `options` (optional) See {Selection::insertText}.
-  #
-  # Returns a {Range} when the text has been inserted
-  # Returns a {Bool} false when the text has not been inserted
-  insertText: (text, options={}) ->
-    willInsert = true
-    cancel = -> willInsert = false
-    willInsertEvent = {cancel, text}
-    @emit('will-insert-text', willInsertEvent)
-    @emitter.emit 'will-insert-text', willInsertEvent
-
-    if willInsert
-      options.autoIndentNewline ?= @shouldAutoIndent()
-      options.autoDecreaseIndent ?= @shouldAutoIndent()
-      @mutateSelectedText (selection) =>
-        range = selection.insertText(text, options)
-        didInsertEvent = {text, range}
-        @emit('did-insert-text', didInsertEvent)
-        @emitter.emit 'did-insert-text', didInsertEvent
-        range
-    else
-      false
-
-  # Essential: For each selection, replace the selected text with a newline.
-  insertNewline: ->
-    @insertText('\n')
-
   # Extended: For each cursor, insert a newline at beginning the following line.
   insertNewlineBelow: ->
     @transact =>
@@ -1000,20 +978,6 @@
         @moveUp()
         @moveToEndOfLine()

-  ###
-  Section: Removing Text
-  ###
-
-  # Essential: For each selection, if the selection is empty, delete the character
-  # preceding the cursor. Otherwise delete the selected text.
-  delete: ->
-    @mutateSelectedText (selection) -> selection.delete()
-
-  # Essential: For each selection, if the selection is empty, delete the character
-  # preceding the cursor. Otherwise delete the selected text.
-  backspace: ->
-    @mutateSelectedText (selection) -> selection.backspace()
-
   # Extended: For each selection, if the selection is empty, delete all characters
   # of the containing word that precede the cursor. Otherwise delete the
   # selected text.
@@ -1054,222 +1018,7 @@
     @deleteToBeginningOfLine()

   ###
-  Section: Searching Text
-  ###
-
-  # Essential: Scan regular expression matches in the entire buffer, calling the
-  # given iterator function on each match.
-  #
-  # If you're programmatically modifying the results, you may want to try
-  # {::backwardsScanInBufferRange} to avoid tripping over your own changes.
-  #
-  # * `regex` A {RegExp} to search for.
-  # * `iterator` A {Function} that's called on each match with an {Object}
-  #   containing the following keys:
-  #   * `match` The current regular expression match.
-  #   * `matchText` A {String} with the text of the match.
-  #   * `range` The {Range} of the match.
-  #   * `stop` Call this {Function} to terminate the scan.
-  #   * `replace` Call this {Function} with a {String} to replace the match.
-  scan: (regex, iterator) -> @buffer.scan(regex, iterator)
-
-  # Extended: Scan regular expression matches in a given range, calling the given
-  # iterator function on each match.
-  #
-  # * `regex` A {RegExp} to search for.
-  # * `range` A {Range} in which to search.
-  # * `iterator` A {Function} that's called on each match with an {Object}
-  #   containing the following keys:
-  #   * `match` The current regular expression match.
-  #   * `matchText` A {String} with the text of the match.
-  #   * `range` The {Range} of the match.
-  #   * `stop` Call this {Function} to terminate the scan.
-  #   * `replace` Call this {Function} with a {String} to replace the match.
-  scanInBufferRange: (regex, range, iterator) -> @buffer.scanInRange(regex, range, iterator)
-
-  # Extended: Scan regular expression matches in a given range in reverse order,
-  # calling the given iterator function on each match.
-  #
-  # * `regex` A {RegExp} to search for.
-  # * `range` A {Range} in which to search.
-  # * `iterator` A {Function} that's called on each match with an {Object}
-  #   containing the following keys:
-  #   * `match` The current regular expression match.
-  #   * `matchText` A {String} with the text of the match.
-  #   * `range` The {Range} of the match.
-  #   * `stop` Call this {Function} to terminate the scan.
-  #   * `replace` Call this {Function} with a {String} to replace the match.
-  backwardsScanInBufferRange: (regex, range, iterator) -> @buffer.backwardsScanInRange(regex, range, iterator)
-
-  ###
-  Section: Tab Behavior
-  ###
-
-  # Essential: Returns a {Boolean} indicating whether softTabs are enabled for this
-  # editor.
-  getSoftTabs: -> @softTabs
-
-  # Essential: Enable or disable soft tabs for this editor.
-  #
-  # * `softTabs` A {Boolean}
-  setSoftTabs: (@softTabs) -> @softTabs
-
-  # Essential: Toggle soft tabs for this editor
-  toggleSoftTabs: -> @setSoftTabs(not @getSoftTabs())
-
-  # Essential: Get the on-screen length of tab characters.
-  #
-  # Returns a {Number}.
-  getTabLength: -> @displayBuffer.getTabLength()
-
-  # Essential: Set the on-screen length of tab characters.
-  #
-  # * `tabLength` {Number} length of a single tab
-  setTabLength: (tabLength) -> @displayBuffer.setTabLength(tabLength)
-
-  # Extended: Determine if the buffer uses hard or soft tabs.
-  #
-  # Returns `true` if the first non-comment line with leading whitespace starts
-  # with a space character. Returns `false` if it starts with a hard tab (`\t`).
-  #
-  # Returns a {Boolean} or undefined if no non-comment lines had leading
-  # whitespace.
-  usesSoftTabs: ->
-    for bufferRow in [0..@buffer.getLastRow()]
-      continue if @displayBuffer.tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment()
-
-      line = @buffer.lineForRow(bufferRow)
-      return true  if line[0] is ' '
-      return false if line[0] is '\t'
-
-    undefined
-
-  # Extended: Get the text representing a single level of indent.
-  #
-  # If soft tabs are enabled, the text is composed of N spaces, where N is the
-  # tab length. Otherwise the text is a tab character (`\t`).
-  #
-  # Returns a {String}.
-  getTabText: -> @buildIndentString(1)
-
-  # If soft tabs are enabled, convert all hard tabs to soft tabs in the given
-  # {Range}.
-  normalizeTabsInBufferRange: (bufferRange) ->
-    return unless @getSoftTabs()
-    @scanInBufferRange /\t/g, bufferRange, ({replace}) => replace(@getTabText())
-
-  ###
-  Section: Soft Wrap Behavior
-  ###
-
-  # Essential: Determine whether lines in this editor are soft-wrapped.
-  #
-  # Returns a {Boolean}.
-  isSoftWrapped: (softWrapped) -> @displayBuffer.isSoftWrapped()
-  getSoftWrapped: ->
-    deprecate("Use Editor::isSoftWrapped instead")
-    @displayBuffer.isSoftWrapped()
-
-  # Essential: Enable or disable soft wrapping for this editor.
-  #
-  # * `softWrapped` A {Boolean}
-  #
-  # Returns a {Boolean}.
-  setSoftWrapped: (softWrapped) -> @displayBuffer.setSoftWrapped(softWrapped)
-  setSoftWrap: (softWrapped) ->
-    deprecate("Use Editor::setSoftWrapped instead")
-    @setSoftWrapped(softWrapped)
-
-  # Essential: Toggle soft wrapping for this editor
-  #
-  # Returns a {Boolean}.
-  toggleSoftWrapped: -> @setSoftWrapped(not @isSoftWrapped())
-  toggleSoftWrap: ->
-    deprecate("Use Editor::toggleSoftWrapped instead")
-    @toggleSoftWrapped()
-
-  # Extended: Gets the column at which column will soft wrap
-  getSoftWrapColumn: -> @displayBuffer.getSoftWrapColumn()
-
-  ###
-  Section: Indentation
-  ###
-
-  # Essential: Get the indentation level of the given a buffer row.
-  #
-  # Returns how deeply the given row is indented based on the soft tabs and
-  # tab length settings of this editor. Note that if soft tabs are enabled and
-  # the tab length is 2, a row with 4 leading spaces would have an indentation
-  # level of 2.
-  #
-  # * `bufferRow` A {Number} indicating the buffer row.
-  #
-  # Returns a {Number}.
-  indentationForBufferRow: (bufferRow) ->
-    @indentLevelForLine(@lineTextForBufferRow(bufferRow))
-
-  # Essential: Set the indentation level for the given buffer row.
-  #
-  # Inserts or removes hard tabs or spaces based on the soft tabs and tab length
-  # settings of this editor in order to bring it to the given indentation level.
-  # Note that if soft tabs are enabled and the tab length is 2, a row with 4
-  # leading spaces would have an indentation level of 2.
-  #
-  # * `bufferRow` A {Number} indicating the buffer row.
-  # * `newLevel` A {Number} indicating the new indentation level.
-  # * `options` (optional) An {Object} with the following keys:
-  #   * `preserveLeadingWhitespace` `true` to preserve any whitespace already at
-  #      the beginning of the line (default: false).
-  setIndentationForBufferRow: (bufferRow, newLevel, {preserveLeadingWhitespace}={}) ->
-    if preserveLeadingWhitespace
-      endColumn = 0
-    else
-      endColumn = @lineTextForBufferRow(bufferRow).match(/^\s*/)[0].length
-    newIndentString = @buildIndentString(newLevel)
-    @buffer.setTextInRange([[bufferRow, 0], [bufferRow, endColumn]], newIndentString)
-
-  # Extended: Indent rows intersecting selections by one level.
-  indentSelectedRows: ->
-    @mutateSelectedText (selection) -> selection.indentSelectedRows()
-
-  # Extended: Outdent rows intersecting selections by one level.
-  outdentSelectedRows: ->
-    @mutateSelectedText (selection) -> selection.outdentSelectedRows()
-
-  # Extended: Get the indentation level of the given line of text.
-  #
-  # Returns how deeply the given line is indented based on the soft tabs and
-  # tab length settings of this editor. Note that if soft tabs are enabled and
-  # the tab length is 2, a row with 4 leading spaces would have an indentation
-  # level of 2.
-  #
-  # * `line` A {String} representing a line of text.
-  #
-  # Returns a {Number}.
-  indentLevelForLine: (line) ->
-    @displayBuffer.indentLevelForLine(line)
-
-  # Extended: Indent rows intersecting selections based on the grammar's suggested
-  # indent level.
-  autoIndentSelectedRows: ->
-    @mutateSelectedText (selection) -> selection.autoIndentSelectedRows()
-
-  # Indent all lines intersecting selections. See {Selection::indent} for more
-  # information.
-  indent: (options={}) ->
-    options.autoIndent ?= @shouldAutoIndent()
-    @mutateSelectedText (selection) -> selection.indent(options)
-
-  # Constructs the string used for tabs.
-  buildIndentString: (number, column=0) ->
-    if @getSoftTabs()
-      tabStopViolation = column % @getTabLength()
-      _.multiplyString(" ", Math.floor(number * @getTabLength()) - tabStopViolation)
-    else
-      _.multiplyString("\t", Math.floor(number))
-
-  ###
-  Section: Undo Operations
+  Section: History
   ###

   # Essential: Undo the last change.
@@ -1281,6 +1030,34 @@
   redo: ->
     @getLastCursor().needsAutoscroll = true
     @buffer.redo(this)
+
+  # Extended: Batch multiple operations as a single undo/redo step.
+  #
+  # Any group of operations that are logically grouped from the perspective of
+  # undoing and redoing should be performed in a transaction. If you want to
+  # abort the transaction, call {::abortTransaction} to terminate the function's
+  # execution and revert any changes performed up to the abortion.
+  #
+  # * `fn` A {Function} to call inside the transaction.
+  transact: (fn) -> @buffer.transact(fn)
+
+  # Extended: Start an open-ended transaction.
+  #
+  # Call {::commitTransaction} or {::abortTransaction} to terminate the
+  # transaction. If you nest calls to transactions, only the outermost
+  # transaction is considered. You must match every begin with a matching
+  # commit, but a single call to abort will cancel all nested transactions.
+  beginTransaction: -> @buffer.beginTransaction()
+
+  # Extended: Commit an open-ended transaction started with {::beginTransaction}
+  # and push it to the undo stack.
+  #
+  # If transactions are nested, only the outermost commit takes effect.
+  commitTransaction: -> @buffer.commitTransaction()
+
+  # Extended: Abort an open transaction, undoing any operations performed so far
+  # within the transaction.
+  abortTransaction: -> @buffer.abortTransaction()

   ###
   Section: Editor Coordinates
@@ -1375,256 +1152,6 @@
   #
   # Returns a {Point}.
   clipScreenPosition: (screenPosition, options) -> @displayBuffer.clipScreenPosition(screenPosition, options)
-
-  ###
-  Section: Grammars
-  ###
-
-  # Essential: Get the current {Grammar} of this editor.
-  getGrammar: ->
-    @displayBuffer.getGrammar()
-
-  # Essential: Set the current {Grammar} of this editor.
-  #
-  # Assigning a grammar will cause the editor to re-tokenize based on the new
-  # grammar.
-  #
-  # * `grammar` {Grammar}
-  setGrammar: (grammar) ->
-    @displayBuffer.setGrammar(grammar)
-
-  # Reload the grammar based on the file name.
-  reloadGrammar: ->
-    @displayBuffer.reloadGrammar()
-
-  ###
-  Section: Managing Syntax Scopes
-  ###
-
-  # Public: Get the syntactic scopes for the most recently added cursor's
-  # position. See {::scopesForBufferPosition} for more information.
-  #
-  # Returns an {Array} of {String}s.
-  scopesAtCursor: -> @getLastCursor().getScopes()
-  getCursorScopes: ->
-    deprecate 'Use Editor::scopesAtCursor() instead'
-    @scopesAtCursor()
-
-  # Essential: Get the syntactic scopes for the given position in buffer
-  # coordinates.
-  #
-  # For example, if called with a position inside the parameter list of an
-  # anonymous CoffeeScript function, the method returns the following array:
-  # `["source.coffee", "meta.inline.function.coffee", "variable.parameter.function.coffee"]`
-  #
-  # * `bufferPosition` A {Point} or {Array} of [row, column].
-  #
-  # Returns an {Array} of {String}s.
-  scopesForBufferPosition: (bufferPosition) -> @displayBuffer.scopesForBufferPosition(bufferPosition)
-
-  # Extended: Get the range in buffer coordinates of all tokens surrounding the
-  # cursor that match the given scope selector.
-  #
-  # For example, if you wanted to find the string surrounding the cursor, you
-  # could call `editor.bufferRangeForScopeAtCursor(".string.quoted")`.
-  #
-  # Returns a {Range}.
-  bufferRangeForScopeAtCursor: (selector) ->
-    @displayBuffer.bufferRangeForScopeAtPosition(selector, @getCursorBufferPosition())
-
-  logCursorScope: ->
-    console.log @scopesAtCursor()
-
-  # {Delegates to: DisplayBuffer.tokenForBufferPosition}
-  tokenForBufferPosition: (bufferPosition) -> @displayBuffer.tokenForBufferPosition(bufferPosition)
-
-  # Extended: Determine if the given row is entirely a comment
-  isBufferRowCommented: (bufferRow) ->
-    if match = @lineTextForBufferRow(bufferRow).match(/\S/)
-      scopes = @tokenForBufferPosition([bufferRow, match.index]).scopes
-      @commentScopeSelector ?= new TextMateScopeSelector('comment.*')
-      @commentScopeSelector.matches(scopes)
-
-  ###
-  Section: Clipboard Operations
-  ###
-
-  # Essential: For each selection, copy the selected text.
-  copySelectedText: ->
-    maintainClipboard = false
-    for selection in @getSelections()
-      selection.copy(maintainClipboard)
-      maintainClipboard = true
-
-  # Essential: For each selection, cut the selected text.
-  cutSelectedText: ->
-    maintainClipboard = false
-    @mutateSelectedText (selection) ->
-      selection.cut(maintainClipboard)
-      maintainClipboard = true
-
-  # Essential: For each selection, replace the selected text with the contents of
-  # the clipboard.
-  #
-  # If the clipboard contains the same number of selections as the current
-  # editor, each selection will be replaced with the content of the
-  # corresponding clipboard selection text.
-  #
-  # * `options` (optional) See {Selection::insertText}.
-  pasteText: (options={}) ->
-    {text, metadata} = atom.clipboard.readWithMetadata()
-
-    containsNewlines = text.indexOf('\n') isnt -1
-
-    if metadata?.selections? and metadata.selections.length is @getSelections().length
-      @mutateSelectedText (selection, index) ->
-        text = metadata.selections[index]
-        selection.insertText(text, options)
-
-      return
-
-    else if atom.config.get("editor.normalizeIndentOnPaste") and metadata?.indentBasis?
-      if !@getLastCursor().hasPrecedingCharactersOnLine() or containsNewlines
-        options.indentBasis ?= metadata.indentBasis
-
-    @insertText(text, options)
-
-  # Extended: For each selection, if the selection is empty, cut all characters
-  # of the containing line following the cursor. Otherwise cut the selected
-  # text.
-  cutToEndOfLine: ->
-    maintainClipboard = false
-    @mutateSelectedText (selection) ->
-      selection.cutToEndOfLine(maintainClipboard)
-      maintainClipboard = true
-
-  ###
-  Section: Folds
-  ###
-
-  # Essential: Fold the most recent cursor's row based on its indentation level.
-  #
-  # The fold will extend from the nearest preceding line with a lower
-  # indentation level up to the nearest following row with a lower indentation
-  # level.
-  foldCurrentRow: ->
-    bufferRow = @bufferPositionForScreenPosition(@getCursorScreenPosition()).row
-    @foldBufferRow(bufferRow)
-
-  # Essential: Unfold the most recent cursor's row by one level.
-  unfoldCurrentRow: ->
-    bufferRow = @bufferPositionForScreenPosition(@getCursorScreenPosition()).row
-    @unfoldBufferRow(bufferRow)
-
-  # Essential: Fold the given row in buffer coordinates based on its indentation
-  # level.
-  #
-  # If the given row is foldable, the fold will begin there. Otherwise, it will
-  # begin at the first foldable row preceding the given row.
-  #
-  # * `bufferRow` A {Number}.
-  foldBufferRow: (bufferRow) ->
-    @languageMode.foldBufferRow(bufferRow)
-
-  # Essential: Unfold all folds containing the given row in buffer coordinates.
-  #
-  # * `bufferRow` A {Number}
-  unfoldBufferRow: (bufferRow) ->
-    @displayBuffer.unfoldBufferRow(bufferRow)
-
-  # Extended: For each selection, fold the rows it intersects.
-  foldSelectedLines: ->
-    selection.fold() for selection in @getSelections()
-
-  # Extended: Fold all foldable lines.
-  foldAll: ->
-    @languageMode.foldAll()
-
-  # Extended: Unfold all existing folds.
-  unfoldAll: ->
-    @languageMode.unfoldAll()
-
-  # Extended: Fold all foldable lines at the given indent level.
-  #
-  # * `level` A {Number}.
-  foldAllAtIndentLevel: (level) ->
-    @languageMode.foldAllAtIndentLevel(level)
-
-  # Extended: Determine whether the given row in buffer coordinates is foldable.
-  #
-  # A *foldable* row is a row that *starts* a row range that can be folded.
-  #
-  # * `bufferRow` A {Number}
-  #
-  # Returns a {Boolean}.
-  isFoldableAtBufferRow: (bufferRow) ->
-    @languageMode.isFoldableAtBufferRow(bufferRow)
-
-  # Extended: Determine whether the given row in screen coordinates is foldable.
-  #
-  # A *foldable* row is a row that *starts* a row range that can be folded.
-  #
-  # * `bufferRow` A {Number}
-  #
-  # Returns a {Boolean}.
-  isFoldableAtScreenRow: (screenRow) ->
-    bufferRow = @displayBuffer.bufferRowForScreenRow(screenRow)
-    @isFoldableAtBufferRow(bufferRow)
-
-  # Extended: Fold the given buffer row if it isn't currently folded, and unfold
-  # it otherwise.
-  toggleFoldAtBufferRow: (bufferRow) ->
-    if @isFoldedAtBufferRow(bufferRow)
-      @unfoldBufferRow(bufferRow)
-    else
-      @foldBufferRow(bufferRow)
-
-  # Extended: Determine whether the most recently added cursor's row is folded.
-  #
-  # Returns a {Boolean}.
-  isFoldedAtCursorRow: ->
-    @isFoldedAtScreenRow(@getCursorScreenPosition().row)
-
-  # Extended: Determine whether the given row in buffer coordinates is folded.
-  #
-  # * `bufferRow` A {Number}
-  #
-  # Returns a {Boolean}.
-  isFoldedAtBufferRow: (bufferRow) ->
-    @displayBuffer.isFoldedAtBufferRow(bufferRow)
-
-  # Extended: Determine whether the given row in screen coordinates is folded.
-  #
-  # * `screenRow` A {Number}
-  #
-  # Returns a {Boolean}.
-  isFoldedAtScreenRow: (screenRow) ->
-    @displayBuffer.isFoldedAtScreenRow(screenRow)
-
-  # TODO: Rename to foldRowRange?
-  createFold: (startRow, endRow) ->
-    @displayBuffer.createFold(startRow, endRow)
-
-  # {Delegates to: DisplayBuffer.destroyFoldWithId}
-  destroyFoldWithId: (id) ->
-    @displayBuffer.destroyFoldWithId(id)
-
-  # Remove any {Fold}s found that intersect the given buffer row.
-  destroyFoldsIntersectingBufferRange: (bufferRange) ->
-    for row in [bufferRange.start.row..bufferRange.end.row]
-      @unfoldBufferRow(row)
-
-  # {Delegates to: DisplayBuffer.largestFoldContainingBufferRow}
-  largestFoldContainingBufferRow: (bufferRow) ->
-    @displayBuffer.largestFoldContainingBufferRow(bufferRow)
-
-  # {Delegates to: DisplayBuffer.largestFoldStartingAtScreenRow}
-  largestFoldStartingAtScreenRow: (screenRow) ->
-    @displayBuffer.largestFoldStartingAtScreenRow(screenRow)
-
-  # {Delegates to: DisplayBuffer.outermostFoldsForBufferRowRange}
-  outermostFoldsInBufferRowRange: (startRow, endRow) ->
-    @displayBuffer.outermostFoldsInBufferRowRange(startRow, endRow)

   ###
   Section: Decorations
@@ -1671,7 +1198,7 @@
   decorateMarker: (marker, decorationParams) ->
     @displayBuffer.decorateMarker(marker, decorationParams)

-  # Extended: Get all the decorations within a screen row range.
+  # Public: Get all the decorations within a screen row range.
   #
   # * `startScreenRow` the {Number} beginning screen row
   # * `endScreenRow` the {Number} end screen row (inclusive)
@@ -1790,10 +1317,12 @@
     @displayBuffer.findMarkers(properties)

   # Extended: Get the {Marker} for the given marker id.
+  #
+  # * `id` {Number} id of the marker
   getMarker: (id) ->
     @displayBuffer.getMarker(id)

-  # Extended: Get all {Marker}s.
+  # Extended: Get all {Marker}s. Consider using {::findMarkers}
   getMarkers: ->
     @displayBuffer.getMarkers()

@@ -2562,6 +2091,472 @@
     @emit 'selection-screen-range-changed', selection
     @emitter.emit 'did-change-selection-range', selection

+  ###
+  Section: Searching and Replacing
+  ###
+
+  # Essential: Scan regular expression matches in the entire buffer, calling the
+  # given iterator function on each match.
+  #
+  # `::scan` functions as the replace method as well via the `replace`
+  #
+  # If you're programmatically modifying the results, you may want to try
+  # {::backwardsScanInBufferRange} to avoid tripping over your own changes.
+  #
+  # * `regex` A {RegExp} to search for.
+  # * `iterator` A {Function} that's called on each match
+  #   * `object` {Object}
+  #     * `match` The current regular expression match.
+  #     * `matchText` A {String} with the text of the match.
+  #     * `range` The {Range} of the match.
+  #     * `stop` Call this {Function} to terminate the scan.
+  #     * `replace` Call this {Function} with a {String} to replace the match.
+  scan: (regex, iterator) -> @buffer.scan(regex, iterator)
+
+  # Public: Scan regular expression matches in a given range, calling the given
+  # iterator function on each match.
+  #
+  # * `regex` A {RegExp} to search for.
+  # * `range` A {Range} in which to search.
+  # * `iterator` A {Function} that's called on each match with an {Object}
+  #   containing the following keys:
+  #   * `match` The current regular expression match.
+  #   * `matchText` A {String} with the text of the match.
+  #   * `range` The {Range} of the match.
+  #   * `stop` Call this {Function} to terminate the scan.
+  #   * `replace` Call this {Function} with a {String} to replace the match.
+  scanInBufferRange: (regex, range, iterator) -> @buffer.scanInRange(regex, range, iterator)
+
+  # Public: Scan regular expression matches in a given range in reverse order,
+  # calling the given iterator function on each match.
+  #
+  # * `regex` A {RegExp} to search for.
+  # * `range` A {Range} in which to search.
+  # * `iterator` A {Function} that's called on each match with an {Object}
+  #   containing the following keys:
+  #   * `match` The current regular expression match.
+  #   * `matchText` A {String} with the text of the match.
+  #   * `range` The {Range} of the match.
+  #   * `stop` Call this {Function} to terminate the scan.
+  #   * `replace` Call this {Function} with a {String} to replace the match.
+  backwardsScanInBufferRange: (regex, range, iterator) -> @buffer.backwardsScanInRange(regex, range, iterator)
+
+  ###
+  Section: Tab Behavior
+  ###
+
+  # Essential: Returns a {Boolean} indicating whether softTabs are enabled for this
+  # editor.
+  getSoftTabs: -> @softTabs
+
+  # Essential: Enable or disable soft tabs for this editor.
+  #
+  # * `softTabs` A {Boolean}
+  setSoftTabs: (@softTabs) -> @softTabs
+
+  # Essential: Toggle soft tabs for this editor
+  toggleSoftTabs: -> @setSoftTabs(not @getSoftTabs())
+
+  # Essential: Get the on-screen length of tab characters.
+  #
+  # Returns a {Number}.
+  getTabLength: -> @displayBuffer.getTabLength()
+
+  # Essential: Set the on-screen length of tab characters.
+  #
+  # * `tabLength` {Number} length of a single tab
+  setTabLength: (tabLength) -> @displayBuffer.setTabLength(tabLength)
+
+  # Extended: Determine if the buffer uses hard or soft tabs.
+  #
+  # Returns `true` if the first non-comment line with leading whitespace starts
+  # with a space character. Returns `false` if it starts with a hard tab (`\t`).
+  #
+  # Returns a {Boolean} or undefined if no non-comment lines had leading
+  # whitespace.
+  usesSoftTabs: ->
+    for bufferRow in [0..@buffer.getLastRow()]
+      continue if @displayBuffer.tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment()
+
+      line = @buffer.lineForRow(bufferRow)
+      return true  if line[0] is ' '
+      return false if line[0] is '\t'
+
+    undefined
+
+  # Extended: Get the text representing a single level of indent.
+  #
+  # If soft tabs are enabled, the text is composed of N spaces, where N is the
+  # tab length. Otherwise the text is a tab character (`\t`).
+  #
+  # Returns a {String}.
+  getTabText: -> @buildIndentString(1)
+
+  # If soft tabs are enabled, convert all hard tabs to soft tabs in the given
+  # {Range}.
+  normalizeTabsInBufferRange: (bufferRange) ->
+    return unless @getSoftTabs()
+    @scanInBufferRange /\t/g, bufferRange, ({replace}) => replace(@getTabText())
+
+  ###
+  Section: Soft Wrap Behavior
+  ###
+
+  # Essential: Determine whether lines in this editor are soft-wrapped.
+  #
+  # Returns a {Boolean}.
+  isSoftWrapped: (softWrapped) -> @displayBuffer.isSoftWrapped()
+  getSoftWrapped: ->
+    deprecate("Use Editor::isSoftWrapped instead")
+    @displayBuffer.isSoftWrapped()
+
+  # Essential: Enable or disable soft wrapping for this editor.
+  #
+  # * `softWrapped` A {Boolean}
+  #
+  # Returns a {Boolean}.
+  setSoftWrapped: (softWrapped) -> @displayBuffer.setSoftWrapped(softWrapped)
+  setSoftWrap: (softWrapped) ->
+    deprecate("Use Editor::setSoftWrapped instead")
+    @setSoftWrapped(softWrapped)
+
+  # Essential: Toggle soft wrapping for this editor
+  #
+  # Returns a {Boolean}.
+  toggleSoftWrapped: -> @setSoftWrapped(not @isSoftWrapped())
+  toggleSoftWrap: ->
+    deprecate("Use Editor::toggleSoftWrapped instead")
+    @toggleSoftWrapped()
+
+  # Public: Gets the column at which column will soft wrap
+  getSoftWrapColumn: -> @displayBuffer.getSoftWrapColumn()
+
+  ###
+  Section: Indentation
+  ###
+
+  # Essential: Get the indentation level of the given a buffer row.
+  #
+  # Returns how deeply the given row is indented based on the soft tabs and
+  # tab length settings of this editor. Note that if soft tabs are enabled and
+  # the tab length is 2, a row with 4 leading spaces would have an indentation
+  # level of 2.
+  #
+  # * `bufferRow` A {Number} indicating the buffer row.
+  #
+  # Returns a {Number}.
+  indentationForBufferRow: (bufferRow) ->
+    @indentLevelForLine(@lineTextForBufferRow(bufferRow))
+
+  # Essential: Set the indentation level for the given buffer row.
+  #
+  # Inserts or removes hard tabs or spaces based on the soft tabs and tab length
+  # settings of this editor in order to bring it to the given indentation level.
+  # Note that if soft tabs are enabled and the tab length is 2, a row with 4
+  # leading spaces would have an indentation level of 2.
+  #
+  # * `bufferRow` A {Number} indicating the buffer row.
+  # * `newLevel` A {Number} indicating the new indentation level.
+  # * `options` (optional) An {Object} with the following keys:
+  #   * `preserveLeadingWhitespace` `true` to preserve any whitespace already at
+  #      the beginning of the line (default: false).
+  setIndentationForBufferRow: (bufferRow, newLevel, {preserveLeadingWhitespace}={}) ->
+    if preserveLeadingWhitespace
+      endColumn = 0
+    else
+      endColumn = @lineTextForBufferRow(bufferRow).match(/^\s*/)[0].length
+    newIndentString = @buildIndentString(newLevel)
+    @buffer.setTextInRange([[bufferRow, 0], [bufferRow, endColumn]], newIndentString)
+
+  # Extended: Indent rows intersecting selections by one level.
+  indentSelectedRows: ->
+    @mutateSelectedText (selection) -> selection.indentSelectedRows()
+
+  # Extended: Outdent rows intersecting selections by one level.
+  outdentSelectedRows: ->
+    @mutateSelectedText (selection) -> selection.outdentSelectedRows()
+
+  # Extended: Get the indentation level of the given line of text.
+  #
+  # Returns how deeply the given line is indented based on the soft tabs and
+  # tab length settings of this editor. Note that if soft tabs are enabled and
+  # the tab length is 2, a row with 4 leading spaces would have an indentation
+  # level of 2.
+  #
+  # * `line` A {String} representing a line of text.
+  #
+  # Returns a {Number}.
+  indentLevelForLine: (line) ->
+    @displayBuffer.indentLevelForLine(line)
+
+  # Extended: Indent rows intersecting selections based on the grammar's suggested
+  # indent level.
+  autoIndentSelectedRows: ->
+    @mutateSelectedText (selection) -> selection.autoIndentSelectedRows()
+
+  # Indent all lines intersecting selections. See {Selection::indent} for more
+  # information.
+  indent: (options={}) ->
+    options.autoIndent ?= @shouldAutoIndent()
+    @mutateSelectedText (selection) -> selection.indent(options)
+
+  # Constructs the string used for tabs.
+  buildIndentString: (number, column=0) ->
+    if @getSoftTabs()
+      tabStopViolation = column % @getTabLength()
+      _.multiplyString(" ", Math.floor(number * @getTabLength()) - tabStopViolation)
+    else
+      _.multiplyString("\t", Math.floor(number))
+
+  ###
+  Section: Grammars
+  ###
+
+  # Essential: Get the current {Grammar} of this editor.
+  getGrammar: ->
+    @displayBuffer.getGrammar()
+
+  # Essential: Set the current {Grammar} of this editor.
+  #
+  # Assigning a grammar will cause the editor to re-tokenize based on the new
+  # grammar.
+  #
+  # * `grammar` {Grammar}
+  setGrammar: (grammar) ->
+    @displayBuffer.setGrammar(grammar)
+
+  # Reload the grammar based on the file name.
+  reloadGrammar: ->
+    @displayBuffer.reloadGrammar()
+
+  ###
+  Section: Managing Syntax Scopes
+  ###
+
+  # Public: Get the syntactic scopes for the most recently added cursor's
+  # position. See {::scopesForBufferPosition} for more information.
+  #
+  # Returns an {Array} of {String}s.
+  scopesAtCursor: -> @getLastCursor().getScopes()
+  getCursorScopes: ->
+    deprecate 'Use Editor::scopesAtCursor() instead'
+    @scopesAtCursor()
+
+  # Essential: Get the syntactic scopes for the given position in buffer
+  # coordinates.
+  #
+  # For example, if called with a position inside the parameter list of an
+  # anonymous CoffeeScript function, the method returns the following array:
+  # `["source.coffee", "meta.inline.function.coffee", "variable.parameter.function.coffee"]`
+  #
+  # * `bufferPosition` A {Point} or {Array} of [row, column].
+  #
+  # Returns an {Array} of {String}s.
+  scopesForBufferPosition: (bufferPosition) -> @displayBuffer.scopesForBufferPosition(bufferPosition)
+
+  # Extended: Get the range in buffer coordinates of all tokens surrounding the
+  # cursor that match the given scope selector.
+  #
+  # For example, if you wanted to find the string surrounding the cursor, you
+  # could call `editor.bufferRangeForScopeAtCursor(".string.quoted")`.
+  #
+  # Returns a {Range}.
+  bufferRangeForScopeAtCursor: (selector) ->
+    @displayBuffer.bufferRangeForScopeAtPosition(selector, @getCursorBufferPosition())
+
+  logCursorScope: ->
+    console.log @scopesAtCursor()
+
+  # {Delegates to: DisplayBuffer.tokenForBufferPosition}
+  tokenForBufferPosition: (bufferPosition) -> @displayBuffer.tokenForBufferPosition(bufferPosition)
+
+  # Extended: Determine if the given row is entirely a comment
+  isBufferRowCommented: (bufferRow) ->
+    if match = @lineTextForBufferRow(bufferRow).match(/\S/)
+      scopes = @tokenForBufferPosition([bufferRow, match.index]).scopes
+      @commentScopeSelector ?= new TextMateScopeSelector('comment.*')
+      @commentScopeSelector.matches(scopes)
+
+  ###
+  Section: Clipboard Operations
+  ###
+
+  # Essential: For each selection, copy the selected text.
+  copySelectedText: ->
+    maintainClipboard = false
+    for selection in @getSelections()
+      selection.copy(maintainClipboard)
+      maintainClipboard = true
+
+  # Essential: For each selection, cut the selected text.
+  cutSelectedText: ->
+    maintainClipboard = false
+    @mutateSelectedText (selection) ->
+      selection.cut(maintainClipboard)
+      maintainClipboard = true
+
+  # Essential: For each selection, replace the selected text with the contents of
+  # the clipboard.
+  #
+  # If the clipboard contains the same number of selections as the current
+  # editor, each selection will be replaced with the content of the
+  # corresponding clipboard selection text.
+  #
+  # * `options` (optional) See {Selection::insertText}.
+  pasteText: (options={}) ->
+    {text, metadata} = atom.clipboard.readWithMetadata()
+
+    containsNewlines = text.indexOf('\n') isnt -1
+
+    if metadata?.selections? and metadata.selections.length is @getSelections().length
+      @mutateSelectedText (selection, index) ->
+        text = metadata.selections[index]
+        selection.insertText(text, options)
+
+      return
+
+    else if atom.config.get("editor.normalizeIndentOnPaste") and metadata?.indentBasis?
+      if !@getLastCursor().hasPrecedingCharactersOnLine() or containsNewlines
+        options.indentBasis ?= metadata.indentBasis
+
+    @insertText(text, options)
+
+  # Public: For each selection, if the selection is empty, cut all characters
+  # of the containing line following the cursor. Otherwise cut the selected
+  # text.
+  cutToEndOfLine: ->
+    maintainClipboard = false
+    @mutateSelectedText (selection) ->
+      selection.cutToEndOfLine(maintainClipboard)
+      maintainClipboard = true
+
+  ###
+  Section: Folds
+  ###
+
+  # Essential: Fold the most recent cursor's row based on its indentation level.
+  #
+  # The fold will extend from the nearest preceding line with a lower
+  # indentation level up to the nearest following row with a lower indentation
+  # level.
+  foldCurrentRow: ->
+    bufferRow = @bufferPositionForScreenPosition(@getCursorScreenPosition()).row
+    @foldBufferRow(bufferRow)
+
+  # Essential: Unfold the most recent cursor's row by one level.
+  unfoldCurrentRow: ->
+    bufferRow = @bufferPositionForScreenPosition(@getCursorScreenPosition()).row
+    @unfoldBufferRow(bufferRow)
+
+  # Essential: Fold the given row in buffer coordinates based on its indentation
+  # level.
+  #
+  # If the given row is foldable, the fold will begin there. Otherwise, it will
+  # begin at the first foldable row preceding the given row.
+  #
+  # * `bufferRow` A {Number}.
+  foldBufferRow: (bufferRow) ->
+    @languageMode.foldBufferRow(bufferRow)
+
+  # Essential: Unfold all folds containing the given row in buffer coordinates.
+  #
+  # * `bufferRow` A {Number}
+  unfoldBufferRow: (bufferRow) ->
+    @displayBuffer.unfoldBufferRow(bufferRow)
+
+  # Extended: For each selection, fold the rows it intersects.
+  foldSelectedLines: ->
+    selection.fold() for selection in @getSelections()
+
+  # Extended: Fold all foldable lines.
+  foldAll: ->
+    @languageMode.foldAll()
+
+  # Extended: Unfold all existing folds.
+  unfoldAll: ->
+    @languageMode.unfoldAll()
+
+  # Extended: Fold all foldable lines at the given indent level.
+  #
+  # * `level` A {Number}.
+  foldAllAtIndentLevel: (level) ->
+    @languageMode.foldAllAtIndentLevel(level)
+
+  # Extended: Determine whether the given row in buffer coordinates is foldable.
+  #
+  # A *foldable* row is a row that *starts* a row range that can be folded.
+  #
+  # * `bufferRow` A {Number}
+  #
+  # Returns a {Boolean}.
+  isFoldableAtBufferRow: (bufferRow) ->
+    @languageMode.isFoldableAtBufferRow(bufferRow)
+
+  # Extended: Determine whether the given row in screen coordinates is foldable.
+  #
+  # A *foldable* row is a row that *starts* a row range that can be folded.
+  #
+  # * `bufferRow` A {Number}
+  #
+  # Returns a {Boolean}.
+  isFoldableAtScreenRow: (screenRow) ->
+    bufferRow = @displayBuffer.bufferRowForScreenRow(screenRow)
+    @isFoldableAtBufferRow(bufferRow)
+
+  # Extended: Fold the given buffer row if it isn't currently folded, and unfold
+  # it otherwise.
+  toggleFoldAtBufferRow: (bufferRow) ->
+    if @isFoldedAtBufferRow(bufferRow)
+      @unfoldBufferRow(bufferRow)
+    else
+      @foldBufferRow(bufferRow)
+
+  # Extended: Determine whether the most recently added cursor's row is folded.
+  #
+  # Returns a {Boolean}.
+  isFoldedAtCursorRow: ->
+    @isFoldedAtScreenRow(@getCursorScreenPosition().row)
+
+  # Extended: Determine whether the given row in buffer coordinates is folded.
+  #
+  # * `bufferRow` A {Number}
+  #
+  # Returns a {Boolean}.
+  isFoldedAtBufferRow: (bufferRow) ->
+    @displayBuffer.isFoldedAtBufferRow(bufferRow)
+
+  # Extended: Determine whether the given row in screen coordinates is folded.
+  #
+  # * `screenRow` A {Number}
+  #
+  # Returns a {Boolean}.
+  isFoldedAtScreenRow: (screenRow) ->
+    @displayBuffer.isFoldedAtScreenRow(screenRow)
+
+  # TODO: Rename to foldRowRange?
+  createFold: (startRow, endRow) ->
+    @displayBuffer.createFold(startRow, endRow)
+
+  # {Delegates to: DisplayBuffer.destroyFoldWithId}
+  destroyFoldWithId: (id) ->
+    @displayBuffer.destroyFoldWithId(id)
+
+  # Remove any {Fold}s found that intersect the given buffer row.
+  destroyFoldsIntersectingBufferRange: (bufferRange) ->
+    for row in [bufferRange.start.row..bufferRange.end.row]
+      @unfoldBufferRow(row)
+
+  # {Delegates to: DisplayBuffer.largestFoldContainingBufferRow}
+  largestFoldContainingBufferRow: (bufferRow) ->
+    @displayBuffer.largestFoldContainingBufferRow(bufferRow)
+
+  # {Delegates to: DisplayBuffer.largestFoldStartingAtScreenRow}
+  largestFoldStartingAtScreenRow: (screenRow) ->
+    @displayBuffer.largestFoldStartingAtScreenRow(screenRow)
+
+  # {Delegates to: DisplayBuffer.outermostFoldsForBufferRowRange}
+  outermostFoldsInBufferRowRange: (startRow, endRow) ->
+    @displayBuffer.outermostFoldsInBufferRowRange(startRow, endRow)

   ###
   Section: Scrolling the Editor


use done to catch q exceptions

use done to catch q exceptions

--- src/hinoki.coffee
+++ src/hinoki.coffee
@@ -67,6 +67,6 @@
                     onError = (err) ->
                         hasErrorOccured = true
                         throw new Error "error resolving promise returned from factory '#{id}'"
-                    instance.then onSuccess, onError
+                    instance.done(onSuccess, onError)

         maybeDone()


Better connection and errors handling

Better connection and errors handling

--- neo4jdriver.coffee
+++ neo4jdriver.coffee
@@ -1,3 +1,5 @@
+NTRU_def = process.env.NODE_TLS_REJECT_UNAUTHORIZED
+
 class Neo4jListener
   constructor: (listener, @_db) ->
     check listener, Function
@@ -21,6 +23,7 @@
     @base = opts.base or opts.root or opts.path or 'db/data'
     @base = @base.replace(/^\/|\/$/g, '')
     @root = "#{@url}/#{@base}"
+    @https = !!~@url.indexOf 'https://'
     check @base, String

     @__service = {}
@@ -61,16 +64,20 @@
             if results?.data
               for result in results.data
                 if clones?[result.id]
-                  if _.isEmpty result.body.errors
-                    @batchResults[result.id] = result.body
+                  if result?.body
+                    if _.isEmpty result.body?.errors
+                      @batchResults[result.id] = result.body
+                    else
+                      for error in result.body.errors
+                        console.error error.message
+                        console.error {code: error.code}
+                      @batchResults[result.id] = []
                   else
-                    for error in result.body.errors
-                      console.error error.message
-                      console.error {code: error.code}
                     @batchResults[result.id] = []

                   clones[result.id] = undefined
                   delete clones[result.id]
+
     , 100

   __getBatchResult: (id, cb) ->
@@ -112,26 +119,29 @@

   __connect: ->
     response = @__call @root
-    switch response.statusCode
-      when 200
-        if response.data.password_change_required
-          throw new Error "To connect to Neo4j - password change is required, please proceed to #{response.data.password_change}"
+    if response?.statusCode
+      switch response.statusCode
+        when 200
+          if response.data.password_change_required
+            throw new Error "To connect to Neo4j - password change is required, please proceed to #{response.data.password_change}"
+          else
+            for key, endpoint of response.data
+              if _.isString endpoint
+                if !!~endpoint.indexOf '://'
+                  @__service[key] = new Neo4jEndpoint key, endpoint, @
+                else
+                  @__service[key] = get: -> endpoint
+                console.success "v#{endpoint}" if key is "neo4j_version"
+            @ready = true
+            console.success "Meteor is successfully connected to Neo4j on #{@url}"
+        when 401
+          throw new Error JSON.stringify response
+        when 403
+          throw new Error JSON.stringify response
         else
-          for key, endpoint of response.data
-            if _.isString endpoint
-              if !!~endpoint.indexOf '://'
-                @__service[key] = new Neo4jEndpoint key, endpoint, @
-              else
-                @__service[key] = get: -> endpoint
-              console.success "v#{endpoint}" if key is "neo4j_version"
-          @ready = true
-          console.success "Meteor is successfully connected to Neo4j on #{@url}"
-      when 401
-        throw new Error JSON.stringify response
-      when 403
-        throw new Error JSON.stringify response
-      else
-        throw new Error JSON.stringify response
+          throw new Error JSON.stringify response
+    else
+      throw new Error "Error with connection to Neo4j"

   __httpCallSync: Meteor.wrapAsync HTTP.call

@@ -141,16 +151,24 @@
     check method, String
     check callback, Match.Optional Function

+    process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0
+
     if options?.headers
       options.headers = _.extend @defaultHeaders, options.headers
     else
       options.headers = @defaultHeaders

+    options = _.extend options, timeout: 10000
+
     try
       unless callback
-        return @__httpCallSync method, url, options
+        res = @__httpCallSync method, url, options
+        process.env.NODE_TLS_REJECT_UNAUTHORIZED = NTRU_def
+        return res
       else
-        HTTP.call method, url, options, callback
+        HTTP.call method, url, options, ->
+          process.env.NODE_TLS_REJECT_UNAUTHORIZED = NTRU_def
+          callback.apply @, arguments
     catch error
       console.error error


rtmp: Fix bug where extended timestamp is not read in type 3

#90

--- rtmp.coffee
+++ rtmp.coffee
@@ -26,6 +26,10 @@
 AVC_PACKET_TYPE_SEQUENCE_HEADER = 0
 AVC_PACKET_TYPE_NALU            = 1
 AVC_PACKET_TYPE_END_OF_SEQUENCE = 2
+
+EXTENDED_TIMESTAMP_TYPE_NOT_USED = 'not-used'
+EXTENDED_TIMESTAMP_TYPE_ABSOLUTE = 'absolute'
+EXTENDED_TIMESTAMP_TYPE_DELTA    = 'delta'

 TIMESTAMP_ROUNDOFF = 4294967296  # 32 bits

@@ -1256,6 +1260,12 @@
           break
         message.timestamp = (chunkMessageHeader[0] << 16) +
           (chunkMessageHeader[1] << 8) + chunkMessageHeader[2]
+
+        if message.timestamp is 0xffffff
+          message.extendedTimestampType = EXTENDED_TIMESTAMP_TYPE_ABSOLUTE
+        else
+          message.extendedTimestampType = EXTENDED_TIMESTAMP_TYPE_NOT_USED
+
         message.timestampDelta = 0
         message.messageLength = (chunkMessageHeader[3] << 16) +
           (chunkMessageHeader[4] << 8) + chunkMessageHeader[5]
@@ -1268,6 +1278,12 @@
           break
         message.timestampDelta = (chunkMessageHeader[0] << 16) +
           (chunkMessageHeader[1] << 8) + chunkMessageHeader[2]
+
+        if message.timestampDelta is 0xffffff
+          message.extendedTimestampType = EXTENDED_TIMESTAMP_TYPE_DELTA
+        else
+          message.extendedTimestampType = EXTENDED_TIMESTAMP_TYPE_NOT_USED
+
         message.messageLength = (chunkMessageHeader[3] << 16) +
           (chunkMessageHeader[4] << 8) + chunkMessageHeader[5]
         message.messageTypeID = chunkMessageHeader[6]
@@ -1284,6 +1300,12 @@
           break
         message.timestampDelta = (chunkMessageHeader[0] << 16) +
         (chunkMessageHeader[1] << 8) + chunkMessageHeader[2]
+
+        if message.timestampDelta is 0xffffff
+          message.extendedTimestampType = EXTENDED_TIMESTAMP_TYPE_DELTA
+        else
+          message.extendedTimestampType = EXTENDED_TIMESTAMP_TYPE_NOT_USED
+
         previousChunk = @previousChunkMessage[message.chunkStreamID]
         if previousChunk?
           message.timestamp = previousChunk.timestamp
@@ -1301,6 +1323,7 @@
           message.messageStreamID = previousChunk.messageStreamID
           message.messageLength = previousChunk.messageLength
           message.timestampDelta = previousChunk.timestampDelta
+          message.extendedTimestampType = previousChunk.extendedTimestampType
           message.messageTypeID = previousChunk.messageTypeID
         else
           throw new Error "#{@clientid}: Chunk reference error for type 3: previous chunk for id #{message.chunkStreamID} is not found (possibly a bug)"
@@ -1309,15 +1332,14 @@
         throw new Error "Unknown format type: #{formatType}"

       # 5.3.1.3. Extended Timestamp
-      if message.formatType is 0
-        if message.timestamp is 0xffffff
-          if chunkBody.length < 4  # buffer is incomplete
-            break
-          message.timestamp = (chunkBody[0] * Math.pow(256, 3)) +
-            (chunkBody[1] << 16) + (chunkBody[2] << 8) + chunkBody[3]
-          chunkBody = chunkBody[4..]
-          headerLen += 4
-      else if message.timestampDelta is 0xffffff
+      if message.extendedTimestampType is EXTENDED_TIMESTAMP_TYPE_ABSOLUTE
+        if chunkBody.length < 4  # buffer is incomplete
+          break
+        message.timestamp = (chunkBody[0] * Math.pow(256, 3)) +
+          (chunkBody[1] << 16) + (chunkBody[2] << 8) + chunkBody[3]
+        chunkBody = chunkBody[4..]
+        headerLen += 4
+      else if message.extendedTimestampType is EXTENDED_TIMESTAMP_TYPE_DELTA
         if chunkBody.length < 4  # buffer is incomplete
           break
         message.timestampDelta = (chunkBody[0] * Math.pow(256, 3)) +


rtmp: Improve comment

rtmp: Improve comment

--- rtmp.coffee
+++ rtmp.coffee
@@ -1,4 +1,4 @@
-# RTMP server
+# RTMP/RTMPE/RTMPT/RTMPTE server
 #
 # RTMP specification is available at:
 # http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/rtmp/pdf/rtmp_specification_1.0.pdf


null check path.split

null check path.split

https://sentry.io/sharelatex-1/sl-web-client-prod/issues/202702369/

--- services/web/public/coffee/ide/file-tree/FileTreeManager.coffee
+++ services/web/public/coffee/ide/file-tree/FileTreeManager.coffee
@@ -173,6 +173,8 @@
 			@_findEntityByPathInFolder @$scope.rootFolder, path

 		_findEntityByPathInFolder: (folder, path) ->
+			if !path? or !folder?
+				return null
 			parts = path.split("/")
 			name = parts.shift()
 			rest = parts.join("/")


Add one level of recursion, better error handling

--- js/book-aligner.coffee
+++ js/book-aligner.coffee
@@ -30,6 +30,8 @@
         dataType: 'json'
         crossDomain: true
         error: (jqXHR, textStatus, errorThrown) ->
+          $('#results').append($('
',{class: 'alert alert-danger', role: 'alert'}).text('Error in Fusion Tables AJAX call.'))
+          console.log errorThrown
           console.log "AJAX Error: #{textStatus}"
           error_callback() if error_callback?
         success: (data) ->
@@ -47,6 +49,8 @@
     dataType: 'json'
     crossDomain: true
     error: (jqXHR, textStatus, errorThrown) ->
+      $('#results').append($('
',{class: 'alert alert-danger', role: 'alert'}).text('Error in HathiTrust AJAX call.'))
+      console.log errorThrown
       console.log "AJAX Error: #{textStatus}"
     success: (data) ->
       console.log data
@@ -69,18 +73,21 @@
   match = identifier_string.match(HT_REGEX)
   ht_id = match[1].split('&')[0]
   process_ht_id(ht_id, 100)
+  ht_query(ht_id)
+
+ht_query = (ht_id, level = 0) ->
   fusion_tables_query "SELECT ia_identifier,score FROM #{HT_IA_TABLE_ID} WHERE ht_identifier = #{fusion_tables_escape(ht_id)} ORDER BY score DESC",
     (data) ->
       if data.rows?
-        process_ia_id(row[0],row[1]) for row in data.rows.reverse()
+        process_ia_id(row[0],(1 - level) * row[1]) for row in data.rows.reverse()
+        if level == 0
+          ia_query(row[0],1) for row in data.rows.reverse()
       else
         no_results()
-
 ht_url = (ht_id) ->
   "https://babel.hathitrust.org/cgi/pt?id=#{ht_id}"

 process_ht_id = (ht_id, score = 0) ->
-  ht_link = $('', {href: ht_url(ht_id), target: '_blank'}).text(ht_id)
   ht_biblio_query(ht_id, score)
   console.log ht_id

@@ -91,6 +98,8 @@
     dataType: 'json'
     crossDomain: true
     error: (jqXHR, textStatus, errorThrown) ->
+      $('#results').append($('
',{class: 'alert alert-danger', role: 'alert'}).text('Error in Internet Archive AJAX call.'))
+      console.log errorThrown
       console.log "AJAX Error: #{textStatus}"
     success: (data) ->
       console.log data
@@ -98,9 +107,9 @@
         $('#table').DataTable().row.add([
           "#{ia_id}",
           data.metadata.title,
-          data.metadata.year,
+          data.metadata.year || '',
           data.metadata.volume || '',
-          data.metadata.imagecount,
+          data.metadata.imagecount || '',
           score
         ]).draw(false)
         $('#table').DataTable().columns.adjust().draw()
@@ -115,10 +124,15 @@
   match = identifier_string.match(IA_REGEX)
   ia_id = match[1].split('&')[0]
   process_ia_id(ia_id, 100)
+  ia_query(ia_id)
+
+ia_query = (ia_id, level = 0) ->
   fusion_tables_query "SELECT ht_identifier,score FROM #{HT_IA_TABLE_ID} WHERE ia_identifier = #{fusion_tables_escape(ia_id)} ORDER BY score DESC",
     (data) ->
       if data.rows?
-        process_ht_id(row[0],row[1]) for row in data.rows.reverse()
+        process_ht_id(row[0],(1 - level) * row[1]) for row in data.rows.reverse()
+        if level == 0
+          ht_query(row[0], 1) for row in data.rows.reverse()
       else
         no_results()

@@ -126,7 +140,6 @@
   "https://archive.org/details/#{ia_id}"

 process_ia_id = (ia_id, score = 0) ->
-  ia_link = $('',{href: ia_url(ia_id),target: '_blank'}).text(ia_id)
   ia_biblio_query(ia_id, score)
   console.log ia_id

@@ -137,6 +150,8 @@
     dataType: 'json'
     crossDomain: true
     error: (jqXHR, textStatus, errorThrown) ->
+      $('#results').append($('
',{class: 'alert alert-danger', role: 'alert'}).text('Error in Google Books AJAX call.'))
+      console.log errorThrown
       console.log "AJAX Error: #{textStatus}"
     success: (data) ->
       console.log data
@@ -146,7 +161,7 @@
           data.volumeInfo.title,
           data.volumeInfo.publishedDate,
           '',
-          data.volumeInfo.pageCount,
+          data.volumeInfo.pageCount || '',
           score
         ]).draw(false)
         $('#table').DataTable().columns.adjust().draw()
@@ -157,16 +172,20 @@
   match = identifier_string.match(GB_REGEX)
   gb_id = match[1].split('&')[0]
   process_gb_id(gb_id, 100)
+  gb_query(gb_id)
+
+gb_query = (gb_id, level = 0) ->
   fusion_tables_query "SELECT ia_identifier FROM #{IA_GB_TABLE_ID} WHERE gb_identifier = #{fusion_tables_escape(gb_id)}",
     (data) ->
       if data.rows?
-        process_ia_id(row[0],100) for row in data.rows.reverse()
+        process_ia_id(row[0],(1 - level)*100) for row in data.rows.reverse()
+        if level == 0
+          ia_query(row[0],1) for row in data.rows.reverse()

 gb_url = (gb_id) ->
   "https://books.google.com/books?id=#{gb_id}"

 process_gb_id = (gb_id, score = 0) ->
-  gb_link = $('',{href: gb_url(gb_id),target: '_blank'}).text(gb_id)
   gb_biblio_query(gb_id, score)
   console.log gb_id


modify comments

modify comments

--- static/news/src/views/micropost.coffee
+++ static/news/src/views/micropost.coffee
@@ -40,7 +40,7 @@
     if not @selected
       $(@el).addClass("mouseover")

-  # When mouse is out background come back to normal.
+  # When mouse is out background comes back to normal.
   onMouseOut: ->
     $(@el).removeClass("mouseover")


chore(cleanup): spacing

chore(cleanup): spacing

--- e2e/reportCard/reportCard.spec.coffee
+++ e2e/reportCard/reportCard.spec.coffee
@@ -9,35 +9,29 @@
 	it 'should get the username', ->
 		@page.fillUsername 'CaryLandholt'
 		@page.search()
-
 		expect(@page.getFullName()).toEqual('Cary Landholt')

 	it 'should get the username', ->
 		@page.fillUsername 'jackmorrissey'
 		@page.search()
-
 		expect(@page.getFullName()).toEqual('Jack Morrissey')

 	it 'should get the username', ->
 		@page.fillUsername 'jyounce'
 		@page.search()
-
 		expect(@page.getFullName()).toEqual('Judd Younce')

 	it 'should get the username', ->
 		@page.fillUsername 'nmehta6'
 		@page.search()
-
 		expect(@page.getFullName()).toEqual('Nachiket Mehta')

 	it 'should get the username', ->
 		@page.fillUsername 'rdovhan'
 		@page.search()
-
 		expect(@page.getFullName()).toEqual('Roman')

 	it 'should get the username', ->
 		@page.fillUsername 'yokun'
 		@page.search()
-
 		expect(@page.getFullName()).toEqual('Yevgeniy Okun')


Add notifications to config recipe

Add notifications to config recipe

--- bin/recipes/config.workshop.template.cson
+++ bin/recipes/config.workshop.template.cson
@@ -1,8 +1,34 @@
 version: "0.1.9"
 liquibase:
   working_directory: ""
+  notifications:
+    development:
+      slack: ""
+      email: []
+      events: []
+    staging:
+      slack: ""
+      email: []
+      events: []
+    production:
+      slack: ""
+      email: []
+      events: []
 scriptella:
   working_directory: ""
+  notifications:
+    development:
+      slack: ""
+      email: []
+      events: []
+    staging:
+      slack: ""
+      email: []
+      events: []
+    production:
+      slack: ""
+      email: []
+      events: []
   etl_properties:
     environments:
       development:


Fixed double headers

Fixed double headers

--- src/scripts/diagnostics.coffee
+++ src/scripts/diagnostics.coffee
@@ -177,10 +177,6 @@
               "text": message
             }
       )
-      res.status 200
-      res.json {
-        "text": "Beginning to resync..."
-      }
     else
       res.status 401
       res.json {


clean up some code related to init syncstring; fix a race condition that didn't matter when everything was over one websocket...

--- src/smc-util/syncstring.coffee
+++ src/smc-util/syncstring.coffee
@@ -809,14 +809,16 @@
     # "@_syncstring_table.set(...)" below because it is critical to
     # to be able to do the touch before @_syncstring_table gets initialized,
     # since otherwise the initial open a file will be very slow.
-    touch: (min_age_m=5) =>
+    touch: (min_age_m=5, cb) =>
         if @_client.is_project()
+            cb?()
             return
         if min_age_m > 0
-            # if min_age_m is 0 always do it immediately; if > 0 check what it was:
+            # if min_age_m is 0 always try to do it immediately; if > 0 check what it was:
             last_active = @_syncstring_table?.get_one()?.get('last_active')
             # if not defined or not set recently, do it.
             if not (not last_active? or +last_active <= +misc.server_minutes_ago(min_age_m))
+                cb?()
                 return
         # Now actually do the set.
         @_client.query
@@ -828,6 +830,8 @@
                     deleted     : @_deleted
                     last_active : misc.server_time()
                     doctype     : misc.to_json(@_doctype)  # important to set here, since this is when syncstring is often first created
+            cb: cb
+

     # The project calls this once it has checked for the file on disk; this
     # way the frontend knows that the syncstring has been initialized in
@@ -911,7 +915,6 @@
         if not @_closed
             cb("already connected")
             return
-        @touch(0)   # critical to do a quick initial touch so file gets opened on the backend
         query =
             syncstrings :
                 string_id         : @_string_id
@@ -929,73 +932,86 @@
                 doctype           : null
                 archived          : null

-        @_syncstring_table = @_client.sync_table(query)
-
-        @_syncstring_table.once 'connected', =>
-            @_handle_syncstring_update()
-            @_syncstring_table.on('change', @_handle_syncstring_update)
-            async.series([
-                (cb) =>
-                    # wait until syncstring is not archived -- if we open a very old syncstring, the patches
-                    # may be archived; we have to wait until after they have been pulled from blob storage before
-                    # we init the patch table below, load from disk, etc.
-                    @_syncstring_table.wait
-                        until : (t) => not t.get_one()?.get('archived')
-                        cb    : cb
-                (cb) =>
-                    async.parallel([@_init_patch_list, @_init_cursors, @_init_evaluator], cb)
-                (cb) =>
-                    @_closed = false
-                    if @_client.is_user() and not @_periodically_touch?
-                        @touch(1)
-                        # touch every few minutes while syncstring is open, so that backend local_hub
-                        # (if open) keeps its side open
-                        @_periodically_touch = setInterval((=>@touch(TOUCH_INTERVAL_M/2)), 1000*60*TOUCH_INTERVAL_M)
-                    if @_client.is_project()
-                        @_load_from_disk_if_newer(cb)
-                    else
-                        cb()
-            ], (err) =>
-                if @_closed
-                    # closed while connecting...
+        async.series([
+            (cb) =>
+                # It is critical to do a quick initial touch so file gets opened on the
+                # backend or syncstring gets created (otherwise creation of various
+                # changefeeds below will FATAL fail).
+                @touch(0, cb)
+            (cb) =>
+                @_syncstring_table = @_client.sync_table(query)
+                @_syncstring_table.once 'connected', =>
+                    @_handle_syncstring_update()
+                    @_syncstring_table.on('change', @_handle_syncstring_update)
                     cb()
+            (cb) =>
+                # wait until syncstring is not archived -- if we open a very old syncstring, the patches
+                # may be archived; we have to wait until after they have been pulled from blob storage before
+                # we init the patch table below, load from disk, etc.
+                @_syncstring_table.wait
+                    until : (t) => not t.get_one()?.get('archived')
+                    cb    : cb
+            (cb) =>
+                async.parallel([@_init_patch_list, @_init_cursors, @_init_evaluator], cb)
+            (cb) =>
+                @_closed = false
+                if @_client.is_user() and not @_periodically_touch?
+                    @touch(1)
+                    # touch every few minutes while syncstring is open, so that backend local_hub
+                    # (if open) keeps its side open
+                    @_periodically_touch = setInterval((=>@touch(TOUCH_INTERVAL_M/2)), 1000*60*TOUCH_INTERVAL_M)
+                if @_client.is_project()
+                    @_load_from_disk_if_newer(cb)
+                else
+                    cb()
+        ], (err) =>
+            if @_closed
+                # closed while connecting...
+                cb()
+                return
+
+            if err
+                cb(err)
+            else
+                # Emit 'init' when ready for use.
+                @_wait_init()
+                @emit('change')
+                @emit('connected')
+                cb()
+        )
+
+    # wait until the syncstring table is ready to be used (so extracted from archive, etc.),
+    # and only then emit an init.
+    _wait_init: =>
+        @_syncstring_table?.wait
+            until : (t) =>
+                tbl = t.get_one()
+                # init must be set in table and archived must NOT be set (so patches are loaded from blob store)
+                init = tbl?.get('init')
+                if init and not tbl?.get('archived')
+                    return init
+                else
+                    return false
+            cb    : (err, init) =>
+                if @_closed # closed while waiting on condition (perfectly reasonable -- do nothing).
                     return
-                @_syncstring_table.wait
-                    until : (t) =>
-                        tbl = t.get_one()
-                        # init must be set in table and archived must NOT be set (so patches are loaded from blob store)
-                        init = tbl?.get('init')
-                        if init and not tbl?.get('archived')
-                            return init
-                        else
-                            return false
-                    cb    : (err, init) =>
-                        if @_closed # closed while waiting on condition (perfectly reasonable -- do nothing).
-                            return
-                        if err
-                            @emit('init', err)
-                            return
-                        init = init.toJS()
-                        err  = init.error
-                        if err
-                            @emit('init', err)
-                            return
-                        if @_client.is_user() and @_patch_list.count() == 0 and (init.size ? 0) > 0
-                            # wait for a change -- i.e., project loading the file from
-                            # disk and making available...  Because init.size > 0, we know that
-                            # there must be SOMETHING in the patches table once initialization is done.
-                            # This is the root cause of https://github.com/sagemathinc/cocalc/issues/2382
-                            @_patches_table.once 'change', =>
-                                @emit('init')
-                        else
-                            @emit('init')
                 if err
-                    cb(err)
+                    @emit('init', err)
+                    return
+                init = init.toJS()
+                err  = init.error
+                if err
+                    @emit('init', err)
+                    return
+                if @_client.is_user() and @_patch_list.count() == 0 and (init.size ? 0) > 0
+                    # wait for a change -- i.e., project loading the file from
+                    # disk and making available...  Because init.size > 0, we know that
+                    # there must be SOMETHING in the patches table once initialization is done.
+                    # This is the root cause of https://github.com/sagemathinc/cocalc/issues/2382
+                    @_patches_table.once 'change', =>
+                        @emit('init')
                 else
-                    @emit('change')
-                    @emit('connected')
-                    cb()
-            )
+                    @emit('init')

     # Delete the synchronized string and **all** patches from the database -- basically
     # delete the complete history of editing this file.


I didn't like the assymmetry between MIN and MAX.

--- src/markov.coffee
+++ src/markov.coffee
@@ -13,8 +13,8 @@
 #
 # Configuration:
 #   HUBOT_MARKOV_PLY - Order of the markov model to build. Default: 1
-#   HUBOT_MARKOV_MIN - Minimum number of tokens to use in training. Default: 1
-#   HUBOT_MARKOV_MAX - Maximum number of tokens in a generated chain. Default: 50
+#   HUBOT_MARKOV_LEARN_MIN - Minimum number of tokens to use in training. Default: 1
+#   HUBOT_MARKOV_GENERATE_MAX - Maximum number of tokens in a generated chain. Default: 50
 #
 # Commands:
 #   hubot markov <seed> - Generate a markov chain, optionally seeded with the provided phrase.
@@ -40,8 +40,8 @@

   # Read markov-specific configuration from the environment.
   ply = process.env.HUBOT_MARKOV_PLY or 1
-  min = process.env.HUBOT_MARKOV_MIN or 1
-  max = process.env.HUBOT_MARKOV_MAX or 50
+  min = process.env.HUBOT_MARKOV_LEARN_MIN or 1
+  max = process.env.HUBOT_MARKOV_GENERATE_MAX or 50

   model = new MarkovModel(storage, ply, min)


Add recents mode to homepage

Add recents mode to homepage

Feature request #1

--- server.coffee
+++ server.coffee
@@ -196,11 +196,12 @@

     # Recent entries
     @app.get '/recents', (req, res) =>
+      numRecents = @config.numRecents ? @recentInfo.recentFiles.length
       markdown = """
       ## Recent Entries

       """
-      for file in @recentInfo.recentFiles
+      for file in @recentInfo.recentFiles[...numRecents]
         entryUrl = "/#{file.year}/#{file.month}/#{file.datepart}/#{file.slug}"
         ymd = @formatDate file.year, file.month, file.date
         markdown += "#{ymd} [#{file.title or '(untitled)'}](#{entryUrl})\n"
@@ -228,7 +229,8 @@
       <id>#{@escapeTags @config.siteURL + '/'}</id>
       <updated>#{new Date(@recentInfo.generatedTime).toISOString()}</updated>
       """
-      for file in @recentInfo.recentFiles
+      numRecents = @config.numRecents ? @recentInfo.recentFiles.length
+      for file in @recentInfo.recentFiles[...numRecents]
         html = @convertToAbsoluteLinks file.body, {
           year       : file.year
           month      : file.month
@@ -262,48 +264,12 @@
       res.setHeader 'Content-Type', 'text/xml; charset=utf-8'
       res.send buf

-    # Homepage: display the most recent entry
+    # Homepage
     @app.get '/', (req, res) =>
-      file = @recentInfo.recentFiles[0]
-      if not file?
-        res.setHeader 'Content-Type', 'text/plain; charset=utf-8'
-        # Empty content message
-        res.send """
-        This is the place where your content will appear.
-
-        Add a first entry like this:
-
-        mkdir -p 2013/05
-        echo "# Test\\n\\nHello World" > 2013/05/27-test.md
-        git add .
-        git commit -m "add test entry"
-        git push origin master
-
-        Finished? Then reload this page slowly.
-        """
-        return
-      filepath = "#{@basedir}/#{file.year}/#{file.month}/#{file.datepart}-#{file.slug}.md"
-      fs.readFile filepath, {encoding:'utf8'}, (err, markdown) =>
-        if err
-          @logMessage err
-          @respondWithServerError res
-          return
-        url = "/#{file.year}/#{file.month}/#{file.datepart}/#{file.slug}"
-        @formatPage {
-          markdown    : markdown
-          link        : url
-          year        : file.year
-          month       : file.month
-          date        : file.date
-          noTitle     : true
-          absolutePath: true
-        }, (err, html) =>
-          if err
-            @logMessage err
-            @respondWithServerError res
-            return
-          res.setHeader 'Content-Type', 'text/html; charset=utf-8'
-          res.send html
+      if @config.homepage is 'recents'
+        @serveRecents req, res
+      else
+        @serveMostRecentEntry req, res

     # Not found
     @app.get '*', (req, res) =>
@@ -333,6 +299,7 @@
       err = @loadConfig()
       if not err?
         @logMessage "loaded #{path.basename @configFilename}"
+        @createIndex()

     # Load page template and watch it for change
     @pageTemplate = fs.readFileSync @pageTemplateFilename, {encoding:'utf8'}
@@ -569,7 +536,11 @@
     files

   # Scan entries and update index
-  createIndex: (numRecents=15) ->
+  createIndex: (numRecents) ->
+    if not numRecents?
+      numRecents = @config.numRecents ? 15
+      if @config.numHomepageRecents?
+        numRecents = Math.max @config.numHomepageRecents, numRecents
     @logMessage "indexing...", noNewline:true
     startTime = new Date().getTime()
     count = 0
@@ -616,6 +587,69 @@
     elapsedTime = new Date().getTime() - startTime
     @logMessage "done (#{elapsedTime} ms)", noTime:true

+  # Serve homepage in recents style
+  serveRecents: (req, res) ->
+    numRecents = @config.numHomepageRecents ? @config.numRecents ? 5
+    markdown = ''
+    for file in @recentInfo.recentFiles[...numRecents]
+      entryUrl = "/#{file.year}/#{file.month}/#{file.datepart}/#{file.slug}"
+      markdown += "[#{file.title or '(untitled)'}](#{entryUrl})\n"
+    @formatPage {
+      markdown   : markdown
+      noTitleLink: true
+      noAuthor   : true
+      noTitle    : true
+    }, (err, html) =>
+      if err
+        @logMessage err
+        @respondWithServerError res
+        return
+      res.setHeader 'Content-Type', 'text/html; charset=utf-8'
+      res.send html
+
+  # Show the most recent entry on homepage
+  serveMostRecentEntry: (req, res) ->
+    file = @recentInfo.recentFiles[0]
+    if not file?
+      res.setHeader 'Content-Type', 'text/plain; charset=utf-8'
+      # Empty content message
+      res.send """
+      This is the place where your content will appear.
+
+      Add a first entry like this:
+
+      mkdir -p 2013/05
+      echo "# Test\\n\\nHello World" > 2013/05/27-test.md
+      git add .
+      git commit -m "add test entry"
+      git push origin master
+
+      Finished? Then reload this page slowly.
+      """
+      return
+    filepath = "#{@basedir}/#{file.year}/#{file.month}/#{file.datepart}-#{file.slug}.md"
+    fs.readFile filepath, {encoding:'utf8'}, (err, markdown) =>
+      if err
+        @logMessage err
+        @respondWithServerError res
+        return
+      url = "/#{file.year}/#{file.month}/#{file.datepart}/#{file.slug}"
+      @formatPage {
+        markdown    : markdown
+        link        : url
+        year        : file.year
+        month       : file.month
+        date        : file.date
+        noTitle     : true
+        absolutePath: true
+      }, (err, html) =>
+        if err
+          @logMessage err
+          @respondWithServerError res
+          return
+        res.setHeader 'Content-Type', 'text/html; charset=utf-8'
+        res.send html
+
 module.exports = BitterServer

 if not module.parent?


git diff server.coffee

git diff server.coffee

--- server.coffee
+++ server.coffee
@@ -65,7 +65,7 @@
 		.map (file) ->
 			out = {
 				date: file[1]
-				title: hmz.titleCase file[2].replace '-', ' '
+				title: hmz.titleCase file[2].replace /-/g, ' '
 				path: [file[1],file[2]].join('-')
 			}

@@ -77,7 +77,7 @@
 				out
 		.then (blogs) ->
 			_(blogs).sortBy (blog) ->
-				parseInt blog.date.replace('-',''), 10
+				parseInt blog.date.replace(/-/g,''), 10
 			.reverse()
 			.valueOf()
 		.catch (err) -> []
@@ -85,7 +85,7 @@
 blog_posts = (start,count) ->
 	blog_load(true).then (blogs) ->
 		_(blogs).sortBy (blog) ->
-			parseInt blog.date.replace('-',''), 10
+			parseInt blog.date.replace(/-/g,''), 10
 		.slice start, count
 		.valueOf()


fix leave lobby issue

fix leave lobby issue

--- server.coffee
+++ server.coffee
@@ -94,8 +94,10 @@
         lobby.emit('netrunner', {type: "games", games: games, notification: "ting"})

       when "leave-lobby"
-        socket.to(socket.gameid).emit('netrunner', {type: "say", user: "__system__", text: "#{socket.request.user.username} left the game."})
+        gameid = socket.gameid
         removePlayer(socket)
+        if socket.request.user
+          socket.broadcast.to(gameid).emit('netrunner', {type: "say", user: "__system__", text: "#{socket.request.user.username} left the game."})

       when "leave-game"
         msg.action = "quit"


prefer ondemand browserify

prefer ondemand browserify

--- server.coffee
+++ server.coffee
@@ -52,9 +52,6 @@
   app.get '', (req, res)->
     res.render('not_configured', {title: 'Not Configured'})

-# serve static files
-app.use express.static(__dirname + "/public")
-
 if process.env.NODE_ENV == 'development'
   browserify  = require("connect-browserify")
   nodejsx     = require("node-cjsx").transform()
@@ -66,6 +63,9 @@
     extensions: [".cjsx", ".coffee", ".js", ".json"]
   )

+# serve static files
+app.use express.static(__dirname + "/public")
+
 httpServer.listen port, ->
   console.log "HTTP server started at http://localhost.cine.io:#{port}"


Add DEBUG_DISABLE_UDP_TRANSPORT

Add DEBUG_DISABLE_UDP_TRANSPORT

--- server.coffee
+++ server.coffee
@@ -52,6 +52,10 @@
 # If true, RTSP requests/responses tunneled in HTTP will be
 # printed to the console
 DEBUG_HTTP_TUNNEL = false
+
+# If true, UDP transport will always be disabled and
+# clients will be forced to use TCP transport.
+DEBUG_DISABLE_UDP_TRANSPORT = false

 detectedVideoWidth = null
 detectedVideoHeight = null
@@ -1356,12 +1360,12 @@
     serverPort = null
     track = null

-    # Check tranpsort
-    # Disable UDP transport over the internet
-#    if client.isGlobal and not /\bTCP\b/.test req.headers.transport
-#      console.log "Unsupported transport: UDP is temporarily disabled"
-#      respondWithUnsupportedTransport callback, {CSeq: req.headers.cseq}
-#      return
+    if DEBUG_DISABLE_UDP_TRANSPORT and
+    (not /\bTCP\b/.test req.headers.transport)
+      # Disable UDP transport and force the client to switch to TCP transport
+      console.log "Unsupported transport: UDP is disabled"
+      respondWithUnsupportedTransport callback, {CSeq: req.headers.cseq}
+      return

     if /trackID=1/.test req.uri  # audio
       track = 'audio'
@@ -1465,6 +1469,7 @@
       return

     preventFromPlaying = false
+
 #    if (req.headers['user-agent']?.indexOf('QuickTime') > -1) and
 #    not client.getClient?.useTCPForVideo
 #      # QuickTime produces poor quality image over UDP.


share: fix easy scroll css issue

share: fix easy scroll css issue

--- src/smc-webapp/share/page.cjsx
+++ src/smc-webapp/share/page.cjsx
@@ -113,7 +113,7 @@
                 {@render_noindex()}

-

+

                     {@render_topbar()}

                         {@props.children}


Implement Volt Switch.

Implement Volt Switch.

--- data/bw/moves.coffee
+++ data/bw/moves.coffee
@@ -793,6 +793,10 @@
   @afterSuccessfulHit = (battle, user, target) ->
     battle.forceSwitch(user)

+extendMove 'volt-switch', ->
+  @afterSuccessfulHit = (battle, user, target) ->
+    battle.forceSwitch(user)
+
 extendMove 'weather-ball', ->
   @getType = (battle, user, target) ->
     if      battle.hasWeather(Weather.SUN)  then 'Fire'


Add check to prevent duplicate names.

Add check to prevent duplicate names.

--- server.coffee
+++ server.coffee
@@ -446,11 +446,12 @@
   @addMigration: (migration) ->
     throw new Error "Migration is missing a name" unless migration.name
     throw new Error "Migration is not a migration instance" unless migration instanceof @_Migration
+    throw new Error "Migration with the name '#{ migration.name }' already exists" if migration.name in _.pluck @Meta.migrations, 'name'

     @Meta.migrations.push migration

   @renameCollectionMigration: (oldName, newName) ->
-    @Meta.migrations.push new @_RenameMigration oldName, newName
+    @addMigration new @_RenameMigration oldName, newName

   @migrateForward: (untilName) ->
     # TODO: Implement


counterS

counterS

--- server.coffee
+++ server.coffee
@@ -64,7 +64,7 @@
     userId = App.userId()
     f = Math.floor(Math.random()*50)

-    oldSorted = (+k for k,v of Db.shared.get('counters') when +k).sort (a,b) -> Db.shared.get('counters', b) - Db.shared.get('counter', a)
+    oldSorted = (+k for k,v of Db.shared.get('counters') when +k).sort (a,b) -> Db.shared.get('counters', b) - Db.shared.get('counters', a)
     log "oldSorted", JSON.stringify oldSorted
     oldPos = oldSorted.indexOf userId
     log "oldPos", oldPos
@@ -76,7 +76,7 @@
         r = Math.floor(Math.random()*lines.length)
         Db.personal(userId).set('funnies', lines[r])

-    newSorted = (+k for k,v of Db.shared.get('counters') when +k).sort (a,b) -> Db.shared.get('counters', b) - Db.shared.get('counter', a)
+    newSorted = (+k for k,v of Db.shared.get('counters') when +k).sort (a,b) -> Db.shared.get('counters', b) - Db.shared.get('counters', a)
     log "newSorted", JSON.stringify newSorted
     newPos = oldSorted.indexOf userId
     log "newPos", newPos


Pass obj (this) to lazy default value initializer

--- bokehjs/src/coffee/core/properties.coffee
+++ bokehjs/src/coffee/core/properties.coffee
@@ -89,7 +89,7 @@
       attr_value = switch
         when _.isUndefined(default_value) then null
         when _.isArray(default_value)     then _.clone(default_value)
-        when _.isFunction(default_value)  then default_value()
+        when _.isFunction(default_value)  then default_value(obj)
         else                                   default_value

       obj.set(attr, attr_value, {silent: true, defaults: true})


payment: move inline configurations out of the constructor

+ use the new "required" helper

--- client/landing/app/Applications/Dashboard.kdapplication/views/producteditform.coffee
+++ client/landing/app/Applications/Dashboard.kdapplication/views/producteditform.coffee
@@ -15,8 +15,7 @@
         type      : "submit"
       cancel      :
         cssClass  : "modal-cancel"
-        callback  : =>
-          @emit 'CancelRequested'
+        callback  : => @emit 'CancelRequested'

     options.fields ?=

@@ -24,34 +23,19 @@
         label           : "Title"
         placeholder     : options.placeholders?.title
         defaultValue    : data.decoded 'title'
+        required        : 'Title is required!'

       description       :
         label           : "Description"
         placeholder     : options.placeholders?.description or "(optional)"
         defaultValue    : data.decoded 'description'

-
       subscriptionType  :
         label           : "Subscription type"
         itemClass       : KDSelectBox
         defaultValue    : data.subscriptionType ? "mo"
-        selectOptions   : do ->
-
-          selectOptions = [
-            { title: "Recurs every month",     value: 'mo' }
-            { title: "Recurs every 3 months",  value: '3 mo' }
-            { title: "Recurs every 6 months",  value: '6 mo' }
-            { title: "Recurs every year",      value: 'yr' }
-            { title: "Recurs every 2 years",   value: '2 yr' }
-            { title: "Recurs every 5 years",   value: '5 yr' }
-          ]
-
-          if options.isRecurOptional
-            selectOptions.push { title: "Single payment", value: 'single' }
-
-          return selectOptions
-
-        callback: @bound 'handleSubscriptionType'
+        selectOptions   : @getSubscriptionOptions()
+        callback        : @bound 'subscriptionTypeChanged'

     options.fields.feeAmount ?=
       label           : "Amount"
@@ -59,10 +43,7 @@
       defaultValue    :
         if data.feeAmount
         then (data.feeAmount / 100).toFixed(2)
-      change          : ->
-        num = parseFloat @getValue()
-
-        @setValue if isNaN num then '' else num.toFixed(2)
+      change          : @bound 'feeChanged'
       nextElementFlat :

         perMonth      :
@@ -75,9 +56,7 @@
         label         : "Price is volatile"
         itemClass     : KDOnOffSwitch
         defaultValue  : data.priceIsVolatile
-        callback      : =>
-          enabled = @inputs.priceIsVolatile.getValue()
-          do @fields.feeAmount[if enabled then 'hide' else 'show']
+        callback      : @bound 'priceVolatilityChanged'

     if options.showOverage
       options.fields.overageEnabled =
@@ -131,10 +110,35 @@
         priceIsVolatile
       }

-  handleSubscriptionType: ->
+  getSubscriptionOptions:->
+
+    selectOptions = [
+      { title: "Recurs every month",     value: 'mo' }
+      { title: "Recurs every 3 months",  value: '3 mo' }
+      { title: "Recurs every 6 months",  value: '6 mo' }
+      { title: "Recurs every year",      value: 'yr' }
+      { title: "Recurs every 2 years",   value: '2 yr' }
+      { title: "Recurs every 5 years",   value: '5 yr' }
+    ]
+
+    if @getOptions().isRecurOptional
+      selectOptions.push { title: "Single payment", value: 'single' }
+
+    return selectOptions
+
+  subscriptionTypeChanged: ->
     subscriptionType = @inputs.subscriptionType.getValue()
     if subscriptionType is 'single'
       @inputs.perMonth.hide()
     else
       @inputs.perMonth.show()
       @inputs.perMonth.updatePartial "/ #{subscriptionType}"
+
+  feeChanged: (num) ->
+    num = parseFloat @getValue()
+
+    @setValue if isNaN num then '' else num.toFixed(2)
+
+  priceVolatilityChanged: ->
+    enabled = @inputs.priceIsVolatile.getValue()
+    do @fields.feeAmount[if enabled then 'hide' else 'show']


Corrected test name

Corrected test name

--- test/generators/collection/sync.coffee
+++ test/generators/collection/sync.coffee
@@ -20,7 +20,7 @@
     @schema: BASE_SCHEMA
     sync: SYNC(Model)

-  describe "Utils.batch (cache: #{cache})", ->
+  describe "Backbone.Collection (cache: #{cache})", ->

     before (done) -> return done() unless options.before; options.before([Model], done)
     after (done) -> callback(); done()


typos around

typos around

--- kites/sharedHosting/api.coffee
+++ kites/sharedHosting/api.coffee
@@ -26,10 +26,10 @@
     @message = message or "You are not authorized to do this."
     @name = 'AuthorizationError'

-normalizeUserPath = (username, path)-> path.replace(/\~/g, '/Users/#{username}')
-safeForUser       = (username, path)-> path.indexOf("/Users/#{username}/") is 0
-escapePath        = (path)-> nodePath.normalize path.replace(/[^a-zA-Z0-9\/\-. ]/g, '')
-                                                    .replace(/\s/g, '\\ ')
+normalizeUserPath = (username, path)-> path?.replace(/\~/g, '/Users/#{username}')
+safeForUser       = (username, path)-> path?.indexOf("/Users/#{username}/") is 0
+escapePath        = (path)-> if path then nodePath.normalize path.replace(/[^a-zA-Z0-9\/\-. ]/g, '')
+                                                                 .replace(/\s/g, '\\ ')

 makedirp = (path, user, cb)->
   exec "mkdir -p #{path} && chown #{user}: #{path}", cb
@@ -227,7 +227,7 @@

     {username, profile, version, appName, userAppPath} = options

-    if not username in dummyAdmins
+    if username not in dummyAdmins
       callback? new AuthorizationError username
       return no

@@ -324,7 +324,7 @@

     {username, authorNick, version, appName} = options

-    if not username in dummyAdmins
+    if username not in dummyAdmins
       callback? new AuthorizationError username
       return no


umount drive before remove

umount drive before remove

--- kites/sharedHosting/api.coffee
+++ kites/sharedHosting/api.coffee
@@ -27,13 +27,17 @@

   mountDrive    : (options, callback)-> mounter.mountDrive      options, callback
   umountDrive   : (options, callback)-> mounter.umountDrive     options, callback
-  removeMount   : (options, callback)-> mounter.removeMount     options, callback
   mountFtpDrive : (options, callback)-> mounter.mountFtpDrive   options, callback
+
+  removeMount   : (options, callback)->
+    mounter.umountDrive options, (error, res)->
+      mounter.removeMount options, callback

   readMountInfo : (options, callback)->
     mounter.readMountInfo options, (err, res)->
       if err then callback err
       else
+        # Check each entry's mount state and remove password
         updateEntries = (entries, index)=>
           if index == entries.length
             callback err, entries


Fix attachment removal test

Fix attachment removal test

--- test/src/system/attachment_test.coffee
+++ test/src/system/attachment_test.coffee
@@ -13,7 +13,7 @@
   test "removing an image", (expectDocument) ->
     after 20, ->
       clickElement getFigure(), ->
-        closeButton = getFigure().querySelector(".#{Trix.config.css.classNames.attachment.removeButton}")
+        closeButton = getFigure().querySelector(".#{Trix.config.css.classNames.attachment.removeButton.split(" ").join(".")}")
         clickElement closeButton, ->
           expectDocument "ab\n"


shiet fix for omck.moe

shiet fix for omck.moe

--- app/assets/javascripts/home.js.coffee
+++ app/assets/javascripts/home.js.coffee
@@ -79,8 +79,8 @@
                 playlist: [ {
                   image: '/assets/bg/omck.jpg'
                   sources: [
-                    { file: 'rtmp://local.dev/'+omck+'/omcktv' }
-                    { file: 'http://local.dev/hls/'+omck+'/omcktv.m3u8' }
+                    { file: 'rtmp://hd.omck.moe/'+omck+'/omcktv' }
+                    { file: 'http://hd.omck.moe/hls/'+omck+'/omcktv.m3u8' }
                   ]
                 } ]
                 primary: 'flash'


validate regexp in query box.

validate regexp in query box.

--- app.nw/sadebugger.coffee
+++ app.nw/sadebugger.coffee
@@ -55,11 +55,21 @@
   scope:
     update: '&'
     visible: '@'
+  controller: ($scope) ->
+    $scope.change = ->
+      console.debug 'change:', @value
+      @pattern = @error = null
+      if @value
+        try
+          @pattern = new RegExp @value
+        catch error
+          console.error "bad query: #{error}"
+          @error = error
   template: '''

     <form class="form-search" style="margin: 20px;">
-

-        <input type="text" class="span2 search-query" ng-trim="false" ng-model="value">
-        <button type="submit" class="btn" ng-click="update({query:value})">查詢</button>
+

+        <input type="text" class="span2 search-query" ng-trim="false" ng-model="value" ng-change="change()">
+        <button type="submit" class="btn" ng-disabled="error" ng-click="update({query:pattern})">查詢</button>

     </form>

'''
@@ -141,23 +151,16 @@
     @alerts.splice index, 1

   $scope.querySpellings = (index, pattern) ->
-    console.log "querySpellings: #{index}, \"#{pattern}\""
+    console.log 'querySpellings:', index, pattern
     return unless @rules[index]?.script

-    p = null
-    if pattern
-      try
-        p = new RegExp pattern
-      catch error
-        console.error "bad query: #{error}"
-
-    unless p
+    unless pattern
       for r in @rules
         r.queryResult = r.script
       console.log 'cleared query result.'
       return

-    q = @rules[index].queryResult = @rules[index].script.query p
+    q = @rules[index].queryResult = @rules[index].script.query pattern

     r = q
     for j in [index - 1..0] by -1


Translate settings.cson into Japanese

Translate settings.cson into Japanese

--- def/ja/settings.cson
+++ def/ja/settings.cson
@@ -98,8 +98,8 @@
       }
       {
         _id: 'core.closeDeletedFileTabs'
-        title: "Close Deleted File Tabs"
-        desc: "Close corresponding editors when a file is deleted outside Atom."
+        title: "削除されたファイルのタブを閉じる"
+        desc: "Atom の外部でファイルが削除された場合、対応するエディタを閉じます。"
       }
       {
         _id: 'core.closeEmptyWindows'
@@ -125,11 +125,11 @@
       }
       {
         _id: 'core.fileSystemWatcher'
-        title: "File System Watcher"
-        desc: "Choose the underlying implementation used to watch for filesystem changes. Emulating changes will miss any events caused by applications other than Atom, but may help prevent crashes or freezes."
+        title: "ファイルシステムウォッチャ"
+        desc: "ファイルシステムの変更を監視するために使用される内部的な実装を選択します。変更をエミュレートすると、Atom 以外のアプリケーションにより発生したイベントは全て失われますが、クラッシュやフリーズの防止に役立ちます。"
         select:
-          native: "Native operating system APIs"
-          atom: "Emulated with Atom events"
+          native: "ネイティブオペレーティングシステム API"
+          atom: "Atom イベントによりエミュレート"
       }
       {
         _id: 'core.followSymlinks'
@@ -149,7 +149,7 @@
       }
       {
         _id: 'core.packagesWithKeymapsDisabled'
-        title: "Packages With Keymaps Disabled"
+        title: "キーマップが無効化されたパッケージ"
       }
       {
         _id: 'core.projectHome'
@@ -158,13 +158,13 @@
       }
       {
         _id: 'core.reopenProjectMenuCount'
-        title: "Reopen Project Menu Count"
-        desc: "How many recent projects to show in the Reopen Project menu."
+        title: "プロジェクト履歴の表示数"
+        desc: "「プロジェクトを再開」に表示するプロジェクト履歴の数を指定します。"
       }
       {
         _id: 'core.restorePreviousWindowsOnStart'
-        title: "Restore Previous Windows On Start"
-        desc: "When checked restores the last state of all Atom windows when started from the icon or atom by itself from the command line; otherwise a blank environment is loaded."
+        title: "起動時に前回のウィンドウを復元する"
+        desc: "'no' の場合、空の環境が読み込まれます。'yes' の場合、アイコンまたは atom コマンドにより起動した時に Atom の全てのウィンドウを前回の状態に復元します。他の方法で起動した場合は空の環境が読み込まれます。'always' の場合、Atom の起動方法に関わらず全てのウィンドウを前回の状態に復元します。"
         select:
           no: "no"
           yes: "yes"
@@ -181,8 +181,8 @@
       }
       {
         _id: 'core.titleBar'
-        title: "Title Bar"
-        desc: "Experimental: A custom title bar adapts to theme colors. Choosing custom-inset adds a bit more padding. The title bar can also be completely hidden.
Note: Switching to a custom or hidden title bar will compromise some functionality.
This setting will require a relaunch of Atom to take effect."
+        title: "タイトルバー"
+        desc: "実験的な機能: custom タイトルバーは、テーマカラーに適応するタイトルバーです。custom-inset を選択した場合、パディングが追加されます。hidden を選択する事で、タイトルバーを完全に隠す事も可能です。
注意: custom または hidden のに切り替えると、いくつかの機能が損なわれる可能性があります。
この設定を有効にするには Atom の再起動が必要です。"
         select:
           native: "native"
           custom: "custom"
@@ -191,18 +191,18 @@
       }
       {
         _id: 'core.useProxySettingsWhenCallingApm'
-        title: "Use Proxy Settings When Calling APM"
-        desc: "Use detected proxy settings when calling the apm command-line tool."
+        title: "APM を呼ぶ時にプロキシ設定を使う"
+        desc: "apm コマンドを呼ぶ時に、検出したプロキシ設定を使います。"
       }
       {
         _id: 'core.versionPinnedPackages'
-        title: "Version Pinned Packages"
-        desc: "List of names of installed packages which are not automatically updated."
+        title: "バージョン固定のパッケージ"
+        desc: "自動アップデートを行わないインストール済みパッケージの一覧です。"
       }
       {
         _id: 'core.useCustomTitleBar'
         title: "カスタムタイトルバーを使う"
-        desc: "Use custom, theme-aware title bar.
Note: This currently does not include a proxy icon.
This setting will require a relaunch of Atom to take effect."
+        desc: "テーマに対応したカスタムタイトルバーを使用します。
注意: プロキシアイコンは含みません。
この設定を有効にするには Atom の再起動が必要です。"
       }
       {
         _id: 'core.warnOnLargeFileLimit'
@@ -237,12 +237,12 @@
       {
         _id: 'editor.fontFamily'
         title: "フォント"
-        desc: "font-family"
+        desc: "フォントファミリ"
       }
       {
         _id: 'editor.fontSize'
         title: "フォントサイズ"
-        desc: "font-size (px)"
+        desc: "フォントサイズ (px)"
       }
       {
         _id: 'editor.invisibles.cr'
@@ -267,12 +267,12 @@
       {
         _id: 'editor.lineHeight'
         title: "行の高さ"
-        desc: "line-height (number)"
+        desc: "行の高さ (数値)"
       }
       {
         _id: 'editor.maxScreenLineLength'
-        title: "Max Screen Line Length"
-        desc: "Defines the maximum width of the editor window before soft wrapping is enforced, in number of characters."
+        title: "画面行の最大長さ"
+        desc: "ソフトラッピングが適用される前のエディタウィンドウの最大幅を文字数で定義します。"
       }
       {
         _id: 'editor.nonWordCharacters'
@@ -296,8 +296,8 @@
       }
       {
         _id: 'editor.showCursorOnSelection'
-        title: "Show Cursor On Selection"
-        desc: "Show cursor while there is a selection."
+        title: "選択時にカーソルを表示する"
+        desc: "選択された項目がある時にカーソルを表示します。"
       }
       {
         _id: 'editor.showIndentGuide'
@@ -358,23 +358,23 @@
       }
       {
         _id: 'system.windows.file-handler'
-        title: "Register as file handler"
-        desc: "Show Atom in the \"Open with\" application list for easy association with file types."
+        title: "ファイルハンドラとして登録する"
+        desc: "ファイルタイプとの簡単な関連付けのために\"このアプリケーションで開く\"に Atom を表示します。"
       }
       {
         _id: 'system.windows.shell-menu-files'
-        title: "Show in file context menus"
-        desc: "Add \"Open with Atom\" to the File Explorer context menu for files."
+        title: "ファイルコンテキストメニューに表示する"
+        desc: "\"Atom で開く\"をファイルエクスプローラのファイル用コンテキストメニューに追加します。"
       }
       {
         _id: 'system.windows.shell-menu-folders'
-        title: "Show in folder context menus"
-        desc: "Add \"Open with Atom\" to the File Explorer context menu for folders."
+        title: "フォルダコンテキストメニューに表示する"
+        desc: "\"Atom で開く\"をファイルエクスプローラのフォルダ用コンテキストメニューに追加します。"
       }
     ]
   }
   system: {
-    notes: "These settings determine how Atom integrates with your operating system."
+    notes: "これらの設定項目では、Atom とオペレーティングシステムの統合方法を決定します。"
   }
   keymaps: {
     notes: {
@@ -391,7 +391,7 @@
     "table-header-selector": "セレクタ"
   }
   packages: {
-    searchBarText: 'Filter packages by name'
+    searchBarText: 'パッケージを名前でフィルタリング'
   }
   themes: {
     notes: {
@@ -404,7 +404,7 @@
     description1: "タブ、ステータスバー、ツリービューとドロップダウンのスタイルを変更します。"
     title2: "シンタックステーマ"
     description2: "テキストエディタの内側のスタイルを変更します。"
-    searchBarText: 'Filter themes by name'
+    searchBarText: 'テーマを名前でフィルタリング'
   }
   updates: {
     "check-updates": "すべてアップデート"


this -> @

this -> @

--- app/assets/js/app.coffee
+++ app/assets/js/app.coffee
@@ -32,9 +32,9 @@

   init: ->
     me = this
-    this.set 'content', []
+    @set 'content', []

-    this.pushObject
+    @pushObject
       name: 'test'

     Ember.$.getJSON config.baseUrl + "monitor/request", (data) ->
@@ -67,6 +67,6 @@

 App.RequestRoute = Ember.Route.extend
   model: (params) ->
-    store = this.get 'store'
+    store = @get 'store'

 #App.RequestController = Ember.Route.extend