Added errors handling for private methods. Fixed issue #1

--- main.coffee
+++ main.coffee
@@ -90,7 +90,11 @@
 getObjFields = (target, fieldname, value) ->
   keys = extractKeys fieldname
   if typeof keys is "string"
-    return assign {}, target, {"#{fieldname}": rescueTypes value}
+    unless "#{Number fieldname}" is "NaN"
+      throw new TypeError "Top-level field name must be a string"
+
+    target[fieldname] = rescueTypes value
+    return target

   res = reduce.call (do keys.reverse), reducer, rescueTypes value
   return rescueObjStruct res, target
@@ -119,7 +123,11 @@

   bb = new Busboy assign {}, {headers: req.headers}, op

-  onField = (name, value) -> fields = getObjFields fields, name, value
+  onField = (name, value) ->
+    try
+      fields = getObjFields fields, name, value
+    catch err
+      return reject err

   onFile = (fieldname, stream, filename, enc, mime) ->
     tmpPath = "#{do tmpDir}/#{do shortid}#{extname filename}"
@@ -128,7 +136,10 @@
       file.originalName = filename
       file.enc = enc
       file.mime = mime
-      files = getObjFields files, fieldname, file
+      try
+        files = getObjFields files, fieldname, file
+      catch err
+        return reject err

     stream
       .on "end", onFileStreamEnd


fix formatter

fix formatter

Signed-off-by: Arnaud Richard <791baf8974538c563f287130527dd623cb0e836d@webshell.io>

--- lib/formatters.coffee
+++ lib/formatters.coffee
@@ -45,7 +45,7 @@
 			if res.statusStr == 'error'
 				result.code = res.statusCode if res.statusCodeInternal
 				result.message = res.message
-				result.data = body if body? && Object.keys(body).length
+				result.data = body if typeof body == 'object' && Object.keys(body).length
 			else
 				body = null if not body?
 				result.data = body


Don't gzip css files, they're not served anyhow

--- js-library/Gruntfile.coffee
+++ js-library/Gruntfile.coffee
@@ -99,8 +99,6 @@
     shell:
       gzip_js_files:
         command: ' find build/ -iname \'*.js\'  -exec bash -c \' gzip -9 -f < "{}" > "{}.gz" \' \\; '
-      gzip_css_files:
-        command: ' find build/ -iname \'*.css\'  -exec bash -c \' gzip -9 -f < "{}" > "{}.gz" \' \\; '

     copy:
       build:
@@ -171,7 +169,7 @@
     'clean', 'copy:build',  'copy:start_stop_files', 'coffee','copy:postFactlinkObject',
     'sass', 'cssUrlEmbed', 'cssmin',
     'concat', 'uglify', 'code_inliner',
-    'shell:gzip_css_files', 'shell:gzip_js_files', 'copy:dist'
+    'shell:gzip_js_files', 'copy:dist'
   ]
   grunt.registerTask 'test',    ['qunit']


Handle pane overlaps with decorations

Handle pane overlaps with decorations

--- lib/jumpy-view.coffee
+++ lib/jumpy-view.coffee
@@ -156,6 +156,18 @@

             editorView.classList.add 'jumpy-jump-mode'

+            getVisibleColumnRange = (editorView) ->
+                charWidth = editorView.getDefaultCharacterWidth()
+                # FYI: asserts:
+                # numberOfVisibleColumns = editorView.getWidth() / charWidth
+                minColumn = editorView.getScrollLeft() / charWidth
+                maxColumn = editorView.getScrollRight() / charWidth
+
+                return [
+                    minColumn
+                    maxColumn
+                ]
+
             drawLabels = (lineNumber, column) =>
                 return unless nextKeys.length

@@ -184,6 +196,7 @@
                     position: 'head'
                 @decorations.push decoration

+            [minColumn, maxColumn] = getVisibleColumnRange editorView
             [firstVisibleRow, lastVisibleRow] = editor.getVisibleRowRange()
             # TODO: Right now there are issues with lastVisbleRow
             for lineNumber in [firstVisibleRow...lastVisibleRow]
@@ -192,7 +205,11 @@
                     drawLabels lineNumber, 0
                 else
                     while ((word = wordsPattern.exec(lineContents)) != null)
-                        drawLabels lineNumber, word.index
+                        column = word.index
+                        # Do not do anything... markers etc.
+                        # if the columns are out of bounds...
+                        if column > minColumn && column < maxColumn
+                            drawLabels lineNumber, column

             @initializeClearEvents(editorView)


syntax colors

syntax colors

--- js/codemirror-syntax.coffee
+++ js/codemirror-syntax.coffee
@@ -1,5 +1,5 @@
 OPERATOR = "tag strong"
-OPERATOR_INLINE = "string"
+OPERATOR_INLINE = "tag strong"

 class Mode
  constructor: (CodeMirror) ->
@@ -221,7 +221,7 @@
    l = "#{l}"
   else if types.code
    stream.skipToEnd()
-   l = "comment"
+   l = "meta"
   else
    if state.start
     match = @matchStart stream, state
@@ -236,7 +236,7 @@
    if state.heading
     l += " header"
    if state.comment
-    l += " meta"
+    l += " comment"
    if state.bold
     l += " strong"
    if state.italics
@@ -246,7 +246,7 @@
    if state.inlineMedia
     l += " link"
    if state.code
-    l += " comment"
+    l += " meta"

   return l


Formatting.

Formatting.

--- src/debug.coffee
+++ src/debug.coffee
@@ -18,7 +18,7 @@
         constructor: (@app_control) ->
             _.extend @, Backbone.Events
             @add_listeners()
-
+
         add_listeners: ->
             interceptor = (variable_name) ->
                 (event_name, target, rest...) ->


Use id's to transition page instead of rendered page element.

--- js/routers/AppRouter.coffee
+++ js/routers/AppRouter.coffee
@@ -8,13 +8,13 @@
     collection = new app.Collections.ActivityCollection
     @view = new app.Views.Home collection: collection
     collection.fetch()
-
+    @view.render()
     flight.goTo "#panel-1"

   goToPanel: (id) ->
     @view = new app.Views["Panel_" + id]
-    page = @view.render(id).el
-    flight.goTo page
+    @view.render(id).el
+    flight.goTo "#panel-" + id

 @app = window.app ? {}
 @app.Routers.AppRouter = AppRouter


don't call if cv isn't a function

don't call if cv isn't a function

--- src/index.coffee
+++ src/index.coffee
@@ -14,7 +14,7 @@
 #  expressions.push p.parse("text: name | filter(name.length() > 5, test | filter(abcde)) | cat(name); css: craig")

-expr = p.parse("text: person.name() | filter(person) ? true : false")
+expr = p.parse("text: person.name().fish ? true : false")

 clip = new Clip({ person: { count: 1, name: "craig" }}, {


fix push options

fix push options

--- src/index.coffee
+++ src/index.coffee
@@ -73,7 +73,7 @@
     log stdout
     error stderr
   .then ->
-    push { repository, src, dst }
+    push { directory, dst, repository, src }
   .then ({ stdout, stderr }) ->
     log stdout
     error stderr


* remove unessary comments

* remove unessary comments

--- src/index.coffee
+++ src/index.coffee
@@ -29,8 +29,6 @@
         value = dest.validateMax(value) if dest instanceof StringType
         value

-  #@stringToValue:-> #must be overrided this method!
-
   validateMin: (value)->
     throw new TypeError 'the min should be less than max:' + value if value > @_max
     try value = parseInt(value) if isString value


added CorrectedStream class

added CorrectedStream class

--- src/index.coffee
+++ src/index.coffee
@@ -1,3 +1,28 @@
+
+{ Readable } = require 'stream'
+
+class CorrectedStream extends Readable
+
+  constructor: (@contentStream, @eolc)->
+    super()
+
+    hasEneded = false
+
+    @contentStream.on 'open', =>
+
+      @contentStream.on 'data', (chunk)=>
+        unless hasEneded
+          [ _, chunk] = LineEndingCorrector.__correct chunk, @eolc
+          @push chunk, 'utf8'
+
+      @contentStream.on 'end', =>
+        unless hasEneded
+          @push null
+        hasEneded = true
+
+    @setEncoding 'utf8'
+
+  _read: (n)=> null

 class LineEndingCorrector

@@ -27,9 +52,26 @@
       newContent = parts.join '\r'
       return [ (content isnt newContent), newContent ]

+  @CorrectedStream: CorrectedStream
+
   @correctSync: (content, optionMap)->
     throw new Error "Expected String" if typeof content isnt 'string'
     { eolc } = LineEndingCorrector.__extractOptions optionMap
     return LineEndingCorrector.__correct content, eolc

+  @correct: (content, optionMap, cbfn)->
+    setImmediate =>
+      try
+        [ wasAltered, output ] = LineEndingCorrector.correctSync content, optionMap
+      catch ex
+        return cbfn ex
+      return cbfn null, wasAltered, output
+
+  @correctStream: (contentStream, optionMap)->
+    { eolc } = LineEndingCorrector.__extractOptions optionMap
+    return new LineEndingCorrector.CorrectedStream contentStream, eolc
+
+
+
+
 @LineEndingCorrector = LineEndingCorrector


update: index.coffee

update: index.coffee

--- src/index.coffee
+++ src/index.coffee
@@ -14,7 +14,7 @@
 module.exports = {
 	Number, String,
 	Bool, Any, Enum, Maybe, Either, Strict, Data,
-	match, show,
+	match, show, sample, samples, showHtml
 }

 if module.parent is null


Log cache hits and misses

Log cache hits and misses

--- src/index.coffee
+++ src/index.coffee
@@ -26,10 +26,12 @@
       callback err
     else
       if cacheItem && (Date.parse(cacheItem.updatedAt) > (Date.now() - THIRTY_MINUTES))
+        console.dir {url: url, etag: cacheItem?.etag, message: "CACHE HIT"}
         callback null, cacheItem.body
       else
         if cacheItem
           headers["If-None-Match"] = cacheItem.etag
+        console.dir {url: url, etag: cacheItem?.etag, message: "API request"}
         web.get url, headers, (err, response, body) ->
           if err
             callback err


fixed path

fixed path

--- src/test/xml.coffee
+++ src/test/xml.coffee
@@ -348,7 +348,7 @@
         ]

         file = xml.tag('file')
-        stream = createReadStream path.join(__dirname,"..","..","..","filename")
+        stream = createReadStream path.join(__dirname, "..", "filename")
         stream.pipe(file)
         xml.end()


remove fullscreen behaviour

remove fullscreen behaviour

--- client/code/view/view/content.coffee
+++ client/code/view/view/content.coffee
@@ -3,7 +3,6 @@
   boxUrl: window.boxServer

   initialize: ->
-    $('body').addClass('fullscreen')
     @settings (settings) =>
       frag = encodeURIComponent JSON.stringify(settings)
       @setupEasyXdm "#{@boxUrl}/#{@model.get 'box'}/#{settings.source.publishToken}/container.html##{frag}"
@@ -54,10 +53,6 @@
                 error: (model, xhr, options) ->
                   console.warn "Error creating dataset (xhr status: #{xhr.status} #{xhr.statusText})"

-  close: ->
-    $('body').removeClass('fullscreen')
-    super()
-
 class Cu.View.AppContent extends Cu.View.ViewContent
   settings: (callback) ->
     @model.publishToken (publishToken) =>


Fixed bug when start is equal to new posts

--- src/tumblrip.coffee
+++ src/tumblrip.coffee
@@ -291,9 +291,9 @@
         .then -> Q.delay options.delay
         .then ->
           start += options.threads
-          # Since the newest items are on the first indexes, we can stop when
+          # Since posts array is sorted from the newest items, we can stop when
           # we reach (or go beyond) the 'nbNewPosts' value.
-          if start > total or start > nbNewPosts
+          if start > total or start >= nbNewPosts
             1
           else
             threaded start


string literals work. hello_world works.

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -25,7 +25,7 @@
 NEGATE  = -> @negated = not @negated; this

 class Variable
-  constructor: (@name, @type, @namespace, @asm) ->
+  constructor: (@name, @type, @namespace) ->
   mangledName: ->
     "_" + @name

@@ -33,13 +33,20 @@
   constructor: ->
     @names = {}
     @variables = []
+    labelCount = 0
+    @nextLabel = -> "_#{labelCount++}"
   find: (name) ->
     @names[name]
-  createVariable: (name, type, asm) ->
-    variable = new Variable name, type, this, asm
+  createVariable: (name, type) ->
+    variable = new Variable name, type, this
     @names[name] = variable
     @variables.push variable
     variable
+  createLiteralString: (value) ->
+    label = @nextLabel()
+    asm = ":#{label} dat #{value.length}, #{JSON.stringify value}"
+    @variables.push {asm}
+    label

 #### Base

@@ -187,8 +194,8 @@
   compileRoot: ->
     o =
       namespace: new Namespace
-    o.namespace.createVariable "printc", "func(int)", stdlib.printc
-    o.namespace.createVariable "prints", "func(string)", stdlib.prints
+    o.namespace.createVariable("printc", "func(int)").asm = stdlib.printc
+    o.namespace.createVariable("prints", "func(string)").asm = stdlib.prints
     code = @compile o
     codes = [code, "set [0x8ffe], 0\n"]
     for variable in o.namespace.variables
@@ -320,26 +327,29 @@
     funcName = @variable.unwrapAll().value
     funcVariable = o.namespace.find funcName
     throw SyntaxError "undefined variable \"#{funcName}\"" unless funcVariable?
+    if funcVariable.type not in ["func(int)", "func(string)"]
+      throw SyntaxError "can't call variable \"#{funcName}\" of type \"#{funcVariable.type}\""
     throw SyntaxError "function \"#{funcName}\" takes 1 argument" unless @args.length == 1
+    required_arg_type = funcVariable.type.match(/\((.*)\)/)[1]
     arg = @args[0].unwrapAll()
     throw SyntaxError "invalid expression" unless arg instanceof Literal
-    if funcVariable.type is "func(int)"
-      if arg.isAssignable()
-        if (arg_variable = o.namespace.find arg.value)?
-          throw SyntaxError "can't pass argument of type #{arg_variable.type} to function \"#{funcName}\"" unless arg_variable.type is "int"
-          arg_access = "[#{arg_variable.mangledName()}]"
-        else
-          throw SyntaxError "unknown identifier: #{arg.value}"
+    if arg.isAssignable()
+      arg_variable = o.namespace.find arg.value
+      throw SyntaxError "undefined variable: #{arg.value}" unless arg_variable?
+      arg_type = arg_variable.type
+      arg_access = "[#{arg_variable.mangledName()}]"
+    else
+      if arg.value.match /^['"]/
+        arg_type = "string"
+        arg_access = o.namespace.createLiteralString JSON.parse arg.value
       else
+        arg_type = "int"
         arg_access = arg.value
-      return [
-        "set push, #{arg_access}"
-        "jsr #{funcVariable.mangledName()}"
-      ].join("\n")
-    else if funcVariable.type is "func(string)"
-      true
-    else
-      throw SyntaxError "can't call variable \"#{funcName}\" of type \"#{funcVariable.type}\""
+    throw SyntaxError "can't pass argument of type #{arg_type} to function \"#{funcName}\"" unless arg_type is required_arg_type
+    return [
+      "set push, #{arg_access}"
+      "jsr #{funcVariable.mangledName()}"
+    ].join("\n")

   # Tag this invocation as creating a new instance.
   newInstance: ->


Clarify

Clarify

--- src/query.coffee
+++ src/query.coffee
@@ -65,17 +65,19 @@
         x = d
         for s in k.split('.')
           x = x[s] if x?
-        if v instanceof RegExp
+
+        if v instanceof RegExp # Regular expresion
           m = v.test(x)
         else if v instanceof Object
-          if Object.keys(v)[0][0] == '$'
+          if Object.keys(v)[0][0] == '$' # Operators
             m = s2.$m(x, v)
-          else
+          else # Object
             m = (JSON.stringify(v) == JSON.stringify(x))
-        else if Array.isArray(x)
+        else if Array.isArray(x) # Array
           m = v in x
-        else
+        else # Scalar
           m = (x == v)
+
       return false unless m
     true
 }


use gists-clones for parse.coffee

use gists-clones for parse.coffee

--- parse.coffee
+++ parse.coffee
@@ -320,7 +320,7 @@
   glibHash = {}
   gmoduleHash = {}
   gcolorHash = {}
-  folder = __dirname + "/" + "data/gists-files/" + gist.id
+  folder = __dirname + "/" + "data/gists-clones/" + gist.id
   fs.mkdir folder, ->

   # we make a simplified data object for each file


Address issue with tight deadlines.

Address issue with tight deadlines.

--- src/scale.coffee
+++ src/scale.coffee
@@ -10,7 +10,9 @@
 proportional = (config, queueLength) ->
   # up to N concurrent jobs process in time P, but once N>C time becomes 2P, and so on
   waitingTime = Math.ceil(queueLength/config.concurrency)*config.processing
-  availableTime = config.target - config.processing
+  # availableTime should not get < config.processing, as we can never process a
+  # job faster than the mean processing time (on average).
+  availableTime = Math.max(config.processing, config.target - config.processing)
   return waitingTime/availableTime

 min = (a, b) -> if a < b then a else b


Hack in a fix for robot.messageRoom()

Hack in a fix for robot.messageRoom()

Let us override the channel ID returned from userFromParams()

--- src/slack.coffee
+++ src/slack.coffee
@@ -8,6 +8,7 @@
     console.log "Sending message"

     user = @userFromParams(params)
+
     strings.forEach (str) =>
       # Escape this
       str = str.replace(/&/g, '&')
@@ -34,7 +35,15 @@
   userFromParams: (params) ->
     # hubot < 2.4.2: params = user
     # hubot >= 2.4.2: params = {user: user, ...}
-    if params.user then params.user else params
+    params = if params.user then params.user else params
+
+    # Ghetto hack to make robot.messageRoom work with Slack's adapter
+    #
+    # Note: Slack's API here uses rooom ID's, not room names. They look
+    # something like C0capitallettersandnumbershere
+    params.reply_to ||= params.room
+
+    params

   run: ->
     self = @


remove unnecessary require

remove unnecessary require

--- lib/view-models/search-view-model.coffee
+++ lib/view-models/search-view-model.coffee
@@ -1,4 +1,3 @@
-_ = require 'underscore-plus'
 {ViewModel} = require './view-model'

 module.exports =


Some slight changes

Some slight changes

--- src/sound.coffee
+++ src/sound.coffee
@@ -658,3 +658,5 @@
     fromPercent: (percent, total, decimal) ->
       r = Math.pow(10, decimal || 0)
       return Math.round(((total / 100) * percent) * r) / r
+
+#test


Using instanceof. Fixes #19

Using instanceof. Fixes #19

--- lib/split-diff.coffee
+++ lib/split-diff.coffee
@@ -45,7 +45,7 @@
     panes = atom.workspace.getPanes()
     for p in panes
       activeItem = p.getActiveItem()
-      if atom.workspace.isTextEditor(activeItem)
+      if activeItem instanceof TextEditor
         if editor1 == null
           editor1 = activeItem
         else if editor2 == null


Removed an unnecessary use of !!

Removed an unnecessary use of !!

The statement will be cast to a boolean by using `and` or `or`, so no extra casting is required.

--- src/spine.coffee
+++ src/spine.coffee
@@ -319,8 +319,8 @@
     result

   eql: (rec) ->
-    !!(rec and rec.constructor is @constructor and
-        ((rec.cid is @cid) or (rec.id and rec.id is @id)))
+    rec and rec.constructor is @constructor and
+      ((rec.cid is @cid) or (rec.id and rec.id is @id))

   save: (options = {}) ->
     unless options.validate is false


Post-processing of stats -- attack speed cap and crit damage at 200%

Signed-off-by: Ian Macalinao <08370e385f4778cad8fe504ec5445edd3d45bd9a@gmail.com>

--- src/stats.coffee
+++ src/stats.coffee
@@ -105,6 +105,10 @@
     if actives.length > 0
       finalStats.actives = actives

+    # Process stats
+    finalStats.as = 2.5 if finalStats.as > 2.5 # Attack speed cap
+    finalStats.critDmg += 200 # Default crit damage
+
     return finalStats

   ##


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/tween.coffee
+++ src/tween.coffee
@@ -23,6 +23,7 @@
         I[property] = f(t)

   tween: (duration, properties) ->
+    # T
     for property, target of properties
       I.activeTweens[property] =
         end: target


Put the utils package on a diet

Put the utils package on a diet

We don’t need those extra methods just for tide.

--- src/utils.coffee
+++ src/utils.coffee
@@ -1,6 +1,4 @@
 _ = require "underscore"
-Immutable = require "immutable"
-
 class Utils
   builtInClassMethods: Object.getOwnPropertyNames (class Noop).prototype

@@ -14,23 +12,4 @@

     methods

-  isPromise: (value) ->
-    value and
-    (typeof value in ["object", "function"]) and
-    (typeof value.then is "function")
-
-  isCursor: (value) ->
-    value and
-    (typeof value is "object") and
-    (typeof value.cursor is "function")
-
-  isImmutable: (value) ->
-    Immutable.Iterable.isIterable value
-
-  hasBlobURLSupport: ->
-    window and
-    "URL" of window and
-    "revokeObjectURL" of window.URL and
-    "createObjectURL" of window.URL
-
-module.exports = new Utils+module.exports = new Utils


add back mis-deleted lines.

add back mis-deleted lines.

--- src/zappa.coffee
+++ src/zappa.coffee
@@ -235,7 +235,8 @@

           if app.settings['databag']
             args[1].params = data
-
+          # Just forward params to express.
+          res.render.apply res, args

         for name, helper of helpers
           do (name, helper) ->


info command

info command

--- lib/ui/console.coffee
+++ lib/ui/console.coffee
@@ -5,6 +5,10 @@
     @cmd = atom.commands.add 'atom-workspace',
       "julia-client:clear-console": =>
         @c?.clear()
+
+    comm.handle 'info', ({msg}) =>
+      console.log @c
+      @c.info msg

   deactivate: ->
     @cmd.dispose()


allow r, g, b and a values to be set

allow r, g, b and a values to be set

--- framer/Color.coffee
+++ framer/Color.coffee
@@ -5,41 +5,41 @@

 		color = @color

-		# If input is already a Color object, return itself
+		# If input is already a Color object, return a copy
 		if (color instanceof Color) then return color

 		if @color == null
-			@_r = 0
-			@_g = 0
-			@_b = 0
-			@_a = 0
-			@_roundA = 0
+			@r = 0
+			@g = 0
+			@b = 0
+			@a = 0
 			return

 		# Convert input to RGB
 		rgb = inputToRGB(color)

-		@_r = rgb.r
-		@_g = rgb.g
-		@_b = rgb.b
-		@_a = rgb.a
-		@_roundA = Math.round(100*@_a) / 100
+		@r = rgb.r
+		@g = rgb.g
+		@b = rgb.b
+		@a = rgb.a

 	@define "r",
 		get: -> @_r
-		set: (value) -> throw "r is readonly"
+		set: (value) -> @_r = Utils.clamp(value, 0, 255)

 	@define "g",
 		get: -> @_g
-		set: (value) -> throw "g is readonly"
+		set: (value) -> @_g = Utils.clamp(value, 0, 255)

 	@define "b",
 		get: -> @_b
-		set: (value) -> throw "b is readonly"
+		set: (value) -> @_b = Utils.clamp(value, 0, 255)

 	@define "a",
 		get: -> @_a
-		set: (value) -> throw "a is readonly"
+		set: (value) ->
+			@_a = Utils.clamp(value, 0, 1)
+			@_roundA = Math.round(100*@_a) / 100

 	toHex: (allow3Char) ->
 		return rgbToHex(@_r, @_g, @_b, allow3Char)


Frame bug

Frame bug

--- framer/Frame.coffee
+++ framer/Frame.coffee
@@ -42,12 +42,10 @@
 		xmin = Math.min(r1.x, r2.x)
 		ymin = Math.min(r1.y, r2.y)

-		new exports.Frame
+		return new exports.Frame
 			x: xmin
 			y: ymin
 			width: Math.max(r1.x + r1.width, r2.x + r2.width) - xmin
 			height: Math.max(r1.y + r1.height, r2.y + r2.height) - ymin

-		return new exports.Frame frame

-


Warning for for prent layer

Warning for for prent layer

--- framer/Layer.coffee
+++ framer/Layer.coffee
@@ -738,6 +738,8 @@

 			return if layer is @_parent

+			throw Error("Layer.parent: a layer cannot be it's own parent.") if layer is @
+
 			# Check the type
 			if not layer instanceof Layer
 				throw Error "Layer.parent needs to be a Layer object"


serialize and deserialize

serialize and deserialize

--- server.coffee
+++ server.coffee
@@ -15,6 +15,12 @@
 meshbluConfig = new MeshbluConfig().toJSON()

 PORT  = process.env.PORT || 80
+
+passport.serializeUser (user, done) ->
+  done null, JSON.stringify user
+
+passport.deserializeUser (id, done) ->
+  done err, JSON.parse id

 app = express()
 app.use cors()
@@ -53,7 +59,7 @@
 app.get '/api/callback', passport.authenticate('estimote-cloud')
 app.get '/', passport.authenticate('octoblu')
 app.get '/api/octoblu/callback', passport.authenticate('octoblu'), (req, res) ->
-  res.redirect 'http://google.com'
+  res.redirect '/api/authorize'

 server = app.listen PORT, ->
   host = server.address().address


Minor bugfix

Minor bugfix

--- server.coffee
+++ server.coffee
@@ -49,7 +49,7 @@
 		@elements[head][1] = 0 if @elements[head][1] > STAGE_HEIGHT

 	head: ->
-		@elements[7]
+		@elements[SNAKE_LENGTH-1]

 	blocks: (other) ->
 		head = other.elements[(SNAKE_LENGTH-1)]
@@ -62,7 +62,7 @@
 	blocksSelf: ->
 		head = @elements[(SNAKE_LENGTH-1)]
 		collision = false
-		for i in [0..6]
+		for i in [0..(SNAKE_LENGTH-2)]
 			collision = true if head[0] == @elements[i][0] and head[1] == @elements[i][1]

 		return collision


Added comments and TODOs.

Added comments and TODOs.

--- server.coffee
+++ server.coffee
@@ -255,10 +255,15 @@

     return unless @reverseName

+    # TODO: Current code is run too many times, for any update of source collection reverse field is updated, which is big overkill for the most common case of maintaining only the list of IDs
+    # TODO: Probably only existence (adding) of IDs in the reverse field should be maintained here and the content of any additional fields will be taken care by Meta._reverseFields anyway
+
     reverseFields = fieldsToProjection @reverseFields
     source = @sourceCollection.findOne id,
       fields: reverseFields
       transform: null
+
+    # TODO: What if returned source is null, document does not exist (anymore)? Should we then call try to remove the ID?

     selector =
       _id: value._id
@@ -405,6 +410,13 @@
         field.updatedWithValue id, v

       if field.reverseName
+        # In updatedWithValue we added possible new entry/ies to reverse fields, but here
+        # we have also to remove those which were maybe removed from the value and are
+        # not referencing anymore a document which got added the entry to its reverse
+        # field in the past. So we make sure that only those documents which are still in
+        # the value have the entry in their reverse fields by creating a query which pulls
+        # the entry from all other.
+
         pathSegments = name.split('.')

         if field.ancestorArray


add run all tests

add run all tests

--- lib/atom-python-test.coffee
+++ lib/atom-python-test.coffee
@@ -34,6 +34,8 @@
     @subscriptions.add atom.commands.add 'atom-workspace', 'atom-python-test:run-all-tests': => @runAllTests()
     @subscriptions.add atom.commands.add 'atom-workspace', 'atom-python-test:run-test-under-cursor': => @runTestUnderCursor()
     @subscriptions.add atom.commands.add 'atom-workspace', 'atom-python-test:close-panel': => @closePanel()
+    @subscriptions.add atom.commands.add 'atom-workspace', 'atom-python-test:run-all-project-tests': => @runAllProjectTests()
+

   deactivate: ->
     @subscriptions.dispose()
@@ -133,5 +135,10 @@
     filePath = file?.path
     @executePyTest(filePath)

+  runAllProjectTests: () ->
+    editor = atom.workspace.getActivePaneItem()
+    fullPath = atom.project.relativizePath(editor.getBuffer().file.path)
+    @executePyTest(fullPath[0])
+
   closePanel: ->
       @atomPythonTestView.destroy()


clean json hash

clean json hash

--- lib/beer_can/ability.coffee
+++ lib/beer_can/ability.coffee
@@ -142,8 +142,8 @@

   defaultAliasActions: ->
-    'read':   ['index', 'show'],
-    'create': ['new'],
-    'update': ['edit'],
+    'read':   ['index', 'show']
+    'create': ['new']
+    'update': ['edit']


client: home: minor: added forgotten param

--- client/app/lib/util/showStackEditor.coffee
+++ client/app/lib/util/showStackEditor.coffee
@@ -16,7 +16,7 @@
     EDITOR_WINDOW?.resizeTo 258, 140

     # (re)open the window
-    EDITOR_WINDOW = window.open route, params
+    EDITOR_WINDOW = window.open route, name, params

     # in case popup blocked
     unless EDITOR_WINDOW


Reordering a couple lines

Reordering a couple lines

--- app/controllers/pot.coffee
+++ app/controllers/pot.coffee
@@ -12,8 +12,8 @@
   constructor: ->
     super
     @html require('views/pot')()
+    @pot.bind 'change', @render
     Spine.bind 'result', @show_winner
-    @pot.bind 'change', @render

   show_winner: (message) =>
     @winner_message.html message


enhance: display risk text in compare summary

--- app/models/analysis.coffee
+++ app/models/analysis.coffee
@@ -19,5 +19,23 @@
     risk isnt ENUMS.RISK.NONE
   ).property "risk"

+  labelClass:( ->
+    cls = 'tag'
+    switch @get "risk"
+      when ENUMS.RISK.UNKNOWN then "#{cls} is-default is-progress"
+      when ENUMS.RISK.NONE then "#{cls} is-success"
+      when ENUMS.RISK.LOW then "#{cls} is-info"
+      when ENUMS.RISK.MEDIUM then "#{cls} is-warning"
+      when ENUMS.RISK.HIGH then "#{cls} is-danger"
+  ).property "risk"
+
+  riskText:( ->
+    switch @get "risk"
+      when ENUMS.RISK.UNKNOWN then "Scanning"
+      when ENUMS.RISK.NONE then "None"
+      when ENUMS.RISK.LOW then "Low"
+      when ENUMS.RISK.MEDIUM then "Medium"
+      when ENUMS.RISK.HIGH then "High"
+  ).property "risk"

 `export default Analysis`


исправил таймер на полминуты

исправил таймер на полминуты

--- app/views/feed/feed.coffee
+++ app/views/feed/feed.coffee
@@ -22,10 +22,9 @@
 		this.box.$el.on 'send', () =>
 			this.news.fetch()

-
 		setInterval () =>
 			this.news.fetch()
-		, 60000
+		, 30000

 	render: () ->


Tests: download generated DZIs and tiles too. Fixes #99

--- test/api._coffee
+++ test/api._coffee
@@ -20,9 +20,18 @@
 TYPE_JS = 'text/javascript; charset=utf-8'
 TYPE_JSON = 'application/json; charset=utf-8'
 TYPE_TEXT = 'text/plain; charset=utf-8'
+TYPE_XML = 'application/xml'
+
+TYPE_IMAGES =
+    png: 'image/png'
+    jpg: 'image/jpeg'
+    jpeg: 'image/jpeg'

 # Matches e.g. '/v1/content/Abc123' and captures the ID:
 CONTENT_BY_ID_REGEX = /// ^/v1/content/ (\w+) $ ///
+
+# We'll set this to a converted DZI object when we get one from the API:
+DZI_CONVERTED = null

 #
 # Asserts that the given actual *text string* is a valid JSONP response,
@@ -131,7 +140,9 @@
     for prop in ['width', 'height', 'tileSize', 'tileOverlap']
         _expectNumberWithin act[prop], 0, Infinity

-    _expectStringMatching act.tileFormat, /^(png|jpg|jpeg)$/
+    _expectStringMatching act.tileFormat, ///
+        ^ ( #{(Object.keys TYPE_IMAGES).join '|'} ) $
+    /// # e.g. ^(png|jpg|jpeg)$

     _expectPartial act, exp

@@ -272,6 +283,9 @@
             expect(resp.body.ready).to.equal true
             # expectContent takes care of expecting `dzi`, etc., in this case.

+            # Save the returned DZI object, for us to test further below:
+            DZI_CONVERTED = resp.body.dzi
+
         # TODO: Need reliable existing image across local dev envs.
         it 'should return info for existing (queued) image', (_) ->
             resp = app.get "/v1/content/#{ids.IMAGE_QUEUED}"
@@ -360,3 +374,41 @@
                 .expect 200
                 .expect 'Access-Control-Allow-Origin', '*'
                 .end _
+
+    describe '(Generated DZIs)', ->
+
+        it 'should be downloadable when returned as ready', (_) ->
+            {url, width, height, tileSize, tileOverlap, tileFormat} = DZI_CONVERTED
+
+            resp = Request.get url, _
+
+            {statusCode, headers, body} = resp
+
+            expect(statusCode).to.equal 200
+            expect(headers['content-type']).to.equal TYPE_XML
+
+            # HACK: For simplicity, hardcoding the exact format of the XML:
+            expect(body).to.be.a 'string'
+            expect(body).to.equal """
+                <?xml version="1.0" encoding="utf-8"?>
+                <Image TileSize="#{tileSize}" Overlap="#{tileOverlap}"
+                 Format="#{tileFormat}" ServerFormat="Default"
+                 xmlns="http://schemas.microsoft.com/deepzoom/2009">
+                <Size Width="#{width}" Height="#{height}" /></Image>
+            """.replace /\n/g, ''
+
+        it 'should have downloadable tiles too', (_) ->
+            # For simplicity, just download the level 0 tile.
+            # Guaranteed to be there for any size image, and even better,
+            # we know internally it's the last tile to be generated.
+            {url, tileFormat} = DZI_CONVERTED
+            tileURL = url.replace /[.]\w+$/, "_files/0/0_0.#{tileFormat}"
+
+            resp = Request.get tileURL, _
+
+            {statusCode, headers, body} = resp
+
+            expect(statusCode).to.equal 200
+            expect(headers['content-type']).to.equal TYPE_IMAGES[tileFormat]
+
+            # No simple way to test the actual image itself; not bothering.


Remove component class. Refactor.

Remove component class. Refactor.

--- slider.coffee
+++ slider.coffee
@@ -1,31 +1,25 @@
-
-class Component
-
-    constructor: ->
-
-    update: ->
-
-    stringify: ->
-        JSON.stringify(@spec)
-
-
-class $blab.Slider extends Component
+class $blab.Slider extends $blab.Component

     constructor: (@spec, sheet) ->

         @sheet = sheet[@spec.id]

-        @container = $("##{@spec.id}")
-        @container.append("

")
-        @container.append("

")
-        @container.append("<input type='text' readonly class='report'>")
-        @report = @container.find('.report')
+        container = $("##{@spec.containerId}")
+        container.append("

")

-        @container.draggable()
-        @container.on 'drag', (event) =>
-            $('#myInput').val event.pageX + ',' + event.pageY
-            @spec.X = event.pageX
-            @spec.Y = event.pageY
+        box = container.children(".box")
+        box.append("

")
+        box.append("

")
+        box.append("<input type='text' readonly class='report'>")
+
+        label = box.children(".label")
+        label.html(@sheet.rowHeaders[0])
+
+        @slider = box.children(".slider")
+        @slider.css("position", "absolute")
+        @slider.css("left", @spec.x)
+        @slider.css("top", @spec.y)
+        @slider.css("width", "100px")

         defaults =
             width: 500
@@ -33,19 +27,15 @@
             min: 0
             max: 10
             step: 1
-            change: (event, ui) =>
-                @report.val ui.value
+            change: (event, ui) ->
+                report.val ui.value
                 $blab.compute()

         settings = $.extend({}, defaults, @spec)
-
-        @container.find('.label').html(@sheet.spec.rowHeaders[0])
-        @container.css("width", settings.width)
-
-        @slider = @container.find('.slider')
         @slider.slider settings

-        @report.val @slider.slider("value")
+        report = box.children(".report")
+        report.val @slider.slider("value")

     update: ->
-        @sheet.spec.data[0][0] =  @slider.slider("value")
+        @sheet.data[0][0] = @slider.slider("value")


added form inputs

added form inputs

--- slider.coffee
+++ slider.coffee
@@ -268,9 +268,10 @@

 	@errors:
 		selectorEmpty: 'slider element selector must return at least one element'
-		elementInvalid: 'slider first argument must be a selector or an element'
+		elementInvalid: 'first argument must be a selector or an element'
 		valueInvalid: 'slider value must be between options.min and options.max'
 		positionInvalid: 'slider position must be between 0 and 1'
+

 	@polling:
 		timeout: null
@@ -297,7 +298,7 @@
 		orientation: 'horizontal'
 		transitionDuration: 350
 		poll: false
-		valueOutput: null
+		formElement: null

 	@instances: []

@@ -311,9 +312,8 @@
 		@options = _.extend {}, Slider.defaults, options ? {}

 		if typeof element is 'string'
-			r = document.querySelectorAll element
-			throw Slider.errors.selectorEmpty if not r.length
-			@element = r[0]
+			@element = document.querySelectorAll element
+			throw Slider.errors.selectorEmpty if not @element
 		else if element instanceof Element
 			@element = element
 		else
@@ -323,6 +323,20 @@

 		@events = new Dispatcher @

+		@onFormElementChange = (e) =>
+			val = e?.target?.value
+			if val?
+				val = Number val
+				if isFinite(val) and not isNaN(val)
+					ok = @value val, updateFormElement: false
+					if not ok?
+						e?.target?.value = @value()
+				else
+					@warn Slider.errors.valueInvalid
+					e?.target?.value = @value()
+
+		@bindFormElement @options.formElement if @options.formElement
+
 		_.addClass @element, 'slider'
 		_.addClass @element, @options.orientation

@@ -339,10 +353,34 @@
 		, 600

 	refresh: ->
+		refresh = =>
+			@position @position(),
+				changeEvent: false
+				transitionEvent: false
+
 		if @transitioning
-			@events.once 'transition', => @position @position()
+			@events.once 'transition', => refresh
 		else
-			@position @position()
+			refresh()
+
+	bindFormElement: (element, options) ->
+		defaults =
+			unbindOldElement: true
+
+		options = _.extend {}, defaults, options
+
+		if typeof element is 'string'
+			element = document.querySelector element
+			throw Slider.errors.selectorEmpty if not element
+		else if element not instanceof Element
+			throw Slider.errors.elementInvalid
+
+		if options.unbindOldElement and @formElement and @onFormElementChange
+			@formElement.removeEventListener 'change', @onFormElementChange
+
+		element.addEventListener 'change', @onFormElementChange
+
+		@formElement = element

 	value: (v, options={}) ->
 		@position v, _.extend options, normalized: false
@@ -356,9 +394,12 @@
 			normalized: true
 			transition: @options.transitionDuration
 			changeEvent: true
+			transitionEvent: true
 			step: if options?.normalized is false then @options.step else @options.step / (@options.max - @options.min)
+			updateFormElement: true

 		options = _.extend {}, defaults, options
+

 		pos = if p is undefined
@@ -401,6 +442,8 @@

 		@[comp]?.position? @normalizedPosition, options for comp, ctr of Slider.components

+		@formElement?.value = @value() if options.updateFormElement
+
 		@events.trigger 'change', value: @value() if options.changeEvent

 		if options.transition
@@ -408,7 +451,7 @@
 				_.delay 17, =>
 					_.removeClass @element, 'transition'
 					@transitioning = false
-					@events.trigger 'transition'
+					@events.trigger 'transition' if options.transitionEvent
 		pos

@@ -560,6 +603,8 @@
 			if @options.location is 'knob'
 				@slider.knob.element.appendChild @hiddenKnobValue = _.div class: 'hidden value'

+
+
 	Fill = class @Fill extends Component

 		@defaults: null


* replace the __getter__/__setter__ to defineProperty

--- src/RefObject.coffee
+++ src/RefObject.coffee
@@ -1,6 +1,6 @@
 AbstractObject  = require("./Object")
 inherits        = require("inherits-ex/lib/inherits")
-
+defineProperty  = require("util-ex/lib/defineProperty")

 # RefObject with RefCount and AddRef/Release Supports.
 #
@@ -14,7 +14,8 @@
   inherits RefObject, AbstractObject
   # initialization method
   initialize: ->
-    @RefCount = 0
+    #@RefCount = 0
+    defineProperty @, 'RefCount', 0
     if @init
       console.error "init method is deprecated, pls use initialize instead"
       RefObject::init = (->) unless RefObject::init


Fixed a bug which could cause a defined reactive variable to be set an undefined session variable value when binding.

--- src/Templates.coffee
+++ src/Templates.coffee
@@ -91,9 +91,14 @@
     }, options)
     options.template.autorun ->
       value = Session.get(sessionVarName)
-      reactiveVar.set(value)
+      reactiveValue = Tracker.nonreactive -> reactiveVar.get()
+      # Prevent setting undefined on an existing reactive variable value if the session has not
+      # yet been set.
+      unless value == undefined and reactiveValue? then reactiveVar.set(value)
     options.template.autorun ->
       value = reactiveVar.get()
+      # Since the reactive variable was only set if the session was defined, the session can be
+      # safely set with the reactive variable value.
       Session.set(sessionVarName, value)
     reactiveVar


make compile_fixture promise compliant

make compile_fixture promise compliant

--- test/test.coffee
+++ test/test.coffee
@@ -10,7 +10,7 @@

 compile_fixture = (fixture_name, done) ->
   @public = path.join(fixture_name, 'public')
-  h.project.compile(Roots, fixture_name, done)
+  h.project.compile(Roots, fixture_name).done(done)

 before (done) ->
   h.project.install_dependencies('*', done)


test for setNonCaps()

test for setNonCaps()

--- test/test.coffee
+++ test/test.coffee
@@ -86,3 +86,9 @@
       assert.equal "1", do ->
         w.setNumbers(["1"], "key")
         w.writtenNumber(1, "key")
+
+  describe "setNonCaps()", ->
+    it "should set a regular expression to the language dictionary", ->
+      assert.equal "True false", do ->
+        w.setNonCaps(/^false$/, "custom")
+        w.capitalizeAll("true false",  "custom")


Remove debug statement

Remove debug statement

--- spec/powershell-spec.coffee
+++ spec/powershell-spec.coffee
@@ -140,7 +140,6 @@
           expect(tokens[14]).toEqual value: "elseif", scopes: ["source.powershell","keyword.control.powershell"]

         it "should highlight 'else'", ->
-          console.log(tokens)
           expect(tokens[29]).toEqual value: "else", scopes: ["source.powershell","keyword.control.powershell"]

       describe "Do-until statements", ->


font let the selectList cancel!

font let the selectList cancel!

--- lib/overlay-select-list-view.coffee
+++ lib/overlay-select-list-view.coffee
@@ -21,5 +21,5 @@
   itemForElement: (item) ->
     @itemGenerator(item)

-  confirmed: ({eventName}) ->
-    @cancel()
+  cancel: ->
+    # Do nothing. We never want this to hide


stupid typo

stupid typo

--- spec/sort-lines-spec.coffee
+++ spec/sort-lines-spec.coffee
@@ -19,7 +19,7 @@
     runs(callback)

   sortLinesInsensitive = (callback) ->
-    editorView.trigger "sort-lines:case-insensitve-sort"
+    editorView.trigger "sort-lines:case-insensitive-sort"
     waitsForPromise -> activationPromise
     runs(callback)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/text.coffee
+++ test/text.coffee
@@ -1,6 +1,6 @@
 module "Text"

-test "text moves up "
+test "text moves up vertically"

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/text.coffee
+++ test/text.coffee
@@ -15,6 +15,8 @@
   equals text.I.y, -5

 test "text fades out", ->
-  te
+  text = Text()
+
+

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/text.coffee
+++ test/text.coffee
@@ -4,7 +4,7 @@
   text = Text()

   equals text.I.x, 0
-  equals
+  equals textI

 test "text fades out", ->
   ;


Add --compatible flag to apm available command

This funnels through to the engine param to the atom.io API

--- src/available.coffee
+++ src/available.coffee
@@ -16,11 +16,14 @@
     options.usage """

       Usage: apm available
+             apm available --themes
+             apm available --compatible 0.49.0

-      List all the Atom packages that have been published to the apm registry.
+      List the Atom packages that have been published to the atom.io registry.
     """
     options.alias('h', 'help').describe('help', 'Print this usage message')
     options.alias('t', 'themes').describe('themes', 'Only list themes')
+    options.alias('c', 'compatible').describe('compatible', 'Only list packages compatitle with this Atom version')
     options.boolean('json').describe('json', 'Output available packages as JSON array')

   getAvailablePackages: (atomVersion, callback) ->
@@ -36,6 +39,8 @@
           proxy: process.env.http_proxy || process.env.https_proxy
           headers:
             authorization: token
+        requestSettings.qs = engine: atomVersion if atomVersion
+
         request.get requestSettings, (error, response, body={}) ->
           if error?
             callback(error)
@@ -52,7 +57,7 @@
     {callback} = options
     options = @parseOptions(options.commandArgs)

-    @getAvailablePackages options.argv.atomVersion, (error, packages) ->
+    @getAvailablePackages options.argv.compatible, (error, packages) ->
       if error?
         callback(error)
         return


[MINOR] Remove redundant ;

[MINOR] Remove redundant ;

--- lib/tap_i18n/tap_i18n-server.coffee
+++ lib/tap_i18n/tap_i18n-server.coffee
@@ -8,7 +8,7 @@
       for lang_tag in self._getProjectLanguages()
         do (lang_tag) ->
           for package_name of self.translations[lang_tag]
-            TAPi18n.addResourceBundle(lang_tag, package_name, self.translations[lang_tag][package_name]);
+            TAPi18n.addResourceBundle(lang_tag, package_name, self.translations[lang_tag][package_name])

           TAPi18next.setLng lang_tag, {fixLng: true}, (lang_translator) ->
             self.server_translators[lang_tag] = lang_translator


Show notification when deleting branches

--- lib/views/delete-branch-view.coffee
+++ lib/views/delete-branch-view.coffee
@@ -18,9 +18,7 @@
       @cancel()

     delete: (branch, remote) ->
-      unless remote
-        git.cmd(['branch', '-D', branch], cwd: @repo.getWorkingDirectory())
-        .then (data) -> notifier.addSuccess(data)
-      else
-        git.cmd(['push', remote, '--delete', branch], cwd: @repo.getWorkingDirectory())
-        .catch (data) -> notifier.addSuccess(data)
+      args = if remote then ['push', remote, '--delete'] else ['branch', '-D']
+      git.cmd(args.concat(branch), cwd: @repo.getWorkingDirectory())
+      .then (message) -> notifier.addSuccess message
+      .catch (error) -> notifier.addError error


moar coffee

moar coffee

--- app/assets/javascripts/dashit/views/projects/new.js.coffee
+++ app/assets/javascripts/dashit/views/projects/new.js.coffee
@@ -13,7 +13,7 @@
     @get("parentView").hideNew()

   submit: (event) ->
-    self = this
+    self = @
     project = @get("project")
     event.preventDefault()
     project.saveResource().fail((e) ->


collision with regular bounds

collision with regular bounds

--- src/collision.coffee
+++ src/collision.coffee
@@ -221,6 +221,7 @@
           yw = target.height/2
           return Collision.rayRectangle source, direction,
             x: target.x + xw
+            y
             xw: xw
             yw: yw
         else


JStackTemplate::create: allow credentials to be passed, tidy up the code

--- workers/social/lib/social/models/computeproviders/stacktemplate.coffee
+++ workers/social/lib/social/models/computeproviders/stacktemplate.coffee
@@ -101,28 +101,29 @@
     success: (client, data, callback)->

       { delegate } = client.connection
-      { profile:{nickname} } = delegate
-
-      { title, description, config } = data
-      return callback new KodingError "Title required."  unless title
-
-      template = new JStackTemplate {
-        title, description, config
-        rules         : data.rules       ? []
-        domains       : data.domains     ? []
-        machines      : data.machines    ? []
-        extras        : data.extras      ? []
-        connections   : data.connections ? []
-        accessLevel   : data.accessLevel ? "private"
-        group         : client.context.group
-        originId      : delegate.getId()
-      }
-
-      template.save (err)->
+
+      unless data?.title
+        return callback new KodingError "Title required."
+
+      stackTemplate = new JStackTemplate
+        originId    : delegate.getId()
+        group       : client.context.group
+        title       : data.title
+        config      : data.config      ? {}
+        description : data.description ? ''
+        rules       : data.rules       ? []
+        domains     : data.domains     ? []
+        machines    : data.machines    ? []
+        extras      : data.extras      ? []
+        connections : data.connections ? []
+        accessLevel : data.accessLevel ? 'private'
+        template    : data.template    ? ''
+        credentials : data.credentials ? []
+
+      stackTemplate.save (err)->
         if err
-          callback new KodingError "Failed to save stack template", err
-        else
-          callback null, template
+        then callback new KodingError 'Failed to save stack template', err
+        else callback null, stackTemplate

   @some$: permit 'list stack templates',


Fix: ready-to-showが正常に働いていなかったのを修正

Fix: ready-to-showが正常に働いていなかったのを修正

--- src/core/core.coffee
+++ src/core/core.coffee
@@ -18,10 +18,11 @@
     height: 600,
     title: "BlitzModderPC",
     icon: path.join(app.getAppPath(), "gui/img/image.jpg"),
-    autoHideMenuBar: true
+    autoHideMenuBar: true,
+    show: false
   )
   mainWindow.once("ready-to-show", ->
-    win.show()
+    mainWindow.show()
     return
   )
   mainWindow.loadURL("file://#{app.getAppPath()}/gui/index.html")


KDView: use getter for element

KDView: use getter for element

--- src/core/view.coffee
+++ src/core/view.coffee
@@ -511,7 +511,7 @@
     @on 'scroll', do ->
       lastRatio = 0
       (event)->
-        el = view.$()[0]
+        el = @getElement()
         {scrollHeight, scrollTop} = el

         dynamicThreshold = if threshold > 1


Prevent IEEE 754 floating point errors in both decimal and numeric

--- src/data-type.coffee
+++ src/data-type.coffee
@@ -219,13 +219,13 @@
       # ParamLenData (TYPE_VARBYTE)
       if parameter.value?
         sign = if parameter.value < 0 then 0 else 1
-        value = Math.abs parameter.value * Math.pow(10, parameter.scale)
+        value = Math.round Math.abs parameter.value * Math.pow(10, parameter.scale)

         if parameter.precision <= 9
           buffer.writeUInt8 5
           buffer.writeUInt8 sign
         # Round to preven IEEE 754 floating point errors
-          buffer.writeUInt32LE Math.round(value)
+          buffer.writeUInt32LE value
         else if parameter.precision <= 19
           buffer.writeUInt8 9
           buffer.writeUInt8 sign
@@ -281,7 +281,7 @@
       # ParamLenData (TYPE_VARBYTE)
       if parameter.value?
         sign = if parameter.value < 0 then 0 else 1
-        value = Math.abs parameter.value * Math.pow(10, parameter.scale)
+        value = Math.round Math.abs parameter.value * Math.pow(10, parameter.scale)

         if parameter.precision <= 9
           buffer.writeUInt8 5


Fix error messages

Fix error messages

--- src/data-type.coffee
+++ src/data-type.coffee
@@ -903,7 +903,7 @@
       if not value? then return null
       if value instanceof Date then return value
       value = Date.parse value
-      if isNaN value then return new TypeError "Invalid time."
+      if isNaN value then return new TypeError "Invalid date."
       value
   0x2A:
     type: 'DATETIME2N'
@@ -958,7 +958,7 @@
       if not value? then return null
       if value instanceof Date then return value
       value = Date.parse value
-      if isNaN value then return new TypeError "Invalid time."
+      if isNaN value then return new TypeError "Invalid date."
       value
   0x2B:
     type: 'DATETIMEOFFSETN'
@@ -1017,7 +1017,7 @@
       if not value? then return null
       if value instanceof Date then return value
       value = Date.parse value
-      if isNaN value then return new TypeError "Invalid time."
+      if isNaN value then return new TypeError "Invalid date."
       value
   0xF0:
     type: 'UDTTYPE'


Make hashes fire change events if the key already exists.

--- src/hash/hash.coffee
+++ src/hash/hash.coffee
@@ -31,8 +31,13 @@
     get: Batman.SimpleHash::get

     set: @mutation (key, value) ->
+      oldResult = Batman.SimpleHash::get.call(this, key)
       result = Batman.SimpleHash::set.call(this, key, value)
-      @fire('itemsWereAdded', key)
+
+      if oldResult? and oldResult != result
+        @fire('itemsWereChanged', [key], [result], [oldResult])
+      else
+        @fire('itemsWereAdded', key)

       result

@@ -44,16 +49,18 @@
   @accessor @defaultAccessor

   _preventMutationEvents: (block) ->
-    @prevent 'change'
-    @prevent 'itemsWereAdded'
-    @prevent 'itemsWereRemoved'
+    @prevent('change')
+    @prevent('itemsWereAdded')
+    @prevent('itemsWereChanged')
+    @prevent('itemsWereRemoved')

     try
       block.call(this)
     finally
-      @allow 'change'
-      @allow 'itemsWereAdded'
-      @allow 'itemsWereRemoved'
+      @allow('change')
+      @allow('itemsWereAdded')
+      @allow('itemsWereChanged')
+      @allow('itemsWereRemoved')

   clear: @mutation ->
     keys = @keys()
@@ -65,27 +72,47 @@

   update: @mutation (object) ->
     addedKeys = []
+    changedKeys = []
+    changedNewValues = []
+    changedOldValues = []
+
     @_preventMutationEvents ->
       Batman.forEach object, (k, v) =>
-        addedKeys.push(k) unless @hasKey(k)
-        @set(k,v)
+        if @hasKey(k)
+          changedKeys.push(k)
+          changedOldValues.push(@get(k))
+          changedNewValues.push(@set(k,v))
+        else
+          addedKeys.push(k)
+          @set(k,v)

     @fire('itemsWereAdded', addedKeys...) if addedKeys.length > 0
+    @fire('itemsWereChanged', changedKeys, changedNewValues, changedOldValues) if changedKeys.length > 0

   replace: @mutation (object) ->
     addedKeys = []
     removedKeys = []
+    changedKeys = []
+    changedOldValues = []
+    changedNewValues = []
+
     @_preventMutationEvents ->
-      @forEach (k, _) =>
-        unless Batman.objectHasKey(object, k)
+      @forEach (k) =>
+        if not Batman.objectHasKey(object, k)
           @unset(k)
           removedKeys.push(k)

       Batman.forEach object, (k,v) =>
-        addedKeys.push(k) unless @hasKey(k)
-        @set(k,v)
+        if @hasKey(k)
+          changedKeys.push(k)
+          changedOldValues.push(@get(k))
+          changedNewValues.push(@set(k,v))
+        else
+          addedKeys.push(k)
+          @set(k,v)

     @fire('itemsWereAdded', addedKeys...) if addedKeys.length > 0
+    @fire('itemsWereChanged', changedKeys, changedNewValues, changedOldValues) if changedKeys.length > 0
     @fire('itemsWereRemoved', removedKeys...) if removedKeys.length > 0

   for k in ['equality', 'hashKeyFor', 'objectKey', 'prefixedKey', 'unprefixedKey']


Handle compiler and runtime errors.

Handle compiler and runtime errors.

--- src/impromptu.coffee
+++ src/impromptu.coffee
@@ -31,7 +31,11 @@

     # Load the prompt file.
     prompt = require @compiledPromptPath
-    prompt.call? @, Impromptu, @prompt.section
+    try
+      prompt.call? @, Impromptu, @prompt.section
+    catch err
+      @_error 'javascript', 'Your prompt file triggered a JavaScript error.', err
+
     return @

   # Returns true if the compiled prompt file exists.
@@ -60,9 +64,18 @@
     # If you're using CS, load the CoffeeScript module to compile and cache it.
     else if /\.coffee$/.test sourcePrompt
       coffee = require 'coffee-script'
-      compiledJs = coffee.compile fs.readFileSync(sourcePrompt).toString()
-      fs.writeFileSync @compiledPromptPath, compiledJs
-      return true
+      try
+        compiledJs = coffee.compile fs.readFileSync(sourcePrompt).toString()
+        fs.writeFileSync @compiledPromptPath, compiledJs
+        return true
+      catch err
+        @_error 'coffeescript', 'Your prompt file is not valid CoffeeScript.', err
+
+  _error: (name, content, err) ->
+    @prompt.section "error:message:#{name}",
+      content: content
+      background: 'red'
+      foreground: 'white'

 # Create custom errors by extending `Impromptu.Error`.


Increasing deadzone

Increasing deadzone

--- src/joysticks.coffee
+++ src/joysticks.coffee
@@ -2,7 +2,7 @@
   type = "application/x-boomstickjavascriptjoysticksupport"
   plugin = null
   AXIS_MAX = 32767
-  DEAD_ZONE = AXIS_MAX * 0.125
+  DEAD_ZONE = AXIS_MAX * 0.2

   joysticks = []


Changed min search text length

Changed min search text length

--- src/js/kotoha.coffee
+++ src/js/kotoha.coffee
@@ -15,7 +15,7 @@
       match[2]

     remote_filter: (query, callback) ->
-      return unless query || query.length < 3
+      return unless query || query.length < 4
       kind = query[0].toLowerCase()
       query = query.slice(1)
       if kind is "k"


重构fetch选链，可初始化传入

重构fetch选链，可初始化传入

--- src/js/player.coffee
+++ src/js/player.coffee
@@ -35,7 +35,7 @@
     class Player
         instance = null

-        @defaults:
+        defaults:
             baseDir: "#{cfg.cdn}#{cfg.version}"
             mode: 'loop'
             mute: false
@@ -45,6 +45,21 @@
             maxRetryTimes: 1
             maxWaitingTime: 4 * 1000
             recoverMethodWhenWaitingTimeout: 'retry'
+
+            # 在基类的默认实现中将fetch设计成Promise模式的接口调用似乎没有必要,
+            # 但对于依赖API远程调用进行歌曲异步选链的场景下, Promise的处理无疑更具灵活性。
+            # XXX: 如要实现自己的API选链逻辑，请务传入自己的fetch方法。
+            fetch: ->
+                def = $.Deferred()
+                cur = @getCur()
+                if @getUrl() is cur
+                    def.resolve()
+                else
+                    setTimeout( =>
+                        @setUrl(cur)
+                        def.resolve()
+                    , 0)
+                def.promise()

         ###*
          * Player初始化方法
@@ -99,7 +114,7 @@
          *  </tr></table>
         ###
         constructor: (options) ->
-            @opts = opts = $.extend({}, Player.defaults, options)
+            @opts = opts = $.extend({}, @defaults, options)
             @waitingTimer = new Timer(100)

             baseDir = opts.baseDir
@@ -203,16 +218,17 @@
                 @_st = 'play'

                 st = @getState()
-                # 只有如下3种情况会触发_fetch选链：
+                # 只有如下3种情况会触发opts.fetch选链：
                 # 1) 内核首次使用或被reset过 (STATES.STOP)
                 # 2) 上一首歌播放完成自动触发下一首的播放 (STATES.END)
                 # 3) 某些移动浏览器无交互时不能触发自动播放 (会被卡在STATES.BUFFERING)
                 if st in [STATES.STOP, STATES.END] or st is STATES.BUFFERING and @curPos() is 0
-                    # XXX: 应该在_fetch中决定是否发起选链。
-                    # 即是否从cache中取, 是否setUrl都是依据_fetch的实现去决定。
-                    # 如果继承时覆盖重写_fetch, 这些都要自己权衡。
+                    # XXX: 应该在opts.fetch中决定是否发起选链。
+                    # 是从cache中取音频链接地址，还是发起选链请求，
+                    # 及setUrl的时机都是依据opts.fetch的实现决定。
+                    # 如果继承时需传入自己的fetch实现，这些都要自己权衡。
                     @trigger('player:fetch:start')
-                    @_fetch().done ->
+                    @opts.fetch().done ->
                         self.trigger('player:fetch:done')
                         play()
                 else
@@ -268,7 +284,7 @@
             ctrl.apply @, ['next', auto]

         ###*
-         * 获取当前歌曲（根据业务逻辑和选链_fetch方法的具体实现可以是音频文件url，也可以是标识id，默认直接传入音频文件url即可）。
+         * 获取当前歌曲（根据业务逻辑和选链opts.fetch方法的具体实现可以是音频文件url，也可以是标识id，默认直接传入音频文件url即可）。
          * 如果之前没有主动执行过setCur，则认为播放列表的第一首歌是当前歌曲。
          * @return {String}
         ###
@@ -282,7 +298,7 @@

         ###*
          * 设置当前歌曲。
-         * @param {String} sid 可以是音频文件url，也可以是音频文件id（如果是文件id，则要自己重写_fetch方法，决定如何根据id获得相应音频的实际地址）。
+         * @param {String} sid 可以是音频文件url，也可以是音频文件id（如果是文件id，则要实现自己的opts.fetch方法，决定如何根据id获得相应音频的实际地址）。
          * @return {player}
         ###
         setCur: (sid) ->
@@ -357,7 +373,7 @@
             @engine.getState()

         ###*
-         * 设置当前播放资源的url。一般而言，这个方法是私有方法，供_fetch等内部方法中调用，客户端无需关心。
+         * 设置当前播放资源的url。一般而言，这个方法是私有方法，供opts.fetch选链使用，客户端无需关心。
          * 但出于调试和灵活性的考虑，依然之暴露为公共方法。
          * @param {String} url
          * @return {player}
@@ -442,22 +458,6 @@

         getEngineType: ->
             @engine.curEngine.engineType
-
-        # 在基类的默认实现中将_fetch设计成Promise模式的接口调用似乎没有必要,
-        # 但对于依赖API远程调用进行歌曲异步选链的场景下, Promise的处理无疑更具灵活性。
-        # XXX: 如要实现自己的API选链逻辑，请务必重写_fetch方法。
-        _fetch: ->
-            def = $.Deferred()
-            cur = @getCur()
-
-            if @getUrl() is cur
-                def.resolve()
-            else
-                setTimeout( =>
-                    @setUrl(cur)
-                    def.resolve()
-                , 0)
-            def.promise()

     Events.mixTo(Player)
     Player


Move windowController assignment to constructor

--- client/landing/app/MainApp/maincontroller/mainviews/contentpanel.coffee
+++ client/landing/app/MainApp/maincontroller/mainviews/contentpanel.coffee
@@ -8,6 +8,7 @@
     @bindTransitionEnd()
     @listenWindowResize()
     @state = 'full'
+    @windowController or= @getSingleton 'windowController'

     @navOpenedOnce = if KD.isLoggedIn() then yes else no

@@ -31,7 +32,6 @@
   changeLayout:(options)->

     {type, hideTabs, name}    = options
-    @windowController or= @getSingleton 'windowController'

     @unsetClass 'full develop social'
     @adjustShadow hideTabs


Fix src bug

Fix src bug

--- src/majordomo.coffee
+++ src/majordomo.coffee
@@ -201,16 +201,21 @@
 module.exports.exec = (command, cb = (->)) ->
     executor.execute command, cb

-module.exports.src = (() ->
-    # This is very tricky and probably bad solution
-    # NOTE: Is there any other possibility how to manage that?
-    if module.parent.children[2]
-        filename = module.parent.children[2].filename
-    else
-        filename = module.parent.filename
-
-    new util.FileSystem path.dirname filename
-)()
+src = null
+Object.defineProperty module.exports, 'src', (
+    get : () ->
+        return src if src
+        src = (() ->
+            # This is very tricky and probably bad solution
+            # NOTE: Is there any other possibility how to manage that?
+            if module.parent.children[2]
+                filename = module.parent.children[2].filename
+            else
+                filename = module.parent.filename
+
+            new util.FileSystem path.dirname filename
+        )()
+    )

 dest = null
 Object.defineProperty module.exports, 'dest', (


fix deprecation warning

fix deprecation warning

--- lib/idris-controller.coffee
+++ lib/idris-controller.coffee
@@ -91,7 +91,7 @@

   getWordUnderCursor: ->
     editor = atom.workspace.getActiveEditor()
-    cursorPosition = editor.getCursor(0).getCurrentWordBufferRange()
+    cursorPosition = editor.getLastCursor().getCurrentWordBufferRange()
     editor.getTextInBufferRange cursorPosition

   loadFile: (uri) ->


Rename deprecated marker option

Rename deprecated marker option

--- lib/misspelling-view.coffee
+++ lib/misspelling-view.coffee
@@ -29,7 +29,7 @@
     @updatePosition() if @isUpdateNeeded()

   createMarker: ->
-    @marker = @editor.markScreenRange(@getScreenRange(), invalidation: 'touch', replicate: false, persistent: false)
+    @marker = @editor.markScreenRange(@getScreenRange(), invalidate: 'touch', replicate: false, persistent: false)
     @subscribe @marker, 'changed', ({newHeadScreenPosition, newTailScreenPosition, isValid}) =>
       @startPosition = newTailScreenPosition
       @endPosition = newHeadScreenPosition


Handle tree view only if it exists

Handle tree view only if it exists

--- lib/models/synced-fs.coffee
+++ lib/models/synced-fs.coffee
@@ -31,9 +31,7 @@
     atom.commands.dispatch(@workspaceView, 'tree-view:reveal-active-file')

   handleEvents: ->
-    @treeViewEl().addEventListener 'drag', (event) => @onTreeViewDrag(event)
-    @treeViewEl().addEventListener 'drop', (event) => @onTreeViewDrop(event)
-    @treeViewEl().addEventListener 'dragend', (event) => @onTreeViewDragEnd(event)
+    @addTreeViewListeners()

     atom.commands.add @workspaceView,
       'learn-ide:resync': (event) => @onResync(event)
@@ -47,9 +45,19 @@
       switch event.type
         when 'core:confirm' then @onCoreConfirmDidDispatch(event)
         when 'tree-view:remove' then @onTreeViewRemove(event)
+        when 'tree-view:toggle' then @addTreeViewListeners(event)

     atom.workspace.observeTextEditors (editor) =>
       editor.onDidSave => @onSave(editor)
+
+  addTreeViewListeners: ->
+    el = @treeViewEl()
+
+    if el? and not @didAddTreeViewListeners?
+      @didAddTreeViewListeners = true
+      el.addEventListener 'drag', (event) => @onTreeViewDrag(event)
+      el.addEventListener 'drop', (event) => @onTreeViewDrop(event)
+      el.addEventListener 'dragend', (event) => @onTreeViewDragEnd(event)

   onSave: (editor) =>
     @convertLineEndings(editor)
@@ -232,7 +240,7 @@
     textContainer.innerText

   getTreeViewSelectedPath: ->
-    selectedEntry = @treeViewEl().querySelector('.selected')
+    selectedEntry = @treeViewEl()?.querySelector('.selected')

     return null unless selectedEntry?
     @getPath(selectedEntry)


Fix comment to indicate configurable timeout

--- gitcommander.coffee
+++ gitcommander.coffee
@@ -72,7 +72,7 @@
   now = do Date.now

   for event in res
-    # ignore events we've already seen and those older than one hour
+    # ignore events we've already seen and those older than the configured limit
     eventDate = new Date event.created_at
     age = now - do eventDate.getTime
     if age >= RECENCY or event.id in events


Don't render react dom attributes for html

Only what's within the React mount point is made dynamic so
we don't need React metadata.

--- lib/utils/static-entry.cjsx
+++ lib/utils/static-entry.cjsx
@@ -14,5 +14,5 @@
   Router.run [routes], locals.path, (Handler, state) ->
     page = find pages, (page) -> page.path is state.pathname
     body = React.renderToString(<Handler config={config} pages={pages} page={page} state={state}/>)
-    html = React.renderToString(<HTML body={body}/>)
+    html = React.renderToStaticMarkup(<HTML body={body}/>)
     callback null, html


the sequences option for uglify2 is true by default but this was breaking the call to `window.external.notify()` (there are a couple of other documented cases: http://translate.google.ca/translate?hl=en&sl=zh-CN&u=http://www.cnblogs.com/blastmann/p/external-notify-cant-use-with-uglifyjs-compressor.html&prev=/search%3Fq%3Dwindow.external.notify%2Buglifyjs%26biw%3D1440%26bih%3D753 and http://stackoverflow.com/questions/17168408/cant-use-window-external-notify-after-uglifyjs-compressed) changing this flag to false as it produces a safer compression (albeit with the tradeoff of a slightly larger file size)

--- src/optimizer.coffee
+++ src/optimizer.coffee
@@ -32,7 +32,7 @@
     switch sysPath.extname(source)
       when '.js'
         # Use uglifyjs to minify the js file
-        result = uglify.minify([source], {compress: {comparisons: false}})
+        result = uglify.minify([source], {compress: {comparisons: false, sequences: false}})
         fs.writeFileSync dest, result.code
         logging.info "minified #{source}"
       else
@@ -117,4 +117,4 @@
     .replace(/[\u2028]/g, "\\u2028")
     .replace(/[\u2029]/g, "\\u2029")

-module.exports = {optimizeDir, concatDeps}+module.exports = {optimizeDir, concatDeps}


WSKernelPicker: Fix restore focused element

--- lib/ws-kernel-picker.coffee
+++ lib/ws-kernel-picker.coffee
@@ -44,6 +44,7 @@
     toggle: ->
         @_path = atom.workspace.getActiveTextEditor().getPath() + '-' + uuid.v4()
         gatewayListing = new CustomListView('No gateways available', @onGateway.bind this)
+        @previouslyFocusedElement = gatewayListing.previouslyFocusedElement
         gatewayListing.setLoading 'Loading gateways...'
         items = Config.getJson('gateways', [])
         if not _.isEmpty items
@@ -55,6 +56,7 @@
     onGateway: (spec) ->
         console.log('Picked a gateway')
         sessionListing = new CustomListView('No sessions available', @onSession.bind this)
+        sessionListing.previouslyFocusedElement = @previouslyFocusedElement
         sessionListing.setLoading 'Loading sessions...'

         services.listRunningSessions(spec.options)
@@ -87,6 +89,7 @@
     onSession: (spec) ->
         unless spec.model?
             kernelListing = new CustomListView('No kernel specs available', @startSession.bind this)
+            kernelListing.previouslyFocusedElement = @previouslyFocusedElement
             kernelListing.setLoading 'Loading kernel specs...'

             services.getKernelSpecs(spec.options)


Mint cs, css, less, xml, ruby added

Mint cs, css, less, xml, ruby added

--- grammars/latex.cson
+++ grammars/latex.cson
@@ -1173,6 +1173,11 @@
       { include: '#mintPython' }
       { include: '#mintJavascript' }
       { include: '#mintJSON' }
+      { include: '#mintCoffeescript'}
+      { include: '#mintCSS'}
+      { include: '#mintLess'}
+      { include: '#mintXML'}
+      { include: '#mintRuby'}
       { include: '#mintGeneric' }
     ]
   }
@@ -1345,7 +1350,216 @@
         name: 'punctuation.definition.arguments.end.latex'
     patterns: [{ include: 'source.json' }]
   }
-
+  mintCoffeescript: {
+    name: 'meta.environment.embedded.coffee.latex'
+    contentName: 'source.embedded.coffee'
+    begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(coffee(?:\\-?script)?)(\\})'
+    beginCaptures:
+      1:
+        name: 'support.function.environment.begin.latex'
+      2:
+        name: 'punctuation.definition.function.begin.latex'
+      3:
+        name: 'punctuation.definition.arguments.begin.latex'
+      4:
+        name: 'variable.parameter.environment.minted.latex'
+      # 5: Used for meta scope name (basically just non starred version)
+      6:
+        name: 'punctuation.definition.arguments.end.latex'
+      7:
+        name: 'punctuation.definition.arguments.optional.begin.latex'
+      8:
+        name: 'variable.parameter.optional.function.environment.latex'
+      9:
+        name: 'punctuation.definition.arguments.optional.end.latex'
+      10:
+        name: 'punctuation.definition.arguments.begin.latex'
+      11:
+        name: 'variable.parameter.language.coffee.latex'
+      12:
+        name: 'punctuation.definition.arguments.end.latex'
+    end: '((\\\\)end)(\\{)(minted)(\\})'
+    endCaptures:
+      1:
+        name: 'support.function.environment.end.latex'
+      2:
+        name: 'punctuation.definition.function.latex'
+      3:
+        name: 'punctuation.definition.arguments.begin.latex'
+      4:
+        name: 'variable.parameter.environment.latex'
+      5:
+        name: 'punctuation.definition.arguments.end.latex'
+    patterns: [{ include: 'source.coffee' }]
+  }
+  mintCSS: {
+    name: 'meta.environment.embedded.css.latex'
+    contentName: 'source.embedded.css'
+    begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(css)(\\})'
+    beginCaptures:
+      1:
+        name: 'support.function.environment.begin.latex'
+      2:
+        name: 'punctuation.definition.function.begin.latex'
+      3:
+        name: 'punctuation.definition.arguments.begin.latex'
+      4:
+        name: 'variable.parameter.environment.minted.latex'
+      # 5: Used for meta scope name (basically just non starred version)
+      6:
+        name: 'punctuation.definition.arguments.end.latex'
+      7:
+        name: 'punctuation.definition.arguments.optional.begin.latex'
+      8:
+        name: 'variable.parameter.optional.function.environment.latex'
+      9:
+        name: 'punctuation.definition.arguments.optional.end.latex'
+      10:
+        name: 'punctuation.definition.arguments.begin.latex'
+      11:
+        name: 'variable.parameter.language.css.latex'
+      12:
+        name: 'punctuation.definition.arguments.end.latex'
+    end: '((\\\\)end)(\\{)(minted)(\\})'
+    endCaptures:
+      1:
+        name: 'support.function.environment.end.latex'
+      2:
+        name: 'punctuation.definition.function.latex'
+      3:
+        name: 'punctuation.definition.arguments.begin.latex'
+      4:
+        name: 'variable.parameter.environment.latex'
+      5:
+        name: 'punctuation.definition.arguments.end.latex'
+    patterns: [{ include: 'source.css' }]
+  }
+  mintLess: {
+    name: 'meta.environment.embedded.less.latex'
+    contentName: 'source.embedded.less'
+    begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(less)(\\})'
+    beginCaptures:
+      1:
+        name: 'support.function.environment.begin.latex'
+      2:
+        name: 'punctuation.definition.function.begin.latex'
+      3:
+        name: 'punctuation.definition.arguments.begin.latex'
+      4:
+        name: 'variable.parameter.environment.minted.latex'
+      # 5: Used for meta scope name (basically just non starred version)
+      6:
+        name: 'punctuation.definition.arguments.end.latex'
+      7:
+        name: 'punctuation.definition.arguments.optional.begin.latex'
+      8:
+        name: 'variable.parameter.optional.function.environment.latex'
+      9:
+        name: 'punctuation.definition.arguments.optional.end.latex'
+      10:
+        name: 'punctuation.definition.arguments.begin.latex'
+      11:
+        name: 'variable.parameter.language.less.latex'
+      12:
+        name: 'punctuation.definition.arguments.end.latex'
+    end: '((\\\\)end)(\\{)(minted)(\\})'
+    endCaptures:
+      1:
+        name: 'support.function.environment.end.latex'
+      2:
+        name: 'punctuation.definition.function.latex'
+      3:
+        name: 'punctuation.definition.arguments.begin.latex'
+      4:
+        name: 'variable.parameter.environment.latex'
+      5:
+        name: 'punctuation.definition.arguments.end.latex'
+    patterns: [{ include: 'source.less' }]
+  }
+  mintXML: {
+    name: 'meta.environment.embedded.xml.latex'
+    contentName: 'source.embedded.xml'
+    begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(xml)(\\})'
+    beginCaptures:
+      1:
+        name: 'support.function.environment.begin.latex'
+      2:
+        name: 'punctuation.definition.function.begin.latex'
+      3:
+        name: 'punctuation.definition.arguments.begin.latex'
+      4:
+        name: 'variable.parameter.environment.minted.latex'
+      # 5: Used for meta scope name (basically just non starred version)
+      6:
+        name: 'punctuation.definition.arguments.end.latex'
+      7:
+        name: 'punctuation.definition.arguments.optional.begin.latex'
+      8:
+        name: 'variable.parameter.optional.function.environment.latex'
+      9:
+        name: 'punctuation.definition.arguments.optional.end.latex'
+      10:
+        name: 'punctuation.definition.arguments.begin.latex'
+      11:
+        name: 'variable.parameter.language.xml.latex'
+      12:
+        name: 'punctuation.definition.arguments.end.latex'
+    end: '((\\\\)end)(\\{)(minted)(\\})'
+    endCaptures:
+      1:
+        name: 'support.function.environment.end.latex'
+      2:
+        name: 'punctuation.definition.function.latex'
+      3:
+        name: 'punctuation.definition.arguments.begin.latex'
+      4:
+        name: 'variable.parameter.environment.latex'
+      5:
+        name: 'punctuation.definition.arguments.end.latex'
+    patterns: [{ include: 'source.xml' }]
+  }
+  mintRuby: {
+    name: 'meta.environment.embedded.ruby.latex'
+    contentName: 'source.embedded.ruby'
+    begin: '((\\\\)begin)(\\{)((minted)\\*?)(\\})(?:(\\[)(.*?)(\\]))?(\\{)(ruby|rb)(\\})'
+    beginCaptures:
+      1:
+        name: 'support.function.environment.begin.latex'
+      2:
+        name: 'punctuation.definition.function.begin.latex'
+      3:
+        name: 'punctuation.definition.arguments.begin.latex'
+      4:
+        name: 'variable.parameter.environment.minted.latex'
+      # 5: Used for meta scope name (basically just non starred version)
+      6:
+        name: 'punctuation.definition.arguments.end.latex'
+      7:
+        name: 'punctuation.definition.arguments.optional.begin.latex'
+      8:
+        name: 'variable.parameter.optional.function.environment.latex'
+      9:
+        name: 'punctuation.definition.arguments.optional.end.latex'
+      10:
+        name: 'punctuation.definition.arguments.begin.latex'
+      11:
+        name: 'variable.parameter.language.ruby.latex'
+      12:
+        name: 'punctuation.definition.arguments.end.latex'
+    end: '((\\\\)end)(\\{)(minted)(\\})'
+    endCaptures:
+      1:
+        name: 'support.function.environment.end.latex'
+      2:
+        name: 'punctuation.definition.function.latex'
+      3:
+        name: 'punctuation.definition.arguments.begin.latex'
+      4:
+        name: 'variable.parameter.environment.latex'
+      5:
+        name: 'punctuation.definition.arguments.end.latex'
+    patterns: [{ include: 'source.ruby' }]
+  }

   # Experimental
   unitMeasure: {


Add \ as operator for use with matrices

Add \ as operator for use with matrices

--- grammars/stata.cson
+++ grammars/stata.cson
@@ -1208,6 +1208,11 @@
         'name': 'keyword.operator.arithmetic.stata'
       }
       {
+        'comment': 'match division operator but not path separator'
+        'match': '(?<![a-zA-Z.])\\\\(?![a-zA-Z.]|$)'
+        'name': 'keyword.operator.matrix.addrow.stata'
+      }
+      {
         'match': '\\|\\|'
         'name': 'keyword.operator.graphcombine.stata'
       }


Provide callback to start()

Provide callback to start()

--- bin/simulatePortals.coffee
+++ bin/simulatePortals.coffee
@@ -49,44 +49,47 @@
     console.log "SHOWN", data
   pixelset.on 'data', (data) ->
     console.log "PIXELSET", data
-  inst.start()
+  inst.start (err) ->
+    if err
+      console.log err
+      process.exit 1

-  # TODO: don't send until runtime hits running state
+    # TODO: don't send until runtime hits running state

-  setPortal = (id, color) ->
-    val = "[#{id}, \"#{rgb2hex(color)}\"]"
-    console.log "Sending #{val}"
-    pixel.send val
+    setPortal = (id, color) ->
+      val = "[#{id}, \"#{rgb2hex(color)}\"]"
+      console.log "Sending #{val}"
+      pixel.send val

-  setPortals = (color) ->
-    for portal,idx in portals.portals
-      continue unless portal
-      if idx is 37
-        if color[2] is 64
-          setPortal idx, [0, 0, 200]
-        else
-          setPortal idx, [0, 0, 64]
-        continue
-      setPortal idx, color
-    console.log 'Sending SHOW'
-    show.send true
+    setPortals = (color) ->
+      for portal,idx in portals.portals
+        continue unless portal
+        if idx is 37
+          if color[2] is 64
+            setPortal idx, [0, 0, 200]
+          else
+            setPortal idx, [0, 0, 64]
+          continue
+        setPortal idx, color
+      console.log 'Sending SHOW'
+      show.send true

-  setTimeout ->
-    console.log 'portals blue'
-    setPortals [0, 0, 64]
     setTimeout ->
-      console.log 'portals green'
-      setPortals [0, 64, 0]
+      console.log 'portals blue'
+      setPortals [0, 0, 64]
       setTimeout ->
-        console.log 'portals red'
-        setPortals [96, 0, 0]
+        console.log 'portals green'
+        setPortals [0, 64, 0]
         setTimeout ->
-          console.log 'portals white'
-          setPortals [64, 64, 64]
+          console.log 'portals red'
+          setPortals [96, 0, 0]
           setTimeout ->
-            console.log "DONE"
+            console.log 'portals white'
+            setPortals [64, 64, 64]
+            setTimeout ->
+              console.log "DONE"
+            , 5000
           , 5000
         , 5000
       , 5000
     , 5000
-  , 5000


Started using Hack for editor font

Started using Hack for editor font

--- editors/atom/config.cson
+++ editors/atom/config.cson
@@ -6,7 +6,7 @@
   metrics:
     userId: "f8484a553ab176923d240d2fcca1cc4aa18f1f9a"
   editor:
-    fontFamily: "Source Code Pro"
+    fontFamily: "Hack"
     tabLength: 4
     invisibles: {}
   core:
@@ -17,8 +17,7 @@
     ]
   minimap:
     v4Preview: true
-  linter:
-    showErrorPanel: false
+  linter: {}
   "linter-flake8":
     executableDir: "/usr/local/bin"
   "autocomplete-plus": {}
@@ -37,3 +36,6 @@
   "fuzzy-finder": {}
   "git-diff": {}
   "markdown-preview": {}
+".coffee.source":
+  editor:
+    tabLength: 2


clearer variable names

clearer variable names

--- src/quad_tree.coffee
+++ src/quad_tree.coffee
@@ -68,8 +68,9 @@

       index = TOP_LEFT

-      if left && !top
-        index = BOTTOM_LEFT
+      if left
+        if !top
+          index = BOTTOM_LEFT
       else
         if top
           index = TOP_RIGHT


Allow end-user to write routes with multiple spaces.

--- guts/routing.coffee
+++ guts/routing.coffee
@@ -18,8 +18,10 @@
                 # Let's loop through the routes file and do the appropriate mapping.
                 # NOTE: I really hate that the value comes before the key.
                 _.each configuredRoutes, (totalString, route) ->
-                        # routes are stored like METHOD /route, so we'll split on spaces.
-                        [method, endpoint] = route.split ' '
+                        # routes are stored like METHOD /route, so we'll split on spaces.
+                        # To make this a bit more dev-friendly, let's get rid of redundant
+                        # spaces.
+                        [method, endpoint] = route.replace(/\s\s+/g, ' ').trim().split ' '

                         # This bigass thing here is for dev-friendliness.  I don't want to punish people
                         # for separating controller calls to multiple lines if they want, and I don't want


faster detection

faster detection

--- extension/chrome/js/src/content.coffee
+++ extension/chrome/js/src/content.coffee
@@ -76,6 +76,7 @@
 			# Get the service URL
 			chrome.extension.sendMessage {action: 'url'}, (url) =>
 				@url = url
+				@checkVersion()
 				return

 		@storeLastVersion: (version) ->
@@ -149,7 +150,6 @@
 						# GMailr is ready
 							@currentGmail = evt.email
 							# GMailUI.Breadcrumbs.add (__msg 'extName'), => @gettingStarted()
-							@checkVersion()

 						when 'viewThread'
 						# User moves to previous or next convo


:lipstick:

:lipstick:

--- src/app/config-panel.coffee
+++ src/app/config-panel.coffee
@@ -17,11 +17,13 @@
         input = $(input)
         name = input.attr('id')
         type = input.attr('type')
+
         @observeConfig name, (value) ->
           if type is 'checkbox'
             input.attr('checked', value)
           else
             input.val(value) if value
+
         input.on 'change', =>
           value = input.val()
           if type == 'checkbox'


add initial selection.prepare implementation

--- src/selection.coffee
+++ src/selection.coffee
@@ -98,7 +98,15 @@
   onUpdate: (range) ->

   prepare: (format, value) ->
-    # TODO implement
+    this.update()
+    range = this.getRange()
+    pos = _.last(@doc.findPath(range.start))
+    target = pos.blot.split(pos.offset)
+    cursor = Parchment.create('cursor')
+    target.parent.insertBefore(cursor, target)
+    cursor.format(format, value)
+    # Cursor will not blink if we make selection
+    this.setNativeRange(cursor.domNode.firstChild, 1)

   scrollIntoView: ->
     return unless @range


Delete bad merge artifacts and reword docs

--- src/selection.coffee
+++ src/selection.coffee
@@ -404,24 +404,16 @@
     @selectLeft() if @isEmpty() and not @editor.isFoldedAtScreenRow(@cursor.getScreenRow())
     @deleteSelectedText()

-  # Deprecated: Use {::deleteToBeginningOfWord} instead.
-  backspaceToBeginningOfWord: ->
-    deprecate("Use Selection::deleteToBeginningOfWord() instead")
-    @deleteToBeginningOfWord()
-
-  # Deprecated: Use {::deleteToBeginningOfLine} instead.
-  backspaceToBeginningOfLine: ->
-    deprecate("Use Selection::deleteToBeginningOfLine() instead")
-    @deleteToBeginningOfLine()
-
-  # Public: Removes the selection or all characters from the start of the
-  # selection back to the previous word boundary if nothing is selected.
+  # Public: Removes the selection or, if nothing is selected, then all
+  # characters from the start of the selection back to the previous word
+  # boundary.
   deleteToPreviousWordBoundary: ->
     @selectToPreviousWordBoundary() if @isEmpty()
     @deleteSelectedText()

-  # Public: Removes the selection or all characters from the start of the
-  # selection up to the next word boundary if nothing is selected.
+  # Public: Removes the selection or, if nothing is selected, then all
+  # characters from the start of the selection up to the next word
+  # boundary.
   deleteToNextWordBoundary: ->
     @selectToNextWordBoundary() if @isEmpty()
     @deleteSelectedText()


CHG: move jquery ajax patch to tao

CHG: move jquery ajax patch to tao

--- frontend/javascripts/shared/ujs.coffee
+++ frontend/javascripts/shared/ujs.coffee
@@ -29,9 +29,3 @@
   if $element.is('button') && $element.data('loading')
     $element.addClass('text-with-icon')
       .prepend createIcon('loading', class: 'spin')
-
-# Make sure that every Ajax request sends the CSRF token
-$.ajaxPrefilter (options, originalOptions, xhr) ->
-  if !options.crossDomain
-    token = $('meta[name=csrf-token]').attr('content')
-    xhr.setRequestHeader('X-CSRF-Token', token) if token


default query text when insert link

default query text when insert link

--- lib/insert-link-view.coffee
+++ lib/insert-link-view.coffee
@@ -37,7 +37,8 @@
     @on "core:cancel", => @detach()

   handleEvents: ->
-    @searchEditor.hiddenInput.on "keyup", => @updateSearch() if posts
+    @searchEditor.hiddenInput.on "keyup", =>
+      @updateSearch(@searchEditor.getText()) if posts
     @searchResult.on "click", "li", (e) => @useSearchResult(e)

   onConfirm: ->
@@ -86,8 +87,8 @@
     else
       @setLink(selection, "", "")

-  updateSearch: ->
-    query = @searchEditor.getText().trim().toLowerCase()
+  updateSearch: (query) ->
+    query = query.trim().toLowerCase()
     results = posts.filter (post) ->
       query and post.title.toLowerCase().contains(query)
     results = results.map (post) ->
@@ -209,7 +210,11 @@
       uri = atom.config.get("markdown-writer.urlForPosts")
       succeed = (body) =>
         posts = body.posts
-        @searchBox.show() if posts.length > 0
+
+        if posts.length > 0
+          @searchBox.show()
+          @searchEditor.setText(@textEditor.getText())
+          @updateSearch(@textEditor.getText())
       error = (err) =>
         @searchBox.hide()
       utils.getJSON(uri, succeed, error)


Require jquery-extensions from space-pen

--- src/space-pen.coffee
+++ src/space-pen.coffee
@@ -1,5 +1,5 @@
 if typeof require is 'function'
-  $ = jQuery = require('../vendor/jquery')
+  $ = jQuery = require('./jquery-extensions')
 else
   $ = jQuery = window.jQuery


Explicit zepto check

Explicit zepto check

--- lib/rails/beforesend.coffee
+++ lib/rails/beforesend.coffee
@@ -9,7 +9,7 @@
 # Skip for Zepto which doesn't have ajaxSetup but does already support
 # `ajaxBeforeSend`. It'd be better to feature test for the event and
 # see if we need to install it.
-if $.ajaxSetup
+unless Zepto?

   # One caveat about using `$.ajaxSetup` is that its easily clobbered.
   # If anything else tries to register another global `beforeSend`


ensureAccess bug

ensureAccess bug

--- lib/route-middleware.coffee
+++ lib/route-middleware.coffee
@@ -11,7 +11,7 @@

   ensureAccess: (req, res, next) ->
     ensureAuth req, res, ->
-      return next 401 unless (req.user is req.person) or req.user.admin
+      return next 401 unless (req.user.id is req.person.id) or req.user.admin
       next()

   ensureAdmin: (req, res, next) ->


Pass both base and filename to template plugin factory

--- src/templates.coffee
+++ src/templates.coffee
@@ -20,7 +20,7 @@
         Buffer containing the rendered contents ###
     throw new Error 'not implemented'

-TemplatePlugin.fromFile = (filename, callback) ->
+TemplatePlugin.fromFile = (filename, base, callback) ->
   ### *callback* with a instance of <TemplatePlugin> created from *filename* ###
   throw new Error 'not implemented'

@@ -40,7 +40,7 @@
         templates = {}
         async.forEach files, (filename, callback) ->
           logger.verbose "loading template: #{ filename }"
-          plugin.class.fromFile path.join(location, filename), (error, template) ->
+          plugin.class.fromFile filename, location, (error, template) ->
             templates[filename] = template
             callback error
         , (error) ->


Animate histogram.

Animate histogram.

--- main.coffee
+++ main.coffee
@@ -159,7 +159,7 @@
 #==== Particles ====

-class Sunlight # extends Photon
+class Sunlight

     colors: ["#ff0", "#00f"]
     sizes: [2, 3]
@@ -175,15 +175,14 @@
         @d = 0
         @setState 0

-
     init: ->
-        @yscale = d3.scale.linear()
-            .domain([@h/2-100, @h/2+100])
-            .range([-1, 1])
-
-        @xscale = d3.scale.linear()
-            .domain([-pi/2, pi/2])
-            .range([@w/2-100, @w/2+100])
+        @xScale = d3.scale.linear() # sim units -> screen units
+            .domain([-1, 1])
+            .range([0, @w])
+
+        @yScale = d3.scale.linear() # sim units -> screen units
+            .domain([-1, 1])
+            .range([@h, 0])

     setState: (@state) ->
         return if @state<0
@@ -209,20 +208,17 @@

     setVelocities: ->

-        rad = 100
-        cy = @h/2  # Canvas center
         @velocity[0] = new Vector @vel0.x, @vel0.y
-        theta = Math.asin((@pos.y - cy)/rad)
-        #@velocity[1] = (new Vector).polar(@vel0.mag(), pi-2*theta)
         @velocity[1] = (new Vector).polar(10, pi/2)

-        #@limit = @w/2 - Math.sqrt(rad*rad-(@pos.y-cy)*(@pos.y-cy))
-        #console.log "??", Math.asin(@yscale(@pos.y))
-        @limit =  @xscale(Math.asin(@yscale(@pos.y)))
-        @bin = Math.floor((pi/2+Math.asin(@yscale(@pos.y)))/(pi)*20)
-        #console.log "bin", @bin
+        g = (u) -> ((u<0)*1.5 + (u>0)/2)*u + 0.375
+        h = (u) -> Math.floor(20*(u+0.5))
+
+        @limit =  @yScale(g(@xScale.invert(@pos.x)))
+        #console.log "???", h(@xScale.invert(@pos.x))
+        @bin = Math.floor((@yScale.invert(@limit)+0.875)/2*20)

-    collision: -> @pos.x > @limit
+    collision: -> @pos.y > @limit

@@ -232,9 +228,9 @@

     maxPhotons: 4000
     rate: 0
-    checked: true

     cy: Canvas.height/2  # Canvas center
+    cx: Canvas.width/2  # Canvas center

     constructor: ->
         @photons = []
@@ -242,23 +238,26 @@
     emit: () ->
         unless @photons.length > @maxPhotons
             # emitPhoton defined in subclass
+            # console.log "photons", @photons
             @photons.push(@emitPhoton()) for [0...@rate]
         @photons = @photons.filter (photon) => photon.visible()
         for photon in @photons
             photon.move()
-            if @checked then photon.draw()
+            photon.draw()

 class Sun extends Emitter

     x: 1
-    l: 200
+    l: 180

     constructor: ->
-        @velocity = new Vector 2, 0
+        #@velocity = new Vector 2, 0
+        @velocity = new Vector  0, 2
         super()

     emitPhoton: ->
-        pos = new Vector @x, @cy + @l*(Math.random()-0.5)
+        #pos = new Vector @x, @cy + @l*(Math.random()-0.5)
+        pos = new Vector @cx + @l*(Math.random()-0.5), 1
         new Sunlight(pos, @velocity)

@@ -269,13 +268,15 @@
     constructor: ->
         @sun = new Sun
         @sun.rate = 10
-
+        @h = new Histo
+
     start: () ->
         setTimeout (=> @animate 20000), 200

     snapshot: () ->
         Canvas.clear()
         @sun.emit()
+        @h.update(count)

     animate: () ->
         @timer = setInterval (=> @snapshot()), 50
@@ -284,15 +285,11 @@
         clearInterval @timer
         @timer = null

-###
+
 count = (0 for [0..19])
 sim = new Simulation
 $("#params4b").on "click", =>
     sim.stop()
 setTimeout (-> sim.start()), 2000
-###
-
-#h = new Histo
-#h.update((20-i for i in  [0...20]))

 new Plot


spawn: pass stream_opts through to process

--- lib/transforms/spawn.coffee
+++ lib/transforms/spawn.coffee
@@ -1,4 +1,8 @@
 {spawn} = require 'child_process'
 {Process} = require './process'
+
+class Spawn
+  constructor: (stream_opts, process_name, process_args) ->
+    return new Process stream_opts, spawn(process_name, process_args)
 module.exports = (Understream) ->
-  Understream.mixin ((args...) -> new Process {}, spawn args...), 'spawn', true
+  Understream.mixin Spawn, 'spawn'


expanded gene json

expanded gene json

--- node/fabricate.coffee
+++ node/fabricate.coffee
@@ -385,9 +385,45 @@
       position: ""

     when 'gene'
-      id: "abstract-expressionism" + _.uniqueId()
-      name: "Abstract Expressionism"
-      counts: {artworks: 5, artists: 10}
+      type:
+        properties: [
+          {
+            key: "use_as_filter"
+            value: "true"
+          }
+          {
+            key: "use_in_artist_related_genes"
+            value: "true"
+          }
+          {
+            key: "browse_category"
+            value: "Style or Movement"
+          }
+        ]
+        id: "j1-style-or-movement"
+        name: "J1 - Style or Movement"
+        published: false
+
+      influential_artist: null
+      influential_gene: null
+      id: "pop-art" + _.uniqueId()
+      name: "Pop Art"
+      description: "_“The Pop artists did images that anybody walking down Broadway could recognize in a split second—comics, picnic tables, men’s trousers, celebrities, shower curtains, refrigerators, coke bottles—all the great modern things that the Abstract Expressionists tried so hard not to notice at all.” –[Andy Warhol](/artist/andy-warhol)_\n\n \n\nPop Art was the dominant movement in early-1960s American art. Short for “popular art,” it featured common household objects and consumer products like Coca-Cola and Campbell’s Soup cans, as well as forms of media—such as comics, newspapers, and magazines—recognizable to the masses. Artists often created Pop works using mechanical or commercial techniques, such as silk-screening. As Warhol suggests, the choice of mundane subject matter and machine-like techniques was a blunt rejection of the heroic subjects and methods of [Abstract Expressionism](/gene/abstract-expressionism), the leading American movement of the 1950s. Not often discussed is the fact that Pop Art originated in England and paralleled similar movements in France and Germany."
+      image_url: "http://static0.artsy.net/genes/4e5e41670d2c670001030350/:version.jpg"
+      image_versions: [
+        "thumb"
+        "square"
+        "tall"
+        "big_and_tall"
+      ]
+      counts:
+        artworks: 1611
+        artists: 80
+
+      published: true
+      follow_count: 511
+      browseable: true
+      created_at: "2011-08-31T14:12:55Z"

     when 'gene_type'
       id: "art-historical-influences" + _.uniqueId()


setup lein project

setup lein project

--- main.coffee
+++ main.coffee
@@ -46,6 +46,9 @@
     execSync "cp #{ resources }ambly.sh start.sh"
     editSync 'start.sh', [[projNameUnderRx, projNameUs]]

+    log 'Compiling ClojureScript'
+    execSync 'lein cljsbuild once dev'
+
   catch e
     logErr e.message


display debug logs only when debug is activated

--- main.coffee
+++ main.coffee
@@ -48,7 +48,7 @@
         console.error @format 'error', text if process.env.NODE_ENV isnt 'test'

     debug: (text) ->
-        console.info @format 'debug', text if process.env.NODE_ENV isnt 'test'
+        console.info @format 'debug', text if process.env.DEBUG

     raw: (text) ->
         console.log text


Add TODO

Add TODO

--- rtmp.coffee
+++ rtmp.coffee
@@ -688,6 +688,8 @@
       when flv.AVC_PACKET_TYPE_NALU
         if not @avcInfo?
           throw new Error "[rtmp:publish] malformed video data: avcInfo is missing"
+
+        # TODO: This must be too heavy and needs better alternative.
         nalUnits = flv.splitNALUnits info.nalUnits, @avcInfo.nalUnitLengthSize
         nalUnitGlob = h264.concatWithStartCodePrefix nalUnits
       when flv.AVC_PACKET_TYPE_EOS


Send BytesRead messages to clients

Send BytesRead messages to clients

--- rtmp.coffee
+++ rtmp.coffee
@@ -849,14 +849,37 @@
       body: new Buffer [
         # Event Type: 6=PingRequest
         0, 6,
-        # Server Timestamp
+       # Server Timestamp
         (lastTimestamp >> 24) & 0xff,
         (lastTimestamp >> 16) & 0xff,
         (lastTimestamp >> 8) & 0xff,
         lastTimestamp & 0xff
       ]

-    @sendData pingRequest
+  scheduleBytesRead: ->
+    @lastSendBytesReadScheduledTime = Date.now()
+    if !@sendBytesReadTimer
+      @sendBytesReadTimer = setTimeout =>
+        @sendBytesRead()
+      , 1000
+
+  sendBytesRead: ->
+    @sendBytesReadTimer = false
+    bytesRead = createRTMPMessage
+      chunkStreamID: 2
+      timestamp: 0
+      messageTypeID: 0x02  # Bytes Read
+      messageStreamID: 0
+      body: new Buffer [
+        # Server Timestamp
+        (@receivedBytes >> 24) & 0xff,
+        (@receivedBytes >> 16) & 0xff,
+        (@receivedBytes >> 8) & 0xff,
+        @receivedBytes & 0xff
+      ]
+
+    console.log "Sending bytes read", @receivedBytes
+    @sendData bytesRead

   stopPlaying: ->
     @isPlaying = false
@@ -1781,8 +1804,11 @@
     outputs = []
     seq = new Sequent

+    @receivedBytes += buf.length
+
+    @scheduleBytesRead()
+
     if @windowAckSize?
-      @receivedBytes += buf.length
       if @receivedBytes - @lastSentAckBytes > @windowAckSize / 2
 #        console.log "[rtmp:send] Ack=#{@receivedBytes}"
         outputs.push @createAck()


try to use cordova qrcode scanner

try to use cordova qrcode scanner

--- offlineapp/app.coffee
+++ offlineapp/app.coffee
@@ -362,6 +362,17 @@
         console.log "Ignore click on undefined url (target url #{$(ev.target).attr 'href'})"
         ev.preventDefault()
         return
+      if url=='#unlockQrcode'
+        if cordova?.plugins?.barcodeScanner?
+          console.log "try qrcode scanner..."
+          alert "scan..."
+          cordova.plugins.barcodeScanner.scan (result) ->
+            if (result.cancelled)
+              console.log "qrcode scan cancelled"
+            else
+              console.log "qrcode scan "+result.text+" "+result.format
+              router.navigate "#unlock/qrcode/#{encodeURIComponent result.text}", trigger:true
+          return
       if url.charAt(0)=='#'
         console.log "Local url #{url}"
         router.navigate url, trigger:true


simplified err handling block

simplified err handling block

--- main.coffee
+++ main.coffee
@@ -156,16 +156,14 @@
     log '✔ Done', 'bgMagenta'
     log ''

-
-  catch e
-    if e.message.match /type\:.+lein/i
-      logErr 'Leiningen is required (http://leiningen.org/)'
-
-    else if e.message.match /type\:.+pod/i
-      logErr 'CocoaPods is required (https://cocoapods.org/)'
-
-    else
-      logErr e.message
+  catch {message}
+    logErr \
+      if message.match /type\:.+lein/i
+        'Leiningen is required (http://leiningen.org/)'
+      else if message.match /type\:.+pod/i
+        'CocoaPods is required (https://cocoapods.org/)'
+      else
+        message

     process.exit 1