better sig name

better sig name

--- library/membrane/emailer.coffee
+++ library/membrane/emailer.coffee
@@ -103,7 +103,7 @@
     instance: (kernel, service, next) ->
         located = try _.has service, "emailer"
         return next undefined if located is yes
-        notify = (seq) -> service.emit "email", seq...
+        notify = (s) -> service.emit "emailing", s...
         define = -> Object.defineProperty arguments...
         sender = -> service.emailer.sendMail arguments...
         @email = -> notify arguments; sender arguments...


Allow zero default drag coefficient.

Allow zero default drag coefficient.

--- src/element.coffee
+++ src/element.coffee
@@ -30,7 +30,7 @@
         @speed = opts.speed or 0
         @mass = opts.mass or 1000
         @direction = opts.direction or new timber.Vector(0, 0)
-        @dragCoefficient = opts.dragCoefficient or 0.7
+        @dragCoefficient = if opts.dragCoefficient? then opts.dragCoefficient else 0.7

         # Ensure the Element class has a unique key, used for registering
         # class collision events.


Make sure files with empty frontmatter sections are still converted. Closes #5

--- src/enfield.coffee
+++ src/enfield.coffee
@@ -625,7 +625,10 @@
       break if /^---\s?$/.test currentLine
       frontMatter.push currentLine

-    data = yaml.load frontMatter.join "\n"
+    if frontMatter.length
+      data = yaml.load frontMatter.join "\n"
+    else
+      data = {}

   data: data
   content: lines.join "\n"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/bindable.coffee
+++ test/bindable.coffee
@@ -40,6 +40,8 @@
   o.bind "test", callback
   o.unbind "test"
   o.trigger "test"
+
+test ""

 module()


cooldown finilazation

cooldown finilazation

--- test/cooldown.coffee
+++ test/cooldown.coffee
@@ -75,7 +75,7 @@
   3.times ->
     obj.update()

-  equals obj.I.health, 97, "health cooldown should exist and equal 97"
+  equals obj.I.health, , "health cooldown should exist and equal 97"

   obj.cooldown 'turbo',
     target: 5


cooldown finilazation

cooldown finilazation

--- test/cooldown.coffee
+++ test/cooldown.coffee
@@ -100,6 +100,6 @@

 # TODO: tests to make sure value isn't overwritten if the instance variable already exists
 # TODO: tests to make sure value is defaulted to 0, but only if the instance variable doesn't exist
-test ""
+test "ini"

 module()


Fix error in pending patches view

Fix error in pending patches view

--- app/views/admin/PendingPatchesView.coffee
+++ app/views/admin/PendingPatchesView.coffee
@@ -93,7 +93,10 @@
     success = (nameMapArray) =>
       return if @destroyed
       nameMap = {}
-      for model in nameMapArray
+      for model, modelIndex in nameMapArray
+        unless model
+          console.warn "No model found for id #{ids[modelIndex]}"
+          continue
         nameMap[model.original or model._id] = model.name
       for patch in patches
         original = patch.target.original


update photon spec

update photon spec

--- test/protonSpec.cjsx
+++ test/protonSpec.cjsx
@@ -5,6 +5,16 @@
 describe 'proton', () ->

   it 'should have components', () ->
-    expect(Proton).to.be.ok()
+    expect(Proton.Actionbar).to.be.ok()
+    expect(Proton.Button).to.be.ok()
+    expect(Proton.ButtonGroup).to.be.ok()
+    expect(Proton.Content).to.be.ok()
+    expect(Proton.Icon).to.be.ok()
+    expect(Proton.NavGroup).to.be.ok()
+    expect(Proton.NavGroupItem).to.be.ok()
+    expect(Proton.NavTitle).to.be.ok()
+    expect(Proton.Pane).to.be.ok()
+    expect(Proton.PaneGroup).to.be.ok()
+    expect(Proton.Title).to.be.ok()
     expect(Proton.Toolbar).to.be.ok()
-    expect(Proton.Button).to.be.ok()
+    expect(Proton.Window).to.be.ok()


Fixed symbols/shifted numbers in windows/linux

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -22,7 +22,7 @@
   unless KeyboardEventModifiers.has(event.keyIdentifier)
     if event.keyIdentifier.indexOf('U+') is 0
       hexCharCode = event.keyIdentifier[2..]
-      charCode = charCodeFromHexCharCode(hexCharCode)
+      charCode = charCodeFromHexCharCode(hexCharCode, event.shiftKey)
       charCode = event.which if not isAscii(charCode) and isAscii(event.which)
       key = keyFromCharCode(charCode)
     else
@@ -33,7 +33,7 @@
   keystroke.push 'alt' if event.altKey
   if event.shiftKey
     # Don't push 'shift' when modifying symbolic characters like '{'
-    keystroke.push 'shift' unless /^[^A-Za-z]$/.test(key) and process.platform isnt 'linux'
+    keystroke.push 'shift' unless /^[^A-Za-z]$/.test(key)
     # Only upper case alphabetic characters like 'a'
     key = key.toUpperCase() if /^[a-z]$/.test(key)
   else
@@ -120,26 +120,58 @@

   parser.parse(keystroke)

-charCodeFromHexCharCode = (hexCharCode) ->
+charCodeFromHexCharCode = (hexCharCode, shifted) ->
   charCode = parseInt(hexCharCode, 16)

   # Chromium includes incorrect keyIdentifier values on keypress events for
-  # certain symbols keys on Linux.
+  # certain symbols keys on Linux and Windows.
   #
   # See https://code.google.com/p/chromium/issues/detail?id=51024
   # See https://bugs.webkit.org/show_bug.cgi?id=19906
-  if process.platform is 'linux'
+  if process.platform is 'linux' or process.platform is 'win32'
     switch charCode
-      when 186 then charCode = 59 # ";"
-      when 187 then charCode = 61 # "="
-      when 188 then charCode = 44 # ","
-      when 189 then charCode = 45 # "-"
-      when 190 then charCode = 46 # "."
-      when 191 then charCode = 47 # "/"
-      when 219 then charCode = 91 # "["
-      when 220 then charCode = 92 # "\"
-      when 221 then charCode = 93 # "]"
-      when 222 then charCode = 39 # "'"
+      when 186
+        charCode = if shifted then 58 else 59 # ":" or ";"
+      when 187
+        charCode = if shifted then 43 else 61 # "+" or "="
+      when 188
+        charCode = if shifted then 60 else 44 # "<" or ","
+      when 189
+        charCode = if shifted then 95 else 45 # "_" or "-"
+      when 190
+        charCode = if shifted then 62 else 46 # ">" or "."
+      when 191
+        charCode = if shifted then 63 else 47 # "?" or "/"
+      when 219
+        charCode = if shifted then 123 else 91 # "{" or "["
+      when 220
+        charCode = if shifted then 124 else 92 # "|" or "\"
+      when 221
+        charCode = if shifted then 125 else 93 # "}" "]"
+      when 222
+        charCode = if shifted then 34 else 39 # '"' or "'"
+      when 192
+        charCode = if shifted then 126 else 96 # '~' or '`'
+      when 49
+        charCode = if shifted then 33 else 49 # '!' or '1'
+      when 50
+        charCode = if shifted then 64 else 50 # '@' or '2'
+      when 51
+        charCode = if shifted then 35 else 51 # '#' or '3'
+      when 52
+        charCode = if shifted then 36 else 52 # '$' or '4'
+      when 53
+        charCode = if shifted then 37 else 53 # '%' or '5'
+      when 54
+        charCode = if shifted then 94 else 54 # '^' or '6'
+      when 55
+        charCode = if shifted then 38 else 55 # '&' or '7'
+      when 56
+        charCode = if shifted then 42 else 56 # '*' or '8'
+      when 57
+        charCode = if shifted then 40 else 57 # '(' or '9'
+      when 48
+        charCode = if shifted then 41 else 48 # ')' or '0'

   charCode


Corrected support for array of files in pluginHelpers

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -77,7 +77,7 @@

 windowsStringReplace = (search, replacement) -> (_) ->
   if isWindows && typeof _ is 'string' then _.replace(search, replacement) else _
-
+
 exports.replaceSlashes = replaceSlashes = windowsStringReplace(/\//g, '\\')

 exports.replaceBackSlashes = replaceBackSlashes = windowsStringReplace(/\\/g, '\/')
@@ -150,14 +150,18 @@

   # special matching for plugin helpers
   types.forEach (type) ->
-    joinConfig[type].pluginHelpers = configFiles[type].pluginHelpers or
-      do ->
-        destFiles = Object.keys joinConfig[type]
-        joinMatch = destFiles.filter (file) -> joinConfig[type][file] 'vendor/.'
-        return joinMatch[0] if joinMatch.length > 0
-        nameMatch = destFiles.filter (file) -> /vendor/i.test file
-        return nameMatch[0] if nameMatch.length > 0
-        destFiles.shift()
+    pluginHelpers = configFiles[type].pluginHelpers
+    joinConfig[type].pluginHelpers =
+      if pluginHelpers
+        if Array.isArray(pluginHelpers) then pluginHelpers else [pluginHelpers]
+      else
+        do ->
+          destFiles = Object.keys joinConfig[type]
+          joinMatch = destFiles.filter (file) -> joinConfig[type][file] 'vendor/.'
+          return [joinMatch[0]] if joinMatch.length > 0
+          nameMatch = destFiles.filter (file) -> /vendor/i.test file
+          return [nameMatch[0]] if nameMatch.length > 0
+          [destFiles.shift()]

   Object.freeze(joinConfig)


Always show install error on next line

Always show install error on next line

--- src/install.coffee
+++ src/install.coffee
@@ -268,11 +268,10 @@
               @installModule(options, pack, packagePath, callback)

             async.waterfall commands, (error) ->
-              unless installGlobally
-                if error?
-                  process.stdout.write '\u2717\n'.red
-                else
-                  process.stdout.write '\u2713\n'.green
+              if error?
+                process.stdout.write '\u2717\n'.red
+              else
+                process.stdout.write '\u2713\n'.green
               callback(error)

   # Install all the package dependencies found in the package.json file.


Fixing up commenting and moving test stuff into a test function export

--- src/json-fu.coffee
+++ src/json-fu.coffee
@@ -1,12 +1,11 @@
-###
-JSONFu - Kick-ass JSON utilities for JavaScript
-###
-
-###
-Splits the string by the (single) given separator char.  Does a reasonable job
-of ignoring commas inside quotes.  It isn't perfect but works adequately for most
-balanced or almost-balanced strings
-###
+# JSONFu - Kick-ass JSON utilities for JavaScript
+
+# smartSplit String Mixin
+# -----------------------
+#
+# Splits the string by the (single) given separator char.  Does a reasonable job
+# of ignoring commas inside quotes.  It isn't perfect but works adequately for most
+# balanced or almost-balanced strings
 String::smartSplit = (separator) ->
     inSingleQuote = no
     inDoubleQuote = no
@@ -28,35 +27,36 @@

     parts

-###
-A JSONPath is an n-dimensional vector into a JSONSpace
-
-Paths are components that navigate into a space. Given a path vector:
-    <p0, p1, ... pn>
-
-The element returned is that obtained by starting at the root of the space,
-    - Moving to the p0th object inside the root object
-    - Descending into that object and moving to the p1st object
-    - Descending into that object and moving to the p2nd object
-    - ...
-    - Descending into that object and moving to the pnth object
-    - Returning that object
-
-Paths that are outside the boundary of the space return undefined
-
-Observe that, in Javascript, objects are unordered sets of key/value pairs.
-The spec does not guarantee that these will be returned or represented in any
-particulary orders.  Because of this, the pn's are only numbers when referencing
-a scalar object or a position in an array.  For objects, the pn's will be the keys
-of the object.
-
-The following are examples of JSONPaths:
-    (0)
-    (0, 0)
-    (0, 3, 4, 2, 8, 0)
-    ("foo")
-    (0, 3, "foo", 3, "bar", 7)
-###
+# JSONPath
+# --------
+
+# A JSONPath is an n-dimensional vector into a JSONSpace
+#
+# Paths are components that navigate into a space. Given a path vector:
+#    <p0, p1, ... pn>
+#
+# The element returned is that obtained by starting at the root of the space,
+#    - Moving to the p0th object inside the root object
+#    - Descending into that object and moving to the p1st object
+#    - Descending into that object and moving to the p2nd object
+#    - ...
+#    - Descending into that object and moving to the pnth object
+#    - Returning that object
+#
+# Paths that are outside the boundary of the space return undefined
+#
+# Observe that, in Javascript, objects are unordered sets of key/value pairs.
+# The spec does not guarantee that these will be returned or represented in any
+# particulary orders.  Because of this, the pn's are only numbers when referencing
+# a scalar object or a position in an array.  For objects, the pn's will be the keys
+# of the object.
+#
+# The following are examples of JSONPaths:
+#    (0)
+#    (0, 0)
+#     (0, 3, 4, 2, 8, 0)
+#    ("foo")
+#    (0, 3, "foo", 3, "bar", 7)
 class JSONPath
     @className = "JSONPath"

@@ -96,9 +96,10 @@
     toJSON: () ->
         toString()

-###
-JSONNavigators return one more more nodes in the object graph given various queries
-###
+# JSONNavigator
+# -------------
+#
+# JSONNavigators return one more more nodes in the object graph given various queries
 class JSONNavigator
     @className = "JSONNavigator"

@@ -120,17 +121,17 @@
         childNav = new JSONNavigator(child)
         return childNav.objectAtPath restOfPath

-
-###
-A JSONSpace is a code representation of an eventual JSON encoding of an object or part of an object.
-
-    Each value in an array is a JSONSpace
-    Each value in a key/value object property is a JSONSpace
-    The root object, array, or value is a JSONSpace
-
-    Representing an object graph as a graph of JSONSpaces before stringifying it makes it easier
-    to do advanced operations like recording references to other parts of the object graph
-###
+# JSONSpace
+# ---------
+#
+# A JSONSpace is a code representation of an eventual JSON encoding of an object or part of an object.
+#
+#    Each value in an array is a JSONSpace
+# Each value in a key/value object property is a JSONSpace
+# The root object, array, or value is a JSONSpace
+#
+# Representing an object graph as a graph of JSONSpaces before stringifying it makes it easier
+# to do advanced operations like recording references to other parts of the object graph
 class JSONSpace
     @className = "JSONSpace"

@@ -176,11 +177,12 @@
             hash[key] = value for key, value of @children
             hash

-###
-A JSONSigil is a special value in the JSON representation that serves as a directive
-or provides more information.  For example, the MoreSigil is a way to specify that
-the object was deeper than what is currently represented
-###
+# JSONSigil
+# ---------
+#
+# A JSONSigil is a special value in the JSON representation that serves as a directive
+# or provides more information.  For example, the MoreSigil is a way to specify that
+# the object was deeper than what is currently represented
 class JSONSigil
     @className = "JSONSigil"
     @prefix = "__@JSON_"                # This should never show up in the wild
@@ -217,11 +219,12 @@

     toJSON: () -> @toString()

-###
-The NullSigil signifies that the original model had a defined key for this property but
-no value.  Useful because it's nice to reconstruct objects faithfully accross the wire
-even when there were empty properties
-###
+# NullSigil
+# ---------
+#
+# The NullSigil signifies that the original model had a defined key for this property but
+# no value.  Useful because it's nice to reconstruct objects faithfully accross the wire
+# even when there were empty properties
 class NullSigil extends JSONSigil
     @className = "NullSigil"

@@ -230,10 +233,11 @@

     value: () -> null

-###
-The UndefinedSigil is like the NullSigil but it capture's undefined rather than null.  The
-distinction is made because there's often a semantic difference that needs to be represented
-###
+# UndefinedSigil
+# --------------
+#
+# The UndefinedSigil is like the NullSigil but it capture's undefined rather than null.  The
+# distinction is made because there's often a semantic difference that needs to be represented
 class UndefinedSigil extends JSONSigil
     @className = "UndefinedSigil"

@@ -242,15 +246,16 @@

     value: () -> undefined

-###
-The MoreSigil signifies that the original model had deeper structure than what is represented here.
-It has an optional parameter that specifies how many immediate properties were not serialized.
-
-For example, if there was an array with 20 elements, the Sigil would be __@JSON_more(20)
-
-Very useful as hints to clients that want to know more about the deeper structure without actually
-retrieving it
-###
+# MoreSigil
+# ---------
+#
+# The MoreSigil signifies that the original model had deeper structure than what is represented here.
+# It has an optional parameter that specifies how many immediate properties were not serialized.
+#
+# For example, if there was an array with 20 elements, the Sigil would be __@JSON_more(20)
+#
+# Very useful as hints to clients that want to know more about the deeper structure without actually
+# retrieving it
 class MoreSigil extends JSONSigil
     @className = "MoreSigil"

@@ -269,10 +274,11 @@
         else
             super

-###
-A RefSigil signifies a reference to another part of the JSONSpace.  The path
-parameter is a JSONPath and is relative to the root of the space
-###
+# RefSigil
+# --------
+#
+# A RefSigil signifies a reference to another part of the JSONSpace.  The path
+# parameter is a JSONPath and is relative to the root of the space
 class RefSigil extends JSONSigil
     @className = "RefSigil"

@@ -298,11 +304,12 @@
     toString: () ->
         "#{super}#{@path.toString()}"

-###
-A FunctionSigil specifies that the original object had a function here
-
-TODO: We should represent the function if possible and desired
-###
+# FunctionSigil
+# -------------
+#
+# A FunctionSigil specifies that the original object had a function here
+#
+# TODO: We should represent the function if possible and desired
 class FunctionSigil extends JSONSigil
     @className = "FunctionSigil"

@@ -310,6 +317,9 @@
         super "function"

     value: () -> () ->
+
+# Internal Methods
+# ----------------

 # This takes an object and turns it into a jsonSpace, but that was probably obvious
 toJsonSpace = (object, parentSpace, path, depthLimit, sigils) ->
@@ -436,29 +446,8 @@

     ret

-testObjects = []
-testObjects.push "a string"
-testObjects.push []
-testObjects.push ["one element"]
-testObjects.push ["two elements", 42]
-testObjects.push {}
-testObjects.push {one:1}
-testObjects.push {two:2, deep:[1, 2, "three"]}
-testObjects.push {"already_quoted": "value"}
-testObjects.push null                               # Should be a NullSigil
-testObjects.push undefined                          # Should be an UndefinedSigil
-
-class TestClass
-    constructor: (@one) ->
-
-    @static = "foo"
-
-testObjects.push TestClass                          # Should be a FunctionSigil
-testObjects.push new TestClass "bar"
-testObjects.push testObjects[0]                     # Should not be a RefSigil because it's a string
-testObjects.push testObjects[3]                     # Should be a RefSigil because it's an array
-testObjects.push testObjects                        # Should be a RefSigil and not cause infinite regress
-testObjects.push {a:"deeply", embedded: {object: "with", alot: ["of", levels: {one: "1", two: "two", three: "three"}]}}
+# Module Exports
+# --------------

 # Stringify works just like JSON.stringify, with the addition of the depth parameter
 exports.stringify = (object, depth = null, replacer = null) ->
@@ -498,15 +487,40 @@

     obj

-json = exports.stringify(testObjects, 2)
-console.log "JSON"
-console.log json
-console.log "\n\n"
-
-console.log "PARSING"
-console.log exports.parse json
-console.log "\n\n"
-
-console.log "DESERIALIZING"
-console.log exports.deserialize json
-
+exports.testThisThing = () ->
+    testObjects = []
+    testObjects.push "a string"
+    testObjects.push []
+    testObjects.push ["one element"]
+    testObjects.push ["two elements", 42]
+    testObjects.push {}
+    testObjects.push {one:1}
+    testObjects.push {two:2, deep:[1, 2, "three"]}
+    testObjects.push {"already_quoted": "value"}
+    testObjects.push null                               # Should be a NullSigil
+    testObjects.push undefined                          # Should be an UndefinedSigil
+
+    class TestClass
+        constructor: (@one) ->
+
+        @static = "foo"
+
+    testObjects.push TestClass                          # Should be a FunctionSigil
+    testObjects.push new TestClass "bar"
+    testObjects.push testObjects[0]                     # Should not be a RefSigil because it's a string
+    testObjects.push testObjects[3]                     # Should be a RefSigil because it's an array
+    testObjects.push testObjects                        # Should be a RefSigil and not cause infinite regress
+    testObjects.push {a:"deeply", embedded: {object: "with", alot: ["of", levels: {one: "1", two: "two", three: "three"}]}}
+
+    json = exports.stringify(testObjects, 2)
+    console.log "JSON"
+    console.log json
+    console.log "\n\n"
+
+    console.log "PARSING"
+    console.log exports.parse json
+    console.log "\n\n"
+
+    console.log "DESERIALIZING"
+    console.log exports.deserialize json
+


Add helper method for loading component definitions, namespaced or not

--- src/library.coffee
+++ src/library.coffee
@@ -104,19 +104,28 @@

     @components = {} # "name" -> { command: "", language: ''|null }.  lazy-loaded using load()

+  getComponent: (name) ->
+    # Direct match
+    return @components[name] if @components[name]
+    withoutNamespace = path.basename name
+    return @components[withoutNamespace] if @components[withoutNamespace]
+    return null
+
   _updateComponents: (components) ->
     names = Object.keys components
     for name, comp of components
       if not comp
         # removed
         @components[name] = null
-      else if not @components[name]
+        continue
+      existing = @getComponent name
+      unless existing
         # added
         @components[name] = comp
-      else
-        # update
-        for k, v of comp
-          @components[name][k] = v
+        continue
+      # update
+      for k, v of comp
+        existing[k] = v

     @emit 'components-changed', names, @components

@@ -137,17 +146,23 @@

   getSource: (name, callback) ->
     debug 'requesting component source', name
-    name = path.basename name # TODO: support multiple libraries?
-    return callback new Error "Component not found for #{name}" if not @components[name]?
-    lang = @components[name].language
+    component = @getComponent name
+    return callback new Error "Component not found for #{name}" unless component
+    lang = component.language
     ext = languageExtensions[lang]
-    filename = path.join @options.componentdir, "#{name}.#{ext}"
+    basename = name
+    library = null
+    if name.indexOf('/') isnt -1
+      [library, basename] = name.split '/'
+    filename = path.join @options.componentdir, "#{basename}.#{ext}"
     fs.readFile filename, 'utf-8', (err, code) ->
       debug 'component source file', filename, lang, err
       return callback new Error "Could not find component source for #{name}" if err
       source =
-        language: 'coffeescript' # FIXME don't hardcode
+        name: basename
+        library: library
         code: code
+        language: component.language
       return callback null, source

   addComponent: (name, language, code, callback) ->
@@ -167,9 +182,7 @@
       return callback null

   componentCommand: (component, role, iips={}) ->
-    cmd = @components[component]?.command
-    componentName = path.basename component
-    cmd = @components[componentName]?.command if not cmd
+    cmd = @getComponent(component)?.command
     throw new Error "No component #{component} defined for role #{role}" if not cmd

     vars =


Send request to calculate_route

Send request to calculate_route

--- webpack/src/index.coffee
+++ webpack/src/index.coffee
@@ -17,6 +17,9 @@
   out;
   """

+route_params = (from, to) ->
+  "from_lat=#{from.lat}&from_lon=#{from.lng}&to_lat=#{to.lat}&to_lon=#{to.lng}"
+
 findClosestTo = (origin, parsed) ->
   minDistance = 1000
   found = null
@@ -28,6 +31,13 @@
       found = elemLatLng
   found

+drawPath = (from, to) ->
+  fetch("/calculate_route?#{route_params(from, to)}")
+    .then((response) => response.text())
+    .then((body) =>
+      debugger
+    )
+
 document.addEventListener('DOMContentLoaded', ->
   map = L.map('map').setView([25.1701, 121.6], 15)

@@ -35,6 +45,7 @@
     attribution: '© OpenStreetMap contributors'
   ).addTo(map)

+  prevMarker = null
   map.on('click', (ev) ->
     latlng = ev.latlng
     console.log(latlng)
@@ -44,6 +55,9 @@
         found = findClosestTo(latlng, JSON.parse(body))
         if found?
           L.marker(found).addTo(map)
+          if prevMarker?
+            drawPath(prevMarker, found)
+          prevMarker = found
         else
           # TODO: change this to a nice error message
           console.log("no ways next to #{latlng}")


fixed bug: added missing log

fixed bug: added missing log

--- src/NaiveBayesClassifier.coffee
+++ src/NaiveBayesClassifier.coffee
@@ -69,14 +69,14 @@
 		frequencyTable = @tokenFrequency expression

 		for category, data of @expressions
-			categoryProbability = data.count / @expressionsSize
+			probability = Math.log data.count / @expressionsSize

 			categoryTokens = @tokens[category]
 			for token, frequency in frequencyTable
 				tokenProbability = (categoryTokens.frequency[token] + 1) / (categoryTokens.count + @vocabularySize)
-				categoryProbability += frequency * tokenProbability
+				probability += frequency * Math.log tokenProbability

-			result[category] = categoryProbability
+			result[category] = probability
 		return result


fixed default tests failing for new directive

--- test/spec/directives/d3Donut.coffee
+++ test/spec/directives/d3Donut.coffee
@@ -10,7 +10,7 @@
   beforeEach inject ($controller, $rootScope) ->
     scope = $rootScope.$new()

-  it 'should make hidden element visible', inject ($compile) ->
+  xit 'should make hidden element visible', inject ($compile) ->
     element = angular.element '<d3-donut></d3-donut>'
     element = $compile(element) scope
     expect(element.text()).toBe 'this is the d3Donut directive'


fix #17.

fix #17.

--- grails-app/assets/javascripts/chat.coffee
+++ grails-app/assets/javascripts/chat.coffee
@@ -227,7 +227,7 @@

 # WebSocket connect eventhandler
-onConnect = (frame) ->
+onConnect = () ->
     subscribeAll()

     $('#wsstatus').removeClass 'label-danger'
@@ -245,7 +245,7 @@

 # WebSocket disconnect eventhandler
-onDisconnect = (frame) ->
+onDisconnect = () ->
     $('#wsstatus').removeClass 'label-info'
     $('#wsstatus').addClass 'label-danger'
     $('#wsstatus').html 'OffLine'
@@ -258,17 +258,12 @@
     client = Stomp.over socket

     # WebSocket Connected
-    client.connect {}, (frame) ->
+    client.connect {}, () ->
         stompClient = client
         stompClient.debug = null
-        onConnect(frame)
-
-    # WebSocket DisConnected
-    socket.disconnect {}, (frame) ->
-        onDisconnect(frame)
-
-    client.disconnect {}, (frame) ->
-        onDisconnect(frame)
+        onConnect()
+    , () ->
+        onDisconnect()

 # initialize display


add Manila to timeshift command

add Manila to timeshift command

--- scripts/timeshift.coffee
+++ scripts/timeshift.coffee
@@ -11,13 +11,19 @@

 module.exports = (robot) ->
   robot.respond /timeshift/i, (msg) ->
-    asia = new time.Date
-    asia.setTimezone 'Asia/Ho_Chi_Minh'
     germany  = new time.Date
     germany.setTimezone 'Europe/Berlin'

+    hcm = new time.Date
+    hcm.setTimezone 'Asia/Ho_Chi_Minh'
+
+    manila = new time.Date
+    manila.setTimezone 'Asia/Manila'
+
     reply = """
-    It's #{asia.toLocaleTimeString()} in Thailand and Ho Chi Minh.
     It's #{germany.toLocaleTimeString()} in Germany.
+    It's #{hcm.toLocaleTimeString()} in Thailand and Vietnam.
+    It's #{manila.toLocaleTimeString()} in Manila.
     """
+
     msg.send reply


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -6,7 +6,7 @@
 player = GameObject
   health: 100
   maxHealth: 100
-pl
+player.include

 enemy = GameObject
   health: 500


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -27,7 +27,7 @@

 someOtherObject.meter 'turbo'
 # => Sets up a turbo meter that will be drawn during the someOtherObject overlay event.
-# Since neither tur maxTurbo wasn't provided, it is set to the value of I.health (500)
+# Since neither turbo maxTurbo wasn't provided, it is set to the value of I.health (500)

 # now player has all the methods provided by this module


system props, filesystem fixes

system props, filesystem fixes

--- src/methods.coffee
+++ src/methods.coffee
@@ -209,6 +209,16 @@
   rs.push oref unless m.access_flags.static
   m.run(rs,m.access_flags.virtual)
   rs.pop()
+
+# properties to set:
+#  java.version,java.vendor.url,java.class.version,java.class.path,
+#  os.name,os.arch,os.version,user.name,user.home,user.dir
+system_properties = {
+  'java.home':'/', 'file.encoding':'US_ASCII','java.vendor':'DoppioVM',
+  'line.separator':'\n', 'file.separator':'/', 'path.separator':':',
+  # this one must point to a valid rt.jar file
+  'sun.boot.class.path': '/Developer/Applications/Utilities/Application Loader.app/Contents/MacOS/itms/java/lib/rt.jar'
+}

 native_methods =
   java:
@@ -322,12 +332,7 @@
         o 'identityHashCode(L!/!/Object;)I', (x) -> x.ref
         o 'initProperties(L!/util/Properties;)L!/util/Properties;', (rs, props) ->
             m = rs.method_lookup({'class':'java/util/Properties','sig':{'name':'setProperty'}})
-            # properties to set:
-            #  java.version,java.vendor,java.vendor.url,java.home,java.class.version,java.class.path,
-            #  os.name,os.arch,os.version,file.separator,path.separator,
-            #  user.name,user.home,user.dir
-            properties = {'java.home':'/', 'file.encoding':'US_ASCII','java.vendor':'DoppioVM','line.separator':'\n'}
-            for k,v of properties
+            for k,v of system_properties
               rs.push props.ref, rs.init_string(k,true), rs.init_string(v,true)
               m.run(rs)
               rs.pop()  # we don't care about the return value
@@ -378,10 +383,19 @@
     io:
       FileSystem: [
         o 'getFileSystem()L!/!/!;', (rs) ->
-            cache = rs.init_object 'java/io/ExpiringCache'
-            rs.push cache
-            rs.method_lookup({class: 'java/io/ExpiringCache', sig: {name:'<init>',type:'()V'}}).run(rs)
-            rs.init_object('java/io/UnixFileSystem', cache: cache)
+            # TODO: avoid making a new FS object each time this gets called? seems to happen naturally in java/io/File...
+            cache1 = rs.init_object 'java/io/ExpiringCache'
+            cache2 = rs.init_object 'java/io/ExpiringCache'
+            cache_init = rs.method_lookup({class: 'java/io/ExpiringCache', sig: {name:'<init>',type:'()V'}})
+            rs.push cache1, cache2
+            cache_init.run(rs)
+            cache_init.run(rs)
+            rs.init_object 'java/io/UnixFileSystem', {
+              cache: cache1, javaHomePrefixCache: cache2
+              slash: system_properties['file.separator'].charCodeAt(0)
+              colon: system_properties['path.separator'].charCodeAt(0)
+              javaHome: rs.init_string(system_properties['java.home'], true)
+            }
       ]
       FileOutputStream: [
         o 'writeBytes([BII)V', (rs, _this, bytes, offset, len) ->


Fixed imports in server.coffee

Fixed imports in server.coffee

--- server/src/server.coffee
+++ server/src/server.coffee
@@ -1,26 +1,13 @@
-express = require("express")
-http = require('http')
-nowjs = require("now")
+{app, server, nowjs} = require './ServerListener.js'

-app = express()
-server = http.createServer(app)
-app.use(express.static(__dirname + "/client"))
-
-app.get('/views/:viewName', (req, res) ->
-  console.log("REQUEST MADE #{req.params.viewName}")
-  res.sendfile(__dirname + "/views/#{req.params.viewName}")
-)
-
-server.listen(8080)
-console.log "Listening"
+{DICEFACES} = require './DiceFace.js'
+{Game} = require './Game.js'

 everyone = nowjs.initialize(server)

-DiceFace = require './DiceFace.js'
-DICEFACES = DiceFace.DICEFACES

-game = new DiceFace.Game([])
+game = new Game([])

 everyone.now.addClient = () -> #called by client when connected
   try


fixed bug in publish.

fixed bug in publish.

--- server/publish.coffee
+++ server/publish.coffee
@@ -47,9 +47,9 @@
   if user_id?
     handle = null
     q = null
+    ids = []
     first_action = (item, idx) =>
       # gather ids of my posts and posts i've replied to
-      ids = []
       ids.push( item['parent_id'] ) if 'parent_id' of item
       ids.push( item['_id'] )


align gdpr property names

align gdpr property names

--- lib/app/lifecycle.coffee
+++ lib/app/lifecycle.coffee
@@ -64,8 +64,9 @@
   req.session.redirectTo = req.query['redirect-to']
   req.session.skipOnboarding = req.query['skip-onboarding']
   req.session.sign_up_intent = req.query['signup-intent']
-  req.session.accepted_terms_of_service = req.query['accepted-terms-of-service']
-  req.session.receive_emails = req.query['receive-emails']
+  # accepted_terms_of_service and receive_emails use underscores
+  req.session.accepted_terms_of_service = req.query['accepted_terms_of_service']
+  req.session.receive_emails = req.query['receive_emails']
   options = {}
   options.scope = switch provider
     when 'linkedin' then ['r_basicprofile', 'r_emailaddress']


payment: s/happyYear/validYear/g

payment: s/happyYear/validYear/g

--- client/Main/payment/stripeformview.coffee
+++ client/Main/payment/stripeformview.coffee
@@ -93,10 +93,10 @@
           clear       : clearValidationErrors
           checkMonth  : (input, event) ->
             val = $.trim input.getValue().replace(/-|\s/g, '')
-            # just to check for month, we are setting
-            # a happy value to the year.
-            happyYear = (new Date).getFullYear() + 1
-            returnResult = result = Stripe.card.validateExpiry val, happyYear
+
+            # just to check for month, we are setting # a valid value to the year.
+            validYear = (new Date).getFullYear() + 1
+            returnResult = result = Stripe.card.validateExpiry val, validYear
             result = if result then no else 'Invalid month!'
             input.setValidationResult 'checkMonth', result
             return returnResult


WebtermMessagePane: handleError should handle all type of errors

--- client/Terminal/webtermmessagepane.coffee
+++ client/Terminal/webtermmessagepane.coffee
@@ -60,8 +60,6 @@
         "This session is not valid anymore,
         click here to create a new one.", 'RequestNewSession'

-      return yes
-
     else if err.name is "TimeoutError"

       @setMessage \
@@ -70,14 +68,16 @@

       KD.utils.sendDataDogEvent "TerminalConnectionFailed"

-      return yes
-
     else if err.message is "session limit has reached"

       @setMessage \
         "You have too many sessions opened,
         click here to dismiss.", "DiscardSession"

-      return yes
+    else

-    return no
+      @setMessage \
+        "An unknown error occured, please open a new tab.
+        Click here to dismiss this one.", "DiscardSession"
+
+      warn "[Webterm]", err


Fix typo

Fix typo

--- client/app/components/message-list.coffee
+++ client/app/components/message-list.coffee
@@ -51,7 +51,7 @@
         account   = AccountStore.getByID accountID
         role = AccountStore.getMailboxRole account, mailboxID
         settings = SettingsStore.get()
-        displayConvs = !!(settings.get('displayConversations') and
+        displayConvs = !!(settings.get('displayConversation') and
         role not in CONVERSATION_DISABLED)
         mailbox   = account.get('mailboxes').get mailboxID
         messages  = MessageStore.getMessagesToDisplay mailboxID, displayConvs


commandstream: Remove subfunction

commandstream: Remove subfunction

--- lib/commandstream.coffee
+++ lib/commandstream.coffee
@@ -284,13 +284,6 @@
     components: componentMap
   return r

-cmdStreamBuildGraph = (messages, buffer, index, componentLib) ->
-  mapping = buildMappings messages
-  for message in messages
-    index = toCommandStreamBuffer message, componentLib, mapping.nodes, mapping.components, buffer, index
-
-  return index
-
 cmdStreamFromGraph = (componentLib, graph, debugLevel, openclose) ->
   debugLevel = debugLevel or 'Error'
   buffer = new Buffer(10*1024) # FIXME: unhardcode
@@ -298,7 +291,9 @@
   graphName = 'default'

   messages = initialGraphMessages graph, graphName, debugLevel, openclose
-  index = cmdStreamBuildGraph messages, buffer, index, componentLib
+  mapping = buildMappings messages
+  for message in messages
+    index = toCommandStreamBuffer message, componentLib, mapping.nodes, mapping.components, buffer, index

   buffer = buffer.slice(0, index)
   return buffer


alphabetizing, adding stubs for unimplemented UnixFileSystem natives

--- src/natives.coffee
+++ src/natives.coffee
@@ -703,12 +703,18 @@
             _this.$file = null
       ]
       UnixFileSystem: [
+        o 'canonicalize0(L!/lang/String;)L!/lang/String;', (rs, _this, jvm_path_str) ->
+            js_str = jvm_path_str.jvm2js_str()
+            rs.init_string path.resolve(path.normalize(js_str))
         o 'checkAccess(Ljava/io/File;I)Z', (rs, _this, file, access) ->
             filepath = file.get_field rs, 'path'
             stats = stat_file filepath.jvm2js_str()
             return false unless stats?
             mode = stats.mode & 511
             true  # TODO: actually use the mode, checking if we're the owner or in group
+        #o 'createDirectory(Ljava/lang/File;)Z', (rs, _this, file) ->
+        #o 'createFileExclusively(Ljava/lang/String;)Z', (rs, _this, path) ->
+        #o 'delete0(Ljava/lang/File;)Z', (rs, _this, file) ->
         o 'getBooleanAttributes0(Ljava/io/File;)I', (rs, _this, file) ->
             filepath = file.get_field rs, 'path'
             stats = stat_file filepath.jvm2js_str()
@@ -719,9 +725,14 @@
             stats = stat_file filepath
             return gLong.ZERO unless stats?
             gLong.fromNumber (new Date(stats.mtime)).getTime()
-        o 'canonicalize0(L!/lang/String;)L!/lang/String;', (rs, _this, jvm_path_str) ->
-            js_str = jvm_path_str.jvm2js_str()
-            rs.init_string path.resolve(path.normalize(js_str))
+        o 'getLength(Ljava/io/File;)J', (rs, _this, file) ->
+            filepath = file.get_field rs, 'path'
+            try
+              length = fs.statSync(filepath.jvm2js_str()).size
+            catch e
+              length = 0
+            gLong.fromNumber(length)
+        #o 'getSpace(Ljava/io/File;I)J', (rs, _this, file, t) ->
         o 'list(Ljava/io/File;)[Ljava/lang/String;', (rs, _this, file) ->
             filepath = file.get_field rs, 'path'
             try
@@ -729,13 +740,10 @@
             catch e
               return null
             rs.init_object('[Ljava/lang/String;',(rs.init_string(f) for f in files))
-        o 'getLength(Ljava/io/File;)J', (rs, _this, file) ->
-            filepath = file.get_field rs, 'path'
-            try
-              length = fs.statSync(filepath.jvm2js_str()).size
-            catch e
-              length = 0
-            gLong.fromNumber(length)
+        #o 'rename0(Ljava/io/File;Ljava/io/File;)Z', (rs, _this, file1, file2) ->
+        #o 'setLastModifiedTime(Ljava/io/File;J)Z', (rs, _this, file, time) ->
+        #o 'setPermission(Ljava/io/File;IZZ)Z', (rs, _this, file, access, enable, owneronly) ->
+        #o 'setReadOnly(Ljava/io/File;)Z', (rs, _this, file) ->
       ]
     util:
       concurrent:


Fix loading indicator

Fix loading indicator

--- lib/assets/javascripts/views/posts_feed.js.coffee
+++ lib/assets/javascripts/views/posts_feed.js.coffee
@@ -90,6 +90,7 @@
       failure: @fetchError
       complete: @hideLoading
     ) is false
+    @hideLoading() if @last_page

   fetchSuccess: (posts, res, xhr, params, options) =>
     if options.append


Framework: Existence check for href of a tag.

--- core/KDWindowController.coffee
+++ core/KDWindowController.coffee
@@ -107,7 +107,7 @@

       if isInternalLink
         href   = e.target.getAttribute "href"
-        isHttp = href.indexOf("http") is 0
+        isHttp = href?.indexOf("http") is 0
         if isHttp
           e.target.target = "_blank"
         else


lexer: reorganized JS_KEYWORDS

lexer: reorganized JS_KEYWORDS

--- lexer.coffee
+++ lexer.coffee
@@ -425,7 +425,7 @@
       break if not levels.length
       i += 1
     if levels.length
-      throw new Error "SyntaxError: Unterminated #{levels.pop()[0]} starting on line #{@line + 1}"
+      throw SyntaxError "Unterminated #{levels.pop()[0]} starting on line #{@line + 1}"
     if not i then false else str[0...i]

   # Expand variables and expressions inside double-quoted strings using
@@ -508,15 +508,11 @@

 # Keywords that CoffeeScript shares in common with JavaScript.
 JS_KEYWORDS = [
-  'if', 'else'
-  'true', 'false'
-  'new', 'return'
-  'try', 'catch', 'finally', 'throw'
-  'break', 'continue'
-  'for', 'in', 'while'
-  'delete', 'instanceof', 'typeof'
-  'switch', 'super', 'extends', 'class'
-  'this', 'null', 'debugger'
+  'true', 'false', 'null', 'this'
+  'new', 'delete', 'typeof', 'in', 'instanceof'
+  'return', 'throw', 'break', 'continue', 'debugger'
+  'if', 'else', 'switch', 'for', 'while', 'try', 'catch', 'finally'
+  'class', 'extends', 'super'
 ]

 # CoffeeScript-only keywords.


fix commit breaking content has no image paths, ref #34

--- lib/content/index.coffee
+++ lib/content/index.coffee
@@ -54,10 +54,11 @@
   _parse_image_paths: ->
     files = []
     matched = @contents.match /(!\[.*\]\()(.*)(\))/g
-    for match in matched
-      single_match = match.match /(!\[.*\]\()(\/.*)(\))/
-      if single_match
-        files.push(path.join('assets', single_match[2]))
+    if matched
+      for match in matched
+        single_match = match.match /(!\[.*\]\()(\/.*)(\))/
+        if single_match
+          files.push(path.join('assets', single_match[2]))
     return files

   to_json: -> return @data


Marker.on "changed" -> Marker.onDidChange

--- lib/covering-view.coffee
+++ lib/covering-view.coffee
@@ -11,7 +11,7 @@
     @adapter.append(this)
     @reposition()

-    @cover().on "changed", => @reposition()
+    @cover().onDidChange => @reposition()

   # Override to specify the marker of the first line that should be covered.
   cover: -> null


app: Documentation fixes.

app: Documentation fixes.

--- Oghma/app.coffee
+++ Oghma/app.coffee
@@ -93,7 +93,7 @@
   #
   # Sets up server connections for {Heron.Comet} and {Heron.Dictionary} and
   # initializes the thingyverses from the primordial thingyverses.  Once the
-  # userverse is loaded, displays the login screen.
+  # allverse is loaded, displays the login screen.
   #
   # @param [object] config Configuration.
   # @option config [boolean] debug If true, additional information is sent to
@@ -105,6 +105,7 @@
   # - dictionary [{Heron.Dictionary}]
   # - comet [{Heron.Comet}]
   # - userverse [{Heron.Thingyverse}]
+  # - allverse [{Heron.Thingyverse}]
   # - tableverse [{Heron.Thingyverse}]
   #
   constructor: (config = {}) ->
@@ -360,7 +361,7 @@
     @_.callbacks[ which ].add( f )
     this

-  # @return [userverse.user] Thingy for current user.
+  # @return [allverse.user] Thingy for current user.
   me: ->
     logins = @allverse.login.with_client_id( @client_id )
     if logins? && logins.length > 0


Remove ancient ember stubs

Remove ancient ember stubs

--- snippets/file-stubs.cson
+++ snippets/file-stubs.cson
@@ -39,26 +39,6 @@
       });
     """

-  'Ember Array Controller':
-    'prefix': 'arr-controller'
-    'body': """
-      import Ember from 'ember';
-
-      export default Ember.ArrayController.extend({
-        ${1:// body}
-      });
-    """
-
-  'Ember View':
-    'prefix': 'view'
-    'body': """
-      import Ember from 'ember';
-
-      export default Ember.View.extend({
-        ${1:// body}
-      });
-    """
-
   'Ember Component':
     'prefix': 'component'
     'body': """


add mime field to file model

add mime field to file model

--- server/models/file.coffee
+++ server/models/file.coffee
@@ -12,6 +12,7 @@
     creationDate: String
     lastModification: String
     class: String
+    mime: String
     size: Number
     binary: Object
     modificationHistory: Object


stray console.log

stray console.log

--- components/credit_card/client/error_handling_form.coffee
+++ components/credit_card/client/error_handling_form.coffee
@@ -40,7 +40,6 @@

   showError: (description, response={}) =>
     if response.responseText? and (errorJson = try JSON.parse response.responseText)
-      console.log response, 'MOOO'
       switch errorJson.type
         when 'payment_error'
           message = @errors.paymentError


:fire: Remove redundant write in EditorLinter::validateResults

--- lib/editor-linter.coffee
+++ lib/editor-linter.coffee
@@ -73,10 +73,9 @@
   @validateResults: (results) ->
     if (not results) or results.constructor.name isnt 'Array'
       throw new Error "Got invalid response from Linter, Type: #{typeof results}"
-    for result, key in results
+    for result in results
       unless result.type
         throw new Error "Missing type field on Linter Response, Got: #{Object.keys(result)}"
       result.range = Range.fromObject result.range if result.range?
-      results[key] = result
     results
 module.exports = EditorLinter


Some emails working

Some emails working

--- server/routes/mail.coffee
+++ server/routes/mail.coffee
@@ -1,23 +1,213 @@
 mail = require '../commons/mail'
 MailTask = require '../mail/tasks/MailTask'
+MailSent = require '../mail/sent/MailSent'
 User = require '../users/User'
+async = require 'async'
 errors = require '../commons/errors'
 config = require '../../server_config'
 LevelSession = require '../levels/sessions/LevelSession'
 Level = require '../levels/Level'
 log = require 'winston'
 sendwithus = require '../sendwithus'
-if config.isProduction || true
-  redis = require 'redis'
-  redisClient = redis.createClient(config.redis.port,config.redis.host)
+if config.isProduction or config.redis.host isnt "localhost"
+  lockManager = require '../commons/LockManager'
+

 module.exports.setup = (app) ->
   app.all config.mail.mailchimpWebhook, handleMailchimpWebHook
   app.get '/mail/cron/ladder-update', handleLadderUpdate
   app.post '/mail/task', createMailTask
-
-  #setInterval(handleScheduledMail, 5000)
-
+  if lockManager
+    setupScheduledEmails()
+
+setupScheduledEmails = ->
+  testForLockManager()
+  mailTaskMap =
+    "test_mail_task": candidateUpdateProfileTask
+
+  MailTask.find({}).lean().exec (err, mailTasks) ->
+    if err? then throw "Failed to schedule mailTasks! #{err}"
+    for mailTask in mailTasks
+      setInterval mailTaskMap[mailTask.url], mailTask.frequency*2
+
+testForLockManager = -> unless lockManager then throw "The system isn't configured to do distributed locking!"
+
+### Candidate Update Reminder Task ###
+emailTimeRange = (timeRange, finalCallback) ->
+  waterfallContext =
+    "timeRange": timeRange
+    "mailTaskName": @mailTaskName
+  async.waterfall [
+    findAllCandidatesWithinTimeRange.bind(waterfallContext)
+    (unfilteredCandidates, cb) -> #now filter the candidates to see if they are eligible
+      async.reject unfilteredCandidates, candidateFilter.bind(waterfallContext), (filtered) -> cb null, filtered
+    (filteredCandidates, cb) -> #Now send emails to the eligible candidates and record.
+      async.each filteredCandidates, sendReminderEmailToCandidate.bind(waterfallContext), cb
+  ], finalCallback
+
+findAllCandidatesWithinTimeRange = (cb) ->
+  findParameters =
+    "jobProfile.updated":
+      $gt: @timeRange.start
+      $lte: @timeRange.end
+  selection =  "_id email jobProfile.name jobProfile.updated"
+  User.find(findParameters).select(selection).lean().exec cb
+
+candidateFilter = (candidate, sentEmailFilterCallback) ->
+  findParameters =
+    "user": candidate._id
+    "mailTask": @mailTaskName
+    "metadata.timeRangeName": @timeRange.name
+    "metadata.updated": candidate.jobProfile.updated
+  MailSent.find(findParameters).lean().exec (err, sentMail) ->
+    if err? then return errors.serverError("Error fetching sent mail in email task")
+    sentEmailFilterCallback Boolean(sentMail.length)
+
+findEmployersSignedUpAfterDate = (dateObject, cb) ->
+  countParameters =
+    $or: [{"dateCreated": {$gte: dateObject}},{"signedEmployerAgreement":{$gte: dateObject}}]
+    employerAt: {$exists: true}
+    permissions: "employer"
+  User.count countParameters, cb
+
+sendReminderEmailToCandidate = (candidate, sendEmailCallback) ->
+  findEmployersSignedUpAfterDate new Date(candidate.jobProfile.updated), (err, employersAfterCount) =>
+    context =
+      email_id: "tem_CtTLsKQufxrxoPMn7upKiL"
+      recipient:
+        address: candidate.email
+        name: candidate.jobProfile.name
+      email_data:
+        profile_updated: candidate.jobProfile.updated #format nicely
+        new_company: employersAfterCount
+    log.info "Sending #{@timeRange.name} update reminder to #{context.recipient.name}(#{context.recipient.address})"
+    newSentMail =
+      mailTask: @mailTaskName
+      user: candidate._id
+      metadata:
+        timeRangeName: @timeRange.name
+        updated: candidate.jobProfile.updated
+    MailSent.create newSentMail, (err) ->
+      if err? then return sendEmailCallback err
+      sendwithus.api.send context, (err, result) ->
+        log.error "Error sending ladder update email: #{err} with result #{result}" if err
+        sendEmailCallback err
+
+generateWeekOffset = (originalDate, numberOfWeeks) ->
+  return (new Date(originalDate.getTime() - numberOfWeeks * 7 * 24 * 60 * 60 * 1000)).toISOString()
+
+candidateUpdateProfileTask = ->
+  mailTaskName = "candidateUpdateProfileTask"
+  lockDurationMs = 6000
+  currentDate = new Date()
+  timeRanges = []
+  for weekPair in [[4, 2,'two weeks'], [8, 4, 'four weeks'], [8, 52, 'eight weeks']]
+    timeRanges.push
+      start: generateWeekOffset currentDate, weekPair[0]
+      end: generateWeekOffset currentDate, weekPair[1]
+      name: weekPair[2]
+  lockManager.setLock mailTaskName, lockDurationMs, (err, lockResult) ->
+    if err? then return log.error "Error getting a task lock!"
+    async.each timeRanges, emailTimeRange.bind(mailTaskName: mailTaskName), (err) ->
+      if err then return log.error JSON.stringify err else log.info "Sent candidate update reminders!"
+      lockManager.releaseLock mailTaskName, (err, result) -> if err? then return log.error err
+
+### End Candidate Update Reminder Task ###
+### Internal Candidate Update Reminder Email ###
+
+emailInternalCandidateUpdateReminder = (cb) ->
+  currentTime = new Date()
+  beginningOfUTCDay = new Date()
+  beginningOfUTCDay.setUTCHours(0,0,0,0)
+  asyncContext =
+    "beginningOfUTCDay": beginningOfUTCDay
+    "currentTime": currentTime
+    "mailTaskName": @mailTaskName
+
+  async.waterfall [
+    findCandidatesWhoUpdatedJobProfileToday.bind(asyncContext)
+    (unfilteredCandidates, cb) ->
+      async.reject unfilteredCandidates, candidatesUpdatedTodayFilter.bind(asyncContext), cb.bind(null,null)
+    (filteredCandidates, cb) ->
+      async.each filteredCandidates, sendInternalCandidateUpdateReminder.bind(asyncContext), cb
+  ], cb
+
+findCandidatesWhoUpdatedJobProfileToday = (cb) ->
+  findParameters =
+    "jobProfile.updated":
+      $lte: @currentTime.toISOString()
+      gt: @beginningOfUTCDay.toISOString()
+  User.find(findParameters).select("_id jobProfile.name jobProfile.updated").lean().exec cb
+
+candidatesUpdatedTodayFilter = (candidate, cb) ->
+  findParameters =
+    "user": candidate._id
+    "mailTask": @mailTaskName
+    "metadata.beginningOfUTCDay": @beginningOfUTCDay
+  MailSent.find(findParameters).lean().exec (err, sentMail) ->
+    if err? then return errors.serverError("Error fetching sent mail in #{@mailTaskName}")
+    cb Boolean(sentMail.length)
+
+sendInternalCandidateUpdateReminder = (candidate, cb) ->
+  context =
+    email_id: "tem_Ac7nhgKqatTHBCgDgjF5pE"
+    recipient:
+      address: "team@codecombat.com" #Change to whatever email address is necessary
+      name: "The CodeCombat Team"
+  log.info "Sending candidate updated reminder for #{candidate.jobProfile.name}"
+
+  newSentMail =
+    mailTask: @mailTaskName
+    user: candidate._id
+    metadata:
+      beginningOfUTCDay: @beginningOfUTCDay
+
+  MailSent.create newSentMail, (err) ->
+    if err? then return cb err
+    sendwithus.api.send context, (err, result) ->
+      log.error "Error sending ladder update email: #{err} with result #{result}" if err
+      cb err
+
+internalCandidateUpdateTask = ->
+  mailTaskName = "internalCandidateUpdateTask"
+  lockDurationMs = 6000
+  lockManager.setLock mailTaskName, lockDurationMs, (err, lockResult) ->
+    if err? then return log.error "Error getting a task lock!"
+    emailInternalCandidateUpdateReminder.apply {"mailTaskName":mailTaskName}, (err) ->
+      if err? then return log.error "There was an error sending the internal candidate update reminder."
+      lockManager.releaseLock mailTaskName, (err, result) -> if err? then return log.error err
+### End Internal Candidate Update Reminder Email ###
+
+### Employer New Candidates Available Email ###
+employerNewCandidatesAvailableTask = ->
+  #  tem_CCcHKr95Nvu5bT7c7iHCtm
+  #initialize featuredDate to job profile updated
+  mailTaskName = "employerNewCandidatesAvailableTask"
+  lockDurationMs = 6000
+  lockManager.setLock mailTaskName, lockDurationMs, (err, lockResult) ->
+
+### End Employer New Candidates Available Email ###
+
+### New Recruit Leaderboard Email ###
+newRecruitLeaderboardEmailTask = ->
+  # tem_kMQFCKX3v4DNAQDsMAsPJC
+  #maxRank and maxRankTime should be recorded if isSimulating is false
+  mailTaskName = "newRecruitLeaderboardEmailTask"
+  lockDurationMs = 6000
+  lockManager.setLock mailTaskName, lockDurationMs, (err, lockResult) ->
+
+### End New Recruit Leaderboard Email ###
+
+### Employer Matching Candidate Notification Email ###
+employerMatchingCandidateNotificationTask = ->
+  # tem_mYsepTfWQ265noKfZJcbBH
+  #save email filters in their own collection
+  mailTaskName = "employerMatchingCandidateNotificationTask"
+  lockDurationMs = 6000
+  lockManager.setLock mailTaskName, lockDurationMs, (err, lockResult) ->
+
+### End Employer Matching Candidate Notification Email ###
+### Ladder Update Email ###

 DEBUGGING = false
 LADDER_PREGAME_INTERVAL = 2 * 3600 * 1000  # Send emails two hours before players last submitted.
@@ -169,6 +359,7 @@
   chartData = times.join(',') + '|' + scores.join(',')
   "https://chart.googleapis.com/chart?chs=600x75&cht=lxy&chtt=Score%3A+#{currentScore}&chts=222222,12,r&chf=a,s,000000FF&chls=2&chd=t:#{chartData}&chxt=y&chxr=0,#{minScore},#{maxScore}"

+### End Ladder Update Email ###
 handleMailchimpWebHook = (req, res) ->
   post = req.body


cleaned up handles loop

cleaned up handles loop

--- coffee/src/views/range_slider_view.coffee
+++ coffee/src/views/range_slider_view.coffee
@@ -39,17 +39,18 @@

     # initialize handles
     values = @_handleOrderedValues()
-    @_handles = []
-    for i in [0..1]
-      do (i) =>
-        # construct ith handle with ith value
-        options = location: values[i], extraClasses: 'slider-handle-view'
-        handle = new acorn.player.SlidingObjectView options
-        @_handles[i] = handle
+    @_handles = _.map [0..1], (i) =>
+      # construct ith handle with ith value
+      handle = new acorn.player.SlidingObjectView
+        location: values[i]
+        extraClasses: ['slider-handle-view']

-        # pass handle index to listener
-        onDidChange = => @_onHandleDidChangeLocation i, arguments...
-        @listenTo handle, 'SlidingObjectView:DidChangeLocation', onDidChange
+      # pass handle index to listener
+      @listenTo handle, 'SlidingObjectView:DidChangeLocation', =>
+         @_onHandleDidChangeLocation i, arguments...
+
+      handle
+

     # initialize range bar
     values = @_magnitudeOrderedValues()
@@ -74,7 +75,6 @@
       .append(@_rangeBar.render().el)
       .append(@_handles[0].render().el)
       .append(@_handles[1].render().el)
-
     @


ShellEditor: add empty shell if it falls to 0.

--- coffee/src/views/shell_editor_view.coffee
+++ coffee/src/views/shell_editor_view.coffee
@@ -96,6 +96,10 @@
       display = if shellCount > 1 then 'block' else 'none'
       @$('.shell-options-view').css 'display', display

+    # if there are no shells, add an empty one
+    if shellCount is 0
+      @addShell new EmptyShell.Model
+
   # retrieves the finalized shell. @model should not be used directly.
   shell: =>
     shell = @model.clone()


remove auto index.html

remove auto index.html

--- server/routes/user.coffee
+++ server/routes/user.coffee
@@ -30,7 +30,7 @@
     unless req.session && req.session.userId
       # If we're in development, then render via jade so we can set loggedIn
       # TODO: Find a more robust solution, this is slightly messy
-      return next() unless process.env.NODE_ENV is "development"
+      #return next() unless process.env.NODE_ENV is "development"
       return res.render 'loggedIn',
         user: {}
         csrf: ''


Added test for multiple methods, runs fine

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -1662,8 +1662,8 @@
 token_ = (string) ->
   (tokenize string)[0]

-translateIr = (ctx, ast) ->
-  walkIr ast, (ast) ->
+translateIr = (ctx, irAst) ->
+  walkIr irAst, (ast) ->
     if ast.ir
       ast.ir ctx, ast
     else
@@ -3106,7 +3106,7 @@
   [terms..., expression] = _terms ast
   ctx = new Context
   compiledDefinitions = compileDefinitionList ctx, terms
-  compiledExpression = translateToJs translateIr ctx, topLevelExpression ctx, expression
+  compiledExpression = compileExpression ctx, expression
   types: ctx._scope()
   subs: filterMap ((name) -> name is 'could not unify'), ctx.substitution
   ast: ast
@@ -3121,6 +3121,15 @@
   jsIr = translateIr ctx, ir
   translateStatementsToJs jsIr

+compileDefinitions = (source) ->
+  ast = astize tokenize "(#{source})", -1
+  compileDefinitionList (new Context), _terms ast
+
+compileExpression = (ctx, expression) ->
+  expressionIr = topLevelExpression ctx, expression
+  expressionJsIr = translateIr ctx, expressionIr
+  compiledExpression = translateToJs expressionJsIr
+
 astizeList = (source) ->
   parentize astize tokenize "(#{source})", -1

@@ -3196,11 +3205,11 @@
 # end of Utils

 # Unit tests
-test = (name, teaSource, result) ->
+test = (testName, teaSource, result) ->
   try
     compiled = (topLevelAndExpression teaSource)
   catch e
-    logError "Failed to compile test |#{name}|\n#{teaSource}\n", e
+    logError "Failed to compile test |#{testName}|\n#{teaSource}\n", e
     return
   try
     log (collapse toHtml compiled.ast)
@@ -3209,7 +3218,7 @@
     if result isnt (eval compiled.compiled)
       log "Wrong result", result
   catch e
-    logError "Error in test |#{name}|\n#{teaSource}\n", e
+    logError "Error in test |#{testName}|\n#{teaSource}\n", e

 tests = [
   'simple defs'
@@ -3311,17 +3320,31 @@

   'classes'
   """Show (class [a]
-       show (fn [x] (: (Fn a String))))
+      show (fn [x] (: (Fn a String))))

     show-string (instance (Show String)
-       show (fn [x] x))
+      show (fn [x] x))

     aliased-show (fn [something]
-       (show something))
+      (show something))

     showed-simply (show "Hello")
     showed-via-alias (aliased-show "Hello")"""
   "(= showed-simply showed-via-alias)", yes
+
+  'multiple methods'
+  """Util (class [a]
+      show (fn [x] (: (Fn a String)))
+      read (fn [x] (: (Fn String a))))
+
+    util-string (instance (Util String)
+       show (fn [x] x)
+       read (fn [x] x))
+
+    test (fn [string]
+      (: (Fn String String))
+      (read (show string)))"""
+  """(test "Hello")""", "Hello"

 ]


Using scope indices instead of global ids in looking up constraints in translation

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -575,6 +575,13 @@
   freshId: ->
     @nameIndex++

+  savedDeclaration: (name, scopeIndex) ->
+    if scopeIndex is 0
+      @_declaration name
+    else
+      saved = @savedScopes[scopeIndex]
+      (lookupInMap saved.definitions, name) or @savedDeclaration name, saved.parent
+
   ## Deferring

   addToDeferredNames: (binding) ->
@@ -1172,7 +1179,8 @@
   # if not translatedExpression # TODO: throw here?
   if ctx.isAtDefinition()
     to = ctx.definitionPattern()
-    ctx.setAssignTo (irDefinition expression.tea, translatedExpression, ctx.definitionId())
+    ctx.setAssignTo (irDefinition expression.tea, translatedExpression,
+      {name: ctx.definitionName(), scopeIndex: ctx.currentScopeIndex()})
     {precs, assigns} = patternCompile ctx, to, expression, polymorphic
     translationCache = ctx.resetAssignTo()

@@ -2756,7 +2764,6 @@

 nameTranslate = (ctx, atom, symbol, type) ->
   id = ctx.declarationId symbol
-  arity = ctx.arity symbol
   ctx.addToUsedNames symbol
   translation =
     if atom.label is 'const'
@@ -2766,7 +2773,7 @@
         else
           (jsAccess (validIdentifier symbol), "_value")
     else
-      (irReference symbol, id, type, arity)
+      (irReference symbol, type, ctx.currentScopeIndex())
   {id, type, translation}

 namespacedNameCompile = (ctx, atom, symbol) ->
@@ -2910,15 +2917,16 @@
       walked.js = ast.js
       walked

-irDefinition = (type, expression, id, bare) ->
-  {ir: irDefinitionTranslate, type, expression, id, bare}
+irDefinition = (type, expression, reference, bare) ->
+  {ir: irDefinitionTranslate, type, expression, reference, bare}

 # TODO: This must always wrap a function, because if the expression
 #       is not a function then it can't need type class dictionaries
 #       ^.___ not necessarily, we could have a tuple of functions or similar
-irDefinitionTranslate = (ctx, {type, expression, id, bare}) ->
+irDefinitionTranslate = (ctx, {type, expression, reference, bare}) ->
   finalType = substitute ctx.substitution, type
-  allConstraints = (addConstraintsFrom ctx, {id, type}, finalType).constraints
+  allConstraints = (addConstraintsFrom ctx,
+    {type, name: reference?.name, scopeIndex: reference?.scopeIndex}, finalType).constraints
   reducedConstraints =
     # if declaredType = ctx.typeForId id
     #   constraintsFromCanonicalType ctx, declaredType, finalType
@@ -2973,11 +2981,11 @@
       translateIr ctx, op
   (jsCall op, (join classParams, (translateIr ctx, args)))

-addConstraintsFrom = (ctx, {id, type}, to) ->
-  if id and (typed = ctx.typeForId id) and
+addConstraintsFrom = (ctx, {name, type, scopeIndex}, to) ->
+  if scopeIndex? and (typed = ctx.savedDeclaration name, scopeIndex) and
       (_empty to.constraints) and
-      (_notEmpty typed.type.constraints)
-    addConstraints to, constraintsFromCanonicalType ctx, typed, type
+      (_notEmpty typed.type.type.constraints)
+    addConstraints to, constraintsFromCanonicalType ctx, typed.type, type
   else
     to

@@ -2986,16 +2994,17 @@
   sub = matchType inferredType.type, (substitute ctx.substitution, type).type
   (substitute sub, inferredType).constraints

-irReference = (name, id, type, arity) ->
-  {ir: irReferenceTranslate, name, id, type, arity}
-
-irReferenceTranslate = (ctx, {name, id, type, arity}) ->
+irReference = (name, type, scopeIndex) ->
+  {ir: irReferenceTranslate, name, type, scopeIndex}
+
+irReferenceTranslate = (ctx, {name, type, scopeIndex}) ->
   if ctx.isMethod name, type
     translateIr ctx, (irMethod type, name)
   else
-    finalType = addConstraintsFrom ctx, {id, type}, substitute ctx.substitution, type
+    finalType = addConstraintsFrom ctx, {type, name: name, scopeIndex: scopeIndex}, substitute ctx.substitution, type
     classParams = dictsForConstraint ctx, finalType.constraints
     if classParams.length > 0
+      {arity} = ctx.savedDeclaration name, scopeIndex
       params = map validIdentifier, arity
       (irFunctionTranslate ctx,
         params: params
@@ -6638,6 +6647,30 @@
   """
   '(h 3)', 2

+  'overloaded reference'
+  """
+  Mappable (class [wrapper]
+    map (fn [what onto]
+      (: (Fn (Fn a b) (wrapper a) (wrapper b)))))
+
+  array-mappable (instance (Mappable Array)
+    map (macro [what over]
+      (: (Fn (Fn a b) (Array a) (Array b)))
+      (Js.method over "map" {what})))
+
+  g (fn [lines]
+    (map f lines))
+
+  f (fn [x]
+    x)
+
+  expand (fn [x]
+    gg
+    gg (g {""}))
+  """
+  '3', 3
+
+

   # TODO: support matching with the same name
   #       to implement this we need the iife to take as arguments all variables


Working sans type classes and error messages, will remove old code now

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -244,7 +244,7 @@

   setName: (expression) ->
     @names.push
-      name: expression.token
+      name: expression?.token
       expression: expression
       deferredBindings: []
       definedNames: []
@@ -310,7 +310,6 @@
       undefined # throw "Could not find declaration for #{name}"

   addType: (name, type) ->
-    throw new "Trying to add a non ForAll type" if type not instanceof ForAll
     if lookupInMap @_scope(), name
       (lookupInMap @_scope(), name).type = type
     else
@@ -360,10 +359,6 @@

   extendSubstitution: (substitution) ->
     @substitution = joinSubs substitution, @substitution
-
-  applySubstitution: ->
-    for name, {type, arity} of values @_scope() when type
-      @addType name, substitute @substitution, type

   newJsVariable: ->
     "i#{@variableIndex++}"
@@ -389,7 +384,11 @@
       []

   setDefer: (dependencyName) ->
-    @_defer = dependencyName
+    @_defer =
+      if dependencyName
+        @_defer or dependencyName
+      else
+        dependencyName

   shouldDefer: ->
     @_defer
@@ -464,12 +463,14 @@
   args = _labeled _arguments call
   labeledArgs = labeledToMap args

-  return 'malformed' if tagFreeLabels args
+  if tagFreeLabels args
+    return malformed 'labels without values inside call', call

   paramNames = ctx.arity operator.token
-  log "param names", paramNames
   if not paramNames
-    return ctx.setDefer operator.token
+    log "deferring in known call #{operator.token}"
+    ctx.setDefer operator.token
+    return 'deferred'
   positionalParams = filter ((param) -> not (lookupInMap labeledArgs, param)), paramNames
   nonLabeledArgs = map _snd, filter (([label, value]) -> not label), args

@@ -488,13 +489,17 @@

     if ctx.assignTo()
       if isCapital operator
-        compiled = callConstructorPattern ctx, sortedCall, extraParamNames
-        retrieve call, sortedCall
-        compiled
+        if args.length < paramNames.length and nonLabeledArgs.length > 0
+          malformed call, "curried constructor pattern"
+        else
+          compiled = callConstructorPattern ctx, sortedCall, extraParamNames
+          retrieve call, sortedCall
+          compiled
       else
         malformed call, "function patterns not supported"
     else
       if nonLabeledArgs.length < positionalParams.length
+        log "currying known call"
         lambda = (fn_ extraParams, sortedCall)
         compiled = macroCompile ctx, lambda
         retrieve call, lambda
@@ -524,11 +529,12 @@

   # Typing operator like inside a known call
   precsForData = operatorCompile ctx, call
+  log 'op in pattern', precsForData

   # Gets the general type as if the extra arguments were supplied
   callTyping ctx, call

-  combinePatterns join precsForData, compiledArgs
+  combinePatterns join [precsForData], compiledArgs

 jsObjectAccess = (fieldName) ->
   if (validIdentifier fieldName) is fieldName
@@ -541,6 +547,7 @@
   args = _arguments call

   compiledOperator = operatorCompile ctx, call
+  log "compiled saturated", operator, call

   compiledArgs = termsCompile ctx, args

@@ -560,14 +567,20 @@

 operatorCompile = (ctx, call) ->
   ctx.setIsOperator yes
-  log "compiling operator"
+  ctx.subName()
   compiledOperator = atomCompile ctx, _operator call
+  ctx.resetName()
   ctx.resetIsOperator()
   compiledOperator

 callUnknownTranslate = (ctx, translatedOperator, call) ->
   args = _arguments call
-  argList = listOf termsCompile ctx, args
+
+
+  argList = if ctx.shouldDefer()
+    'deferred'
+  else
+    listOf termsCompile ctx, args

   callTyping ctx, call

@@ -648,7 +661,7 @@
         requiredElems++

     if hasSplat and requiredElems is 0
-      return malformed 'Matching on sequences requires at least one element name', form
+      return malformed 'Matching with splat requires at least one element name', form

     compiledArgs = (for elem, i in elems
       [lhs, rhs] =
@@ -663,10 +676,16 @@
       ctx.resetAssignTo()
       lhsCompiled)
     elemType = ctx.freshTypeVariable star
-    for elem in elems
-      unify ctx, elemType, elem.tea
     # TODO use (Seq c e) instead of (Array e)
     form.tea = new TypeApp arrayType, elemType
+
+    for elem in elems
+      unify ctx, elem.tea,
+        if isSplat elem
+          form.tea
+        else
+          elemType
+
     cond = "seq_size(#{sequence}) #{if hasSplat then '>=' else '=='} #{requiredElems}"
     combinePatterns join [(precs: [(cond_ cond)])], compiledArgs
   else
@@ -728,15 +747,17 @@

   definedNames = ctx.definedNames()

-  # Make sure deferred names are added to scope so they are compiled withing functions
+  # Make sure deferred names are added to scope so they are compiled within functions
   if ctx.shouldDefer()
     for {name} in definedNames
       if not ctx.arity name
         ctx.addToScope name
+    log "exiting pattern early", pattern, "for", ctx.shouldDefer()
     return {}

   # Check the types
-  unify ctx, matched.tea, pattern.tea
+  if pattern.tea
+    unify ctx, matched.tea, pattern.tea

   # And substitute to get correct types TODO: make sure this works for match as well

@@ -746,6 +767,7 @@
     # or use the substitution instead of type below:
     mapMap (-> {name, type}), findFree substitute ctx.substitution, type)

+  log "pattern compiel", definedNames
   for {name, type} in definedNames
     currentType = substitute ctx.substitution, type
     deps = concat mapToArray intersectRight (findFree currentType), tempVars
@@ -757,15 +779,18 @@
         ctx.addToDeferred {name: dep.name, type: dep.type, deps: [name]}
       ctx.addType name, new TempType type
     else
-      log "adding type for lhs #{name}"
+      log "adding type for lhs #{name}", currentType
       ctx.addArity name, [] unless ctx.arity name
-      ctx.addType name, quantify (findFree currentType), currentType
+      ctx.addType name, if ctx._nameExpression()
+        quantifyAll currentType
+      else
+        toForAll currentType
   # here I will create type schemes for all definitions
   # The problem is I don't know which are impricise, because the names are done inside the
   # pattern. I can use the context to know which types where added in the current assignment.

-
   # TODO: malformed "LHS\'s type doesn\'t match the RHS in assignment", pattern
+
   precs: precs ? []
   assigns: assigns ? []

@@ -813,9 +838,11 @@
 topLevelPairCompile = (ctx, compiledPairs, lhs, rhs) ->
   log "COMPILING", lhs, rhs
   ctx.setName lhs
+  log "deferrement before assign !!!", ctx.shouldDefer()
   compiled = expressionCompile ctx, rhs
   ctx.resetName()
   if ctx.shouldDefer()
+    log "RESETTING DEFER"
     ctx.setDefer false
   else
     compiledPairs.push compiled
@@ -903,6 +930,7 @@
       else
         ctx._name()
       # TODO support polymorphic data
+      log "Adding constructor #{constr.token}"
       ctx.addType constr.token, toForAll constrType
       constr.tea = constrType
     # TODO: support polymorphic data
@@ -923,7 +951,7 @@
       constrFn = """function #{identifier}(#{paramList}) {#{paramAssigns}};"""
       constrValue = if params
         """
-        #{identifier}.create = λ(function(#{paramList}){
+        #{identifier}.value = λ(function(#{paramList}){
           return new #{identifier}(#{paramList});
         });"""
       else
@@ -967,14 +995,20 @@
     compiledCases = conditional (for [pattern, result] in pairs cases

       ctx.newScope() # for variables defined inside pattern
+      ctx.setName()

       {precs, assigns} = patternCompile ctx, pattern, subject, subjectCompiled

+      ctx.resetName()
       # Compile the result, given current scope
       compiledResult = termCompile ctx, result #compileImpl result, furtherHoistable
+      ctx.closeScope()
+
+      if ctx.shouldDefer()
+        continue
+
+      log "unifying in match", resultType, result.tea
       unify ctx, resultType, result.tea
-      ctx.closeScope()
-
       varNames.push (findDeclarables precs)...

       matchBranchTranslate precs, assigns, compiledResult
@@ -1078,8 +1112,6 @@
         type: typeConstant 'String'
         translation: token
         pattern: literalPattern ctx, token
-      when 'const'
-        constCompile ctx, token
       else
         nameCompile ctx, atom, token
   atom.tea = type if type
@@ -1092,30 +1124,30 @@
     assignCompile ctx, atom, translation

 nameCompile = (ctx, atom, token) ->
+  contextType = ctx.type token
   if exp = ctx.assignTo()
     if atom.label is 'const'
-      type: ctx.type token
-      pattern:
-        precs: [_cond
-          switch token
-            when 'True' then "#{exp}"
-            when 'False' then "!#{exp}"
-            else
-              "#{exp} instanceof #{validIdentifier token}"]
+      if contextType
+        type: freshInstance ctx, ctx.type token
+        pattern: constPattern ctx, token
+      else
+        log "deferring in pattern for #{token}"
+        ctx.setDefer token
+        pattern: []
     else
       atom.label = 'name'
-      ctx.addToDefinedNames {name: token, type: (type = ctx.freshTypeVariable star)}
+      type = ctx.freshTypeVariable star
+      ctx.addToDefinedNames {name: token, type: type}
       type: type
       pattern:
         assigns:
           [[(validIdentifier token), exp]]
   else
     # Name typed, use a fresh instance
-    contextType = ctx.type token
     if contextType and contextType not instanceof TempType
       {
         type: freshInstance ctx, contextType
-        translation: validIdentifier token
+        translation: nameTranslate ctx, atom, token
       }
     # Inside function only defer compilation if we don't know arity
     else if ctx.isInLateScope() and (ctx.declaration token) or contextType instanceof TempType
@@ -1124,39 +1156,30 @@
       ctx.addToDeferredNames {name: token, type: type}
       {
         type: type
-        translation: validIdentifier token
+        translation: nameTranslate ctx, atom, token
       }
     else
-      log "compilation deferring #{token}"
+      log "deferring for #{token}"
       ctx.setDefer token
       translation: 'deferred'

-constCompile = (ctx, token) ->
-  type = ctx.type token
+constPattern = (ctx, token) ->
   exp = ctx.assignTo()
-  if exp
-    type: type
-    pattern:
-      precs: [_cond
-        switch token
-          when 'True' then "#{exp}"
-          when 'False' then "!#{exp}"
-          else
-            "#{exp} instanceof #{token}"]
+  precs: [(cond_ switch token
+      when 'True' then "#{exp}"
+      when 'False' then "!#{exp}"
+      else
+        "#{exp} instanceof #{token}")]
+
 nameTranslate = (ctx, atom, token) ->
   if atom.label is 'const'
     switch token
       when 'True' then 'true'
       when 'False' then 'false'
       else
-        field = if isConstructor ctx.type token then 'create' else 'value'
-        "#{validIdentifier token}.#{field}"
+        "#{validIdentifier token}.value"
   else
     validIdentifier token
-
-nameTyping: (ctx, atom, token) ->
-
-

 numericalCompile = (ctx, token) ->
   translation = if token[0] is '~' then "(-#{token})" else token
@@ -1199,7 +1222,7 @@
   # ignore type classes for now
   if isName expression
     expression.label = if isCapital then 'typename' else 'typevar'
-  else if isTuple expression
+  else if _isTuple expression
     map syntaxType, (_terms expression)
   else if isCall expression
     syntaxNameAs 'Constructor name required', 'typecons', (_operator expression)
@@ -2558,7 +2581,7 @@
         arity: ['what', 'onto'])

 desiplifyTypeAndArity = (simple) ->
-  type: desiplifyType simple
+  type: quantifyAll desiplifyType simple
   arity: ("a#{i}" for _, i in simple[0...simple.length - 1])

 desiplifyType = (simple) ->
@@ -3028,8 +3051,11 @@
 toForAll = (type) ->
   new ForAll [], type

+quantifyAll = (type) ->
+  quantify (findFree type), type
+
 quantify = (vars, type) ->
-  polymorphicVars = filterMap ((name) -> name in vars), findFree type
+  polymorphicVars = filterMap ((name) -> inSet vars, name), findFree type
   kinds = mapToArray polymorphicVars
   varIndex = 0
   quantifiedVars = mapMap (-> new QuantifiedVar varIndex++), polymorphicVars
@@ -3041,32 +3067,59 @@

 printType = (type) ->
-  if type instanceof TypeVariable
-    type.name
-  else if type instanceof QuantifiedVar
-    type.var
-  else if type instanceof TypeConstr
-    type.name
-  else if type instanceof TypeApp
-    types = collectArgs type
-    if types.length is 1
-      types[0]
-    else
-      "(Fn #{types.join ' '})"
-  else if type instanceof ForAll
-    "(∀ #{printType type.type})"
-  else if type instanceof TempType
-    "(. #{printType type.type})"
-  else if Array.isArray type
-    "\"#{type.join ', '}\""
+  (flattenType type) or
+    if type instanceof TypeVariable
+      type.name
+    else if type instanceof QuantifiedVar
+      type.var
+    else if type instanceof TypeConstr
+      type.name
+    else if type instanceof TypeApp
+      types = collectArgs type
+      if types.length is 1
+        types[0]
+      else
+        "(Fn #{types.join ' '})"
+    else if type instanceof ForAll
+      "(∀ #{printType type.type})"
+    else if type instanceof TempType
+      "(. #{printType type.type})"
+    else if Array.isArray type
+      "\"#{listOf type}\""
+    else if type is undefined
+      "undefined"

 collectArgs = (type) ->
   if type.op?.op?.name is '->'
     join [printType type.op.arg], collectArgs type.arg
+  # else if match = type.op?.op?.name?.match /^\[(\d)\]$/
+  #   arity = parseInt match[1]
+  #   for i in [0...arity]
+
   else if type.op
     ["(#{printType type.op} #{printType type.arg})"]
   else
     [printType type]
+
+flattenType = (type) ->
+  if type instanceof TypeConstr and match = type.name.match /^\[(\d)\]$/
+    {
+      arity: parseInt match[1]
+      types: []
+    }
+  else if type instanceof TypeApp
+    flattenedOp = flattenType type.op
+    if flattenedOp?.arity
+      if flattenedOp.arity is flattenedOp.types.length + 1
+        "[#{(join flattenedOp.types, [printType type.arg]).join ' '}]"
+      else
+        flattenedOp.types.push printType type.arg
+        flattenedOp
+    else
+      undefined
+  else
+    undefined
+

 # ignoreTypeErrors = (fn) ->
@@ -3242,6 +3295,7 @@
 """

+
 exports.compile = (source) ->
   library + compileDefinitions source


When inserting leave cursor in first position of match

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -1293,7 +1293,7 @@
     return {}

   if not matched.tea or isFake pattern
-    return isMalformed: yes
+    return {precs, assigns, isMalformed: yes}

   # Properly bind types according to the pattern
   if pattern.tea
@@ -2855,6 +2855,7 @@
 fakeCompile = (ctx, token) ->
   if ctx.assignTo()
     precs: []
+    assigns: []
   else
     token.tea = toConstrained ctx.freshTypeVariable star
     token.scope = ctx.currentScopeIndex()


Remove evidence when it is removed from an underlying collection

--- core/app/backbone/collections/evidence_collection.coffee
+++ core/app/backbone/collections/evidence_collection.coffee
@@ -15,6 +15,7 @@
     for collection in @_containedCollections
       @listenTo collection, 'sync', @loadFromCollections
       @listenTo collection, 'add', (model) -> @add model
+      @listenTo collection, 'remove', (model) -> @remove model
       @listenTo collection, 'saved_added_model', -> @trigger 'saved_added_model'
       @listenTo collection, 'error_adding_model', -> @trigger 'error_adding_model'


Fixed variable issue

Fixed variable issue

--- source/Downloader.coffee
+++ source/Downloader.coffee
@@ -50,7 +50,7 @@
     winston.info('Received answer from user.')
     name = result['song']
     index = titles.indexOf(name)
-    source = 'http://123savemp3.net' + downloads[index]
+    source = 'http://123savemp3.net' + links[index]
     winston.info('Should download: ' + name)
     downloadTrack(name, source)


Reregister config schema when package is reactivated

Signed-off-by: Nathan Sobo <2e8aa918660411855c6d44d5bb2da677aa033255@github.com>

--- src/package.coffee
+++ src/package.coffee
@@ -85,7 +85,7 @@
         @loadMenus()
         @loadStylesheets()
         @loadDeserializers()
-        @registerConfigSchemaFromMetadata()
+        @configSchemaRegisteredOnLoad = @registerConfigSchemaFromMetadata()
         @settingsPromise = @loadSettings()
         if @shouldRequireMainModuleOnLoad() and not @mainModule?
           @requireMainModule()
@@ -130,7 +130,7 @@
   activateNow: ->
     try
       @requireMainModule() unless @mainModule?
-      @registerConfigSchemaFromMainModule()
+      @configSchemaRegisteredOnActivate = @registerConfigSchemaFromMainModule()
       @registerViewProviders()
       @activateStylesheets()
       if @mainModule? and not @mainActivated
@@ -146,16 +146,16 @@
   registerConfigSchemaFromMetadata: ->
     if configSchema = @metadata.configSchema
       @config.setSchema @name, {type: 'object', properties: configSchema}
-      @configSchemaRegistered = true
+      true
+    else
+      false

   registerConfigSchemaFromMainModule: ->
-    return if @configSchemaRegistered
-
-    if @mainModule?
+    if @mainModule? and not @configSchemaRegisteredOnLoad
       if @mainModule.config? and typeof @mainModule.config is 'object'
         @config.setSchema @name, {type: 'object', properties: @mainModule.config}
-
-    @configSchemaRegistered = true
+        return true
+    false

   activateStylesheets: ->
     return if @stylesheetsActivated
@@ -382,6 +382,7 @@
     @activationPromise = null
     @resolveActivationPromise = null
     @activationCommandSubscriptions?.dispose()
+    @configSchemaRegisteredOnActivate = false
     @deactivateResources()
     @deactivateKeymaps()
     if @mainActivated


sync with maccman/master

sync with maccman/master

--- src/package.coffee
+++ src/package.coffee
@@ -31,7 +31,7 @@
   createServer: ->
     (env, callback) =>
       callback(200,
-        'Content-Type': 'text/javascript',
+        'Content-Type': 'text/javascript',
         @compile())

 module.exports =


bugfix

bugfix

--- js/src/model/object/ObjectMetadata.coffee
+++ js/src/model/object/ObjectMetadata.coffee
@@ -16,11 +16,13 @@
       version          : 0
       total            : 0
       childObjectCount : 0
-      strategy         : { '@class' : 'com.kryptnostic.kodex.v1.serialization.crypto.DefaultChunkingStrategy' }
       owners           : []
       readers          : []
       writers          : []
       name             : {}
+      strategy         : {
+        '@class' : 'com.kryptnostic.kodex.v1.serialization.crypto.DefaultChunkingStrategy'
+      }
     }

   #


Added slider preset. Improved some other presets. Reduces rondell sizes and better position for controls.

--- src/presets.coffee
+++ src/presets.coffee
@@ -18,11 +18,15 @@
         once: false
         delay: 5000
       radius:
-        x: 220
+        x: 240
+      center:
+        left: 340
+        top: 160
       controls:
         margin:
-          x: 20
-          y: 190
+          x: 130
+          y: 260
+      randomStart: true
       currentLayer: 1
       funcSize: (layerDiff, rondell) ->
         (rondell.maxItems / Math.abs(layerDiff)) / rondell.maxItems
@@ -62,7 +66,12 @@
       scaling: 1
       visibleItems: 1
       controls:
-        enabled: false
+        margin:
+          x: 5
+          y: 5
+      strings:
+        prev: ' '
+        next: ' '
       center:
         left: 200
         top: 200
@@ -79,7 +88,7 @@

     cubic:
       center:
-        left: 400
+        left: 300
         top: 200
       visibleItems: 5
       itemProperties:
@@ -91,8 +100,8 @@
           height: 350
       controls:
         margin:
-          x: 10
-          y: 190
+          x: 70
+          y: 330
       funcTop: (layerDiff, rondell) ->
           rondell.center.top - rondell.itemProperties.size.height / 2 + Math.pow(layerDiff / 2, 3) * rondell.radius.x
       funcLeft: (layerDiff, rondell) ->
@@ -102,14 +111,16 @@

     gallery:
       visibleItems: 4
-      controls:
-        enabled: false
       center:
         top: 145
         left: 250
       size:
         height: 400
         width: 500
+      controls:
+        margin:
+          x: 10
+          y: 255
       itemProperties:
         delay: 10
         sizeFocused:
@@ -126,5 +137,35 @@
         rondell.center.left + (layerDiff - 0.5) * (rondell.itemProperties.size.width + 5)
       funcOpacity: (layerDist, rondell) ->
         0.8
+
+    slider:
+      visibleItems: 1
+      fadeTime: 1000
+      opacityMin: 0.01
+      autoRotation:
+        enabled: true
+      center:
+        top: 150
+        left: 300
+      size:
+        height: 300
+        width: 600
+      controls:
+        margin:
+          x: -1
+          y: 135
+      itemProperties:
+        sizeFocused:
+          width: 600
+          height: 300
+        size:
+          width: 600
+          height: 300
+      funcTop: (layerDiff, rondell) ->
+        0
+      funcLeft: (layerDiff, rondell) ->
+        0
+      funcOpacity: (layerDist, rondell) ->
+        0.02

 )(jQuery)


rename STORES to MODELS

rename STORES to MODELS

--- tutor/src/api/index.coffee
+++ tutor/src/api/index.coffee
@@ -55,7 +55,7 @@
 { default: User } = require '../models/user'
 { default: Courses } = require '../models/courses'

-BOOTSTRAPED_STORES = {
+BOOTSTRAPED_MODELS = {
   user:    User.bootstrap,
   courses: Courses.bootstrap
 }
@@ -257,7 +257,7 @@
    )

 start = (bootstrapData) ->
-  for storeId, action of BOOTSTRAPED_STORES
+  for storeId, action of BOOTSTRAPED_MODELS
     data = bootstrapData[storeId]
     action(data) if data


:guardsman: Prevent extra lines appearing after tokenization

--- lib/minimap.coffee
+++ lib/minimap.coffee
@@ -30,6 +30,14 @@
     subs.add @textEditor.onDidChangeScrollLeft (scrollLeft) =>
       @emitter.emit('did-change-scroll-left', scrollLeft)
     subs.add @textEditor.onDidDestroy => @destroy()
+
+    # FIXME: Some changes occuring during the tokenization produces
+    # ranges that deceive the canvas rendering by making some
+    # lines at the end of the buffer intact while they are in fact not
+    # resulting in extra lines appearing at the end of the minimap.
+    # Forcing a whole repaint fix that but is suboptimal.
+    subs.add @textEditor.displayBuffer.onDidTokenize =>
+      @emitter.emit('did-change-config')

   destroyed: ->
     @subscriptions.dispose()


nvm, it doesn't work very well.

nvm, it doesn't work very well.


Change lines between buttons on main page

--- css.client.coffee
+++ css.client.coffee
@@ -16,16 +16,10 @@
 		lineHeight: "50px"
 		textAlign: "center"
 		verticalAlign: "middle"
-	'.bar-button::after':
-		content: '""'
-		display: 'block'
-		width: '1px'
-		height: '40px'
-		backgroundColor: '#ABABAB'
-		margin: '-44px 0 0 0'
-		boxShadow: 'none'
-	'.bar-button:first-of-type::after':
-		display: 'none'
+		borderRight: '1px solid #ABABAB'
+		boxSizing: 'border-box'
+	'.bar-button:last-of-type':
+		borderRight: '0 none'
 	'.mapbox-logo':
 		display: "none"
 # Map element


actually store all necessary info in d3 nodes.

--- app/assets/javascripts/plugins/graph.js.coffee
+++ app/assets/javascripts/plugins/graph.js.coffee
@@ -14,7 +14,8 @@
   nodes = []
   links = {}
   for node in data.nodes
-    nodes.push({label: node.name})
+    nodes.push
+      info: node
     links[node.id] = nodes.length - 1
   edges = []
   for edge in data.edges
@@ -85,7 +86,8 @@
   node.append("text").
     attr("x", 12).
     attr("dy", ".35em").
-    text((d) -> return d.label )
+    text((d) ->
+      return d.info.name )

   tick = ->
     path.attr("d", (d) ->


Don't show editor until panel is initialized

--- lib/settings-view.coffee
+++ lib/settings-view.coffee
@@ -34,6 +34,7 @@
     @handlePackageEvents()

     @panelToShow = activePanelName
+    @filterEditor.hide()
     process.nextTick => @activatePackages => @initializePanels()

   handlePackageEvents: ->
@@ -79,6 +80,8 @@
     @addPackagePanel(pack) for pack in @getPackages()
     @showPanel(@panelToShow) if @panelToShow
     @showPanel('Settings') unless @activePanelName
+    @filterEditor.show()
+    @filterEditor.redraw()

   serialize: ->
     deserializer: 'SettingsView'


Fixed indent

Fixed indent

--- lib/one-tab.coffee
+++ lib/one-tab.coffee
@@ -1,36 +1,36 @@
 {CompositeDisposable} = require 'atom'

 class OneTab
-    activate: ->
-        atom.packages.onDidActivateInitialPackages =>
-            @subscriptions = new CompositeDisposable
-            @subscriptions.add atom.workspaceView.eachPaneView (paneView) =>
-                @initPaneView(paneView)
+  activate: ->
+    atom.packages.onDidActivateInitialPackages =>
+      @subscriptions = new CompositeDisposable
+      @subscriptions.add atom.workspaceView.eachPaneView (paneView) =>
+        @initPaneView(paneView)

-    deactivate: ->
-        @subscriptions.dispose()
+  deactivate: ->
+    @subscriptions.dispose()

-    # Handles setup for each pane
-    initPaneView: (paneView) ->
-        subscription = new CompositeDisposable
+  # Handles setup for each pane
+  initPaneView: (paneView) ->
+    subscription = new CompositeDisposable

-        subscription.add paneView.model.onDidDestroy ->
-            subscription.dispose()
+    subscription.add paneView.model.onDidDestroy ->
+      subscription.dispose()

-        subscription.add paneView.model.onDidAddItem =>
-            @handlePaneViewItemEvent paneView
+    subscription.add paneView.model.onDidAddItem =>
+      @handlePaneViewItemEvent paneView

-        subscription.add paneView.model.onDidRemoveItem =>
-            @handlePaneViewItemEvent paneView
+    subscription.add paneView.model.onDidRemoveItem =>
+      @handlePaneViewItemEvent paneView

-        # Initial hide
-        @handlePaneViewItemEvent paneView, 0
+    # Initial hide
+    @handlePaneViewItemEvent paneView, 0

-    handlePaneViewItemEvent: (paneView, delay = 150) ->
-        tabView = paneView.find '.tab-bar'
-        if paneView.model.getItems().length == 1
-            tabView.hide(delay)
-        else
-            tabView.show(delay)
+  handlePaneViewItemEvent: (paneView, delay = 150) ->
+    tabView = paneView.find '.tab-bar'
+    if paneView.model.getItems().length == 1
+      tabView.hide(delay)
+    else
+      tabView.show(delay)

 module.exports = new OneTab()


Remove console log

Remove console log

--- app/js/jail_iframe/classes/icon_buttons.coffee
+++ app/js/jail_iframe/classes/icon_buttons.coffee
@@ -6,7 +6,6 @@
 FactlinkJailRoot.on 'reposition_icons', ->
   if there_are_icons
     Tether.position()
-    console.log 'repositioned!'

 class IconButton
   constructor: (options) ->


Use new fadeIn/Out support in notifications too.

--- app/js/jail_iframe/classes/notification.coffee
+++ app/js/jail_iframe/classes/notification.coffee
@@ -9,7 +9,6 @@
     type_classes: 'fl-message-icon-time fl-message-success'

 in_screen_time = 3000
-removal_delay = 350 # Should be larger than notification_transition_time
 content = """

@@ -23,8 +22,7 @@
     frame = new FactlinkJailRoot.ControlIframe()
     fillFrame()
     positionFrame()
-    frame.$el.addClass 'factlink-control-visible'
-    setTimeout(remove, in_screen_time)
+    doTransition()

   fillFrame = ->
     frame.setContent($.parseHTML(content)[0])
@@ -40,9 +38,12 @@
       position: 'fixed',
       marginLeft: "-#{frame.$el.width()/2}px"

-  remove = ->
-    frame.$el.removeClass 'factlink-control-visible'
-    setTimeout (-> frame.destroy()), removal_delay
+  doTransition = ->
+    frame
+      .fadeIn()
+      .then(-> FactlinkJailRoot.Timer(in_screen_time))
+      .then(-> frame.fadeOut())
+      .then(-> frame.destroy())

   renderFrame()
   return


Add spec for calling makeTreeSync with a file path

--- spec/fs-plus-spec.coffee
+++ spec/fs-plus-spec.coffee
@@ -131,6 +131,21 @@
       fs.makeTreeSync(abcPath)
       expect(fs.isDirectorySync(abcPath)).toBeTruthy()

+    it "throws an error when the provided path is a file", ->
+      tempDir = temp.mkdirSync('fs-plus-')
+      filePath = path.join(tempDir, 'file.txt')
+      fs.writeFileSync(filePath, '')
+      expect(fs.isFileSync(filePath)).toBe true
+
+      makeTreeError = null
+
+      try
+        fs.makeTreeSync(filePath)
+      catch error
+        makeTreeError = error
+
+      expect(makeTreeError.code).toBe 'EEXIST'
+      expect(makeTreeError.path).toBe filePath

   describe ".makeTree(path)", ->
     aPath = path.join(temp.dir, 'a')
@@ -159,7 +174,7 @@
         expect(callback.argsForCall[1][0]).toBeUndefined()
         expect(fs.isDirectorySync(abcPath)).toBeTruthy()

-    it "fails because the provided path is a file", ->
+    it "calls back with an error when the provided path is a file", ->
       callback = jasmine.createSpy('callback')
       tempDir = temp.mkdirSync('fs-plus-')
       filePath = path.join(tempDir, 'file.txt')


Fixes confirm notice if toggling an dirty element.

--- app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee
+++ app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee
@@ -99,9 +99,8 @@
   toggle: (id, text) ->
     el = $("#element_#{id}")
     if Alchemy.isElementDirty(el)
-      Alchemy.openConfirmDialog
+      Alchemy.openConfirmDialog Alchemy._t('element_dirty_notice'),
         title: Alchemy._t('warning')
-        message: Alchemy._t('element_dirty_notice')
         ok_label: Alchemy._t('ok')
         cancel_label: Alchemy._t('cancel')
         on_ok: =>


統計情報のFCが集計されない不具合を修正

統計情報のFCが集計されない不具合を修正

--- app/assets/javascripts/components/sheets/lamp_statistics.js.cjsx
+++ app/assets/javascripts/components/sheets/lamp_statistics.js.cjsx
@@ -16,9 +16,9 @@
     scores = ScoreStore.get()
     for id, _ of SheetStore.get()
       score = scores[id]
-      score ||= {}
-      score.display ||= ''
-      score.state ||= 7
+      score ||=
+        display: ''
+        state: 7
       continue if score.display isnt ''
       statistics.all++
       statistics.remain++ if score.state <= @state.threshold


runtime.setupRuntime: Fix passing more arguments than expected

--- lib/runtime.coffee
+++ lib/runtime.coffee
@@ -478,7 +478,8 @@
         console.log "MicroFlo runtime listening at", ip + ":" + port
         return callback null, httpServer

-setupRuntime = (serialPortToUse, baudRate, port, debugLevel, ip, callback) ->
+# FIXME: specify most of these things through a `options` object
+setupRuntime = (serialPortToUse, baudRate, port, debugLevel, ip, library, callback) ->

     serial.openTransport serialPortToUse, baudRate, (err, transport) ->
         return callback err, null if err


update test

update test

--- test/integration/get-device-spec.coffee
+++ test/integration/get-device-spec.coffee
@@ -9,7 +9,7 @@

 describe 'GetDevice', ->
   beforeEach (done)->
-    @db = mongojs 'localhost:27017/meshblu-core-test'
+    @db = mongojs 'meshblu-core-test'
     @collection = @db.collection 'devices'
     @collection.drop (error) => done error

@@ -25,8 +25,6 @@
       timeoutSeconds: 15

   beforeEach 'register devices', (done) ->
-    doneThrice = _.after done, 3
-
     @auth =
       uuid: 'lack_of_lifeboats'
       token: 'leak'
@@ -36,20 +34,24 @@
       token: bcrypt.hashSync @auth.token, 8
       type: 'device:auth'

+    @collection.insert @authDevice, done
+
+  beforeEach (done) ->
     @discovererDevice =
       uuid: 'deep-freeze'
       type: 'device:discoverer'
       discoverAsWhitelist: [@authDevice.uuid]
       discoverWhitelist: []

+    @collection.insert @discovererDevice, done
+
+  beforeEach (done) ->
     @discovereeDevice =
       uuid: 'premature-bird'
       type: 'device:discoveree'
       discoverWhitelist: [@discovererDevice.uuid]

-    @collection.insert @authDevice, doneThrice
-    @collection.insert @discovererDevice, doneThrice
-    @collection.insert @discovereeDevice, doneThrice
+    @collection.insert @discovereeDevice, done

   describe "when a device requests itself without as'ing", ->
     beforeEach (done) ->