some formatting work

some formatting work

--- lib/compiler.coffee
+++ lib/compiler.coffee
@@ -227,29 +227,29 @@

                 # build up our runtime method table
                 @ejs_intrinsics = Object.create null,
-                        moduleGet:            { value: @handleModuleGet, enumerable: true }
-                        moduleImportBatch:    { value: @handleModuleImportBatch, enumerable: true }
-                        getLocal:             { value: @handleGetLocal, enumerable: true }
-                        setLocal:             { value: @handleSetLocal, enumerable: true }
-                        getGlobal:            { value: @handleGetGlobal, enumerable: true }
-                        setGlobal:            { value: @handleSetGlobal, enumerable: true }
-                        slot:                 { value: @handleGetSlot, enumerable: true }
-                        setSlot:              { value: @handleSetSlot, enumerable: true }
-                        invokeClosure:        { value: @handleInvokeClosure, enumerable: true }
-                        makeClosure:          { value: @handleMakeClosure, enumerable: true }
-                        makeAnonClosure:      { value: @handleMakeAnonClosure, enumerable: true }
-                        createArgScratchArea: { value: @handleCreateArgScratchArea, enumerable: true }
-                        makeClosureEnv:       { value: @handleMakeClosureEnv, enumerable: true }
-                        typeofIsObject:       { value: @handleTypeofIsObject, enumerable: true }
-                        typeofIsFunction:     { value: @handleTypeofIsFunction, enumerable: true }
-                        typeofIsString:       { value: @handleTypeofIsString, enumerable: true }
-                        typeofIsSymbol:       { value: @handleTypeofIsSymbol, enumerable: true }
-                        typeofIsNumber:       { value: @handleTypeofIsNumber, enumerable: true }
-                        typeofIsBoolean:      { value: @handleTypeofIsBoolean, enumerable: true }
-                        builtinUndefined:     { value: @handleBuiltinUndefined, enumerable: true }
-                        isNullOrUndefined:    { value: @handleIsNullOrUndefined, enumerable: true }
-                        isUndefined:          { value: @handleIsUndefined, enumerable: true }
-                        isNull:               { value: @handleIsNull, enumerable: true }
+                        moduleGet:            value: @handleModuleGet
+                        moduleImportBatch:    value: @handleModuleImportBatch
+                        getLocal:             value: @handleGetLocal
+                        setLocal:             value: @handleSetLocal
+                        getGlobal:            value: @handleGetGlobal
+                        setGlobal:            value: @handleSetGlobal
+                        slot:                 value: @handleGetSlot
+                        setSlot:              value: @handleSetSlot
+                        invokeClosure:        value: @handleInvokeClosure
+                        makeClosure:          value: @handleMakeClosure
+                        makeAnonClosure:      value: @handleMakeAnonClosure
+                        createArgScratchArea: value: @handleCreateArgScratchArea
+                        makeClosureEnv:       value: @handleMakeClosureEnv
+                        typeofIsObject:       value: @handleTypeofIsObject
+                        typeofIsFunction:     value: @handleTypeofIsFunction
+                        typeofIsString:       value: @handleTypeofIsString
+                        typeofIsSymbol:       value: @handleTypeofIsSymbol
+                        typeofIsNumber:       value: @handleTypeofIsNumber
+                        typeofIsBoolean:      value: @handleTypeofIsBoolean
+                        builtinUndefined:     value: @handleBuiltinUndefined
+                        isNullOrUndefined:    value: @handleIsNullOrUndefined
+                        isUndefined:          value: @handleIsUndefined
+                        isNull:               value: @handleIsNull

                 @opencode_intrinsics =
                         unaryNot          : true
@@ -301,7 +301,7 @@

                 @doInsideBBlock entry_bb, => ir.createBr return_bb
                 @doInsideBBlock return_bb, =>
-                        #@createCall @ejs_runtime.log, [(consts.string ir, "done with literal initialization")], ""
+                        #@createCall @ejs_runtime.log, [consts.string(ir, "done with literal initialization")], ""
                         ir.createRetVoid()

                 @literalInitializationBB = entry_bb
@@ -361,14 +361,8 @@
                 ir.createLoad null_alloca, "nullload"

         loadUndefinedEjsValue: ->
-                return ir.createLoad(@currentFunction.undef_alloca, "undefload") if @currentFunction.undef_alloca?
-                if @currentFunction.undef_alloca?
-                        undef_alloca = @currentFunction.undef_alloca
-                else
-                        undef_alloca = @createAlloca @currentFunction, types.EjsValue, "undef_alloca"
+                undef_alloca = @createAlloca @currentFunction, types.EjsValue, "undef_alloca"
                 @storeUndefined undef_alloca
-                if not @currentFunction.undef_alloca?
-                        @currentFunction.undef_alloca = undef_alloca
                 ir.createLoad undef_alloca, "undefload"

         storeUndefined: (alloca, name) ->
@@ -493,7 +487,7 @@
         createPropertyStore: (obj,prop,rhs,computed) ->
                 if computed
                         # we store obj[prop], prop can be any value
-                        @createCall @ejs_runtime.object_setprop, [obj, (@visit prop), rhs], "propstore_computed"
+                        @createCall @ejs_runtime.object_setprop, [obj, @visit(prop), rhs], "propstore_computed"
                 else
                         # we store obj.prop, prop is an id
                         if prop.type is Identifier
@@ -513,7 +507,7 @@
                         loadprop = @visit prop

                         if @options.record_types
-                                @createCall @ejs_runtime.record_getprop, [(consts.int32 @genRecordId()), obj, loadprop], ""
+                                @createCall @ejs_runtime.record_getprop, [consts.int32(@genRecordId()), obj, loadprop], ""

                         @createCall @ejs_runtime.object_getprop, [obj, loadprop], "getprop_computed", canThrow
                 else
@@ -521,13 +515,13 @@
                         pname = @getAtom prop.name

                         if @options.record_types
-                                @createCall @ejs_runtime.record_getprop, [(consts.int32 @genRecordId()), obj, pname], ""
+                                @createCall @ejs_runtime.record_getprop, [consts.int32(@genRecordId()), obj, pname], ""

                         @createCall @ejs_runtime.object_getprop, [obj, pname], "getprop_#{prop.name}", canThrow

-        visitOrNull: (n) -> (@visit n) || @loadNullEjsValue()
-        visitOrUndefined: (n) -> (@visit n) || @loadUndefinedEjsValue()
+        visitOrNull:      (n) -> @visit(n) || @loadNullEjsValue()
+        visitOrUndefined: (n) -> @visit(n) || @loadUndefinedEjsValue()

         visitProgram: (n) ->
                 # by the time we make it here the program has been
@@ -556,7 +550,7 @@
                 if iife_dest_bb
                         ir.createBr iife_dest_bb
                         ir.setInsertPoint iife_dest_bb
-                        rv = @createLoad (@findIdentifierInScope n.ejs_iife_rv.name), "%iife_rv_load"
+                        rv = @createLoad @findIdentifierInScope(n.ejs_iife_rv.name), "%iife_rv_load"
                         rv
                 else
                         n
@@ -842,7 +836,7 @@
                 if @iifeStack.top.iife_rv?
                         # if we're inside an IIFE, convert the return statement into a store to the iife_rv alloca + a branch to the iife's dest bb
                         if n.argument?
-                                ir.createStore (@visit n.argument), @findIdentifierInScope @iifeStack.top.iife_rv.name
+                                ir.createStore @visit(n.argument), @findIdentifierInScope @iifeStack.top.iife_rv.name
                         ir.createBr @iifeStack.top.iife_dest_bb
                 else
                         # otherwise generate an llvm IR ret
@@ -851,7 +845,7 @@
                         if @finallyStack.length > 0
                                 @currentFunction.returnValueAlloca = @createAlloca @currentFunction, types.EjsValue, "returnValue" unless @currentFunction.returnValueAlloca?
                                 ir.createStore rv, @currentFunction.returnValueAlloca
-                                ir.createStore (consts.int32 ExitableScope.REASON_RETURN), @currentFunction.cleanup_reason
+                                ir.createStore consts.int32(ExitableScope.REASON_RETURN), @currentFunction.cleanup_reason
                                 ir.createBr @finallyStack[0]
                         else
                                 return_alloca = @createAlloca @currentFunction, types.EjsValue, "return_alloca"
@@ -919,7 +913,7 @@
                         throw new Error "binary assignment operators '#{n.operator}' shouldn't exist at this point"

                 if @options.record_types
-                        @createCall @ejs_runtime.record_assignment, [(consts.int32 @genRecordId()), rhvalue], ""
+                        @createCall @ejs_runtime.record_assignment, [consts.int32(@genRecordId()), rhvalue], ""
                 @storeValueInDest rhvalue, lhs

                 # we need to visit lhs after the store so that we load the value, but only if it's used
@@ -1117,7 +1111,7 @@
                                 type: Literal
                                 value: n.argument.property.name
                                 raw: "'#{n.argument.property.name}'"
-                        return @createCall callee, [(@visitOrNull n.argument.object), (@visit fake_literal)], "result"
+                        return @createCall callee, [@visitOrNull(n.argument.object), @visit(fake_literal)], "result"

                 else if n.operator is "!"
                         arg_value =  @visitOrNull n.argument
@@ -1185,7 +1179,7 @@
                         # inside the then branch, left was falsy
                         if n.operator is "||"
                                 # for || we evaluate the second and store it
-                                ir.createStore (@visit n.right), result
+                                ir.createStore @visit(n.right), result
                         else if n.operator is "&&"
                                 # for && we short circuit out here
                                 ir.createStore left_visited, result
@@ -1286,7 +1280,7 @@

                 unescapedName = n.callee.name.slice 1
                 intrinsicHandler = @ejs_intrinsics[unescapedName]
-                if not intrinsicHandler
+                if not intrinsicHandler?
                         throw "Internal error: ctor should not be null"

                 intrinsicHandler.call @, n, @opencode_intrinsics[unescapedName], true
@@ -1328,7 +1322,7 @@
                 debug.log -> "calling getFunction for #{val}"
                 rv = @module.getFunction val

-                if not rv
+                if not rv?
                         debug.log -> "Symbol '#{val}' not found in current scope"
                         rv = @loadGlobal n

@@ -1694,11 +1688,12 @@
         handleSetGlobal: (exp, opencode) ->
                 if @options.frozen_global
                         throw new SyntaxError "cannot set global property '#{exp.arguments[0].name}' when using --frozen-global"
-
-                pname = @getAtom exp.arguments[0].name
+
+                pname = exp.arguments[0].name
+                patom = @getAtom pname
                 value = @visit exp.arguments[1]

-                @createCall @ejs_runtime.global_setprop, [pname, value], "globalpropstore_#{exp.arguments[0].name}"
+                @createCall @ejs_runtime.global_setprop, [patom, value], "globalpropstore_#{pname}"

         # this method assumes it's called in an opencoded context
         emitEjsvalTo: (val, type, prefix) ->
@@ -1901,8 +1896,8 @@

         createEjsvalICmpUGt: (arg, i64_const, name) -> ir.createICmpUGt @getEjsvalBits(arg), i64_const, name
         createEjsvalICmpULt: (arg, i64_const, name) -> ir.createICmpULt @getEjsvalBits(arg), i64_const, name
-        createEjsvalICmpEq: (arg, i64_const, name) ->  ir.createICmpEq @getEjsvalBits(arg), i64_const, name
-        createEjsvalAnd: (arg, i64_const, name) ->     ir.createAnd @getEjsvalBits(arg), i64_const, name
+        createEjsvalICmpEq:  (arg, i64_const, name) -> ir.createICmpEq  @getEjsvalBits(arg), i64_const, name
+        createEjsvalAnd:     (arg, i64_const, name) -> ir.createAnd     @getEjsvalBits(arg), i64_const, name

         isObject: (val) ->
                 throw new Error("not supported on this architecture") if @options.target_pointer_size isnt 64


add 32 bit versions of these

add 32 bit versions of these

--- lib/compiler.coffee
+++ lib/compiler.coffee
@@ -1996,45 +1996,55 @@
         createEjsvalAnd:     (arg, i64_const, name) -> ir.createAnd     @getEjsvalBits(arg), i64_const, name

         isObject: (val) ->
-                throw new Error("not supported on this architecture") if @options.target_pointer_size isnt 64
-                @createEjsvalICmpUGt val, consts.int64_lowhi(0xfffbffff, 0xffffffff), "cmpresult"
+                if @options.target_pointer_size is 64
+                        @createEjsvalICmpUGt val, consts.int64_lowhi(0xfffbffff, 0xffffffff), "cmpresult"
+                else
+                        mask = @createEjsvalAnd(val, consts.int64_lowhi(0xffffffff, 0x00000000), "mask.i")
+                        ir.createICmpEq(mask, consts.int64_lowhi(0xffffff88, 0x00000000), "cmpresult")

         isString: (val) ->
-                throw new Error("not supported on this architecture") if @options.target_pointer_size isnt 64
-                mask = @createEjsvalAnd val, consts.int64_lowhi(0xffff8000, 0x00000000), "mask.i"
-                ir.createICmpEq mask, consts.int64_lowhi(0xfffa8000, 0x00000000), "cmpresult"
+                if @options.target_pointer_size is 64
+                        mask = @createEjsvalAnd val, consts.int64_lowhi(0xffff8000, 0x00000000), "mask.i"
+                        ir.createICmpEq mask, consts.int64_lowhi(0xfffa8000, 0x00000000), "cmpresult"
+                else
+                        mask = @createEjsvalAnd(val, consts.int64_lowhi(0xffffffff, 0x00000000), "mask.i")
+                        ir.createICmpEq(mask, consts.int64_lowhi(0xffffff85, 0x00000000), "cmpresult")

         isNumber: (val) ->
-                throw new Error("not supported on this architecture") if @options.target_pointer_size isnt 64
-                @createEjsvalICmpULt val, consts.int64_lowhi(0xfff80001, 0x00000000), "cmpresult"
+                if @options.target_pointer_size is 64
+                        @createEjsvalICmpULt val, consts.int64_lowhi(0xfff80001, 0x00000000), "cmpresult"
+                else
+                        throw "not implemented yet"

         isBoolean: (val) ->
-                throw new Error("not supported on this architecture") if @options.target_pointer_size isnt 64
-                mask = @createEjsvalAnd val, consts.int64_lowhi(0xffff8000, 0x00000000), "mask.i"
-                ir.createICmpEq mask, consts.int64_lowhi(0xfff98000, 0x00000000), "cmpresult"
-
-        isTrue: (val) ->
-                throw new Error("not supported on this architecture") if @options.target_pointer_size isnt 64
-                ir.createICmpEq val, consts.ejsval_true(), "cmpresult"
-
-        isFalse: (val) ->
-                throw new Error("not supported on this architecture") if @options.target_pointer_size isnt 64
-                ir.createICmpEq val, consts.ejsval_false(), "cmpresult"
+                if @options.target_pointer_size is 64
+                        mask = @createEjsvalAnd val, consts.int64_lowhi(0xffff8000, 0x00000000), "mask.i"
+                        ir.createICmpEq mask, consts.int64_lowhi(0xfff98000, 0x00000000), "cmpresult"
+                else
+                        mask = @createEjsvalAnd(val, consts.int64_lowhi(0xffffffff, 0x00000000), "mask.i")
+                        ir.createICmpEq(mask, consts.int64_lowhi(0xffffff83, 0x00000000), "cmpresult")
+
+        # these two could/should be changed to check for the specific bitpattern of _ejs_true/_ejs_false
+        isTrue: (val) -> ir.createICmpEq val, consts.ejsval_true(), "cmpresult"
+        isFalse: (val) -> ir.createICmpEq val, consts.ejsval_false(), "cmpresult"

         isUndefined: (val) ->
-                throw new Error("not supported on this architecture") if @options.target_pointer_size isnt 64
-                @createEjsvalICmpEq val, consts.int64_lowhi(0xfff90000, 0x00000000), "cmpresult"
+                if @options.target_pointer_size is 64
+                        @createEjsvalICmpEq(val, consts.int64_lowhi(0xfff90000, 0x00000000), "cmpresult")
+                else
+                        mask = @createEjsvalAnd(val, consts.int64_lowhi(0xffffffff, 0x00000000), "mask.i")
+                        ir.createICmpEq(mask, consts.int64_lowhi(0xffffff82, 0x00000000), "cmpresult")

         isNull: (val) ->
-                throw new Error("not supported on this architecture") if @options.target_pointer_size isnt 64
-                @createEjsvalICmpEq val, consts.int64_lowhi(0xfffb8000, 0x00000000), "cmpresult"
+                if @options.target_pointer_size is 64
+                        @createEjsvalICmpEq val, consts.int64_lowhi(0xfffb8000, 0x00000000), "cmpresult"
+                else
+                        mask = @createEjsvalAnd(val, consts.int64_lowhi(0xffffffff, 0x00000000), "mask.i")
+                        ir.createICmpEq(mask, consts.int64_lowhi(0xffffff87, 0x00000000), "cmpresult")

         handleTypeofIsObject: (exp, opencode) ->
                 arg = @visitOrNull exp.arguments[0]
-                if opencode and @options.target_pointer_size is 64
-                        @createEjsBoolSelect(@isObject(arg))
-                else
-                        @createCall @ejs_runtime.typeof_is_object, [arg], "is_object", false
+                @createEjsBoolSelect(@isObject(arg))

         handleTypeofIsFunction: (exp, opencode) ->
                 arg = @visitOrNull exp.arguments[0]
@@ -2114,42 +2124,27 @@

         handleTypeofIsString: (exp, opencode) ->
                 arg = @visitOrNull exp.arguments[0]
-                if opencode and @options.target_pointer_size is 64
-                        @createEjsBoolSelect(@isString(arg))
-                else
-                        @createCall @ejs_runtime.typeof_is_string, [arg], "is_string", false
+                @createEjsBoolSelect(@isString(arg))

         handleTypeofIsNumber:    (exp, opencode) ->
                 arg = @visitOrNull exp.arguments[0]
-                if opencode and @options.target_pointer_size is 64
-                        @createEjsBoolSelect(@isNumber(arg))
-                else
-                        @createCall @ejs_runtime.typeof_is_number,    [arg], "is_number", false
+                @createEjsBoolSelect(@isNumber(arg))

         handleTypeofIsBoolean:   (exp, opencode) ->
                 arg = @visitOrNull exp.arguments[0]
-                if opencode and @options.target_pointer_size is 64
-                        @createEjsBoolSelect(@isBoolean(arg))
-                else
-                        @createCall @ejs_runtime.typeof_is_boolean,   [arg], "is_boolean", false
+                @createEjsBoolSelect(@isBoolean(arg))

         handleIsUndefined: (exp, opencode) ->
                 arg = @visitOrNull exp.arguments[0]
-                if opencode and @options.target_pointer_size is 64
-                        @createEjsBoolSelect @createEjsvalICmpEq arg, consts.int64_lowhi(0xfff90000, 0x00000000), "cmpresult"
-                else
-                        @createCall @ejs_runtime.typeof_is_undefined, [arg], "is_undefined", false
+                @createEjsBoolSelect(@isUndefined(arg))

         handleIsNull: (exp, opencode) ->
                 arg = @visitOrNull exp.arguments[0]
-                if opencode and @options.target_pointer_size is 64
-                        @createEjsBoolSelect(@isNull(arg))
-                else
-                        @createCall @ejs_runtime.typeof_is_null,      [arg], "is_null", false
+                @createEjsBoolSelect(@isNull(arg))

         handleIsNullOrUndefined: (exp, opencode) ->
                 arg = @visitOrNull exp.arguments[0]
-                if opencode and @options.target_pointer_size is 64
+                if opencode
                         @createEjsBoolSelect(ir.createOr(@isNull(arg), @isUndefined(arg), "or"))
                 else
                         @createCall @ejs_binops["=="],   [@loadNullEjsValue(), arg], "is_null_or_undefined", false
@@ -2256,7 +2251,7 @@

 exports.compile = (tree, base_output_filename, source_filename, options) ->
         abi = if (options.target is "armv7" or options.target is "armv7s") then new ArmABI() else new ABI()
-
+
         tree = insert_toplevel_func tree, source_filename

         debug.log -> escodegen.generate tree


factor out as much of the cond_truthy mess we do into a central place (and pass args in then,else order instead of else,then like every use was.).  Also, fix the merge_bb problem with visitIfOrCondExp - we need to actually be in the merge_bb when we return, and the doInsideBlock call was resetting the insertPoint to the bb we had upon entry.

--- lib/compiler.coffee
+++ lib/compiler.coffee
@@ -425,6 +425,13 @@
                 scope = LoopExitableScope.findLabeled n.label.name
                 # XXX check if scope is null, throw if so
                 scope.exitFore()
+
+        generateCondBr: (exp, then_bb, else_bb) ->
+                exp_value = @visit exp
+                cond_truthy = @createCall @ejs_runtime.truthy, [exp_value], "cond_truthy"
+                cmp = ir.createICmpEq cond_truthy, consts.false(), "cmpresult"
+                ir.createCondBr cmp, else_bb, then_bb
+                exp_value

         visitFor: (n) ->
                 insertBlock = ir.getInsertBlock()
@@ -444,9 +451,7 @@

                 @doInsideBlock test_bb, =>
                         if n.test
-                                cond_truthy = @createCall @ejs_runtime.truthy, [@visit(n.test)], "cond_truthy"
-                                cmp = ir.createICmpEq cond_truthy, consts.false(), "cmpresult"
-                                ir.createCondBr cmp, merge_bb, body_bb
+                                @generateCondBr n.test, body_bb, merge_bb
                         else
                                 ir.createBr body_bb

@@ -477,10 +482,7 @@
                 ir.createBr while_bb

                 @doInsideBlock while_bb, =>
-                        cond_truthy = @createCall @ejs_runtime.truthy, [@visit(n.test)], "cond_truthy"
-                        cmp = ir.createICmpEq cond_truthy, consts.false(), "cmpresult"
-
-                        ir.createCondBr cmp, merge_bb, body_bb
+                        @generateCondBr n.test, body_bb, merge_bb

                 scope = new LoopExitableScope n.label, while_bb, merge_bb
                 scope.enter()
@@ -561,9 +563,6 @@
                 if load_result
                         cond_val = @createAlloca @currentFunction, types.EjsValue, "%cond_val"

-                # first we convert our conditional EJSValue to a boolean
-                cond_truthy = @createCall @ejs_runtime.truthy, [@visit(n.test)], "cond_truthy"
-
                 insertBlock = ir.getInsertBlock()
                 insertFunc = insertBlock.parent

@@ -571,9 +570,7 @@
                 else_bb  = new llvm.BasicBlock "else", insertFunc if n.alternate?
                 merge_bb = new llvm.BasicBlock "merge", insertFunc

-                # we invert the test here - check if the condition is false/0
-                cmp = ir.createICmpEq cond_truthy, consts.false(), "cmpresult"
-                ir.createCondBr cmp, (if else_bb? then else_bb else merge_bb), then_bb
+                @generateCondBr n.test, then_bb, (if else_bb? then else_bb else merge_bb)

                 @doInsideBlock then_bb, =>
                         then_val = @visit n.consequent
@@ -586,11 +583,11 @@
                                 ir.createStore else_val, cond_val if load_result
                                 ir.createBr merge_bb

-                @doInsideBlock merge_bb, =>
-                        if load_result
-                                @createLoad cond_val, "cond_val_load"
-                        else
-                                merge_bb
+                ir.setInsertPoint merge_bb
+                if load_result
+                        @createLoad cond_val, "cond_val_load"
+                else
+                        merge_bb

         visitReturn: (n) ->
                 debug.log "visitReturn"
@@ -892,9 +889,6 @@
                 debug.log -> "operator = '#{n.operator}'"
                 result = @createAlloca @currentFunction, types.EjsValue, "result_#{n.operator}"

-                left_visited = @visit n.left
-                cond_truthy = @createCall @ejs_runtime.truthy, [left_visited], "cond_truthy"
-
                 insertBlock = ir.getInsertBlock()
                 insertFunc = insertBlock.parent

@@ -903,8 +897,7 @@
                 merge_bb = new llvm.BasicBlock "cond_merge", insertFunc

                 # we invert the test here - check if the condition is false/0
-                cmp = ir.createICmpEq cond_truthy, consts.false(), "cmpresult"
-                ir.createCondBr cmp, right_bb, left_bb
+                left_visited = @generateCondBr n.left, left_bb, right_bb

                 @doInsideBlock left_bb, =>
                         # inside the else branch, left was truthy


Use direct encoding param for backwards compatibility

--- src/cson.coffee
+++ src/cson.coffee
@@ -100,10 +100,10 @@
     null

   readObjectSync: (objectPath) ->
-    parseObject(objectPath, fs.readFileSync(objectPath, {encoding: 'utf8'}))
+    parseObject(objectPath, fs.readFileSync(objectPath, 'utf8'))

   readObject: (objectPath, callback) ->
-    fs.readFile objectPath, {encoding: 'utf8'}, (error, contents) =>
+    fs.readFile objectPath, 'utf8', (error, contents) =>
       if error?
         callback?(error)
       else


Fixed : a lot of bugs

Fixed : a lot of bugs

--- lib/hfactory.coffee
+++ lib/hfactory.coffee
@@ -45,8 +45,8 @@

 withActor = (type, actor) ->
-  if not type then throw new Error "Actor's type undefined"
-  if not actor then throw new Error "Actor undefined"
+  throw new Error "Actor's type undefined" if not type
+  throw new Error "Actor undefined" if not actor
   if actors[type]
     logger.warn "Actor '#{type}' already defined"
   else
@@ -54,8 +54,8 @@
     actors[type] = actor

 withAdapter = (type, adapter) ->
-  if not type then throw new Error "Adapter's type undefined"
-  if not adapter then throw new Error "Adapter undefined"
+  throw new Error "Adapter's type undefined" if not type
+  throw new Error "Adapter undefined" if not adapter
   if adapters[type]
     logger.warn "Adapter '#{type}' already defined"
   else
@@ -66,7 +66,7 @@
 newActor = (type, properties) ->
   # Controls and warning about builtinAdapters override
   throw new Error "Actor's type undefined" if not type
-  if not actors[type]
+  if not actors[type]
     actors[type] = require type
   else if typeof actors[type] is "string"
     actors[type] = require actors[type]
@@ -75,7 +75,7 @@
 newAdapter = (type, properties) ->
   # Controls and warning about builtinAdapters override
   throw new Error "Adapter's type undefined" if not type
-  if not adapters[type]
+  if not adapters[type]
     adapters[type] = require type
   else if typeof adapters[type] is "string"
     adapters[type] = require adapters[type]
@@ -90,22 +90,22 @@
   files = fs.readdirSync path
   files.forEach (file) ->
     # TODO Recursive file loading
-    if (file.indexOf ".coffee" isnt -1) and (fs.statSync("#{path}/#{file}").isFile())
+    pos = file.indexOf ".coffee"
+    if (pos isnt -1) and (fs.statSync("#{path}/#{file}").isFile())
       callback file.substr(0, pos), "#{path}/#{file}"

-loadActors = () ->
-  builtinActorsName.forEach(name) ->
-    require "./actor/#{name}"
+loadActors = ->
+  builtinActorsName.forEach (name) ->
+    actors[name]=require "./actor/#{name}"
   loadDirectory "#{process.cwd()}/actors", withActor

 loadAdapters = () ->
-  builtinAdaptersName.forEach(name) ->
-    require "./adapters/#{name}"
+  builtinAdaptersName.forEach (name) ->
+    adapters[name]=require "./adapters/#{name}"
   loadDirectory "#{process.cwd()}/adapters", withAdapter

-
-loadAdapters
-loadActors
+loadActors()
+loadAdapters()

 exports.withActor = withActor


Fixed browserify tests

Fixed browserify tests

--- config/browserify/tests.coffee
+++ config/browserify/tests.coffee
@@ -5,7 +5,7 @@
     output: './_temp/browserify/backbone-orm-underscore.tests.js'
     files: ['test/parameters.coffee', 'test/option_sets.coffee', './test/spec/lib/**/*.tests.coffee', './test/spec/sync/**/*.tests.coffee']
     options:
-      ignore: ['../../../option_sets', '../../../backbone-orm', '../../../../backbone-orm']
+      ignore: ['../../../option_sets', '../../option_sets', '../../../backbone-orm', '../../../../backbone-orm']
       shim:
         'backbone-orm': {path: './backbone-orm.js', exports: 'BackboneORM', depends: {jquery: 'jQuery', underscore: '_', backbone: 'Backbone', stream: 'stream'}}

@@ -13,7 +13,7 @@
     output: './_temp/browserify/backbone-orm-lodash.tests.js'
     files: ['test/parameters.coffee', 'test/option_sets.coffee', './test/spec/lib/**/*.tests.coffee', './test/spec/sync/**/*.tests.coffee']
     options:
-      ignore: ['../../../option_sets', '../../../backbone-orm', '../../../../backbone-orm']
+      ignore: ['../../../option_sets', '../../option_sets', '../../../backbone-orm', '../../../../backbone-orm']
       shim:
         underscore: {path: path.resolve(path.join('.', path.relative('.', require.resolve('lodash')))), exports: '_'}
         'backbone-orm': {path: './backbone-orm.js', exports: 'BackboneORM', depends: {jquery: 'jQuery', underscore: '_', backbone: 'Backbone', stream: 'stream'}}


Add exec command to deployment run script

Signed-off-by: Sonmez Kartal <d2d15c3d37396a5f2a09f8b42cf5f27d43a3fbde@koding.com>

--- config/generateRunFile.coffee
+++ config/generateRunFile.coffee
@@ -696,6 +696,12 @@
     export HOME=/home/ec2-user
     export KONFIG_JSON='#{KONFIG.JSON}'

+    COMMAND=$1
+    shift
+
+    case "$COMMAND" in
+      exec) exec "$@";;
+    esac

     """
 module.exports = { dev: generateDev, default: generateDev, sandbox: generateSandbox, prod: generateSandbox }


Instead of `gzcat`, use `gunzip -c`.

Instead of `gzcat`, use `gunzip -c`.

It seems that the `gzcat` alias is less widely used than `gunzip`.

--- lib/man-view.coffee
+++ lib/man-view.coffee
@@ -20,7 +20,7 @@
                     return
                 file = stdout.replace("\n", "")
                 if file.match(/\.gz$/)
-                    cmd = "gzcat '#{file}' | groff -mandoc -T html"
+                    cmd = "gunzip -c '#{file}' | groff -mandoc -T html"
                 else
                     cmd = "groff -mandoc -T html '#{file}'"
                 exec cmd, (error, stdout, stderr) =>


Use original editors, not after delay. Fix #216

--- lib/messages.coffee
+++ lib/messages.coffee
@@ -725,20 +725,21 @@
   docs.find().forEach (doc) ->
     ShareJS.model.delete doc._id

-  editor2messageUpdate = (id) ->
+  editor2messageUpdate = (id, editors) ->
     Meteor.clearTimeout editorTimers[id]
     doc = Meteor.wrapAsync(ShareJS.model.getSnapshot) id
     #console.log id, 'changed to', doc.snapshot
-    msg = Messages.findOne id
+    msg = findMessage id
     unless msg.body == doc.snapshot
       _messageUpdate id,
         body: doc.snapshot
-      , msg.editing, msg
+      , editors, msg

   delayedEditor2messageUpdate = (id) ->
     Meteor.clearTimeout editorTimers[id]
+    editors = findMessage(id).editing
     editorTimers[id] = Meteor.setTimeout ->
-      editor2messageUpdate id
+      editor2messageUpdate id, editors
     , idle

 Meteor.methods
@@ -876,7 +877,7 @@
       Messages.update id,
         $pull: editing: Meteor.user().username
       unless Messages.findOne(id).editing?.length
-        editor2messageUpdate id
+        editor2messageUpdate id, [Meteor.user().username]
         ShareJS.model.delete id
     ## xxx should add to last MessagesDiff (possibly just made) that
     ## Meteor.user().username just committed this version.


added findWhere to paginated collections ;fixes #1028

--- app/assets/javascripts/admin/models/pagination_collection.coffee
+++ app/assets/javascripts/admin/models/pagination_collection.coffee
@@ -3,7 +3,7 @@

 class PaginationCollection

-  constructor: (models, options) ->
+  constructor : (models, options) ->

     _.extend(this, Backbone.Events)

@@ -23,11 +23,11 @@

     @currentModels = @fullCollection.models.slice()
     @state = _.defaults(_.clone(@state) ? {},
-      pageSize: 10
-      currentPage: 0
-      sorting: null
-      filter: null
-      filterQuery: ""
+      pageSize : 10
+      currentPage : 0
+      sorting : null
+      filter : null
+      filterQuery : ""
     )

     @length = Math.min(@state.pageSize, @fullCollection.length)
@@ -39,37 +39,37 @@
     @listenTo(@fullCollection, "sync", @_passthroughEvent("sync"))

-  add: ->
+  add : ->
     @fullCollection.add.apply(@fullCollection, arguments)

-  remove: ->
+  remove : ->
     @fullCollection.remove.apply(@fullCollection, arguments)

-  set: ->
+  set : ->
     @fullCollection.set.apply(@fullCollection, arguments)

-  fetch: ->
+  fetch : ->
     @fullCollection.fetch.apply(@fullCollection, arguments)

-  create: ->
+  create : ->
     @fullCollection.create.apply(@fullCollection, arguments)

-  reset: ->
+  reset : ->
     @fullCollection.reset.apply(@fullCollection, arguments)

-  setPageSize: (pageSize) ->
+  setPageSize : (pageSize) ->
     @state.pageSize = pageSize
     @_reset()
     return

-  setSorting: (field, order) ->
+  setSorting : (field, order) ->
     @setSort(field, order)
     return

-  setSort: (field, order) ->
+  setSort : (field, order) ->

     if order == "asc"
       order = 1
@@ -94,7 +94,7 @@
     return

-  setFilter: (fields, query) ->
+  setFilter : (fields, query) ->

     if query == '' or not _.isString(query)
       @state.filterQuery = ""
@@ -115,30 +115,30 @@
     return

-  at: (index) ->
+  at : (index) ->
     return @currentModels[index]

-  get: (index) ->
+  get : (index) ->
     return @at(index)

-  clone: ->
+  clone : ->
     clonedCollection = new PaginationCollection(null, {
-      fullCollection: @fullCollection
+      fullCollection : @fullCollection
     })
     clonedCollection.setPageSize(@state.pageSize)
     return clonedCollection

-  _lastPageIndex: ->
+  _lastPageIndex : ->
     return Math.ceil(@currentModels.length / @state.pageSize) - 1

-  _passthroughEvent: (eventType) ->
+  _passthroughEvent : (eventType) ->
     return (args...) ->
       @_reset()
       @trigger(eventType, args...)
       return

-  _resetModels: ->
+  _resetModels : ->
     models = @fullCollection.models.slice()

     if @state.filter?
@@ -155,7 +155,7 @@
     return

-  _reset: ->
+  _reset : ->
     @_resetModels()
     @models = @currentModels.slice(
       @state.currentPage * @state.pageSize,
@@ -168,43 +168,48 @@
     return

-  getPaginationInfo: ->
+  getPaginationInfo : ->
     return {
-      firstPage: 0
-      lastPage: @_lastPageIndex()
-      currentPage: @state.currentPage
-      pageSize: @state.pageSize
+      firstPage : 0
+      lastPage : @_lastPageIndex()
+      currentPage : @state.currentPage
+      pageSize : @state.pageSize
     }

-  getPreviousPage: ->
+  getPreviousPage : ->
     @getPage(@state.currentPage - 1)
     return

-  getNextPage: ->
+  getNextPage : ->
     @getPage(@state.currentPage + 1)
     return

-  getFirstPage: ->
+  getFirstPage : ->
     @getPage(0)
     return

-  getLastPage: ->
+  getLastPage : ->
     @getPage(@_lastPageIndex())
     return

-  getPage: (pageIndex) ->
+  getPage : (pageIndex) ->
     if 0 <= pageIndex < Math.ceil(@currentModels.length / @state.pageSize)
       @state.currentPage = pageIndex
       @_reset()
     return

-  toJSON: ->
+  toJSON : ->
     @models.map((model) -> model.toJSON())

+  findWhere : ->
+
+    return @fullCollection.findWhere.apply(@fullCollection, arguments)
+
+
 module.exports = PaginationCollection


:art: create checkpoint at desirable position for P

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -12,6 +12,7 @@
   destroyNonLastSelection
   getEndOfLineForBufferRow
   setTextAtBufferPosition
+  setBufferRow
 } = require './utils'
 swrap = require './selection-wrapper'
 settings = require './settings'
@@ -585,6 +586,7 @@
       switch @location
         when 'before'
           range = setTextAtBufferPosition(@editor, [row, 0], text)
+          setBufferRow(selection.cursor, range.start.row)
           @groupChangesSinceBufferCheckpoint('undo')
           range
         when 'after'


#667 - skip logic not using option names in multi select criteria

--- dkobo/koboform/static/js/xlform/XLF.model.rowDetails.skipLogic.coffee
+++ dkobo/koboform/static/js/xlform/XLF.model.rowDetails.skipLogic.coffee
@@ -73,6 +73,11 @@

     if @get_correct_type() == 'dropdown'
       choices = @_get_question().getList().options.models
+
+      _.each choices, (model) ->
+        if !model.get('name')?
+          model.set('name', XLF.sluggify model.get 'label')
+
       choice_names = _.map(choices, (model) -> model.get('name'))

       if value in choice_names


inline labels

inline labels

--- src/draw.coffee
+++ src/draw.coffee
@@ -2,7 +2,7 @@
   SVG = world.SVG()
   grid_size = world.get('size')

-  draw_path = (label, world) ->
+  draw_path = (world) ->
     dict = {
       class: ['draw_path', label]
       stroke: world.get 'stroke'
@@ -14,12 +14,12 @@
       dict['d'] = path
       SVG.path dict

-  draw_world = (label, world) ->
+  draw_child = (world) ->
     SVG.g {
-      class: ['draw_world', label]
+      class: ['draw_child', "#{world}"]
       transform: world.bind -> world.get('transform')
     }, world.bind -> _.flatten [
-      draw_path(label,world)
+      draw_path(world)
     ]

   SVG.svg {
@@ -29,5 +29,6 @@
     width: grid_size
     height: grid_size
   }, _.flatten [
-    world.map_child draw_world
+    world.map_child (section) ->
+      section.map_child draw_child
   ]


string replaced by regex

string replaced by regex

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

             result = parametersSplited.map( (item) ->

-                words = item.trim().split(' ')
+                words = item.trim().split(/\s/)

                 objectType: if words[1] then words[0] else undefined
                 varName: if words[1] then words[1] else words[0]


try to fix some null reference errors

try to fix some null reference errors

--- lib/refactor.coffee
+++ lib/refactor.coffee
@@ -33,7 +33,7 @@
       watcher = new Watcher @moduleManager, editor
       @watchers.add watcher
       editor.onDidDestroy =>
-        @watchers.delete watcher
+        @watchers?.delete watcher
         watcher.dispose()
     @disposables.add atom.commands.add 'atom-text-editor', 'refactor:rename', @onRename
     @disposables.add atom.commands.add 'atom-text-editor', 'refactor:done', @onDone


GitProvider: Test: generate new url for each test

--- workers/social/lib/social/models/gitprovider/gitprovider.test.coffee
+++ workers/social/lib/social/models/gitprovider/gitprovider.test.coffee
@@ -3,6 +3,7 @@
 { expect
   withConvertedUser
   expectAccessDenied
+  generateRandomString
   checkBongoConnectivity } = require '../../../../testhelper'

@@ -45,7 +46,7 @@
           readme      =
             content   : 'This is awesome stack template!!!'

-          originalUrl = 'https://github.com/koding/test'
+          originalUrl = "https://github.com/koding/#{generateRandomString()}"
           title       = 'Imported stack template'

           importData = { template, readme, originalUrl }


remove useless code

remove useless code

--- src/game.coffee
+++ src/game.coffee
@@ -112,7 +112,7 @@
 class @Game
   constructor: (id) ->
     @state = States.Empty
-    @entities = Empty: 0, Mine: 1, Flag: 2
+    @entities = Mine: 1
     @nbMines = 50
     @flags = 0
     @safe = 0


Use game dictionary

Use game dictionary

--- src/game.coffee
+++ src/game.coffee
@@ -1,8 +1,8 @@
 {vector} = require './god/vector'

-games = [
-  require("./game/example").game
-]
+games = {
+  example: require("./game/example").game
+}

 IPAD = [1024, 690]
 HALF_WIDTH = IPAD[0] / 2
@@ -56,7 +56,7 @@
   ]
 }

-choose_game = -> games[0]
+choose_game = (query) -> games[query.name]

 extensible = ['language', 'shapes']


If an +add is pending, tell the user where they should send it

--- bot.iced
+++ bot.iced
@@ -85,7 +85,9 @@

 			if user
 				for transaction in user.history
-					if transaction.status is "pending" then return bot.sendMessage chatterID, "You already have an +add request pending"
+					if transaction.status is "pending" and transaction.amount is "add"
+						bot.sendMessage chatterID, "You already have an +add request pending"
+						return bot.sendMessage chatterID, "#{transaction.amount} DOGE are pending to #{transaction.address}. If the request isn't completed in 30 minutes, it will automatically be cancelled."

 			amount = message.split(" ")[1]
 			amount = parseFloat amount, 10
@@ -117,6 +119,7 @@
 					"amount": body.amount
 					"status": "pending"
 					"tx": body.tx
+					"address": body.address
 				await Users_collection.update {id: chatterID}, {$set: {lastAddFundTx: body.tx}, $push:{history: fundTx}}, {w:1}, defer(err)
 				if err
 					console.error err


Update requests.coffee

Update requests.coffee

--- SingularityUI/app/views/requests.coffee
+++ SingularityUI/app/views/requests.coffee
@@ -16,17 +16,15 @@
         @lastRequestsSubFilter = @options.requestsSubFilter

     fetch: ->
-        if @lastRequestsFilter is 'active'
-            @collection = app.collections.requestsActive
-
-        if @lastRequestsFilter is 'paused'
-            @collection = app.collections.requestsPaused
-
-        if @lastRequestsFilter is 'pending'
-            @collection = app.collections.requestsPending
-
-        if @lastRequestsFilter is 'cleaning'
-            @collection = app.collections.requestsCleaning
+        @collection = switch @lastRequestsFilter
+            when 'active'
+                app.collections.requestsActive
+            when 'paused'
+                app.collections.requestsPaused
+            when 'pending'
+                app.collections.requestsPending
+            when 'cleaning'
+                app.collections.requestsCleaning

         @collection.fetch()

@@ -183,4 +181,4 @@
                     else
                         $row.removeClass('filtered')

-module.exports = RequestsView+module.exports = RequestsView


Fixed some bugs in Sequence

Fixed some bugs in Sequence

--- lib/sequence.coffee
+++ lib/sequence.coffee
@@ -14,19 +14,23 @@

 class Sequence
   constructor: (src) ->
-    if typeof src.sequence == 'function'
+    if not src?
+      throw new Error('use null for the empty sequence')
+    else if typeof src.sequence == 'function'
       seq = src.sequence()
-      @first = seq.first
-      @rest  = seq.rest
+      @first = -> seq.first()
+      @rest  = -> seq.rest()
     else if typeof src.first == 'function' and typeof src.rest == 'function'
-      @first = src.first
-      @rest  = src.rest
+      @first = -> src.first()
+      @rest  = -> src.rest()
     else if typeof src.length == 'number'
       n = src.length
       partial = (i) =>
         if i < n then Sequence.conj src[i], -> partial(i+1) else null
       @first = -> src[0]
       @rest  = -> partial(1)
+    else
+      throw new Error('cannot make a sequence from #{src}')

   @conj: (first, rest = (-> null), mode = null) ->
     r = rest() if mode == 'forced'
@@ -45,23 +49,22 @@

   @::S = this

+  make = (seq) -> seq and new Sequence(seq)
+
   @memo: (name, f) ->
-    @[name]        = (seq) -> f.call(this, new Sequence(seq))
+    @[name]        = (seq) -> f.call(this, make(seq))
     @["#{name}__"] = (seq) -> f.call(this, seq)
-    @::[name]      = -> x = f.call(@S, this); (@[name] = -> x)()
+    @::[name]      =       -> x = f.call(@S, this); (@[name] = -> x)()

   @method: (name, f) ->
-    @[name]        = (seq, args...) -> f.call(this, new Sequence(seq), args...)
+    @[name]        = (seq, args...) -> f.call(this, make(seq), args...)
     @["#{name}__"] = (seq, args...) -> f.call(this, seq, args...)
-    @::[name]      = (args...) -> f.call(@S, this, args...)
+    @::[name]      = (args...)      -> f.call(@S, this, args...)

   @operator: (name, f) ->
-    @[name]        = (seq, other, args...) ->
-      f(new Sequence(seq), new Sequence(other), args...)
-    @["#{name}__"] = (seq, other, args...) ->
-      f.call(this, seq, other, args...)
-    @::[name]      = (other, args...) ->
-      f.call(@S, this, new Sequence(other), args...)
+    @[name]        = (seq, other, args...) -> f(make(seq), make(other), args...)
+    @["#{name}__"] = (seq, other, args...) -> f.call(this, seq, other, args...)
+    @::[name]      = (other, args...) -> f.call(@S, this, make(other), args...)

   @memo 'size', (seq) ->
     step = (s, n) -> if s then recur -> step s.rest(), n + 1 else n


Small changes in sequence.

Small changes in sequence.

--- lib/sequence.coffee
+++ lib/sequence.coffee
@@ -19,8 +19,12 @@
       @rest  = -> null
     else if typeof src.toSeq == 'function'
       seq = src.toSeq()
-      @first = -> seq.first()
-      @rest  = -> seq.rest()
+      if seq?
+        @first = -> seq.first()
+        @rest  = -> seq.rest()
+      else
+        @first = ->
+        @rest  = -> null
     else if typeof src.first == 'function' and typeof src.rest == 'function'
       @first = -> src.first()
       @rest  = -> src.rest()
@@ -39,6 +43,12 @@
     else
       throw new Error("cannot make a sequence from #{src}")

+  @accepts: (src) ->
+    not src? or
+    typeof(src.toSeq) == 'function' or
+    (typeof(src.first) == 'function' and typeof(src.rest) == 'function') or
+    typeof(src.length) == 'number'
+
   @conj: (first, rest = (-> null), mode = null) ->
     r = rest() if mode == 'forced'
     new Sequence
@@ -75,7 +85,7 @@
     @::[name]      = (other, args...) -> f.call(@S__, this, make(other), args...)

   @method 'empty', (seq) ->
-    not seq? or typeof seq.first() == 'undefined'
+    not seq? or typeof(seq.first()) == 'undefined'

   @memo 'size', (seq) ->
     step = (s, n) -> if s then recur -> step s.rest(), n + 1 else n


update context

update context

--- def/context.cson
+++ def/context.cson
@@ -46,6 +46,8 @@
     "tabs:split-down": "ペイン分割 ↓"
     "tabs:split-left": "ペイン分割 ←"
     "tabs:split-right": "ペイン分割 →"
+  ".tab.pending-tab":
+    "tabs:keep-pending-tab": "プレビュー状態を解除"
   ".tab-bar":
     "pane:reopen-closed-item": "閉じたタブを開く"
   ".tree-view.full-menu":


update context localize

update context localize

--- def/context.cson
+++ def/context.cson
@@ -18,19 +18,15 @@
     "pane:split-right-and-copy-active-item": "ペイン分割･複製 →"
     "pane:close": "ペインを閉じる"
   "atom-pane":
-    "pane:split-up": "ペイン分割 ↑"
-    "pane:split-down": "ペイン分割 ↓"
-    "pane:split-left": "ペイン分割 ←"
-    "pane:split-right": "ペイン分割 →"
+    "pane:split-up-and-copy-active-item": "ペイン分割 ↑"
+    "pane:split-down-and-copy-active-item": "ペイン分割 ↓"
+    "pane:split-left-and-copy-active-item": "ペイン分割 ←"
+    "pane:split-right-and-copy-active-item": "ペイン分割 →"
     "pane:close": "ペインを閉じる"
   "atom-text-editor:not([mini])":
     "encoding-selector:show": "エンコーディング選択"
     "spell-check:correct-misspelling": "スペル修正"
     "symbols-view:go-to-declaration": "宣言に移動"
-  "atom-pane[data-active-item-path] .item-views":
-    "tree-view:reveal-active-file": "ツリービューに表示"
-  "atom-pane[data-active-item-path] .tab.active":
-    "tree-view:reveal-active-file": "ツリービューに表示"
   ".overlayer":
     "autocomplete:toggle": "オートコンプリート"
     "grammar-selector:show": "文法を選択"
@@ -56,7 +52,7 @@
   ".tree-view.full-menu":
     "tree-view:add-file": "新規ファイル"
     "tree-view:add-folder": "新規フォルダ"
-    "tree-view:move": "移動・名称変更..."
+    "tree-view:move": "移動・名前を変更..."
     "tree-view:duplicate": "複製"
     "tree-view:remove": "削除"
     "tree-view:copy": "コピー"
@@ -78,7 +74,7 @@
   ".tree-view.full-menu .project-root > .header":
     "tree-view:add-file": "新規ファイル"
     "tree-view:add-folder": "新規フォルダ"
-    "tree-view:move": "移動・名称変更..."
+    "tree-view:move": "移動・名前を変更..."
     "tree-view:duplicate": "複製"
     "tree-view:remove": "削除"
     "tree-view:copy": "コピー"
@@ -106,3 +102,20 @@
     "tree-view:copy": "コピー"
     "tree-view:cut": "カット"
     "tree-view:paste": "ペースト"
+  "atom-pane[data-active-item-path] .item-views":
+    "tree-view:reveal-active-file": "ツリービューに表示"
+  "atom-pane[data-active-item-path] .tab.active":
+    "tree-view:rename": "移動・名前を変更..."
+    "tree-view:reveal-active-file": "ツリービューに表示"
+  ".platform-darwin atom-pane[data-active-item-path] .tab.active":
+    "tree-view:show-current-file-in-file-manager": "Finder で表示"
+  ".platform-win32 atom-pane[data-active-item-path] .tab.active":
+    "tree-view:show-current-file-in-file-manager": "エクスプローラで表示"
+  ".platform-linux atom-pane[data-active-item-path] .tab.active":
+    "tree-view:show-current-file-in-file-manager": "ファイルマネージャで表示"
+  ".platform-darwin atom-text-editor:not([mini])":
+    "tree-view:show-current-file-in-file-manager": "Finder で表示"
+  ".platform-win32 atom-text-editor:not([mini])":
+    "tree-view:show-current-file-in-file-manager": "エクスプローラで表示"
+  ".platform-linux atom-text-editor:not([mini])":
+    "tree-view:show-current-file-in-file-manager": "ファイルマネージャで表示"


Fixes #42: correct Atom snippets.

Fixes #42: correct Atom snippets.

--- tools/syntax_highlighting/atom/language-do/snippets/language-do.cson
+++ tools/syntax_highlighting/atom/language-do/snippets/language-do.cson
@@ -18,22 +18,22 @@
 		'prefix': 'le'
 		'body': 'let $1 = $2'
 	'let structure':
-		'prefix': 'ls'
+		'prefix': 'les'
 		'body': 'let $1 = new $2'
 	'let array':
-		'prefix': 'la'
+		'prefix': 'lea'
 		'body': 'let $1 = new array($2)'
 	'if':
 		'prefix': 'if'
 		'body': 'if $1 then\n\t$2\nend'
 	'if else':
-		'prefix': 'ie'
+		'prefix': 'ife'
 		'body': 'if $1 then\n\t$2\nelse\n\t$3\nend'
 	'else':
 		'prefix': 'el'
 		'body': 'else\n\t$1'
 	'else if':
-		'prefix': 'ei'
+		'prefix': 'eli'
 		'body': 'else if $1 then\n\t$2'
 	'while':
 		'prefix': 'wh'


Avoid splitting cached icon-names

Avoid splitting cached icon-names

If a tab's iconName property already exists as an Array object, it won't
have a split method defined. This raises a breaking error at runtime.

--- lib/tab-view.coffee
+++ lib/tab-view.coffee
@@ -187,7 +187,7 @@

   updateIcon: ->
     if @iconName
-      names = @iconName.split /\s+/g
+      names = unless Array.isArray(@iconName) then @iconName.split(/\s+/g) else @iconName
       @itemTitle.classList.remove('icon', "icon-#{names[0]}", names...)

     if @iconName = @item.getIconName?()


Add watch command and use slack reactions as command shortcuts

--- src/jira.coffee
+++ src/jira.coffee
@@ -28,6 +28,7 @@
 moment = require 'moment'
 Octokat = require 'octokat'
 Fuse = require 'fuse.js'
+TICKET_CREATED_TEXT = "Ticket created"

 module.exports = (robot) ->
   jiraUrl = process.env.HUBOT_JIRA_URL
@@ -41,14 +42,15 @@
     robot.adapter.customMessage payload

   fetch = (url, opts) ->
-    robot.logger.info "Fetching: #{url}"
     options =
       headers:
         "X-Atlassian-Token": "no-check"
         "Content-Type": "application/json"
         "Authorization": 'Basic ' + new Buffer("#{process.env.HUBOT_JIRA_USERNAME}:#{process.env.HUBOT_JIRA_PASSWORD}").toString('base64')
     options = _(options).extend opts
-
+    options.headers["Content-Length"] = options.body.length if options.method is "POST"
+
+    robot.logger.info "Fetching: #{url}"
     nodeFetch(url,options).then (response) ->
       if response.status >= 200 and response.status < 300
         return response
@@ -57,9 +59,10 @@
         error.response = response
         throw error
     .then (response) ->
-      response.json()
+      response.json() if response.status isnt 204
     .catch (error) ->
       robot.logger.error error.stack
+      throw error

   token = process.env.HUBOT_GITHUB_TOKEN
   octo = new Octokat token: token
@@ -80,6 +83,7 @@
   transitions = JSON.parse process.env.HUBOT_JIRA_TRANSITIONS_MAP if process.env.HUBOT_JIRA_TRANSITIONS_MAP
   transitionRegex = eval "/(?\:^|\\s)((?\:#{prefixes}-)(?\:\\d+))\\s+(?\:to\\s+|>\\s?)(#{(transitions.map (t) -> t.name).join "|"})/i" if transitions
   rankRegex = eval "/(?\:^|\\s)((?\:#{prefixes}-)(?\:\\d+)) rank (up|down|top|bottom)/i"
+  watchRegex = eval "/(?\:^|\\s)((?\:#{prefixes}-)(?\:\\d+)) watch(?: @?([\\w._]*))?/i"
   assignRegex = eval "/(?\:^|\\s)((?\:#{prefixes}-)(?\:\\d+)) assign @?([\\w._]*)/i"
   commentRegex = eval "/(?\:^|\\s)((?\:#{prefixes}-)(?\:\\d+))\\s?(?\:<\\s?)([^]+)/i"
   labelsRegex = /(?:\s+|^)#\S+/g
@@ -123,6 +127,14 @@
       results = f.search name
       result = if results? and results.length >=1 then results[0] else undefined
       return result
+
+  buildQueryString = (params) ->
+    "?#{("#{encodeURIComponent k}=#{encodeURIComponent v}" for k,v of params when v).join "&"}"
+
+  fuzzyFind = (term, arr, keys) ->
+    f = new Fuse arr, keys: keys, shouldSort: yes
+    results = f.search term
+    result = if results? and results.length >=1 then results[0]

   report = (project, type, msg) ->
     reporter = null
@@ -185,7 +197,7 @@
     .then (json) ->
       issue.key = json.key
       send msg,
-        text: "Ticket created"
+        text: TICKET_CREATED_TEXT
         attachments: [ buildJiraAttachment issue, no ]

       if toState
@@ -259,9 +271,7 @@
       keywords: "experiment exploratory task"
       color: "#f6c342"
     ]
-    f = new Fuse colors, keys: ['keywords'], shouldSort: yes
-    results = f.search json.fields.issuetype.name
-    result = if results? and results.length >=1 then results[0] else color: "#003366"
+    result = fuzzyFind json.fields.issuetype.name, colors, ['keywords']

     fields = []
     fieldsFallback = ""
@@ -351,7 +361,7 @@
       robot.logger.error error.stack

   handleTransitionRequest = (msg, includeAttachment=yes) ->
-    msg.finish()
+    msg.finish?()
     [ __, ticket, toState ] = msg.match
     ticket = ticket.toUpperCase()

@@ -376,8 +386,8 @@
     .catch (error) ->
       send msg, "An error has occured: #{error}"

-  handleRankRequest = (msg) ->
-    msg.finish()
+  handleRankRequest = (msg, includeAttachment=yes) ->
+    msg.finish?()
     [ __, ticket, direction ] = msg.match
     ticket = ticket.toUpperCase()
     direction = direction.toLowerCase()
@@ -398,12 +408,12 @@
     .then (json) ->
       send msg,
         text: "Ranked <#{jiraUrl}/browse/#{ticket}|#{ticket}> to `#{direction}`"
-        attachments: [ buildJiraAttachment json, no ]
+        attachments: [ buildJiraAttachment json, no ] if includeAttachment
     .catch (error) ->
       send msg, "An error has occured: #{error}"

   handleAssignRequest = (msg, includeAttachment=yes) ->
-    msg.finish()
+    msg.finish?()
     [ __, ticket, person ] = msg.match
     person = if person is "me" then msg.message.user.name else person
     ticket = ticket.toUpperCase()
@@ -413,15 +423,14 @@
       fetch("#{jiraUrl}/rest/api/2/user/search?username=#{slackUser.email_address}")
       .then (user) ->
         jiraUser = user[0] if user and user.length is 1
-        if jiraUser
-          fetch "#{jiraUrl}/rest/api/2/issue/#{ticket}",
-            method: "PUT"
-            body: JSON.stringify
-              fields:
-                assignee:
-                  name: jiraUser.name
-        else
-          send msg, "Cannot find jira user <@#{slackUser.id}>"
+        throw "Cannot find jira user <@#{slackUser.id}>" unless jiraUser
+
+        fetch "#{jiraUrl}/rest/api/2/issue/#{ticket}",
+          method: "PUT"
+          body: JSON.stringify
+            fields:
+              assignee:
+                name: jiraUser.name
       .then () ->
         fetch "#{jiraUrl}/rest/api/2/issue/#{ticket}"
       .then (json) ->
@@ -433,8 +442,36 @@
     else
       send msg, "Cannot find slack user `#{person}`"

+  handleWatchRequest = (msg, includeAttachment=yes) ->
+    msg.finish?()
+    [ __, ticket, person ] = msg.match
+    person = if person is "me" or not person then msg.message.user.name else person
+
+    ticket = ticket.toUpperCase()
+    slackUser = lookupSlackUser person
+
+    if slackUser
+      fetch("#{jiraUrl}/rest/api/2/user/search?username=#{slackUser.email_address}")
+      .then (user) ->
+        jiraUser = user[0] if user and user.length is 1
+        throw "Cannot find jira user <@#{slackUser.id}>" unless jiraUser
+
+        fetch "#{jiraUrl}/rest/api/2/issue/#{ticket}/watchers",
+          method: "POST"
+          body: JSON.stringify jiraUser.name
+      .then ->
+        fetch "#{jiraUrl}/rest/api/2/issue/#{ticket}"
+      .then (json) ->
+        send msg,
+          text: "Added <@#{slackUser.id}> as a :watch:er on <#{jiraUrl}/browse/#{ticket}|#{ticket}>"
+          attachments: [ buildJiraAttachment json, no ] if includeAttachment
+      .catch (error) ->
+        send msg, "#{error}"
+    else
+      send msg, "Cannot find slack user `#{person}`"
+
   addComment = (msg) ->
-    msg.finish()
+    msg.finish?()
     [ __, ticket, comment ] = msg.match

     fetch "#{jiraUrl}/rest/api/2/issue/#{ticket}/comment",
@@ -534,6 +571,15 @@

     Where `<ticket>` is the JIRA ticket number
     and `<state>` is one of the following: #{(transitions.map (t) -> "`#{t.name}`").join ',  '}
+    """
+
+    watch = """
+    *Watching Tickets*
+    > `<ticket>` watch [`@username]`]
+
+    Where `<ticket>` is the JIRA ticket number
+    `@username` is optional, if specified the corresponding JIRA user will become
+    the watcher on the ticket, if omitted the message author will become the watcher
     """

     search = """
@@ -558,10 +604,82 @@
       responses = [ transition ]
     else if _(["search", "searching"]).contains topic
       responses = [ search ]
+    else if _(["watch", "watching"]).contains topic
+      responses = [ watch ]
     else
-      responses = [ overview, opening, rank, comment, assignment, transition, search ]
+      responses = [ overview, opening, rank, comment, assignment, transition, watch, search ]

     send msg, "\n#{responses.join '\n\n\n'}"
+
+  robot.on "jira_ticket_created_message", (msg) ->
+    reactions = ["+1", "-1", "watch", "raising_hand", "white_check_mark", "fast_forward"]
+    dispatchNextReaction = ->
+      reaction = reactions.shift()
+      return unless reaction
+      params =
+        channel: msg.channel
+        timestamp: msg.ts
+        name: reaction
+        token: process.env.HUBOT_SLACK_TOKEN
+      fetch("https://slack.com/api/reactions.add#{buildQueryString params}").then dispatchNextReaction
+    dispatchNextReaction()
+
+  robot.adapter.client.on "raw_message", (msg) ->
+    robot.emit "jira_ticket_created_message", msg if msg.type is "message" and msg.user is robot.adapter.self.id and msg.text is TICKET_CREATED_TEXT
+    return unless msg.type is "reaction_added"
+    return unless msg.item_user is robot.adapter.self.id
+    return unless msg.user isnt robot.adapter.self.id
+
+    switch msg.item.channel[0]
+      when "G"
+        endpoint = "groups"
+      when "C"
+        endpoint = "channels"
+      else
+        return
+
+    params =
+      channel: msg.item.channel
+      latest: msg.item.ts
+      oldest: msg.item.ts
+      inclusive: 1
+      count: 1
+      token: process.env.HUBOT_SLACK_TOKEN
+
+    fetch("https://slack.com/api/#{endpoint}.history#{buildQueryString params}")
+    .then (json) ->
+      message = json.messages?[0]
+      return unless message? and message.type is "message"
+      attachment = message.attachments?[0]
+      return unless attachment?.title_link?
+      ticket = attachment.title_link.split "#{jiraUrl}/browse/"
+      return unless ticket and ticket.length is 2
+      ticket = ticket[1]
+
+      switch msg.reaction
+        when "+1", "-1"
+          handleRankRequest
+            message: room: msg.item.channel
+            match: [ "", ticket, if msg.reaction is "+1" then "up" else "down" ]
+          , no
+        when "watch"
+          handleWatchRequest
+            message: room: msg.item.channel
+            match: [ "", ticket, robot.adapter.client.getUserByID(msg.user).name ]
+          , no
+        when "white_check_mark", "fast_forward"
+          term = if msg.reaction is "white_check_mark" then "done" else "progress"
+          result = fuzzyFind term, transitions, ['jira']
+          if result
+            handleTransitionRequest
+              message: room: msg.item.channel
+              match: [ "", ticket, result.name ]
+            , no
+        when "raising_hand"
+          handleAssignRequest
+            message: room: msg.item.channel
+            match: [ "", ticket, robot.adapter.client.getUserByID(msg.user).name ]
+          , no

   robot.respond /(?:j|jira) (?:s|search|find|query) (.+)/, (msg) ->
     [__, term] = msg.match
@@ -607,6 +725,7 @@

   robot.hear transitionRegex, handleTransitionRequest if transitions
   robot.hear rankRegex, handleRankRequest
+  robot.hear watchRegex, handleWatchRequest
   robot.hear assignRegex, handleAssignRequest
   robot.hear commentRegex, addComment


Improve the labels regex to handle labels with hyphens in them

--- src/jira.coffee
+++ src/jira.coffee
@@ -43,7 +43,7 @@
               reporter = user[0] if user and user.length is 1
             finally
               quoteRegex = /`(.*?)`/
-              labelsRegex = /#\w\w+\s?/g
+              labelsRegex = /#\S+\s?/g
               labels = ["triage"]
               message = msg.match[1]


KodingAppsController: Simplfy script injecting mechanism, add support for internal app files which is development only

--- client/Main/kodingappscontroller.coffee
+++ client/Main/kodingappscontroller.coffee
@@ -36,33 +36,15 @@
   # Please make sure about your changes on it.
   @putAppScript = (app, callback = noop)->

-    identifier = app.identifier.replace /\./g, '_'
-
     @unloadAppScript app

-    if app.style and $("head .internal-style-#{identifier}").length is 0
-
-      style        = new KDCustomHTMLView
-        tagName    : "link"
-        cssClass   : "internal-style-#{identifier}"
-        attributes :
-          rel      : "stylesheet"
-          href     : "#{app.style}?#{KD.utils.uniqueId()}"
-
-      $('head')[0].appendChild style.getElement()
-
-    if app.script and $("head .internal-script-#{identifier}").length is 0
-
-      script       = new KDCustomHTMLView
-        tagName    : "script"
-        cssClass   : "internal-script-#{identifier}"
-        bind       : "load"
-        load       : -> callback null
-        attributes :
-          type     : "text/javascript"
-          src      : "#{app.script}?#{KD.utils.uniqueId()}"
-
-      $('head')[0].appendChild script.getElement()
+    identifier = app.identifier
+
+    if app.style
+      @appendScriptElement 'style',  { url:app.style, identifier, callback }
+
+    if app.script
+      @appendScriptElement 'script', { url:app.script, identifier, callback }

   @unloadAppScript = (app, callback = noop)->

@@ -117,6 +99,48 @@
         cancel       :
           style      : "modal-cancel"
           callback   : -> modal.destroy()
+
+  @appendScriptElement = (type, {identifier, url, callback})->
+
+    identifier = identifier.replace /\./g, '_'
+    cssClass   = "internal-#{type}-#{identifier}"
+    vmName     = FSHelper.getVMNameFromPath url
+    tagName    = type
+
+    # Which means this is an invm-app
+    if vmName
+
+      file = FSHelper.createFileFromPath url
+      file.fetchContents (err, partial)->
+        return  if err
+        obj = new KDCustomHTMLView {cssClass, tagName, partial}
+
+        if type is 'script'
+          obj.once 'viewAppended', -> callback null
+
+        KD.utils.defer -> obj.appendToSelector 'head'
+
+    else
+
+      url  = "#{url}?#{KD.utils.uniqueId()}"
+      bind = ''
+      load = noop
+
+      if type is 'style'
+        tagName    = 'link'
+        attributes =
+          rel      : 'stylesheet'
+          href     : url
+      else
+        attributes =
+          type     : "text/javascript"
+          src      : url
+        bind       = "load"
+        load       = -> callback null
+
+      $('head')[0].appendChild (new KDCustomHTMLView {
+        cssClass, tagName, attributes, bind, load
+      }).getElement()

   # #
   # MAKE NEW APP


instruction book refactor

instruction book refactor

--- client/app/MainApp/book/bookview.coffee
+++ client/app/MainApp/book/bookview.coffee
@@ -639,11 +639,7 @@
     return callback @unreadPages if @unreadPages

     @getStorage().fetchValue "lastReadVersion", (lastReadVersion=0)=>
-
-      version = @getVersion()
-
-      if version > lastReadVersion
-
+      if @getVersion() > lastReadVersion
         unreadPages = @getNewerPages lastReadVersion

         @newPagePointer ?= 0


client: remove markdownUrls util

client: remove markdownUrls util

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- client/app/lib/util/markdownUrls.coffee
+++ client/app/lib/util/markdownUrls.coffee
@@ -1,47 +0,0 @@
-urlGrabber = require 'app/util/urlGrabber'
-regexps    = require 'app/util/regexps'
-
-
-formatUrls = (text) ->
-
-  urls          = urlGrabber text
-  processedUrls = {}
-
-  for url in urls
-    continue  if processedUrls[url]
-
-    urlWithProtocol = unless regexps.hasProtocol.test url
-    then "http://#{url}"
-    else url
-
-    urlMarkdown = "[#{url}](#{urlWithProtocol})"
-
-    urlRegExp = new RegExp "(\\s|^)(#{url})(\\s|$)", 'g'
-    text      = text.replace urlRegExp, (match, p1, p2, p3) -> p1 + urlMarkdown + p3
-
-    processedUrls[url] = yes
-
-  return text
-
-
-formatUrlsByPosition = (text, startIndex, endIndex) ->
-
-  text = text.substring startIndex, endIndex
-  return formatUrls text
-
-
-module.exports = markdownUrls = (body) ->
-
-  regExp    = new RegExp '(`|```)([^`]+)\\1', 'g'
-  prevIndex = 0
-  result    = ''
-
-  while match = regExp.exec body
-    formattedText   = formatUrlsByPosition body, prevIndex, match.index
-    result         += formattedText
-    result         += match[0]
-    prevIndex       = regExp.lastIndex
-
-  result += formatUrlsByPosition body, prevIndex
-
-  return result


Fixed: stores couldn't work when mangled

--- client/app/libs/flux/store/Store.coffee
+++ client/app/libs/flux/store/Store.coffee
@@ -1,30 +1,36 @@
 {EventEmitter} = require 'events'
 AppDispatcher = require '../../../AppDispatcher'

+
 module.exports = class Store extends EventEmitter

+    uniqID: null
+
     # this variable will be shared with all subclasses so we store the items by subclass
+    # we don't use `@constructor.name` because it breaks when mangled
+    _nextUniqID = 0
     _handlers = {}
     _addHandlers = (type, callback) ->
-        storeName = @constructor.name
-        _handlers[storeName] = {} unless _handlers[storeName]?
-        _handlers[storeName][type] = callback
+
+        _handlers[@uniqID] = {} unless _handlers[@uniqID]?
+        _handlers[@uniqID][type] = callback

     # Registers the store's callbacks to the dispatcher
     _processBinding = ->
-        storeName = @constructor.name
         @dispatchToken = AppDispatcher.register (payload) =>
             {type, value} = payload.action
-            if (callback = _handlers[storeName][type])? then callback.call @, value
-
-        console.log "#{storeName} -- #{@dispatchToken}"
+            if (callback = _handlers[@uniqID][type])? then callback.call @, value

     constructor: ->
         super()
+        # set a uniq ID
+        @uniqID = _nextUniqID++
         @__bindHandlers _addHandlers.bind @
         _processBinding.call @

     # Must be overriden by each store
-    __bindHandlers: (handle) -> throw new Error "The store #{@constructor.name} must define a `__bindHandlers` method"
+    __bindHandlers: (handle) ->
+        if __DEV__
+            throw new Error "The store #{@constructor.name} must define a `__bindHandlers` method"


Cleaned up module class

Cleaned up module class

--- src/Module.coffee
+++ src/Module.coffee
@@ -2,21 +2,17 @@

 class Module
 	view: (@viewName, @viewClass) =>
-		infect.set @viewName, @viewClass
-		name = !!@viewName
-		klass = !!@viewClass
-		if !name and !klass then return @view
-		else if !name or !klass then throw new Error "Missing argument"
-		else return this
+		if !!@viewName and !!@viewClass
+			infect.set @viewName, @viewClass
+			return this
+		else return @view
 	viewModel: (@viewModelName, @viewModelClass) =>
-		infect.set @viewModelName, @viewModelClass
-		name = !!@viewModelName
-		klass = !!@viewModelClass
-		if !name and !klass then return @viewModel
-		else if !name or !klass then throw new Error "Missing argument"
-		else return this
+		if !!@viewModelName and !!@viewModelClass
+			infect.set @viewModelName, @viewModelClass
+			return this
+		else return @viewModel
 	init: =>
-		@viewModel = new @viewModelClass()
+		@viewModel = new @viewModelClass?()
 		@view = new @viewClass @viewModel
 		return this
 	infect: (name, infectable) =>


implement

implement

--- src/main.coffee
+++ src/main.coffee
@@ -36,9 +36,15 @@
                 assert(not relativeFilePath.startsWith(m[2]), "Negation violated.")
         else
             if m[3] == ""
-                assert(not relativeFilePath.endsWith("/" + m[2]), "Negation violated.")
+                assert(not (
+                    relativeFilePath.endsWith("/" + m[2]) or
+                    relativeFilePath == m[2]
+                ), "Negation violated.")
             else
-                assert(not relativeFilePath.includes("/" + m[2]), "Negation violated.")
+                assert(not (
+                    relativeFilePath.includes("/" + m[2]) or
+                    relativeFilePath.startsWith(m[2])
+                ), "Negation violated.")
     rawCodeType = path.extname(filePath).substr(1) # strip the leading "."
     rawCode = fs.readFileSync(filePath, {encoding: "utf8"})
     baseDirectory = path.dirname(filePath)


Fix bug in algorithm

Fix bug in algorithm

--- src/main.coffee
+++ src/main.coffee
@@ -32,7 +32,7 @@
       cell.z + CELL_COLUMNS - 1,
       cell.z + CELL_COLUMNS,
       cell.z + CELL_COLUMNS + 1
-    ] when cells.getAt(cellPosition)?.alive
+    ] when cells.getAt(cellPosition - 1)?.alive

     if cell.alive
       cell.toBeKilled = true if aliveNeighbors not in [2..3]


Use specific version of jQuery, v1.7.1  There is trouble using most recent version of jquery (v1.7.2) with most recent version of jquery mobile.

--- src/main.coffee
+++ src/main.coffee
@@ -1,6 +1,6 @@
 require.config
 	paths:
-		jquery: 'http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min'
+		jquery: 'http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min'
 		jqm:    'http://code.jquery.com/mobile/1.1.0-rc.1/jquery.mobile-1.1.0-rc.1.min'
 		json2:  'http://ajax.cdnjs.com/ajax/libs/json2/20110223/json2'
 		jsd:    'lib/jsdeferred'


implementing event emitters: stop events for PS.$drain()

--- src/main.coffee
+++ src/main.coffee
@@ -109,7 +109,11 @@
     stream  = path_or_stream
   ### TAINT intermediate solution ###
   R       = STPS.sink stream, ( error ) => throw error if error?
+  seen_events = []
   R.on    = ( P... ) -> stream.on P...
+  R.on 'finish', -> seen_events.push 'finish'; debug '777822', '###############', seen_events
+  R.on 'end', -> seen_events.push 'end'; debug '777822', '###############', seen_events
+  R.on 'close', -> seen_events.push 'close'; debug '777822', '###############', seen_events
   return R


work in progress: making the code actually be evaluated

--- src/main.coffee
+++ src/main.coffee
@@ -47,8 +47,8 @@
 )

 execute = (code) -> (
-  s = code.split('').reverse().join('')
-  outputarea.text(s)
+  f = eval(code)
+  run(f, outputarea)
 )

 showOrHideCode = (-> (


Plugin name and conf location is consistent

--- src/main.coffee
+++ src/main.coffee
@@ -1,6 +1,6 @@
 Alchemy::plugins.neo4jBackend = (instance) ->
   a: instance
-  conf: instance.conf.plugins["neo4j"]
+  conf: instance.conf.plugins["neo4jBackend"]
   graphJSON:  {}
   constructor: ->
     conf = @conf


fix code highlighting in expression results fixes https://github.com/JunoLab/Juno.jl/issues/76

--- lib/ui/views.coffee
+++ lib/ui/views.coffee
@@ -87,12 +87,14 @@

   code: ({text, attrs, scope}) ->
     grammar = atom.grammars.grammarForScopeName("source.julia")
-    highlighted = Highlighter.highlight(text, grammar, {scopePrefix: 'syntax--', block: attrs['block']})
-    @render {type: 'html', block: attrs['block'], content: highlighted}
+    block = attrs?.block || false
+    highlighted = Highlighter.highlight(text, grammar, {scopePrefix: 'syntax--', block})
+    @render {type: 'html', block, content: highlighted}

   latex: ({attrs, text}) ->
-    latex = @ink.KaTeX.texify(text, attrs['block'])
-    @render {type: 'html', block: attrs['block'], content: latex}
+    block = attrs?.block || false
+    latex = @ink.KaTeX.texify(text, block)
+    @render {type: 'html', block, content: latex}

   views:
     dom:     (a...) -> views.dom  a...


tidy up

tidy up

--- app/assets/javascripts/components/mark/tools/text-row-tool/index.cjsx
+++ app/assets/javascripts/components/mark/tools/text-row-tool/index.cjsx
@@ -3,7 +3,6 @@
 Draggable        = require 'lib/draggable'
 DeleteButton     = require './delete-button'
 DragHandle       = require './drag-handle'
-
 MarkButtonMixin  = require 'lib/mark-button-mixin'

 RADIUS = 10
@@ -23,7 +22,7 @@
 module.exports = React.createClass
   displayName: 'TextRowTool'

-  mixins: [MarkButtonMixin]
+  mixins: [MarkButtonMixin] # adds MarkButton and helper methods to each mark

   statics:
     defaultValues: ({x, y}) ->
@@ -37,9 +36,6 @@
       y: y - DEFAULT_HEIGHT/2
       yUpper: y - DEFAULT_HEIGHT/2 # not sure if these are needed
       yLower: y + DEFAULT_HEIGHT/2
-
-  getInitialState: ->
-    foo: 'bar'

   getDeleteButtonPosition: ->
     x: 100
@@ -101,7 +97,9 @@
             </g>
         }

-        { if @props.selected then @renderMarkButton() }
+        { # REQUIRES MARK-BUTTON-MIXIN
+          if @props.selected then @renderMarkButton()
+        }

       </g>
     </g>


Do not allow empty queries

Do not allow empty queries

--- app/assets/javascripts/rglossa/controllers/searches_controller.coffee
+++ app/assets/javascripts/rglossa/controllers/searches_controller.coffee
@@ -8,6 +8,10 @@
   corpusBinding: 'controllers.corpus.model'

   createSearch: (searchModel, queries) ->
+    # Empty queries are not allowed
+    return unless queries.some (query) ->
+      query.query isnt ''
+
     metadataValueIds = {}
     $('[data-metadata-selections] input[type="hidden"]').each (index, input) ->
       $input = $(input)


fix: also allow python rc

fix: also allow python rc

This fixes an error with the latest python3 on ubuntu 19.10 being an RC

--- lib/wakatime.coffee
+++ lib/wakatime.coffee
@@ -326,7 +326,7 @@

   anaconda = /continuum|anaconda/gi
   isAnaconda = not not anaconda.test(versionString)
-  re = /python\s+(\d+)\.(\d+)\.(\d+)\s/gi
+  /python\s+(\d+)\.(\d+)\.(\d+)([a-z0-9]+)?\s/gi
   ver = re.exec(versionString)
   if not ver?
     return isAnaconda


raising an error on unrecognized options

--- optparse.coffee
+++ optparse.coffee
@@ -14,10 +14,13 @@
   args: args.slice 0
   while arg: args.shift()
     is_option: !!(arg.match(LONG_FLAG) or arg.match(SHORT_FLAG))
+    matched_rule: no
     for rule in @rules
       if rule.letter is arg or rule.flag is arg
         options[rule.name]: if rule.has_argument then args.shift() else true
+        matched_rule: yes
         break
+    throw new Error "unrecognized option: " + arg if is_option and not matched_rule
     options.arguments.push arg unless is_option
   options


removed include to old mist translator

removed include to old mist translator

--- src/mist.coffee
+++ src/mist.coffee
@@ -14,7 +14,6 @@
 path = require 'path'
 spawn = (require 'child_process').spawn
 MistParser = require './mist-parser'
-MistTranslator = require './mist-translator'

 ninjaProc = process.env.NINJA || 'ninja'


Updated molo.main shorthand

Updated molo.main shorthand

--- src/molo.coffee
+++ src/molo.coffee
@@ -183,6 +183,8 @@
   mainHasBeenCalled = false

   root.molo.main = (name, callback) ->
+    [name, callback] = ['main', name] if typeof name is 'function'
+
     if mainHasBeenCalled then throw new TypeError 'molo.main can only be called once.'

     root.molo.require name, callback


exposing hal browser more robustly

exposing hal browser more robustly

--- src/Server.coffee
+++ src/Server.coffee
@@ -47,7 +47,7 @@
     @expressServer.use express.bodyParser()

     # expose the HAL browser
-    @expressServer.use '/hal', express.static 'thirdparty/hal-browser'
+    @expressServer.use '/hal', express.static __dirname + '/../thirdparty/hal-browser'

     @expressServer.get '/', (request, response) =>
       response.send 200, 'hello'


modify meta key

modify meta key

--- lib/atomizr.coffee
+++ lib/atomizr.coffee
@@ -92,7 +92,7 @@

     # Conversion
     sublime =
-      meta: @meta
+      "#": @meta
       scope: null
       completions: []


This is not a class...

This is not a class...

--- src/affqis.coffee
+++ src/affqis.coffee
@@ -12,15 +12,15 @@
 #         fail to establish a connection, or a session if we succeed.
 connect = (realm, host, port, cb) ->
   affqisUrl = "ws://#{host}:#{port}/affqis"
-  @connection = new autobahn.Connection
+  connection = new autobahn.Connection
     url: affqisUrl
     realm: realm
     max_retries: 0

-  @connection.onopen = (session) ->
     cb(null, session)
+  connection.onopen = (session) ->

-  @connection.onclose = (reason) =>
+  connection.onclose = (reason) =>
     switch reason
       when "unreachable"
         console.error "Cannot establish connection to #{affqisUrl}"


Issue #46: Added matching on channel id, filter by failureRate. Fixed the date from and status matching.

--- src/alerts.coffee
+++ src/alerts.coffee
@@ -7,20 +7,25 @@

 getAllChannels = (callback) -> Channel.find({}).exec callback

-findTransactionsMatchingStatus = (channelId, status, dateFrom, callback) ->
-	pat = /(\d)xx/.exec status
-	statusMatch = if pat then new RegExp("#{pat[0]}\d\d") else status
-	console.log "channeId: #{channelId}"
-	console.log "statusMatch: #{statusMatch}"
-	console.log "dateFrom: #{dateFrom}"
+findTransactionsMatchingStatus = (channelID, status, dateFrom, failureRate, callback) ->
+	pat = /\dxx/.exec status
+	if pat
+		statusMatch = "$gte": status[0]*100, "$lt": status[0]*100+100
+	else
+		statusMatch = status

 	Transaction.find({
-	#	request: timestamp: $gte: dateFrom
-	#	"$or": [
-	#		{ response: status: statusMatch }
-	#		{ routes: "$elemMatch": response: status: statusMatch }
-	#	]
-	}, '_id').exec callback
+		"request.timestamp": $gte: dateFrom
+		channelID: channelID
+		"$or": [
+			{ "response.status": statusMatch }
+			{ routes: "$elemMatch": "response.status": statusMatch }
+		]
+	}, '_id').exec (err, results) ->
+		if not err and results? and failureRate? and results.length < failureRate
+			callback err, []
+		else
+			callback err, results

 alertingTask = (job, done) ->
 	logger.info "Running transaction alerts task"
@@ -31,8 +36,7 @@
 	getAllChannels (err, results) ->
 		for channel in results
 			for alert in channel.alerts
-				status = ""
-				findTransactionsMatchingStatus channel._id, status, lastAlertDate, (err, trx) ->
+				findTransactionsMatchingStatus channelID, alert.status, lastAlertDate, alert.failureRate, (err, trx) ->
 					console.log "do stuff"

 	job.attrs.data.lastAlertDate = new Date()


vm.create(): handle limits on template disks.

--- src/api/vm.coffee
+++ src/api/vm.coffee
@@ -92,13 +92,26 @@
     }
   )

-  xapiExistingVdis = existingDisks and map(existingDisks, (vdi) =>
+  xapi = @getXapi(template)
+
+  diskSizesByDevice = {}
+
+  forEach(xapi.getObject(template._xapiId).$VBDs, (vbd) =>
+    if (
+      vbd.type is 'Disk' and
+      (vdi = vbd.$VDI)
+    )
+      diskSizesByDevice[vbd.device] = +vdi.virtual_size
+
+    return
+  )
+
+  xapiExistingVdis = existingDisks and map(existingDisks, (vdi, device) =>
     sr = @getObject(vdi.$SR)

-    # FIXME: handle existing VDIs when not resized.
-    if vdi.size
+    if vdi.size?
       size = parseSize(vdi.size)
-      limits.disk += size
+      diskSizesByDevice[device] = size

     objectIds.push(sr.id)

@@ -109,6 +122,8 @@
     }
   )

+  forEach(diskSizesByDevice, (size) => limits.disk += size)
+
   xapiVifs = VIFs and map(VIFs, (vif) =>
     network = @getObject(vif.network)

@@ -126,7 +141,6 @@
   else unless user.permission is 'admin'
     throw new Unauthorized()

-  xapi = @getXapi(template)
   xapiVm = yield xapi.createVm(template._xapiId, {
     installRepository: installation && installation.repository,
     nameDescription: name_description,
@@ -141,7 +155,7 @@

   if resourceSet
     @addAcl(user.id, vm.id, 'admin').catch(noop)
-    xapi.xo.setData(xapiVm.$id, 'resourceSet', resourceSet).catch(noop)
+    yield xapi.xo.setData(xapiVm.$id, 'resourceSet', resourceSet).catch(noop)

   return vm.id


don't operate on proxy

don't operate on proxy

--- client/skr/models/mixins/SkuLine.coffee
+++ client/skr/models/mixins/SkuLine.coffee
@@ -40,7 +40,7 @@
         this.uom.set( uom.serialize() ) if uom

     onSkuChange: ->
-        return unless @sku
+        return if @sku.isProxy
         sl = @sku.sku_locs.findWhere(sku_id: @sku.id)
         if sl
             @set(sku_loc: sl)


Admin SPA: Fix broken tests, change 'value:' to 'path:'.

--- test-client/unit/spa/admin/controllersSpec.coffee
+++ test-client/unit/spa/admin/controllersSpec.coffee
@@ -28,87 +28,87 @@
     #expect(true).toEqual(false)

   it 'can sort a single page and a single folder', ->
-    page = -> [{ value: '/a',  pageId: 'ab12cd' }]
-    folder = -> [{ value: '/a/' }]
+    page = -> [{ path: '/a',  pageId: 'ab12cd' }]
+    folder = -> [{ path: '/a/' }]
     expect(test.sortItemsInPlace page()).toEqual page()
     expect(test.sortItemsInPlace folder()).toEqual folder()
     #expect(true).toEqual(false)

   it 'can sort pages in the same folder', ->
     key = -> [
-        { value: '/',  pageId: 'ab12cd' },
-        { value: '/a', pageId: 'ab12cd' },
-        { value: '/z', pageId: 'ab12cd' }]
+        { path: '/',  pageId: 'ab12cd' },
+        { path: '/a', pageId: 'ab12cd' },
+        { path: '/z', pageId: 'ab12cd' }]
     expect(test.sortItemsInPlace key().reverse()).toEqual key()
     expect(test.sortItemsInPlace key()).toEqual key()
     #expect(true).toEqual(false)

   it 'can sort a page and a folder', ->
     key = -> [
-        { value: '/a',  pageId: 'ab12cd' },
-        { value: '/a/', pageId: 'ab12cd' }]
+        { path: '/a',  pageId: 'ab12cd' },
+        { path: '/a/', pageId: 'ab12cd' }]
     expect(test.sortItemsInPlace key().reverse()).toEqual key()
     expect(test.sortItemsInPlace key()).toEqual key()
     #expect(true).toEqual(false)

   it 'can sort folders', ->
     key = -> [
-        { value: '/a/' },
-        { value: '/b/' }]
+        { path: '/a/' },
+        { path: '/b/' }]
     expect(test.sortItemsInPlace key().reverse()).toEqual key()
     expect(test.sortItemsInPlace key()).toEqual key()
     #expect(true).toEqual(false)

   it 'can sort deep folders', ->
     key = -> [
-        { value: '/a/a/a/' },
-        { value: '/a/b/' },
-        { value: '/c/' }]
+        { path: '/a/a/a/' },
+        { path: '/a/b/' },
+        { path: '/c/' }]
     expect(test.sortItemsInPlace key().reverse()).toEqual key()
     expect(test.sortItemsInPlace key()).toEqual key()
     #expect(true).toEqual(false)

   it 'can sort a folder and its index page', ->
     key = -> [
-        { value: '/a/' },
-        { value: '/a/', pageId: 'ab12cd' }]
+        { path: '/a/' },
+        { path: '/a/', pageId: 'ab12cd' }]
     expect(test.sortItemsInPlace key().reverse()).toEqual key()
     expect(test.sortItemsInPlace key()).toEqual key()
     #expect(true).toEqual(false)

   it 'can sort a few pages and folders, pages first', ->
     key = -> [
-        { value: '/' },
-        { value: '/', pageId: 'ab12cd' },
-        { value: '/a', pageId: 'ab12cd' },
-        { value: '/z', pageId: 'ab12cd' },
-        { value: '/a/' },
-        { value: '/a/', pageId: 'ab12cd' }]
+        { path: '/' },
+        { path: '/', pageId: 'ab12cd' },
+        { path: '/a', pageId: 'ab12cd' },
+        { path: '/z', pageId: 'ab12cd' },
+        { path: '/a/' },
+        { path: '/a/', pageId: 'ab12cd' }]
     expect(test.sortItemsInPlace key().reverse()).toEqual key()
     expect(test.sortItemsInPlace _(key()).shuffle()).toEqual key()
     #expect(true).toEqual(false)

   it 'can sort many pages and folders', ->
     key = -> [
-        { value: '/' },
-        { value: '/', pageId: 'ab12cd' },
-        { value: '/a', pageId: 'ab12cd'  },
-        { value: '/aa', pageId: 'ab12cd'  },
-        { value: '/z', pageId: 'ab12cd'  },
-        { value: '/zz', pageId: 'ab12cd'  },
-        { value: '/aa/' },
-        { value: '/aa/', pageId: 'ab12cd'  },
-        { value: '/aa/a', pageId: 'ab12cd'  },
-        { value: '/aa/z', pageId: 'ab12cd'  },
-        { value: '/aa/aa/' },
-        { value: '/aa/aa/a', pageId: 'ab12cd'  },
-        { value: '/aa/aa/z', pageId: 'ab12cd'  },
-        { value: '/aa/aa/a/' },
-        { value: '/b/' },
-        { value: '/c/cc/ccc/' },
-        { value: '/zz/a', pageId: 'ab12cd'  },
-        { value: '/zz/z', pageId: 'ab12cd'  },
-        { value: '/zz/b/' }]
+        { path: '/' },
+        { path: '/', pageId: 'ab12cd' },
+        { path: '/a', pageId: 'ab12cd'  },
+        { path: '/aa', pageId: 'ab12cd'  },
+        { path: '/z', pageId: 'ab12cd'  },
+        { path: '/zz', pageId: 'ab12cd'  },
+        { path: '/aa/' },
+        { path: '/aa/', pageId: 'ab12cd'  },
+        { path: '/aa/a', pageId: 'ab12cd'  },
+        { path: '/aa/z', pageId: 'ab12cd'  },
+        { path: '/aa/aa/' },
+        { path: '/aa/aa/a', pageId: 'ab12cd'  },
+        { path: '/aa/aa/z', pageId: 'ab12cd'  },
+        { path: '/aa/aa/a/' },
+        { path: '/b/' },
+        { path: '/c/cc/ccc/' },
+        { path: '/zz/a', pageId: 'ab12cd'  },
+        { path: '/zz/z', pageId: 'ab12cd'  },
+        { path: '/zz/b/' }]
     expect(test.sortItemsInPlace key().reverse()).toEqual key()
     expect(test.sortItemsInPlace _(key()).shuffle()).toEqual key()
     expect(test.sortItemsInPlace key()).toEqual key()
@@ -126,62 +126,62 @@
       expect(applyUpdateHitCounts key).toEqual key

     it 'can show a single homepage', ->
-      key = [{ hideCount: 0, depth: 0, value: '/', displayPath: '', open: false, pageId: 'p0' }]
+      key = [{ hideCount: 0, depth: 0, path: '/', displayPath: '', open: false, pageId: 'p0' }]
       expect(applyUpdateHitCounts key).toEqual key

     it 'can hide a page in a closed folder', ->
       key = [
-        { hideCount: 0, depth: 0, value: '/aa/', displayPath: 'aa/', open: false },
-        { hideCount: 1, depth: 1, value: '/aa/bb', displayPath: 'bb', open: false, pageId: 'pbb' },
+        { hideCount: 0, depth: 0, path: '/aa/', displayPath: 'aa/', open: false },
+        { hideCount: 1, depth: 1, path: '/aa/bb', displayPath: 'bb', open: false, pageId: 'pbb' },
         ]
       expect(applyUpdateHitCounts key).toEqual key

     it 'can show a page in an open folder', ->
       key = [
-        { hideCount: 0, depth: 0, value: '/aa/', displayPath: 'aa/', open: true },
-        { hideCount: 0, depth: 1, value: '/aa/bb', displayPath: 'bb', open: false, pageId: 'pbb' },
+        { hideCount: 0, depth: 0, path: '/aa/', displayPath: 'aa/', open: true },
+        { hideCount: 0, depth: 1, path: '/aa/bb', displayPath: 'bb', open: false, pageId: 'pbb' },
         ]
       expect(applyUpdateHitCounts key).toEqual key

     it 'can hide one deeper folder', ->
       key = [
-        { hideCount: 0, depth: 0, value: '/aa/', displayPath: 'aa/', open: false },
-        { hideCount: 1, depth: 1, value: '/aa/bb/', displayPath: 'bb/', open: false },
+        { hideCount: 0, depth: 0, path: '/aa/', displayPath: 'aa/', open: false },
+        { hideCount: 1, depth: 1, path: '/aa/bb/', displayPath: 'bb/', open: false },
         ]
       expect(applyUpdateHitCounts key).toEqual key

     it 'can hide many deeper folders', ->
       # /aa/ shouldn't be pop()ed to many times.
       key = [
-        { hideCount: 0, depth: 0, value: '/aa/', displayPath: 'aa/', open: false },
-        { hideCount: 1, depth: 1, value: '/aa/bb/', displayPath: 'bb/', open: false },
-        { hideCount: 1, depth: 1, value: '/aa/cc/', displayPath: 'cc/', open: false },
-        { hideCount: 1, depth: 1, value: '/aa/dd/', displayPath: 'dd/', open: false },
+        { hideCount: 0, depth: 0, path: '/aa/', displayPath: 'aa/', open: false },
+        { hideCount: 1, depth: 1, path: '/aa/bb/', displayPath: 'bb/', open: false },
+        { hideCount: 1, depth: 1, path: '/aa/cc/', displayPath: 'cc/', open: false },
+        { hideCount: 1, depth: 1, path: '/aa/dd/', displayPath: 'dd/', open: false },
         ]
       expect(applyUpdateHitCounts key).toEqual key

     it 'can show a deeper folder that is no child of any prev closed folder', ->
       key = [
-        { hideCount: 0, depth: 0, value: '/aa/', displayPath: 'aa/', open: false },
-        { hideCount: 0, depth: 0, value: '/bb/cc/', displayPath: 'bb/cc/', open: false }, # not in /aa/
+        { hideCount: 0, depth: 0, path: '/aa/', displayPath: 'aa/', open: false },
+        { hideCount: 0, depth: 0, path: '/bb/cc/', displayPath: 'bb/cc/', open: false }, # not in /aa/
         ]
       expect(applyUpdateHitCounts key).toEqual key

     it 'can handle a folder that is a child of the last but one prev folder', ->
       key = [
-        { hideCount: 0, depth: 0, value: '/a/', displayPath: 'a/', open: false },
-        { hideCount: 1, depth: 1, value: '/a/b/', displayPath: 'b/', open: false },
-        { hideCount: 2, depth: 2, value: '/a/b/c', displayPath: 'c', open: false },  # in /a/b/
-        { hideCount: 1, depth: 1, value: '/a/x/y/', displayPath: 'x/y/', open: false }, # in /a/
+        { hideCount: 0, depth: 0, path: '/a/', displayPath: 'a/', open: false },
+        { hideCount: 1, depth: 1, path: '/a/b/', displayPath: 'b/', open: false },
+        { hideCount: 2, depth: 2, path: '/a/b/c', displayPath: 'c', open: false },  # in /a/b/
+        { hideCount: 1, depth: 1, path: '/a/x/y/', displayPath: 'x/y/', open: false }, # in /a/
         ]
       expect(applyUpdateHitCounts key).toEqual key

     it 'same as above, but folders open', ->
       key = [
-        { hideCount: 0, depth: 0, value: '/a/', displayPath: 'a/', open: true },
-        { hideCount: 0, depth: 1, value: '/a/b/', displayPath: 'b/', open: true },
-        { hideCount: 0, depth: 2, value: '/a/b/c', displayPath: 'c', open: true },  # in /a/b/
-        { hideCount: 0, depth: 1, value: '/a/x/y/', displayPath: 'x/y/', open: true}, # in /a/
+        { hideCount: 0, depth: 0, path: '/a/', displayPath: 'a/', open: true },
+        { hideCount: 0, depth: 1, path: '/a/b/', displayPath: 'b/', open: true },
+        { hideCount: 0, depth: 2, path: '/a/b/c', displayPath: 'c', open: true },  # in /a/b/
+        { hideCount: 0, depth: 1, path: '/a/x/y/', displayPath: 'x/y/', open: true}, # in /a/
         ]
       expect(applyUpdateHitCounts key).toEqual key


Update integration tests

Update integration tests

--- test/integration/datatypes-in-results-test.coffee
+++ test/integration/datatypes-in-results-test.coffee
@@ -7,6 +7,7 @@

 config = JSON.parse(fs.readFileSync(process.env.HOME + '/.tedious/test-connection.json', 'utf8')).config
 config.options.textsize = 8 * 1024
+tdsVersion = config.options.tdsVersion ? '7_4'

 if (debug)
   config.options.debug =
@@ -81,6 +82,31 @@
 exports.smallDatetimeNull = (test) ->
   execSql(test, "select cast(null as smalldatetime)", null)

+if tdsVersion >= '7_3_A'
+  exports.datetime2 = (test) ->
+    execSql(test, "select cast('2011-12-4 10:04:23' as datetime2)", new Date('December 4, 2011 10:04:23 +00'))
+
+  exports.datetime2Null = (test) ->
+    execSql(test, "select cast(null as datetime2)", null)
+
+  exports.time = (test) ->
+    execSql(test, "select cast('10:04:23' as time)", new Date(Date.UTC(1970, 0, 1, 10, 4, 23)))
+
+  exports.timeNull = (test) ->
+    execSql(test, "select cast(null as time)", null)
+
+  exports.date = (test) ->
+    execSql(test, "select cast('2014-03-08' as date)", new Date(Date.UTC(2014, 2, 8)))
+
+  exports.dateNull = (test) ->
+    execSql(test, "select cast(null as date)", null)
+
+  exports.dateTimeOffset = (test) ->
+    execSql(test, "select cast('2014-02-14 22:59:59.9999999 +05:00' as datetimeoffset)", new Date(Date.UTC(2014, 1, 14, 17, 59, 59, 999)))
+
+exports.dateTimeOffsetNull = (test) ->
+  execSql(test, "select cast(null as datetimeoffset)", null)
+
 exports.numericSmallValue = (test) ->
   execSql(test, "select cast(9.3 as numeric(3,2))", 9.3)

@@ -171,19 +197,19 @@
   execSql(test, "select cast(null as nvarchar(max))", null)

 exports.varbinary = (test) ->
-  execSql(test, "select cast(0x1234 as varbinary(4))", [0x12, 0x34])
+  execSql(test, "select cast(0x1234 as varbinary(4))", new Buffer [0x12, 0x34])

 exports.varbinaryNull = (test) ->
   execSql(test, "select cast(null as varbinary(10))", null)

 exports.binary = (test) ->
-  execSql(test, "select cast(0x1234 as binary(4))", [0x12, 0x34, 0x00, 0x00])
+  execSql(test, "select cast(0x1234 as binary(4))", new Buffer [0x12, 0x34, 0x00, 0x00])

 exports.binaryNull = (test) ->
   execSql(test, "select cast(null as binary(10))", null)

 exports.varbinaryMax = (test) ->
-  execSql(test, "select cast(0x1234 as varbinary(max))", [0x12, 0x34])
+  execSql(test, "select cast(0x1234 as varbinary(max))", new Buffer [0x12, 0x34])

 exports.varbinaryMaxNull = (test) ->
   execSql(test, "select cast(null as varbinary(max))", null)
@@ -219,7 +245,7 @@
   execSql(test, "select cast(null as ntext) as text", null)

 exports.image = (test) ->
-  execSql(test, "select cast(0x1234 as image)", [0x12, 0x34])
+  execSql(test, "select cast(0x1234 as image)", new Buffer [0x12, 0x34])

 exports.imageNull = (test) ->
   execSql(test, "select cast(null as image)", null)
@@ -239,6 +265,7 @@

 exports.xmlWithSchema = (test) ->
   # Cannot use temp tables, as schema collections as not available to them.
+  # Schema must be created manually in database in order to make this test work properly (sql 2012)

   xml = '<root/>'

@@ -265,6 +292,12 @@

   execSql(test, "#{dropTable} #{dropSchema} #{createSchema} #{createTable} #{insert} #{select}", xml)

+exports.udt = (test) ->
+  execSql(test, "select geography::STGeomFromText('LINESTRING(-122.360 47.656, -122.343 47.656 )', 4326) as geo", new Buffer [230,16,0,0,1,20,135,22,217,206,247,211,71,64,215,163,112,61,10,151,94,192,135,22,217,206,247,211,71,64,203,161,69,182,243,149,94,192])
+
+exports.udtNull = (test) ->
+  execSql(test, "select cast(null as geography)", null)
+
 execSql = (test, sql, expectedValue) ->
   test.expect(2)

@@ -277,7 +310,7 @@
   request.on('row', (columns) ->
     if expectedValue instanceof Date
       test.strictEqual(columns[0].value.getTime(), expectedValue.getTime())
-    else if expectedValue instanceof Array
+    else if expectedValue instanceof Array or expectedValue instanceof Buffer
       test.deepEqual(columns[0].value, expectedValue)
     else
       test.strictEqual(columns[0].value, expectedValue)


Expand list of reserved keywords even more

--- lib/helpers.coffee
+++ lib/helpers.coffee
@@ -338,28 +338,32 @@
 # reservedWords
 ###

-exports.reservedWords = [
+exports.reserved =
   # taken from COFFEE_KEYWORDS (lexer.coffee)
   # (also, don't check for 'undefined' because it's already explicitly
   # accounted for elsewhere)
-  'then'
-  'unless'
-  'until'
-  'loop'
-  'of'
-  'by'
-  'when'
+  keywords: [
+    'then', 'unless', 'until', 'loop', 'of', 'by', 'when' ]
+
+  # taken from RESERVED (lexer.coffee)
+  reserved: [
+    'case', 'default', 'function', 'var', 'void', 'with', 'const', 'let', 'enum'
+    'export', 'import', 'native', '__hasProp', '__extends', '__slice', '__bind'
+    '__indexOf', 'implements', 'interface', 'package', 'private', 'protected'
+    'public', 'static', 'yield' ]
+
   # taken from COFFEE_ALIAS_MAP (lexer.coffee)
-  'and'
-  'or'
-  'is'
-  'isnt'
-  'not'
-  'yes'
-  'no'
-  'on'
-  'off'
-]
+  aliases: [
+    'and', 'or', 'is', 'isnt', 'not', 'yes', 'no', 'on', 'off' ]
+
+exports.reservedWords =
+  exports.reserved.keywords.concat \
+  exports.reserved.reserved.concat \
+  exports.reserved.aliases
+
+###*
+# Next until
+###

 exports.nextUntil = (body, node, fn) ->
   idx = body.indexOf(node)
@@ -367,6 +371,10 @@
     next = body[i]
     return next if next?.type and fn(next)

+###*
+# Next until a non-comment node
+###
+
 exports.nextNonComment = (body, node) ->
   exports.nextUntil(body, node, (n) ->
     n.type isnt 'BlockComment' and


support underscores and dashes in root pathname parser

--- earl.coffee
+++ earl.coffee
@@ -49,7 +49,7 @@

     Earl.root = '/'
     if window.location.pathname.match('.html')
-      Earl.root += location.pathname.match(/\/(\w+\.html)/)[1] + '/'
+      Earl.root += location.pathname.match(/\/([\w-_]+\.html)/)[1] + '/'

     # Earl, don't forget to update us if the browser back or forward button pressed
     window.addEventListener 'popstate', (ev) ->
@@ -67,7 +67,7 @@
     loc = fetch('location')
     loc.query_params = query_params or {}

-    # if the url has query pachrameters, parse and merge them into params
+    # if the url has query parameters, parse and merge them into params
     if url.indexOf('?') > -1
       [url, query_params] = url.split('?')

@@ -91,7 +91,6 @@

     loc.url = url or '/'
     loc.hash = hash
-
     save loc

@@ -167,13 +166,13 @@

     dom.HISTORY_IGNORANT_LINK props, props.children

-
 # Earl's Reactive nerves keep him vigilant in making sure that changes in location
 # state are reflected in the browser history. Earl also updates the window title
 # for you, free of charge, if you set fetch('document').title.

 react_to_location = ->
   monitor = bus.reactive ->
+
     loc = fetch 'location'
     doc = fetch 'document'


Redelegating events when rendering actions

--- src/ribs.coffee
+++ src/ribs.coffee
@@ -396,6 +396,7 @@
         renderActions: ->
             for view in @subviews("action")
                 view.render()
+                view.delegateEvents()

         initializeList: ->
             $list = $ "<ul/>", class: "list"