change to 2-space-indent

change to 2-space-indent

--- src/factory.coffee
+++ src/factory.coffee
@@ -13,257 +13,256 @@
 # inject [container1, container2], (id1, id2) ->

 module.exports.inject = (
-    arrayify
-    getIdsToInject
-    _inject
-) ->
-    ->
-        len = arguments.length
-        unless (len is 2) or (len is 3)
-            throw new Error "2 or 3 arguments required but #{len} were given"
-
-        containers = arrayify arguments[0]
-
-        if containers.length is 0
-            throw new Error 'at least 1 container is required'
-
-        cb = if len is 2 then arguments[1] else arguments[2]
-
-        unless 'function' is typeof cb
-            throw new Error 'cb must be a function'
-
-        dependencyIds = if len is 2 then getIdsToInject cb else arguments[1]
-
-        _inject containers, dependencyIds, cb
+  arrayify
+  getIdsToInject
+  _inject
+) ->
+  ->
+    len = arguments.length
+    unless (len is 2) or (len is 3)
+      throw new Error "2 or 3 arguments required but #{len} were given"
+
+    containers = arrayify arguments[0]
+
+    if containers.length is 0
+      throw new Error 'at least 1 container is required'
+
+    cb = if len is 2 then arguments[1] else arguments[2]
+
+    unless 'function' is typeof cb
+      throw new Error 'cb must be a function'
+
+    dependencyIds = if len is 2 then getIdsToInject cb else arguments[1]
+
+    _inject containers, dependencyIds, cb

 # where it goes from promise land to callback land...

 # TODO rename
 module.exports._inject = (
-    getOrCreateManyInstances
-) ->
-    (containers, ids, cb) ->
-        promise = getOrCreateManyInstances containers, ids
-        promise
-            .then (instances) ->
-                process.nextTick ->
-                    cb.apply null, instances
-            .catch (error) ->
-                error.event = 'error'
-                unless error.container?
-                    throw new Error "error has no container property. there is probably a bug in hinoki itself. original error: #{error}"
-                error.container.emit error.container, error
+  getOrCreateManyInstances
+) ->
+  (containers, ids, cb) ->
+    promise = getOrCreateManyInstances containers, ids
+    promise
+      .then (instances) ->
+        process.nextTick ->
+          cb.apply null, instances
+      .catch (error) ->
+        error.event = 'error'
+        unless error.container?
+          throw new Error "error has no container property. there is probably a bug in hinoki itself. original error: #{error}"
+        error.container.emit error.container, error

 ###################################################################################
 # functions that do the heavy lifting

 module.exports.getOrCreateManyInstances = (
-    getOrCreateInstance
-    Promise
-) ->
-    (containers, ids) ->
-        Promise.all(ids).map (id) ->
-            getOrCreateInstance containers, id
+  getOrCreateInstance
+  Promise
+) ->
+  (containers, ids) ->
+    Promise.all(ids).map (id) ->
+      getOrCreateInstance containers, id

 module.exports.getOrCreateInstance = (
-    Promise
-    getIdsToInject
-    addToId
-    getOrCreateManyInstances
-    resolveInstanceInContainers
-    isCyclic
-    isUndefined
-    cycleRejection
-    resolveFactoryInContainers
-    unresolvableFactoryRejection
-    startingWith
-    createInstance
-    getKey
-    callFactory
-    factoryReturnedUndefinedRejection
-) ->
-    (containers, id) ->
-        # resolveInstanceInContainers has the opportunity
-        # to return an error through a rejected promise that
-        # is returned by getOrCreateManyInstances unchanged
-        instanceResultPromise = resolveInstanceInContainers containers, id
-
-        if instanceResultPromise?
-            return instanceResultPromise.then (instanceResult) ->
-                instanceResult.container.emit instanceResult.container,
-                    event: 'instanceResolved'
-                    id: id
-                    instance: instanceResult.instance
-                    resolver: instanceResult.resolver
-                    container: instanceResult.container
-                instanceResult.instance
-
-        # no instance available. we need a factory.
-        # let's check for cycles first since
-        # we can't use a factory if the id contains a cycle.
-
-        if isCyclic id
-            return cycleRejection
-                container: containers[0]
-                id: id
-
-        # no cycle - yeah!
-        # lets find the container that can give us a factory
-
-        # resolveFactoryInContainers has the opportunity
-        # to return an error through a rejected promise that
-        # is returned by getOrCreateManyInstances unchanged
-        factoryResultPromise = resolveFactoryInContainers containers, id
-
-        unless factoryResultPromise?
-            # we are out of luck: the factory could not be found
-            return unresolvableFactoryRejection
-                container: containers[0]
-                id: id
-
-        # we've got a factory
-
-        factoryResultPromise.then (factoryResult) ->
-
-            {factory, resolver, container} = factoryResult
-
-            container.emit container,
-                event: 'factoryFound'
-                id: id
-                factory: factory
-                resolver: resolver
-                container: container
-
-            # if the instance is already being constructed
-            # wait for that instead of starting a second construction.
-            # a factory must only be called exactly once per container.
-
-            underConstruction = container.getUnderConstruction container, id
-
-            if underConstruction?
-                container.emit container,
-                    event: 'instanceUnderConstruction'
-                    id: id
-                    value: underConstruction
-                    container: container
-                return underConstruction
-
-            # there is no instance under construction. lets make one
-
-            # lets resolve the dependencies of the factory
-
-            remainingContainers = startingWith containers, container
-
-            dependencyIds = getIdsToInject factory
-
-            dependencyIds = dependencyIds.map (x) ->
-                addToId id, x
-
-            dependencyPromises = getOrCreateManyInstances remainingContainers, dependencyIds
-
-            key = getKey id
-
-            container.setUnderConstruction container, key, instancePromise
-
-            instancePromise = Promise.resolve(dependencyPromises).then (dependencyInstances) ->
-
-                # the dependencies are ready
-                # and we can finally call the factory
-
-                callFactory container, id, factory, dependencyInstances
-
-            instancePromise.then (value) ->
-                if isUndefined value
-                    return factoryReturnedUndefinedRejection
-                        container: container
-                        id: id
-                        factory: factory
-                # instance is fully constructed
-                container.setInstance container, key, value
-                container.unsetUnderConstruction container, key
-                value
+  Promise
+  getIdsToInject
+  addToId
+  getOrCreateManyInstances
+  resolveInstanceInContainers
+  isCyclic
+  isUndefined
+  cycleRejection
+  resolveFactoryInContainers
+  unresolvableFactoryRejection
+  startingWith
+  createInstance
+  getKey
+  callFactory
+  factoryReturnedUndefinedRejection
+) ->
+  (containers, id) ->
+    # resolveInstanceInContainers has the opportunity
+    # to return an error through a rejected promise that
+    # is returned by getOrCreateManyInstances unchanged
+    instanceResultPromise = resolveInstanceInContainers containers, id
+
+    if instanceResultPromise?
+      return instanceResultPromise.then (instanceResult) ->
+        instanceResult.container.emit instanceResult.container,
+          event: 'instanceResolved'
+          id: id
+          instance: instanceResult.instance
+          resolver: instanceResult.resolver
+          container: instanceResult.container
+        instanceResult.instance
+
+    # no instance available. we need a factory.
+    # let's check for cycles first since
+    # we can't use a factory if the id contains a cycle.
+
+    if isCyclic id
+      return cycleRejection
+        container: containers[0]
+        id: id
+
+    # no cycle - yeah!
+    # lets find the container that can give us a factory
+
+    # resolveFactoryInContainers has the opportunity
+    # to return an error through a rejected promise that
+    # is returned by getOrCreateManyInstances unchanged
+    factoryResultPromise = resolveFactoryInContainers containers, id
+
+    unless factoryResultPromise?
+      # we are out of luck: the factory could not be found
+      return unresolvableFactoryRejection
+        container: containers[0]
+        id: id
+
+    # we've got a factory
+
+    factoryResultPromise.then (factoryResult) ->
+      {factory, resolver, container} = factoryResult
+
+      container.emit container,
+        event: 'factoryFound'
+        id: id
+        factory: factory
+        resolver: resolver
+        container: container
+
+      # if the instance is already being constructed
+      # wait for that instead of starting a second construction.
+      # a factory must only be called exactly once per container.
+
+      underConstruction = container.getUnderConstruction container, id
+
+      if underConstruction?
+        container.emit container,
+          event: 'instanceUnderConstruction'
+          id: id
+          value: underConstruction
+          container: container
+        return underConstruction
+
+      # there is no instance under construction. lets make one
+
+      # lets resolve the dependencies of the factory
+
+      remainingContainers = startingWith containers, container
+
+      dependencyIds = getIdsToInject factory
+
+      dependencyIds = dependencyIds.map (x) ->
+        addToId id, x
+
+      dependencyPromises = getOrCreateManyInstances remainingContainers, dependencyIds
+
+      key = getKey id
+
+      container.setUnderConstruction container, key, instancePromise
+
+      instancePromise = Promise.resolve(dependencyPromises).then (dependencyInstances) ->
+
+        # the dependencies are ready
+        # and we can finally call the factory
+
+        callFactory container, id, factory, dependencyInstances
+
+      instancePromise.then (value) ->
+        if isUndefined value
+          return factoryReturnedUndefinedRejection
+            container: container
+            id: id
+            factory: factory
+        # instance is fully constructed
+        container.setInstance container, key, value
+        container.unsetUnderConstruction container, key
+        value

 module.exports.callFactory = (
-    Promise
-    isThenable
-    exceptionRejection
-    rejectionRejection
-
-) ->
-    (container, id, factory, dependencyInstances) ->
-        try
-            instanceOrPromise = factory.apply null, dependencyInstances
-        catch err
-            return exceptionRejection
-                exception: err
-                id: id
-                container: container
-
-        unless isThenable instanceOrPromise
-            # instanceOrPromise is not a promise but an instance
-            container.emit container,
-                event: 'instanceCreated',
-                id: id
-                instance: instanceOrPromise
-                factory: factory
-                container: container
-            return Promise.resolve instanceOrPromise
-
-        # instanceOrPromise is a promise
-
+  Promise
+  isThenable
+  exceptionRejection
+  rejectionRejection
+
+) ->
+  (container, id, factory, dependencyInstances) ->
+    try
+      instanceOrPromise = factory.apply null, dependencyInstances
+    catch err
+      return exceptionRejection
+        exception: err
+        id: id
+        container: container
+
+    unless isThenable instanceOrPromise
+      # instanceOrPromise is not a promise but an instance
+      container.emit container,
+        event: 'instanceCreated',
+        id: id
+        instance: instanceOrPromise
+        factory: factory
+        container: container
+      return Promise.resolve instanceOrPromise
+
+    # instanceOrPromise is a promise
+
+    container.emit container,
+      event: 'promiseCreated'
+      id: id
+      promise: instanceOrPromise
+      container: container
+      factory: factory
+
+    return instanceOrPromise
+      .then (value) ->
         container.emit container,
-            event: 'promiseCreated'
-            id: id
-            promise: instanceOrPromise
-            container: container
-            factory: factory
-
-        return instanceOrPromise
-            .then (value) ->
-                container.emit container,
-                    event: 'promiseResolved'
-                    id: id
-                    value: value
-                    container: container
-                    factory: factory
-                return value
-            .catch (rejection) ->
-                rejectionRejection
-                    container: container
-                    id: id
-                    rejection: rejection
+          event: 'promiseResolved'
+          id: id
+          value: value
+          container: container
+          factory: factory
+        return value
+      .catch (rejection) ->
+        rejectionRejection
+          container: container
+          id: id
+          rejection: rejection

 ###################################################################################
 # path manipulation

 module.exports.getKey = ->
-    (id) ->
-        if Array.isArray id then id[0] else id
+  (id) ->
+    if Array.isArray id then id[0] else id

 module.exports.getKeys = (
-    arrayify
-) ->
-    (id) ->
-        arrayify id
+  arrayify
+) ->
+  (id) ->
+    arrayify id

 module.exports.idToString = (
-    getKeys
-) ->
-    (id) ->
-        getKeys(id).join ' <- '
+  getKeys
+) ->
+  (id) ->
+    getKeys(id).join ' <- '

 module.exports.addToId = (
-    arrayify
-) ->
-    (id, key) ->
-        [key].concat arrayify id
+  arrayify
+) ->
+  (id, key) ->
+    [key].concat arrayify id

 module.exports.isCyclic = (
-    arrayOfStringsHasDuplicates
-    getKeys
-) ->
-    (id) ->
-        arrayOfStringsHasDuplicates getKeys id
+  arrayOfStringsHasDuplicates
+  getKeys
+) ->
+  (id) ->
+    arrayOfStringsHasDuplicates getKeys id

 ###################################################################################
 # functions that resolve factories
@@ -271,47 +270,47 @@
 # returns either null or a promise that resolves to {resolver: , factory: }

 module.exports.resolveFactoryInContainer = (
-    Promise
-    factoryNotFunctionRejection
-    some
-    getKey
-) ->
-    (container, id) ->
-        some container.factoryResolvers, (resolver) ->
-            factory = resolver container, getKey id
-
-            # this resolver can't resolve the factory
-            unless factory?
-                return
-
-            unless 'function' is typeof factory
-                # we are out of luck: the resolver didnt return a function
-                return factoryNotFunctionRejection
-                    container: container
-                    id: id
-                    factory: factory
-                    resolver: resolver
-
-            Promise.resolve {
-                resolver: resolver
-                factory: factory
-            }
+  Promise
+  factoryNotFunctionRejection
+  some
+  getKey
+) ->
+  (container, id) ->
+    some container.factoryResolvers, (resolver) ->
+      factory = resolver container, getKey id
+
+      # this resolver can't resolve the factory
+      unless factory?
+        return
+
+      unless 'function' is typeof factory
+        # we are out of luck: the resolver didnt return a function
+        return factoryNotFunctionRejection
+          container: container
+          id: id
+          factory: factory
+          resolver: resolver
+
+      Promise.resolve
+        resolver: resolver
+        factory: factory
+
 # returns either null or a promise that resolves to {container: , resolver: , factory: }

 module.exports.resolveFactoryInContainers = (
-    some
-    resolveFactoryInContainer
-) ->
-    (containers, id) ->
-        some containers, (container) ->
-            promise = resolveFactoryInContainer container, id
-
-            unless promise?
-                return
-
-            promise.then (result) ->
-                result.container = container
-                result
+  some
+  resolveFactoryInContainer
+) ->
+  (containers, id) ->
+    some containers, (container) ->
+      promise = resolveFactoryInContainer container, id
+
+      unless promise?
+        return
+
+      promise.then (result) ->
+        result.container = container
+        result

 ###################################################################################
 # functions that resolve instances
@@ -319,193 +318,190 @@
 # returns either null or a promise that resolves to {resolver: , instance: }

 module.exports.resolveInstanceInContainer = (
-    Promise
-    some
-    getKey
-) ->
-    (container, id) ->
-        some container.instanceResolvers, (resolver) ->
-            instance = resolver container, getKey id
-
-            unless instance?
-                return
-
-            Promise.resolve {
-                resolver: resolver
-                instance: instance
-            }
+  Promise
+  some
+  getKey
+) ->
+  (container, id) ->
+    some container.instanceResolvers, (resolver) ->
+      instance = resolver container, getKey id
+
+      unless instance?
+        return
+
+      Promise.resolve
+        resolver: resolver
+        instance: instance

 # returns either null or a promise that resolves to {container: , resolver: , instance: }

 module.exports.resolveInstanceInContainers = (
-    some
-    resolveInstanceInContainer
-) ->
-    (containers, id) ->
-        some containers, (container) ->
-            promise = resolveInstanceInContainer container, id
-
-            unless promise?
-                return
-
-            promise.then (result) ->
-                result.container = container
-                result
+  some
+  resolveInstanceInContainer
+) ->
+  (containers, id) ->
+    some containers, (container) ->
+      promise = resolveInstanceInContainer container, id
+
+      unless promise?
+        return
+
+      promise.then (result) ->
+        result.container = container
+        result

 ###################################################################################
 # util

 module.exports.getIdsToInject = (
-    parseFunctionArguments
-) ->
-    (factory) ->
-        if factory.$inject?
-            factory.$inject
-        else
-            parseFunctionArguments factory
+  parseFunctionArguments
+) ->
+  (factory) ->
+    if factory.$inject?
+      factory.$inject
+    else
+      parseFunctionArguments factory

 ###################################################################################
 # error

 module.exports.cycleRejection = (
-    Promise
-    idToString
-) ->
-    (params) ->
-        Promise.reject
-            error: "circular dependency #{idToString(params.id)}"
-            type: 'cycle'
-            id: params.id
-            container: params.container
+  Promise
+  idToString
+) ->
+  (params) ->
+    Promise.reject
+      error: "circular dependency #{idToString(params.id)}"
+      type: 'cycle'
+      id: params.id
+      container: params.container

 module.exports.unresolvableFactoryRejection = (
-    Promise
-    getKey
-    idToString
-) ->
-    (params) ->
-        Promise.reject
-            error: "unresolvable factory '#{getKey(params.id)}' (#{idToString(params.id)})"
-            type: 'unresolvableFactory'
-            id: params.id
-            container: params.container
+  Promise
+  getKey
+  idToString
+) ->
+  (params) ->
+    Promise.reject
+      error: "unresolvable factory '#{getKey(params.id)}' (#{idToString(params.id)})"
+      type: 'unresolvableFactory'
+      id: params.id
+      container: params.container

 module.exports.exceptionRejection = (
-    Promise
-    getKey
-) ->
-    (params) ->
-        Promise.reject
-            error: "exception in factory '#{getKey(params.id)}': #{params.exception}"
-            type: 'exception'
-            id: params.id
-            exception: params.exception
-            container: params.container
+  Promise
+  getKey
+) ->
+  (params) ->
+    Promise.reject
+      error: "exception in factory '#{getKey(params.id)}': #{params.exception}"
+      type: 'exception'
+      id: params.id
+      exception: params.exception
+      container: params.container

 module.exports.rejectionRejection = (
-    Promise
-    getKey
-) ->
-    (params) ->
-        Promise.reject
-            error: "promise returned from factory '#{getKey(params.id)}' was rejected with reason: #{params.rejection}"
-            type: 'rejection'
-            id: params.id
-            rejection: params.rejection
-            container: params.container
+  Promise
+  getKey
+) ->
+  (params) ->
+    Promise.reject
+      error: "promise returned from factory '#{getKey(params.id)}' was rejected with reason: #{params.rejection}"
+      type: 'rejection'
+      id: params.id
+      rejection: params.rejection
+      container: params.container

 module.exports.factoryNotFunctionRejection = (
-    Promise
-    getKey
-) ->
-    (params) ->
-        Promise.reject
-            error: "factory '#{getKey(params.id)}' is not a function: #{params.factory}"
-            type: 'factoryNotFunction'
-            id: params.id
-            factory: params.factory
-            container: params.container
+  Promise
+  getKey
+) ->
+  (params) ->
+    Promise.reject
+      error: "factory '#{getKey(params.id)}' is not a function: #{params.factory}"
+      type: 'factoryNotFunction'
+      id: params.id
+      factory: params.factory
+      container: params.container

 module.exports.factoryReturnedUndefinedRejection = (
-    Promise
-    getKey
-) ->
-    (params) ->
-        Promise.reject
-            error: "factory '#{getKey(params.id)}' returned undefined"
-            type: 'factoryReturnedUndefined'
-            id: params.id
-            factory: params.factory
-            container: params.container
+  Promise
+  getKey
+) ->
+  (params) ->
+    Promise.reject
+      error: "factory '#{getKey(params.id)}' returned undefined"
+      type: 'factoryReturnedUndefined'
+      id: params.id
+      factory: params.factory
+      container: params.container

 ###################################################################################
 # sugar for container construction

 module.exports.newContainer = (
-    defaultInstanceResolver
-    defaultFactoryResolver
-    defaultEmit
-    defaultSetInstance
-    defaultSetUnderConstruction
-    defaultUnsetUnderConstruction
-    defaultGetUnderConstruction
-    EventEmitter
-) ->
-    (factories = {}, instances = {}) ->
-        {
-            factories: factories
-            instances: instances
-            factoryResolvers: [defaultFactoryResolver]
-            instanceResolvers: [defaultInstanceResolver]
-            underConstruction: {}
-            setInstance: defaultSetInstance
-            setUnderConstruction: defaultSetUnderConstruction
-            unsetUnderConstruction: defaultUnsetUnderConstruction
-            getUnderConstruction: defaultGetUnderConstruction
-            emit: defaultEmit
-            emitter: new EventEmitter
-        }
+  defaultInstanceResolver
+  defaultFactoryResolver
+  defaultEmit
+  defaultSetInstance
+  defaultSetUnderConstruction
+  defaultUnsetUnderConstruction
+  defaultGetUnderConstruction
+  EventEmitter
+) ->
+  (factories = {}, instances = {}) ->
+    factories: factories
+    instances: instances
+    factoryResolvers: [defaultFactoryResolver]
+    instanceResolvers: [defaultInstanceResolver]
+    underConstruction: {}
+    setInstance: defaultSetInstance
+    setUnderConstruction: defaultSetUnderConstruction
+    unsetUnderConstruction: defaultUnsetUnderConstruction
+    getUnderConstruction: defaultGetUnderConstruction
+    emit: defaultEmit
+    emitter: new EventEmitter

 ###################################################################################
 # defaults

 module.exports.defaultInstanceResolver = ->
-    (container, id) ->
-        container.instances?[id]
+  (container, id) ->
+    container.instances?[id]

 module.exports.defaultFactoryResolver = (
-    parseFunctionArguments
-) ->
-    (container, id) ->
-        factory = container.factories?[id]
-        unless factory?
-            return
-
-        # add $inject such that parseFunctionArguments is called only once per factory
-        if not factory.$inject? and 'function' is typeof factory
-            factory.$inject = parseFunctionArguments factory
-
-        return factory
+  parseFunctionArguments
+) ->
+  (container, id) ->
+    factory = container.factories?[id]
+    unless factory?
+      return
+
+    # add $inject such that parseFunctionArguments is called only once per factory
+    if not factory.$inject? and 'function' is typeof factory
+      factory.$inject = parseFunctionArguments factory
+
+    return factory

 module.exports.defaultSetInstance = ->
-    (container, id, instance) ->
-        container.instances[id] = instance
+  (container, id, instance) ->
+    container.instances[id] = instance

 module.exports.defaultEmit = ->
-    (container, event) ->
-        container.emitter.emit event.event, event
-        container.emitter.emit 'any', event
+  (container, event) ->
+    container.emitter.emit event.event, event
+    container.emitter.emit 'any', event

 module.exports.defaultSetUnderConstruction = ->
-    (container, id, underConstruction) ->
-        container.underConstruction[id] = underConstruction
+  (container, id, underConstruction) ->
+    container.underConstruction[id] = underConstruction

 module.exports.defaultUnsetUnderConstruction = ->
-    (container, id) ->
-        value = container.underConstruction[id]
-        delete container.underConstruction[id]
-        value
+  (container, id) ->
+    value = container.underConstruction[id]
+    delete container.underConstruction[id]
+    value

 module.exports.defaultGetUnderConstruction = ->
-    (container, id) ->
-        container.underConstruction[id]
+  (container, id) ->
+    container.underConstruction[id]


remove the extension

remove the extension

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

 module.exports = (robot) ->

-  robot.respond /horsify me (.*)/i, (msg) ->
+  robot.respond /horsify me (.*) (\w+)?/i, (msg) ->
     horseface = "http://horseface.me/horsify?url="
     imagery = msg.match[1]

@@ -28,11 +28,4 @@
       images = images.responseData?.results
       if images?.length > 0
         image = msg.random images
-        cb ensureImageExtension image.unescapedUrl
-
-ensureImageExtension = (url) ->
-  ext = url.split('.').pop()
-  if /(png|jpe?g|gif)/i.test(ext)
-    url
-  else
-    "#{url}#.png"
+        cb image.unescapedUrl


fix(contenteditable): add default prop for `onSelectionChanged`

--- src/components/contenteditable/contenteditable.cjsx
+++ src/components/contenteditable/contenteditable.cjsx
@@ -58,6 +58,7 @@
     extensions: []
     spellcheck: true
     floatingToolbar: true
+    onSelectionChanged: =>

   coreServices: [MouseService, ClipboardService]


Buffer is managed by separate class now Viewport now manages drawn items

--- src/ui-scroller.coffee
+++ src/ui-scroller.coffee
@@ -35,57 +35,120 @@
                 insertAfter(item.node, insert_point)
         insert_point = item.node

+
+class Buffer
+    constructor: ->
+        @start = 0
+        @length = 0
+
+    truncateTo: (start, end) =>
+        if @start < start
+            for i in [@start...start]
+                delete @[i]
+            @length = Math.max(0, @length - (start - @start))
+            @start = start
+        cur_end = @start + @length - 1
+        if cur_end > end
+            for i in [cur_end...end]
+                delete @[i]
+            @length = Math.max(0, @length - (cur_end - end))
+
+    addItemsToEnd: (items) =>
+        for item, idx in items
+            @[@start + @length + idx] = item
+        @length += items.length
+
+    addItemsToStart: (items) =>
+        @start -= items.length
+        for item, idx in items
+            @[@start + idx] = item
+        @length += items.length
+
+
 class ScrollerViewport
     constructor: (@_scope, @_element) ->
-        @_items_requesting = false
+        @_settings =
+            paddingTop:
+                min: 20
+                max: 100
+            paddingBottom:
+                min: 20
+                max: 100
+            itemsPerRequest: 10
+        @_requesting_top_items = false
+        @_requesting_bottom_items = false
         @_getItems = @_scope.scrollerSource.bind(@)
         @_watchHandler = window.setInterval(@_updateState, 1000)
         @_drawnItems = []
         @_scope.list = @_drawnItems
-        @_buffer = {
-            start: 0
-            length: 0
-        }
+        @_buffer = new Buffer

-    _requestMoreItems: =>
-        return if @_items_requesting
-        @_items_requesting = true
-        pos = @_buffer.start + @_buffer.length
-        @_getItems pos, 10, (err, res) =>
-            @_items_requesting = false
+    _requestMoreTopItems: =>
+        return if @_requesting_top_items
+        @_requesting_top_items = true
+        start = @_buffer.start - @_settings.itemsPerReqeust
+        @_getItems start, @_settings.itemsPerRequest, (err, res) =>
+            @_requesting_top_items = false
             if !err
-                @_addBufferItems(pos, res)
+                @_buffer.addItemsToStart(res)

-    _addBufferItems: (pos, items) =>
-        console.log("Adding buffer items")
-        if pos == @_buffer.start + @_buffer.length
-            # add items to end
-            for item, idx in items
-                @_buffer[pos + idx] = item
-            @_buffer.length += items.length
+    _requestMoreBottomItems: =>
+        return if @_requesting_bottom_items
+        @_requesting_bottom_items = true
+        start = @_buffer.start + @_buffer.length
+        @_getItems start, @_settings.itemsPerRequest, (err, res) =>
+            @_requesting_bottom_items = false
+            if !err
+                @_buffer.addItemsToEnd(res)

     _updateState: =>
-        bottom = @_element.scrollHeight - @_element.scrollTop
-        if bottom <= @_element.offsetHeight
-            @_tryDrawBottomItems()
+        if @_element.scrollTop > @_settings.paddingTop.max
+            @_removeTopDrawnItem()
+        else if @_element.scrollTop < @_settings.paddingTop.min
+            @_tryDrawTopItem()

-    _pushDrawnItem: (item) =>
-        newItems = @_drawnItems[..]
-        newItems.push(item)
-        @_drawnItems = newItems
+        paddingBottom = @_element.scrollHeight - @_element.scrollTop - @_element.offsetHeight
+        if paddingBottom < @_settings.paddingBottom.min
+            @_tryDrawBottomItem()
+        else if paddingBottom > @_settings.paddingBottom.max
+            @_removeBottomDrawnItem()
+
+    _updateDrawnItems: (@_drawnItems) =>
         @_scope.$apply =>
             @_scope.list = @_drawnItems
             window.setTimeout(@_updateState, 0)

-    _tryDrawBottomItems: =>
+    _addTopDrawnItem: (item) =>
+        @_updateDrawnItems([item].concat(@_drawnItems))
+
+    _addBottomDrawnItem: (item) =>
+        @_updateDrawnItems(@_drawnItems.concat(item))
+
+    _removeTopDrawnItem: =>
+        @_updateDrawnItems(@_drawnItems[1..])
+
+    _removeBottomDrawnItem: =>
+        @_updateDrawnItems(@_drawnItems[...-1])
+
+    _tryDrawTopItem: =>
+        if @_drawnItems.length > 0
+            neededIndex = @_drawnItems[0].index - 1
+        else
+            neededIndex = -1
+        if neededIndex of @_buffer
+            @_addTopDrawnItem({index: neededIndex, data: @_buffer[neededIndex]})
+        else
+            @_requestMoreTopItems()
+
+    _tryDrawBottomItem: =>
         if @_drawnItems.length > 0
             neededIndex = @_drawnItems[@_drawnItems.length - 1].index + 1
         else
             neededIndex = 0
         if neededIndex of @_buffer
-            @_pushDrawnItem({index: neededIndex, data: @_buffer[neededIndex]})
+            @_addBottomDrawnItem({index: neededIndex, data: @_buffer[neededIndex]})
         else
-            @_requestMoreItems()
+            @_requestMoreBottomItems()

 angular.module('ui.scroller', [])


compute/ctrl: cheeck permissions after disabled stack update

fixes #10887

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

--- client/app/lib/providers/computecontroller.coffee
+++ client/app/lib/providers/computecontroller.coffee
@@ -56,7 +56,8 @@

     super

-    { mainController, groupsController, router } = kd.singletons
+    { mainController, groupsController, router
+      appStorageController, notificationController } = kd.singletons

     @ui = ComputeController_UI
     @storage = new ComputeStorage
@@ -87,6 +88,11 @@

       @checkGroupStackRevisions()
       @checkGroupStacks()
+
+      kd.singletons.notificationController.on 'DisabledUserStackAdded', (data) =>
+        debug 'disabled user stack added', data
+        if groupsController.canEditGroup()
+          @checkMachinePermissions()

       if groupsController.canEditGroup()
         @checkMachinePermissions()


clear the cloned stack information from stack and stack template

--- client/app/lib/providers/computecontroller.coffee
+++ client/app/lib/providers/computecontroller.coffee
@@ -1299,6 +1299,33 @@
           showError 'Failed to share credential'  if err
           callback()
     else showError 'Failed to share credential'
+
+
+  removeClonedFromAttr: (stackTemplate, callback = kd.noop) ->
+
+    @ui.askFor 'dontWarnMe', {}, (status) =>
+
+      return callback yes  unless status.confirmed
+
+      { reactor } = kd.singletons
+
+      stack = @findStackFromTemplateId stackTemplate._id
+      { config } = stack
+
+      delete config.clonedFrom
+      delete config.needUpdate
+
+      stack.modify { config }, (err) ->
+        reactor.dispatch 'STACK_UPDATED', stack
+
+      { config } = stackTemplate
+
+      delete config.clonedFrom
+      delete config.needUpdate
+
+      stackTemplate.update { config }, (err) ->
+        reactor.dispatch 'UPDATE_STACK_TEMPLATE_SUCCESS', { stackTemplate }
+        callback no

   ###*


ComputeController: destroyStack: this calls kloud.apply with destroy: true, and then immediately marks the stack as destroyed

--- client/app/lib/providers/computecontroller.coffee
+++ client/app/lib/providers/computecontroller.coffee
@@ -642,6 +642,31 @@
       (@errorHandler call, 'buildStack', stack) err

+  destroyStack: (stack, callback) ->
+
+    return  unless stack
+
+    stack.machines.forEach (machineId) =>
+      return  unless machine = @findMachineFromMachineId machineId
+
+      @eventListener.triggerState machine,
+        status      : Machine.State.Terminating
+        percentage  : 0
+
+      machine.getBaseKite( createIfNotExists = no ).disconnect()
+
+    call = @getKloud().buildStack { stackId: stack._id, destroy: yes }
+
+    .then (res) =>
+
+      stack.destroy callback
+
+    .timeout globals.COMPUTECONTROLLER_TIMEOUT
+
+    .catch (err) ->
+      console.error "Destroy stack failed:", err
+      callback err
+

   start: (machine) ->

@@ -1059,7 +1084,8 @@

     @ui.askFor 'reinitStack', {}, =>

-      stack.delete (err) =>
+      @destroyStack stack, (err) =>
+
         return showError err  if err

         @reset()


envdataprovider: add removeCollaborationMachine

--- client/app/lib/userenvironmentdataprovider.coffee
+++ client/app/lib/userenvironmentdataprovider.coffee
@@ -230,6 +230,11 @@
     sinkrow.dash queue, callback

+  removeCollaborationMachine: (machine) ->
+
+    @removeMachine 'collaboration', machine
+
+
   removeMachine: (type, machine) ->

     envData = globals.userEnvironmentData[type]


fix bad method

fix bad method

--- services/CartoDbUploader.coffee
+++ services/CartoDbUploader.coffee
@@ -19,6 +19,4 @@
     req.end()
     cb()

-  payload: ->
-
 module.exports = CartoDbUploader


Tests: IDEView: Added ::createEditor_.

Tests: IDEView: Added ::createEditor_.

--- client/ide/test/views/tabview/ideview.test.coffee
+++ client/ide/test/views/tabview/ideview.test.coffee
@@ -1,8 +1,35 @@
+kd                    = require 'kd'
 expect                = require 'expect'
+FSFile                = require 'app/util/fs/fsfile'
+FSHelper              = require 'app/util/fs/fshelper'
 IDEView               = require 'ide/views/tabview/ideview'
+IDEEditorPane         = require 'ide/workspace/panes/ideeditorpane'
+IDETailerPane         = require 'ide/workspace/panes/idetailerpane'
 IDEApplicationTabView = require 'ide/views/tabview/ideapplicationtabview'

 ideView = null
+
+
+createEditorPane = (paneClass) ->
+
+  file    = FSHelper.createFileInstance path: '/foo/bar'
+  content = 'Foo bar baz waz here'
+  options = { name: 'Foo Bar' }
+
+  expect.spyOn IDEView.prototype, 'updateStatusBar'
+  expect.spyOn IDEView.prototype, 'bindListeners'
+  spy = expect.spyOn IDEView.prototype, 'trimUntitledFileName'
+  expect.spyOn IDEView.prototype, 'handlePaneRemoved'
+
+  clazz      = paneClass or IDEEditorPane
+  ideView    = new IDEView
+  editorPane = new clazz { file, content }
+
+  expect.spyOn(ideView.tabView, 'addPane').andCallThrough()
+
+  pane = ideView.createPane_ editorPane, options, file
+
+  return { ideView, editorPane, pane, file }

 describe 'IDEView', ->
@@ -17,9 +44,7 @@
   describe 'constructor', ->

-    it 'should be instantiated', ->
-
-      expect(ideView).toBeA IDEView
+    it 'should be instantiated', -> expect(ideView).toBeA IDEView

     it 'should have default options set and invoke required methods', ->
@@ -38,3 +63,41 @@
       expect(ideView.openFiles).toEqual []
       expect(ideView.setHash).toHaveBeenCalled()
       expect(ideView.bindListeners).toHaveBeenCalled()
+
+
+  describe '::createPane_', ->
+
+    it 'should return error if missing arguments provided', ->
+
+      expect(-> ideView.createPane_()).toThrow        /Missing argument/
+      expect(-> ideView.createPane_ {}).toThrow       /Missing argument/
+      expect(-> ideView.createPane_ null, {}).toThrow /Missing argument/
+      expect(-> ideView.createPane_ {}, {}).toThrow   /must be an instance of KDView/
+
+
+    it 'should create a KDTabPaneView and append it to ideView\'s tabView', ->
+
+      { ideView, editorPane, pane } = createEditorPane()
+
+      expect(ideView.tabView.addPane).toHaveBeenCalled()
+      expect(pane).toBeA kd.TabPaneView
+      expect(pane.view).toBe editorPane
+      expect(pane.subViews.first).toBe editorPane
+      expect(ideView.tabView.panes).toInclude pane
+      expect(ideView.trimUntitledFileName).toHaveBeenCalledWith 'Foo Bar'
+      expect(pane.tabHandle.subViews.length).toBe 1
+
+      pane.emit 'KDObjectWillBeDestroyed'
+      expect(ideView.handlePaneRemoved).toHaveBeenCalled()
+
+
+
+    it 'should create tail icon if the view is an instance of an IDETailerPane', ->
+
+      { ideView, editorPane, pane } = createEditorPane IDETailerPane
+
+      { subViews } = pane.tabHandle
+
+      expect(subViews.length).toBe 2
+      expect(subViews.last).toBeA kd.CustomHTMLView
+      expect(subViews.last.options.cssClass).toBe 'tail-icon'


Added check for alias existance.

Added check for alias existance.

--- client/landing/app/CommonViews/fatih/fatih.coffee
+++ client/landing/app/CommonViews/fatih/fatih.coffee
@@ -118,9 +118,11 @@
       return plugin

   updatePluginsKeyword: (prefs) ->
+    return unless prefs
     for plugin of @plugins
-      @plugins[plugin].setOption "keyword", prefs.aliases[plugin]
-      log "#{plugin}'s keyword set to #{prefs.aliases[plugin]}"
+      keyword = prefs.aliases?[plugin]
+      @plugins[plugin].setOption "keyword", keyword if keyword
+      # log "#{plugin}'s keyword set to #{prefs.aliases[plugin]}"

   runDefaultPlugins: (keyword) ->
     @defaultPluginsRunning = yes


Added registration verbiage and no-email case

--- client/templates/_partials/loginOrRegister.coffee
+++ client/templates/_partials/loginOrRegister.coffee
@@ -42,9 +42,16 @@
         else
           Modal.allowMultiple = true
           Modal.hide 'loginOrRegisterModal'
-          Modal.show 'checkEmailRegistrationLinkModal',
-            title: 'Verification Email Sent'
-            message: 'Please check your email to verify your registration.'
+          if email isnt ''
+            Modal.show 'checkEmailRegistrationLinkModal',
+              title: 'Verification Email Sent'
+              message: "Please check your email to verify your registration.
+                Until you verify, you will not have the ability to create or participate in projects."
+          else
+            Modal.show 'checkEmailRegistrationLinkModal',
+              title: 'You Cannot Verify Without An Email Address'
+              message: "Please add an email address on your profile page in order to send the verification email.
+                Without verifying, you will not have the ability to create or participate in projects."

 Template.signOutButton.events
   'click': (event) ->


Specify top and left values for line

Specify top and left values for line

--- player.coffee
+++ player.coffee
@@ -26,6 +26,8 @@
         stroke: 'black',
         stokeWidth: 3,
         fille: 'white'
+        left: 200
+        top: 200
       })

     lastPos: ->


Remove unnecessary whitespace :lipstick:

--- spec/latex-spec.coffee
+++ spec/latex-spec.coffee
@@ -2,7 +2,6 @@
 path = require "path"
 temp = require "temp"
 wrench = require "wrench"
-
 latex = require "../lib/latex"

 {View, WorkspaceView} = require "atom"


compact spec

compact spec

--- spec/paner-spec.coffee
+++ spec/paner-spec.coffee
@@ -223,51 +223,39 @@
         command = 'very-top'
         orientations = ['vertical', 'horizontal', 'horizontal']
         describe "when p1 is active", ->
-          it "move to very top", ->
-            expectPanePaths({active: p1, command, paths: [f1, f2, f3], orientations})
-        describe "when p2 is active", ->
-          it "move to very top", ->
-            expectPanePaths({active: p2, command, paths: [f2, f1, f3], orientations})
-        describe "when p3 is active", ->
-          it "move to very top", ->
-            expectPanePaths({active: p3, command, paths: [f3, f1, f2], orientations})
+          it "move to very top", -> expectPanePaths({active: p1, command, paths: [f1, f2, f3], orientations})
+        describe "when p2 is active", ->
+          it "move to very top", -> expectPanePaths({active: p2, command, paths: [f2, f1, f3], orientations})
+        describe "when p3 is active", ->
+          it "move to very top", -> expectPanePaths({active: p3, command, paths: [f3, f1, f2], orientations})

       describe "very-bottom", ->
         command = 'very-bottom'
         orientations = ['horizontal', 'horizontal', 'vertical']
         describe "when p1 is active", ->
-          it "move to very bottom", ->
-            expectPanePaths({active: p1, command, paths: [f2, f3, f1], orientations})
-        describe "when p2 is active", ->
-          it "move to very bottom", ->
-            expectPanePaths({active: p2, command, paths: [f1, f3, f2], orientations})
-        describe "when p3 is active", ->
-          it "move to very bottom", ->
-            expectPanePaths({active: p3, command, paths: [f1, f2, f3], orientations})
+          it "move to very bottom", -> expectPanePaths({active: p1, command, paths: [f2, f3, f1], orientations})
+        describe "when p2 is active", ->
+          it "move to very bottom", -> expectPanePaths({active: p2, command, paths: [f1, f3, f2], orientations})
+        describe "when p3 is active", ->
+          it "move to very bottom", -> expectPanePaths({active: p3, command, paths: [f1, f2, f3], orientations})

       describe "very-left", ->
         command = 'very-left'
         describe "when p1 is active", ->
-          it "move to very left", ->
-            expectPanePaths({active: p1, command, paths: [f1, f2, f3]})
-        describe "when p2 is active", ->
-          it "move to very left", ->
-            expectPanePaths({active: p2, command, paths: [f2, f1, f3]})
-        describe "when p3 is active", ->
-          it "move to very left", ->
-            expectPanePaths({active: p3, command, paths: [f3, f1, f2]})
+          it "move to very left", -> expectPanePaths({active: p1, command, paths: [f1, f2, f3]})
+        describe "when p2 is active", ->
+          it "move to very left", -> expectPanePaths({active: p2, command, paths: [f2, f1, f3]})
+        describe "when p3 is active", ->
+          it "move to very left", -> expectPanePaths({active: p3, command, paths: [f3, f1, f2]})

       describe "very-right", ->
         command = 'very-right'
         describe "when p1 is active", ->
-          it "move to very right", ->
-            expectPanePaths({active: p1, command, paths: [f2, f3, f1]})
-        describe "when p2 is active", ->
-          it "move to very right", ->
-            expectPanePaths({active: p2, command, paths: [f1, f3, f2]})
-        describe "when p3 is active", ->
-          it "move to very right", ->
-            expectPanePaths({active: p3, command, paths: [f1, f2, f3]})
+          it "move to very right", -> expectPanePaths({active: p1, command, paths: [f2, f3, f1]})
+        describe "when p2 is active", ->
+          it "move to very right", -> expectPanePaths({active: p2, command, paths: [f1, f3, f2]})
+        describe "when p3 is active", ->
+          it "move to very right", -> expectPanePaths({active: p3, command, paths: [f1, f2, f3]})

     describe "all vertical", ->
       beforeEach ->
@@ -288,48 +276,36 @@
       describe "very-top", ->
         command = 'very-top'
         describe "when p1 is active", ->
-          it "move to very top", ->
-            expectPanePaths({active: p1, command, paths: [f1, f2, f3]})
-        describe "when p2 is active", ->
-          it "move to very top", ->
-            expectPanePaths({active: p2, command, paths: [f2, f1, f3]})
-        describe "when p3 is active", ->
-          it "move to very top", ->
-            expectPanePaths({active: p3, command, paths: [f3, f1, f2]})
+          it "move to very top", -> expectPanePaths({active: p1, command, paths: [f1, f2, f3]})
+        describe "when p2 is active", ->
+          it "move to very top", -> expectPanePaths({active: p2, command, paths: [f2, f1, f3]})
+        describe "when p3 is active", ->
+          it "move to very top", -> expectPanePaths({active: p3, command, paths: [f3, f1, f2]})
       describe "very-bottom", ->
         command = 'very-bottom'
         describe "when p1 is active", ->
-          it "move to very bottom", ->
-            expectPanePaths({active: p1, command, paths: [f2, f3, f1]})
-        describe "when p2 is active", ->
-          it "move to very bottom", ->
-            expectPanePaths({active: p2, command, paths: [f1, f3, f2]})
-        describe "when p3 is active", ->
-          it "move to very bottom", ->
-            expectPanePaths({active: p3, command, paths: [f1, f2, f3]})
+          it "move to very bottom", -> expectPanePaths({active: p1, command, paths: [f2, f3, f1]})
+        describe "when p2 is active", ->
+          it "move to very bottom", -> expectPanePaths({active: p2, command, paths: [f1, f3, f2]})
+        describe "when p3 is active", ->
+          it "move to very bottom", -> expectPanePaths({active: p3, command, paths: [f1, f2, f3]})

       describe "very-left", ->
         command = 'very-left'
         orientations = ['horizontal', 'vertical', 'vertical']
         describe "when p1 is active", ->
-          it "move to very left", ->
-            expectPanePaths({active: p1, command, paths: [f1, f2, f3], orientations})
-        describe "when p2 is active", ->
-          it "move to very left", ->
-            expectPanePaths({active: p2, command, paths: [f2, f1, f3], orientations})
-        describe "when p3 is active", ->
-          it "move to very left", ->
-            expectPanePaths({active: p3, command, paths: [f3, f1, f2], orientations})
+          it "move to very left", -> expectPanePaths({active: p1, command, paths: [f1, f2, f3], orientations})
+        describe "when p2 is active", ->
+          it "move to very left", -> expectPanePaths({active: p2, command, paths: [f2, f1, f3], orientations})
+        describe "when p3 is active", ->
+          it "move to very left", -> expectPanePaths({active: p3, command, paths: [f3, f1, f2], orientations})

       describe "very-right", ->
         command = 'very-right'
         orientations = ['horizontal', 'vertical', 'vertical']
         describe "when p1 is active", ->
-          it "move to very right", ->
-            expectPanePaths({active: p1, command, paths: [f2, f3, f1], orientations})
-        describe "when p2 is active", ->
-          it "move to very right", ->
-            expectPanePaths({active: p2, command, paths: [f1, f3, f2], orientations})
-        describe "when p3 is active", ->
-          it "move to very right", ->
-            expectPanePaths({active: p3, command, paths: [f1, f2, f3], orientations})
+          it "move to very right", -> expectPanePaths({active: p1, command, paths: [f2, f3, f1], orientations})
+        describe "when p2 is active", ->
+          it "move to very right", -> expectPanePaths({active: p2, command, paths: [f1, f3, f2], orientations})
+        describe "when p3 is active", ->
+          it "move to very right", -> expectPanePaths({active: p3, command, paths: [f1, f2, f3], orientations})


rmd2html.sh -> rmd2html.py

rmd2html.sh -> rmd2html.py

--- src/smc-webapp/editor-html-md/editor-html-md.coffee
+++ src/smc-webapp/editor-html-md/editor-html-md.coffee
@@ -431,7 +431,7 @@

     rmd_to_html: (cb) =>
         @to_html_via_exec
-            command     : "rmd2html.sh"
+            command     : "rmd2html.py"
             args        : [@filename]
             cb          : cb


:dizzy::tropical_fish: Updated in browser at strd6.github.io/editor

--- pixie.cson
+++ pixie.cson
@@ -1,2 +1,2 @@
 entryPoint: "lib/styl"
-version: "0.2.8"
+version: "0.2.8-pre.0"


Change grammar to only allow type synonyms at top-level

--- src/grammar.coffee
+++ src/grammar.coffee
@@ -57,8 +57,28 @@
   # all parsing must end here.
   Root: [
     o '',                                       -> new Block
-    o 'Body'
-    o 'Block TERMINATOR'
+    o 'RootBody'
+    o 'RootBlock TERMINATOR'
+  ]
+
+  # Special top-level body
+  RootBody: [
+    o 'RootLine',                               -> Block.wrap [$1]
+    o 'RootBody TERMINATOR RootLine',           -> $1.push $3
+    o 'RootBody TERMINATOR'
+  ]
+
+  # Special top-level block
+  RootBlock: [
+    o 'INDENT OUTDENT',                         -> new Block
+    o 'INDENT RootBody OUTDENT',                -> $2
+  ]
+
+  # Lines at the root can contain type synonyms
+  RootLine: [
+    o 'TypeAssign'
+    o 'Expression'
+    o 'Statement'
   ]

   # Any list of statements and expressions, separated by line breaks or semicolons.
@@ -90,7 +110,6 @@
     o 'Invocation'
     o 'Code'
     o 'DeclareType'
-    o 'TypeAssign'
     o 'Operation'
     o 'Assign'
     o 'If'


allow cross-fork compares

allow cross-fork compares

Cross fork compare uses ':' to delineate "origin:branch" for cross-fork
comparisons.


Remove local Component class.

Remove local Component class.

--- plotxy.coffee
+++ plotxy.coffee
@@ -1,16 +1,6 @@
-class Component
+class $blab.PlotXY extends $blab.Component

-    constructor: ->
-
-    update: ->
-
-    stringify: ->
-        JSON.stringify(@spec)
-
-class $blab.PlotXY extends Component
-
-    constructor: (@spec, sheet) ->
-        #@sheets = (app.sheet[id] for id in @spec.sheetIds)
+    constructor: (@spec, sheet, file) ->
         @sheets = (sheet[id] for id in @spec.sheetIds)
         defaults = {}
         @chart = c3.generate(


Fixing bugs in mouse handling

Fixing bugs in mouse handling

--- src/js/Game.coffee
+++ src/js/Game.coffee
@@ -85,9 +85,9 @@
       when 'dragging'
         @draggingUp event
       when 'down'
-        x = event.offsetX || event.layerX - $(event.target).position().left
-        y = event.offsetY || event.layerY - $(event.target).position().top
-        if @state.info.block is @renderer.resolveScreenCoordinates(x, y).block
+        mouseX = event.pageX - $('#main-canvas').offset().left
+        mouseY = event.pageY - $('#main-canvas').offset().top
+        if @state.info.block is @renderer.resolveScreenCoordinates(mouseX, mouseY).block
           @selectBlock @state.info.block
           @state.type = 'normal'
       when 'normal'
@@ -99,8 +99,8 @@
     return off

   canvasMove: (event) =>
-    mouseX = event.pageX - $('#main-canvas').position().left
-    mouseY = event.pageY - $('#main-canvas').position().top
+    mouseX = event.pageX - $('#main-canvas').offset().left
+    mouseY = event.pageY - $('#main-canvas').offset().top
     switch @state.type
       when 'down'
         # If the user moves more than @settings.draggingOffset pixels
@@ -111,7 +111,7 @@
       when 'dragging'
         @draggingMove event
       when 'normal'
-        if side = @renderer.sideAtScreenCoordinates mouseX, mouseY
+        if (side = @renderer.sideAtScreenCoordinates(mouseX, mouseY)) and side isnt 'floor'
           $('body').css 'cursor', @settings.dragCursor
         else
           $('body').css 'cursor', @settings.defaultCursor
@@ -122,8 +122,8 @@
   canvasDown: (event) =>
     switch @state.type
       when 'normal'
-        mouseX = event.pageX - $('#main-canvas').position().left
-        mouseY = event.pageY - $('#main-canvas').position().top
+        mouseX = event.pageX - $('#main-canvas').offset().left
+        mouseY = event.pageY - $('#main-canvas').offset().top
         info = @renderer.resolveScreenCoordinates mouseX, mouseY
         if info.block
           @state.type  = 'down'
@@ -150,9 +150,9 @@
         changed = yes
       @map.setNeedsRedraw yes if changed

-    x = event.pageX - $('#main-canvas').position().left
-    y = event.pageY - $('#main-canvas').position().top
-    info = @renderer.resolveScreenCoordinates(x, y)
+    mouseX = event.pageX - $('#main-canvas').offset().left
+    mouseY = event.pageY - $('#main-canvas').offset().top
+    info = @renderer.resolveScreenCoordinates mouseX, mouseY

     # If the user drags the stack onto another block, draw it there
     if info.side is 'top' or info.side is 'floor'


Make keydownEvent more realistic for modifier-only keystrokes

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -53,11 +53,27 @@
   bubbles = true
   cancelable = true
   view = null
+
   key = key.toUpperCase() if /^[a-z]$/.test(key)
   if key.length is 1
     keyIdentifier = "U+#{key.charCodeAt(0).toString(16)}"
   else
-    keyIdentifier = key[0].toUpperCase() + key[1..]
+    switch key
+      when 'ctrl'
+        keyIdentifier = 'Control'
+        ctrl = true
+      when 'alt'
+        keyIdentifier = 'Alt'
+        alt = true
+      when 'shift'
+        keyIdentifier = 'Shift'
+        shift = true
+      when 'cmd'
+        keyIdentifier = 'Meta'
+        cmd = true
+      else
+        keyIdentifier = key[0].toUpperCase() + key[1..]
+
   location = KeyboardEvent.DOM_KEY_LOCATION_STANDARD
   event.initKeyboardEvent('keydown', bubbles, cancelable, view,  keyIdentifier, location, ctrl, alt, shift, cmd)
   Object.defineProperty(event, 'target', get: -> target) if target?


radio function

radio function

--- spotify-control.coffee
+++ spotify-control.coffee
@@ -15,7 +15,8 @@
 #   hubot sp pause - pauses the music, play again with sp playtoggle
 #   hubot sp prev - go to previous track
 #   hubot sp next - go to next track
-#   hubot sp open <URI> - not implemented yet
+#   hubot sp open <URI> - opens the spotify URI supplied
+#   hubot sp radio <URI> - starts spotify radio using the given URI as a base
 #   hubot sp current - not implemented yet
 #   hubot sp art - not implemented yet
 #   hubot sp search - not implemented yet
@@ -77,6 +78,21 @@
       .catch (err) ->
         msg.send "Error: #{err}"

+  robot.respond /sp radio (.*?)$/i, (msg) ->
+    seed = msg.match[1]
+    seedArray = seed.split ":"
+    uriLength = seedArray.length
+    if (uriLength > 1) and (seedArray[0] is "spotify")
+      identifier = seedArray[uriLength - 1]
+      type = seedArray[uriLength - 2]
+      link = "spotify:app:radio:#{type}:#{identifier}"
+      getInterface()
+      .then (iface) ->
+        iface.OpenUri link
+        msg.send "Started the :radio:"
+    else
+      msg.send "Not a valid spotify URL, must start with spotify:"
+
 spotifyPid = () ->
   def = q.defer()
   pidof 'spotify', (err, pid) ->


Clearer delete text.

Clearer delete text.

--- packages/sanfrancisco-c3/design/terminal/screens/character/character.coffee
+++ packages/sanfrancisco-c3/design/terminal/screens/character/character.coffee
@@ -101,7 +101,7 @@
       message = "Do you really want to retire this agent? You will lose control of them and you cannot undo this action."

     else
-      message = "Do you really want to delete this design? You cannot undo this action."
+      message = "Do you really want to delete the whole agent design? You cannot undo this action."

     # Double check that the user wants to be removed from their character.
     @terminal.showDialog


clearing out callbacks each time a route resolve is started in dashboard

--- src/coffee/routes/dashboard.coffee
+++ src/coffee/routes/dashboard.coffee
@@ -1,6 +1,7 @@
 lft.config ['$routeProvider', ($routeProvider) ->

   callbacks = []
+
   next = (user) ->
     callback user for callback in callbacks

@@ -9,6 +10,7 @@
     controller: 'DashboardController'
     resolve:
       activeUser: ['Auth', (Auth) ->
+        callbacks = []
         active = Auth.filter 'active'
         active.then next
         active


WIP: Edit attachments on backspace

WIP: Edit attachments on backspace

--- src/trix/models/composition.coffee
+++ src/trix/models/composition.coffee
@@ -129,10 +129,16 @@
         range[0]--
       else
         range[1]++
+
       locationRange = @document.locationRangeFromRange(range)
-
-    @document.removeTextAtLocationRange(locationRange)
-    @setLocationRange(locationRange.collapse())
+      attachment = @getAttachmentAtLocationRange(locationRange)
+
+    if attachment
+      @setLocationRange(locationRange)
+      # @editAttachment(attachment)
+    else
+      @document.removeTextAtLocationRange(locationRange)
+      @setLocationRange(locationRange.collapse())

   moveTextFromLocationRange: (locationRange) ->
     position = @getPosition()
@@ -338,3 +344,8 @@

   refreshAttachments: ->
     @attachments.refresh(@document.getAttachments())
+
+  getAttachmentAtLocationRange: (locationRange) ->
+    document = @document.getDocumentAtLocationRange(locationRange)
+    if document.toString() is "#{Trix.OBJECT_REPLACEMENT_CHARACTER}\n"
+      document.getAttachments()[0]


webapp: fix absolute image URLs *between* two projects -- issue #1778

--- src/smc-webapp/misc_page.coffee
+++ src/smc-webapp/misc_page.coffee
@@ -162,9 +162,21 @@
                     src = y.attr(attr)
                     if not src?
                         continue
+                    {join} = require('path')
+                    i = src.indexOf('/projects/')
+                    j = src.indexOf('/files/')
+                    if src.indexOf(document.location.origin) == 0 and i != -1 and j != -1 and j > i
+                        # the href is inside the app, points to the current project or another one
+                        # j-i should be 36, unless we ever start to have different (vanity) project_ids
+                        path = src.slice(j + '/files/'.length)
+                        project_id = src.slice(i + '/projects/'.length, j)
+                        new_src = join('/', window.smc_base_url, project_id, 'raw', path)
+                        y.attr(attr, new_src)
+                        continue
                     if src.indexOf('://') != -1
+                        # link points somewhere else
                         continue
-                    {join} = require('path')
+                    # we do not have an absolute url, hence we assume it is a relative URL to a file in a project
                     new_src = join('/', window.smc_base_url, opts.project_id, 'raw', opts.file_path, src)
                     y.attr(attr, new_src)


Parse html from the editor element

Parse html from the editor element

--- src/trix/controllers/editor_controller.coffee
+++ src/trix/controllers/editor_controller.coffee
@@ -1,9 +1,10 @@
 #= require trix/controllers/text_controller
 #= require trix/controllers/toolbar_controller
 #= require trix/controllers/debug_controller
+#= require trix/html_parser

 class Trix.EditorController
-  constructor: (textElement, toolbarElement, @inputElement, debugElement) ->
+  constructor: (@textElement, toolbarElement, @inputElement, debugElement) ->
     @text = @createText()
     @textController = new Trix.TextController textElement, @text
     @textController.delegate = this
@@ -13,7 +14,9 @@
     @debugController.render()

   createText: ->
-    if @inputElement?.value
+    if @textElement.textContent.trim()
+      Trix.HTMLParser.createTextFrom(@textElement)
+    else if @inputElement?.value
       Trix.Text.fromJSON(@inputElement.value)
     else
       new Trix.Text


Adding more snippets

Adding more snippets

--- snippets/go.cson
+++ snippets/go.cson
@@ -5,3 +5,6 @@
   'go2':
     'prefix': 'go forward'
     'body': 'go forward'
+  'go3':
+    'prefix': 'go to the hash location called "hash"'
+    'body': 'go to the hash location called "$1"'


LOGGING disabled

LOGGING disabled

--- source/Art/Engine/Elements/Widgets/ScrollElement.coffee
+++ source/Art/Engine/Elements/Widgets/ScrollElement.coffee
@@ -212,7 +212,7 @@

     @_updateOnScreenInfo()

-    log "tracking: #{@_pendingState._tracking} #{_scrollPosition}"
+    # log "tracking: #{@_pendingState._tracking} #{_scrollPosition}"

   _updateOnScreenInfo: ->
     {isVertical, windowSize} = @


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -125,7 +125,6 @@

   @name meter
   @methodOf Metered#
-  @returns {Point} The position of this object
   ###
   meter: (options={}) ->
     Object.reverseMerge options,


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -7,7 +7,7 @@
   health: 100
   maxHealth: 100

-enem
+enemy = GameObj

 player.position()


load cardInfo from token

load cardInfo from token

--- js/inner/billing.coffee
+++ js/inner/billing.coffee
@@ -186,7 +186,6 @@
       success: (data) =>
         mixpanel.track('Paid')
         @loadPlanData(data)
-        @loadExistingCard()
         @loadInvoices()
         VM.org().subpage('add-containers')

@@ -203,13 +202,13 @@
           $('#confirmForm').modal('hide') # TODO: eww
           VM.org().subpage('add_containers')

-
   newPlan: (plan, event) =>
     vals =
       panelLabel: 'Pay' # TODO: better label (?)
       price: 100 * plan.price
       description: "#{plan.name} plan"
       token: (token) =>
+        @cardInfo(token.card)
         @ajaxNewPlan(plan.id, token, event)

     StripeCheckout.open(_.extend @stripeDefaults(), vals)


adding native/trapped method coverage

adding native/trapped method coverage

fixes #78

--- tools/coverage.coffee
+++ tools/coverage.coffee
@@ -19,6 +19,23 @@
         op_stats[@name]++
         old_fn.call @, rs
   op_stats
+
+setup_native_stats = ->
+  # monkeypatch native and trapped calls
+  native_stats = {}
+  for sig, func of natives.native_methods
+    native_stats[sig] = 0
+    natives.native_methods[sig] = do (func, sig) ->
+      (args...) ->
+        native_stats[sig]++
+        func args...
+  for sig, func of natives.trapped_methods
+    native_stats[sig] = 0
+    natives.trapped_methods[sig] = do (func, sig) ->
+      (args...) ->
+        native_stats[sig]++
+        func args...
+  native_stats

 print_usage = (stats) ->
   names = (name for name,count of stats)
@@ -54,15 +71,26 @@
   Usage: $0
   Optional flags:
     --print-usage
+    -n, --natives
+    -o, --opcodes
     -q, --quiet
     -h, --help
   '''
   return optimist.showHelp() if argv.help? or argv.h?
+  do_opcodes = argv.o? or argv.opcodes?
+  do_natives = argv.n? or argv.natives?

-  op_stats = setup_opcode_stats()
+  unless do_opcodes or do_natives
+    console.error 'Must select natives, opcodes, or both'
+    return optimist.showHelp()
+
+  op_stats = setup_opcode_stats() if do_opcodes
+  native_stats = setup_native_stats() if do_natives
   run_all_tests(argv.q? or argv.quiet?)

   if argv['print-usage']?
-    print_usage op_stats
+    print_usage op_stats if do_opcodes
+    print_usage native_stats if do_natives
   else
-    print_unused op_stats, 'opcodes'
+    print_unused op_stats, 'opcodes' if do_opcodes
+    print_unused native_stats, 'native methods' if do_natives


sitemapGenerator: Fixed sitemap generator, excluded guests in jName collection.

--- workers/sitemapgenerator/lib/sitemapgenerator/sitemapgeneratorworker.coffee
+++ workers/sitemapgenerator/lib/sitemapgenerator/sitemapgeneratorworker.coffee
@@ -47,8 +47,11 @@
     name = "sitemap.xml"
     content = @generateSitemapIndexString sitemapNames
     @saveSitemap name, content
+    console.timeEnd 'Sitemap generator worker'

   generate:=>
+    console.time 'Sitemap generator worker'
+    console.log 'Sitemap generation started.'
     {JName, JSitemap} = @bongo.models

     feedLinksAdded = no
@@ -61,11 +64,16 @@
           'slugs.group': 'koding',
           'slugs.constructorName': 'JNewStatusUpdate'
         }
-        { 'slugs.usedAsPath':'username' }
+        {
+          'slugs.usedAsPath':'username'
+          'slugs.slug':{ $not: /guest-*/ } # We don't want to count guests.
+        }
       ]
     }

     JName.count selector, (err, count)=>
+      console.log err  if err
+      console.log "There are ", count, " items to be added to sitemaps."
       numberOfNamePages = Math.ceil(count / NAMEPERPAGE)

       queue = [1..numberOfNamePages].map (pageNumber)=>=>


uncaught exception handler

uncaught exception handler

--- tools/parallel.coffee
+++ tools/parallel.coffee
@@ -1,5 +1,5 @@
 ###
-## executes all threads and adds the result of the
+## executes all threads and adds the result of the
 ## callback to the results array in the same order.
 ## the result of threads[i] will be placed in results[i]
 ##
@@ -21,17 +21,17 @@
     , (err, [r1, r2, r3, r4])->
         console.log r1, r2, r3, r4

-##
+##
 ##    >> res1 res2 res3 res4
 ##
-## 2. an array with thread functions followed by a callback
+## 2. an array with thread functions followed by a callback
 ##    function which recieves the results array or an error object
 ##

     threads = for i in [1..4]
         do (i)->
             (fn)-> fn null, 'res'+i
-
+
     floyd.tools.parallel threads, (err, [r1, r2, r3, r4])->
         console.log r1, r2, r3, r4

@@ -41,70 +41,72 @@

     if typeof threads[0] isnt 'function'
         threads = threads[0]
-
+
     if !(threads instanceof floyd.tools.parallel.Stack)
         threads = new floyd.tools.parallel.Stack threads
-
+
     threads.run done
-

-module.exports.Stack =
-
+
+module.exports.Stack =
+
     class Stack extends Array
-
+
         ##
         constructor: (threads)->
             super()
             @_count = 0
-
+
             if threads
                 for thread in threads
                     @push thread
-
+
             @_error = null
             @_results = []
-
-
+
+
         ##
         ##
-        run: (@_done)->
+        run: (@_done)->
             if !@_count
                 return @_done()
-
+
             @_running = true
-
+
             for i in [0..@length-1]
                 @_exec i
-
+
             return undefined
-
-
+
+
         ##
         ##
         push: (thread)->
             super thread
             @_count++

-            if @_running
+            if @_running
                 @_exec @length - 1
-
-
-
-        ##
+
+
+
+        ##
         _exec: (i)->
             setImmediate =>
-
-                @[i] (err, res)=>
-
+
+                fn = (err, res)=>
+
                     if err
-                        @_error = err
-
+                        @_error = err
+
                     else
                         @_results[i] = res
-
+
                     if --@_count is 0
                         @_running = false
                         @_done @_error, @_results
-
-
-    +
+                try
+                    @[i] fn
+                catch e
+                    fn e


Social: remove can from admin list

Social: remove can from admin list

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -658,7 +658,7 @@

   dummyAdmins = [ 'sinan', 'devrim', 'gokmen', 'fatihacet', 'arslan',
                   'sent-hil', 'cihangirsavas', 'leeolayvar', 'stefanbc',
-                  'szkl', 'canthefason', 'nitin', 'usirin', 'kodinglearn' ] # kodinglearn is nitin's impersonation account
+                  'szkl', 'nitin', 'usirin', 'kodinglearn' ] # kodinglearn is nitin's impersonation account

   isEmailVerified: (callback) ->


Nodejs Web Server: require JAppStorage synchronously for relationship

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -270,15 +270,16 @@

     relationships           : ->

-      # requiring JStackTemplate here solved problems after turning stacktemplate's
-      # targetType from string to object.
+      # bongo doesn't wait models to be loaded and this causes errors
+      # in node.js tests so synchronously requiring them
+      JAppStorage      = require './appstorage'
       JCredential      = require './computeproviders/credential'
       JStackTemplate   = require './computeproviders/stacktemplate'

       return {
         appStorage    :
           as          : 'appStorage'
-          targetType  : 'JAppStorage'
+          targetType  : JAppStorage

         storage       :
           as          : 'storage'