linked_heatmaps: Switch colors of N/K

linked_heatmaps: Switch colors of N/K

--- linked_heatmaps/linked_heatmaps.coffee
+++ linked_heatmaps/linked_heatmaps.coffee
@@ -151,7 +151,7 @@
                .attr("y", -pad.top*0.75)
                .attr("text-anchor", "middle")
                .attr("dominant-baseline", "middle")
-               .attr("fill", [darkBlue, darkRed][j])
+               .attr("fill", [darkRed, darkBlue][j])

     for j in [2..3]
       panels[i][j].append("text").attr("id", "mptitle#{i}#{j}")
@@ -191,13 +191,13 @@
       pcurves.append("path")
              .datum(data.p)
              .attr("d", pCurve(mindex, chr, data.nll))
-             .attr("stroke", darkBlue)
+             .attr("stroke", darkRed)
              .attr("fill", "none")
              .attr("stroke-width", "2")
       pcurves.append("path")
              .datum(data.p)
              .attr("d", pCurve(mindex, chr, data.kll))
-             .attr("stroke", darkRed)
+             .attr("stroke", darkBlue)
              .attr("fill", "none")
              .attr("stroke-width", "2")

@@ -205,13 +205,13 @@
       mcurves.append("path")
              .datum(data.m[0...data.nll[chr].length])
              .attr("d", mCurve(pindex, chr, data.nll))
-             .attr("stroke", darkBlue)
+             .attr("stroke", darkRed)
              .attr("fill", "none")
              .attr("stroke-width", "2")
       mcurves.append("path")
              .datum(data.m[0...data.nll[chr].length])
              .attr("d", mCurve(pindex, chr, data.kll))
-             .attr("stroke", darkRed)
+             .attr("stroke", darkBlue)
              .attr("fill", "none")
              .attr("stroke-width", "2")


Added logout route

Added logout route

--- tunemachine/routes/auth.coffee
+++ tunemachine/routes/auth.coffee
@@ -53,3 +53,9 @@
                       return models.err res, err

           res.redirect config.domain
+
+  app.get '/auth/logout', (req, res) ->
+    delete req.session.user_id
+    delete req.session.access_token
+
+    res.redirect config.domain


leading 0s for number to hex method

leading 0s for number to hex method

[0,255,255] would have made '#0ffff'

--- components/ColorArrayToCss.coffee
+++ components/ColorArrayToCss.coffee
@@ -2,13 +2,18 @@

 exports.getComponent = ->
   c = new noflo.Component
+
+  hex = (n) ->
+    s = n.toString(16)
+    if s.length is 1
+      s = "0"+s
+    return s
+
   c.inPorts.add 'in', (event, payload) ->
     if event is 'disconnect'
       c.outPorts.out.disconnect()

     return unless event is 'data'
-    hex = (n) ->
-      return n.toString(16)
     rgb = payload
     out = "#"+hex(rgb[0])+hex(rgb[1])+hex(rgb[2])
     c.outPorts.out.send out


Set main graph for iframe runtimes

Set main graph for iframe runtimes

--- components/OpenProjectMode.coffee
+++ components/OpenProjectMode.coffee
@@ -79,6 +79,14 @@
         sendGraphs client, route.project.graphs, route.graphs
       )
       .then(() ->
+        unless client.definition.protocol is 'iframe'
+          return Promise.resolve()
+        unless route.graphs?.length
+          return Promise.resolve()
+        client.transport.setMain(route.graphs[0])
+        return Promise.resolve()
+      )
+      .then(() ->
         getRemoteNodes client, route
       )
       .then(() ->


Test for set_cookie/get_cookie.

Test for set_cookie/get_cookie.

--HG--
branch : qunit

--- src/js/collection-editor-tests.coffee
+++ src/js/collection-editor-tests.coffee
@@ -9,6 +9,11 @@
 test "URN construction", ->
   equal( cite_urn('namespace','collection','row'), 'urn:cite:namespace:collection.row' )
   equal( cite_urn('namespace','collection','row','version'), 'urn:cite:namespace:collection.row.version' )
+
+test "values set by set_cookie should be readable by get_cookie", ->
+  set_cookie 'cookie_test', 'test value', 60
+  equal( get_cookie('cookie_test'), 'test value' )
+  set_cookie 'cookie_test', 'delete', -1

 module "access token cookies",
   setup: ->


Use default gl account

Use default gl account

--- client/skr/models/Sku.coffee
+++ client/skr/models/Sku.coffee
@@ -4,7 +4,9 @@
     props:
         id:                  {"type":"integer","required":true}
         default_vendor_id:   {"type":"integer","required":true}
-        gl_asset_account_id: {"type":"integer","required":true}
+        gl_asset_account_id: {"type":"integer","required":true, default: ->
+            Skr.Models.GlAccount.default_ids.asset
+        }
         default_uom_code:    {"type":"string","required":true}
         code:                {"type":"string","required":true}
         description:         {"type":"string","required":true}
@@ -14,7 +16,9 @@

     associations:
         default_vendor:   { model: "Vendor" }
-        gl_asset_account: { model: "GlAccount" }
+        gl_asset_account: { model: "GlAccount", default: ->
+            Skr.Models.GlAccount.fetchById(this.gl_asset_account_id)
+        }
         sku_locs:         { collection: "SkuLoc" }
         sku_vendors:      { collection: "SkuVendor" }
         uoms:             { collection: "Uom" }


Update git-remove

Update git-remove

--- lib/models/git-remove.coffee
+++ lib/models/git-remove.coffee
@@ -7,7 +7,7 @@

   if currentFile? and not showSelector
     if window.confirm 'Are you sure?'
-      atom.workspaceView.getActiveView().remove()
+      atom.workspace.getActivePaneItem().destroy()
       git.cmd
         args: ['rm', '-f', '--ignore-unmatch', currentFile],
         stdout: (data) ->  new StatusView(type: 'success', message: "Removed #{prettify data}")


Filter MSI-less distributions

Filter MSI-less distributions

--- src/tools/remotes.coffee
+++ src/tools/remotes.coffee
@@ -14,7 +14,7 @@
       echo "Fetching <#{url}>..."
       fs.CreateTextFile f, true
       .WriteLine tab = ajax.get url
-    tab = for z in tsv tab
+    tab = for z in tsv tab when msi z
       parse z, k
     r = r.concat tab
   r.sort semver.cmpi
@@ -29,3 +29,6 @@
   id: [semver, [dist]]
   dist: dist
   src: line
+
+msi = (line)->
+  ~line.files.indexOf 'msi'


clean up code

clean up code

--- lib/notifications-log.coffee
+++ lib/notifications-log.coffee
@@ -22,16 +22,12 @@
     })

   render: ->
-
-    # Create root element
     @element = document.createElement('div')
     @element.classList.add('notifications-log')

-    # Add Header
     header = document.createElement('header')
     @element.appendChild(header)

-    # Add Buttons
     for type, icon of typeIcons
       button = document.createElement('button')
       button.classList.add('notification-type', 'btn', 'icon', "icon-#{icon}", 'show-type', type)
@@ -40,12 +36,10 @@
       @disposables.add atom.tooltips.add(button, {title: "Toggle #{type} notifications"})
       header.appendChild(button)

-    # Add Container
     @list = document.createElement('ul')
     @list.classList.add('notifications-log-items')
     @element.appendChild(@list)

-    # Add Notifications
     lastNotification = null
     for notification in atom.notifications.getNotifications()
       if lastNotification?


Refactor ReagAggregateRepository

Refactor ReagAggregateRepository

--- src/read_aggregate_repository.coffee
+++ src/read_aggregate_repository.coffee
@@ -25,24 +25,17 @@
     @findById id for id in ids

-  find: (query, projection) ->
-    # find ReadAggregates based on query and projection
-
+  find: (query) ->
     # get AggregateIds first
-    aggregateIds = @_findAggregateIdsByDomainEventCriteria query, projection
+    aggregateIds = @findIds query

     # now fetch all ReadAggregates matching the AggregateIds and return as array
     @findById aggregateId for aggregateId in aggregateIds

   findIds: (query) ->
-    # only return aggregateIds
-    projection =
-      _id: 0
-      aggregateId: 1
-
     # ask the adapter to find the ids and return them
-    aggregateIds = @_findAggregateIdsByDomainEventCriteria query, projection
+    aggregateIds = @_findAggregateIdsByDomainEventCriteria query

 module.exports = ReadAggregateRepository


Avoid using atom global in registerDefaultCommands

--- src/register-default-commands.coffee
+++ src/register-default-commands.coffee
@@ -115,7 +115,7 @@
     'editor:select-line': -> @selectLinesContainingCursors()
   )

-  atom.commands.add 'atom-text-editor', stopEventPropagationAndGroupUndo(
+  atom.commands.add 'atom-text-editor', stopEventPropagationAndGroupUndo(atom.config,
     'core:backspace': -> @backspace()
     'core:delete': -> @delete()
     'core:cut': -> @cutSelectedText()
@@ -175,7 +175,7 @@
     'editor:scroll-to-cursor': -> @scrollToCursorPosition()
   )

-  atom.commands.add 'atom-text-editor:not([mini])', stopEventPropagationAndGroupUndo(
+  atom.commands.add 'atom-text-editor:not([mini])', stopEventPropagationAndGroupUndo(atom.config,
     'editor:indent': -> @indent()
     'editor:auto-indent': -> @autoIndentSelectedRows()
     'editor:indent-selected-rows': -> @indentSelectedRows()
@@ -200,13 +200,13 @@
         commandListener.call(@getModel(), event)
   newCommandListeners

-stopEventPropagationAndGroupUndo = (commandListeners) ->
+stopEventPropagationAndGroupUndo = (config, commandListeners) ->
   newCommandListeners = {}
   for commandName, commandListener of commandListeners
     do (commandListener) ->
       newCommandListeners[commandName] = (event) ->
         event.stopPropagation()
         model = @getModel()
-        model.transact atom.config.get('editor.undoGroupingInterval'), ->
+        model.transact config.get('editor.undoGroupingInterval'), ->
           commandListener.call(model, event)
   newCommandListeners


replace filter done

replace filter done

--- chrome-app/directives/image-replacer.coffee
+++ chrome-app/directives/image-replacer.coffee
@@ -9,7 +9,6 @@
     xhr.open 'GET', image.getAttribute('data-src'), true
     xhr.responseType = 'blob'
     xhr.onload = (e) ->
-      console.log 'here'
       image.src = window.URL.createObjectURL this.response
     xhr.send()

@@ -17,13 +16,11 @@

   scope:
     content: '=ffImageReplacer'
-  link: ($scope, $element, $attrs) ->
-    console.log(1)
-    console.log $scope
-
-    unregister = $scope.$watch 'content', ->
-      console.log(2)
-      console.log $scope
-      replace($element)
+  link:
+    pre: () ->
+      console.log 'before render'
+    post: ($scope, $element, $attrs) ->
+      $scope.$watch 'content', ->
+        replace($element)


new external submit case

new external submit case

--- demo/app.coffee
+++ demo/app.coffee
@@ -7,6 +7,24 @@
 ##

 angular.module('app', ['CoolForm', 'AnotherDemoModule'])
+
+angular.module('app').controller('ctrl', ($scope) ->
+    $scope.before = (data) ->
+      console.log data
+      confirm("Do you want to send?")
+
+    $scope.success = (result, errors) ->
+      console.log "success"
+
+    $scope.error = (validate, errors) ->
+      console.log "error"
+
+    $scope.doSend = ->
+      if $scope.sendCb? then $scope.sendCb()
+
+    $scope.send = () ->
+      console.log "pas cool"
+)

 angular.module('DemoModule', []).factory('demoFactory', ->
   validator = (name, fields, options) ->


use containerselector

use containerselector

--- core/app/backbone/views/fact_relations_view.coffee
+++ core/app/backbone/views/fact_relations_view.coffee
@@ -2,6 +2,9 @@
   tagName: "div"
   className: "page evidence-list fact-relations-container"
   template: "fact_relations/fact_relations"
+
+  containerSelector: "ul.evidence-listing"
+
   initialize: (options) ->
     @_views = []
     @collection.bind "add", @addFactRelation, this
@@ -14,7 +17,7 @@
       type: @options.type

   highlightFactRelation: (view) ->
-    $("ul.evidence-listing", @el).scrollTo view.el, 800
+    @$(@containerSelector).scrollTo view.el, 800
     view.highlight()

   addFactRelation: (factRelation, factRelations, options = {}) ->
@@ -22,7 +25,7 @@
     @closeEmptyView()
     @_views.push factRelationView
     factRelationView.render()
-    @$el.find("ul.evidence-listing").append factRelationView.el
+    @$(@containerSelector).append factRelationView.el
     if options.highlight
       @highlightFactRelation factRelationView


hide replace part for find mode

hide replace part for find mode

--- client/ace/lib/acefindandreplaceview.coffee
+++ client/ace/lib/acefindandreplaceview.coffee
@@ -94,7 +94,18 @@

     @$().css { height }
     @resizeEditor height
+    @toggleReplacePart(isReplaceMode)
     @show()
+
+
+  toggleReplacePart: (visible) ->
+
+    toggle = (elem, visible) -> if visible then elem.show() else elem.hide()
+
+    toggle(@replaceInput, visible)
+    toggle(@replaceButton, visible)
+    toggle(@replaceAllButton, visible)
+

   resizeEditor: (height) ->
     if @getDelegate()?.ace?


SliderComponent: Include the fill height in updateFrame and set fill borderRadius outside of constructor (avoid init override)

--- framer/Components/SliderComponent.coffee
+++ framer/Components/SliderComponent.coffee
@@ -33,14 +33,15 @@

 		@fill = new Layer
 			backgroundColor: "#333"
-			width: 0, borderRadius: 50
-			force2d: true
+			width: 0, force2d: true

 		super options

 		@knobSize = options.knobSize or 30
 		@knob.superLayer = @fill.superLayer = @
+
 		@fill.height = @height
+		@fill.borderRadius = @borderRadius

 		@knob.draggable.enabled = true
 		@knob.draggable.speedY = 0
@@ -87,6 +88,7 @@
 			# y: -@knob.height / 2
 			# height: @height + @knob.height

+		@fill.height = @height
 		@knob.centerY()

 	_setRadius: =>


stack-editor: disable save button

stack-editor: disable save button

--- client/stack-editor/lib/editor/index.coffee
+++ client/stack-editor/lib/editor/index.coffee
@@ -293,9 +293,11 @@
         appManager.tell 'Stacks', 'exitFullscreen'  unless @getOption 'skipFullscreen'
         @handleGenerateStack()

+    saveButtonCssClass = unless @isMine then ' isntMine' else ''
+    cssClass = "GenericButton save-test#{saveButtonCssClass}"
     @buttons.addSubView @saveButton = new kd.ButtonView
       title          : 'SAVE'
-      cssClass       : 'GenericButton save-test'
+      cssClass       : cssClass
       loader         : yes
       callback       : =>
         appManager.tell 'Stacks', 'exitFullscreen'  unless @getOption 'skipFullscreen'


Tests: Fixes for teams test.

Tests: Fixes for teams test.

--- client/test/lib/helpers/teamshelpers.coffee
+++ client/test/lib/helpers/teamshelpers.coffee
@@ -366,6 +366,7 @@
     switch messageType
       when 'messageWithCode'
         browser
+          .pause                  3000
           .assert.containsText    "#{textAreaSelector} .ChatItem .SimpleChatListItem", messageWithShortCode
       when 'messageWithImage'
         browser
@@ -378,8 +379,9 @@
       when 'messageWithEmoji'
         browser
           .waitForElementVisible  emojiSmileySelector, 20000
-          .assert.elementPresent  emojiSmileySelector
-          .assert.elementPresent  emojiThumbsUpSelector
+          .waitForElementVisible  emojiSmileySelector, 20000
+          .pause                  2000
+          .waitForElementVisible  emojiThumbsUpSelector, 20000

   createChannelsAndCheckList: (browser, user) ->


whoops, update the other urls in the config.

--- config/main.sjc-production.coffee
+++ config/main.sjc-production.coffee
@@ -226,7 +226,7 @@
     certFile    : "/opt/ssl_certs/wildcard.koding.com.cert"
     keyFile     : "/opt/ssl_certs/wildcard.koding.com.key"
     webProtocol : 'https:'
-    webHostname : "broker-#{version}a.koding.com"
+    webHostname : "broker.koding.com"
     webPort     : null
     authExchange: authExchange
     authAllExchange: authAllExchange
@@ -237,7 +237,7 @@
     certFile    : "/opt/ssl_certs/wildcard.koding.com.cert"
     keyFile     : "/opt/ssl_certs/wildcard.koding.com.key"
     webProtocol : 'https:'
-    webHostname : "brokerkite-#{version}a.koding.com"
+    webHostname : "brokerkite.koding.com"
     webPort     : null
     authExchange: authExchange
     authAllExchange: authAllExchange


fixed placeholder and selected bugs

fixed placeholder and selected bugs

--- lib/select2.component.coffee
+++ lib/select2.component.coffee
@@ -4,13 +4,10 @@
   defaults =
     minimumResultsForSearch: "5"
     width: "100%"
-  $( '#'+@data.id ).select2 _.defaults @data.options, defaults
-  enabled = @data.options.disabled or true
-  readonly = @data.options.readonly or false
-  selected = [] unless _.isArray @data.selected and @data.selected.length
-  $( '#'+@data.id ).select2 "enable", enabled
-  $( '#'+@data.id ).select2 "readonly", readonly
-  $( '#'+@data.id ).select2 "val", selected
+  $select2 = $( 'select#'+@data.id ).select2 _.defaults @data.options, defaults
+  $select2.select2 "enable", @data.options.disabled if @data.options.disabled
+  $select2.select2 "readonly", @data.options.readonly if @data.options.readonly
+  $select2.select2 "val", @data.selected if @data.selected

 Template.Select2.destroyed = ->
   $('body .select2-hidden-accessible').remove()


Detangle v1 / v0 lifecycle callbacks

Detangle v1 / v0 lifecycle callbacks

--- src/trix/core/helpers/custom_elements.coffee
+++ src/trix/core/helpers/custom_elements.coffee
@@ -21,38 +21,43 @@
   document.head.insertBefore(element, document.head.firstChild)
   element

-rewriteLifecycleCallbacks = (definition) ->
-  result = Trix.copyObject(definition)
-
-  for key, value of lifecycleMap
-    if callback = result[key]
-      result[value] = callback
-      delete result[key]
-
-  # Call `initialize` once in `connectedCallback` if defined
-  if result.initialize
-    {connectedCallback} = result
-    result.connectedCallback = ->
-      @initialize?()
-      @initialize = null
-      connectedCallback?.call(this)
-
-  result
-
 rewriteFunctionsAsValues = (definition) ->
   object = {}
   for key, value of definition
     object[key] = if typeof value is "function" then {value, writable: true} else value
   object

-lifecycleMap = do ->
+rewriteLifecycleCallbacks = do ->
+  extract = (definition) ->
+    callbacks = {}
+    for key in ["initialize", "connect", "disconnect"]
+      callbacks[key] = definition[key]
+      delete definition[key]
+    callbacks
+
   if window.customElements
-    connect: "connectedCallback"
-    disconnect: "disconnectedCallback"
+    (definition) ->
+      {initialize, connect, disconnect} = extract(definition)
+
+      # Call `initialize` once in `connectedCallback` if defined
+      if initialize or connect
+        original = connect
+        connect = ->
+          @initialize?()
+          @initialize = null
+          original?.call(this)
+
+      definition.initialize = initialize if initialize
+      definition.connectedCallback = connect if connect
+      definition.disconnectedCallback = disconnect if disconnect
+      definition
   else
-    initialize: "createdCallback"
-    connect: "attachedCallback"
-    disconnect: "detachedCallback"
+    (definition) ->
+      {initialize, connect, disconnect} = extract(definition)
+      definition.createdCallback = initialize if initialize
+      definition.attachedCallback = connect if connect
+      definition.detachedCallback = disconnect if disconnect
+      definition

 registerElement = do ->
   if window.customElements


persist translation on sw viewer

persist translation on sw viewer

--- src/ubret/spacewarp_viewer/initialize.coffee
+++ src/ubret/spacewarp_viewer/initialize.coffee
@@ -98,8 +98,6 @@
     )

   getNextSubject: =>
-    console.log 'getNextSubject'
-
     @requestChannels()

   # Request FITS files for each channel
@@ -132,6 +130,7 @@

           # Get image data
           dataunit.getFrameAsync(0, (arr) =>
+
             # Compute extent
             [min, max] = dataunit.getExtent(arr)

@@ -147,12 +146,16 @@

   # Initialize a WebFITS object
   initWebFITS: =>
-    @wfits.teardown() if @wfits?
+    @wfits?.teardown()

     @wfits = new astro.WebFITS(@el, @dimension)
-
+
     unless @wfits.ctx?
       alert 'Something went wrong initializing the context'
+
+    # Load offsets if they exists
+    @wfits.xOffset = @opts.xOffset or @wfits.xOffset
+    @wfits.yOffset = @opts.yOffset or @wfits.yOffset

   # Call when all FITS received and WebFITS library is received
   allChannelsReceived: (e) =>
@@ -163,16 +166,20 @@

     @wfits.setCalibrations(1, 1, 1)
     @wfits.setScales.apply(@wfits, @defaultScales)
-    @wfits.setAlpha(@defaultAlpha)
-    @wfits.setQ(@defaultQ)
+    @wfits.setAlpha(@opts.alpha or @defaultAlpha)
+    @wfits.setQ(@opts.q or @defaultQ)
+
+    # Enable mouse controls
+    @wfits.setupControls({
+      onmousemove: (e) =>
+        @settings({
+          xOffset: @wfits.getXOffset()
+          yOffset: @wfits.getYOffset()
+        })
+    })

     # Default to color composite
     @wfits.drawColor('i', 'r', 'g')
-
-    # Enable mouse controls
-    @wfits.setupControls()
-
-    @trigger 'swviewer:loaded'

   start: =>
     console.log 'start'
@@ -195,10 +202,10 @@
       @wfits.setStretch(@stretch)

   updateAlpha: =>
-    @wfits.setAlpha(@opts.alpha)
+    @wfits?.setAlpha(@opts.alpha)

   updateQ: =>
-    @wfits.setQ(@opts.q)
+    @wfits?.setQ(@opts.q)

   updateScale: =>
     band = @opts.scale.band


bongo server "connected" event

bongo server "connected" event

--- components/list/KDListViewController.coffee
+++ components/list/KDListViewController.coffee
@@ -226,8 +226,6 @@
   # change the method name during refactoring - Sinan 10 May 2012
   selectItem:(item, event = {})->
     return unless item?
-
-    console.trace()

     @lastEvent = event
     @deselectAllItems() unless event.metaKey or event.ctrlKey or event.shiftKey


Add support for project-level rules

Add support for project-level rules

--- src/commandline.coffee
+++ src/commandline.coffee
@@ -6,6 +6,7 @@
 ###

+resolve = require('resolve').sync
 path = require("path")
 fs   = require("fs")
 glob = require("glob")
@@ -279,12 +280,25 @@
 loadRules = (moduleName, ruleName = undefined) ->
     try
         try
-            ruleModule = require moduleName
-        catch e
-            # Maybe the user used a relative path from the command line. This
-            # doesn't make much sense from a config file, but seems natural
-            # with the --rules option.
-            ruleModule = require path.resolve(process.cwd(), moduleName)
+            # Try to find the project-level rule first.
+            rulePath = resolve moduleName, {
+                basedir: process.cwd()
+            }
+            ruleModule = require rulePath
+        try
+            # This seems awkward, but the ?= will prevent it from trying to
+            # require if the previous step succeeded without an exception.
+            #
+            # Globally installed rule
+            ruleModule ?= require moduleName
+
+        # Maybe the user used a relative path from the command line. This
+        # doesn't make much sense from a config file, but seems natural
+        # with the --rules option.
+        #
+        # No try around this one, an exception here should abort the rest of
+        # this function.
+        ruleModule ?= require path.resolve(process.cwd(), moduleName)

         # Most rules can export as a single constructor function
         if typeof ruleModule is 'function'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -151,6 +151,7 @@
     ###
     include: (modules...) ->
       for Module in modules
+        Module = Module
         self.extend Module(I, self)

       return self


getMerged: conservatively handle all cases


Account.Credentials: disable autocomplete in share field for now

--- client/Account/views/credentials.coffee
+++ client/Account/views/credentials.coffee
@@ -66,11 +66,11 @@
       fields            :
         username        :
           label         : "User"
-          type          : "hidden"
-          nextElement   :
-            userWrapper :
-              itemClass : KDView
-              cssClass  : "completed-items"
+          # type          : "hidden"
+          # nextElement   :
+          #   userWrapper :
+          #     itemClass : KDView
+          #     cssClass  : "completed-items"
         owner           :
           label         : "Give ownership"
           itemClass     : KodingSwitch
@@ -92,8 +92,8 @@

         log "Here we go", data

-        { usernames, owner } = data
-        target = usernames.first
+        { username, owner } = data
+        target = username#s.first

         unless target
           return new KDNotificationView
@@ -113,31 +113,31 @@

     {fields, inputs, buttons} = view.form

-    @userController       = new KDAutoCompleteController
-      form                : view.form
-      name                : "username"
-      itemClass           : MemberAutoCompleteItemView
-      itemDataPath        : "profile.nickname"
-      outputWrapper       : fields.userWrapper
-      selectedItemClass   : MemberAutoCompletedItemView
-      listWrapperCssClass : "users"
-      submitValuesAsText  : yes
-      dataSource          : (args, callback)=>
-        {inputValue} = args
-        if /^@/.test inputValue
-          query = 'profile.nickname': inputValue.replace /^@/, ''
-          KD.remote.api.JAccount.one query, (err, account)=>
-            if not account
-              @userController.showNoDataFound()
-            else
-              callback [account]
-        else
-          KD.remote.api.JAccount.byRelevance inputValue, {}, (err, accounts)->
-            callback accounts
-
-    fields.username.addSubView userRequestLineEdit = @userController.getView()
-    @userController.on "ItemListChanged", (count)->
-      userRequestLineEdit[if count is 0 then 'show' else 'hide']()
+    # @userController       = new KDAutoCompleteController
+    #   form                : view.form
+    #   name                : "username"
+    #   itemClass           : MemberAutoCompleteItemView
+    #   itemDataPath        : "profile.nickname"
+    #   outputWrapper       : fields.userWrapper
+    #   selectedItemClass   : MemberAutoCompletedItemView
+    #   listWrapperCssClass : "users"
+    #   submitValuesAsText  : yes
+    #   dataSource          : (args, callback)=>
+    #     {inputValue} = args
+    #     if /^@/.test inputValue
+    #       query = 'profile.nickname': inputValue.replace /^@/, ''
+    #       KD.remote.api.JAccount.one query, (err, account)=>
+    #         if not account
+    #           @userController.showNoDataFound()
+    #         else
+    #           callback [account]
+    #     else
+    #       KD.remote.api.JAccount.byRelevance inputValue, {}, (err, accounts)->
+    #         callback accounts
+
+    # fields.username.addSubView userRequestLineEdit = @userController.getView()
+    # @userController.on "ItemListChanged", (count)->
+    #   userRequestLineEdit[if count is 0 then 'show' else 'hide']()

     view.addSubView view.form


test: esthetic

test: esthetic


killall --> pkill (as suggested by singlet).

--- salvus/hub.coffee
+++ salvus/hub.coffee
@@ -2978,7 +2978,7 @@
     killall: (cb) =>
         winston.debug("kill all processes running on this local hub (including the local hub itself)")
         @_exec_on_local_hub
-            command : "killall -9 -u #{@username}"
+            command : "pkill -9 -u #{@username}"  # pkill is *WAY better* than killall (which evidently does not work in some cases)
             dot_sagemathcloud_path : false
             timeout : 30
             cb      : (err, out) =>


Add more comparison operators to grammar.

--- grammars/powershell.cson
+++ grammars/powershell.cson
@@ -119,7 +119,7 @@
     'match': '\\b(?i:(as|assert|del|exec|print|param))\\b'
   }
   {
-    'match': '-eq|-ceq|-ieq|-lt|-gt|-le|-ge|-ne|-notlike|-not'
+    'match': '-eq|-ceq|-ieq|-lt|-gt|-le|-ge|-ne|-notlike|-not|-like|-match|-notmatch|-contains|-notcontains|-in|-notin|-replace'
     'name': 'keyword.operator.comparison.powershell'
   }
   {


escape CDATA in script element

escape CDATA in script element


MAJOR UX bug -- if you copy and paste the cell start uuid line (the cell separate line), then the worksheet will have two cells with the same uuid, which causes all hell to break loose (and breaks everything).  I put code in to randomly regenerate repeat uuids.  Easy to fix.

--- salvus/local_hub.coffee
+++ salvus/local_hub.coffee
@@ -817,17 +817,27 @@
         opts = defaults opts,
             kill : false    # if true, just remove all running flags.
             auto : false    # if true, run all cells that have the auto flag set
-        # scan the string @content for execution requests, etc...
+        # Here we:
+        #    - scan the string @content for execution requests.
+        #    - also, if we see a cell UUID that we've seen already, we randomly generate
+        #      a new cell UUID; clients can annoyingly generate non-unique UUID's (e.g., via
+        #      cut and paste) so we fix that.
         winston.debug("sage_update: opts=#{misc.to_json(opts)}")
         i = 0
+        prev_ids = {}
         while true
             i = @content.indexOf(diffsync.MARKERS.cell, i)
             if i == -1
                 break
             j = @content.indexOf(diffsync.MARKERS.cell, i+1)
             if j == -1
-                break  # corrupt (TODO)
+                break  # corrupt and is the last one, so not a problem.
             id  = @content.slice(i+1,i+37)
+            if prev_ids[id]?
+                # oops, repeated "unique" id, so fix it.
+                id = uuid.v4()
+                @content = @content.slice(0,i+1) + id + @content.slice(i+37)
+            prev_ids[id] = true
             flags = @content.slice(i+37, j)
             if opts.kill
                 new_flags = ''


added warning

added warning

--- core/app/backbone/collections/messages.coffee
+++ core/app/backbone/collections/messages.coffee
@@ -3,6 +3,7 @@

   initialize: (models, options) -> @conversationId = options.conversation.id

+  # Warning: does not work for fetch
   url: -> "/c/#{@conversationId}/messages"

   createNew: (content, sender, options)->


Added functionality to support responses to "dud" and "sim" subjects specified using metadata in the manifest. Also provided functionality to validate the spatial click coordinates of a "sim" classification based on X, Y and tolerance values, also provided as metadata in a manifest.

--- app/classifier/index.cjsx
+++ app/classifier/index.cjsx
@@ -310,9 +310,45 @@

+  withinTolerance: (userX, userY, metaX, metaY, tolerance) ->
+    distance = Math.sqrt((userY - metaY)**2 + (userX - metaX)**2)
+    isWithinTolerance = distance < tolerance
+    return isWithinTolerance
+
   renderSummary: (classification) ->

       Thanks!
+
+      {
+        subjectClass = @props.subject.metadata['#Type']?.toUpperCase()
+        userMadeAnnotation = @props.classification.annotations?.length > 0 && @props.classification.annotations[0].value.length > 0
+        subjectSuccessMessage = @props.subject.metadata['#F_Success']
+        subjectFailureMessage = @props.subject.metadata['#F_Fail']
+        subjectXPos = parseFloat(@props.subject.metadata['#X1'])
+        subjectYPos = parseFloat(@props.subject.metadata['#Y1'])
+        subjectTol = parseFloat(@props.subject.metadata['#Tol1'])
+        userXPos = @props.classification.annotations[0]?.value[0]?.x
+        userYPos = @props.classification.annotations[0]?.value[0]?.y
+        isWithinTolerance = @withinTolerance(userXPos, userYPos, subjectXPos, subjectYPos, subjectTol)
+
+        console.log(subjectClass)
+        console.log(userMadeAnnotation)
+        console.log(@props.classification.annotations?.length)
+        console.log(@props.classification.annotations[0])
+
+        if subjectClass == 'DUD'
+          if userMadeAnnotation == true
+
{subjectFailureMessage}

+          else
+
{subjectSuccessMessage}

+        else if subjectClass == 'SIM'
+          if !(userMadeAnnotation?) || !isWithinTolerance
+
{subjectFailureMessage}

+          else
+
{subjectSuccessMessage}

+        else
+
You classified some real data!

+        }

       {if @props.workflow.configuration.custom_summary and 'world_wide_telescope' in @props.workflow.configuration.custom_summary


update route 'base': redirect random tag

--- routes/base.coffee
+++ routes/base.coffee
@@ -1,2 +1,7 @@
+_ = require 'lodash'
+
 module.exports = (req, res) ->
-  res.render 'base'+  tagIds = _.pluck(req.app.locals.tags, 'id')
+  tagIndex = _.random(0, tagIds.length - 1)
+  tagId = tagIds[tagIndex]
+  res.redirect "/tag/#{tagId}"


github-commit-link: Only show the first line of the commit

This is traditionally the message summary.  From Git's own docs [1]:

> The first line of the commit message should be a short description
> (50 characters is the soft limit, see DISCUSSION in git-commit(1)),
> and should skip the full stop.

I sometimes write long, detailed commit messages (like this one), and
there's no need to spam the channel with all of this motivational
background.  The summary line should be sufficient context there, and
folks looking for more information can follow the URL (which I've
moved to it's own line).

[1]: https://github.com/git/git/blob/v1.9.0/Documentation/SubmittingPatches#L89

--- src/scripts/github-commit-link.coffee
+++ src/scripts/github-commit-link.coffee
@@ -59,6 +59,6 @@
             url = commit_obj.url.replace(/api\./,'')
           url = url.replace(/repos\//,'')
           url = url.replace(/commits/,'commit')
-          msg.send "Commit: " + commit_obj.commit.message + " " + url
+          msg.send "Commit: " + commit_obj.commit.message.split("\n")[0] + "\n" + url
     else
       msg.send "Hey! You need to set HUBOT_GITHUB_REPO and HUBOT_GITHUB_TOKEN before I can link to that commit."


improve readability of mark validations, keep all marks inside range of data points

--- app/lib/canvas-graph.coffee
+++ app/lib/canvas-graph.coffee
@@ -160,8 +160,13 @@
     markRightX = Math.max @startingPoint, @toCanvasXPoint(e)

     # no overlapping of marks
-    markLeftX = (Math.max markLeftX, (@closestXBelow + @handleWidth() || markLeftX))
-    markRightX = (Math.min markRightX, (@closestXAbove - @handleWidth() || markRightX))
+    markLeftX = Math.max markLeftX,
+                        (@closestXBelow + @handleWidth() || markLeftX),
+                        (@canvasGraph.toCanvasXCoord(@canvasGraph.smallestX))
+
+    markRightX = Math.min markRightX,
+                         (@closestXAbove - @handleWidth() || markRightX),
+                         (@canvasGraph.toCanvasXCoord(@canvasGraph.largestX))

     # max and min width on creating / resizing marks
     width = (Math.min (Math.max (Math.abs markRightX - markLeftX), @minWidth()), @maxWidth())
@@ -175,8 +180,10 @@
     markWidth = parseInt(@element.style.width, 10)

     # no overlapping of marks or moving out of canvas bounds
-    leftXPos = Math.max (@toCanvasXPoint(e) - @pointerOffset), (@closestXBelow || -@handleWidth()) + @handleWidth()
-    leftXPos = Math.min leftXPos, ((@closestXAbove || @canvas.width + @handleWidth()) - markWidth) - @handleWidth()
+    leftXPos = Math.max (@toCanvasXPoint(e) - @pointerOffset),
+                        (@closestXBelow || -@handleWidth()) + @handleWidth()
+    leftXPos = Math.min leftXPos,
+                        ((@closestXAbove || @canvas.width + @handleWidth()) - markWidth) - @handleWidth()

     markRightX = leftXPos + markWidth


Use more relaxed closure defaults

Use more relaxed closure defaults

--- lib/engines/closure-compiler.coffee
+++ lib/engines/closure-compiler.coffee
@@ -2,8 +2,7 @@

 module.exports = class ClosureCompiler extends (require './engine')
   defaults:
-    compilation_level: 'advanced_optimizations'
-    language: 'ecmascript5'
+    compilation_level: 'SIMPLE_OPTIMIZATIONS'

   compress: (str, cb) ->
     try


fixes integration test app by adding snapshot EJSON types

--- tests/test_application.coffee
+++ tests/test_application.coffee
@@ -79,6 +79,8 @@
     'CustomerApp.CustomerCreated': (event) -> @name = event.customerName
   }

+CustomerApp.Customer.registerSnapshotType 'CustomerApp.CustomerSnapshot'
+
 # -------------- PROCESSES ---------------

 class CustomerApp.CustomerRegistration extends Space.eventSourcing.Process
@@ -133,6 +135,8 @@
   _onRegistrationInitiated: (event) ->
     { @customerId, @customerName } = event
     @_state = @STATES.creatingCustomer
+
+CustomerApp.CustomerRegistration.registerSnapshotType 'CustomerApp.CustomerRegistrationSnapshot'

 # -------------- ROUTERS --------------- #


Items are now in rows of 1

Items are now in rows of 1

--- lib/installed-packages-panel.coffee
+++ lib/installed-packages-panel.coffee
@@ -83,6 +83,11 @@
         container.append(packageRow)
         packView = new AvailablePackageView(pack, @packageManager)
         packView.widen()
+      packageRow = $$ -> @div class: 'row'
+      container.append(packageRow)
+      # TODO if pack.valid?
+      packView = new AvailablePackageView(pack, @packageManager)
+      packView.widen()
       packageViews.push(packView) # used for search filterin'
       packageRow.append(packView)
     packageViews


Remove updates from installed panel

Remove updates from installed panel

--- lib/installed-packages-panel.coffee
+++ lib/installed-packages-panel.coffee
@@ -18,10 +18,6 @@
           @div class: 'section-heading icon icon-package', =>
             @text 'Installed Packages'
             @span outlet: 'totalPackages', class:'section-heading-count', ' (…)'
-          @div outlet: 'checkingMessage', class: 'alert alert-info featured-message icon icon-hourglass', 'Checking for updates\u2026'
-          @div outlet: 'noUpdatesMessage', class: 'alert alert-info featured-message icon icon-heart', 'All of your installed packages are up to date!'
-          @div outlet: 'updateMessage', class: 'alert alert-info loading-area icon icon-cloud-download', =>
-            @a outlet: 'updateLink', "Updates available"
           @div class: 'editor-container', =>
             @subview 'filterEditor', new TextEditorView(mini: true, placeholderText: 'Filter packages by name')

@@ -48,8 +44,6 @@

   initialize: (@packageManager) ->
     @packageViews = []
-    @noUpdatesMessage.hide()
-    @updateMessage.hide()

     @subscribe @packageManager, 'package-install-failed', (pack, error) =>
       @searchErrors.append(new ErrorView(@packageManager, error))
@@ -60,25 +54,7 @@
     @filterEditor.getEditor().on 'contents-modified', =>
       @matchPackages()

-    @checkForUpdates()
     @loadPackages()
-
-  checkForUpdates: ->
-    @checkingMessage.show()
-
-    @packageManager.getOutdated()
-      .then (updates) =>
-        if updates.length > 0
-          @updateLink.text("#{updates.length} updates available…")
-            .on 'click', () =>
-              @parents('.settings-view').view()?.showPanel('Available Updates', {back: 'Manage Packages', updates: updates})
-          @updateMessage.show()
-          @checkingMessage.hide()
-        else
-
-      .catch (error) =>
-        @checkingMessage.hide()
-        @updateErrors.append(new ErrorView(@packageManager, error))

   filterPackages: (packages) ->
     packages.dev = packages.dev.filter ({theme}) -> not theme


Clean up Event

Clean up Event

--- app/entities/Event.coffee
+++ app/entities/Event.coffee
@@ -1,23 +1,11 @@
 Entity   = require 'core/Entity'
-mediator = require 'mediator'
+Scriptable = require 'components/Scriptable'

 module.exports = class Event extends Entity
   constructor: (owningLevel, object) ->
     object.properties.physicsType = 'static'
     object.properties.isSensor    = true

-    @repeat = false
-
     super owningLevel, object

-
-  onTouch: =>
-    super
-
-
-  onTouchBegin: =>
-    super
-
-
-  onTouchEnd: =>
-    super
+    @scriptable = new Scriptable @


remove todo

remove todo

--- apps/articles/routes.coffee
+++ apps/articles/routes.coffee
@@ -21,12 +21,7 @@
   ]).fail(next).then =>
     res.locals.sd.ARTICLES = articles.toJSON()
     res.locals.sd.ARTICLES_COUNT = articles.count
-    # TODO: For QA purpose we show admins the first vertical regardless.
-    # After VB QA we'll only use the else code.
-    if req.user?.isEditorialAdmin()
-      vertical = verticals.first()
-    else
-      vertical = verticals.running()[0]
+    vertical = verticals.running()[0]
     res.render 'articles', vertical: vertical, articles: articles

 @show = (req, res, next) ->


clean up polling code

clean up polling code

--- app/js/player-ctrl.coffee
+++ app/js/player-ctrl.coffee
@@ -2,20 +2,20 @@

 mod.controller('PlayerCtrl', ($scope, $interval) ->
   'use strict'
+  poller = null

-  monitorPlayer = ->
-    checkPlayerStatus = $interval(->
-      console.log('player status poll')
+  checkPlayerStatus = ->
+    console.log('player status poll')
+    $scope.playing =
+      now: 'Joe - ' + Math.random().toString(36).replace(/[^a-z]+/g, '')

-      $scope.playing =
-        now: 'Joe - ' + Math.random().toString(36).replace(/[^a-z]+/g, '')
-
-    ,1000)
+  startMonitoring = ->
+    poller = $interval(checkPlayerStatus, 1000)

   stopMonitoring = ->
-    if angular.isDefined(monitor)
-      $interval.cancel(monitor)
-      monitor = undefined
+    if angular.isDefined(poller)
+      $interval.cancel(poller)
+      poller = undefined

   $scope.$on '$destroy', -> $scope.stopMonitoring()

@@ -28,5 +28,5 @@
   $scope.next = ->
     console.log 'next'

-  monitorPlayer()
+  startMonitoring()
 )


fix indent problem caused by transaltion merge (#6465)

--- app/locale/pl.coco.coffee
+++ app/locale/pl.coco.coffee
@@ -1,11 +1,11 @@
 module.exports = nativeDescription: "polski", englishDescription: "Polish", translation:
   new_home:
-     title: "CodeCombat - Gra koderska do nauki Pythona i JavaScriptu"
-     meta_keywords: "CodeCombat, python, javascript, Coding Games"
-     meta_description: "Naucz się pisać kod poprzez programowanie gry. Naucz się Pythona, JavaScript i HTMLa w trakcie rozwiązywania zagadek i naucz soę tworzenia twoich własnych gier i stron internetowych"
-     meta_og_url: "https://codecombat.com"
-     become_investor: "Zostań inwestorem w CodeCombat"
-     built_for_teachers_title: "Gra koderska zbudowana z myślą o nauczycielach"
+    title: "CodeCombat - Gra koderska do nauki Pythona i JavaScriptu"
+    meta_keywords: "CodeCombat, python, javascript, Coding Games"
+    meta_description: "Naucz się pisać kod poprzez programowanie gry. Naucz się Pythona, JavaScript i HTMLa w trakcie rozwiązywania zagadek i naucz soę tworzenia twoich własnych gier i stron internetowych"
+    meta_og_url: "https://codecombat.com"
+    become_investor: "Zostań inwestorem w CodeCombat"
+    built_for_teachers_title: "Gra koderska zbudowana z myślą o nauczycielach"
     built_for_teachers_blurb: "Uczenie dzieci programowania często może być przytłaczające. CodeCombat pomaga wszystkim nauczycielom nauczyć uczniów jak kodować albo w JavaScripcie albo w Pythonie, dwóch z najbardziej popularnych języków programowania. Z kompleskowym programem nauczania, który zawiera sześć lekcji informatyki i który wspomaga naukę poprzez projektowe umiejętności gamedevowe i webdevowe, dzieci będą poczyniać postępy w podróży od podstawowej składni do rekurencji!"
     built_for_teachers_subtitle1: "Informatyka"
     built_for_teachers_subblurb1: "Rozpoczynając z naszym darmowym kursem Wprowadzenia do Informatyki, uczniowie doskonalą podstawowe znajomości programistyczne takie jak pętle while/for, funkcje i algorytmy."
@@ -77,7 +77,7 @@
     global_subtitle3: "Nauczyciele"
     global_subtitle4: "Kraje"
     go_to_my_classes: "Przejdź do moich klas"
-    go_to_my_courses: "Przejdź do moich kursów"
+    go_to_my_courses: "Przejdź do moich kursów"
     quotes_quote1: "Nazwij dowolny program online, próbowałem go. Żaden z nich nie pasuje do CodeCombat i Ozarii. Każdy nauczyciel, który chce, aby jego uczniowie nauczyli się kodować... zacznij tutaj!"
     quotes_quote2: " Zaskoczyło mnie, jak CodeCombat sprawia, że nauka informatyki jest łatwa i intuicyjna. Wyniki na egzaminie AP były znacznie wyższe niż się spodziewałem i uważam, że CodeCombat jest powodem, dla którego tak się stało."
     quotes_quote3: "CodeCombat był najbardziej korzystny w nauczaniu moich uczniów umiejętności kodowania w prawdziwym życiu. Mój mąż jest inżynierem oprogramowania i przetestował wszystkie moje programy. Uznał to za swój najlepszy wybór."
@@ -85,7 +85,7 @@
     quotes_quote5: "Chociaż zajęcia zaczynają się w każdą sobotę o 7 rano, mój syn jest tak podekscytowany, że budzi się przede mną! CodeCombat stwarza mojemu synowi ścieżkę rozwoju umiejętności kodowania."
     quotes_quote5_author: "Latthaphon Pohpon, Parent"
     see_example: "Zobacz przykład"
-    slogan: "Najbardziej angażujący sposób na naukę informatyki!"
+    slogan: "Najbardziej angażujący sposób na naukę informatyki!"
     slogan_power_of_play: "Naucz się kodować dzięki mocy zabawy"
     teach_cs1_free: "Ucz podstaw informatyki za darmo"
     teachers_love_codecombat_title: "Nauczyciele kochają CodeCombat"


change notification type

change notification type


Don't open multiple windows

Don't open multiple windows


Added getMove for all other pieces

Added getMove for all other pieces


:art: Un-use camel case for properties

:art: Un-use camel case for properties

--- lib/view.coffee
+++ lib/view.coffee
@@ -7,20 +7,20 @@
 class LinterView extends EventEmitter
   constructor: (@Linter) ->
     super()
-    @decorations = []
-    @root = document.createElement 'div'
+    @Decorations = []
     @BarCurrent = null
     @BarProject = null
-    @root.id = 'linter-panel'
-    @bubble = null
+    @Root = document.createElement 'div'
+    @Root.id = 'linter-panel'
+    @Bubble = null

   remove: ->
-    @bubble?.destroy()
+    @Bubble?.destroy()
     @removeDecorations()
     @removeErrors()

   removeDecorations: ->
-    @decorations.forEach (decoration) ->
+    @Decorations.forEach (decoration) ->
       try decoration.destroy()

   removeErrors: ->
@@ -28,7 +28,7 @@
       this.root.removeChild this.root.firstChild

   updateBubble: (Point) ->
-    @bubble?.destroy()
+    @Bubble?.destroy()
     return unless @Linter.Messages.length
     TextEditor = @Linter.ActiveEditor
     ActiveFile = TextEditor.getPath()
@@ -41,28 +41,28 @@
       LeRange = new Range([P[0][0] - 1, P[0][1] - 1], [P[1][0] - 1, P[1][1]])
       return unless LeRange.containsPoint Point
       Marker = TextEditor.markBufferRange LeRange, {invalidate: 'never'}
-      @bubble = TextEditor.decorateMarker Marker, type: 'overlay', item: new Bubble(@, Message)
+      @Bubble = TextEditor.decorateMarker Marker, type: 'overlay', item: new Bubble(@, Message)
       Found = true

   update: ->
-    @bubble?.destroy()
+    @Bubble?.destroy()
     @removeDecorations()
     @removeErrors()
     TextEditor = @Linter.ActiveEditor
     ActiveFile = TextEditor.getPath()
     @Linter.Messages.forEach (Message) =>
       Entry = @messageLine Message
-      @root.appendChild Entry
+      @Root.appendChild Entry

       return if Message.File isnt ActiveFile or not Message.Position
       P = Message.Position
       Marker = TextEditor.markBufferRange [[P[0][0] - 1, P[0][1] - 1], [P[1][0] - 1, P[1][1]]], {invalidate: 'never'}

-      @decorations.push TextEditor.decorateMarker(
+      @Decorations.push TextEditor.decorateMarker(
         Marker, type: 'line-number', class: 'line-number-' + Message.Type.toLowerCase()
       )

-      @decorations.push TextEditor.decorateMarker(
+      @Decorations.push TextEditor.decorateMarker(
         Marker, type: 'highlight', class: 'highlight-' + Message.Type.toLowerCase()
       )


better styled gridplot buttons

better styled gridplot buttons

--- static/coffee/common/grid_plot.coffee
+++ static/coffee/common/grid_plot.coffee
@@ -55,21 +55,11 @@
     build_views(@childviews, childmodels, {})
     @set_child_view_states()

-
-  startPan : ->
-
-    all_tools = _.flatten(_.map(_.pluck(this.childviews, 'tools'), _.values))
-    pan_tools = _.where(all_tools, {constructor:PanToolView})
-    _.each(pan_tools, (t) ->
-      t_name = t.evgen.toolName
-      console.log('activating ', t_name)
-      t.evgen.eventSink.trigger('active_tool', t_name))
-
   makeButton : (eventSink, constructor, toolbar_div, button_name) ->

     all_tools = _.flatten(_.map(_.pluck(this.childviews, 'tools'), _.values))
     specific_tools = _.where(all_tools, {constructor:constructor})
-    button = $("<button>#{button_name}</button>")
+    button = $("<button class='btn btn-small'>#{button_name}</button>")
     toolbar_div.append(button)
     tool_active = false;
     button_activated = false;
@@ -100,7 +90,7 @@
   addGridToolbar : ->

     @button_bar = $("<div class='grid_button_bar'/>")
-    @button_bar.attr('style',     "position:absolute; left:100px;  border:2px solid green")
+    @button_bar.attr('style',     "position:absolute; left:10px; top:0px; ")
     @toolEventSink = _.extend({}, Backbone.Events)
     @atm = new ActiveToolManager(@toolEventSink)
     @atm.bind_bokeh_events()


dont need as-promised

dont need as-promised

--- test/main.coffee
+++ test/main.coffee
@@ -3,7 +3,6 @@
 fixtures = require './fixtures'

 chai = require 'chai'
-chai.use require('chai-as-promised')

 should = chai.should()
 expect = chai.expect


Add more tests for add()

Add more tests for add()

--- test/spec.coffee
+++ test/spec.coffee
@@ -110,12 +110,33 @@

 suite 'Traversal methods', ->

-    test 'add', ->
+    test 'add multiple (Rye)', ->
         list = list_items()
         list2 = list_items()
         list3 = list.add(list2)
         assert list3.length is 3, "List length shouldn't change"
         assert.deepEqual list3.pluck('className'), ['a', 'b', 'c'], "Elements are the same"
+
+    test 'add multiple (Array)', ->
+        list = list_items()
+        list2 = list_items().get()
+        list3 = list.add(list2)
+        assert list3.length is 3, "List length shouldn't change"
+        assert.deepEqual list3.pluck('className'), ['a', 'b', 'c'], "Elements are the same"
+
+    test 'add single item', ->
+        list = list_items()
+        div = document.createElement('div')
+        list3 = list.add(div)
+        assert list3.length is 4, "Has four items"
+        assert.deepEqual list3.pluck('className'), ['a', 'b', 'c', ''], "Classes match"
+        assert list3.get(3) is div, "Element was appended to the list"
+
+    test 'add invalid item', ->
+        list = list_items()
+        list3 = list.add('bacon')
+        assert list3.length is 3, "Length didn't change"
+        assert.deepEqual list3.pluck('className'), ['a', 'b', 'c'], "Classes match"

     test 'next', ->
         el = $('.a').next()


fix data

fix data

--- data/points/zrender.coffee
+++ data/points/zrender.coffee
@@ -1,4 +1,4 @@
-id: "zender"
+id: "zrender"
 title: "ZRender"
 function: [ "chart-library" ]
 language: [ "javascript" ]


Add specs for getTemplateData.

Add specs for getTemplateData.

--- test/spec/collection_view_spec.coffee
+++ test/spec/collection_view_spec.coffee
@@ -309,6 +309,10 @@
       renderSpy.restore()
       renderAllItemsSpy.restore()

+    it 'should not return item data in getTemplateData', ->
+      data = collectionView.getTemplateData()
+      expect(data).to.eql {length: collection.length}
+
     it 'should dispose itself correctly', ->
       expect(collectionView.dispose).to.be.a 'function'
       model = collection.at 0
@@ -544,6 +548,12 @@
         addOne()
         expect($loading.css('display')).to.be 'none'

+      it 'should pass sync status to template data', ->
+        data = collectionView.getTemplateData()
+        expect(data).to.eql {
+          length: collection.length, synced: collection.isSynced()
+        }
+
       it 'should also dispose when templated', ->
         collectionView.dispose()


chore (plugin test) improve pause and resume test cases

--- testApp/app/controllers/plugin.coffee
+++ testApp/app/controllers/plugin.coffee
@@ -293,14 +293,20 @@

   @addPause = () ->
     document.addEventListener "pause", () ->
-      pause_result.innerHTML = "YES: " + new Date().toLocaleTimeString();
+      if pause_result.innerHTML == "NO"
+        pause_result.innerHTML = "YES: " + new Date().toLocaleTimeString();
+      else
+        pause_result.innerHTML += "
another one: " + new Date().toLocaleTimeString();

     navigator.notification.alert "Event listener added: pause"

   @addResume = () ->
     # alert needs to be wrapped in setTimeout to work
     document.addEventListener "resume", () ->
-      resume_result.innerHTML = "YES: " + new Date().toLocaleTimeString();
+      if resume_result.innerHTML == "NO"
+        resume_result.innerHTML = "YES: " + new Date().toLocaleTimeString();
+      else
+        resume_result.innerHTML += "
another one: " + new Date().toLocaleTimeString();

     navigator.notification.alert "Event listener added: resume"


Minor fix

Minor fix

--- lib/Strategy/DBStrategy/Save/TemporaryTable.coffee
+++ lib/Strategy/DBStrategy/Save/TemporaryTable.coffee
@@ -4,7 +4,7 @@
 Save = require "../Save"

 class TemporaryTable extends Save
-  constructor: (input, options, dependencies) ->
+  constructor: (input, dependencies) ->
     _.defaults input,
       bufferTableName: "UpsertData"
       idFieldName: "_uid"


Final

Final

--- views/jsx/referral/all.cjsx
+++ views/jsx/referral/all.cjsx
@@ -107,7 +107,6 @@
                     'Completed'
                   else
                     'Confirmed'
-
                 }
               </small>
             else


reproduced curl tests for discoveras in mocha!

--- test/integration/get-device-spec.coffee
+++ test/integration/get-device-spec.coffee
@@ -28,10 +28,10 @@
         discovererDeviceData = type: 'device:discoverer', discoverAsWhitelist: [@authDevice.uuid]
         @meshbluHttp.register discovererDeviceData, (error, @discovererDevice) =>
           discovereeDeviceData = type: 'device:discoveree', discoverWhitelist: [@discovererDevice.uuid]
-          @meshbluHttp.register discovereeDeviceData, (error, @discovererDevice) => done()
+          @meshbluHttp.register discovereeDeviceData, (error, @discovereeDevice) => done()

     describe 'when a device requests itself (whoami)', ->
-      beforeEach (done) ->
+      beforeEach (done) ->
         auth =
           uuid: @authDevice.uuid
           token: @authDevice.token
@@ -51,3 +51,133 @@
       it 'should give us a device', ->
         device = JSON.parse @response.rawData
         expect(device.type).to.equal 'device:auth'
+
+    describe 'when a device requests itself with a bad token (whoami)', ->
+      beforeEach (done) ->
+        auth =
+          uuid: @authDevice.uuid
+          token: '12344'
+
+        job =
+          metadata:
+            auth: auth
+            fromUuid: auth.uuid
+            toUuid: auth.uuid
+            jobType: 'GetDevice'
+
+
+        @jobManager.do 'request', 'response', job, (@error, @response) => done()
+
+        @dispatcher.doSingleRun =>
+
+      it "should tell us we're not allowed", ->
+        expect(@response.metadata.code).to.equal 403
+
+    xdescribe "when auth tries to discover discovererDevice but is only in it's discoverAsWhitelist", ->
+      beforeEach (done) ->
+        auth =
+          uuid: @authDevice.uuid
+          token: @authDevice.token
+
+        job =
+          metadata:
+            auth: auth
+            fromUuid: auth.uuid
+            toUuid: @discovererDevice.uuid
+            jobType: 'GetDevice'
+
+
+        @jobManager.do 'request', 'response', job, (@error, @response) => done()
+
+        @dispatcher.doSingleRun =>
+
+      it "should tell us we're not allowed", ->
+        expect(@response.metadata.code).to.equal 403
+
+    describe "when authDevice tries to discover discovererDevice as the discovererDevice", ->
+      beforeEach (done) ->
+        auth =
+          uuid: @authDevice.uuid
+          token: @authDevice.token
+
+        job =
+          metadata:
+            auth: auth
+            fromUuid: @discovererDevice.uuid
+            toUuid: @discovererDevice.uuid
+            jobType: 'GetDevice'
+
+
+        @jobManager.do 'request', 'response', job, (@error, @response) => done()
+
+        @dispatcher.doSingleRun =>
+
+      it 'should give us a device', ->
+        device = JSON.parse @response.rawData
+        expect(device.type).to.equal 'device:discoverer'
+
+
+    describe "when authDevice tries to discover discovereeDevice", ->
+      beforeEach (done) ->
+        auth =
+          uuid: @authDevice.uuid
+          token: @authDevice.token
+
+        job =
+          metadata:
+            auth: auth
+            fromUuid: auth.uuid
+            toUuid: @discovereeDevice.uuid
+            jobType: 'GetDevice'
+
+
+        @jobManager.do 'request', 'response', job, (@error, @response) => done()
+
+        @dispatcher.doSingleRun =>
+
+      it "should tell us we're not allowed", ->
+        expect(@response.metadata.code).to.equal 403
+
+
+    describe "when authDevice tries to discover discovereeDevice as discovererDevice", ->
+      beforeEach (done) ->
+        auth =
+          uuid: @authDevice.uuid
+          token: @authDevice.token
+
+        job =
+          metadata:
+            auth: auth
+            fromUuid: @discovererDevice.uuid
+            toUuid: @discovereeDevice.uuid
+            jobType: 'GetDevice'
+
+
+        @jobManager.do 'request', 'response', job, (@error, @response) => done()
+
+        @dispatcher.doSingleRun =>
+
+      it 'should give us a device', ->
+        device = JSON.parse @response.rawData
+        expect(device.type).to.equal 'device:discoveree'
+
+    describe "when authDevice tries to discover discovereeDevice as discovereeDevice", ->
+      beforeEach (done) ->
+        auth =
+          uuid: @authDevice.uuid
+          token: @authDevice.token
+
+        job =
+          metadata:
+            auth: auth
+            fromUuid: @discovereeDevice.uuid
+            toUuid: @discovereeDevice.uuid
+            jobType: 'GetDevice'
+
+
+        @jobManager.do 'request', 'response', job, (@error, @response) => done()
+
+        @dispatcher.doSingleRun =>
+
+      it "should tell us we're not allowed", ->
+        expect(@response.metadata.code).to.equal 403


fixing BaseCollection test

fixing BaseCollection test

--- test/shared/base/collection.test.coffee
+++ test/shared/base/collection.test.coffee
@@ -17,7 +17,7 @@
       # Make 'sync' a noop.
       collection.sync = ->

-      should.not.exist collection.params
+      collection.params.should.eql {}

       collection.fetch
         url: 'foo'


added unit tests for castling through check

--- test/spec/chess/pieces/king_spec.coffee
+++ test/spec/chess/pieces/king_spec.coffee
@@ -103,3 +103,15 @@
 		expect(@board.get(3,0).name).toBe "Rook"
 		expect(@board.get(6,7).name).toBe "King"
 		expect(@board.get(5,7).name).toBe "Rook"
+
+	it "cannot castle through a check", ->
+		# queen threatens area between the castle
+		@board.set 0, 4, "K"
+		@board.set 0, 0, "R"
+		@board.set 7, 3, "q"
+
+		moves = @board.getMoves 0, 4
+		expect(moves.length).toBe 3
+		expect(moves).toContain [1, 4, "move"]
+		expect(moves).toContain [0, 5, "move"]
+		expect(moves).toContain [1, 5, "move"]


Remove use of the local rm function in generate.spec.coffee in favor of the lib one.

--- test/spec/commands/generate.spec.coffee
+++ test/spec/commands/generate.spec.coffee
@@ -1,20 +1,11 @@
 fs = require 'fs'
 {resolve, existsSync:eS} = require 'path'
 {run} = require resolve __dirname, '../../../lib/utils/commands'
+{rmSync:rm} = require resolve __dirname, '../../../lib/utils/files'

 TEST_ROOT = resolve '.'
 FIXTURES_ROOT = resolve __dirname, '../../../test/fixtures/commands/generate'
 NEAT_BIN = resolve __dirname, '../../../bin/neat'
-
-rm = (path) ->
-  if eS path
-    stats = fs.lstatSync path
-    if stats.isDirectory()
-      paths = fs.readdirSync path
-      rm "#{path}/#{p}" for p in paths if paths?
-      fs.rmdirSync path
-    else
-      fs.unlinkSync path

 describe '`neat generate', ->
   beforeEach ->


Use range-matching for parametric-escape sequences

Newlines will terminate an unclosed string, unless they've been escaped.
Although silly, the following statements are both valid Roff source:

    \b'Valid
    \b'Va\
    lid

--- grammars/roff.cson
+++ grammars/roff.cson
@@ -892,14 +892,18 @@

 				# Parametric/function-like escape sequences
 				name: "constant.character.escape.function.roff"
-				match: "((\\\\)[bCDhHSlLovwxXN])((.)(.*?)(\\4))"
-				captures:
+				begin: "((\\\\)[bCDhHSlLovwxXN])((.))"
+				end:   "(\\4)|(?<!\\\\)(?=$)"
+				contentName: "string.other.roff"
+				patterns: [include: "#escapes"]
+				beginCaptures:
 					1: {name: "entity.name.function.roff"}
 					2: {name: "punctuation.definition.escape.roff"}
 					3: {name: "string.other.roff"}
 					4: {name: "punctuation.definition.begin.roff"}
-					5: {patterns: [include: "#escapes"]}
-					6: {name: "punctuation.definition.end.roff"}
+				endCaptures:
+					0: {name: "string.other.roff"}
+					1: {name: "punctuation.definition.end.roff"}
 			},{

 				# Transparent throughput


Fix bug with matching some Vim modeline variations

"Troff" and "Roff" weren't being recognised in Vim-style modelines. Some
trivial optimisations have also been made to Emacs's modeline pattern.


bug 161: does not attempt to convert date to Date

--- modules/directives/date/date.coffee
+++ modules/directives/date/date.coffee
@@ -32,7 +32,7 @@
       ### Update the date picker when the model changes ###
       controller.$render = ()->
         date = controller.$viewValue
-        date = new Date(date) unless date instanceof Date
+        ### We don't need to convert to a Date here since setDate accepts a string
         element.datepicker("setDate", date)

     ### Create the datepicker widget ###


Added Case methods as well as a helper method that handles options for all cases.

--- src/fogbugz.coffee
+++ src/fogbugz.coffee
@@ -2,7 +2,7 @@
 xml2js = require 'xml2js'
 fogbugzURL = null

-SetURL = (url) -> fogbugzURL = "https://" + url + "/api.asp?"
+SetURL = (url) -> fogbugzURL = url + "/api.asp?"

 #User Authentication
 LogOn = (username, password, callback) ->


Whitespace

Whitespace

--- src/methods.coffee
+++ src/methods.coffee
@@ -1,8 +1,10 @@
 Meteor.methods
   midiNoteOn: (noteInfo) ->
     chatStream.emit 'midiNoteOn', noteInfo
+
   midiDrumsNoteOn: (noteInfo) ->
     chatStream.emit 'midiDrumsNoteOn', noteInfo
+
   skeleton: (skeleton) ->
     chatStream.emit 'skeleton', skeleton


cleaning up the eval native

cleaning up the eval native

--- src/natives.coffee
+++ src/natives.coffee
@@ -253,13 +253,10 @@
       ]
     doppio:
       JavaScript: [
-        o 'eval(Ljava/lang/String;)Ljava/lang/String;', (rs, jvm_str) ->
-          str = jvm_str.jvm2js_str()
-          rv = eval str
-          return null unless rv?
-          # Coerce to string.
-          rv = ""+rv
-          return rs.init_string rv
+        o 'eval(Ljava/lang/String;)Ljava/lang/String;', (rs, to_eval) ->
+          rv = eval to_eval.jvm2js_str()
+          # Coerce to string, if possible.
+          if rv? then rs.init_string "#{rv}" else null
       ]
   java:
     lang:


removed link in help

removed link in help

--- src/pairbot.coffee
+++ src/pairbot.coffee
@@ -106,7 +106,6 @@
     msg.send "  stop pairing"
     msg.send "  list pairs"
     msg.send "  help"
-    msg.send "Want to help? Bug? Feature request? https://github.com/fhwrdh/hubot-pairbot"

 extras =
     Shell: 'testing'


pass results of mapreduce to view

pass results of mapreduce to view

--- src/service.coffee
+++ src/service.coffee
@@ -309,22 +309,17 @@

           persistence.findTotalCommits (err, cursor) ->
             if err?
-              logger.error "findTotlaCommists", {err: err}
+              logger.error "findTotlaCommits", {err: err}
               return callback err

             cursor.toArray (err, docs) ->
-              commits = []
+              if err?
+                logger.error "findTotlaCommits:toArray", {err: err}
+                return callback err
+
               commitCount = 0
-              pastFile = null
-              docs.forEach (doc, index) ->
-                if doc.file isnt pastFile
-                  pastFile = doc.file
-                  commitCount += (_.uniq commits).length
-                  commits = []
-                if doc.convention? and Object.keys(doc.convention).length > 1
-                  (if key isnt 'lang'
-                      commits = commits.concat doc.convention[key].commits
-                  ) for key of doc.convention
+              docs.forEach (doc) ->
+                commitCount += doc.value

               desc.commitCount = commitCount
               # caching


Fix bug with ajax contact form not showing error validation after first submit

--- vendor/assets/javascripts/form_backup.coffee
+++ vendor/assets/javascripts/form_backup.coffee
@@ -1,7 +1,7 @@
 $.fn.formBackup = ->
   return false if !localStorage

-  forms = this
+  forms = @
   datas = {}
   ls = false
   datas.href = window.location.href
@@ -17,12 +17,12 @@
           datas[id] = ls[id]

   forms.find('input, textarea').keyup (e) ->
-    datas[$(this).attr('id')] = $(this).val()
+    datas[$(@).attr('id')] = $(@).val()
     localStorage.setItem 'formBackup', JSON.stringify(datas)

   forms.on 'click', (e) ->
-    $submit = $(this).find('button[type="submit"]')
+    $submit = $(@).find('button[type="submit"]')
     window.ClientSideValidations.callbacks.form.pass = ($element, callback) ->
       $submit.prev().fadeIn()
       localStorage.removeItem 'formBackup'
-      $(this).resetClientSideValidations()+      forms.resetClientSideValidations()


Handle left and right arrows to change slides.

--- vendor/javascripts/prez-controller.js.coffee
+++ vendor/javascripts/prez-controller.js.coffee
@@ -24,6 +24,7 @@
         $(".prez-slide", @document).each (i) -> $(@).attr "data-slide", "#{i + 1}"
         @changeSlideTo 1 unless changeToHashSlide()
         $(@window).on "hashchange", changeToHashSlide
+        $(@document).on "keydown", Prez.handlers.keyDown

     changeSlideTo: (nextValue) ->
         $next = $ ".prez-slide[data-slide='#{nextValue}']", @document
@@ -43,6 +44,21 @@
     nextSlide: -> @changeSlideBy 1
     prevSlide: -> @changeSlideBy -1
     end: -> @window.close()
+
+    KEY_LEFT = 37
+    KEY_RIGHT = 39
+
+    @handlers:
+        keyDown: (e) ->
+            return if $(e.target).is("input, textarea, select, option")
+
+            switch e.which
+                when KEY_LEFT
+                    e.preventDefault()
+                    Prez.current?.prevSlide()
+                when KEY_RIGHT
+                    e.preventDefault()
+                    Prez.current?.nextSlide()

 $(document).on "click", "#new-window", (e) ->
     return if Prez.current
@@ -107,5 +123,7 @@
     e.preventDefault()
     Prez.current?.end()

+$(document).on "keydown", Prez.handlers.keyDown
+
 $ ->
     $("#in-window-not-implemented-modal").modal show: false


pass group to slack modal

pass group to slack modal

--- vue/src/shared/services/group_service.coffee
+++ vue/src/shared/services/group_service.coffee
@@ -79,6 +79,8 @@
       perform: ->
         openModal
           component: 'InstallSlackModal'
+          props:
+            group: group

     remove_slack:
       name: 'install_slack.remove_slack'


Set a better default clustering for news

--- web/source/javascripts/angular-app.js.coffee
+++ web/source/javascripts/angular-app.js.coffee
@@ -87,7 +87,7 @@
   Network.then (network) ->
     $scope.network = network
     $scope.selectableClusterings = network.clusterings
-    $scope.controls.selectedClustering = _.last($scope.selectableClusterings)
+    $scope.controls.selectedClustering = _.first($scope.selectableClusterings)
     $scope.controls.sortRightToLeft = true

   $scope.scoreLeftRight = (index) ->


mp4: Specify mp4 filename as argument

mp4: Specify mp4 filename as argument

--- mp4.coffee
+++ mp4.coffee
@@ -1350,5 +1350,9 @@
 # (copyright sign) + 'too'
 class CTOOBox extends GenericDataBox

-mp4file = new MP4File 'example.mp4'
+if process.argv.length < 3
+  console.log "Error: specify an mp4 filename"
+  return 1
+
+mp4file = new MP4File process.argv[2]
 mp4file.parse()


Reset cover when modal is closed

Reset cover when modal is closed

--- resources/assets/coffee/react/profile-page/header.coffee
+++ resources/assets/coffee/react/profile-page/header.coffee
@@ -54,6 +54,7 @@

   toggleEdit: =>
     if @state.editing
+      @coverReset()
       $('.blackout').css display: 'none'
       $(document).off 'click.profilePageHeader:toggleHeaderEdit'
     else


Remove code supporting the former settings port from the frontend

--- background_scripts/main.coffee
+++ background_scripts/main.coffee
@@ -216,20 +216,6 @@
   chrome.tabs.get(request.id, (tab) ->
     chrome.windows.update(tab.windowId, { focused: true })
     chrome.tabs.update(request.id, { selected: true }))
-
-#
-# Used by the content scripts to get settings from the local storage.
-#
-handleSettings = (request, port) ->
-  switch request.operation
-    when "get" # Get a single settings value.
-      port.postMessage key: request.key, value: Settings.get request.key
-    when "set" # Set a single settings value.
-      Settings.set request.key, request.value
-    when "fetch" # Fetch multiple settings values.
-      values = request.values
-      values[key] = Settings.get key for own key of values
-      port.postMessage { values }

 chrome.tabs.onSelectionChanged.addListener (tabId, selectionInfo) ->
   if (selectionChangedHandlers.length > 0)
@@ -650,7 +636,6 @@
 # Port handler mapping
 portHandlers =
   keyDown: handleKeyDown,
-  settings: handleSettings,
   completions: handleCompletions

 sendRequestHandlers =


"Waiting for connection" dialogue didn't disappear

--- proxy.coffee
+++ proxy.coffee
@@ -147,8 +147,8 @@
         return
       .on '*listen', ({port}) ->
         server = require('net').createServer (c) ->
-          log 'interpreter connected from ' + addr c
-          server?.close(); server = null; client = c; setUpInterpreterConnection()
+          log 'interpreter connected from ' + c?.request?.connection?.remoteAddress
+          server?.close(); server = null; client = c; toBrowser '*connected'; setUpInterpreterConnection()
           return
         server.on 'error', (err) -> server = null; toBrowser '*listenError', err: '' + err; return
         server.listen port, -> log "listening for connections from interpreter on port #{port}"; return


don't set RIDE_CONNECT for spawned interpreters

--- proxy.coffee
+++ proxy.coffee
@@ -224,7 +224,7 @@
           exe = process.env.DYALOG_IDE_INTERPRETER_EXE || 'dyalog'
           log "spawning interpreter #{JSON.stringify exe}"
           child = spawn exe, ['+s', '-q'], stdio: ['pipe', 'ignore', 'ignore'], env: extend process.env,
-            RIDE_CONNECT: hp, RIDE_INIT: "CONNECT:#{hp}", RIDE_SPAWNED: '1'
+            RIDE_INIT: "CONNECT:#{hp}", RIDE_SPAWNED: '1'
           toBrowser '*spawned', pid: child.pid
           child.on 'error', (err) ->
             server?.close(); server = client = null


Master. Programmer.

Master. Programmer.

I'm dabes

--- scripts/ayp.coffee
+++ scripts/ayp.coffee
@@ -119,7 +119,7 @@
   # We listen to everything.
   # Almost...everything...
   # Don't listen to robot commands via the alias (.)
-  robot.hear /^[^\.].*/i (msg) ->
+  robot.hear /^[^\.].*/i, (msg) ->
     return if !msg.message.text

     buffer.store(


fix config misspell

fix config misspell

--- app/config.coffee
+++ app/config.coffee
@@ -20,7 +20,7 @@
 #   env: 'staging'
 #   host: 'http://boorgle-staging.herokuapp.com/api/v1'
 #   clientId: 'boorgle-web-mobile-localhost'
-#   oauthEndpoint: 'http://boorgle-staging.herokuapp.com//oauth/'
+#   oauthEndpoint: 'http://boorgle-staging.herokuapp.com/oauth/'
 #   oauthRedirectUri: 'http://localhost:9294'

 else if document.URL.indexOf('http://') is -1 && document.URL.indexOf('https://') is -1


Improve the request function

Improve the request function

--- src/tabelog.coffee
+++ src/tabelog.coffee
@@ -15,55 +15,35 @@
 cheerio   = require 'cheerio'
 request   = require 'request'

-endpoint   = 'http://tabelog.com/rst/rstsearch'
-
-timeOffset = process.env.HUBOT_TIMEZONE_OFFSET
-
 module.exports = (robot) ->
   robot.respond /tabelog(:? (lunch|dinner))?( for [^ ]+)?( (:?in|at) [^ ]+)?$/i, (msg) ->
-    type = undefined
-    kw = undefined
-    loc = undefined
+    params = {}
     for s in msg.match
       if _.isString s
         if s.match /^ (lunch|dinner)/i
-          type = RegExp.$1
-          console.log 'type' + type
+          params.type = RegExp.$1
+          console.log 'type: ' + params.type
         else if s.match /^ for /i
-          kw = s.slice(5)
-          console.log 'kw: ' + kw
+          params.kw = s.slice(5)
+          console.log 'kw: ' + params.kw
         else if s.match /^ in /i
-          loc = s.slice(4)
-          console.log 'loc: ' + loc
+          params.loc = s.slice(4)
+          console.log 'loc: ' + params.loc

-    params = []
-
-    unless type
+    unless params.type
       d = new Date()
       h = d.getHours()
       if 12 < h and h < 14
-        type = 'lunch'
+        params.type = 'lunch'
       else if 17 < h and h < 22
-        type = 'dinner'
+        params.type = 'dinner'

-    if type == 'lunch'
-      params.push 'SrtT=rtl'
-    else if type == 'dinner'
-      params.push 'SrtT=rtd'
-    else
-      params.push 'SrtT=rt'
-
-    if kw
-      params.push 'sk=' + encodeURIComponent kw
-    if loc
-      params.push 'sa=' + encodeURIComponent loc
-
-    findRestaurants endpoint + '?' + params.join('&'), (restaurants)->
+    findRestaurants params, (restaurants)->
       message = "I can't find any restaurant"
-      if kw
-        message += ' for ' + kw
-      if loc
-        message += ' in ' + loc
+      if params.kw
+        message += ' for ' + params.kw
+      if params.loc
+        message += ' in ' + params.loc
       restaurant = _.sample(restaurants, 1)[0]
       console.log restaurant
       if restaurant
@@ -75,20 +55,34 @@
         message += ' ' + restaurant.link
       msg.reply message

-findRestaurants = (url, callback) ->
-  console.log "url: " + url
-  request url, (error, response, body) ->
-    if !error and response.statusCode is 200
-      $ = cheerio.load body
-      restaurants = $('.rstlst-group').map (idx, elem)->
-        $restaurant = $(this)
-        return {
-          name: $restaurant.find('.mname a').text(),
-          link: $restaurant.find('.mname a').attr('href'),
-          image: $restaurant.find('.photoimg img').attr('src'),
-          score: $restaurant.find('.score-overall .score').text(),
-          stars: $restaurant.find('.score-overall .star').text(),
-        }
-      callback restaurants
+  endpoint   = 'http://tabelog.com/rst/rstsearch'

+  findRestaurants = (params, callback) ->
+    qs = []
+    if params.type == 'lunch'
+      qs.push 'SrtT=rtl'
+    else if params.type == 'dinner'
+      qs.push 'SrtT=rtd'
+    else
+      qs.push 'SrtT=rt'
+    if params.kw
+      qs.push 'sk=' + encodeURIComponent params.kw
+    if params.loc
+      qs.push 'sa=' + encodeURIComponent params.loc

+    url = endpoint + '?' + qs.join('&')
+    console.log "url: " + url
+    request url, (error, response, body) ->
+      if !error and response.statusCode is 200
+        $ = cheerio.load body
+        restaurants = $('.rstlst-group').map (idx, elem)->
+          $restaurant = $(this)
+          return {
+            name: $restaurant.find('.mname a').text(),
+            link: $restaurant.find('.mname a').attr('href'),
+            image: $restaurant.find('.photoimg img').attr('src'),
+            score: $restaurant.find('.score-overall .score').text(),
+            stars: $restaurant.find('.score-overall .star').text(),
+          }
+        callback restaurants
+


:lipstick:

:lipstick:

--- src/extensions/tree-view/src/tree-view.coffee
+++ src/extensions/tree-view/src/tree-view.coffee
@@ -201,9 +201,9 @@

   openSelectedEntry: (changeFocus) ->
     selectedEntry = @selectedEntry()
-    if (selectedEntry instanceof DirectoryView)
+    if selectedEntry instanceof DirectoryView
       selectedEntry.view().toggleExpansion()
-    else if (selectedEntry instanceof FileView)
+    else if selectedEntry instanceof FileView
       @rootView.open(selectedEntry.getPath(), { changeFocus })

   moveSelectedEntry: ->


Readable

Readable

--- todomvc/app.cjsx
+++ todomvc/app.cjsx
@@ -18,7 +18,7 @@
     Task.observe (state) => @setState todos: Task.find()
     SPARouter.listen
       '/'          : @setState.bind @, context: "find"
-      '/active'    : @setState.bind @, context: "uncompleted"
+      '/active'    : @setState.bind @, context: "active"
       '/completed' : @setState.bind @, context: "completed"
     SPARouter.path ""


milliseconds should be optional

milliseconds should be optional

--- src/timeago.coffee
+++ src/timeago.coffee
@@ -69,7 +69,7 @@

   parse: (iso8601) ->
     timeStr = $.trim(iso8601)
-    timeStr = timeStr.replace(/\.\d\d\d+/,"")
+    timeStr = timeStr.replace(/\.\d+/,"")
     timeStr = timeStr.replace(/-/,"/").replace(/-/,"/")
     timeStr = timeStr.replace(/T/," ").replace(/Z/," UTC")
     timeStr = timeStr.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2")


Add windows poi

Add windows poi

--- views/env.coffee
+++ views/env.coffee
@@ -74,7 +74,7 @@
       title: 'poi'
       message: msg
       icon: options?.icon || path.join(ROOT, 'assets', 'icons', 'icon.png')
-      sound: config.get('poi.notify.sound', true)
+      sound: false
   else
     # According to MDN Notification API docs:
     #   https://developer.mozilla.org/en-US/docs/Web/API/Notification/Notification
@@ -83,9 +83,9 @@
     new Notification 'poi',
       icon: if options?.icon then "file://#{options.icon}" else "file://#{ROOT}/assets/icons/icon.png"
       body: msg
-    if config.get('poi.notify.sound', true)
-      sound = new Audio "file://#{ROOT}/assets/audio/poi.mp3"
-      sound.play()
+  if config.get('poi.notify.sound', true)
+    sound = new Audio "file://#{ROOT}/assets/audio/poi.mp3"
+    sound.play()
 modals = []
 window.modalLocked = false
 window.toggleModal = (title, content, footer) ->


Fix a type breaking subscriber registration

--- pushd.coffee
+++ pushd.coffee
@@ -40,7 +40,7 @@
 createSubscriber = (fields, cb) ->
     throw new Error("Invalid value for `proto'") unless service = pushservices.getService(fields.proto)
     throw new Error("Invalid value for `token'") unless fields.token = service.validateToken(fields.token)
-    return new Subscriber(redis, fields, cb)
+    Subscriber::create(redis, fields, cb)

 authorize = (realm) ->
     if allow_from = settings.server?.acl?[realm]


Add spec: up.modal.open pushes right-anchored elements away from the edge of the screen in order to prevent jumping

--- spec_app/spec/javascripts/up/modal_spec.js.coffee
+++ spec_app/spec/javascripts/up/modal_spec.js.coffee
@@ -7,6 +7,8 @@
       it 'should have tests'

     describe 'up.modal.open', ->
+
+      assumedScrollbarWidth = 15

       it "loads the given link's destination in a dialog window", (done) ->
         $link = affix('a[href="/path/to"][up-modal=".middle"]').text('link')
@@ -49,12 +51,36 @@
           expect($modal).toExist()
           expect($modal.css('overflow-y')).toEqual('scroll')
           expect($body.css('overflow-y')).toEqual('hidden')
-          expect(parseInt($body.css('padding-right'))).toBeAround(15, 10)
+          expect(parseInt($body.css('padding-right'))).toBeAround(assumedScrollbarWidth, 10)

           up.modal.close().then ->
             expect($body.css('overflow-y')).toEqual('scroll')
             expect(parseInt($body.css('padding-right'))).toBe(0)

+            done()
+
+      it 'pushes right-anchored elements away from the edge of the screen in order to prevent jumping', (done) ->
+
+        $anchoredElement = affix('div[up-anchored=right]').css
+          position: 'absolute'
+          top: '0'
+          right: '30px'
+
+        promise = up.modal.open(url: '/foo', target: '.container')
+
+        @lastRequest().respondWith
+          status: 200
+          contentType: 'text/html'
+          responseText:
+            """
+
text

+            """
+
+        promise.then ->
+          expect(parseInt($anchoredElement.css('right'))).toBeAround(30 + assumedScrollbarWidth, 10)
+
+          up.modal.close().then ->
+            expect(parseInt($anchoredElement.css('right'))).toBeAround(30 , 10)
             done()

     describe 'up.modal.close', ->


disable initials in gray circle for users w/o avatar

--- @client/avatar.coffee
+++ @client/avatar.coffee
@@ -151,7 +151,7 @@
   # will reveal content behind it that is undesirable to show.
   style.backgroundColor = 'white' if show_avatar

-  add_initials = !user.avatar_file_name && style.width > 8
+  add_initials = false && !user.avatar_file_name && style.width > 8

   if add_initials
     style.textAlign = 'center'


formatting

formatting

--- src/twitter.coffee
+++ src/twitter.coffee
@@ -116,10 +116,9 @@
         @subscriber = new Subscriber
         @subscriber.subscribe @user
         @subscriber.subscribe @search
-        @subscriber.update = (data, source) =>
-            @publish data
+        @subscriber.update = (data, source) => @publish data

-datefix =(str) ->
+datefix = (str) ->
     # Twitter seems to give some wacky date format
     # that IE can't handle, so I convert it to something more normal.
     str.replace(/^(.+) (\d+:\d+:\d+) ((?:\+|-)\d+) (\d+)$/, "$1 $4 $2 GMT$3")


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -39,7 +39,7 @@

   equals player.I.x, 12

-  window.keydown.left = false
+  window.keydown.right = false

 test "should move player up while key is pressed", ->
   ;


Bump version in version.coffee to 1.3.21

--- src/version.coffee
+++ src/version.coffee
@@ -1 +1 @@
-FormRenderer.VERSION = '1.3.19'
+FormRenderer.VERSION = '1.3.21'


Drop packageCache

Drop packageCache

--- bundle.coffee
+++ bundle.coffee
@@ -14,7 +14,6 @@
   constructor: (src, destination, @options = {}) ->
     @src = path.resolve(src)
     @options.cache = {}
-    @options.packageCache = {}
     @options.fullPaths = true
     @target =
       name: path.basename(destination)


full mode

full mode

--- src/Main.coffee
+++ src/Main.coffee
@@ -14,9 +14,10 @@
     new Phaser.Game(896, 504, mode, @parent, this)

   preload:->
-    @game.stage.fullScreenScaleMode = Phaser.StageScaleMode.SHOW_ALL
-    @game.input.onDown.add(@goFull)
-#   @game.load.onLoadComplete.addOnce(@ready)
+    @game.stage.scaleMode = Phaser.StageScaleMode.EXACT_FIT
+    @game.stage.scale.setShowAll()
+    @game.stage.scale.refresh()
+    @game.load.onLoadComplete.addOnce(@ready)
     @game.state.add('splash', new Splash(), false)
     @game.state.add('landscape', new Landscape(), false)
     @game.load.image('labs', 'assets/labs.png')
@@ -37,10 +38,6 @@
   render:->
     @game.debug.renderText('CLICK!', 20, 20, "#FFFFFF")

-  goFull:=>
-    @game.stage.scale.startFullScreen()
-    @ready()
-
   ready:=>
     @game.state.start('splash')


Ensure directories are added first

Ensure directories are added first

--- src/asar.coffee
+++ src/asar.coffee
@@ -24,29 +24,39 @@
     filesystem = new Filesystem(src)
     files = []

-
     if options.ordering
-      ordering = fs.readFileSync(options.ordering).toString().split('\n').map (line) ->
+      orderingFiles = fs.readFileSync(options.ordering).toString().split('\n').map (line) ->
         line = line.split(':').pop() if line.indexOf(':') isnt -1
         line = line.trim()
         line = line[1..-1] if line[0] is '/'
         line

+      ordering = []
+      for file in orderingFiles
+        pathComponents = file.split(path.sep)
+        str = src
+        for pathComponent in pathComponents
+          str = path.join(str, pathComponent)
+          ordering.push(str)
+
+      filenamesSorted = []
       missing = 0
       total = filenames.length
-      for filename in filenames
-        if ordering.indexOf(path.relative(src, filename)) is -1
+
+      for file in ordering
+        if filenamesSorted.indexOf(file) is -1 and filenames.indexOf(file) isnt -1
+          filenamesSorted.push(file)
+
+      for file in filenames
+        if filenamesSorted.indexOf(file) is -1
+          filenamesSorted.push(file)
           missing += 1

-      console.log("Ordering file has #{(total - missing) / total * 100}% coverage.
-                   Sorting ASAR contents to match.")
+      console.log("Ordering file has #{(total - missing) / total * 100}% coverage.")
+    else
+      filenamesSorted = filenames

-      filenames = filenames.sort (a, b) ->
-        a = path.relative(src, a)
-        b = path.relative(src, b)
-        ordering.indexOf(a) - ordering.indexOf(b)
-
-    for filename in filenames
+    for filename in filenamesSorted
       file = metadata[filename]
       switch file.type
         when 'directory'


Move grammar override assignment into loadStateSync

Signed-off-by: Max Brunsfeld <78036c9b69b887700d5846f26a788d53b201ffbb@github.com>

--- src/atom.coffee
+++ src/atom.coffee
@@ -667,9 +667,6 @@
     document.querySelector(@workspaceParentSelectorctor).appendChild(workspaceElement)

   deserializeEditorWindow: ->
-    if grammarOverridesByPath = @state.grammars?.grammarOverridesByPath
-      @grammars.grammarOverridesByPath = grammarOverridesByPath
-
     @deserializeWorkspace()

   loadThemes: ->
@@ -731,6 +728,9 @@

     @deserializeTimings.atom = Date.now() -  startTime

+    if grammarOverridesByPath = @state.grammars?.grammarOverridesByPath
+      @grammars.grammarOverridesByPath = grammarOverridesByPath
+
     @packages.packageStates = @state.packageStates ? {}

     startTime = Date.now()


Adding the final parts to the event listener so that the tabs swap

--- flaps.coffee
+++ flaps.coffee
@@ -9,8 +9,14 @@
 		@tabcontent = document.querySelectorAll('.flap')

 	setListeners: () ->
-		tab.addEventListener('click', @eventListener) for tab in @tabheaders
+		tab.addEventListener('click', @eventListener.bind(@)) for tab in @tabheaders

-	eventListener: () ->
-		item = @.dataset.itemPosition
+	eventListener: (e) ->
+		item = e.toElement.dataset.itemPosition

+		if item != @current
+			current = document.querySelector('.flap:nth-of-type('+@current+')')
+			newCurrent = document.querySelector('.flap:nth-of-type('+item+')')
+			current.classList.add('flap-closed');
+			newCurrent.classList.remove('flap-closed');
+			@current = item


adding spaces

adding spaces

--- packages/notifications/lib/both/collections.coffee
+++ packages/notifications/lib/both/collections.coffee
@@ -14,32 +14,32 @@

 NotificationsSchema = new SimpleSchema
   owner:
-    type:String
+    type: String
     regEx: SimpleSchema.RegEx.Id

   link:
-    type:String
+    type: String
     optional: true

-  title:
-    type:String
+  title:
+    type: String

   read:
-    type:Boolean
-    defaultValue: false
+    type: Boolean
+    defaultValue:  false

-  date:
-    type:Date
-    autoValue: ->
+  date:
+    type: Date
+    autoValue:  ->
       if this.isInsert
         new Date()

   icon:
-    type:String
-    defaultValue: 'circle-o'
+    type: String
+    defaultValue:  'circle-o'

   class:
-    type:String
-    defaultValue: 'default'
+    type: String
+    defaultValue:  'default'

 Notifications.attachSchema(NotificationsSchema)


normalize location case

normalize location case

- the api now returns all lower-case names, but ili nearby expects
states to be upper case
- capitalize all two-letter locations returned by the api (i.e. states)
- lower-case all other returned locations to be safe

--- site/app.coffee
+++ site/app.coffee
@@ -260,6 +260,15 @@
 getEpidataHander = (callback) ->
   return (result, message, epidata) ->
     if result == 1
+      # normalize the case of locations
+      for row in epidata
+        if row.location.length() == 2
+          # states should be upper case
+          row.location = row.location.toUpperCase()
+        else
+          # regions should be lower case
+          row.location = row.location.toLowerCase()
+      # invoke the supplied callback
       callback(epidata)
     else
       msg = "The Epidata API says '#{message}'. (error ##{result})"


Different method for notifications

Different method for notifications

--- client.coffee
+++ client.coffee
@@ -92,9 +92,11 @@
 			if !round.get('finished') and !Db.shared.get('votes', Plugin.userId())
 				Icon.render data: 'new', style: { display: 'block', margin: '0 10 0 0' }, size: 34

-			if unread = Social.newComments(round.key())
-				Dom.div !->
-				Ui.unread unread, null, {marginLeft: '4px'}
+			Event.renderBubble [round.key()], style: marginLeft: '4px'
+
+			# if unread = Social.newComments(round.key())
+			# 	Dom.div !->
+			# 	Ui.unread unread, null, {marginLeft: '4px'}

 			Dom.h2 Util.stringToQuestion(round.get('question'))
 			Dom.onTap -> Page.nav ['round', round.key()]


Indication marker added

Indication marker added

Indication marker will be drawn when outside gameborder, so users will
know where to go to join the game.

--- client.coffee
+++ client.coffee
@@ -581,11 +581,53 @@
 						map.removeLayer window.flagCurrentLocation
 					marker.addTo(map)
 					window.flagCurrentLocation = marker
-					areaBounds = boundaryRectangle.getBounds()
-					if !(areaBounds.contains(latLngObj))
-						distance = latLngObj.distanceTo(areaBounds.getCenter())
-						log 'you are too far away to play the game.'
-						log distance + 'meters from center'
+					if boundaryRectangle?
+						areaBounds = boundaryRectangle.getBounds()
+						if !(areaBounds.contains(latLngObj))
+							log 'you are outside gameborder'
+							log location[0], location[1]
+							#TODO add correct icon & radius calculation
+							radius = 0
+							if latLngObj.lng<areaBounds.getWest()
+								if latLngObj.lat>areaBounds.getNorth()
+									#NW
+									text = "north west"
+								else if latLngObj.lat<areaBounds.getSouth()
+									#SW
+									text = "south west"
+								else
+									#W
+									indicationLat = latLngObj.lat
+									text = "west"
+							else if latLngObj.lng>areaBounds.getEast()
+								if latLngObj.lat>areaBounds.getNorth()
+									#NE
+									text= "north east"
+								else if latLngObj.lat<areaBounds.getSouth()
+									#SE
+									text = "south east"
+								else
+									#E
+									indicationLat = latLngObj.lat
+									text = "east"
+							else if latLngObj.lat>areaBounds.getNorth()
+								#N
+								indicationLng = latLngObj.lng
+								text = "north"
+							else
+								#S
+								indicationLng = latLngObj.lng
+								text = "south"
+							log indicationLat
+							log indicationLng
+							indicationLatLng = L.latLng(indicationLat, indicationLng)
+							popuptext = "You are " + latLngObj.distanceTo(indicationLatLng) + "m to the " + text
+							markerIdc = L.marker(indicationLatLng)
+							markerIdc.bindPopup(popuptext)
+							if window.flagCurrentLocation
+								map.removeLayer window.flagCurrentLocation
+							markerIdc.addTo(map)
+							window.flagCurrentLocation = markerIdc
 					# Checking if users are capable of taking over flags
 					Dom.div ->
 						Db.shared.observeEach 'game', 'flags', (flag) !->
@@ -603,3 +645,5 @@
 					map.removeLayer flagCurrentLocation
 					window.flagCurrentLocation = null

+
+