indent lines

indent lines

--- lib/js2coffee.coffee
+++ lib/js2coffee.coffee
@@ -31,6 +31,9 @@
 #
 # 2. This node is now passed onto `Builder#build()`.

+indent_lines = (indent,lines) ->
+  indent + lines.replace(/\n/g,"\n"+indent)
+
 buildCoffee = (str, opts = {}) ->

   str  = str.replace /\r/g, ''
@@ -54,21 +57,24 @@
         srclines.push parseInt(g)
         ""

-    srclines = _.uniq srclines.sort(),true
+    srclines = _.sortBy(_.uniq(srclines), (i) -> i)

     text = rtrim(text)
+    indent = text.match /^\s*/

     if srclines.length > 0
-      minline = Math.min(srclines...)
+      minline = _.last(srclines)

       precomments = builder.comments_not_done_to(minline)
       if precomments
-        res.push precomments
+        res.push indent_lines indent,precomments

     if text
       if keep_line_numbers
-          text = text + "#" + srclines.join(",") + "  "
+          text = text + "#" +srclines.join(",") + "#  "
       res.push rtrim(text + " "+ltrim(builder.line_comments(srclines)))
+    else
+      res.push ""

   comments = builder.comments_not_done_to(1E10)
   if comments
@@ -102,7 +108,10 @@
   l: (n) ->
     # todo: this could be configurable debug helper
     # console.log n if n.lineno in [1]
-    "\uFEFE#{n.lineno}\uFEFE"
+    if n and n.lineno
+       "\uFEFE#{n.lineno}\uFEFE"
+    else
+      ""

   make_comment: (comment) ->
     c = ("##{line}" for line in comment.value.split("\n")).join("\n")
@@ -113,10 +122,12 @@

   comments_not_done_to: (lineno) ->
     res = []
-    while 1
+    loop
       break if @comments.length == 0
       c = @comments[0]
-      if c.lineno < lineno
+      # console.log "test comment #{c.lineno}<#{lineno} #{c.lineno < lineno} #{i.lineno for i in @comments}"
+      if (c.lineno < lineno)
+        # console.log "took",c
         res.push(@make_comment c)
         @comments.shift()
         continue
@@ -134,7 +145,8 @@

     # get comments from tokenizer
     if not @comments?
-      @comments = node.tokenizer.comments
+      @comments = _.sortBy node.tokenizer.comments, (n) ->
+        n.start

     # apply ast transforms
     @transform node
@@ -248,7 +260,7 @@
     # sometimes. They should be ignored.

     unless n.expression?
-      @l(n)+""
+      ""

     else if n.expression.typeName() == 'object_init'

@@ -256,7 +268,7 @@
       if n.parenthesized
         @l(n)+src
       else
-        @l(n)+"#{unshift(blockTrim(src))}#{@line_comment(n.lineno)}\n"
+        @l(n)+"#{unshift(blockTrim(src))}\n"

     else
       @l(n)+@build(n.expression) + "\n"
@@ -407,7 +419,7 @@
       @l(n)+"/#{value}/#{flag}"

   'string': (n) ->
-    @l(n)+strEscape n.value
+    @l(n)+ strEscape n.value

   # `call`
   # A Function call.
@@ -489,7 +501,7 @@

     if n.finallyBlock?
       c.add "finally"
-      c.scope @body(n.finallyBlock),1,n.finallyBlock
+      c.scope @body(n.finallyBlock)

     @l(n)+c

@@ -565,7 +577,7 @@
     @l(n)+c

   'if': (n) ->
-    c = new Code @,n
+    c = new Code

     keyword = if n.positive then "if" else "unless"
     body_   = @body(n.thenPart)
@@ -594,7 +606,7 @@
     @l(n)+c

   'switch': (n) ->
-    c = new Code @,n
+    c = new Code

     c.add "switch #{@build n.discriminant}\n"

@@ -653,7 +665,7 @@
       list = _.map n.children, (item) => @build item

       c = new Code @,n
-      c.scope list.join("\n"),1,n #TODO
+      c.scope list.join("\n")
       c = "{#{c}}"  if options.brackets?
       @l(n)+c

@@ -689,9 +701,9 @@
   'var': (n) ->
     # TODO: add correct source line numbers instead of n.lineno for all
     list = _.map n.children, (item) =>
-      "#{unreserve item.value} = #{if item.initializer? then @build(item.initializer) else @l(item)+'undefined'}"
-
-    @l(n)+_.compact(list).join("\n") + "\n\n"
+      "#{unreserve item.value} = #{if item.initializer? then @build(item.initializer) else 'undefined'}"
+
+    @l(n)+_.compact(list).join("\n") + "\n"

   # ### Unsupported things
   #


Pass a callback to Campfire speak calls, so that our response ends successfully

--- lifeguard.coffee
+++ lifeguard.coffee
@@ -48,7 +48,10 @@

     process.on "SIGTERM", => @_shutDown()

-    process.on "uncaughtException", => @_shutDown()
+    #process.on "uncaughtException", (err) => @_shutDown(err)
+    process.on "uncaughtException", (err) =>
+      console.error "Error is ", err
+      process.exit()

     # start our new process, if the app directory exists.  if it doesn't, just
     # watch and wait
@@ -194,10 +197,7 @@
       @d = require("domain").create()

       @d.on "error", (err) =>
-        if err.code == "ECONNRESET"
-          # ignore
-        else
-          console.log "Campfire error: ", err
+        console.error "Campfire error: ", err

       @d.run =>
         Campfire = (require "campfire").Campfire
@@ -214,7 +214,8 @@
           @campfire_room = room

           for msg in @campfire_queue
-            @campfire_room.speak msg
+            @campfire_room.speak msg, (err) =>
+              # ok

           @campfire_queue = []

@@ -222,7 +223,8 @@

         @lifeguard.on "notify", (msg) =>
           if @campfire_room
-            @campfire_room.speak msg
+            @campfire_room.speak msg, (err) =>
+              # ok
           else
             @campfire_queue.push msg


Don't bin jslib events when FACTLINK doesn't exist, e.g. on the CI

--- core/app/assets/javascripts/tour.coffee
+++ core/app/assets/javascripts/tour.coffee
@@ -25,6 +25,8 @@
       @detectDeselecting()

   bindJsLibraryEvents: ->
+    return unless FACTLINK? # On the CI there is no js-library, so we just skip this
+
     FACTLINK.on 'modalOpened', =>
       @state.open_modal()


Document the NoFlo entry point module

Document the NoFlo entry point module

--- src/lib/NoFlo.coffee
+++ src/lib/NoFlo.coffee
@@ -1,28 +1,88 @@
 #     NoFlo - Flow-Based Programming for Node.js
 #     (c) 2011 Henri Bergius, Nemein
 #     NoFlo may be freely distributed under the MIT license
-internalSocket = require "./InternalSocket"
-component = require "./Component"
-asynccomponent = require "./AsyncComponent"
-port = require "./Port"
-arrayport = require "./ArrayPort"
-graph = require "./Graph"
-{Network} = require "./Network"
-{LoggingComponent} = require "./LoggingComponent"

+# # NoFlo
+#
+# NoFlo is a Flow-Based Programming environment for JavaScript. This file provides the
+# main entry point to the NoFlo network.
+#
+# Find out more about using NoFlo from <http://noflojs.org/documentation/>
+#
+# ## Main APIs
+#
+# ### Graph interface
+#
+# [Graph](Graph.html) is used for instantiating FBP graph definitions.
+exports.graph = require('./Graph')
+exports.Graph = exports.graph.Graph
+# ## Network interface
+#
+# [Network](Network.html) is used for running NoFlo graphs.
+exports.Network = require('./Network').Network
+
+# ### Component Loader
+#
+# The [ComponentLoader](ComponentLoader.html) is responsible for finding and loading
+# NoFlo components.
 if typeof process is 'object' and process.title is 'node'
-  componentLoader = require "./nodejs/ComponentLoader"
+  # Node.js version of the Component Loader finds components and graphs by traversing
+  # the NPM dependency tree from a given root directory on the file system.
+  exports.ComponentLoader = require('./nodejs/ComponentLoader').ComponentLoader
 else
-  componentLoader = require './ComponentLoader'
+  # Browser version of the Component Loader finds components and graphs by traversing
+  # the [Component](http://component.io/) dependency tree from a given Component package
+  # name.
+  exports.ComponentLoader = require('./ComponentLoader').ComponentLoader

+# ### Component baseclasses
+#
+# These baseclasses can be used for defining NoFlo components.
+exports.Component = require('./Component').Component
+exports.AsyncComponent = require('./AsyncComponent').AsyncComponent
+exports.LoggingComponent = require('./LoggingComponent').LoggingComponent
+
+# ### NoFlo ports
+#
+# These classes are used for instantiating ports on NoFlo components.
+exports.Port = require('./Port').Port
+exports.ArrayPort = require('./ArrayPort').ArrayPort
+
+# ### NoFlo sockets
+#
+# The NoFlo [internalSocket](InternalSocket.html) is used for connecting ports of
+# different components together in a network.
+exports.internalSocket = require('./InternalSocket')
+
+# ## Network instantiation
+#
+# This function handles instantiation of NoFlo networks from a Graph object. It creates
+# the network, and then starts execution by sending the Initial Information Packets.
+#
+#     noflo.createNetwork(someGraph, function (network) {
+#       console.log('Network is now running!');
+#     });
+#
+# It is also possible to instantiate a Network but delay its execution by giving the
+# third `delay` parameter. In this case you will have to handle connecting the graph and
+# sending of IIPs manually.
+#
+#     noflo.createNetwork(someGraph, function (network) {
+#       network.connect(function () {
+#         network.sendInitials();
+#         console.log('Network is now running!');
+#       })
+#     }, true);
 exports.createNetwork = (graph, callback, delay) ->
-  network = new Network graph
+  network = new exports.Network graph

   networkReady = (network) ->
     callback network if callback?
+    # Send IIPs
     network.sendInitials()

   if graph.nodes.length is 0
+    # Empty network, no need to connect it up
     setTimeout ->
       networkReady network
     , 0
@@ -31,27 +91,29 @@
   # Ensure components are loaded before continuing
   network.loader.listComponents ->
     if delay
+      # In case of delayed execution we don't wire it up
       callback network if callback?
       return
+    # Wire the network up
     network.connect ->
       networkReady network

   network

+# ### Starting a network from a file
+#
+# It is also possible to start a NoFlo network by giving it a path to a `.json` or `.fbp` network
+# definition file.
+#
+#     noflo.loadFile('somefile.json', function (network) {
+#       console.log('Network is now running!');
+#     });
 exports.loadFile = (file, callback) ->
-  graph.loadFile file, (net) ->
+  exports.graph.loadFile file, (net) ->
     exports.createNetwork net, callback

+# ### Saving a network definition
+#
+# NoFlo graph files can be saved back into the filesystem with this method.
 exports.saveFile = (graph, file, callback) ->
-  graph.save file, -> callback file
-
-exports.Component = component.Component
-exports.ComponentLoader = componentLoader.ComponentLoader
-exports.AsyncComponent = asynccomponent.AsyncComponent
-exports.LoggingComponent = LoggingComponent
-exports.Port = port.Port
-exports.ArrayPort = arrayport.ArrayPort
-exports.Graph = graph.Graph
-exports.Network = Network
-exports.graph = graph
-exports.internalSocket = internalSocket
+  exports.graph.save file, -> callback file


modified

modified

--- src/sortsina.coffee
+++ src/sortsina.coffee
@@ -17,10 +17,10 @@
   host = "http://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php"
   c = param.category ? 'sh_b'
   s = param.sort ? 'amount'
-  l = param.len ? '40'
-
+  n = param.topn ? '40'
+
   options =
-    url: "#{host}/Market_Center.getHQNodeData?page=1&num=#{l}&sort=#{s}&asc=0&node=#{c}&symbol=&_s_r_a=sort"
+    url: "#{host}/Market_Center.getHQNodeData?page=1&num=#{n}&sort=#{s}&asc=0&node=#{c}&symbol=&_s_r_a=sort"
     json: false
     encoding: null


Take version from package.json also when there's no .git directory (source releases)

--- build/tasks/set-version-task.coffee
+++ build/tasks/set-version-task.coffee
@@ -5,7 +5,7 @@
   {spawn} = require('./task-helpers')(grunt)

   getVersion = (callback) ->
-    if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH is 'master'
+    if !fs.existsSync('../../.git') or (process.env.JANKY_SHA1 and process.env.JANKY_BRANCH is 'master')
       {version} = require(path.join(grunt.config.get('atom.appDir'), 'package.json'))
       callback(null, version)
     else


Switch CourseInstance firstLevelURL to return dungeons-of-kithgard

Since we switched from using forked levels

--- app/models/CourseInstance.coffee
+++ app/models/CourseInstance.coffee
@@ -24,4 +24,4 @@
     @fetch(options)

   firstLevelURL: ->
-    "/play/level/course-dungeons-of-kithgard?course=#{@get('courseID')}&course-instance=#{@id}"+    "/play/level/dungeons-of-kithgard?course=#{@get('courseID')}&course-instance=#{@id}"


updated optimize step to include ./app.build.js

--- csi.coffee
+++ csi.coffee
@@ -334,6 +334,8 @@
       commands.install.action()

       if argv.optimize
+        profile = buildProfile()
+
         # returns the on-disk filename for a css dep like "css!100:foo.css"
         cssFilename = (moduleName) ->
           _.compose.apply(_, [
@@ -372,11 +374,13 @@
         # config for the require.js optimizer that includes empty modules for
         # all excluded files
         pathsConfig = () ->
+          base = {strings: "empty:"}
+          base[excluded] = "empty:" for excluded in (profile.exclude or [])
           allComponents().reduce (paths, component) ->
             if component.build?.exclude
               paths[module] = "empty:" for module in component.build.exclude
             paths
-          , extend(true, {strings: "empty:"}, config.paths)
+          , extend(true, base, config.paths)

         output = (text, opts) ->
           text = minify(text) if opts.minify
@@ -394,7 +398,6 @@
           write cssName, cssText
           log "writing optimized#{opts.minify and "/minified" or ""} file to #{cssName}"

-        profile = buildProfile()
         for module in profile.modules
           css = {}
           if isComponent() and /^\.[\/\\]/.test(module.name)


pass modal into its own setup

pass modal into its own setup

--- modules/modal/main/index.coffee
+++ modules/modal/main/index.coffee
@@ -60,7 +60,7 @@

     @contentContainer = modal.append('div').attr('class', 'hx-modal-content')

-    if @setup then @setup(@contentContainer.node())
+    if @setup then @setup(@contentContainer.node(), this)

     @emit('show', @contentContainer.node())
     @emit('showstart')
@@ -164,4 +164,4 @@

 hx.modal =
   dialog: modalDialog
-  input: modalInput+  input: modalInput


only select top level

only select top level

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -1071,7 +1071,11 @@

       for target in @getSelectedEntries()
         entryPath = target.querySelector(".name").dataset.path
-        unless path.dirname(entryPath) in @dragPaths
+        parentSelected = @dragPaths.some (path) -> entryPath.startsWith path
+
+        if parentSelected
+          @deselect [target]
+        else
           @dragPaths.push(entryPath)
           newElement = target.cloneNode(true)
           if newElement.classList.contains("directory")
@@ -1126,6 +1130,9 @@
         return if initialPaths.includes(newDirectoryPath)

         entry.classList.remove('selected')
+
+        parentSelected = initialPaths.some (path) -> newDirectoryPath.startsWith path
+        return if parentSelected

         # iterate backwards so files in a dir are moved before the dir itself
         for initialPath in initialPaths by -1


Ensure an entry is selected before performing certain actions

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -423,6 +423,8 @@

   expandDirectory: (isRecursive=false) ->
     selectedEntry = @selectedEntry()
+    return unless selectedEntry?
+
     if isRecursive is false and selectedEntry.isExpanded
       @moveDown() if selectedEntry.directory.getEntries().length > 0
     else
@@ -438,6 +440,8 @@

   openSelectedEntry: (options={}, expandDirectory=false) ->
     selectedEntry = @selectedEntry()
+    return unless selectedEntry?
+
     if selectedEntry.classList.contains('directory')
       if expandDirectory
         @expandDirectory(false)
@@ -450,6 +454,8 @@

   openSelectedEntrySplit: (orientation, side) ->
     selectedEntry = @selectedEntry()
+    return unless selectedEntry?
+
     pane = atom.workspace.getActivePane()
     if pane and selectedEntry.classList.contains('file')
       if atom.workspace.getActivePaneItem()
@@ -472,6 +478,8 @@

   openSelectedEntryInPane: (index) ->
     selectedEntry = @selectedEntry()
+    return unless selectedEntry?
+
     pane = atom.workspace.getPanes()[index]
     if pane and selectedEntry.classList.contains('file')
       atom.workspace.openURIInPane selectedEntry.getPath(), pane


Update: atom configuration

Update: atom configuration

--- Users/mingelz/.atom/config.cson
+++ Users/mingelz/.atom/config.cson
@@ -14,6 +14,9 @@
     showInvisibles: true
   "exception-reporting":
     userId: "53fd9ef9-f628-4235-8535-c7c0a1ee661b"
+  "platformio-ide-terminal":
+    toggles:
+      autoClose: true
   "linter-ui-default":
     useBusySignal: false
   "vim-mode-plus":


Dispose of PaneAxis subscriptions when destroyed

--- src/pane-axis.coffee
+++ src/pane-axis.coffee
@@ -104,5 +104,6 @@
     subscription.dispose()

   destroyed: ->
+    @subscriptions.dispose()
     @emitter.emit 'did-destroy'
     @emitter.dispose()


panelutil: remove chrscales() and fix calc_chrscales()

--- src/panelutil.coffee
+++ src/panelutil.coffee
@@ -54,6 +54,9 @@
     data

 # calculate chromosome scales for chrpanelframe (and so lodchart)
+# type = "xaxis"     normal x-axis scale
+#      = "yaxis"     y-axis scale
+#      = "revyaxis"  reversed y-axis scale
 calc_chrscales = (plot_width, left_margin, gap, chr, start, end, reverse=false) ->
     # calculate chromosome lengths, start and end in pixels
     n_chr = chr.length
@@ -66,66 +69,22 @@
         chr_start_pixels.push(chr_end_pixels[i-1] + gap)
         chr_end_pixels.push(chr_start_pixels[i] + tot_pixels/tot_chr_length * chr_length[i])

+    right = plot_width + left_margin*2
     xscale = {}
     for i of chr
+        domain = [start[i], end[i]]
+        range = [chr_start_pixels[i], chr_end_pixels[i]]
+
+        if reverse # for y-axis
+            domain.reverse()
+            range = [right - range[1], right - range[0]]
+
         xscale[chr[i]] = d3.scale.linear()
-                           .domain([start[i], end[i]])
-                           .range([chr_start_pixels[i], chr_end_pixels[i]])
-
-    if reverse # reverse the scale (right-to-left rather than left-to-right)
-        right = plot_width + left_margin*2
-        for i of chr
-            xscale[chr[i]].range([right - chr_end_pixels[i], right - chr_start_pixels[i]])
+                           .domain(domain)
+                           .range(range)

     # return the scale
     xscale
-
-
-# calculate chromosome start/end + scales, for heat map
-chrscales = (data, width, chrGap, leftMargin, pad4heatmap) ->
-    # start and end of chromosome positions
-    chrStart = []
-    chrEnd = []
-    chrLength = []
-    totalChrLength = 0
-    maxd = 0
-    for chr in data.chrnames
-        d = maxdiff(data.posByChr[chr])
-        maxd = d if d > maxd
-
-        rng = d3.extent(data.posByChr[chr])
-        chrStart.push(rng[0])
-        chrEnd.push(rng[1])
-        L = rng[1] - rng[0]
-        chrLength.push(L)
-        totalChrLength += L
-
-    # adjust lengths for heatmap
-    if pad4heatmap
-        data.recwidth = maxd
-        chrStart = chrStart.map (x) -> x-maxd/2
-        chrEnd = chrEnd.map (x) -> x+maxd/2
-        chrLength = chrLength.map (x) -> x+maxd
-        totalChrLength += (chrLength.length*maxd)
-
-    # break up x axis into chromosomes by length, with gaps
-    data.chrStart = []
-    data.chrEnd = []
-    cur = leftMargin
-    cur += chrGap/2 unless pad4heatmap
-    data.xscale = {}
-    for chr,i in data.chrnames
-        data.chrStart.push(cur)
-        w = Math.round((width-chrGap*(data.chrnames.length-pad4heatmap))/totalChrLength*chrLength[i])
-        data.chrEnd.push(cur + w)
-        cur = data.chrEnd[i] + chrGap
-        # x-axis scales, by chromosome
-        data.xscale[chr] = d3.scale.linear()
-                             .domain([chrStart[i], chrEnd[i]])
-                             .range([data.chrStart[i], data.chrEnd[i]])
-
-    # return data with new stuff added
-    data

 # Select a set of categorical colors
 # ngroup is positive integer


clearer variable names

clearer variable names

--- src/quad_tree.coffee
+++ src/quad_tree.coffee
@@ -114,12 +114,14 @@

       subdivide: ->
         increased_depth = I.depth + 1
+
+        bounds = I.bounds

-        x = I.bounds.x
-        y = I.bounds.y
+        x = bounds.x
+        y = bounds.y

-        width = I.bounds.width
-        height = I.bounds.height
+        width = bounds.width
+        height = bounds.height

         half_width = (width / 2).floor()
         half_height = (height / 2).floor()


IDE: Accept callback in resurrectSnapshot.

--- client/ide/lib/workspace/idelayoutmanager.coffee
+++ client/ide/lib/workspace/idelayoutmanager.coffee
@@ -164,7 +164,7 @@
    * @param {Array} snapshot
    * @param {boolean=} silent  Don't dispatch `SplitViewWasMerged` or `NewSplitViewCreated` event.
   ###
-  resurrectSnapshot: (snapshot, silent = no) ->
+  resurrectSnapshot: (snapshot, silent = no, callback) ->

     ## The `ideApp` is an `IDEAppController`s instance
     ideApp = @getDelegate()
@@ -191,6 +191,8 @@

     @isRestored = yes
     @emit 'LayoutResurrected'
+
+    callback?()

   ###*


fix missing fun

fix missing fun

--- static/index.coffee
+++ static/index.coffee
@@ -35,7 +35,7 @@

   maybeStartEditing = (evt) ->
     return true  unless wantsToEdit evt
-    startEditing evt
+    edit currentContent

   maybeCancelEditing = (evt) ->
     return true  unless evt.which is 27


added a delay to brain init

added a delay to brain init

--- src/sentiment.coffee
+++ src/sentiment.coffee
@@ -12,6 +12,8 @@
 #   Michi Kono
 #   Will Barksdale
 #
+# Configuration:
+#   HUBOT_SENTIMENT_BRAIN_INIT_TIMEOUT=N - wait for N milliseconds for brain data to load from redis. (default 10000)
 # Notes:
 #   Requires underscore and sentiment NPM modules
 #
@@ -31,8 +33,9 @@
 #
 sentiment = require 'sentiment'
 _ = require 'underscore'
-
-module.exports = (robot) ->
+INIT_TIMEOUT = (if process.env.HUBOT_REACT_INIT_TIMEOUT then parseInt(process.env.HUBOT_SENTIMENT_BRAIN_INIT_TIMEOUT) else 10000)
+
+start = (robot) ->
   # helper method to get the week of the year in numeric form
   getWeekOfYear = (dateObj = new Date()) ->
     onejan = new Date(dateObj.getFullYear(), 0, 1)
@@ -193,3 +196,17 @@
   robot.respond /where( i|')s( the)? (sadness|stress)\??/i, (msg) ->
     # responds in the current channel
     msg.send getSadChannels(10)
+
+module.exports = (robot) ->
+  loaded = _.once(->
+    console.log "starting hubot-sentiment..."
+    start robot
+    return
+  )
+  if _.isEmpty(robot.brain.data) or _.isEmpty(robot.brain.data._private)
+    robot.brain.once "loaded", loaded
+    setTimeout loaded, INIT_TIMEOUT
+  else
+    loaded()
+  return
+


Override SubjectViewer::rescale Ignore max height. Scale width of page to viewport.

--- overrides.coffee
+++ overrides.coffee
@@ -3,6 +3,14 @@
 SubjectViewer = require 'zooniverse-readymade/lib/subject-viewer'

 SubjectViewer::template = require './templates/subject-viewer'
+
+SubjectViewer::rescale = ()->
+  scale = @markingSurface.el.parentNode.offsetWidth / @maxWidth
+  @markingSurface.maxWidth = @maxWidth
+  @markingSurface.maxHeight = @maxHeight
+  @markingSurface.svg.attr
+    width: scale * @maxWidth
+    height: scale * @maxHeight

 ClassifyPage = require 'zooniverse-readymade/lib/classify-page'

@@ -38,7 +46,6 @@
 # moving back and forward through the array of marked SVG rectangles
 classify_page.el.on decisionTree.LOAD_TASK, ({originalEvent: detail: {task}})->
   rectangles = []
-  scale = ms.el.parentNode.offsetWidth / subjectViewer.maxWidth

   for tool in ms.tools
     tool.deselect()
@@ -51,9 +58,7 @@
   rect_index = parseInt (subjectViewer.taskIndex - INITIAL_STEPS) / ANNOTATION_STEPS

   if task.key in ['illustration', 'review']
-    ms.svg.attr
-      width: scale * subjectViewer.maxWidth
-      height: scale * subjectViewer.maxHeight
+    subjectViewer.rescale()
     ms.rescale 0, 0, subjectViewer.maxWidth, subjectViewer.maxHeight

   if task.key in ['details', 'parts'] and rectangles.length > 0
@@ -89,12 +94,6 @@
     current_tool.mark.details = value.details

 classify_page.on classify_page.LOAD_SUBJECT, (e, subject)->
-  scale = ms.el.parentNode.offsetWidth / subjectViewer.maxWidth
-  ms.maxWidth = subjectViewer.maxWidth
-  ms.maxHeight = subjectViewer.maxHeight
-  ms.svg.attr
-    width: scale * subjectViewer.maxWidth
-    height: scale * subjectViewer.maxHeight
   ms.rescale 0, 0, subjectViewer.maxWidth, subjectViewer.maxHeight

   bhl_link.setAttribute 'href', subject.metadata.bhl_url


fixed wrong indent

fixed wrong indent

--- packages/crypto/client/crypto.coffee
+++ packages/crypto/client/crypto.coffee
@@ -166,7 +166,7 @@
         self.busy = false
         self.flush()

-       done: (result) ->
+      done: (result) ->
         self.current?.onDone? null, result

       error: (error) ->


 Merge branch 'master.fix-the-widgetz' into 'master'

Fix The Widgetz

--- client/Main/widgetcontroller.coffee
+++ client/Main/widgetcontroller.coffee
@@ -21,6 +21,7 @@
       ]

     KD.remote.api.JCustomPartials.some query, {}, (err, widgets) =>
+      return  unless widgets or err
       for widget in widgets
         target   = "published"
         key      = "viewInstance"


Add directory method to fs

Add directory method to fs

--- src/stdlib/fs.coffee
+++ src/stdlib/fs.coffee
@@ -27,6 +27,11 @@
   # Set the current working directory to `path`.
   changeWorkingDirectory: (path) ->
     OSX.NSFileManager.defaultManager.changeCurrentDirectoryPath path
+
+  # Return the dirname of the given path. That is the path with any trailing
+  # non-directory component removed.
+  directory: (path) ->
+    @absolute(path).replace(new RegExp(@base(path) + '$'), '')

   # Returns true if the file specified by path exists
   exists: (path) ->


Add newlines.

Add newlines.

--- test/unit/scanSpec.coffee
+++ test/unit/scanSpec.coffee
@@ -11,6 +11,7 @@
       session:
         title: 'Breast Subject 1 Session 1'
         scans: [
+          _cls: 'Scan'
           number: '1'
           protocol: 'sp1'
           time_series:
@@ -53,8 +54,10 @@
             .to.exist
           expect(scan.session, "The scan session reference is incorrect")
             .to.equal(session)
+
         it 'should convert the number to an integer', ->
           expect(scan.number, "The scan number is incorrect").to.equal(1)
+
         it 'should have a virtual title property', ->
           expect(scan.title, "The scan title is missing").to.exist
           expect(scan.title, "The scan title is incorrect")


Disable automatic recompilation of AppleScripts

--- user/config.cson
+++ user/config.cson
@@ -57,6 +57,8 @@
     showSearchWrapIcon: false
   "grammar-selector": {}
   hex: {}
+  "language-applescript":
+    autoCompile: false
   "lisp-paredit": {}
   minimap:
     autoToggle: false


oneliner

oneliner

--- jasmine-harness.coffee
+++ jasmine-harness.coffee
@@ -1,10 +1,7 @@
-attach = (obj) ->
-  window[key] = obj[key] for key of obj
-
-runner = require('./spec/lib/jasmine.js')
+expose = (obj) -> window[key] = obj[key] for key of obj

 # attach jasmine to window
-attach runner
+expose require('./spec/lib/jasmine.js')

 html = require './spec/lib/jasmine-html.js'
 tap = require './spec/lib/jasmine.tap_reporter.js'
@@ -12,8 +9,8 @@

 # helper = require('./spec/SpecHelper')
 # mock = require('./spec/Mock')
-# expose helper
-# expose mock
+# attach helper
+# attach mock

 # insert test files here
 # require('./spec/BaconSpec')


xhr_send should be 204 not 200, also it's good to set content type

--- src/trans-xhr.coffee
+++ src/trans-xhr.coffee
@@ -79,7 +79,9 @@
         for message in d
             jsonp.didMessage(message)

-        res.writeHead(200)
+        # FF assumes that the response is XML.
+        res.setHeader('Content-Type', 'text/plain')
+        res.writeHead(204)
         res.end()
         return true


Rewrote the way it saves the window position and size

--- index.cjsx
+++ index.cjsx
@@ -19,37 +19,30 @@

 window.poiStatsWindow = null

-savePoiStatsWindowPosition = ->
-    if window.poiStatsWindow != null
-        config.set 'plugin.PoiStatistics.pos', window.poiStatsWindow.getPosition()
-        if process.env.DEBUG?
-            window.log 'New position: ' + window.poiStatsWindow.getPosition()
-
-savePoiStatsWindowSize = ->
-    if window.poiStatsWindow != null
-        config.set 'plugin.PoiStatistics.size', window.poiStatsWindow.getSize()
-        if process.env.DEBUG?
-            window.log 'New size: ' + window.poiStatsWindow.getSize()
-
-onPoiStatsWindowClosed = ->
-    window.poiStatsWindow = null
-    if process.env.DEBUG?
-        window.log 'Poi Stats closed.'
+handleWindowMoveResize = ->
+  b1 = window.poiStatsWindow.getBounds()
+  # console.log "Moved to: #{JSON.stringify(b1)}"
+  setTimeout(( ->
+    b2 = window.poiStatsWindow.getBounds()
+    if JSON.stringify(b2) == JSON.stringify(b1)
+      config.set 'plugin.PoiStatistics.bounds', b2
+      # console.log "   Saved:  #{JSON.stringify(b2)}"
+  ), 5000)

 initialPoiStatsWindow = ->
-    pos = config.get 'plugin.PoiStatistics.pos', [0, 0]
-    size = config.get 'plugin.PoiStatistics.size', [800, 750]
+    defaultBounds = {x: 0, y: 0, width: 800, height: 750}
+    b = config.get 'plugin.PoiStatistics.bounds', defaultBounds
     newWindow = windowManager.createWindow
-        x: pos[0]
-        y: pos[1]
-        width: size[0]
-        height: size[1]
+        x: b.x
+        y: b.y
+        width: b.width
+        height: b.height
         realClose: true
     window.poiStatsWindow = newWindow
     newWindow.setMinimumSize 600, 425
-    newWindow.on 'move', savePoiStatsWindowPosition
-    newWindow.on 'resize', savePoiStatsWindowSize
-    newWindow.on 'closed', onPoiStatsWindowClosed
+    newWindow.on 'move', handleWindowMoveResize
+    newWindow.on 'resize', handleWindowMoveResize
+    newWindow.on 'closed', -> window.poiStatsWindow = null
     newWindow.loadUrl "file://#{__dirname}/index.html"
     newWindow.show()
     if process.env.DEBUG?


add cond

add cond

--- index.cjsx
+++ index.cjsx
@@ -9,6 +9,16 @@

 window.addEventListener 'layout.change', (e) ->
   {layout} = e.detail
+
+getCondStyle = (cond) ->
+  if cond > 49
+    color: '#FFFF00'
+  else if cond < 20
+    color: '#DD514C'
+  else if cond < 30
+    color: '#F37B1D'
+  else
+    null

 getHpStyle = (percent) ->
   if percent <= 25
@@ -245,9 +255,11 @@
       enemyIntercept: 0
       enemyName: "深海棲艦"
       result: "不明"
+      shipCond: [0, 0, 0, 0, 0, 0]
+      deckId: 0
     handleResponse: (e) ->
       {method, path, body, postBody} = e.detail
-      {afterHp, nowHp, maxHp, damageHp, shipName, shipLv, enemyInfo, getShip, enemyFormation, enemyTyku, enemyIntercept, enemyName, result} = @state
+      {afterHp, nowHp, maxHp, damageHp, shipName, shipLv, enemyInfo, getShip, enemyFormation, enemyTyku, enemyIntercept, enemyName, result, shipCond, deckId} = @state
       if path == '/kcsapi/api_req_map/start' || formationFlag
         @setState
           enemyInformation: 0
@@ -270,6 +282,7 @@
             shipLv[i] = _ships[shipId].api_lv
             maxHp[i] = _ships[shipId].api_maxhp
             nowHp[i] = _ships[shipId].api_nowhp
+            deckId = postBody.api_deck_id - 1
           for tmp, i in shipLv
             damageHp[i] = 0
           getShip = null
@@ -453,7 +466,14 @@
             getShip = null
           formationFlag = true
           result = body.api_win_rank
-
+        when '/kcsapi/api_port/port'
+          flag = true
+          _deck = window._decks[deckId]
+          {_ships} = window
+          for shipId, i in _deck.api_ship
+            continue if shipId == -1
+            shipCond[i] = _ships[shipId].api_cond
+          shipLv[i] = -1 for i in [6..11]
       return unless flag
       @setState
         afterHp: afterHp
@@ -468,6 +488,8 @@
         enemyIntercept: enemyIntercept
         enemyName: enemyName
         result: result
+        shipCond: shipCond
+        deckId: deckId

     componentDidMount: ->
       window.addEventListener 'game.response', @handleResponse
@@ -489,7 +511,7 @@
                 continue unless @state.shipLv[i] != -1
                 continue unless i < 6

-                  Lv. {@state.shipLv[i]} - {tmpName}
+                  Lv. {@state.shipLv[i]} - {tmpName} Cond: {@state.shipCond[i]}

                     <ProgressBar bsStyle={getHpStyle @state.nowHp[i] / @state.maxHp[i] * 100}
                       now={@state.nowHp[i] / @state.maxHp[i] * 100}
@@ -553,7 +575,7 @@
                   for j in [0..1]
                     list.push
                 else
-                  list.push Lv. {@state.shipLv[i]} - {tmpName}
+                  list.push Lv. {@state.shipLv[i]} - {tmpName} Cond: {@state.shipCond[i]}
                   list.push <ProgressBar bsStyle={getHpStyle @state.nowHp[i] / @state.maxHp[i] * 100} now={@state.nowHp[i] / @state.maxHp[i] * 100} label={if @state.damageHp[i] > 0 then "#{@state.nowHp[i]} / #{@state.maxHp[i]} (-#{@state.damageHp[i]})" else "#{@state.nowHp[i]} / #{@state.maxHp[i]}"} />
                 if @state.shipLv[i + 6] == -1
                   for j in [0..1]


Add select rank report

Add select rank report

--- index.cjsx
+++ index.cjsx
@@ -28,6 +28,22 @@
           mapLv[map.api_id] = 0
           if map.api_eventmap?
             mapLv[map.api_id] = map.api_eventmap.api_selected_rank
+      # Eventmap select report
+      when '/kcsapi/api_req_map/select_eventmap_rank'
+        {_teitokuLv, _nickNameId} = window
+        info =
+          teitokuLv: _teitokuLv
+          teitokuId: _nickNameId
+          mapareaId: parseInt(postBody.api_maparea_id)
+          rank: parseInt(postBody.api_rank)
+        try
+          yield request.postAsync "http://#{SERVER_HOSTNAME}/api/report/v2/select_rank",
+            form:
+              data: JSON.stringify info
+            headers:
+              'User-Agent': 'Reporter v2.1.0'
+        catch err
+          console.error err
       # Create ship report
       when '/kcsapi/api_req_kousyou/createship'
         creating = true


:lipstick:

:lipstick:

--- src/workspace.coffee
+++ src/workspace.coffee
@@ -137,15 +137,15 @@
   # open.
   updateWindowTitle: =>
     if projectPath = atom.project?.getPaths()[0]
-      titleAddition = '- Atom'
+      appName = 'Atom'
       if item = @getActivePaneItem()
-        document.title = "#{item.getTitle?() ? 'untitled'} - #{projectPath} #{titleAddition}"
+        document.title = "#{item.getTitle?() ? 'untitled'} - #{projectPath} - #{appName}"
         atom.setRepresentedFilename(item.getPath?() ? projectPath)
       else
-        document.title = "#{projectPath} #{titleAddition}"
+        document.title = "#{projectPath} - #{appName}"
         atom.setRepresentedFilename(projectPath)
     else
-      document.title = "untitled #{titleAddition}"
+      document.title = "untitled - #{appName}"
       atom.setRepresentedFilename('')

   # On OS X, fades the application window's proxy icon when the current file


:non-potable_water: Fix leak in Workspace::buildTextEditor

--- src/workspace.coffee
+++ src/workspace.coffee
@@ -561,10 +561,10 @@
         throw error

     @project.bufferForPath(filePath, options).then (buffer) =>
-      editor = @buildTextEditor(Object.assign({buffer, largeFileMode}, options))
-      disposable = atom.textEditors.add(editor)
-      editor.onDidDestroy -> disposable.dispose()
-      editor
+      new TextEditor(Object.assign(
+        {@clipboard, @assert, buffer, largeFileMode},
+        options
+      ))

   handleGrammarUsed: (grammar) ->
     return unless grammar?
@@ -581,10 +581,12 @@
   #
   # Returns a {TextEditor}.
   buildTextEditor: (params) ->
-    params = Object.assign({@clipboard, @assert}, params)
-    editor = new TextEditor(params)
-    @textEditorRegistry.maintainConfig(editor)
-    @textEditorRegistry.maintainGrammar(editor)
+    editor = new TextEditor(Object.assign({@clipboard, @assert}, params))
+    subscriptions = new CompositeDisposable(
+      @textEditorRegistry.maintainConfig(editor),
+      @textEditorRegistry.maintainGrammar(editor)
+    )
+    editor.onDidDestroy -> subscriptions.dispose()
     editor

   # Public: Asynchronously reopens the last-closed item's URI if it hasn't already been


add feedback link

add feedback link

--- fav.coffee
+++ fav.coffee
@@ -158,6 +158,8 @@
         div class:"top_background", ->
           img src: "top_background.png", alt: "Enter your twitter ID"
         footer class: "global_footer", ->
+          text "Feedback:"
+          a href:"http://twitter.com/hanachin_", -> text "@hanachin_"
         script src: '/jquery-1.7.1.min.js', charset: 'utf-8'
         script src: '/jquery.tmpl.min.js', charset: 'utf-8'
         script src: '/jquery.bottom-1.0.js', charset: 'utf-8'


stupid whitespace

stupid whitespace

--- test/dependency.coffee
+++ test/dependency.coffee
@@ -39,7 +39,8 @@
       should.not.exist err
       should.exist compiled

-      compiled.should.eql '\nconsole.log("hello");\n'
+      compiled = compiled.replace /\n/g, ''
+      compiled.should.eql 'console.log("hello");'
       done()

   #it "should accept the resolvers data structure in 'create'", (done) ->


file: test simplification

file: test simplification

--- test/file/index.coffee
+++ test/file/index.coffee
@@ -9,27 +9,26 @@

   scratch = test.scratch @

-  describe 'content', ->
-
-    they 'write a string', (ssh, next) ->
-      # Write the content
-      mecano
-        ssh: ssh
-      .file
-        target: "#{scratch}/file"
-        content: 'Hello'
-      , (err, written) ->
-        written.should.be.true()
-      .file # Write the same content
-        target: "#{scratch}/file"
-        content: 'Hello'
-      , (err, written) ->
-        return next err if err
-        # Content has change
-        written.should.be.false()
-        fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-          content.should.eql 'Hello'
-          next()
+  describe 'options content', ->
+
+    they 'is a string', (ssh, next) ->
+      mecano
+        ssh: ssh
+      .file
+        target: "#{scratch}/file"
+        content: 'Hello'
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file
+        target: "#{scratch}/file"
+        content: 'Hello'
+      , (err, status) ->
+        status.should.be.false() unless err
+      .file.assert
+        target: "#{scratch}/file"
+        content: 'Hello'
+      .then next
+
     they 'is a function', (ssh, next) ->
       content = 'invalid'
       mecano
@@ -51,13 +50,13 @@
       .file
         target: "#{scratch}/file"
         content: 'Hello'
-      , (err, written) ->
-        written.should.be.true()
-      .file
-        target: "#{scratch}/file"
-        content: 'Hello'
-      , (err, written) ->
-        written.should.be.false()
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file
+        target: "#{scratch}/file"
+        content: 'Hello'
+      , (err, status) ->
+        status.should.be.false() unless err
       .then next

     they 'doesnt increment if target is same than generated content', (ssh, next) ->
@@ -69,25 +68,25 @@
       .file
         target: "#{scratch}/file_copy"
         source: "#{scratch}/file"
-      , (err, written) ->
-        return next err if err
-        written.should.be.true()
-        fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-          content.should.eql 'Hello'
-          next()
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/file"
+        content: 'Hello'
+      .then next

     they 'empty file', (ssh, next) ->
-      mecano.file
-        ssh: ssh
+      mecano
+        ssh: ssh
+      .file
         target: "#{scratch}/empty_file"
         content: ''
-      , (err, written) ->
-        return next err if err
-        written.should.be.true()
-        fs.readFile ssh, "#{scratch}/empty_file", 'utf8', (err, content) ->
-          return next err if err
-          content.should.eql ''
-          next()
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/empty_file"
+        content: ''
+      .then next

     they 'touch file', (ssh, next) ->
       mecano
@@ -96,13 +95,11 @@
         target: "#{scratch}/empty_file"
         content: ''
         unless_exists: true
-      , (err, written) ->
-        written.should.be.true()
-      .call (options, next) ->
-        fs.readFile ssh, "#{scratch}/empty_file", 'utf8', (err, content) ->
-          return next err if err
-          content.should.eql ''
-          next()
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/empty_file"
+        content: ''
       .file
         target: "#{scratch}/empty_file"
         content: 'toto'
@@ -110,40 +107,38 @@
         target: "#{scratch}/empty_file"
         content: ''
         unless_exists: true
-      , (err, written) ->
-        written.should.be.false()
-      .call (options, next) ->
-        fs.readFile ssh, "#{scratch}/empty_file", 'utf8', (err, content) ->
-          return next err if err
-          content.should.eql 'toto'
-          next()
+      , (err, status) ->
+        status.should.be.false() unless err
+      .file.assert
+        target: "#{scratch}/empty_file"
+        content: 'toto'
       .then next

     they 'handle integer type', (ssh, next) ->
-      mecano.file
-        ssh: ssh
+      mecano
+        ssh: ssh
+      .file
         target: "#{scratch}/a_file"
         content: 123
-      , (err, written) ->
-        return next err if err
-        written.should.be.true()
-        fs.readFile ssh, "#{scratch}/a_file", 'ascii', (err, content) ->
-          return next err if err
-          content.should.eql '123'
-          next()
+      , (err, status) ->
+        status.should.be.true() unless err
+      .assert.file
+        target: "#{scratch}/a_file"
+        content: '123'
+      .then next

     they 'create parent directory', (ssh, next) ->
-      mecano.file
-        ssh: ssh
+      mecano
+        ssh: ssh
+      .file
         target: "#{scratch}/a/missing/dir/a_file"
         content: 'hello'
-      , (err, written) ->
-        return next err if err
-        written.should.be.true()
-        fs.readFile ssh, "#{scratch}/a/missing/dir/a_file", 'utf8', (err, content) ->
-          return next err if err
-          content.should.eql 'hello'
-          next()
+      , (err, status) ->
+        status.should.be.true() unless err
+      .assert.file
+        target: "#{scratch}/a/missing/dir/a_file"
+        content: 'hello'
+      .then next

   describe 'link', ->

@@ -159,12 +154,12 @@
       .file
         content: 'ok'
         target: "#{scratch}/link"
-      .call (_, callback) ->
-        fs.readFile ssh, "#{scratch}/link", 'ascii', (err, data) ->
-          data.should.eql 'ok'
-          fs.readFile ssh, "#{scratch}/target", 'ascii', (err, data) ->
-            data.should.eql 'ok'
-            callback()
+      .assert.file
+        target: "#{scratch}/target"
+        content: 'ok'
+      .assert.file
+        target: "#{scratch}/link"
+        content: 'ok'
       .then next

     they 'throw error if link is a directory', (ssh, next) ->
@@ -195,12 +190,12 @@
         content: 'ok'
         target: "#{scratch}/link"
         unlink: true
-      .call (_, callback) ->
-        fs.readFile ssh, "#{scratch}/link", 'ascii', (err, data) ->
-          data.should.eql 'ok'
-          fs.readFile ssh, "#{scratch}/target", 'ascii', (err, data) ->
-            data.should.eql 'ko'
-            callback()
+      .assert.file
+        target: "#{scratch}/target"
+        content: 'ko'
+      .assert.file
+        target: "#{scratch}/link"
+        content: 'ok'
       .then next

     they 'dont follow link if option "unlink" and link is directory', (ssh, next) ->
@@ -231,7 +226,7 @@
         target: "#{scratch}/a_file"
         content: 'ok'
         mode: 0o0700
-      , (err, written) ->
+      , (err, status) ->
         return next err if err
         fs.stat ssh, "#{scratch}/a_file", (err, stat) ->
           return next err if err
@@ -252,14 +247,14 @@
         target: "#{scratch}/a_file"
         content: 'ok'
         mode: 0o0705
-      , (err, written) ->
-        written.should.be.true()
+      , (err, status) ->
+        status.should.be.true() unless err
       .file
         target: "#{scratch}/a_file"
         content: 'ok'
         mode: 0o0705
-      , (err, written) ->
-        written.should.be.false()
+      , (err, status) ->
+        status.should.be.false() unless err
       .then next

     they 'change permission after modification', (ssh, next) ->
@@ -274,7 +269,7 @@
         target: "#{scratch}/a_file"
         content: 'World'
         mode: 0o0755
-      , (err, written) ->
+      , (err, status) ->
         return next err if err
         fs.stat ssh, "#{scratch}/a_file", (err, stat) ->
           return next err if err
@@ -291,13 +286,12 @@
         to: '# to'
         content: 'here we are\n# from\nlets try to replace that one\n# to\nyou coquin'
         replace: 'my friend'
-      , (err, written) ->
-        return next err if err
-        written.should.be.true()
-        fs.readFile ssh, "#{scratch}/fromto.md", 'utf8', (err, content) ->
-          return next err if err
-          content.should.eql 'here we are\n# from\nmy friend\n# to\nyou coquin'
-          next()
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/fromto.md"
+        content: 'here we are\n# from\nmy friend\n# to\nyou coquin'
+      .then next

     they 'with from and with to append', (ssh, next) ->
       fs.writeFile ssh, "#{scratch}/fromto.md", 'here we are\nyou coquin', (err) ->
@@ -310,12 +304,11 @@
           to: '# to'
           append: true
           replace: 'my friend'
-        , (err, written) ->
-          written.should.be.true()
-        .call (options, next) ->
-          fs.readFile ssh, "#{scratch}/fromto.md", 'utf8', (err, content) ->
-            content.should.eql 'here we are\nyou coquin\n# from\nmy friend\n# to'
-            next()
+        , (err, status) ->
+          status.should.be.true()
+        .file.assert
+          target: "#{scratch}/fromto.md"
+          content: 'here we are\nyou coquin\n# from\nmy friend\n# to'
         .file
           target: "#{scratch}/fromto.md"
           from: '# from'
@@ -323,28 +316,27 @@
           append: true
           replace: 'my best friend'
           eof: true
-        , (err, written) ->
-          written.should.be.true()
-        .call (options, next) ->
-          fs.readFile ssh, "#{scratch}/fromto.md", 'utf8', (err, content) ->
-            content.should.eql 'here we are\nyou coquin\n# from\nmy best friend\n# to\n'
-            next()
+        , (err, status) ->
+          status.should.be.true()
+        .file.assert
+          target: "#{scratch}/fromto.md"
+          content: 'here we are\nyou coquin\n# from\nmy best friend\n# to\n'
         .then next

     they 'with from and without to', (ssh, next) ->
-      mecano.file
-        ssh: ssh
+      mecano
+        ssh: ssh
+      .file
         target: "#{scratch}/fromto.md"
         from: '# from'
         content: 'here we are\n# from\nlets try to replace that one\n# to\nyou coquin'
         replace: 'my friend'
-      , (err, written) ->
-        return next err if err
-        written.should.be.true()
-        fs.readFile ssh, "#{scratch}/fromto.md", 'utf8', (err, content) ->
-          return next err if err
-          content.should.eql 'here we are\n# from\nmy friend'
-          next()
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/fromto.md"
+        content: 'here we are\n# from\nmy friend'
+      .then next

     they 'without from and with to', (ssh, next) ->
       mecano.file
@@ -353,76 +345,72 @@
         to: '# to'
         content: 'here we are\n# from\nlets try to replace that one\n# to\nyou coquin'
         replace: 'my friend'
-      , (err, written) ->
-        return next err if err
-        written.should.be.true()
-        fs.readFile ssh, "#{scratch}/fromto.md", 'utf8', (err, content) ->
-          return next err if err
-          content.should.eql 'my friend\n# to\nyou coquin'
-          next()
-
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/fromto.md"
+        content: 'my friend\n# to\nyou coquin'
+      .then next

   describe 'replace', ->

     they 'without match and place_before a string', (ssh, next) ->
-      mecano.file
-        ssh: ssh
+      mecano
+        ssh: ssh
+      .file
         target: "#{scratch}/fromto.md"
         content: 'here we are\nyou+coquin'
         replace: 'my friend'
         place_before: 'you+coquin' # Regexp must escape the plus sign
-      , (err, written) ->
-        return next err if err
-        fs.readFile ssh, "#{scratch}/fromto.md", 'utf8', (err, content) ->
-          return next err if err
-          content.should.eql 'here we are\nmy friend\nyou+coquin'
-          next()
+      .file.assert
+        target: "#{scratch}/fromto.md"
+        content: 'here we are\nmy friend\nyou+coquin'
+      .then next

     they 'without match and place_before a regexp', (ssh, next) ->
-      mecano.file
-        ssh: ssh
+      mecano
+        ssh: ssh
+      .file
         target: "#{scratch}/fromto.md"
         content: 'here we are\nyou coquin'
         replace: 'my friend'
         place_before: /^you coquin$/m
-      , (err, written) ->
-        return next err if err
-        fs.readFile ssh, "#{scratch}/fromto.md", 'utf8', (err, content) ->
-          return next err if err
-          content.should.eql 'here we are\nmy friend\nyou coquin'
-          next()
+      .file.assert
+        target: "#{scratch}/fromto.md"
+        content: 'here we are\nmy friend\nyou coquin'
+      .then next

   describe 'match & replace', ->

     they 'with match a line as a string', (ssh, next) ->
-      mecano.file
-        ssh: ssh
+      mecano
+        ssh: ssh
+      .file
         target: "#{scratch}/fromto.md"
         match: 'lets try to replace that one'
         content: 'here we are\nlets try to replace that one\nyou coquin'
         replace: 'my friend'
-      , (err, written) ->
-        return next err if err
-        written.should.be.true()
-        fs.readFile ssh, "#{scratch}/fromto.md", 'utf8', (err, content) ->
-          return next err if err
-          content.should.eql 'here we are\nmy friend\nyou coquin'
-          next()
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/fromto.md"
+        content: 'here we are\nmy friend\nyou coquin'
+      .then next

     they 'with match a word as a string', (ssh, next) ->
-      mecano.file
-        ssh: ssh
+      mecano
+        ssh: ssh
+      .file
         target: "#{scratch}/fromto.md"
         match: 'replace'
         content: 'replace that one\nand\nreplace this one\nand not this one'
         replace: 'switch'
-      , (err, written) ->
-        return next err if err
-        written.should.be.true()
-        fs.readFile ssh, "#{scratch}/fromto.md", 'utf8', (err, content) ->
-          return next err if err
-          content.should.eql 'switch that one\nand\nswitch this one\nand not this one'
-          next()
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/fromto.md"
+        content: 'switch that one\nand\nswitch this one\nand not this one'
+      .then next

     they 'with match as a regular expression', (ssh, next) ->
       # With a match
@@ -433,50 +421,48 @@
         content: 'email=david(at)adaltas(dot)com\nusername=root'
         match: /(username)=(.*)/
         replace: '$1=david (was $2)'
-      , (err, written) ->
-        return next err if err
-        written.should.be.true()
+      , (err, status) ->
+        status.should.be.true() unless err
       .file # Without a match
         target: "#{scratch}/replace"
         match: /this wont work/
         replace: '$1=david (was $2)'
-      , (err, written) ->
-        return next err if err
-        written.should.be.false()
-        fs.readFile ssh, "#{scratch}/replace", 'utf8', (err, content) ->
-          return next err if err
-          content.should.eql 'email=david(at)adaltas(dot)com\nusername=david (was root)'
-          next()
+      , (err, status) ->
+        status.should.be.false() unless err
+      .file.assert
+        target: "#{scratch}/replace"
+        content: 'email=david(at)adaltas(dot)com\nusername=david (was root)'
+      .then next

     they 'with match as a regular expression and multiple content', (ssh, next) ->
-      mecano.file
-        ssh: ssh
+      mecano
+        ssh: ssh
+      .file
         match: /(.*try) (.*)/
         content: 'here we are\nlets try to replace that one\nyou coquin'
         replace: ['my friend, $1']
         target: "#{scratch}/replace"
-      , (err, written) ->
-        return next err if err
-        written.should.be.true()
-        fs.readFile ssh, "#{scratch}/replace", 'utf8', (err, content) ->
-          return next err if err
-          content.should.eql 'here we are\nmy friend, lets try\nyou coquin'
-          next()
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/replace"
+        content: 'here we are\nmy friend, lets try\nyou coquin'
+      .then next

     they 'with match with global and multilines', (ssh, next) ->
-      mecano.file
-        ssh: ssh
+      mecano
+        ssh: ssh
+      .file
         target: "#{scratch}/replace"
         match: /^property=.*$/mg
         content: '#A config file\n#property=30\nproperty=10\nproperty=20\n#End of Config'
         replace: 'property=50'
-      , (err, written) ->
-        return next err if err
-        written.should.be.true()
-        fs.readFile ssh, "#{scratch}/replace", 'utf8', (err, content) ->
-          return next err if err
-          content.should.eql '#A config file\n#property=30\nproperty=50\nproperty=50\n#End of Config'
-          next()
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/replace"
+        content: '#A config file\n#property=30\nproperty=50\nproperty=50\n#End of Config'
+      .then next

     they 'will replace target if source or content does not exists', (ssh, next) ->
       mecano
@@ -488,20 +474,18 @@
         target: "#{scratch}/a_file"
         match: /(.*content)/
         replace: 'a text'
-      , (err, written) ->
-        written.should.be.true()
+      , (err, status) ->
+        status.should.be.true() unless err
       .file
         target: "#{scratch}/a_file"
         match: /(.*content)/
         replace: 'a text'
-      , (err, written) ->
-        written.should.be.false()
-      .then (err) ->
-        return next err if err
-        fs.readFile ssh, "#{scratch}/a_file", 'utf8', (err, content) ->
-          return next err if err
-          content.should.eql 'This is\na text\nfor testing'
-          next()
+      , (err, status) ->
+        status.should.be.false() unless err
+      .file.assert
+        target: "#{scratch}/a_file"
+        content: 'This is\na text\nfor testing'
+      .then next

   describe 'place_before', ->

@@ -509,36 +493,30 @@
       # File does not exist, it create it with the content
       mecano.file
         ssh: ssh
-        target: "#{scratch}/file"
+        target: "#{scratch}/a_file"
         content: 'hello'
         append: true
-      , (err) ->
-        return next err if err
-        # Check file content
-        fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-          return next err if err
-          content.should.eql 'hello'
-          next()
+      .file.assert
+        target: "#{scratch}/a_file"
+        content: 'hello'
+      .then next

     they 'is true, prepend the content', (ssh, next) ->
       # File doesnt exists, creates one
       mecano
         ssh: ssh
       .file
-        target: "#{scratch}/file"
+        target: "#{scratch}/a_file"
         content: 'world'
         place_before: true
       .file # File exists, prepends to it
-        target: "#{scratch}/file"
+        target: "#{scratch}/a_file"
         replace: 'hello'
         place_before: true
-      .then (err) ->
-        return next err if err
-        # Check file content
-        fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-          return next err if err
-          content.should.eql 'hello\nworld'
-          next()
+      .file.assert
+        target: "#{scratch}/a_file"
+        content: 'hello\nworld'
+      .then next

   describe 'append', ->

@@ -546,36 +524,30 @@
       # File does not exist, it create it with the content
       mecano.file
         ssh: ssh
-        target: "#{scratch}/file"
+        target: "#{scratch}/a_file"
         content: 'hello'
         append: true
-      , (err) ->
-        return next err if err
-        # Check file content
-        fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-          return next err if err
-          content.should.eql 'hello'
-          next()
+      .file.assert
+        target: "#{scratch}/a_file"
+        content: 'hello'
+      .then next

     they 'append content to existing file', (ssh, next) ->
       # File does not exists, it create one
       mecano
         ssh: ssh
       .file
-        target: "#{scratch}/file"
+        target: "#{scratch}/a_file"
         content: 'hello'
         append: true
       .file # File exists, it append to it
-        target: "#{scratch}/file"
+        target: "#{scratch}/a_file"
         content: 'world'
         append: true
-      .then (err) ->
-        return next err if err
-        # Check file content
-        fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-          return next err if err
-          content.should.eql 'helloworld'
-          next()
+      .file.assert
+        target: "#{scratch}/a_file"
+        content: 'helloworld'
+      .then next

   describe 'match & append or place_before', ->

@@ -583,111 +555,92 @@

       they 'place_before true, replace a string, match a regexp', (ssh, next) ->
         # Prepare by creating a file with content
-        mecano.file
-          ssh: ssh
+        mecano
+          ssh: ssh
+        .file
           target: "#{scratch}/file"
           content: 'you coquin\nhere we are\n'
-        , (err) ->
-          # File does not exist, it create it with the content
-          mecano.file
-            ssh: ssh
-            target: "#{scratch}/file"
-            match: /.*coquin/
-            replace: 'new coquin'
-            place_before: true
-          , (err, written) ->
-            return next err if err
-            written.should.be.true()
-            fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-              return next err if err
-              content.should.eql 'new coquin\nhere we are\n'
-              # Write a second time with same match
-              mecano.file
-                ssh: ssh
-                target: "#{scratch}/file"
-                match: /.*coquin/
-                replace: 'new coquin'
-                place_before: true
-              , (err, written) ->
-                return next err if err
-                written.should.be.false()
-                # Check file content
-                fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-                  return next err if err
-                  content.should.eql 'new coquin\nhere we are\n'
-                  next()
+        .file
+          target: "#{scratch}/file"
+          match: /.*coquin/
+          replace: 'new coquin'
+          place_before: true
+        , (err, status) ->
+          status.should.be.true() unless err
+        .file.assert
+          target: "#{scratch}/file"
+          content: 'new coquin\nhere we are\n'
+        # Write a second time with same match
+        .file
+          target: "#{scratch}/file"
+          match: /.*coquin/
+          replace: 'new coquin'
+          place_before: true
+        , (err, status) ->
+          status.should.be.false() unless err
+        .file.assert
+          target: "#{scratch}/file"
+          content: 'new coquin\nhere we are\n'
+        .then next

       they 'place_before true, replace a string, match a string', (ssh, next) ->
-        # Prepare by creating a file with content
-        mecano.file
-          ssh: ssh
+        mecano
+          ssh: ssh
+        .file
           target: "#{scratch}/file"
           content: 'you coquin\nhere we are\n'
-        , (err) ->
-          # File does not exist, it create it with the content
-          mecano.file
-            ssh: ssh
-            target: "#{scratch}/file"
-            match: "you coquin"
-            replace: 'new coquin'
-            place_before: true
-          , (err, written) ->
-            return next err if err
-            written.should.be.true()
-            fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-              return next err if err
-              content.should.eql 'new coquin\nhere we are\n'
-              # Write a second time with same match
-              mecano.file
-                ssh: ssh
-                target: "#{scratch}/file"
-                match: "new coquin"
-                replace: 'new coquin'
-                place_before: true
-              , (err, written) ->
-                return next err if err
-                written.should.be.false()
-                # Check file content
-                fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-                  return next err if err
-                  content.should.eql 'new coquin\nhere we are\n'
-                  next()
+        .file
+          target: "#{scratch}/file"
+          match: "you coquin"
+          replace: 'new coquin'
+          place_before: true
+        , (err, status) ->
+          status.should.be.true() unless err
+        .file.assert
+          target: "#{scratch}/file"
+          content: 'new coquin\nhere we are\n'
+        # Write a second time with same match
+        .file
+          target: "#{scratch}/file"
+          match: "new coquin"
+          replace: 'new coquin'
+          place_before: true
+        , (err, status) ->
+          status.should.be.false() unless err
+        .file.assert
+          target: "#{scratch}/file"
+          content: 'new coquin\nhere we are\n'
+        .then next

       they 'place_after', (ssh, next) ->
         # Prepare by creating a file with content
-        mecano.file
-          ssh: ssh
+        mecano
+          ssh: ssh
+        .file
           target: "#{scratch}/file"
           content: 'here we are\nyou coquin\n'
-        , (err) ->
-          # File does not exist, it create it with the content
-          mecano.file
-            ssh: ssh
-            target: "#{scratch}/file"
-            match: /.*coquin/
-            replace: 'new coquin'
-            append: true
-          , (err, written) ->
-            return next err if err
-            written.should.be.true()
-            fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-              return next err if err
-              content.should.eql 'here we are\nnew coquin\n'
-              # Write a second time with same match
-              mecano.file
-                ssh: ssh
-                target: "#{scratch}/file"
-                match: /.*coquin/
-                replace: 'new coquin'
-                append: true
-              , (err, written) ->
-                return next err if err
-                written.should.be.false()
-                # Check file content
-                fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-                  return next err if err
-                  content.should.eql 'here we are\nnew coquin\n'
-                  next()
+        .file
+          target: "#{scratch}/file"
+          match: /.*coquin/
+          replace: 'new coquin'
+          append: true
+        , (err, status) ->
+          status.should.be.true() unless err
+        .file.assert
+          target: "#{scratch}/file"
+          content: 'here we are\nnew coquin\n'
+        # Write a second time with same match
+        .file
+          target: "#{scratch}/file"
+          match: /.*coquin/
+          replace: 'new coquin'
+          append: true
+        , (err, status) ->
+          status.should.be.false() unless err
+        .file.assert
+          target: "#{scratch}/file"
+          content: 'here we are\nnew coquin\n'
+        .then next

     they 'will append if no match', (ssh, next) ->
       # Prepare by creating a file with content
@@ -701,14 +654,12 @@
         match: /will never work/
         replace: 'Add this line'
         append: true
-      , (err, written) ->
-        return next err if err
-        written.should.be.true()
-        # Check file content
-        fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-          return next err if err
-          content.should.eql 'here we are\nyou coquin\nAdd this line'
-          next()
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/file"
+        content: 'here we are\nyou coquin\nAdd this line'
+      .then next

     describe 'place_before/place_after a match if it is a regexp', ->

@@ -724,193 +675,159 @@
           match: /will never work/
           replace: 'Add this line'
           place_before: /^.*we.*$/m
-        , (err, written) ->
-          return next err if err
-          written.should.be.true()
-          # Check file content
-          fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-            return next err if err
-            content.should.eql 'Add this line\nhere we are\nyou coquin\nshould we\nhave fun'
-            next()
+        , (err, status) ->
+          status.should.be.true() unless err
+        .file.assert
+          target: "#{scratch}/file"
+          content: 'Add this line\nhere we are\nyou coquin\nshould we\nhave fun'
+        .then next

       they 'place_after', (ssh, next) ->
         # Prepare by creating a file with content
-        mecano.file
-          ssh: ssh
+        mecano
+          ssh: ssh
+        .file
           target: "#{scratch}/file"
           content: 'here we are\nyou coquin\nshould we\nhave fun'
-        , (err) ->
-          # File does not exist, it creates it with the content
-          mecano.file
-            ssh: ssh
-            target: "#{scratch}/file"
-            match: /will never work/
-            replace: 'Add this line'
-            append: /^.*we.*$/m
-          , (err, written) ->
-            return next err if err
-            written.should.be.true()
-            # Check file content
-            fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-              return next err if err
-              content.should.eql 'here we are\nAdd this line\nyou coquin\nshould we\nhave fun'
-              next()
+        .file
+          target: "#{scratch}/file"
+          match: /will never work/
+          replace: 'Add this line'
+          append: /^.*we.*$/m
+        , (err, status) ->
+          status.should.be.true() unless err
+        .file.assert
+          target: "#{scratch}/file"
+          content: 'here we are\nAdd this line\nyou coquin\nshould we\nhave fun'
+        .then next

     describe 'place_before/place_after multiple times if regexp with global flag', ->

       they 'place_before', (ssh, next) ->
-        # Prepare by creating a file with content
-        mecano.file
-          ssh: ssh
+        mecano
+          ssh: ssh
+        .file
           target: "#{scratch}/file"
           content: 'here we are\nyou coquin\nshould we\nhave fun'
-        , (err) ->
-          # File does not exist, it create it with the content
-          mecano.file
-            ssh: ssh
-            target: "#{scratch}/file"
-            match: /will never work/
-            replace: 'Add this line'
-            place_before: /^.*we.*$/gm
-          , (err, written) ->
-            return next err if err
-            written.should.be.true()
-            # Check file content
-            fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-              return next err if err
-              content.should.eql 'Add this line\nhere we are\nyou coquin\nAdd this line\nshould we\nhave fun'
-              next()
+        .file
+          target: "#{scratch}/file"
+          match: /will never work/
+          replace: 'Add this line'
+          place_before: /^.*we.*$/gm
+        , (err, status) ->
+          status.should.be.true() unless err
+        .file.assert
+          target: "#{scratch}/file"
+          content: 'Add this line\nhere we are\nyou coquin\nAdd this line\nshould we\nhave fun'
+        .then next

       they 'place_after', (ssh, next) ->
         # Prepare by creating a file with content
-        mecano.file
-          ssh: ssh
+        mecano
+          ssh: ssh
+        .file
           target: "#{scratch}/file"
           content: 'here we are\nyou coquin\nshould we\nhave fun'
-        , (err) ->
-          # File does not exist, it create it with the content
-          mecano.file
-            ssh: ssh
-            target: "#{scratch}/file"
-            match: /will never work/
-            replace: 'Add this line'
-            append: /^.*we.*$/gm
-          , (err, written) ->
-            return next err if err
-            written.should.be.true()
-            # Check file content
-            fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-              return next err if err
-              content.should.eql 'here we are\nAdd this line\nyou coquin\nshould we\nAdd this line\nhave fun'
-              next()
-
-
-    they 'will append place_after a match if append is a string', (ssh, next) ->
-      # Prepare by creating a file with content
-      mecano.file
-        ssh: ssh
-        target: "#{scratch}/file"
-        content: 'here we are\nyou coquin\nshould we\nhave fun'
-      , (err) ->
-        # File does not exist, it create it with the content
-        mecano.file
-          ssh: ssh
+        .file
           target: "#{scratch}/file"
           match: /will never work/
           replace: 'Add this line'
-          append: 'we'
-        , (err, written) ->
-          return next err if err
-          written.should.be.true()
-          # Check file content
-          fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-            return next err if err
-            content.should.eql 'here we are\nAdd this line\nyou coquin\nshould we\nAdd this line\nhave fun'
-            next()
+          append: /^.*we.*$/gm
+        , (err, status) ->
+          status.should.be.true() unless err
+        .file.assert
+          target: "#{scratch}/file"
+          content: 'here we are\nAdd this line\nyou coquin\nshould we\nAdd this line\nhave fun'
+        .then next
+
+
+    they 'will append place_after a match if append is a string', (ssh, next) ->
+      mecano
+        ssh: ssh
+      .file
+        target: "#{scratch}/file"
+        content: 'here we are\nyou coquin\nshould we\nhave fun'
+      .file
+        target: "#{scratch}/file"
+        match: /will never work/
+        replace: 'Add this line'
+        append: 'we'
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/file"
+        content: 'here we are\nAdd this line\nyou coquin\nshould we\nAdd this line\nhave fun'
+      .then next

     describe 'will detect new line if no match', ->

       they 'place_before', (ssh, next) ->
-        # Create file for the test
-        mecano.file
-          ssh: ssh
+        mecano
+          ssh: ssh
+        .file
           target: "#{scratch}/file"
           content: 'here we are\nyou coquin'
-        , (err) ->
-          # File exist, append replace string to it and detect missing line break
-          mecano.file
-            ssh: ssh
-            target: "#{scratch}/file"
-            match: /will never be found/
-            replace: 'Add this line'
-            place_before: true
-          , (err, written) ->
-            return next err if err
-            written.should.be.true()
-            # Check file content
-            fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-              return next err if err
-              content.should.eql 'Add this line\nhere we are\nyou coquin'
-              next()
-
-      they 'place_after', (ssh, next) ->
-        # Create file for the test
-        mecano.file
-          ssh: ssh
-          target: "#{scratch}/file"
-          content: 'here we are\nyou coquin'
-        , (err) ->
-          # File exist, append replace string to it and detect missing line break
-          mecano.file
-            ssh: ssh
-            target: "#{scratch}/file"
-            match: /will never be found/
-            replace: 'Add this line'
-            append: true
-          , (err, written) ->
-            return next err if err
-            written.should.be.true()
-            # Check file content
-            fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-              return next err if err
-              content.should.eql 'here we are\nyou coquin\nAdd this line'
-              next()
-
-    describe 'create file if not exists', ->
-
-      they 'place_before', (ssh, next) ->
-        # File does not exist, it create it with the content
-        mecano.file
-          ssh: ssh
+        .file
           target: "#{scratch}/file"
           match: /will never be found/
           replace: 'Add this line'
           place_before: true
-        , (err, written) ->
-          return next err if err
-          written.should.be.true()
-          # Check file content
-          fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-            return next err if err
-            content.should.eql 'Add this line'
-            next()
+        , (err, status) ->
+          status.should.be.true() unless err
+        .file.assert
+          target: "#{scratch}/file"
+          content: 'Add this line\nhere we are\nyou coquin'
+        .then next

       they 'place_after', (ssh, next) ->
-        # File does not exist, it create it with the content
-        mecano.file
-          ssh: ssh
+        mecano
+          ssh: ssh
+        .file
+          target: "#{scratch}/file"
+          content: 'here we are\nyou coquin'
+        .file
           target: "#{scratch}/file"
           match: /will never be found/
           replace: 'Add this line'
           append: true
-        , (err, written) ->
-          return next err if err
-          written.should.be.true()
-          # Check file content
-          fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-            return next err if err
-            content.should.eql 'Add this line'
-            next()
+        , (err, status) ->
+          status.should.be.true() unless err
+        .file.assert
+          target: "#{scratch}/file"
+          content: 'here we are\nyou coquin\nAdd this line'
+        .then next
+
+    describe 'create file if not exists', ->
+
+      they 'place_before', (ssh, next) ->
+        mecano
+          ssh: ssh
+        .file
+          target: "#{scratch}/file"
+          match: /will never be found/
+          replace: 'Add this line'
+          place_before: true
+        , (err, status) ->
+          status.should.be.true() unless err
+        .file.assert
+          target: "#{scratch}/file"
+          content: 'Add this line'
+        .then next
+
+      they 'place_after', (ssh, next) ->
+        mecano
+          ssh: ssh
+        .file
+          target: "#{scratch}/file"
+          match: /will never be found/
+          replace: 'Add this line'
+          append: true
+        , (err, status) ->
+          status.should.be.true() unless err
+        .file.assert
+          target: "#{scratch}/file"
+          content: 'Add this line'
+        .then next

     they 'match is optional', (ssh, next) ->
       mecano
@@ -922,61 +839,56 @@
         target: "#{scratch}/a_file"
         replace: 'Add this line'
         append: true
-      , (err, written) ->
-        written.should.be.true()
+      , (err, status) ->
+        status.should.be.true() unless err
       .file
         target: "#{scratch}/a_file"
         replace: 'Add this line'
         append: true
-      , (err, written) ->
-        written.should.be.false()
+      , (err, status) ->
+        status.should.be.false() unless err
       .file
         target: "#{scratch}/a_file"
         write: [
           replace: 'Add this line'
           append: true
         ]
-      , (err, written) ->
-        written.should.be.false()
-      .call (options, next) ->
-        fs.readFile ssh, "#{scratch}/a_file", 'utf8', (err, content) ->
-          content.should.eql 'Here we are\nyou coquin\nAdd this line'
-          next()
+      , (err, status) ->
+        status.should.be.false() unless err
+      .file.assert
+        target: "#{scratch}/a_file"
+        content: 'Here we are\nyou coquin\nAdd this line'
       .then next

   describe 'backup', ->

     they 'create a file', (ssh, next) ->
       # First we create a file
-      mecano.file
-        ssh: ssh
-        target: "#{scratch}/file"
-        content: 'Hello'
-      , (err, written) ->
-        return next err if err
-        # If nothing has change, there should be no backup
-        mecano.file
-          ssh: ssh
-          target: "#{scratch}/file"
-          content: 'Hello'
-          backup: '.bck'
-        , (err, written) ->
-          return next err if err
-          written.should.be.false()
-          fs.exists ssh, "#{scratch}/file.bck", (err, exists) ->
-            exists.should.be.false()
-            # If content is different, check the backup
-            mecano.file
-              ssh: ssh
-              target: "#{scratch}/file"
-              content: 'Hello Node'
-              backup: '.bck'
-            , (err, written) ->
-              return next err if err
-              written.should.be.true()
-              fs.readFile ssh, "#{scratch}/file.bck", 'utf8', (err, content) ->
-                content.should.eql 'Hello'
-                next()
+      mecano
+        ssh: ssh
+      .file
+        target: "#{scratch}/file"
+        content: 'Hello'
+      .file
+        target: "#{scratch}/file"
+        content: 'Hello'
+        backup: '.bck'
+      , (err, status) ->
+        status.should.be.false() unless err
+      .call (_, callback) ->
+        fs.exists ssh, "#{scratch}/file.bck", (err, exists) ->
+          exists.should.be.false() unless err
+          callback err
+      .file
+        target: "#{scratch}/file"
+        content: 'Hello Node'
+        backup: '.bck'
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/file.bck"
+        content: 'Hello'
+      .then next

     they 'a non-existing file', (ssh, next) ->
       mecano.file
@@ -984,102 +896,89 @@
         target: "#{scratch}/new_file"
         content: 'Hello'
         backup: true
-      , (err, written) ->
-        written.should.be.true() unless err
-        next err
-
+      , (err, status) ->
+        status.should.be.true() unless err
+      .then next

   describe 'write', ->

     they 'do multiple replace', (ssh, next) ->
-      # First we create a file
-      mecano.file
-        ssh: ssh
+      mecano
+        ssh: ssh
+      .file
         target: "#{scratch}/file"
         content: 'username: me\nemail: my@email\nfriends: you'
-      , (err, written) ->
-        return next err if err
-        mecano.file
-          ssh: ssh
-          target: "#{scratch}/file"
-          write: [
-            match: /^(username).*$/m
-            replace: "$1: you"
-          ,
-            match: /^email.*$/m
-            replace: ""
-          ,
-            match: /^(friends).*$/m
-            replace: "$1: me"
-          ]
-        , (err, written) ->
-            return next err if err
-            written.should.be.true()
-            fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-              return next err if err
-              content.should.eql 'username: you\n\nfriends: me'
-              next()
+      .file
+        target: "#{scratch}/file"
+        write: [
+          match: /^(username).*$/m
+          replace: "$1: you"
+        ,
+          match: /^email.*$/m
+          replace: ""
+        ,
+          match: /^(friends).*$/m
+          replace: "$1: me"
+        ]
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/file"
+        content: 'username: you\n\nfriends: me'
+      .then next

     they 'use append', (ssh, next) ->
-      # First we create a file
-      mecano.file
-        ssh: ssh
+      mecano
+        ssh: ssh
+      .file
         target: "#{scratch}/file"
         content: 'username: me\nfriends: you'
-      , (err, written) ->
-        return next err if err
-        mecano.file
-          ssh: ssh
-          target: "#{scratch}/file"
-          write: [
-            match: /^(username).*$/m
-            replace: "$1: you"
-          ,
-            match: /^email.*$/m
-            replace: "email: your@email"
-            append: 'username'
-          ,
-            match: /^(friends).*$/m
-            replace: "$1: me"
-          ]
-        , (err, written) ->
-            return next err if err
-            written.should.be.true()
-            fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-              return next err if err
-              content.should.eql 'username: you\nemail: your@email\nfriends: me'
-              next()
+      .file
+        target: "#{scratch}/file"
+        write: [
+          match: /^(username).*$/m
+          replace: "$1: you"
+        ,
+          match: /^email.*$/m
+          replace: "email: your@email"
+          append: 'username'
+        ,
+          match: /^(friends).*$/m
+          replace: "$1: me"
+        ]
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/file"
+        content: 'username: you\nemail: your@email\nfriends: me'
+      .then next

     they 'handle partial match', (ssh, next) ->
       # First we create a file
-      mecano.file
-        ssh: ssh
+      mecano
+        ssh: ssh
+      .file
         target: "#{scratch}/file"
         content: 'username: me\nfriends: none'
-      , (err, written) ->
-        return next err if err
-        # First write will not find a match
-        mecano.file
-          ssh: ssh
-          target: "#{scratch}/file"
-          write: [
-            match: /^will never match$/m
-            replace: "useless"
-          ,
-            match: /^email.*$/m
-            replace: "email: my@email"
-            append: 'username'
-          ,
-            match: /^(friends).*$/m
-            replace: "$1: you"
-          ]
-        , (err, written) ->
-            return next err if err
-            written.should.be.true()
-            fs.readFile ssh, "#{scratch}/file", 'utf8', (err, content) ->
-              return next err if err
-              content.should.eql 'username: me\nemail: my@email\nfriends: you'
-              next()
+      .file
+        target: "#{scratch}/file"
+        write: [
+          match: /^will never match$/m
+          replace: "useless"
+        ,
+          match: /^email.*$/m
+          replace: "email: my@email"
+          append: 'username'
+        ,
+          match: /^(friends).*$/m
+          replace: "$1: you"
+        ]
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/file"
+        content: 'username: me\nemail: my@email\nfriends: you'
+      .then next

   describe 'error', ->

@@ -1098,7 +997,7 @@
         ssh: ssh
         target: "#{scratch}/file"
         source: "#{scratch}/does/not/exists"
-      , (err, written) ->
+      , (err) ->
         err.message.should.eql "Source does not exist: \"#{scratch}/does/not/exists\""
         next()

@@ -1108,32 +1007,37 @@
         target: "#{scratch}/file"
         source: "#{scratch}/does/not/exists"
         local: true
-      , (err, written) ->
+      , (err) ->
         err.message.should.eql "Source does not exist: \"#{scratch}/does/not/exists\""
         next()

   describe 'eof', ->

     they 'auto-detected', (ssh, next) ->
-      mecano.file
-        ssh: ssh
+      mecano
+        ssh: ssh
+      .file
         target: "#{scratch}/file"
         content: 'this is\r\nsome content'
         eof: true
-      , (err) ->
-        return next err if err
-        fs.readFile ssh, "#{scratch}/file", (err, content) ->
-          content.toString().should.eql 'this is\r\nsome content\r\n'
-          next()
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/file"
+        content: 'this is\r\nsome content\r\n'
+      .then next

     they 'not detected', (ssh, next) ->
-      mecano.file
+      mecano
+        ssh: ssh
+      .file
         ssh: ssh
         target: "#{scratch}/file"
         content: 'this is some content'
         eof: true
-      , (err) ->
-        return next err if err
-        fs.readFile ssh, "#{scratch}/file", (err, content) ->
-          content.toString().should.eql 'this is some content\n'
-          next()
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/file"
+        content: 'this is some content\n'
+      .then next


replacing should.be.eql wtih should.eql

replacing should.be.eql wtih should.eql

--- test/giphy.spec.coffee
+++ test/giphy.spec.coffee
@@ -394,7 +394,7 @@
         callback.should.have.been.called.once
         callback.should.have.been.calledWith 'testing'
         should.exist result
-        result.should.be.eql 'result'
+        result.should.eql 'result'

       it 'calls the callback with a multiple value collection', ->
         callback = @fakes.stub().returns 'result'
@@ -402,7 +402,7 @@
         callback.should.have.been.called.once
         callback.should.have.been.calledWith sinon.match('testing1').or sinon.match('testing2')
         should.exist result
-        result.should.be.eql 'result'
+        result.should.eql 'result'

       it 'handles null or empty data', ->
         callback = sinon.spy()
@@ -415,7 +415,7 @@
       it 'returns .data.images.original.url', ->
         uri = @giphy.getUriFromResultData sampleData
         should.exist uri
-        uri.should.be.eql sampleData.images.original.url
+        uri.should.eql sampleData.images.original.url

       it 'does not return a uri for invalid input', ->
         uri = @giphy.getUriFromResultData null


added test for #15

added test for #15

--- test/index_test.coffee
+++ test/index_test.coffee
@@ -130,6 +130,15 @@
         )
       done
     )
+
+  it "should remove the ast property when done", (done) ->
+
+    vinylfs.src("#{dir}/fixtures/core/*.js")
+      .pipe(amdOptimize("index"))
+      .on("data", (file) ->
+        assert(not ("ast" in file))
+      )
+      .on("end", done)

 describe "src", ->


adapt  to use result object, give path.path to error message

--- test/lib/fspath.coffee
+++ test/lib/fspath.coffee
@@ -56,7 +56,6 @@
   ]
   rootDirs = [ '.git', 'lib', 'test', 'node_modules' ]
   rootPaths = [].concat(rootDirs).concat(rootFiles)
-

   # path.path
   describe 'constructor', ->
@@ -903,7 +902,7 @@
           path.list (error, result) ->
             if error? then return done error
             for path in result.paths
-              assert (path.path in rootPaths), 'results shouldnt have |' + path + '|'
+              assert (path.path in rootPaths), 'results shouldnt have |' + path.path + '|'
             done()

       describe 'with only `done` option', ->
@@ -913,7 +912,7 @@
           path.list done:(error, result) ->
             if error? then return done error
             for path in result.paths
-              assert (path.path in rootPaths), 'results shouldnt have |' + path + '|'
+              assert (path.path in rootPaths), 'results shouldnt have |' + path.path + '|'
             done()

       describe 'with only `all` option', ->
@@ -923,7 +922,7 @@
           path.list all:(error, result) ->
             if error? then return done error
             for path in result.paths
-              assert (path.path in rootPaths), 'results shouldnt have |' + path + '|'
+              assert (path.path in rootPaths), 'results shouldnt have |' + path.path + '|'
             done()

       describe 'with `each` and `done` option', ->
@@ -931,12 +930,12 @@
         it 'should list all the project root paths', (done) ->
           path = new Path
           path.list
-            each:(path) ->
-              assert (path.path in rootPaths), 'each shouldnt receive : |' + path + '|'
+            each:(result) ->
+              assert (result.path.path in rootPaths), 'each shouldnt receive : |' + result.path.path + '|'
             done: (error, result) ->
               if error? then return done error
               for path in result.paths
-                assert (path.path in rootPaths), 'results shouldnt have |' + path + '|'
+                assert (path.path in rootPaths), 'results shouldnt have |' + path.path + '|'
               done()

@@ -949,7 +948,7 @@
             done:(error, result) ->
               if error? then return done error
               for path in result.paths
-                assert (path.path in rootPaths), 'results shouldnt have |' + path + '|'
+                assert (path.path in rootPaths), 'results shouldnt have |' + path.path + '|'
               done()

       describe 'with `accept` \'lib\' and `done` option', ->
@@ -973,7 +972,7 @@
           path = new Path
           result = path.list()
           for path in result.paths
-            assert (path.path in rootPaths), 'results shouldnt have |' + path + '|'
+            assert (path.path in rootPaths), 'results shouldnt have |' + path.path + '|'

       describe 'with only `accept` option', ->

@@ -981,7 +980,7 @@
           path = new Path
           result = path.list accept:-> true
           for path in result.paths
-            assert (path.path in rootPaths), 'results shouldnt have |' + path + '|'
+            assert (path.path in rootPaths), 'results shouldnt have |' + path.path + '|'

         it 'accept \'lib\' should only list lib', ->
           path = new Path
@@ -996,9 +995,9 @@
           path = new Path
           result = path.list each:(path) -> each[path.path] = true
           for path in result.paths
-            assert (path.path in rootPaths), 'results shouldnt have |' + path + '|'
+            assert (path.path in rootPaths), 'results shouldnt have |' + path.path + '|'
           for path in rootPaths
-            assert each[path]?, 'each should have received path: |' + path + '|'
+            assert each[path]?, 'each should have received path: |' + path.path + '|'

   # path.files
@@ -1008,7 +1007,7 @@
       path.files (error, result) ->
         if error? then return done error
         for path in result.paths
-          assert (path.path in rootFiles), 'results shouldnt have |' + path + '|'
+          assert (path.path in rootFiles), 'results shouldnt have |' + path.path + '|'
         done()

   describe 'sync files', ->
@@ -1016,7 +1015,7 @@
       path = new Path
       result = path.files()
       for path in result.paths
-        assert (path.path in rootFiles), 'results shouldnt have |' + path + '|'
+        assert (path.path in rootFiles), 'results shouldnt have |' + path.path + '|'

   # path.dirs
@@ -1026,7 +1025,7 @@
       path.dirs (error, result) ->
         if error? then return done error
         for path in result.paths
-          assert (path.path in rootDirs), 'results shouldnt have |' + path + '|'
+          assert (path.path in rootDirs), 'results shouldnt have |' + path.path + '|'
         done()

   describe 'sync dirs', ->
@@ -1034,7 +1033,7 @@
       path = new Path
       result = path.dirs()
       for path in result.paths
-        assert (path.path in rootDirs), 'results shouldnt have |' + path + '|'
+        assert (path.path in rootDirs), 'results shouldnt have |' + path.path + '|'

   # path.reset
   describe 'reset', ->


Oscillator

Oscillator

--- test/oscillator.coffee
+++ test/oscillator.coffee
@@ -14,7 +14,7 @@
   o = Oscillator
     period: 30
     amplitude: 10
-    offset: Math.
+    offset: Math.TAU /4

   o(0) # => 0
   o(30) # => 0


Issue #20: use relative scheme-relative URL in async.coffee

The previous fix by @gingerlime only changed the generated JS and gets
squashed whenever we recompile the CoffeeScript sources.

--- async.coffee
+++ async.coffee
@@ -1,5 +1,5 @@

-ZXCVBN_SRC = 'http://dl.dropbox.com/u/209/zxcvbn/zxcvbn.js'
+ZXCVBN_SRC = '//dl.dropbox.com/u/209/zxcvbn/zxcvbn.js'

 # adapted from http://friendlybit.com/js/lazy-loading-asyncronous-javascript/
 async_load = ->


Link to hOCR reader

Link to hOCR reader

--- src/js/ogl-browse.coffee
+++ src/js/ogl-browse.coffee
@@ -3,7 +3,7 @@

 repos = []

-add_hathitrust_repo = (repo_li_id, identifier) ->
+add_hathitrust_repo = (repo_li_id, identifier, repo_name) ->
   console.log('add hathitrust repo: ' + identifier)
   repo_li = $("##{repo_li_id}")
   loader = ($('<div>').attr('class','ui active mini loader'))
@@ -25,6 +25,7 @@
       for record_key,record of data.records
         # console.log(record)
         repo_li.append($('<p>').append($('<a>').attr('href',record.recordURL).attr('target','_blank').text(identifier + ' on HathiTrust')))
+        repo_li.append($('<p>').append($('<a>').attr('href','https://ryanfb.github.io/hocr-reader/#/read/OpenGreekAndLatin/' + repo_name).attr('target','_blank').text(identifier + ' in hOCR Reader')))
         xml = $.parseXML(record['marc-xml'])
         # console.log(xml)
         for key in ['245','100','243','260'] # 500 504 700
@@ -41,12 +42,13 @@
         repo_li.append($('<p>').text(enumcron))
       loader.remove()

-add_archive_repo = (repo_li_id, identifier) ->
+add_archive_repo = (repo_li_id, identifier, repo_name) ->
   console.log('add archive repo: ' + identifier)
   # $.ajax "https://archive.org/details/#{identifier}&output=json",
   archive_link = $('<a>').attr('href',"https://archive.org/details/#{identifier}").attr('target','_blank').text(identifier + ' on archive.org')
   repo_li = $("##{repo_li_id}")
   repo_li.append($('<p>').append(archive_link))
+  repo_li.append($('<p>').append($('<a>').attr('href','https://ryanfb.github.io/hocr-reader/#/read/OpenGreekAndLatin/' + repo_name).attr('target','_blank').text(identifier + ' in hOCR Reader')))
   loader = ($('<div>').attr('class','ui active mini loader'))
   repo_li.append(loader)
   $.ajax "https://openlibrary.org/ia/#{identifier}.json",
@@ -86,9 +88,9 @@
           ocr_identifier = ocr_identifier.replace(/\.ark-/,'.ark:').replace(/-/g,'/')
         else
           ocr_identifier = ocr_identifier.replace(/\.-/,'.$')
-        add_hathitrust_repo(repo_li_id, ocr_identifier)
+        add_hathitrust_repo(repo_li_id, ocr_identifier, repo.name)
       else # archive.org
-        add_archive_repo(repo_li_id, ocr_identifier)
+        add_archive_repo(repo_li_id, ocr_identifier, repo.name)

 grab_repo_page = (url, callback) ->
   console.log('grab_repo_page: ' + url)


Make leaflet latlng equality error margin larger.

The original stricter margin caused the map to automatically
drift westwards when west enough, because the map does a project+unproject
for the same point when fitting the center inside world bounds.

--- src/map-base-view.coffee
+++ src/map-base-view.coffee
@@ -2,7 +2,7 @@
     Backbone         = require 'backbone'
     Marionette       = require 'backbone.marionette'
     i18n             = require 'i18next'
-    leaflet          = require 'leaflet'
+    L                = require 'leaflet'
     markercluster    = require 'leaflet.markercluster'
     leaflet_snogylop = require 'leaflet.snogylop'

@@ -23,6 +23,8 @@

     if getIeVersion() and getIeVersion() < 9
         ICON_SIZE *= .8
+
+    L.extend L.LatLng, MAX_MARGIN: 1.0e-7

     class MapBaseView extends Backbone.Marionette.View
         @WORLD_LAT_LNGS: [


Expressions of the form e**f(x) can now be differentiated

--- src/operators/Pow.coffee
+++ src/operators/Pow.coffee
@@ -374,13 +374,26 @@
             Add = require("operators/Add")
             Mul = require("operators/Mul")
             Constant = require("terminals").Constant
-            if variable in @children.right.getAllVariables
-                throw new Error("I can't differentiate with a variable on the top of a power")
+
+            if variable in @children.right.getAllVariables()
+                if ((@children.left instanceof terminals.SymbolicConstant and
+                    (@children.left.label == "e" or @children.left.label == "E")) or
+                    (@children.left instanceof terminals.Constant and
+                    Math.abs(@children.left.evaluate() - Math.E) < 5e-15))
+                        # There's something very close to \e on the bottom, so we're
+                        # good to go.
+                        # The derivative of e^f(x) is f'(x) e^f(x).
+                        powerDerivative = @children.right.differentiate(variable, equivalencies)
+                        return new Mul(powerDerivative, @copy())
+                    else
+                        throw new Error("Can't differentiate variable in exponent")
+
             if @children.right.evaluate?() == 0
                 return new Constant(0)
+
             return new Mul(new Pow(@children.left, new Add(@children.right, new Constant(-1))),
-                                         @children.left.differentiate(variable, equivalencies),
-                                         @children.right).expandAndSimplify(equivalencies)
+                                   @children.left.differentiate(variable, equivalencies),
+                                   @children.right).expandAndSimplify(equivalencies)

         # Check if this node contains a given variable.
         #


Update to use sentence case 'Log out'

Update to use sentence case 'Log out'

--- tutor/src/components/navbar/logout.cjsx
+++ tutor/src/components/navbar/logout.cjsx
@@ -12,7 +12,7 @@
     label: React.PropTypes.string

   getDefaultProps: ->
-    label: 'Log Out'
+    label: 'Log out'

   onLinkClick: (ev) ->
     ev.currentTarget.querySelector('form').submit()