Use both \ and / in regexes so anymatch OKs Win/POSIX paths

--- 4-dual-target/brunch-config.coffee
+++ 4-dual-target/brunch-config.coffee
@@ -1,6 +1,6 @@
 module.exports = config:
   files:
     javascripts: joinTo:
-      'libraries.js': /^app\/jquery\.js/
-      'app.js': /^(?!app\/jquery\.js)/
+      'libraries.js': /^app[\\\/]jquery\.js/
+      'app.js': /^(?!app[\\\/]jquery\.js)/
     stylesheets: joinTo: 'app.css'


codeland

codeland

--- web/scripts/codeland.coffee
+++ web/scripts/codeland.coffee
@@ -1,6 +1,14 @@
 "use strict"
 # things assigned to root will be available outside this module
 root = exports ? this.codeland = {}
+
+deepcopy = (src) -> $.extend(true, {},src)
+
+#IE Support ....
+console ?= {}
+console.log ?= ->
+
+

 # BACKEND Methods useful for all games
@@ -22,15 +30,19 @@
 #Updates the player data
 root.storeGameCompletionData = (key,data) ->
     throw new Error("Cannot be null") unless val? && data?
-    p = root.getPlayer()
-    p.games[key] = data
-    root.store("PlayerData",p)
-
+    updatePlayer( (p)-> p.games[key] = data )
+
+root.getGame = ->
+    return getPlayer().currentGame
+
+
 root.getPlayer = ->
     @currentPlayer ?= root.load("CurrentPlayer")
     @currentPlayer ?= {
-        first : 'Jim'
-        last : 'Jam'
+        id : +(new Date())
+        currentGame : ''
+        first : ''
+        last : ''
         avator : 'generic'
         games : {
             java1a : {
@@ -41,7 +53,10 @@
         }
     }

-deepcopy = (src) -> $.extend(true, {},src)
+root.updatePlayer = (callback) ->
+    p  = root.getPlayer()
+    callback(p)
+    root.store("CurrentPlayer", player)

 _sequence1 = {


Fixed a problem when thang uses non changed default position component and don't have config and pos (#599)

--- app/views/editor/level/thangs/ThangsTabView.ozar.coffee
+++ app/views/editor/level/thangs/ThangsTabView.ozar.coffee
@@ -683,8 +683,10 @@
     else
       components = _.cloneDeep thangType.get('components') ? []
     components = @createEssentialComponents(thangType.get('components')) unless components.length
-    physical = _.find components, (c) -> c.config?.pos?
-    physical.config.pos = x: pos.x, y: pos.y, z: physical.config.pos.z if physical
+    positionComponent = _.find(components or [], (c) => c.original in LevelComponent.positionIDs)
+    if positionComponent
+      positionComponent.config ?= {}
+      positionComponent.config.pos = {x: pos.x, y: pos.y, z: positionComponent.config?.pos?.z || 0}
     thang = thangType: thangType.get('original'), id: thangID, components: components
     if batchInsert
       @thangsBatch.push thang


Formatting

Formatting

--- lib/tintan.coffee
+++ lib/tintan.coffee
@@ -3,6 +3,7 @@
 fs         = require 'fs'
 libxml     = require 'libxmljs'
 spawn      = require('child_process').spawn
+

 class $

@@ -33,53 +34,52 @@
   @tasks: (tintan)-> require('./tintan/tasks') tintan

   @home: @mem ->
-     dirs = [  process.env.TI_HOME
-       , '/Library/Application Support/Titanium'
-       , path.join(process.env.HOME, 'Library/Application Support/Titanium')
-       , path.join(process.env.HOME, '.titanium')
-       ]
-     return d for d in dirs when fs.existsSync d
+    dirs = [process.env.TI_HOME
+            '/Library/Application Support/Titanium'
+            path.join(process.env.HOME, 'Library/Application Support/Titanium')
+            path.join(process.env.HOME, '.titanium')
+    ]
+    return d for d in dirs when fs.existsSync d

   @os: {
-     'linux':  'linux'
-     'darwin': 'osx'
-     'win32':  'win'
-    }[require('os').platform()]
+    'linux':  'linux'
+    'darwin': 'osx'
+    'win32':  'win'
+  }[require('os').platform()]

   @ios_version: @mem ->
-     iphone_dir = path.join(process.env.HOME, 'Library', 'Application Support', 'iPhone Simulator')
-     if fs.existsSync iphone_dir
-       return process.env.IOS_VERSION if process.env.IOS_VERSION &&
-           fs.existsSync(path.join(iphone_dir, process.env.IOS_VERSION))
-       fs.readdirSync(iphone_dir).sort()[-1..][0]
+    iphone_dir = path.join(process.env.HOME, 'Library', 'Application Support', 'iPhone Simulator')
+    if fs.existsSync iphone_dir
+      return process.env.IOS_VERSION if process.env.IOS_VERSION &&
+        fs.existsSync(path.join(iphone_dir, process.env.IOS_VERSION))
+      fs.readdirSync(iphone_dir).sort()[-1..][0]

   @android_version: @mem ->
-     android_dir = path.join(@android_home(), 'platforms')
-     if fs.existsSync android_dir
-       return process.env.ANDROID_VERSION if process.env.ANDROID_VERSION &&
-           fs.existsSync(path.join(android_dir, 'android-' + (process.env.ANDROID_VERSION - 1)))
-       (d.split('-')[1] for d in fs.readdirSync(android_dir)).sort((a,b)-> a - b)[-1..][0]
+    android_dir = path.join(@android_home(), 'platforms')
+    if fs.existsSync android_dir
+      return process.env.ANDROID_VERSION if process.env.ANDROID_VERSION &&
+        fs.existsSync(path.join(android_dir, 'android-' + (process.env.ANDROID_VERSION - 1)))
+      (d.split('-')[1] for d in fs.readdirSync(android_dir)).sort((a,b)-> a - b)[-1..][0]

   @android_home: @mem ->
-     brew_location = '/usr/local/Cellar/android-sdk'
-     if process.env.ANDROID_SDK && fs.existsSync process.env.ANDROID_SDK
-       process.env.ANDROID_SDK
-     else if fs.existsSync brew_location
-       path.join brew_location, fs.readdirSync(brew_location).sort()[-1..][0]
-
+    brew_location = '/usr/local/Cellar/android-sdk'
+    if process.env.ANDROID_SDK && fs.existsSync process.env.ANDROID_SDK
+      process.env.ANDROID_SDK
+    else if fs.existsSync brew_location
+      path.join brew_location, fs.readdirSync(brew_location).sort()[-1..][0]

   @platform: process.env.TI_PLATFORM || {osx: 'iphone'}[@os] || 'android'

   @sdk: @mem ->
-     if fs.existsSync process.env.TI_SDK
-       process.env.TI_SDK
-     else
-       fs.readdirSync(path.join(@home(), 'mobilesdk', @os)).sort()[-1..][0]
+    if fs.existsSync process.env.TI_SDK
+      process.env.TI_SDK
+    else
+      fs.readdirSync(path.join(@home(), 'mobilesdk', @os)).sort()[-1..][0]

   @py: @mem ->
-     return py for py in [process.env.PYTHON, process.env.TI_PYTHON,
-                          process.env.PYTHON_EXECUTABLE] when fs.existsSync py
-     @pathSearch 'python'
+    return py for py in [process.env.PYTHON, process.env.TI_PYTHON,
+                         process.env.PYTHON_EXECUTABLE] when fs.existsSync py
+    @pathSearch 'python'

   @titan: (args ...)->
     @tipy.apply(this, [['titanium.py'], args])
@@ -100,6 +100,7 @@
 class AppXML

   file: -> $._('tiapp.xml')
+
   exist: -> fs.existsSync @file()

   constructor: ->
@@ -115,8 +116,7 @@
   name: -> @doc.get('./name').text()

   version: ->
-    v = @doc.get('./version').text()
-    v = v.split '.'
+    v = @doc.get('./version').text().split '.'
     if v.length < 3
       v = v.concat('0' for i in [0 .. (2 - v.length)])
     v.join '.'
@@ -124,10 +124,13 @@
   targets: (devices ...)->
     if devices.length > 0
       enabled = (device for device in devices when !!@doc.
-          get('./deployment-targets/target[@device="'+device+'" and contains(text(), "true")]'))
+        get('./deployment-targets/target[@device="' + device +
+            '" and contains(text(), "true")]'))
       enabled.length == devices.length
     else
-      @doc.find('./deployment-targets/target[contains(text(), "true")]/@device').map (i)-> i.text()
+      @doc.find('./deployment-targets/target[contains(text(), "true")]/@device')
+          .map (i)-> i.text()
+

 class Tintan


Kloud::info do not silence pending errors anymore, kloud.info does not have that

--- client/Main/kite/kites/kloud.coffee
+++ client/Main/kite/kites/kloud.coffee
@@ -39,7 +39,6 @@
           @requestingInfo[machineId].forEach ({ resolve }) ->
             resolve State: currentState
           @requestingInfo[machineId] = null
-        .catch(require('kite').Error.codeIs "107", (err) => ) # SILENCE THIS ERROR!
         .finally => @needsRequest[machineId] = yes

     new Promise (resolve, reject) =>


terminal: ignore IDE fullscreen keybinding [fixes #80924820]

--- client/Terminal/src/Terminal.coffee
+++ client/Terminal/src/Terminal.coffee
@@ -144,7 +144,11 @@
     @keyInput?.destroy()
     super()

+  ignoreKeyDownEvent = (event) ->
+    (event.ctrlKey or event.metaKey) and event.shiftKey and event.keyCode is 13
+
   keyDown: (event) ->
+    return  if ignoreKeyDownEvent event
     @inputHandler.keyDown event

   keyPress: (event) ->


fixes as requested to presume de facto callback signature

--- client/app/MainApp/KD.extend.coffee
+++ client/app/MainApp/KD.extend.coffee
@@ -35,33 +35,35 @@
           mainController = KD.getSingleton("mainController")
           mainController.once "accountChanged.to.loggedIn", =>
             if groupName and KD.isLoggedIn()
-              @joinGroup_ groupName, (res)=>
-                unless res then return @notify_ "Joining to #{groupName} group failed", "error"
+              @joinGroup_ groupName, (err)=>
+                return @notify_ "Joining #{groupName} group failed", "error"  if err
                 KD.lastFuncCall?()
                 KD.lastFuncCall = null
         KD.lastFuncCall = callback
     else if groupName
-      @joinGroup_ groupName, (res)=>
-        if res then callback?()
-        else @notify_ "Joining to #{groupName} group failed", "error"
-    else callback?()
+      @joinGroup_ groupName, (err)=>
+        return @notify_ "Joining #{groupName} group failed", "error"  if err
+        callback?()
+    else
+      callback?()

   joinGroup_:(groupName, callback)->
     return callback yes  unless groupName

     @whoami().fetchGroups (err, groups)=>
-      return callback no  if err or not groups
+      return callback err  if err
+
       for group in groups
         if groupName is group.group.slug
-          return callback yes
+          return callback null

       @remote.api.JGroup.one { slug: groupName }, (err, currentGroup)=>
         return @notify_ err.message, "error"  if err
-        return callback yes                   unless currentGroup.privacy is 'public'
+        return callback null                  unless currentGroup.privacy is 'public'
         currentGroup.join (err)=>
-          return callback no  if err
-          @notify_ "You have joined to #{groupName} group!", "success"
-          return callback yes
+          return callback err  if err
+          @notify_ "You have joined #{groupName} group!", "success"
+          return callback null

   nick:-> KD.whoami().profile.nickname


Fix problem with showIntermediateStops argument passing

--- src/transit.coffee
+++ src/transit.coffee
@@ -197,7 +197,7 @@
                 mode: 'TRANSIT,WALK'
                 numItineraries: 3
                 maxWalkDistance: 3000
-                showIntermediateStops: true
+                showIntermediateStops: 'true'

             args =
                 dataType: 'json'


once game is over, must reload page

once game is over, must reload page

--- source/javascripts/all.js.coffee
+++ source/javascripts/all.js.coffee
@@ -161,11 +161,8 @@

       enterToRetry = @createElement()
       enterToRetry.font = '16pt "Share"'
-      enterToRetry.fillText("ENTER TO RETRY", 170, 350)
+      enterToRetry.fillText("GAME OVER", 190, 350)
       stage.append(enterToRetry)
-
-      canvas.Input.keyDown Input.Enter, () ->
-        canvas.Scene.call("Wait", {params: {score: 0}})

     render: (stage) ->
       stage.refresh()


fixed the routes

fixed the routes

--- server/lib/server/index.coffee
+++ server/lib/server/index.coffee
@@ -295,7 +295,6 @@
     res.end "#{state}"

 app.get "/-/odesk/callback", (req,res)->
-  console.log ">>>>>>", res
   {oauth_token, oauth_verifier} = req.query

   console.log oauth_token, oauth_verifier
@@ -424,7 +423,7 @@
           if err
             subPage = JGroup.render[prefix].subPage {account, name, section}
             return serve subPage, res
-          else unless models? then res.send 404, error_404()
+          else unless models? then next()
           else
             subPage = JGroup.render[prefix].subPage {account, name, section, models}
             return serve subPage, res


About: initial lever.co wiring

About: initial lever.co wiring

--- client/landing/About/AppView.coffee
+++ client/landing/About/AppView.coffee
@@ -91,7 +91,7 @@

Careers

-
Shoot us an email if you think you should be a part of Koding!

+
If you think your picture is missing above, click here to check our current openings!

     {{> @footer}}


Fix the world

Fix the world

--- client/src/GestureController.coffee
+++ client/src/GestureController.coffee
@@ -89,8 +89,6 @@
         #console.log "sign: ", sign
         #console.log "active signs: ", @state.activeSigns

-        signFeedback = true
-
         if sign in @state.activeSigns
             data.signIndex += 1
         else if(oldIndex > 0)
@@ -143,19 +141,19 @@
                         sign_ok = false
             if(sign.extendedFingers)
                 extendedFingers = sign.extendedFingers
-                if(extendedFingers.indexFinger is not undefined)
+                if(extendedFingers.indexFinger?)
                     if extendedFingers.indexFinger != handModel.extendedFingers.indexFinger
                         sign_ok = false
-                if(extendedFingers.middleFinger is not undefined)
+                if(extendedFingers.middleFinger?)
                     if extendedFingers.middleFinger != handModel.extendedFingers.middleFinger
                         sign_ok = false
-                if(extendedFingers.ringFinger is not undefined)
+                if(extendedFingers.ringFinger?)
                     if extendedFingers.ringFinger != handModel.extendedFingers.ringFinger
                         sign_ok = false
-                if(extendedFingers.pinky is not undefined)
-                    if extendedFingers.pinky != handModel.extendedFingers.ringFinger
-                        sign_ok = false
-                if(extendedFingers.thumb is not undefined)
+                if(extendedFingers.pinky?)
+                    if extendedFingers.pinky != handModel.extendedFingers.pinky
+                        sign_ok = false
+                if(extendedFingers.thumb?)
                     if extendedFingers.thumb != handModel.extendedFingers.thumb
                         sign_ok = false
         return sign_ok


add defaultTabName option

add defaultTabName option

--- lib/edit-in-new-tab.coffee
+++ lib/edit-in-new-tab.coffee
@@ -1,4 +1,10 @@
 {CompositeDisposable} = require 'atom'
+
+String::replaceAll = (search, replacement) ->
+  # Kudos http://stackoverflow.com/a/17606289
+  #
+  target = this
+  target.replace new RegExp(search, 'g'), replacement

 module.exports = EditInNewTab =
   config:
@@ -33,10 +39,16 @@
       type: "boolean"
       default: false
       order: 4
+    defaultTabName:
+      title: "Default Tab-name"
+      description: "Define a default scheme for new tabs. Accept`%origin%` placeholder for original's file-name"
+      type: "string"
+      default: ""
+      order: 5
     outputFormat:
       title: "Formatting Options"
       type: 'object'
-      order: 5
+      order: 6
       properties:
         select:
           title: "Select"
@@ -71,9 +83,11 @@

   activate: (state) ->
     # Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
+    #
     @subscriptions = new CompositeDisposable

     # Register commands
+    #
     @subscriptions.add atom.commands.add 'atom-workspace', 'edit-in-new-tab:copy-selection': => @editInNewTab(false)
     @subscriptions.add atom.commands.add 'atom-workspace', 'edit-in-new-tab:move-selection': => @editInNewTab(true)
     @subscriptions.add atom.commands.add 'atom-workspace', 'edit-in-new-tab:settings': => @openSettings()
@@ -94,12 +108,19 @@
       return atom.beep()

     # Move to new Tab?
+    #
     if cutSelection is true
       parentSelection.delete()

     targetPane = atom.config.get('edit-in-new-tab.targetPane')
+    defaultTabName = atom.config.get('edit-in-new-tab.defaultTabName')

-    atom.workspace.open(null, { split: targetPane })
+    if not defaultTabName
+      newTabname = null
+    else
+      newTabName = defaultTabName.replaceAll("%origin%", atom.workspace.getActiveTextEditor().getFileName().toString())
+
+    atom.workspace.open(newTabName, { split: targetPane })
       .then (newTab) ->
         options =
           select: atom.config.get('edit-in-new-tab.outputFormat.select')
@@ -118,6 +139,7 @@

           atom.workspace.observeTextEditors (editor) ->
             # makes sure tabs don't get mixed up
+            #
             return unless editor.id is childEditor.id

             childEditor.onDidChange ->


Update selectors export

Update selectors export

Added support for @lukasa's language-restructuredtext package.

--- lib/emojis-provider.coffee
+++ lib/emojis-provider.coffee
@@ -4,7 +4,7 @@
 emoji = require('emoji-images')

 module.exports =
-  selector: '.source.gfm, .text.md, .text.html, .text.slim, .text.plain, .text.git-commit, .comment, .string, .source.emojicode'
+  selector: '.source.gfm, .text.md, .text.restructuredtext, .text.html, .text.slim, .text.plain, .text.git-commit, .comment, .string, .source.emojicode'

   wordRegex: /::?[\w\d_\+-]+$/
   emojiFolder: 'atom://autocomplete-emojis/node_modules/emoji-images/pngs'


Revert unintentional commit to lines-yardstick-spec.coffee

--- spec/lines-yardstick-spec.coffee
+++ spec/lines-yardstick-spec.coffee
@@ -139,22 +139,23 @@
       expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 36)).left).toBe 237.5
       expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 37)).left).toBe 244.09375

-    ffit "handles lines containing a mix of left-to-right and right-to-left characters", ->
-      editor.setText('Persian, locally known as Parsi or Farsi (زبان فارسی), the predominant modern descendant of Old Persian.\n')
-
-      atom.styles.addStyleSheet """
-      * {
-        font-size: 14px;
-        font-family: monospace;
-      }
-      """
-
-      lineTopIndex = new LineTopIndex({defaultLineHeight: editor.getLineHeightInPixels()})
-      linesYardstick = new LinesYardstick(editor, mockLineNodesProvider, lineTopIndex, atom.grammars)
-      expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 15))).toEqual({left: 126, top: 0})
-      expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 62))).toEqual({left: 521, top: 0})
-      expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 58))).toEqual({left: 487, top: 0})
-      expect(linesYardstick.pixelPositionForScreenPosition(Point(0, Infinity))).toEqual({left: 873.625, top: 0})
+    if process.platform is 'darwin' # Expectations fail on win32
+      it "handles lines containing a mix of left-to-right and right-to-left characters", ->
+        editor.setText('Persian, locally known as Parsi or Farsi (زبان فارسی), the predominant modern descendant of Old Persian.\n')
+
+        atom.styles.addStyleSheet """
+        * {
+          font-size: 14px;
+          font-family: monospace;
+        }
+        """
+
+        lineTopIndex = new LineTopIndex({defaultLineHeight: editor.getLineHeightInPixels()})
+        linesYardstick = new LinesYardstick(editor, mockLineNodesProvider, lineTopIndex, atom.grammars)
+        expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 15))).toEqual({left: 126, top: 0})
+        expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 62))).toEqual({left: 521, top: 0})
+        expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 58))).toEqual({left: 487, top: 0})
+        expect(linesYardstick.pixelPositionForScreenPosition(Point(0, Infinity))).toEqual({left: 873.625, top: 0})

   describe "::screenPositionForPixelPosition(pixelPosition)", ->
     it "converts pixel positions to screen positions", ->


Update spec to not use proxy settings when calling apm

The command is meant to fail therefore there is no need to use proxy
using proxy in this case causes the test to timeout.

--- spec/package-manager-spec.coffee
+++ spec/package-manager-spec.coffee
@@ -7,6 +7,7 @@

   beforeEach ->
     spyOn(atom.packages, 'getApmPath').andReturn('/an/invalid/apm/command/to/run')
+    atom.config.set('core.useProxySettingsWhenCallingApm', false)
     packageManager = new PackageManager()

   it "handle errors spawning apm", ->


Simplify [up-hungry] spec

Simplify [up-hungry] spec

--- spec/unpoly/radio_spec.js.coffee
+++ spec/unpoly/radio_spec.js.coffee
@@ -173,7 +173,7 @@
           expect('.target').toHaveText('new target')
           expect('.hungry').toHaveText('old hungry')

-      it 'only updates [up-hungry] elements in the targeted layer, even if the response would yield matching elements for multiple layers', asyncSpec (next) ->
+      it 'only updates [up-hungry] elements in the targeted layer, even if the response would yield matching elements for multiple layers', ->
         up.layer.config.openDuration = 0
         up.layer.config.closeDuration = 0

@@ -184,31 +184,27 @@

         up.layer.open fragment: """

-
old inside

-
update

+            old inside

           """

-        next =>
-          expect(up.layer.isOverlay()).toBe(true)
-
-          up.render
-            target: '.inside-text',
-            document: """
-

-                new outside
-

-

-
new inside

-
update

-

-              """,
-            origin: document.querySelector('.inside-link')
-
-        next =>
-          expect(closeEventHandler).not.toHaveBeenCalled()
-          expect($('.inside-text')).toHaveText('new inside')
-          expect($('.outside')).toHaveText('old outside')
+        expect(up.layer.isOverlay()).toBe(true)
+
+        up.render
+          target: '.inside',
+          document: """
+

+              new outside
+

+

+              new inside
+

+            """,
+          layer: 'front'
+
+        expect(closeEventHandler).not.toHaveBeenCalled()
+        expect($('.inside')).toHaveText('new inside')
+        expect($('.outside')).toHaveText('old outside')

       it 'does not update an [up-hungry] element if it was contained by the original fragment', asyncSpec (next) ->
         container = fixture('.container')


fix include::

fix include::

--- lib/worker.coffee
+++ lib/worker.coffee
@@ -13,7 +13,7 @@
   folder = path.dirname(filePath)
   Opal.ENV['$[]=']("PWD", path.dirname(attributes.opalPwd))
   opts = Opal.hash2(['base_dir', 'safe', 'doctype', 'attributes'], {
-      'base_dir': 'file://'.concat(folder),
+      'base_dir': folder,
       'safe': attributes.safemode,
       'doctype': attributes.doctype,
       'attributes': concatAttributes


Removed duplicate papyrus-assembly settings

--- settings/papyrus-assembly.cson
+++ settings/papyrus-assembly.cson
@@ -1,10 +1,5 @@
-'.source.papyrus-assembly.skyrim':
+'.source.papyrus-assembly':
 	editor:
 		commentStart: '; '
 		foldStartPattern: '^\\s*\\.(info|userFlagsRef)'
 		foldEndPattern: '^\\s*\\.(endInfo|endUserFlagsRef)'
-'.source.papyrus-assembly.fallout4':
-	editor:
-		commentStart: '; '
-		foldStartPattern: '^\\s*\\.(info|userFlagsRef)'
-		foldEndPattern: '^\\s*\\.(endInfo|endUserFlagsRef)'


Don't set @glyph if you can use @mget("glyph")

--- bokehjs/src/coffee/renderer/glyph/glyph_renderer.coffee
+++ bokehjs/src/coffee/renderer/glyph/glyph_renderer.coffee
@@ -13,10 +13,8 @@
     initialize: (options) ->
       super(options)

-      @glyph = @mget("glyph")
-
-      @selection_glyph    = @mget("selection_glyph")    or @glyph
-      @nonselection_glyph = @mget("nonselection_glyph") or @glyph
+      @selection_glyph    = @mget("selection_glyph")    or @mget("glyph")
+      @nonselection_glyph = @mget("nonselection_glyph") or @mget("glyph")

       @need_set_data = true


Fix pinch zoom

Fix pinch zoom


Fixing IE file attachment on support letters

--- app/assets/javascripts/frontend/custom_questions/support_letters.js.coffee
+++ app/assets/javascripts/frontend/custom_questions/support_letters.js.coffee
@@ -21,9 +21,7 @@
       else
         filename = "File uploaded"
       file_title = $("" + filename + "")
-      hidden_input = $("<input class='js-support-letter-attachment-id'>").prop('type', 'hidden').
-                                                                          prop('name', $el.attr("name")).
-                                                                          prop('value', data.result['id'])
+      hidden_input = $("<input class='js-support-letter-attachment-id' type='hidden' name='#{$el.attr("name")}' value='#{data.result['id']}' />")

       parent.find(".errors-container").html("")
       parent.find(".errors-container").closest("label").removeClass("question-has-errors")


Magic numbers are bad, mmmkay

Magic numbers are bad, mmmkay

--- src/weapons.coffee
+++ src/weapons.coffee
@@ -36,8 +36,8 @@
   fire: ->
     if @canFire
       projDx = if @holder.facing == 'right' then 1 else -1
-      startX = if @holder.facing == 'right' then @holder.right() + 1 else @holder.left() - 30 # TODO
-      startY = @holder.top() + 9                                                              # TODO
+      startX = if @holder.facing == 'right' then @holder.right() + 1 else @holder.left() - 30 # TODO - magic numbers
+      startY = @holder.top() + 9                                                              # TODO - magic numbers
       proj   = new @projType { group: 'projectiles', x: startX, y: startY, dx: projDx }
       @holder.scene.addEntity(proj)
       Kona.Sounds.play(@projSound) if @projSound != ''


fix new find fail on search old requests

--- noddy/service/v2/find.coffee
+++ noddy/service/v2/find.coffee
@@ -73,7 +73,8 @@
         afnd.data.meta.refresh = afnd.v2.refresh
         afnd.data.availability.push({type: 'article', url: afnd.v2.url}) if afnd.v2.url
       try
-        if request = oab_request.find _finder(afnd.v2.metadata) + ' AND type:article'
+        finder = _finder(afnd.v2.metadata)
+        if finder isnt '' and request = oab_request.find finder + ' AND type:article'
           rq = type: 'article', _id: request._id
           rq.ucreated = if opts.uid and request.user?.id is opts.uid then true else false
           rq.usupport = if opts.uid then API.service.oab.supports(request._id, opts.uid)? else false


webterm: Ignore some useless control code.

--- client/app/Applications/WebTerm.kdapplication/src/ControlCodeReader.coffee
+++ client/app/Applications/WebTerm.kdapplication/src/ControlCodeReader.coffee
@@ -252,6 +252,7 @@
             1001: -> terminal.inputHandler.setMouseMode true, true, false
             1002: -> terminal.inputHandler.setMouseMode true, true, true
             1003: -> terminal.inputHandler.setMouseMode true, true, true
+            1034: ignored "interpret meta key"
             1047: -> terminal.changeScreenBuffer 1
             1048: -> terminal.cursor.savePosition()
             1049: -> terminal.cursor.savePosition(); terminal.changeScreenBuffer 1
@@ -276,6 +277,7 @@
             1001: -> terminal.inputHandler.setMouseMode false, false, false
             1002: -> terminal.inputHandler.setMouseMode false, false, false
             1003: -> terminal.inputHandler.setMouseMode false, false, false
+            1034: ignored "don't interpret meta key"
             1047: -> terminal.changeScreenBuffer 0
             1048: -> terminal.cursor.restorePosition()
             1049: -> terminal.changeScreenBuffer 0; terminal.cursor.moveTo 0, terminal.sizeY - 1


Drop to default amplitude

Drop to default amplitude

--- batsignal.coffee
+++ batsignal.coffee
@@ -13,7 +13,7 @@
 else
 	Meteor.subscribe 'messages'

-	allMessages.observe added: (msg) -> if not msg.isRead then speak msg.message, speed: 140, amplitude: 200, pitch: 30, wordgap: 10
+	allMessages.observe added: (msg) -> if not msg.isRead then speak msg.message, speed: 140, pitch: 30, wordgap: 10

 	getMessage = -> messages.findOne {}


Without arguments, functions must use parentheses in CoffeeScript.

http://code.google.com/p/phantomjs/issues/detail?id=326

--- examples/simpleserver.coffee
+++ examples/simpleserver.coffee
@@ -27,7 +27,7 @@
     response.write "
"
     response.write ""
     response.write ""
-    response.close
+    response.close()
   )
   if service
     console.log "Web server running on port " + port


minor touch ups

minor touch ups

--- library/membrane/bilateral.coffee
+++ library/membrane/bilateral.coffee
@@ -71,7 +71,7 @@
             set = "#{symbol}.#{name}.%s = (%s)"
             directives = value?.uplink?.directives
             return unless _.isPlainObject directives
-            assert json = JSON.stringify directives
+            assert json = try JSON.stringify directives
             template = format set, "directives", json
             context.invokes.push "\r\n#{template}\r\n"
             uplinks = context.uplinks ?= new Object
@@ -85,9 +85,9 @@
     # externalized and transferred (by the `Screenplay`) to a client.
     @uplink: (directives, implement) ->
         invalidFunc = "no function for the uplink"
-        implement = _.find arguments, _.isFunction
+        implement = try_.find arguments, _.isFunction
         directives = {} unless _.isPlainObject directives
-        assert _.isFunction(implement), invalidFunc
+        assert _.isFunction(implement or null), invalidFunc
         assert p = @prototype; overwrap = (container) ->
             assert _.isArray c = _.toArray arguments or []
             assert _.isArray s = [@__origin, socket: @socket]
@@ -117,7 +117,7 @@
             assert mangled = "#{@location}/#{reference}"
             mangled += "/#{nsp}" if _.isString nsp = @nsp
             assert value; return @socket.on mangled, =>
-                logger.info uplinking, reference, nsp
+                try logger.info uplinking, reference, nsp
                 value.call this, i(arguments)..., (params...) =>
                     id = @socket.sacks = (@socket.sacks ?= 0) + 1
                     ack = type: "ack", name: mangled, ack: "data"


spacing

spacing

--- models/sale_artwork.coffee
+++ models/sale_artwork.coffee
@@ -59,7 +59,6 @@

   formatBidsAndReserve: ->
     bid = if (@get('bidder_positions_count') is 0) then '' else @bidCount()
-
     reserve = @reserveLabel() unless @get('reserve_status') is 'no_reserve'
     reserve = "This work has a reserve" if reserve? and not bid
     bidAndReserve = _.compact([bid, reserve]).join(', ')


Catch errors parsing apm ls output

Catch errors parsing apm ls output

--- lib/package-manager.coffee
+++ lib/package-manager.coffee
@@ -35,7 +35,13 @@
     args = ['ls', '--json']
     @runCommand args, (code, stdout, stderr) ->
       if code is 0
-        packages = JSON.parse(stdout)
+        try
+          packages = JSON.parse(stdout)
+        catch parseError
+          error = new Error('Fetching local packages failed.')
+          error.stdout = ''
+          error.stderr = parseError.message
+          return callback(error)
         callback(null, packages)
       else
         error = new Error('Fetching local packages failed.')


Open Atom when the notification is clicked

--- lib/package-updater.coffee
+++ lib/package-updater.coffee
@@ -2,6 +2,7 @@
 glob = require 'glob'
 {BufferedProcess} = require 'atom'

+ATOM_BUNDLE_IDENTIFIER = 'com.github.atom'
 INSTALLATION_LINE_PATTERN = /^Installing +([^@]+)@(\S+).+\s+(\S+)$/

 module.exports =
@@ -13,7 +14,8 @@
       @notify
         title: 'Atom Package Updates'
         message: summary
-        sender: 'com.github.atom'
+        sender: ATOM_BUNDLE_IDENTIFIER
+        activate: ATOM_BUNDLE_IDENTIFIER

   runApmUpgrade: (callback) ->
     command = atom.packages.getApmPath()


fix(send): Don't put a tooltip on the very obvious send button

--- internal_packages/composer/lib/composer-view.cjsx
+++ internal_packages/composer/lib/composer-view.cjsx
@@ -423,7 +423,6 @@

       <button className="btn btn-toolbar btn-emphasis btn-text btn-send" style={order: -100}
-              data-tooltip="Send message"
               ref="sendButton"
               onClick={@_sendDraft}><RetinaImg name="icon-composer-send.png" mode={RetinaImg.Mode.ContentIsMask} />Send</button>


fix(onboarding): fix chrome render bug on startup

--- internal_packages/onboarding/lib/page-router.cjsx
+++ internal_packages/onboarding/lib/page-router.cjsx
@@ -31,8 +31,8 @@
   _initializeWindowSize: =>
     return if @_unmounted
     {width, height} = React.findDOMNode(@refs.activePage).getBoundingClientRect()
+    atom.setSize(width, height)
     atom.center()
-    atom.setSizeAnimated(width, height, 0)
     atom.show()

   _updateWindowSize: =>


set social/activity/pm/form fetchingItemClass

--- client/Social/Activity/views/privatemessage/form.coffee
+++ client/Social/Activity/views/privatemessage/form.coffee
@@ -22,16 +22,18 @@
   createUserAutoComplete: ->

     @autoComplete = new KDAutoCompleteController
-      form                : this
-      name                : 'userController'
-      placeholder         : 'Type a username...'
-      itemClass           : ActivityAutoCompleteUserItemView
-      itemDataPath        : 'profile.nickname'
-      outputWrapper       : @chatHeads
-      selectedItemClass   : ChatHead
-      listWrapperCssClass : 'private-message'
-      submitValuesAsText  : yes
-      dataSource          : @bound 'fetchAccounts'
+      form                  : this
+      name                  : 'userController'
+      placeholder           : 'Type a username...'
+      itemClass             : ActivityAutoCompleteUserItemView
+      fetchingItemClass     : FetchingActivityAutoCompleteUserItemView
+      nothingFoundItemClass : KDAutoCompleteNothingFoundItem
+      itemDataPath          : 'profile.nickname'
+      outputWrapper         : @chatHeads
+      selectedItemClass     : ChatHead
+      listWrapperCssClass   : 'private-message'
+      submitValuesAsText    : yes
+      dataSource            : @bound 'fetchAccounts'

     autoCompleteView = @autoComplete.getView()


Sidebar: Append new <em> element to each sidebar topic item

--- client/app/lib/activity/sidebar/sidebartopicitem.coffee
+++ client/app/lib/activity/sidebar/sidebartopicitem.coffee
@@ -76,9 +76,8 @@
   pistachio: ->

     """
-    {span.ttag{#(name)}}
+    {em{#(name)}}
     {{> @followButton}}
     {{> @unreadCount}}
     """

-


pricing: update storage info for plans

pricing: update storage info for plans

--- client/landing/site.landing/coffee/pricing/plans.coffee
+++ client/landing/site.landing/coffee/pricing/plans.coffee
@@ -57,11 +57,11 @@
     description  : 'Best for expanded learning or for running a small blog/website'
     cssClass     : 'hobbyist'
     planFeatures : [
-      { partial: '1 GB RAM'     , cssClass: 'ram' }
-      { partial: '1 Core'       , cssClass: 'cpu' }
-      { partial: '3 GB Storage' , cssClass: 'storage' }
-      { partial: '1 VM total'   , cssClass: 'vm-count' }
-      { partial: '1 Always on'  , cssClass: 'always-on disabled' }
+      { partial: '1 GB RAM'      , cssClass: 'ram' }
+      { partial: '1 Core'        , cssClass: 'cpu' }
+      { partial: '10 GB Storage' , cssClass: 'storage' }
+      { partial: '1 VM total'    , cssClass: 'vm-count' }
+      { partial: '1 Always on'   , cssClass: 'always-on disabled' }
     ]
   ,
     title        : 'Developer'
@@ -74,7 +74,7 @@
     planFeatures : [
       { partial: '1 GB RAM'      , cssClass: 'ram' }
       { partial: '1 Core'        , cssClass: 'cpu' }
-      { partial: '15 GB Storage' , cssClass: 'storage' }
+      { partial: '25 GB Storage' , cssClass: 'storage' }
       { partial: '3 VMs total'   , cssClass: 'vm-count' }
       { partial: '1 Always on'   , cssClass: 'always-on' }
     ]
@@ -89,7 +89,7 @@
     planFeatures : [
       { partial: '1 GB RAM'      , cssClass: 'ram' }
       { partial: '1 Core'        , cssClass: 'cpu' }
-      { partial: '25 GB Storage' , cssClass: 'storage' }
+      { partial: '50 GB Storage' , cssClass: 'storage' }
       { partial: '5 VMs total'   , cssClass: 'vm-count' }
       { partial: '2 Always on'   , cssClass: 'always-on' }
     ]


video: opentok fix createPublisher method

--- client/app/lib/videocollaboration/services/opentok.coffee
+++ client/app/lib/videocollaboration/services/opentok.coffee
@@ -140,23 +140,17 @@
    * @return {OT.Publisher} publisher
    * @see {@link https://tokbox.com/opentok/libraries/client/js/reference/OT.html#initPublisher}
   ###
-  createPublisher: (element, publisherOptions = {}) ->
+  createPublisher: (view, publisherOptions = {}, callback) ->

-    publisherOptions.name       or= KD.nick()
+    publisherOptions.name       or= getNick()
     publisherOptions.style      or= { nameDisplayMode: on }
     publisherOptions.insertMode or= 'append'

     publisherOptions.height = 265
     publisherOptions.width  = 325

-    publisher = OT.initPublisher element, publisherOptions
+    publisher = OT.initPublisher view.getElement(), publisherOptions, (err) =>
+      if err
+      then callback err
+      else callback null, publisher

-    publisher.on
-      accessAllowed      : => @emit 'CameraAccessAllowed'
-      accessDenied       : => @emit 'CameraAccessDenied'
-      accessDialogOpened : => @emit 'CameraAccessQuestionAsked'
-      accessDialogClosed : => @emit 'CameraAccessQuestionAnswered'
-
-    return publisher
-
-


Remove the marker movement hack.

Remove the marker movement hack.

--- lib/suggestion-list.coffee
+++ lib/suggestion-list.coffee
@@ -94,17 +94,8 @@
     else
       @destroyOverlay()
       @displayBufferPosition = bufferPosition
-      marker = @suggestionMarker = editor.markBufferPosition(bufferPosition)
-
-      # HACK: When the marker is at the cursor position, it will move with the
-      # cursor, but we want it planted to the buffer position at the beginning
-      # of the prefix. When the marker moves forward, this callback will move it
-      # back to the correct position.
-      marker.onDidChange ({newHeadBufferPosition}) =>
-        if newHeadBufferPosition.column > @displayBufferPosition.column
-          marker.setBufferRange([@displayBufferPosition, @displayBufferPosition])
-
-      @overlayDecoration = editor.decorateMarker(marker, {type: 'overlay', item: this})
+      marker = @suggestionMarker = editor.markBufferRange([bufferPosition, bufferPosition])
+      @overlayDecoration = editor.decorateMarker(marker, {type: 'overlay', item: this, position: 'tail'})
       @addKeyboardInteraction()
       @active = true


Undo linter recommendation, fix 14 test !?

--- lib/symbol-provider.coffee
+++ lib/symbol-provider.coffee
@@ -239,7 +239,7 @@
   getLocalityScore: (bufferPosition, bufferRowsContainingSymbol) ->
     if bufferRowsContainingSymbol?
       rowDifference = Number.MAX_VALUE
-      rowDifference = (Math.min(rowDifference, bufferRow - bufferPosition.row) for bufferRow in bufferRowsContainingSymbol)
+      rowDifference = Math.min(rowDifference, bufferRow - bufferPosition.row) for bufferRow in bufferRowsContainingSymbol
       locality = @computeLocalityModifier(rowDifference)
       locality
     else


Loading state between carousel swaps

Loading state between carousel swaps

--- desktop/apps/editorial_features/components/venice_2017/client/index.coffee
+++ desktop/apps/editorial_features/components/venice_2017/client/index.coffee
@@ -78,6 +78,7 @@
     $(vid).css({'opacity': 1, 'z-index': 100})

   swapVideo: ->
+    $('.venice-overlay__play').attr 'data-state', 'loading'
     @VeniceVideoView.trigger 'swapVideo',
       video: @chooseVideoFile()


fixes JS driver func wrap bug

fixes JS driver func wrap bug

--- drivers/javascript2/src/ast.coffee
+++ drivers/javascript2/src/ast.coffee
@@ -479,13 +479,13 @@
     mt: 'forEach'

 funcWrap = (val) ->
-    if val instanceof Function
-        return new Func {}, val
+    val = rethinkdb.expr(val)

     ivarScan = (node) ->
         unless node instanceof TermBase then return false
         if node instanceof ImplicitVar then return true
         if (node.args.map ivarScan).some((a)->a) then return true
+        if (v for own k,v of node.optargs).map(ivarScan).some((a)->a) then return true
         return false

     if ivarScan(val)


crash similarity comparisons and query functions

--- server/query-api.coffee
+++ server/query-api.coffee
@@ -55,10 +55,26 @@
         return cb "ERROR: Crashreport not found with id:#{id}", null if not crashreport?
         return cb null, crashreport

+get_crashreports_by_crashreason = (crashreason, cb) ->
+    DB_CRASHREPORTS.find({"stack-trace.crash_reason":crashreason}).run (err,arr) ->
+        return if err? then cb err, null else cb null, arr
+
 get_crash_similarity_by_id = (id, cb) ->
     get_crashreport_by_id id, (err, crashreport) ->
         return if err? then cb err, null else cb null, crash_similarity(crashreport)

+get_similar_crashes_by_id = (id, cb) ->
+    get_crashreport_by_id id, (err, crashreport) ->
+        return cb err, null if err?
+        # TODO: when similarity value is stored in db, this pre-filtering with crash reason will become obsolete
+        get_crashreports_by_crashreason crashreport["stack-trace"].crash_reason, (err, arr) ->
+            return cb err, null if err?
+            similarity = crash_similarity(crashreport)
+            ids_of_similar_crashes = []
+            for report in arr
+                if similarity == crash_similarity(report)
+                    ids_of_similar_crashes.push report.id
+            cb null, ids_of_similar_crashes

 init_query_api = (settings, app, db) ->
     DB_CRASHREPORTS = db.collection('crashreports')
@@ -72,9 +88,9 @@
             else
                 res.send {"ok":"1","crashdata": crashdata}

-    app.get "/crashsimilarity/:id", (req, res) ->
+    app.get "/similarcrashes/:id", (req, res) ->
         id = req.params.id
-        get_crash_similarity_by_id id, (err,data) ->
+        get_similar_crashes_by_id id, (err,data) ->
             if err?
                 res.send {"ok":"0","errors": err} if err?
             else


reset the body's classlist on unmount

reset the body's classlist on unmount

--- src/components/pinned-header-footer-card/index.cjsx
+++ src/components/pinned-header-footer-card/index.cjsx
@@ -27,15 +27,14 @@
   mixins: [ScrollListenerMixin]

   componentWillMount: ->
+    @previousBodyClasses = document.body.className
     cardBodyClass = @props.cardType
-    @documentBodyClass = 'pinned-view'
-
     document.body.className = "#{cardBodyClass}-view"
-    document.body.classList.add(@documentBodyClass)
+    document.body.classList.add('pinned-view')
     document.body.classList.add('pinned-force-shy') if @props.forceShy

   componentWillUnmount: ->
-    document.body.classList.remove(@documentBodyClass)
+    document.body.className = @previousBodyClasses

   getPosition: (el) -> el.getBoundingClientRect().top - document.body.getBoundingClientRect().top


Fix signature

Fix signature

--- spec/UrlMiddleware.coffee
+++ spec/UrlMiddleware.coffee
@@ -68,7 +68,7 @@
     it 'should send application:redirect action', (done) ->
       checkRedirect = (data) ->
         chai.expect(data).to.eql '#gist/abc123'
-      mw.receiveAction newAction, 'application:redirect', checkRedirect, done
+      mw.receiveAction 'application:redirect', checkRedirect, done
       window.location.hash = '#example/abc123'
   describe 'on hash change to an gist URL', ->
     it 'should send github:gist action', (done) ->
@@ -81,5 +81,5 @@
           component: null
           nodes: []
           remote: []
-      mw.receiveAction newAction, 'github:gist', checkOpen, done
+      mw.receiveAction 'github:gist', checkOpen, done
       window.location.hash = '#gist/abc123'


Severely enhance the api structure

Severely enhance the api structure

--- library/gearing/api.coffee
+++ library/gearing/api.coffee
@@ -39,6 +39,20 @@
 # It supports strictly methods defined in the HTTP specification.
 module.exports.Api = class Api extends service.Service

+    # This method is intended for indicating to a client that the
+    # method that has been used to make the request is not supported
+    # by this service of the internals that are comprising service.
+    # Can be used from the outside, but generally should not be done.
+    unsupported: (request, response, next) ->
+        methodNotAllowed = 405
+        codes = http.STATUS_CODES
+        message = codes[methodNotAllowed]
+        accept = request?.headers?.accept or ""
+        handles = (mime) -> accept.indexOf(mime) >= 0
+        response.statusCode methodNotAllowed
+        response.end(error: message) if handles("json")
+        response.end(message); this
+
     # Process the already macted HTTP request according to the REST
     # specification. That is, see if the request method conforms to
     # to the RFC, and if so, dispatch it onto corresponding method
@@ -48,32 +62,37 @@
         tokens = super(request, response, next)
         parameters = [request, response, next]
         knowns = ["GET", "PUT", "POST", "DELETE", "OPTIONS"]
-        method = request.method?.toUpperCase()?.trim()
+        method = request?.method?.toUpperCase()?.trim()
         return @unsupported(parameters...) unless method in knowns
         missing = "Missing implementation for #{method} method"
         throw new Error(missing) unless method of this
         variables = [tokens.resource, tokens.domain]
         this[method](request, response, variables...); this

-    # This method is intended for indicating to a client that the
-    # method that has been used to make the request is not supported
-    # by this service of the internals that are comprising service.
-    # Can be used from the outside, but generally should not be done.
-    unsupported: (request, response, next) ->
-        methodNotAllowed = 405
-        codes = http.STATUS_CODES
-        message = codes[methodNotAllowed]
-        accept = request.headers.accept or ""
+    # This method should generally be used to obtain HTTP methods that
+    # are allowed on this resources. This is not the only possible way
+    # of implementing this method, because it usually can have a lot of
+    # different interpretations other than the one in the HTTP spec.
+    OPTIONS: (request, response) ->
+        accept = request?.headers?.accept or ""
         handles = (mime) -> accept.indexOf(mime) >= 0
-        response.statusCode methodNotAllowed
-        response.end(error: message) if handles("json")
-        response.end(message); this
+        knowns = ["GET", "PUT", "POST", "DELETE", "OPTIONS"]
+        checkIfSupported = (method) => @[method] isnt @unsupported
+        supported = _.filter(knowns, checkIfSupported)
+        return respons.end(methods: supported) if handles("json")
+        response.end(supported.join(", ")); this

-    # Get the contents of the resources at the established path. It
-    # is a good idea for this HTTP method to be idempotent. As the
-    # rule, this method does not have to alter any contents or data
-    # of the resource. Use for unobtrusive retrieval of resources.
-    GET: @::unsupported
+    # Delete the contents of the resources at the establushed path. It
+    # generally should destroy the contents of the resource for good.
+    # Be sure to provide enough protection for your API for destructive
+    # HTTP methods like this one. Apply it to indicate destruction.
+    DELETE: @::unsupported
+
+    # Append the contents to the resources at the established path. It
+    # usually means adding new content in addition to the old one. This
+    # HTTP method nicely maps to INSERT method of the storage engines.
+    # Use this method to successively append new contents to resources.
+    POST: @::unsupported

     # Alter the contents of the resources at the established path. It
     # usually means replacing the old contents with the new contents.
@@ -81,20 +100,8 @@
     # Use this method to repetidely replace the contents of resources.
     PUT: @::unsupported

-    # Append the contents to the resources at the established path. It
-    # usually means adding new content in addition to the old one. This
-    # HTTP method nicely maps to INSERT method of the storage engines.
-    # Use this method to successively append new contents to resources.
-    POST: @::unsupported
-
-    # Delete the contents of the resources at the establushed path. It
-    # generally should destroy the contents of the resource for good.
-    # Be sure to provide enough protection for your API for destructive
-    # HTTP methods like this one. Apply it to indicate destruction.
-    DELETE: @::unsupported
-
-    # This method should generally be used to obtain HTTP methods that
-    # are allowed on this resources. This is not the only possible way
-    # of implementing this method, because it usually can have a lot of
-    # different interpretations other than the one in the HTTP spec.
-    OPTIONS: @::unsupported
+    # Get the contents of the resources at the established path. It
+    # is a good idea for this HTTP method to be idempotent. As the
+    # rule, this method does not have to alter any contents or data
+    # of the resource. Use for unobtrusive retrieval of resources.
+    GET: @::unsupported


move S-keybinding describe to the right place

Things get a little tricky when you're ignoring `fdescribe`s.

--- spec/vim-mode-spec.coffee
+++ spec/vim-mode-spec.coffee
@@ -140,7 +140,6 @@
         expect(editor.getCursorScreenPosition()).toEqual [0, 0]
         expect(vimState.getRegister('"').text).toBe '0'

-    describe "the S keybinding", ->
       it "deletes count characters to the right and enters insert mode", ->
         editor.setText("012345")
         editor.setCursorScreenPosition([0, 0])
@@ -152,6 +151,7 @@
         expect(editor.getCursorScreenPosition()).toEqual [0, 0]
         expect(vimState.getRegister('"').text).toBe '012'

+    describe "the S keybinding", ->
       it "deletes the entire line and enters insert mode", ->
         editor.setText("12345\nabcde\nABCDE")
         editor.setCursorScreenPosition([1,3])


Changed API key env var name for consistency

--- src/scripts/virustotal.coffee
+++ src/scripts/virustotal.coffee
@@ -26,7 +26,7 @@

     if VIRUSTOTAL_API_KEY?
       hash = msg.match[1].toLowerCase()
-      data = "apikey=#{encodeURIComponent VIRUS_TOTAL_API_KEY}&resource=#{encodeURIComponent hash}"
+      data = "apikey=#{encodeURIComponent VIRUSTOTAL_API_KEY}&resource=#{encodeURIComponent hash}"

       robot.http(vt_file_report_url)
         .post(data) (err, res, body) ->
@@ -54,7 +54,7 @@
     if VIRUSTOTAL_API_KEY?
       url = msg.match[1].toLowerCase()

-      data = "apikey=#{encodeURIComponent VIRUS_TOTAL_API_KEY}&resource=#{encodeURIComponent url}"
+      data = "apikey=#{encodeURIComponent VIRUSTOTAL_API_KEY}&resource=#{encodeURIComponent url}"

       robot.http(vt_url_report_url)
         .post(data) (err, res, body) ->


Reverse bc.i txid hash

Reverse bc.i txid hash

--- client/tx/lib/blockchain/bci.coffee
+++ client/tx/lib/blockchain/bci.coffee
@@ -13,7 +13,7 @@
   xhr.done (res) ->
     if res.unspent_outputs
       unspent = for { tx_hash, tx_output_n, value, script, confirmations } in res.unspent_outputs when confirmations > 0
-        hash: tx_hash
+        hash: bytesToHex (hexToBytes tx_hash).reverse()
         index: tx_output_n
         value: value
         script: script


Add killing info

Add killing info

--- src/ChildProcess.coffee
+++ src/ChildProcess.coffee
@@ -14,6 +14,7 @@

   pipe : null

+  command: null

   constructor:->
     log.debug "ChildProcess.constructor()", arguments
@@ -26,6 +27,8 @@
     expect(@child).to.be.null
     expect(command).to.be.a 'string'
     expect(taskName).to.be.a 'string'
+
+    @command = taskName

     @child = _exec command, (err, stdout, stderr) =>
       if err?.code? and err.code isnt 0
@@ -45,6 +48,8 @@
     expect(command,"Invalid @command argument").to.be.a "string"
     expect(args,"Invalid @args argument").to.be.an "array"
     expect(options,"Invalid @options").to.be.an "object"
+
+    @command = command

     @child = _spawn(command,args,options)
     ChildProcess.children[@child.pid] = @child
@@ -77,6 +82,7 @@

   kill: (signal="SIGINT")->
     log.debug "ChildProcess.kill()",arguments
+    log.info "Killing ", @command
     @child.kill(signal)


Remove encrypted option for now, not particularly useful.

--- client/controls.coffee
+++ client/controls.coffee
@@ -130,11 +130,6 @@
       id: 'blanksasnull'
       label: 'Null Blank Columns'
       popover: "If a column is a blank (whitespace-only), set it to null."
-    }
-    {
-      id: 'encrypted'
-      label: 'Encrypted'
-      popover: "Check if the file has client side encryption."
     }
   ]


Use per-OS tmp dir

Use per-OS tmp dir

--- spec/sync-settings-spec.coffee
+++ spec/sync-settings-spec.coffee
@@ -2,6 +2,8 @@
 SpecHelper = require './spec-helpers'
 run = SpecHelper.callAsync
 fs = require 'fs'
+path = require 'path'
+os = require 'os'
 # Use the command `window:run-package-specs` (cmd-alt-ctrl-p) to run specs.
 #
 # To run a specific `it` or `describe` block add an `f` to the front (e.g. `fit`
@@ -11,23 +13,25 @@

   describe "low-level", ->
     describe "::fileContent", ->
+      tmpPath = path.join(os.tmpdir(), 'atom-sync-settings.tmp')
+
       it "returns null for not existing file", ->
-        expect(SyncSettings.fileContent("/tmp/atom-sync-settings.tmp")).toBeNull()
+        expect(SyncSettings.fileContent(tmpPath)).toBeNull()

       it "returns null for empty file", ->
-        fs.writeFileSync "/tmp/atom-sync-settings.tmp", ""
+        fs.writeFileSync tmpPath, ""
         try
-          expect(SyncSettings.fileContent("/tmp/atom-sync-settings.tmp")).toBeNull()
+          expect(SyncSettings.fileContent(tmpPath)).toBeNull()
         finally
-          fs.unlinkSync "/tmp/atom-sync-settings.tmp"
+          fs.unlinkSync tmpPath

       it "returns content of existing file", ->
         text = "alabala portocala"
-        fs.writeFileSync "/tmp/atom-sync-settings.tmp", text
+        fs.writeFileSync tmpPath, text
         try
-          expect(SyncSettings.fileContent("/tmp/atom-sync-settings.tmp")).toEqual text
+          expect(SyncSettings.fileContent(tmpPath)).toEqual text
         finally
-          fs.unlinkSync "/tmp/atom-sync-settings.tmp"
+          fs.unlinkSync tmpPath

   describe "high-level", ->
     TOKEN_CONFIG = 'sync-settings.personalAccessToken'


payments: add new views to includes

payments: add new views to includes

--- client/includes.coffee
+++ client/includes.coffee
@@ -153,7 +153,8 @@
   "app/MainApp/payment/billingformmodal.coffee",
   "app/MainApp/payment/paymentconfirmationmodal.coffee",
   "app/MainApp/payment/paymentdeleteconfirmationmodal.coffee",
-  "app/MainApp/payment/buyvmconfirmview.coffee"
+  "app/MainApp/payment/vmproductview.coffee",
+  "app/MainApp/payment/buyvmconfirmview.coffee",
   "app/MainApp/payment/buyvmmodal.coffee",

   #maintabs


Revert "Removed unused template helper"

Revert "Removed unused template helper"

This reverts commit 5292657dac5edf95436665e081ee41760a1a77f2.

--- core/app/backbone/views/users/user_statistics_view.coffee
+++ core/app/backbone/views/users/user_statistics_view.coffee
@@ -2,5 +2,8 @@
   className: 'statistics'
   template: 'users/statistics'

+  templateHelpers: =>
+    topic: @model.user_topics().first()?.toJSON()
+
   initialize: ->
     @listenTo @model, 'change', @render


A line

A line

--- src/smc-webapp/customize.cjsx
+++ src/smc-webapp/customize.cjsx
@@ -90,7 +90,7 @@
     propTypes :
         style : rtypes.object
     render: ->
-        <Redux}>
+        <Redux>
             <SiteDescription style={@props.style}/>
         </Redux>


Correct jquery plugin registration to allow chaining multiple elements

--- storyturtle.coffee
+++ storyturtle.coffee
@@ -4,8 +4,11 @@
 http://simplectic.com/story_turtle
 ###
 $ = @jQuery
-$.fn.storyturtle = ->
-  game = @hide()
+localStorage = @localStorage
+
+storyturtle = {}
+storyturtle.init = (game) ->
+  game.hide()
     .width(300)
     .height(350)

@@ -188,3 +191,7 @@
     false

   game.show()
+
+$.fn.storyturtle = ->
+  @each ->
+    storyturtle.init $(@)


rename group by mail request

rename group by mail request

--- config/initializers/requests.coffee
+++ config/initializers/requests.coffee
@@ -30,6 +30,6 @@

 # Attachments
 mailRequest = -> emit doc.mail_id, doc
-Attachment.defineRequest "mail", mailRequest, requests.checkError
+Attachment.defineRequest "byMail", mailRequest, requests.checkError
 Attachment.fromMail = (params, callback) ->
-    Mailbox.request "mail", params, callback
+    Mailbox.request "byMail", params, callback


Removed: notification when application is started

--- config/initializers/socketio.coffee
+++ config/initializers/socketio.coffee
@@ -26,10 +26,6 @@
         Application.find id, (err, app) ->
             return console.log err.stack if err # no notification, no big deal
             switch app.state
-                when 'installed'
-                    notifhelper.createTemporary
-                        text: "#{app.name} is ready."
-                        resource: {app: app.slug}
                 when 'broken'
                     notifhelper.createTemporary
                         text: "#{app.name}'s installation failled."


version 2 of the opinion views ui

version 2 of the opinion views ui

--- @client/opinion_views.coffee
+++ @client/opinion_views.coffee
@@ -76,15 +76,31 @@
   else
     null

-group_colors = {}
+window.group_colors = {}
 window.get_color_for_groups = (group_array) ->
-  num_groups = group_array.length
-  hues = getNiceRandomHues num_groups
-  colors = group_colors
-  for hue,idx in hues
+  if 'Unreported' not in group_array
+    group_array = group_array.slice()
+    group_array.push 'Unreported'
+  colors = getColors(group_array.length)
+
+  for color,idx in colors
     if group_array[idx] not of group_colors
-      group_colors[group_array[idx]] = hsv2rgb hue, Math.random() / 2 + .5, Math.random() / 2 + .5
+      if group_array[idx] == 'Unreported'
+        color = 'black'
+      group_colors[group_array[idx]] = color
   group_colors
+
+window.get_color_for_group = (val) ->
+  group_colors[val]
+
+
+  # num_groups = group_array.length
+  # hues = getNiceRandomHues num_groups
+  # colors = group_colors
+  # for hue,idx in hues
+  #   if group_array[idx] not of group_colors
+  #     group_colors[group_array[idx]] = hsv2rgb hue, Math.random() / 2 + .5, Math.random() / 2 + .5
+  # group_colors

@@ -104,7 +120,12 @@
       user = fetch(u)
       user.key == fetch('/current_user').user

-
+  by_date:
+    key: 'date'
+    name: 'By date'
+    pass: (u) ->
+      true
+    options: ['Today', 'Past week', 'Custom']

 window.influence_network = {}
@@ -152,13 +173,13 @@
     total_influence = 0
     for u, amount of influence.influenced
       total_influence += amount
-    if total_influence > max_influence
-      max_influence = total_influence
+    if num_influenced + total_influence > max_influence
+      max_influence = num_influenced + total_influence

     influencer_scores[user] = num_influenced + total_influence

   for user, influence of influence_network
-    influencer_scores[user] /= max_influence
+    influencer_scores[user] /= Math.sqrt(max_influence)

@@ -167,34 +188,6 @@

 default_weights =
-  # weighed_by_recency:
-  #   key: 'weighed_by_recency'
-  #   name: 'Recent'
-  #   label: 'Give greater weight to newer opinions.'
-  #   weight: (u, opinion, proposal) ->
-  #     if !proposal.time_created
-  #       proposals = fetch '/proposals'
-  #       earliest = null
-  #       for p in proposals.proposals
-  #         t = new Date(proposal.created_at).getTime()
-  #         if !earliest || earliest > t
-  #           earliest = t
-  #         proposal.time_created = earliest
-
-  #     latest = Date.now()
-  #     # if !proposal.latest_opinion
-  #     #   opinions = opinionsForProposal proposal
-  #     #   latest = null
-  #     #   for o in opinions
-  #     #     if !latest || o.updated_at > latest.updated_at
-  #     #       latest = o
-  #     #   proposal.latest_opinion = new Date(latest.updated_at).getTime()
-
-  #     # latest = proposal.latest_opinion
-  #     earliest = proposal.time_created
-  #     ot = new Date(opinion.updated_at).getTime()
-
-  #     .1 + .9 * (ot - earliest) / (latest - earliest)

   weighed_by_substantiated:
     key: 'weighed_by_substantiated'
@@ -206,8 +199,8 @@
     icon: (color) ->
       color ?= 'black'
       SVG
-        width: 23
-        height: 23
+        width: 14
+        height: 14
         viewBox: "0 0 23 23"
         dangerouslySetInnerHTML: __html: """
           <g id="Group-8" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
@@ -240,8 +233,8 @@
     icon: (color) ->
       color ?= 'black'
       SVG
-        width: 23
-        height: 23
+        width: 14
+        height: 14
         viewBox: "0 0 23 23"
         dangerouslySetInnerHTML: __html: """
           <g id="weigh" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
@@ -265,8 +258,8 @@
     icon: (color) ->
       color ?= 'black'
       SVG
-        width: 23
-        height: 23
+        width: 14
+        height: 14
         viewBox: "0 0 23 23"
         dangerouslySetInnerHTML: __html: """
           <g id="influence" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
@@ -288,6 +281,9 @@

 toggle_opinion_filter = (view, replace_existing) ->
   _activate_opinion_view(view, 'filter', replace_existing)
+
+set_opinion_date_filter = (view) ->
+  _activate_opinion_view(view, 'date-filter', true)

 _activate_opinion_view = (view, view_type, replace_existing) ->
@@ -333,7 +329,7 @@
           0

       get_group: if view.group? then (u, opinion, proposal) ->
-        view.group(u, opinion, proposal) or 'Unknown'
+        view.group(u, opinion, proposal) or 'Unreported'
       options: if view.group? then view.options

@@ -366,118 +362,8 @@

-styles += """
-  .toggle_buttons {
-    list-style: none;
-    margin: auto;
-    text-align: center
-  }
-  .toggle_buttons li {
-    display: inline-block;
-  }
-  .toggle_buttons button {
-    background-color: white;
-    color: #{focus_blue};
-    font-weight: 600;
-    font-size: 12px;
-    border: 1px solid #{focus_blue};
-    padding: 4px 16px;
-  }
-  .toggle_buttons .first button {
-    border-radius: 8px 0 0 8px;
-    border-right: none;
-  }
-  .toggle_buttons .last button {
-    border-radius: 0px 8px 8px 0px;
-    border-left: none;
-  }
-
-  .toggle_buttons .active button {
-    background-color: #{focus_blue};
-    color: white;
-  }
-"""
-
-ToggleButtons = (items, view_state) ->
-  toggle_state = fetch view_state
-  toggle_state.active ?= items[0]?.label
-
-  toggled = (e, item) ->
-    view_state.active = item.label
-    item.callback?()
-    save view_state
-
-  UL
-    key: 'toggle buttons'
-    className: 'toggle_buttons'
-
-    for item, idx in items
-      do (item) =>
-        LI
-          className: "#{if view_state.active == item.label then 'active' else ''} #{if idx == 0 then 'first' else if idx == items.length - 1 then 'last' else ''}"
-          'data-view-state': item.label
-          BUTTON
-            onClick: (e) -> toggled(e, item)
-            onKeyDown: (e) ->
-              if e.which == 13 || e.which == 32 # ENTER or SPACE
-                toggled(e, item)
-                e.preventDefault()
-
-            item.label
-
-HelpIcon = (help_text, style) ->
-  style ?= {}
-  show_tooltip = (e) ->
-    tooltip = fetch 'tooltip'
-    tooltip.coords = $(e.target).offset()
-    el_with_width = e.target
-    while !el_with_width.offsetWidth
-      el_with_width = el_with_width.parentElement
-    tooltip.coords.left += el_with_width.offsetWidth / 2
-    tooltip.tip = help_text
-    save tooltip
-    e.preventDefault()
-
-  hide_tooltip = (e) ->
-    tooltip = fetch 'tooltip'
-    tooltip.coords = null
-    save tooltip
-
-  BUTTON
-    style: _.defaults style or {},
-      backgroundColor: 'transparent'
-      border: 'none'
-      padding: 0
-      display: 'inline-block'
-      width: style.width or 17
-      height: style.height or 17
-
-    onMouseEnter: show_tooltip
-    onMouseLeave: hide_tooltip
-    onClick: (e) ->
-      if fetch('tooltip').coords
-        hide_tooltip(e)
-      else
-        show_tooltip(e)
-
-    SVG
-      width: style.width or 17
-      height: style.height or 17
-      viewBox: "0 0 17 17.0000094"
-
-      G
-        stroke: "none"
-        strokeWidth: "1"
-        fill: "none"
-        fillRule: "evenodd"
-        G
-          transform: "translate(0.000000, 0.000000)"
-          fill: "#000000"
-          fillRule: "nonzero"
-          PATH
-            d: "M8.5,2.833339 C7.71759653,2.833339 7.08333333,3.4676022 7.08333333,4.25000567 C7.08333333,5.03240913 7.71759653,5.66667233 8.5,5.66667233 C9.28240347,5.66667233 9.91666667,5.03240913 9.91666667,4.25000567 C9.91666667,3.4676022 9.28240347,2.833339 8.5,2.833339 Z M6.61111111,6.61111678 L7.55555556,7.55556122 L7.55555556,13.2222279 L6.61111111,13.2222279 L6.61111111,14.1666723 L10.3888889,14.1666723 L10.3888889,13.2222279 L9.44444444,13.2222279 L9.44444444,6.61111678 L7.55555556,6.61111678 L6.61111111,6.61111678 Z"
-          PATH
-            d: "M8.5,2.14741805e-14 C3.81117296,2.14741805e-14 0,3.81117333 0,8.5 C0,13.1888361 3.81117296,17 8.5,17 C13.188827,17 17,13.1888361 17,8.5 C17,3.81117333 13.188827,2.14741805e-14 8.5,2.14741805e-14 Z M8.5,0.944444444 C12.6784115,0.944444444 16.0555556,4.32158889 16.0555556,8.49999056 C16.0555556,12.6784206 12.6784115,16.055565 8.5,16.055565 C4.32158851,16.055565 0.944444444,12.6784206 0.944444444,8.49999056 C0.944444444,4.32158889 4.32158851,0.944444444 8.5,0.944444444 Z"
+
+

 OpinionViews = ReactiveComponent
@@ -498,6 +384,7 @@
           delete opinion_views_ui[attr]
       save opinion_views_ui

+    has_other_filters = get_participant_attributes().length > 0
     opinion_views = fetch 'opinion_views'
     opinion_views_ui = fetch 'opinion_views_ui'
     view_buttons = [
@@ -513,7 +400,10 @@
       },
       {
         label: 'More views'
-        callback: ->
+        callback: (item, previous_state) ->
+          if previous_state == 'More views'
+            opinion_views_ui.active = 'All opinions'
+            save opinion_views_ui
           clear_all()
       }
     ]
@@ -534,11 +424,19 @@

       if opinion_views_ui.active == 'More views'
         needs_expansion = @props.additional_width && @props.style?.width
+        width = 0
+        if needs_expansion
+          if has_other_filters
+            width = @props.style.width + @props.additional_width
+          else
+            width = Math.min(660, @props.style.width + @props.additional_width)
+
         DIV
           style:
-            width: if needs_expansion then @props.style.width + @props.additional_width else '100%'
+            width: if width then width
+            maxWidth: if !has_other_filters then 660
             position: 'relative'
-            right: if needs_expansion then @props.additional_width # - @props.style.width
+            right: if needs_expansion then width - @props.style.width
             border: '1px solid #B6B6B6'
             borderRadius: 8
             marginTop: 18
@@ -547,7 +445,7 @@
           DIV
             style:
               position: 'absolute'
-              left: (document.querySelector('[data-view-state="More views"]')?.offsetLeft or 60) + 35
+              left: (document.querySelector('[data-view-state="More views"]')?.offsetLeft or 60) + 35 - (if needs_expansion then (@props.style.width + @props.additional_width - width ) else 0)
               top: -17

             dangerouslySetInnerHTML: __html: """<svg width="25px" height="13px" viewBox="0 0 25 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g id="Page-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g id="Artboard" transform="translate(-1086.000000, -586.000000)" fill="#FFFFFF" stroke="#979797"><polyline id="Path" points="1087 599 1098.5 586 1110 599"></polyline></g></g></svg>"""
@@ -557,15 +455,151 @@
               display: 'flex'

             OpinionFilters()
-
             OpinionWeights()
+
+
+
+DateFilters = ->
+  opinion_views = fetch 'opinion_views'
+
+  cb = (activated) ->
+    pass = (u, opinion, proposal) ->
+      date = new Date(opinion.updated_at).getTime()
+      now = Date.now()
+
+      earliest = latest = null
+      if activated.label != 'Custom'
+        clear_custom_date()
+
+      if activated.label == 'Today'
+        earliest = now - 1000 * 60 * 60 * 24
+
+      else if activated.label == 'Past week'
+        earliest = now - 1000 * 60 * 60 * 24 * 7
+
+      else if activated.label == 'Custom'
+
+        if date_toggle_state.start
+          earliest = date_toggle_state.start
+
+        if date_toggle_state.end
+          latest = date_toggle_state.end
+
+      (earliest == null || earliest <= date) && (latest == null || latest >= date)
+
+    view =
+      key: 'date-filter'
+      salience: (u, opinion, proposal) -> if pass(u, opinion, proposal) then 1 else .1
+      weight:   (u, opinion, proposal) -> if pass(u, opinion, proposal) then 1 else .1
+
+    set_opinion_date_filter view
+
+  clear_custom_date = ->
+    date_toggle_state.start = null
+    date_toggle_state.end = null
+    save date_toggle_state
+
+
+  has_other_filters = get_participant_attributes().length > 0
+
+
+  date_toggle_state = fetch 'opinion-date-filter'
+  date_options = [
+    {
+      label: 'All'
+      callback: ->
+        clear_custom_date()
+        if opinion_views.active_views['date-filter']
+          delete opinion_views.active_views['date-filter']
+          save opinion_views
+    }
+    { label: 'Today', callback: cb }
+    { label: 'Past week', callback: cb }
+    { label: 'Custom', callback: cb }
+  ]
+
+  DIV
+    className: 'grays' # for toggle buttons
+    SPAN
+      className: 'opinion_view_name'
+      style:
+        paddingRight: 8
+      'Filter opinions by date:'
+
+    ToggleButtons date_options, date_toggle_state,
+      display: if has_other_filters then 'inline-block'
+      marginTop: if !has_other_filters then 8
+
+    if date_toggle_state.active == 'Custom'
+
+      DIV
+        className: 'opinion-date-filter'
+
+        LABEL null,
+          'From:'
+        INPUT
+          type: 'date'
+          id: 'start'
+          name: 'opinion-start'
+          onChange: (e) ->
+            date_toggle_state.start = new Date(e.target.value).getTime()
+            save date_toggle_state
+            cb date_options[3]
+            e.preventDefault()
+
+        LABEL null,
+          'To:'
+        INPUT
+          type: 'date'
+          id: 'end'
+          name: 'opinion-end'
+          onChange: (e) ->
+            date_toggle_state.end = new Date(e.target.value).getTime()
+            save date_toggle_state
+            cb date_options[3]
+            e.preventDefault()
+
+
+# Attributes are additional filters that can distinguish amongst all
+# participants. We take them from legacy opinion views, and from
+# qualifying user_tags
+window.get_participant_attributes = ->
+  attributes = []
+  is_admin = fetch('/current_user').is_admin
+  show_others = !customization('hide_opinions') || is_admin
+  custom_filters = customization 'opinion_views'
+  user_tags = customization 'user_tags'
+
+  if show_others
+    if custom_filters
+      for filter in custom_filters
+        if filter.visibility == 'open' || is_admin
+          if filter.pass
+            attributes.push _.extend {}, filter,
+              name: filter.label
+              options: [true, false]
+
+    if user_tags
+      for tag in user_tags
+        name = tag.key
+        if (tag.visibility == 'open' || is_admin) && \
+           (tag.self_report?.input in ['dropdown', 'boolean', 'checklist']) && \
+           !tag.no_opinion_view # set in the user_tags customization to prevent an opinion view from automatically getting created
+
+          attributes.push
+            key: name
+            name: tag.view_name or tag.name or tag.self_report?.question or name
+            pass: do(name) -> (u, value) ->
+              u.tags[name] == value
+            options: tag.self_report.options or (if tag.self_report.input == 'boolean' then [true, false])
+            input_type: tag.self_report?.input
+
+  attributes

 OpinionFilters = ReactiveComponent
   displayName: 'OpinionFilters'
   render: ->
-    custom_filters = customization 'opinion_views'
-    user_tags = customization 'user_tags'

     opinion_views = fetch 'opinion_views'
     opinion_views_ui = fetch 'opinion_views_ui'
@@ -573,38 +607,7 @@
     is_admin = fetch('/current_user').is_admin
     show_others = !customization('hide_opinions') || is_admin

-
-    # Attributes are additional filters that can distinguish amongst all
-    # participants. We take them from legacy opinion views, and from
-    # qualifying user_tags
-    attributes = []
-    if show_others
-
-      # everyone has a filter by date
-      attributes.push default_filters.by_date
-
-      if custom_filters
-        for filter in custom_filters
-          if filter.visibility == 'open' || is_admin
-            if filter.pass
-              attributes.push _.extend {}, filter,
-                name: filter.label
-                options: [true, false]
-
-      if user_tags
-        for name, tag of user_tags
-
-          if (tag.visibility == 'open' || is_admin) && \
-             (tag.self_report?.input in ['dropdown', 'boolean', 'checklist']) && \
-             !tag.no_opinion_view # set in the user_tags customization to prevent an opinion view from automatically getting created
-
-            attributes.push
-              key: name
-              name: tag.name or tag.self_report?.question or name
-              pass: do(name) -> (u, value) -> u.tags[name] == value
-              options: tag.self_report.options or (if tag.self_report.input == 'boolean' then [true, false])
-              input_type: tag.self_report?.input
-
+    attributes = get_participant_attributes()

     active_filters = {}
     for k,v of opinion_views.active_views
@@ -637,21 +640,21 @@
         opinion_views_ui.selected_vals_for_attribute[attribute] = {}

       pass = (u) ->
-          user = fetch(u)
-          val_for_user = user.tags[attribute]
-          is_array = Array.isArray(val_for_user)
-
-          passing_vals = (val for val,enabled of opinion_views_ui.selected_vals_for_attribute[attribute] when enabled)
-
-          passes = false
-          for passing_val in passing_vals
-            if passing_val == 'true'
-              passing_val = true
-            else if passing_val == 'false'
-              passing_val = false
-            passes ||= val_for_user == passing_val || (is_array && passing_val in val_for_user)
-
-          passes
+        user = fetch(u)
+        val_for_user = user.tags[attribute]
+        is_array = Array.isArray(val_for_user)
+
+        passing_vals = (val for val,enabled of opinion_views_ui.selected_vals_for_attribute[attribute] when enabled)
+
+        passes = false
+        for passing_val in passing_vals
+          if passing_val == 'true'
+            passing_val = true
+          else if passing_val == 'false'
+            passing_val = false
+          passes ||= val_for_user == passing_val || (is_array && passing_val in val_for_user)
+
+        passes

       view =
         key: attribute
@@ -677,110 +680,128 @@
         opinion_views_ui.group_by = null
       save opinion_views_ui

-
-
     if opinion_views_ui.group_by
       all_groups = opinion_views.active_views.group_by.options
       group_colors = get_color_for_groups all_groups
+
+
+
+
+

     DIV
       style:
         flex: 1
         paddingRight: 24

-      DIV null,
-        SPAN
-          className: 'opinion_view_name'
+
+
+      DateFilters()
+
+
+      if attributes.length > 0
+        DIV
           style:
-            paddingRight: 8
-          'Add opinion filter:'
-
-
-        UL
-          style:
-            listStyle: 'none'
-            display: 'inline'
+            marginTop: 18
+
+
+
+          DIV
+            style:
+              display: 'flex'
+            SPAN
+              className: 'opinion_view_name'
+              style:
+                paddingRight: 16
+                whiteSpace: 'nowrap'
+              'More filters:'
+
+
+            UL
+              style:
+                listStyle: 'none'
+                display: 'inline'
+
+
+              for attribute, cnt in attributes
+                continue if opinion_views_ui.visible_attributes[attribute.key]
+                do (attribute) ->
+                  LI
+                    style:
+                      display: 'inline-block'
+                    BUTTON
+                      className: 'filter opinion_view_button'
+                      onClick: -> toggle_attribute_visible(attribute)
+                      onKeyDown: (e) =>
+                        if e.which == 13 || e.which == 32 # ENTER or SPACE
+                          toggle_attribute_visible(attribute)
+                          e.preventDefault()
+
+                      "+ #{attribute.name}"

           for attribute, cnt in attributes
-            continue if opinion_views_ui.visible_attributes[attribute.key]
-            do (attribute) ->
-              LI
-                style:
-                  display: 'inline-block'
+            continue if !opinion_views_ui.visible_attributes[attribute.key]
+            do (attribute) =>
+
+              DIV
+                className: 'attribute_group'
+
                 BUTTON
-                  className: 'filter opinion_view_button'
+                  className: 'attribute_group_header'
                   onClick: -> toggle_attribute_visible(attribute)
                   onKeyDown: (e) =>
                     if e.which == 13 || e.which == 32 # ENTER or SPACE
                       toggle_attribute_visible(attribute)
                       e.preventDefault()

-                  "+ #{attribute.name}"
-
-
-        for attribute, cnt in attributes
-          continue if !opinion_views_ui.visible_attributes[attribute.key]
-          do (attribute) =>
-
-            DIV
-              className: 'attribute_group'
-
-              BUTTON
-                onClick: -> toggle_attribute_visible(attribute)
-                onKeyDown: (e) =>
-                  if e.which == 13 || e.which == 32 # ENTER or SPACE
-                    toggle_attribute_visible(attribute)
-                    e.preventDefault()
-
-                "#{attribute.name}"
-
-                SPAN
-                  style:
-                    float: 'right'
-                  'x'
-
-              UL
-                style:
-                  border: '1px solid #D9D9D9'
-                  borderTop: 'none'
-                  padding: '2px 4px 4px 4px'
-                  borderRadius: '0 0 8px 8px'
-
-                for val in attribute.options
-                  is_grouped = opinion_views_ui.group_by == attribute.key
-                  checked = !!opinion_views_ui.selected_vals_for_attribute[attribute.key][val]
-
-                  do (val) =>
-                    LI
-                      style:
-                        display: 'inline-block'
-                      LABEL
-                        className: "attribute_value_selector"
-
-                        SPAN
-                          className: if is_grouped then 'toggle_switch' else ''
-
-                          INPUT
-                            type: 'checkbox'
-                            # className: 'bigger'
-                            value: val
-                            checked: checked
-                            onChange: (e) ->
-                              # create a view on the fly for this attribute
-                              opinion_views_ui.selected_vals_for_attribute[attribute.key][val] = e.target.checked
-                              save opinion_views_ui
-                              update_view_for_attribute(attribute)
-
-                          if is_grouped
+                  "#{attribute.name}"
+
+                  SPAN
+                    style:
+                      float: 'right'
+                    'x'
+
+                if attribute.render
+                  attribute.render()
+                else
+                  UL
+                    className: 'attribute_container'
+
+                    for val in attribute.options
+                      is_grouped = opinion_views_ui.group_by == attribute.key
+                      checked = !!opinion_views_ui.selected_vals_for_attribute[attribute.key][val]
+
+                      do (val) =>
+                        LI
+                          style:
+                            display: 'inline-block'
+                          LABEL
+                            className: "attribute_value_selector"
+
+                            SPAN
+                              className: if is_grouped then 'toggle_switch' else ''
+
+                              INPUT
+                                type: 'checkbox'
+                                # className: 'bigger'
+                                value: val
+                                checked: checked
+                                onChange: (e) ->
+                                  # create a view on the fly for this attribute
+                                  opinion_views_ui.selected_vals_for_attribute[attribute.key][val] = e.target.checked
+                                  save opinion_views_ui
+                                  update_view_for_attribute(attribute)
+
+                              if is_grouped
+                                SPAN
+                                  className: 'toggle_switch_circle'
+                                  style:
+                                    backgroundColor: if checked then group_colors[val]
+
                             SPAN
-                              className: 'toggle_switch_circle'
-                              style:
-                                backgroundColor: if checked then group_colors[val]
-
-                        SPAN
-                          className: 'attribute_value_value'
-                          "#{val}"
+                              className: 'attribute_value_value'
+                              "#{val}"

       if attributes.length > 0
         cur_val = -1
@@ -799,14 +820,18 @@
             SPAN
               className: 'opinion_view_name'
               style:
-                paddingRight: 8
-
-              "Group opinions by: "
+                paddingRight: 16
+
+              "Group opinions by:"

             SELECT
               style:
                 maxWidth: '100%'
                 marginRight: 12
+                borderColor: '#bbb'
+                backgroundColor: '#f9f9f9'
+                borderRadius: 2
+
               onChange: (ev) ->
                 if ev.target.value != null
                   attribute = attributes[ev.target.value]
@@ -814,20 +839,20 @@
                 else
                   opinion_views_ui.group_by = null

-                if !opinion_views_ui.visible_attributes[opinion_views_ui.group_by]
-                  !opinion_views_ui.visible_attributes[opinion_views_ui.group_by] = true
+                if opinion_views_ui.group_by && (!opinion_views_ui.visible_attributes[opinion_views_ui.group_by] ||  \
+                                    (o for o,val of opinion_views_ui.selected_vals_for_attribute[opinion_views_ui.group_by] when val).length == 0)
+                              # if no attribute value is selected, which mean all are enabled, select them all
+                  opinion_views_ui.visible_attributes[opinion_views_ui.group_by] = true
                   for option in attribute.options
                     opinion_views_ui.selected_vals_for_attribute[attribute.key][option] = true
                 save opinion_views_ui

-
-
                 if opinion_views_ui.group_by
                   view =
                     key: 'group_by'
-                    name: "Group by #{opinion_views_ui.group_by}"
+                    name: attribute.name
                     group: (u, opinion, proposal) ->
-                      group_val = fetch(u).tags[opinion_views_ui.group_by] or 'Unknown'
+                      group_val = fetch(u).tags[opinion_views_ui.group_by] or 'Unreported'
                       if attribute.input_type == 'checklist'
                         group_val.split(',')
                       else
@@ -867,7 +892,7 @@

     DIV
       style:
-        padding: '0px 24px 0 48px'
+        paddingLeft: 36
         borderLeft: '1px solid #DEDDDD'

       DIV
@@ -890,7 +915,7 @@
                 alignItems: 'center'

               BUTTON
-                className: "weight opinion_view_button #{if activated_weights[k] then 'active'}"
+                className: "weight opinion_view_button #{if activated_weights[k] then 'active' else ''}"
                 onClick: ->
                   toggle_weight v
                 onKeyDown: (e) ->
@@ -900,12 +925,13 @@

                 if v.icon
-                  SPAN
-                    style:
-                      paddingRight: 12
-                    v.icon if activated_weights[k] then 'white'
-
-                v.name
+                  v.icon if activated_weights[k] then 'white'
+
+                SPAN
+                  style:
+                    paddingLeft: if v.icon then 10
+
+                  v.name

               HelpIcon v.label,
                 width: 18
@@ -1073,41 +1099,26 @@

 styles += """
-  button.filter-weight-sort-button {
-    background-color: #666;
-    color: white;
-    vertical-align: middle;
-    border: 1px solid #464646;
-    box-shadow: 0px 1px 2px rgba(0,0,0,.5);
-    border-radius: 8px;
-    display: flex;
-    align-items: center;
-    height: 34px;
-    font-family: 'Fira Sans Condensed';
-    text-transform: uppercase;
-    font-size: 14px;
-    font-weight: 600;
-
-  }

   button.opinion_view_button {
     border: 1px solid #E0E0E0;
+    border-bottom-color: #aaa;
     background-color: #F0F0F0;
-    box-shadow: inset 0 -1px 1px 0 rgba(0,0,0,0.62);
+    // box-shadow: inset 0 -1px 1px 0 rgba(0,0,0,0.62);
     border-radius: 8px;
     font-size: 12px;
     color: #000000;
     font-weight: 400;
   }
   button.opinion_view_button.filter {
-    padding: 6px 18px;
-    margin: 0 4px 4px 0;
+    padding: 4px 12px;
+    margin: 0 8px 8px 0;

   }
   button.opinion_view_button.weight {
     width: 100%;
     display: flex;
-    padding: 8px 24px;
+    padding: 4px 12px 4px 12px;
     text-align: left;
     align-items: center;
     margin-right: 12px;
@@ -1138,7 +1149,13 @@
     margin-top: 8px;
   }

-  .attribute_group button {
+  .attribute_container {
+    border: 1px solid #D9D9D9;
+    border-top: none;
+    padding: 2px 4px 4px 4px;
+    border-radius: 0 0 8px 8px;
+  }
+  .attribute_group .attribute_group_header {
     background: #F0F0F0;
     border: 1px solid #E0E0E0;
     border-radius: 8px 8px 0 0;
@@ -1162,6 +1179,20 @@
     font-size: 12px;
     font-weight: 400;
     font-family: 'Fira Sans Condensed';
+  }
+
+  .opinion-date-filter {
+    margin-top: 8px;
+  }
+  .opinion-date-filter label {
+    padding: 0 8px 0 18px;
+    color: #666;
+    font-size: 12px;
+  }
+
+  .opinion-date-filter input {
+    font-size: 12px;
+    width: 128px;
   }

 """
@@ -1210,4 +1241,80 @@
 #   tick 1000

+styles += """
+  .toggle_buttons {
+    list-style: none;
+    margin: auto;
+    text-align: center
+  }
+  .toggle_buttons li {
+    display: inline-block;
+  }
+  button.sort_proposals {
+    border-radius: 8px;
+  }
+  button.sort_proposals, .toggle_buttons button {
+    background-color: white;
+    color: #{focus_blue};
+    font-weight: 600;
+    font-size: 12px;
+    border: 1px solid;
+    border-color: #{focus_blue};
+    border-right: none;
+    padding: 4px 16px;
+  }
+  .toggle_buttons li:first-child button {
+    border-radius: 8px 0 0 8px;
+    border-right: none;
+  }
+  .toggle_buttons li:last-child button {
+    border-radius: 0px 8px 8px 0px;
+    border-right: 1px solid;
+  }
+
+  button.sort_proposals, .toggle_buttons .active button {
+    background-color: #{focus_blue};
+    color: white;
+  }
+
+  .grays .toggle_buttons button {
+    color: #444;
+    border-color: #444;
+  }
+  .grays .toggle_buttons .active button {
+    background-color: #444;
+    color: white;
+  }
+"""
+
+window.ToggleButtons = (items, view_state, style) ->
+  toggle_state = fetch view_state
+  toggle_state.active ?= items[0]?.label
+
+  toggled = (e, item) ->
+    prev = view_state.active
+    view_state.active = item.label
+    save view_state
+
+    item.callback?(item, prev)
+
+  UL
+    key: 'toggle buttons'
+    className: 'toggle_buttons'
+    style: style or {}
+
+    for item in items
+      do (item) =>
+        LI
+          className: if view_state.active == item.label then 'active'
+          'data-view-state': item.label
+          BUTTON
+            onClick: (e) -> toggled(e, item)
+            onKeyDown: (e) ->
+              if e.which == 13 || e.which == 32 # ENTER or SPACE
+                toggled(e, item)
+                e.preventDefault()
+
+            item.label
+
 window.OpinionViews = OpinionViews


Fix mega check

Fix mega check

During development, I accidentally broken it by removing tier
when the intention was just to remove the default parameter (which
was quite useless, really).

--- js/app.coffee
+++ js/app.coffee
@@ -67,13 +67,13 @@
   @checkDoublesTier = (tier, pokemon) ->
       @tiers[pokemon.doublesTier] >= @tiers[tier] and not pokemon.legendary and @checkDoublesMega pokemon, tier

-  @checkMega = (pokemon) ->
+  @checkMega = (pokemon, tier) ->
     if @megas
       true
     else
       not pokemon.mega or @tiers[pokemon.mega.tier] < @tiers[tier]

-  @checkDoublesMega = (pokemon) ->
+  @checkDoublesMega = (pokemon, tier) ->
     if @megas
       true
     else


Allow skipping modifier keys in bindings for KeyHandlerMode instances


Fix process completion detection

Fix process completion detection

--- src/tools/routine.idle.coffee
+++ src/tools/routine.idle.coffee
@@ -5,6 +5,8 @@
 routine._ = []

 module.exports = ->
-  routine._ = for callback in routine._ when not callback()
-    callback
+  was = routine._
+  routine._ = will = []
+  for callback in was when not callback()
+    will.push callback
   return