account: make active session bold and add (Active) text

--- client/account/lib/accountsessionlistitem.coffee
+++ client/account/lib/accountsessionlistitem.coffee
@@ -34,7 +34,7 @@

   pistachio: ->

-    { groupName, lastAccess, lastLoginDate } = @getData()
+    { groupName, lastAccess, lastLoginDate, clientId } = @getData()

     hostname = globals.config.domains.main

@@ -43,12 +43,17 @@
       then hostname
       else "#{groupName}.#{hostname}"

+    activeSession = ''
+    if kookies.get('clientId') is clientId
+      group = "#{group} (Active)"
+      activeSession = ' active'
+
     lastAccess = lastLoginDate or lastAccess

     """

-
#{group}

+
#{group}

Last access: #{timeago lastAccess}

       {div.delete-button{> @deleteButton }}


tray notifications are updated for linking the group titles

--- client/app/MainApp/notificationcontroller.coffee
+++ client/app/MainApp/notificationcontroller.coffee
@@ -180,9 +180,9 @@
           when "groupInvited"
             "#{actorName} has invited you to #{subjectObj.title}."
           when "groupJoined"
-            "#{actorName} has joined #{subjectObj.title}."
+            "#{actorName} has joined #{subjectObj.title}."
           when "groupLeft"
-            "#{actorName} has left #{subjectObj.title}."
+            "#{actorName} has left #{subjectObj.title}."
           else
             if actorType is "follower"
               "#{actorName} started following you."


added a random command

added a random command

--- src/scripts/xkcd.coffee
+++ src/scripts/xkcd.coffee
@@ -8,23 +8,41 @@
 #   None
 #
 # Commands:
-#   hubot xkcd - The latest XKCD comic
+#   hubot xkcd [latest]- The latest XKCD comic
 #   hubot xkcd <num> - XKCD comic <num>
+#   hubot xkcd random - XKCD comic <num>
 #
 # Author:
 #   twe4ked

 module.exports = (robot) ->
-  robot.respond /xkcd\s?(\d+)?/i, (msg) ->
-    if msg.match[1] == undefined
-      num = ''
-    else
-      num = "#{msg.match[1]}/"
-
-    msg.http("http://xkcd.com/#{num}info.0.json")
+  robot.respond /xkcd(\s+latest)?$/i, (msg) ->
+    msg.http("http://xkcd.com/info.0.json")
       .get() (err, res, body) ->
         if res.statusCode == 404
           msg.send 'Comic not found.'
         else
           object = JSON.parse(body)
           msg.send object.title, object.img, object.alt
+
+  robot.respond /xkcd\s+(\d+)/i, (msg) ->
+    num = "#{msg.match[1]}"
+
+    msg.http("http://xkcd.com/#{num}/info.0.json")
+      .get() (err, res, body) ->
+        if res.statusCode == 404
+          msg.send 'Comic #{num} not found.'
+        else
+          object = JSON.parse(body)
+          msg.send object.title, object.img, object.al
+
+  robot.respond /xkcd\s+random/i, (msg) ->
+    max = 1100 #max as of 3/2013 TODO: add way to get current max
+    num = Math.floor((Math.random()*max)+1)
+    msg.http("http://xkcd.com/#{num}/info.0.json")
+      .get() (err, res, body) ->
+        if res.statusCode == 404
+          msg.send 'Comic not found.'
+        else
+          object = JSON.parse(body)
+          msg.send object.title, object.img, object.alt


allow probing for cayw url

allow probing for cayw url

--- chrome/content/zotero-better-bibtex/web-endpoints.coffee
+++ chrome/content/zotero-better-bibtex/web-endpoints.coffee
@@ -160,6 +160,10 @@

 Zotero.BetterBibTeX.endpoints.cayw = { supportedMethods: ['GET'] }
 Zotero.BetterBibTeX.endpoints.cayw.init = (url, data, sendResponseCallback) ->
+  if url.query.probe
+    sendResponseCallback(200, 'text/plain', Zotero.BetterBibTeX.release)
+    return
+
   doc = new Zotero.BetterBibTeX.CAYW.Document()

   deferred = Q.defer()


reactivity: chat input: search regexp change

--- client/activity/lib/components/searchdropup/index.coffee
+++ client/activity/lib/components/searchdropup/index.coffee
@@ -54,7 +54,7 @@

     { value } = textData

-    matchResult = value.match /\/s(.+)/
+    matchResult = value.match /\/s (.+)/
     if matchResult
       query = matchResult[1]
       ActivityFlux.actions.chatInputSearch.setQuery query


Don't dispatch any event when "IDEView" is merged or splitted.

--- client/ide/lib/workspace/idelayoutmanager.coffee
+++ client/ide/lib/workspace/idelayoutmanager.coffee
@@ -156,32 +156,40 @@
    * Resurrect saved snapshot from server.
    *
    * @param {Array} snapshot
-  ###
-  resurrectSnapshot: (snapshot) ->
+   * @param {boolean=} quite  Don't dispatch `SplitViewWasMerged` or `NewSplitViewCreated` event.
+  ###
+  resurrectSnapshot: (snapshot, quite = no) ->

     ## The `ideApp` is an `IDEAppController`s instance
     ideApp = @getDelegate()

+    ideApp.quite = no
+
     # if has the fake view
     if ideApp.fakeViewsDestroyed
-      ideApp.mergeSplitView()
+      ideApp.mergeSplitView quite
       ideApp.setActiveTabView ideApp.ideViews.first.tabView

     ideApp.splitTabView { type: snapshot[1].direction, quite }  if snapshot[1]

+    for index, item of snapshot when item.type is 'split'  # Defensive check.
+      ideView = ideApp.ideViews[index]

       if not item.views.length or item.views.first.context
         ideView.setHash item.hash

-    for index, item of snapshot
-      tabView = ideApp.ideViews[index]?.tabView
-      @resurrectPanes_ item.views, tabView
+      @resurrectPanes_ item.views, ideView.tabView, quite

     ideApp.recalculateHandles()
     ideApp.isLocalSnapshotRestored = yes

-  resurrectPanes_: (items, tabView) ->
+  ###*
+   * @param {Array} items
+   * @param {IDEApplicationTabView} tabView
+   * @param {boolean=} quite  Don't dispatch any event when "IDEView" is merged or splitted
+  ###
+  resurrectPanes_: (items, tabView, quite) ->

     ## The `ideApp` is an `IDEAppController`s instane
     ideApp = @getDelegate()
@@ -205,8 +213,8 @@
         if item.views.length
           # since we are in a for loop to be able to preserve item and tabview
           # for the defer, we are creating a scope and passing them into there.
-          do (item, tabView) =>
-            kd.utils.defer => @resurrectPanes_ item.views, tabView
+          do (item, tabView, quite) =>
+            kd.utils.defer => @resurrectPanes_ item.views, tabView, quite

       else
         # Don't use `active tab view` logic for new pane creation.


MachineStateModal: Added stopping state text.

--- client/ide/views/modals/machinestatemodal.coffee
+++ client/ide/views/modals/machinestatemodal.coffee
@@ -50,6 +50,7 @@
     stateTexts       =
       Stopped        : 'is turned off.'
       Starting       : 'is starting now.'
+      Stopping       : 'is stopping now.'
       Running        : 'up and running.'
       Building       : 'is building now.'
       NotInitialized : 'is not initialized.'


refactor: Change to something possibly more readable

--- src/transaction-runner.coffee
+++ src/transaction-runner.coffee
@@ -675,8 +675,10 @@

   isMultipart: (headers) ->
     contentType = caseless(headers).get('Content-Type')
-    return false unless contentType
-    return contentType.indexOf('multipart') > -1
+    if contentType
+      contentType.indexOf('multipart') > -1
+    else
+      false

   # Finds newlines not preceeded by carriage returns and replaces them by
   # newlines preceeded by carriage returns.


Dropbox: Handled os kite error on upload process.

--- client/app/MainApp/filetree/controllers/findertreecontroller.coffee
+++ client/app/MainApp/filetree/controllers/findertreecontroller.coffee
@@ -821,9 +821,14 @@
         duration   : 120000

       kiteController.run "mkdir -p Web ; zip -r #{relativePath} #{plainPath}", (err, res) =>
-        return  warn err if err
-        notification.notificationSetTitle "Uploading zipped file..."
-        kallback()
+        if err
+          message = if err.name is "ExitError" then "An error occured. It seems zip is not installed on your VM."
+          else "An error occured, please try again."
+          notification.notificationSetTitle message
+          notification.notificationSetTimer 4000
+          notification.setClass "error"
+        else
+          kallback()
     else
       notification = new KDNotificationView
         title      : "Uploading your file..."


WebTerm is now working with the new structure change

No more repeating keystrokes.

--- client/landing/app/MainApp/account-mixins.coffee
+++ client/landing/app/MainApp/account-mixins.coffee
@@ -43,17 +43,25 @@

       channels = {}

-      request =(kiteName, method, args, onMethod='on')->
-        callbackId = listenerId++
+      scrub = (method, args, callback) ->
         scrubber = new Scrubber localStore
         scrubber.scrub args, =>
           scrubbed = scrubber.toDnodeProtocol()
           scrubbed.method or= method
-          fetchChannel kiteName, (channel)=>
+          callback scrubbed
+
+      request =(kiteName, method, args, onMethod='on')->
+        scrub method, args, (scrubbed) ->
+          fetchChannel kiteName, (channel)->
             channel[onMethod](
               "reply-client-message",
               messageHandler.bind null, kiteName
             )
+            channel.emit "client-message", JSON.stringify(scrubbed)
+
+      response = (kiteName, method, args) ->
+        scrub method, args, (scrubbed) ->
+          fetchChannel kiteName, (channel)=>
             channel.emit "client-message", JSON.stringify(scrubbed)

       messageHandler =(kiteName, args) ->
@@ -62,7 +70,7 @@
         unscrubbed = scrubber.unscrub args, (callbackId)->
           unless remoteStore.has callbackId
             remoteStore.add callbackId, ->
-              request kiteName, callbackId, [].slice.call arguments
+              response kiteName, callbackId, [].slice.call(arguments)
           remoteStore.get callbackId
         callback.apply @, unscrubbed

@@ -81,6 +89,5 @@
         scrubber = new Scrubber localStore
         args = [options, callback]
         {method} = options
-        onMethod = if options.autoCull is false then 'on' else 'once'
         delete options.autoCull
-        request options.kiteName, method, args, onMethod
+        request options.kiteName, method, args


relative movement

relative movement

--- src/draw_turtle.coffee
+++ src/draw_turtle.coffee
@@ -44,15 +44,15 @@
       world.reset('i', world.get('v_i'))
       world.reset('j', world.get('v_j'))
     right: (world, args) ->
-      console.log("right #{world}:")
-      console.log world.doc.x
-      world.put('v_i', -world.get('v_j'))
-      world.put('v_j',  world.get('v_i'))
+      next_v_i = -world.get('v_j')
+      next_v_j =  world.get('v_i')
+      world.put('v_i', next_v_i )
+      world.put('v_j', next_v_j )
     left: (world, args) ->
-      console.log("left #{world}:")
-      console.log world.doc.x
-      world.put('v_i',  world.get('v_j'))
-      world.put('v_j', -world.get('v_i'))
+      next_v_i =  world.get('v_j')
+      next_v_j = -world.get('v_i')
+      world.put('v_i', next_v_i )
+      world.put('v_j', next_v_j )
     children: {
       ME: {
         i: 2


Added comment

Added comment

--- core/app/backbone/modules/memory_profiler.coffee
+++ core/app/backbone/modules/memory_profiler.coffee
@@ -8,6 +8,8 @@
       MemoryProfiler.view_count_collection = new ViewCountCollection

       old_constructor = Backbone.View::constructor
+      # Note: This hack only works for Marionette views, since these views call
+      # their super() constructor. Backbone.View's don't
       Backbone.View.::constructor = (options)->
         old_constructor.apply @, arguments
         openView(this)


tweaks

tweaks

Former-commit-id: a728d6cba8d8084364bb872b4ad2711805e5f2f7 [formerly eda2205a5bc76c7d8375bb35bb93e10b0c9c70bc] [formerly c305acc5e88df4bed5a267d3b6239f41d6476de8 [formerly 6bdd4804a9416136abae8303461d6e98ac28bb83]] [formerly d969dbb80069a5beba69b9c323c445513d4d7673 [formerly ca9cc10b59960d53389243a2b3b50f3de3166ac5] [formerly 3a36385e79063f00cb8bf67c722ee760594f67f3 [formerly 3a36385e79063f00cb8bf67c722ee760594f67f3 [formerly d1f9ad59e73230be81c1a2f11f3740bd41bb9465]]]]
Former-commit-id: b45baf432e2d86f0bf6967c4254addc5183a0abb [formerly e1878a09ecd85776ee56ca7faada2ec6448ca81a] [formerly 0af20e9e1bb6d44de76d9bb306ec9ee379c33717 [formerly f0214175d23048e0a3c868dbbea6f1b8c5008a03]]
Former-commit-id: c90320eae1aa213549616fc95e64fbeb974e1a4b [formerly e6de7b7cc3bb4e7a95725b3b934d26bdc7cf0481]
Former-commit-id: 92e2d6a3847fdbed2bb8dd8f0a7024669f94442b
Former-commit-id: 0054de37f6ae8c97010709c264ae2f9cf76fe73c

--- @client/saas_landing_page/pricing.coffee
+++ @client/saas_landing_page/pricing.coffee
@@ -19,7 +19,7 @@
       DIV
         style: h1

-        'Pricing'
+        'Start Using Consider.it'

       DIV
         style: _.extend {}, base_text,
@@ -216,7 +216,7 @@
         style: _.extend {}, base_text,
           width: 600
           height: 96
-          margin: '10px auto 60px auto'
+          margin: '10px auto 40px auto'

         for reason, idx in plan.reasons
           if @local.custom_reason_hover == reason.icon


operations: fix requiredAffiliation()

operations: fix requiredAffiliation()

--- src/local/operations.coffee
+++ src/local/operations.coffee
@@ -776,13 +776,10 @@

 class ManageNodeSubscriptions extends PrivilegedOperation
     requiredAffiliation: =>
-        if @actorAffiliation is 'owner'
-            yes
-        else if @nodeConfig.channelType is 'topic' and
-                @actorAffiliation is 'moderator'
-            yes
+        if @nodeConfig.channelType is 'topic'
+            'moderator'
         else
-            no
+            'owner'

     privilegedTransaction: (t, cb) ->
         defaultAffiliation = null
@@ -817,13 +814,10 @@

 class ManageNodeAffiliations extends PrivilegedOperation
     requiredAffiliation: =>
-        if @actorAffiliation is 'owner'
-            yes
-        else if @nodeConfig.channelType is 'topic' and
-                @actorAffiliation is 'moderator'
-            yes
+        if @nodeConfig.channelType is 'topic'
+            'moderator'
         else
-            no
+            'owner'

     privilegedTransaction: (t, cb) ->
         async.series @req.affiliations.map(({user, affiliation}) =>
@@ -905,13 +899,10 @@

 class AuthorizeSubscriber extends PrivilegedOperation
     requiredAffiliation: =>
-        if @actorAffiliation is 'owner'
-            yes
-        else if @nodeConfig.channelType is 'topic' and
-                @actorAffiliation is 'moderator'
-            yes
+        if @nodeConfig.channelType is 'topic'
+            'moderator'
         else
-            no
+            'owner'

     privilegedTransaction: (t, cb) ->
         if @req.allow


test: Redundant require

test: Redundant require

--- test/integration/sanitation-test.coffee
+++ test/integration/sanitation-test.coffee
@@ -1,6 +1,5 @@
 {assert} = require('chai')
 clone = require('clone')
-express = require('express')
 {EventEmitter} = require('events')

 {runDredd, createServer, runDreddWithServer} = require('./helpers')


Improve formatting of jCounter.jQuery.coffee

--- file/js/jCounter.jQuery.coffee
+++ file/js/jCounter.jQuery.coffee
@@ -12,7 +12,7 @@
 	$.fn.jCounter = (container, options) ->
 		# properly handle multiple elements passed
 		if @length > 1
-			return @.each (k, v) ->
+			return @each (k, v) ->
 				$(v).jCounter container, options

 		# break if element already has got an inline jCounter assigned
@@ -25,24 +25,26 @@

 		max = @attr('maxlength') ? options.max

-		jCounter = 	(if not container?
-					# create inline jCounter
-					id = @attr('id') ? @attr('id', "jCounterID#{jCounterID++}").attr('id')
-
-					@addClass('jCounterInput').wrap("""

""").parent().append """#{max}"""
-
-					$(@).parent().children(".jCounter").css
-						borderTopRightRadius: @css 'border-top-right-radius'
-						borderBottomRightRadius: @css 'border-bottom-right-radius'
+		jCounter = (
+			if container?
+				if typeof container is 'object'
+					container
 				else
-					if typeof container is 'object'
-						container
-					else
-						$ container)
+					$ container
+			else
+				# create inline jCounter
+				id = @attr('id') ? @attr('id', "jCounterID#{jCounterID++}").attr('id')
+
+				@addClass('jCounterInput').wrap("""

""").parent().append """#{max}"""
+
+				$(@).parent().children(".jCounter").css
+					borderTopRightRadius: @css 'border-top-right-radius'
+					borderBottomRightRadius: @css 'border-bottom-right-radius'
+		)

 		# handle keyX events
 		@on 'keypress keyup keydown change', =>
-			length = if options.countUp then @.val().length else max - @.val().length
+			length = if options.countUp then @val().length else max - @val().length

 			# determine new color
 			color = (if options.countUp
@@ -65,7 +67,7 @@
 			jCounter.text(length).removeClass('color-1 color-2 color-3').addClass "color-#{color}"

 			# update position of inline jCounter in case the element changed size
-			if not container?
+			unless container?
 				jCounter.css 'margin-left', -(jCounter.outerWidth() + parseFloat(@css('border-right-width')))
 				@css 'padding-right', 5 + jCounter.outerWidth()
 		@trigger 'keypress'


update try again text

update try again text

--- static/embed/oab_embed.coffee
+++ static/embed/oab_embed.coffee
@@ -923,7 +923,7 @@

Article DOI or URL
<input class="_oab_form" id="_oab_doi" type="text" placeholder="e.g 10.1126/scitranslmed.3008973">

Continue

-    Try again
+    Try another


Fix typo.

Fix typo.

--- framer/Extras/Preloader.coffee
+++ framer/Extras/Preloader.coffee
@@ -137,7 +137,7 @@

 	_handleTimeout: =>
 		return unless @isLoading
-		console.warning("Preloader timeout, ending")
+		console.warn("Preloader timeout, ending")
 		@end()

 exports.enable = ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/timed_events.coffee
+++ src/timed_events.coffee
@@ -4,7 +4,7 @@

 player = GameObject()

-player.incldue
+player.include Timed

 @see GameObject


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/timed_events.coffee
+++ src/timed_events.coffee
@@ -23,7 +23,7 @@

   @name every
   @methodOf TimedEvents#
-  @param {Number} n The number of frames to wait before executing the
+  @param {Number} n The number of frames to wait before executing the callback
   @returns {Number} Distance between the two objects
   ###
   every: (n, fn) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/timed_events.coffee
+++ src/timed_events.coffee
@@ -1,4 +1,4 @@
 TimedEvents = (I={}) ->
   every: (n) ->
-    if I.age.mod(5) is 0
+    if I.age.mod(n) is 0
       doSomething()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/title_screen.coffee
+++ src/title_screen.coffee
@@ -15,7 +15,7 @@
 ###

 ###*
-Overlays the title text in the middle of the screen. Uses
+Overlays the title text in the middle of the screen. Uses App.name

 @name update
 @methodOf TitleScreen#


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/title_screen.coffee
+++ src/title_screen.coffee
@@ -1,5 +1,5 @@
 ###*
-The Title Screen class sets up a simple game title screen using the
+The Title Screen class sets up a simple game title screen using the <code>App

 By default, images are loaded asynchronously. A proxy object is
 returned immediately. Even though it has a draw method it will not


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/level_state.coffee
+++ src/level_state.coffee
@@ -5,6 +5,7 @@
   self = GameState(I)

   self.bind "enter", ->
+    #
     engine.camera().fadeIn 10

     engine.loadMap I.level, ->


smart labels

smart labels

--- src/config/controls.coffee
+++ src/config/controls.coffee
@@ -23,9 +23,8 @@
     action_key: (world) -> world.get('_LABEL')
     action: (world) -> world.get('signals')[ world.get('action_key') ]
     name: (world) ->
-      return world.get('_LABEL')# TODO: remove
       action = world.get('action')
-      action['name'] if action?
+      if action? then action['name'] else world.get('_LABEL')
     click: (world, args) ->
       action = world.get('action')
       current = world.get('current')
@@ -36,12 +35,9 @@
       _LABEL: "game_controls"
       _AUTHORITY: (world) -> world.get('BUTTON')
       _CHILDREN: [
-        {_LABEL: "init"}
-        {_LABEL: "restart"}
-        {_LABEL: "help"}
-        {_LABEL: "interactive"}
-        {_LABEL: "batch"}
-        {_LABEL: "loop"}
+        {_LABEL: "step"}
+        {_LABEL: "reset"}
+        {_LABEL: "run"}
       ]
     }
     {


fixing build status

fixing build status

--- prviewer.coffee
+++ prviewer.coffee
@@ -206,7 +206,7 @@

       # Grab build status codes (if they exist).
       if statuses.length > 0
-        status = statuses[0].state
+        status = statuses[statuses.length - 1].state
         for config in settings.buildStatuses
           if new RegExp(config.regex, 'i').test status
             pull.buildStatusClass = config.class


Add .get() and .expire() methods to Biskoto

--- src/biskoto.coffee
+++ src/biskoto.coffee
@@ -1,8 +1,11 @@
 define ->
   class Biskoto
+    encode = encodeURIComponent
+    decode = decodeURIComponent
+
     @get: (name) ->
       if document.cookie
-        cookies = decodeURIComponent(document.cookie).split(/;\s/g)
+        cookies = decode(document.cookie).split(/;\s/g)

         for cookie in cookies
           if cookie.indexOf(name) is 0
@@ -10,4 +13,41 @@

       return null

+    @set: (name, value, options = {}) ->
+      if typeof value is 'string'
+        json_value = value
+      else
+        json_value = JSON.stringify(value)
+
+      document.cookie = "#{name}=#{json_value}#{@_cookieOptions(options)}"
+
+    @expire: (name, options = {}) ->
+      options.expires = -1
+      @set(name, '', options)
+
+    ###
+      options = {
+        expires : Integer (seconds)
+        secure  : Boolean
+        domain  : String
+        path    : String
+      }
+    ###
+    @_cookieOptions: (options = {}) ->
+      cookie_str = ''
+
+      for key, value of options
+        if key is 'expires'
+          cookie_str += "; expires=#{@_createExpireDate(options.expires)}"
+        else if key is 'domain'
+          cookie_str += "; domain=#{value}"
+
+      cookie_str += "; path=#{if options.path then options.path else '/'}"
+
+    @_createExpireDate: (seconds) ->
+      new Date(
+        +new Date() + (seconds * 1000)
+      ).toUTCString()
+
+  window.Biskoto = Biskoto
   return Biskoto


Improved error handling.

Improved error handling.

--- app/assets/javascripts/app/controllers/navigation.coffee
+++ app/assets/javascripts/app/controllers/navigation.coffee
@@ -215,6 +215,7 @@
       return
     else if e.keyCode is 13 # enter
       href = @$('#global-search-result .nav-tab.is-hover').attr('href')
+      return if !href
       @locationExecute(href)
       @emptyAndClose()
       return


Improved websocket reconnection after wakeup.

--- app/assets/javascripts/app/lib/app_post/websocket.coffee
+++ app/assets/javascripts/app/lib/app_post/websocket.coffee
@@ -91,6 +91,15 @@
     # initial connect
     @connect()

+    # send ping after visibilitychange to check if connection is open again after wakeup
+    $(document).bind('visibilitychange', =>
+      console.log('visibilitychange')
+      return if document.hidden
+      return if !@connectionEstablished
+      console.log('ping')
+      @ping()
+    )
+
   channel: ->
     @backend

@@ -153,7 +162,7 @@
     @log 'debug', 'send websocket ping'
     @send(event: 'ping')

-    # check if ping is back within 2 min
+    # check if ping is back within 90 sec
     App.Delay.clear 'websocket-ping-check', 'ws'
     check = =>
       @log 'debug', 'no websocket ping response, reconnect...'
@@ -165,7 +174,7 @@

     @log 'debug', 'received websocket pong'

-    # test again after 1 min
+    # test again after 60 sec
     App.Delay.set(@ping, 60000, 'websocket-pong', 'ws')

   connect: =>


Delete popup confirmation

Delete popup confirmation

--- app/assets/javascripts/backend/bank_statements.js.coffee
+++ app/assets/javascripts/backend/bank_statements.js.coffee
@@ -111,34 +111,6 @@

   $(document).on "change", "#hide-lettered", ->
     bankReconciliation.uiUpdate()
-
-  # $(document).on "submit", "#intervalReconciliationForm", (event) ->
-  #
-  #   period_start = $('input[name="period_start"]').datepicker('getDate')
-  #   period_end = $('input[name="period_end"]').datepicker('getDate')
-  #
-  #   if !period_start || !period_end
-  #     event.preventDefault()
-  #     return
-  #
-  #   event.preventDefault()
-  #
-  #   $.ajax "/backend/bank_statements/reconciliation/items/bank_statements_items_count",
-  #     type: 'get'
-  #     dataType: 'JSON'
-  #     data:
-  #       cash_id: $('#cash_id').val()
-  #       period_start: moment(period_start).format('YYYY-MM-DD')
-  #       period_end: moment(period_end).format('YYYY-MM-DD')
-  #     success: (bank_statement_items_count) =>
-  #       if bank_statement_items_count > 1000
-  #         if !confirm("Vous venez se sélectionner plus de 1000 lignes avec l'intervalle que vous avez défini. Cela peut engendrer un temps de réponse plus long. Souhaitez-vous continuer ?")
-  #           $(this).unbind(event).submit()
-  #       else
-  #           $(this).unbind(event).submit()
-  #
-  #       return true
-

   $(document).on "datepicker-change", "#set_period", (event, dates) ->
     current_params = document.location.search


minor change to history buffer (simple semantic adjustment)

--- app/assets/javascripts/carmenta/history_buffer.js.coffee
+++ app/assets/javascripts/carmenta/history_buffer.js.coffee
@@ -13,6 +13,9 @@
     @stack.shift() if @stack.length > @maxLength
     @index = @stack.length - 1

+    #Carmenta.log(@stack.length, '------------------------------------------------------------')
+    #Carmenta.log(@stack)
+

   undo: ->
     return null if @index < 1
@@ -21,6 +24,6 @@

   redo: ->
-    return null if @index > @stack.length - 2
+    return null if @index >= @stack.length - 1
     @index += 1
     return @stack[@index]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_selector.coffee
+++ src/engine_selector.coffee
@@ -18,7 +18,7 @@
   Get the game object matching the given selector that is closest to the given position.

   @param selector
-  @param
+  @param {Point} position
   ###
   closest: (selector, position) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_selector.coffee
+++ src/engine_selector.coffee
@@ -59,7 +59,7 @@
   @param {String} selector
   @returns {Array} An array of the objects found
   ###
-  each:
+  each: ()

   find: (selector) ->
     results = []


refactored tests

refactored tests

--- src/middleware/stats.coffee
+++ src/middleware/stats.coffee
@@ -9,10 +9,8 @@

 sdc = new statsd_client statsd_server

-
-
 exports.incrementTransactionCount = (ctx, done) ->
-  logger.info 'sending count to statsd for ' + domain + '.' + ctx.authorisedChannel._id
+  logger.info 'sending counts to statsd for ' + domain + '.' + ctx.authorisedChannel._id
   transactionStatus = ctx.transactionStatus
   try
     sdc.increment domain # Overall Counter
@@ -34,8 +32,6 @@
   catch error
     logger.error error

-
-
 exports.koaMiddleware = `function *statsMiddleware(next) {

       yield next;


fix handle_renderable

fix handle_renderable

--- src/context.coffee
+++ src/context.coffee
@@ -23,8 +23,8 @@
         true

   handle_renderable = (object) ->
-    if fn = object?._lead_render
-      fn.apply @
+    if object?._lead_render
+      @render object
       true

   handle_using_extension = (object) ->


:art: Remove need for explicit returns

:art: Remove need for explicit returns

--- src/notational/items.coffee
+++ src/notational/items.coffee
@@ -80,14 +80,12 @@
         el   : patch(el, diff(tree, newTree))
         tree : newTree
       }
-    [selectedItemProp.changes()], (current, ...) ->
+    [selectedItemProp.changes()], R.tap (current, ...) ->
       # Scroll item into the view if outside the visible border and was triggered by selectItem change
       if selectedRow = current.el.querySelector('.is-selected')
         selectedRow.scrollIntoViewIfNeeded(false) # centerIfNeeded=false => croll minimal possible to avoid jumps
-      return current
-    [searchStream], (current, ...) ->
+    [searchStream], R.tap (current, ...) ->
       current.el.querySelector('.tbody').scrollTop = 0 #return to top
-      return current

   return {


durable

durable

--- src/durable.coffee
+++ src/durable.coffee
@@ -24,7 +24,6 @@
 @name Durable
 @module
 @constructor
-
 @param {Object} I Instance variables
 @param {Core} self Reference to including object
 ###


honor rename

honor rename

--- src/tests/generator-as-source.test.coffee
+++ src/tests/generator-as-source.test.coffee
@@ -65,7 +65,7 @@
   await T.perform null, [ 1, 2, 3, 4, ], ->
     return new Promise ( resolve ) ->
       pipeline = []
-      pipeline.push PS.$generate g 10
+      pipeline.push PS.new_generator_source g 10
       pipeline.push $ ( d, send ) -> send if d is 5 then null else d
       pipeline.push PS.$show()
       pipeline.push PS.$collect()


forking

forking

--- counter-tests.coffee
+++ counter-tests.coffee
@@ -1,4 +1,4 @@
-Tinytest.add 'mongo-counter', (test) ->
+Tinytest.add 'konecty-mongo-counter', (test) ->
   deleteCounters()
   test.equal incrementCounter('foo'), 1
   test.equal incrementCounter('foo'), 2


refactor: Simplify searching of elements


Docco for leaf tier files.

Docco for leaf tier files.

--- src/lib/strata_.coffee
+++ src/lib/strata_.coffee
@@ -31,6 +31,9 @@
 #  * Descent - What we call an attempt to move from a parent node to a child
 #              node in  the tree, which may be paused because it encounters a
 #              lock on a tier.
+#  * Record - A JSON object stored in the b-tree.
+#  * Object - JavaScript objects are referred to as objects in this
+#             documentation and are not to be confused with actual records.

 #
 class Mutation
@@ -285,65 +288,177 @@
       true
     false

+# ### Leaf Tier Files
+#
+# A leaf tier maintains an array of file positions called an address array. A
+# file position in the address array references a record in the leaf tier file
+# by its file position. The addresses in the address array are sorted by the
+# the sort order of the referenced records according to the sort order of the
+# b-tree.
+#
+# A leaf tier file contains JSON objects, one object on each line.
+#
+# There are three types of objects in a leaf tier file, insert objects, delete
+# objects, and address array objects.
+#
+# An insert object contains a *record* and the index in the address array where
+# the record's address would be inserted to preserve the sort order of the
+# address array.
+#
+# Beginning with an empty array and reading from the start of the file, the leaf
+# tier address array is reconstituted by reading the object at the current
+# position in the file, then inserting the position of the current object into
+# the address array at the index in the insert object.
+#
+# We record a deletion by appending a delete object. A delete object contains
+# an index in the address array that should be deleted. When reading the leaf
+# tier from the start of the file, we insert an address into the address array
+# when we encounter an insert object, and we delete an address when we encounter
+# a delete object.
+#
+# On occasion, we can store an address array object, An address array object
+# contains the address array itself.  We store a copy of a constructed address
+# array object in the leaf tier file so that we can read a large leaf tier file
+# quickly.
+#
+# When we read a leaf tier file, if we read from the back of the file toward the
+# front, we can read backward until we find an address array object. Then we can
+# read forward to the end of the file, applying the inserts and deletes that
+# occured after we wrote the address array object.
+#
+# When a leaf tier file is large, stashing the constructed address array at the
+# end means that the leaf tier can be loaded quickly, because we will only have
+# to read backwards a few entries to find a mostly completed address array. We
+# can then read forward from the array to amend the address array with the
+# inserts and deletes that occured after it was written.
+#
+# Not all of the records will be loaded when we go backwards, but we have their
+# file position from the address array, so we can jump to them and load them as
+# we need them. That is, if we need them, because owing to binary search, we
+# might only need a few records out of a great many records to find the record
+# we're looking for.
+#
+# Over time, a leaf tier file can grow fat with deleted records — an
+# insert object and a subsequent delete object. We vacuum a leaf tier file by
+# writing it to a new leaf tier file.
+
+# `LeafIO` — Keeps track of a file descriptor opened for append, and the
+# append file position. The `LeafIO` class does *not* contain all methods
+# relating to leaf tier file I/O. You can find more leaf tier file methods in
+# the `IO` class below.
+#
+# TODO Rename `RecordIO`.
 class LeafIO
+  # Construct a leaf file tier writer for the given file name.
   constructor: (@filename) ->
-
-  _drain: (callback) -> @out.once "drain", -> callback()
-
+    @length = 1024
+
+  # #### Leaf Tier File Object Format
+  #
+  # Each leaf tier object is a JSON array. The first element of the array is an
+  # integer.
+  #
+  # If the integer is greater than zero, it indicates an insert object.  The
+  # integer is the one based index into the zero based address array, indicating
+  # the index where the position of the current insert object should be
+  # inserted. The second element of the leaf tier object array is the record
+  # object.
+
+  # Write an insert object.
+  writeInsert: (object, index, _) ->
+    @_writeJSON [ index + 1, object ], _
+
+  # If the integer is less than zero, it indicates a delete object. The absolute
+  # value of the integer is the one based index into the zero based addrss
+  # array, indicating the index of address array element that should be deleted.
+  # There are no other elements in the leaf tier object array.
+
+  # Write a delete object.
+  writeDelete: (index, _) ->
+    @_writeJSON [ -(index + 1) ], _
+
+  # If the integer is zero, it indicates an address array object. The integer
+  # value of zero is used only as an indicator. The second element is the
+  # address of the leaf tier to the right of current leaf tier. The third
+  # element of the leaf tier object array is the address array.
+
+  # Write an address array object.
+  writeAddresses: (right, addresses, _) ->
+    @_writeJSON [ 0, right, addresses ], _
+
+  # Append an object to the leaf tier file as a single line of JSON.
   _writeJSON: (object, _) ->
-    @out or= fs.createWriteStream @filename,
-      flags: "a"
-      encoding: "utf8"
-      mode: 0666
-    @position or= fs.stat(@filename, _).size
+    @position     or= fs.stat(@filename, _).size
+    @fd           or= fs.open @filename, "a+", 0644, _

     json            = JSON.stringify object
     position        = @position
-    @position      += Buffer.byteLength(json, "utf8") + 1
-
-    buffered = @out.write(json)
-    buffered = @out.write("\n") && buffered
-
+    length          = Buffer.byteLength(json, "utf8")
+    buffer          = new Buffer(length + 1)
+    offset          = 0
+
+    # Write may be interrupted by a signal, so we keep track of how many bytes
+    # are actually written and write the difference if we come up short.
+    do
+      count = buffer.length - offset
+      written = fs.write @fd, buffer, offset, count, @position, _
+      @position += written
+      offset += written
+    while offset != buffer.length
+
+    # What's the point if it doesn't make it to disk?
+    fs.fsync @fd, _
+
+    # Return the file position of the inserted record.
     position

-  writeInsert: (object, index, _) ->
-    @_writeJSON [ index + 1, object ], _
-
-  writeDelete: (index, _) ->
-    @_writeJSON [ -(index + 1) ], _
-
-  writeAddresses: (right, addresses, _) ->
-    @_writeJSON [ 0, right, addresses ], _
-
+  # Each line is terminated by a newline. In case your concerned that this
+  # simple search will mistake a byte inside a multi-byte character for a
+  # newline, have a look at
+  # [UTF-8](http://en.wikipedia.org/wiki/UTF-8#Description). All bytes
+  # participating in a multi-byte character have their leading bit set, all
+  # single-byte characters have their leading bit unset.
+
+  # Search for the newline that separates JSON records.
   _readJSON: (buffer, read) ->
     for offset in [0...read]
       if buffer[offset] is 0x0A
         return JSON.parse buffer.toString("utf8", 0, offset + 1)

+  # Jump to a position in the file and read a specific object. Because objects
+  # are cached in memory by a leaf tier, we're not going to get a request to
+  # read an object that has been written to the current write stream. When we
+  # read in a leaf tier, we'll cache any objects we encounter prior to read the
+  # ordering array.
+  #
+  # Note how we allow we keep track of the minimum buffer size that will
+  # accommodate the largest possible buffer.
   readObject: (address, _) ->
-    @in or= fs.open @filename, "r", _
+    @fd or= fs.open @filename, "a+", _
     @position or= fs.stat(@filename, _).size
-    length = 1024
     loop
-      buffer = new Buffer(length)
+      buffer = new Buffer(@length)
       read = fs.read @in, buffer, 0, buffer.length, address, _
       if json = @_readJSON(buffer, read)
         break
-      if length > @position - address
+      if @length > @position - address
         throw new Error "cannot find end of record."
-      length *= 2
+      @length += @length >>> 1
     json.pop()

-  _closeOut: (callback) ->
-    @out.once "close", -> callback()
-    @out.destroySoon()
-
+  # Close the file descriptor if it is open and reset the file descriptor and
+  # append position.
   close: (_) ->
-    @close(@in, _) if @in
-    @_closeOut(_) if @out
-    @position = @in = @out = null
-
+    @close(@fd, _) if @fd
+    @position = @fd = null
+
+# ### Branch Tier Files
+#
+# A branch tier file contains an array of object addresses
+
+#
 class IO
+  # Set directory and extractor. Initialze cache and MRU list.
   constructor: (@directory, @extractor) ->
     @cache          = {}
     @head           = { address: -1 }
@@ -351,7 +466,11 @@
     @head.previous  = @head
     @nextAddress    = 0

+  # Create the database detroying any exisiting database.
+
+  # —
   create: (_) ->
+    # Create the directory if it doesn't exist.
     try
       stat = fs.stat @directory, _
       if not stat.isDirectory()
@@ -359,11 +478,16 @@
     catch e
       throw e if e.code isnt "ENOENT"
       fs.mkdir @directory, 0755, _
-    root = @_allocateBranches true
-    leaf = @_allocateLeaves([])
+    # Create a root branch with a single empty leaf.
+    root = @allocateBranches true
+    leaf = @allocateLeaves([])
     root.addresses.push leaf.address
-    @_writeBranches root, "", _
-
+    # Write the root branch.
+    @writeBranches root, "", _
+
+  # Open an existing database.
+
+  # —
   open: (_) ->
     try
       stat = fs.stat @directory, _
@@ -379,6 +503,7 @@
         address = parseInt match[1], 10
         @nextAddress = address + 1 if address > @nextAddress

+  # Link tier to the head of the MRU list.
   link: (entry) ->
     next = @head.next
     entry.next = next
@@ -387,12 +512,14 @@
     entry.previous = @head
     entry

+  # Unlnk a tier from the MRU list.
   unlink: (entry) ->
     { next, previous } = entry
     next.previous = previous
     previous.next = next
     entry

+  # TODO Move down, bring read and write up.
   lock: (exclusive, address, leaf, callback) ->
     if not tier = @cache[address]
       @cache[address] = tier =
@@ -552,23 +679,25 @@
         key = tierOrAddress.cache[address] = @key address, 0, _
       key

+  # Write the branches to a file as a JSON string. We tuck the tier properties
+  # into an object before we serialize it, so that it is easy deserialize.
+  writeBranches: (tier, suffix, _) ->
+    filename = @filename tier.address, suffix
+    record = [ tier.penultimate, tier.next, tier.addresses ]
+    json = JSON.stringify(record) + "\n"
+    fs.writeFile filename, json, "utf8", _
+
+  # Read branches from a brach tier file and assign the tier properties to the
+  # given tier object.
+  #
   # TODO: Move link to head always, to maintain MRU.
   readBranches: (tier, _) ->
     filename = @filename tier.address
-    tier.addresses = JSON.parse fs.readFile filename, "utf8", _
-    tier.penultimate = tier.addresses.pop()
-    tier.loaded = true
-    tier
-
-  # Write the branches to a file as a JSON string. The penultimate flag is
-  # pushed onto the end of the objects array for serialization, and popped
-  # before the function returns.
-  writeBranches: (tier, suffix, _) ->
-    filename = @filename(tier.address, suffix)
-    record [ tier.penultimate, tier.next, tier.addresses ]
-    json = JSON.stringify(record) + "\n"
-    fs.writeFile filename, json, "utf8", _
-
+    json = fs.readFile filename, "utf8", _
+    [ penultimate, next, addresses ] = JSON.parse json
+    extend tier, { penultimate, next, addresses }
+
+  # TODO Consider: can't I always just rewrite and link?
   rewriteBranches: (tier, _) ->
     @writeBranches(tier, ".new", _)

@@ -587,6 +716,9 @@
     tier.cache = cache
     tier

+  # Move a new branch tier file into place. Unlink the existing branch tier
+  # file, then rename the new branch tier file to the permanent name of the
+  # branch tier file.
   relink: (tier, _) ->
     replacement = @filename(tier.address, ".new")
     stat = fs.stat replacement, _


Workspace: Fixed opening a url for preview pane.

--- client/landing/app/CommonViews/workspace/panes/collaborativepreviewpane.coffee
+++ client/landing/app/CommonViews/workspace/panes/collaborativepreviewpane.coffee
@@ -9,8 +9,7 @@
     {@previewer} = @previewPane

     if @isJoinedASession
-      @workspaceRef.once "value", (snapshot) =>
-        @previewer.openPath snapshot.val().url
+      @workspaceRef.once "value", (snapshot) => @openPath snapshot
     else
       @previewer.on "ready", => @saveUrl()

@@ -18,10 +17,13 @@

     @previewer.on "ViewerRefreshed",       => @saveUrl yes

-    @workspaceRef.on "value", (snapshot)   =>
-      @previewer.openPath snapshot.val().url
+    @workspaceRef.on "value", (snapshot)   => @openPath snapshot

     @workspaceRef.onDisconnect().remove() if @amIHost
+
+  openPath: (snapshot) ->
+    value = snapshot.val()
+    @previewer.openPath value.url  if value?.url

   saveUrl: (force) ->
     url = @previewer.path.replace /\?.*/, ""


Fix mod filter breaking when switching scoreboard type

--- resources/assets/coffee/react/beatmapset-page/main.coffee
+++ resources/assets/coffee/react/beatmapset-page/main.coffee
@@ -72,8 +72,6 @@

     @setState
       currentScoreboardType: scoreboardType
-    #   scores: []
-    #   userScore: null

     return if scoreboardType != 'global' && !currentUser.isSupporter

@@ -83,6 +81,8 @@
       _.without @state.enabledMods, enabledMod
     else if enabledMod != null
       _.concat @state.enabledMods, enabledMod
+    else
+      @state.enabledMods

     @scoresCache ?= {}
     cacheKey = "#{@state.currentBeatmapId}-#{@state.currentPlaymode}-#{_.sortBy enabledMods}-#{scoreboardType}"


CADS-90 : modified coffee script to send yes/no for eventLabel

--- c3/app/assets/javascripts/trackInputOnEvent.coffee
+++ c3/app/assets/javascripts/trackInputOnEvent.coffee
@@ -1,11 +1,12 @@
-valueEntered = (selector) -> if $("##{selector}").val()? then "yes" else "no"
+valueEntered = (selector) -> if ($("##{selector}").val())!="" then "yes" else "no"
 window.trackInputOnEventInit = (path, label, trackChangesTo, whenEventTriggered) ->

   if $("#" + trackChangesTo)
     $("." + whenEventTriggered).on "click", ->
       trackChange(path, label, trackChangesTo);

+
 trackChange = (path, label, trackChangesTo) ->
-  trackEvent(path, 'edit', label, valueEntered(trackChangesTo));
+  trackEvent(path, label, valueEntered(trackChangesTo));


WIP Fixed retention bug

WIP Fixed retention bug

--- muleview/app/controller/ChartsController.coffee
+++ muleview/app/controller/ChartsController.coffee
@@ -171,8 +171,8 @@
       # Prevent latency mess:
       return unless @lastRequestId == currentRequestId

-      @lightChartsContainer.setLoading("Processing...")
-      @mainChartContainer.setLoading("Processing...")
+      @lightChartsContainer.setLoading({msg: "Processing...", msgCls: "load-mask-no-image"})
+      @mainChartContainer.setLoading({msg: "Processing...", msgCls: "load-mask-no-image"})

       # Enable buttons:
@@ -186,7 +186,7 @@
       @data = data
       Ext.defer =>
         @updateRetentionsStore()
-        @defaultRetention = @currentRetName || @retentionsStore.getAt(0).get("name")
+        @defaultRetention = @retention || @retentionsStore.getAt(0).get("name")

         @initLightCharts()
         @lightChartsContainer.setLoading(false)
@@ -219,7 +219,7 @@
       data: @data[retName]

   showRetention: (retName) ->
-    @currentRetName = retName
+    @retention = retName
     @renderChart()

     @retentionsMenu.select(retName)
@@ -232,7 +232,7 @@
         lightChart.show()

     @resetRefreshTimer()
-    Muleview.event "viewChange", @keys, @currentRetName
+    Muleview.event "viewChange", @keys, @retention

   fixDuplicateAndMissingTimestamps: (data) ->
     recordsByTimestamp = {}
@@ -261,9 +261,9 @@
     @mainChartContainer.removeAll()
     if @showSubkeys
       @mainChartContainer.setLoading(true)
-      Muleview.Mule.getGraphData @key, @currentRetName, (data) =>
+      Muleview.Mule.getGraphData @key, @retention, (data) =>
         @fixDuplicateAndMissingTimestamps(data)
-        @alerts = Ext.StoreManager.get("alertsStore").getById("#{@key};#{@currentRetName}")?.toGraphArray(@currentRetName)
+        @alerts = Ext.StoreManager.get("alertsStore").getById("#{@key};#{@retention}")?.toGraphArray(@retention)
         @subkeys = Ext.Array.difference(Ext.Object.getKeys(data), [@key])
         @addChart
           showAreas: true
@@ -275,12 +275,12 @@
     else
       @addChart
         topKeys: @keys
-        data: @data[@currentRetName]
+        data: @data[@retention]

   addChart: (cfg) ->
     common = {
       flex: 1
-      title: ("#{key};#{@currentRetName}" for key in @keys).join("
")
+      title: ("#{key};#{@retention}" for key in @keys).join("
")
       listeners:
         closed: =>
           Muleview.event "legendChange", false
@@ -307,8 +307,8 @@
   editAlerts: ->
     ae = Ext.create "Muleview.view.AlertsEditor",
       key: @key
-      retention: @currentRetName
-      data: @data[@currentRetName][@key]
+      retention: @retention
+      data: @data[@retention][@key]
     ae.show()

   updateZoomStatsAndHighlight: (timestampMin, timestampMax) ->
@@ -322,7 +322,7 @@
     sum = 0
     count = 0

-    data = @data[@currentRetName]
+    data = @data[@retention]
     firstKey = data[@keys[0]]

     # I'm assuming all keys have the same x values


Remove exception for links.

Remove exception for links.

I'd put this in when deving to prevent a Scribe bug from failing all the time,
but it should just fail now until we fix it.

--- tests/webdriver/lib/scribedriver.coffee
+++ tests/webdriver/lib/scribedriver.coffee
@@ -59,7 +59,6 @@
           precedingAttrs = followingAttrs = {}
           if appendingToLine(index)
             precedingAttrs = getAttrsAt(index - 1)
-            delete precedingAttrs['link'] if precedingAttrs['link']?
           else if prependingToLine(index)
             precedingAttrs = followingAttrs = getAttrsAt(index)
           else


Add support for using other regions than US

--- src/grafana.coffee
+++ src/grafana.coffee
@@ -15,6 +15,7 @@
 #   HUBOT_GRAFANA_S3_ACCESS_KEY_ID - Optional; Access key ID for S3
 #   HUBOT_GRAFANA_S3_SECRET_ACCESS_KEY - Optional; Secret access key for S3
 #   HUBOT_GRAFANA_S3_PREFIX - Optional; Bucket prefix (useful for shared buckets)
+#   HUBOT_GRAFANA_S3_REGION - Optional; Bucket region (defaults to us-standard)
 #
 # Dependencies:
 #   "knox": "^0.9.2"
@@ -37,6 +38,8 @@
   s3_access_key = process.env.HUBOT_GRAFANA_S3_ACCESS_KEY_ID
   s3_secret_key = process.env.HUBOT_GRAFANA_S3_SECRET_ACCESS_KEY
   s3_prefix = process.env.HUBOT_GRAFANA_S3_PREFIX
+  s3_region = 'us-standard'
+  s3_region = process.env.HUBOT_GRAFANA_S3_REGION if process.env.HUBOT_GRAFANA_S3_REGION

   # Get a specific dashboard with options
   robot.respond /(?:grafana|graph|graf) (?:dash|dashboard|db) ([A-Za-z0-9\-\:_]+)(.*)?/i, (msg) ->
@@ -210,7 +213,8 @@
     client = knox.createClient {
       key    : s3_access_key
       secret : s3_secret_key,
-      bucket : s3_bucket
+      bucket : s3_bucket,
+      region : s3_region
     }

     headers = {
@@ -225,8 +229,7 @@
     req = client.put(filename, headers)
     req.on 'response', (res) ->
       if (200 == res.statusCode)
-        image = "https://s3.amazonaws.com/#{s3_bucket}/#{filename}"
-        sendRobotResponse msg, title, image, link
+        sendRobotResponse msg, title, client.https(filename), link
       else
         msg.send "#{title} - [Upload Error] - #{link}"
     req.end(content);


Correct cursor positioning when exceeding max length.

--- src/input/InputView.coffee
+++ src/input/InputView.coffee
@@ -184,7 +184,8 @@
     exceededMaxLength = v.length > (@mask?.maxlength or @maxlength)

     # Indicates an invalid character was pressed.
-    invalidCharacter = newValue is @value and newCursorPos isnt cursorPos
+    invalidCharacter = newValue is @value and
+    (newCursorPos isnt cursorPos or cursorPos is v.length)

     # Detect if we should indicate an invalid character was entered or the
     # max-length of the input or mask has been reached. If so, don't updated
@@ -200,6 +201,12 @@

     @value = newValue

+    # If we've exceeded the max length, and there's no mask to correct the
+    # cursor position, manually move it back by one. This handles the case
+    # where the user types in-the-middle of a string which has exceeded the
+    # maximum input length.
+    if exceededMaxLength and not @mask then newCursorPos--
+
     # Always update the cursor position.
     e.target.setSelectionRange newCursorPos, newCursorPos


Last bits of callout panels, fixing damage analogy panel, minor other tweaks

--- src/javascript/main.coffee
+++ src/javascript/main.coffee
@@ -292,6 +292,27 @@
   fleetBroadcasts:
     icon: 'images/icons/UI/WindowIcons/chatchannel.png'

+  dscan:
+    icon: 'images/dscan.png'
+  probeScan:
+    icon: 'images/probescan.png'
+  warpCelestial:
+    icon: 'images/warpto.png'
+  warpBookmark:
+    icon: 'images/warpBookmark.png'
+  warpFleetmate:
+    icon: 'images/warpFleetmate.png'
+  warpScanResult:
+    icon: 'images/warpto.png'
+  aligns:
+    icon: 'images/align.png'
+  accelGate:
+    icon: 'images/accelGate.png'
+  selfDestruct:
+    icon: 'images/icons/UI/WindowIcons/terminate.png'
+  trash:
+    icon: 'images/icons/UI/WindowIcons/Reprocess.png'
+
 # alias styles
 STYLES.HighSec = STYLES.high
 STYLES.LowSec = STYLES.low
@@ -600,7 +621,7 @@
           dom.div {className: 'col-md-12'}, socialMiscPanel

         dom.div {className: 'row'}
-          dom.div {className: 'col-md-6'}, miscStats
+          dom.div {className: 'col-md-12'}, miscStats

         # Misc

@@ -966,24 +987,37 @@
       id: 29988
       ehp: 150000
       fit: 'https://o.smium.org/loadout/private/23324/6303219387142766592'
-    # avatar:
-    #   pluralName: 'Avatars'
-    #   id: 11567
-    #   ehp: 22600000
-    #   fit: 'https://o.smium.org/loadout/private/23322/7459269642280763392'
+    naglfar:
+      pluralName: 'Naglfars'
+      id: 19722
+      ehp: 1500000
+      fit: 'https://o.smium.org/loadout/private/24661/3437198039918313472'
+    avatar:
+      pluralName: 'Avatars'
+      id: 11567
+      ehp: 22600000
+      fit: 'https://o.smium.org/loadout/private/23322/7459269642280763392'
   render: ->
     if @props.damage == 0
       return null
     else
-      ship = _.sample @ships
-      numShips = d3.round(@props.damage / ship.ehp, 1)
+      shipToUse = null
+      numShips = 0
+      for name, ship of @ships
+        shipToUse = name
+        numShips = d3.round(@props.damage / ship.ehp, 1)
+        if numShips < 100
+          break
+
+      ship = @ships[shipToUse]
       stamps = []
       for i in [0...Math.floor(numShips)]
         stamps.push dom.img {key: i, src: eveIconUrl(ship.id), width: 32, height: 32, className: 'stamp pull-left'}

       return dom.div {className: 'row'},
         dom.h4 {className: 'pull-left'},
-          dom.em null,"You have dealt enough damage to kill #{numShips} "
+          dom.em null,
+            "You have dealt enough damage to kill #{numShips} "
             dom.a {href: ship.fit}, ship.pluralName
         dom.div {className: 'clearfix'}, ''
         dom.div {className: ''}, stamps
@@ -1602,6 +1636,11 @@
           icon: 'chat'
         }
         {
+          value: @props.stats.socialDirectTrades
+          description: 'Direct Trades'
+          icon: 'trades'
+        }
+        {
           value: @props.stats.socialMailsSent
           description: 'Mails Sent'
           icon: 'mailSent'
@@ -1610,11 +1649,6 @@
           value: @props.stats.socialMailsReceived
           description: 'Mails Received'
           icon: 'mailReceived'
-        }
-        {
-          value: @props.stats.socialDirectTrades
-          description: 'Direct Trades'
-          icon: 'trades'
         }
         {
           value: @props.stats.socialFleetJoins
@@ -1635,19 +1669,62 @@
 MiscStats = React.createClass(
   displayName: 'MiscStats'
   render: ->
-    dom.div {className: ''},
-      dom.h3 null, 'The Rest!'
-      dom.ul null,
-        dom.li null, "#{numFmt @props.stats.genericConeScans} Directional Scans"
-        dom.li null, "#{numFmt @props.stats.genericRequestScans} Probe Scans"
-        dom.li null, "#{numFmt @props.stats.travelWarpsToCelestial} warps to celestial"
-        dom.li null, "#{numFmt @props.stats.travelWarpsToBookmark} warps to bookmark"
-        dom.li null, "#{numFmt @props.stats.travelWarpsToFleetMember} warps to fleet member"
-        dom.li null, "#{numFmt @props.stats.travelWarpsToScanResult} warps to scan result"
-        dom.li null, "#{numFmt @props.stats.travelAlignTo} aligns"
-        dom.li null, "#{numFmt @props.stats.travelAccelerationGateActivations} acceleration gate activations"
-        dom.li null, "#{numFmt @props.stats.combatSelfDestructs} self destructs"
-        dom.li null, "#{numFmt @props.stats.inventoryTrashItemQuantity} items trashed"
+    if @props.stats
+      callouts = [
+        {
+          value: @props.stats.genericConeScans
+          description: 'Directional Scans'
+          icon: 'dscan'
+        }
+        {
+          value: @props.stats.genericRequestScans
+          description: 'Probe Scans'
+          icon: 'probeScan'
+        }
+        {
+          value: @props.stats.travelWarpsToCelestial
+          description: 'Warps to Celestial'
+          icon: 'warpCelestial'
+        }
+        {
+          value: @props.stats.travelWarpsToBookmark
+          description: 'Warps to Bookmark'
+          icon: 'warpBookmark'
+        }
+        {
+          value: @props.stats.travelWarpsToFleetMember
+          description: 'Warps to Fleet Member'
+          icon: 'warpFleetmate'
+        }
+        {
+          value: @props.stats.travelWarpsToScanResult
+          description: 'Warps to Scan Result'
+          icon: 'warpScanResult'
+        }
+        {
+          value: @props.stats.travelAlignTo
+          description: 'Aligns'
+          icon: 'aligns'
+        }
+        {
+          value: @props.stats.travelAccelerationGateActivations
+          description: 'Activated Acceleration Gate'
+          icon: 'accelGate'
+        }
+        {
+          value: @props.stats.combatSelfDestructs
+          description: 'Self Destructs'
+          icon: 'selfDestruct'
+        }
+        {
+          value: @props.stats.inventoryTrashItemQuantity
+          description: 'Items Trashed'
+          icon: 'trash'
+        }
+      ]
+      return React.createElement(CalloutPanel, {title: 'The Rest', callouts: callouts, columns: 4})
+    else
+      null
 )

 PieDataPanel = React.createClass(
@@ -1705,7 +1782,7 @@
       showColor: true
     }
   render: ->
-    colors = d3.scale.category20()
+    colors = d3.scale.category20b()
     rows = _.map @props.data, (d) =>
       if d.value == 0 and not @props.showZero
         return null
@@ -1837,7 +1914,7 @@
     if @props.total == 0
       return

-    color = d3.scale.category20()
+    color = d3.scale.category20b()

     outerRadius = @props.width / 2

@@ -1897,8 +1974,8 @@
       return

     radius = 90
-    arcWidth = 7
-    padding = 1
+    arcWidth = 10
+    padding = 2

     getOffset = (i) ->
       return radius - (i * (arcWidth + padding))
@@ -1929,7 +2006,7 @@
               .attr 'transform', "translate(#{radius},#{radius})"

     arcs.append 'path'
-        .attr 'fill', 'red'
+        .attr 'fill',STYLES.null.color
         .attr 'd', hullArc

     arcs.append 'path'


Invoke main callback when package waterfall drains

--- src/install.coffee
+++ src/install.coffee
@@ -240,6 +240,7 @@
                 process.stdout.write '\u2717\n'.red
               else
                 process.stdout.write '\u2713\n'.green
+              callback()

   # Install all the package dependencies found in the package.json file.
   #


Updated property naming convension to camelCase

--- src/model/audits.coffee
+++ src/model/audits.coffee
@@ -8,36 +8,36 @@

 activeParticipantSchema = new Schema
-  "UserID": { type: String, required: false }
-  "AlternativeUserID": { type: String, required: false }
-  "UserIsRequestor": { type: String, required: false }
-  "NetworkAccessPointID": { type: String, required: false }
-  "NetworkAccessPointTypeCode": { type: String, required: false }
-  "RoleIDCode": codeTypeSchema
+  "userID": { type: String, required: false }
+  "alternativeUserID": { type: String, required: false }
+  "userIsRequestor": { type: String, required: false }
+  "networkAccessPointID": { type: String, required: false }
+  "networkAccessPointTypeCode": { type: String, required: false }
+  "roleIDCode": codeTypeSchema

 ParticipantObjectIdentificationSchema = new Schema
-  "ParticipantObjectID": { type: String, required: false }
-  "ParticipantObjectTypeCode": { type: String, required: false }
-  "ParticipantObjectTypeCodeRole": { type: String, required: false }
-  "ParticipantObjectIDTypeCode": codeTypeSchema
-  "ParticipantObjectQuery": { type: String, required: false }
-  "ParticipantObjectDetail":
+  "participantObjectID": { type: String, required: false }
+  "participantObjectTypeCode": { type: String, required: false }
+  "participantObjectTypeCodeRole": { type: String, required: false }
+  "participantObjectIDTypeCode": codeTypeSchema
+  "participantObjectQuery": { type: String, required: false }
+  "participantObjectDetail":
     "type": { type: String, required: false }
     "value": { type: String, required: false }

 auditRecordSchema = new Schema
   "rawMessage":  { type: String, required: false }
-  "EventIdentification":
-    "EventDateTime": { type: Date, required: true, default: Date.now }
-    "EventOutcomeIndicator": { type: String, required: false }
-    "EventActionCode": { type: String, required: false }
-    "EventID": codeTypeSchema
-    "EventTypeCode": codeTypeSchema
-  "ActiveParticipant": [ activeParticipantSchema ]
-  "AuditSourceIdentification":
-    "AuditSourceID": { type: String, required: false }
-  "ParticipantObjectIdentification": [ ParticipantObjectIdentificationSchema ]
+  "eventIdentification":
+    "eventDateTime": { type: Date, required: true, default: Date.now }
+    "eventOutcomeIndicator": { type: String, required: false }
+    "eventActionCode": { type: String, required: false }
+    "eventID": codeTypeSchema
+    "eventTypeCode": codeTypeSchema
+  "activeParticipant": [ activeParticipantSchema ]
+  "auditSourceIdentification":
+    "auditSourceID": { type: String, required: false }
+  "participantObjectIdentification": [ ParticipantObjectIdentificationSchema ]

 exports.Audit = mongoose.model 'Audit', auditRecordSchema


Fix javascript client requires

Fix javascript client requires

--- lib/panoptes-subject-uploader.iced
+++ lib/panoptes-subject-uploader.iced
@@ -2,7 +2,8 @@

 minimist = require 'minimist'
 promptly = require 'promptly'
-{auth, apiClient} = require 'panoptes-client'
+auth = require 'panoptes-client/lib/auth'
+apiClient = require 'panoptes-client/lib/api-client'
 request = require 'request'
 path = require 'path'
 Baby = require 'babyparse'


Further fixes for AM/PM parsing, section of code wasn't being hit by tests

--- src/models/punch.coffee
+++ src/models/punch.coffee
@@ -300,13 +300,13 @@
     command = command.replace ///#{match[0]} ?///i, ''
   else if match = command.match REGEX.time
     timeMatch = match[0]
-    today = moment()
-    if hourStr = timeMatch.match /\b(([0-1][0-9])|(2[0-3])):/i
+    if hourStr = timeMatch.match /\b((0?[1-9]|1[0-2])|(([0-1][0-9])|(2[0-3]))):/i
       hour = parseInt (hourStr[0].replace(':', ''))
       if hour <= 12
-        isPM = today.format('a') is 'pm'
+        period = moment().format('a')
+        isPM = period is 'pm'
         if not timeMatch.match /(a|p)m?/i
-          timeMatch = timeMatch + " #{today.format('a')}"
+          timeMatch = "#{timeMatch} #{period}"
     today = moment(timeMatch, 'h:mm a')
     if isPM
       today.add(12, 'hours')


Made fix for punch rows that stretched (even incorrectly) into tomorrow

--- src/models/punch.coffee
+++ src/models/punch.coffee
@@ -105,7 +105,6 @@
           timePiece = moment.tz(row[headers[MODES[i]]],
                                 'hh:mm:ss a',
                                 constants.TIMEZONE)
-
           newDate = moment.tz("#{row[headers.today]} #{row[headers[MODES[i]]]}",
                               'MM/DD/YYYY hh:mm:ss a',
                               constants.TIMEZONE)
@@ -115,9 +114,12 @@
       block = parseInt(comps[0]) + (parseFloat(comps[1]) / 60)
       datetimes.block = block
     else if datetimes.length is 2
+      if datetimes[1].isBefore datetimes[0]
+        datetimes[1].add(1, 'days')
       elapsed = _calculateElapsed datetimes[0], datetimes[1], mode, user
       if elapsed < 0
-        Logger.error 'Elapsed time is less than 0', new Error(datetimes)
+        Logger.error 'Invalid punch row: elapsed time is less than 0', new Error(datetimes)
+        return

     foundProjects = []
     for i in [1..6]


removed redundant code

removed redundant code

--- src/model/users.coffee
+++ src/model/users.coffee
@@ -1,15 +1,5 @@
 mongoose = require "mongoose"
 Schema = mongoose.Schema
-
-
-ConfigSchema = new Schema
-    "firstname":    { type: String, required: true }
-    "surname":      { type: String, required: true }
-    "email":      { type: String, required: true, unique: true }
-
-#compile the Config Schema into a Model
-exports.Config = mongoose.model 'Config', ConfigSchema
-

 UserSchema = new Schema
     "firstname":		{ type: String, required: true }


Correct typo, add file type & quality support

--- lib/scripts/pdf_a4_portrait.coffee
+++ lib/scripts/pdf_a4_portrait.coffee
@@ -52,23 +52,27 @@
   paperSize.height = options.height
 else
   paperSize.format = options.format || 'A4'
-  paperSize.orientation = options.portrait || 'portrait'
+  paperSize.orientation = options.orientation || 'portrait'

 page.paperSize = paperSize

+
 # Output to parent process
+fileOptions =
+  type: options.type || 'pdf'
+  quality: options.quality || 75

 # Option 1: Output file to stdout
 # Not working in Ubuntu 12.04 (at least not in my environment)
 if options.buffer
-  page.render('/dev/stdout', format: 'pdf')
+  page.render('/dev/stdout', fileOptions)

 # Option 2: Output filename to stdout
 else
-  filename = options.filename || ("#{options.directory || '/tmp'}/phantom-pdf-#{sys.pid}-#{size}.pdf")
-  page.render(filename, format: 'pdf')
+  filename = options.filename || ("#{options.directory || '/tmp'}/html-pdf-#{sys.pid}-#{size}.#{fileOptions.type}")
+  page.render(filename, fileOptions)
   sys.stdout.write(filename)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -170,19 +170,7 @@
   player.include Metered

   player.meter 'health',
-    borderColor: 'brown'
-    color: 'pink'
-    font: '30px Comic Sans'
-    height: 20
-    position:
-      x: 5
-      y: 5
-    radius: 3
-    showName: true
-    text: 'Boss Health'
-    width: 150

-  # => Sets up a health meter, using all the configuration options
   </pre></code>

   @name meter


Koding group allows creation of groups

Koding group allows creation of groups

--- client/app/MainApp/VirtualizationController.coffee
+++ client/app/MainApp/VirtualizationController.coffee
@@ -58,9 +58,6 @@
   createGroupVM:(type='personal', callback)->
     defaultVMOptions = {cpu : 1, disk : 1, ram : 1}
     group = KD.singletons.groupsController.getCurrentGroup()
-
-    if group.slug is 'koding'
-      return callback "Koding group does not support to create additional VMs"

     group.fetchBundle (err, bundle)->
       switch type


Providers: AWS: add more instance types

Providers: AWS: add more instance types

--- client/app/lib/providers/instance-types/aws.coffee
+++ client/app/lib/providers/instance-types/aws.coffee
@@ -11,6 +11,26 @@
     ram:'4GB RAM'
     cpu:'1x CPU'
     description: ''
+  'c3.large':
+    cpu:'2x CPU'
+    ram:'3.75GB RAM'
+    description:''
+  'c3.xlarge':
+    cpu:'4x CPU'
+    ram:'7.5GB RAM'
+    description:''
+  'c3.2xlarge':
+    cpu:'8x CPU'
+    ram:'15GB RAM'
+    description:''
+  'c3.4xlarge':
+    cpu:'16x CPU'
+    ram:'30GB RAM'
+    description:''
+  'c3.8xlarge':
+    cpu:'32x CPU'
+    ram:'60GB RAM'
+    description:''
   'm4.large':
     ram:'8GB RAM'
     cpu:'2x CPU'


minor micro-optimization, because it makes me feel good

--- src/natives.coffee
+++ src/natives.coffee
@@ -84,8 +84,9 @@
                 source_file = 'unknown'
               line_nums = sf.method.code?.attrs?[0]?.entries
               if line_nums?
-                # XXX: WUT
-                ln = util.last(row.line_number for i,row of line_nums when row.start_pc <= sf.pc)
+                # get the last line number before the stack frame's pc
+                for i,row of line_nums when row.start_pc <= sf.pc
+                  ln = row.line_number
               ln ?= -1
               stack.push new JavaObject rs, rs.class_lookup(c2t "java/lang/StackTraceElement"), {
                 'java/lang/StackTraceElement/declaringClass': rs.init_string util.ext_classname cls.toClassString()


Convert to use "tap" event.

Convert to use "tap" event.

--- src/select/SelectBox.coffee
+++ src/select/SelectBox.coffee
@@ -218,8 +218,8 @@
   ###
   _attachSelectBoxListeners: ->
     indicator = @select(".#{@viewClasses.indicator}")
-    @addEventListener 'clickish', @onSelectBoxClick
-    @addEventListenerTo indicator, 'clickish', @_rippleSelectionInk
+    @addEventListener 'tap', @onSelectBoxClick
+    @addEventListenerTo indicator, 'tap', @_rippleSelectionInk

   ###*


linter changes

linter changes

--- config.cson
+++ config.cson
@@ -83,8 +83,8 @@
     sidebarWidth: 300
   "linter-python":
     executablePath: "/usr/local/bin/pylama"
-    ignoreCodes: "E111,E501"
-    withPylint: true
+    ignoreCodes: "E111,E501,E114"
+    withPep8: true
   "linter-ui-default":
     panelHeight: 69
     showPanel: true


Comments

Comments

--- templates/coffee.coffee
+++ templates/coffee.coffee
@@ -3,7 +3,7 @@
 singleQuote = (str) ->
   "'" + str.replace("'", "\\'") + "'"

-commonJsRequire = (dep) ->
+commonJSRequire = (dep) ->
   "require(" + singleQuote(dep) + ")"

 browserRequire = (dep) ->
@@ -43,6 +43,8 @@
   globals = _.filter(_.pluck(options.dependencies, 'global'))

   s "((root, factory) ->"
+  s ""
+  s "  # AMD"
   s "  if typeof define is 'function' and define.amd"

   if deps.length
@@ -58,11 +60,16 @@
     s "    #{__module__} = factory(root)"
     s "    define -> #{__module__}"

+  s ""
+  s "  # CommonJS"
   s "  else if typeof module is 'object' && typeof module.exports is 'object'"

   s (if options.global
     "    module.exports = "
-  else "    ") + "factory(#{['root'].concat(deps.map(commonJsRequire)).join(', ')})"
+  else "    ") + "factory(#{['root'].concat(deps.map(commonJSRequire)).join(', ')})"
+
+  s ""
+  s "  # Browser and the rest"
   s "  else"

   s (if options.global


Fix video record regex

Fix video record regex

--- packages/rocketchat-ui-message/message/messageBox.coffee
+++ packages/rocketchat-ui-message/message/messageBox.coffee
@@ -239,7 +239,7 @@
 	@location = new ReactiveVar false

 	@autorun =>
-		videoRegex = /video\/webm/i
+		videoRegex = /video\/webm|video\/\*/i
 		videoEnabled = !RocketChat.settings.get("FileUpload_MediaTypeWhiteList") || RocketChat.settings.get("FileUpload_MediaTypeWhiteList").match(videoRegex)
 		if RocketChat.settings.get('Message_VideoRecorderEnabled') and (navigator.getUserMedia? or navigator.webkitGetUserMedia?) and videoEnabled and RocketChat.settings.get('FileUpload_Enabled')
 			@showVideoRec.set true


Server: Tests: refactor getteam handler test file

--- servers/lib/server/handlers/getteam.test.coffee
+++ servers/lib/server/handlers/getteam.test.coffee
@@ -1,17 +1,18 @@
-Bongo                               = require 'bongo'
-koding                              = require './../bongo'
-
-{ daisy }                           = Bongo
-{ expect }                          = require 'chai'
 { Relationship }                    = require 'jraphical'
-{ generateRandomEmail
-  generateRandomString }           = require '../../../testhelper'
-
+{ async
+  expect
+  request
+  querystring
+  generateRandomEmail
+  generateRandomString
+  checkBongoConnectivity }          = require '../../../testhelper'
 { generateGetTeamRequestParams
   generateCreateTeamRequestParams } = require '../../../testhelper/handler/teamhelper'

-request                             = require 'request'
-querystring                         = require 'querystring'
+
+beforeTests = -> before (done) ->
+
+  checkBongoConnectivity done

 # here we have actual tests
@@ -19,52 +20,50 @@

   it 'should send HTTP 404 if group does not exist using any method', (done) ->

-    queue                 = []
-    methods               = ['post', 'get', 'put', 'patch']
-    groupSlug             = generateRandomString()
+    queue     = []
+    methods   = ['post', 'get', 'put', 'patch']
+    groupSlug = generateRandomString()

     methods.forEach (method) ->
-      getTeamRequestParams  = generateGetTeamRequestParams { method, groupSlug }
+      getTeamRequestParams = generateGetTeamRequestParams { method, groupSlug }

-      queue.push ->
+      queue.push (next) ->
         request getTeamRequestParams, (err, res, body) ->
-          expect(err)             .to.not.exist
-          expect(res.statusCode)  .to.be.equal 404
-          expect(body)            .to.be.equal 'no group found'
-          queue.next()
+          expect(err).to.not.exist
+          expect(res.statusCode).to.be.equal 404
+          expect(body).to.be.equal 'no group found'
+          next()

-    queue.push -> done()
-
-    daisy queue
+    async.series queue, done

   it 'should send HTTP 200 if slug is valid using any method', (done) ->

-    queue                     = []
-    methods                   = ['post', 'get', 'put', 'patch']
-    groupSlug                 = generateRandomString()
+    queue     = []
+    methods   = ['post', 'get', 'put', 'patch']
+    groupSlug = generateRandomString()

-    queue.push ->
+    queue.push (next) ->
       options = { body : { slug : groupSlug } }
       generateCreateTeamRequestParams options, (createTeamRequestParams) ->

         request.post createTeamRequestParams, (err, res, body) ->
-          expect(err)             .to.not.exist
-          expect(res.statusCode)  .to.be.equal 200
-          queue.next()
+          expect(err).to.not.exist
+          expect(res.statusCode).to.be.equal 200
+          next()

     methods.forEach (method) ->
       getTeamRequestParams = generateGetTeamRequestParams { method, groupSlug }

-      queue.push ->
+      queue.push (next) ->
         request getTeamRequestParams, (err, res, body) ->
-          expect(err)             .to.not.exist
-          expect(res.statusCode)  .to.be.equal 200
-          queue.next()
+          expect(err).to.not.exist
+          expect(res.statusCode).to.be.equal 200
+          next()

-    queue.push -> done()
-
-    daisy queue
+    async.series queue, done

+beforeTests()
+
 runTests()


Cleaner error reporting for `addFolder` endpoint

--- services/web/app/coffee/Features/Editor/EditorHttpController.coffee
+++ services/web/app/coffee/Features/Editor/EditorHttpController.coffee
@@ -96,8 +96,10 @@
 		EditorController.addFolder project_id, parent_folder_id, name, "editor", (error, doc) ->
 			if error == "project_has_to_many_files"
 				res.status(400).json(req.i18n.translate("project_has_to_many_files"))
+			else if error.message == 'invalid element name'
+				res.status(400).json(req.i18n.translate('invalid_file_name'))
 			else if error?
-				next(error)
+				res.status(500).json(req.i18n.translate('generic_something_went_wrong'))
 			else
 				res.json doc


add event tracking for syntax check

add event tracking for syntax check

--- services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
+++ services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
@@ -93,6 +93,7 @@
 			$scope.pdf.logEntries = [] if $scope.check
 			# keep track of whether this is a compile or check
 			$scope.check = if options.check then true else false
+			event_tracking.sendMB "syntax-check-request" if options.check
 			# send appropriate check type to clsi
 			checkType = switch
 				when $scope.check then "validate" # validate only
@@ -152,6 +153,7 @@
 				$scope.pdf.url = last_pdf_url
 				$scope.shouldShowLogs = true
 				$scope.pdf.failedCheck = true if response.status is "validation-fail"
+				event_tracking.sendMB "syntax-check-#{response.status}"
 				fetchLogs(fileByPath, { validation: true })
 			else if response.status == "exited"
 				$scope.pdf.view = 'pdf'
@@ -281,6 +283,7 @@
 							warnings.push result
 				all = [].concat errors, warnings
 				logHints = HumanReadableLogs.parse {type: "Syntax", all, errors, warnings}
+				event_tracking.sendMB "syntax-check-return-count", {errors:errors.length, warnings:warnings.length}
 				accumulateResults logHints

 			processBiber = (log) ->
@@ -366,12 +369,14 @@
 			$scope.pdf.compiling = true

 			if options?.force
-				# for forced compile, turn off validation check
+				# for forced compile, turn off validation check and ignore errors
 				$scope.stop_on_validation_error = false
 				$scope.shouldShowLogs = false # hide the logs while compiling
+				event_tracking.sendMB "syntax-check-turn-off-checking"

 			if options?.try
 				$scope.shouldShowLogs = false # hide the logs while compiling
+				event_tracking.sendMB "syntax-check-try-compile-anyway"

 			ide.$scope.$broadcast("flush-changes")


reset project to large state each time

reset project to large state each time

--- services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
+++ services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
@@ -25,6 +25,7 @@
 			$scope.pdf.timedout   = false
 			$scope.pdf.failure    = false
 			$scope.pdf.uncompiled = false
+			$scope.pdf.projectTooLarge = false
 			$scope.pdf.url        = null

 			if response.status == "timedout"


fix(e2e): stop clicking on label

fix(e2e): stop clicking on label

--- vue/tests/e2e/helpers/pageHelper.coffee
+++ vue/tests/e2e/helpers/pageHelper.coffee
@@ -104,7 +104,7 @@
     page.fillIn '.auth-email-form__email input', email
     page.click '.auth-email-form__submit'
     page.fillIn '.auth-signup-form input', 'New Account'
-    page.click('.auth-signup-form__legal-accepted label')
+    page.click('.auth-signup-form__legal-accepted .v-input--selection-controls__input')
     page.click '.auth-signup-form__submit'
     page.expectElement '.auth-complete'
     page.loadPath 'use_last_login_token'
@@ -114,7 +114,7 @@
     page = pageHelper(test)
     page.click '.auth-email-form__submit'
     page.fillIn '.auth-signup-form__name input', name
-    page.click('.auth-signup-form__legal-accepted label')
+    page.click('.auth-signup-form__legal-accepted .v-input--selection-controls__input')
     page.click '.auth-signup-form__submit'

   waitFor: (selector, wait = 5000) ->


Indent code

Indent code

--- public/app/common/components/controls/button/button.cjsx
+++ public/app/common/components/controls/button/button.cjsx
@@ -12,10 +12,10 @@

   render: ->
     className = cx
-      'cc-button': true,
-      secondary: this.props.secondary,
-      "#{this.props.size}":             @props.size?
-      "#{this.props.className or ''}":  true
+      'cc-button':                     true,
+      secondary:                       this.props.secondary,
+      "#{this.props.size}":            @props.size?
+      "#{this.props.className or ''}": true

      { this.props.children }


Add more errors to the test

Add more errors to the test

--- test/base32-test.coffee
+++ test/base32-test.coffee
@@ -52,7 +52,7 @@

   'When decoding a string with common errors':
     topic: ->
-      base32.decode 'dHqqetbjcdgq6t90an850haj8d0n6h9O64t36dLn6rvjO8a04cj2aqh6S8'
+      base32.decode 'dHqqetbjcdgq6t9Oan850hAj8d0n6h9O64t36dLn6rvjO8a04cj2aqh6S8'

     'it should be the same as the original': (topic) ->
       assert.equal topic, teststring


Minor clean up.

Minor clean up.

--- test/bundle_test.coffee
+++ test/bundle_test.coffee
@@ -13,7 +13,7 @@
   'test bundle defining require': ->
     dir = path.join __dirname, 'fixtures', 'sample-package'
     bundle = new Bundle dir
-    context = {}
-    (-> eval(bundle.toString())).call context
+    result_in_closure = -> eval bundle.toString()
+    result_in_closure.call context = {}
     @assert context.require
     @assertEqual 'function', typeof(context.require)


Profile map shows users's classifications

--- scripts/src/controllers/Profile.coffee
+++ scripts/src/controllers/Profile.coffee
@@ -3,6 +3,8 @@
   $ = require 'jQuery'

   User = require 'zooniverse/models/User'
+  Project = require 'zooniverse/models/Project'
+
   ZooniverseProfile = require 'zooniverse/controllers/Profile'

   Map = require 'zooniverse/controllers/Map'
@@ -15,6 +17,7 @@
     template: TEMPLATE

     map: null
+    userLayer: null
     scoreboard: null

     elements: $.extend
@@ -36,10 +39,16 @@
     userChanged: =>
       super

+      @map.removeLayer @userLayer if @userLayer?
+
       if User.current?
         @usernameContainer.html User.current.name
         @scoreboard.update()

+        query = "SELECT * FROM #{Project.current.cartoTable} WHERE user_id='#{User.current.id}'"
+        url = "http://#{Project.current.cartoUser}.cartodb.com/tiles/#{Project.current.cartoTable}/{z}/{x}/{y}.png?sql=#{query}"
+        @userLayer = @map.addLayer url
+
     favoriteTemplate: favoriteTemplate

   module.exports = Profile


fix icons not changing properly

fix icons not changing properly

--- jsapp/xlform/src/view.row.coffee
+++ jsapp/xlform/src/view.row.coffee
@@ -359,8 +359,8 @@
       iconDef = $icons.get(@getRawType())
       if (iconDef and isRowLocked(@ngScope.rawSurvey, @getRowName()))
         $indicatorIcon = @$('.card__indicator__icon .k-icon')
-        $indicatorIcon.toggleClass(iconDef.get("iconClassName"))
-        $indicatorIcon.toggleClass(iconDef.get("iconClassNameLocked"))
+        $indicatorIcon.removeClass(iconDef.get("iconClassName"))
+        $indicatorIcon.addClass(iconDef.get("iconClassNameLocked"))

       # disable adding new question options
       if (@isLockable() and @hasRestriction(LOCKING_RESTRICTIONS.choice_add.name))


отображается расстояние до места на карте

--- app/assets/javascripts/map.coffee
+++ app/assets/javascripts/map.coffee
@@ -1,11 +1,13 @@
 $ ->
   map.init()
+  map.get_current_location()

 map =
   #параметры карты
   dom: document.getElementById("map-canvas")
+  current_location: new google.maps.LatLng(55.751667, 37.617778)
   options:
-    center: new google.maps.LatLng(55.751667, 37.617778),
+    center: @current_location,
     disableDefaultUI: true,
     zoom: 12

@@ -16,39 +18,51 @@
         current_location = new google.maps.LatLng(position.coords.latitude, position.coords.longitude)
         map.google_map.setCenter current_location

+        console.log(map.current_location)
+        map.current_location = current_location
+
+        console.log(map.current_location)
+
         current_loc_marker_options =
           position: current_location
           icon: 'assets/logo.jpg'
           map: map.google_map
         new google.maps.Marker current_loc_marker_options

-        map.get_directions(current_location)
+        places.objects.forEach(places.get_distance)
+        places.objects.forEach(places.render)
+
+        map.get_directions(map.current_location)
+
+
+        #TODO: сделать так, чтобы места появлялись на карте даже в случае отсутствия текущей геолокации

   get_directions: (start_point) ->
     #построение маршрута до ближайшего места, принимает объект карты и место начала
-    directionsService = new google.maps.DirectionsService()
-    directionsDisplay = new google.maps.DirectionsRenderer()
-    directionsDisplay.setMap(map.google_map)
+    directions_renderer_options =
+      map: map.google_map
+      suppressMarkers: true
+    directions_renderer = new google.maps.DirectionsRenderer(directions_renderer_options)

     end_point = new google.maps.LatLng(places.objects[0].lat, places.objects[0].lng)

-    direction_options =
-      origin: start_point,
-      destination: end_point,
+    directions_service_options =
+      origin: start_point
+      destination: end_point
       travelMode: google.maps.TravelMode.DRIVING
+    directions_service = new google.maps.DirectionsService()

-    directionsService.route(direction_options, (response, status) ->
+    directions_service.route(directions_service_options, (response, status) ->
       #в случае успеха запроса выводи маршрут
       if (status == google.maps.DirectionsStatus.OK)
-        directionsDisplay.setDirections(response)
+        directions_renderer.setDirections(response)
     )

   init: ->
     #запуск карты, центрование на текущем месте и отображение всех мест
     map.google_map = new google.maps.Map(@.dom, @.options)
-    @.get_current_location()

-    places.objects.forEach(places.render)
+

 places =
   #параметры мест
@@ -75,7 +89,7 @@
     marker = new google.maps.Marker(place_marker_options)

     place_infobox = new InfoBox(infobox_options)
-    infobox_content = "" + place.type + ": " + place.name
+    infobox_content = "" + place.type + ": " + place.name + " расстояние: " + place.distance

     google.maps.event.addListener marker, 'mouseover', ->
       place_infobox.setContent(infobox_content)
@@ -83,6 +97,25 @@

     google.maps.event.addListener marker, 'mouseout', ->
       place_infobox.close()
+
+
+  rad:(x) ->
+    #перевод в радианы
+    x * Math.PI / 180
+
+  get_distance:(place) ->
+    cur_loc_lat = map.current_location.lat()
+    cur_loc_lng = map.current_location.lng()
+    earth_radius = 6371
+    place_lat = place.lat
+    place_lng = place.lng
+
+    dLat = places.rad(place_lat - cur_loc_lat)
+    dLong = places.rad(place_lng - cur_loc_lng)
+    a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(places.rad(cur_loc_lat)) * Math.cos(places.rad(cur_loc_lat)) * Math.sin(dLong / 2) * Math.sin(dLong / 2)
+    c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
+    place.distance = earth_radius * c
+

 infobox_options =
   #параметры отображения окна инфы при ховере поместу


Changed projectId to projectSlug

Changed projectId to projectSlug

--- app/coffee/directives/wiki.coffee
+++ app/coffee/directives/wiki.coffee
@@ -146,7 +146,7 @@
         if tree[0] == "link"
             if tree[1].href == _.string.slugify(tree[1].href)
                 # It's an internal link to a wiki page
-                tree[1].href = scope.urls.wikiUrl(scope.projectId, tree[1].href)
+                tree[1].href = scope.urls.wikiUrl(scope.projectSlug, tree[1].href)
             return null

         for t in tree


Better constantize

Better constantize

--- test/core_object.coffee
+++ test/core_object.coffee
@@ -70,5 +70,7 @@
     test: "my_val"

   o.include "TestM"
+
+  equals o.test(), "my_val"

 module undefined


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/core_object.coffee
+++ test/core_object.coffee
@@ -43,7 +43,18 @@
   equals o.test2, "cool"

 test "#include multiple", ->
-
+    o = Core
+    test: "my_val"
+
+  M = (I, self) ->
+    self.attrReader "test"
+
+    test2: "cool"
+
+  o.include M
+
+  equals o.test(), "my_val"
+  equals o.test2, "cool"

 module()


Update messages to include an app name.

Update messages to include an app name.

--- scripts/crashlytics-notifications.coffee
+++ scripts/crashlytics-notifications.coffee
@@ -24,8 +24,8 @@

   robot.router.post "/hubot/crashlytics-notify", (req, res) ->

-    @failing ||= []
     query = querystring.parse(url.parse(req.url).query)
+    app = query.app || null

     try
       data = req.body
@@ -38,10 +38,10 @@
         issue = data.payload

         if issue.impact_level == 1
-          robot.messageRoom '#publishmobileautomati', 'New issue in Crashlytics! See it at ' + issue.url
+          robot.messageRoom '#publishmobileautomati', 'New Crashlytics issue in ' + app + '! See it at ' + issue.url
         else
-          robot.messageRoom '#publishmobileautomati', 'Crashlytics issue ' + issue.display_id + ' upgraded to impact level' + issue.impact_level + '. Affects ' + issue.impacted_devices_count + ' users, ' + issue.crashes_count + ' times.'
-
+          robot.messageRoom '#publishmobileautomati', 'Crashlytics issue ' + issue.display_id + ' in ' + app + ' upgraded to impact level' + issue.impact_level + '. Affected ' + issue.impacted_devices_count + ' users ' + issue.crashes_count + ' times.'
+
         res.writeHead 204, { 'Content-Length': 0}

       res.end()


Show remove 'button' on header of open metadata select

--- app/assets/javascripts/rglossa/react/components/left/metadata_select.jsx.coffee
+++ app/assets/javascripts/rglossa/react/components/left/metadata_select.jsx.coffee
@@ -64,6 +64,8 @@
         'active-category': @state.isActive

       `
-
{this.props.category.name}

+

+          {this.props.category.name}{this.state.isActive ?    : null}
+

         <input ref="hidden" type="hidden" name={this.props.category.id} />
       `


trial big image mode

trial big image mode

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -11,6 +11,8 @@
 MarkingTool    = require './marking-tool'
 # MarkingToolControls = require './marking-tool-controls'
 InvertSvg      = require '../lib/invert-svg-image'
+
+BIG_MODE = !!~location.search.indexOf 'big=1'

 KEYS =
   space:  32
@@ -22,14 +24,14 @@
   four:   52

 DEV_SUBJECTS = [
-  './dev-subjects-images/01_12DEC02_N04066_0001-45-scaled.png'
-  './dev-subjects-images/01_12DEC02_N04066_0002-45-scaled.png'
-  './dev-subjects-images/01_12DEC02_N04066_0003-45-scaled.png'
-  './dev-subjects-images/01_12DEC02_N04066_0004-45-scaled.png'
-  # './dev-subjects-images/01_12DEC02_N04066_0001-50-scaled.png'
-  # './dev-subjects-images/01_12DEC02_N04066_0002-50-scaled.png'
-  # './dev-subjects-images/01_12DEC02_N04066_0003-50-scaled.png'
-  # './dev-subjects-images/01_12DEC02_N04066_0004-50-scaled.png'
+  # './dev-subjects-images/01_12DEC02_N04066_0001-45-scaled.png'
+  # './dev-subjects-images/01_12DEC02_N04066_0002-45-scaled.png'
+  # './dev-subjects-images/01_12DEC02_N04066_0003-45-scaled.png'
+  # './dev-subjects-images/01_12DEC02_N04066_0004-45-scaled.png'
+  './dev-subjects-images/01_12DEC02_N04066_0001-50-scaled.png'
+  './dev-subjects-images/01_12DEC02_N04066_0002-50-scaled.png'
+  './dev-subjects-images/01_12DEC02_N04066_0003-50-scaled.png'
+  './dev-subjects-images/01_12DEC02_N04066_0004-50-scaled.png'
   # './dev-subjects-images/01_12DEC02_N04066_0001-51-scaled.png'
   # './dev-subjects-images/01_12DEC02_N04066_0002-51-scaled.png'
   # './dev-subjects-images/01_12DEC02_N04066_0003-51-scaled.png'
@@ -171,6 +173,7 @@
     for i in [0...@numFrames]
       @markingSurfaceList[i] = new MarkingSurface
         tool: MarkingTool
+      @markingSurfaceList[i].svg.attr 'viewBox', '256 0 256 256' if BIG_MODE
       @markingSurfaceList[i].svgRoot.attr 'id', "classifier-svg-root-#{i}"
       @surfacesContainer.append @markingSurfaceList[i].el

@@ -194,7 +197,6 @@

     setTimeout =>
       @el.find('a, button, input, textarea, select').filter('section *:visible').first().focus()
-

   addGhostMark: (mark) ->
     svgElement = null
@@ -293,8 +295,8 @@
         @markingSurfaceList[i].addShape 'image',
         id:  frame_id
         class:  'frame-image'
-        width:  '100%'
-        height: '100%'
+        width:  '512'
+        height: '512'
         preserveAspectRatio: 'true'

       img_src = if @invert then subject_info.inverted[i] else subject_info.standard[i]


toggled the tutorial to be always done for now

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -23,8 +23,6 @@

   constructor: ->
     super
-
-    #debugger

     @subjectViewer = new SubjectViewer

@@ -101,7 +99,8 @@
     getEmptySubject().select()

   onUserSignIn: =>
-    tutorialDone = User.current?.project.tutorial_done
+    #tutorialDone = User.current?.project.tutorial_done
+    tutorialDone = true
     doingTutorial = Subject.current?.metadata.tutorial

     if tutorialDone


clear textarea on new subject

clear textarea on new subject

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -534,6 +534,8 @@
     @sendClassification()
     @canvasGraph.marks.destroyAll() #clear old marks
     @recordedClickEvents = []
+    @talkComment.val('')
+

     @sim_count ||= 0
     @sim_count +=1


remove code for supplemental-tutorial opt-out

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -129,9 +129,6 @@
   onChangeSupplementalOption: ->
     console.log 'onChangeSupplementalOption(): '
     return unless User.current?
-    # supplementalOption = User.current.preferences.planet_hunter.supplemental_option
-    # supplementalOption = not supplementalOption
-    # User.current?.setPreference 'supplemental_option', supplementalOption

   onChangeMiniCourseOption: ->
     console.log 'onChangeMiniCourseOption(): '
@@ -194,37 +191,30 @@
     # console.log 'SPLIT DESIGNATION: ', User.current.project.splits.mini_course_sup_tutorial
     if User.current?
       @handleSplitDesignation()
-
       if User.current.preferences?.planet_hunter?
         preferences = User.current.preferences.planet_hunter
         if +preferences.count is 0
-          # HANDLE SUPPLEMENTAL TUTORIAL SPLITS
           if @splitDesignation in ['a', 'b', 'c', 'g', 'h', 'i']
             @courseOptionIsChecked = false
-            # supplementalOption = true
           else if @splitDesignation in ['d', 'e', 'f', 'j', 'k', 'l']
             @courseOptionIsChecked = true
-            # supplementalOption = false
-
-        # handle first-time users
-        if +preferences.count is 0 or not User.current?
-          console.log 'First-time user. Loading tutorial...'
-          # @onClickTutorial()
-          @launchTutorial()
-
-      # User.current?.setPreference 'supplemental_option', supplementalOption

       if @courseOptionIsChecked
         User.current?.setPreference 'course', 'yes'
       else
         User.current?.setPreference 'course', 'no'

+    # handle first-time users
+    if +preferences?.count? is 0 or not User.current?
+      console.log 'First-time user. Loading tutorial...'
+      # @onClickTutorial()
+      @launchTutorial()
+
     Subject.next() unless @classification?

   handleSplitDesignation: ->
     console.log 'handleSplitDesignation()'
     @splitDesignation = User.current.project.splits.mini_course_sup_tutorial
-    # supplementalOption = User.current.preferences.planet_hunter.supplemental_option?
     @splitDesignation = 'd' # DEBUG CODE

     # HANDLE MINI-COURSE SPLITS
@@ -550,26 +540,12 @@

     # display supplemental tutorial
     for classification_count in @whenToDisplayTips
-      # if User.current.preferences.planet_hunter.supplemental_option and @course.count is classification_count
       if @course.count is classification_count
         console.log "*** DISPLAY SUPPLEMENTAL TUTOTIAL # #{classification_count} *** "
         @supplementalTutorial.first = "displayOn_" + classification_count.toString()
         @supplementalTutorial.start()

         if @course.count is 7
-
-          # if @allowCustomCourseInterval
-          #   newElement = document.createElement('div')
-          #   newElement.setAttribute 'class', "custom-interval-setter"
-          #   newElement.setAttribute 'style', "padding-top: 20px;"
-          #   newElement.innerHTML = """
-          #

-          #
Launch mini-course every

-          #       <input type="number" id="course-interval-sup-tut" name="course-interval-sup-tut" value="5"></input>
-          #
 classifications!

-          #

-          #   """
-          #   @supplementalTutorial.container.getElementsByClassName('zootorial-content')[0].appendChild(newElement)

           newElement = document.createElement('div')
           newElement.setAttribute 'class', "supplemental-tutorial-option-container"


Only have one task expanded at a time

Only have one task expanded at a time

--- app/controllers/tasks.item.coffee
+++ app/controllers/tasks.item.coffee
@@ -66,6 +66,7 @@

   expand: (e) ->
     if e.target.className isnt "checkbox"
+      @el.parent().find(".expanded").removeClass("expanded")
       @el.addClass("expanded animout")
       @el.draggable({ disabled: true })


Filter regex is case insensitive.

Filter regex is case insensitive.

--- picoCTF-web/web/coffee/management-problems.coffee
+++ picoCTF-web/web/coffee/management-problems.coffee
@@ -359,7 +359,7 @@

   onFilterChange: (filter) ->
     try
-      newFilter = new RegExp(filter)
+      newFilter = new RegExp filter, "i"
       @setState update @state,
         filterRegex: $set: newFilter
     catch


Filter respects problemClassifier.

Filter respects problemClassifier.

--- picoCTF-web/web/coffee/management-problems.coffee
+++ picoCTF-web/web/coffee/management-problems.coffee
@@ -66,7 +66,9 @@

 ProblemClassifier = React.createClass
   render: ->
-    <div/>
+    <Panel header="Category" defaultExpanded collapsible>
+      test
+    </Panel>

 Problem = React.createClass
   getInitialState: ->
@@ -150,7 +152,7 @@

   filterProblems: (problems) ->
     visibleProblems = _.filter problems, ((problem) ->
-      @state.filterRegex.exec problem.name
+      (@state.filterRegex.exec problem.name) != null and @state.problemClassifier problem
     ).bind this

     sortedProblems = _.sortBy visibleProblems, @state.activeSort.name


Really remove the commandCancel callback

--- app/javascripts/cs_console.coffee
+++ app/javascripts/cs_console.coffee
@@ -223,7 +223,7 @@
   # Assign callbacks to instance variables
   initCallbacks: (options)=>
     @commandValidate = options.commandValidate
-    @commandHandle = options.commandHandle
+    @commandHandle = options.commandHandle

   # submit the corrent input, store the input in the command history
   submit: =>


Use buffer coordinates in TokenizedBuffer::scopeDescriptorForPosition

Fixes #7073

--- src/tokenized-buffer.coffee
+++ src/tokenized-buffer.coffee
@@ -405,7 +405,7 @@

     iterator = @tokenizedLines[row].getTokenIterator()
     while iterator.next()
-      if iterator.getScreenEnd() > column
+      if iterator.getBufferEnd() > column
         scopes = iterator.getScopes()
         break


fix #62

fix #62

--- app/js/services/blockchain.coffee
+++ app/js/services/blockchain.coffee
@@ -156,11 +156,15 @@
                 return def.promise
             if first_block + blocks_to_fetch > head_block
                 blocks_to_fetch = head_block - first_block
+
+            block_nums = []
+            block_nums.push [i] for i in [first_block...first_block-blocks_to_fetch]
             requests =
                 blocks: @blockchain_api.list_blocks(first_block, blocks_to_fetch)
-                signers: @rpc.request("batch", ["blockchain_get_block_signee", [i] for i in [first_block...first_block+blocks_to_fetch]])
-                missed: @rpc.request("batch", ["blockchain_list_missing_block_delegates", [i] for i in [first_block...first_block+blocks_to_fetch]])
+                signers: @rpc.request("batch", ["blockchain_get_block_signee", block_nums])
+                missed: @rpc.request("batch", ["blockchain_list_missing_block_delegates", block_nums])
                 config: @get_info()
+
             @q.all(requests).then (results) =>
                 blocks = results.blocks
                 missed = results.missed.result