Aggiunto comando per rubrica

Aggiunto comando per rubrica

--- scripts/example.coffee
+++ scripts/example.coffee
@@ -1,106 +1,18 @@
-# Description:
-#   Example scripts for you to examine and try out.
-#
-# Notes:
-#   They are commented out by default, because most of them are pretty silly and
-#   wouldn't be useful and amusing enough for day to day huboting.
-#   Uncomment the ones you want to try and experiment with.
-#
-#   These are from the scripting documentation: https://github.com/github/hubot/blob/master/docs/scripting.md
-
 module.exports = (robot) ->

-  # robot.hear /badger/i, (res) ->
-  #   res.send "Badgers? BADGERS? WE DON'T NEED NO STINKIN BADGERS"
-  #
-  # robot.respond /open the (.*) doors/i, (res) ->
-  #   doorType = res.match[1]
-  #   if doorType is "pod bay"
-  #     res.reply "I'm afraid I can't let you do that."
-  #   else
-  #     res.reply "Opening #{doorType} doors"
-  #
-  # robot.hear /I like pie/i, (res) ->
-  #   res.emote "makes a freshly baked pie"
-  #
-  # lulz = ['lol', 'rofl', 'lmao']
-  #
-  # robot.respond /lulz/i, (res) ->
-  #   res.send res.random lulz
-  #
-  # robot.topic (res) ->
-  #   res.send "#{res.message.text}? That's a Paddlin'"
-  #
-  #
-  # enterReplies = ['Hi', 'Target Acquired', 'Firing', 'Hello friend.', 'Gotcha', 'I see you']
-  # leaveReplies = ['Are you still there?', 'Target lost', 'Searching']
-  #
-  # robot.enter (res) ->
-  #   res.send res.random enterReplies
-  # robot.leave (res) ->
-  #   res.send res.random leaveReplies
-  #
-  # answer = process.env.HUBOT_ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING
-  #
-  # robot.respond /what is the answer to the ultimate question of life/, (res) ->
-  #   unless answer?
-  #     res.send "Missing HUBOT_ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING in environment: please set and try again"
-  #     return
-  #   res.send "#{answer}, but what is the question?"
-  #
-  # robot.respond /you are a little slow/, (res) ->
-  #   setTimeout () ->
-  #     res.send "Who you calling 'slow'?"
-  #   , 60 * 1000
-  #
-  # annoyIntervalId = null
-  #
-  # robot.respond /annoy me/, (res) ->
-  #   if annoyIntervalId
-  #     res.send "AAAAAAAAAAAEEEEEEEEEEEEEEEEEEEEEEEEIIIIIIIIHHHHHHHHHH"
-  #     return
-  #
-  #   res.send "Hey, want to hear the most annoying sound in the world?"
-  #   annoyIntervalId = setInterval () ->
-  #     res.send "AAAAAAAAAAAEEEEEEEEEEEEEEEEEEEEEEEEIIIIIIIIHHHHHHHHHH"
-  #   , 1000
-  #
-  # robot.respond /unannoy me/, (res) ->
-  #   if annoyIntervalId
-  #     res.send "GUYS, GUYS, GUYS!"
-  #     clearInterval(annoyIntervalId)
-  #     annoyIntervalId = null
-  #   else
-  #     res.send "Not annoying you right now, am I?"
-  #
-  #
-  # robot.router.post '/hubot/chatsecrets/:room', (req, res) ->
-  #   room   = req.params.room
-  #   data   = JSON.parse req.body.payload
-  #   secret = data.secret
-  #
-  #   robot.messageRoom room, "I have a secret: #{secret}"
-  #
-  #   res.send 'OK'
-  #
-  # robot.error (err, res) ->
-  #   robot.logger.error "DOES NOT COMPUTE"
-  #
-  #   if res?
-  #     res.reply "DOES NOT COMPUTE"
-  #
-  # robot.respond /have a soda/i, (res) ->
-  #   # Get number of sodas had (coerced to a number).
-  #   sodasHad = robot.brain.get('totalSodas') * 1 or 0
-  #
-  #   if sodasHad > 4
-  #     res.reply "I'm too fizzy.."
-  #
-  #   else
-  #     res.reply 'Sure!'
-  #
-  #     robot.brain.set 'totalSodas', sodasHad+1
-  #
-  # robot.respond /sleep it off/i, (res) ->
-  #   robot.brain.set 'totalSodas', 0
-  #   res.reply 'zzzzz'
+	robot.respond /telefono (.*)/i, (res) ->
+		nome = res.match[1]
+		rubrica = [
+			["Abbruzzese Guglielmo","0692710 369, 335 1346777"], ["Ambrosino Tullio","0692710 355"], ["Anzuini Emanuele","0692710 412"], ["Avella Massimiliano","0692710 237, 334 5952310"], ["Bellini Lorenzo",", 335 7160411"], ["Berni Mirko","0692710 302, 333 4970764"], ["Bevilacqua Ovidio","0692710"], ["Bianconi Fabio","0692710 367"], ["Bicocchi Andrea","0692710 547, 335 8095023"], ["Bidello Mario","0692710 219, 334 6809720"], ["Bonifazi Massimiliano","0692710 222"], ["Borro Andrea","0692710 243"], ["Braida Duilio","0692710 236, 333 6053185"], ["Brugnone Pierluigi","0692710 469, 334 6719787"], ["Bruni Pierluigi","0692710 380, 335 8095042"], ["Bruzzese Emiliano","0692710 258"], ["Cacciotti Emanuele","0692710 365"], ["Cannatà Carlo","0692710 368, 335 5997684"], ["Cannavale Alessandro","0692710 531, 335 1809157"], ["Caporale Francesco Alessandro","0692710 530"], ["Cappello Giacomo","0692710 466"], ["Carinci Umberto","0692710 396, 335 6985736"], ["Cavallo Andrea","0692710 233, 331 6817265"], ["Cesarano Nunzio","0692710 402"], ["Chieruzzi Andrea","0692710 306"], ["Cimino Vincenzo","0692710 406, 338 1171511"], ["Cioccolanti Luciano","0692710 388, 334 6719777"], ["Cirillo Lucio","0692710, 335 5997666"], ["Coco Remigio","0692710 541"], ["Coen David","0692710 264"], ["Collalto Mauro","0692710 204, 335 6639863"], ["Corrieri Fabio","0692710 274, 335 1857857"], ["Corsi Fabio","0692710, 340 5060172"], ["D'Angelo Roberto","0692710 374, 331 6817264"], ["D'Antonangelo Dario","0692710 279, 334 3099138"], ["D'Incertopadre Cristian","0692710 460, 338 3038912"], ["De Angelis Andrea","0692710 350, 335 7707927"], ["De Cenzo Giancarlo","0692710 307"], ["De Francesco - Test Plant","0692710 470"], ["De Francesco Fabrizio","0692710 273"], ["De Leoni Giorgio","0692710, 335 5997653"], ["De Maria Massimo","0692710 308, 335 1891627"], ["De Sortis Francesco","0692710 309, 333 8745359"], ["De Sortis Mario","0692710 322"], ["Del Vecchio Cesare","0692710 532, 335 8095008"], ["Delmiglio Alessandro","0692710 403"], ["Di Donna Marco","0692710 346"], ["Di Felice Alberto","0692710 268"], ["Di Giovanni Giandomenico","0692710 545"], ["Di Minica Daniele","0692710 246, 331 4926974"], ["Di Pietro Maurizio","0692710 310, 349 3940026"], ["Di Pietro Oreste","0692710 534"], ["Donello Valerio","0692710 241"], ["Felici Demis","0692710 311"], ["Felici Gianni","0692710 524"], ["Felici Mauro","0692710 226, 333 4970712"], ["Felici Pierluigi","0692710 312"], ["Felici Roberto","0692710 228, 335 8200251"], ["Ferrazza Ascenzo","0692710 411, 339 5458901"], ["Foletto Gabriele","0692710 313"], ["Fontana Emanuele","0692710 387, 333 6303379"], ["Fortini Fabio","0692710 249, 335 7696680"], ["Fumo Nicodemo","0692710 463"], ["Funari Marco","0692710 262, 333 4991130"], ["Gallelli Sergio","0692710 375, 331 6759369"], ["Gazzolo Severino","0692710 315"], ["Giannì Carmine","0692710 275"], ["Granato Marco","0692710 316"], ["Granato Roberto","0692710 317"], ["Grandini Irene","0692710 381"], ["Grasso Tommaso","0692710 373"], ["Guagnano Andres","0692710 544, 335 5970853"], ["Guerrieri Pietro","0692710 323"], ["Guerriero Alessandro","0692710 372"], ["Iannazzo Alessandro",", 334 6135746"], ["Lancio Riccardo","0692710 242, 333 9200553"], ["Lanzuisi Matteo","0692710 339"], ["Latella Germana","0692710 370, 335 8051924"], ["Lavalle Guido","0692710 239"], ["Leanza Mirko","0692710 209, 366 6758427"], ["Leto Giuseppe","0692710 255, 333 4991043"], ["Loretelli Luca","0692710 331"], ["Lozzi Marco","0692710 272"], ["Luppi Adriano","0692710 229, 333 9200405"], ["Mancini Alessandro","0692710 319, 335 1409012"], ["Marini Nicola","0692710391, 333 6053139"], ["Martini Marco","0692710 320"], ["Mengozzi Mario",""], ["Menozzi Marcello","0692710 210, 335 7940517"], ["Merola Salvatore","0692710 265"], ["Meschino Roberto","0692710 251, 334 6727478"], ["Monaco Federico","0692710 389"], ["Nalli Federico","0692710 366"], ["Nirchi Daniele","0692710 384"], ["Origlia Roberto","0692710 513, 339 7753000"], ["Pacetti Demetrio","0692710 385"], ["Paoletti Ermanno","0692710 276, 333 4991192"], ["Pellegrini Diego ","0692710 361"], ["Pellegrini Fausto","0692710 215, 339 3180897"], ["Percoco Walter","0692710 591"], ["Piccardo Sabrina","0692710 325, 335 5665508"], ["Piccolo Paolo","0692710 410"], ["Pierini Pierluigi","0692710 326, 333 4991491"], ["Pizzo Roberto","0692710 240"], ["Pontremolesi Luigi","0692710 408"], ["Ponzi Alessandro","0692710 327, 331 6442333"], ["Provinciali David",", 334 6719782"], ["Pullia Alberto","0692710 540, 335 8095006"], ["Puntuorno Leopoldo","0692710 335"], ["Recchia Emanuele","0692710 328, 334 6727475"], ["Repetto Fabio","0692710 546"], ["Ricci Roberto","0692710 329, 335 8051907"], ["Righetti Daniele","0692710 330"], ["Romani Claudio","0692710 351"], ["Romani Fabio","0692710 404, 339 7855584"], ["Romani Fabio (IPS)","0692710 304"], ["Romani Mario","0692710 205"], ["Rosciarelli Fabrizio","0692710 383, 331 6817290"], ["Rozzi Renato","0692710 332"], ["Rucci Daniele","0692710 269, 334 2508860"], ["Santarelli Francesco","0692710 376"], ["Santillo Ugo","0692710 521, 335 7483217"], ["Santinelli Luciano","0692710 347, 333 4970404"], ["Sartori Danilo Lauro","0692710 235"], ["Scarano Andrea","0692710 400"], ["Serafinelli Fabio","0692710 305"], ["Siragusa Vincenzo","0692710 472"], ["Sperduti Alberto","0692710 377, 335 5997656"], ["Sperduti Stefano","0692710 337"], ["Spinilli Walter","0692710 234"], ["Squitti Mario","0692710 220, 338 6246611"], ["Talamini Alcide Paolo","0692710 211"], ["Testa Carlo","0692710 267, 335 6985707"], ["Testi Andrea","0692710 537, 335 1891625"], ["Tomaselli Fabio",""], ["Vannozzi Federico","0692710 340, 333 6918755"], ["Venafro Mauro","0692710 254"], ["Verdi Dario","0692710 212, 335 8295952"], ["Zurlo Massimiliano","0692710 378"]
+			]
+
+		con = for n in rubrica
+			n[0] + ": " + n[1] if n[0].toLowerCase().indexOf(nome.toLowerCase())>=0
+
+		a = ""
+		for r in con
+			a = a + r + "\n" if r?
+
+		a = "Non lo trovo, sicuro che l'hai scritto bene?" if a==""
+
+		res.reply a


cleaning and shrinking the option parser

--- optparse.coffee
+++ optparse.coffee
@@ -1,19 +1,22 @@
-# Create an OptionParser with a list of valid options.
+# Create an OptionParser with a list of valid options, in the form:
+#   [short-flag (optional), long-flag, description]
+# And an optional banner for the usage help.
 op: exports.OptionParser: (rules, banner) ->
-  @banner:        banner or 'Usage: [Options]'
-  @options_title: 'Available options:'
-  @rules:         build_rules(rules)
+  @banner:  banner or 'Usage: [Options]'
+  @rules:   build_rules(rules)
   this

-# Parse the argument array, calling defined callbacks, returning the remaining non-option arguments.
+# Parse the argument array, populating an options object with all of the
+# specified options, and returning it. options.arguments will be an array
+# containing the remaning non-option arguments.
 op::parse: (args) ->
   options: {arguments: []}
-  args:   args.concat []
-  while (arg: args.shift())
+  args: args.slice 0
+  while arg: args.shift()
     is_option: false
     for rule in @rules
       if rule.letter is arg or rule.flag is arg
-        options[rule.name]: if rule.argument then args.shift() else true
+        options[rule.name]: if rule.has_argument then args.shift() else true
         is_option: true
         break
     options.arguments.push arg unless is_option
@@ -21,21 +24,13 @@

 # Return the help text for this OptionParser, for --help and such.
 op::help: ->
-  longest: 0
-  has_shorts: false
-  lines: [@banner, '', @options_title]
+  lines: [@banner, '', 'Available options:']
   for rule in @rules
-    has_shorts: true if rule.letter
-    longest: rule.flag.length if rule.flag.length > longest
-  for rule in @rules
-    if has_shorts
-      text: if rule.letter then spaces(2) + rule.letter + ', ' else spaces(6)
-    text += spaces(longest, rule.flag) + spaces(3)
-    text += rule.description
-    lines.push text
+    spaces:   15 - rule.flag.length
+    spaces:   if spaces > 0 then (' ' for i in [0..spaces]).join('') else ''
+    let_part: if rule.letter then rule.letter + ', ' else '    '
+    lines.push '  ' + let_part + rule.flag + spaces + rule.description
   lines.join('\n')
-
-# Private:

 # Regex matchers for option flags.
 LONG_FLAG:  /^(--[\w\-]+)/
@@ -46,27 +41,17 @@
 # [letter-flag, long-flag, help], or [long-flag, help].
 build_rules: (rules) ->
   for tuple in rules
-    tuple.unshift(null) if tuple.length < 3
-    build_rule(tuple...)
+    tuple.unshift null if tuple.length < 3
+    build_rule tuple...

 # Build a rule from a short-letter-flag, long-form-flag, and help text.
 build_rule: (letter, flag, description) ->
   match: flag.match(OPTIONAL)
   flag:  flag.match(LONG_FLAG)[1]
   {
-    name:         flag.substr(2)
+    name:         flag.substr 2
     letter:       letter
     flag:         flag
     description:  description
-    argument:     !!(match and match[1])
+    has_argument: !!(match and match[1])
   }
-
-# Space-pad a string with the specified number of characters.
-spaces: (num, text) ->
-  builder: []
-  if text
-    return text if text.length >= num
-    num -= text.length
-    builder.push text
-  while num -= 1 then builder.push ' '
-  builder.join ''


tag-bank port bug fix

tag-bank port bug fix

--- source/01__file_bank.coffee
+++ source/01__file_bank.coffee
@@ -5,16 +5,16 @@
     constructor: (options) ->
         super options

-        {@protocol, @hostname, @port, @path, @extensions} = options
+        {@protocol, @hostname, @path, @extensions} = options
+
+        if options.port?
+            @port = options.port

         if not @protocol?
             @protocol = window.location.protocol

         if not @hostname?
             @hostname = window.location.hostname
-
-        if not @port?
-            @port = window.location.port

         if not @path?
             @path = "/"
@@ -110,7 +110,9 @@
         parser.href = path
         parser.protocol = @protocol
         parser.hostname = @hostname
-        parser.port     = @port
+
+        if @port?
+            parser.port = @port

         urls = []
         if _no_extension


probably last Atom config update before I delete it

--- dotfiles/atom/config.cson
+++ dotfiles/atom/config.cson
@@ -1,4 +1,4 @@
-'global':
+'*':
   'exception-reporting':
     'userId': '3ba70428-f782-5321-e73a-7b678f36511c'
   'release-notes':
@@ -8,13 +8,18 @@
   'metrics':
     'userId': '9e20623847c7c22109bc7b34e71c459485f8781a'
   'editor':
+    'fontFamily': 'Menlo'
+    'fontSize': 12
     'showInvisibles': true
     'showIndentGuide': true
-    'fontFamily': 'Menlo'
-    'fontSize': 12
     'autoIndentOnPaste': false
+    'invisibles': {}
   'core':
-    'projectHome': '/Users/MoOx/Sync/Development'
+    'ignoredNames': [
+      '.sync'
+      'node_modules'
+      'dist'
+    ]
     'disabledPackages': [
       'autosave'
     ]
@@ -22,11 +27,7 @@
       'yosemite-unity-ui'
       'atom-light-syntax'
     ]
-    'useReactEditor': true
-    'ignoredNames': [
-      'node_modules'
-      'dist'
-    ]
+    'projectHome': '/Users/MoOx/Sync/Development'
   'autocomplete-plus':
     'includeCompletionsFromAllBuffers': true
   'tree-view':
@@ -45,3 +46,5 @@
   'atom-beautify':
     'beautifyEntireFileOnSave': false
     'analytics': false
+  'linter-eslint':
+    'disableWhenNoEslintrcFileInPath': true


project updated

project updated

--- source/module/seeker.coffee
+++ source/module/seeker.coffee
@@ -13,10 +13,7 @@

   constructor: ->

-    @base = switch $$.os
-      when 'macos' then '~/OneDrive/缓存'
-      when 'windows' then 'E:/OneDrive/缓存'
-      else throw new Error 'invalid os'
+    @base = './temp'

   ###


Fix problem with middleware that tries to parse json body

--- packages/rocketchat-cors/cors.coffee
+++ packages/rocketchat-cors/cors.coffee
@@ -6,6 +6,9 @@
 		return next()

 	if req.headers['transfer-encoding'] is undefined and isNaN(req.headers['content-length'])
+		return next()
+
+	if req.headers['content-type'] not in ['', undefined]
 		return next()

 	buf = ''
@@ -20,6 +23,7 @@
 		catch err
 			req.body = buf

+		req._body = true
 		next()


new opinion view UI, mostly complete

new opinion view UI, mostly complete

--- @client/opinion_views.coffee
+++ @client/opinion_views.coffee
@@ -22,12 +22,13 @@
   salience = {}
   groups = {}

+  has_groups = false
+  for view_name, view of active_views
+    has_groups ||= view.get_group?
+
   for o in opinions
     weight = 1
     min_salience = 1
-    has_groups = false
-    for view_name, view of active_views
-      has_groups ||= view.get_group?

     if has_groups
       u_groups = []
@@ -166,46 +167,63 @@

 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_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'
-    name: 'Substantiated'
-    label: 'Add weight to opinions that justify their stance with pro and/or con reasons.'
+    name: 'Gave reasons'
+    label: 'Add weight to opinions that explained their stance with pro and/or con reasons.'
     weight: (u, opinion, proposal) ->
       point_inclusions = Math.min(8,opinion.point_inclusions?.length or 0)
       .1 + point_inclusions
+    icon: (color) ->
+      color ?= 'black'
+      SVG
+        width: 23
+        height: 23
+        viewBox: "0 0 23 23"
+        dangerouslySetInnerHTML: __html: """
+          <g id="Group-8" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+              <polygon id="Rectangle" stroke="#{color}" points="0 0 23 0 23 17.8367347 11.5 17.8367347 6.41522296 23 6.41522296 17.8367347 0 17.8367347"></polygon>
+              <ellipse id="Oval" fill="#{color}" cx="4.66666683" cy="8.43750016" rx="1.5333335" ry="1.43750016"></ellipse>
+              <line stroke="#{color}" x1="8.37575758" y1="8.5" x2="17.7575758" y2="8.5" id="Line-5" stroke="#979797" stroke-linecap="square"></line>
+              <ellipse id="Oval" fill="#{color}" cx="4.66666683" cy="3.43750016" rx="1.5333335" ry="1.43750016"></ellipse>
+              <line stroke="#{color}" x1="8.37575758" y1="3.5" x2="17.7575758" y2="3.5" id="Line-5" stroke="#979797" stroke-linecap="square"></line>
+              <ellipse id="Oval" fill="#{color}" cx="4.66666683" cy="13.4375002" rx="1.5333335" ry="1.43750016"></ellipse>
+              <line stroke="#{color}" x1="8.37575758" y1="13.5" x2="17.7575758" y2="13.5" id="Line-5" stroke="#979797" stroke-linecap="square"></line>
+          </g>
+        """

   weighed_by_deliberative:
     key: 'weighed_by_deliberative'
-    name: 'Deliberative'
+    name: 'Weighed tradeoffs'
     label: 'Add weight to opinions that acknowledge both pro and con tradeoffs.'
     weight: (u, opinion, proposal) ->
       point_inclusions = opinion.point_inclusions
@@ -219,6 +237,17 @@
         2
       else
         .1
+    icon: (color) ->
+      color ?= 'black'
+      SVG
+        width: 23
+        height: 23
+        viewBox: "0 0 23 23"
+        dangerouslySetInnerHTML: __html: """
+          <g id="weigh" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+              <path fill="#{color}" d="M22.9670971,20.1929176 L22.9670971,20.1683744 C22.9815886,20.1324736 22.9922265,20.0943011 22.9987304,20.0548621 C22.9987304,20.0548621 22.9987304,20.0548621 22.9987304,20.0548621 C23.0004232,20.0293408 23.0004232,20.0036859 22.9987304,19.9781646 C22.9987304,19.9781646 22.9987304,19.9566893 22.9987304,19.9444177 C22.9987304,19.9321461 22.9987304,19.9229424 22.9987304,19.9137387 C22.9949502,19.8726516 22.9867644,19.8324016 22.9743971,19.7940906 L22.9743971,19.7940906 L18.9399256,8.02562631 C18.9506738,7.99615751 18.9588334,7.96529502 18.9642589,7.93358931 C18.9781664,7.81275485 18.9534152,7.68990536 18.8954544,7.59208741 C18.8374936,7.49426946 18.7510753,7.42950274 18.6552252,7.41204631 L13.4527523,6.45792942 C13.4151172,5.40049872 12.8144155,4.50934755 11.9927506,4.29199205 L11.9927506,0.460184995 C11.9927506,0.20603184 11.8293343,0 11.6277502,0 C11.426166,0 11.2627497,0.20603184 11.2627497,0.460184995 L11.2627497,4.29199205 C10.6280528,4.45682465 10.10874,5.03001236 9.89521475,5.80139883 L4.44940823,4.80126344 C4.35356714,4.78372914 4.25612781,4.81493501 4.1785425,4.88801078 C4.1009572,4.96108654 4.04958681,5.07004101 4.03574107,5.19088674 C4.03155491,5.23059483 4.03155491,5.27082675 4.03574107,5.31053484 L0.025602933,16.9992337 L0.025602933,16.9992337 C0.0132356207,17.0375447 0.00504978749,17.0777948 0.00126957058,17.1188818 C0.00126957058,17.1188818 0.00126957058,17.1403571 0.00126957058,17.1495608 C0.00126957058,17.1587645 0.00126957058,17.1710361 0.00126957058,17.1833077 C-0.000423190195,17.208829 -0.000423190195,17.2344839 0.00126957058,17.2600052 C0.00126957058,17.2600052 0.00126957058,17.2600052 0.00126957058,17.2600052 C0.00774547506,17.2994541 0.0183845876,17.3376312 0.0329029418,17.3735175 L0.0329029418,17.3980607 C0.0488060712,17.4339253 0.0684473538,17.4669431 0.0913030117,17.4962335 L0.699637073,18.2632085 C2.75983325,20.8606357 6.10004983,20.8606357 8.160246,18.2632085 L8.76858006,17.4962335 C8.79161354,17.465998 8.81126198,17.431936 8.82698013,17.3949928 L8.82698013,17.3704496 C8.84147172,17.3345488 8.85210954,17.2963764 8.85861351,17.2569373 C8.85861351,17.2569373 8.85861351,17.2569373 8.85861351,17.2569373 C8.86030627,17.231416 8.86030627,17.2057611 8.85861351,17.1802398 C8.85861351,17.1802398 8.85861351,17.1587645 8.85861351,17.1464929 C8.85861351,17.1342213 8.85861351,17.1250176 8.85861351,17.1158139 C8.85483329,17.0747269 8.84664746,17.0344768 8.83428014,16.9961658 L8.83428014,16.9961658 L5.01394224,5.83207783 L9.80274797,6.71256512 C9.85757198,7.80370794 10.5108448,8.69745358 11.3689468,8.85528909 C12.2270488,9.0131246 13.0556206,8.39194297 13.3554189,7.36602781 L18.1320579,8.2434472 L14.1584198,19.7879548 L14.1584198,19.7879548 C14.1460238,19.8262538 14.1378369,19.8665089 14.1340865,19.9076029 C14.1340865,19.9076029 14.1340865,19.9290782 14.1340865,19.9382819 C14.1340865,19.9474856 14.1340865,19.9597572 14.1340865,19.9720288 C14.1323937,19.9975501 14.1323937,20.023205 14.1340865,20.0487263 C14.1340865,20.0487263 14.1340865,20.0487263 14.1340865,20.0487263 C14.1405624,20.0881752 14.1512015,20.1263523 14.1657199,20.1622386 L14.1657199,20.1867818 C14.1816474,20.2226264 14.2012863,20.2556402 14.2241199,20.2849546 L14.832454,21.0519296 C16.8926502,23.6493568 20.2328667,23.6493568 22.2930629,21.0519296 L22.901397,20.2849546 C22.9264857,20.2581473 22.9485857,20.2271881 22.9670971,20.1929176 Z M15.0490209,19.5087759 L18.5627585,9.26505789 L22.0959627,19.5087759 L15.0490209,19.5087759 Z M0.916203999,16.7200548 L4.44940823,6.48247262 L7.96314577,16.7200548 L0.916203999,16.7200548 Z M1.24227106,17.6404248 L7.63707871,17.6404248 C5.86694476,19.8538124 3.01240501,19.8538124 1.24227106,17.6404248 L1.24227106,17.6404248 Z M11.6277502,7.95813251 C11.02159,7.94807762 10.5340258,7.32653669 10.5327488,6.56223802 C10.5327488,6.51928742 10.5327488,6.48247262 10.5327488,6.43952202 C10.5385866,6.42154559 10.5434641,6.40309702 10.5473489,6.38429982 C10.5510895,6.34660935 10.5510895,6.308478 10.5473489,6.27078753 C10.6654865,5.57212652 11.1829331,5.09866324 11.7464056,5.17365144 C12.3098781,5.24863963 12.7375156,5.84787683 12.7373515,6.56223802 C12.7373515,6.60518862 12.7373515,6.64507132 12.7373515,6.68802192 C12.7187894,6.74960828 12.7113093,6.81562397 12.7154515,6.88129962 C12.5971458,7.51670329 12.1454062,7.96392904 11.6277502,7.95813251 L11.6277502,7.95813251 Z M15.375088,20.4291459 L21.7698956,20.4291459 C19.9997617,22.6425334 17.1452219,22.6425334 15.375088,20.4291459 L15.375088,20.4291459 Z" id="Shape" fill-rule="nonzero"></path>
+          </g>
+          """

   weighed_by_influence:
     key: 'weighed_by_influence'
@@ -233,9 +262,26 @@

       u = u.key or u
       .1 + (influencer_scores[u] or 0)
+    icon: (color) ->
+      color ?= 'black'
+      SVG
+        width: 23
+        height: 23
+        viewBox: "0 0 23 23"
+        dangerouslySetInnerHTML: __html: """
+          <g id="influence" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+              <ellipse id="Oval" fill="#{color}" fill-rule="nonzero" transform="translate(11.364066, 2.300000) scale(-1, 1) translate(-11.364066, -2.300000) " cx="11.3640662" cy="2.3" rx="2.39243499" ry="2.3"></ellipse>
+              <path d="M12.5828538,4.6 L11.3640662,4.6 L10.1452786,4.6 C7.83466033,4.6 5.98108747,6.46815021 5.98108747,8.79694019 L5.98108747,13.403338 C5.98108747,13.940751 6.41274142,14.3502086 6.92056961,14.3502086 C7.4537892,14.3502086 7.86005174,13.9151599 7.86005174,13.403338 L7.86005174,8.7201669 C7.86005174,8.54102921 7.98700879,8.41307371 8.16474865,8.41307371 C8.34248851,8.41307371 8.46944556,8.54102921 8.46944556,8.7201669 L8.46944556,13.2753825 L8.46944556,22.0531293 C8.46944556,22.5905424 8.90109951,23 9.4089277,23 C9.94214729,23 10.3484098,22.5649513 10.3484098,22.0531293 L11.0085865,13.2753825 L11.6687631,13.2753825 L12.3797226,22.0531293 C12.3797226,22.5905424 12.8113765,23 13.3192047,23 C13.8524243,23 14.2586868,22.5649513 14.2586868,22.0531293 L14.2586868,13.2497914 L14.2586868,8.7201669 C14.2586868,8.54102921 14.3856439,8.41307371 14.5633837,8.41307371 C14.7411236,8.41307371 14.8680806,8.54102921 14.8680806,8.7201669 L14.8680806,13.403338 C14.8680806,13.940751 15.2997346,14.3502086 15.8075628,14.3502086 C16.3407824,14.3502086 16.7470449,13.9151599 16.7470449,13.403338 L16.7470449,8.79694019 C16.7470449,6.49374131 14.8934721,4.6 12.5828538,4.6 Z" id="Path" fill="#{color}" fill-rule="nonzero" transform="translate(11.364066, 13.800000) scale(-1, 1) translate(-11.364066, -13.800000) "></path>
+              <path d="M22.3105861,0 L18.7361296,0 C18.35544,0 18.0468076,0.283811841 18.0468076,0.633885886 C18.0468076,0.983959931 18.35544,1.26777177 18.7361296,1.26777177 L20.6463788,1.26777177 L16.9489473,4.66785115 C16.6797441,4.91540471 16.6797441,5.31678126 16.9489473,5.56433482 C17.2181506,5.81188839 17.6546293,5.81188839 17.9238325,5.56433482 L21.621264,2.16425545 L21.6213559,3.92088002 C21.6213559,4.27095406 21.9299884,4.5547659 22.310678,4.5547659 C22.6913675,4.5547659 23,4.27095406 23,3.92088002 L23,0.633885886 C22.9999081,0.283727323 22.6912756,0 22.3105861,0 Z" id="Path" fill="#{color}" fill-rule="nonzero"></path>
+              <path d="M5.56354114,0 L1.98908469,0 C1.60839511,0 1.29976266,0.283811841 1.29976266,0.633885886 C1.29976266,0.983959931 1.60839511,1.26777177 1.98908469,1.26777177 L3.89933392,1.26777177 L0.201902425,4.66785115 C-0.0673008082,4.91540471 -0.0673008082,5.31678126 0.201902425,5.56433482 C0.471105657,5.81188839 0.907584371,5.81188839 1.1767876,5.56433482 L4.8742191,2.16425545 L4.87431101,3.92088002 C4.87431101,4.27095406 5.18294346,4.5547659 5.56363305,4.5547659 C5.94432263,4.5547659 6.25295508,4.27095406 6.25295508,3.92088002 L6.25295508,0.633885886 C6.25286317,0.283727323 5.94423072,0 5.56354114,0 Z" id="Path" fill="#{color}" fill-rule="nonzero" transform="translate(3.126478, 2.875000) scale(-1, 1) translate(-3.126478, -2.875000) "></path>
+              <path d="M22.3105861,16.1 L18.7361296,16.1 C18.35544,16.1 18.0468076,16.3838118 18.0468076,16.7338859 C18.0468076,17.0839599 18.35544,17.3677718 18.7361296,17.3677718 L20.6463788,17.3677718 L16.9489473,20.7678511 C16.6797441,21.0154047 16.6797441,21.4167813 16.9489473,21.6643348 C17.2181506,21.9118884 17.6546293,21.9118884 17.9238325,21.6643348 L21.621264,18.2642555 L21.6213559,20.02088 C21.6213559,20.3709541 21.9299884,20.6547659 22.310678,20.6547659 C22.6913675,20.6547659 23,20.3709541 23,20.02088 L23,16.7338859 C22.9999081,16.3837273 22.6912756,16.1 22.3105861,16.1 Z" id="Path" fill="#{color}" fill-rule="nonzero" transform="translate(19.873522, 18.975000) scale(1, -1) translate(-19.873522, -18.975000) "></path>
+              <path d="M5.56354114,16.1 L1.98908469,16.1 C1.60839511,16.1 1.29976266,16.3838118 1.29976266,16.7338859 C1.29976266,17.0839599 1.60839511,17.3677718 1.98908469,17.3677718 L3.89933392,17.3677718 L0.201902425,20.7678511 C-0.0673008082,21.0154047 -0.0673008082,21.4167813 0.201902425,21.6643348 C0.471105657,21.9118884 0.907584371,21.9118884 1.1767876,21.6643348 L4.8742191,18.2642555 L4.87431101,20.02088 C4.87431101,20.3709541 5.18294346,20.6547659 5.56363305,20.6547659 C5.94432263,20.6547659 6.25295508,20.3709541 6.25295508,20.02088 L6.25295508,16.7338859 C6.25286317,16.3837273 5.94423072,16.1 5.56354114,16.1 Z" id="Path" fill="#{color}" fill-rule="nonzero" transform="translate(3.126478, 18.975000) scale(-1, -1) translate(-3.126478, -18.975000) "></path>
+          </g>
+        """

 toggle_group = (view, replace_existing) ->
   _activate_opinion_view(view, 'group', replace_existing)
+

 toggle_weight = (view, replace_existing) ->
   _activate_opinion_view(view, 'weight', replace_existing)
@@ -288,9 +334,10 @@

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

@@ -319,11 +366,157 @@

+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
   displayName: 'OpinionViews'

   render : ->
+
+    clear_all = ->
+      to_remove = []
+      for k,v of opinion_views.active_views
+        if v.view_type in ['filter', 'weight'] || k == 'group_by'
+          to_remove.push v
+      for view in to_remove
+        toggle_opinion_filter view
+
+      for attr in ['group_by', 'selected_vals_for_attribute', 'visible_attributes']
+        if opinion_views_ui[attr]
+          delete opinion_views_ui[attr]
+      save opinion_views_ui
+
+    opinion_views = fetch 'opinion_views'
+    opinion_views_ui = fetch 'opinion_views_ui'
+    view_buttons = [
+      {
+        label: 'All opinions'
+        callback: clear_all
+      }
+      {
+        label: 'Just you'
+        callback: ->
+          clear_all()
+          toggle_opinion_filter default_filters.just_you
+      },
+      {
+        label: 'More views'
+        callback: ->
+          clear_all()
+      }
+    ]

     DIV
       style: (@props.style or {})
@@ -337,16 +530,35 @@
         if customization('verification-by-pic')
           VerificationProcessExplanation()

+        ToggleButtons view_buttons, opinion_views_ui
+
+      if opinion_views_ui.active == 'More views'
+        needs_expansion = @props.additional_width && @props.style?.width
         DIV
           style:
-            display: 'flex'
-            justifyContent: 'space-between'
-
-          OpinionFilters
-            containerWidth: @props.style?.width or 400
-
-          OpinionWeights
-            containerWidth: @props.style?.width or 400
+            width: if needs_expansion then @props.style.width + @props.additional_width else '100%'
+            position: 'relative'
+            right: if needs_expansion then @props.additional_width # - @props.style.width
+            border: '1px solid #B6B6B6'
+            borderRadius: 8
+            marginTop: 18
+            padding: '18px 24px'
+
+          DIV
+            style:
+              position: 'absolute'
+              left: (document.querySelector('[data-view-state="More views"]')?.offsetLeft or 60) + 35
+              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>"""
+
+          DIV
+            style:
+              display: 'flex'
+
+            OpinionFilters()
+
+            OpinionWeights()

 OpinionFilters = ReactiveComponent
@@ -356,7 +568,7 @@
     user_tags = customization 'user_tags'

     opinion_views = fetch 'opinion_views'
-    opinion_view_ui = fetch 'opinion_views_ui'
+    opinion_views_ui = fetch 'opinion_views_ui'

     is_admin = fetch('/current_user').is_admin
     show_others = !customization('hide_opinions') || is_admin
@@ -367,6 +579,10 @@
     # 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
@@ -374,6 +590,7 @@
               attributes.push _.extend {}, filter,
                 name: filter.label
                 options: [true, false]
+
       if user_tags
         for name, tag of user_tags

@@ -394,7 +611,7 @@
       if v.view_type == 'filter'
         active_filters[k] = v

-    if !opinion_view_ui.initialized
+    if !opinion_views_ui.initialized
       initial_filter = null

       if !show_others
@@ -404,8 +621,8 @@
       #     if filter.label == dfault
       #       toggle_opinion_filter filter
       #       break
-      opinion_view_ui.initialized = true
-      save opinion_view_ui
+      opinion_views_ui.initialized = true
+      save opinion_views_ui

     update_view_for_attribute = (attribute) ->
@@ -414,17 +631,17 @@

       # having no selections for an attribute paradoxically means that all values are valid.
       has_one_enabled = false
-      for val,enabled of opinion_view_ui.selected_vals_for_attribute[attribute]
+      for val,enabled of opinion_views_ui.selected_vals_for_attribute[attribute]
         has_one_enabled ||= enabled
       if !has_one_enabled
-        opinion_view_ui.selected_vals_for_attribute[attribute] = {}
+        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_view_ui.selected_vals_for_attribute[attribute] when enabled)
+          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
@@ -439,303 +656,203 @@
       view =
         key: attribute
         # pass: pass
-        salience: (u) ->
-          if pass(u)
-            1
-          else
-            .1
-
-        weight: (u) ->
-          if pass(u)
-            1
-          else
-            .05
+        salience: (u) -> if pass(u) then 1 else .1
+        weight:   (u) -> if pass(u) then 1 else .1

       toggle_opinion_filter view, has_one_enabled

-    toggle_comparison = (e) ->
-      enabled = e.target.checked
-
-      if enabled == null # toggle if argument not passed
-        opinion_views.enable_comparison = !opinion_views.enable_comparison
-      else
-        opinion_views.enable_comparison = enabled
-      save opinion_views
+    show_all_not_available = !show_others
+
+    if attributes.length > 0
+      opinion_views_ui.visible_attributes ?= {}
+      opinion_views_ui.selected_vals_for_attribute ?= {}
+
+    for attribute, cnt in attributes
+      opinion_views_ui.selected_vals_for_attribute[attribute.key] ?= {}
+
+    toggle_attribute_visible = (attribute) ->
+      opinion_views_ui.visible_attributes[attribute.key] = !opinion_views_ui.visible_attributes[attribute.key]
+      if !opinion_views_ui.visible_attributes[attribute.key] && opinion_views_ui.group_by == attribute.key
+        toggle_group opinion_views.active_views.group_by
+        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: {}
-
-      DropOverlay
-        open_area_on: 'activation'
-        render_anchor: (menu_showing) =>
-          if !menu_showing
-            [
-              FilterOpinionsIcon
-                height: 30
-
-              SPAN
-                style:
-                  paddingLeft: 8
-                'filter opinions'
-            ]
-          else
-            SPAN
-              style:
-                paddingLeft: 8
-              'close opinion filters'
-
-        # anchor_style:
-        #   float: 'right'
-        anchor_open_style:
-          backgroundColor: focus_color()
-        anchor_class_name: 'filter-weight-sort-button'
-        drop_area_style:
-          top: 52
-          width: @props.containerWidth + 50
-          left: -25
-        dummy: opinion_view_ui # for helping the child component of DropOverlay rerender properly
-                               # on state change
-        dummy2: opinion_views
-
-        DIV null,
-          DIV
-            style:
-              position: 'absolute'
-              left: 60
-              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>"""
-
-          UL
-            style:
-              listStyle: 'none'
-
-            for option in [{name: 'Show only your opinions', label: 'Focus on expressing your own opinion.', filter: default_filters.just_you},
-                           {name: 'Show other people’s opinions too', label: 'Learn what others think.', filter: default_filters.everyone} ]
-
-              do (option) =>
-                show_all_not_available = option.filter == default_filters.everyone && !show_others
-                LI
-                  style:
-                    padding: '8px 0'
-
-                  if show_all_not_available
-                    "This forum is currently configured to hide other people's opinions"
-
-                  LABEL
-                    className: 'opinion_view_label'
-
-                    style:
-                      pointerEvents: if show_all_not_available then 'none'
-                      opacity: if show_all_not_available then .4
-
-                    INPUT
-                      type: 'radio'
-                      checked: (option.filter.key == 'everyone' && default_filters.just_you.key not of active_filters) || option.filter.key of active_filters
-                      className: 'bigger opinion_view_checker'
-                      onChange: ->
-                        if !(option.filter.key == 'everyone' && default_filters.just_you.key not of active_filters)
-                          toggle_opinion_filter option.filter
-
-
-
-                    SPAN
-                      className: 'opinion_view_label_block'
-
-                      DIV
-                        className: 'opinion_view_name'
-                        option.name
-                      DIV
-                        className: 'opinion_view_explanation'
-                        option.label
-
-                  if option.filter.key == 'everyone'
-
-                    DIV
-                      style:
-                        pointerEvents: if opinion_views.active_views[default_filters.just_you.key] then 'none'
-                        opacity: if opinion_views.active_views[default_filters.just_you.key] then .4
-
-                      if attributes.length > 0
-                        if !opinion_view_ui.visible_attributes?
-                          opinion_view_ui.visible_attributes = {}
-                          # opinion_view_ui.visible_attributes[attributes[0].key] = true
-                        if !opinion_view_ui.selected_vals_for_attribute?
-                          opinion_view_ui.selected_vals_for_attribute = {}
-
-                        for attribute, cnt in attributes
-                          opinion_view_ui.selected_vals_for_attribute[attribute.key] ?= {}
-
-                          do (attribute) =>
-                            toggle_attribute_visible = ->
-                              opinion_view_ui.visible_attributes[attribute.key] = !opinion_view_ui.visible_attributes[attribute.key]
-                              save opinion_view_ui
-
-                            DIV
-                              className: 'attribute_group'
-
-                              BUTTON
-                                className: 'attribute_expander'
-
-                                onClick: toggle_attribute_visible
-                                onKeyDown: (e) =>
-                                  if e.which == 13 || e.which == 32 # ENTER or SPACE
-                                    toggle_attribute_visible()
-                                    e.preventDefault()
-                                SPAN
-                                  className: 'attribute_caret'
-                                  dangerouslySetInnerHTML: __html: if opinion_view_ui.visible_attributes[attribute.key] then '▼' else '▶'
-
-                                "Narrow by #{attribute.name}"
-
-
-                              if opinion_view_ui.visible_attributes[attribute.key]
-
-                                DIV
-                                  style: {}
-
-                                  for val in attribute.options
-                                    do (val) =>
-
-                                      LABEL
-                                        className: 'attribute_value_selector'
-
-                                        INPUT
-                                          type: 'checkbox'
-                                          # className: 'bigger'
-                                          value: val
-                                          checked: !!opinion_view_ui.selected_vals_for_attribute[attribute.key][val]
-                                          onChange: (e) ->
-                                            # create a view on the fly for this attribute
-                                            opinion_view_ui.selected_vals_for_attribute[attribute.key][val] = e.target.checked
-                                            save opinion_view_ui
-                                            update_view_for_attribute(attribute)
-
-                                        SPAN
-                                          className: 'attribute_value_value'
-                                          "#{val}"
-
-                      if attributes.length > 0
-                        DIV
-                          style:
-                            margin: "18px 0 0 32px"
-
-                          LABEL null,
-                            DIV
-                              className: 'opinion_view_name'
-
-                              "Group opinions by: "
-
-                            SELECT
-                              style:
-                                maxWidth: '100%'
-                              onChange: (ev) ->
-                                opinion_view_ui.group_by = ev.target.value
-                                save opinion_view_ui
-
-                                if opinion_view_ui.group_by
-
-                                  view =
-                                    key: 'group_by'
-                                    name: "Group by #{opinion_view_ui.group_by}"
-                                    group: (u, opinion, proposal) ->
-                                      group_val = fetch(u).tags[opinion_view_ui.group_by] or 'Unknown'
-                                      if attribute.input_type == 'checklist'
-                                        group_val.split(',')
-                                      else
-                                        group_val
-                                  toggle_group view, true
-                                else
-                                  delete opinion_views.active_views.group_by
-                                  save opinion_views
-
-
-                              value: opinion_view_ui.group_by
-
-                              OPTION
-                                value: null
-                                ""
-                              for attribute in attributes
-                                do (attribute) =>
-                                  OPTION
-                                    value: attribute.key
-                                    attribute.name or attribute.question
-
-                            DIV
-                              className: 'opinion_view_explanation'
-                              "Colorize opinions based on the selected attribute."
-
-          DIV
-            style:
-              marginTop: 24
-
-            LABEL
-              className: 'opinion_view_label'
-              style:
-                cursor: 'pointer'
-
-              INPUT
-                type: 'checkbox'
-                className: 'bigger opinion_view_checker'
-                checked: opinion_views.enable_comparison
-                onChange: toggle_comparison
-
-              SPAN
-                className: 'opinion_view_label_block'
-
-                DIV
-                  className: 'opinion_view_name'
-                  # style:
-                  #   paddingLeft: 12
-                  TRANSLATE 'engage.compare_all', "Compare to everyone"
-
-                DIV
-                  className: 'opinion_view_explanation'
-                  "Filtered out opinions are shown as translucent rather than hidden."
-
-
-      DIV null,
+      style:
+        flex: 1
+        paddingRight: 24
+
+      DIV null,
+        SPAN
+          className: 'opinion_view_name'
+          style:
+            paddingRight: 8
+          'Add opinion filter:'
+
+
         UL
           style:
             listStyle: 'none'
-            paddingTop: 4
-
-          if default_filters.just_you.key of active_filters
-            draw_filter_cancel default_filters.just_you, -> toggle_opinion_filter default_filters.just_you
-
-          for attribute, vals of (opinion_view_ui.selected_vals_for_attribute or {})
-            for val, enabled of vals
-              do (attribute, val) ->
-                if enabled
-                  draw_filter_cancel {name: "#{attribute}: #{val}"}, (v) ->
-                    opinion_view_ui.selected_vals_for_attribute[attribute][val] = false
-                    save opinion_view_ui
-                    update_view_for_attribute attribute
-
-
-draw_filter_cancel = (v, cb) ->
-  LI
-    style:
-      textAlign: 'right'
-
-    SPAN
-      style:
-        fontSize: 14
-        fontWeight: 600
-        fontFamily: 'Fira Sans Condensed'
-        paddingRight: 4
-      v.name
-
-    BUTTON
-      style:
-        border: 'none'
-        backgroundColor: 'transparent'
-        color: '#727272'
-        padding: "0 4px"
-      onClick: -> cb v
-      onKeyDown: (e) =>
-        if e.which == 13 || e.which == 32 # ENTER or SPACE
-          cb v
-      'x'
+            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) =>
+
+            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
+                            SPAN
+                              className: 'toggle_switch_circle'
+                              style:
+                                backgroundColor: if checked then group_colors[val]
+
+                        SPAN
+                          className: 'attribute_value_value'
+                          "#{val}"
+
+      if attributes.length > 0
+        cur_val = -1
+        for attr, idx in attributes
+          if opinion_views_ui.group_by == attr.key
+            cur_val = idx
+        DIV
+          style:
+            marginTop: 18
+
+          LABEL
+            style:
+              display: 'flex'
+              alignItems: 'center'
+
+            SPAN
+              className: 'opinion_view_name'
+              style:
+                paddingRight: 8
+
+              "Group opinions by: "
+
+            SELECT
+              style:
+                maxWidth: '100%'
+                marginRight: 12
+              onChange: (ev) ->
+                if ev.target.value != null
+                  attribute = attributes[ev.target.value]
+                  opinion_views_ui.group_by = attribute?.key
+                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
+                  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}"
+                    group: (u, opinion, proposal) ->
+                      group_val = fetch(u).tags[opinion_views_ui.group_by] or 'Unknown'
+                      if attribute.input_type == 'checklist'
+                        group_val.split(',')
+                      else
+                        group_val
+                    options: attribute.options
+
+                  toggle_group view, true
+
+                else
+                  delete opinion_views.active_views.group_by
+                  save opinion_views
+
+
+              value: cur_val
+
+              OPTION
+                value: null
+                ""
+              for attribute,idx in attributes
+                do (attribute) =>
+                  OPTION
+                    value: idx
+                    attribute.name or attribute.question
+
+            HelpIcon 'Opinions will be colorized based on this attribute so you can see differences between groups.'

 OpinionWeights = ReactiveComponent
@@ -749,84 +866,51 @@
         activated_weights[k] = v

     DIV
-      style: {}
-
-      DropOverlay
-        open_area_on: 'activation'
-        render_anchor: (menu_showing) =>
-          if !menu_showing
-            [
-              WeighOpinionsIcon
-                height: 30
-
-              SPAN
-                style:
-                  paddingLeft: 8
-                'weigh opinions'
-            ]
-          else
-            SPAN
+      style:
+        padding: '0px 24px 0 48px'
+        borderLeft: '1px solid #DEDDDD'
+
+      DIV
+        className: 'opinion_view_name'
+        style:
+          textAlign: 'center'
+          marginBottom: 12
+        'Weigh opinions'
+
+      UL
+        style:
+          listStyle: 'none'
+
+        for k,v of default_weights
+          do (k,v) ->
+            LI
               style:
-                paddingLeft: 8
-              'close opinion weights'
-
-        # anchor_style:
-        #   float: 'right'
-        anchor_open_style:
-          backgroundColor: focus_color()
-        anchor_class_name: 'filter-weight-sort-button'
-        drop_area_style:
-          top: 52
-          width: @props.containerWidth + 50
-          right: -25
-
-        DIV null,
-          DIV
-            style:
-              position: 'absolute'
-              right: 60
-              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>"""
-
-
-          UL
-            style:
-              listStyle: 'none'
-
-            for k,v of default_weights
-              LI
-                style:
-                  padding: '8px 0'
-
-                LABEL
-                  className: 'opinion_view_label'
-
-                  INPUT
-                    type: 'checkbox'
-                    checked: k of activated_weights
-                    className: 'bigger opinion_view_checker'
-                    onChange: do (k,v) -> ->
-                      toggle_weight v
-
+                marginBottom: 8
+                display: 'flex'
+                alignItems: 'center'
+
+              BUTTON
+                className: "weight opinion_view_button #{if activated_weights[k] then 'active'}"
+                onClick: ->
+                  toggle_weight v
+                onKeyDown: (e) ->
+                  if e.which == 13 || e.which == 32 # ENTER or SPACE
+                    toggle_weight v
+                    e.preventDefault()
+
+
+                if v.icon
                   SPAN
-                    className: 'opinion_view_label_block'
-
-                    DIV
-                      className: 'opinion_view_name'
-                      v.name
-                    DIV
-                      className: 'opinion_view_explanation'
-                      v.label
-
-      DIV null,
-        UL
-          style:
-            listStyle: 'none'
-            paddingTop: 4
-
-          for k,v of activated_weights
-            do (k,v) ->
-              draw_filter_cancel v, (v) -> toggle_weight(v)
+                    style:
+                      paddingRight: 12
+                    v.icon if activated_weights[k] then 'white'
+
+                v.name
+
+              HelpIcon v.label,
+                width: 18
+                height: 18
+

 VerificationProcessExplanation = ReactiveComponent
@@ -1006,9 +1090,35 @@

   }

-  label.opinion_view_label {
+  button.opinion_view_button {
+    border: 1px solid #E0E0E0;
+    background-color: #F0F0F0;
+    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;
+
+  }
+  button.opinion_view_button.weight {
+    width: 100%;
     display: flex;
-  }
+    padding: 8px 24px;
+    text-align: left;
+    align-items: center;
+    margin-right: 12px;
+
+  }
+
+  button.opinion_view_button.active {
+    background-color: #{focus_blue};
+    color: white;
+  }
+
   .opinion_view_label input.opinion_view_checker {
     position: relative;
     top: 3px;
@@ -1020,48 +1130,36 @@
     cursor: pointer;
   }
   .opinion_view_name {
-    font-family: 'Fira Sans Condensed';
-    font-weight: bold;
-    font-size: 18px;
+    font-size: 14px;
+    font-weight: 600;
   }
-  .opinion_view_explanation {
-    color: #444444;
-    font-size: 14px;
-  }

   .attribute_group {
-    margin-top: 12px;
-    margin-left: 53px;
-    position: relative;
-  }
-  .attribute_group button.attribute_expander {
-    border: none;
-    background-color: transparent;
-    color: black;
-    font-weight: 400;
-    font-family: 'Fira Sans Condensed';
-    padding: 4px 0;
-    position: relative;
+    margin-top: 8px;
+  }
+
+  .attribute_group button {
+    background: #F0F0F0;
+    border: 1px solid #E0E0E0;
+    border-radius: 8px 8px 0 0;
+    font-size: 12px;
+    width: 100%;
+    padding: 4px 12px;
     text-align: left;
-  }
-  .attribute_expander .attribute_caret {
-    position: absolute;
-    left: -20px;
-    top: 7px;
-    font-size: 12px;
   }

   .attribute_value_selector {
     display: block;
     cursor: pointer;
+    margin-right: 18px;
   }
   .attribute_value_selector input {
     position: relative;
     top: 2px;
   }
   .attribute_value_selector .attribute_value_value {
-    padding-left: 16px;
-    font-size: 14px;
+    padding-left: 8px;
+    font-size: 12px;
     font-weight: 400;
     font-family: 'Fira Sans Condensed';
   }


put first user in admin group

put first user in admin group

--- server/allow_and_startup.coffee
+++ server/allow_and_startup.coffee
@@ -12,10 +12,6 @@
   SettingsToClient.update( {_id: 'settings'},
     {$set: {specializations: Meteor.settings.NeuronCatalogSpecializations}},
     {upsert: true})
-
-  # ensure first user is in the admin group
-  first_user_doc = Meteor.users.findOne({}, {sort: {createdAt: 1}})
-  Roles.addUsersToRoles(first_user_doc._id, ['admin'])

   # Ensure existance of roles we use.
   for role_name in ['admin','read-write','read-only']
@@ -72,3 +68,11 @@
   remove: (userId, doc) ->
     Roles.userIsInRole(userId, ['admin'])
 )
+
+Accounts.onCreateUser (options, user) ->
+  if Meteor.users.find().count()==0
+    # first user, add to admin group
+    user.roles = user.roles || []
+    if !("admin" in user.roles)
+      user.roles.push "admin"
+  user


Remove console logging

Remove console logging

--- web/site/src/javascript/status/StatusDrawer.coffee
+++ web/site/src/javascript/status/StatusDrawer.coffee
@@ -155,7 +155,6 @@
           .width width
           .height height
         chart.updateChart [d.properties.env_nvData?[index]]
-        console.log d.properties.env_nvData?[index]

   _plot_comf_hist: ->
     [width, height] = [@node_width, comfort_plot_height]


change list of album when page is updated

--- server/controllers/album.coffee
+++ server/controllers/album.coffee
@@ -10,27 +10,33 @@
 module.exports = (app) ->

     index: (req, res) ->
+
+        out = []
+        initAlbums = (albums, callback) =>
+            if albums.length > 0
+                albumModel = albums.pop()
+                album = albumModel.toObject()
+                Photo.fromAlbum album.id, (err, photos) =>
+                    album.thumb = photos[0].id
+                    album.orientation = photos[0].orientation
+                    initAlbums albums, callback
+            else
+                callback()
+
         request = if req.public then 'public' else 'all'

         async.parallel [
-            (cb) -> Photo.albumsThumbs cb
             (cb) -> Album.request request, cb
             (cb) -> i18n.getLocale null, cb
         ], (err, results) ->

-            [photos, albums, locale] = results
-            out = []
-            for albumModel in albums
-                album = albumModel.toObject()
-                album.thumb = photos[album.id]
-                out.push album
-
-            imports = """
-                    window.locale = "#{locale}";
-                    window.initalbums = #{JSON.stringify(out)};
-                """
-
-            res.render 'index.jade', imports: imports
+            [albums, locale] = results
+            initAlbums albums, () =>
+                imports = """
+                        window.locale = "#{locale}";
+                        window.initalbums = #{JSON.stringify(out)};
+                    """
+                res.render 'index.jade', imports: imports

     fetch: (req, res, next, id) ->


Fix race condition in spec

Fix race condition in spec

--- spec/app/editor-spec.coffee
+++ spec/app/editor-spec.coffee
@@ -308,10 +308,11 @@

         spyOn(atom, "confirm")

+        contentsConflictedHandler = jasmine.createSpy("contentsConflictedHandler")
+        editSession.on 'contents-conflicted', contentsConflictedHandler
         fs.write(path, "a file change")
-
-        waitsFor "file to trigger contents-changed event", (done) ->
-          editSession.one 'contents-conflicted', done
+        waitsFor ->
+          contentsConflictedHandler.callCount > 0

         runs ->
           expect(atom.confirm).toHaveBeenCalled()


Update test

Update test

--- spec/powershell-spec.coffee
+++ spec/powershell-spec.coffee
@@ -233,11 +233,13 @@

       it "tokenizes comparison operators when prepended with a case sensitivity marker", ->
         for operator in comparisonOperators
-          {tokens} = grammar.tokenizeLine operator.replace('-', '-i')
-          expect(tokens[0]).toHaveScopes ["source.powershell","keyword.operator.comparison.powershell"]
-
-          {tokens} = grammar.tokenizeLine operator.replace('-', '-c')
-          expect(tokens[0]).toHaveScopes ["source.powershell","keyword.operator.comparison.powershell"]
+          insensitiveOperator = operator.replace('-', '-i')
+          {tokens} = grammar.tokenizeLine insensitiveOperator
+          expect(tokens[0]).toEqual value: insensitiveOperator, scopes: ["source.powershell","keyword.operator.comparison.powershell"]
+
+          sensitiveOperator = operator.replace('-', '-c')
+          {tokens} = grammar.tokenizeLine sensitiveOperator
+          expect(tokens[0]).toEqual value: sensitiveOperator, scopes: ["source.powershell","keyword.operator.comparison.powershell"]

       it "will not tokenize the operators if there's more characters", ->
         for operator in comparisonOperators


Update hexadecimal test

Update hexadecimal test

--- spec/powershell-spec.coffee
+++ spec/powershell-spec.coffee
@@ -336,8 +336,10 @@
       it "tokenizes constant hexadecimal integer values", ->
         for constant in constants
           {tokens} = grammar.tokenizeLine constant
-          expect(tokens[0].value).toEqual constant
-          expect(tokens[0]).toHaveScopes ["constant.numeric.integer.hexadecimal.powershell"]
+          expect(tokens[0].value).toEqual "0x"
+          expect(tokens[0]).toHaveScopes ["source.powershell", "constant.numeric.hexadecimal.powershell", "keyword.operator.math.powershell"]
+          expect(tokens[1].value).toEqual constant.substr(2)
+          expect(tokens[1]).toHaveScopes ["source.powershell", "constant.numeric.hexadecimal.powershell", "support.constant.powershell"]

   describe "Types", ->
     types = [ "[string]", "[Int32]", "[System.Diagnostics.Process]"]


Access activatePackage via atom.packages

--- spec/status-bar-spec.coffee
+++ spec/status-bar-spec.coffee
@@ -195,8 +195,8 @@

   describe "grammar label", ->
     beforeEach ->
-      atom.activatePackage('language-text', sync: true)
-      atom.activatePackage('language-javascript', sync: true)
+      atom.packages.activatePackage('language-text', sync: true)
+      atom.packages.activatePackage('language-javascript', sync: true)

     it "displays the name of the current grammar", ->
       expect(statusBar.find('.grammar-name').text()).toBe 'JavaScript'


Perform a document poll after setting the editorElement width

--- spec/wrap-guide-spec.coffee
+++ spec/wrap-guide-spec.coffee
@@ -84,19 +84,11 @@
   describe "when the editor's scroll left changes", ->
     it "updates the wrap guide position to a relative position on screen", ->
       editor.setText("a long line which causes the editor to scroll")
-      if editorElement.logicalDisplayBuffer
-        editorElement.setWidth(100)
-      else
-        editor.setWidth(100)
-
+      editorElement.style.width = "100px"
+      atom.views.performDocumentPoll()
       initial = getLeftPosition(wrapGuide)
       expect(initial).toBeGreaterThan(0)
-
-      if editorElement.logicalDisplayBuffer
-        editorElement.setScrollLeft(10)
-      else
-        editor.setScrollLeft(10)
-
+      editorElement.setScrollLeft(10)
       expect(getLeftPosition(wrapGuide)).toBe(initial - 10)
       expect(wrapGuide).toBeVisible()


Nuova regexp per indicare il nome della stanza in cui ricordare lo standup

--- scripts/standup.coffee
+++ scripts/standup.coffee
@@ -93,11 +93,12 @@
     return

   # Finds the room for most adaptors
-  findRoom = (msg) ->
+  findRoom = (msg, room) ->
     console.log(msg)
-    room = msg.envelope.room
     if _.isUndefined(room)
-      room = msg.envelope.user.reply_to
+      room = msg.envelope.room
+      if _.isUndefined(room)
+        room = msg.envelope.user.reply_to
     room

   # Stores a standup in the brain.
@@ -170,20 +171,21 @@
       msg.send 'Deleted your ' + time + ' standup.'
     return

-  robot.respond /create standup ((?:[01]?[0-9]|2[0-4]):[0-5]?[0-9])(\sUTC([+-]([0-9]|1[0-3])))?(\s"(.*)")?$/i, (msg) ->
+  robot.respond /create standup ((?:[01]?[0-9]|2[0-4]):[0-5]?[0-9])(\sUTC([+-]([0-9]|1[0-3])))?(\s\@([^\s]*))?(\s"(.*)")?$/i, (msg) ->
     time = msg.match[1]
     utc = msg.match[3]
-    custom_message = msg.match[6]
-    room = findRoom(msg)
+    room = findRoom(msg, msg.match[6])
+    custom_message = msg.match[8]
     saveStandup room, time, utc, custom_message
-    print_message = 'Ok, from now on I\'ll remind this room to do a standup every weekday at ' + time
+    print_message = 'Ok, from now on I\'ll remind to do a standup every weekday at ' + time

     if utc
-        print_message += ' UTC' + utc
+      print_message += ' UTC' + utc

     if custom_message
-        print_message += ' with message: ' + custom_message
-
+      print_message += ' with message: ' + custom_message
+
+    print_message += ' in the ' + room + ' room'
     msg.send print_message
     return


Track username

Track username

--- scripts/standup.coffee
+++ scripts/standup.coffee
@@ -29,8 +29,10 @@
       msg.send standup

   robot.hear /\/standup (.*)$/i, (msg) ->
-    status = msg.match[1] + "\n"
+    status = msg.message.user.name + ": " + msg.match[1] + "\n"
     if !standup
       standup = status
     else
       standup += status
+
+    msg.send "Standup recorded."


Add some comments

Add some comments

--- src/6.object.coffee
+++ src/6.object.coffee
@@ -1,3 +1,9 @@
+# `deepAssign` and `deepAbsorb` must use deep clone when traverse isn't needed. If otherwise
+# using direct assignment, then it will have severe side-effects: when sources are more than 1,
+# the first or middle source's value may have been changed after the whole thing finishes.
+# `deepAssign` and `deepAbsorb` should only traverse normal objects. If they also traverse
+# arrays, then it will look very unnatural. It's weird to merge arrays.
+
 Object.isObject = (x) -> typeof x in ["object", "function"] and x != null
 Object.isNormalObject = (x) -> Object.isObject(x) and typeof x != "function" and not Array.isArray(x)
 # TODO: need to use new concept


testing webhook, just log request body...

--- routes/index.coffee
+++ routes/index.coffee
@@ -18,8 +18,8 @@

 ### New Card ###
 router.post '/card', (req, res) ->
-  log req
-  res.send()
+  log req.body
+  res.send 200

 #  if req.body?.card?.id
 #    parse.updateCard req.body


fixing sprite bounds crushing during alpha merge

--- src/drawable.coffee
+++ src/drawable.coffee
@@ -110,22 +110,22 @@

       canvas.context().globalAlpha = I.alpha

-    if I.sprite
-      if I.sprite.draw?
-        I.sprite.draw(canvas, 0, 0)
+    if sprite = I.sprite
+      if sprite.draw?
+        sprite.draw(canvas, -sprite.width / 2, -sprite.height / 2)
       else
         warn?("Sprite has no draw method!")
     else
       if I.radius?
         canvas.drawCircle
-          x: I.width/2
-          y: I.height/2
+          x: 0
+          y: 0
           radius: I.radius
           color: I.color
       else
         canvas.drawRect
-          x: 0
-          y: 0
+          x: -I.width/2
+          y: -I.height/2
           width: I.width
           height: I.height
           color: I.color
@@ -168,8 +168,6 @@
     transform = transform.concat(Matrix.HORIZONTAL_FLIP) if I.hflip
     transform = transform.concat(Matrix.VERTICAL_FLIP) if I.vflip

-    transform = transform.concat(Matrix.translation(-I.width/2, -I.height/2))
-
     if I.spriteOffset
       transform = transform.concat(Matrix.translation(I.spriteOffset.x, I.spriteOffset.y))


/help now bolds command names.

/help now bolds command names.

--- server/commands.coffee
+++ server/commands.coffee
@@ -211,7 +211,7 @@
 makeCommand "help", (user, room, next, commandName) ->
   message = []
   for name, description of HelpDescriptions
-    message.push(description)
+    message.push("/#{name}: #{description}")
   message = message.join("
")
   user.message(message)
   next(null, message)


Don't try to reopen spec windows on launch

--- src/browser/atom-application.coffee
+++ src/browser/atom-application.coffee
@@ -419,13 +419,13 @@
     states = []
     for window in @windows
       if loadSettings = window.getLoadSettings()
-        states.push(_.pick(loadSettings,
-          'initialPaths'
-          'isSpec'
-          'devMode'
-          'safeMode'
-          'apiPreviewMode'
-        ))
+        unless loadSettings.isSpec
+          states.push(_.pick(loadSettings,
+            'initialPaths'
+            'devMode'
+            'safeMode'
+            'apiPreviewMode'
+          ))
     fs.writeFileSync(@getStatePath(), JSON.stringify(states))

   loadState: ->


refactor

refactor

--- lib/request/handle/get/query-functions/index.coffee
+++ lib/request/handle/get/query-functions/index.coffee
@@ -1,24 +1,17 @@
-# indexof(CompanyName,'lfreds') eq 1

-convertOperator = (odataOperator) ->
-  operator = undefined
-  switch odataOperator
-    when 'eq' then operator = '=='
-    when 'ne' then operator = '!='
-    when 'gt' then operator = '>'
-    when 'ge' then operator = '>='
-    when 'lt' then operator = '<'
-    when 'le' then operator = '<='
-  return operator

 module.exports =
+  # indexof(CompanyName,'X') eq 1
   indexof: (query, key, odataOperator, value) ->
-    [innerKey, innerValue] = key.substring(key.indexOf('(')+1, key.indexOf(')')).split(',')
-    operator = convertOperator(odataOperator)
-    return query.$where("this.#{innerKey}.indexOf(#{innerValue}) #{operator} #{value}")
+    [key, target] = key.substring(key.indexOf('(') + 1, key.indexOf(')')).split(',')

+    operator = convertToOperator(odataOperator)
+    return query.$where("this.#{key}.indexOf(#{target}) #{operator} #{value}")
+
+  # year(publish_date) eq 2000
   year: (query, key, odataOperator, value) ->
     key = key.substring(key.indexOf('(')+1, key.indexOf(')'))
+
     start = new Date(+value, 0, 1)
     end = new Date(+value + 1, 0, 1)

@@ -33,4 +26,16 @@
       when 'ge' then query.where(key).gte(start)
       when 'lt' then query.where(key).lt(start)
       when 'le' then query.where(key).lt(end)
-    return query+    return query
+
+convertToOperator = (odataOperator) ->
+  operator = undefined
+  switch odataOperator
+    when 'eq' then operator = '=='
+    when 'ne' then operator = '!='
+    when 'gt' then operator = '>'
+    when 'ge' then operator = '>='
+    when 'lt' then operator = '<'
+    when 'le' then operator = '<='
+  return operator
+


removed redundant zoomer properties

removed redundant zoomer properties

Former-commit-id: d92f32a68def15b07187537c41fad49fd2744ee9 [formerly b3f9e9bb764cf63b7579d7d2115fafaf70288591]
Former-commit-id: a66d5e81d2a6665cfef270375f3090b876eee83c

--- src/g/zoomer.coffee
+++ src/g/zoomer.coffee
@@ -33,8 +33,10 @@
     residueFont: "13" # in px
     canvasEventScale: 1

-    boxRectHeight: 5
-    boxRectWidth: 5
+    # overview box
+    boxRectHeight: 2
+    boxRectWidth: 2
+    overviewboxPaddingTop: 10

     # menu
     menuFontsize: "14px"
@@ -63,12 +65,12 @@

   # @param n [int] row that should be on top
   setTopOffset: (n) ->
-    val = (n - 1)
+    val = Math.max 0, (n - 1)
+    height = 0
     for i in [0..val] by 1
       seq = @model.at i
       height += seq.attributes.height || 1
-    val = Math.max 0, val
-    @set "_alignmentScrollTop",val * @get("rowHeight")
+    @set "_alignmentScrollTop",height * @get("rowHeight")

   # length of all elements left to the main sequence body: labels, metacell, ..
   getLabelWidth: ->


Fixed bug in Sketch that would make them unstoppable.

--- src/client/sketch.coffee
+++ src/client/sketch.coffee
@@ -38,20 +38,20 @@
       @mouseX = e.x
       @mouseY = e.y

-  isRunning = false
+  isRunning: false

   start: ->
-    isRunning = true
+    @isRunning = true

     # set up draw loop
     render = =>
       @draw()
-      if(isRunning)
+      if(@isRunning)
         window.requestAnimationFrame render

     window.requestAnimationFrame render

-  stop: -> isRunning = false
+  stop: -> @isRunning = false

   toString: -> "Sketch"


Define a match function that can be used for each chunck.

--- src/coffee/import.coffee
+++ src/coffee/import.coffee
@@ -76,18 +76,18 @@
   processProducts: (products) ->
     console.warn "Mapping done. About to process existing product(s) ..."
     filterInput = QueryUtils.mapMatchFunction(@matchBy)(products)
-    @client.productProjections.staged().filter(filterInput).fetch()
+    @client.productProjections.staged().where(filterInput).fetch()
     .then (payload) =>
       existingProducts = payload.body.results
-      console.warn "Comparing against #{payload.body.total} existing product(s) ..."
-      @initMatcher existingProducts
+      console.warn "Comparing against #{payload.body.count} existing product(s) ..."
+      matchFn = @initMatcher @matchBy, existingProducts
       productsToUpdate =
       if @validator.updateVariantsOnly
         @mapVariantsBasedOnSKUs existingProducts, products
       else
         products
       console.warn "Processing #{_.size productsToUpdate} product(s) ..."
-      @createOrUpdate(productsToUpdate, @validator.types)
+      @createOrUpdate(productsToUpdate, @validator.types, matchFn)
     .then (result) ->
       # TODO: resolve with a summary of the import
       console.warn "Finished processing #{_.size result} product(s)"
@@ -126,99 +126,31 @@
       else
         Promise.resolve filteredResult

-  initMatcher: (existingProducts) ->
-    @existingProducts = existingProducts
-    @id2index = {}
-    @customAttributeValue2index = {}
-    @sku2index = {}
-    @sku2variantInfo = {}
-    @slug2index = {}
-    _.each existingProducts, (product, productIndex) =>
-      @id2index[product.id] = productIndex
-      if product.slug?
-        slug = product.slug[GLOBALS.DEFAULT_LANGUAGE]
-        @slug2index[slug] = productIndex if slug?
-
-      product.variants or= []
-      variants = [product.masterVariant].concat(product.variants)
-
-      _.each variants, (variant, variantIndex) =>
-        sku = variant.sku
-        if sku?
-          @sku2index[sku] = productIndex
-          @sku2variantInfo[sku] =
-            index: variantIndex - 1 # we reduce by one because of the masterVariant
-            id: variant.id
-        @customAttributeValue2index[@getCustomAttributeValue variant] = productIndex if @customAttributeNameToMatch?
-
-    #console.warn "id2index", @id2index
-    #console.warn "customAttributeValue2index", @customAttributeValue2index
-    #console.warn "sku2index", @sku2index
-    #console.warn "slug2index", @slug2index
-    #console.warn "sku2variantInfo", @sku2variantInfo
-
-  mapVariantsBasedOnSKUs: (existingProducts, products) ->
-    console.warn "Mapping variants for #{_.size products} product type(s) ..."
-    productsToUpdate = {}
-    _.each products, (entry) =>
-      _.each entry.product.variants, (variant) =>
-        productIndex = @sku2index[variant.sku]
-        if productIndex?
-          existingProduct = productsToUpdate[productIndex]?.product or _.deepClone existingProducts[productIndex]
-          variantInfo = @sku2variantInfo[variant.sku]
-          variant.id = variantInfo.id
-          if variant.id is 1
-            existingProduct.masterVariant = variant
-          else
-            existingProduct.variants[variantInfo.index] = variant
-          productsToUpdate[productIndex] =
-            product: existingProduct
-            header: entry.header
-            rowIndex: entry.rowIndex
-        else
-          console.warn "Ignoring variant as no match by SKU found for: ", variant
-    _.map productsToUpdate
-
-  getCustomAttributeValue: (variant, name) ->
-    variant.attributes or= []
-    attrib = _.find variant.attributes, (attribute) =>
-      attribute.name is (@customAttributeNameToMatch unless name)
-    attrib?.value
-
-  match: (entry) ->
-    product = entry.product
-    # 1. match by id
-    index = @id2index[product.id] if product.id?
-    if not index
-      # 2. match by custom attribute
-      index = @_matchOnCustomAttribute product
-    if not index
-      # 3. match by sku
-      index = @sku2index[product.masterVariant.sku] if product.masterVariant.sku?
-    if not index and (entry.header.has(CONS.HEADER_SLUG) or entry.header.hasLanguageForBaseAttribute(CONS.HEADER_SLUG))
-      # 4. match by slug (if header is present)
-      index = @slug2index[product.slug[GLOBALS.DEFAULT_LANGUAGE]] if product.slug? and product.slug[GLOBALS.DEFAULT_LANGUAGE]?
-
-    return @existingProducts[index] if index > -1
-
-  _matchOnCustomAttribute: (product) ->
-    attribute = undefined
-    if @customAttributeNameToMatch?
-      product.variants or= []
-      variants = [product.masterVariant].concat(product.variants)
-      _.find variants, (variant) =>
-        variant.attributes or= []
-        attribute = _.find variant.attributes, (attrib) =>
-          attrib.name is @customAttributeNameToMatch
-        attribute?
-
-    if attribute?
-      @customAttributeValue2index[attribute.value]
-
-  createOrUpdate: (products, types) ->
+  initMatcher: (matchBy, existingProducts) ->
+    temp = existingProducts
+    match = (matchBy, entry) ->
+      map = {}
+      _.each temp, (p, index) ->
+        switch matchBy
+          when 'id' then map[p.id] = p
+          when 'slug' then map[p.slug[GLOBALS.DEFAULT_LANGUAGE]] = p
+          when 'sku'
+            p.variants or= []
+            variants = [p.masterVariant].concat(p.variants)
+            _.each variants, (v) ->
+              map[v.sku] = p
+
+      identifier = switch matchBy
+        when 'id' then entry.product.id
+        when 'slug' then entry.product.slug[GLOBALS.DEFAULT_LANGUAGE]
+        when 'sku' then entry.product.masterVariant.sku
+
+      map[identifier]
+
+  createOrUpdate: (products, types, matchFn) ->
     Promise.all _.map products, (entry) =>
       @repeater.execute =>
-        existingProduct = @match(entry)
+        existingProduct = matchFn(@matchBy, entry)
         if existingProduct?
           @update(entry.product, existingProduct, types, entry.header, entry.rowIndex)
         else
@@ -229,7 +161,6 @@
           Promise.resolve() # will retry in case of Gateway Timeout
         else
           Promise.reject e
-

   _isBlackListedForUpdate: (attributeName) ->
     if _.isEmpty @blackListedCustomAttributesForUpdate


Remove unused shell,fs imports

Remove unused shell,fs imports

--- lib/changes/status-list-element.coffee
+++ lib/changes/status-list-element.coffee
@@ -18,8 +18,6 @@

 FileSummary   = require './file-summary'
 FileSummaryElement   = require './file-summary-element'
-shell             = require 'shell'
-fs                = require 'fs'

 BaseTemplate = """
 <div class="unstaged column-header">Unstaged changes


Fix small bug in logs' parser

Fix small bug in logs' parser

--- admin/static/coffee/log_view.coffee
+++ admin/static/coffee/log_view.coffee
@@ -409,16 +409,16 @@
                                             datacenter_id: datacenter_id
                                             datacenter_id_trunked: datacenter_id.slice 24
                     else if group is 'databases'
-                        for datacenter_id of json_data[group]
-                            if json_data[group][datacenter_id] is null
+                        for database_id of json_data[group]
+                            if json_data[group][database_id] is null
                                 msg += @log_delete_something_template
                                     type: 'database'
-                                    id: datacenter_id
-                            else if datacenter_id is 'new'
+                                    id: database_id
+                            else if database_id is 'new'
                                 msg += @log_new_database_template
-                                    database_name: json_data[group][datacenter_id]['name']
-                            else
-                                for attribute of json_data[group][datacenter_id]
+                                    database_name: json_data[group][database_id]['name']
+                            else
+                                for attribute of json_data[group][database_id]
                                     if attribute is 'name'
                                         msg += @log_database_new_name_template
                                             name: json_data[group][database_id][attribute]
@@ -538,7 +538,7 @@
                                             value += ', '

                                     msg += @log_database_value_small_template
-                                        database_id: datacenter_id
+                                        database_id: database_id
                                         database_name: if databases.get(database_id)? then databases.get(database_id).get 'name' else 'removed database'
                                         attribute: value


when running a case switch motion on the whole line, the cursor should end up on the first character of that line. Could not find a cleaner solution for now.

--- lib/operators/general-operators.coffee
+++ lib/operators/general-operators.coffee
@@ -142,6 +142,10 @@

     @vimState.activateCommandMode()

+    if @motion.isLinewise?()
+      @editor.moveToPreviousWordBoundary()
+      @editor.moveToFirstCharacterOfLine()
+
 #
 # In visual mode or after `g` with a motion, it makes the selection uppercase
 #
@@ -156,6 +160,10 @@

     @vimState.activateCommandMode()

+    if @motion.isLinewise?()
+      @editor.moveToPreviousWordBoundary()
+      @editor.moveToFirstCharacterOfLine()
+
 #
 # In visual mode or after `g` with a motion, it makes the selection lowercase
 #
@@ -169,6 +177,10 @@
         text.toLowerCase()

     @vimState.activateCommandMode()
+
+    if @motion.isLinewise?()
+      @editor.moveToPreviousWordBoundary()
+      @editor.moveToFirstCharacterOfLine()

 #
 # It copies everything selected by the following motion.


fix cropped images in elasticsearch

fix cropped images in elasticsearch

--- api/apps/articles/model/save.coffee
+++ api/apps/articles/model/save.coffee
@@ -172,7 +172,7 @@
       featured: article.featured
       tags: article.tags
       body: sections and stripHtmlTags(sections.join(' ')) or ''
-      image_url: crop(article.thumbnail_image, 70, 70)
+      image_url: crop(article.thumbnail_image, { width: 70, height: 70 })
     , (error, response) ->
       console.log('ElasticsearchIndexingError: Article ' + article.id + ' : ' + error) if error
   )


Server: revert get to post

Server: revert get to post

--- servers/lib/server/index.coffee
+++ servers/lib/server/index.coffee
@@ -234,7 +234,7 @@
   return res.send 500, err

-app.get "/:name?/Register", (req, res) ->
+app.post "/:name?/Register", (req, res) ->
   { JUser } = koding.models
   context = { group: 'koding' }
   { redirect } = req.body


cleaned unused code

cleaned unused code

--- platforms/remote/lib/Platform.coffee
+++ platforms/remote/lib/Platform.coffee
@@ -1,5 +1,3 @@
-
-__MODULES__ = null

 module.exports =

@@ -44,11 +42,6 @@
         ##
         ##
         boot: (modules, attempt=0)->
-            __MODULES__ ?= modules
-
-            if !modules && !attempt
-                modules = __MODULES__
-

             delayed = {}

@@ -86,7 +79,7 @@
                         err = e
                         delayed[path] = init

-            if ++attempt < 10 && _count = floyd.tools.objects.keys(delayed).length
+            if ++attempt < 10 && floyd.tools.objects.keys(delayed).length
                 #console.log 'delayed build', err, delayed

                 @boot delayed, attempt


Refactor JS

Refactor JS

--- app/assets/javascripts/pages.coffee
+++ app/assets/javascripts/pages.coffee
@@ -1,71 +1,82 @@
+class PagesController
+  constructor: ->
+
+  init: ->
+    @$content = $('#page_content')
+
+    @preview()
+
+    autosize(@$content)
+    $('#page_parent_id').select2()
+
+    @_initDropzone()
+    @_initMousetrap()
+
+  _initDropzone: ->
+    $container = $('.js-content-container')
+    $container.dropzone
+      url: $container.data('attachmentsPath')
+      paramName: 'attachment[data]'
+      previewsContainer: false
+      clickable: '.js-upload'
+      params:
+        authenticity_token: $('meta[name=csrf-token]').prop('content')
+        'attachment[page_id]': $container.data('pageId')
+      success: (file, json) =>
+        text = if json.is_image
+          "[![](#{json.path})](#{json.path})"
+        else
+          "[#{json.file_name}](#{json.path})"
+        @insert(text)
+        @preview()
+
+  _initMousetrap: ->
+    Mousetrap.bind 'e', ->
+      Turbolinks.visit $('.js-edit-page').prop('href')
+
+    Mousetrap.bindGlobal 'mod+s', (e) ->
+      if /wiki_title|wiki_content|wiki_slug|page_title|page_content/.test(e.target.id)
+        e.preventDefault()
+        $('.js-submit-page').click()
+
+  preview: ->
+    return unless @$content.length
+
+    path = @$content.data('previewPath')
+    params =
+      page:
+        content: @$content.val()
+    $.ajax
+      method: 'POST'
+      dataType: 'json'
+      url: path
+      data: params
+      success: (data) ->
+        $('.js-preview').html(data.html)
+      error: (jqXHR, textStatus, errorThrown) ->
+        console.error(jqXHR, textStatus, errorThrown)
+        alert(errorThrown)
+
+  insert: (text, pos, mode = 'before') ->
+    @$content.selection('insert', { text: text, mode: mode })
+
+    evt = document.createEvent('Event')
+    evt.initEvent('autosize:update', true, false)
+    @$content.get(0).dispatchEvent(evt)
+
+    @preview()
+
+    if pos
+      end = @$content.selection('getPos').end + pos
+      @$content.selection('setPos', { start: end, end: end })
+
+controller = new PagesController()
+
 $ ->
-  preview()
-  autosize($('#page_content'))
-  $('#page_parent_id').select2()
-  initDropzone()
-  initMousetrap()
-  $('[data-toggle=tooltip]').tooltip()
-
-initDropzone = ->
-  $container = $('.js-content-container')
-  $container.dropzone
-    url: $container.data('attachmentsPath')
-    paramName: 'attachment[data]'
-    previewsContainer: false
-    clickable: '.js-upload'
-    params:
-      authenticity_token: $('meta[name=csrf-token]').prop('content')
-      'attachment[page_id]': $container.data('pageId')
-    success: (file, json) ->
-      text = if json.is_image
-        "[![](#{json.path})](#{json.path})"
-      else
-        "[#{json.file_name}](#{json.path})"
-      insert(text)
-      preview()
-
-initMousetrap = ->
-  Mousetrap.bind 'e', ->
-    Turbolinks.visit $('.js-edit-page').prop('href')
-
-  Mousetrap.bindGlobal 'mod+s', (e) ->
-    if /wiki_title|wiki_content|wiki_slug|page_title|page_content/.test(e.target.id)
-      console.log('hoge', )
-      e.preventDefault()
-      $('.js-submit-page').click()
-
-preview = (e) ->
-  $content = $('#page_content')
-  return unless $content.length
-
-  path = $content.data('previewPath')
-  params =
-    page:
-      content: $content.val()
-  $.ajax
-    method: 'POST'
-    dataType: 'json'
-    url: path
-    data: params
-    success: (data) ->
-      $('.js-preview').html(data.html)
-    error: (jqXHR, textStatus, errorThrown) ->
-      console.error(jqXHR, textStatus, errorThrown)
-      alert(errorThrown)
-
-insert = (text, mode = 'before') ->
-  $content = $('#page_content')
-  $content
-    .selection('insert', { text: text, mode: mode })
-
-  evt = document.createEvent('Event')
-  evt.initEvent('autosize:update', true, false)
-  $content.get(0).dispatchEvent(evt)
-
-  preview()
+  controller.init()

 $(document).on 'keyup', '#page_content', _.throttle ->
-  preview()
+  controller.preview()
 , 1000

 $(document).on 'click', '.js-insert', (e) ->
@@ -73,13 +84,9 @@
   $elm = $(this)

   text = $elm.data('text')
-  insert(text.replace(/\\n/g, '\n'))
+  pos = $elm.data('pos') || 0

-  pos = $elm.data('pos')
-  if pos
-    $content = $('#page_content')
-    end = $content.selection('getPos').end + pos
-    $content.selection('setPos', { start: end, end: end })
+  controller.insert(text.replace(/\\n/g, '\n'), pos)

 $(document).on 'click', '.js-upload', (e) ->
   e.preventDefault()


Fix typeahead re-initialization

Fix typeahead re-initialization

Fixes: #570

Signed-off-by: Jürgen Löhel <a2d00de43eff6c65127a988f75aabd48f4667899@suse.com>

--- app/assets/javascripts/teams.coffee
+++ app/assets/javascripts/teams.coffee
@@ -1,6 +1,7 @@
 $(document).on "page:change", ->

   set_typeahead = (url) ->
+    $('.remote .typeahead').typeahead 'destroy'
     bloodhound = new Bloodhound(
         datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
         queryTokenizer: Bloodhound.tokenizers.whitespace,


Get added parcel from diff patch patch array.

--- src/coffee/utils/order-utils.coffee
+++ src/coffee/utils/order-utils.coffee
@@ -52,14 +52,14 @@
             actions.push action

           else
-            delivery = deliveryDiff
             # iterate over parcel instances
-            _.each _.keys(delivery.parcels), (parcelIndex) ->
+            _.each _.keys(deliveryDiff.parcels), (parcelIndex) ->
               if parcelIndex != '_t'
-                parcel = delivery.parcels[parcelIndex]
-                if _.isArray parcel
+                parcelDiff = deliveryDiff.parcels[parcelIndex]
+
+                if _.isArray parcelDiff
                   # delivery was added
-                  delivery = _.last parcel
+                  parcel = _.last parcelDiff
                   action =
                     action: 'addParcelToDelivery'
                     measurements: old_obj.deliveries[deliveryIndex].parcels[parcelIndex].measurements


Add user Facebook photo in me page.

Add user Facebook photo in me page.

--- app/client/js/react/pages/me.coffee
+++ app/client/js/react/pages/me.coffee
@@ -16,11 +16,14 @@
     @component = React.createFactory React.createClass

       render: ->
+        fid = userStore.user.providers.facebook.id
         publishedSongs = userStore.songs.filter (song) -> song.isPublished()

         div className: 'page',
           p {}, @getWelcomeMessage userStore.user.name
-          # img className: 'user-picture', src: user.thirdPartyUserData.picture.data.url
+          img
+            className: 'user-picture'
+            src: "http://graph.facebook.com/#{fid}/picture?type=normal"
           nav {},
             login.component {}
             button


:lipstick:

:lipstick:

--- src/git.coffee
+++ src/git.coffee
@@ -1,6 +1,6 @@
 path = require 'path'
 fs = require 'fs-plus'
-{Repository} = require('../build/Release/git.node')
+{Repository} = require '../build/Release/git.node'

 statusIndexNew = 1 << 0
 statusIndexModified = 1 << 1


Using arrow keys for asteroids.

Using arrow keys for asteroids.

--- examples/asteroids.coffee
+++ examples/asteroids.coffee
@@ -96,11 +96,11 @@

     # Map key presses to behaviours.
     commands =
-        107 : () ->
+        38 : () ->
             ship.thrust()
-        106 : () ->
+        37 : () ->
             ship.starboard()
-        108 : () ->
+        39 : () ->
             ship.port()
         32 : () ->
             bullet = ship.shootBullet()
@@ -110,7 +110,7 @@

     # Attach behaviours.
-    $(document).keypress (event) ->
+    $(document).keydown (event) ->
         key = event.which || event.keyCode
         console.log(key)


added ignore in invite listener

added ignore in invite listener

--- src/irc.coffee
+++ src/irc.coffee
@@ -271,6 +271,11 @@
     bot.addListener 'invite', (channel, from) ->
       console.log('%s invited you to join %s', from, channel)

+      if from in options.ignoreUsers
+        console.log('Ignoring user: %s', from)
+        # we'll ignore this message if it's from someone we want to ignore
+        return
+
       if not process.env.HUBOT_IRC_PRIVATE or process.env.HUBOT_IRC_IGNOREINVITE
         bot.join channel