Bye btc

Bye btc

--- scripts/bitcoin.coffee
+++ scripts/bitcoin.coffee
@@ -1,50 +0,0 @@
-# Description:
-#   Find the latest Bitcoin price in specified currency
-#
-# Dependencies:
-#   "cheerio": ""
-#
-# Configuration:
-#   None
-#
-# Commands:
-#   hubot bitcoin price (in) <currency>
-#
-# Author:
-#   Fred Wu
-
-cheerio = require('cheerio')
-
-module.exports = (robot) ->
-  robot.respond /bitcoin price\s(in\s)?(.*)/i, (msg) ->
-    currency = msg.match[2].trim().toUpperCase()
-    bitcoinPrice(msg, currency)
-
-bitcoinPrice = (msg, currency) ->
-  msg
-    .send "Looking up... sit tight..."
-  msg
-    .http("http://bitcoinprices.com/")
-    .get() (err, res, body) ->
-      msg.send "#{getPrice(currency, body)}"
-
-getPrice = (currency, body) ->
-  $ = cheerio.load(body)
-
-  lastPrice   = null
-  highPrice   = null
-  lowPrice    = null
-  priceSymbol = null
-
-  $('table.currencies td.symbol').each (i) ->
-    if $(this).text() == currency
-      priceSymbol = $(this).next().next().next().next().next().next().text()
-      lastPrice = "#{priceSymbol}#{$(this).next().next().next().next().next().text()}"
-      highPrice = "#{priceSymbol}#{$(this).next().next().next().text()}"
-      lowPrice  = "#{priceSymbol}#{$(this).next().next().next().next().text()}"
-      false
-
-  if lastPrice == null
-    "Can't find the price for #{currency}. :("
-  else
-    "#{currency}: #{lastPrice} (H: #{highPrice} | L: #{lowPrice})"


fix a minor localization bug

fix a minor localization bug

--- library/exposure/localized.coffee
+++ library/exposure/localized.coffee
@@ -158,10 +158,10 @@
         assert session = @session, "no session detected"
         negotiator = new require("negotiator") @request
         neg = negotiator.language(_.keys(cache) or [])
+        delete @session.language unless @session.langlock
         selector = language or @session.language or neg
         selector = selector or "en" # hardcoded default
         assert not _.isEmpty(sel = selector), "lang fail"
-        delete @session.language unless @session.langlock
         @session.language ?= sel if neg and sel isnt "en"
         assert messages = cache[selector] or new Object
         amount = _.keys(messages).length # of messages


Added tiny alien species

Added tiny alien species

--- src/definitions/creature-species.coffee
+++ src/definitions/creature-species.coffee
@@ -20,4 +20,10 @@
 	typeName: 'violent-donkey'

 	name: 'violent donkey'
-	symbol: 'h'+	symbol: 'h'
+
+class exports.TinyAlien extends Species
+	typeName: 'tiny-alien'
+
+	name: 'tiny alien'
+	symbol: 'i'


[fix] Fix test if this is a multiline account (#679)

Should fix #672

--- server/konnectors/freemobile.coffee
+++ server/konnectors/freemobile.coffee
@@ -263,7 +263,7 @@
     # YYYYMM_freemobile_NNNNNNNNNN.pdf (NN..NN is line number)
     # - Import overall pdf with name YYYYMM_freemobile.pdf

-    isMultiline = $('div[class="consommation"]').length > 1
+    isMultiline = $('div.infosConso').length > 1
     $('div.factLigne.is-hidden').each ->
         amount = $($(this).find('.montant')).text()
         amount = amount.replace '€', ''


Bug fix

Bug fix

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -40,7 +40,7 @@
           port: '3000'
           base: 'dev/'
           open:
-            target: 'http://localhost:<%= connect.server.options.port %>/jade/'
+            target: 'http://localhost:<%= connect.server.options.port %>/jade/jade.html'

     watch:
       options:


Never assign a 0 height or width when measuring editor scroll view

--- src/editor-scroll-view-component.coffee
+++ src/editor-scroll-view-component.coffee
@@ -182,15 +182,17 @@
   measureHeightAndWidth: ->
     return unless @isMounted()

-    {editor} = @props
     node = @getDOMNode()
     computedStyle = getComputedStyle(node)
+    {editor} = @props

     unless computedStyle.height is '0px'
-      editor.setHeight(node.clientHeight)
+      clientHeight = node.clientHeight
+      editor.setHeight(clientHeight) if clientHeight > 0

     unless computedStyle.width is '0px'
-      editor.setWidth(node.clientWidth)
+      clientWidth = node.clientWidth
+      editor.setWidth(clientWidth) if clientHeight > 0

   focus: ->
     @refs.input.focus()


Use express-partials >= 0.0.5

Use express-partials >= 0.0.5


minor comment

minor comment

--- fizzylogo_Objects.coffee
+++ fizzylogo_Objects.coffee
@@ -170,7 +170,8 @@
           # parts of the signature
           continue

-      # ok a signature has matched completely
+      # ok we took a signature, and now here it either matched or it didn't,
+      # as indicated by the soFarEverythingMatched flag

       if eachSignature.isEmpty() and soFarEverythingMatched


rephrasing of "handling operator precedence" code in Objects.coffee. Pushing as separate commit because it makes it look like a big code change but it's not, it's just a whole lot of code being indented. I wanted to stress with the previous commit how small of a change this actually is.

--- fizzylogo_Objects.coffee
+++ fizzylogo_Objects.coffee
@@ -73,6 +73,17 @@
       eachSignature = classContainingMethods.msgPatterns[eachSignatureIndex]
       eachPriority = classContainingMethods.priorities[eachSignatureIndex]

+      goodMatchSoFar = true
+
+      #if eachSignature.flToString() == "( + ( operandum ) )"
+      #  log "obtained eachPriority: " + eachPriority
+      log "previousPriority, eachPriority: " + previousPriority + " , " + eachPriority
+      if previousPriority? and eachPriority?
+        if previousPriority <= eachPriority
+          log "breaking matching due to priority going up: previousPriority, eachPriority: " + previousPriority + " , " + eachPriority
+          goodMatchSoFar = false
+
+
       #log "evaluation " + indentation() + "  matching - checking if this signature matches: " + eachSignature.flToString()
       methodInvocation = methodInvocationToBeChecked

@@ -87,108 +98,95 @@
       #log "evaluation " + indentation() + "  matching - checking if signature matches this invocation " + methodInvocation.flToString()
       #log "evaluation " + indentation() + "  matching - checking if signature matches this invocation " + methodInvocation.flToString()

-      soFarEverythingMatched = true
       originalMethodInvocationStart = methodInvocation.cursorStart

-      until eachSignature.isEmpty() or methodInvocation.isEmpty()
-
-        # TODO this should be before the "until" loop
-        # but we put it here for the time being to be
-        # safe
-        #if eachSignature.flToString() == "( + ( operandum ) )"
-        #  log "obtained eachPriority: " + eachPriority
-
-        log "previousPriority, eachPriority: " + previousPriority + " , " + eachPriority
-        if previousPriority? and eachPriority?
-          if previousPriority <= eachPriority
-            log "breaking matching due to priority going up: previousPriority, eachPriority: " + previousPriority + " , " + eachPriority
-            soFarEverythingMatched = false
-            break
-
-        log "evaluation " + indentation() + "  matching: - next signature piece: " + eachSignature.flToString() + " is token: " + " with: " + methodInvocation.flToString()
-
-        [eachElementOfSignature, eachSignature] = eachSignature.nextElement()
-
-
-        # the element of a signature can only be of two kinds:
-        # a token or an FLList containing one parameter (with
-        # prepended "@" in case the parameter doesn't require
-        # evaluation)
-
-        if eachElementOfSignature.flClass != FLList and eachElementOfSignature.flClass != FLToken
-          theContext.throwing = true
-          # TODO this error should really be a stock error referanceable
-          # from the workspace because someone might want to catch it.
-          theContext.returned = FLException.createNew "signature of a method should only contain tokens or lists. Found instead: " + eachElementOfSignature.flToString() + " . Perhaps some variable in the signature has been closed?"
-          return [theContext, methodInvocationToBeChecked]
-
-        if eachElementOfSignature.flClass == FLToken
-          # if the signature contains a token, the message
-          # must contain the same token, otherwise we don't
-          # have a match.
-
-          [eachElementOfInvocation, methodInvocation] = methodInvocation.nextElement()
-
-
-          #log "evaluation " + indentation() + "  matching tokens: - next signature piece: " + eachElementOfSignature.flToString() + " is token: " + (eachElementOfSignature.flClass == FLToken) + " with: " + eachElementOfInvocation.flToString()
-
-          # ok at least the message contains a token, but
-          # now we have to check that they spell the same
-          log "******* evaluation " + indentation() +
-            "  matching tokens: - next signature piece: " +
-            eachElementOfSignature.flToString() +
-            " is token: " + (eachElementOfSignature.flClass == FLToken) +
-            " with: " + eachElementOfInvocation.flToString()
-          if eachElementOfSignature.value == eachElementOfInvocation.value
-            log "evaluation " + indentation() + "  matching - token matched: " + eachElementOfSignature.flToString()
-            # OK good match of tokens,
-            # check the next token in the signature
+      if goodMatchSoFar
+        until eachSignature.isEmpty() or methodInvocation.isEmpty()
+
+          log "evaluation " + indentation() + "  matching: - next signature piece: " + eachSignature.flToString() + " is token: " + " with: " + methodInvocation.flToString()
+
+          [eachElementOfSignature, eachSignature] = eachSignature.nextElement()
+
+
+          # the element of a signature can only be of two kinds:
+          # a token or an FLList containing one parameter (with
+          # prepended "@" in case the parameter doesn't require
+          # evaluation)
+
+          if eachElementOfSignature.flClass != FLList and eachElementOfSignature.flClass != FLToken
+            theContext.throwing = true
+            # TODO this error should really be a stock error referanceable
+            # from the workspace because someone might want to catch it.
+            theContext.returned = FLException.createNew "signature of a method should only contain tokens or lists. Found instead: " + eachElementOfSignature.flToString() + " . Perhaps some variable in the signature has been closed?"
+            return [theContext, methodInvocationToBeChecked]
+
+          if eachElementOfSignature.flClass == FLToken
+            # if the signature contains a token, the message
+            # must contain the same token, otherwise we don't
+            # have a match.
+
+            [eachElementOfInvocation, methodInvocation] = methodInvocation.nextElement()
+
+
+            #log "evaluation " + indentation() + "  matching tokens: - next signature piece: " + eachElementOfSignature.flToString() + " is token: " + (eachElementOfSignature.flClass == FLToken) + " with: " + eachElementOfInvocation.flToString()
+
+            # ok at least the message contains a token, but
+            # now we have to check that they spell the same
+            log "******* evaluation " + indentation() +
+              "  matching tokens: - next signature piece: " +
+              eachElementOfSignature.flToString() +
+              " is token: " + (eachElementOfSignature.flClass == FLToken) +
+              " with: " + eachElementOfInvocation.flToString()
+            if eachElementOfSignature.value == eachElementOfInvocation.value
+              log "evaluation " + indentation() + "  matching - token matched: " + eachElementOfSignature.flToString()
+              # OK good match of tokens,
+              # check the next token in the signature
+              continue
+            else
+              # no match between the tokens, check next signature
+              goodMatchSoFar = false
+              break
+
+          else
+            # the signature has a param. we have to check if
+            # it requires an evaluation or not
+            log "evaluation " + indentation() + "  matching - getting the token inside the parameter: " + eachElementOfSignature.flToString()
+            paramToken = eachElementOfSignature.getParamToken()
+            #dir paramToken
+            log "evaluation " + indentation() + "  matching - token inside the parameter: " + paramToken.flToString()
+            if eachElementOfSignature.isEvaluatingParam()
+              log "evaluation " + indentation() + "  matching - need to evaluate next msg element from invocation: " + methodInvocation.flToString() + " and bind to: " + paramToken.flToString()
+
+              # note how we need to evaluate the params in a context that has the
+              # same SELF as the calling one, not the new one that
+              # we are creating with the new SELF of the callee, otherwise, say,
+              # passing self, self would always bind
+              # to the receiver, which we don't want
+              # like in "7 * self" we don't want to bind self to 7
+
+              # yield from
+              [returnedContext, methodInvocation] = methodInvocation.partialEvalAsMessage theContext, eachPriority
+
+              valueToBeBound = returnedContext.returned
+
+            else
+              # don't need to evaluate the parameter
+              log "evaluation " + indentation() + "  matching - need to get next msg element from invocation: " + methodInvocation.flToString() + " and bind to: " + paramToken.flToString()
+              [valueToBeBound, methodInvocation] = methodInvocation.nextElement()
+
+
+            log "evaluation " + indentation() + "  matching - adding paramater " + paramToken.flToString() + " to tempVariables dictionary in current frame"
+            newContext.tempVariablesDict[ValidIDfromString paramToken.value] = valueToBeBound
+
+            # ok we matched a paramenter, now let's keep matching further
+            # parts of the signature
             continue
-          else
-            # no match between the tokens, check next signature
-            soFarEverythingMatched = false
-            break
-
-        else
-          # the signature has a param. we have to check if
-          # it requires an evaluation or not
-          log "evaluation " + indentation() + "  matching - getting the token inside the parameter: " + eachElementOfSignature.flToString()
-          paramToken = eachElementOfSignature.getParamToken()
-          #dir paramToken
-          log "evaluation " + indentation() + "  matching - token inside the parameter: " + paramToken.flToString()
-          if eachElementOfSignature.isEvaluatingParam()
-            log "evaluation " + indentation() + "  matching - need to evaluate next msg element from invocation: " + methodInvocation.flToString() + " and bind to: " + paramToken.flToString()
-
-            # note how we need to evaluate the params in a context that has the
-            # same SELF as the calling one, not the new one that
-            # we are creating with the new SELF of the callee, otherwise, say,
-            # passing self, self would always bind
-            # to the receiver, which we don't want
-            # like in "7 * self" we don't want to bind self to 7
-
-            # yield from
-            [returnedContext, methodInvocation] = methodInvocation.partialEvalAsMessage theContext, eachPriority
-
-            valueToBeBound = returnedContext.returned
-
-          else
-            # don't need to evaluate the parameter
-            log "evaluation " + indentation() + "  matching - need to get next msg element from invocation: " + methodInvocation.flToString() + " and bind to: " + paramToken.flToString()
-            [valueToBeBound, methodInvocation] = methodInvocation.nextElement()
-
-
-          log "evaluation " + indentation() + "  matching - adding paramater " + paramToken.flToString() + " to tempVariables dictionary in current frame"
-          newContext.tempVariablesDict[ValidIDfromString paramToken.value] = valueToBeBound
-
-          # ok we matched a paramenter, now let's keep matching further
-          # parts of the signature
-          continue

       # ok we took a signature, and now here it either matched or it didn't,
-      # as indicated by the soFarEverythingMatched flag
-
-      if eachSignature.isEmpty() and soFarEverythingMatched
+      # as indicated by the goodMatchSoFar flag
+
+      if eachSignature.isEmpty() and goodMatchSoFar

         # now, the correct PC that we need to report is
         # the original plus what we consumed from matching the


fixing partialEvalAsMessage call (called with one parameter too many)

--- fizzylogo_Objects.coffee
+++ fizzylogo_Objects.coffee
@@ -153,7 +153,7 @@
             # like in "7 * self" we don't want to bind self to 7

             # yield from
-            [returnedContext, methodInvocation] = methodInvocation.partialEvalAsMessage theContext, methodInvocation
+            [returnedContext, methodInvocation] = methodInvocation.partialEvalAsMessage theContext

             valueToBeBound = returnedContext.returned


Fix paper type field behavior when saving MMT

--- app/assets/javascripts/controllers/manuscript_manager_template_edit_controller.js.coffee
+++ app/assets/javascripts/controllers/manuscript_manager_template_edit_controller.js.coffee
@@ -50,7 +50,7 @@
       phase.set('name', oldName)

     saveTemplate: (transition)->
-      @toggleProperty 'editMode'
+      @set 'editMode', false
       @get('model').save().then( (template) =>
         @set('dirty', false)
         @set('errorText', '')


Update migration 7 to interate over all messages with urls

--- server/startup/migrations/v7.coffee
+++ server/startup/migrations/v7.coffee
@@ -4,7 +4,7 @@
 		up: ->

 			console.log 'Populate urls in messages'
-			query = ChatMessage.find({ 'urls.0': { $exists: true }, urls: { $type: 2 } })
+			query = ChatMessage.find({ 'urls.0': { $exists: true } })
 			count = query.count()
 			query.forEach (message, index) ->
 				console.log "#{index + 1} / #{count}"


comando elena .*

comando elena .*

--- scripts/example.coffee
+++ scripts/example.coffee
@@ -37,5 +37,5 @@
 	robot.respond /elena/i, (res) ->
 		res.reply 'Tutto qui?\n... prova a vedere qui: https://www.google.it/search?q=elena+riz&tbm=isch&tbo=u&source=univ&sa=X&ved=0ahUKEwjrqqrE7dvKAhWEPhQKHaHhDYkQsAQIHw&biw=1920&bih=507#tbm=isch&q=elena+riz\n\nSe ne vuoi qualcuna nuda... basta chiedere!'

-	robot.respond /elena nuda/i, (res) ->
-		res.reply 'Brutto porco!\n... prova a vedere qui: https://www.google.it/search?q=elena+riz&tbm=isch&tbo=u&source=univ&sa=X&ved=0ahUKEwjrqqrE7dvKAhWEPhQKHaHhDYkQsAQIHw&biw=1920&bih=507#tbm=isch&q=elena+riz+nuda'
+	robot.respond /elena (.*)/i, (res) ->
+		res.reply 'Brutto porco!\n... prova a vedere qui: https://www.google.it/search?q=elena+riz&tbm=isch&tbo=u&source=univ&sa=X&ved=0ahUKEwjrqqrE7dvKAhWEPhQKHaHhDYkQsAQIHw&biw=1920&bih=507#tbm=isch&q=elena+riz+'+res.match[1]


Fow crash when there are no contact to import

--- server/utils/import_contacts.coffee
+++ server/utils/import_contacts.coffee
@@ -65,25 +65,28 @@
             , contacts.accountName

         numberProcessed = 0
-        total = contacts.google?.length
-        async.eachSeries contacts.google, (gContact, cb) ->
-            GoogleContactHelper.updateCozyContact gContact, contacts
-            , contacts.accountName, token, (err, updatedContact)->
-                numberProcessed += 1
-                realtimer.sendContacts
-                    number: numberProcessed
-                    total: total
+        if not contacts.google?
+            callback()
+        else
+            total = contacts.google.length
+            async.eachSeries contacts.google, (gContact, cb) ->
+                GoogleContactHelper.updateCozyContact gContact, contacts
+                , contacts.accountName, token, (err, updatedContact)->
+                    numberProcessed += 1
+                    realtimer.sendContacts
+                        number: numberProcessed
+                        total: total

-                # Throttle the async loop to prevent performance burst.
-                setTimeout -> cb err
-                , 1
-        , (err)->
-            return callback err if err
-            _ = localizationManager.t
-            notification.createOrUpdatePersistent "leave-google-contacts",
-                app: 'import-from-google'
-                text: _ 'notif_import_contact', total: total
-                resource:
-                    app: 'contacts'
-                    url: 'contacts/'
-            callback()
+                    # Throttle the async loop to prevent performance burst.
+                    setTimeout -> cb err
+                    , 1
+            , (err)->
+                return callback err if err
+                _ = localizationManager.t
+                notification.createOrUpdatePersistent "leave-google-contacts",
+                    app: 'import-from-google'
+                    text: _ 'notif_import_contact', total: total
+                    resource:
+                        app: 'contacts'
+                        url: 'contacts/'
+                callback()


instead of checking for darwin, check for notificationcenter

--- src/ui/views/notifications.coffee
+++ src/ui/views/notifications.coffee
@@ -75,17 +75,18 @@
         return if !text or quietIf(c, chat_id)

         if viewstate.showPopUpNotifications
-            isDarwin = require('os').platform() == 'darwin'
+            isNotificationCenter = notifier.constructor.name == 'NotificationCenter'
             #
             icon = path.join __dirname, '..', '..', 'icons', 'icon@8.png'
-            if isDarwin && viewstate.showIconNotification
+            # Only for NotificationCenter (darwin)
+            if isNotificationCenter && viewstate.showIconNotification
                 contentImage = fixlink entity[cid]?.photo_url
             else
                 contentImage = undefined
             #
             notifier.notify
                 title: if viewstate.showUsernameInNotification
-                           if !isDarwin && !viewstate.showIconNotification
+                           if !isNotificationCenter && !viewstate.showIconNotification
                                "#{sender} (via YakYak)"
                            else
                                sender
@@ -98,7 +99,7 @@
                 wait: true
                 sender: 'com.github.yakyak'
                 sound: !viewstate.muteSoundNotification && notifierSupportsSound
-                icon: icon if !isDarwin && viewstate.showIconNotification
+                icon: icon if !isNotificationCenter && viewstate.showIconNotification
                 contentImage: contentImage
             , (err, res) ->
               if res?.trim().match(/Activate/i)


debug 4 joseph

debug 4 joseph

--- server/controllers/accounts.coffee
+++ server/controllers/accounts.coffee
@@ -21,7 +21,7 @@
     # outside of this request lifecycle, we fetch mails
     accountCreated.then (account) ->
         account.fetchMails()
-        .catch (err) -> console.log "FETCH MAIL FAILED", err
+        .catch (err) -> console.log "FETCH MAIL FAILED", err.stack

 # fetch an account by id, add it to the request
 module.exports.fetch = (req, res, next) ->


文字数制限

文字数制限

--- crawler/keyword.coffee
+++ crawler/keyword.coffee
@@ -39,5 +39,6 @@
   ## 二回以上言われていない言葉は切る
   for key,value of hash
     delete hash[key] if value < 2
+    delete hash[key] if key.length < 3

   return hash


Try to get attachment markdown working

Try to get attachment markdown working

--- scripts/pudding.coffee
+++ scripts/pudding.coffee
@@ -191,9 +191,10 @@
       content:
         text: ''
         fallback: 'Instances'
-        pretext: "All #{default_role} instances"
+        pretext: "All _#{default_role}_ instances"
         color: '#77cc77'
         fields: fields
+        mrkdwn_in: ['fields', 'pretext', 'title']
       username: robot.name

     robot.emit 'slack.attachment', payload


WebtermView: just try once to reconnect existing terminal

--- client/Terminal/webtermview.coffee
+++ client/Terminal/webtermview.coffee
@@ -118,7 +118,7 @@
       @sessionId = remote.session

       @emit "WebTermConnected", remote
-      @reconnectionInProgress = false
+      @_triedToReconnect = no

     .catch (err) =>

@@ -139,9 +139,9 @@

     kite.on 'close', =>

-      if not kite.isDisconnected and not kite.invalid
-        kite.once 'connected', =>
-          @webtermConnect if remote? then 'resume' else 'create'
+      if not kite.isDisconnected and not @_triedToReconnect
+        @_triedToReconnect = yes
+        @webtermConnect if remote? then 'resume' else 'create'

   connectToTerminal: ->


Mothra

Mothra

--- src/scripts/jenkins-notifier.coffee
+++ src/scripts/jenkins-notifier.coffee
@@ -41,12 +41,19 @@
       if data.build.phase == 'COMPLETED'
         console.log "jenkins-notify: A build has finished! Oooh, the excitement!!"
         if data.build.status == 'FAILURE'
+	  console.log "Failure"
           if data.name in @failing
             build = "is still"
           else
             build = "started"
           robot.messageRoom "#dev", "#{data.name} build ##{data.build.number} #{build} failing (#{encodeURI(data.build.full_url)})"
           @failing.push data.name unless data.name in @failing
+	  if data.name == 'Mothra (QA)'
+	    http.get 'http://xserve:5051/mothra-qa', (res) ->
+	      console.log "MOTHRA!!"
+	      console.log res.statusCode
+	    robot.messageRoom "#dev", "Mothra has the upper hand!"
+	    robot.messageRoom "#dev", "http://i.imgur.com/CoqJxBx.gif"
         if data.build.status == 'SUCCESS'
           console.log "Success"
           if data.name in @failing


added sec info

added sec info

--- src/scripts/opendns-umbrella.coffee
+++ src/scripts/opendns-umbrella.coffee
@@ -9,6 +9,7 @@
 #
 # Commands:
 #   hubot opendns <url> - Gets OpenDNS Domain Reputation
+#   hubot opendns secinfo <domain> - Gets OpenDNS Security Information for a given domain
 #
 # Author:
 #   Scott J Roberts - @sroberts
@@ -44,3 +45,35 @@
             msg.send "Doh! #{res.statusCode}: Which means that didn't work."
     else
       msg.send "OpenDNS API key not configured."
+module.exports = (robot) ->
+  robot.respond /opendns secinfo (.*)/i, (msg) ->
+
+    if OPENDNS_KEY?
+      artifact = msg.match[1].toLowerCase()
+
+      msg.http("https://investigate.api.opendns.com/security/name/#{artifact}.json")
+        .headers('Authorization': 'Bearer ' + OPENDNS_KEY)
+        .get() (err, res, body) ->
+          if res.statusCode is 200
+
+            opendns_json = JSON.parse body
+
+            if opendns_json.found is true
+              response = "Here is the OpenDNS security info about #{artifact}:\n"
+
+              response += "- DGA score of #{opendns_json.dga_score}\," if opendns_json.dga_score is not 0
+              response += "- The domain name has #{opendns_json.entropy} entropy\n"
+              response += "- Google Pagerank score of #{opendns_json.pagerank}\n"
+              response += "- SecureRank2 score of #{opendns_json.securerank2}\n"
+              response += "- Popularity score of #{opendns_json.popularity}\n"
+              response += "- It looks like it could be a FastFlux domain\n" if opendns_json.fastflux is true
+              response += "- It's associate with #{opendns_json.dga_score} attacks\n" if opendns_json.attack
+              response += "- The threat type is #{opendns_json.threat_type}\n" if opendns_json.threat_type
+
+              msg.send response
+            else
+              msg.send "OpenDNS doesn't know about #{artifact}."
+        else
+          msg.send "Doh! #{res.statusCode}: Which means that didn't work."
+    else
+      msg.send "OpenDNS API key not configured."


router: ::clear to disregard entrypoint since we use subdomains for teams

--- client/app/lib/kodingrouter.coffee
+++ client/app/lib/kodingrouter.coffee
@@ -183,11 +183,7 @@
                 else onError()
           else onError()

-  clear: (route, replaceState = yes) ->
-    unless route
-      {entryPoint} = globals.config
-      route = if isLoggedIn() and isGroup() and not isKoding()
-      then "/#{globals.config.entryPoint?.slug}"
-      else '/'
+
+  clear: (route = '/', replaceState = yes) ->

     super route, replaceState


Magic numbers suck

Magic numbers suck

--- server/test/units/indicator.coffee
+++ server/test/units/indicator.coffee
@@ -220,7 +220,7 @@
       }]
   )
   indicatorData = new IndicatorData(
-    externalId: 15, data: indicatorData
+    externalId: 14, data: indicatorData
   )

   async.parallel([


test dates with undefined values

test dates with undefined values

--- coach/specs/user/model.spec.coffee
+++ coach/specs/user/model.spec.coffee
@@ -41,11 +41,18 @@
     matches = _.where(User.courses, ecosystem_book_uuid: TWO_COURSE_ECOSYSTEM_ID)
     expect(matches).to.have.length(2)

-    course = User.getCourse(TWO_COURSE_ECOSYSTEM_ID)
-    expect(course.id).to.equal(matches[1].id)
+    matches[0].roles[0].joined_at = "2016-01-06T19:59:44.072Z"
+    matches[1].roles[0].joined_at = "2017-01-06T19:59:44.072Z"
+    expect(User.getCourse(TWO_COURSE_ECOSYSTEM_ID).id).to.equal(matches[1].id)

-    matches[0].roles[0].joined_at = "2017-01-06T19:59:44.072Z"
-    course = User.getCourse(TWO_COURSE_ECOSYSTEM_ID)
-    expect(course.id).to.equal(matches[0].id)
+    matches[0].roles[0].joined_at = "2017-01-06T20:00:00.100Z"
+    matches[1].roles[0].joined_at = "2017-01-06T20:00:00.000Z"
+    expect(User.getCourse(TWO_COURSE_ECOSYSTEM_ID).id).to.equal(matches[0].id)
+
+    # test that it doesn't blowup if role joined_at is missing
+    delete matches[0].roles[0].joined_at
+    matches[1].roles[0].joined_at = "2017-01-06T20:00:00.000Z"
+    delete matches[0].roles[0].joined_at
+    expect(User.getCourse(TWO_COURSE_ECOSYSTEM_ID).id).to.equal(matches[1].id)

     undefined


Store Dispatcher unsubscribe function directly in object

Instantiating new functions in large number is costly. This drops
subscribed function memory use by 0.49KiB and makes Bacon significantly
faster on Node 0.10.

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -918,7 +918,7 @@
     @pushing = false
     @ended = false
     @prevError = undefined
-    @unsubscribeFromSource = nop
+    @unsubSrc = undefined

   hasSubscribers: ->
     @subscriptions.length > 0
@@ -966,6 +966,10 @@
     else
       @push event

+  unsubscribeFromSource: ->
+    @unsubSrc() if @unsubSrc
+    @unsubSrc = undefined
+
   subscribe: (sink) =>
     if @ended
       sink end()
@@ -975,11 +979,8 @@
       subscription = { sink: sink }
       @subscriptions.push(subscription)
       if @subscriptions.length == 1
-        unsubSrc = @_subscribe @handleEvent
-        @unsubscribeFromSource = ->
-          unsubSrc()
-          @unsubscribeFromSource = nop
-      assertFunction @unsubscribeFromSource
+        @unsubSrc = @_subscribe @handleEvent
+        assertFunction @unsubSrc
       =>
         @removeSub subscription
         @unsubscribeFromSource() unless @hasSubscribers()


Embed: make sure that the embedded link has a description

--- client/activity/lib/views/embedbox.coffee
+++ client/activity/lib/views/embedbox.coffee
@@ -11,9 +11,12 @@
     super options, data

   viewAppended: ->
+
     return  unless data = @getData()

     embedType = (getEmbedType data.link_embed?.type) or 'link'
+
+    return  if embedType is 'link' and not data.link_embed?.description

     containerClass = switch embedType
       when 'image'  then EmbedBoxImageView


socialcontroller: add bot channel to prefetched data

--- client/app/lib/socialapicontroller.coffee
+++ client/app/lib/socialapicontroller.coffee
@@ -36,6 +36,9 @@
       when 'followedChannels' then mapChannels
       when 'popularPosts', 'pinnedMessages', 'navigated' then mapActivities
       when 'privateMessages'                   then mapPrivateMessages
+      when 'bot'
+        data = data.data
+        mapChannel

     return fn(data) or []

@@ -750,12 +753,5 @@
       }, (err, response) ->
         return callback err  if err

-        { channelId } = response.data
-
-        { socialapi } = kd.singletons
-
-        socialapi.channel.byId {id: channelId}, (err, channel) ->
-
-          return callback err  if err
-
-          return callback null, mapChannel { channel: channel }
+        return callback null, mapChannel response.data
+


sort price_range and medium

sort price_range and medium

--- collections/filter_artworks.coffee
+++ collections/filter_artworks.coffee
@@ -1,4 +1,5 @@
 _ = require 'underscore'
+Backbone = require 'backbone'
 Artworks = require '../collections/artworks.coffee'
 { API_URL } = require('sharify').data

@@ -6,6 +7,40 @@
   url: "#{API_URL}/api/v1/filter/artworks?aggregations=true"

   parse: (data) ->
-    @counts = data.aggregations
+    @counts = @prepareCounts data.aggregations

-    return data.hits+    return data.hits
+
+  prepareCounts: (aggregations)->
+    # _.map destroys the keys, hence the iteration
+    for k, v of aggregations
+      aggregations[k] = @prepareAggregate v, k
+
+    aggregations
+
+  prepareAggregate: (aggregate, name) ->
+
+    # maps the sorted order but keeps the keys
+    mapKeys = (keys, aggregate)->
+      newMap = {}
+      _.each keys, (key)->
+        newMap[key] = aggregate[key]
+      newMap
+
+    aggregateMap =
+      # sorts the price_range from lowest to highest
+      'price_range': (aggregate) ->
+        keys = _.sortBy _.keys(aggregate), (key) ->
+          return -1 if key is "*-*"
+          [from, to] = key.split('-')
+          return 0 if from is "*"
+          return parseInt from
+        mapKeys keys, aggregate
+      'dimension_range': (aggregate) -> aggregate
+      # sorts medium alphabetically
+      'medium': (aggregate) ->
+        keys = _.sortBy _.keys(aggregate), (key) -> key
+        mapKeys keys, aggregate
+      'total': (aggregate) -> aggregate
+
+    aggregateMap[name]? aggregate


removed logging

removed logging

--- client/landing/app/Applications/Activity.kdapplication/ContentDisplays/Discussion.coffee
+++ client/landing/app/Applications/Activity.kdapplication/ContentDisplays/Discussion.coffee
@@ -157,8 +157,6 @@
     @setTemplate @pistachio()
     @template.update()

-
-    log "PrettyPrintin"
     @$("code").addClass "prettyprint"
     @$("pre").addClass "prettyprint"
     prettyPrint()


collaboration: add wrapper for throwing

collaboration: add wrapper for throwing

--- client/ide/collaborationcontroller.coffee
+++ client/ide/collaborationcontroller.coffee
@@ -881,3 +881,13 @@
         # remove the leaving participant's info from the collaborative doc
         @removeParticipant KD.nick()

+
+  throwError: throwError = (format, args...) ->
+
+    argIndex = 0
+    error = new Error """
+      IDE.CollaboratonController:
+      #{ format.replace /%s/g, -> args[argIndex++] or '%s' }
+    """
+
+    throw error


fix remote syntax

fix remote syntax

--- services/engine.coffee
+++ services/engine.coffee
@@ -5,7 +5,7 @@
 DataService = somata_client.bindRemote 'maia:data'

 validateNewDevice = (user, new_device, cb) ->
-    DataService.getUser user, (err, user) ->
+    DataService 'getUser', user, (err, user) ->
         if user?
             DataService 'createDevice', new_device, cb
         else


Fix capturePage() spec.

Fix capturePage() spec.

--- spec/api-browser-window-spec.coffee
+++ spec/api-browser-window-spec.coffee
@@ -77,10 +77,11 @@
       w.close()

   describe 'BrowserWindow.capturePage(rect, callback)', ->
-    it 'calls the callback with a Buffer', ->
+    it 'calls the callback with a Buffer', (done) ->
       w = new BrowserWindow(show: false)
       w.capturePage {x: 0, y: 0, width: 100, height: 100}, (image) ->
-        assert.equal image.constructor.name, 'SlowBuffer'
+        assert.equal image.constructor.name, 'Buffer'
+        done()
         w.close()

   describe 'beforeunload handler', ->


Added extra projection to limit return fields for bulk rerun

--- src/api/transactions.coffee
+++ src/api/transactions.coffee
@@ -30,6 +30,9 @@
     when "full"
       # view all transaction data
       return {}
+    when "idsOnly"
+      # view only '_id's
+      return { "_id": 1 }
     else
       # no filterRepresentation supplied - simple view
       # view minimum required data for transactions


Determine autocomplete position before filtering matches

--- src/app/autocomplete.coffee
+++ src/app/autocomplete.coffee
@@ -87,11 +87,12 @@
     @originalSelectedText = @editor.getSelectedText()
     @originalSelectionBufferRange = @editor.getSelection().getBufferRange()
     @allMatches = @findMatchesForCurrentSelection()
-    @filterMatches()

     cursorScreenPosition = @editor.getCursorScreenPosition()
     {left, top} = @editor.pixelPositionForScreenPosition(cursorScreenPosition)
     @css {left: left, top: top + @editor.lineHeight}
+
+    @filterMatches()
     @editor.lines.append(this)
     @miniEditor.focus()


[grunt] pass into data `homepage` from `packages.json`

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -98,6 +98,7 @@
       site:
         name: '<%= pkg.name %>'
         desc: '<%= pkg.description %>'
+        homepage: '<%= pkg.homepage %>'
         version: '<%= pkg.version %>'
         locales: '<%= i18n.locales.list %>'
         baseLocale: '<%= i18n.baseLocale %>'


Updating build targets and adding nodes

Updating build targets and adding nodes

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -1,4 +1,7 @@
 module.exports = (grunt) ->
+
+  # Build target.
+  target = grunt.option('target') || 'js'

   # Project configuration.
   config =
@@ -9,28 +12,44 @@
           bare: true
           sourceMap: true
           sourceMapDir: 'dist/'
-        files:
-          'dist/moltin.js': [
-            'src/moltin.coffee',
-            'src/features/storage.coffee',
-            'src/features/storage.tvjs.coffee',
-            'src/features/*.coffee'
-          ]
+        files: [
+          {
+            dest: 'dist/moltin.' + ( if target != 'js' then target + '.' else '' ) + 'js'
+            src: [
+              'src/moltin.coffee',
+              'src/' + target + '/ajax.coffee',
+              'src/' + target + '/storage.coffee',
+              'src/abstract.coffee',
+              'src/features/*.coffee'
+              'src/' + target + '/export.coffee'
+            ]
+          }
+        ]
     preprocess:
       inline:
         options:
           context:
-            TARGET: grunt.option('target') || 'js'
+            TARGET: target
           inline: true
-        src: 'dist/moltin.js'
-    concat:
-      options:
-        separator: ';'
-        banner: '/*! <%= pkg.name %> minified - v<%= pkg.version %> - ' +
-          '<%= grunt.template.today("yyyy-mm-dd") %> */' + "\n"
-      css:
-        src: ['src/css/*.css']
-        dest: 'dist/moltin.css'
+        src: 'dist/moltin.' + ( if target != 'js' then target + '.' else '' ) + 'js'
+    replace:
+      dist:
+        options:
+          patterns: [
+            {
+              match: /[\t]+;\n/g,
+              replacement: () ->
+                return ''
+            }
+          ]
+        files: [
+          {
+            expand: true,
+            flatten: true,
+            src: ['dist/moltin.' + ( if target != 'js' then target + '.' else '' ) + 'js'],
+            dest: 'dist/'
+          }
+        ]
     karma:
       unit:
         options:
@@ -43,17 +62,14 @@
           sourceMap: true
           sourceMapIncludeSources: true
           drop_console: true
-          banner: '/*! <%= pkg.name %> minified - v<%= pkg.version %> - ' +
+          banner: '/*! <%= pkg.name %> (' + target + ') minified - v<%= pkg.version %> - ' +
           '<%= grunt.template.today("yyyy-mm-dd") %> */'
-        files:
-          'dist/moltin.min.js': 'dist/moltin.js'
-    cssmin:
-      compress:
-        options:
-          banner: '/*! <%= pkg.name %> minified - v<%= pkg.version %> - ' +
-          '<%= grunt.template.today("yyyy-mm-dd") %> */' + "\n"
-        files:
-          'dist/moltin.min.css': ['dist/moltin.css']
+        files: [
+          {
+            dest: 'dist/moltin.' + ( if target != 'js' then target + '.' else '' ) + 'min.js'
+            src: 'dist/moltin.' + ( if target != 'js' then target + '.' else '' ) + 'js'
+          }
+        ]
     compress:
       main:
         options:
@@ -63,8 +79,8 @@
         src: ['*.min.js', '*.min.css', '*.min.map']
         dest: 'dist/gzip/'
     watch:
-      files: ['src/*.coffee', 'src/features/*.coffee', 'src/css/*.css']
-      tasks: ['coffee', 'preprocess:inline', 'concat', 'karma', 'uglify', 'cssmin', 'compress']
+      files: ['src/*.coffee', 'src/**/*.coffee']
+      tasks: ['coffee', 'preprocess:inline', 'replace', 'karma', 'uglify', 'compress']

   # Do we have credentials?
   if grunt.file.exists('aws-credentials.json')
@@ -104,14 +120,14 @@
   # These plugins provide necessary tasks.
   grunt.loadNpmTasks 'grunt-contrib-coffee'
   grunt.loadNpmTasks 'grunt-contrib-uglify'
-  grunt.loadNpmTasks 'grunt-contrib-concat'
-  grunt.loadNpmTasks 'grunt-contrib-cssmin'
   grunt.loadNpmTasks 'grunt-contrib-compress'
+  grunt.loadNpmTasks 'grunt-replace'
   grunt.loadNpmTasks 'grunt-preprocess'
   grunt.loadNpmTasks 'grunt-karma'
   grunt.loadNpmTasks 'grunt-contrib-watch'
   grunt.loadNpmTasks 'grunt-aws-s3'
   grunt.loadNpmTasks 'grunt-contrib-copy'

-  # Default task.
+  # Tasks.
+  grunt.registerTask 'build', ['coffee', 'preprocess:inline', 'replace', 'karma', 'uglify', 'compress']
   grunt.registerTask 's3', ['copy:aws', 'aws_s3:production']


Changed last commit about revoking in tlsAuth middleware.

--- src/middleware/tlsAuthentication.coffee
+++ src/middleware/tlsAuthentication.coffee
@@ -56,7 +56,7 @@

     else
       return done(new Error 'Keystore does not exist')
-
+
     if mutualTLS
       exports.getTrustedClientCerts (err, certs) ->
         if err
@@ -79,7 +79,8 @@
 clientLookup = (fingerprint, subjectCN, issuerCN) ->
   logger.debug "Looking up client linked to cert with fingerprint #{fingerprint} with subject #{subjectCN} and issuer #{issuerCN}"
   deferred = Q.defer()
-
+  promises = []
+
   Client.findOne certFingerprint: fingerprint, (err, result) ->
     deferred.reject err if err

@@ -88,19 +89,30 @@

       ## MyEdit - May 24
       ## Since we have found a cert-client match, (CA chaining or not)
-      ## we can heck here for revocation list match.
-      ##
-      revoked = false
-      RevokedCert.findOne {certFingerprint: fingerprint, issuerDN: issuerCN}, (err,result) ->
-        if err
-          return deferred.reject err
-        if not result?
-          logger.info "Certificate for cn=#{subjectCN} issuerCN=#{issuerCN} is in revocation list."
-          revoked = true
-      ######
-      if revoked?
-        return deferred.resolve null
-      return deferred.resolve result
+      ## we can heck here for revocation list match.
+      ##
+      revocation = (fp,issCN) ->
+        deff = Q.defer()
+        RevokedCert.findOne {fingerprint:fp, issuerDN: issCN}, (err,revoked) ->
+          if err
+            deff.reject err
+          if revoked?
+            logger.info "revoked: "
+            deff.resolve null
+          else
+            logger.info "not revoked: " + result
+            deff.resolve result
+        return deff.promise
+
+
+      promise_rev = revocation(fingerprint,issuerCN)
+      promise_rev.then (res) ->
+        logger.info "promise res: " + res
+        return deferred.resolve res
+
+      promises.push promise_rev
+      ##
+      #return deferred.resolve result

     if subjectCN is issuerCN
       # top certificate reached
@@ -136,7 +148,9 @@
         logger.warn "tlsClientLookup.type config option does not contain a known value, defaulting to 'strict'. Available options are 'strict' and 'in-chain'."
       deferred.resolve null

-  return deferred.promise
+  (Q.all promises).then ->
+    return deferred.promise
+  return

 if process.env.NODE_ENV == "test"
   exports.clientLookup = clientLookup


Trust client certs signed by the OpenHIM

--- src/middleware/tlsAuthentication.coffee
+++ src/middleware/tlsAuthentication.coffee
@@ -44,6 +44,7 @@
       options =
         key:  keystore.key
         cert: keystore.cert.data
+        ca: [keystore.cert.data]
     else
       return done(new Error 'Keystore does not exist')

@@ -53,7 +54,7 @@
           logger.error "Could not fetch trusted certificates: #{err}"
           return done err, null

-        options.ca = certs
+        options.ca.push certs
         options.requestCert = true
         options.rejectUnauthorized = false  # we test authority ourselves
         done null, options


payload generation is more explicit

payload generation is more explicit

--- client/images.coffee
+++ client/images.coffee
@@ -233,7 +233,11 @@
   canvas

 handle_file_step_two = ( chosen_file, template, opts ) ->
+  payload = {original_file: chosen_file}
   if opts.full_image
+
+    if opts.preserve_full_image
+      payload.cached_image = opts.full_image

     max_width = 150 # from .no-thumb-item width
     max_height = 200 # from .no-thumb-item height
@@ -250,9 +254,13 @@
       actual_width = opts.full_image.width*scale

     thumb = getThumbnail(opts.full_image, scale)
+    payload.thumb_canvas = thumb
+
     div = template.find("#preview")
     $(div).empty()
     div.appendChild(thumb)
+  template.payload.set( payload )
+  template.upload_ready.set( true )

 handle_files = (fileList, template) ->
   # template is template instance of InsertImageDialog
@@ -301,6 +309,11 @@
   @selected_files = new ReactiveVar()
   @selected_files.set([])

+  @payload = new ReactiveVar()
+
+  @upload_ready = new ReactiveVar()
+  @upload_ready.set( false )
+
   # prevent dropping files onto page creating navigation
   # http://stackoverflow.com/a/6756680/1633026
   window.addEventListener 'dragover', ((e) ->
@@ -323,6 +336,8 @@

 Template.InsertImageDialog.events
   "change #insert_image": (event, template) ->
+    template.upload_ready.set( false )
+
     div = template.find("#preview")
     $(div).empty()
     append_spinner(div)


Add a 1 second delay to the smoke tests (#81)

* Add a 1 second delay to the smoke tests

Fixes a race condition where smoke tests exit before container can be attached to.

See here for more info: https://github.com/overleaf/sharelatex/issues/274

* give the smoke tests additional work to do

* escape slashes

--- test/smoke/coffee/SmokeTests.coffee
+++ test/smoke/coffee/SmokeTests.coffee
@@ -17,10 +17,37 @@
 					resources: [
 						path: "main.tex"
 						content: """
-							\\documentclass{article}
-							\\begin{document}
-							Hello world
-							\\end{document}
+% Membrane-like surface
+% Author: Yotam Avital
+\\documentclass{article}
+\\usepackage{tikz}
+\\usetikzlibrary{calc,fadings,decorations.pathreplacing}
+\\begin{document}
+\\begin{tikzpicture}
+  \\def\\nuPi{3.1459265}
+  \\foreach \\i in {5,4,...,2}{% This one doesn't matter
+    \\foreach \\j in {3,2,...,0}{% This will crate a membrane
+                               % with the front lipids visible
+      % top layer
+      \\pgfmathsetmacro{\\dx}{rand*0.1}% A random variance in the x coordinate
+      \\pgfmathsetmacro{\\dy}{rand*0.1}% A random variance in the y coordinate,
+                                     % gives a hight fill to the lipid
+      \\pgfmathsetmacro{\\rot}{rand*0.1}% A random variance in the
+                                      % molecule orientation
+      \\shade[ball color=red] ({\\i+\\dx+\\rot},{0.5*\\j+\\dy+0.4*sin(\\i*\\nuPi*10)}) circle(0.45);
+      \\shade[ball color=gray] (\\i+\\dx,{0.5*\\j+\\dy+0.4*sin(\\i*\\nuPi*10)-0.9}) circle(0.45);
+      \\shade[ball color=gray] (\\i+\\dx-\\rot,{0.5*\\j+\\dy+0.4*sin(\\i*\\nuPi*10)-1.8}) circle(0.45);
+      % bottom layer
+      \\pgfmathsetmacro{\\dx}{rand*0.1}
+      \\pgfmathsetmacro{\\dy}{rand*0.1}
+      \\pgfmathsetmacro{\\rot}{rand*0.1}
+      \\shade[ball color=gray] (\\i+\\dx+\\rot,{0.5*\\j+\\dy+0.4*sin(\\i*\\nuPi*10)-2.8}) circle(0.45);
+      \\shade[ball color=gray] (\\i+\\dx,{0.5*\\j+\\dy+0.4*sin(\\i*\\nuPi*10)-3.7}) circle(0.45);
+      \\shade[ball color=red] (\\i+\\dx-\\rot,{0.5*\\j+\\dy+0.4*sin(\\i*\\nuPi*10)-4.6}) circle(0.45);
+    }
+  }
+\\end{tikzpicture}
+\\end{document}
 						"""
 					]
 		}, (@error, @response, @body) =>


Update for new helper method signatures

Update for new helper method signatures

--- spec/tree-view-package-spec.coffee
+++ spec/tree-view-package-spec.coffee
@@ -4216,7 +4216,7 @@
           deltaFile = findFileContainingText(treeView.roots[0], 'delta.txt')

           [dragStartEvent, dragEnterEvent, dropEvent] =
-            eventHelpers.buildInternalDragEvents(deltaFile, alphaDir.querySelector('.header'), alphaDir)
+            eventHelpers.buildInternalDragEvents([deltaFile], alphaDir.querySelector('.header'), alphaDir, treeView)

         it "prompts to replace the file", ->
           spyOn(atom, 'confirm')
@@ -4310,7 +4310,7 @@
       describe "when dragging directories", ->
         [oldAFilePath, oldBFilePath, oldCFilePath, oldNestedDirPath, oldNestedFilePath,
          onlyOldDirPath, onlyOldFilePath, newAlphaDirPath, newAFilePath, newBFilePath,
-        newCFilePath, newNestedDirPath, newNestedFilePath, onlyNewDirPath, onlyNewFilePath] = []
+         newCFilePath, newNestedDirPath, newNestedFilePath, onlyNewDirPath, onlyNewFilePath] = []
         [dragStartEvent, dragEnterEvent, dropEvent] = []

         beforeEach ->
@@ -4359,7 +4359,7 @@
           newAlphaDir = findDirectoryContainingText(gammaDir, 'alpha')

           [dragStartEvent, dragEnterEvent, dropEvent] =
-            eventHelpers.buildInternalDragEvents(newAlphaDir, treeView.roots[0].querySelector('.header'), treeView.roots[0])
+            eventHelpers.buildInternalDragEvents([newAlphaDir], treeView.roots[0].querySelector('.header'), treeView.roots[0], treeView)

         it "recursively walks the directory structure and prompts to replace each conflicting file", ->
           spyOn(atom, 'confirm').andReturn 1


Open feed in new tab

Open feed in new tab


cycleButton: bugfix: super and $el.empty in render

--- coffee/src/views/cycle_button_view.coffee
+++ coffee/src/views/cycle_button_view.coffee
@@ -37,6 +37,9 @@

   render: =>
+    super
+    @$el.empty()
+
     @views = []

     # build views from data


editSummaryView: bugfix: only save fields on blur

saving (and validating) on keyup caused the field to reset to default
values at awkward times

--- coffee/src/views/edit_summary_view.coffee
+++ coffee/src/views/edit_summary_view.coffee
@@ -22,8 +22,8 @@
     '''

   events: => _.extend super,
-    'keyup input': @saveData
-    'keyup textarea': @saveData
+    'blur input': @saveData
+    'blur textarea': @saveData

   initialize: =>


Add settings view package description

Add settings view package description

--- src/packages/settings-view/package.cson
+++ src/packages/settings-view/package.cson
@@ -1,2 +1,3 @@
 'main': './lib/settings'
 'deferredDeserializers': ['SettingsView']
+'description': 'GUI pane for Atom settings'


bongo/xhr: preallocate array for responseQueue

--- node_modules_koding/bongo/lib/expressify.coffee
+++ node_modules_koding/bongo/lib/expressify.coffee
@@ -7,7 +7,7 @@

     { channelName, queue: requestQueue } = req.body

-    responseQueue = []
+    responseQueue = new Array requestQueue.length

     workQueue = requestQueue.map (body, i) -> ->
       bongo.handleRequest channelName, body, (secretName, callbackId, args) ->


Re-added geo_put API call for backward compatibility

--- coffee/router.coffee
+++ coffee/router.coffee
@@ -28,7 +28,7 @@
     ctrl.oneOrMoreGeos JSON.parse url.parse(req.url, true).json
   app.get '/put_geojson', resRouter.bind undefined, (req) ->
     ctrl.oneOrMoreGeoJson JSON.parse url.parse(req.url, true).json
-  app.get '/put_params', resRouter.bind undefined, (req) ->
+  putParams = resRouter.bind undefined, (req) ->
     parts = url.parse req.url, true
     meta = {}
     meta[k] = cohers(v) for k, v of parts.query when not (k in ['lat','lng'])
@@ -36,6 +36,8 @@
     ctrl.newGeo.bind ctrl,
       lnglat: [parseFloat(lng), parseFloat(lat)]
       meta: meta
+  app.get '/put_params', putParams
+  app.get '/geo_put', putParams # *depricated* legacy call

   # Get geo by id
   app.get '/geo/:id', ctrl.idGeo.bind ctrl, map.docToGeo


New test: close on close

New test: close on close

--- tests/html/src/endtoendtests.coffee
+++ tests/html/src/endtoendtests.coffee
@@ -100,3 +100,21 @@
             equals(e.reason, "All transports failed")
             equals(e.wasClean, false)
             start()
+
+asyncTest "close on close", ->
+    expect(4)
+    r = newSockJS('/close', 'jsonp-polling')
+    r.onopen = (e) ->
+        ok(true)
+    r.onmessage = (e) ->
+        ok(false)
+    r.onclose = (e) ->
+        equals(e.code, 3000)
+        equals(e.reason, "Go away!")
+        equals(e.wasClean, true)
+        r.onclose = ->
+            ok(false)
+        r.close()
+
+        u.delay 10, ->
+            start()


browserify→webpack

browserify→webpack

--- gulp/tasks/js.coffee
+++ gulp/tasks/js.coffee
@@ -7,11 +7,8 @@

 browserSync  = require "browser-Sync"

-
 webpack      = require "gulp-webpack"
 webpackConf  = require '../../webpack.config.js'
-
-b.on 'update', bundle

 gulp.task "js", ()->
   gulp.src config.js.src+'main.js'


config: change sandbox hosted zone

config: change sandbox hosted zone

--- config/main.sandbox.coffee
+++ config/main.sandbox.coffee
@@ -7,7 +7,7 @@

   prod_simulation_server = "10.0.0.137"

-  hostedZone          = options.hostedZone     or "sandbox.koding.io"
+  hostedZone          = options.hostedZone     or "svm.koding.io"
   hostname            = options.hostname       or "prod-v1_2_4-anna"
   publicHostname      = options.publicHostname or "https://koding.me"
   region              = options.region         or "aws"


config: fix sandbox options

config: fix sandbox options

--- config/main.sandbox.coffee
+++ config/main.sandbox.coffee
@@ -14,15 +14,15 @@
     port : '80'

-  options.serviceHost or= "10.0.0.136"
-  options.publicPort or= "80"
-  options.hostname or= "sandbox.koding.com#{if options.publicPort is "80" then "" else ":"+options.publicPort}"
-  options.protocol or= "https:"
-  options.publicHostname or= "#{options.protocol}//#{options.hostname}"
-  options.region or= "aws"
-  options.configName or= "sandbox"
-  options.environment or= "sandbox"
-  options.projectRoot or= "/opt/koding"
+  options.serviceHost = "10.0.0.136"
+  options.publicPort = "80"
+  options.hostname = "sandbox.koding.com#{if options.publicPort is "80" then "" else ":"+options.publicPort}"
+  options.protocol = "https:"
+  options.publicHostname = "#{options.protocol}//#{options.hostname}"
+  options.region = "aws"
+  options.configName = "sandbox"
+  options.environment = "sandbox"
+  options.projectRoot = "/opt/koding"
   options.version or= options.tag
   options.build or= "1111"
   options.tunnelUrl or= "http://devtunnelproxy.koding.com"


config: change newkontrol port to 80

config: change newkontrol port to 80

--- config/main.staging.coffee
+++ config/main.staging.coffee
@@ -232,7 +232,7 @@
     webPort     : 3020
   newkontrol      :
     host          : "stage-kontrol.sj.koding.com"
-    port          : 4000
+    port          : 80
     certFile      : "/opt/koding/go/src/koding/kontrol/kontrolproxy/files/10.0.5.231_cert.pem"
     keyFile       : "/opt/koding/go/src/koding/kontrol/kontrolproxy/files/10.0.5.231_key.pem"
   proxyKite       :


syntax error fixed

syntax error fixed

--- components/tree/treeviewcontroller.coffee
+++ components/tree/treeviewcontroller.coffee
@@ -294,9 +294,8 @@
       scrollView         : no
       selection          : no
       view               : new options.listViewClass
-        type             : options.type
         tagName          : "ul"
-        type             : "jtree"
+        type             : options.type or "jtree"
         itemClass        : options.treeItemClass
         itemChildClass   : options.itemChildClass
         itemChildOptions : options.itemChildOptions


Improve heuristics for showing "no data"

The solar curtailment chart will no longer show a "no data" message when
technologies have data for time periods other than those shown.

Closes #3660

--- app/assets/javascripts/d3/solar_curtailment_curve.coffee
+++ app/assets/javascripts/d3/solar_curtailment_curve.coffee
@@ -12,7 +12,12 @@
     refresh: ->
       super()

-      if _.all(@visibleData(), (s) -> _.all(s.values, (v) -> v == 0))
+      noData = _.all(
+        @visibleData(),
+        (s) => _.all(@model.series.with_gquery(s.key).future_value(), (v) -> v == 0)
+      )
+
+      if noData
         @svg.selectAll('g.no-data').style('display', 'inline')
       else
         @svg.selectAll('g.no-data').style('display', 'none')


remove test alert

remove test alert

--- app/assets/javascripts/routes/paper_edit_route.js.coffee
+++ app/assets/javascripts/routes/paper_edit_route.js.coffee
@@ -23,7 +23,7 @@

     confirmSubmitPaper: ->
       return unless @modelFor('paperEdit').get('allMetadataTasksCompleted')
-      @modelFor('paperEdit').save() #then().catch( alert('hello'))
+      @modelFor('paperEdit').save()
       @transitionTo('paper.submit')

     savePaper: ->


jupyter -- top menus: slight transparent

--- src/smc-webapp/jupyter/top-menubar.cjsx
+++ src/smc-webapp/jupyter/top-menubar.cjsx
@@ -4,11 +4,13 @@
 File, Edit, etc....
 ###

-{ButtonGroup, DropdownButton, MenuItem} = require('react-bootstrap')
+{ButtonGroup, Dropdown, MenuItem} = require('react-bootstrap')

 {Icon} = require('../r_misc')

 {React, ReactDOM, rclass, rtypes}  = require('../smc-react')
+
+OPACITY='.93'

 exports.TopMenubar = rclass
     shouldComponentUpdate: ->
@@ -19,154 +21,194 @@
         actions : rtypes.object.isRequired

     render_file: ->
-        <DropdownButton noCaret bsStyle='default' title='File' key='file' id='menu-file' style={border:0, backgroundColor: 'rgb(247,247,247)'}>
-            <MenuItem eventKey="new">New Notebook...</MenuItem>
-            <MenuItem eventKey="open">Open...</MenuItem>
-            <MenuItem divider />
-            <MenuItem eventKey="copy">Make a Copy...</MenuItem>
-            <MenuItem eventKey="rename">Rename...</MenuItem>
-            <MenuItem eventKey="save">Save</MenuItem>
-            <MenuItem eventKey="timetravel">Publish...</MenuItem>
-            <MenuItem divider />
-            <MenuItem eventKey="timetravel">TimeTravel...</MenuItem>
-            <MenuItem divider />
-            <MenuItem eventKey="print">Print Preview</MenuItem>
-            <MenuItem eventKey="download" disabled>Download As...</MenuItem>
-            <MenuItem eventKey="download-ipynb"   > Notebook (.ipynb)</MenuItem>
-            <MenuItem eventKey="download-python"  > Python (.py)</MenuItem>
-            <MenuItem eventKey="download-html"    > HTML (.html)</MenuItem>
-            <MenuItem eventKey="download-markdown"> Markdown (.md)</MenuItem>
-            <MenuItem eventKey="download-rst"     > reST (.rst)</MenuItem>
-            <MenuItem eventKey="download-pdf"     > PDF via LaTeX (.pdf)</MenuItem>
-            <MenuItem divider />
-            <MenuItem eventKey="trusted" disabled={true}>Trusted Notebook</MenuItem>
-            <MenuItem divider />
-            <MenuItem eventKey="close">Close and Halt</MenuItem>
-        </DropdownButton>
+        <Dropdown key='file' id='menu-file'>
+            <Dropdown.Toggle noCaret bsStyle='default' style={border:0, backgroundColor: 'rgb(247,247,247)'}>
+                File
+            </Dropdown.Toggle>
+            <Dropdown.Menu style={opacity:OPACITY}>
+                <MenuItem eventKey="new">New Notebook...</MenuItem>
+                <MenuItem eventKey="open">Open...</MenuItem>
+                <MenuItem divider />
+                <MenuItem eventKey="copy">Make a Copy...</MenuItem>
+                <MenuItem eventKey="rename">Rename...</MenuItem>
+                <MenuItem eventKey="save">Save</MenuItem>
+                <MenuItem eventKey="timetravel">Publish...</MenuItem>
+                <MenuItem divider />
+                <MenuItem eventKey="timetravel">TimeTravel...</MenuItem>
+                <MenuItem divider />
+                <MenuItem eventKey="print">Print Preview</MenuItem>
+                <MenuItem eventKey="download" disabled>Download As...</MenuItem>
+                <MenuItem eventKey="download-ipynb"   > Notebook (.ipynb)</MenuItem>
+                <MenuItem eventKey="download-python"  > Python (.py)</MenuItem>
+                <MenuItem eventKey="download-html"    > HTML (.html)</MenuItem>
+                <MenuItem eventKey="download-markdown"> Markdown (.md)</MenuItem>
+                <MenuItem eventKey="download-rst"     > reST (.rst)</MenuItem>
+                <MenuItem eventKey="download-pdf"     > PDF via LaTeX (.pdf)</MenuItem>
+                <MenuItem divider />
+                <MenuItem eventKey="trusted" disabled={true}>Trusted Notebook</MenuItem>
+                <MenuItem divider />
+                <MenuItem eventKey="close">Close and Halt</MenuItem>
+            </Dropdown.Menu>
+        </Dropdown>

     render_edit: ->
-        <DropdownButton noCaret bsStyle='default' title='Edit' key='edit'  id='menu-edit'  style={border:0, backgroundColor: 'rgb(247,247,247)'}>
-            <MenuItem eventKey="cut-cells"  onSelect={=>@props.actions.undo()}>Undo</MenuItem>
-            <MenuItem eventKey="copy-cells" onSelect={=>@props.actions.redo()}>Redo</MenuItem>
-            <MenuItem divider />
-            <MenuItem eventKey="cut-cells">Cut Cells</MenuItem>
-            <MenuItem eventKey="copy-cells">Copy Cells</MenuItem>
-            <MenuItem eventKey="paste-cells-above">Paste Cells Above</MenuItem>
-            <MenuItem eventKey="paste-cells-below">Paste Cells Below</MenuItem>
-            <MenuItem eventKey="paste-cells-and-replace">Paste Cells & Replace</MenuItem>
-            <MenuItem eventKey="delete-cells"      onSelect={=>@props.actions.delete_selected_cells()}>Delete Cells</MenuItem>
-            <MenuItem eventKey="undo-delete-cells" onSelect={=>@props.actions.undo()}>Undo Delete Cells</MenuItem>
-            <MenuItem divider />
-            <MenuItem eventKey="split-cell" onSelect={=>@props.actions.split_current_cell()}>Split Cell</MenuItem>
-            <MenuItem eventKey="merge-cell-above"  onSelect={=>@props.actions.merge_cell_above()}>Merge Cell Above</MenuItem>
-            <MenuItem eventKey="merge-cell-below"  onSelect={=>@props.actions.merge_cell_below()}>Merge Cell Below</MenuItem>
-            <MenuItem divider />
-            <MenuItem eventKey="move-cell-up" onSelect={=>@props.actions.move_selected_cells(-1)}>Move Cell Up</MenuItem>
-            <MenuItem eventKey="move-cell-down"  onSelect={=>@props.actions.move_selected_cells(1)}>Move Cell Down</MenuItem>
-            <MenuItem divider />
-            <MenuItem eventKey="edit-notebook-metadata">Edit Notebook Metadata</MenuItem>
-            <MenuItem divider />
-            <MenuItem eventKey="find-and-replace">Find and Replace</MenuItem>
-        </DropdownButton>
+        <Dropdown key='edit' id='menu-edit'>
+            <Dropdown.Toggle noCaret bsStyle='default' style={border:0, backgroundColor: 'rgb(247,247,247)'}>
+                Edit
+            </Dropdown.Toggle>
+            <Dropdown.Menu style={opacity:OPACITY}>
+                <MenuItem eventKey="cut-cells"  onSelect={=>@props.actions.undo()}>Undo</MenuItem>
+                <MenuItem eventKey="copy-cells" onSelect={=>@props.actions.redo()}>Redo</MenuItem>
+                <MenuItem divider />
+                <MenuItem eventKey="cut-cells">Cut Cells</MenuItem>
+                <MenuItem eventKey="copy-cells">Copy Cells</MenuItem>
+                <MenuItem eventKey="paste-cells-above">Paste Cells Above</MenuItem>
+                <MenuItem eventKey="paste-cells-below">Paste Cells Below</MenuItem>
+                <MenuItem eventKey="paste-cells-and-replace">Paste Cells & Replace</MenuItem>
+                <MenuItem eventKey="delete-cells"      onSelect={=>@props.actions.delete_selected_cells()}>Delete Cells</MenuItem>
+                <MenuItem eventKey="undo-delete-cells" onSelect={=>@props.actions.undo()}>Undo Delete Cells</MenuItem>
+                <MenuItem divider />
+                <MenuItem eventKey="split-cell" onSelect={=>@props.actions.split_current_cell()}>Split Cell</MenuItem>
+                <MenuItem eventKey="merge-cell-above"  onSelect={=>@props.actions.merge_cell_above()}>Merge Cell Above</MenuItem>
+                <MenuItem eventKey="merge-cell-below"  onSelect={=>@props.actions.merge_cell_below()}>Merge Cell Below</MenuItem>
+                <MenuItem divider />
+                <MenuItem eventKey="move-cell-up" onSelect={=>@props.actions.move_selected_cells(-1)}>Move Cell Up</MenuItem>
+                <MenuItem eventKey="move-cell-down"  onSelect={=>@props.actions.move_selected_cells(1)}>Move Cell Down</MenuItem>
+                <MenuItem divider />
+                <MenuItem eventKey="edit-notebook-metadata">Edit Notebook Metadata</MenuItem>
+                <MenuItem divider />
+                <MenuItem eventKey="find-and-replace">Find and Replace</MenuItem>
+            </Dropdown.Menu>
+        </Dropdown>

     render_view: ->
-        <DropdownButton noCaret bsStyle='default' title='View' key='view'  id='menu-view'  style={border:0, backgroundColor: 'rgb(247,247,247)'}>
-            <MenuItem eventKey="toggle-header">Toggle Header</MenuItem>
-            <MenuItem eventKey="toggle-toolbar">Toggle Toolbar</MenuItem>
-            <MenuItem divider />
-            <MenuItem eventKey="" disabled>Cell Toolbar...</MenuItem>
-            <MenuItem eventKey="cell-toolbar-none"     > None</MenuItem>
-            <MenuItem eventKey="cell-toolbar-metadata" > Edit Metadata</MenuItem>
-            <MenuItem eventKey="cell-toolbar-raw"      > Raw Cell Format</MenuItem>
-            <MenuItem eventKey="cell-toolbar-slideshow"> Slideshow</MenuItem>
-            <MenuItem divider />
-            <MenuItem eventKey="view-zoom-in">Zoom In</MenuItem>
-            <MenuItem eventKey="view-zoom-out">Zoom Out</MenuItem>
-        </DropdownButton>
+        <Dropdown key='view'  id='menu-view'>
+            <Dropdown.Toggle noCaret bsStyle='default' style={border:0, backgroundColor: 'rgb(247,247,247)'}>
+                View
+            </Dropdown.Toggle>
+            <Dropdown.Menu style={opacity:OPACITY}>
+                <MenuItem eventKey="toggle-header">Toggle Header</MenuItem>
+                <MenuItem eventKey="toggle-toolbar">Toggle Toolbar</MenuItem>
+                <MenuItem divider />
+                <MenuItem eventKey="" disabled>Cell Toolbar...</MenuItem>
+                <MenuItem eventKey="cell-toolbar-none"     > None</MenuItem>
+                <MenuItem eventKey="cell-toolbar-metadata" > Edit Metadata</MenuItem>
+                <MenuItem eventKey="cell-toolbar-raw"      > Raw Cell Format</MenuItem>
+                <MenuItem eventKey="cell-toolbar-slideshow"> Slideshow</MenuItem>
+                <MenuItem divider />
+                <MenuItem eventKey="view-zoom-in">Zoom In</MenuItem>
+                <MenuItem eventKey="view-zoom-out">Zoom Out</MenuItem>
+            </Dropdown.Menu>
+        </Dropdown>

     render_insert: ->
-        <DropdownButton noCaret bsStyle='default' title='Insert' key='insert'  id='menu-insert'  style={border:0, backgroundColor: 'rgb(247,247,247)'}>
-            <MenuItem eventKey="insert-cell-above" onSelect={=>@props.actions.insert_cell(-1)}>Insert Cell Above</MenuItem>
-            <MenuItem eventKey="insert-cell-below" onSelect={=>@props.actions.insert_cell(1)} >Insert Cell Below</MenuItem>
-        </DropdownButton>
+        <Dropdown key='insert'  id='menu-insert'>
+            <Dropdown.Toggle noCaret bsStyle='default' style={border:0, backgroundColor: 'rgb(247,247,247)'}>
+                 Insert
+            </Dropdown.Toggle>
+            <Dropdown.Menu style={opacity:OPACITY}>
+                <MenuItem eventKey="insert-cell-above" onSelect={=>@props.actions.insert_cell(-1)}>Insert Cell Above</MenuItem>
+                <MenuItem eventKey="insert-cell-below" onSelect={=>@props.actions.insert_cell(1)} >Insert Cell Below</MenuItem>
+            </Dropdown.Menu>
+        </Dropdown>

     render_cell: ->
-        <DropdownButton noCaret bsStyle='default' title='Cell' key='cell'  id='menu-cell'  style={border:0, backgroundColor: 'rgb(247,247,247)'}>
-            <MenuItem eventKey="run-cells" onSelect={=>@props.actions.run_selected_cells()}>Run Cells</MenuItem>
-            <MenuItem eventKey="run-cells-select-below">Run Cells and Select Below</MenuItem>
-            <MenuItem eventKey="run-cells-insert-below">Run Cells and Insert Below</MenuItem>
-            <MenuItem eventKey="run-all" onSelect={=>@props.actions.run_all_cells()}>Run All</MenuItem>
-            <MenuItem eventKey="run-all-below">Run All Above</MenuItem>
-            <MenuItem eventKey="run-all-below">Run All Below</MenuItem>
-            <MenuItem divider />
-            <MenuItem eventKey="" disabled>Cell Type...</MenuItem>
-            <MenuItem eventKey="cell-type-code"      onSelect={=>@props.actions.set_selected_cell_type('code')} > Code</MenuItem>
-            <MenuItem eventKey="cell-type-markdown"  onSelect={=>@props.actions.set_selected_cell_type('markdown')} > Markdown</MenuItem>
-            <MenuItem eventKey="cell-type-nbconvert" onSelect={=>@props.actions.set_selected_cell_type('nbconvert')} > Raw NBConvert</MenuItem>
-            <MenuItem divider />
-            <MenuItem eventKey="" disabled>Current Outputs...</MenuItem>
-            <MenuItem eventKey="current-outputs-toggle"     > Toggle</MenuItem>
-            <MenuItem eventKey="current-outputs-toggle-scrolling" > Toggle Scrolling</MenuItem>
-            <MenuItem eventKey="current-outputs-clear"      > Clear</MenuItem>
-            <MenuItem divider />
-            <MenuItem eventKey="" disabled>All Output...</MenuItem>
-            <MenuItem eventKey="all-outputs-toggle"     > Toggle</MenuItem>
-            <MenuItem eventKey="all-outputs-toggle-scrolling" > Toggle Scrolling</MenuItem>
-            <MenuItem eventKey="all-outputs-clear"      > Clear</MenuItem>
-        </DropdownButton>
+        <Dropdown key='cell'  id='menu-cell'>
+            <Dropdown.Toggle noCaret bsStyle='default' style={border:0, backgroundColor: 'rgb(247,247,247)'}>
+                Cell
+            </Dropdown.Toggle>
+            <Dropdown.Menu style={opacity:OPACITY}>
+                <MenuItem eventKey="run-cells" onSelect={=>@props.actions.run_selected_cells()}>Run Cells</MenuItem>
+                <MenuItem eventKey="run-cells-select-below">Run Cells and Select Below</MenuItem>
+                <MenuItem eventKey="run-cells-insert-below">Run Cells and Insert Below</MenuItem>
+                <MenuItem eventKey="run-all" onSelect={=>@props.actions.run_all_cells()}>Run All</MenuItem>
+                <MenuItem eventKey="run-all-below">Run All Above</MenuItem>
+                <MenuItem eventKey="run-all-below">Run All Below</MenuItem>
+                <MenuItem divider />
+                <MenuItem eventKey="" disabled>Cell Type...</MenuItem>
+                <MenuItem eventKey="cell-type-code"      onSelect={=>@props.actions.set_selected_cell_type('code')} > Code</MenuItem>
+                <MenuItem eventKey="cell-type-markdown"  onSelect={=>@props.actions.set_selected_cell_type('markdown')} > Markdown</MenuItem>
+                <MenuItem eventKey="cell-type-nbconvert" onSelect={=>@props.actions.set_selected_cell_type('nbconvert')} > Raw NBConvert</MenuItem>
+                <MenuItem divider />
+                <MenuItem eventKey="" disabled>Current Outputs...</MenuItem>
+                <MenuItem eventKey="current-outputs-toggle"     > Toggle</MenuItem>
+                <MenuItem eventKey="current-outputs-toggle-scrolling" > Toggle Scrolling</MenuItem>
+                <MenuItem eventKey="current-outputs-clear"      > Clear</MenuItem>
+                <MenuItem divider />
+                <MenuItem eventKey="" disabled>All Output...</MenuItem>
+                <MenuItem eventKey="all-outputs-toggle"     > Toggle</MenuItem>
+                <MenuItem eventKey="all-outputs-toggle-scrolling" > Toggle Scrolling</MenuItem>
+                <MenuItem eventKey="all-outputs-clear"      > Clear</MenuItem>
+            </Dropdown.Menu>
+        </Dropdown>

     # obviously TODO regarding kernel selection
     render_kernel: ->
-        <DropdownButton noCaret bsStyle='default' title='Kernel' key='kernel'  id='menu-kernel'  style={border:0, backgroundColor: 'rgb(247,247,247)'}>
-            <MenuItem eventKey="kernel-interrupt">Inerrrupt</MenuItem>
-            <MenuItem eventKey="kernel-restart">Restart</MenuItem>
-            <MenuItem eventKey="kernel-restart-clear">Restart & Clear Output</MenuItem>
-            <MenuItem eventKey="kernel-run-all">Restart & Run All</MenuItem>
-            <MenuItem eventKey="kernel-reconnect">Reconnect</MenuItem>
-            <MenuItem divider />
-            <MenuItem eventKey="" disabled>Change kernel...</MenuItem>
-            <MenuItem eventKey="kernel-change-anaconda"     > Anaconda (Python 3)</MenuItem>
-            <MenuItem eventKey="kernel-change-python2sage" > Python 2 (SageMath</MenuItem>
-            <MenuItem eventKey="kernel-change-anaconda"     > Anaconda (Python 3)</MenuItem>
-            <MenuItem eventKey="kernel-change-python2sage" > Python 2 (SageMath</MenuItem>
-            <MenuItem eventKey="kernel-change-anaconda"     > Anaconda (Python 3)</MenuItem>
-            <MenuItem eventKey="kernel-change-python2sage" > Python 2 (SageMath</MenuItem>
-            <MenuItem eventKey="kernel-change-anaconda"     > Anaconda (Python 3)</MenuItem>
-            <MenuItem eventKey="kernel-change-python2sage" > Python 2 (SageMath</MenuItem>
-            <MenuItem eventKey="kernel-change-anaconda"     > Anaconda (Python 3)</MenuItem>
-            <MenuItem eventKey="kernel-change-python2sage" > Python 2 (SageMath</MenuItem>
-            <MenuItem eventKey="kernel-change-anaconda"     > Anaconda (Python 3)</MenuItem>
-            <MenuItem eventKey="kernel-change-python2sage" > Python 2 (SageMath</MenuItem>
-            <MenuItem eventKey="kernel-change-anaconda"     > Anaconda (Python 3)</MenuItem>
-            <MenuItem eventKey="kernel-change-python2sage" > Python 2 (SageMath</MenuItem>
-        </DropdownButton>
+        <Dropdown key='kernel'  id='menu-kernel'>
+            <Dropdown.Toggle noCaret bsStyle='default' style={border:0, backgroundColor: 'rgb(247,247,247)'}>
+                Kernel
+            </Dropdown.Toggle>
+            <Dropdown.Menu style={opacity:OPACITY}>
+                <MenuItem eventKey="kernel-interrupt">Inerrrupt</MenuItem>
+                <MenuItem eventKey="kernel-restart">Restart</MenuItem>
+                <MenuItem eventKey="kernel-restart-clear">Restart & Clear Output</MenuItem>
+                <MenuItem eventKey="kernel-run-all">Restart & Run All</MenuItem>
+                <MenuItem eventKey="kernel-reconnect">Reconnect</MenuItem>
+                <MenuItem divider />
+                <MenuItem eventKey="" disabled>Change kernel...</MenuItem>
+                <MenuItem eventKey="kernel-change-anaconda"     > Anaconda (Python 3)</MenuItem>
+                <MenuItem eventKey="kernel-change-python2sage" > Python 2 (SageMath</MenuItem>
+                <MenuItem eventKey="kernel-change-anaconda"     > Anaconda (Python 3)</MenuItem>
+                <MenuItem eventKey="kernel-change-python2sage" > Python 2 (SageMath</MenuItem>
+                <MenuItem eventKey="kernel-change-anaconda"     > Anaconda (Python 3)</MenuItem>
+                <MenuItem eventKey="kernel-change-python2sage" > Python 2 (SageMath</MenuItem>
+                <MenuItem eventKey="kernel-change-anaconda"     > Anaconda (Python 3)</MenuItem>
+                <MenuItem eventKey="kernel-change-python2sage" > Python 2 (SageMath</MenuItem>
+                <MenuItem eventKey="kernel-change-anaconda"     > Anaconda (Python 3)</MenuItem>
+                <MenuItem eventKey="kernel-change-python2sage" > Python 2 (SageMath</MenuItem>
+                <MenuItem eventKey="kernel-change-anaconda"     > Anaconda (Python 3)</MenuItem>
+                <MenuItem eventKey="kernel-change-python2sage" > Python 2 (SageMath</MenuItem>
+                <MenuItem eventKey="kernel-change-anaconda"     > Anaconda (Python 3)</MenuItem>
+                <MenuItem eventKey="kernel-change-python2sage" > Python 2 (SageMath</MenuItem>
+            </Dropdown.Menu>
+        </Dropdown>

     render_widgets: ->
-        <DropdownButton noCaret bsStyle='default' title='Widgets' key='widgets'  id='menu-widgets'  style={border:0, backgroundColor: 'rgb(247,247,247)'}>
-            <MenuItem eventKey="widgets-save-with-snapshots">Save notebook with snapshots</MenuItem>
-            <MenuItem eventKey="widgets-download">Download widget state</MenuItem>
-            <MenuItem eventKey="widgets-embed">Embed widgets</MenuItem>
-        </DropdownButton>
+        <Dropdown key='widgets' id='menu-widgets'>
+            <Dropdown.Toggle noCaret bsStyle='default' style={border:0, backgroundColor: 'rgb(247,247,247)'}>
+                 Widgets
+            </Dropdown.Toggle>
+            <Dropdown.Menu style={opacity:OPACITY}>
+                <MenuItem eventKey="widgets-save-with-snapshots">Save notebook with snapshots</MenuItem>
+                <MenuItem eventKey="widgets-download">Download widget state</MenuItem>
+                <MenuItem eventKey="widgets-embed">Embed widgets</MenuItem>
+            </Dropdown.Menu>
+        </Dropdown>

     render_help: ->
-        <DropdownButton noCaret bsStyle='default' title='Help' key='help'  id='menu-help'  style={border:0, backgroundColor: 'rgb(247,247,247)'}>
-            <MenuItem eventKey="help-ui-tour">User Interface Tour</MenuItem>
-            <MenuItem eventKey="help-keyboard">Keyboard Shortcuts</MenuItem>
-            <MenuItem divider />
-            <MenuItem eventKey="help-notebook-help"><Icon name='external-link'/> Notebook Help</MenuItem>
-            <MenuItem eventKey="help-markdown"><Icon name='external-link'/> Markdown</MenuItem>
-            <MenuItem divider />
-            <MenuItem eventKey="help-python"><Icon name='external-link'/> Python</MenuItem>
-            <MenuItem eventKey="help-ipython"><Icon name='external-link'/> IPython</MenuItem>
-            <MenuItem eventKey="help-numpy"><Icon name='external-link'/> NumPy</MenuItem>
-            <MenuItem eventKey="help-scipy"><Icon name='external-link'/> SciPy</MenuItem>
-            <MenuItem eventKey="help-matplotlib"><Icon name='external-link'/> Matplotlib</MenuItem>
-            <MenuItem eventKey="help-sympy"><Icon name='external-link'/> SymPy</MenuItem>
-            <MenuItem eventKey="help-pandas"><Icon name='external-link'/> Pandas</MenuItem>
-            <MenuItem eventKey="help-sagemath"><Icon name='external-link'/> SageMath</MenuItem>
-            <MenuItem divider />
-            <MenuItem eventKey="help-about">About</MenuItem>
-        </DropdownButton>
+        <Dropdown key='help'  id='menu-help'>
+            <Dropdown.Toggle noCaret bsStyle='default' style={border:0, backgroundColor: 'rgb(247,247,247)'}>
+                Help
+            </Dropdown.Toggle>
+            <Dropdown.Menu style={opacity:OPACITY}>
+                <MenuItem eventKey="help-ui-tour">User Interface Tour</MenuItem>
+                <MenuItem eventKey="help-keyboard">Keyboard Shortcuts</MenuItem>
+                <MenuItem divider />
+                <MenuItem eventKey="help-notebook-help"><Icon name='external-link'/> Notebook Help</MenuItem>
+                <MenuItem eventKey="help-markdown"><Icon name='external-link'/> Markdown</MenuItem>
+                <MenuItem divider />
+                <MenuItem eventKey="help-python"><Icon name='external-link'/> Python</MenuItem>
+                <MenuItem eventKey="help-ipython"><Icon name='external-link'/> IPython</MenuItem>
+                <MenuItem eventKey="help-numpy"><Icon name='external-link'/> NumPy</MenuItem>
+                <MenuItem eventKey="help-scipy"><Icon name='external-link'/> SciPy</MenuItem>
+                <MenuItem eventKey="help-matplotlib"><Icon name='external-link'/> Matplotlib</MenuItem>
+                <MenuItem eventKey="help-sympy"><Icon name='external-link'/> SymPy</MenuItem>
+                <MenuItem eventKey="help-pandas"><Icon name='external-link'/> Pandas</MenuItem>
+                <MenuItem eventKey="help-sagemath"><Icon name='external-link'/> SageMath</MenuItem>
+                <MenuItem divider />
+                <MenuItem eventKey="help-about">About</MenuItem>
+            </Dropdown.Menu>
+        </Dropdown>

     render: ->


Validation requiring path fix

Validation requiring path fix

--- browser.coffee
+++ browser.coffee
@@ -48,7 +48,7 @@
 	passwordRevealButton = '#revealpass'
 	passwordRevealIcon = '#revealpass i'
 	# Deps
-	validation = require './lib/validation.coffee'  # virtual name
+	validation = require '/lib/validation.coffee'  # virtual name
 	# Reveal password
 	$("body").on 'click', passwordRevealButton, ->
 		switch $(passwordField).prop('type')


Fix language item in roses.coffee

Fix language item in roses.coffee

--- handlers/roses.coffee
+++ handlers/roses.coffee
@@ -104,7 +104,7 @@

 				db.getUserByUsername username, (err, user) ->
 					unless user?
-						api.replyTo message, __("Unknown user “#{username}”."), no, callback
+						api.replyTo message, __("Unknown user “%1$s”.", username), no, callback
 						return

 					sender = {}


:lipstick: Add one more working test.

:lipstick: Add one more working test.

--- spec/jumpy-spec.coffee
+++ spec/jumpy-spec.coffee
@@ -39,7 +39,7 @@
             statusBarPromise

     describe "when the jumpy:toggle event is triggered", ->
-        fit "draws correct labels", ->
+        it "draws correct labels", ->
             labels = textEditorElement.querySelectorAll('.jumpy.label')
             expect(labels.length)
                 .toBe NUM_TOTAL_WORDS + NUM_CAMEL_SPECIFIC_MATCHES
@@ -48,7 +48,8 @@
             expect(labels[82].innerHTML).toBe 'de'
             expect(labels[83].innerHTML).toBe 'df'
         it "clears beacon effect", ->
-            expect(editorView.querySelectorAll('cursors .cursor.beacon')).not.toExist()
+            expect(textEditorElement.
+                querySelectorAll('cursors .cursor.beacon')).not.toExist()
         it "only uses jumpy keymaps", ->
             expect(atom.keymap.keyBindings.length).toBe (26 * 2) + 5 + 1


Actually make the latest and oldest params required.

--- packages/rocketchat-api/server/routes.coffee
+++ packages/rocketchat-api/server/routes.coffee
@@ -146,13 +146,14 @@
 		if not @bodyParams.roomId?
 			return RocketChat.API.v1.failure 'Body parameter "roomId" is required.'

-		latestDate = new Date
-		if @bodyParams.latest?
-			latestDate = new Date(@bodyParams.latest)
-
-		oldestDate = undefined
-		if @bodyParams.oldest?
-			oldestDate = new Date(@bodyParams.oldest)
+		if not @bodyParams.latest?
+			return RocketChat.API.v1.failure 'Body parameter "latest" is required.'
+
+		if not @bodyParams.oldest?
+			return RocketChat.API.v1.failure 'Body parameter "oldest" is required.'
+
+		latestDate = new Date(@bodyParams.latest)
+		oldestDate = new Date(@bodyParams.oldest)

 		inclusive = false
 		if @bodyParams.inclusive?


clean up uts.

clean up uts.

--- spec/utils-spec.coffee
+++ spec/utils-spec.coffee
@@ -30,9 +30,7 @@
     @editorFileWinFormat= "\\tmp\\utils-spec-dummy.ts"
     @editorFile2 = "/tmp/utils-spec-dummy2.ts"

-    #vtspyOn(@textEditor, "getURI").andReturn(@editorFile);
     spyOn(@textEditor, "getURI").andReturn(@editorFileWinFormat);
-    #vtspyOn(@textEditor2, "getURI").andReturn(@editorFile);
     # note: editor and editor2 need to use the same format to mimic a real test
     spyOn(@textEditor2, "getURI").andReturn(@editorFileWinFormat);
     spyOn(@textEditor3, "getURI").andReturn(@editorFile2);
@@ -49,33 +47,20 @@
     console.log("result.stringify2=#{JSON.stringify(result)}")

   it 'getTextEditors works', () ->
-    #vt add
-    # override @textEditor to return unix formatted files
-    #spyOn(@textEditor, "getURI").andReturn(@editorFile);
-    #vt end
     params = {}
     params.uri = @editorFile
-    # params.uri = @editorFileWinFormat

     result = @utils.getTextEditors params
-    #console.log("vt: result=#{JSON.stringify(result)}")
-    # console.log("vt: result=#{result}")
-    console.log("my object: %o", result)

     expect(result.length).toEqual(2)
-    # expect(result[0].getURI()).toEqual(@editorFile)
-    # expect(result[1].getURI()).toEqual(@editorFile)
     expect(result[0].getURI()).toEqual(@editorFileWinFormat)
     expect(result[1].getURI()).toEqual(@editorFileWinFormat)

-  #vt add
   it 'normalizePath works', () ->
     # windows path
     result = @utils.normalizePath('c:\\tmp\\dummy.txt')
-    console.log('result=' + result)
     expect(result).toEqual('c:/tmp/dummy.txt')

     # unix path
     result = @utils.normalizePath('/tmp/dummy.txt')
     expect(result).toEqual('/tmp/dummy.txt')
-  #vt end


移動操作をキー入力に対応した

移動操作をキー入力に対応した

--- src/coffee/kotoawase.coffee
+++ src/coffee/kotoawase.coffee
@@ -4,9 +4,12 @@
 KanaTable = require('./kana.coffee')
 {KanaComparator, ComparatorList} = require('./compalator.coffee')

+KEYCODE_LEFT = 37
+KEYCODE_UP = 38
+KEYCODE_RIGHT = 39
+KEYCODE_DOWN = 40
+
 Vue::attach = (selector) -> $(selector).append @$el
-
-Message_Empty = '　'

 $ ->
 	compList = new ComparatorList()
@@ -56,3 +59,15 @@
 						@$data.messageState = 'panel-danger'

 	content.attach '#stage'
+
+	$('body').keydown( (e) ->
+		switch e.keyCode
+			when KEYCODE_LEFT
+				content.shiftLeft()
+			when KEYCODE_UP
+				content.shiftUp()
+			when KEYCODE_RIGHT
+				content.shiftRight()
+			when KEYCODE_DOWN
+				content.shiftDown()
+	)


adding changes required by new coffee-subscript approach

--- src/apl.coffee
+++ src/apl.coffee
@@ -15,4 +15,6 @@
 # A `compile()` method is exposed so it can be called by
 # [coffee-subscript](https://github.com/ngn/coffee-subscript#readme) and
 # others.
-exports.compile = require('./compiler').compile
+compile = require("./compiler").compile
+exports.compile = (code) -> "(function(){#{compile code, embedded: true}})()"
+exports.fn = compile: (code) -> "function(){#{compile code, embedded: true}}"


tribal lands disclaimer

tribal lands disclaimer

--- src/app.coffee
+++ src/app.coffee
@@ -32,7 +32,7 @@
       useD3: true
       citation: "Arizona Geological Survey"
       details: '
Earth fissures are open surface fractures that may be as much as a mile in length, up to 15 ft wide, and 10s of feet deep. In Arizona, earth fissures form as a result of land subsidence driven by groundwater withdrawal. The Arizona Geological Survey has mapped and reported earth fissures throughout south-central and southeastern Arizona since the 1990s and published earth fissure study area maps in accordance with Arizona Revised Statute § 27-152.01(3) since 2007.  Mapping of fissures involves incorporating mapped earth fissures from previous studies, examining remotely sensed subsidence data and aerial imagery, and on-site field mapping using high-precision GPS receivers.

Between 2007 and 2012, AZGS identified 24 discreet areas throughout southern and central Arizona with concentrations of earth fissures totaling more than 150 miles of mapped fissures. These include uninterrupted open fissures and fissure gullies and discontinuous linear trends of potholes, abbreviated open cracks, elongated depressions, and other collapse features. An additional 180 miles of reported/unconfirmed earth fissures appear on AZGS maps. These features include fissures previously mapped by professional geologists appearing in published maps or figures which could not be confirmed through recent surface mapping by AZGS Geologists.

A reasonable effort was made to identify all earth fissures in the study area although some fissures may remain unmapped due to overprinting or disturbance by development, agricultural tilling, or lack of surface expression along the fissure at the time mapping was conducted. In addition, earth fissures are not static features. They may change in width, depth or length, and new fissures may appear after an area has been mapped. For these reasons the State of Arizona does not guarantee the maps are error free. Blank areas within study area boundaries have been investigated and no surface evidence of fissures was found as of the date of map publication.

For additional information on the origin and impacts of earth fissures and free PDF maps of earth fissure study areas, visit the Arizona Geological Survey’s Earth Fissure Center.
'
-      brief: 'Earth fissures are open surface fractures that may be as much as a mile in length, up to 15 ft wide, and 10s of feet deep.'
+      brief: '
Earth fissures are open surface fractures that may be as much as a mile in length, up to 15 ft wide, and 10s of feet deep.

Earth fissure hazard data are currently unavailable for tribal lands in Arizona. To assess earth fissure hazards on tribal lands, please seek out further information from the appropriate Tribal Division of Public Safety Department or Environmental Protection.

'
       links: [
           text: "AZGS Earth Fissure Center"
           url: "http://www.azgs.az.gov/EFC.shtml"
@@ -294,7 +294,7 @@
       active: false
       citation: "Arizona Division of Emergency Management"
       details: '
This layer shows areas with potentially High and Medium Risk of flooding. Data are drawn from the Federal Emergency Management Agency (FEMA) digital flood insurance rate maps (DFIRM) database, dated May 2010. High flooding potential includes areas covered by all of the “A” Special Flood Hazard zones. These areas have a 1% annual chance of flooding, also known as the 100-year flood, and a 26% chance of flooding within the span of a 30-year mortgage. Medium flood potential includes  areas within the “Shaded X” zones. These are areas between the limits of the 100-year and 500-year flood zones.

The flood potential layer was created by JE Fuller/Hydrology and Geomorpholgy, Inc. for the Arizona Division of Emergency Management (ADEM), as part of a larger study for the 2010 State of Arizona Multi-Hazard Mitigation Plan. Data is current as of May 2010. These data are provided as guidance only. Local Flood Control Districts should be consulted for additional or more detailed information.
'
-      brief: 'Areas with High and Medium flooding potential as represented by the 100- and 500- flood zones determined by the Federal Emergency Management Agency (FEMA) digital flood insurance rate maps (DFIRM) database, dated May 2010.'
+      brief: '
Areas with High and Medium flooding potential as represented by the 100- and 500- flood zones determined by the Federal Emergency Management Agency (FEMA) digital flood insurance rate maps (DFIRM) database, dated May 2010.

Flood hazard data are currently unavailable for tribal lands in Arizona. To assess flood hazards on tribal lands, please seek out further information from the appropriate Tribal Division of Public Safety Department or Environmental Protection.
'
       links: [
           text: "2010 State of Arizona Multi-Hazard Mitigation Plan"
           url: "http://www.dem.azdema.gov/operations/mitigation/hazmitplan/hazmitplan.html"


map view model

map view model

--- src/app.coffee
+++ src/app.coffee
@@ -515,6 +515,32 @@
             addresses: that.model.get("addresses").models

   )
+
+  MapView = Backbone.View.extend
+    el: "#address-map"
+    initialize: ->
+      require ["leaflet"], ->
+        @.map = L.map "address-map"
+          .setView [0, 0], 10
+    render: (options) ->
+      that = @
+      require ["leaflet"], ->
+        L.tileLayer 'http://{s}.tiles.mapbox.com/v3/steveranford.icifhpgl/{z}/{x}/{y}.png',
+          attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © Mapbox'
+          maxZoom: 18
+        .addTo @.map
+        console.log @el
+        $(that.el).hide()
+    addMarker: (lat,long,popupContent) ->
+      that = @
+      require ["leaflet"], ->
+        location = L.latLng lat,long
+        L.marker location
+          .addTo(@.map)
+          .bindPopup(popupContent).openPopup();
+        @.map.setView [lat, long], 15
+        $(that.el).show()
+
   Router = Backbone.Router.extend(routes:
     "": "home"
     "view/:id": "viewPerson"


Start the connectivity check straight away, rather than waiting for bootstrapping to succeed.

--- src/app.coffee
+++ src/app.coffee
@@ -33,6 +33,8 @@
 					ensureConnected(continuous)
 				, 10 * 1000) # Every 10 seconds perform this check.

+console.log('Starting connectivity check..')
+ensureConnected(true)

 knex('config').select('value').where(key: 'uuid').then ([ uuid ]) ->
 	if not uuid?.value
@@ -92,9 +94,6 @@
 	setInterval(updateIpAddr, 30 * 1000) # Every 30s
 	updateIpAddr()

-	console.log('Starting connectivity check..')
-	ensureConnected(true)
-
 	# Tell the supervisor updater that we have successfully started, so that it can do whatever it needs to.
 	supervisor.startupSuccessful()


changed earth fissure layer name

changed earth fissure layer name

--- src/app.coffee
+++ src/app.coffee
@@ -25,7 +25,7 @@
       layerName: "Earth Fissures"
       serviceUrl: app.geoserverUrl
       serviceType: "WFS"
-      typeName: "azgs:earthfissures2"
+      typeName: "azgs:earthfissures"
       active: false
       useD3: true
       citation: "Arizona Geological Survey"


Adds an event that fires when a route has completed.

--- src/app.coffee
+++ src/app.coffee
@@ -225,6 +225,7 @@
                 container.html @template(name)
                 parentController = container.parent().controller() or @rootController
                 @createController element: container, parent: parentController, name: name
+                @trigger 'routeComplete'
               @rootController.sync()
               window.scrollTo(0, 0)
               next(req, next) if subroutes


db store

db store

--- src/app.coffee
+++ src/app.coffee
@@ -2,7 +2,6 @@
 express = require('express');
 home = require('./../routes/lib/home');
 mongoose = require('mongoose');
-mongo = require('mongodb');
 passport = require('./authentication');
 mongoStore = require('connect-mongo')(express);
 moment = require('moment');
@@ -46,7 +45,7 @@

 # sessions
 storeConf = {
-	db: {db: config.dbname,host: 'localhost'},
+	db: {db: config.dbname,host: process.env.MONGOHQ_URL or 'mongodb://localhost/'+config.dbname},
 	secret: config.sessionSecret
 };


Resizes panes. All panes resize for now, whatevs!

--- src/atom/pane.coffee
+++ src/atom/pane.coffee
@@ -11,6 +11,42 @@
     @el = $ "

"
     @el.addClass "pane " + @position
     @el.append html
+
+    mousemove = (event) =>
+      if @position == "left"
+        @el.width event.clientX
+      else if @position == "right"
+        @el.width window.outerWidth - event.clientX
+      else if @position == "bottom"
+        @el.height window.outerHeight - event.clientY
+      else if @position == "top"
+        @el.height event.clientY - @el.offset().top
+
+    mousedown = (event) =>
+      maxEdgeDistance = 10
+      edgeDistance = switch @position
+        when 'top'
+          @el.height() - event.clientY + @el.offset().top
+        when 'left'
+          @el.width() - event.clientX + @el.offset().left
+        when 'bottom'
+          event.clientY - @el.offset().top
+        when 'right'
+          event.clientX - @el.offset().left
+        else
+          throw "pane position for #{this} can't be `#{@position}`"
+
+      if edgeDistance < maxEdgeDistance
+        $(document).on 'mouseup', 'body', mouseup
+        $(document).on 'mousemove', 'body', mousemove
+
+    mouseup = (event) =>
+      $(document).off 'mouseup', 'body', mouseup
+      $(document).off 'mousemove', 'body', mousemove
+
+    if @position != 'main'
+      id = "##{@paneID}"
+      $(document).on 'mousedown', id, mousedown

   add: ->
     verticalDiv = $('#app-vertical')


debug view base class

debug view base class

--- client/app/lib/view.coffee
+++ client/app/lib/view.coffee
@@ -1,5 +1,4 @@
 module.exports = class View extends Backbone.View
-    tagName: 'section'

     template: ->

@@ -10,9 +9,8 @@
         model: @model?.toJSON()

     render: ->
-        # console.debug "Rendering #{@constructor.name}", @
         @beforeRender()
-        @$el.html @template({})
+        @$el.html @template @getRenderData()
         @afterRender()
         @