improve

improve

--- lib/main.coffee
+++ lib/main.coffee
@@ -1,7 +1,13 @@
 {CompositeDisposable} = require 'atom'
 _ = require 'underscore-plus'

+Config =
+  debug:
+    type: 'boolean'
+    default: false
+
 module.exports =
+  config: Config
   PaneAxis: null
   Pane:     null

@@ -9,13 +15,13 @@
     @disposables = new CompositeDisposable

     @disposables.add atom.commands.add 'atom-workspace',
-      'paner:maximize':    => @maximize()
-      'paner:swap-item':   => @swapItem()
-      'paner:very-top':    => @very('top')
-      'paner:very-bottom': => @very('bottom')
-      'paner:very-left':   => @very('left')
-      'paner:very-right':  => @very('right')
-      'paner:test':        => @test()
+      'paner:maximize':     => @maximize()
+      'paner:swap-item':    => @swapItem()
+      'paner:very-top':     => @very('top')
+      'paner:very-bottom':  => @very('bottom')
+      'paner:very-left':    => @very('left')
+      'paner:very-right':   => @very('right')
+      'paner:toggle-debug': => @toggleDebug()

   maximize: ->
     workspaceElement = atom.views.getView(atom.workspace)
@@ -24,25 +30,10 @@
     @disposables.add @getActivePane().onDidChangeActive ->
       workspaceElement.classList.remove('paner-maximize')

-  getPanes: ->
-    atom.workspace.getPanes()
-
-  getActivePane: ->
-    atom.workspace.getActivePane()
-
-  getPaneInfo: (pane) ->
-    pane:      pane
-    item:      pane.getActiveItem()
-    index:     pane.getActiveItemIndex()
-    container: pane.getContainer()
-    root:      pane.getContainer().getRoot()
-    parent:    pane.getParent()
-
-  getOrientation: (direction) ->
-    if direction in ['top', 'bottom']
-      'vertical'
-    else
-      'horizontal'
+  toggleDebug: ->
+    atom.config.toggle('paner.debug')
+    state = atom.config.get('paner.debug') and "enabled" or "disabled"
+    console.log "paner: debug #{state}"

   # Like Vim's `ctrl-w x`, select pane within current PaneAxis.
   #
@@ -76,9 +67,6 @@
     # Revert original setting
     atom.config.set('core.destroyEmptyPanes', configDestroyEmptyPanes)

-  deactivate: ->
-    @disposables.dispose()
-
   movePane: (srcPane, dstPane) ->
     for item, i in srcPane.getItems()
       srcPane.moveItemToPane item, dstPane, i
@@ -87,13 +75,6 @@
   copyPaneAxis: (paneAxis) ->
     {container, orientation} = paneAxis
     new paneAxis.constructor({container, orientation, children: paneAxis.getChildren()})
-
-    # clone = atom.deserializers.deserialize(paneAxis.serialize())
-    # container = paneAxis.getContainer()
-    # clone.setContainer(container)
-    # clone.children.forEach (child) ->
-    #   child.setContainer(container)
-    #   child.setParent(clone)

   # [FIXME]
   # Occasionally blank pane, remain on original pane position.
@@ -110,18 +91,12 @@
     @PaneAxis  ?= root.constructor

     if root.getOrientation() isnt orientation
-      console.log "Different orientation"
-      # root.unsubscribeFromChild(child)
-      origRoot = new @PaneAxis({
-        container,
-        orientation: root.getOrientation(),
-        children: root.getChildren()
-        })
+      @debug "Different orientation"
+      for child in root.getChildren()
+        root.unsubscribeFromChild(child)

-      for child in root.getChildren()
-        root.removeChild(child)
-      root.destroy()
-      root  = new @PaneAxis({container, orientation, children: [origRoot]})
+      origRoot = @copyPaneAxis root
+      root     = new @PaneAxis({container, orientation, children: [origRoot]})

     newPane = new @Pane()
     switch direction
@@ -131,21 +106,29 @@
         root.addChild(newPane)
     @movePane thisPane, newPane

-    if root isnt paneInfo.root
-      container.setRoot(root)
-      @reparentChildren root, orientation
+    container.setRoot(root) if root isnt paneInfo.root
+    @reparent(axis) for axis in @getAllAxis(root)

     container.destroyEmptyPanes()
     newPane.activateItemAtIndex index
     newPane.activate()

+  getAllAxis: (root, list=[]) ->
+    for child in root.getChildren()
+      if child instanceof @PaneAxis
+        list.push child
+        @getAllAxis child, list
+    return list
+
+
   isEqualOrientationAxis: (axis, orientation) ->
     (axis instanceof @PaneAxis) and (axis.getOrientation() is orientation)

-  reparentChildren: (parent, orientation) ->
-    console.log "Reparent"
-    for axis in parent.children when @isEqualOrientationAxis(axis, orientation)
-      console.log "Reparent: found"
+  reparent: (axis) ->
+    parent = axis.getParent()
+    # console.log "parent: #{parent.getOrientation()}, me: #{axis.getOrientation()}"
+    if parent.getOrientation() is axis.getOrientation()
+      # console.log "match!"
       children   = axis.getChildren()
       firstChild = children.shift()
       firstChild.setFlexScale()
@@ -153,14 +136,33 @@
       while children.length
         parent.insertChildAfter(firstChild, children.shift())
       axis.destroy()
+      parent

-  # reparent
-  # if paneInfo.root.getOrientation() isnt orientation
-  #   for child in root.children when (child instanceof @PaneAxis) and (child.getOrientation() is orientation)
-  #     console.log "reParent!!"
-  #     children  = child.getChildren()
-  #     child.reparentLastChild()
-  #     children.shift()
-  #     while children.length
-  #       root.addChild(children.shift())
-  #     child.destroy()
+
+  # Utility
+  getPanes: ->
+    atom.workspace.getPanes()
+
+  getActivePane: ->
+    atom.workspace.getActivePane()
+
+  getPaneInfo: (pane) ->
+    pane:      pane
+    item:      pane.getActiveItem()
+    index:     pane.getActiveItemIndex()
+    container: pane.getContainer()
+    root:      pane.getContainer().getRoot()
+    parent:    pane.getParent()
+
+  getOrientation: (direction) ->
+    if direction in ['top', 'bottom']
+      'vertical'
+    else
+      'horizontal'
+
+  debug: (msg) ->
+    return unless atom.config.get('paner.debug')
+    console.log msg
+
+  deactivate: ->
+    @disposables.dispose()


deleted empty parameter list

deleted empty parameter list

--- lib/main.coffee
+++ lib/main.coffee
@@ -25,7 +25,7 @@
     @disposable.dispose()

   # Internal: Toggles the spell-check activation state.
-  toggle: () ->
+  toggle: ->
     editorId = atom.workspace.getActiveTextEditor().id

     if spellCheckViews[editorId]['active']


Update main.coffee

Update main.coffee

--- lib/main.coffee
+++ lib/main.coffee
@@ -243,7 +243,7 @@
         config.commands = [];

       view = new MainEditView(main, title, filePath, config, selectedAction);
-      paneItem = pane.addItem(view, 0);
+      paneItem = pane.addItem(view, {index: 0});
       pane.activateItem(paneItem);

   # Called when the save button was pressed. This saves changes that were made


#335 warn and disable when vim-mode is activated.

--- lib/main.coffee
+++ lib/main.coffee
@@ -23,6 +23,23 @@
     @subscribe(Base.init(service))
     @registerCommands()
     @registerVimStateCommands()
+
+    warnAndDisablePackage = ->
+      message = """
+      vim-mode-plus is automatically disabled because vim-mode is enabled.__
+      To use vim-mode-plus
+      **Disable vim-mode first then manally enable vim-mode-plus**.__
+      """.replace(/_/g, ' ')
+      atom.notifications.addWarning(message, dismissable: true)
+      atom.packages.disablePackage('vim-mode-plus')
+
+    if atom.packages.isPackageActive('vim-mode')
+      warnAndDisablePackage()
+    else
+      disposable = atom.packages.onDidActivatePackage (pack) ->
+        if pack.name is 'vim-mode'
+          disposable.dispose()
+          warnAndDisablePackage()

     if atom.inDevMode()
       developer = (new (require './developer'))


Verify tokenizedLine is set

Verify tokenizedLine is set

Check that a tokenizedLine was able to be obtained before attempting to
use it in all locations.

--- lib/main.coffee
+++ lib/main.coffee
@@ -26,6 +26,8 @@
       # E128 - continuation line under-indented for visual indent
       # E131 - continuation line unaligned for hanging indent
       tokenizedLine = textEditor.tokenizedLineForScreenRow(lineNumber)
+      if tokenizedLine is undefined
+        break
       offset = 0
       for token in tokenizedLine.tokens
         if not token.firstNonWhitespaceIndex
@@ -55,6 +57,8 @@
       # F841 - local variable 'SYMBOL' is assigned but never used
       symbol = /'([^']+)'/.exec(message)[1]
       tokenizedLine = textEditor.tokenizedLineForScreenRow(lineNumber)
+      if tokenizedLine is undefined
+        break
       offset = 0
       for token in tokenizedLine.tokens
         if token.value is symbol
@@ -81,6 +85,8 @@
     when 'H501'
       # H501 - do not use locals() for string formatting
       tokenizedLine = textEditor.tokenizedLineForScreenRow(lineNumber)
+      if tokenizedLine is undefined
+        break
       offset = 0
       for token in tokenizedLine.tokens
         if 'meta.function-call.python' in token.scopes


:memo: Add docs for ignoredMessageTypes

:memo: Add docs for ignoredMessageTypes

--- lib/main.coffee
+++ lib/main.coffee
@@ -11,6 +11,7 @@

     ignoredMessageTypes:
       title: 'Ignored message Types'
+      description: 'Comma separated list of message types to completely ignore'
       type: 'array'
       default: []
       items:


remove ;

remove ;

--- lib/main.coffee
+++ lib/main.coffee
@@ -1,4 +1,4 @@
-isNewFuzzyFinder = require('./fuzzy-finder-version-satisfied');
+isNewFuzzyFinder = require('./fuzzy-finder-version-satisfied')
 module.exports =
   config:
     maxFilesToRemember:


Expose the Minimap class through the API

--- lib/main.coffee
+++ lib/main.coffee
@@ -221,6 +221,11 @@
   onDidDeactivatePlugin: (callback) ->
     @emitter.on 'did-deactivate-plugin', callback

+  # Returns the {Minimap} class.
+  #
+  # Returns a {Function}.
+  minimapClass: -> Minimap ?= require './minimap'
+
   # Returns the {Minimap} object associated to the
   # passed-in `TextEditorElement`.
   #


fix depeciated errors pt2

fix depeciated errors pt2

--- keymaps/clang-format.cson
+++ keymaps/clang-format.cson
@@ -1,2 +1,2 @@
-'#atom-text-editor':
+'atom-text-editor':
   'cmd-shift-k': 'clang-format:format'


handle submit event of PageForm with PageContent

--- components/page.cjsx
+++ components/page.cjsx
@@ -98,24 +98,7 @@
 )

 PageContent = React.createClass(
-  getInitialState: ->
-    {content: page.content}
-  render: ->
-    style = {
-      whiteSpace: 'pre'
-      marginBottom: '20px'
-    }
-    return (
-

-
{this.state.content}

-        <PageForm />
-

-    )
-)
-
-PageForm = React.createClass(
-  handleSubmit: (e) ->
-    e.preventDefault()
+  handlePageSubmit: ->
     ce = $("
").html($("#content").children().first().children().first().html())
     if($.browser.webkit)
       ce.find("div").replaceWith(()-> return "\n" + this.innerHTML)
@@ -130,8 +113,29 @@
     })
     PageApp.model.url = ()-> return $("#contents").attr("action")
     PageApp.model.save()
+  getInitialState: ->
+    {content: page.content}
   render: ->
-    <form id="contents" action={if page.id then "/pages/#{page.id}" else "/pages/"} method="POST" onSubmit={this.handleSubmit}>
+    style = {
+      whiteSpace: 'pre'
+      marginBottom: '20px'
+    }
+    return (
+

+
{this.state.content}

+        <PageForm onPageSubmit={@handlePageSubmit} />
+

+    )
+)
+
+PageForm = React.createClass(
+  propTypes:
+    onPageSubmit: React.PropTypes.func.isRequired
+  handleSubmit: (e) ->
+    e.preventDefault()
+    @props.onPageSubmit()
+  render: ->
+    <form id="contents" action={if page.id then "/pages/#{page.id}" else "/pages/"} method="POST" onSubmit={@handleSubmit}>
       <input type="submit" value="save" />
     </form>
 )


run: print only if err

run: print only if err

--- lib/run.coffee
+++ lib/run.coffee
@@ -140,7 +140,7 @@
           error = true
           throw err
     context.then (err) ->
-      console.log 'ERROR', err unless error
+      console.log 'ERROR', err if err and not error
       callback()
   .then (err) =>
     @emit 'end'


repl creates an emit bus and pushes errors to it for other listeners

--- lib/repl.coffee
+++ lib/repl.coffee
@@ -14,14 +14,13 @@
   constructor: (@uri="sclang://localhost:57120", projectRoot, @onClose) ->
     @projectRoot = projectRoot
     @ready = Q.defer()
+    @makeBus()

   stop: ->
     @sclang?.quit()
     @postWindow.destroy()

   createPostWindow: ->
-    unless @bus
-      @makeBus()

     onClose = () =>
       @sclang?.quit()
@@ -31,6 +30,7 @@

   makeBus: ->
     @bus = new Bacon.Bus()
+    @emit = new Bacon.Bus()

   startSCLang: () ->
     opts =
@@ -48,15 +48,16 @@
         when 'compileError'
           # stdout
           # dirs
+          i = 0
           for error in error.errors
             @bus.push rendering.renderParseError(error)
-            # update any of these files that are open
-            # goto first error
-
-        # initFailure
-        # descrepency
-        # systemError
+            error.index = i
+            @emit.push(error)
+            i += 1
         else
+          # initFailure
+          # descrepency
+          # systemError
           @bus.push("
ERROR: #{state}
")
           @bus.push("
#{error}
")


Fixed bug in parsing cookie header

Fixed bug in parsing cookie header

--- lib/soap.coffee
+++ lib/soap.coffee
@@ -41,20 +41,18 @@
 }

+# Separation by comma is important as some devices don't comform to standard
+COOKIE_REGEX = /\s*(.+?)\s*=\s*"?(.*?)"?\s*(,|;|$)/g
+
 cookiesToObj = (cookieLine) ->
   cookies = {}
-  for c in cookieLine.trim().split ','
-    nv = c.split '='
-    continue if nv.length < 2
-    cookies[nv[0].trim()] = nv[1].trim()
+  while match = COOKIE_REGEX.exec(cookieLine)
+    cookies[match[1]] = match[2]
   return cookies

 cookiesToStr = (obj) ->
-  l = []
-  for cn, cv of obj
-    l.push("#{cn}=#{cv}")
-  return l# + 'path=/'
+  "#{cn}=#{cv}" for cn, cv of obj

 event = (xml) ->


These were for Device Architecture 1.1, stick to 1.0 for now

--- lib/ssdp.coffee
+++ lib/ssdp.coffee
@@ -11,10 +11,6 @@
 # Initializes a UPnP Device
 start = (config, callback) ->

-    # unique ID's used by Control Points
-    bootId = new Date().getTime()
-    configId = Math.floor(Math.random() * 16777216)
-
     # generate SSDP (HTTPU/HTTPMU) header suiting the message type
     makeMessage = (type, nt, nts) ->
         makeServerString = ->
@@ -43,8 +39,6 @@
             'NTS': 'ssdp:' + nts
             'SERVER': makeServerString config
             'USN': config['device']['uuid']
-            'BOOTID.UPNP.ORG': bootId
-            'CONFIGID.UPNP.ORG': configId

         # append Notification Type to USN, except for
         # requests with only uuid as NT
@@ -54,8 +48,7 @@
         switch type
             when 'NOTIFY'
                 useHeaders = [ 'HOST', 'CACHE-CONTROL', 'LOCATION',
-                               'NT', 'NTS', 'SERVER', 'USN',
-                               'BOOTID.UPNP.ORG', 'CONFIGID.UPNP.ORG' ]
+                               'NT', 'NTS', 'SERVER', 'USN' ]
             else
                 useHeaders = []


Use async.queue to limit the number of concurrent SSDP/UDP messages

--- lib/ssdp.coffee
+++ lib/ssdp.coffee
@@ -37,8 +37,11 @@
        @makeType()
        @uuid ]

-  # Create a UDP socket, send messages, then close socket.
-  sendMessages = (messages, address = '239.255.255.250', port = 1900) ->
+  # Queue for UDP messages to avoid hitting open file descriptor limit.
+  messageQueue = async.queue (task, callback) ->
+    { messages } = task
+    address = task.address ? '239.255.255.250'
+    port = task.port ? 1900
     socket = dgram.createSocket 'udp4'
     # Messages with specified destination do not need TTL limit.
     socket.setTTL 4 unless address?
@@ -46,7 +49,10 @@
     log.debug "Sending #{messages.length} messages to #{address}:#{port}."
     async.concat messages,
       (msg) -> socket.send msg, 0, msg.length, port, address
-      -> socket.close()
+      ->
+        socket.close()
+        callback()
+  , 5

   announce = (timeout = 1800) ->
     # To "kill" any instances that haven't timed out on control points yet,
@@ -62,20 +68,17 @@

   # Possible subtypes are 'alive' or 'byebye'.
   notify = (subtype) =>
-    sendMessages(
-      @makeSSDPMessage('notify',
+    messageQueue.push messages: @makeSSDPMessage('notify',
         nt: nt, nts: "ssdp:#{subtype}", host: null
       ) for nt in @makeNotificationTypes()
-    )

   answer = (address, port) =>
-    sendMessages(
-      @makeSSDPMessage('ok',
+    messageQueue.push { messages: @makeSSDPMessage('ok',
         st: st, ext: null
       ) for st in @makeNotificationTypes()
       address
       port
-    )
+    }

   listen()
   announce()


:new: Add InitTiles in View

:new: Add InitTiles in View

--- lib/view.coffee
+++ lib/view.coffee
@@ -2,13 +2,15 @@
 {Range} = require 'atom'

 Bubble = require './view-bubble'
-LeftTile = require './view-left-tile'
+Tiles = require './view-tiles'

 class LinterView extends EventEmitter
   constructor: (@Linter) ->
     super()
     @decorations = []
     @root = document.createElement 'div'
+    @BarCurrent = null
+    @BarProject = null
     @root.id = 'linter-panel'
     @bubble = null

@@ -66,16 +68,21 @@

     @updateBubble(TextEditor.getCursors()[0].getBufferPosition())

-  updateLeftTile: (nbMessages) ->
-    # Remove old tile
-    @leftTile?.destroy?()
-    @leftTile = null
-
-    # Create new tile with `@Linter.Messages` length
-    el = new LeftTile(nbMessages)
-    @leftTile = @Linter.StatusBar.addLeftTile
-      item: el
-      priority: -100
+  initTiles: ->
+    @BarCurrent = Tiles.currentFile(this)
+    @BarProject = Tiles.wholeProject(this)
+    @BarStatus = Tiles.status()
+    @BarStatus.Child.classList.add 'icon-check'
+    @BarStatus.Child.textContent = 'No Errors'
+    @Linter.StatusBar.addLeftTile
+      item: @BarCurrent,
+      priority: -1001
+    @Linter.StatusBar.addLeftTile
+      item: @BarProject,
+      priority: -1000
+    @Linter.StatusBar.addLeftTile
+      item: @BarStatus.Root
+      priority: -999

   messageLine: (Message) ->
     Entry = document.createElement 'div'


Add scroll listener to admin log viewer

Add scroll listener to admin log viewer

--- packages/rocketchat-logger/client/views/viewLogs.coffee
+++ packages/rocketchat-logger/client/views/viewLogs.coffee
@@ -96,3 +96,8 @@
 		Meteor.setTimeout ->
 			template.checkIfScrollIsAtBottom()
 		, 2000
+
+	wrapper.addEventListener 'scroll', ->
+		template.atBottom = false
+		Meteor.defer ->
+			template.checkIfScrollIsAtBottom()


Add bower paths to less task in Gulpfile

--- Gulpfile.coffee
+++ Gulpfile.coffee
@@ -11,12 +11,13 @@
 paths = {
   styles: ['app/assets/styles/**/*.less']
   coffee: ['app/**/*.coffee', 'Gulpfile.coffee']
+  bower:  ['bower_components']
 }

 gulp.task 'style', ->
   gulp.src(paths.styles)
   .pipe(sourcemaps.init())
-  .pipe(less()).on('error', gutil.log)
+  .pipe(less(paths: paths.bower)).on('error', gutil.log)
   .pipe(prefix())
   .pipe(minifyCSS())
   .pipe(sourcemaps.write())


format change

format change

--- Gulpfile.coffee
+++ Gulpfile.coffee
@@ -216,9 +216,10 @@
         (req, res, next) ->
             if req.url.match(/\.svg$/)
                 res.setHeader('Content-Type', 'image/svg+xml')
-            next()
             if req.url.match(/\.ttf$/)
                 res.setHeader('Content-Type', 'font/ttf')
+            next()
+
     ]
   connect.server(config)


Using elapsed time in delayed events

Using elapsed time in delayed events

--- src/engine_delay.coffee
+++ src/engine_delay.coffee
@@ -20,7 +20,7 @@
     return

   ###*
-  Execute a callback after a number of steps have passed.
+  Execute a callback after a number of s have passed.

       engine.delay 5, ->
         engine.add


clean up test logic

clean up test logic

--- js/test/auth/credential-provider/CredentialProviders-test.coffee
+++ js/test/auth/credential-provider/CredentialProviders-test.coffee
@@ -21,10 +21,9 @@

     beforeEach ->
       CredentialProvider.destroy()
-      id         = Math.floor(Math.random() * 1000)
-      principal  = "krypt|demo-#{id}"
+      principal  = 'krypt|demo'
+      credential = 'fake-credential'
       keypair    = Forge.rsa.generateKeyPair({bits: 128, e: 0x10001})
-      credential = 'fake-credential'

     afterEach ->
       CredentialProvider.destroy()


read state from nextProps, not current

read state from nextProps, not current

--- src/exercise/index.cjsx
+++ src/exercise/index.cjsx
@@ -15,7 +15,7 @@

   getStepState: (props) ->
     props ?= @props
-    {item} = @props
+    {item} = props

     step: _.last(item)
     parts: item


Math.imul polyfill for Safari

Math.imul polyfill for Safari

--- src/factory/hash.coffee
+++ src/factory/hash.coffee
@@ -7,11 +7,11 @@

 # Fix imul in old/broken browsers
 imul = (a, b) ->
-  ah  = (a >>> 16) & 0xffff
+  ah = (a >>> 16) & 0xffff
   al = a & 0xffff
-  bh  = (b >>> 16) & 0xffff
+  bh = (b >>> 16) & 0xffff
   bl = b & 0xffff
-  (al * bl) + (((ah * bl + al * bh) << 16) >>> 0)|0
+  (al * bl) + (((ah * bl + al * bh) << 16) >>> 0) | 0

 if Math.imul?
   test = Math.imul 0xffffffff, 5


Troubleshoot: TroubleshootModal service titles are changed

--- client/Main/troubleshoot/troubleshootstatusview.coffee
+++ client/Main/troubleshoot/troubleshootstatusview.coffee
@@ -5,13 +5,13 @@
     options.cssClass = "troubleshoot-status"
     super options, data

-    premium = if KD.config.usePremiumBroker then "Premium " else ""
+    premium = if KD.config.usePremiumBroker then "VIP " else ""
     @createItem "connection", "Internet Connection"
-    @createItem "bongo", "Bongo"
-    @createItem "liveUpdate", "Live Update"
-    @createItem "broker", "#{premium} Broker"
-    @createItem "brokerKite", "#{premium} Broker Kite"
-    @createItem "osKite", "OS-Kite"
+    @createItem "bongo", "Social Server"
+    @createItem "liveUpdate", "Live Updates"
+    @createItem "broker", "#{premium} Messaging Server"
+    @createItem "brokerKite", "#{premium} Kite Messaging Server"
+    @createItem "osKite", "VM Server"
     @createItem "vm", "VMs"
     @createItem "webServer", "Webserver"
     @createItem "version", "Latest version"


StacksCustomViews: pass stackTemplate along with steps

--- client/admin/lib/views/stacks/stackscustomviews.coffee
+++ client/admin/lib/views/stacks/stackscustomviews.coffee
@@ -372,7 +372,7 @@

       console.log options
       {callback, cancelCallback, data} = options
-      {provider, credential} = data
+      {provider, credential, stackTemplate} = data

       container = @views.container 'step-bootstrap'

@@ -411,9 +411,11 @@
             cssClass    : 'bootstrap-output hidden'

         outputView.on 'BootstrappingDone', => @addTo container,
-          navButton_next :
-            callback     : ->
-              callback {provider, credential}
+          button        :
+            title       : 'Continue'
+            cssClass    : 'solid compact green nav next'
+            callback    : ->
+              callback {provider, credential, stackTemplate}

         if state
           outputView.show()
@@ -465,7 +467,8 @@

       console.log options

-      {callback, cancelCallback, data} = options
+      {callback, cancelCallback, data}      = options
+      {stackTemplate, credential, provider} = data

       container = @views.container 'step-creds'


NavigationMachineItem: Create PermanentShareConfirmationModal.

--- client/app/lib/navigation/navigationmachineitem.coffee
+++ client/app/lib/navigation/navigationmachineitem.coffee
@@ -8,6 +8,7 @@
 JView = require '../jview'
 Machine = require '../providers/machine'
 MachineSettingsPopup = require '../providers/machinesettingspopup'
+PermanentShareConfirmationModal = require 'app/activity/sidebar/permanentshareconfirmationmodal'

 module.exports = class NavigationMachineItem extends JView
@@ -142,6 +143,7 @@

     if not m.isMine() and m.isPermanent() and not m.isApproved()
       kd.utils.stopDOMEvent e
+      new PermanentShareConfirmationModal { position: @getPopupPosition 20 }, m

     super


client: update digitalocean default template

--- client/app/lib/providers/templates/digitalocean.coffee
+++ client/app/lib/providers/templates/digitalocean.coffee
@@ -12,7 +12,7 @@
   "resource": {
     "digitalocean_droplet": {
       "do-instance": {
-        "name": "do-instance",
+        "name": "koding-${var.koding_group_slug}-${var.koding_stack_id}-${count.index+1}",
         "size": "512mb",
         "region": "nyc2",
         "image": "ubuntu-14-04-x64",
@@ -38,7 +38,7 @@
     # this is the name of your VM
     do-instance:
       # and this is its identifier (required)
-      name: do-instance
+      name: 'koding-${var.koding_group_slug}-${var.koding_stack_id}-${count.index+1}'
       # select your instance_type here: eg. 512mb
       size: 512mb
       # select your instance zone which must be in provided region: eg. nyc2


add routes for application install resource

--- config/routes.coffee
+++ config/routes.coffee
@@ -1,6 +1,7 @@
 exports.routes = (map) ->
     map.get '/', 'applications#index'
     map.get '/api/applications', 'applications#applications'
+    map.get '/api/applications/install', 'applications#install'

     map.get '/authenticated', 'passport#isAuthenticated'
     map.post '/register', 'passport#register'


Home: PasswordForm: send password and passwordConfirm values too to backend

--- client/Home/AppView.coffee
+++ client/Home/AppView.coffee
@@ -39,10 +39,15 @@
         forms                 :
           password            :
             callback          : (form) =>
+
               appManager.require 'Login', (controller) =>
+
                 KD.mixpanel 'Register submit, click'
+
+                formData.password        = form.password
+                formData.passwordConfirm = form.passwordConfirm
+
                 controller.getView().doRegister formData, @signUpForm
-                log 'set the password here'
                 modal.destroy()
             fields            :
               password        :


Returning true from the listener to stop preventing document's clicks

--- src/angular-date-range-picker.coffee
+++ src/angular-date-range-picker.coffee
@@ -226,6 +226,7 @@

     angular.element(document).bind "click", (e) ->
       $scope.$apply -> $scope.hide()
+      true

     _makeQuickList()
     _calculateRange()


Add missing listener removal

Add missing listener removal

--- resources/assets/coffee/react/profile-page/extra.coffee
+++ resources/assets/coffee/react/profile-page/extra.coffee
@@ -60,6 +60,7 @@

   _removeListeners: ->
     $(document).off '.profileContentsExtra'
+    $(window).off '.profileContentsExtra'

   _tabsStick: (_e, target) =>


Adding venture and caracal fit to damage analogy

--- src/javascript/main.coffee
+++ src/javascript/main.coffee
@@ -823,6 +823,16 @@
 DamageAnalogyPanel = React.createClass(
   displayName: 'DamageAnalogyPanel'
   ships:
+    venture:
+      pluralName: 'Ventures'
+      id: 32880
+      ehp: 3770
+      fit: 'https://o.smium.org/loadout/private/24585/6674104450400911360'
+    caracal:
+      pluralName: 'Caracals'
+      id: 621
+      ehp: 18000
+      fit: 'https://o.smium.org/loadout/private/24584/5861380819909607424'
     proteus:
       pluralName: 'Proteii'
       id: 29988


Client: Activity: Added RemovedFromChannel event handler to actionCreators

--- client/activity/lib/flux/actions/realtime/actioncreators.coffee
+++ client/activity/lib/flux/actions/realtime/actioncreators.coffee
@@ -24,6 +24,13 @@

     channel.on 'MessageRemoved', (message) ->
       dispatch actions.REMOVE_MESSAGE_SUCCESS, { messageId: message.id }
+
+    channel.on 'RemovedFromChannel', (account) ->
+
+      channelId = channel.id
+      accountId = account._id
+
+      dispatch actions.REMOVE_PARTICIPANT_FROM_CHANNEL, { channelId, accountId }

 bindMessageEvents = (message) ->


register singletons taken into a block

register singletons taken into a block

--- client/landing/app/MainApp/maincontroller/maincontroller.coffee
+++ client/landing/app/MainApp/maincontroller/maincontroller.coffee
@@ -11,7 +11,6 @@
     - accountChanged.to.loggedOut   [account, connectedState, firstLoad]
   ###

-
   connectedState =
     connected   : no

@@ -20,6 +19,16 @@
     options.failWait  = 5000            # duration in miliseconds to show a connection failed modal

     super options, data
+
+    @appStorages = {}
+
+    @createSingletons()
+    @setFailTimer()
+    @attachListeners()
+
+    @introductionTooltipController = new IntroductionTooltipController
+
+  createSingletons:->

     KD.registerSingleton "mainController",            this
     KD.registerSingleton "appManager",   appManager = new ApplicationManager
@@ -45,13 +54,6 @@
       KD.registerSingleton "activityController",   new ActivityController
       KD.registerSingleton "kodingAppsController", new KodingAppsController
       @emit 'AppIsReady'
-
-    @setFailTimer()
-    @attachListeners()
-
-    @appStorages = {}
-
-    @introductionTooltipController = new IntroductionTooltipController

   # FIXME GG
   getAppStorageSingleton:(appName, version)->


exponential backoff for lock up to maximum time

--- services/document-updater/app/coffee/LockManager.coffee
+++ services/document-updater/app/coffee/LockManager.coffee
@@ -14,6 +14,7 @@

 module.exports = LockManager =
 	LOCK_TEST_INTERVAL: 50 # 50ms between each test of the lock
+	MAX_TEST_INTERVAL: 1000 # back off to 1s between each test of the lock
 	MAX_LOCK_WAIT_TIME: 10000 # 10s maximum time to spend trying to get the lock
 	LOCK_TTL: 30 # seconds. Time until lock auto expires in redis.

@@ -41,6 +42,7 @@

 	getLock: (doc_id, callback = (error, lockValue) ->) ->
 		startTime = Date.now()
+		testInterval = LockManager.LOCK_TEST_INTERVAL
 		do attempt = () ->
 			if Date.now() - startTime > LockManager.MAX_LOCK_WAIT_TIME
 				e = new Error("Timeout")
@@ -52,7 +54,9 @@
 				if gotLock
 					callback(null, lockValue)
 				else
-					setTimeout attempt, LockManager.LOCK_TEST_INTERVAL
+					setTimeout attempt, testInterval
+					# back off when the lock is taken to avoid overloading
+					testInterval = Math.max(testInterval * 2, LockManager.MAX_TEST_INTERVAL)

 	checkLock: (doc_id, callback = (err, isFree)->)->
 		key = keys.blockingKey(doc_id:doc_id)


better errors

better errors

--- src/driver/druidDriver.coffee
+++ src/driver/druidDriver.coffee
@@ -388,13 +388,22 @@
       callback(e)
       return

-    requester druidQuery.getQuery(), (err, ds) ->
+
+    queryObj = druidQuery.getQuery()
+    requester queryObj, (err, ds) ->
       if err
-        callback(err)
+        callback({
+          message: err
+          query: queryObj
+        })
         return

       if ds.length isnt 1
-        callback("got unexpected result from Druid")
+        callback({
+          message: "unexpected result form Druid (all)"
+          query: queryObj
+          result: ds
+        })
         return

       splits = [{
@@ -502,13 +511,21 @@
       callback(e)
       return

-    requester druidQuery.getQuery(), (err, ds) ->
+    queryObj = druidQuery.getQuery()
+    requester queryObj, (err, ds) ->
       if err
-        callback(err)
+        callback({
+          message: err
+          query: queryObj
+        })
         return

       if ds.length isnt 1
-        callback("unexpected result form Druid")
+        callback({
+          message: "unexpected result form Druid (topN)"
+          query: queryObj
+          result: ds
+        })
         return

       filterAttribute = condensedQuery.split.attribute


collision: cleaning up a bit

collision: cleaning up a bit

--- app/_src/components/blog/estimation-survey/collision.coffee
+++ app/_src/components/blog/estimation-survey/collision.coffee
@@ -51,7 +51,6 @@
 		setTimeout =>
 			@watcher = scrollMonitor.create @$context
 			@watcher.enterViewport @_onEnterViewport
-			@watcher.recalculateLocation()
 		, 1

 	_onEnterViewport: () =>
@@ -80,14 +79,14 @@
 		)

 		@svg.on("mousemove", () ->
-			onInteract(this)
+			onInteraction(this)
 		)
 		@svg.on("touchmove", () ->
-		  onInteract(this)
+		  onInteraction(this)
 		)

 		that = this
-		onInteract = (arg) ->
+		onInteraction = (arg) ->
 			p1 = d3.mouse(arg)
 			that.root.px = p1[0]
 			that.root.py = p1[1]


hot fix

hot fix

--- scripts/befool.blog.coffee
+++ scripts/befool.blog.coffee
@@ -48,8 +48,8 @@
                     robot.emit "tweet_new_posts_in_befool_blog", published_posts.reverse()
                     robot.brain.set 'befool:homepage:old:posts', JSON.stringify(posts)
                 else
-                    published_posts.reverse().each (post) ->
-                        rebot.logger.info "I would post :" + post.title
+                    $(published_posts.reverse()).each (i, post) ->
+                        robot.logger.info "I would post :" + post.title
             else
                 robot.logger.info "But there is not post updated."


fix(message-sidebar): remove faulty component. fixes T2391.

Summary: I'm proposing this patch as a temp fix for this issue. Desired functionality should probably be defined before adding more code.

Test Plan: Attempting to test manually... can't get my runtime edgehill to propagate these changes tho :(

Reviewers: bengotow

Reviewed By: bengotow

Maniphest Tasks: T2391

Differential Revision: https://phab.nylas.com/D1911

--- static/package-template/lib/my-message-sidebar.cjsx
+++ static/package-template/lib/my-message-sidebar.cjsx
@@ -52,7 +52,7 @@
     #    mode={RetinaImg.Mode.ContentIsMask}/>
     #

-
Hi there {@state.contact.name}!

+
{@state.contact.displayName()} is the focused contact.

   _renderPlaceholder: =>


Fix issue where there would be space between screen edge and error

--- framer/Extras/ErrorDisplay.coffee
+++ framer/Extras/ErrorDisplay.coffee
@@ -23,7 +23,7 @@

 	constructor: ->

-		@_context ?= new Context name:"Mobile Error"
+		@_context ?= new Context name:"ErrorDisplay"
 		@_context.index = 1000

 		@_context.run =>
@@ -43,7 +43,7 @@
 			error = new Layer
 				name: "error"
 				y: Align.bottom
-				width: Screen.width
+				width: Canvas.width
 				height: Config.height
 				backgroundColor: "rgba(255,0,0,1)"

@@ -67,13 +67,14 @@
 				@_errorLayer = null

 			@_errorLayer = error
+			@resize()

 		return @_errorLayer

 	resize: =>
 		return unless @_errorLayer
-		@_errorLayer.width = Screen.width
-		@_errorLayer.y = Align.bottom
+		@_errorLayer.width = Canvas.width
+		@_errorLayer.y = Canvas.height - @_errorLayer.height
 		@_errorLayer.text.size = Utils.frameInset(@_errorLayer, Config.textInset)
 		@_errorLayer.text.point = Align.center

@@ -88,11 +89,6 @@
 				scale: 1
 			curve: "spring(800, 55, 10)"

-		# Terrible hacky fix for blurred text bug on Chrome desktop
-		if Utils.isChrome() and Utils.isDesktop()
-			animation.onAnimationEnd ->
-				Utils.delay 0, -> error.text.html = message + " "
-
 	destroy: ->
 		@_context?.destroy()


Only trigger tree-view:directory-modified events when expanded

--- lib/directory-view.coffee
+++ lib/directory-view.coffee
@@ -40,7 +40,7 @@
     if isExpanded then @expand() else @collapse()

     @subscribe @directory, 'entry-added entry-removed', =>
-      @trigger 'tree-view:directory-modified'
+      @trigger 'tree-view:directory-modified' if @isExpanded

   beforeRemove: ->
     @directory.destroy()


Updated nav scroll in the API docs page (#424)

--- app/assets/javascripts/documentation/swagger_inlined.coffee
+++ app/assets/javascripts/documentation/swagger_inlined.coffee
@@ -1,9 +1,10 @@
 scrollToRef = (event) ->
   event.preventDefault()
   target = $(event.target).attr('href')
-  $('body').animate(
-    scrollTop: ($(target).offset().top - 10), 'fast'
-  )
+  window.scrollTo({
+    top: $(target).offset().top - 90,
+    behavior: 'smooth'
+  })

 waypointer = ->
   for l in $('#navmenu-fixed a')
@@ -28,7 +29,7 @@
       log "added key " + key
       window.authorizations.add "access_token", new ApiKeyAuthorization("access_token", key, "query")
     return
-
+
   url = window.swagger_url
   window.swaggerUi = new SwaggerUi(
     url: url
@@ -71,7 +72,6 @@
     localStorage.setItem('access_token', $('#input_apiKey').val())
     return

-  $('#header').headroom()
   $('.set-token').click (e) ->
     e.preventDefault()
     $('#header').addClass('headroom--pinned').removeClass('headroom--unpinned')
@@ -80,14 +80,14 @@
       input_key.val($(e.target).attr('data-token')).change()
       input_key.fadeIn('fast')
     )
-
-
+
+
   $('.newtoken-scope-check').change (e) ->
     app = $(e.target).parents('.application_list_box')
     link = app.find('.authorize_new_token_link')
     checked = app.find('.newtoken-scope-check input:checked')
     scopes = []
-    scopes.push($(s).attr('id')) for s in checked
+    scopes.push($(s).attr('id')) for s in checked
     url = link.attr('data-base')
     url = "#{url}&scope=#{scopes.join('+')}" if scopes.length > 0
     link.attr('href', url).text(url)
@@ -112,4 +112,3 @@
   window.setTimeout (->
     operationsAfterSwaggerLoads()
   ), 2000
-


Update url_manager.coffee

Update url_manager.coffee

--- app/assets/javascripts/oxalis/controller/url_manager.coffee
+++ app/assets/javascripts/oxalis/controller/url_manager.coffee
@@ -20,7 +20,7 @@

   parseUrl : ->

-    stateString = location.hash
+    stateString = location.hash.slice(1)
     state       = {}

     if stateString?


some url manager fix

some url manager fix

--- app/assets/javascripts/oxalis/controller/url_manager.coffee
+++ app/assets/javascripts/oxalis/controller/url_manager.coffee
@@ -11,9 +11,8 @@

   constructor : (@model) ->

-    url           = document.URL
-    @baseUrl      = url.match(/^([^#]*)#?/)[1]
-    @initialState = @parseUrl url
+    @baseUrl      = document.location.pathname
+    @initialState = @parseUrl()

     @update = _.throttle(
       => location.replace(@buildUrl())
@@ -23,9 +22,9 @@
     _.extend(@, Backbone.Events)

-  parseUrl : (url)->
+  parseUrl : ->

-    stateString = url.match(/^.*#([\d.-]*(?:,[\d.-]*)*)$/)?[1]
+    stateString = location.hash.slice(1)
     state       =
       position : null
       mode : null
@@ -53,7 +52,7 @@
     @model.flycam3d.on
       changed : => @update()

-    @listenTo(app.vent, "changeViewMode", @update)
+    #@listenTo(app.vent, "changeViewMode", @update)

   buildUrl : ->


fix loop

fix loop

--- scripts/chat-logger.coffee
+++ scripts/chat-logger.coffee
@@ -224,12 +224,12 @@

       # months of last year
       last_year = _.last(years)
-      for idx,v in chat_data()[room][last_year]
+      for idx,v of chat_data()[room][last_year]
         links.push "#{last_year}/#{idx}" if v

       # days of last month
       last_month = chat_data()[room][last_year].length - 1
-      for idx,v in chat_data()[room][last_year][last_month]
+      for idx,v of chat_data()[room][last_year][last_month]
         links.push "#{last_year}/#{last_month}/#{idx}" if v

     send_links res, "#{base_path}/#{room}", links
@@ -239,7 +239,7 @@
     room = req.params.room
     year = parseInt req.params.year
     if chat_data()[room]?[year]?
-      links.push "#{idx}" if v for idx,v in chat_data()[room][year]
+      links.push "#{idx}" if v for idx,v of chat_data()[room][year]
     send_links res, "#{base_path}/#{room}/#{year}", links

   robot.router.get "/#{base_path}/:room/:year/:month", (req, res) ->
@@ -248,7 +248,7 @@
     year = parseInt req.params.year
     month = parseInt(req.params.month) - 1
     if chat_data()[room]?[year]?[month]?
-      links.push "#{idx}" if v for idx,v in chat_data()[room][year][month]
+      links.push "#{idx}" if v for idx,v of chat_data()[room][year][month]
     send_links res, "#{base_path}/#{room}/#{year}/#{month}", links

   robot.router.get "/#{base_path}/:room/:year/:month/:date", (req, res) ->


Commenting out enterprise creation

Commenting out enterprise creation

--- app/assets/javascripts/darkswarm/services/enterprise_creation_service.js.coffee
+++ app/assets/javascripts/darkswarm/services/enterprise_creation_service.js.coffee
@@ -7,43 +7,43 @@
       country: availableCountries[0]

     create: =>
-      Loading.message = "Creating " + @enterprise.name
-      $http(
-        method: "POST"
-        url: "/api/enterprises"
-        data:
-          enterprise: @prepare()
-        params:
-          token: SpreeApiKey
-      ).success((data) =>
-        Loading.clear()
-        @enterprise.id = data
-        RegistrationService.select('about')
-      ).error((data) =>
-        Loading.clear()
-        console.log angular.toJson(data)
-        alert('Failed to create your enterprise.\nPlease ensure all fields are completely filled out.')
-      )
-      #RegistrationService.select('about')
+      # Loading.message = "Creating " + @enterprise.name
+      # $http(
+      #   method: "POST"
+      #   url: "/api/enterprises"
+      #   data:
+      #     enterprise: @prepare()
+      #   params:
+      #     token: SpreeApiKey
+      # ).success((data) =>
+      #   Loading.clear()
+      #   @enterprise.id = data
+      #   RegistrationService.select('about')
+      # ).error((data) =>
+      #   Loading.clear()
+      #   console.log angular.toJson(data)
+      #   alert('Failed to create your enterprise.\nPlease ensure all fields are completely filled out.')
+      # )
+      RegistrationService.select('about')

     update: (step) =>
-      Loading.message = "Updating " + @enterprise.name
-      $http(
-        method: "PUT"
-        url: "/api/enterprises/#{@enterprise.id}"
-        data:
-          enterprise: @prepare()
-        params:
-          token: SpreeApiKey
-      ).success((data) ->
-        Loading.clear()
-        RegistrationService.select(step)
-      ).error((data) ->
-        Loading.clear()
-        console.log angular.toJson(data)
-        alert('Failed to create your enterprise.\nPlease ensure all fields are completely filled out.')
-      )
-      #RegistrationService.select(step)
+      # Loading.message = "Updating " + @enterprise.name
+      # $http(
+      #   method: "PUT"
+      #   url: "/api/enterprises/#{@enterprise.id}"
+      #   data:
+      #     enterprise: @prepare()
+      #   params:
+      #     token: SpreeApiKey
+      # ).success((data) ->
+      #   Loading.clear()
+      #   RegistrationService.select(step)
+      # ).error((data) ->
+      #   Loading.clear()
+      #   console.log angular.toJson(data)
+      #   alert('Failed to create your enterprise.\nPlease ensure all fields are completely filled out.')
+      # )
+      RegistrationService.select(step)

     prepare: =>
       enterprise = {}


Fixing deploy hooks for reals

Fixing deploy hooks for reals

--- scripts/github-hook.coffee
+++ scripts/github-hook.coffee
@@ -117,18 +117,19 @@
     """

   formatDeployment = (event) ->
-    {creator, sha, name, environment, description} = event.data.deployment
+    {repository, deployment} = event.data
+    {creator, sha, environment} = deployment

     """
-    [#{creator.login}](https://github.com/{creator.login}) started a deployment of #{sha[0..8]} in [#{name}](https://github.com/#{name}) to #{environment}
+    [#{creator.login}](#{creator.html_url}) started a deployment of #{sha[0..8]} in [#{repository.name}](#{repository.html_url}) to #{environment}
     """

   formatDeploymentStatus = (event) ->
     {repository, deployment} = event.data
-    {state, creator, description} = event.data.deployment_status
+    {state, creator} = event.data.deployment_status

     return null if state is "pending"

     """
-    Deployment of [#{repository.name}](https://github.com/#{repository.name}) to #{deployment.environment} by [#{creator.login}](https://github.com/#{creator.login}) has ended in #{state}
+    Deployment of [#{repository.name}](#{repository.html_url}) to #{deployment.environment} by [#{creator.login}](#{creator.html_url}) has ended in #{state}
     """


Check for body in block.

Check for body in block.

--- src/instrumenter.coffee
+++ src/instrumenter.coffee
@@ -75,6 +75,9 @@
         # TODO(Constellation)
         # calculate precise offset or attach in
         # CoffeeScriptRedux compiler
+        notFound =
+            start: {line: 0, column: 0}
+            end: {line: 0, column: 0}
         structured = new StructuredCode(program.raw)
         estraverse.traverse program,
             leave: (node, parent) ->
@@ -91,8 +94,11 @@
                 else
                     node.loc = switch node.type
                         when 'BlockStatement'
-                            start: node.body[0].loc.start
-                            end: node.body[node.body.length - 1].loc.end
+                            if node.body.length
+                                start: node.body[0].loc.start
+                                end: node.body[node.body.length - 1].loc.end
+                            else
+                                notFound
                         when 'VariableDeclarator'
                             if node?.init?.loc?
                                 start: node.id.loc.start
@@ -107,8 +113,7 @@
                             start: node.declarations[0].loc.start
                             end: node.declarations[node.declarations.length - 1].loc.end
                         else
-                            start: {line: 0, column: 0}
-                            end: {line: 0, column: 0}
+                            notFound
                 return

 module.exports = Instrumenter


Add some repl-context commands

Add some repl-context commands

--- lib/ide-haskell-repl.coffee
+++ lib/ide-haskell-repl.coffee
@@ -74,6 +74,9 @@
       'ide-haskell-repl:history-back': commandFunction 'historyBack'
       'ide-haskell-repl:history-forward': commandFunction 'historyForward'
       'ide-haskell-repl:ghci-reload': commandFunction 'ghciReload'
+      'ide-haskell-repl:ghci-reload': commandFunction 'ghciReload'
+      'ide-haskell-repl:reload-repeat': commandFunction 'ghciReloadRepeat'
+      'ide-haskell-repl:toggle-auto-reload-repeat': commandFunction 'toggleAutoReloadRepeat'
       'ide-haskell-repl:ghci-interrupt': commandFunction 'interrupt'

     externalCommandFunction = (func) => ({currentTarget}) =>


Add support TeamDrive

Add support TeamDrive

--- src/lib/gas-manager.coffee
+++ src/lib/gas-manager.coffee
@@ -47,7 +47,8 @@
         request.get({
           url : Manager.DOWNLOAD_URL + fileId,
           qs :{
-            'access_token' : accessToken
+            'access_token' : accessToken,
+            'supportsTeamDrives': true
           }
         }, cb)
       ,(response, body, cb)=>
@@ -90,7 +91,8 @@
           method: 'delete',
           url : "#{API_ROOT}/files/#{fileId}"
           qs : {
-            'access_token' : accessToken
+            'access_token' : accessToken,
+            'supportsTeamDrives': true
           }
         }, cb)
       (response, body, cb)->
@@ -132,7 +134,8 @@
           url : "#{UPLOAD_API_ROOT}/files",
           qs :{
             'convert' : true
-            'access_token' : accessToken
+            'access_token' : accessToken,
+            'supportsTeamDrives': true
           }
         }, cb)
       (response, body, cb)=>
@@ -167,7 +170,8 @@
             json : {title:filename}
             url : "#{API_ROOT}/files/#{fileId}"
             qs :
-              'access_token' : accessToken_
+              'access_token' : accessToken_,
+              'supportsTeamDrives': true
           }, (err, res, b)->
             return cb(err, null, res) if err
             return cb("Bad status code #{res.statusCode}", null, res) if res.statusCode != 200
@@ -197,7 +201,8 @@
           json : {id: toFolderId}
           url : "#{API_ROOT}/files/#{fileId}/parents",
           qs :{
-            'access_token' : accessToken
+            'access_token' : accessToken,
+            'supportsTeamDrives': true
           }
         }, (err, response, body)->
           cb(null, accessToken, response, body)
@@ -210,7 +215,8 @@
           method : 'delete',
           url : "#{API_ROOT}/files/#{fileId}/parents/#{fromFolderId}",
           qs :{
-            'access_token' : accessToken
+            'access_token' : accessToken,
+            'supportsTeamDrives': true
           }
         }, (response, body)->
           cb(null, fileId)
@@ -237,7 +243,8 @@
           }
           url : "#{UPLOAD_API_ROOT}/files/#{fileId}",
           qs :{
-            'access_token' : accessToken
+            'access_token' : accessToken,
+            'supportsTeamDrives': true
           }
         }, cb)
       (response, body ,cb)=>


#5: Allow folding on short files when not autofolding

--- lib/fold-functions.coffee
+++ lib/fold-functions.coffee
@@ -31,19 +31,22 @@
     if atom.config.get('fold-functions.autofold')
       atom.workspace.observeTextEditors (editor) =>
         editor.displayBuffer.tokenizedBuffer.onDidTokenize =>
-          @fold('fold', editor)
+          if shortfileCutoff = atom.config.get('fold-functions.shortfileCutoff')
+            # make sure the file is longer than the cutoff before folding
+            if shortfileCutoff > 0 and editor.getLineCount() >= shortfileCutoff
+              console.log('autofold')
+              @fold('autofold', editor)

   deactivate: ->
     @subscriptions.dispose()

-  fold: (action) ->
+  fold: (action, editor) ->
     if !action then action = 'fold'
-    editor = atom.workspace.getActiveTextEditor()
-    if shortfileCutoff = atom.config.get('fold-functions.shortfileCutoff')
-      if shortfileCutoff > 0 and editor.getLineCount() <= shortfileCutoff
-        return
+    if not editor
+      editor = atom.workspace.getActiveTextEditor()

     @indentLevel = null
+    hasFoldableLines = false
     for row in [0..editor.getLastBufferRow()]
       foldable = editor.isFoldableAtBufferRow(row)
       isFolded = editor.isFoldedAtBufferRow(row)
@@ -63,6 +66,9 @@
       # ignore commented lines
       if isCommented
         continue
+
+      if foldable
+        hasFoldableLines = true

       isFunction = @hasScopeAtBufferRow(editor, row, 'meta.function')
       if foldable and isFunction and not isCommented


add profile settings btn

add profile settings btn

--- app/directives/group-page-toolbar/group-page-toolbar.coffee
+++ app/directives/group-page-toolbar/group-page-toolbar.coffee
@@ -32,6 +32,7 @@
           Toast.show('You\'ve been signed out')

       $scope.menuItems = [
+        {label: 'Profile Settings', onClick: $scope.openProfileSettings, icon: 'person', adminOnly: false},
         {label: 'Email Settings', onClick: $scope.openEmailSettings, icon: 'mail', adminOnly: false},
         {label: 'Give Feedback', onClick: $scope.openFeedbackForm, icon: 'live_help', adminOnly: false},
         {label: 'Admin Panel', onClick: $scope.openAdminPanel, icon: 'local_pizza', adminOnly: true},


CSVData: Fixed parsed for text with more than 1000 lines and opts.defer: true.

The bug would insert an empty line every 1000 lines.

--- src/base/CSVData.coffee
+++ src/base/CSVData.coffee
@@ -166,6 +166,7 @@
 				columns[column_idx] = column_chars.join("")
 				column_chars.splice(0)
 				column_idx = column_idx + 1
+				return

 			end_row = =>
 				if columns.length > 0
@@ -174,6 +175,8 @@
 					columns = []

 				column_idx = 0
+				return
+

 			while (idx < len)
 				char = text.charAt(idx)
@@ -219,6 +222,11 @@
 				# do we have a line ending
 				if charCode == 13 or charCode == 10

+					if (charCode == 13 and nextCharCode == 10)
+						idx = idx + 2
+					else
+						idx = idx + 1
+
 					if in_quotes
 						column_chars.push(String.fromCharCode(10))
 					else
@@ -230,12 +238,8 @@
 							CUI.setTimeout
 								ms: 10
 								call: do_work
+
 							return
-
-					if (charCode == 13 and nextCharCode == 10)
-						idx = idx + 2
-					else
-						idx = idx + 1

 					continue


DOM

DOM

- fixed "elementsUntil": In a detached tree, this function would
  return the wrong result, if called without a selector and
  with no object stopped at. It is expected to return the full
  "path" until no further parentNode is found. This also fixes
  "parents" and "parentUntil", as they utilize "elementsUntil".

--- src/base/DOM/DOM.coffee
+++ src/base/DOM/DOM.coffee
@@ -846,7 +846,10 @@

 			testDocElem = CUI.DOM.parent(testDocElem)
 			if testDocElem == null
-				return null
+				if selector
+					return null
+				else
+					return path

 			path.push(testDocElem)


remove tabIndex>=0 for less unsafe influence

--- content_scripts/link_hints.coffee
+++ content_scripts/link_hints.coffee
@@ -386,7 +386,7 @@
             #     focus it to let user press space to activate the popup menu
             # <object> & <embed>: for Flash games which have their own key event handlers
             #     since we have been able to blur them by pressing `Escape`
-            if clickEl.nodeName.toLowerCase() in ["input", "select", "object", "embed"] or clickEl.tabIndex >= 0
+            if clickEl.nodeName.toLowerCase() in ["input", "select", "object", "embed"]
               clickEl.focus()
             linkActivator clickEl


switch/when instead of if/else

switch/when instead of if/else

--- src/cleaner/cleaner.coffee
+++ src/cleaner/cleaner.coffee
@@ -14,50 +14,52 @@
   for char in line.split ''
     if escape_stack.length
       prev = escape_stack.pop()
-      if state is quoting
-        clean += prev + char
-      else if state is commenting
-        if char is '/'
-          state = ready
-          clean += '  '
-        else
-          clean += '  '
-      else # if state is ready
-        switch char
-          when '*'
+      switch state
+        when quoting
+          clean += prev + char
+        when commenting
+          if char is '/'
+            state = ready
             clean += '  '
-            state = commenting
-          when '/'
-            return clean
-          when '\''
-            clean += prev + char
-            state = quoting
           else
-            clean += prev + char
+            clean += '  '
+        else # ready
+          switch char
+            when '*'
+              clean += '  '
+              state = commenting
+            when '/'
+              return clean
+            when '\''
+              clean += prev + char
+              state = quoting
+            else
+              clean += prev + char
     else
-      if state is quoting
-        switch char
-          when '\\'
+      switch state
+        when quoting
+          switch char
+            when '\\'
+              escape_stack.push char
+            when '\''
+              clean += char
+              state = ready
+            else
+              clean += char
+        when commenting
+          if char is '*'
             escape_stack.push char
-          when '\''
-            clean += char
-            state = ready
           else
-            clean += char
-      else if state is commenting
-        if char is '*'
-          escape_stack.push char
-        else
-          clean += ' '
-      else # if state is ready
-        switch char
-          when '/'
-            escape_stack.push char
-          when '\''
-            clean += char
-            state = quoting
-          else
-            clean += char
+            clean += ' '
+        else # ready
+          switch char
+            when '/'
+              escape_stack.push char
+            when '\''
+              clean += char
+              state = quoting
+            else
+              clean += char
   clean

 cleanInput = (input) ->


Teamwork: Implemented fetching playgrounds from a remote manifest file.

--- client/landing/app/Applications/Teamwork.kdapplication/Views/teamworkapp.coffee
+++ client/landing/app/Applications/Teamwork.kdapplication/Views/teamworkapp.coffee
@@ -28,10 +28,7 @@
           title           : "Playgrounds"
           itemClass       : KDButtonViewWithMenu
           cssClass        : "clean-gray playgrounds-button"
-          menu            :
-            Facebook      :
-              callback    : (panel, workspace) =>
-                @handlePlaygroundSelection "Facebook"
+          menu            : []
         ]
         floatingPanes     : [ "chat" , "terminal", "preview" ]
         layout            :
@@ -52,6 +49,25 @@

     if options.playground
       @handlePlaygroundSelection options.playground
+    else
+      @teamwork.on "PanelCreated", =>
+        @doCurlRequest "https://raw.github.com/koding/Teamwork/master/Playgrounds/manifest.json", (err, manifest) =>
+          @populatePlaygroundsButton manifest
+
+  populatePlaygroundsButton: (playgrounds) ->
+    button   = @teamwork.getActivePanel().headerButtons.Playgrounds
+    menu     = []
+
+    playgrounds.forEach (playground) =>
+      item   = {}
+      {name} = playground
+      item[name] = {}
+      item[name].callback = =>
+        @handlePlaygroundSelection name, playground.manifestUrl
+
+      menu.push item
+
+    button.setOption "menu", menu

   showToolsModal: (panel, workspace) ->
     modal       = new KDModalView
@@ -181,9 +197,8 @@

     return rawOptions

-  handlePlaygroundSelection: (playground) ->
-    manifestPath = "https://raw.github.com/fatihacet/TeamworkPlaygrounds/master/#{playground}.json"
-    @doCurlRequest manifestPath, (err, manifest) =>
+  handlePlaygroundSelection: (playground, manifestUrl) ->
+    @doCurlRequest manifestUrl, (err, manifest) =>
       @teamwork.startNewSession @mergePlaygroundOptions manifest, playground
       @teamwork.container.setClass playground
       @teamwork.on "WorkspaceSyncedWithRemote", =>


Cleanup paginator

Cleanup paginator

--- resources/assets/coffee/react/beatmaps/paginator.coffee
+++ resources/assets/coffee/react/beatmaps/paginator.coffee
@@ -19,36 +19,43 @@
 {div,a,span,i} = React.DOM
 el = React.createElement

-Beatmaps.Paginator = React.createClass
-  componentDidMount: ->
+class Beatmaps.Paginator extends React.PureComponent
+  constructor: (props) ->
+    super props
+
     @throttledAutoPagerOnScroll = _.throttle(@autoPagerOnScroll, 500)
-    $(window).on 'scroll.paginator', @throttledAutoPagerOnScroll
-    Timeout.set 0, @throttledAutoPagerOnScroll
+    @autoPagerTriggerDistance = 3000

-  componentWillUnmount: ->
+  componentDidMount: =>
+    Timeout.set 0, @throttledAutoPagerOnScroll
+    $(window).on 'scroll.paginator', @throttledAutoPagerOnScroll
+    $(document).on 'turbolinks:before-cache.paginator', @componentWillUnmount
+
+
+  componentWillUnmount: =>
     $(window).off '.paginator'
+    $(document).off '.paginator'
     @throttledAutoPagerOnScroll.cancel()

-  render: ->
-    if @props.paging.load || @props.paging.more
-      div
-        className: 'beatmapsets-show-more'
-        if @props.paging.loading
-          el Icon, name: 'refresh', modifiers: ['spin']
-        else if @props.paging.more
-          a
-            href: @props.paging.url
-            className: 'beatmapsets-show-more__link'
-            ref: (el) => @autoPagerTarget = el
-            onClick: @showMore
-            osu.trans('common.buttons.show_more')
-    else
-      div()
+  render: =>
+    return div() if !@props.paging.loading && !@props.paging.more
+
+    div
+      className: 'beatmapsets-show-more'
+      if @props.paging.loading
+        el Icon, name: 'refresh', modifiers: ['spin']
+      else if @props.paging.more
+        a
+          href: @props.paging.url
+          className: 'beatmapsets-show-more__link'
+          ref: (el) => @autoPagerTarget = el
+          onClick: @showMore
+          osu.trans('common.buttons.show_more')

-  autoPagerOnScroll: (e) ->
+  autoPagerOnScroll: =>
     return if !@props.paging.more || @props.paging.loading

     currentTarget = @autoPagerTarget.getBoundingClientRect().top
@@ -60,9 +67,6 @@
     $(document).trigger 'beatmap:load_more'

-  autoPagerTriggerDistance: 3000
-
-
-  showMore: (e) ->
+  showMore: (e) =>
     e.preventDefault()
     $(document).trigger 'beatmap:load_more'


Add a short comment.

Add a short comment.

--- src/block-editor.coffee
+++ src/block-editor.coffee
@@ -62,6 +62,7 @@
         contentId: undefined

     $scope.trustedConcat = ->
+        # The regular expressions protect us from XSS.
         args = Array.prototype.slice.call arguments
         return $sce.trustAsResourceUrl(args.join '')


Call registerOpener from workspace

Call registerOpener from workspace

--- lib/markdown-preview.coffee
+++ lib/markdown-preview.coffee
@@ -5,7 +5,7 @@
     atom.workspaceView.command 'markdown-preview:show', =>
       @show()

-    atom.project.registerOpener (uriToOpen) ->
+    atom.workspace.registerOpener (uriToOpen) ->
       fs = require 'fs-plus'
       url = require 'url'


fix input char computation

fix input char computation

--- src/mentions-kinder.coffee
+++ src/mentions-kinder.coffee
@@ -29,7 +29,8 @@
     window.foo = @

   handleInput: (e)=>
-    char = String.fromCharCode(e.charCode)
+    charCode = e.charCode || e.which || e.keyCode
+    char = String.fromCharCode(charCode)
     if !@isAutocompleting() && @trigger[char]
       e.preventDefault()
       @startAutocomplete(char)


Added problems

Added problems

--- server/materials/data/topics/inclusion_exclusion.coffee
+++ server/materials/data/topics/inclusion_exclusion.coffee
@@ -9,6 +9,9 @@
     return {
         topic: topic("Формула включения-исключения", "Задачи на формулу включения-исключения", [
             label("TODO"),
-            problem(80),
+            problem(552),
         ]),
+        advancedProblems: [
+            problem({testSystem: "codeforces", contest: "585", problem: "E"}),
+        ]
     }


release the lock if it took too long to acquire it

--- services/document-updater/app/coffee/LockManager.coffee
+++ services/document-updater/app/coffee/LockManager.coffee
@@ -41,7 +41,10 @@
 				metrics.inc "doc-not-blocking"
 				timeTaken = profile.log("got lock").end()
 				if timeTaken > MAX_REDIS_REQUEST_LENGTH
-					callback err, false # took too long to get the lock, bail out
+					# took too long, so try to free the lock
+					LockManager.releaseLock doc_id, lockValue, (err, result) ->
+						return callback(err) if err? # error freeing lock
+						callback null, false # tell caller they didn't get the lock
 				else
 					callback err, true, lockValue
 			else


Add Another Color Picker To Show Different States

Add Another colour picker to show the different states that a colour
picker could be in (no colour, new colour, current colour) because this
will show how it in those states.

--- examples/kitchen-sink/coffeescript/kitchen-sink.coffee
+++ examples/kitchen-sink/coffeescript/kitchen-sink.coffee
@@ -18,6 +18,8 @@
       select_example: "thing-value"
       stuff_or_things: ["stuff-value"]
       single_select_typeahead: "stuff-value"
+      colorTwo: "#999"
+      colorThree: "#000"

   # githubSearchUrl: (username) ->
   #   "https://api.github.com/search/users?q=#{username}&per_page=30"
@@ -39,6 +41,7 @@
     saved:
       select_example: true
       time_of_day: true
+      colorTwo: true
       addresses: [{address: true}, {}]
     # errors: ["Test Error", "Moo"]
     onSubmit: @onSubmit,
@@ -196,6 +199,10 @@
             type: "color"
             xs: "12",
             label: false
+          f.input "colorThree",
+            type: "color"
+            xs: "12",
+            label: false
         div className: "row",
           div className: "col-xs-12",
             h3 {}, "Nested Fields (Eg. Has Many)"


Use left joystick for dpad.

Use left joystick for dpad.

--- player.coffee
+++ player.coffee
@@ -121,6 +121,14 @@
     @gl.pixelStorei @gl.UNPACK_FLIP_Y_WEBGL, true

   input_state: (port, device, index, id) =>
+    if id == @core.DEVICE_ID_JOYPAD_LEFT && @inputs[port]?.axes[0] < -.5
+      return true
+    if id == @core.DEVICE_ID_JOYPAD_RIGHT && @inputs[port]?.axes[0] > .5
+      return true
+    if id == @core.DEVICE_ID_JOYPAD_UP && @inputs[port]?.axes[1] < -.5
+      return true
+    if id == @core.DEVICE_ID_JOYPAD_DOWN && @inputs[port]?.axes[1] > .5
+      return true
     @inputs[port]?.buttons[{
       0: 0
       1: 2


fix filename

fix filename

--- plugin.coffee
+++ plugin.coffee
@@ -8,15 +8,15 @@

   class CleanCssPlugin extends wintersmith.ContentPlugin

-    constructor: (@_filename, @base_, @_text) ->
-
+    constructor: (@_filename, @_text) ->
+
     getFilename: ->
-      @_filename
+      @_filename.relative

     render: (locals, contents, templates, callback) ->
-      if @_filename.indexOf('.min.css') is -1
+      if @_filename.relative.indexOf('.min.css') is -1
         try
-          logger.verbose "CleanCss #{@_filename}"
+          logger.verbose "CleanCss #{@_filename.relative}"
           css = cleancss.process(@_text)
           callback null, new Buffer(css)
         catch error
@@ -24,12 +24,12 @@
       else
         callback null, new Buffer @_text

-  CleanCssPlugin.fromFile = (filename, base, callback) ->
-    fs.readFile path.join(base, filename), (error, buffer) ->
+  CleanCssPlugin.fromFile = (filename, callback) ->
+    fs.readFile filename.full, (error, buffer) ->
       if error
         callback error
       else
-        callback null, new CleanCssPlugin filename, base, buffer.toString()
+        callback null, new CleanCssPlugin filename, buffer.toString()

   wintersmith.registerContentPlugin 'style', '**/*.css', CleanCssPlugin
-  callback() # tell the plugin manager we are done+  callback() # tell the plugin manager we are done


Delete define.coffee

Delete define.coffee

更新為js檔案


fixed missleading log file

fixed missleading log file

--- app/coffee/s3Wrapper.coffee
+++ app/coffee/s3Wrapper.coffee
@@ -98,7 +98,7 @@
 		options = buildDefaultOptions(bucketName, "head", key)
 		request options, (err, res)->
 			if err?
-				logger.err err:err, res:res, bucketName:bucketName, key:key, "something went wrong copying file in aws"
+				logger.err err:err, res:res, bucketName:bucketName, key:key, "something went wrong checking file in aws"
 			if !res?
 				logger.err err:err, res:res, bucketName:bucketName, key:key, "no response object returned when checking if file exists"
 				err = new Error("no response from s3 #{bucketName} #{key}")


got realpath working

got realpath working

--- src/command_line.coffee
+++ src/command_line.coffee
@@ -59,7 +59,13 @@
     console.log "Unexpected input. toffee --help for examples"
     process.exit 1
   else
-    recurseRun program.args[0], program.args[0]
+    try
+      start_path = fs.realpathSync program.args[0]
+    catch e
+      console.log "Input file/path not found. toffee --help for examples"
+      process.exit 1
+    start_path = path.normalize start_path
+    recurseRun start_path, start_path

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


Name should be lowecase ch for keypress events

--- src/io/web-keyhandling.coffee
+++ src/io/web-keyhandling.coffee
@@ -19,7 +19,7 @@

 	if pressEvent?
 		ch = pressEvent.char ? String.fromCharCode pressEvent.charCode
-		name ?= ch
+		name ?= ch.toLowerCase()

 	key =
 		ch: ch


Only set default comment date if unspecified

--- lib/argumenta/comment.coffee
+++ lib/argumenta/comment.coffee
@@ -28,7 +28,7 @@
     @commentText   = params.commentText ? params.comment_text
     @discussionId  = params.discussionId ? params.discussion_id
     # Defaults
-    @commentDate = @commentDate or new Date()
+    @commentDate = @commentDate ? new Date()

   # Checks for equality with another comment.
   #


:bug: Fix deprecated minimap methods calls

--- lib/minimap-git-diff.coffee
+++ lib/minimap-git-diff.coffee
@@ -1,11 +1,13 @@
-{Subscriber} = require 'emissary'
+{CompositeDisposable} = require 'event-kit'
 MinimapGitDiffBinding = require './minimap-git-diff-binding'

 class MinimapGitDiff
-  Subscriber.includeInto(this)

   bindings: {}
   pluginActive: false
+  constructor: ->
+    @subscriptions = new CompositeDisposable
+
   isActive: -> @pluginActive
   activate: (state) ->
     @gitDiff = atom.packages.getLoadedPackage('git-diff')
@@ -33,14 +35,14 @@

     @pluginActive = true

-    @subscribe @minimapModule, 'activated', @createBindings
-    @subscribe @minimapModule, 'deactivated', @destroyBindings
+    @subscriptions.add @minimapModule.onDidActivate @createBindings
+    @subscriptions.add @minimapModule.onDidDeactivate @destroyBindings

   deactivatePlugin: ->
     return unless @pluginActive

     @pluginActive = false
-    @unsubscribe()
+    @subscriptions.dispose()
     @destroyBindings()

   createBindings: =>


ensure that options are numerical

ensure that options are numerical

--- src/jquery-picturetape.coffee
+++ src/jquery-picturetape.coffee
@@ -16,6 +16,11 @@
       offset_y: 0
     # Merge default settings with options.
     @settings = $.extend @settings, options
+    # ensure numerical values
+    foreach set in ['x', 'y', 'anchor_x', 'anchor_y', 'offset_x', 'offset_y']
+      @settings[set] = Number(@settings[set])
+
+
     # set parent to relative if needed
     if $(@el).css('position') == 'static'
       $(el).css 'position', 'relative'


Remove logging

Remove logging

--- lib/spell-check-task.coffee
+++ lib/spell-check-task.coffee
@@ -38,17 +38,13 @@

     # Do the work now if not busy or queue it for later.
     @constructor.jobs.unshift(job)
-    if @constructor.jobs.length is 1
-      @constructor.startNextJob()
-    else
-      console.log('Queuing work ' + job.args.id)
+    @constructor.startNextJob() if @constructor.jobs.length is 1

   @piggybackExistingJob: (newJob) ->
     if (@jobs.length > 0)
       for i in [0..@jobs.length-1]
         job = @jobs[i]
         if (@isDuplicateRequest(job, newJob))
-          console.log('Piggybacking ' + job.args.relativePath + ' on ' + job.args.id)
           job.callbacks = job.callbacks.concat(newJob.callbacks)
           return true
     return false
@@ -66,16 +62,11 @@

   @startNextJob: () ->
     job = @jobs[0]
-    console.log('Starting work ' + job.args.id)
     job.task?.start job.args, @dispatchMisspellings

   @dispatchMisspellings: (data) =>
-    console.log('Completed work ' + data.id)
     job = @removeFromArray(@jobs, (j) -> j.args.id is data.id)
     for callback in job.callbacks
       callback(data.misspellings)

-    if @jobs.length > 0
-      @startNextJob()
-    else
-      console.log('Queue is empty')
+    @startNextJob() if @jobs.length > 0


Display errors when they come from ajax

Display errors when they come from ajax

--- js/inner.coffee
+++ js/inner.coffee
@@ -29,7 +29,7 @@
   if xhr.responseText.indexOf("<!DOCTYPE") is 0
     notifyError "An unknown error occurred: (#{xhr.status} - #{xhr.statusText})."
   else
-    notifyError xhr.responseText or xhr.statusText
+    notifyError xhr.responseText or xhr.statusText, null, null, true
 )

 $(document).ajaxSend((ev, xhr, options) ->
@@ -53,13 +53,13 @@
   accepts: {json: "application/json"}
   dataType: "json"

-notifyError = (message, file, line) ->
+notifyError = (message, file, line, always_display) ->
   # jquery errors sometimes call this with a different signature, not sure
   # what's happening there
   if message instanceof Object and file instanceof Object
     message = file.message
     file = null
-  if VM? and window.renderContext? and (window.renderContext.env == "development" or window.renderContext.env == "test")
+  if always_display or (VM? and window.renderContext? and (window.renderContext.env == "development" or window.renderContext.env == "test"))
     if file
       message += "\\nfile: #{file}"
       message += "\\nline: #{line}"


Further reversion

Further reversion

--- src/core/observable.coffee
+++ src/core/observable.coffee
@@ -75,9 +75,9 @@
         _model = @_model(); ko.utils.unwrapObservable(arg) for arg in args = [@key].concat(@args or [])
         kb.utils.wrappedEventWatcher(@)?.emitter(_model or null) # update the event watcher
         if @read
-          @_value.update(@read.apply(@_vm, args))
+          @update(@read.apply(@_vm, args))
         else if !_.isUndefined(_model)
-          kb.ignore => @_value.update(kb.getValue(_model, kb.peek(@key), @args))
+          kb.ignore => @update(kb.getValue(_model, kb.peek(@key), @args))
         return @_value.value()

       write: (new_value) => kb.ignore =>
@@ -88,7 +88,7 @@
           new_value = kb.getValue(_model, kb.peek(@key), @args)
         else if _model
           kb.setValue(_model, kb.peek(@key), unwrapped_new_value)
-        @_value.update(new_value)
+        @update(new_value)

       owner: @_vm
     }
@@ -115,8 +115,10 @@
         # update references
         new_value = kb.getValue(new_model, kb.peek(@key), @args)
         @_model(new_model)
-        if not new_model then  @_value.update(null)
-        else if not _.isUndefined(new_value) then @_value.update(new_value)
+        if not new_model
+          @update(null)
+        else if not _.isUndefined(new_value)
+          @update(new_value)
     }
     kb.EventWatcher.useOptionsOrCreate({event_watcher: event_watcher}, model or null, @, {emitter: @model, update: (=> kb.ignore => @_value.update()), key: @key, path: create_options.path})
     @_value.rawValue() or @_value.update() # wasn't loaded so create
@@ -148,4 +150,12 @@
   # @return [kb.TYPE_UNKNOWN|kb.TYPE_SIMPLE|kb.TYPE_ARRAY|kb.TYPE_MODEL|kb.TYPE_COLLECTION] provides the type of the wrapped value.
   valueType: -> @_value.valueType(kb.peek(@_model), kb.peek(@key))

+  ####################################################
+  # Internal
+  ####################################################
+  # @private
+  update: (new_value) ->
+    return if @__kb_released # destroyed, nothing to do
+    @_value.update.apply(@_value, arguments)
+
 kb.observable = (model, options, view_model) -> new kb.Observable(model, options, view_model)


Mark grouped inputs of `WirePattern` as `required` by default. Refs #202

--- src/lib/Helpers.coffee
+++ src/lib/Helpers.coffee
@@ -116,6 +116,8 @@
   for name in inPorts
     unless component.inPorts[name]
       throw new Error "no inPort named '#{name}'"
+    # Make the port required
+    component.inPorts[name].options.required = true
   for name in outPorts
     unless component.outPorts[name]
       throw new Error "no outPort named '#{name}'"


added invalidation to the Tree for more efficiency

--- src/lib/helpers.coffee
+++ src/lib/helpers.coffee
@@ -7,30 +7,36 @@
     @root = root.replace(/\/$/, '')
     @__map()

+  __relative_path: (full_path) ->
+    full_path.slice(@root.length).replace(/\/_/g, '/') or '/'
+
   __map: ->
     tree = {}
+    @__map_dir(tree, @root)
+    @tree = tree

-    read_dir = (dir) =>
-      files = fs.readdirSync(dir).map (file) =>
-        return if file[0] is '.'
-        file_path = path.join(dir, file)
-        relative_path = file_path.slice(@root.length).replace(/\/_/g, '/')
-
-        unless fs.statSync(file_path).isDirectory()
-          tree[relative_path] = file_path
-          return relative_path
-
-        tree[relative_path] = read_dir(file_path)
-      .filter((f) -> f?)
-      .sort()
+  __map_dir: (tree, dir) ->
+    console.log
+    files = fs.readdirSync(dir).map (file) =>
+      return if file[0] is '.'
+      file_path = path.join(dir, file)

-      {
-        path: dir
-        files: files
-      }
+      if fs.statSync(file_path).isDirectory()
+        @__map_dir(tree, file_path)
+      else
+        @__map_file(tree, file_path)
+
+      @__relative_path(file_path)

-    tree['/'] = read_dir(@root)
-    @tree = tree
+    .filter((f) -> f?)
+    .sort()
+
+    tree[@__relative_path(dir)] =
+      path: dir
+      files: files
+
+  __map_file: (tree, file_path) ->
+    tree[@__relative_path(file_path)] = file_path

   subtree: (root_path) ->
     real_path = @tree[root_path.replace(/\/$/, '')]
@@ -64,6 +70,16 @@
       o = exports.parse_filename(file)
       return file if o.base is opts.base and o.type is opts.type
     null
+
+  invalidate: (full_path) ->
+    relative_path = @__relative_path(full_path)
+    if @is_dir(relative_path)
+      for k in Object.keys(@tree)
+        delete @tree[k] if relative_path is k or k.slice(relative_path.length) + '/' is relative_path
+      @__map_dir(@tree, full_path)
+    else
+      delete @tree[relative_path]
+      @__map_file(@tree, full_path)

 exports.directory_tree = (root) -> new Tree(root)


Expose the mongodb driver database instance from the Database object

--- lib/poutine/database.coffee
+++ lib/poutine/database.coffee
@@ -15,12 +15,12 @@
 # driver, specifically it's Db object.
 class Configuration
   constructor: (name, options = {})->
+    server = new Server(options.host || "127.0.0.1", options.port || 27017)
+    @_client = new Db(name, server, options)
     @_pool = new Pool
       name:     name
       create:   (callback)=>
-        server = new Server(options.host || "127.0.0.1", options.port || 27017)
-        client = new Db(name, server, options)
-        client.open callback
+        @_client.open callback
       destroy:  (connection)->
         connection.close()
       max:      options.pool || 50
@@ -43,6 +43,7 @@
 class Database extends EventEmitter
   constructor: (@_configuration)->
     @_collections = []
+    @db = @_configuration._client
     @ObjectID = require("mongodb").BSONPure.ObjectID
     # Tracks how many times we called begin, only release TCP connection
     # when zero.
@@ -51,6 +52,7 @@
       weak this, ->
         if @_connecting
           @_configuration.release @_connection
+    return this

   # Use this if you need access to the MongoDB driver's connection object.  It
   # passes, error, a connection and a reference to the end method.  Don't forget


Bring back group bundles after a wrong merge.

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -84,6 +84,7 @@
         'resolvePendingRequests','fetchVocabulary', 'fetchMembershipStatuses',
         'setBackgroundImage', 'removeBackgroundImage', 'fetchAdmin', 'inviteByEmail',
         'inviteByEmails', 'inviteByUsername', 'kickMember', 'transferOwnership',
+        'fetchBundle', 'createBundle', 'destroyBundle', 'updateBundle', 'fetchRolesByClientId',
         'remove', 'sendSomeInvitations', 'fetchNewestMembers', 'countMembers'
       ]
     schema          :
@@ -118,6 +119,9 @@
             default       : '1'
           customOptions   : Object
     relationships   :
+      bundle        :
+        targetType  : 'JGroupBundle'
+        as          : 'owner'
       permissionSet :
         targetType  : JPermissionSet
         as          : 'owner'
@@ -328,6 +332,27 @@
       ]
       if 'private' is group.privacy
         queue.push -> group.createMembershipPolicy -> queue.next()
+      if groupData['group-vm'] is 'on'
+        if groupData['member-vm'] is 'on'
+          limits =
+            cpu             : { quota: groupData['vm-cpu'] }
+            ram             : { quota: groupData['vm-ram'] }
+            disk            : { quota: groupData['vm-disk'] }
+            users           : { quota: groupData['vm-user'] }
+            'cpu per user'  : { quota: groupData['vm-cpu-member'] }
+            'ram per user'  : { quota: groupData['vm-ram-member'] }
+            'disk per user' : { quota: groupData['vm-disk-member'] }
+        else
+          limits =
+            cpu             : { quota: groupData['vm-cpu'] }
+            ram             : { quota: groupData['vm-ram'] }
+            disk            : { quota: groupData['vm-disk'] }
+            users           : { quota: groupData['vm-user'] }
+        queue.push -> group.createBundle limits, (err)->
+          if err then callback err
+          else
+            console.log 'group bundle created'
+            queue.next()

       queue.push =>
         @emit 'GroupCreated', { group, creator: owner }
@@ -1194,3 +1219,55 @@
       unless err
         for admin in admins
           admin.sendNotification event, contents
+
+  updateBundle: (formData, callback = (->)) ->
+    @fetchBundle (err, bundle) =>
+      return callback err  if err?
+      bundle.update $set: { overagePolicy: formData.overagePolicy }, callback
+      bundle.fetchLimits (err, limits) ->
+        return callback err  if err?
+        queue = limits.map (limit) -> ->
+          limit.update { $set: quota: formData.quotas[limit.title] }, fin
+        dash queue, callback
+        fin = queue.fin.bind queue
+
+  updateBundle$: permit 'change bundle',
+    success: (client, formData, callback)->
+      @updateBundle formData, callback
+
+  destroyBundle: (callback) ->
+    @fetchBundle (err, bundle) =>
+      return callback err  if err?
+      return callback new KodingError 'Bundle not found!'  unless bundle?
+
+      bundle.remove callback
+
+  destroyBundle$: permit 'change bundle',
+    success: (client, callback) -> @destroyBundle callback
+
+  createBundle: (limits, callback) ->
+    @fetchBundle (err, bundle) =>
+      return callback err  if err?
+      return callback new KodingError 'Bundle exists!'  if bundle?
+
+      JGroupBundle = require '../bundle/groupbundle'
+
+      bundle = new JGroupBundle {}, limits
+      bundle.save (err) =>
+        return callback err  if err?
+
+        @addBundle bundle, callback
+
+  createBundle$: permit 'change bundle',
+    success: (client, limits, callback) -> @createBundle limits, callback
+
+  fetchBundle$: permit 'change bundle',
+    success: (client, rest...) -> @fetchBundle rest...
+
+  getDefaultLimits:->
+    {
+      cpu             : { quota: 1 }
+      ram             : { quota: 64 }
+      disk            : { quota: 500 }
+      users           : { quota: 20 }
+    }


JGroup: fetchData{,At} helpers for handling JGroupData over JGroup inst.

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -546,6 +546,34 @@
         if oldSecretName then "group.secret.#{oldSecretName}"

+  fetchData: (callback) ->
+
+    slug = @getAt 'slug'
+
+    JGroupData = require './groupdata'
+    JGroupData.one { slug }, (err, data) ->
+      return callback err  if err
+
+      if not data
+        data = new JGroupData { slug }
+        data.save (err) ->
+          return callback err  if err
+          callback null, data
+      else
+        callback null, data
+
+
+  fetchDataAt: (path, callback) ->
+
+    @fetchData (err, data) ->
+
+      return callback err  if err
+
+      path = "data.#{path}"  if path.indexOf 'data' != 0
+      callback null, data.getAt path
+
+
+
   sendNotification: (event, contents, callback) ->

     message = {


Fixed issue #1936: Generated chat widget code misses required option chatId.

--- app/assets/javascripts/app/controllers/_channel/chat.coffee
+++ app/assets/javascripts/app/controllers/_channel/chat.coffee
@@ -348,7 +348,7 @@
         params[key] = value
     paramString = ''
     for key, value of params
-      if !_.isEmpty(value)
+      if _.isNumber(value) || _.isBoolean(value) || !_.isEmpty(value)
         if paramString != ''
           # coffeelint: disable=no_unnecessary_double_quotes
           paramString += ",\n"


#431 Refactor

#431 Refactor

--- app/assets/javascripts/oxalis/model/binary/pushqueue.coffee
+++ app/assets/javascripts/oxalis/model/binary/pushqueue.coffee
@@ -96,16 +96,12 @@

     @updatePipeline.executePassAlongAction =>

-      deferred = $.Deferred()
       GzipWorker().send(
         method : "compress"
         args : [transmitBuffer]
-      ).done( (buffer) =>
-        @getSendSocket()
-         .send(buffer)
-         .done(-> deferred.resolve())
+      ).then( (buffer) =>
+        @getSendSocket().send(buffer)
       )
-      return deferred.promise()

   getSendSocket : ->


improved date matching. recognition of dates with a year prefix instead of suffix, and a separate function for dates with no separator

--- matching.coffee
+++ matching.coffee
@@ -315,29 +315,130 @@
     j: j
     token: password[i..j]

-# known bug: this doesn't cover all short dates w/o separators like 111911.
-
-date_rx = /(\d{1,2})( |-|\/|\.|_)?(\d{1,2}?)\2?(19\d{2}|200\d|201\d|\d{2})/
 date_match = (password) ->
+  # match dates with separators 1/1/1911 and dates without 111997
+  result = date_without_sep_match(password).concat date_sep_match(password)
+  console.log result
+  result
+
+date_without_sep_match = (password) ->
+  date_matches = []
+  for digit_match in findall password, /\d{4,8}/ # 1197 is length-4, 01011997 is length 8
+    [i, j] = [digit_match.i, digit_match.j]
+    token = password[i..j]
+    end = token.length
+    candidates_round_1 = [] # parse year alternatives
+    if token.length <= 6
+      candidates_round_1.push # 2-digit year prefix
+        daymonth: token[2..]
+        year: token[0..1]
+        i: i
+        j: j
+      candidates_round_1.push # 2-digit year suffix
+        daymonth: token[0...end-2]
+        year: token[end-2..]
+        i: i
+        j: j
+    if token.length >= 6
+      candidates_round_1.push # 4-digit year prefix
+        daymonth: token[4..]
+        year: token[0..3]
+        i: i
+        j: j
+      candidates_round_1.push # 4-digit year suffix
+        daymonth: token[0...end-4]
+        year: token[end-4..]
+        i: i
+        j: j
+    candidates_round_2 = [] # parse day/month alternatives
+    for candidate in candidates_round_1
+      switch candidate.daymonth.length
+        when 2 # ex. 1 1 97
+          candidates_round_2.push
+            day: candidate.daymonth[0]
+            month: candidate.daymonth[1]
+            year: candidate.year
+            i: candidate.i
+            j: candidate.j
+        when 3 # ex. 11 1 97 or 1 11 97
+          candidates_round_2.push
+            day: candidate.daymonth[0..1]
+            month: candidate.daymonth[2]
+            year: candidate.year
+            i: candidate.i
+            j: candidate.j
+          candidates_round_2.push
+            day: candidate.daymonth[0]
+            month: candidate.daymonth[1..2]
+            year: candidate.year
+            i: candidate.i
+            j: candidate.j
+        when 4 # ex. 11 11 97
+          candidates_round_2.push
+            day: candidate.daymonth[0..1]
+            month: candidate.daymonth[2..3]
+            year: candidate.year
+            i: candidate.i
+            j: candidate.j
+    # final loop: reject invalid dates
+    for candidate in candidates_round_2
+      day = parseInt(candidate.day)
+      month = parseInt(candidate.month)
+      year = parseInt(candidate.year)
+      [valid, [day, month, year]] = check_date(day, month, year)
+      continue unless valid
+      date_matches.push
+        pattern: 'date'
+        i: candidate.i
+        j: candidate.j
+        token: password[i..j]
+        separator: ''
+        day: day
+        month: month
+        year: year
+  date_matches
+
+date_rx_year_suffix = ///
+  ( \d{1,2} )                         # day or month
+  ( \s | - | / | \\ | _ | \. )        # separator
+  ( \d{1,2} )                         # month or day
+  \2                                  # same separator
+  ( 19\d{2} | 200\d | 201\d | \d{2} ) # year
+///
+date_rx_year_prefix = ///
+  ( 19\d{2} | 200\d | 201\d | \d{2} ) # year
+  ( \s | - | / | \\ | _ | \. )        # separator
+  ( \d{1,2} )                         # day or month
+  \2                                  # same separator
+  ( \d{1,2} )                         # month or day
+///
+date_sep_match = (password) ->
   matches = []
-  for match in findall password, date_rx
-    [day, month, year] = (parseInt(match[k]) for k in [1,3,4])
-    separator = match[2] or ''
-    if 12 <= month <= 31 and day <= 12
-      [day, month] = [month, day]
-    if day > 31 or month > 12
-      continue
-    if year < 20
-      year += 2000 # hey, it could be 1920, but this is only for display
-    else if year < 100
-      year += 1900
-    matches.push
-      pattern: 'date'
-      i: match.i
-      j: match.j
-      token: password[match.i..match.j]
-      separator: separator
-      day: day
-      month: month
-      year: year
-  matches
+  for match in findall password, date_rx_year_suffix
+    [match.day, match.month, match.year] = (parseInt(match[k]) for k in [1,3,4])
+    match.sep = match[2]
+    matches.push match
+  for match in findall password, date_rx_year_prefix
+    [match.day, match.month, match.year] = (parseInt(match[k]) for k in [4,3,1])
+    match.sep = match[2]
+    matches.push match
+  for match in matches
+    [valid, [day, month, year]] = check_date(match.day, match.month, match.year)
+    continue unless valid
+    pattern: 'date'
+    i: match.i
+    j: match.j
+    token: password[match.i..match.j]
+    separator: match.sep
+    day: day
+    month: month
+    year: year
+
+check_date = (day, month, year) ->
+  if 12 <= month <= 31 and day <= 12 # tolerate both day-month and month-day order
+    [day, month] = [month, day]
+  if day > 31 or month > 12
+    return [false, []]
+  unless 1900 <= year <= 2019
+    return [false, []]
+  [true, [day, month, year]]


remove turkish comment

remove turkish comment

--- client/landing/app/Applications/Activity.kdapplication/ContentDisplays/Discussion.coffee
+++ client/landing/app/Applications/Activity.kdapplication/ContentDisplays/Discussion.coffee
@@ -130,7 +130,6 @@
       @editDiscussionLink.unsetClass "hidden"
       @deleteDiscussionLink.unsetClass "hidden"

-    # bu daha derinde - SY
     # quick fix - noli me judicare - MSA
     activity.on 'CommentIsAdded', (reply) =>
       activity.repliesCount = reply.repliesCount


nap by default finally

nap by default finally

--- src/manipulators.coffee
+++ src/manipulators.coffee
@@ -2,8 +2,6 @@
 Library of manipulator functions.
 ###
 _ = require 'underscore'
-stylus = require 'stylus'
-nib = require 'nib'

 ###
 Pre-manipulators
@@ -24,9 +22,9 @@
 @compileStylus = (contents, filename) ->
   if filename? and filename.match(/.styl$/)?
     css = ''
-    stylus(contents)
+    require('stylus')(contents)
       .set('filename', filename)
-      .use(nib())
+      .use(require('nib')())
       .render (err, out) -> throw err if err; css = out
     css
   else


Add missing colons

Add missing colons

--- Src/coffeescript/cql-execution/src/elm/quantity.coffee
+++ Src/coffeescript/cql-execution/src/elm/quantity.coffee
@@ -72,7 +72,7 @@
   , 'a': 'a_j', 'ANN': 'a_j', 'ann': 'a_j', 'A': 'a_j'
   , 'months': 'mo_g', 'month':'mo_g'
   , 'mo': 'mo_j', 'MO': 'mo_j'
-  , 'weeks': 'week', 'week': 'week', 'wk', 'week', 'WK', 'week'
+  , 'weeks': 'week', 'week': 'week', 'wk': 'week', 'WK': 'week'
   , 'days': 'day', 'day':'day', 'd': 'day', 'D': 'day'
   , 'hours': 'hour', 'hour': 'hour', 'h': 'hour', 'H': 'hour'
   , 'minutes': 'minute', 'minute': 'minute', 'min': 'minute', 'MIN': 'minute'


Coffee-style syntax improvement

Coffee-style syntax improvement

--- src/dom_text_mapper.coffee
+++ src/dom_text_mapper.coffee
@@ -308,18 +308,22 @@

   # Helper function for position calculating
-  _calculatePositionIndex(node, startInfo, shouldRestoreSelection = true) =>
-    if startInfo then text = @_findFirstTextNode node    # Get the first text node
-    else text = @_findLastTextNode node       # Get the last text node
+  _calculatePositionIndex(node, isStart, shouldRestoreSelection = true) =>
+    text = if isStart
+      @_findFirstTextNode node    # Get the first text node
+    else
+      @_findLastTextNode node       # Get the last text node
     return null unless text            # Return if there is no text node
     origText = text.nodeValue          # Save the original text
     return null unless origText.length      # Return if it's empty
     @_saveSelection() if shouldRestoreSelection # Save the original selection
     origCorpus = @_getFreshCorpus false, true # Save the original corpus
-    origChar = if startInfo then origText[0] else origText[origText.length-1] # Get the original character
+    origChar = if isStart then origText[0] else origText[origText.length-1] # Get the original character
     changedChar = if origChar is "." then "," else "." # Choose a replacement
-    if startInfo then changedText = changedChar + origText.substring(1) # Calculate new text
-    else changedText = origText.substr(0, origText.length - 1) + changedChar
+    changedText = if isStart
+      changedChar + origText.substring(1) # Calculate new text
+    else
+      origText.substr(0, origText.length - 1) + changedChar
     text.nodeValue = changedText   # Actually change the text
     changedCorpus = @_getFreshCorpus false, true  # Check the current corpus
     index = @_getDiffIndex origCorpus, changedCorpus # Find the difference
@@ -331,7 +335,7 @@
     # The final result is the position of the difference in corpus
     res =
       page: 0
-    if startInfo then res.start = index else res.end = index + 1
+    if isStart then res.start = index else res.end = index + 1
     res

   # Calculates the starting position of a given node


Add "Run Package Specs" menu item

Add "Run Package Specs" menu item

--- menus/base.cson
+++ menus/base.cson
@@ -55,6 +55,7 @@
         submenu: [
           { label: 'Open In Dev Mode...', command: 'application:open-dev' }
           { label: 'Run Atom Specs', command: 'application:run-all-specs' }
+          { label: 'Run Package Specs', command: 'window:run-package-specs' }
           { label: 'Toggle Developer Tools', command: 'window:toggle-dev-tools' }
         ]
       }


fix pandoc parser math

fix pandoc parser math

--- lib/pandoc-convert.coffee
+++ lib/pandoc-convert.coffee
@@ -182,7 +182,7 @@
 # callback(error, html)
 pandocRender = (text='', {args, projectDirectoryPath, fileDirectoryPath}, callback)->
   args = args or []
-  args = ['-f', atom.config.get('markdown-preview-enhanced.pandocMarkdownFlavor') + ('-tex_math_dollars'),
+  args = ['-f', atom.config.get('markdown-preview-enhanced.pandocMarkdownFlavor'), # -tex_math_dollars doesn't work properly
           '-t', 'html']
           .concat(args).filter((arg)->arg.length)


clearer default tool names

clearer default tool names

--- app/models/dashboard.coffee
+++ app/models/dashboard.coffee
@@ -16,7 +16,7 @@
   createTool: (toolType) =>
     @get('tools').add
       type: toolType
-      name: "new-tool-#{@count}"
+      name: "new-#{toolType}-#{@count}"
       channel: "#{toolType}-#{@count}"
     @count += 1


fix Chinese character problem when pandoc exports.

--- lib/process-graphs.coffee
+++ lib/process-graphs.coffee
@@ -167,7 +167,7 @@
             outputType = options.output || 'text'

             if outputType == 'text'
-              cb(null, {start, end, content, type: 'code_chunk', hide: options.hide, data: "```\n#{data.trim()}\n```\n", cmd})
+              cb(null, {start, end, content, type: 'code_chunk', hide: options.hide, data: "<pre>#{data.trim()}</pre>", cmd})
             else if outputType == 'none'
               cb(null, {start, end, content, type: 'code_chunk', hide: options.hide, cmd})
             else if outputType == 'html'


Tweaks

Tweaks

--- lib/relations/many.coffee
+++ lib/relations/many.coffee
@@ -93,8 +93,8 @@
             # Update foreign keys of relations that should no longer point to this model
             (query = {})[@foreign_key] = model.attributes.id
             @reverse_model_type.cursor({$ids: dependent_ids}).toModels (err, attached_models) =>
+              @_clearDependentSaves(model)
               @_clearHasOneRelatedModels(attached_models, callback)
-              @_clearDependentSaves(model)

         return queue.await callback

@@ -160,7 +160,7 @@
     throw new Error "\nModel added twice: #{util.inspect(current_related_model)}\nand\n#{util.inspect(related_model)}" if current_related_model
     collection.add(related_model)

-## TODO: update isLoaded when adding / removing
+  # TODO: update isLoaded when adding / removing
   remove: (model, related_model) ->
     collection = @_ensureCollection(model)
     current_related_model = collection.get(related_model.id)
@@ -187,8 +187,7 @@
     (model._orm.dependent_saves or= {})[@key] or= []
     model._orm.dependent_saves[@key].push(id)

-  _clearDependentSaves: (model) ->
-    delete model._orm?.dependent_saves?[@key]
+  _clearDependentSaves: (model) -> delete model._orm?.dependent_saves?[@key]

   # TODO: ensure initialize is called only once and only from initializeModel
   _ensureCollection: (model) -> @_bindBacklinks(model)


Run simulations immediately even in static mode

--- src/code/stores/simulation-store.coffee
+++ src/code/stores/simulation-store.coffee
@@ -73,8 +73,7 @@

     @settings.isTimeBased = simulationType is AppSettingsStore.store.SimulationType.time

-    if @settings.isTimeBased
-      @_runSimulation()
+    @_runSimulation()

     if simulationType is AppSettingsStore.store.SimulationType.diagramOnly
       SimulationActions.collapseSimulationPanel()
@@ -83,8 +82,7 @@
     @settings.simulationPanelExpanded = true
     @settings.modelIsRunning = true
     @_updateModelIsRunnable()
-    if @settings.isTimeBased
-      @_runSimulation()
+    @_runSimulation()
     @notifyChange()

   onCollapseSimulationPanel: ->


type to create a new input cell, cleanup

--- app/scripts/notebook.coffee
+++ app/scripts/notebook.coffee
@@ -19,6 +19,8 @@
     console.error error
     newOutputCell.html displayPretty(error) + "
See console for details."
     newOutputCell.addClass "error"
+  insertCellCursorAfter newOutputCell
+  inputCell.editor.getInputField().blur()

 findOrCreateCellCursor = () ->
   cursor = $("#cursor")
@@ -35,21 +37,18 @@

 moveCellCursorDown = () ->
-  console.log "moveCellCursorDown"
   cursor = findOrCreateCellCursor()
   cell = $(cursor).next("div.cell")
   if cell
     insertCellCursorAfter(cell)

 moveCellCursorUp = () ->
-  console.log "moveCellCursorUp"
   cursor = findOrCreateCellCursor()
   cell = $(cursor).prev("div.cell")
   if cell
     insertCellCursorBefore(cell)

 moveCellCursorRight = () ->
-  console.log "moveCellCursorRight"
   cursor = findOrCreateCellCursor()
   nextCell = $(cursor).next(".cell")
   textarea = nextCell.children("textarea")
@@ -57,14 +56,16 @@
   editor.focus()
   cursor.remove()

+setCursorToEndOfDoc = (editor) ->
+  editor.setCursor editor.lineCount() - 1, editor.getLine(editor.lineCount() - 1).length
+
 moveCellCursorLeft = () ->
-  console.log "moveCellCursorLeft"
   cursor = findOrCreateCellCursor()
   previousCell = $(cursor).prev(".cell")
   textarea = previousCell.children("textarea")
   editor = textarea[0].editor
   editor.focus()
-  editor.setCursor editor.lineCount() - 1, editor.getLine(editor.lineCount() - 1).length
+  setCursorToEndOfDoc(editor)
   cursor.remove()

@@ -103,6 +104,7 @@
     mode: "coffeescript"
   inputCell.editor = editor
   addKeyMap editor, inputCell
+  editor

 for i in [1, 2, 3]
@@ -113,3 +115,14 @@
 $(document).bind 'keydown', 'up', (ev) -> moveCellCursorUp()
 $(document).bind 'keydown', 'right', (ev) -> moveCellCursorRight()
 $(document).bind 'keydown', 'left', (ev) -> moveCellCursorLeft()
+
+$(document).keypress (ev) ->
+  if ev.target.nodeName.toLowerCase() == "body"
+    # we're not inside a textarea, create a new input cell
+    inputCellDiv = $ '
<textarea id="input_1" rows="3" cols="80"></textarea>
'
+    $(findOrCreateCellCursor()).replaceWith(inputCellDiv)
+    textarea = inputCellDiv.children("textarea")[0]
+    textarea.value = String.fromCharCode(ev.which)
+    editor = makeInputCell(textarea)
+    setCursorToEndOfDoc(editor)
+    editor.focus()