Check all languages instead of just first for country servers

--- server_setup.coffee
+++ server_setup.coffee
@@ -80,14 +80,13 @@

 setupCountryRedirectMiddleware = (app, country="china", countryCode="CN", languageCode="zh", serverID="tokyo") ->
   shouldRedirectToCountryServer = (req) ->
-    firstLanguage = req.acceptedLanguages[0]
-    speaksLanguage = firstLanguage and firstLanguage.indexOf(languageCode) isnt -1
+    speaksLanguage = _.any req.acceptedLanguages, (language) -> language.indexOf languageCode isnt -1
     unless config[serverID]
       ip = req.headers['x-forwarded-for'] or req.connection.remoteAddress
       ip = ip?.split(/,? /)[0]  # If there are two IP addresses, say because of CloudFlare, we just take the first.
       geo = geoip.lookup(ip)
       #if speaksLanguage or geo?.country is countryCode
-      #  log.info("Should we redirect to #{serverID} server? speaksLanguage: #{speaksLanguage}, firstLanguage: #{firstLanguage}, ip: #{ip}, geo: #{geo} -- so redirecting? #{geo?.country is 'CN' and speaksLanguage}")
+      #  log.info("Should we redirect to #{serverID} server? speaksLanguage: #{speaksLanguage}, acceptedLanguages: #{req.acceptedLanguages}, ip: #{ip}, geo: #{geo} -- so redirecting? #{geo?.country is 'CN' and speaksLanguage}")
       return geo?.country is countryCode and speaksLanguage
     else
       #log.info("We are on #{serverID} server. speaksLanguage: #{speaksLanguage}, acceptedLanguages: #{req.acceptedLanguages[0]}")


support roomTypes with no permissions

support roomTypes with no permissions

--- packages/rocketchat-lib/lib/roomTypes.coffee
+++ packages/rocketchat-lib/lib/roomTypes.coffee
@@ -33,7 +33,7 @@
 	getAllTypes = ->
 		typesPermitted = []
 		roomTypesOrder.forEach (type) ->
-			if roomTypes[type].permissions? and RocketChat.authz.hasAtLeastOnePermission roomTypes[type].permissions
+			if not roomTypes[type].permissions? or RocketChat.authz.hasAtLeastOnePermission roomTypes[type].permissions
 				typesPermitted.push roomTypes[type]

 		return typesPermitted


Added console.info to snippets.cson

Added console.info to snippets.cson

--- share/snippets.cson
+++ share/snippets.cson
@@ -2,6 +2,9 @@
   'console.log':
     'prefix': 'log'
     'body': 'console.log(\'${1:Boot!}\');$2'
+  'console.info':
+    'prefix': 'info'
+    'body': 'console.info(\'${1:Boot!}\');$2'
   'console.error':
     'prefix': 'err'
     'body': 'console.error(\'${1:Boot!}\');$2'


update

update

--- lib/build/build-status-view.coffee
+++ lib/build/build-status-view.coffee
@@ -56,10 +56,10 @@
     socket.on 'timeout', ->
       console.log "socket timeout"

-    socket.on 'state', (state) =>
-      console.log "update state via socket"
-      @find('.task-state').text state
-      @updateQRCode() if state == 'complete'
+    socket.on 'update', (job) =>
+      console.log "task updated"
+      @find('.task-state').text job.state
+      @updateQRCode(job.data.platform) if job.state == 'complete'

   onClickRefresh: ->
     Q.nfcall request.get,
@@ -73,18 +73,21 @@

       @updateQRCode(body.data.platform) if body.state == 'complete'
       @showError() if body.state == 'failed'
+    .catch (err) ->
+      alert "error: #{err}"

   showError: ->

   updateQRCode: (platform) ->
+    console.log "update qrcode for platform: #{platform}"
     qr = qrcode(4, 'M')
-    # if platform == 'ios'
-    qr.addData("#{@serverSecured}/archives/#{@id}/install/ios")
-    # else if platform == 'android'
-      # qr.addData("#{@serverSecured}/archives/#{@id}.apk")
-    # else
-    #   throw new Error('qrcode: unkown platform.')
+    if platform == 'ios'
+      qr.addData("#{@serverSecured}/archives/#{@id}/install/ios")
+    else if platform == 'android'
+      qr.addData("#{@serverSecured}/archives/#{@id}.apk")
+    else
+      throw new Error('qrcode: unkown platform.')
     qr.make()
     imgTag = qr.createImgTag(8)
     @find('#qrcode').empty().append(imgTag)


Update: disable spell-check in atom

Update: disable spell-check in atom

--- Users/USERNAME/.atom/config.cson
+++ Users/USERNAME/.atom/config.cson
@@ -2,6 +2,9 @@
   "autoclose-html":
     makeNeverCloseSelfClosing: false
   core:
+    disabledPackages: [
+      "spell-check"
+    ]
     openEmptyEditorOnStart: false
     packagesWithKeymapsDisabled: [
       "platformio-ide-terminal"


Update the URL properly from wherever the query gets updated from.

--- web/site/search.coffee
+++ web/site/search.coffee
@@ -121,13 +121,6 @@
       console.log("The search box was :", $scope.searchBoxQuery)
       console.log("The original query was :", $scope.originalQuery)

-      if $scope.searchBoxQuery? and $scope.searchBoxQuery.length > 0
-        if $location.search()["debug"]?
-          $location.search("query=" + $scope.searchBoxQuery + "&debug=1")
-        else
-          $location.search("query=" + $scope.searchBoxQuery)
-      # $location.replace($location.search, $scope.searchBoxQuery)
-
       # Handle user clearing search box and hitting enter
       if $scope.searchBoxQuery? and $scope.searchBoxQuery != ""
         $scope.originalQuery = $scope.searchBoxQuery
@@ -138,6 +131,7 @@
       $scope.offset = 0
       console.log("NEW SEARCH:", $scope.query)
       $scope.search()
+

     # Def search: the function that is actually calling the search from Haskell.
     # Get the results from Solr and pass them into parseResults.
@@ -146,8 +140,17 @@
       if query.length > 0 and volId < 0
         $scope.query = query
         $scope.originalQuery = query
+        $scope.searchBoxQuery = query
       else if query.length > 0
         $scope.query = query
+
+      if $scope.searchBoxQuery? and $scope.searchBoxQuery.length > 0
+        if $location.search()["debug"]?
+          $location.search("query=" + $scope.searchBoxQuery + "&debug=1")
+        else
+          $location.search("query=" + $scope.searchBoxQuery)
+      else
+        $location.search("")

       if !$scope.query?
         $scope.query = "*"


added missing init file

added missing init file

--- Tests/spec/dialog.spec.coffee
+++ Tests/spec/dialog.spec.coffee
@@ -1,8 +1,6 @@
-if require?
-    require "../../../JsTestBundle/Resources/config/init-test.coffee"
-
-    # file to test
-    require "../../Resources/coffee/dialog.coffee"
+require "../../../JsTestBundle/Resources/config/init.coffee"
+# file to test
+require "../../Resources/coffee/dialog.coffee"

 describe "app dialog", ->


Route: fix infinite redirect loop

Route: fix infinite redirect loop

--- client/Intro/views/registerinlineform.coffee
+++ client/Intro/views/registerinlineform.coffee
@@ -148,7 +148,7 @@
           duration  : 2000

         firstRoute = KD.getSingleton("router").visitedRoutes.first
-        if firstRoute and /^\/(?:Reset|Register|Confirm)\//.test firstRoute
+        if firstRoute and /^\/(?:Reset|Register|Confirm|R)\//.test firstRoute
           firstRoute = "/Activity"

         {entryPoint} = KD.config


Dock: application quit icon hidden

Dock: application quit icon hidden

--- client/Main/navigation/navigationlink.coffee
+++ client/Main/navigation/navigationlink.coffee
@@ -31,18 +31,20 @@
     appsHasIcon.push 'Editor'
     @icon.hide()  if @name in appsHasIcon

-    @quitIcon = new KDCustomHTMLView
-      tagName  : "span"
-      cssClass : "quit-icon"
-      click    : =>
-        appManager = KD.singleton('appManager')
-        router     = KD.singleton('router')
+    # needs better styling and ux - SY

-        appManager.quitByName @name
+    # @quitIcon = new KDCustomHTMLView
+    #   tagName  : "span"
+    #   cssClass : "quit-icon"
+    #   click    : =>
+    #     appManager = KD.singleton('appManager')
+    #     router     = KD.singleton('router')

-        if appManager.getFrontApp().getOptions().name is @name
-          lastOpened = router.visitedRoutes[0]
-          router.back()
+    #     appManager.quitByName @name
+
+    #     if appManager.getFrontApp().getOptions().name is @name
+    #       lastOpened = router.visitedRoutes[0]
+    #       router.back()

     @on "DragStarted", @bound 'dragStarted'

@@ -58,7 +60,7 @@

     # This check is for custom items which isn't connected to an app
     # or if the item is a separator
-    return false  if not path or @positionChanged() or (event.target is @quitIcon.getElement())
+    return false  if not path or @positionChanged() # or (event.target is @quitIcon.getElement())

     mc = KD.getSingleton 'mainController'
     mc.emit "NavigationLinkTitleClick",
@@ -77,8 +79,8 @@
       {{> @icon}}

       #{@name}
-      {{> @quitIcon}}
     """
+      # {{> @quitIcon}}

   dragStarted: (event, dragState)->


Dock: item on drag weird y position hardcoded

--- client/Main/navigation/navigationlist.coffee
+++ client/Main/navigation/navigationlist.coffee
@@ -35,7 +35,7 @@

       view.on 'DragFinished', =>
         view.setX view._index * @viewWidth
-        view.setY view._y
+        view.setY 0
         lastChange  = 0

   moveItemToIndex:(item, index)->


Done with the Semantic-like module structure


activity: remove modal routes from main routes

--- client/activity/lib/reactivityroutes.coffee
+++ client/activity/lib/reactivityroutes.coffee
@@ -2,26 +2,13 @@
 ActivityAppComponent      = require 'activity/components/appcomponent'
 SingleChannelRoute        = require 'activity/routes/singlepublicchannel'
 SinglePrivateMessageRoute = require 'activity/routes/singleprivatechannel'
-ChannelThreadPane        = require 'activity/components/channelthreadpane'
-PrivateMessageThreadPane = require 'activity/components/privatemessagethreadpane'
-CreatePublicChannelModal = require 'activity/components/createpublicchannelmodal'
-CreatePrivateChannelModal = require 'activity/components/createprivatechannelmodal'
-BrowsePublicChannelsModal = require 'activity/components/browsepublicchannelsmodal'
-BrowsePrivateChannelsModal = require 'activity/components/browseprivatechannelsmodal'

 module.exports = [
   path: '/Channels'
-
   component: ActivityAppComponent
-
   onLeave: ->
     threadActions.changeSelectedThread null
     messageActions.changeSelectedMessage null
-
-  indexRoute:
-    components:
-      content: ChannelThreadPane
-      modal: BrowsePublicChannelsModal

   childRoutes: [
     new SingleChannelRoute
@@ -29,16 +16,8 @@
 ,
   path: '/Messages'
   component: ActivityAppComponent
-  indexRoute:
-    components:
-      content: PrivateMessageThreadPane
-      modal: BrowsePrivateChannelsModal
   childRoutes: [
     new SinglePrivateMessageRoute
-    path: '/NewMessage'
-    components:
-      content: PrivateMessageThreadPane
-      modal: CreatePrivateChannelModal
   ]
 ]


set finished to fix #169

set finished to fix #169

--- src/layout/mixins/events.coffee
+++ src/layout/mixins/events.coffee
@@ -35,6 +35,8 @@
       email:
         description: text + "\n\nCheck it out here!: " + document.location.href

+finished = false
+
 exports.events = {
   _LABEL: "events"
   interval: my.duration.step
@@ -44,6 +46,7 @@
   step: (world, button) ->
     world.send 'tick'
     world.send 'fetch'
+    return if finished
     world.send 'execute'
     world.send 'prefetch'

@@ -78,6 +81,7 @@
   done: (world, args) ->
     success = args > 0
     world.send 'stop'
+    finished = true
     if success
       $('.done_dialog').dialog
         modal: true


Read from the ports list

Read from the ports list

--- spec/Network.coffee
+++ spec/Network.coffee
@@ -189,11 +189,11 @@
       it 'shouldn\'t have the process in the old location', ->
         chai.expect(n.processes.Callback).to.be.undefined
       it 'should have informed the ports of their new node name', ->
-        for name, port of n.processes.Func.component.inPorts
+        for name, port of n.processes.Func.component.inPorts.ports
           chai.expect(port.name).to.equal name
           chai.expect(port.node).to.equal 'Func'
           chai.expect(port.getId()).to.equal "Func #{name.toUpperCase()}"
-        for name, port of n.processes.Func.component.outPorts
+        for name, port of n.processes.Func.component.outPorts.ports
           chai.expect(port.name).to.equal name
           chai.expect(port.node).to.equal 'Func'
           chai.expect(port.getId()).to.equal "Func #{name.toUpperCase()}"


Tests/Solving: Use finally instead of then

Required to get called if errors happens

--- spec/Solving.coffee
+++ spec/Solving.coffee
@@ -25,7 +25,8 @@
       layout = require './fixtures/helloworld/index'
       t.deliver page
       layout t
-      .then (c, d) ->
+      .finally (c, d) ->
+        chai.expect(d).to.be.a 'string'
         chai.expect(c.namedPath()).to.eql [
           'color'
           'user'
@@ -35,7 +36,7 @@
           'directed'
           'sections'
         ]
-        chai.expect(d).to.be.a 'string'
         clean = d.replace /\n/g, ''
         chai.expect(clean).to.equal '

Foo

Bar

'
         return done()
+


OAuth: unify checkGroupIntegrationSettings and use updated version on GL

--- workers/social/lib/social/models/oauth.coffee
+++ workers/social/lib/social/models/oauth.coffee
@@ -22,6 +22,33 @@

   # -- OAUTH PROVIDERS ----------------------------------------------------8<--

+  checkGroupIntegrationSettings = (provider, client, callback) ->
+
+    { sessionToken: clientId, context: { group: slug } } = client
+
+    JSession = require './session'
+    JSession.one { clientId }, (err, session) ->
+      return callback err  if err
+      return callback new KodingError 'Session invalid'  unless session
+
+      JGroup = require './group'
+      JGroup.one { slug }, (err, group) ->
+
+        if not err and group and group.config?[provider]?.enabled
+
+          settings = {
+            url: group.config[provider].url
+            applicationId: group.config[provider].applicationId
+            state: session._id
+          }
+
+          callback null, settings
+
+        else
+
+          callback new KodingError 'Integration is not enabled'
+
+
   @PROVIDERS =

     # -- GITLAB PROVIDER --------------------------------------------------8<--
@@ -30,32 +57,6 @@
       title   : 'GitLab OAuth Provider'
       enabled : true
       getUrl  : (client, urlOptions, callback) ->
-
-        checkGroupGitLabSettings = (client, callback) ->
-
-          { sessionToken: clientId, context: { group: slug } } = client
-
-          JSession = require './session'
-          JSession.one { clientId }, (err, session) ->
-            return callback err  if err
-            return callback new KodingError 'Session invalid'  unless session
-
-            JGroup = require './group'
-            JGroup.one { slug }, (err, group) ->
-
-              if not err and group and group.config?.gitlab?.enabled
-
-                settings = {
-                  url: group.config.gitlab.url
-                  applicationId: group.config.gitlab.applicationId
-                  state: session._id
-                }
-
-                callback null, settings
-
-              else
-
-                callback new KodingError 'Integration is not enabled'

         { returnUrl, redirectUri } = urlOptions
         { applicationId, host, port } = KONFIG.gitlab
@@ -64,7 +65,7 @@
         host = urlOptions.host ? host
         redirectUri = "#{redirectUri}?returnUrl=#{returnUrl}"  if returnUrl

-        checkGroupGitLabSettings client, (err, data) ->
+        checkGroupIntegrationSettings 'gitlab', client, (err, data) ->
           return callback err  if err

           url = "#{protocol}#{host}#{port}"


Fix those headers

Fix those headers

--- lib/styles/default/behavior.coffee
+++ lib/styles/default/behavior.coffee
@@ -4,9 +4,7 @@
 MAX_FILTER_SIZE = 20

-#############
-# Searching #
-#############
+# ## Searching

 # A map of search string -> DOM node (the links in the table of contents)
 searchableNodes = []
@@ -65,9 +63,7 @@
   node$.html markedText + nodeText[furthestIndex...]

-####################
-# DOM Construction #
-####################
+# ## DOM Construction

 buildNav = (relativeRoot) ->
   nav$ = $("""


Ensure that all unused list items get removed

--- lib/suggestion-list-element.coffee
+++ lib/suggestion-list-element.coffee
@@ -106,7 +106,6 @@
   renderItems: ->
     items = @visibleItems() || []
     items.forEach ({word, label, renderLabelAsHtml, className}, index) =>
-
       li = @ol.childNodes[index]
       unless li
         li = document.createElement('li')
@@ -140,8 +139,7 @@
       else
         labelSpan?.remove()

-    itemToRemove = items.length
-    li.remove() while li = @ol.childNodes[itemToRemove++]
+    li.remove() while li = @ol.childNodes[items.length]

     @selectedLi?.scrollIntoView(false)


Close overlay on hash change regardless of click, closes #40

--- app/controllers/overlay.coffee
+++ app/controllers/overlay.coffee
@@ -13,8 +13,6 @@

   hidden: true

-  clickedVeryRecently: false
-
   constructor: ->
     super
     @el.addClass @constructor::className
@@ -24,13 +22,8 @@
     @hide() if @hidden
     @el.prependTo document.body

-  onClick: =>
-    @clickedVeryRecently = true
-    setTimeout (=> @clickedVeryRecently = false), 50
-
   onHashChange: =>
-    if @clickedVeryRecently
-      setTimeout (=> @hide()), 250
+    setTimeout (=> @hide()), 250

   onKeyDown: (e) =>
     if e.which is ESC
@@ -41,17 +34,15 @@

   show: ->
     @el.toggleClass 'hidden', false
-    $(@associated)?.toggleClass 'showing-overlay', true
+    $(@associated).toggleClass 'showing-overlay', true
     @hidden = false
-    addEventListener 'click', @onClick, false
     addEventListener 'hashchange', @onHashChange, false
     addEventListener 'keydown', @onKeyDown, false

   hide: ->
     @el.toggleClass 'hidden', true
-    $(@associated)?.toggleClass 'showing-overlay', false
+    $(@associated).toggleClass 'showing-overlay', false
     @hidden = true
-    removeEventListener 'click', @onClick, false
     removeEventListener 'hashchange', @onHashChange, false
     removeEventListener 'keydown', @onKeyDown, false


remove console.log messages

remove console.log messages


Remove commented emails

Remove commented emails


refactor($tests): virtualEnv helpers uses match matcher

The helpers methods defined in virtualEnv now uses the match matcher
to compare results or error message with the regexp.

--- specs/support/helpers/spec_helpers.coffee
+++ specs/support/helpers/spec_helpers.coffee
@@ -42,8 +42,7 @@
           async.reject new Error "run failed"

       it "status", -> @status.should be 1
-      it "should fail with #{re}", ->
-        re.test(@results).should be true
+      it 'results', -> @results.should match re

   runShouldFailWith: (re, block) ->
     describe desc, ->
@@ -66,5 +65,5 @@

           async.resolve()

-      it "run should fail with #{re}", ->
-        re.test(@reason.message).should be true
+      it 'error message', ->
+        @reason.message.should match re


app: Remove App#kinetic_panel which is no longer used (use #table instead).

--- oghma/app.coffee
+++ oghma/app.coffee
@@ -53,9 +53,6 @@

   # Viewport
   viewport: null
-
-  # {Oghma.Ext.Kinetic} Panel.
-  kinetic_panel: null

   # Synonym for `kinetic_panel.stage`.
   stage: null


signature: allow ObjectIds in signature annotations

--- node_modules_koding/bongo/lib/signature.coffee
+++ node_modules_koding/bongo/lib/signature.coffee
@@ -1,4 +1,6 @@
 class Signature
+
+  { ObjectId } = require 'bongo'

   constructor: (@paramTypes) ->
     rest = no
@@ -62,7 +64,7 @@
     when type is Number
       return 'number' is typeof param

-    when type is String
+    when type is String, ObjectId # ObjectIds come as strings from the client
       return 'string' is typeof param

     # NOTE: I don't care about old safari/chrome RegExp bug.  This is node.js.


add modal back

add modal back

--- views/components/main/parts/miniship.cjsx
+++ views/components/main/parts/miniship.cjsx
@@ -131,6 +131,25 @@
               deckId = parseInt(postBody.api_deck_id) - 1
               inBattle[deckId] = true
           escapeId = towId = -1
+          {decks, states} = @state
+          {_ships, _slotitems} = window
+          damagedShips = []
+          for deckId in [0..3]
+            continue unless inBattle[deckId]
+            deck = decks[deckId]
+            for shipId, idx in deck.api_ship
+              continue if shipId == -1 or idx == 0
+              ship = _ships[shipId]
+              if ship.api_nowhp / ship.api_maxhp < 0.250001 and !goback[shipId]
+                # 应急修理要员/女神
+                safe = false
+                for slotId in ship.api_slot.concat(ship.api_slot_ex || -1)
+                  continue if slotId == -1
+                  safe = true if _slotitems[slotId].api_type[3] is 14
+                if !safe
+                  damagedShips.push("Lv. #{ship.api_lv} - #{ship.api_name}")
+          if damagedShips.length > 0
+            toggleModal __('Attention!'), damagedShips.join(' ') + __('is heavily damaged!')
         else
           flag = false
       return unless flag


Fix crash when window-plugin loaded

Fix crash when window-plugin loaded

--- views/tabarea.cjsx
+++ views/tabarea.cjsx
@@ -203,8 +203,7 @@
         <MenuItem key={plugin.name} eventKey={plugin.name} onSelect={plugin.handleClick}>
           {plugin.displayName}
         </MenuItem>
-    pluginContents = for plugin, index in @state.plugins when !plugin.handleClick?
-      if !plugin.enabled then continue
+    pluginContents = for plugin, index in @state.plugins when !plugin.handleClick? && !plugin.windowURL? && plugin.enabled
       <div id={plugin.name} key={plugin.name} className="poi-app-tabpane poi-plugin"
         onSelected={(key) => @setState {activePluginName: key}}>
         <PluginWrap plugin={plugin} />


Change JSX autoindent handling. Support indeting multiple blocks of JSX interspersed with JS as would be invoked by using the Atom menu Edit->Lines->Auto Indent for all text. [ci skip]

--- lib/auto-indent.coffee
+++ lib/auto-indent.coffee
@@ -55,10 +55,11 @@
     document.addEventListener 'mouseup', @onMouseUp

     @disposables.add @editor.onDidChangeCursorPosition (event) => @changedCursorPosition(event)
-    @disposables.add @editor.onDidStopChanging () => @didStopChanging()
+    @handleOnDidStopChanging()

   destroy: () ->
     @disposables.dispose()
+    @onDidStopChangingHandler.dispose()
     document.removeEventListener 'mousedown', @onMouseDown
     document.removeEventListener 'mouseup', @onMouseUp

@@ -111,10 +112,27 @@
         return

     highestRow = Math.max selectedRange.start.row, selectedRange.end.row
-    if @jsxInScope(highestRow)
-      endPointOfJsx = new Point highestRow,0
-      startPointOfJsx =  autoCompleteJSX.getStartOfJSX @editor, endPointOfJsx
-      @indentJSX new Range(startPointOfJsx, endPointOfJsx)
+    lowestRow = Math.min selectedRange.start.row, selectedRange.end.row
+
+    # remove the handler for didStopChanging to avoid this change causing a new event
+    @onDidStopChangingHandler.dispose()
+
+    # work backwards through buffer rows indenting JSX as needed
+    while ( highestRow >= lowestRow )
+      if @jsxInScope(highestRow)
+        endPointOfJsx = new Point highestRow,0
+        startPointOfJsx =  autoCompleteJSX.getStartOfJSX @editor, endPointOfJsx
+        @indentJSX new Range(startPointOfJsx, endPointOfJsx)
+        highestRow = startPointOfJsx.row - 1
+      else highestRow = highestRow - 1
+
+    # renable this event handler after 300ms as per the default timeout for change events
+    # to avoid this method being recalled!
+    setTimeout(@handleOnDidStopChanging, 300)
+    return
+
+  handleOnDidStopChanging: =>
+    @onDidStopChangingHandler = @editor.onDidStopChanging () => @didStopChanging()

   # is the jsx on this line in scope
   jsxInScope: (bufferRow) ->


we should REALLY upgrade marionette...

we should REALLY upgrade marionette...

--- core/app/backbone/views/profile_view.coffee
+++ core/app/backbone/views/profile_view.coffee
@@ -14,6 +14,8 @@

   initialize: ->
     @collection = @ordered(@collection)
+  # TODO remove on updating marionette
+  initialEvents: -> # don't rerender on collection change

   ordered: (collection)->
     ordered_channels = new ChannelList()


Sprite prerendering.

Sprite prerendering.

--- emulator/core/units/ppu.coffee
+++ emulator/core/units/ppu.coffee
@@ -135,6 +135,8 @@
         @secondaryOAM = (new Sprite for [0..7])
         @spriteCount = 0
         @spriteNumber = 0
+        @spriteScanline = (0 for [0..261])
+        @spriteScanlinePixels = (null for [0..261])

     resetRegisters: ->
         @setControl 0       #  8-bit PPUCTRL register
@@ -467,6 +469,7 @@
         @cycle = 0
         @scanline++
         @incrementFrame() if @scanline > 261
+        @prerenderSprites() if 0 <= @scanline <= 239

     incrementFrame: ->
         @scanline = 0
@@ -516,7 +519,7 @@
     renderFramePixel: ->
         @renderedSprite = null
         backgroundColor = @renderBackgroundPixel()
-        spriteColor = @renderSpritePixel()
+        spriteColor = @$renderSpritePixel()
         if (spriteColor & 0x03) and (backgroundColor & 0x03)
             @spriteZeroHit ||= @renderedSprite.zeroSprite                    # Both bagckground and sprite pixels are visible
             if @renderedSprite.inFront then spriteColor else backgroundColor # Choose target by rendering priority
@@ -631,22 +634,31 @@
     #   C = color palette number
     # Byte 3 = x screen coordinate

-    renderSpritePixel: ->
-        return 0 if @$isSpritePixelInvisible()
-        rightX = @cycle - 1
-        leftX = rightX - 8
+    prerenderSprites: ->
+        for i in [0...@spriteScanline.length]
+            @spriteScanline[i] = null
+            @spriteScanlinePixels[i] = 0
         for spriteNumber in [0...@spriteCount]
             sprite = @secondaryOAM[spriteNumber]
-            if leftX <= sprite.x <= rightX
-                columnNumber = rightX - sprite.x
-                columnNumber ^= 0x07 if not sprite.horizontalFlip
+            for columnNumber in [0..7]
+                x = sprite.x + columnNumber + 1
+                break if x > FRAME_BUFFER_WIDTH
+                continue if x < 1 or @spriteScanline[x]
+                columnNumber ^= 0x07 unless sprite.horizontalFlip
                 colorBit0 = ((sprite.patternRow0 >>> columnNumber) & 1)
                 colorBit1 = ((sprite.patternRow1 >>> columnNumber) & 1) << 1
                 colorNumber = colorBit1 | colorBit0
                 if colorNumber
-                    @renderedSprite = sprite
-                    return sprite.paletteNumber | colorNumber
-        return 0
+                    @spriteScanlinePixels[x] = sprite.paletteNumber | colorNumber
+                    @spriteScanline[x] = sprite
+        undefined
+
+    renderSpritePixel: ->
+        if @$isSpritePixelInvisible()
+            0
+        else
+            @renderedSprite = @spriteScanline[@cycle]
+            @spriteScanlinePixels[@cycle]

     isSpritePixelInvisible: ->
         not @spritesVisible or @spriteClipping and (@cycleFlags & F_CLIP_LEFT)


add reconnect delay

add reconnect delay

--- lib/client/javascripts/nali/connection.js.coffee
+++ lib/client/javascripts/nali/connection.js.coffee
@@ -15,8 +15,10 @@

   keepAliveTimer: null
   journal:        []
+  reconnectDelay: 0

   onOpen: ( event ) ->
+    @reconnectDelay = 0
     @trigger 'open'

   onError: ( event ) ->
@@ -24,7 +26,8 @@

   onClose: ( event ) ->
     @trigger 'close'
-    @open()
+    setTimeout ( => @open() ), @reconnectDelay * 100
+    @reconnectDelay += 1

   onMessage: ( message ) ->
     @[ message.action ] message


improve .version() handling

improve .version() handling

--- src/Database.coffee
+++ src/Database.coffee
@@ -1,274 +1,217 @@
-# js-idb-wrapper
-This wrapper is aimed to simplify the use of indexedDB and is designed to work as lazy as it can: all databases and object stores will not be created, upgraded or opened as long as no any explicit data operations perform.
-
-
-## Usage
-
-Get a database reference:
-```js
-//get a reference of the indexedDB named "app_db"
-//it wouldn't really open the db
-db = IDB('app_db');
-```
-
-Get a object store reference:
-```js
-//get a reference of the object store "users" inside the "app_db" indexedDB
-//it wouldn't really open the db
-users = IDB('app_db').store('users');
-```
-
-Define a database schema:
-```js
-//describing how object stores inside app_db would look like
-//upgrade will be performed only when:
-//  1. the db is attempt to open for actual data read/write, and
-//  2. the stored db version is smaller than the largest defined version.
-IDB('app_db').version(1, {
-
-    //store named "users"
-    "users": [
-        "KEY(id) AUTO",  //the keypath of the objects will be auto increased and assgin to the property "id"
-        "name",  //the property named "idx1" will be well indexed
-        "email UNIQUE" //the index will not allow duplicate values for a single key
-    ],
-
-    //store named "locaions"
-    "locations": [
-        "KEY AUTO",  //the key will be auto increased but not assign into the objects
-        "name",
-        "pos.x",  //can index nested porperty
-        "pos.y",
-        "point(pos.x,pos.y)",  //compound index, where properties "x" and "y" can be found in the objects
-        "tags ARRAY" //the index will add an entry in the index for each array element
-    ]
-});
-```
-
-You can upgrade the database by defining a larger version:
-```js
-IDB('app_db').version(1, {  //define version 1
-    "store1": [
-        "KEY AUTO",
-        "idx1",
-        "idx2 UNIQUE",
-        "idx3 ARRAY"
-}).version(2, {  //this is version 2
-    "store1", [
-        "KEY AUTO",  //do not make any change on store's keypath
-        "idx1 ARRAY",  //can re-index with "multi-entry" feature
-        "idx2",  //can remove "unique" feature, but cannot add it back
-        "idx3", //can re-index without "multi-entry" feature
-});
-//notice: the database will not upgrade util the first explicit operation perform.
-```
-The database will only upgrade to the largest version and skip all other smaller versions. You can, however, define different versions in your code for readability.
-
-
-#### accessing stored data
-The following operations will open (and upgrade if need) corresponding database to access data in stores.
-
-Almost all of the functions will return a promise object. A promise object contains two functions:
-1. promise.then(), to retrieve data
-2. promise.catch(), to catch any exceptions.
-
-
-##### retrieving meta data
-Get database's name:
-```js
-IDB('app_db').name(function(dbName) {
-    console.log(dbName); //the database's name
-}).then(function() {
-    //do something after name get
-});
-```
-
-Get database's version:
-```js
-IDB('app_db').version(function(dbVersion) {
-    console.log(dbVersion); //the database's version
-}).then(function() {
-    //do something after version get
-});
-```
-
-Get store's name:
-```js
-IDB('app_db').store('users').name(function(storeName) {
-    console.log(storeName); //the store's name
-}).then(function() {
-    //do something after name get
-});
-```
-
-Get store's keypath:
-```js
-IDB('app_db').store('users').key().then(function(keyPath) {
-    console.log(keyPath); //the store's keypath
-}).then(function() {
-    //do something after keypath get
-});
-```
-
-Get store's index names:
-```js
-IDB('app_db').store('users').indexes(function(indexNames) {
-    for (var i in indexNames) { console.log(indexNames[i]); } //array of index names
-}).then(function() {
-    //do something after index names get
-});
-```
-
-
-##### access store objects
-add/update/delete objects in store:
-```js
-users = IDB('app_db').store('users');
-
-//add data
-users.add({name: "Andy", email: "andy@mail.com"}).then(function() {
-    //do something after data added
-}).catch(function() {
-    //fail to add data
-});
-
-//update data with key(id) = 1
-users.update({id: 0, name: "Ben", email: "ben@mail.com").then(function(){
-    //do something after data updated
-}).catch(function() {
-    //fail to update data
-});
-
-//remove data with key = 1
-users.delete(0).then(function(){function(){
-    //do something after data deleted
-}).catch(function() {
-    //fail to delete data
-});
-
-//remove all data
-users.clear().then(function(){function(){
-    //do something after store clear
-}).catch(function() {
-    //fail to clear store
-});
-```
-
-You can perform multiple operations on multiple stores in one database. All actions will be execute in one transaction:
-```js
-//you need providing the name of the stores you want to work on
-IDB("app_db").batch("users", "locations", function(users, locations) {
-    users.add({name: "C", email: "C@mail.com"});
-    users.add({name: "D", email: "D@mail.com"});
-    users.add({name: "E", email: "E@mail.com"});
-    users.update({id: 67, name: "SomeOne", email: "some@one.com"});
-
-    locations.add({x: 2, y: 3});
-    locations.remove(12);
-}).then(function() {
-    //do something after operations done
-}).catch(function() {
-    //fail to complete all tasks, nothing changed
-});
-```
-
-##### Query
-query store's objects over a defined index:
-```js
-locations = IDB('app_db').store('locations');
-
-
-query01 = locations.where("name = China");
-
-query02 = locations.where("name = 'Hong Kong'"); //use '...' if there are any spaces
-
-query03 = locations.where("pos.x > 5");
-
-query04 = locations.where("pos.x < 8");
-
-query05 = locations.where("pos.x >= 4");
-
-query06 = locations.where("pos.x <= 7");
-
-query07 = locations.where("1 < pos.x < 9");
-
-query08 = locations.where("2 <= pos.x < 9");
-
-query09 = locations.where("1 < pos.x <= 8");
-
-query10 = locations.where("2 <= pos.x <= 8");
-
-query11 = locations.where("9 > pos.x > 1");
-
-query12 = locations.where("8 => pos.x > 1");
-
-query13 = locations.where("9 > pos.x => 2");
-
-query14 = locations.where("8 => pos.x => 2");
-
-query15 = locations.where("[2,1] <= position <= [9,8]"); //use [...] for compound index
-
-query16 = locations.all(); //select all objects in store, no filter here
-
-
-//reverse the result set
-query16r1 = query16.order(-1); //, or
-query16r2 = query16.order("desc"); //, or
-query16r3 = query16.order("DESC");
-
-
-//limit the result set (limitation always applied after order reversed.)
-query16l1 = query16.limit(5); //limit to the first 5 items
-query16l2 = query16.limit(3, 6); //limit to the first 6 items starting at the 3rd one
-
-
-//get the first reached item
-query01.first(function(item, key) {/*...*/});
-
-//iterate each item
-query03.each(function(item, key) {/*...*/});
-
-//get the whole result set
-query05.list(function(itemArray, keyArray) {/*...*/});
-
-//count the number of items in the result set
-query07.count(function(totalNumber) {/*...*/});
-
-
-//you can chaining the function call above:
-IDB('app_db').store('locations').where('position < [5,9]').order(-1).limit(2,2).each(function(item, key) {
-    //do something with each item
-    return [key, item.x];
-}).then(function(iteratedResult) {
-    //do something after query
-    for (var i in iteratedResult) {
-        d = iteratedResult[i];
-        console.log("object(key="+d[0]+") has property x with value "+d[1]);
-    }
-    //...
-}).catch(function() {
-    //fail to query data
-});
-```
-
-##### remove database
-To remove the whole database, use reomve() function:
-```js
-IDB('app_db').remove().then(function() {
-    //do something after removed
-}).catch(function() {
-    //fail to remove database
-});
-```
-
-
-#### on version conflict
-There may be a case that the database is ready to be upgraded but it is still in use somewhere, say, an older version of web page is opened in anther tab. (see: https://developer.mozilla.org/en-US/docs/Web/API/IDBOpenDBRequest/onblocked)
-
-You can set a handler to deal with the case:
-```js
-IDB('app_db').onVersionConflict(function() {
-    //handle the case here
-    //for example, refresh the page
-}).version(/*...*/);  //return the database reference for chaining
-```
+class Database
+
+
+	constructor: (dbName)->
+		@_name = dbName                              #the database name
+		@_version = null                             #the database version
+		@_dbDefinition = null                        #the Schema object for this database
+		@_idbDatabase = null                         #the IDBDatabase object this object belong to
+		@_batchTx = null                             #IDBTransaction object that used for batch process
+		@_onVersionConflictHandler = (event)->       #IDBOpenDBRequest.onblocked handler
+			throw event
+
+
+
+
+	#return the database name
+	name: (cb)->
+		@getIDBDatabase().then (idb)->cb(idb.name)
+
+
+
+
+	#if not parameters given, return the database's version
+	#if paramters given, define the database with given version
+	version: (args...)->
+
+		if args.length is 1
+			cb = args[0]
+			@getIDBDatabase().then (idb)->cb(idb.version)
+
+		else if args.length is 2
+			versionNumber = args[0]
+			dbDefination = args[1]
+
+			#define the database only if the version is larger
+			if @_version is null or @_version <= versionNumber
+				@_dbDefinition = dbDefination
+				@_version = versionNumber
+
+				#close current idb connection if any
+				if @_idbDatabase isnt null
+					@_idbDatabase.close()
+					@_idbDatabase =
+					@_batchTx = null
+
+				@  #return this for chaining
+
+
+
+
+	#setter of IDBOpenDBRequest.onblocked handler
+	onVersionConflict: (handler)->
+		@_onVersionConflictHandler = handler
+
+		@  #return this for chaining
+
+
+
+
+	#getter of IDBDatabase object which this object refer to
+	getIDBDatabase: ()->
+		if @_idbDatabase?
+			newPromise @_idbDatabase
+		else
+			r = indexedDB.open(@_name, @_version)
+			r.onupgradeneeded = (event)=>@doUpgrade(event.target.result, event.target.transaction)
+			r.onblocked = (event)=>
+				if event.newVersion is @_version  #check if required version is defined
+					return  #simply return
+				else
+					@_onVersionConflictHandler(event) if typeof @_onVersionConflictHandler is "function"
+
+			IDBRequest2Q( r )
+			.then (event)=>
+				@_idbDatabase = event.target.result
+
+
+
+
+	#getter of IDBTransaction objects of this object
+	#return _batchTx if set (by calling @batch)
+	#or return a new IDBTransaction object
+	getIDBTransaction: (storeNames, mode)->
+		if @_batchTx
+			newPromise @_batchTx
+		else
+			@getIDBDatabase().then (idb)->
+				idb.transaction(storeNames, mode)
+
+
+
+
+	store: (storeName)->
+		StoreManager(storeName, @)
+
+
+
+
+	close: ->
+		@_idbDatabase.close()
+		@_idbDatabase =
+		@_batchTx = null
+
+
+
+
+	#remove the database form disk
+	#this object will no longer usable
+	remove: ->
+		r = indexedDB.deleteDatabase(@_name)
+		deferred = newDefer()
+		#ignore onblocked(IDBVersionChangeEvent) because we're deleteing db
+		r.onblocked = deferred.resolve
+		r.onerror = deferred.reject
+		r.onsuccess = deferred.resolve
+		(toPromise deferred)
+		.then =>
+			#clear all properties
+			@_name =
+			@_version =
+			@_dbDefinition =
+			@_onVersionConflictHandler =
+			@_idbDatabase =
+			@_batchTx = null
+
+
+
+
+	#create a transaction for upcoming actions
+	#usage: db.batch("store1", "store2", ..., batchFunction)
+	batch: (storeNames..., batchFunc)->
+
+		#first, clear any previous batch transaction
+		@_batchTx = null
+
+		#get and set a new transaction to _batchTx
+		@getIDBTransaction(storeNames, "readwrite").then (tx)=>
+			@_batchTx = tx
+
+			#run function
+			try
+				batchFunc.apply null, (@.store(storeName) for storeName in storeNames)
+			finally
+				@_batchTx = null #clear tx
+
+			IDBTx2Q(tx)
+
+
+
+
+
+	doUpgrade: (idb, tx)->
+
+		if @_dbDefinition is null
+			throw new IDBError "DB definition not found."
+
+		_schema = new Schema(@_dbDefinition)
+
+		#list of functions that make change to the db
+		actions = []
+
+		currentStoreNames = idb.objectStoreNames
+
+		#update/remove indexes of current stores
+		for storeName in currentStoreNames
+			do (storeName)->
+				if _schema.stores.hasOwnProperty storeName
+					store = tx.objectStore(storeName)
+					currentIndexNames = store.indexNames
+					storeSchema = _schema.stores[storeName]
+
+					for indexName in currentIndexNames
+						do (indexName)->
+							if storeSchema.indexes.hasOwnProperty indexName
+								index = store.index(indexName)
+								indexSchema = storeSchema.indexes[indexName]
+
+								#adding UNIQUE for existed index is not allow
+								if not index.unique and indexSchema.option.unique
+									throw new IDBError("Turning existed index(#{indexName}) to be unique is not allowed.")
+
+								#rebuild the index if any changes on schema
+								#TAKE YOU"RE OWN RISK!!
+								if (index.keyPath isnt indexSchema.key or
+								index.unique isnt indexSchema.option.unique or
+								index.multiEntry isnt indexSchema.option.multiEntry)
+									actions.push ->store.deleteIndex(indexName)
+									actions.push ->store.createIndex(indexName, indexSchema.key, indexSchema.option)
+
+							#remove unused index
+							else
+								actions.push ->store.deleteIndex(indexName)
+
+					#create newly added indexes
+					for indexName, indexSchema of storeSchema.indexes when indexName not in currentIndexNames
+						do (store, indexName, indexSchema)->
+							actions.push ->
+								store.createIndex(indexName, indexSchema.key, indexSchema.option)
+
+				#remove unused stores
+				else
+					actions.push ->idb.deleteObjectStore(storeName)
+
+
+		#create newly added stores
+		for storeName, storeSchema of _schema.stores when storeName not in currentStoreNames
+			store = idb.createObjectStore(storeName, storeSchema.option)
+
+			for indexName, indexSchema of storeSchema.indexes
+				do (store, indexName, indexSchema)->
+					actions.push ->
+						store.createIndex(indexName, indexSchema.key, indexSchema.option)
+
+
+		#seems no any error, perform upgrade now
+		action() for action in actions
+


Syntax errors now work

Syntax errors now work

--- lib/omnisharp-atom/features/syntax-errors.coffee
+++ lib/omnisharp-atom/features/syntax-errors.coffee
@@ -10,50 +10,27 @@
     constructor: (atomSharper) ->
       @atomSharper = atomSharper
       @decorations = {}
-      @editors = []
+

     activate: =>
-
-      @editorSubscription = @atomSharper.onEditor (editor) =>
-        @detectSyntaxErrorsIn editor
-
-      atom.on 'omni-sharp-server:state-change-complete', @codeCheckAllExistingEditors

       @atomSharper.onEditor (editor) =>
         @registerEventHandlerOnEditor editor

-      # todo - remove emit here and subscribe directly from error-pane-view
       @editorDestroyedSubscription = @atomSharper.onEditorDestroyed (filePath) =>
-        editorsCount = @editors.length
-        while editorsCount--
-          if @editors[editorsCount].buffer.file.path == filePath
-            @editors.splice editorsCount, 1
+        #todo: what do we need to do with regards to cleanup? Should we be destroying
+        #all markers?

-        atom.emit 'omnisharp-atom:clear-syntax-errors', filePath

     registerEventHandlerOnEditor: (editor) =>
-      #what's the current editor?
-      console.log "made it"
+
       textBuffer = editor.getBuffer()
       textBuffer.onDidStopChanging =>
-        Omni.codecheck(null, editor).then (data) ->
-          console.log data
-          #@drawDecorations
+        return if OmniSharpServer.vm.isOff

-
-    detectSyntaxErrorsIn: (editor) =>
-      #@decorations[editor.id] = [];
-      #buffer = editor.getBuffer()
-      #buffer.on 'changed', _.debounce(Omni.codecheck, 200)
-      #atom.on "omni:quick-fixes", _.bind(@drawDecorations, this)
-
-      #Omni.codecheck null, editor
-
-      #@editors.push editor
-
-    codeCheckAllExistingEditors: (state) =>
-      if state == 'ready'
-        Omni.codecheck null, editor for editor in @editors
+        #todo: don't fire this if omnisharp isn't loaded.
+        Omni.codecheck(null, editor).then (data) =>
+          @drawDecorations data, editor

     getWordAt: (str, pos) =>
       if str == undefined
@@ -71,24 +48,18 @@
       start: left + 1
       end: left + 1 + right

+
+    destroyDecorationsInEditor: (editor) =>
+      _.each @decorations[editor.id], (decoration) => decoration.getMarker().destroy()
+
+
     drawDecorations: ({QuickFixes}, editor) ->
+
+      #clear all existing decorations for this editor.
+      @destroyDecorationsInEditor editor
       #short out if we have no quickfixes
       if QuickFixes.length is 0
-        #todo: we have no errors, but we don't know what editor we're on
-        #so we have no reference as to what editor to clear
         return
-
-      quickFixPath = _.first(_.pluck(QuickFixes, "FileName"));
-
-      #editor = _.find @editors, (editor) ->
-    #    editor.buffer.file.path == quickFixPath
-
-
-      path = editor?.buffer.file.path
-
-      return if path != quickFixPath
-
-      _.each @decorations[editor.id], (decoration) => decoration.getMarker().destroy()

       ranges = _.map QuickFixes, (error) =>
         line = error.Line - 1


Allow retrieving 20 legislation detail items at a time.


change logging color

change logging color

--- library/membrane/duplex.coffee
+++ library/membrane/duplex.coffee
@@ -364,7 +364,7 @@
         assert sserver = kernel.serverSocket, "no HTTP socket"
         assert ssecure = kernel.secureSocket, "no HTTPS socket"
         assert f = "Disconnecting %s socket handle".toString()
-        l = (socket) -> logger.warn f.magenta, socket.id.bold
+        l = (socket) -> try logger.warn f.blue, socket.id.bold
         p = (c) -> l(c); c.emit "shutdown", -> c.disconnect()
         assert contexts = _.map [sserver, ssecure], resolve
         _.each contexts, (context, vector, addition) =>


prep for disambiguation

prep for disambiguation

--- library/nucleus/compose.coffee
+++ library/nucleus/compose.coffee
@@ -152,11 +152,11 @@
             return null if unique.length is h.length
             assert outstanding = _.difference h, unique
             target = _.head outstanding; h.unshift this
-            assert left = h[_.indexOf(h, target) - 1]
-            assert right = h[_.indexOf(h, target) + 1]
-            shadow = left.watermark or (left is this)
-            assert shadow, "original: #{left.identify()}"
-            left.rebased right; @refactoring trigger
+            assert left = -> h[_.indexOf(h, target) - 1]
+            assert right = -> h[_.indexOf(h, target) + 1]
+            shadow = left().watermark or (left() is this)
+            assert shadow, "original: #{left().identify()}"
+            left().rebased right(); @refactoring trigger

     # Scan the supplied class and return an entire inheritance hierarchy
     # of classes. The hierarchy is represented as an array of prototypes


minor changes in the service

minor changes in the service

--- library/nucleus/service.coffee
+++ library/nucleus/service.coffee
@@ -231,8 +231,8 @@
     # as the response if it is desirable. If not, the request will
     # simply be reject with Node.js/Connect. Beware about `next`!
     rescuing: (error, request, response, next) ->
-        expose = "failures:exposeExceptions"
-        assert plain = @constructor.identify()
+        assert plain = try @constructor.identify()
+        assert expose = "failures:exposeExceptions"
         assert not _.isEmpty method = request.method
         identify = @constructor.identify().underline
         template = "Exception in a #{method} at %s: %s"


Fix minor typo

Fix minor typo

--- library/vending/mirrors.coffee
+++ library/vending/mirrors.coffee
@@ -74,7 +74,7 @@
                 inputs: doc.inputs()
                 method: method

-    # This block describes certain method a abrbitrary service. The
+    # This block describes certain method of abrbitrary service. The
     # exact process of how it is being documented depends on how the
     # documented function is implemented. Please refer to `Document`
     # class and its module implementation for more information on it.


:art: Use config onDidChange instead of observe

--- lib/linter-plus.coffee
+++ lib/linter-plus.coffee
@@ -17,7 +17,7 @@
     @h = H
     @linters = []

-    @subscriptions.add atom.config.observe 'linter-plus.lintOnFly', (value) =>
+    @subscriptions.add atom.config.onDidChange 'linter-plus.lintOnFly', (value) =>
       @_setLintOnFly(value)
     @subscriptions.add atom.workspace.onDidChangeActivePaneItem (editor) =>
       @activeEditor = editor


Use targetPathBase not @target.pathname when combining URLs

--- lib/load-config.coffee
+++ lib/load-config.coffee
@@ -63,15 +63,15 @@
           target: match.target
           rewrite: (reqUrl)->
             targetPathBase = if @target.protocol == 'file:'
-              ''
+              '/'
             else
               @target.pathname
             if @target.nopath
               @target.pathname
             else if @target.nostrippath
-              path.join @target.pathname, reqUrl
+              path.join targetPathBase, reqUrl
             else
-              path.join @target.pathname, reqUrl.substring(@_path.length)
+              path.join targetPathBase, reqUrl.substring(@_path.length)
       else
         match =
           target: host.default
@@ -91,7 +91,8 @@

       match

-  reloadConfig()
+  reloadConfig (err)->
+    throw err if err?

   get: (key)->
     json[key]


fix for bubblemouth border when hover with point over board

Former-commit-id: 2f9a25d84ce7c8ad250d1ee2e0f91a3b97f18f4e [formerly 18514972f4f9c88ff907b054d80af9d8dbe18f15] [formerly 73951c583fa94a459eef3b1008bebae0c4bdac38 [formerly 5c581ad3ae364e226c6e9a1bc9b6e702bd7421e2]] [formerly 857d7684f51f18457276ed3c1297b5d875aae5e8 [formerly 9ca32e5185ff72a01c38918df15dff05b6390b1f] [formerly 1bce5231b1dece86f3e1424acbf6749f5022ea6b [formerly 1bce5231b1dece86f3e1424acbf6749f5022ea6b [formerly d51cccbbe7ed01d2bb8ce959294feab45f1f9925]]]]
Former-commit-id: 7696c095972db4f775a85b0f3b9a697ae4b8f3ab [formerly 2f56d162db3c6eb1a46818f08489c07fd927cbb5] [formerly 5d4e4c028a24416b5276c86e72b1ad674ef13132 [formerly 26d912578f723fb6a4e338df1acb792b4d5460cb]]
Former-commit-id: 66d9b6abeec0cef4042bccf04ac3d25a386d0924 [formerly 5dc58e0b9c271f16634935ebb8d4c2a2ee9cc211]
Former-commit-id: faba95d9ab5259d38cf5eb7756ccfc178c26b40e
Former-commit-id: 07082ff842c0b72ef004cd05e6c4611201234610

--- @client/franklin.coffee
+++ @client/franklin.coffee
@@ -932,7 +932,7 @@
         height: h
         fill: fill
         stroke: if @proposal.has_focus == 'opinion' then focus_blue else '#eee'
-        stroke_width: if dash != "none" then stroke_width else 0
+        stroke_width: if get_proposal_mode() == 'crafting' then stroke_width else 0
         dash_array: dash

 ####


Update node-vk-music-sync lib

Update node-vk-music-sync lib

--- lib/musicParser.coffee
+++ lib/musicParser.coffee
@@ -22,12 +22,12 @@
   downloadTrack = (data, callback) ->
     filename = "#{data.artist} - #{data.title}.mp3"
     console.log "Start download track".grey, filename.magenta
-    try
+
     file = fs.createWriteStream "#{params.dlPath}/#{filename}"
     file.on 'error', (e) ->
       console.log "Error write file '#{filename}'. Aborted.".red.bold

-    req = http.get data.url, (response) ->
+    http.get data.url, (response) ->
       response.pipe file

       response.on 'error', ->
@@ -45,15 +45,27 @@
   return {
     downloadCollection: ->
       getCachedCollection ->
-        throw new Error 'Server return error' if not musicJson
         if musicJson.length isnt 0
           collectionPosition = 0
           loopFn = ->
             track = musicJson[collectionPosition++]
-            track.artist = track.artist.replace '/', ''
-            track.title = track.title.replace '/', ''
-            track.artist = track.artist.replace /\s{2,}/g, ' '
-            track.title = track.title.replace /\s{2,}/g, ' '
+            return if not track
+            # Trim strings for corrective filename
+            try
+              filteredSymbols = [
+                ['/', '']
+                ['[', '']
+                [']', '']
+                [/\s{2,}/g, ' ']
+              ]
+              for symbol in filteredSymbols
+                track.artist = track.artist.replace symbol[0], symbol[1]
+                track.title = track.title.replace symbol[0], symbol[1]
+
+            catch error
+              console.log error.toString().red.bold
+              return
+
             checkOnExists track, (exists) ->
               if exists
                 console.log "#{track.artist} - #{track.title}.mp3" + ' already exists.'.yellow


temporarily disable ttl behaviour

temporarily disable ttl behaviour

allow existing packs without temporary flag to expire

--- app/coffee/PackManager.coffee
+++ app/coffee/PackManager.coffee
@@ -204,7 +204,7 @@
 			return callback(err) if err?
 			if not pack?
 				MongoAWS.unArchivePack project_id, doc_id, pack_id, callback
-			else if pack.expiresAt? and not pack.temporary
+			else if pack.expiresAt? and not pack.temporary and false # TODO: remove false, temporarily disabled
 				# we only need to touch the TTL on the listing of changes in the project
 				# because diffs on individual documents are always done after that
 				PackManager.increaseTTL pack, callback


Fix Italics Button

Fix Italics Button

--- app/controllers/editor.coffee
+++ app/controllers/editor.coffee
@@ -174,15 +174,39 @@
       leftpos = @selrange.getBoundingClientRect().right - (@controls.width() / 2) + 'px'
       @controls.css {top: toppos, left: leftpos}

+  newString: (str) ->
+    @selrange.surroundContents(document.createElement("span"))
+    text = @el.find("span").text()
+    @el.find("span").text(str).contents().unwrap()
+
   formatBold: (e) =>
     @selrange.surroundContents(document.createElement("span"))
     text = @el.find("span").text()
     @el.find("span").text("**"+text+"**").contents().unwrap()

   formatItalics: (e) =>
-    @selrange.surroundContents(document.createElement("span"))
-    text = @el.find("span").text()
-    @el.find("span").text("*"+text+"*").contents().unwrap()
+
+    str = @selrange.toString()
+    if str.substring(0,1) is "*" and str.substring(str.length-1,str.length) is "*"
+      @newString(str.substring(1,str.length-1))
+    else
+      @sel.collapseToStart()
+      @sel.modify("move", "backward", "character")
+      @sel.modify("extend", "forward", "word")
+      @sel.modify("extend", "forward", "character")
+      @selrange = @sel.getRangeAt(0)
+      str = @selrange.toString()
+      console.log(str)
+
+      if str.substring(0,1) is "*" and str.substring(str.length-1,str.length) is "*"
+        @newString(str.substring(1,str.length-1))
+      else
+        @sel.collapseToStart()
+        @sel.modify("extend", "forward", "word")
+        @selrange = @sel.getRangeAt(0)
+        str = @selrange.toString()
+        @newString(" *" + str.substring(1,str.length) + "*")
+        console.log("creating astericks")

   formatHead: (e) =>
     @sel.modify("extend", "backward", "paragraphboundary")


fixed null ptr

fixed null ptr

--- app/js/services/wallet.coffee
+++ app/js/services/wallet.coffee
@@ -54,7 +54,7 @@
                         @get_setting('autocomplete').then (result) =>
                             @autocomplete = result.value if result
                         @get_setting('interface_locale').then (result) =>
-                            if result.value
+                            if result and result.value
                                 @interface_locale = result.value
                                 @translate.use(result.value)


Remove miniterm.cjsx

Remove miniterm.cjsx

--- src/smc-webapp/project_miniterm.cjsx
+++ src/smc-webapp/project_miniterm.cjsx
@@ -1,191 +0,0 @@
-###############################################################################
-#
-#    CoCalc: Collaborative Calculation in the Cloud
-#
-#    Copyright (C) 2016, Sagemath Inc.
-#
-#    This program is free software: you can redistribute it and/or modify
-#    it under the terms of the GNU General Public License as published by
-#    the Free Software Foundation, either version 3 of the License, or
-#    (at your option) any later version.
-#
-#    This program is distributed in the hope that it will be useful,
-#    but WITHOUT ANY WARRANTY; without even the implied warranty of
-#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#    GNU General Public License for more details.
-#
-#    You should have received a copy of the GNU General Public License
-#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-###############################################################################
-
-###
-miniterm.cjsx -- a small terminal that lets you enter a single bash command.
-
-IDEAS FOR LATER:
-
- - [ ] persistent history (in database/project store) -- this is in the log
- - [ ] tab completion
- - [ ] mode to evaluate in another program, e.g., %gp <...>
- - [ ] help
-
-###
-
-{rclass, React, rtypes, ReactDOM}  = require('./app-framework')
-{Button, FormControl, InputGroup, FormGroup, Row, Col} = require('react-bootstrap')
-{ErrorDisplay, Icon} = require('./r_misc')
-
-{webapp_client} = require('./webapp_client')  # used to run the command -- could change to use an action and the store.
-
-output_style =
-    position  : 'absolute'
-    zIndex    : 2
-    width     : '93%'
-    boxShadow : '0px 0px 7px #aaa'
-    maxHeight : '450px'
-    overflow  : 'auto'
-
-BAD_COMMANDS =
-    sage    : "Create a Sage worksheet instead,\nor type 'sage' in a full terminal."
-    ipython : "Create a Jupyter notebook instead,\nor type 'ipython' in a full terminal."
-    gp      : "Create a Sage worksheet in GP mode\nor type 'gp' in a full terminal."
-    vi      : "Type vi in a full terminal instead,\nor just click on the file in the listing."
-    vim     : "Type vim in a full terminal instead,\nor just click on the file in the listing."
-    emacs   : "Type emacs in a full terminal instead,\nor just click on the file in the listing."
-    open    : "The open command is not yet supported\nin the miniterminal.  See\nhttps://github.com/sagemathinc/cocalc/issues/230"
-
-EXEC_TIMEOUT = 10 # in seconds
-
-exports.MiniTerminal = MiniTerminal = rclass
-    displayName : 'MiniTerminal'
-
-    propTypes :
-        project_id   : rtypes.string.isRequired
-        current_path : rtypes.string  # provided by the project store; undefined = HOME
-        actions      : rtypes.object.isRequired
-
-    getInitialState: ->
-        input  : ''
-        stdout : undefined
-        state  : 'edit'   # 'edit' --> 'run' --> 'edit'
-        error  : undefined
-
-    execute_command: ->
-        @setState(stdout:'', error:'')
-        input = @state.input.trim()
-        if not input
-            return
-        error = BAD_COMMANDS[input.split(' ')[0]]
-        if error
-            @setState
-                state : 'edit'
-                error : error
-            return
-
-        input0 = input + '\necho $HOME "`pwd`"'
-        @setState(state:'run')
-
-        @_id = (@_id ? 0) + 1
-        id = @_id
-        start_time = new Date()
-        webapp_client.exec
-            project_id : @props.project_id
-            command    : input0
-            timeout    : EXEC_TIMEOUT
-            max_output : 100000
-            bash       : true
-            path       : @props.current_path
-            err_on_exit: false
-            cb         : (err, output) =>
-                if @_id != id
-                    # computation was cancelled -- ignore result.
-                    return
-                if err
-                    @setState(error:JSON.stringify(err), state:'edit')
-                else if output.exit_code != 0 and new Date() - start_time >= .98*EXEC_TIMEOUT
-                    # we get no other error except it takes a long time and the exit_code isn't 0.
-                    @setState
-                        state : 'edit'
-                        error : "Miniterminal commands are limited to #{EXEC_TIMEOUT} seconds.\nFor longer or interactive commands,\nuse a full terminal."
-                else
-                    if output.stdout
-                        # Find the current path
-                        # after the command is executed, and strip
-                        # the output of "pwd" from the output:
-                        s = output.stdout.trim()
-                        i = s.lastIndexOf('\n')
-                        if i == -1
-                            output.stdout = ''
-                        else
-                            s = s.slice(i+1)
-                            output.stdout = output.stdout.slice(0,i)
-                        i = s.indexOf(' ')
-                        full_path = s.slice(i+1)
-                        if full_path.slice(0,i) == s.slice(0,i)
-                            # only change if in project
-                            path = s.slice(2*i+2)
-                            @props.actions.open_directory(path)
-                    if not output.stderr
-                        # only log commands that worked...
-                        @props.actions.log({event:'miniterm', input:input})
-                    @props.actions.fetch_directory_listing()  # update directory listing (command may change files)
-                    @setState(state:'edit', error:output.stderr, stdout:output.stdout)
-                    if not output.stderr
-                        @setState(input:'')
-
-    render_button: ->
-        switch @state.state
-            when 'edit'
-                <Button onClick={@execute_command}>
-                    <Icon name='play' />
-                </Button>
-            when 'run'
-                <Button onClick={@execute_command}>
-                    <Icon name='cc-icon-cocalc-ring' spin  />
-                </Button>
-
-    render_output: (x, style) ->
-        if x
-

-                e.preventDefault(); @setState(stdout:'', error:'')}
-                   href=''
-                   style={right:'10px', top:'0px', color:'#666', fontSize:'14pt', position:'absolute'}>
-                       <Icon name='times' />
-
-                {x}
-

-
-    keydown: (e) ->
-        # IMPORTANT: if you do window.e and look at e, it's all null!! But it is NOT
-        # all null right now -- see
-        #     http://stackoverflow.com/questions/22123055/react-keyboard-event-handlers-all-null
-        ## e.persist(); window.e = e  # for debugging
-        if e.keyCode == 27
-            @setState(input: '', stdout:'', error:'')
-
-    render: ->
-        # NOTE: The style in form below offsets Bootstrap's form margin-bottom of +15 to look good.
-        # We don't use inline, since we still want the full horizontal width.
-

-            <form onSubmit={(e) => e.preventDefault(); @execute_command()} style={marginBottom: '-10px'}>
-                <FormGroup>
-                    <InputGroup>
-                        <FormControl
-                            type        = 'text'
-                            value       = {@state.input}
-                            ref         = 'input'
-                            placeholder = 'Terminal command...'
-                            onChange    = {(e) => e.preventDefault(); @setState(input:ReactDOM.findDOMNode(@refs.input).value)}
-                            onKeyDown   = {@keydown}
-                            />
-                        <InputGroup.Button>
-                            {@render_button()}
-                        </InputGroup.Button>
-                    </InputGroup>
-                </FormGroup>
-            </form>
-

-                {@render_output(@state.error, {color:'darkred', margin:0})}
-                {@render_output(@state.stdout, {margin:0})}
-

-


domainify: don't reinvent the wheel (i.e. domain.bind)

--- lib/understream.coffee
+++ lib/understream.coffee
@@ -11,15 +11,11 @@
 # and re-emitting them from the stream.
 domainify = (stream) ->
   if stream instanceof Transform
-    orig_transform = stream._transform.bind stream
-    stream._transform = (chunk, enc, cb) ->
-      dmn = domain.create()
-      dmn.on 'error', (err) ->
-        dmn.dispose()
-        stream.emit 'error', err
-      dmn.run -> orig_transform chunk, enc, (args...) ->
-        dmn.dispose()
-        cb args...
+    dmn = domain.create()
+    stream._transform = dmn.bind stream._transform.bind stream
+    dmn.on 'error', (err) ->
+      dmn.dispose()
+      stream.emit 'error', err

 is_readable = (instance) ->
   instance? and


fixes on Wave 7 german translation

fixes on Wave 7 german translation

--- coffeescripts/cards-de.coffee
+++ coffeescripts/cards-de.coffee
@@ -478,15 +478,19 @@
         "Commander Alozen":
             text: """Zu Beginn der Kampfphase darfst du ein feindliches Schiff in Reichweite 1 in die Zielerfassung nehmen."""
         "Raider-class Corvette (Fore)":
+            ship: "Korv. der Sturm-Klasse (Bug)"
             name: "Korv. der Sturm-Klasse (Bug)"
             text: """Ein Mal pro Runde darfst du, nachdem du einen Primärwaffen-Angriff durchgeführt hast, 2 Energie ausgeben, um einen weiteren Primärwaffen-Angriff durchzuführen."""
         "Raider-class Corvette (Aft)":
+            ship: "Korv. der Sturm-Klasse (Heck)"
             name: "Korv. der Sturm-Klasse (Heck)"
+        "Bossk":
+            text: """Sobald du einen Angriff durchführst und triffst, kannst du , bevor du Schaden verursachst, 1 deiner %CRIT% negieren, um 2 %HIT% hinzuzufügen."""
         "Talonbane Cobra":
             ship: "Kihraxz-Jäger"
             text: """Sobald du angreifst oder verteidigst, wird der Effekt deiner Kampfvorteile durch Reichweite verdoppelt."""
         "Miranda Doni":
-            text: """Ein Mal pro Runde darfst du, sobald du angreifst, entweder 1 Schild ausgeben, um 1 zusätzlichen Angriffswürfel zu werfen, oder 1 Angriffswürfel weniger werfen, um 1 Schild wiederaufzuladen."""
+            text: """Ein Mal pro Runde darfst du, sobald du angreifst, entweder 1 Schild ausgeben, um 1 zusätzlichen Angriffswürfel zu werfen, oder 1 Angriffswürfel weniger werfen, um 1 Schild wiederaufzuladen."""
         '"Redline"':
             name: '"Rote Linie"'
             ship: "TIE-Vergelter"


Update german for WAVE 10

Update german for WAVE 10

--- coffeescripts/cards-de.coffee
+++ coffeescripts/cards-de.coffee
@@ -83,6 +83,9 @@
         "Heroes of the Resistance Expansion Pack": "Helden des Widerstands Erweiterung"
         "U-Wing Expansion Pack": "U-Flügler Erweiterung"
         "TIE Striker Expansion Pack": "TIE-Stürmer Erweiterung"
+        "Quadjumper Expansion Pack": "Quadjumper Erweiterung"
+        "Sabine's TIE Fighter Expansion Pack": "Sabines TIE-Jäger Erweiterung"
+        "Upsilon-class Shuttle Expansion Pack": "Shuttle der Ypsilon-Klasse Erweiterung"
     ui:
         shipSelectorPlaceholder: "Wähle ein Schiff"
         pilotSelectorPlaceholder: "Wähle einen Piloten"
@@ -217,6 +220,9 @@
     exportObj.renameShip 'U-Wing', 'U-Flügler'
     # Move TIE Striker to TIE-Stürmer
     exportObj.renameShip 'TIE Striker', 'TIE-Stürmer'
+    # Move Upsilon-class Shuttle to Shuttle der Ypsilon-Klasse
+    exportObj.renameShip 'Upsilon-class Shuttle', 'Shuttle der Ypsilon-Klasse'
+
     pilot_translations =
         "Wedge Antilles":
             text: """Wenn du angreifst, sinkt der Wendigkeitswert des Verteidigers um 1 (Minimum 0)."""
@@ -838,14 +844,33 @@
             name: "Sabine Wren (Abschaum)"
             text: '''Sobald du dich gegen ein feindliches Schiff innerhalb deines mobilen Feuerwinkels und Reichweite 1-2 verteidigst, darfst du 1 %FOCUS% zu deinem Wurf hinzufügen.'''
         # Wave X
+        'Ahsoka Tano':
+            ship: "TIE-Jäger"
+            text: '''Zu Beginn der Kampfphase darfst du 1 Fokusmarker ausgeben, um ein freundliches Schiff in Reichweite 1 zu wählen. Es darf 1 freie Aktion durchführen.'''
+        'Captain Rex':
+            ship: "TIE-Jäger"
+            text: '''Nachdem du einen Angriff durchgeführt hast, ordne dem Verteidiger die Zustandskarte "Sperrfeuer" zu.'''
         'Sabine Wren (TIE Fighter)':
-            text: '''Immediately before you reveal your maneuver, you may perform a free boost or barrel roll action.'''
+            ship: "TIE-Jäger"
+            text: '''Unmittelbar bevor du dein Manöver aufdeckst, darfst du als freie Aktion einen Schub oder eine Fassrolle durchführen.'''
         '"Zeb" Orrelios (TIE Fighter)':
-            text: '''When defending, you may cancel %CRIT% results before %HIT% results.'''
+            ship: "TIE-Jäger"
+            text: '''Sobald du verteidigst, darfst du %CRIT% vor %HIT% negieren.'''
+        'Major Stridan':
+            ship: "Shuttle der Ypsilon-Klasse"
+            text: '''Was deine Aktionen und Aufwertungskarten betrifft, darfst du freundliche Schiffe in Reichweite 2-3 behandeln, als wären sie in Reichweite 1.'''
+        'Lieutenant Dormitz':
+            ship: "Shuttle der Ypsilon-Klasse"
+            text: '''Während des Spielaufbaus dürfen freundliche Schiffe überall auf der Spielfläche in Reichweite 1-2 zu dir platziert werden.'''
+        'Constable Zuvio':
+            text: '''Sobald du ein Rückwärtsmanöver aufdeckst, darfst du über die Stopper an deinem Bug eine Bombe legen (auch eine Bombe mit dem Stichwort "Aktion:").'''
+        'Sarco Plank':
+            text: '''Sobald du dich verteidigst, darfst du, anstatt deinen Wendigkeitswert zu verwenden, Würfel in Höhe der Geschwindigkeit des Manövers, das du in dieser Runde ausgeführt hast, verwenden.'''
         'Kylo Ren':
-            text: '''The first time you are hit by an attack each round, deal the "I'll Show You the Dark Side" Condition card to the attacker.'''
+            ship: "Shuttle der Ypsilon-Klasse"
+            text: '''Das erste Mal wenn du in jeder Runde von einem Angriff getroffen wirst, ordnest du dem Angreifer die Zustandskarte "Ich zeige dir die Dunkle Seite" zu.'''
         'Unkar Plutt':
-            text: '''At the end of the Activation phase, you must assign a tractor beam token to each ship you are touching.'''
+            text: '''Am Ende der Aktivierungsphase musst du jedem Schiff, das du berührst, einen Traktorstrahlmarker zuordnen.'''
         'Blue Squadron Pathfinder':
             ship: "U-Flügler"
             name: "Aufklärer der Blauen Staffel"
@@ -878,26 +903,6 @@
         '''Black Squadron Scout''':
             ship: "TIE-Stürmer"
             name: "Scout der Schwarzen Staffel"
-        'Ahsoka Tano':
-            ship: "TIE-Jäger"
-            text: '''At the start of the Combat phase, you may spend 1 focus token to choose a friendly ship at Range 1.  It may perform 1 free action.'''
-        'Captain Rex':
-            ship: "TIE-Jäger"
-            text: '''After you perform an attack, assign the "Suppressive Fire" Condition card to the defender.'''
-        'Sabine Wren (TIE Fighter)':
-            ship: "TIE-Jäger"
-            text: '''Immediately before you reveal your maneuver, you may perform a free boost or barrel roll action.'''
-        '"Zeb" Orrelios (TIE Fighter)':
-            ship: "TIE-Jäger"
-            text: '''When defending, you may cancel %CRIT% results before %HIT% results.'''
-        'Major Stridan':
-            text: '''For the purpose of your actions and Upgrade cards, you may treat friendly ships at Range 2-3 as being at Range 1.'''
-        'Lieutenant Dormitz':
-            text: '''During setup, friendly ships may placed anywhere in the play area at Range 1-2 of you.'''
-        'Constable Zuvio':
-            text: '''When you reveal a reverse maneuver, you may drop a bomb using your front guides (including a bomb with the "Action:" header).'''
-        'Sarco Plank':
-            text: '''When defending, instead of using your agility value, you may roll a number of defense dice equal to the speed of the maneuver you executed this round.'''

     upgrade_translations =
         "Ion Cannon Turret":
@@ -1423,7 +1428,7 @@
             text: """Ein Mal pro Runde darfst du nach dem Verteidigen 1 Angriffswürfel werfen. Bei %HIT% nimmt der Angreifer 1 Schaden. Bei %CRIT% nehmen du und der Angreifer je 1 Schaden."""
         'Thermal Detonators':
             name: "Thermaldetonatoren"
-            text: """Sobald du dein Manöverrad aufdeckst, darfst du diese Karte ablegen, um 1 Thermaldetonatormarker zu legen.%LINEBREAK%Der Marker detoniert am ende der Aktivierungsphase."""
+            text: """Sobald du dein Manöverrad aufdeckst, darfst du diese Karte ablegen, um 1 Thermaldetonatormarker zu legen.%LINEBREAK%Der Marker detoniert am Ende der Aktivierungsphase."""
         "Overclocked R4":
             name: "Übertakteter R4"
             text: """Sobald du in der Kampfphase einen Fokusmarker ausgibst, darfst du 1 Stressmarker erhalten, um deinem Schiff 1 Fokusmarker zuzuordnen."""
@@ -1464,11 +1469,12 @@
             text: '''Aktion: Wähle ein gestresstes Schiff in Reichweite 1-2 und wirf 1 Angriffswürfel. Bei %HIT% oder %CRIT% entferne 1 Stressmarker und teile ihm 1 verdeckte Schadenskarte zu.'''
         # Wave X
         'Kylo Ren':
-            text: '''%DE_IMPERIALONLY%%LINEBREAK%Action: Assign the "I'll Show You the Dark Side" Condition card to an enemy ship at Range 1-3.'''
+            text: '''%DE_IMPERIALONLY%%LINEBREAK%Aktion: Ordne einem feindlichen Schiff in Reichweite 1-3 die Zustandskarte "Ich zeige dir die Dunkle Seite" zu.'''
         'Unkar Plutt':
-            text: '''%DE_SCUMONLY%%LINEBREAK%After executing a maneuver that causes you to overlap an enemy ship, you may suffer 1 damage to perform 1 free action.'''
+            text: '''%DE_SCUMONLY%%LINEBREAK%Nachdem du ein Manöver ausgeführt hast, das zur Überschneidung mit einem freundlichen Schiff führt, darfst du 1 Schaden nehmen, um 1 freie Aktion durchzuführen.'''
         'A Score to Settle':
-            text: '''During setup, before the "Place Forces" step, choose 1 enemy ship and deal the "A Debt to Pay" Condition card to it.%LINEBREAK%Wehn attacking a ship that has the "A Debt to Pay" Condition card, you may change 1 %FOCUS% result to a %CRIT% result.'''
+            name: "Eine offene Rechnung"
+            text: '''Wähle während des Spielaufbaus, vor dem Schritt "Streitkräfte platzieren", 1 feindliches Schiff und ordne ihm die Zustandskarte "Unbezahlte Schulden" zu.%LINEBREAK%Sobald du ein Schiff mit der Zustandskarte "Unbezahlte Schulden" angreifst, darfst du 1 %FOCUS% in ein %CRIT% ändern.'''
         'Jyn Erso':
             text: '''%DE_REBELONLY%%LINEBREAK%Aktion: Wähle 1 freundliches Schiff in Reichweite 1-2. Weise jenem Schiff 1 Fokusmarker für jedes feindliches Schiff in deinem Feuerwinkel in Reichweite 1-3 zu. Auf diese Weise darfst du nicht mehr als 3 Marker zuweisen.'''
         'Cassian Andor':
@@ -1492,17 +1498,21 @@
         'M9-G8':
             text: '''%DE_REBELONLY%%LINEBREAK%Sobald ein Schiff angreift, das du in der Zielerfassung hast, darfst du 1 Angriffswürfel wählen. Der Angreifer muss den gewählten Würfel neu würfeln.%LINEBREAK%Du kannst andere freundliche Schiffe in die Zielerfassung nehmen.'''
         'EMP Device':
-            text: '''During the Combat phase, instead of performing any attacks, you may discard this card to assign 2 ion tokens to each ship at Range 1.'''
+            name: "EMP Emitter"
+            text: '''Während der Kampfphase darfst du, statt Angriffe durchzuführen, diese Karte ablegen, um jedes Schiff, das in Reichweite 1 ist, 2 Ionenmarker zuzuordnen.'''
         'Captain Rex':
-            text: '''%REBELONLY%%LINEBREAK%After you perform an attack that does not hit, you may assign 1 focus token to your ship.'''
+            text: '''%REBELONLY%%LINEBREAK%Nachdem du einen Angriff durchgeführt hast, der nicht getroffen hat, darfst du deinem Schiff 1 Fokusmarker zuordnen.'''
         'General Hux':
-            text: '''%IMPERIALONLY%%LINEBREAK%Action: Choose up to 3 friendly ships at Range 1-2.  Assign 1 focus token to each and assign the "Fanatical Devotion" Condition card to 1 of them.  Then receive 1 stress token.'''
+            text: '''%IMPERIALONLY%%LINEBREAK%Aktion: Wähle bis zu 3 freundliche Schiffe in Reichweite 1-2. Ordne jedem dieser Schiffe 1 Fokusmarker zu und ordne einem von ihnen die Zustandskarte "Fanatische Ergebenheit" zu. Dann erhältst du 1 Stressmarker.'''
         'Operations Specialist':
-            text: '''%LIMITED%%LINEBREAK%After a friendly ship at Range 1-2 performs an attack that does not hit, you may assign 1 focus token to a friendly ship at Range 1-3 of the attacker.'''
+            name: "Einsatzspezialist"
+            text: '''%LIMITED%%LINEBREAK%Nachdem ein freundliches Schiff in Reichweite 1-2 einen Angriff durchgeführt hat, der nicht trifft, darfst du einem freundlichen Schiff in Reichweite 1-3 des Angreifers 1 Fokusmarker zuordnen.'''
         'Targeting Synchronizer':
-            text: '''When a friendly ship at Range 1-2 is attacking a ship you have locked, the friendly ship treats the "Attack (target lock): header as "Attack:."  If a game effect instructs you to spend a target lock, it may spend your target lock instead.'''
+            name: "Zielsynchronisiersystem"
+            text: '''Sobald ein freundliches Schiff in Reichweite 1-2 ein Schiff angreift, das du in der Zielerfassung hast, behandelt das freundliche Schiff das Stichwort "Angriff (Zielerfassung):" wie "Angriff:". Weist ein Spieleffekt jenes Schiff an, einen Zielerfassungsmarker auszugeben, darf es stattdessen deine Zielerfassungsmarker ausgeben.'''
         'Hyperwave Comm Scanner':
-            text: '''At the start of the "Place Forces" step, you may choose to treat your pilot skill value as "0," "6," or "12" until the end of the step.%LINEBREAK%During setup, after another friendly ship is placed at Range 1-2, you may assign 1 focus or evade token to it.'''
+            name: "Hyperwellen-Kommunikations-Scanner"
+            text: '''Zu Beginn des Schritts "Streitkräfte platzieren" darfst du wählen, deinen Pilotenwert bis zum Ende des Schritts wie 0, 6 oder 12 zu behandeln.%LINEBREAK% Während des Spielaufbaus, nachdem ein anderes freundliches Schiff in Reichweite 1-2 zu dir platziert worden ist, darfst du ihm 1 Fokus- oder Ausweichmarker zuordnen.'''
         'Trick Shot':
             name: "Trickschuss"
             text: '''Sobald du angreifst, darfst du 1 zusätzlichen Angriffswürfel werfen, falls die Schussbahn versperrt ist.'''
@@ -1510,7 +1520,8 @@
             name: "Kopilotenass"
             text: '''Sobald du mit einer Primärwaffe angreifst, muss der Verteidiger 1 Fokusmarker ausgeben, falls möglich.%LINEBREAK%Sobald du verteidigst, muss der Angreifer 1 Fokusmarker ausgeben, falls möglich.'''
         '''Scavenger Crane''':
-            text: '''After a ship at Range 1-2 is destroyed, you may choose a discarded %TORPEDO%, %MISSILE%, %BOMB%, %CANNON%, %TURRET%, or Modification Upgrade card that was equipped to your ship and flip it faceup.  Then roll 1 attack die.  On a blank result, discard Scavenger Crane.'''
+            name: "Schrottsammler-Kran"
+            text: '''Nachdem ein SChiff in Reichweite 1-2 zerstört worden ist, darfst du eine abgelegte %TORPEDO%, %MISSILE%, %BOMB%, %CANNON%, %TURRET%-, oder Modifikations-Aufwertungskarte, die dein Schiff ausgerüstet hatte, wählen und aufdecken. DAnn wirfst du 1 Angriffswürfel. Bei einer Leerseite wird Schrottsammler-Kran abgelegt.'''
         'Bodhi Rook':
             text: '''%REBELONLY%%LINEBREAK%Sobald du eine Zielerfassung durchführst, kannst du ein feindliches Schiff in Reichweite 1-3 zu einem freundlichen Schiff in die Zielerfassung nehmen.'''
         'Baze Malbus':
@@ -1527,7 +1538,7 @@
             name: "Expertenwissen"
             text: '''Sobald du angreifst und falls du nicht gestresst bist, darfst du alle deine %FOCUS% in %HIT% ändern.'''
         'BoShek':
-            text: '''When a ship you are touching activates, you may look at its chosen maneuver.  If you do, its owner must rotate the dial to an adjacent maneuver.  The ship can reveal and execute that maneuver even while stressed.'''
+            text: '''Sobald ein Schiff, das du berührst, aktiviert wird, darfst du dir sein gewähltes Manöver ansehen. Falls du das tust, muss sein Besitzer das Rad auf ein nebenstehendes Manöver drehen. Das Schiff kann dieses Manöver aufdecken und ausführen, selbst wenn es gestresst ist.'''
         # C-ROC
         'Heavy Laser Turret':
             text: '''C-ROC Cruiser only.%LINEBREAK%Attack (energy): Spend 2 energy from this card to perform this attack against 1 ship (even a ship outside of your firing arc).'''
@@ -1624,10 +1635,13 @@
         'Smuggling Compartment':
             name: "Schmuggelverschläge"
             text: '''Nur für YT-1300 und YT-2400.%LINEBREAK%Füge deiner Aufwertungsleiste ein %ILLICIT% hinzu.%LINEBREAK%Du darfst 1 zusätzliche Modifikation-Aufwertung mit Kosten von 3 oder weniger ausrüsten.'''
+        # Wave X
         'Captured TIE':
-            text: '''TIE Fighter only.%REBELONLY%%LINEBREAK%Enemy ships with a pilot skill value lower than yours cannot declare you as the target of an attack.  After you perform an attack or when you are the only remaining friendly ship, discard this card.'''
+            name: "Gekaperter TIE"
+            text: '''Nur für TIE-Jäger.%REBELONLY%%LINEBREAK%Feindliche Schiffe mit einem Pilotenwert, der niedriger ist als deiner, können dich nicht zum Ziel eines Angriffs deklarieren. Nachdem du einen Angriff durchgeführt hast oder sobald du das einzige freundliche Schiff im Spiel bist, lege diese Karte ab.'''
         'Spacetug Tractor Array':
-            text: '''Quadjumper only.%LINEBREAK%Action: Choose a ship inside your firing arc at Range 1 and assign a tractor beam token to it.  If it is a friendly ship, resolve the effect of the tractor beam token as though it were an enemy ship.'''
+            name: "Schleppertraktorstrahl"
+            text: '''Nur für Quadjumper.%LINEBREAK%Aktion: Wähle ein SChiff innerhalb deines Feuerwinkels und in Reichweite 1 und ordne ihm einen Traktorstrahlmarker zu. Falls es ein freundliches Schiff ist, handle den Effekt des Traktorstrahlmarkers ab, als wäre es ein feindliches Schiff.'''
         'Lightweight Frame':
             name: "Leichtgewichtrahmen"
             text: '''Nur für TIE.%LINEBREAK%Sobald du verteidigst, falls es nach dem Würfeln der Verteidigungswürfel, mehr Angriffswürfel als Verteidigungswürfel gibt, wirf 1 zusätzlichen Verteidigungswürfel.%LINEBREAK%Du kannst diese Karte nicht ausrüsten, falls dein Wendigkeitswert 3 oder höher ist.'''
@@ -1775,14 +1789,16 @@
             text: '''Nur für Jagdschiffe der Lanzen-Klasse.%LINEBREAK%Nachdem du einen Angriff durchgeführt hast, der getroffen hat, darfst du dem Verteidiger 1 Traktorstrahlmarker zuordnen, falls er innerhalb deines mobilen Feuerwinkels in Reichweite 1-2 ist.'''
         # Wave X
         '''Sabine's Masterpiece''':
+            name: "Sabines Meisterwerk"
             ship: "TIE-Jäger"
-            text: '''TIE Fighter only.%REBELONLY%%LINEBREAK%Your upgrade bar gains the %CREW% and %ILLICIT% upgrade icons.'''
+            text: '''Nur für TIE-Jäger.%REBELONLY%%LINEBREAK%Füge deiner Aufwertungsleiste die Symbole %CREW% und %ILLICIT% hinzu.'''
         '''Kylo Ren's Shuttle''':
-            text: '''Upsilon-class Shuttle only.%LINEBREAK%At the end of the Combat phase, choose an unstressed enemy ship at Range 1-2.  Its owner must assign a stress token to it or assign a stress token to another ship at Range 1-2 of you that that player controls.'''
+            name: "Kylo Rens Shuttle"
+            text: '''Nur für Shuttle der Ypsilon-Klasse.%LINEBREAK%Wähle am Ende der Kampfphase ein nicht-gestresstes feindliches Schiff in Reichweite 1-2. Sein Besitzer muss ihm einen Stressmarker zuordnen oder er muss einem anderen Schiff in Reichweite 1-2 zu dir, das er kontrolliert, einen Stressmarker zuordnen.'''
         '''Pivot Wing''':
             name: "Schwenkflügel"
             ship: 'U-Flügler'
-            text: '''Nur U-Flügler. %DUALCARD%%LINEBREAK%Angriffsposition: Erhöhe deinen Wendigkeitswert um 1.%LINEBREAK%Nachdem du ein Manöver ausgeführt hast, darfst du diese Karte umdrehen.%LINEBREAK%Landeposition: Sobald du ein (0 %STOP%)-Manöver aufdeckst, darfst du dein Schiff um 180° drehen.%LINEBREAK%Nachdem du ein Manöver ausgeführt hast, darfst du diese Karte umdrehen.'''
+            text: '''Nur für U-Flügler. %DUALCARD%%LINEBREAK%Angriffsposition: Erhöhe deinen Wendigkeitswert um 1.%LINEBREAK%Nachdem du ein Manöver ausgeführt hast, darfst du diese Karte umdrehen.%LINEBREAK%Landeposition: Sobald du ein (0 %STOP%)-Manöver aufdeckst, darfst du dein Schiff um 180° drehen.%LINEBREAK%Nachdem du ein Manöver ausgeführt hast, darfst du diese Karte umdrehen.'''
         '''Adaptive Ailerons''':
             name: "Adaptive Querruder"
             ship: "TIE-Stürmer"


corrected German name of the Imperial Aces Expansion Pack

--- coffeescripts/cards-de.coffee
+++ coffeescripts/cards-de.coffee
@@ -44,7 +44,7 @@
         "TIE Advanced Expansion Pack": "TIE-Advanced Erweiterung"
         "Lambda-Class Shuttle Expansion Pack": "Lambda Class Shuttle Erweiterung"
         "Slave I Expansion Pack": "Slave I Erweiterung"
-        "Imperial Aces Expansion Pack": "Imperial Aces Erweiterung"
+        "Imperial Aces Expansion Pack": "Fliegerasse des Imperiums Erweiterung"
         "Rebel Transport Expansion Pack": "Rebel Transport Expansion Pack"
         "Z-95 Headhunter Expansion Pack": "Z-95 Headhunter Expansion Pack"
         "TIE Defender Expansion Pack": "TIE Defender Expansion Pack"


wave 6 stuff aded

wave 6 stuff aded

I added several pilots and updates from wave 6. scum.

--- coffeescripts/cards-es.coffee
+++ coffeescripts/cards-es.coffee
@@ -437,6 +437,40 @@
             text: """Si no tienes escudos y al menos una carta de daño asignada a tu nave, incrementa tu valor de agilidad en 1."""
         "Captain Oicunn":
             text: """Después de ejecutar un maniobra, cada nave enemiga que estés tocando sufre 1 daño."""
+        "Prince Xizor":
+            text: """Cuando defiendas, una unidad aliada a alcance 1 puede sufrir 1 de los resultados de %HIT% o %CRIT% no cancelado."""
+        "Serissu":
+            text: """Cuando otra nave aliada a alcance 1 defienda, podrá volver a tirar 1 dado de defensa."""
+        "Laetin A'shera":
+            text: """Esta carta todavía no ha sido revelada."""
+        "IG-88A":
+            text: """Esta carta todavía no ha sido revelada."""
+        "IG-88B":
+            text: """Esta carta todavía no ha sido revelada."""
+        "IG-88C":
+            text: """Esta carta todavía no ha sido revelada."""
+        "IG-88D":
+            text: """Puedes realizar una maniobra (%SLOOPLEFT% 3) o (%SLOOPRIGHT% 3) utilizando la plantilla (%TURNLEFT% 3) o (%TURNRIGHT% 3) segun corresponda."""
+        "Boba Fett (Scum)":
+            text: """Cuando ataques o defiendas, puedes volver a tirar 1 de tus dados por cada nave a distancia 1."""
+        "Kath Scarlet (Scum)":
+            text: """Esta carta todavía no ha sido revelada."""
+        "Unspoiled PS6 Scum Firespray Pilot":
+            text: """Esta carta todavía no ha sido revelada."""
+        "Unspoiled PS7 Scum Y-Wing Pilot":
+            text: """Esta carta todavía no ha sido revelada."""
+        "Unspoiled PS5 Scum Y-Wing Pilot":
+            text: """Esta carta todavía no ha sido revelada."""
+        "Unspoiled PS7 Scum HWK Pilot":
+            text: """Esta carta todavía no ha sido revelada."""
+        "Unspoiled PS5 Scum HWK Pilot":
+            text: """Esta carta todavía no ha sido revelada."""
+        "Unspoiled PS3 Scum HWK Pilot":
+            text: """Esta carta todavía no ha sido revelada."""
+        "N'Dru Suhlak":
+            text: """Cuando ataques, si no hay ninguna nave aliada a distancia 1-2, tira un dado de ataque adicional."""
+        "Unspoiled PS5 Scum Z-95 Pilot":
+            text: """Esta carta todavía no ha sido revelada."""

     upgrade_translations =
         "Ion Cannon Turret":
@@ -650,7 +684,7 @@
         "Chardaan Refit":
             name: "Reajuste Chardaan"
             ship: "Ala-A"
-            text: """text: Solo Ala-A.

This card has a negative squad point cost. Esta carta tiene un valor negativo en puntos de escuadrón."""
+            text: """text: """Solo Ala-A.

This card has a negative squad point cost."""Esta carta tiene un valor negativo en puntos de escuadrón."""
         "Proton Rockets":
             name: "Cohetes de Protones"
             text: """Ataque (Concentración): Descarta esta carta para realizar este ataque.

Puedes tirar dados de ataque adicionales igual a tu valor de agilidad, hasta un máximo de 3 dados de ataque adicionales."""
@@ -739,7 +773,35 @@
         "Ion Torpedoes":
             name: "Torpedos de Iones"
             text: """Ataque (Blanco Fijado): Retira el blanco fijado y descarta esta carta para realizar este ataque.

Si el ataque impacta, el defensor y cada nave a alcance 1 de él recibe 1 marcador de iones."""
-
+        "Bodyguard":
+            text: """%SCUMONLY%

Al principio de la fase de combate, puedes gastar una ficha de concentración para elegir una nave aliada a distancia 1, que tenga habilidad de piloto mayor. Incremente su agilidad en 1 hasta el final de la ronda."""
+        "Calc???":
+            text: """Esta carta todavía no ha sido revelada."""
+        "Accuracy Corrector":
+            text: """Cuando ataques, puedes ignorar todos los resultados de los dados. Entonces, puedes añadir 2 resultados de %HIT%.

Tus dados no pueden ser modificados de nuevo durante este ataque."""
+        "Inertial Dampeners":
+            text: """Cuando reveles tu dial de maniobra, puedes descartar esta carta para en su lugar realizar una maniobra [0%STOP%] blanca. Despúes recibe un marcador de tensión."""
+        "Flechette Cannon":
+            text: """Esta carta todavía no ha sido revelada."""
+        '"Mang??? Cannon"':
+            text: """Esta carta todavía no ha sido revelada."""
+        "Dead Man's Switch":
+            text: """Cuando seas destruido, cada nave a distancia 1 sufre 1 daño."""
+        "Feedback Array":
+            text: """DUrante la fase de combate, en lugar de realizar cualquier ataque, puedes recibir un marcador de iones y sufri 1 daño para seleccionar 1 nave enemiga a alcance 1. Esa nave sufre 1 daño."""
+        '"Hot Shot" Blaster':
+            text: """Attack: Descarta esta carta para atacar 1 nave (incluso aunque se halle fuera de tu arco de fuego)."""
+        "Greedo":
+            text: """%SCUMONLY%

El primer ataque y defensa que efectues en cada ronda, el primer daño que se asigne será boca arriba."""
+        "Salvaged Astromech":
+            text: """Cuando se te asignen cartas de daño con la palabra Ship, puedes descartarla inmediatamente (antes de resolver su efecto).

Entonces, descarta esta carta de mejora."""
+        "Bomb Loadout":
+            text: """Solo ala-Y.

Tu barra de mejoras gana el símbolo %BOMB%."""
+        '"Genius"':
+            text: """Si equipas una bomba que deba ser lanzada antes de que reveles el dial de maniobra, podrás en su lugar lanzarla despues de que ejectures la maniobra."""
+        "Unhinged Astromech":
+            text: """Puedes considerar todas las maniobras de velocidad 3 como maniobras verdes."""
+
     modification_translations =
         "Stealth Device":
             name: "Dispositivo de Sigilo"
@@ -782,7 +844,9 @@
             text: """Una vez por ronda, trás realizar una acción, puedes hacer una acción gratuita de alguna de tus mejoras equipadas que tenga el texto "Acción:".  Recibe una ficha de tension."""
         "Tactical Jammer":
             text: """Tu nave obstruye ataques enemigos."""
-
+        "Autoth???":
+            text: """Esta carta todavía no ha sido revelada."""
+
     title_translations =
         "Slave I":
             name: "Esclavo 1"
@@ -832,5 +896,15 @@
             text: """Solo YT-2400.

Mientras tu nave tenga una mejora de %CANNON% equipada, no podrá realizar atques con el armamento principal attacks pero podrá realizar ataques con su mejora de armamento secundario %CANNON% contra naves enemigas fuera de su arco de fuego principal."""
         "Dauntless":
             text: """Solo Decimator VT-49.

Después de ejecutar una maniobra que te haga solapar con otra nave, puedes realizar 1 acción gratuita. Recibe 1 ficha de tensión."""
+        "Virago":
+            text: """Solo StarViper.

TU barra de mejoras gana los iconos de mejora de %SYSTEM% y %ILLICIT%.

No puedes equipar esta carta si tu habilidad de piloto es "3" o inferior."""
+        '"Heavy Scyk" Interceptor (Cannon)':
+            text: """Solo Interceptor M3-A.

Tu barra de mejoras gana el icono de mejora de %CANNON%, %TORPEDO%, o %MISSILE%."""
+        '"Heavy Scyk" Interceptor (Torpedo)':
+            text: """Solo Interceptor M3-A.

Tu barra de mejoras gana el icono de mejora de  %CANNON%, %TORPEDO%, o %MISSILE%."""
+        '"Heavy Scyk" Interceptor (Missile)':
+            text: """Solo Interceptor M3-A.

Tu barra de mejoras gana el icono de mejora de  %CANNON%, %TORPEDO%, o %MISSILE%."""
+        "IG-2000":
+            text: """Solo Agressor.

Tienes el texto de habilidad de piloto de otras naves IG-2000 aliadas, adicionalmente a la tuya propia."""

     exportObj.setupCardData basic_cards, pilot_translations, upgrade_translations, modification_translations, title_translations


attribution to TinyColor

attribution to TinyColor

--- framer/Color.coffee
+++ framer/Color.coffee
@@ -1,5 +1,7 @@
 {BaseClass} = require "./BaseClass"
 libhusl		= require "husl"
+
+# the Color class is inspirated by TinyColor https://github.com/bgrins/TinyColor

 class exports.Color extends BaseClass
 	constructor: (@color, r, g, b) ->


Fix bug for empty new song.

Fix bug for empty new song.

--- app/client/js/users/store.coffee
+++ app/client/js/users/store.coffee
@@ -161,8 +161,8 @@
       .of @
       .map (store) ->
         if store.newSong.id == song.id
-          return store.newSong.lyrics.trim()
-        store.songs?[song.id]?.lyrics.trim()
+          return store.newSong?.lyrics?.trim()
+        store.songs?[song.id]?.lyrics?.trim()
       .filter (lyrics) ->
         # Exists and has more than one word.
         lyrics && lyrics.split(/\s+/).length > 1


Add a comment for clarity.

Add a comment for clarity.

--- app/coffee/UpdatesManager.coffee
+++ app/coffee/UpdatesManager.coffee
@@ -188,6 +188,8 @@
 		for update in updates
 			earliestUpdate = summarizedUpdates[summarizedUpdates.length - 1]
 			if earliestUpdate and earliestUpdate.meta.start_ts - update.meta.end_ts < @TIME_BETWEEN_DISTINCT_UPDATES
+				# check if the user in this update is already present in the earliest update,
+				# if not, add them to the users list of the earliest update
 				userExists = false
 				for user in earliestUpdate.meta.users
 					if (!user and !update.meta.user) or (user.id == update.meta.user?.id)


refactor: try using did insert element

refactor: try using did insert element

--- app/components/vnc-viewer.coffee
+++ app/components/vnc-viewer.coffee
@@ -22,7 +22,7 @@
       "Pop Out Modal"
   ).property "isPoppedOut"

-  didUpdate: ->
+  didInsertElement: ->
     canvasEl = @element.getElementsByClassName("canvas")[0]
     that = @
     @set "rfb", new RFB


Fixed resize bug.

Fixed resize bug.

--- client/app/Applications/WebTerm.kdapplication/src/Terminal.coffee
+++ client/app/Applications/WebTerm.kdapplication/src/Terminal.coffee
@@ -31,16 +31,16 @@
     @screenBuffer = new WebTerm.ScreenBuffer(this)
     @cursor = new WebTerm.Cursor(this)

-    @outputbox = $(document.createElement("div"))
-    @outputbox.css "cursor", "text"
-    @container.append @outputbox
-
     @measurebox = $(document.createElement("div"))
     @measurebox.css "position", "fixed"
     @measurebox.css "visibility", "hidden"
     @container.append @measurebox
     @updateSizeTimer = null
     @updateSize()
+
+    @outputbox = $(document.createElement("div"))
+    @outputbox.css "cursor", "text"
+    @container.append @outputbox

     @container.on "mousedown mousemove mouseup mousewheel contextmenu", (event) =>
       @inputHandler.mouseEvent event if @inSession


fix (NavBar) Cast title to string before passing it to native

--- src/models/NavigationBar.coffee
+++ src/models/NavigationBar.coffee
@@ -88,7 +88,7 @@
         relativeTo = options.relativeTo ? steroids.app.path

         parameters = if title
-          title: title
+          title: String(title)
         else
           titleImagePath: relativeTo + options.titleImagePath

@@ -212,10 +212,10 @@
       params = {}

       if options.constructor.name == "String"
-        params.title = options
+        params.title = String(options)

       if options.title?
-        params.title = options.title
+        params.title = String(options.title)
         params.titleImagePath = ""

       params.border = options.border


Improve the performance for count method

--- src/arangodb.coffee
+++ src/arangodb.coffee
@@ -758,7 +758,8 @@
         aql = aql.filter(w)
       merge true, bindVars, where.bindVars

-    aql = qb.let('count', aql.return(returnVariable)).return(qb.LENGTH('count'))
+    aql = aql.collectWithCountInto(returnVariable).return(returnVariable)
+
     @execute model, 'query', aql, bindVars, (err, result) ->
       callback err, result._result[0]


Inverted name and clarified comment

Inverted name and clarified comment

--- core/app/assets/javascripts/lib/prevent_scroll_propagation.coffee
+++ core/app/assets/javascripts/lib/prevent_scroll_propagation.coffee
@@ -1,10 +1,9 @@
 #= require jquery.mousewheel

-weShouldHandleScrolling = ($el) ->
-  # There isn't anything to scroll, and we only show a scrollbar when there is
-  return false if $el[0].scrollHeight <= $el.innerHeight() && $el.css('overflow-y') == 'auto'
-
-  true
+useNativeBehaviour = ($el) ->
+  # We explicitly only want a scrollbar when there is something to scroll (overflow-y: auto),
+  # but there isn't anything to scroll
+  $el.css('overflow-y') == 'auto' && $el[0].scrollHeight <= $el.innerHeight()

 preventScroll = ($el, delta) ->
   # Scrolling upward but cannot scroll any further
@@ -19,8 +18,7 @@
   @each ->
     $(this).on 'mousewheel', (event, delta) ->
       $el = $(event.delegateTarget)
-
-      return unless weShouldHandleScrolling($el)
+      return if useNativeBehaviour($el)

       event.stopPropagation() # allow nesting of scrollable containers
       event.preventDefault() if preventScroll($el, delta)


parseLocation: Improve regexp to recognize 5.domain.com.


Use the argument parser before looping args to process. [#197]

--- src/bindings.coffee
+++ src/bindings.coffee
@@ -35,16 +35,17 @@
       id = args.shift()
       formatter = @view.formatters[id]

+      args = Rivets.ArgumentParser.parse(args)
       processedArgs = []

       for arg, ai in args
-        processedArgs.push if str = arg.match(/^\'(.+)?\'$/)
-          str[1]
+        processedArgs.push if arg.type is 0
+          arg.value
         else
           @formatterObservers[fi] or= {}

           unless observer = @formatterObservers[fi][ai]
-            observer = new Rivets.Observer @view, @view.models, arg, @sync
+            observer = new Rivets.Observer @view, @view.models, arg.value, @sync
             @formatterObservers[fi][ai] = observer

           observer.value()