more stuff works

more stuff works

--- spec/BaconSpec.coffee
+++ spec/BaconSpec.coffee
@@ -653,33 +653,33 @@
     expect(Bacon.constant(1).flatMap(->).toString()).to.equal("Bacon.constant(1).flatMap(function)")

 describe "EventStream.flatMapLatest", ->
-  describe.skip "spawns new streams but collects values from the latest spawned stream only", ->
+  describe "spawns new streams but collects values from the latest spawned stream only", ->
     expectStreamEvents(
       -> series(3, [1, 2]).flatMapLatest (value) ->
         Bacon.sequentially(t(2), [value, error(), value])
       [1, 2, error(), 2], unstable)
-  describe.skip "Accepts a constant EventStream/Property as an alternative to a function", ->
+  describe "Accepts a constant EventStream/Property as an alternative to a function", ->
     expectStreamEvents(
       -> Bacon.once("asdf").flatMapLatest(Bacon.constant("bacon"))
       ["bacon"], unstable)
   it "toString", ->
     expect(Bacon.never().flatMapLatest(->).toString()).to.equal("Bacon.never().flatMapLatest(function)")

-describe.skip "Property.flatMapLatest", ->
-  describe.skip "spawns new streams but collects values from the latest spawned stream only", ->
+describe "Property.flatMapLatest", ->
+  describe "spawns new streams but collects values from the latest spawned stream only", ->
     expectStreamEvents(
       -> series(3, [1, 2]).toProperty(0).flatMapLatest (value) ->
         Bacon.sequentially(t(2), [value, value])
       [0, 1, 2, 2], unstable)
-  describe.skip "Accepts a constant EventStream/Property as an alternative to a function", ->
+  describe "Accepts a constant EventStream/Property as an alternative to a function", ->
     expectStreamEvents(
       -> Bacon.constant("asdf").flatMapLatest(Bacon.constant("bacon"))
       ["bacon"], unstable)
   it "toString", ->
     expect(Bacon.constant(1).flatMapLatest(->).toString()).to.equal("Bacon.constant(1).flatMapLatest(function)")

-describe.skip "EventStream.flatMapFirst", ->
-  describe.skip "spawns new streams and ignores source events until current spawned stream has ended", ->
+describe "EventStream.flatMapFirst", ->
+  describe "spawns new streams and ignores source events until current spawned stream has ended", ->
     expectStreamEvents(
       -> series(2, [2, 4, 6, 8]).flatMapFirst (value) ->
         series(1, ["a" + value, "b" + value, "c" + value])
@@ -687,22 +687,22 @@
   it "toString", ->
     expect(Bacon.never().flatMapFirst(->).toString()).to.equal("Bacon.never().flatMapFirst(function)")

-describe.skip "EventStream.merge", ->
-  describe.skip "merges two streams and ends when both are exhausted", ->
+describe "EventStream.merge", ->
+  describe "merges two streams and ends when both are exhausted", ->
     expectStreamEvents(
       ->
         left = series(1, [1, error(), 2, 3])
         right = series(1, [4, 5, 6]).delay(t(4))
         left.merge(right)
       [1, error(), 2, 3, 4, 5, 6], unstable)
-  describe.skip "respects subscriber return value", ->
+  describe "respects subscriber return value", ->
     expectStreamEvents(
       ->
         left = repeat(2, [1, 3]).take(3)
         right = repeat(3, [2]).take(3)
         left.merge(right).takeWhile(lessThan(2))
       [1])
-  describe.skip "does not duplicate same error from two streams", ->
+  describe "does not duplicate same error from two streams", ->
     expectStreamEvents(
       ->
         src = series(1, [1, error(), 2, error(), 3])
@@ -710,22 +710,22 @@
         right = src.map((x) -> x * 2)
         left.merge(right)
       [1, 2, error(), 2, 4, error(), 3, 6])
-  describe.skip "works with synchronous sources", ->
+  describe "works with synchronous sources", ->
     expectStreamEvents(
       -> Bacon.fromArray([1,2]).merge(Bacon.fromArray([3,4]))
       [1,2,3,4])
   it "toString", ->
     expect(Bacon.once(1).merge(Bacon.once(2)).toString()).to.equal("Bacon.once(1).merge(Bacon.once(2))")

-describe.skip "EventStream.delay", ->
-  describe.skip "delays all events (except errors) by given delay in milliseconds", ->
+describe "EventStream.delay", ->
+  describe "delays all events (except errors) by given delay in milliseconds", ->
     expectStreamEvents(
       ->
         left = series(2, [1, 2, 3])
         right = series(1, [error(), 4, 5, 6]).delay(t(6))
         left.merge(right)
       [error(), 1, 2, 3, 4, 5, 6], unstable)
-  describe.skip "works with synchronous streams", ->
+  describe "works with synchronous streams", ->
     expectStreamEvents(
       ->
         left = Bacon.fromArray([1, 2, 3])
@@ -735,20 +735,20 @@
   it "toString", ->
     expect(Bacon.never().delay(1).toString()).to.equal("Bacon.never().delay(1)")

-describe.skip "EventStream.debounce", ->
-  describe.skip "throttles input by given delay, passing-through errors", ->
+describe "EventStream.debounce", ->
+  describe "throttles input by given delay, passing-through errors", ->
     expectStreamEvents(
       -> series(2, [1, error(), 2]).debounce(t(7))
       [error(), 2])
-  describe.skip "waits for a quiet period before outputing anything", ->
+  describe "waits for a quiet period before outputing anything", ->
     th.expectStreamTimings(
       -> series(2, [1, 2, 3, 4]).debounce(t(3))
       [[11, 4]])
-  describe.skip "works with synchronous source", ->
+  describe "works with synchronous source", ->
     expectStreamEvents(
       -> Bacon.fromArray([1, 2, 3, 4]).debounce(t(3))
       [4])
-  describe.skip "works in combination with scan", ->
+  describe "works in combination with scan", ->
     count = 0
     expectPropertyEvents(
       -> series(2, [1,2,3]).debounce(1).scan(0, (x,y) -> count++; x + y)
@@ -760,137 +760,137 @@
     expect(Bacon.never().debounce(1).toString()).to.equal("Bacon.never().debounce(1)")

-describe.skip "EventStream.debounceImmediate(delay)", ->
-  describe.skip "outputs first event immediately, then ignores events for given amount of milliseconds", ->
+describe "EventStream.debounceImmediate(delay)", ->
+  describe "outputs first event immediately, then ignores events for given amount of milliseconds", ->
     th.expectStreamTimings(
       -> series(2, [1, 2, 3, 4]).debounceImmediate(t(3))
       [[2, 1], [6, 3]], unstable)
-  describe.skip "works with synchronous source", ->
+  describe "works with synchronous source", ->
     expectStreamEvents(
       -> Bacon.fromArray([1, 2, 3, 4]).debounceImmediate(t(3))
       [1])
   it "toString", ->
     expect(Bacon.never().debounceImmediate(1).toString()).to.equal("Bacon.never().debounceImmediate(1)")

-describe.skip "EventStream.throttle(delay)", ->
-  describe.skip "outputs at steady intervals, without waiting for quiet period", ->
+describe "EventStream.throttle(delay)", ->
+  describe "outputs at steady intervals, without waiting for quiet period", ->
     th.expectStreamTimings(
       -> series(2, [1, 2, 3]).throttle(t(3))
       [[5, 2], [8, 3]])
-  describe.skip "works with synchronous source", ->
+  describe "works with synchronous source", ->
     expectStreamEvents(
       -> Bacon.fromArray([1, 2, 3]).throttle(t(3))
       [3])
   it "toString", ->
     expect(Bacon.never().throttle(1).toString()).to.equal("Bacon.never().throttle(1)")

-describe.skip "EventStream.bufferWithTime", ->
-  describe.skip "returns events in bursts, passing through errors", ->
+describe "EventStream.bufferWithTime", ->
+  describe "returns events in bursts, passing through errors", ->
     expectStreamEvents(
       -> series(2, [error(), 1, 2, 3, 4, 5, 6, 7]).bufferWithTime(t(7))
       [error(), [1, 2, 3, 4], [5, 6, 7]])
-  describe.skip "keeps constant output rate even when input is sporadical", ->
+  describe "keeps constant output rate even when input is sporadical", ->
     th.expectStreamTimings(
       -> th.atGivenTimes([[0, "a"], [3, "b"], [5, "c"]]).bufferWithTime(t(2))
       [[2, ["a"]], [4, ["b"]], [6, ["c"]]]
       unstable
     )
-  describe.skip "works with empty stream", ->
+  describe "works with empty stream", ->
     expectStreamEvents(
       -> Bacon.never().bufferWithTime(t(1))
       [])
-  describe.skip "allows custom defer-function", ->
+  describe "allows custom defer-function", ->
     fast = (f) -> sc.setTimeout(f, 0)
     th.expectStreamTimings(
       -> th.atGivenTimes([[0, "a"], [2, "b"]]).bufferWithTime(fast)
       [[0, ["a"]], [2, ["b"]]])
-  describe.skip "works with synchronous defer-function", ->
+  describe "works with synchronous defer-function", ->
     sync = (f) -> f()
     th.expectStreamTimings(
       -> th.atGivenTimes([[0, "a"], [2, "b"]]).bufferWithTime(sync)
       [[0, ["a"]], [2, ["b"]]])
-  describe.skip "works with synchronous source", ->
+  describe "works with synchronous source", ->
     expectStreamEvents(
       -> series(2, [1,2,3]).bufferWithTime(t(7))
       [[1,2,3]])
   it "toString", ->
     expect(Bacon.never().bufferWithTime(1).toString()).to.equal("Bacon.never().bufferWithTime(1)")

-describe.skip "EventStream.bufferWithCount", ->
-  describe.skip "returns events in chunks of fixed size, passing through errors", ->
+describe "EventStream.bufferWithCount", ->
+  describe "returns events in chunks of fixed size, passing through errors", ->
     expectStreamEvents(
       -> series(1, [1, 2, 3, error(), 4, 5]).bufferWithCount(2)
       [[1, 2], error(), [3, 4], [5]])
-  describe.skip "works with synchronous source", ->
+  describe "works with synchronous source", ->
     expectStreamEvents(
       -> Bacon.fromArray([1,2,3,4,5]).bufferWithCount(2)
       [[1, 2], [3, 4], [5]])
   it "toString", ->
     expect(Bacon.never().bufferWithCount(1).toString()).to.equal("Bacon.never().bufferWithCount(1)")

-describe.skip "EventStream.bufferWithTimeOrCount", ->
-  describe.skip "flushes on count", ->
+describe "EventStream.bufferWithTimeOrCount", ->
+  describe "flushes on count", ->
     expectStreamEvents(
       -> series(1, [1, 2, 3, error(), 4, 5]).bufferWithTimeOrCount(t(10), 2)
       [[1, 2], error(), [3, 4], [5]])
-  describe.skip "flushes on timeout", ->
+  describe "flushes on timeout", ->
     expectStreamEvents(
       -> series(2, [error(), 1, 2, 3, 4, 5, 6, 7]).bufferWithTimeOrCount(t(7), 10)
       [error(), [1, 2, 3, 4], [5, 6, 7]])
   it "toString", ->
     expect(Bacon.never().bufferWithTimeOrCount(1, 2).toString()).to.equal("Bacon.never().bufferWithTimeOrCount(1,2)")

-describe.skip "EventStream.takeUntil", ->
-  describe.skip "takes elements from source until an event appears in the other stream", ->
+describe "EventStream.takeUntil", ->
+  describe "takes elements from source until an event appears in the other stream", ->
     expectStreamEvents(
       ->
         src = repeat(3, [1, 2, 3])
         stopper = repeat(7, ["stop!"])
         src.takeUntil(stopper)
       [1, 2], unstable)
-  describe.skip "works on self-derived stopper", ->
+  describe "works on self-derived stopper", ->
     expectStreamEvents(
       ->
         src = repeat(3, [3, 2, 1])
         stopper = src.filter(lessThan(3))
         src.takeUntil(stopper)
       [3])
-  describe.skip "includes source errors, ignores stopper errors", ->
+  describe "includes source errors, ignores stopper errors", ->
     expectStreamEvents(
       ->
         src = repeat(2, [1, error(), 2, 3])
         stopper = repeat(7, ["stop!"]).merge(repeat(1, [error()]))
         src.takeUntil(stopper)
       [1, error(), 2], unstable)
-  describe.skip "works with Property as stopper", ->
+  describe "works with Property as stopper", ->
     expectStreamEvents(
       ->
         src = repeat(3, [1, 2, 3])
         stopper = repeat(7, ["stop!"]).toProperty()
         src.takeUntil(stopper)
       [1, 2], unstable)
-  describe.skip "considers Property init value as stopper", ->
+  describe "considers Property init value as stopper", ->
     expectStreamEvents(
       ->
         src = repeat(3, [1, 2, 3])
         stopper = Bacon.constant("stop")
         src.takeUntil(stopper)
       [])
-  describe.skip "ends immediately with synchronous stopper", ->
+  describe "ends immediately with synchronous stopper", ->
     expectStreamEvents(
       ->
         src = repeat(3, [1, 2, 3])
         stopper = Bacon.once("stop")
         src.takeUntil(stopper)
       [])
-  describe.skip "ends properly with a never-ending stopper", ->
+  describe "ends properly with a never-ending stopper", ->
     expectStreamEvents(
       ->
         src = series(1, [1,2,3])
         stopper = new Bacon.Bus()
         src.takeUntil(stopper)
       [1,2,3])
-  describe.skip "unsubscribes its source as soon as possible", ->
+  describe "unsubscribes its source as soon as possible", ->
      expectStreamEvents(
        ->
         startTick = sc.now()
@@ -899,14 +899,14 @@
           expect(sc.now()).to.equal(startTick + 1))
         .takeUntil Bacon.later(1)
       [])
-  describe.skip "it should unsubscribe its stopper on end", ->
+  describe "it should unsubscribe its stopper on end", ->
      expectStreamEvents(
        ->
          startTick = sc.now()
          Bacon.later(1,'x').takeUntil(Bacon.later(20).onUnsub(->
            expect(sc.now()).to.equal(startTick + 1)))
        ['x'])
-  describe.skip "it should unsubscribe its stopper on no more", ->
+  describe "it should unsubscribe its stopper on no more", ->
      expectStreamEvents(
        ->
          startTick = sc.now()
@@ -916,7 +916,7 @@
   it "toString", ->
     expect(Bacon.later(1, "a").takeUntil(Bacon.later(2, "b")).toString()).to.equal("Bacon.later(1,a).takeUntil(Bacon.later(2,b))")

-describe.skip "When an Event triggers another one in the same stream, while dispatching", ->
+describe "When an Event triggers another one in the same stream, while dispatching", ->
   it "Delivers triggered events correctly", ->
     bus = new Bacon.Bus
     values = []
@@ -937,46 +937,46 @@
     bus.push("foo")
     expect(values).to.deep.equal(["foo"])

-describe.skip "EventStream.awaiting(other)", ->
-  describe.skip "indicates whether s1 has produced output after s2 (or only the former has output so far)", ->
+describe "EventStream.awaiting(other)", ->
+  describe "indicates whether s1 has produced output after s2 (or only the former has output so far)", ->
     expectPropertyEvents(
       -> series(2, [1, 1]).awaiting(series(3, [2]))
       [false, true, false, true])
-  describe.skip "supports Properties", ->
+  describe "supports Properties", ->
     expectPropertyEvents(
       -> series(2, [1, 1]).awaiting(series(3, [2]).toProperty())
       [false, true, false, true])
   it "toString", ->
     expect(Bacon.never().awaiting(Bacon.once(1)).toString()).to.equal("Bacon.never().awaiting(Bacon.once(1))")

-describe.skip "EventStream.endOnError", ->
-  describe.skip "terminates on error", ->
+describe "EventStream.endOnError", ->
+  describe "terminates on error", ->
     expectStreamEvents(
       -> repeat(1, [1, 2, error(), 3]).endOnError()
       [1, 2, error()])
-  describe.skip "accepts predicate function", ->
+  describe "accepts predicate function", ->
     expectStreamEvents(
       -> series(1, [1, 2, error(), 3, new Bacon.Error({serious:true}), 4]).endOnError((e) -> e?.serious)
       [1,2,error(),3,error()])
-  describe.skip "accepts extractor string", ->
+  describe "accepts extractor string", ->
     expectStreamEvents(
       -> series(1, [1, 2, error(), 3, new Bacon.Error({serious:true}), 4]).endOnError(".serious")
       [1,2,error(),3,error()])
-  describe.skip "works with synchronous source", ->
+  describe "works with synchronous source", ->
     expectStreamEvents(
       -> Bacon.fromArray([1, 2, error(), 3]).endOnError()
       [1, 2, error()])
   it "toString", ->
     expect(Bacon.never().endOnError().toString()).to.equal("Bacon.never().endOnError()")

-describe.skip "Bacon.constant", ->
-  describe.skip "creates a constant property", ->
+describe "Bacon.constant", ->
+  describe "creates a constant property", ->
     expectPropertyEvents(
       -> Bacon.constant("lol")
     ["lol"])
   it "ignores unsubscribe", ->
     Bacon.constant("lol").onValue(=>)()
-  describe.skip "provides same value to all listeners", ->
+  describe "provides same value to all listeners", ->
     c = Bacon.constant("lol")
     expectPropertyEvents((-> c), ["lol"])
     it "check check", ->
@@ -991,107 +991,107 @@
     c.onValue(f)
     f.verify("lol")

-describe.skip "Bacon.never", ->
-  describe.skip "should send just end", ->
+describe "Bacon.never", ->
+  describe "should send just end", ->
     expectStreamEvents(
       -> Bacon.never()
       [])

-describe.skip "Bacon.once", ->
-  describe.skip "should send single event and end", ->
+describe "Bacon.once", ->
+  describe "should send single event and end", ->
     expectStreamEvents(
       -> Bacon.once("pow")
       ["pow"])
-  describe.skip "accepts an Error event as parameter", ->
+  describe "accepts an Error event as parameter", ->
     expectStreamEvents(
       -> Bacon.once(new Bacon.Error("oop"))
       [error()])

-describe.skip "EventStream.concat", ->
-  describe.skip "provides values from streams in given order and ends when both are exhausted", ->
+describe "EventStream.concat", ->
+  describe "provides values from streams in given order and ends when both are exhausted", ->
     expectStreamEvents(
       ->
         left = series(2, [1, error(), 2, 3])
         right = series(1, [4, 5, 6])
         left.concat(right)
       [1, error(), 2, 3, 4, 5, 6], unstable)
-  describe.skip "respects subscriber return value when providing events from left stream", ->
+  describe "respects subscriber return value when providing events from left stream", ->
     expectStreamEvents(
       ->
         left = repeat(3, [1, 3]).take(3)
         right = repeat(2, [1]).take(3)
         left.concat(right).takeWhile(lessThan(2))
       [1])
-  describe.skip "respects subscriber return value when providing events from right stream", ->
+  describe "respects subscriber return value when providing events from right stream", ->
     expectStreamEvents(
       ->
         left = series(3, [1, 2])
         right = series(2, [2, 4, 6])
         left.concat(right).takeWhile(lessThan(4))
       [1, 2, 2])
-  describe.skip "works with Bacon.never()", ->
+  describe "works with Bacon.never()", ->
     expectStreamEvents(
       -> Bacon.never().concat(Bacon.never())
       [])
-  describe.skip "works with Bacon.once()", ->
+  describe "works with Bacon.once()", ->
     expectStreamEvents(
       -> Bacon.once(2).concat(Bacon.once(1))
       [2, 1])
-  describe.skip "works with Bacon.once() and Bacon.never()", ->
+  describe "works with Bacon.once() and Bacon.never()", ->
     expectStreamEvents(
       -> Bacon.once(1).concat(Bacon.never())
       [1])
-  describe.skip "works with Bacon.never() and Bacon.once()", ->
+  describe "works with Bacon.never() and Bacon.once()", ->
     expectStreamEvents(
       -> Bacon.never().concat(Bacon.once(1))
       [1])
-  describe.skip "works with Bacon.once() and async source", ->
+  describe "works with Bacon.once() and async source", ->
     expectStreamEvents(
       -> Bacon.once(1).concat(series(1, [2, 3]))
       [1, 2, 3])
-  describe.skip "works with Bacon.once() and Bacon.fromArray()", ->
+  describe "works with Bacon.once() and Bacon.fromArray()", ->
     expectStreamEvents(
       -> Bacon.once(1).concat(Bacon.fromArray([2, 3]))
       [1, 2, 3], unstable)
   it "toString", ->
     expect(Bacon.once(1).concat(Bacon.once(2)).toString()).to.equal("Bacon.once(1).concat(Bacon.once(2))")

-describe.skip "EventStream.startWith", ->
-  describe.skip "provides seed value, then the rest", ->
+describe "EventStream.startWith", ->
+  describe "provides seed value, then the rest", ->
     expectStreamEvents(
       ->
         left = series(1, [1, 2, 3])
         left.startWith('pow')
       ['pow', 1, 2, 3], unstable)
-  describe.skip "works with synchronous source", ->
+  describe "works with synchronous source", ->
     expectStreamEvents(
       ->
         left = Bacon.fromArray([1, 2, 3])
         left.startWith('pow')
       ['pow', 1, 2, 3], unstable)

-describe.skip "Property.startWith", ->
-  describe.skip "starts with given value if the Property doesn't have an initial value", ->
+describe "Property.startWith", ->
+  describe "starts with given value if the Property doesn't have an initial value", ->
     expectPropertyEvents(
       ->
         left = series(1, [1, 2, 3]).toProperty()
         left.startWith('pow')
       ['pow', 1, 2, 3], unstable)
-  describe.skip "works with synchronous source", ->
+  describe "works with synchronous source", ->
     expectPropertyEvents(
       ->
         left = Bacon.fromArray([1, 2, 3]).toProperty()
         left.startWith('pow')
       ['pow', 1, 2, 3], unstable)
-  describe.skip "starts with the initial value of the Property if any", ->
+  describe "starts with the initial value of the Property if any", ->
     expectPropertyEvents(
       ->
         left = series(1, [1, 2, 3]).toProperty(0)
         left.startWith('pow')
       [0, 1, 2, 3], unstable)

-describe.skip "EventStream.toProperty", ->
-  describe.skip "delivers current value and changes to subscribers", ->
+describe "EventStream.toProperty", ->
+  describe "delivers current value and changes to subscribers", ->
     expectPropertyEvents(
       ->
         s = new Bacon.Bus()
@@ -1101,17 +1101,17 @@
           s.end()
         p
       ["a", "b"])
-  describe.skip "passes through also Errors", ->
+  describe "passes through also Errors", ->
     expectPropertyEvents(
       -> series(1, [1, error(), 2]).toProperty()
       [1, error(), 2])

-  describe.skip "supports null as value", ->
+  describe "supports null as value", ->
     expectPropertyEvents(
       -> series(1, [null, 1, null]).toProperty(null)
       [null, null, 1, null])

-  describe.skip "does not get messed-up by a transient subscriber (bug fix)", ->
+  describe "does not get messed-up by a transient subscriber (bug fix)", ->
     expectPropertyEvents(
       ->
         prop = series(1, [1,2,3]).toProperty(0)
@@ -1119,7 +1119,7 @@
           Bacon.noMore
         prop
       [0, 1, 2, 3])
-  describe.skip "works with synchronous source", ->
+  describe "works with synchronous source", ->
     expectPropertyEvents(
       -> Bacon.fromArray([1,2,3]).toProperty()
       [1,2,3])
@@ -1134,18 +1134,18 @@
     Bacon.fromArray([1,2,3,4,5]).map(id).toProperty().skip(4).onValue()
     expect(calls).to.equal(1)

-describe.skip "Property.toEventStream", ->
-  describe.skip "creates a stream that starts with current property value", ->
+describe "Property.toEventStream", ->
+  describe "creates a stream that starts with current property value", ->
     expectStreamEvents(
       -> series(1, [1, 2]).toProperty(0).toEventStream()
       [0, 1, 2], unstable)
-  describe.skip "works with synchronous source", ->
+  describe "works with synchronous source", ->
     expectStreamEvents(
       -> Bacon.fromArray([1, 2]).toProperty(0).toEventStream()
       [0, 1, 2], unstable)

-describe.skip "Property.toProperty", ->
-  describe.skip "returns the same Property", ->
+describe "Property.toProperty", ->
+  describe "returns the same Property", ->
     expectPropertyEvents(
       -> Bacon.constant(1).toProperty()
       [1])
@@ -1155,8 +1155,8 @@
       fail()
     catch e

-describe.skip "Property.map", ->
-  describe.skip "maps property values", ->
+describe "Property.map", ->
+  describe "maps property values", ->
     expectPropertyEvents(
       ->
         s = new Bacon.Bus()
@@ -1168,8 +1168,8 @@
         p
       [2, 4, error()])

-describe.skip "Property.filter", ->
-  describe.skip "should filter values", ->
+describe "Property.filter", ->
+  describe "should filter values", ->
     expectPropertyEvents(
       -> series(1, [1, error(), 2, 3]).toProperty().filter(lessThan(3))
       [1, error(), 2])
@@ -1182,7 +1182,7 @@
     values = []
     p.onValue((v) => values.push(v))
     expect(values).to.deep.equal([1])
-  describe.skip "can filter by Property value", ->
+  describe "can filter by Property value", ->
     expectPropertyEvents(
       ->
         src = series(2, [1, 2, 3, 4]).delay(t(1)).toProperty()
@@ -1190,20 +1190,20 @@
         src.filter(ok)
       [2, 3])

-describe.skip "Property.take(1)", ->
-  describe.skip "takes the Initial event", ->
+describe "Property.take(1)", ->
+  describe "takes the Initial event", ->
     expectPropertyEvents(
       -> series(1, [1,2,3]).toProperty(0).take(1)
       [0])
-  describe.skip "takes the first Next event, if no Initial value", ->
+  describe "takes the first Next event, if no Initial value", ->
     expectPropertyEvents(
       -> series(1, [1,2,3]).toProperty().take(1)
       [1])
-  describe.skip "works for constants", ->
+  describe "works for constants", ->
     expectPropertyEvents(
       -> Bacon.constant(1)
       [1])
-  describe.skip "works for never-ending Property", ->
+  describe "works for never-ending Property", ->
     expectPropertyEvents(
       -> repeat(1, [1,2,3]).toProperty(0).take(1)
       [0])
@@ -1211,41 +1211,41 @@
       -> repeat(1, [1,2,3]).toProperty().take(1)
       [1])

-describe.skip "Bacon.once().take(1)", ->
-  describe.skip "works", ->
+describe "Bacon.once().take(1)", ->
+  describe "works", ->
     expectStreamEvents(
       -> Bacon.once(1).take(1)
       [1])

-describe.skip "Property.takeWhile", ->
-  describe.skip "takes while predicate is true", ->
+describe "Property.takeWhile", ->
+  describe "takes while predicate is true", ->
     expectPropertyEvents(
       -> series(1, [1, error("wat"), 2, 3])
         .toProperty()
         .takeWhile(lessThan(3))
       [1, error("wat"), 2])
-  describe.skip "extracts field values", ->
+  describe "extracts field values", ->
     expectPropertyEvents(
       -> series(1, [{good:true, value:"yes"}, {good:false, value:"no"}])
            .toProperty()
            .takeWhile(".good").map(".value")
       ["yes"])
-  describe.skip "can filter by Property value", ->
+  describe "can filter by Property value", ->
     expectPropertyEvents(
       ->
         src = series(1, [1,1,2,3,4,4,8,7]).toProperty()
         odd = src.map((x) -> x % 2)
         src.takeWhile(odd)
       [1,1])
-  describe.skip "works with never-ending Property", ->
+  describe "works with never-ending Property", ->
     expectPropertyEvents(
       -> repeat(1, [1, error("wat"), 2, 3])
         .toProperty()
         .takeWhile(lessThan(3))
       [1, error("wat"), 2])

-describe.skip "Property.takeUntil", ->
-  describe.skip "takes elements from source until an event appears in the other stream", ->
+describe "Property.takeUntil", ->
+  describe "takes elements from source until an event appears in the other stream", ->
     expectPropertyEvents(
       -> series(2, [1,2,3]).toProperty().takeUntil(Bacon.later(t(3)))
       [1])


Add spec for IP metadata forwarding

Add spec for IP metadata forwarding

--- spec/Component.coffee
+++ spec/Component.coffee
@@ -956,6 +956,51 @@

       sin1.post new IP 'closeBracket', 'msg'
       sin2.post new IP 'closeBracket', 'delay'
+
+    it 'should forward IP metadata for map-style components', (done) ->
+      c = new component.Component
+        inPorts:
+          in:
+            datatype: 'string'
+        outPorts:
+          out:
+            datatype: 'string'
+          error:
+            datatype: 'object'
+        process: (input, output) ->
+          str = input.getData()
+          if typeof str isnt 'string'
+            return output.complete new Error 'Input is not string'
+          output.pass str.toUpperCase()
+
+      c.inPorts.in.attach sin1
+      c.outPorts.out.attach sout1
+      c.outPorts.error.attach sout2
+
+      source = [
+        'foo'
+        'bar'
+        'baz'
+      ]
+      count = 0
+      sout1.on 'ip', (ip) ->
+        chai.expect(ip.type).to.equal 'data'
+        chai.expect(ip.count).to.be.a 'number'
+        chai.expect(ip.length).to.be.a 'number'
+        chai.expect(ip.data).to.equal source[ip.count].toUpperCase()
+        chai.expect(ip.length).to.equal source.length
+        count++
+        done() if count is source.length
+
+      sout2.on 'ip', (ip) ->
+        console.log 'Unexpected error', ip
+        done ip.data
+
+      n = 0
+      for str in source
+        sin1.post new IP 'data', str,
+          count: n++
+          length: source.length

     describe 'with custom callbacks', ->


revert hash reset change

revert hash reset change

--- src/scripts/modules/media/body/body.coffee
+++ src/scripts/modules/media/body/body.coffee
@@ -447,6 +447,7 @@
       jumpToHash = () =>
         if currentPage.get('loaded') and not @fragmentReloaded and window.location.hash
           @fragmentReloaded = true
+
           hash = window.location.hash
           window.location.hash = ''
           window.location.hash = hash


Active sourceless monitoring for StreamGroup sources

We were looping streams, but not the shadow stream created inside the
stream group.

--- src/streammachine/master/monitoring.coffee
+++ src/streammachine/master/monitoring.coffee
@@ -2,11 +2,15 @@

 module.exports = class Monitoring extends require("events").EventEmitter
     constructor: (@master,@log,@opts) ->
-        # -- check monitored streams for sources -- #
+        # -- check monitored streams and stream groups for sources -- #

         @_streamInt = setInterval =>
             for k,s of @master.streams
                 @master.alerts.update "sourceless", s.key, !s.source? if s.opts.monitored
+
+            for k,sg of @master.stream_groups
+                @master.alerts.update "sourceless", sg._stream.key, !sg._stream.source? if sg._stream.opts.monitored
+
         , 5*1000

         # -- Monitor Slave Status -- #


Floor dist_sq to 1

Floor dist_sq to 1

--- js/particles.coffee
+++ js/particles.coffee
@@ -58,7 +58,7 @@

   force: (pos, vel) =>
     difference = Vector.subtract(MOUSE.pos, pos)
-    dist_sq = Math.max(difference.mag_sq(), 25)
+    dist_sq = Math.max(difference.mag_sq(), 1)

     velocity = @vel.mag()
     g_force = 10000000 * MOUSE.mass * @mass / dist_sq
@@ -138,7 +138,7 @@
   explode_particles: (evt) =>
     for particle in @particles
       difference = Vector.subtract(MOUSE.pos, particle.pos)
-      dist = Math.max(difference.mag(), 20)
+      dist = Math.max(difference.mag(), 1)

       push_force = -1000000
       vel_mag = push_force / dist


Update branch text on save

Update branch text on save

--- src/app/status-bar.coffee
+++ src/app/status-bar.coffee
@@ -34,6 +34,7 @@
       @subscribeToBuffer()
       @updatePathText()

+    @updateBranchText()
     @updateCursorPositionText()
     @editor.on 'cursor-move', => _.defer => @updateCursorPositionText()

@@ -43,14 +44,27 @@
     @buffer?.off '.status-bar'
     @buffer = @editor.getBuffer()
     @buffer.on 'change.status-bar', => _.defer => @updateBufferModifiedText()
-    @buffer.on 'after-save.status-bar', => _.defer => @updateBufferModifiedText()
-    @updateBufferModifiedText()
+    @buffer.on 'after-save.status-bar', => _.defer =>
+      @updateBranchText()
+      @updateBufferModifiedText()

   updateBufferModifiedText: ->
     if @buffer.isModified()
       @bufferModified.text('*')
     else
       @bufferModified.text('')
+
+  updateBranchText: ->
+    if path = @editor.getPath()
+      @head = Git.open(path)?.getShortHead()
+    else
+      @head = null
+
+    if @head
+      @branchArea.show()
+      @branchLabel.text(@head)
+    else
+      @branchArea.hide()

   updatePathText: ->
     path = @editor.getPath()
@@ -62,9 +76,4 @@

   updateCursorPositionText: ->
     { row, column } = @editor.getCursorBufferPosition()
-    if @head
-      @branchArea.show()
-      @branchLabel.text(@head)
-    else
-      @branchArea.hide()
     @cursorPosition.text("#{row + 1},#{column + 1}")


add some reddits

add some reddits

--- src/scripts/controllers/redditelly.coffee
+++ src/scripts/controllers/redditelly.coffee
@@ -30,6 +30,7 @@
             'movietrailers'
             'trailers'
             'television'
+            'standupcomedy'
         ]
     }, {
         name: 'sports'
@@ -49,6 +50,12 @@
             'gaming'
             'games'
         ]
+    }, {
+        name: 'learn'
+        list: [
+            'lectures'
+            'interview'
+        ]
     }]

     $scope.goTo = (subreddit) ->


improved config.locale setting

improved config.locale setting

--- src/scripts/daterangepicker/config.coffee
+++ src/scripts/daterangepicker/config.coffee
@@ -93,13 +93,13 @@
           new DateRange(title, from, to)

   _locale: (val) ->
-    val || {
+    $.extend({
       applyButtonTitle: 'Apply'
       cancelButtonTitle: 'Cancel'
       inputFormat: 'L'
       startLabel: 'Start'
       endLabel: 'End'
-    }
+    }, val || {})

   _orientation: (val) ->
     val = 'right' unless val in ['right', 'left']


PwmWrite: don't reapply all values all the time, just changed ones

--- components/PwmWrite.coffee
+++ components/PwmWrite.coffee
@@ -20,10 +20,10 @@
       @startGpio()
     @inPorts.dutycycle.on 'data', (value) =>
       @dutyCycle = value
-      @applyParameters()
+      @pwm.setDutyCycle(@dutyCycle) if @ready
     @inPorts.period.on 'data', (value) =>
       @period = value
-      @applyParameters()
+      @pwm.setPeriod(@period) if @ready

   stopGpio: () ->
     return unless @pwm


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color.coffee
+++ test/color.coffee
@@ -89,7 +89,7 @@
   equals color.g(), 24
   equals color.b(), 101

-test "Color should create from 3 or 4 numbers", ->
+test "Color parse 3 or 4 number arguments", ->
   color = Color(4, 34, 102, 0.4)
   equals color.r(), 4
   equals color.g(), 34


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color.coffee
+++ test/color.coffee
@@ -175,7 +175,7 @@
     a: -> 0.3

 test "should parse 549E00", ->
-  color = Color('#549E00')
+  color = Color(549E00)

 test "should be able to lighten", ->
   color = Color('#f00').lighten(0.1)


make prettier run only after saving all local changes... since doing otherwise is so confusing.

--- src/smc-webapp/code-editor/actions.coffee
+++ src/smc-webapp/code-editor/actions.coffee
@@ -9,6 +9,7 @@

 immutable       = require('immutable')
 underscore      = require('underscore')
+async           = require('async')

 schema          = require('smc-util/schema')

@@ -689,27 +690,35 @@
             tabWidth : editor_settings.get('tab_size')
             parser   : parser
             useTabs  : not editor_settings.get('spaces_instead_of_tabs')
-        @set_status("Running prettier on committed version...")
-        webapp_client.prettier
-            project_id : @project_id
-            path       : @path
-            options    : options
-            cb         : (err, resp) =>
-                @set_status("")
-                if err
-                    error = "Error formatting code: \n#{err}"
-                else if resp.status == 'error'
-                    start = resp.error?.loc?.start
-                    if start?
-                        error = "Syntax error on line #{start.line} column #{start.column} -- fix and then run prettier again."
-                    else
-                        error = "Syntax error -- please fix then run prettier again."
-                else
-                    error = undefined
-                if error
-                    @setState(error: error)
-                else
-                    @setState(error: '')
+        async.series([
+            (cb) =>
+                @set_status("Ensuring your latest changes are saved...")
+                @set_syncstring_to_codemirror()
+                @_syncstring._save(cb)
+            (cb) =>
+                @set_status("Running Prettier code formatter...")
+                webapp_client.prettier
+                    project_id : @project_id
+                    path       : @path
+                    options    : options
+                    cb         : (err, resp) =>
+                        @set_status("")
+                        if err
+                            error = "Error formatting code: \n#{err}"
+                        else if resp.status == 'error'
+                            start = resp.error?.loc?.start
+                            if start?
+                                error = "Syntax error on line #{start.line} column #{start.column} -- fix and then run prettier again."
+                            else
+                                error = "Syntax error -- please fix then run prettier again."
+                        else
+                            error = undefined
+                        @setState(error: '')
+                        cb(error)
+        ], (err) =>
+            if err
+                @setState(error: err)
+        )

     ###
     format_dialog_action: (cmd) ->


remove active param from workflow request to fix test workflow button

--- app/pages/project/index.cjsx
+++ app/pages/project/index.cjsx
@@ -84,7 +84,7 @@
       @context.geordi?.forget ['experiment','cohort']

   fetchProjectData: (ownerName, projectName, user) ->
-    @setState({
+    @setState({
       error: null,
       loading: true,
       preferences: null,
@@ -120,7 +120,7 @@
             awaitProjectCompleteness,
             awaitProjectRoles,
             awaitPreferences
-          ]).then(([background, owner, pages, projectAvatar, projectIsComplete, projectRoles, preferences]) =>
+          ]).then(([background, owner, pages, projectAvatar, projectIsComplete, projectRoles, preferences]) =>
               @setState({ background, owner, pages, projectAvatar, projectIsComplete, projectRoles, preferences })
               @getSelectedWorkflow(project, preferences)
             ).catch((error) => @setState({ error }); console.error(error); );
@@ -201,7 +201,7 @@
       linkedWorkflows[randomIndex]

   getWorkflow: (selectedWorkflowID) ->
-    apiClient.type('workflows').get({ active: true, id: "#{selectedWorkflowID}", project_id: @state.project.id })
+    apiClient.type('workflows').get({ id: "#{selectedWorkflowID}", project_id: @state.project.id })
       .catch (error) =>
         console.error error
         # TODO: Handle 404 once json-api-client error handling is fixed.


Removes unused variables

Removes unused variables

--- benchmark/benchmark.coffee
+++ benchmark/benchmark.coffee
@@ -13,8 +13,6 @@

 global.crc = require '../src'
 global.bufferCRC32 = require 'buffer-crc32'
-global.string100 = getBuffer(100).toString()
-global.string1kb = getBuffer(1024).toString()

 suite = new benchmark.Suite


CHG: fix component test

CHG: fix component test

--- test/javascripts/tao/component_test.coffee
+++ test/javascripts/tao/component_test.coffee
@@ -14,15 +14,6 @@
       @attribute 'json', type: 'object', default: {x: 1}

       @attribute 'array', type: 'array'
-
-      _init: ->
-        @trigger 'initialized'
-
-      _connected: ->
-        @trigger 'connected'
-
-      _disconnected: ->
-        @trigger 'disconnected'

       _nameChanged: ->
         @trigger 'nameChanged'


Use :: instead of .prototype.

Use :: instead of .prototype.

--- src/dispatcher.coffee
+++ src/dispatcher.coffee
@@ -7,7 +7,7 @@

 dispatcherIds = new IdFactory('Dispatcher_ID')

-Dispatcher.prototype.register = ->
+Dispatcher::register = ->
   args = _.compact(arguments)

   if args.length == 3


Fix itemsWereRemoved handler for iterator binding.

Even though it's not actually used...

--- src/view/bindings/iterator_binding.coffee
+++ src/view/bindings/iterator_binding.coffee
@@ -50,10 +50,12 @@
     @backingView.finishAppendItems()

   handleItemsRemoved: (oldItems) =>
-    for subview in @backingView.subviews._storage
-      if subview.get(@attributeName) == item
-        subview.die()
-    return
+    for item in oldItems
+      for subview in @backingView.subviews._storage
+        if subview.get(@attributeName) == item
+          subview.unset(@attributeName)
+          subview.die()
+          break

   die: ->
     @prototypeNode = null


Allow undefined new values in iteratorBinding.

--- src/view/bindings/iterator_binding.coffee
+++ src/view/bindings/iterator_binding.coffee
@@ -46,7 +46,7 @@

   handleItemsAdded: (newItems) =>
     @backingView.beginAppendItems()
-    @backingView.appendItem(item) for item in newItems
+    @backingView.appendItem(item) for item in newItems if newItems
     @backingView.finishAppendItems()

   handleItemsRemoved: (oldItems) =>


added aborted xhr in the event of page change request before xhr finishes

--- lib/assets/javascripts/turbolinks.js.coffee
+++ lib/assets/javascripts/turbolinks.js.coffee
@@ -4,6 +4,7 @@
 loadedAssets   = null
 pageCache      = {}
 createDocument = null
+xhr            = null

 visit = (url) ->
   if browserSupportsPushState
@@ -20,11 +21,13 @@
   # Remove hash from url to ensure IE 10 compatibility
   safeUrl = removeHash url

+  xhr.abort() if xhr
   xhr = new XMLHttpRequest
   xhr.open 'GET', safeUrl, true
   xhr.setRequestHeader 'Accept', 'text/html, application/xhtml+xml, application/xml'
   xhr.setRequestHeader 'X-XHR-Referer', referer
-
+  xhr.onabort = ->
+    xhr = null
   xhr.onload = =>
     doc = createDocument xhr.responseText

@@ -38,12 +41,11 @@
       else
         resetScrollPosition()
       triggerEvent 'page:load'
-
-  xhr.onabort = -> console.log 'Aborted turbolink fetch!'
+    xhr = null

   xhr.onerror = ->
-    console.log 'turbolink fetch Failed!'
     document.location.href = url
+    xhr = null

   xhr.send()


add delay to hide view

add delay to hide view

--- lib/client/javascripts/nali/model.js.coffee
+++ lib/client/javascripts/nali/model.js.coffee
@@ -349,10 +349,10 @@
     # функция возвращает объект вида при успехе либо null при неудаче
     if ( view = @view( name ) )? then view.show insertTo else null

-  hide: ( name ) ->
+  hide: ( name, delay ) ->
     # удаляет html-код вида со страницы
     # функция возвращает объект вида при успехе либо null при неудаче
-    if ( view = @view( name ) )? then view.hide() else null
+    if ( view = @view( name ) )? then view.hide delay else null

   # валидации


Unattach resize event when NewHomeView is destoryed

--- app/views/NewHomeView.coffee
+++ app/views/NewHomeView.coffee
@@ -102,6 +102,10 @@
     setTimeout(@fitToPage, 0)
     super()

+  destroy: ->
+    $(window).off 'resize', @fitToPage
+    super()
+
   logoutAccount: ->
     Backbone.Mediator.publish("auth:logging-out", {})
     logoutUser()


fixed primary gradient bug

fixed primary gradient bug

--- src/art/engine/elements/fillable_base.coffee
+++ src/art/engine/elements/fillable_base.coffee
@@ -20,8 +20,8 @@
   @drawProperty
     # from:   default: "topLeft", preprocess: (v) -> point v
     # to:     default: null, preprocess: (v) -> v? && point v
-    from: preprocess: (v, oldValue) -> !v || if v instanceof PointLayoutBase then v else new PointLayout v, oldValue
-    to:   preprocess: (v, oldValue) -> !v || if v instanceof PointLayoutBase then v else new PointLayout v, oldValue
+    from: preprocess: (v) -> v && if v instanceof PointLayoutBase then v else new PointLayout v
+    to:   preprocess: (v) -> v && if v instanceof PointLayoutBase then v else new PointLayout v

     colors: default: null
     gradientRadius: default: null


Add Polish translation

Add Polish translation

--- client/i18n/polish.coffee
+++ client/i18n/polish.coffee
@@ -37,4 +37,4 @@
     signupCodeIncorrect: "Kod rejestracji jest nieprawidłowy."
     signInRequired: "Musisz być zalogowany, aby to zrobić."

-T9n.map "en", en
+T9n.map "pl", pl


Typo.

Typo.

--- client/publication.coffee
+++ client/publication.coffee
@@ -864,7 +864,7 @@
   $(document).on 'mouseup.publicationAnnotations', (e) =>
     if Session.get 'currentHighlightId'
       # Highlight is currently selected, so we do not update location and
-      # leave to Annotator.updateLocation to handel this. This allows making
+      # leave to Annotator.updateLocation to handle this. This allows making
       # one highlight immediatelly after another, without having to go through
       # a publication-only location after new highlight is created, befure
       # location is updated to this new highlight location.


Turned Given/When/Then into it()

Turned Given/When/Then into it()

--- cakes.coffee
+++ cakes.coffee
@@ -10,12 +10,14 @@
 	describe(message, callback)
 	return

-depict = (label, args)->
-	if args.length == 1
-		args[1] = args[0]
-		label = ''
+dic = (type, label, args)->  # Dictate to describe() or it()
+	if type in ['describe', 'it']
+		if args.length == 1
+			args[1] = args[0]
+			label = ''

-	describe label.replace('%s', args[0]), args[1]
+		global[type] label.replace('%s', args[0]), args[1]
+

 ###
 exports.Background = (action, callback)->
@@ -23,28 +25,33 @@
 ###

 exports.Scenario = ->
-	depict "\n    Scenario: %s".green, arguments
+	dic 'describe', "\n    Scenario: %s".green, arguments
+

 exports.Given = ->
-	depict "Given:".yellow+" %s", arguments
+	dic 'it', "Given:".yellow+" %s", arguments

 exports.When = ->
-	depict " When:".yellow+" %s", arguments
+	dic 'it', " When:".yellow+" %s", arguments
+
+exports.Then = ->
+	dic 'it', " Then:".yellow+" %s", arguments
+

 exports.And = ->
-	depict "  and".grey+"  %s", arguments
-
-exports.Then = ->
-	depict " Then:".yellow+" %s", arguments
+	dic 'describe', "  and".grey+"  %s", arguments

 exports.But = ->
-	depict "  But".yellow+"  %s", arguments
+	dic 'describe', "  But".yellow+"  %s", arguments
+
+
+exports.step = ->
+	dic 'it', '', arguments
+

 exports.Spec = ->  # describe() start of spec file
-	depict '%s'.blue, arguments
+	dic 'describe', '%s'.blue, arguments

-exports.step = (title, fn)->
-	it title, fn

 # Add function names to global scope.
 (global[name] = func for name, func of module.exports)


config: use dev segment for sandbox

config: use dev segment for sandbox

--- config/main.sandbox.coffee
+++ config/main.sandbox.coffee
@@ -54,7 +54,7 @@
   # configuration for socialapi, order will be the same with
   # ./go/src/socialapi/config/configtypes.go

-  segment                 = '4c570qjqo0'
+  segment                 = 'kb2hfdgf20'

   socialapi =
     proxyUrl                : "#{customDomain.local}/api/social"


updated files array pagesDev

updated files array pagesDev

--- config/plugins/jade.coffee
+++ config/plugins/jade.coffee
@@ -55,8 +55,8 @@

     watch:
       jadePages:
-        files: "<%= files.jade.pageRoot %> <%= files.jade.pages %>"
+        files: ["<%= files.jade.pageRoot %><%= files.jade.pages %>", "<%= files.jade.templates %>"]
         tasks: ["jade:pagesDev"]
       jadeTemplates:
         files: "<%= files.jade.templates %>"
-        tasks: ["jade:templates", "concat_sourcemap:js"]
+        tasks: ["jade:templates", "concat_sourcemap:js"]


rename axis rule to axis line, since they are perpendicular to the actual rule guides

--- static/coffee/renderers/guide/axis.coffee
+++ static/coffee/renderers/guide/axis.coffee
@@ -17,7 +17,7 @@
     super(attrs, options)

     guidespec = @mget('guidespec')
-    @rule_props = new line_properties(@, guidespec, 'rule_')
+    @rule_props = new line_properties(@, guidespec, 'axis_')
     @major_tick_props = new line_properties(@, guidespec, 'major_tick_')
     @major_label_props = new text_properties(@, guidespec, 'major_label_')

@@ -232,13 +232,13 @@

   level: 'overlay'

-  rule_line_color: 'black'
-  rule_line_width: 2
-  rule_line_alpha: 1.0
-  rule_line_join: 'miter'
-  rule_line_cap: 'butt'
-  rule_line_dash: []
-  rule_line_dash_offset: 0
+  axis_line_color: 'black'
+  axis_line_width: 2
+  axis_line_alpha: 1.0
+  axis_line_join: 'miter'
+  axis_line_cap: 'butt'
+  axis_line_dash: []
+  axis_line_dash_offset: 0

   major_tick_in: 2
   major_tick_out: 4


use @ in coffee script, and spacing fix

use @ in coffee script, and spacing fix

--- core/app/backbone/models/topic.coffee
+++ core/app/backbone/models/topic.coffee
@@ -1,8 +1,8 @@
 class window.Topic extends Backbone.Model
   newChannelForUser: (user) ->
     new Channel
-      title: this.get 'title'
-      slug_title: this.get 'slug_title'
+      title: @get 'title'
+      slug_title: @get 'slug_title'
       username: user.get 'username'

   withCurrentOrCreatedChannelFor: (user, options)->
@@ -14,7 +14,7 @@
       console.info "saving channel #{ch.get 'title'} to #{ch.url()}"
       ch.save({},
         success: (m,r)-> options.success?(m,r)
-        error:   (m,r)->
+        error:   (m,r)->
           user.channels.remove(ch)
           options.error?(m,r)
       )


Fixed yellow color typo

Fixed yellow color typo

--- styles/default_theme/default_theme.coffee
+++ styles/default_theme/default_theme.coffee
@@ -31,7 +31,7 @@
     t1: '#5f5f5f'
     light: '#efefef'
     dark: '#838383'
-    yellow: 'FFD000'
+    yellow: '#FFD000'
     green: '#BCE346'
     blue: '#00BEFF'
     red: '#f16b5c'


tests

tests

--- apps/home/test/client.coffee
+++ apps/home/test/client.coffee
@@ -23,10 +23,12 @@
         ]).models
         sd: {}
       }
+      sinon.stub _, 'defer'
       Backbone.$ = $
       done()

   after ->
+    _.defer.restore()
     benv.teardown()

   beforeEach ->
@@ -75,10 +77,12 @@
         ]).models
         sd: {}
       }
+      sinon.stub _, 'defer'
       Backbone.$ = $
       done()

   after ->
+    _.defer.restore()
     benv.teardown()

   beforeEach ->
@@ -112,4 +116,32 @@
     _.last(Backbone.sync.args)[2].success [fabricate 'post', title: "Retrospect on Andy Foobar"]
     $('body').html().should.include "Retrospect on Andy Foobar"

-  it 'renders featured artists'+  it 'renders featured artists'
+
+describe 'HomeAuthRouter', ->
+
+  before (done) ->
+    benv.setup => done()
+
+  after ->
+    benv.teardown()
+
+  beforeEach ->
+    HomeAuthRouter = benv.require '../client/auth_router.coffee'
+    HomeAuthRouter.__set__ 'mediator', @mediator = { trigger: sinon.stub() }
+    @router = new HomeAuthRouter
+
+  it 'on login opens the login modal', ->
+    @router.login()
+    @mediator.trigger.args[0][0].should.equal 'open:auth'
+    @mediator.trigger.args[0][1].mode.should.equal 'login'
+
+  it 'on signup opens the signup modal', ->
+    @router.signup()
+    @mediator.trigger.args[0][0].should.equal 'open:auth'
+    @mediator.trigger.args[0][1].mode.should.equal 'register'
+
+  it 'on forgot opens the forgot modal', ->
+    @router.forgot()
+    @mediator.trigger.args[0][0].should.equal 'open:auth'
+    @mediator.trigger.args[0][1].mode.should.equal 'forgot'


cleanup

cleanup

--- apps/home/test/routes.coffee
+++ apps/home/test/routes.coffee
@@ -3,7 +3,6 @@
 Backbone = require 'backbone'
 rewire = require 'rewire'
 routes = rewire '../routes'
-sd = require '../../../lib/shared_data.coffee'

 describe '#index', ->


Trigger default marionette events

Trigger default marionette events

--- vendor/assets/javascripts/region.js.coffee
+++ vendor/assets/javascripts/region.js.coffee
@@ -3,9 +3,10 @@
 class NavigationView.Region extends Backbone.Marionette.Region
   show: (newView, direction) ->
     @ensureEl()
-    @setNewView newView
-    @setDirection direction
+    @setNewView(newView)
+    @setDirection(direction)
     @render()
+    @triggerEvents(newView)

   setNewView: (newView) ->
     @newView = new NavigationView.RegionWrapper(originalView: newView)
@@ -21,3 +22,7 @@
   animate: ->
     @newView.animateIn @direction
     @currentView?.animateOut @direction
+
+  triggerEvents: (view) ->
+    Marionette.triggerMethod.call(this, "show", view)
+    Marionette.triggerMethod.call(view, "show")


WebsocketServer cannot forward message to senders.

spelling mistake.

Signed-off-by: Manson Li <f2a598d7717cc6757bef28b84a9ab02e5efc6048@matchstick.tv>

--- lib/comm/WebSocketServer.coffee
+++ lib/comm/WebSocketServer.coffee
@@ -36,7 +36,7 @@
             else
                 txconn = @txconn[senderId]
                 if txconn
-                    txconn.send JSON.stringify message.data
+                    txconn.send JSON.stringify messageObj.data
                 else
                     @_respReceiverError 'Invalid sender id'


Bugfixes in executing commands in the Cordova folder

--- cake/tasks/cordova.coffee
+++ cake/tasks/cordova.coffee
@@ -14,15 +14,12 @@
   initAndroid: =>
     @_initialize =>
       @addAndroid()
-      process.exit()
   initIOS: =>
     @_initialize =>
       @addIOS()
-      process.exit()
   initAll: =>
     @_initialize =>
       @addAll()
-      process.exit()

   addAndroid: => @_add 'android'
   addIOS:     => @_add 'ios'
@@ -52,37 +49,52 @@
           args.push name
           appNamePrompt callback
         else
-          do callback
+          callback()

     appNamePrompt = (callback) =>
       commander.prompt 'App name (optional): ', (name) =>
         name = _s.titleize _s.clean name
         args.push name if name isnt ''
-        do callback
+        callback()

     packageNamePrompt =>
-      @exec args
-      if callback
-        callback()
-      else
-        process.exit()
+      @exec args, =>
+        if callback
+          callback()
+        else
+          process.exit()

   _add: (platforms...) ->
     platforms.unshift 'add'
+    platforms.unshift 'platform'
     onExit = =>
       for platform in platforms when platform isnt 'add'
         fs.createReadStream("./cake/gitignore/#{platform}.gitignore")
           .pipe(fs.createWriteStream("#{@cordovaPath}/platforms/#{platform}/.gitignore"))
+      process.exit()
+    {command} = this
+    @command = "../.#{@command}"
     @exec(platforms, onExit, cwd: @cordovaPath, env: process.env)
+    @command = command

   _remove: (platforms...) ->
     platforms.unshift 'remove'
+    platforms.unshift 'platform'
+    {command} = this
+    @command = "../.#{@command}"
     @exec(platforms, (->), cwd: @cordovaPath)
+    @command = command

   _build: (platforms...) ->
     platforms.unshift 'build'
+    {command} = this
+    @command = "../.#{@command}"
     @exec(platforms, (->), cwd: @cordovaPath)
+    @command = command

   _emulate: (platforms...) ->
     platforms.unshift 'emulate'
-    @exec(platforms, (->), cwd: @cordovaPath)+    {command} = this
+    @command = "../.#{@command}"
+    @exec(platforms, (->), cwd: @cordovaPath)
+    @command = command


Add more buttonview tests

Add more buttonview tests

--- test/components/buttons/buttonview.coffee
+++ test/components/buttons/buttonview.coffee
@@ -78,3 +78,24 @@
     it 'should enable', ->
       @view.enable()
       @view.domElement[0].disabled.should.be.false
+
+  describe 'focus', ->
+    it 'should focus', ->
+      @view.$().trigger = @sinon.stub()
+      @view.focus()
+      @view.$().trigger.should.calledOnce
+      @view.$().trigger.should.calledWith 'focus'
+
+  describe 'setFocus', ->
+    it 'should set focus', ->
+      @view.$().trigger = @sinon.stub()
+      @view.setFocus()
+      @view.$().trigger.should.calledOnce
+      @view.$().trigger.should.calledWith 'focus'
+
+  describe 'blur', ->
+    it 'should blur', ->
+      @view.$().trigger = @sinon.stub()
+      @view.blur()
+      @view.$().trigger.should.calledOnce
+      @view.$().trigger.should.calledWith 'blur'


Fixed issue where total duration was calculated incorrrectly

--- src/core/javascripts/models/basket.js.coffee
+++ src/core/javascripts/models/basket.js.coffee
@@ -113,5 +113,5 @@
     totalDuration: ->
       duration = 0
       for item in @items
-        duration =+ item.service.listed_duration if item.service and item.service.listed_duration
+        duration += item.service.listed_duration if item.service and item.service.listed_duration
       return duration


error.url for easier handling

error.url for easier handling

--- components/Measure.coffee
+++ components/Measure.coffee
@@ -10,12 +10,12 @@
       dimensions: new noflo.Port 'array'
       error: new noflo.Port 'object'
     super 'url', 'dimensions'
-
+
   doAsync: (url, callback) ->
     image = new Image()
     image.onload = () =>
       if (image.naturalWidth? and image.naturalWidth is 0) or image.width is 0
-        image.onerror()
+        image.onerror new Error "#{url} didn't come back as a valid image."
         return
       dimensions = [image.width, image.height]
       @outPorts.dimensions.beginGroup url
@@ -24,8 +24,8 @@
       @outPorts.dimensions.disconnect()
       callback null
     image.onerror = (err) ->
+      err.url = url
       return callback err
     image.src = url
-    null

 exports.getComponent = -> new Measure


update :memo: and now seems to unnecessary for special care for ctrl-f/b/d/u

--- lib/cursor-style-manager.coffee
+++ lib/cursor-style-manager.coffee
@@ -37,14 +37,11 @@
     for cursor in @editor.getCursors()
       cursor.setVisible(cursor in cursorsToShow)

-    # [NOTE] In BlockwiseSelect we add selections(and corresponding cursors) in bluk.
-    # But corresponding cursorsComponent(HTML element) is added in sync.
-    # So to modify style of cursorsComponent, we have to make sure corresponding cursorsComponent
-    # is available by component in sync to model.
-    # [FIXME]
-    # When ctrl-f, b, d, u in vL mode, I had to call updateSync to show cursor correctly
-    # But it wasn't necessary before I iintroduce `moveToFirstCharacterOnVerticalMotion` for `ctrl-f`
-    @editorElement.component.updateSync()
+    # [NOTE] When activating visual-blockwise-mode multiple slections are added in bulk.
+    # But corresponding cursorsComponent(HTML element) is added asynchronously.
+    # We need to make sure that corresponding cursor's domNode is available to modify it's style.
+    if @submode is 'blockwise'
+      @editorElement.component.updateSync()

     # [NOTE] Using non-public API
     cursorNodesById = @editorElement.component.linesComponent.cursorsComponent.cursorNodesById


Unfocus spec

Unfocus spec

--- test/serializations/taskpaper-spec.coffee
+++ test/serializations/taskpaper-spec.coffee
@@ -194,8 +194,7 @@

   describe 'Item Path Types', ->

-    it.only 'should use item path type keywords', ->
+    it 'should use item path type keywords', ->
       outline.evaluateItemPath('task').length.should.equal(0)
       one.setAttribute('data-type', 'task')
-      debugger
       outline.evaluateItemPath('task').length.should.equal(1)


get buffer from editor not from file system

--- lib/init.coffee
+++ lib/init.coffee
@@ -174,7 +174,7 @@
 doc.getElementById('lupa-refresh').addEventListener('click', ->
     f = new File({
         path: currentFile,
-        contents: fs.readFileSync(currentFile)
+        contents: new Buffer(editor.getBuffer().getText())
     })
     plugin.invalidate(f)
     plugin.process(f).subscribe(update1)


Use regex to parse Authorization header

Use regex to parse Authorization header

The current implementation breaks on URI's with comma's and is not RFC compliant suggesting regex based parsing.
Based on passport-http digest implementation

--- lib/auth/digest.coffee
+++ lib/auth/digest.coffee
@@ -27,39 +27,19 @@

   # Parse authorization header.
   parseAuthorization: (header) ->
-    results = {}
-    isInQuotes = false
-    lastStringStartingBoundary = 0
-
-    #Need to pull off authentication type first
-    results.type = /^([a-zA-Z]+)\s/.exec(authorizationHeader)[1]
-
-    authorizationHeader = authorizationHeader.substring(results.type.length + 1) # type + 1 whitespace
-
+    opts = {}
+    parts = header.split(' ')
+    params = parts.slice(1).join(' ')
+    tokens = params.split(/,(?=(?:[^"]|"[^"]*")*$)/) #split the parameters by comma
+
     i = 0
-    while i < authorizationHeader.length
-      if authorizationHeader[i] == '\"' and authorizationHeader[i - 1] != '\\'
-        # WE've found an un-escaped quote (do escaped quotes exist, need to check the RFC)
-        isInQuotes = !isInQuotes;
-
-      # If we got to the end of a key value pair or the end of the header
-      if (authorizationHeader[i] == ',' or i == authorizationHeader.length - 1) and !isInQuotes
-        currentValueLen = (if i == authorizationHeader.length - 1 then authorizationHeader.length else i) - lastStringStartingBoundary
-        keyValue = authorizationHeader.substr(lastStringStartingBoundary, currentValueLen)
-
-        #Strip whitespace..
-        keyValue = keyValue.replace(/^\s+|\s+$/g, '')
-        pair = /^(.+)?=(.+)/.exec(keyValue)
-
-        #de-code quotes and un-escape inter-stitial quotes if appropriate
-        # I'm lost as to the correct behaviour of this bit tbh, the rfcs don't seem to be specifc
-        # around whether quoted strings need to quote the quotes or not!! (that I can find anyway :) )
-        value = pair[2].replace(/^"|"$/g, '')
-        results[pair[1]] = value
-        lastStringStartingBoundary = i + 1  # skip the comma.
-
+    len = tokens.length
+    while i < len
+      param = /(\w+)=["]?([^"]+)["]?$/.exec(tokens[i]) #strip quotes and whitespace
+      if param
+        opts[param[1]] = param[2]
       i++
-    results
+    opts

   # Validating hash.
   validate: (ha2, co, hash) ->


select: add onvalue callback

select: add onvalue callback

--- select/index.coffee
+++ select/index.coffee
@@ -79,6 +79,7 @@
     name
     onblur
     onfocus
+    onvalue
     tabindex
     options
   }) =>
@@ -152,7 +153,9 @@

           if selectedIndex < 0
             selectedIndex = options.length + selectedIndex
-          @valueWrite.next options[selectedIndex % options.length].value
+          nextValue = options[selectedIndex % options.length].value
+          @valueWrite.next nextValue
+          onvalue? nextValue
         z 'select',
           disabled: if isDisabled then true
           name: name
@@ -202,6 +205,7 @@
                 isSelected: (option.value or null) is value
               onclick: (e) =>
                 @valueWrite.next option.value
+                onvalue? option.value
                 e.currentTarget.parentElement.parentElement.blur()
               option.label
       z '.helper',


Add logging for debug

Add logging for debug

--- lib/arrivals.coffee
+++ lib/arrivals.coffee
@@ -7,6 +7,7 @@
     trainsInterval = Meteor.setInterval updateArrivals,10000

 updateArrivals = ->
+  console.log "Updating arrivals"
   res = HTTP.get "http://developer.itsmarta.com/RealtimeTrain/RestServiceNextTrain/GetRealtimeArrivals?apikey=#{share.api_key_MARTA}"
   if res.statusCode isnt 200
     return console.error "Failed to get train data",res


Spelling

Spelling

Fixes #52

--- lib/championify.coffee
+++ lib/championify.coffee
@@ -140,7 +140,7 @@
   }, (err, r) ->
     console.log(err) if err
     hlp.updateProgressBar(10) # Just max it.
-    cl 'Looks like were all done. Login and enjoy!'
+    cl 'Looks like we\'re all done. Login and enjoy!'
     done()


small tweak to console

small tweak to console

--- lib/cli/console.coffee
+++ lib/cli/console.coffee
@@ -15,7 +15,10 @@
   context.$_ = -> console.log(arguments)
   if Caboose.app?.models?
     for m in Caboose.app.models
-      name = if m.__name__? then m.__name__ else /function ([^\(]+)/.exec(m.toString())[1]
+      name = m.__name__
+      unless name?
+        parsed = /function ([^\(]+)/.exec(m.toString())
+        name = parsed[1] if parsed?
       context[name] = m

   for file in Caboose.path.controllers.readdir_sync()


Overdraw lines to discourage Blink from repainting the entire editor

--- src/display-buffer.coffee
+++ src/display-buffer.coffee
@@ -34,6 +34,7 @@
   horizontalScrollMargin: 6
   horizontalScrollbarHeight: 15
   verticalScrollbarWidth: 15
+  lineOverdraw: 8

   constructor: ({tabLength, @editorWidthInChars, @tokenizedBuffer, buffer}={}) ->
     super
@@ -241,7 +242,9 @@

     heightInLines = Math.ceil(@getHeight() / @getLineHeight()) + 1
     startRow = Math.floor(@getScrollTop() / @getLineHeight())
-    endRow = Math.min(@getLineCount(), Math.ceil(startRow + heightInLines))
+    endRow = Math.min(@getLineCount(), startRow + heightInLines + @lineOverdraw)
+    startRow = Math.max(0, startRow - @lineOverdraw)
+
     [startRow, endRow]

   intersectsVisibleRowRange: (startRow, endRow) ->


Add missing APIs to DisplayMarker

Add missing APIs to DisplayMarker

--- src/display-marker.coffee
+++ src/display-marker.coffee
@@ -13,6 +13,9 @@
     @bufferMarker.destroy()
     @destroyed = true
     @emitter.emit('did-destroy')
+
+  compare: (otherMarker) ->
+    @bufferMarker.compare(otherMarker.bufferMarker)

   onDidDestroy: (callback) ->
     @emitter.on('did-destroy', callback)
@@ -56,6 +59,12 @@
   setTailScreenPosition: (screenPosition, options) ->
     bufferPosition = @layer.translateScreenPosition(screenPosition, options)
     @bufferMarker.setTailPosition(bufferPosition, options)
+
+  getStartBufferPosition: ->
+    @bufferMarker.getStartPosition()
+
+  getEndBufferPosition: ->
+    @bufferMarker.getEndPosition()

   isValid: ->
     @bufferMarker.isValid()


improve indent line behaviour

improve indent line behaviour

--- lib/commands.coffee
+++ lib/commands.coffee
@@ -40,11 +40,21 @@

   indentListLine: ->
     editor = atom.workspace.getActiveTextEditor()
-    line = editor.lineTextForBufferRow(editor.getCursorBufferPosition().row)
-
-    inList = [LIST_TL_REGEX, LIST_UL_REGEX, LIST_OL_REGEX].some (regex) ->
-      regex.exec(line)
-    if inList then editor.indentSelectedRows() else editor.insertText(" ")
+    cursor = editor.getCursorBufferPosition()
+    line = editor.lineTextForBufferRow(cursor.row)
+
+    if @_isListLine(line)
+      editor.indentSelectedRows()
+    else if @_isAtLineBeginning(line, cursor.column)
+      editor.indent()
+    else
+      editor.insertText(" ") # convert tab to space
+
+  _isListLine: (line) ->
+    [LIST_TL_REGEX, LIST_UL_REGEX, LIST_OL_REGEX].some (rgx) -> rgx.exec(line)
+
+  _isAtLineBeginning: (line, col) ->
+    col == 0 || line.substring(0, col).trim() == ""

   jumpToPreviousHeading: ->
     editor = atom.workspace.getActiveTextEditor()


use component command instead of comp

use component command instead of comp

--- lib/commands.coffee
+++ lib/commands.coffee
@@ -16,7 +16,7 @@
 # global template path

 # Main mgen command options
-prog.version('0.0.6')
+prog.version('0.0.7')

 Package = require './package'
 Namespace = require './namespace'
@@ -24,7 +24,7 @@

 # Components Command
 # copies a Meteor component boilerplate folder and moves it into component directory.
-prog.command('comp <component_name>')
+prog.command('component <component_name>')
   .description('Create a component template, JS, and Sass files')

   .option("-d, --directory <path>", "Change the default component directory 'client/components'", "client/components/")
@@ -35,12 +35,11 @@

   .on('--help', ->
     puts '  Create a component boilerplate folder including a HTML template, JS, & Sass file.'
-    puts '  Plural item names will produce a parent & child component. e.g. posts with child post'
     puts "  If a component name has multiple words use 'snake_case'"
     puts ''
     puts '  Examples:'
-    puts '  $ mgen comp posts'
-    puts '  $ mgen comp widget_delux'
+    puts '  $ mgen component header'
+    puts '  $ mgen component stock_ticker'
     puts ''
   )


Add error callbacks to the fetch methods

--- lib/notification-element.coffee
+++ lib/notification-element.coffee
@@ -112,7 +112,7 @@
             issueButton.textContent = "View Issue"
             fatalNotification.textContent += " This issue has already been reported."
           else
-            issueButton.setAttribute('href', result.shortUrl)
+            issueButton.setAttribute('href', result.shortUrl) if result.shortUrl?
             fatalNotification.textContent += " You can help by creating an issue. Please explain what actions triggered this error."

         toolbar = document.createElement('div')
@@ -188,14 +188,18 @@
         if data.items?
           for issue in data.items
             return callback?(issue) if issue.title.indexOf(@getIssueTitle()) > -1
-        callback?(null)
+        callback(null)
+      error: ->
+        callback(null)

   getShortUrl: (callback) ->
     $.ajax 'http://git.io',
       type: 'POST'
       data: url: @getIssueUrl()
-      success: (data, status, xhr) =>
+      success: (data, status, xhr) ->
         callback(xhr.getResponseHeader('Location'))
+      error: ->
+        callback(null)

   getIssueUrl: ->
     repoUrl = @getRepoUrl()


Sim: Fix critical bug where whitespace was being incorrectly joined again.

--- server/index.coffee
+++ server/index.coffee
@@ -98,9 +98,9 @@
       return  if message.trim().length == 0
       return  if message.length > MAX_MESSAGE_LENGTH
       if message[0] == '/'
-        [ command, args... ] = message.split(/\s+/)
+        [ wholeMatch, command, args ] = message.match(/^(\S+)\s+(.*)$/)
         command = command.substr(1)
-        args = args.join(' ').split(/,/g)
+        args = args.split(',')
         commands.executeCommand(server, user, lobby, command, args...)
       else
         server.userMessage(lobby, user, message)


Alts: Fix typo.

Alts: Fix typo.

--- server/index.coffee
+++ server/index.coffee
@@ -195,11 +195,11 @@
     ##############

     'createAlt': (user, altName) ->
-      altname = altName?.trim()
+      altName = altName?.trim()
       if !alts.isAltNameValid(altName)
         user.error(errors.INVALID_ALT_NAME, "Invalid Alt Name")
         return
-      alts.createAlt user.id, altName.trim(), (err, success) ->
+      alts.createAlt user.id, altName, (err, success) ->
         user.error(errors.INVALID_ALT_NAME, err.message)  if err
         user.send('altCreated', altName)  if success


Fetcher: properly catch & propagate download errors.

--- lib/fetcher._coffee
+++ lib/fetcher._coffee
@@ -11,14 +11,14 @@
     source = content.url
     destination = path.join @path, content.id.toString()

-    writer = request(source).pipe fs.createWriteStream destination
-    writer.on 'readable', ->
-      console.log 'readable'
-    writer.on 'data', (chunk) ->
-      console.log 'data', chunk
+    reader = request source
+    reader.on 'error', (error) ->
+      callback error
+
+    writer = fs.createWriteStream destination
     writer.on 'error', (error) ->
       callback error
     writer.on 'finish', (error) ->
-      if error?
-        return callback error
-      callback null, destination
+      callback error, destination
+
+    reader.pipe writer


Agent pie: start/end angle pie transition work

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -211,7 +211,7 @@
             #console.log('1. in tween - d: ',d)
             #myInterpolater = d3.interpolate(window.oldPieData[myInstanceDataId][i] ? d, d)
             interpolateVal = (window.oldPieData[myInstanceDataId] ? 0)
-            if interpolateVal <> 0 and not window.oldPieData[myInstanceDataId].empty()
+            if interpolateVal != 0 and not window.oldPieData[myInstanceDataId].empty()
                 interpolateVal = window.oldPieData[myInstanceDataId][i]
             myInterpolater = d3.interpolate(interpolateVal, d)
             #console.log('myInterpolater(0): ',myInterpolater(0))


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -58,8 +58,11 @@
     if !sum
       arcs.append("svg:text")
         .attr('fill', "#fff")
-        .attr("text-anchor", "middle").text("No Results").attr('font-size', '28px')
+        .attr("text-anchor", "middle").text("No Results").attr('font-size', mFontSize + 'px')
     else
+      arcs.append("svg:text")
+        .attr('fill', "#fff")
+        .attr("text-anchor", "middle").text(sum).attr('font-size', mFontSize*2 + 'px')
       arcs.append("svg:text").attr("transform", (d, i) ->
         procent_val = Math.round(data[i].value/sum * 100)
         #d.innerRadius = (radiuso * (100-procent_val)/100) - radiuso/2 #45  #45=max text size/2


--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -2,6 +2,7 @@

     @accessor 'data'
     @instanceDataId = ''
+    @oldPieData = {}

     #constructor: ->
     ready: ->
@@ -20,7 +21,7 @@
         #$(@node).fadeOut().fadeIn()
         @container = $(@node).parent()
         #@instanceDataId = $(@node).attr('data-id')
-        @instanceDataId = @instanceDataId ? $(@node).attr('data-id')
+        #@instanceDataId = @instanceDataId ? $(@node).attr('data-id')
         console.log('onData -> @instanceDataId: ',@instanceDataId)

         #


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -321,45 +321,44 @@
             .x((d) -> return d.x)
             .y((d) -> return d.y)
             ([]) #create an empty quadtree to hold label positions
-        console.log("post-labelLayout")

         maxLabelWidth = 0
         maxLabelHeight = 0

-        labels.text((d) ->
+        #labels.text((d) ->
             # Set the text *first*, so we can query the size
             # of the label with .getBBox()
-            return d.value
-        )
-        .each((d, i) ->
+        #    return d.value
+        #)
+        #.each((d, i) ->
             # Move all calculations into the each function.
             # Position values are stored in the data object
             # so can be accessed later when drawing the line

             # calculate the position of the center marker
-            a = (d.startAngle + d.endAngle) / 2
+        #    a = (d.startAngle + d.endAngle) / 2

             #trig functions adjusted to use the angle relative
             #to the "12 o'clock" vector:
-            d.cx = Math.sin(a) * (radius - 75)
-            d.cy = -Math.cos(a) * (radius - 75)
+        #    d.cx = Math.sin(a) * (radius - 75)
+        #    d.cy = -Math.cos(a) * (radius - 75)

             # calculate the default position for the label,
             #   so that the middle of the label is centered in the arc
-            bbox = this.getBBox()
+        #    bbox = this.getBBox()
             #bbox.width and bbox.height will
             #describe the size of the label text
-            labelRadius = radius - 20
-            d.x =  Math.sin(a) * (labelRadius)
-            d.l = d.x - bbox.width / 2 - 2
-            d.r = d.x + bbox.width / 2 + 2
-            d.y = -Math.cos(a) * (radius - 20)
-            d.b = d.oy = d.y + 5
-            d.t = d.y - bbox.height - 5
+        #    labelRadius = radius - 20
+        #    d.x =  Math.sin(a) * (labelRadius)
+        #    d.l = d.x - bbox.width / 2 - 2
+        #    d.r = d.x + bbox.width / 2 + 2
+        #    d.y = -Math.cos(a) * (radius - 20)
+        #    d.b = d.oy = d.y + 5
+        #    d.t = d.y - bbox.height - 5

             # check whether the default position
             #   overlaps any other labels
-            conflicts = []
+        #    conflicts = []
             #labelLayout.visit( (node, x1, y1, x2, y2) ->
                 #recurse down the tree, adding any overlapping
                 #node is the node in the quadtree,
@@ -384,50 +383,51 @@
             #            conflicts.push(p) #add to conflict list
             #)

-            if conflicts.length
-                console.log(d, " conflicts with ", conflicts);
-                rightEdge = d3.max(conflicts, (d2) ->
-                    return d2.r
-                )
-
-                d.l = rightEdge
-                d.x = d.l + bbox.width / 2 + 5
-                d.r = d.l + bbox.width + 10
-
-            else console.log("no conflicts for ", d)
+        #    if conflicts.length
+        #        console.log(d, " conflicts with ", conflicts);
+        #        rightEdge = d3.max(conflicts, (d2) ->
+        #            return d2.r
+        #        )
+
+        #        d.l = rightEdge
+        #        d.x = d.l + bbox.width / 2 + 5
+        #        d.r = d.l + bbox.width + 10
+
+        #    else console.log("no conflicts for ", d)

             # add this label to the quadtree, so it will show up as a conflict
             #   for future labels.
             #labelLayout.add( d )
-            maxLabelWidth = Math.max(maxLabelWidth, bbox.width+10)
-            maxLabelHeight = Math.max(maxLabelHeight, bbox.height+10)
-        )
-        .transition() #we can use transitions now!
-        .attr("x", (d) ->
-                    return d.x
-                )
-                .attr("y", (d) ->
-                    return d.y
-                )
-
-
-        pointers = pointerGroup.selectAll("path.pointer")
-            .data(piedata)
-        pointers.enter()
-            .append("path")
-            .attr("class", "pointer")
-            .style("fill", "none")
-            .style("stroke", "black")
-            .attr("marker-end", "url(#circ)")
-        pointers.exit().remove()
-
-        pointers.transition().attr("d", (d) ->
-            if d.cx > d.l
-                return "M" + (d.l+2) + "," + d.b + "L" + (d.r-2) + "," + d.b + " " + d.cx + "," + d.cy
-            else
-                return "M" + (d.r-2) + "," + d.b + "L" + (d.l+2) + "," + d.b + " " + d.cx + "," + d.cy
-
-        )
+        #    maxLabelWidth = Math.max(maxLabelWidth, bbox.width+10)
+        #    maxLabelHeight = Math.max(maxLabelHeight, bbox.height+10)
+        #)
+        #.transition() #we can use transitions now!
+        #.attr("x", (d) ->
+        #            return d.x
+        #        )
+        #        .attr("y", (d) ->
+        #            return d.y
+        #        )
+
+
+        #pointers = pointerGroup.selectAll("path.pointer")
+        #    .data(piedata)
+        #pointers.enter()
+        #    .append("path")
+        #    .attr("class", "pointer")
+        #    .style("fill", "none")
+        #    .style("stroke", "black")
+        #    .attr("marker-end", "url(#circ)")
+        #pointers.exit().remove()
+
+        #pointers.transition().attr("d", (d) ->
+        #    if d.cx > d.l
+        #        return "M" + (d.l+2) + "," + d.b + "L" + (d.r-2) + "," + d.b + " " + d.cx + "," + d.cy
+        #    else
+        #        return "M" + (d.r-2) + "," + d.b + "L" + (d.l+2) + "," + d.b + " " + d.cx + "," + d.cy
+        #
+        #)
+
   renderX5: (data) ->
         #console.log("update pie", data);


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -347,7 +347,7 @@

             # add this label to the quadtree, so it will show up as a conflict
             #   for future labels.
-            labelLayout.add( d )
+            #labelLayout.add( d )
             maxLabelWidth = Math.max(maxLabelWidth, bbox.width+10)
             maxLabelHeight = Math.max(maxLabelHeight, bbox.height+10)
         )


--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -10,7 +10,7 @@
         #console.log('@instanceDataId',@instanceDataId)
         #@oldPieData[@instanceDataId] = ''
         window.oldPieData = window.oldPieData or {}
-        window.oldPieData[@instanceDataId] = window.oldPieData[@instanceDataId] or {}
+        window.oldPieData[@instanceDataId] = window.oldPieData[@instanceDataId] or []

     onData: (data) ->
         #console.log('testData',@testData)
@@ -397,6 +397,7 @@
         console.log('window.oldPieData BOTTOM',window.oldPieData)
         #window.oldPieData[@instanceDataId] = piedata
         window.oldPieData = window.oldPieData or {}
-        window.oldPieData[@instanceDataId] = window.oldPieData[@instanceDataId] or {}
+        window.oldPieData[@instanceDataId] = window.oldPieData[@instanceDataId] or []
+        window.oldPieData[@instanceDataId] = piedata
         console.log('piedata',piedata)
         return


Remove excess Meteor.userId() checkes, for better error messages

--- lib/messages.coffee
+++ lib/messages.coffee
@@ -611,7 +611,7 @@
   messageUpdate: (id, message) -> _messageUpdate id, message

   messageNew: (group, parent = null, position = null, message = {}) ->
-    check Meteor.userId(), String  ## should be done by 'canPost'
+    #check Meteor.userId(), String  ## should be done by 'canPost'
     check parent, String if parent?
     check position, Number if position?
     check group, String
@@ -683,7 +683,7 @@
     _messageParent child, parent, position

   messageEditStart: (id) ->
-    check Meteor.userId(), String
+    #check Meteor.userId(), String  ## should be done by 'canEdit'
     unless canEdit id
       throw new Meteor.Error 'messageEditStart.unauthorized',
         "Insufficient permissions to edit message '#{id}'"
@@ -716,7 +716,7 @@
     ## Meteor.user().username just committed this version.

   messageImport: (group, parent, message, diffs) ->
-    check Meteor.userId(), String  ## should be done by 'canImport'
+    #check Meteor.userId(), String  ## should be done by 'canImport'
     check group, String
     check parent, String if parent?
     check message,
@@ -780,6 +780,7 @@
     id

   messageSuperdelete: (message) ->
+    #check Meteor.userId(), String  ## should be done by 'canSuperdelete'
     check message, String
     unless canSuperdelete message
       throw new Meteor.Error 'messageSuperdelete.unauthorized',
@@ -833,6 +834,7 @@
   recomputeAuthors: ->
     ## Force recomputation of all `authors` fields to be the latest update
     ## for each updator.
+    #check Meteor.userId(), String  ## should be done by 'canSuper'
     unless canSuper wildGroup
       throw new Meteor.Error 'recomputeAuthors.unauthorized',
         "Insufficient permissions to recompute authors in group '#{wildGroup}'"
@@ -855,6 +857,7 @@
           updators: updators  ## last one

   recomputeRoots: ->
+    #check Meteor.userId(), String  ## should be done by 'canSuper'
     unless canSuper wildGroup
       throw new Meteor.Error 'recomputeRoots.unauthorized',
         "Insufficient permissions to recompute roots in group '#{wildGroup}'"


More efficient _submessageLastUpdate

More efficient _submessageLastUpdate

--- lib/messages.coffee
+++ lib/messages.coffee
@@ -57,6 +57,9 @@
     published: $ne: false    ## published is false or Date
     deleted: $ne: true
     private: $ne: true
+  ,
+    fields:
+      updated: true
   .forEach (submessage) ->
     updated = dateMax updated, submessage.updated
   updated


Tweak ShareJS internals to maybe Fix #49

--- lib/messages.coffee
+++ lib/messages.coffee
@@ -321,6 +321,7 @@
         old = Messages.findOne id
         return unless old?
         unless old.editing?.length
+          ShareJS.model.delete id
           ShareJS.initializeDoc id, old.body ? ''
           timer = null
           listener = Meteor.bindEnvironment (opData) ->


delete dead code

delete dead code

--- js/src/model/search/IndexedMetadata.coffee
+++ js/src/model/search/IndexedMetadata.coffee
@@ -1,27 +0,0 @@
-define 'kryptnostic.search.indexed-metadata', [
-  'require'
-  'lodash'
-  'kryptnostic.schema.indexed-metadata'
-  'kryptnostic.schema.validator'
-], (require) ->
-
-  _         = require 'lodash'
-  SCHEMA    = require 'kryptnostic.schema.indexed-metadata'
-  validator = require 'kryptnostic.schema.validator'
-
-  DEFAULT_OPTS = {}
-
-  #
-  # JSON request to for a piece of indexed metadata in Kryptnostic services.
-  # Author: rbuckheit
-  #
-  class IndexedMetadata
-
-    constructor : (opts) ->
-      _.extend(this, DEFAULT_OPTS, opts)
-      @validate()
-
-    validate : ->
-      validator.validate(this, IndexedMetadata, SCHEMA)
-
-  return IndexedMetadata


Need to require OS

Need to require OS

--- lib/grammar-utils/operating-system.coffee
+++ lib/grammar-utils/operating-system.coffee
@@ -1,3 +1,5 @@
+os = require 'os'
+
 # Public: GrammarUtils.OperatingSystem - a module which exposes different
 # platform related helper functions.
 module.exports =


hotfix: popover position

hotfix: popover position

--- app/assets/javascripts/admin/moi_tree/dialog.js.coffee
+++ app/assets/javascripts/admin/moi_tree/dialog.js.coffee
@@ -51,7 +51,7 @@

   positionPopover: ->
     $text = $(@text)
-    position = $text.position()
+    position = $text[0].getBoundingClientRect();
     left = position.left + 5 # 5 is just padding
     leftOffset = $text[0].getBBox().width # this gets the width of svg
     leftFinal = left + (leftOffset * currentTreeZoom)


Refactor admin/user links behavior

Refactor admin/user links behavior

--- app/assets/javascripts/kiitos/user_dashboard.js.coffee
+++ app/assets/javascripts/kiitos/user_dashboard.js.coffee
@@ -43,20 +43,12 @@
     enableButton()
     count()

-  manageOptions = (admin=false) ->
-    if admin is false
-      $('#admin-options, #admin-manage-area').hide()
-      $('#user-option').addClass('active')
-      $('#admin-option').removeClass('active')
-      $('#new-kiito').show()
-    else
-      $('#admin-options, #admin-manage-area').show()
-      $('#new-kiito').hide()
-      $('#user-option').removeClass('active')
-      $('#admin-option').addClass('active')
+  manageOptions = (el) ->
+    unless $(el).hasClass('active')
+      $('#admin-menu li').removeClass('active')
+      $(el).toggleClass('active')
+      $('#admin-options, #admin-manage-area').toggle()
+      $('#new-kiito').toggle()

-  $('#admin-option').click ->
-    manageOptions true
-
-  $('#user-option').click ->
-    manageOptions()
+  $('#admin-menu li').click (e)->
+    manageOptions e.currentTarget


increased wait timeouts #1266

increased wait timeouts #1266

--- app/assets/javascripts/test/pages/DashboardPage.coffee
+++ app/assets/javascripts/test/pages/DashboardPage.coffee
@@ -21,13 +21,13 @@
   openExplorativeTab : ->

     @waitForElement(explorativeTab).click()
-    browser.waitForVisible(explorativeTaskList)
+    browser.waitForVisible(explorativeTaskList, 1000)

   openTasksTab : ->

     @waitForElement(tasksTab).click()
-    browser.waitForVisible(newTaskButton)
+    browser.waitForVisible(newTaskButton, 1000)

   clickGetTaskButton : ->


Get trigger location from Parse GeoPoint and populate form

--- app/client/survey_admin/survey_admin_forms_edit.coffee
+++ app/client/survey_admin/survey_admin_forms_edit.coffee
@@ -370,7 +370,7 @@
           if type == 'datetime'
             _datetimeTrigger.data('DateTimePicker').date new Date triggerProps.datetime
           else
-            coordinates = triggerProps.loc.coordinates
-            latLng = L.latLng coordinates[1], coordinates[0]
+            location = @trigger.get 'location'
+            latLng = L.latLng location.latitude, location.longitude
             addMarker latLng, true
             resizeMap()


only publish user information to logged-in users

--- model.coffee
+++ model.coffee
@@ -256,9 +256,10 @@
     found

   Meteor.publish "userData", ->
-    Meteor.users.find {},
-      fields:
-        username: 1
+    if @userId
+      Meteor.users.find {},
+        fields:
+          username: 1

   # ----------------------------------------


IDE: Collaboration: Change event name.

IDE: Collaboration: Change event name.

--- client/activity/lib/views/privatemessage/participantheads.coffee
+++ client/activity/lib/views/privatemessage/participantheads.coffee
@@ -45,7 +45,7 @@
     @addSubView @previewContainer
     @addSubView @actionsContainer

-    @on 'ShowNewParticipantView', @bound 'onNewParticipantClick'
+    @on 'ShowParticipantAutocomplete', @bound 'onNewParticipantClick'

   ###*


home/stacks: fix disabled user stack title

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

--- client/home/lib/stacks/components/stacktemplateitem/index.coffee
+++ client/home/lib/stacks/components/stacktemplateitem/index.coffee
@@ -129,7 +129,13 @@
     { template, stack, onOpen } = @props

     if stack?.getOldOwner()
-      return <DisabledStack stack={stack} onOpen={onOpen} />
+      return (
+        <DisabledStack
+          template={template}
+          stack={stack}
+          onOpen={onOpen}
+        />
+      )

     if not template
       return null
@@ -158,12 +164,12 @@

-DisabledStack = ({ stack, onOpen }) ->
+DisabledStack = ({ template, stack, onOpen }) ->

+      children={makeTitle({ stack, template })} />

       Last Updated <TimeAgo from={stack.meta.modifiedAt} />


Fix keys test after API update

Fix keys test after API update

Connects-To: #356
Change-Type: patch

--- tests/integration/models/keys.spec.coffee
+++ tests/integration/models/keys.spec.coffee
@@ -76,9 +76,7 @@

 			it 'should be rejected if the key id is invalid', ->
 				promise = resin.models.key.get(99999999999)
-				m.chai.expect(promise).to.be.rejectedWith('Request error: Internal Server Error')
-				# TODO: used to work before, https://www.flowdock.com/app/rulemotion/platform/threads/v_RHSp7A6eGwvb4l4G6Ro2dd4Ss
-				# m.chai.expect(promise).to.be.rejectedWith('Key not found: 99999999999')
+				m.chai.expect(promise).to.be.rejectedWith('Request error')

 		describe 'resin.models.key.remove()', ->


environments: display "coming soon" notification when "usage" link is clicked

--- client/Environments/views/environmentsmainscene.coffee
+++ client/Environments/views/environmentsmainscene.coffee
@@ -19,9 +19,13 @@
       cssClass : "top-warning"
       partial  : """

-          You are on a free developer plan, see your usage or upgrade.
+          You are on a free developer plan, see your usage or upgrade.

       """
+      click: (event) ->
+        if "usage" in event.target.classList
+          KD.utils.stopDOMEvent event
+          new KDNotificationView title: "Coming soon..."

     @paymentController = KD.getSingleton("paymentController")
     @paymentController.fetchActiveSubscription tags: "vm", (err, subscription) =>


GroupGeneralSettingsView: Changes for new design.

--- client/admin/lib/views/groupgeneralsettingsview.coffee
+++ client/admin/lib/views/groupgeneralsettingsview.coffee
@@ -13,45 +13,26 @@
 showError          = require 'app/util/showError'
 Encoder            = require 'htmlencode'

-createSection = ({name, title, description, expanded}) ->
-
-  expanded ?= no
+
+createSection = (options = {}) ->
+
+  { name, title, description } = options

   section = new KDCustomHTMLView
     tagName  : 'section'
-    cssClass : if expanded then 'AppModal-section' else 'AppModal-section is-collapsed'
-
-  section.addSubView new KDCustomHTMLView
-    tagName  : 'header'
-    cssClass : 'AppModal-sectionHeader'
-    partial  : title
+    cssClass : kd.utils.curry 'AppModal-section', name

   section.addSubView desc = new KDCustomHTMLView
     tagName  : 'p'
     cssClass : 'AppModal-sectionDescription'
     partial  : description

-  desc.addSubView new KDButtonView
-    cssClass     : "AppModal-sectionToggle solid small #{if expanded then 'gray' else 'green'}"
-    title        : if expanded then 'collapse' else 'expand'
-    callback     : ->
-      if section.hasClass 'is-collapsed'
-        section.unsetClass 'is-collapsed'
-        @unsetClass 'green'
-        @setClass 'gray'
-        @setTitle 'collapse'
-      else
-        section.setClass 'is-collapsed'
-        @setClass 'green'
-        @unsetClass 'gray'
-        @setTitle 'expand'
-
   return section

 addInput = (form, options) ->

-  { name, label, description, itemClass } = options
+  { name, label, description, itemClass, nextElement } = options

   itemClass  or= KDInputView
   form.inputs ?= {}
@@ -68,6 +49,8 @@
   field.addSubView form.inputs[name] = input = new itemClass options
   field.addSubView new KDCustomHTMLView tagName : 'p', partial : description  if description

+  field.addSubView nextElement  if nextElement and nextElement instanceof KDView
+
   return input

 addButton = (form, options) ->
@@ -82,8 +65,6 @@
   }

-
-
 module.exports = class GroupGeneralSettingsView extends KDView

   constructor: (options = {}, data) ->
@@ -94,22 +75,17 @@

     @forms = {}

-    @createNameURLForm()
-    @createChannelsForm()
+    @createGeneralSettingsForm()
     @createIconForm()
     @createDeletionForm()

-  createNameURLForm: ->
+  createGeneralSettingsForm: ->

     group = @getData()
     url   = if group.slug is 'koding' then '' else "#{group.slug}."

-    @addSubView section = createSection
-      name        : 'name-url'
-      title       : 'CHANGE TEAM NAME & URL'
-      description : "Your team name is #{group.title} and your URL is https://#{url}koding.com."
-      expanded    : yes
+    @addSubView section = createSection name: 'general-settings'

     section.addSubView form = new KDFormView #callback : @bound 'saveSettings'

@@ -117,38 +93,27 @@
       label        : 'Name'
       description  : 'Your team name is displayed in menus and emails. It usually is (or includes) the name of your company.'
       name         : 'title'
-      defaultValue : Encoder.htmlDecode group.title ? ""
+      defaultValue : Encoder.htmlDecode group.title ? ''
       placeholder  : 'Please enter a title here'

     addInput form,
       label        : 'URL'
       description  : 'Your team URL can only contain lowercase letters, numbers and dashes (and must start with a letter or number). We don\'t recommend changing this.'
       name         : 'url'
-      defaultValue : Encoder.htmlDecode group.title ? ""
+      defaultValue : Encoder.htmlDecode group.title ? ''
       placeholder  : 'Please enter a title here'

-    addButton form,
-      title    : 'Save Name & URL'
-      callback : -> console.log 'lolooloo'
-
-
-  createChannelsForm: ->
-
-    @addSubView section = createSection
-      name        : 'channels'
-      title       : 'DEFAULT CHANNELS'
-      description : 'Your new members will automatically join to #general channel. Here you can specify more channels for new team members to join automatically.'
-
-    section.addSubView form = new KDFormView
-
-    addInput form,
-      label        : 'Channels'
-      description  : 'Please add channel names separated by commas.'
+    addInput form,
+      label        : 'Default Channels'
+      description  : 'Your new members will automatically join to #general channel. Here you can specify more channels for new team members to join automatically.'
       name         : 'channels'
       placeholder  : 'product, design, ux, random etc'
+      nextElement  : new KDCustomHTMLView
+        tagName    : 'span'
+        partial    : 'Please add channel names separated by commas.'

     addButton form,
-      title    : 'Save Default Channels'
+      title    : 'Save Changes'
       callback : -> console.log 'lolooloo'

@@ -159,7 +124,6 @@

     @addSubView section = createSection
       name        : 'deletion'
-      title       : 'DELETE TEAM'
       description : 'If Koding is no use to your team anymore, you can delete your team page here.'

     section.addSubView form = new KDFormView


AdminMembersView: List admins and mods.

AdminMembersView: List admins and mods.

--- client/admin/lib/views/members/adminmembersview.coffee
+++ client/admin/lib/views/members/adminmembersview.coffee
@@ -2,7 +2,7 @@
 KDView                = kd.View
 KDTabView             = kd.TabView
 KDTabPaneView         = kd.TabPaneView
-TeamMembersView       = require './teammembersview.coffee'
+TeamMembersCommonView = require './teammemberscommonview.coffee'

 module.exports = class AdminMembersView extends KDView
@@ -26,10 +26,10 @@
     tabView.addPane moderatorsPane     = new KDTabPaneView name: 'Moderators'
     tabView.addPane blockedMembersPane = new KDTabPaneView name: 'Blocked'

-    allMembersPane.addSubView     new TeamMembersView {}, data
-    adminsPane.addSubView         new TeamMembersView {}, data
-    moderatorsPane.addSubView     new TeamMembersView {}, data
-    blockedMembersPane.addSubView new TeamMembersView {}, data
+    allMembersPane.addSubView  new TeamMembersCommonView { fetcherMethod: 'fetchMembers'    }, data
+    adminsPane.addSubView      new TeamMembersCommonView { fetcherMethod: 'fetchAdmins'     }, data
+    moderatorsPane.addSubView  new TeamMembersCommonView { fetcherMethod: 'fetchModerators' }, data
+    # blockedMembersPane.addSubView new TeamMembersView {}, data

     tabView.showPaneByIndex 0
     @addSubView tabView


Nginx: disable access log for health check URI

--- deployment/nginx.coffee
+++ deployment/nginx.coffee
@@ -174,7 +174,7 @@
       index index.html index.htm;
       location = /healthcheck {
         return 200;
-        #access_log off;
+        access_log off;
       }

       #{createStubLocation(environment)}


deployment: add extensive caching to assets

--- deployment/nginx.coffee
+++ deployment/nginx.coffee
@@ -186,6 +186,16 @@
       }
       """

+createAsset = (inDevEnvironment) ->
+  return "" if inDevEnvironment
+  return """
+expires 1M; # 1 month
+\t\tadd_header Cache-Control \"public\";
+\t\tlocation ~* \.(map)$ {
+\t\t  return 404;
+\t\t  access_log off;
+\t\t}"""
+
 module.exports.create = (KONFIG, environment)->
   workers = KONFIG.workers

@@ -265,6 +275,10 @@
     #{createHttpsRedirector(KONFIG)}

     #{createHealthcheck(KONFIG)}
+
+    # this keys are used for internal caching purposes.
+    proxy_cache_path /tmp/nginx levels=1:2 keys_zone=asset_cache:10m max_size=10g inactive=60m;
+    proxy_cache_key "$scheme$request_method$host$request_uri";

     # start server
     server {
@@ -296,10 +310,29 @@
       }

       location @assets {
+        proxy_cache asset_cache;
+        proxy_cache_revalidate on;
+        proxy_cache_lock on;
+        proxy_cache_valid  200 302  60m;
+        proxy_cache_valid  404	  1m;
+        proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
+
         proxy_pass            http://s3.amazonaws.com/koding-assets$uri;
         proxy_set_header      X-Real-IP       $remote_addr;
         proxy_set_header      X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_connect_timeout 1;
+
+        # unset headers
+        proxy_hide_header x-amz-id-2;
+        proxy_hide_header x-amz-request-id;
+        proxy_hide_header x-amz-expiration;
+        proxy_hide_header x-amz-meta-s3cmd-attrs;
+
+        # ignore possible future headers that might break caching
+        proxy_ignore_headers X-Accel-Expires Expires Cache-Control;
+
+        # finally add status
+        add_header X-K-Proxy-Cache $upstream_cache_status;

         resolver 8.8.8.8;
       }
@@ -307,13 +340,7 @@
       # no need to send static file serving requests to webserver
       # serve static content from nginx
       location /a/ {
-
-        #{if inDevEnvironment then '' else "
-          location ~* \.(map)$ {
-            return 404;
-            access_log off;
-          }"}
-
+        #{createAsset(inDevEnvironment)}
         root #{KONFIG.projectRoot}/website/;
         try_files $uri @assets;
         # no need to send those requests to nginx access_log


[client][app][Main][tab] updated routes as removed leading slashes

--- client/landing/app/MainApp/maintabs/maintabview.coffee
+++ client/landing/app/MainApp/maintabs/maintabview.coffee
@@ -48,13 +48,13 @@
     appView     = pane.getMainView()
     appInstance = appManager.getByView appView
     options     = appInstance.getOptions()
-    @getSingleton('router').handleRoute "/#{options.route}"
+    @getSingleton('router').handleRoute "#{options.route}"

     if $(event.target).hasClass "close-tab"
       pane.mainView.destroy()
       return no

-    @showPane pane  if options.route is 'Develop'
+    @showPane pane  if options.route is '/Develop'

   showHandleContainer:()->
     @tabHandleContainer.$().css top : -25