Refactored the implementation of code-segmentation, and added lots of clarifying code-comments. This fixes several block-comment, as well comment-folding and -embedding related issues

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -17,15 +17,26 @@

 module.exports = Utils =
-  # Escape regular expression characters in a string
-  #
-  # Code from http://zetafleet.com/ via http://simonwillison.net/2006/Jan/20/escape/
-  regexpEscape: (string) ->
-    string.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
+
+  # Code from <http://zetafleet.com/>
+  # via <http://blog.simonwillison.net/post/57956816139/escape>
+  regexpEscapePattern : /[-[\]{}()*+?.,\\^$|#\s]/g
+  regexpEscapeReplace : '\\$&'
+
+  # Escape regular expression characters in a String, an Array of Strings or
+  # any Object having a proper toString-method
+  regexpEscape: (obj) ->
+    if _.isArray obj
+      _.invoke(obj, 'replace', @regexpEscapePattern, @regexpEscapeReplace)
+    else if _.isString obj
+      obj.replace(@regexpEscapePattern, @regexpEscapeReplace)
+    else
+      @regexpEscape "#{obj}"

   # Detect and return the language that a given file is written in.
   #
-  # The language is also annotated with a name property, matching the laguages key in LANGUAGES.
+  # The language is also annotated with a name property, matching the language's
+  # key in LANGUAGES.
   getLanguage: (filePath, languageDefinitions = './languages') ->
     unless @_languageDetectionCache?
       @_languageDetectionCache = []
@@ -36,7 +47,8 @@
         language.name = name

         for matcher in language.nameMatchers
-          # If the matcher is a string, we assume that it's a file extension.  Stick it in a regex:
+          # If the matcher is a string, we assume that it's a file extension.
+          # Stick it in a regex:
           matcher = ///#{@regexpEscape matcher}$/// if _.isString matcher

           @_languageDetectionCache.push [matcher, language]
@@ -46,14 +58,17 @@
     for pair in @_languageDetectionCache
       return pair[1] if baseName.match pair[0]

-  # Map a list of file paths to relative target paths by stripping prefixes off of them.
+  # Map a list of file paths to relative target paths by stripping prefixes.
   mapFiles: (resolveRoot, files, stripPrefixes) ->
-    # Ensure that we're dealing with absolute paths across the board
+    # Ensure that we're dealing with absolute paths across the board.
     files = files.map (f) -> path.resolve resolveRoot, f
-    # And that the strip prefixes all end with a /, to avoid a target path being absolute.
-    stripPrefixes = stripPrefixes.map (p) -> path.join( "#{path.resolve resolveRoot, p}#{CompatibilityHelpers.pathSep}" )
-
-    # Prefixes are stripped in order of most specific to least (# of directories deep)
+
+    # And that the strip prefixes all end with a /, avoids absolute target path.
+    stripPrefixes = stripPrefixes.map (p) ->
+      path.join "#{path.resolve resolveRoot, p}#{CompatibilityHelpers.pathSep}"
+
+    # Prefixes are stripped in the order of most specific to least
+    # (# of directories deep)
     prefixes = stripPrefixes.sort (a,b) => @pathDepth(b) - @pathDepth(a)

     result = {}
@@ -62,11 +77,11 @@
       file = absPath

       for stripPath in stripPrefixes
-        file = file[stripPath.length..] if file[0...stripPath.length] == stripPath
-
-      # We also strip the extension under the assumption that the consumer of this path map is going
-      # to substitute in their own.  Plus, if they care about the extension, they can get it from
-      # the keys of the map.
+        file = file[stripPath.length..] if file[0...stripPath.length] is stripPath
+
+      # We also strip the extension under the assumption that the consumer of
+      # this path map is going to substitute in their own.  Plus, if they care
+      # about the extension, they can get it from the keys of the map.
       result[absPath] = if not path.extname(file) then file else file[0...-path.extname(file).length]

     result
@@ -80,9 +95,9 @@

       result.push arg if arg.slice(-1) == CompatibilityHelpers.pathSep

-    # For now, we try to avoid ambiguous situations by guessing the FIRST directory given.  The
-    # assumption is that you don't want merged paths, but probably did specify the most important
-    # source directory first.
+    # For now, we try to avoid ambiguous situations by guessing the FIRST
+    # directory given.  The assumption is that you don't want merged paths,
+    # but probably did specify the most important source directory first.
     result = _(result).uniq()[...1]

   # How many directories deep is a given path?
@@ -93,9 +108,9 @@
   splitSource: (data, language, options={}) ->
     lines = data.split /\r?\n/

-    # Always strip shebangs - but don't shift it off the array to avoid the perf hit of walking the
-    # array to update indices.
-    lines[0] = '' if lines[0][0..1] == '#!'
+    # Always strip shebangs - but don't shift it off the array to
+    # avoid the perf hit of walking the array to update indices.
+    lines[0] = '' if lines[0][0..1] is '#!'

     # Special case: If the language is comments-only, we can skip pygments
     return [new @Segment [], lines] if language.commentsOnly
@@ -110,123 +125,237 @@
     whitespaceMatch = if options.requireWhitespaceAfterToken then '\\s' else '\\s?'

     if language.singleLineComment?
-      singleLineMatcher = ///^\s*(#{language.singleLineComment.join('|')})(?:#{whitespaceMatch}(.*))?$///
+      singleLines = @regexpEscape(language.singleLineComment).join '|'
+      aSingleLine = ///^\s*(?:#{singleLines})(?:#{whitespaceMatch}(.*))?$///

     if language.multiLineComment?
       mlc = language.multiLineComment

-      blockStarts = _.invoke _.select(mlc, (v, i) -> i % 3 == 0), 'replace', /([\\\*\{\}])/g, '\\\$1'
-      blockLines  = _.invoke _.select(mlc, (v, i) -> i % 3 == 1), 'replace', /([\\\*\{\}])/g, '\\\$1'
-      blockEnds   = _.invoke _.select(mlc, (v, i) -> i % 3 == 2), 'replace', /([\\\*\{\}])/g, '\\\$1'
-
-
-      blockStartMatcher = ///^\s*(#{blockStarts.join '|'})(?:#{blockLines.join '|'})?(?:#{whitespaceMatch}(.*))?$///
-      blockLineMatcher =  ///^\s*(#{blockLines.join '|'})#{whitespaceMatch}(.*)$///
-      blockEndMatcher =   ///^\s*(?:#{blockLines.join '|'})?(.*)(#{blockEnds.join '|'})$///
-
-      blockSingleLineMatcher = ///^\s*(#{blockStarts.join '|'})#{whitespaceMatch}(.*)#{whitespaceMatch}(#{blockEnds.join '|'})///
-
-      if language.singleLineComment?
-        singleLineMatcher = ///#{singleLineMatcher.source}|#{blockSingleLineMatcher.source}///
-      else
-        singleLineMatcher = blockSingleLineMatcher
+      blockStarts = @regexpEscape(_.select mlc, (v, i) -> i % 3 == 0).join '|'
+      blockLines  = @regexpEscape(_.select mlc, (v, i) -> i % 3 == 1).join '|'
+      blockEnds   = @regexpEscape(_.select mlc, (v, i) -> i % 3 == 2).join '|'
+
+      # No need to match for any particular real content in `aBlockStart`, as
+      # either `aBlockLine`, `aBlockEnd` or the `isBlock` catch-all fallback
+      # handles the real content, in the implementation below.
+      aBlockStart = ///^\s*(?:#{blockStarts}(?:#{blockLines})?(?:#{whitespaceMatch}|$))///
+      aBlockLine  = ///^\s*(?:#{blockLines}#{whitespaceMatch})(.*)$///
+      aBlockEnd   = ///^\s*(?:#{blockLines}#{whitespaceMatch})?(.*)(?:#{blockEnds})$///
+      aEmptyLine  = ///^\s*(?:#{blockLines})$///

     if language.ignorePrefix?
-      stripIgnorePrefix = ///(#{language.singleLineComment.join '|'})#{whitespaceMatch}#{Utils.regexpEscape language.ignorePrefix}///
+      {ignorePrefix} = language

     if language.foldPrefix?
-      stripFoldPrefix = ///(#{language.singleLineComment.join '|'})#{whitespaceMatch}#{Utils.regexpEscape language.foldPrefix}///
-
-    inBlock = false
+      {foldPrefix} = language
+
+    if (ignorePrefix? or foldPrefix?) and (singleLines? or blockStarts?)
+      stripMarks = []
+      stripMarks.push ignorePrefix if ignorePrefix?
+      stripMarks.push foldPrefix if foldPrefix?
+      stripMarks = @regexpEscape(stripMarks).join '|'
+
+      # The dirty stuff happens here …
+      singleStrip = ///
+        (
+          #{singleLines}                    # The comment marker(s) to keep …
+          #{whitespaceMatch}                # … plus whitespace
+        )
+        (?:#{stripMarks})                   # The marker(s) to strip from result
+      /// if singleLines?
+
+      # … and here. 8-)
+      blockStrip = ///
+        (
+          #{blockStarts}                    # The comment marker(s) to keep …
+          (?:#{blockLines})?                # … optionally plus one more mark
+          #{whitespaceMatch}                # … plus whitespace
+        )
+        (?:#{stripMarks})                   # The marker(s) to strip from result
+      /// if blockStarts?
+
+
+    isBlock   = false
+    isFolded  = false
+    isIgnored = false
+
+    blockline = null
+    indent    = 0

     for line in lines

-      if inBlock
-        if (match = line.match blockEndMatcher)?
-          currSegment.comments.push match[1]
-          inBlock = false
-
-        else if (match = line.match blockLineMatcher)?
-          currSegment.comments.push match[2]
-
-        else
-          ###
-          # We are in a multi-line block-comment, hence the whole line is part
-          # of the comment.  This is especially needed for multi-line comment
-          # contents starting immediately at the beginning of a line (without
-          # indention, like those in CoffeeScripts).  The `blockLineMatcher`
-          # from above can not catch those comments due to the `whitespaceMatch`
-          # restrictions from above.  Empty block-comment lines, like the one
-          # after this paragraph have no `whitespaceMatch` restriction …
-          #
-          # @description This comment itself is a multi-line block-comment with
-          #             `@doctags`, indention and (needlessly) prefixed by `'#'`.
-          # @description The very first comment in this file would render it's
-          #              content as code if this `else`-clause is missing.
-          ###
-          if ///^\s*(#{blockLines.join '|'})$///.test line
-            currSegment.comments.push ""
-          else
-            currSegment.comments.push line
-
-      # Match that line to the language's multi line comment syntax, if it exists
-      else if language.multiLineComment? and (match = line.match blockStartMatcher)?
+      # Match that line to the language's block-comment syntax, if it exists
+      if aBlockStart? and not isBlock and aBlockStart.test line
+
+        isBlock = true

         if currSegment.code.length > 0
           segments.push currSegment
-          currSegment = new @Segment
-
-        if line[line.length - 1] == language.foldPrefix
-          currSegment.hide = yes
-
-        currSegment.comments.push match[2]
-        inBlock = true
+          currSegment   = new @Segment
+          isFolded      = false
+
+        # After stripping the block-comments start, preserving any inline stuff,
+        # we continue at the `if isBlock` statement below.  We don't touch the
+        # `line` itself, as we might need it.
+        blockline = line.replace aBlockStart, ''
+
+        if foldPrefix? and blockline.indexOf(foldPrefix) is 0
+          ### ^ collapsing block-comments:
+          # In block-comments only `aBlockStart` may initiate the collapsing.
+          # This comment utilizes this syntax, by starting the comment with `^`.
+          ###
+
+          # Let's strip the “^” character from our documentation,
+          # using the untouched original `line`
+          blockline = line.replace blockStrip, '$1'
+          isFolded  = true
+
+        else if ignorePrefix? and blockline.indexOf(ignorePrefix) is 0
+          ### } embedded block-comments:
+          # In block-comments only `aBlockStart` may initiate the embedding.
+          # This comment utilizes this syntax, by starting the comment with `}`.
+          ###
+
+          # Let's strip the “}” character from our documentation,
+          # using the untouched original `line`
+          blockline = line.replace blockStrip, '$1'
+          isIgnored = true
+
+      # This flag is triggered above.
+      if isBlock
+
+        # Catch all lines, unless there is a `blockline` from above.
+        # These lines have all leading spaces stripped - this should be enhanced
+        # to stripped only as many whitespaces as the initial `aBlockStart` has
+        # as indent.
+        # blockline = line.replace /^\s+/, '' unless blockline?
+        blockline = line unless blockline?
+
+        # Match a block-comment's end, even when `isFolded or isIgnored` flags
+        # are true …
+        if (match = blockline.match aBlockEnd)?
+          ### … to ensure that we finally leave the block-comment, especially the ones on a single-line like this one. ###
+          isBlock = false
+
+          # Strip any `blockline`-prefixes or -suffixes when
+          # `isFolded or isIgnored` flags are false.
+          blockline = match[1] unless isFolded or isIgnored
+
+        # Match a block-comment's line, when `isFolded or isIgnored` are false.
+        else if not (isFolded or isIgnored) and (match = blockline.match aBlockLine)?
+          # Strip any `blockline`-prefixes or -suffixes and continue below.
+          blockline = match[1]
+
+        if isIgnored
+          currSegment.code.push blockline
+
+          # Make sure that the next cycle starts fresh,
+          # if we are going to leave the block.
+          isIgnored = false if not isBlock
+
+        else
+          # The previous cycle contained code, so lets start a new segment, but
+          # only if the `isFolded` flag is false or if this is the first line to
+          # fold (= foldMarker is empty).
+          if currSegment.code.length > 0 and (not isFolded or currSegment.foldMarker is '')
+            segments.push currSegment
+            currSegment = new @Segment
+
+          if isFolded
+
+            # If the foldMarker is empty assign `blockline` to `foldMarker` …
+            if currSegment.foldMarker is ''
+              currSegment.foldMarker = blockline
+
+            # … else collect the `blockline` as code.
+            else
+              currSegment.code.push blockline
+
+          else
+
+            ###
+            # Implements the default handler for the `blockline` from above as
+            # well as a catch-all fallback handling anything in a block-comment
+            # that doesn't match anything above.
+            #
+            # So are in a multi-line block-comment, hence the whole line is part
+            # of the comment.  This is especially needed for multi-line comment
+            # contents starting immediately at the beginning of a line (without
+            # indention, like those in CoffeeScripts).  The `aBlockLine` from
+            # above can not catch those comments due to the `whitespaceMatch`
+            # restrictions from above.  Empty block-comment lines, like the one
+            # after this paragraph have no `whitespaceMatch` restriction …
+            #
+            # @description This comment is a block-comment with `@doctags`,
+            #              indention and (needlessly) prefixed by `'#'`.
+            # @description The very first comment in this file would render it's
+            #              content as code if this `else`-clause is missing.
+            ###
+            if aEmptyLine.test line
+              currSegment.comments.push ""
+
+              ###
+              Collect all but start- and end-block-comment lines, but include
+              single-line block-comments that match `aBlockStart` and
+              `aBlockEnd`, having the `isBlock` flag set to false at this point.
+              ###
+            else if (not isBlock and aBlockStart.test line) or not aBlockEnd.test line
+              currSegment.comments.push blockline
+
+        # Make sure the next cycle starts fresh.
+        blockline = null

       # Match that line to the language's single line comment syntax.
       #
       # However, we treat all comments beginning with } as inline code commentary
       # and comments starting with ^ cause that comment and the following code
       # block to start folded.
-      else if (match = line.match singleLineMatcher)?
-
-        value = (match[2] || match[4])
-
-        if value? and value isnt ''
+      else if (match = line.match aSingleLine)?
+
+        singleline = match[1]
+
+        if singleline? and singleline isnt ''

           # } For example, this comment should be treated as part of our code.
           # } Achieved by prefixing the comment's content with “}”
-          if stripIgnorePrefix? and value.indexOf(language.ignorePrefix) is 0
-
-            # **Unfold this code ->**
-            # ^ The previous cycle contained code, so lets start a new segment,
-            # } but only if the previous code-line isn't a comment forced to be
-            # } part of the code, as implemented here.  This allows embedding a
-            # } series of code-comments, even folded like this one.
-            if currSegment.code.length > 0 and \
-               not (currSegment.code[currSegment.code.length - 1].match singleLineMatcher)?
-              segments.push currSegment
-              currSegment = new @Segment
+          if ignorePrefix? and singleline.indexOf(ignorePrefix) is 0
+
+            # } Hint: never start a new segment here, these comments are code !
+            # } If we would do so the segments look visually not so appealing in
+            # } the narrowed single-column-view.

             # Let's strip the “}” character from our documentation
-            currSegment.code.push line.replace stripIgnorePrefix, match[1]
+            currSegment.code.push line.replace singleStrip, '$1'

           else

             # The previous cycle contained code, so lets start a new segment
+            # and stop any folding.
             if currSegment.code.length > 0
               segments.push currSegment
-              currSegment = new @Segment
-
+              currSegment   = new @Segment
+              isFolded      = false
+
+            # It's always a good idea to put a comment before folded content
+            # like this one here, because folded comments always have their
+            # own code-segment in their current implementation (see above).
+            # Without a leading comment, the folded code's segment would just
+            # follow the above's code segment, which looks visually not so
+            # appealing in the narrowed single-column-view.
+            #
+            # TODO: _Alternative (a)_: Improve folded comments to not start a new segment, like embedded comments from above. _(preferred solution)_
+            # TODO: _Alternative (b)_: Improve folded comments visual appearance in single-column view. _(easy solution)_
+            #
             # ^ … if we start this comment with “^” instead of “}” it and all
             # } code up to the next segment's first comment starts folded
-            if stripFoldPrefix? and value.indexOf(language.foldPrefix) is 0
-
-              # } … so folding stopped above, as this is a new segment !
+            if foldPrefix? and singleline.indexOf(foldPrefix) is 0
+
+              # } … so folding stops below, as this is a new segment !
               # Let's strip the “^” character from our documentation
-              currSegment.foldMarker = line.replace stripFoldPrefix, match[1]
+              currSegment.foldMarker = line.replace singleStrip, '$1'

             else
-              currSegment.comments.push value
+              currSegment.comments.push singleline

       else
         currSegment.code.push line


Make {FILE_ACTIVE_NAME_BASE} work

Make {FILE_ACTIVE_NAME_BASE} work

Use path.basename and path.extname instead of path.join

--- lib/runner.coffee
+++ lib/runner.coffee
@@ -92,7 +92,7 @@
       arg = arg.replace(/{FILE_ACTIVE_PATH}/g, path.join(codeContext.filepath, '..'))
     if codeContext.filename?
       arg = arg.replace(/{FILE_ACTIVE_NAME}/g, codeContext.filename)
-      arg = arg.replace(/{FILE_ACTIVE_NAME_BASE}/g, path.join(codeContext.filename, '..'))
+      arg = arg.replace(/{FILE_ACTIVE_NAME_BASE}/g, path.basename(codeContext.filename, path.extname(codeContext.filename)))
     if project_path?
       arg = arg.replace(/{PROJECT_PATH}/g, project_path)


Changed error API

Changed error API

--- lib/sentry.coffee
+++ lib/sentry.coffee
@@ -30,19 +30,19 @@
       hostname: os.hostname()
       enable_env: ['production']

-  error: (err, culprit, logger, extra) =>
+  error: (err, logger, culprit, extra={}) =>
     unless err instanceof Error
       console.error 'error must be an instance of Error', err
       err = new Error "CONVERT_TO_ERROR: #{JSON.stringify(err, null, 2)}"
     data =
-      culprit: culprit # big text that appears at the top
       message: err.message # smaller text that appears right under culprit (and shows up in HipChat)
       logger: logger
       server_name: @hostname
       platform: 'node'
       level: 'error'
-      extra: _(extra or {}).extend
+      extra: _.extend extra,
         stacktrace: err.stack
+    _.extend data, culprit: culprit if not _.isNull culprit

     @_send data


support /overview on fairs

support /overview on fairs

--- apps/fair/components/browse/router.coffee
+++ apps/fair/components/browse/router.coffee
@@ -7,6 +7,7 @@

   routes:
     ':id'                                  : 'booths'
+    ':id/overview'                         : 'booths'
     ':id/browse/artists'                   : 'artists'
     ':id/browse/artist/:artist_id'         : 'artist'
     ':id/browse/booths'                    : 'booths'


allow deep extension with mixed in values

--- source/object.coffee
+++ source/object.coffee
@@ -128,6 +128,6 @@
     # Copy over the mixin to the prototype and merge objects
     for key, value of mixin
       if isPlainObject(value) and isPlainObject(@prototype[key])
-        _.extend @prototype[key], value
+        _.deepExtend @prototype[key], value
       else
         @prototype[key] = value


fix basic_shared.coffee

fix basic_shared.coffee

--- lib/generators/transponder/install/templates/basic_shared.coffee
+++ lib/generators/transponder/install/templates/basic_shared.coffee
@@ -2,5 +2,5 @@
 #= require_tree ./services
 #= require_tree ./helpers
 #= require_tree ./presenters
-#= require <%= file_name %>/initializers/boot
-#= require <%= file_name %>/initializers/manifest +#= require initializers/boot
+#= require initializers/manifest


Use Menu.buildFromTemplate instead of raw menu APIs.

--- spec/api/menu.coffee
+++ spec/api/menu.coffee
@@ -26,12 +26,11 @@

   describe 'MenuItem.click', ->
     it 'should be called with the item object passed', (done) ->
-      menu = new Menu
-      menuItem = new MenuItem
+      menu = Menu.buildFromTemplate [
         label: 'text'
         click: (item) ->
           assert.equal item.constructor.name, 'MenuItem'
           assert.equal item.label, 'text'
           done()
-      menu.append menuItem
-      menu.delegate.executeCommand menuItem.commandId
+      ]
+      menu.delegate.executeCommand menu.items[0].commandId


solidrun-imx6.coffee: Update getting started links

Signed-off-by: Zachary Walchuk <9de8c4480303b5335cd2a33eefe814615ba3612a@resin.io>

--- solidrun-imx6.coffee
+++ solidrun-imx6.coffee
@@ -11,9 +11,9 @@

 	instructions: commonImg.instructions
 	gettingStartedLink:
-		windows: 'http://docs.resin.io/#/pages/installing/gettingStarted-Hummingboard.md#windows'
-		osx: 'http://docs.resin.io/#/pages/installing/gettingStarted-Hummingboard.md#on-mac-and-linux'
-		linux: 'http://docs.resin.io/#/pages/installing/gettingStarted-Hummingboard.md#on-mac-and-linux'
+		windows: 'http://docs.resin.io/hummingboard/nodejs/getting-started/#adding-your-first-device'
+		osx: 'http://docs.resin.io/hummingboard/nodejs/getting-started/#adding-your-first-device'
+		linux: 'http://docs.resin.io/hummingboard/nodejs/getting-started/#adding-your-first-device'
 	supportsBlink: false

 	options: [ networkOptions.group ]


Re-enable ass-spec

Re-enable ass-spec

--- spec/ass-spec.coffee
+++ spec/ass-spec.coffee
@@ -1,106 +1,104 @@
-if false
+_ = require 'lodash'
+ASS = require 'lib-ass'
+{Directory} = require 'atom'
+fs = require 'fs'
+path = require 'path'

-  _ = require 'lodash'
-  ASS = require 'lib-ass'
-  {Directory} = require 'atom'
-  fs = require 'fs'
-  path = require 'path'
+# NOTE
+# Manually specify {fixtures} if you only want to run specific tests.
+# A {fixture} is a relative path + filename (without extension).
+# fixtures = [
+#   "flavors/github/task-lists"
+# ]

-  # NOTE
-  # Manually specify {fixtures} if you only want to run specific tests.
-  # A {fixture} is a relative path + filename (without extension).
-  # fixtures = [
-  #   "flavors/github/task-lists"
-  # ]
+# Automatically generate the {fixtures} array from the file system.
+# Include all .ass files found inside /spec/fixtures, by their relative path
+# but excluding the file extension. This {fixture} is used to generate
+# identifiers for tasks.
+unless fixtures
+  getFixturesFrom = (directoryPath = '') ->
+    results = []
+    directory = new Directory(path.join(__dirname, './fixtures' + directoryPath))
+    entries = directory.getEntriesSync()
+    for entry in entries
+      if entry.isFile()
+        filename = entry.getBaseName()
+        if filename.substr(-4) is '.ass'
+          fixture = directoryPath + "/" + filename.substr(0, filename.length - 4)
+          results.push fixture
+      else
+        results = results.concat(getFixturesFrom(directoryPath + "/" + entry.getBaseName()))
+    return results

-  # Automatically generate the {fixtures} array from the file system.
-  # Include all .ass files found inside /spec/fixtures, by their relative path
-  # but excluding the file extension. This {fixture} is used to generate
-  # identifiers for tasks.
-  unless fixtures
-    getFixturesFrom = (directoryPath = '') ->
-      results = []
-      directory = new Directory(path.join(__dirname, './fixtures' + directoryPath))
-      entries = directory.getEntriesSync()
-      for entry in entries
-        if entry.isFile()
-          filename = entry.getBaseName()
-          if filename.substr(-4) is '.ass'
-            fixture = directoryPath + "/" + filename.substr(0, filename.length - 4)
-            results.push fixture
+  fixtures = getFixturesFrom()
+
+describe "Markdown grammar", ->
+  grammar = null
+
+  beforeEach ->
+    waitsForPromise ->
+      atom.packages.activatePackage('language-markdown')
+
+    runs ->
+      grammar = atom.grammars.grammarForScopeName('text.md')
+
+  # Test the grammar
+  it "parses the grammar", ->
+    expect(grammar).toBeDefined()
+    expect(grammar.scopeName).toBe "text.md"
+
+  for fixture in fixtures
+
+    # Try to load the fixture
+    try
+      absolutePath = path.join(__dirname, "fixtures/#{fixture}.ass")
+      fileContents = fs.readFileSync(absolutePath, 'utf8')
+      ass = new ASS(fileContents)
+      tests = ass.getTests()
+    catch error
+      ass = null
+      tests = 0
+
+    # Test the basics of the fixture
+    it "should load #{absolutePath}", ->
+      expect(ass).not.toEqual(null)
+
+    it "should define at least one test", ->
+      expect(tests.length > 0).toEqual(true)
+
+    # Everything seems to be okay, let's run the tests
+    describe fixture, ->
+      grammar = null
+
+      beforeEach ->
+        waitsForPromise ->
+          atom.packages.activatePackage('language-markdown')
+
+        runs ->
+          grammar = atom.grammars.grammarForScopeName('text.md')
+
+      # Cycle through the tests we've created in ASS
+      # and we need to do it in a closure apparently
+      _.forEach tests, (test) ->
+
+        unless test.isValid
+          xit "should pass test: #{fixture}/#{test.id}", ->
+            return
         else
-          results = results.concat(getFixturesFrom(directoryPath + "/" + entry.getBaseName()))
-      return results

-    fixtures = getFixturesFrom()
-
-  describe "Markdown grammar", ->
-    grammar = null
-
-    beforeEach ->
-      waitsForPromise ->
-        atom.packages.activatePackage('language-markdown')
-
-      runs ->
-        grammar = atom.grammars.grammarForScopeName('text.md')
-
-    # Test the grammar
-    it "parses the grammar", ->
-      expect(grammar).toBeDefined()
-      expect(grammar.scopeName).toBe "text.md"
-
-    for fixture in fixtures
-
-      # Try to load the fixture
-      try
-        absolutePath = path.join(__dirname, "fixtures/#{fixture}.ass")
-        fileContents = fs.readFileSync(absolutePath, 'utf8')
-        ass = new ASS(fileContents)
-        tests = ass.getTests()
-      catch error
-        ass = null
-        tests = 0
-
-      # Test the basics of the fixture
-      it "should load #{absolutePath}", ->
-        expect(ass).not.toEqual(null)
-
-      it "should define at least one test", ->
-        expect(tests.length > 0).toEqual(true)
-
-      # Everything seems to be okay, let's run the tests
-      describe fixture, ->
-        grammar = null
-
-        beforeEach ->
-          waitsForPromise ->
-            atom.packages.activatePackage('language-markdown')
-
-          runs ->
-            grammar = atom.grammars.grammarForScopeName('text.md')
-
-        # Cycle through the tests we've created in ASS
-        # and we need to do it in a closure apparently
-        _.forEach tests, (test) ->
-
-          unless test.isValid
-            xit "should pass test: #{fixture}/#{test.id}", ->
-              return
-          else
-
-            it "should pass test: #{fixture}/#{test.id}", ->
-              i = 0
-              tokens = grammar.tokenizeLines(test.input)
-              for line, a in tokens
-                for token, b in line
-                  expectation = test.tokens[i]
-                  # NOTE
-                  # A token.value without a length has been created, and is
-                  # ignored. I believe this happens when an optional capture in
-                  # the grammar is empty. As far as I can tell, these can be
-                  # safely ignored, because you would omit these (unexpected)
-                  # tokens when writing manual tests.
-                  if tokens[a][b].value.length
-                    expect(tokens[a][b]).toEqual value: expectation.value, scopes: expectation.scopes
-                  i++
-              return
+          it "should pass test: #{fixture}/#{test.id}", ->
+            i = 0
+            tokens = grammar.tokenizeLines(test.input)
+            for line, a in tokens
+              for token, b in line
+                expectation = test.tokens[i]
+                # NOTE
+                # A token.value without a length has been created, and is
+                # ignored. I believe this happens when an optional capture in
+                # the grammar is empty. As far as I can tell, these can be
+                # safely ignored, because you would omit these (unexpected)
+                # tokens when writing manual tests.
+                if tokens[a][b].value.length
+                  expect(tokens[a][b]).toEqual value: expectation.value, scopes: expectation.scopes
+                i++
+            return


:lipstick:

:lipstick:

--- spec/git-spec.coffee
+++ spec/git-spec.coffee
@@ -199,7 +199,6 @@
       newFilePath = path.join(repo.getWorkingDirectory(), 'b.txt')
       fs.writeFileSync(newFilePath, '', 'utf8')

-
     it 'returns the status of all modified paths', ->
       statuses = repo.getStatuses()
       expect(statuses['a.txt']).toBe 1 << 9


Streamline issue

Streamline issue

--- lib/content._coffee
+++ lib/content._coffee
@@ -57,7 +57,7 @@
     download.on 'error', () ->
         # What do we do here? Something with Streamline?

-    download.on 'end', () ->
+    download.on 'end', (_) ->
         path = Path.join DIR_BY_ID_PATH, "#{id}.json"
         readFile path, _


add another way to extend Space.Object

add another way to extend Space.Object

--- source/object.coffee
+++ source/object.coffee
@@ -7,31 +7,36 @@
   # Extends this class and return a child class with inherited prototype
   # and static properties.
   #
-  # There are five ways you can call this method:
+  # There are various ways you can call this method:
   #
   # 1. Space.Object.extend()
   # --------------------------------------------
   # Creates an anonymous child class without extra prototype properties.
   # Basically the same as `class extend Space.Object` in coffeescript
   #
-  # 2. Space.Object.extend({ prop: 'first', … })
+  # 2. Space.Object.extend(className)
+  # --------------------------------------------
+  # Creates a named child class without extra prototype properties.
+  # Basically the same as `class ClassName extend Space.Object` in coffeescript
+  #
+  # 3. Space.Object.extend({ prop: 'first', … })
   # --------------------------------------------
   # Creates an anonymous child class with extra prototype properties.
   # Same as:
   # class extend Space.Object
   #   prop: 'first'
   #
-  # 3. Space.Object.extend(namespace, className)
+  # 4. Space.Object.extend(namespace, className)
   # --------------------------------------------
   # Creates a named class which inherits from Space.Object and assigns
   # it to the given namespace object.
   #
-  # 4. Space.Object.extend(className, prototype)
+  # 5. Space.Object.extend(className, prototype)
   # --------------------------------------------
   # Creates a named class which inherits from Space.Object and extra prototype
   # properties which are assigned to the new class
   #
-  # 5. Space.Object.extend(namespace, className, prototype)
+  # 6. Space.Object.extend(namespace, className, prototype)
   # --------------------------------------------
   # Creates a named class which inherits from Space.Object, has extra prototype
   # properties and is assigned to the given namespace.
@@ -43,7 +48,9 @@
     extension = {}

     # Only one param: (extension) ->
-    if args.length is 1 then extension = args[0]
+    if args.length is 1
+      if _.isObject(args[0]) then extension = args[0]
+      if _.isString(args[0]) then className = args[0]

     # Two params must be: (namespace, className) OR (className, extension) ->
     if args.length is 2
@@ -69,6 +76,8 @@
     # Assign the optional custom constructor for this class
     Parent = this
     Constructor = extension.Constructor ? -> Parent.apply(this, arguments)
+
+    className = className.substr className.lastIndexOf('.') + 1

     # Create a named constructor for this class so that debugging
     # consoles are displaying the class name nicely.


Add comment about +1

Add comment about +1

--- lib/document.coffee
+++ lib/document.coffee
@@ -137,7 +137,7 @@
                 ref.object @compress, (object) ->
                     ref.offset = offset
                     out.push object
-                    offset += object.length + 1
+                    offset += object.length + 1 # plus one for newline
                     proceed()
             else
                 @xref_offset = offset


Added LVars

Added LVars

--- lib/friebyrd.coffee
+++ lib/friebyrd.coffee
@@ -24,7 +24,15 @@
   # F.test1();
   #=> [100, 101, 101, 110, 110]

-  # exports and sundries
+  class LVar
+    constructor: (name) ->
+      @name = name
+
+  F.lvar = (name) -> new LVar(name)
+
+  F.isLVar = (v) -> (v instanceof LVar)
+
+    # exports and sundries

   if module?
     module.exports = F


Remove dependency from spec

Remove dependency from spec

--- spec/fonts-cjk-spec.coffee
+++ spec/fonts-cjk-spec.coffee
@@ -1,5 +1,3 @@
-fontsCJK = require('../lib/fonts-cjk')
-
 describe 'Fonts CJK', ->
   [textEditorElement, workspaceElement] = []

@@ -14,7 +12,6 @@
     waitsForPromise ->
       atom.workspace.open('file.txt')
     runs ->
-      fontsCJK.activate()
       textEditorElement = atom.workspace.getActiveTextEditor().getElement()
       workspaceElement = atom.views.getView(atom.workspace)
       jasmine.attachToDOM(workspaceElement)


Add jasmine stylesheet

Add jasmine stylesheet

--- spec/spec-bootstrap.coffee
+++ spec/spec-bootstrap.coffee
@@ -3,6 +3,12 @@
 # Start the crash reporter before anything else.
 require('crash-reporter').start(productName: @pkgJson.name, companyName: 'atom-shell-starter')

+
+link = document.createElement 'link'
+link.rel = 'stylesheet'
+link.href = '../vendor/jasmine/lib/jasmine-2.1.3/jasmine.css'
+document.head.appendChild link
+
 window.jasmineRequire = require '../vendor/jasmine/lib/jasmine-2.1.3/jasmine'
 require '../vendor/jasmine/lib/jasmine-2.1.3/jasmine-html'
 require '../vendor/jasmine/lib/jasmine-2.1.3/boot'


removing comments

removing comments

--- lib/getWeeks.coffee
+++ lib/getWeeks.coffee
@@ -15,7 +15,6 @@
         while date.toString() isnt endDate.toString()
             count++ if date.getDay() is 0
             date = nextDate date
-            # console.log(date)
             if date.toString() is 'Invalid Date'
                 throw  Error 'Invalid Date'


Изменил механизм переключения тумбнейлов

--- src/chocolate.coffee
+++ src/chocolate.coffee
@@ -4,12 +4,6 @@

 isHistory = not not (window.history and history.pushState)
 isStorage = 'localStorage' of window and window['localStorage']?
-# isFullscreen = document.webkitRequestFullScreen || document.mozRequestFullScreen
-# test = document.getElementById 'choco-overlay'
-# if test.webkitRequestFullScreen
-#   test.webkitRequestFullScreen()
-# else
-#   test.mozRequestFullScreen()

 class Chocolate
   images: {}
@@ -308,38 +302,36 @@
    Обновление тумбнейлов
   ###
   updateThumbnails: ->
-    @thumbnails.find('.selected').removeClass 'selected'
-
-    thumbnail = @thumbnails.find('[data-cid=' + @current + ']').addClass 'selected'
+    before = @thumbnails.find('.selected').removeClass('selected').attr 'data-cid'
+    after  = @thumbnails.find('[data-cid=' + @current + ']').addClass 'selected'

     if not @dimensions.thumbnail
-      @dimensions.thumbnail = toInt thumbnail.outerWidth true
+      @dimensions.thumbnail = toInt after.outerWidth true

     width = @dimensions.thumbnail
-    left  = thumbnail.get(0).offsetLeft
+    left  = after.get(0).offsetLeft

     range = (left, width) -> left: left, right: left + width

     container = range @thumbnails.scrollLeft(), @thumbnails.width()
     element   = range left, width

-    if container.left > element.left or container.right < element.right
-      if container.right < element.right
+    if before
+      if @current > before
         offset = container.left + width
-      else if container.left > element.left
+      else
         offset = container.left - width
-      else
-        offset = null
-
-      if offset isnt null
-        future = range offset, @thumbnails.width()
-
-        if future.right < element.left
-          offset = width + left
-        else if future.left > element.left
-          offset = 0
-
-        @thumbnails.scrollLeft offset
+    else
+      offset = element.left - (@thumbnails.width() / 2) + (width / 2)
+
+    future = range offset, @thumbnails.width()
+
+    if future.right < element.left
+      offset = width + left
+    else if future.left > element.left
+      offset = 0
+
+    @thumbnails.scrollLeft offset
     @


TODO: coffeescript function must include coffeescript_helpers in output.

--- src/coffeekup.coffee
+++ src/coffeekup.coffee
@@ -157,6 +157,7 @@
     delete arguments[0]
     __ck.tag(name, arguments)

+  # TODO: The CoffeeScript helpers are needed here.
   coffeescript = (code) ->
     script ";(#{code})();"


moving initialization code out of view into modules

--- src/core/view.coffee
+++ src/core/view.coffee
@@ -40,19 +40,13 @@
     if @autoBindEventHandlers is true or @bindAllEvents is true
       bindAllEventHandlers.call(@)

-    setupBodyTemplate.call(@)
-
     @cid = _.uniqueId(@name) if @name?

     @$el.attr("data-luca-id", @name || @cid)

-    Luca.cache( @cid, @ )
-
-    # FIXME:
-    # Does this prevent inheritance of hooks all the way up the chain?
+    Luca.cacheInstance( @cid, @ )
+
     @setupHooks _( Luca.View::hooks.concat( @hooks ) ).uniq()
-
-    setupTemplate.call(@) if @template and not @isField

     if @mixins?.length > 0
       for module in @mixins


spec for issue: https://github.com/the-grid/Flowerflip/issues/35

--- spec/Thenable.coffee
+++ spec/Thenable.coffee
@@ -1229,6 +1229,58 @@
           chai.expect(res).to.eql [15]
           done()

+    describe 'contest via subbranching w/ abortions', ->
+      it 'should resolve', (done) ->
+
+        failedComponent = (n,d) ->
+          n.tree 'failedComponent'
+          .deliver()
+          .then (n) ->
+            n.abort('failedComponent aborted')
+
+        component = (n,d) ->
+          n.tree 'component'
+          .deliver d
+          .maybe failedComponent
+          .else (c,d) ->
+            d
+          .then (c, d) ->
+            d
+
+        multiply = (multiplier, c, data) ->
+          c.tree 'a'
+          .deliver data
+
+          .then "#{multiplier}", (c, d) ->
+            c.branch 'doubled', (b, data) ->
+              data * 6
+            c.branch 'tripled', (b, data) ->
+              data * 3
+
+          .maybe failedComponent
+          .else (c,d) ->
+            d
+
+          .then 'variation', (c, d) ->
+            c.branch 'top', (b, data) ->
+              data * 3
+            c.branch 'bottom', (b, data) ->
+              data * 3
+
+           .all [component]
+           .then (n,results) ->
+             results[0]
+        Root()
+        .deliver 5
+        .contest "contest-multiply", [
+          multiply.bind @, 2
+        ], (c, results) ->
+          results[0]
+        .finally 'enfin-fini',   (c, res) ->
+          chai.expect(res).to.eql [45]
+          done()
+
+

    #d88888ba                                   88
@@ -1537,4 +1589,3 @@

       it 'should work', (done) ->
         test done
-


[frontend] fix event URL on profile

[frontend] fix event URL on profile

--- src/coffee/content/profile.coffee
+++ src/coffee/content/profile.coffee
@@ -183,7 +183,7 @@
             unless Array.isArray player.extra.event
                 player.extra.event = [ player.extra.event ]

-            $profile.append helper.createLinkedSubtitle module.i18n.getMessage('events'), player.extra.event, 'https://plus.google.com/event/'
+            $profile.append helper.createLinkedSubtitle module.i18n.getMessage('events'), player.extra.event, 'https://plus.google.com/events/'

         if Object.has(player, 'err') and not (Array.isArray(player.err) and player.err.length is 0)
             unless Array.isArray player.err


refactor TaggedSource

refactor TaggedSource

--- src/compiler/tagged-source.coffee
+++ src/compiler/tagged-source.coffee
@@ -25,12 +25,12 @@
     @tags = {}
     @original_lines = @source.split(/\r?\n/)
     # strip tags
-    @lines = @_extractTags()
+    [@lines,@tags] = @extractTags(@original_lines)

     # index of selected lines. Initially all.
     @selectedLines = [0...@lines.length]

-  # Combine the selected lines.
+  # Combines the selected lines.
   # @return {String}
   getOutput: ->
     numAsc = ((a,b) -> a - b)
@@ -50,20 +50,30 @@
   selectNone: ->
     @selectedLines = []

-  # Extracts tags from lines, and strip them from the source.
-  _extractTags: ->
-    for line, lineno in @original_lines
+  # Extracts tags from lines, and remove source tags from the source lines.
+  # Return the extracted tags and cleaned up lines.
+  #
+  # @param {[Lines]} Lines of source code to extract tags from.
+  # @return {[Lines,Tags]}
+  extractTags: (sourceLines) ->
+    tags = {}
+    lines = for line, lineno in sourceLines
       if match = @tagre.exec(line)
-        # store the tagged line
+        # This line is tagged
         tagData = match[1]
-        tags = tagData.split(",")
-        for tag in tags
-          @tags[tag] = lineno
+        newTags = tagData.split(",")

-        # strip out the tag
+        # Add new tags
+        for tag in newTags
+          tags[tag] = lineno
+
+        # Remove the tag
         line.replace(@tagre,"")
       else
+        # Untagged line. Return as is.
         line
+
+    return [lines, tags]

   # Returns line number of a tag. Raises if tag doesn't exist.
   #


bug fix

bug fix

--- app/assets/javascripts/swell_media/medium-editor-input/medium-editor-input.js.coffee
+++ app/assets/javascripts/swell_media/medium-editor-input/medium-editor-input.js.coffee
@@ -14,8 +14,8 @@
 		$textarea.addClass('medium-editor-textarea')
 		$textarea.after('

')
 		$editable = $($textarea.next()[0])
-		content = $($textarea.val())
-		$editable.html( if content.is('.medium-editor-content') then content.html() else $textarea.val() )
+		content = $('

').html($textarea.val())
+		$editable.html( if content.children().first().is('.medium-editor-content') then content.children().first().html() else $textarea.val() )
 		args = $editable.data("medium-editor-input") or { cleanPastedHTML: true, buttonLabels: 'fontawesome', buttons: ['bold', 'italic', 'underline', 'anchor', 'header1', 'header2', 'quote'] }

 		args.extensions = {


Minor change in sqf specs

Minor change in sqf specs

--- spec/sqf-spec.coffee
+++ spec/sqf-spec.coffee
@@ -9,7 +9,7 @@
       grammar = atom.grammars.grammarForScopeName("source.sqf")

   it "parses the grammar", ->
-    expect(grammar).toBeDefined()
+    expect(grammar).toBeTruthy()
     expect(grammar.scopeName).toBe "source.sqf"

   it "tokenizes multi-line strings", ->


Replace leading whitespace in commit message.

This allows for proper indentation to display ASCII art.

Fixes travis-ci/travis-ci#2003

--- assets/scripts/app/helpers/helpers.coffee
+++ assets/scripts/app/helpers/helpers.coffee
@@ -41,6 +41,7 @@
     if !!options.repo
       message = @githubify(message, options.repo.get('owner'), options.repo.get('name'))
     message.replace /\n/g, '<br/>'
+    message.replace /^ +/, ' '

   pathFrom: (url) ->
     (url || '').split('/').pop()


#221 still complex but much better

#221 still complex but much better

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -50,6 +50,7 @@
       "stayOnTransformString"
     else
       "stayOn#{@getName()}"
+
     if @isMode('visual', 'linewise')
       settings.get(param)
     else
@@ -70,19 +71,15 @@
       persistent: false

   restorePoint: (selection) ->
-    # [FIXME] WIP: set to 'head' on needStay
-    if @wasNeedStay # [FIXME]
-      if @isMode('visual', 'blockwise')
-        swrap(selection).setBufferPositionFromProperty('start')
-      else
-        swrap(selection).setBufferPositionFromProperty('head')
+    if @wasNeedStay
+      swrap(selection).setBufferPositionFromProperty('head')
     else
       swrap(selection).setBufferPositionFromProperty('start')

   observeSelectAction: ->
     unless @instanceof('Select')
-      if @wasNeedStay = @needStay() # [FIXME] WIP
-        unless @isMode('visual', ['characterwise', 'linewise'])
+      if @wasNeedStay = @needStay() # [FIXME] dirty cache
+        unless @isMode('visual')
           @onWillSelectTarget => swrap.preserveCharacterwise(@editor)
       else
         @onDidSelectTarget => swrap.preserveCharacterwise(@editor)


Add tests for missing checksum data

Add tests for missing checksum data

--- test/document-spec.coffee
+++ test/document-spec.coffee
@@ -81,13 +81,26 @@
         done()

   it 'should return 403 when provided with current valid Etag', (done) ->
-    req.get('/turer/' + trip._id + '?api_key=dnt')
-      .expect(200)
-      .end (err, res) ->
+    req.get('/turer/' + trip._id + '?api_key=dnt').expect(200).end (err, res) ->
+      assert.ifError(err)
+      req.get('/turer/' + trip._id + '?api_key=dnt')
+        .set('if-none-match', res.header.etag)
+        .expect(304, done)
+
+  it 'should return documents without checksum', (done) ->
+    doc = data.get 'turer', false, 37
+    req.get("/turer/#{doc._id}?api_key=dnt").expect(200).end (err, res) ->
+      assert.ifError(err)
+      assert.deepEqual res.body, doc
+      done()
+
+  it 'should ignore etags for documents without checksum', (done) ->
+    doc = data.get 'turer', false, 37
+    req.get("/turer/#{doc._id}?api_key=dnt").set('if-none-match', 'foobar')
+      .expect(200).end (err, res) ->
         assert.ifError(err)
-        req.get('/turer/' + trip._id + '?api_key=dnt')
-          .set('if-none-match', res.header.etag)
-          .expect(304, done)
+        assert.deepEqual res.body, doc
+        done()

   it 'should return newly created document (with id)', (done) ->
     doc = _id: new ObjectID().toString(), name: 'kristian'


Works (though far from complete) for YAML files

--- lib/provider.coffee
+++ lib/provider.coffee
@@ -96,15 +96,16 @@
   buildWordList: () =>
     possibleWords = []
     for citation in @references
-      if citation.entryTags and citation.entryTags.title and (citation.entryTags.author or citation.entryTags.editor)
+      if citation.entryTags and citation.entryTags.title and (citation.entryTags.authors or citation.entryTags.author or citation.entryTags.editor)
+
         citation.entryTags.prettyTitle =
           @prettifyTitle citation.entryTags.title

-        citation.entryTags.authors = []
-
-        if citation.entryTags.author?
-          citation.entryTags.authors =
-            citation.entryTags.authors.concat @cleanAuthors citation.entryTags.author.split ' and '
+        if not citation.entryTags.authors
+          citation.entryTags.authors = []
+          if citation.entryTags.author?
+            citation.entryTags.authors =
+              citation.entryTags.authors.concat @cleanAuthors citation.entryTags.author.split ' and '

         if citation.entryTags.editor?
           citation.entryTags.authors =
@@ -137,34 +138,31 @@
         # What type of file is this?
         ftype = file.split('.')
         ftype = ftype[ftype.length - 1]
-        console.warn("#{ftype}")

         if fs.statSync(file).isFile()

           if ftype is "bib"
             parser = new bibtexParse(fs.readFileSync(file, 'utf-8'))
             references = references.concat parser.parse()
-            console.log(parser.parse())

           if ftype is "yaml"
             parsedyaml = yaml.load fs.readFileSync(file, 'utf-8')
             # Convert yaml to parsed bibtex format
-            yamlref = []
             for r in parsedyaml
               # NOTE this is dirty -- but it works for now
-              t_ref = Object
+              t_ref = {}
               t_ref.citationKey = r['id']
               t_ref.entryType = r['type']
-              t_ref.entryTags = Object
-              # FIXME test
-              t_ref.entryTags.authors = []
-              for a in r['author']:
-                na = Object
+              t_ref.entryTags = {}
+              authors = []
+              for a in r['author']
+                na = {}
                 na.familyName = a['family']
                 na.personalName = a['given']
-                t_ref.entryTags.authors = t_ref.entryTags.authors.concat na
-              yamlref = yamlref.concat t_ref
-              console.log(yamlref)
+                authors = authors.concat na
+              t_ref.entryTags.authors = authors
+              t_ref.entryTags.title = r['title']
+              references = references.concat t_ref

         else
           console.warn("'#{file}' does not appear to be a file, so autocomplete-bibtex will not try to parse it.")


no need for global @req storage

no need for global @req storage

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

-  parse_request: (req=@req) ->
+  parse_request: (req) ->
     @_body = {}
     for key, val in req.body
       continue if key.match(/oauth/)


Fix(renderer) : Dropped last line in code

Removed the slice on grammar.tokenizeLines

Fix #27

--- lib/renderer.coffee
+++ lib/renderer.coffee
@@ -96,7 +96,7 @@

     codeBlock.empty()

-    for tokens in grammar.tokenizeLines(text).slice(0, -1)
+    for tokens in grammar.tokenizeLines(text)
       lineText = _.pluck(tokens, 'value').join('')
       htmlEolInvisibles = ''
       codeBlock.append(EditorView.buildLineHtml({tokens, text: lineText, htmlEolInvisibles}))


Updated events docs.

Updated events docs.

--- src/directives/events/base.coffee
+++ src/directives/events/base.coffee
@@ -1,10 +1,18 @@
-##
-## @name
-## Events
-##
-## @description
-## A directive for handling basic html events (e.g., blur, keyup, focus, etc.)
-##
+###
+@chalk overview
+@name Events
+
+@description
+A directive for handling basic html events (e.g., blur, keyup, focus, etc.)
+Currently MacGyver has blur, focus, keydown, keyup, mouseenter and mouseleave
+
+@param {Expression} mac-blur       Expression to evaulate on blur
+@param {Expression} mac-focus      Expression to evulate on focus
+@param {Expression} mac-keydown    Expression to evulate on keydown
+@param {Expression} mac-keyup      Expression to evulate on keyup
+@param {Expression} mac-mouseenter Expression to evulate on mouseenter
+@param {Expression} mac-mouseleave Expression to evulate on mouseleave
+###

 for event in ["Blur", "Focus", "Keydown", "Keyup", "Mouseenter", "Mouseleave"]
   do (event) ->
@@ -12,7 +20,7 @@
       restrict: "A"
       link: (scope, element, attributes) ->
         expression = $parse attributes["mac#{event}"]
-        element.on event.toLowerCase(), (event) ->
-          scope.$apply -> expression scope, $event: event
+        element.on event.toLowerCase(), ($event) ->
+          scope.$apply -> expression scope, {$event}
           true
     ]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/active_bounds.coffee
+++ test/active_bounds.coffee
@@ -1,6 +1,7 @@
 module "ActiveBounds"

-App.
+App.width = 640
+Ap

 test "should remove objects outside of activeBounds", 2, ->
   obj = GameObject


minor edit to gruntfile

minor edit to gruntfile

--- src/Gruntfile.coffee
+++ src/Gruntfile.coffee
@@ -19,6 +19,9 @@
         ext: ".js"

     watch:
-      coffeescript:
-        files: ["coffee/client/**/*.coffee", "coffee/shared/**/*.coffee"]
-        tasks: ["newer:coffee"]
+      coffeescript_client:
+        files: ["coffee/client/**/*.coffee"]
+        tasks: ["newer:coffee:client"]
+      coffeescript_shared:
+        files: ["coffee/shared/**/*.coffee"]
+        tasks: ["newer:coffee:shared"]


coach is missing on some sections, i.e. 4.3 in college physics

--- src/scripts/modules/media/body/body.coffee
+++ src/scripts/modules/media/body/body.coffee
@@ -104,7 +104,7 @@
     handleCoach: ($el) ->
       return unless @isCoach()
       @hideExercises($el)
-      $summary = $el.find('section.summary[data-depth="1"], section.section-summary[data-depth="1"]')
+      $summary = $el.find('section.summary[data-depth], section.section-summary[data-depth]')
       @makeRegionForCoach($summary) if $summary.length > 0

     # Toggle the visibility of teacher's edition elements


Added no class alert to checkin

Added no class alert to checkin

--- client/components/Checkins.coffee
+++ client/components/Checkins.coffee
@@ -65,6 +65,11 @@
             <p>
             Не забудьте с собой паспорт (если паспорта еще нет, то свидетельство о рождении) -- его могут спросить охранники на входе!
             </p>
+
+            <Alert bsStyle="danger">
+                Занятия 9 декабря не будет. Ниже — запись на 16 декабря.
+            </Alert>
+
             {
             if @state.result?.error
                 <Alert bsStyle="danger">


Update individual content routes to be admin-only (#181)

--- app/core/Router.coffee
+++ app/core/Router.coffee
@@ -177,9 +177,6 @@

     'minigames/conditionals': go('minigames/ConditionalMinigameView')

-    'ozaria/avatar-selector': () ->
-      @routeDirectly('ozaria/site/avatarSelector', [], { vueRoute: true, baseTemplate: 'base-empty' })
-
     'parents': go('ParentsView')

     'paypal/subscribe-callback': go('play/CampaignView')
@@ -218,26 +215,28 @@
     # 'play/spectate/:levelID': go('play/SpectateView')
     # 'play/:map': go('play/CampaignView')

-    # Adding this route to test interactives until we have the intro levels implemented
-    # TODO: remove this route when intro level is ready to test the interactives.
+    # These are admin-only routes since they are only used internally for testing -> interactive/, cinematic/, cutscene/, ozaria/avatar-selector
     'interactive/:interactiveIdOrSlug(?code-language=:codeLanguage)': (interactiveIdOrSlug, codeLanguage) ->
       props = {
         interactiveIdOrSlug: interactiveIdOrSlug,
         codeLanguage: codeLanguage # This will also come from intro level page later
       }
-      @routeDirectly('interactive', [], {vueRoute: true, baseTemplate: 'base-empty', propsData: props})
+      @routeDirectly('interactive', [], {vueRoute: true, baseTemplate: 'base-empty', propsData: props}) if me.isAdmin()

     'cinematic/:cinematicIdOrSlug': (cinematicIdOrSlug) ->
       props = {
         cinematicIdOrSlug: cinematicIdOrSlug,
       }
-      @routeDirectly('cinematic', [], {vueRoute: true, baseTemplate: 'base-empty', propsData: props})
+      @routeDirectly('cinematic', [], {vueRoute: true, baseTemplate: 'base-empty', propsData: props}) if me.isAdmin()

     'cutscene/:cutsceneId': (cutsceneId) ->
       props = {
         cutsceneId: cutsceneId,
       }
-      @routeDirectly('cutscene', [], { vueRoute: true, baseTemplate: 'base-empty', propsData: props })
+      @routeDirectly('cutscene', [], { vueRoute: true, baseTemplate: 'base-empty', propsData: props }) if me.isAdmin()
+
+    'ozaria/avatar-selector': () ->
+      @routeDirectly('ozaria/site/avatarSelector', [], { vueRoute: true, baseTemplate: 'base-empty' }) if me.isAdmin()

     'premium': go('PremiumFeaturesView', { redirectStudents: true, redirectTeachers: true })
     'Premium': go('PremiumFeaturesView', { redirectStudents: true, redirectTeachers: true })


improved openDelay setting behavior

improved openDelay setting behavior

--- lib/autohide-tree-view.coffee
+++ lib/autohide-tree-view.coffee
@@ -16,9 +16,10 @@
     sheet = getStylesheet().sheet
     sheet.insertRule "#{selector} { #{property}: #{value}; }", sheet.cssRules.length

-  calculateHideDuration = (delay) ->
-    delay ?= atom.config.get 'autohide-tree-view.hideDelay'
-    hideDuration = if delay is 0 then 0 else .05 + delay
+  calculateHideDuration = (hideDelay) ->
+    hideDelay ?= atom.config.get 'autohide-tree-view.hideDelay'
+    openDelay = atom.config.get 'autohide-tree-view.openDelay'
+    hideDuration = if hideDelay is 0 then 0 else .05 + hideDelay - openDelay

   config:
     unfoldSpeed:
@@ -64,11 +65,12 @@
     updateStylesheet '.tree-view-resizer:hover', 'max-width', "#{maxWidth}px!important"

   applyHideDelay: (delay) ->
-    duration = calculateHideDuration()
+    duration = calculateHideDuration(delay)
     updateStylesheet '.tree-view-resizer', 'transition-duration', "#{duration}s"

   applyOpenDelay: (delay) ->
     updateStylesheet '.tree-view-resizer', 'transition-delay', "#{delay}s"
+    @applyHideDelay()

   applyMinWidth: (width) ->
     updateStylesheet '.tree-view-resizer', 'min-width', "#{width}px!important"


Polish beginning and end markers

Polish beginning and end markers

--- lib/linter-codeclimate.coffee
+++ lib/linter-codeclimate.coffee
@@ -9,6 +9,12 @@
 getEnabledEngines = (configFilePath) ->
   configYaml = YAML.safeLoad(FS.readFileSync(configFilePath, "utf8"))
   (engine for engine, attrs of configYaml["engines"] when attrs["enabled"] == true)
+
+getPosEnd = (posEnd, posBeg) ->
+  if posBeg == posEnd
+    return posBeg + 2
+  else
+    posEnd || 120

 module.exports =
   config:
@@ -47,7 +53,7 @@
         linterNameArray = linterMap['*']

         if (linterMap.hasOwnProperty(grammarName) == true)
-          linterNameArray.push(linter) for linter in linterMap[grammarName]
+          linterNameArray.push(linter) for linter in linterMap[grammarName] when (linter not in linterNameArray)

         configurationFilePath = Helpers.findFile(fileDir, configurationFile)
         if (!configurationFilePath)
@@ -64,11 +70,11 @@
           return []

         configEnabledEngines = getEnabledEngines(configurationFilePath)
-        linterEnabledEngines = (linter for linter in linterNameArray when (linter in configEnabledEngines))
+        linterEnabledEngines = (linter for linter in linterNameArray when ((linter in configEnabledEngines) == true))

         cmd = ["codeclimate analyze",
                "-f json",
-               makeEngineString(linterEnabledEngines),
+               makeEngineString(linter for linter in linterEnabledEngines when linter),
                "'" + atom.project.relativize(filePath) + "'",
                "< /dev/null"].join(" ")

@@ -82,23 +88,22 @@
             linterResults = []
             for issue in messages
               if (issue.location.positions)
-                locLineBegin = issue.location.positions.begin.line
-                locLineEnd = issue.location.positions.end.line
-                locPosBegin = issue.location.positions.begin.column || 0
-                locPosEnd = 80
+                locLine = issue.location.positions.begin.line - 1
+                locPosBeg = (issue.location.positions.begin.column - 1) || 0
+                locPosEnd = getPosEnd(issue.location.positions.end.column, issue.location.positions.begin.column)
               else
-                locLineBegin = issue.location.lines.begin
-                locLineEnd = issue.location.lines.end
-                locPosBegin = 0
-                locPosEnd = 80
+                locLine = issue.location.lines.begin - 1
+                locPosBeg = 0
+                locPosEnd = 120

               do (issue) ->
                 linterResults.push({
                   type: "Warning"
                   text: issue.description
                   filePath: filePath
-                  range: [[locLineBegin-1,locPosBegin], [locLineEnd-1,locPosEnd]]
+                  range: [[locLine,locPosBeg], [locLine,locPosEnd]]
                 })
+
             console.log("Code Climate analysis: " + (Date.now() - analysisBeginTime) + "ms")
             return linterResults
           )


As per oauth 1.0 spec section 3.1, nonce and timestamp are required

--- lib/memory-nonce-store.coffee
+++ lib/memory-nonce-store.coffee
@@ -10,7 +10,7 @@

   isNew:   (nonce, timestamp)->

-    return true if typeof nonce is 'undefined' or nonce is null
+    return false if typeof nonce is 'undefined' or nonce is null

     notInArray = @used.indexOf(nonce) is -1
     @setUsed(nonce, timestamp)


Better examples

Better examples

--- client/landing/app/Applications/Groups.kdapplication/Views/productsettingsview.coffee
+++ client/landing/app/Applications/Groups.kdapplication/Views/productsettingsview.coffee
@@ -113,8 +113,9 @@

     codeWidget = """@content = new KDButtonView
                     cssClass   : "clean-gray test-input"
-                    title      : "Subscribed!"
+                    title      : "Subscribed! View Video"
                     callback   : ->
+                      console.log "Open video..."

                     @payment = new PaymentWidget
                       planCode        : '#{code}'
@@ -122,6 +123,7 @@
                       content         : @content

                     @payment.on "subscribed", ->
+                      console.log "User is subscribed."
                   """

     @embedButton = new KDButtonView


finished List class

finished List class

--- src/builtin/list.coffee
+++ src/builtin/list.coffee
@@ -8,14 +8,29 @@
     return

   insert: (index, element) ->
+    @value = @value.splice(index, 0, element)
+    return

   remove: (element) ->
-    @pop(@index(element))
+    @value = @pop(@index(element))
     return

   pop: (index)  ->
+    @value = @value.splice(index, 1)
+    return

   sort: ->
+    @value =
+      @value
+      .sort (a, b) ->
+        if a.__lt__(b)
+          return -1
+        else if a.__eq__(b)
+          return 0
+        else if a.__gt__(b)
+          return 1
+    return

   reverse: (operand) ->
-    @value.reverse()
+    @value = @value.reverse()
+    return


added test case

added test case

--- test/unit/can-test.coffee
+++ test/unit/can-test.coffee
@@ -4,32 +4,56 @@
 someText = null

 describe "can", ->
-
-  browser = null
-  can = sodaSync.can
-    with: -> browser
-    pre: -> @timeout 30000

-  it "create client", (done) ->
-    {browser} = sodaSync.createClient (
-      host: "localhost"
-      port: 4444
-      url: "http://www.google.com"
-      browser: "firefox"
-      mode: 'sync'
-    )
-    done()
+  describe "no pre", ->

-  describe "with soda can", ->
-    it "should work", can
-      pre: ->
-        @timeout 30000
-        someText = 'Test1'
-    , ->
-      someText.should.equal 'Test1'
-      @session()
-      @open '/'
-      @getTitle().toLowerCase().should.include 'google'
-      @testComplete()
+    browser = null
+    can = sodaSync.can
+      with: -> browser

+    it "create client", (done) ->
+      {browser} = sodaSync.createClient (
+        host: "localhost"
+        port: 4444
+        url: "http://www.google.com"
+        browser: "firefox"
+        mode: 'sync'
+      )
+      done()
+
+    describe "with soda can", ->
+      it "should work", can ->
+        @session()
+        @open '/'
+        @getTitle().toLowerCase().should.include 'google'
+        @testComplete()
+
+  describe "with pre", ->
+
+    browser = null
+    can = sodaSync.can
+      with: -> browser
+      pre: -> @timeout 30000
+
+    it "create client", (done) ->
+      {browser} = sodaSync.createClient (
+        host: "localhost"
+        port: 4444
+        url: "http://www.google.com"
+        browser: "firefox"
+        mode: 'sync'
+      )
+      done()
+
+    describe "with soda can", ->
+      it "should work", can
+        pre: ->
+          @timeout 30000
+          someText = 'Test1'
+      , ->
+        someText.should.equal 'Test1'
+        @session()
+        @open '/'
+        @getTitle().toLowerCase().should.include 'google'
+        @testComplete()


+ createObject/createObjectWith test

+ createObject/createObjectWith test

--- test/inherits-test.coffee
+++ test/inherits-test.coffee
@@ -156,6 +156,105 @@
     assert.equal isInheritedFrom(C11, C1), C11, "C11 > C1"
     assert.equal isInheritedFrom(C11, C), C2, "C11 > C"

+  describe "createObject", ->
+    createObject = require('../lib/createObject')
+
+    it 'should call the parent\'s constructor method if it no constructor', ->
+      A12 = ->
+      assert.equal inherits(A12, A1), true
+      a = createObject(A12)
+      assert.instanceOf a, A12
+      assert.instanceOf a, A1
+      assert.instanceOf a, A
+      assert.instanceOf a, Root
+      assert.equal a.inited, "A1"
+
+    it 'should call the root\'s constructor method if its parent no constructor yet', ->
+      A2 = ->
+      assert.equal inherits(A2, A), true
+      a = createObject(A2)
+      assert.instanceOf a, A2
+      assert.instanceOf a, A
+      assert.instanceOf a, Root
+      assert.equal a.inited, "Root"
+    it 'should pass the correct arguments to init', ->
+      class A2
+      assert.equal inherits(A2, A), true
+      a = createObject(A2, "hiFirst", 1881)
+      assert.instanceOf a, A2
+      assert.instanceOf a, A
+      assert.instanceOf a, Root
+      assert.equal a.inited, "hiFirst"
+      assert.equal a.other, 1881
+    it 'should pass the correct arguments to constructor', ->
+      A2 = (@first, @second)->
+      assert.equal inherits(A2, A), true
+      a = createObject(A2, "hiFirst", 1881)
+      assert.instanceOf a, A2
+      assert.instanceOf a, A
+      assert.instanceOf a, Root
+      assert.equal a.first, "hiFirst"
+      assert.equal a.second, 1881
+      should.not.exist a.inited
+      should.not.exist a.other
+
+  describe "createObjectWith", ->
+    createObject = require('../lib/createObjectWith')
+
+    it 'should call the parent\'s constructor method if it no constructor', ->
+      class A12
+      assert.equal inherits(A12, A1), true
+      a = createObject(A12)
+      assert.instanceOf a, A12
+      assert.instanceOf a, A1
+      assert.instanceOf a, A
+      assert.instanceOf a, Root
+      assert.equal a.inited, "A1"
+
+    it 'should call the root\'s constructor method if its parent no constructor yet', ->
+      class A2
+      assert.equal inherits(A2, A), true
+      a = createObject(A2)
+      assert.instanceOf a, A2
+      assert.instanceOf a, A
+      assert.instanceOf a, Root
+      assert.equal a.inited, "Root"
+    it 'should pass the correct arguments to init', ->
+      class A2
+      assert.equal inherits(A2, A), true
+      a = createObject(A2, ["hiFirst", 1881])
+      assert.instanceOf a, A2
+      assert.instanceOf a, A
+      assert.instanceOf a, Root
+      assert.equal a.inited, "hiFirst"
+      assert.equal a.other, 1881
+    it 'should pass the correct arguments to constructor', ->
+      class A2
+        constructor: (@first, @second)->
+      assert.equal inherits(A2, A), true
+      a = createObject(A2, ["hiFirst", 1881])
+      assert.instanceOf a, A2
+      assert.instanceOf a, A
+      assert.instanceOf a, Root
+      assert.equal a.first, "hiFirst"
+      assert.equal a.second, 1881
+      should.not.exist a.inited
+      should.not.exist a.other
+    it 'should pass the correct arguments to init for internal arguments', ->
+      class A2
+        constructor: ->
+          if not (this instanceof A2)
+            return createObject(A2, arguments)
+          super
+      assert.equal inherits(A2, A), true
+      a = A2("hiFirst~", 1181)
+      assert.instanceOf a, A2
+      assert.instanceOf a, A
+      assert.instanceOf a, Root
+      assert.equal a.inited, "hiFirst~"
+      assert.equal a.other, 1181
+
+
 describe "mixin", ->
   it "should mixin class", ->
     class Root
@@ -227,3 +326,5 @@
     A::m.should.have.been.calledOnce
     A::m.should.have.been.calledWith "a", 12
     mCallOrder.should.be.deep.equal ['B1', 'C', 'A1', 'A']
+
+


unessential code changes in the plumbs.negotiate

--- library/nucleus/plumbs.coffee
+++ library/nucleus/plumbs.coffee
@@ -108,16 +108,22 @@
 # a queriable form. This queriable forms allows to negotiate for
 # media types, accepted encoding, accepted language and so on.
 # Middleware can be used to serve the most appropriare content.
-module.exports.negotiate = (kernel) ->
-    (request, response, next) ->
-        ack = "could not instantiate a negotiator"
-        terrible = "no valid request object found"
-        noLibrary = "could not load negotiator lib"
-        assert _.isObject(Negotiator), noLibrary
-        assert _.isObject(request or 0), terrible
-        request.negotiate = n = Negotiator request
-        assert _.isObject(request.negotiate), ack
-        return next() unless request.headersSent
+module.exports.negotiate = (kernel) -> (request, response) ->
+    terrible = "no valid request object found"
+    noLibrary = "could not load negotiator lib"
+    acked = "could not instantiate a negotiator"
+    noHeaders = "unable to locate request headers"
+    assert _.isObject(kernel), "no kernel supplied"
+    assert _.isObject(Negotiator or 0), noLibrary
+    assert _.isObject(request or null), terrible
+    assert _.isObject(request?.headers), noHeaders
+    request.negotiate = Negotiator request or {}
+    assert _.isObject(request.negotiate), acked
+    assert unix = moment().unix().toString().bold
+    message = "Running negotiate middleware at U=%s"
+    logger.debug message.toString(), unix.toString()
+    assert _.isFunction next = _.last arguments
+    return next() unless request.headersSent

 # A middleware that adds a `send` method to the response object.
 # This allows for automatic setting of `Content-Type` headers


minor code reordering

minor code reordering

--- library/nucleus/scaled.coffee
+++ library/nucleus/scaled.coffee
@@ -60,56 +60,6 @@
     # kernel. Refer to that kernel and to `identica` method there
     # for more information on semantics and the way of working it.
     @identica -> "#{@APPLICATION.name}@#{@APPLICATION.version}"
-
-    # A configuration routine that ensures the scope config has the
-    # Seaport hub related configuration data. If so, it proceeds to
-    # retrieving that info and using it to locate and connect to a
-    # Seaport hub, which is then installed as the kernel instance
-    # variable, so that it can be accessed by the other routines.
-    @configure "access the service Seaport hub", (next) ->
-        assert _.isString host = nconf.get "hub:host"
-        assert _.isNumber port = nconf.get "hub:port"
-        assert _.isObject opts = nconf.get "hub:opts"
-        @seaport = seaport.connect host, port, opts
-        assert _.isObject(@seaport), "seaport failed"
-        assert @seaport.register?, "a broken seaport"
-        shl = "#{host}:#{port}".toString().underline
-        msg = "Locate a Seaport hub at #{shl}".blue
-        logger.info msg; return next undefined
-
-    # Shutdown the kernel instance. This includes shutting down both
-    # HTTP and HTTPS server that may be running, stopping the router
-    # and unregistering all the services as a precauting. After that
-    # the scope is being dispersed and some events are being emited.
-    # This implementaton cleans up some of the scalability resources.
-    shutdownKernel: (reason, eol=yes) ->
-        util.puts require("os").EOL if eol
-        @emit "shutdownScaledKernel", arguments...
-        message = "Graceful shutdown of Scaled kernel"
-        try @spserver.close() if @spserver.close?
-        try @serverProxy.close() if @serverProxy?
-        try @secureProxy.close() if @secureProxy?
-        terminator = (proxy) -> try proxy.close()
-        _.each @queueOfHttps or Array(), terminator
-        _.each @queueOfHttp or Array(), terminator
-        logger.warn message.red; super reason, no
-
-    # The kernel preemption routine is called once the kernel has
-    # passed the initial launching and configuration phase, but is
-    # yet to start up the router, connect services and instantiate
-    # an actual application. This method gets passes continuation
-    # that does that. The method can either invoke it or omit it.
-    kernelPreemption: (continuation) ->
-        assert _.isObject(@options), "got no options"
-        either = @options.master or @options.instance
-        assert either, "no master and no instance mode"
-        return continuation() unless @options.master
-        logger.warn "The kernel is booting as master"
-        assert nconf.get("master"), "no master config"
-        assert not _.isEmpty @createSeaportServer()
-        assert not _.isEmpty @startupHttpsMaster()
-        assert not _.isEmpty @startupHttpMaster()
-        continuation.call @ if @options.instance

     # Prepare and setup an HTTPS master server. This server is the
     # proxy server that is going to consume all the HTTPS requests
@@ -322,3 +272,53 @@
         logger.info msg.green, "#{record}".bold
         try config.server.http = record or null
         try nconf.defaults config; return super
+
+    # A configuration routine that ensures the scope config has the
+    # Seaport hub related configuration data. If so, it proceeds to
+    # retrieving that info and using it to locate and connect to a
+    # Seaport hub, which is then installed as the kernel instance
+    # variable, so that it can be accessed by the other routines.
+    @configure "access the service Seaport hub", (next) ->
+        assert _.isString host = nconf.get "hub:host"
+        assert _.isNumber port = nconf.get "hub:port"
+        assert _.isObject opts = nconf.get "hub:opts"
+        @seaport = seaport.connect host, port, opts
+        assert _.isObject(@seaport), "seaport failed"
+        assert @seaport.register?, "a broken seaport"
+        shl = "#{host}:#{port}".toString().underline
+        msg = "Locate a Seaport hub at #{shl}".blue
+        logger.info msg; return next undefined
+
+    # The kernel preemption routine is called once the kernel has
+    # passed the initial launching and configuration phase, but is
+    # yet to start up the router, connect services and instantiate
+    # an actual application. This method gets passes continuation
+    # that does that. The method can either invoke it or omit it.
+    kernelPreemption: (continuation) ->
+        assert _.isObject(@options), "got no options"
+        either = @options.master or @options.instance
+        assert either, "no master and no instance mode"
+        return continuation() unless @options.master
+        logger.warn "The kernel is booting as master"
+        assert nconf.get("master"), "no master config"
+        assert not _.isEmpty @createSeaportServer()
+        assert not _.isEmpty @startupHttpsMaster()
+        assert not _.isEmpty @startupHttpMaster()
+        continuation.call @ if @options.instance
+
+    # Shutdown the kernel instance. This includes shutting down both
+    # HTTP and HTTPS server that may be running, stopping the router
+    # and unregistering all the services as a precauting. After that
+    # the scope is being dispersed and some events are being emited.
+    # This implementaton cleans up some of the scalability resources.
+    shutdownKernel: (reason, eol=yes) ->
+        try util.puts require("os").EOL if eol
+        this.emit "shutdownScaledKernel", arguments...
+        message = "Graceful shutdown of Scaled kernel"
+        try @spserver.close() if @spserver.close?
+        try @serverProxy.close() if @serverProxy?
+        try @secureProxy.close() if @secureProxy?
+        terminator = (proxy) -> try proxy.close()
+        _.each @queueOfHttps or Array(), terminator
+        _.each @queueOfHttp or Array(), terminator
+        logger.warn message.red; super reason, no


Back always false after display page

Back always false after display page

--- src/coffee/glide.coffee
+++ src/coffee/glide.coffee
@@ -177,7 +177,7 @@
     if @isAndroid() and @os.version < '4' and @back is false
       window.scrollTo 0,0

-    if @back is true then @back = false
+    @back = false

   # Private: Hide DOM that has just been transitioned
   #


woops

woops

--- query_runner.coffee
+++ query_runner.coffee
@@ -144,7 +144,7 @@

   postResult: (query, result) ->
     if result.data.length == 0
-      if result.errors.length
+      if result.errors?.length
         txt = result.errors.map((e) -> "#{e.message}```#{e.message_details}```").join("\n")
         @reply(":warning: #{query.share_url}\n#{txt}")
       else


Switch to a get request as it should have been

--- scripts/xcode-server.coffee
+++ scripts/xcode-server.coffee
@@ -17,7 +17,7 @@
 querystring = require('querystring')

 module.exports = (robot) ->
-  robot.router.post "/hubot/xcode-publish-notify", (req, res) ->
+  robot.router.get "/hubot/xcode-publish-notify", (req, res) ->
     query = querystring.parse(url.parse(req.url).query)
     app = query.app || null
     success = query.success || false


changed comment headings and simplified code in popup.coffee

--- src/lib/popup.coffee
+++ src/lib/popup.coffee
@@ -4,36 +4,31 @@
 # For all details and documentation:
 # <http://neocotic.com/template>

-#### Private variables
+# Private variables
+# -----------------

 # Easily accessible reference to the extension controller.
 {ext} = chrome.extension.getBackgroundPage()

-#### Private functions
-
- # Calculate the widest text used by the `div` elements in the popup and assign
- # it to all the others.
-resizePopupText = ->
-  textItems   = document.getElementsByClassName 'text'
-  width       = 0
-
-  for textItem in textItems
-    width = textItem.scrollWidth if textItem.scrollWidth > width
-  for textItem in textItems
-    textItem.style.width = "#{width}px"
-
-#### Popup page setup
+# Popup page setup
+# ----------------

 popup = window.popup =

-  #### Public functions
+  # Public functions
+  # ----------------

   # Initialize the popup page.
   init: ->
     # Insert the prepared HTML in to the popup's body.
     document.body.innerHTML = ext.popupHTML
-    # Fix dimensions of feature text
-    resizePopupText()
+    # Calculate the widest text used by the `div` elements in the popup and
+    # assign it to all of the others.
+    textItems   = document.getElementsByClassName 'text'
+    width       = 0
+    for textItem in textItems when textItem.scrollWidth > width
+      width = textItem.scrollWidth
+    textItem.style.width = "#{width}px" for textItem in textItems

   # Send a request to the background page using the information provided.
   sendRequest: (item) ->


Link button working properly for instances and on main

--- client/coffeescript/templates/post.coffee
+++ client/coffeescript/templates/post.coffee
@@ -23,10 +23,18 @@
     return oembedded
   identifier: -> @_id
   link_href: ->
-    return "/#{ @_id }"
+    #for main socrenchus instance:
+    if window.instance is undefined
+      return "/p/#{ @_id }"
+    else #give full "instanced" link if not on main
+      return "/i/#{window.instance}/p/#{ @_id }"
   parent_href: ->
     if @parent_id?
-      return "/#{ @parent_id }"
+        #for main socrenchus instance:
+      if window.instance is undefined
+        return "/p/#{ @parent_id }"
+      else #give full "instanced" link if not on main
+        return "/i/#{window.instance}/p/#{ @parent_id }"
     else
       return false
 )


SettingsView: Cache created participant views.

--- client/ide/views/chat/settingspane.coffee
+++ client/ide/views/chat/settingspane.coffee
@@ -118,10 +118,18 @@
     @everyone.unsetClass 'loading'
     @everyone.destroySubViews()

-    participants = @rtm.getFromModel('participants').asArray()
+    participants      = @rtm.getFromModel('participants').asArray()
+    @participantViews = {}

     for participant in participants
-      @everyone.addSubView new IDE.ChatParticipantView {}, participant
+      @createParticipantView participant
+
+
+  createParticipantView: (data) =>
+
+    view = new IDE.ChatParticipantView {}, data
+    @participantViews[data.nickname] = view
+    @everyone.addSubView view

   viewAppended: JView::viewAppended


text.shell.spec: removed old code.

text.shell.spec: removed old code.

--- coffee/test/shells/text.shell.spec.coffee
+++ coffee/test/shells/text.shell.spec.coffee
@@ -4,7 +4,6 @@
 goog.require 'acorn.util.test'

 describe 'acorn.shells.TextShell', ->
-  derives = athena.lib.util.derives
   Shell = acorn.shells.Shell
   TextShell = acorn.shells.TextShell


change to sigle quote

change to sigle quote

--- coffeescript/004-webworkers/csmain.coffee
+++ coffeescript/004-webworkers/csmain.coffee
@@ -34,7 +34,7 @@
              </ul>",
             ctx
         )
-        $("#params").html(list)
+        $('#params').html(list)

         worker = new Worker 'worker.js'
         worker.onmessage = (e) ->


let coffee do the !threequals

let coffee do the !threequals

--- lib/commands/run.coffee
+++ lib/commands/run.coffee
@@ -413,7 +413,7 @@
       else
         outputStream.on 'end', ->
           try options.stdout.end() catch # ignore
-          try options.stderr !== process.stderr && options.stderr.end() catch # ignore
+          try options.stderr != process.stderr && options.stderr.end() catch # ignore

         # For non-TTY, we keep stdout and stderr separate, and pipe them appropriately to our
         # process's streams.


get rid of some old wonky bullshit

get rid of some old wonky bullshit

--- client/Framework/core/KDObject.coffee
+++ client/Framework/core/KDObject.coffee
@@ -15,12 +15,6 @@
     @listeningTo = []
     super
     @once 'ready', => @readyState = READY
-
-  if KD.MODE is 'development'
-    interfere:(o)-> o
-    o:(o)-> @interfere o
-  else
-    o:(o)->o

   bound: Bongo.bound


shortcuts: expose getPlatformBinding in instance level too (because coffeescript can\'t handle circular deps, that\'s why)

--- client/app/lib/shortcuts/index.coffee
+++ client/app/lib/shortcuts/index.coffee
@@ -391,6 +391,8 @@

     return model[klass._bindingGetterMethodName()]()

+  getPlatformBinding: klass._getPlatformBinding
+

   # Returns _binding_ array index of a _keyconfig#Model_ for the current platform.
   #


Imagemin: Remove pngquant from svgmin options.

--- src/imagemin/index.coffee
+++ src/imagemin/index.coffee
@@ -43,8 +43,6 @@
 	# svgmin
 	unless not @svg or gf.hasSection 'svgmin'
 		gf.addSection 'svgmin',
-			options:
-				pngquant: true
 			main:
 				files: [
 					expand: true


add fallback for old themeing options

add fallback for old themeing options

--- src/javascripts/theme.coffee
+++ src/javascripts/theme.coffee
@@ -26,8 +26,8 @@
     if theme.constructor == String
       @loadInternalTheme(theme)
     else
-      @loadCss(theme.css)
-      @loadHtml(theme.html)
+      @loadCss(@app.options.themeCss || theme.css)
+      @loadHtml(@app.options.themeHtml || theme.html)

   loadInternalTheme: (name) =>
     pathPrefix = "themes/#{name}/index"


Expression.simplify now expands first. Resolves #100, #99.

--- coffeequate/src/Expression.coffee
+++ coffeequate/src/Expression.coffee
@@ -101,7 +101,9 @@
 		# @param equivalencies [Object] Optional. A map of variable labels to a list of equivalent variable labels.
 		# @return [Expression] A simplified Expression.
 		simplify: (equivalencies={}) ->
-			if @expr.simplify?
+			if @expr.expandAndSimplify?
+				expr = @expr.expandAndSimplify(equivalencies)
+			else if @expr.simplify?
 				expr = @expr.simplify(equivalencies)
 			else
 				expr = @expr.copy()


fixed showing initial state in KDMultipleChoice - can be seen in Account>Email Notifications

--- components/inputs/KDMultipleChoice.coffee
+++ components/inputs/KDMultipleChoice.coffee
@@ -34,12 +34,13 @@
     @currentValue = [] if options.multiple

   setDomElement:(cssClass)->
-    {labels, name} = @getOptions()
+    {labels, name, defaultValue} = @getOptions()
     @inputName = name

     labelItems = ""
     for label in labels
-      clsName     = "multiple-choice-#{label}"
+      activeClass = if label is defaultValue then ' active' else ''
+      clsName     = "multiple-choice-#{label}#{activeClass}"
       labelItems += "#{label}"

     @domElement = $ """


Removes manifest coffee file to trigger plugins, they already have methods on their files

--- coffee/manifest.coffee
+++ coffee/manifest.coffee
@@ -1,21 +0,0 @@
-jQuery ->
-
-  $('#mp-menu').offScreen()
-  $('.navigation-bar').responsiveNavBar()
-  $('table.responsive').responsiveTables()
-
-  #Closing buttons
-  $('.close').each ->
-    $(@).click (e) ->
-      e.preventDefault()
-      $(@).parent().fadeOut()
-
-  #Tooltips
-  $("[data-toggle=tooltip]").tooltip()
-
-  #Datepicker
-  $("[data-datepicker]").each ->
-    input = $(@)
-    input.pickadate
-      selectYears: input.data('select-years') || false
-      selectMonths: input.data('select-months') || false


(feature) Return disconnect function after create.

--- src/jailed_sandbox.coffee
+++ src/jailed_sandbox.coffee
@@ -55,6 +55,8 @@
         console.error "Sandbox timed out after #{config.timeout}ms!"
         runner.disconnect()
       ), config.timeout
+
+    disconnect

   debug: (code, customApi, config) ->
     code = "debugger;\n#{code}\n //@ sourceURL=debug.js\n"


scoped interval call

scoped interval call

--- app/script/main.coffee
+++ app/script/main.coffee
@@ -30,7 +30,7 @@

 evalE = codeP.filter(enabledP).sampledBy($run.asEventStream("click").doAction(".preventDefault").merge(runBus))

-interval = (i, fn) -> setInterval(fn, i)
+interval = (i, fn) -> Bacon.interval(i).takeUntil(evalE).onValue(fn)

 Figure = require("./figure.coffee")


Fix #162 - no confirm

Fix #162 - no confirm

--- src/layout/mixins/programs.coffee
+++ src/layout/mixins/programs.coffee
@@ -114,7 +114,7 @@

   program_row = (name, contents) ->
     buttons = make.buttons "action", contents, my.action, ((button, args) ->
-        if sprite.get('editable') and confirm('Are you sure you want to remove that action?')
+        if sprite.get('editable') #and confirm('Are you sure you want to remove that action?')
           button.up.remove_child(button)
           button.send 'click'
           button.send 'brick', -1


Adds ... `unshove`

Adds ... `unshove`

--- collections/recent_connections.coffee
+++ collections/recent_connections.coffee
@@ -1,13 +1,12 @@
 Base = require './base.coffee'
 Backbone = require 'backbone'
 Backbone.LocalStorage = require 'backbone.localstorage'
-Block = require "../models/block.coffee"
+Block = require '../models/block.coffee'

 module.exports = class RecentConnections extends Backbone.Collection
-
   model: Block

-  localStorage: new Backbone.LocalStorage "RecentConnections"
+  localStorage: new Backbone.LocalStorage RecentConnections

   shove: (model) ->
     @fetch
@@ -15,3 +14,7 @@
         @sync('delete', found) if (found = @get model.id)
         @trigger('add', model) unless found
         @create model.toJSON()
+
+
+  unshove: (model) ->
+    @sync('delete', @get model.id)


ComponentIo: Fix issues found testing noflo-ui

--- src/lib/loader/ComponentIo.coffee
+++ src/lib/loader/ComponentIo.coffee
@@ -11,7 +11,6 @@
     dependency = dependencies.shift()
     dependency = dependency.replace '/', '-'
     @getModuleComponents loader, dependency, (err) =>
-      return callback err if err
       @getModuleDependencies loader, dependencies, callback

   getModuleComponents: (loader, moduleName, callback) ->
@@ -26,7 +25,7 @@

     return callback() unless definition.noflo

-    @getModuleDependencies loader, definition.dependencies, (err) ->
+    @getModuleDependencies loader, Object.keys(definition.dependencies), (err) ->
       return callback err if err

       prefix = loader.getModulePrefix definition.name
@@ -46,7 +45,8 @@
           loader.registerComponent prefix, name, "/#{moduleName}/#{cPath}"
       if definition.noflo.graphs
         for name, cPath of definition.noflo.graphs
-          loader.registerGraph prefix, name, "/#{moduleName}/#{cPath}"
+          def = require "/#{moduleName}/#{cPath}"
+          loader.registerGraph prefix, name, def

       if definition.noflo.loader
         # Run a custom component loader


Correct return code when unauthorised, clarify post comment

--- code/serv.coffee
+++ code/serv.coffee
@@ -57,7 +57,7 @@
       server_ip: server_ip

 # POST REQUESTS
-# These will likely result in changes to the mongodb database
+# These should make changes somewhere, likely to the mongodb database

 # TODO: should this be middleware?
 check_api_key = (req, res, next) ->
@@ -108,7 +108,7 @@
   Box.findOne {name: req.params.box_name}, (err, box) ->
     return res.send { error: "Box not found" }, 404 unless box?
     User.findOne {apikey: req.body.apikey}, (err, user) ->
-      return res.send { error: "Unauthorised" } unless user?
+      return res.send { error: "Unauthorised" }, 403 unless user?
       return res.send { error: "Unauthorised" }, 403 unless user._id.toString() == box.user.toString()
       try
         name = SSHKey.extract_name req.body.sshkey


camel case test

camel case test

--- components/clock/test/view.coffee
+++ components/clock/test/view.coffee
@@ -107,31 +107,31 @@

   describe '#stateCallback', ->
     it 'defaults to reloading the page when the clock state changes', ->
-      clock_view = new ClockView
+      clockView = new ClockView
         model: new Sale(fabricate('sale'), clockState: 'open')
         el: $("

")

-      clock_view.start()
+      clockView.start()
       _.last(Backbone.sync.args)[2].success { time: moment().format() }

       location.reload.called.should.equal false
-      clock_view.model.set('clockState', 'closed')
+      clockView.model.set('clockState', 'closed')
       location.reload.called.should.equal true

     it 'can be overridden with anything', ->
       @hello = 'cat'

-      clock_view = new ClockView
+      clockView = new ClockView
         model: new Sale(fabricate('sale'), clockState: 'open')
         el: $("

")
         stateCallback: =>
           @hello = 'world'

-      clock_view.start()
+      clockView.start()
       _.last(Backbone.sync.args)[2].success { time: moment().format() }

       @hello.should.equal 'cat'

-      clock_view.model.set('clockState', 'closed')
+      clockView.model.set('clockState', 'closed')

       @hello.should.equal 'world'


Add deprecation warnings

Add deprecation warnings

--- src/keymap-manager.coffee
+++ src/keymap-manager.coffee
@@ -1,5 +1,6 @@
 _ = require "underscore-plus"
 CSON = require 'season'
+Grim = require 'grim'
 fs = require 'fs-plus'
 path = require 'path'
 {Emitter} = require 'emissary'
@@ -451,15 +452,18 @@

   # Deprecated: Use {::addKeymap} instead.
   add: (source, bindings) ->
+    Grim.deprecate("Use KeymapManager::addKeymap instead.")
     @addKeymap(source, bindings)

   # Deprecated: Use {::removeKeymap} instead.
   remove: (source) ->
+    Grim.deprecate("Use KeymapManager::removeKeymap instead.")
     @removeKeymap(source)

   # Deprecated: Handle a jQuery keyboard event. Use {::handleKeyboardEvent} with
   # a raw keyboard event instead.
   handleKeyEvent: (event) ->
+    Grim.deprecate("Use KeymapManager::handleKeyboardEvent instead.")
     originalEvent = event.originalEvent ? event
     Object.defineProperty(originalEvent, 'target', get: -> event.target) unless originalEvent.target?
     @handleKeyboardEvent(originalEvent)
@@ -468,33 +472,40 @@
   # Deprecated: Translate a jQuery keyboard event to a keystroke string. Use
   # {::keystrokeForKeyboardEvent} with a raw KeyboardEvent instead.
   keystrokeStringForEvent: (event) ->
+    Grim.deprecate("Use KeymapManager::keystrokeForKeyboardEvent instead.")
     @keystrokeForKeyboardEvent(event.originalEvent ? event)

   # Deprecated: Use {::addKeymap} with a map from selectors to key
   # bindings.
   bindKeys: (source, selector, keyBindings) ->
+    Grim.deprecate("Use KeymapManager::addKeymap instead.")
     keyBindingsBySelector = {}
     keyBindingsBySelector[selector] = keyBindings
     @addKeymap(source, keyBindingsBySelector)

   # Deprecated: Use {::findKeyBindings} with the 'command' param.
   keyBindingsForCommand: (command) ->
+    Grim.deprecate("Use KeymapManager::findKeyBindings instead.")
     @findKeyBindings({command})

   # Deprecated: Use {::findKeyBindings} with the 'keystrokes' param.
   keyBindingsForKeystroke: (keystroke) ->
+    Grim.deprecate("Use KeymapManager::findKeyBindings instead.")
     @findKeyBindings({keystrokes: keystroke})

   # Deprecated: Use {::findKeyBindings} with the 'target' param.
   keyBindingsMatchingElement: (target, keyBindings) ->
+    Grim.deprecate("Use KeymapManager::findKeyBindings instead.")
     @findKeyBindings({target: target[0] ? target, keyBindings})

   # Deprecated: Use {::findKeyBindings} with the 'command' and 'target'
   # params
   keyBindingsForCommandMatchingElement: (command, target) ->
+    Grim.deprecate("Use KeymapManager::findKeyBindings instead.")
     @findKeyBindings({command, target: target[0] ? target})

   # Deprecated: Use {::findKeyBindings} with the 'keystrokes' and 'target'
   # params
   keyBindingsForKeystrokeMatchingElement: (keystrokes, target) ->
+    Grim.deprecate("Use KeymapManager::findKeyBindings instead.")
     @findKeyBindings({keystrokes, target: target[0] ? target})


Cleanup

Cleanup

--- SingularityUI/app/views/taskS3LogsTable.coffee
+++ SingularityUI/app/views/taskS3LogsTable.coffee
@@ -1,95 +0,0 @@
-View = require './view'
-
-class TaskS3LogsTableView extends View
-
-    initialize: ->
-        super
-        @options.count ?= 10
-
-    render: ->
-        @$el.html '

'
-
-
-        class TaskS3LogsPaginationView extends Teeble.PaginationView
-            template: '''
-                <div class="<%= pagination_class %>">
-

-

-                            <a href="#" class="pagination-previous previous <% if (prev_disabled){ %><%= pagination_disabled %><% } %>">
-
-                                Previous
-
-

-
-

-                            <a href="#" class="pagination-next next <% if(next_disabled){ %><%= pagination_disabled %><% } %>">
-                                Next
-
-
-

-

-

-            '''
-
-        @collection.pager
-            reset: true
-            success: =>
-                @tableView = new Teeble.TableView
-                    compile: Handlebars.compile
-                    collection: @collection
-                    pagination: true
-                    table_class: 'table teeble-table'
-                    subviews: $.extend {}, @subviews,
-                        pagination: TaskS3LogsPaginationView
-                    partials: [
-                        header: 'Log file	'
-                        cell: '{{ shortKey }}	'
-                    ,
-                        header: 'Size	'
-                        cell: '{{ sizeHuman }}	'
-                    ,
-                        header: 'Last modified	'
-                        cell: '{{ lastModifiedHuman }}	'
-                    ,
-                        header: '	'
-                        cell: """
-                                   Download
-                                 	"""
-                    ]
-
-                @tableView.setElement @el
-
-                @tableView.render()
-                @postTaskS3LogsTableViewRender()
-
-                @collection.on 'sync', =>
-                    @tableView.render()
-                    @postTaskS3LogsTableViewRender()
-
-        @
-
-    postTaskS3LogsTableViewRender: ->
-        @fixPagination()
-
-    fixPagination: ->
-        $empty = @$el.find('.teeble_empty')
-        if $empty.length
-            if not @tableEverNotEmpty
-                @$el.html('

No logs

')
-            else
-                $empty.html '''
Unfortunately, the API didn't tell us how many total logs there are, so we let you go one page too far. Please page back now. (Ask @wsorenson...) :)
'''
-        else
-            if @$el.find('tbody tr').length < @options.count
-                if @$el.find('.pagination-previous').hasClass('disabled')
-                    @$el.find('.pagination').hide()
-                else
-                    @$el.find('.pagination-next').addClass('disabled')
-            @tableEverNotEmpty = true
-
-module.exports = TaskS3LogsTableView