refactored - less repetition, easier to read

--- benchmarks/validators.coffee
+++ benchmarks/validators.coffee
@@ -22,7 +22,7 @@

 # don't actually download the draft, because GitHub Pages might be down.
 # yet you still set the URL as a remote reference - it's how it's done in
-# z-schema tests.
+# the z-schema tests.
 actualDraft = require("fs").readFileSync("./test/json-schema/draft-04/schema", "utf8")
 request(remoteSchema, (error, response, body) ->
   zValidator.setRemoteReference(remoteSchema, JSON.parse(actualDraft)))
@@ -32,14 +32,19 @@
     when 3 then require "../src/draft3"
     when 4 then require "../src/draft4"

-
-
 # do the actual benchmarking
 module.exports =

   benchmark: ({draft, name, schema, valid_doc, repeats}) ->

     libraries = []
+
+    benchmarker = (description, theValidator, callback) ->
+      libraries.push new Benchmark description, (bm) ->
+        bm.setup -> theValidator
+        bm.measure (validator) ->
+          for i in [1..repeats]
+            result = callback(validator)

     console.log """

@@ -50,75 +55,40 @@
     """

     # these validators work for either draft 3 or 4 of JSON Schema
-    jsck = new Benchmark "JSCK: valid document", (bm) ->
-      bm.setup -> new (JSCK draft)(schema)
-      bm.measure (validator) ->
-        for i in [1..repeats]
-          result = validator.validate(valid_doc)
-    libraries.push jsck
+    benchmarker("JSCK: valid document", new (JSCK draft)(schema), (validator) ->
+      validator.validate(valid_doc))

-    jsonschema = new Benchmark "jsonschema: valid document", (bm) ->
-      bm.setup -> new JSONSchema()
-      bm.measure (validator) ->
-        for i in [1..repeats]
-          result = validator.validate(valid_doc, schema).errors
-    libraries.push jsonschema
+    benchmarker("jsonschema: valid document", new JSONSchema(), (validator) ->
+      validator.validate(valid_doc, schema).errors)

-    tv4Benchmark = new Benchmark "tv4: valid document", (bm) ->
-      bm.setup -> tv4
-      bm.measure (validator) ->
-        for i in [1..repeats]
-          result = validator.validate(valid_doc, schema).error
-    libraries.push tv4Benchmark
+    benchmarker("tv4: valid document", tv4, (validator) ->
+      validator.validate(valid_doc, schema).error)

     switch draft
       when 4 # these validators work only for draft 4 of JSON Schema

-        jayschema = new Benchmark "jayschema: valid document", (bm) ->
-          bm.setup -> new JaySchema()
-          bm.measure (validator) ->
-            for i in [1..repeats]
-              result = validator.validate(valid_doc, schema)
-        libraries.push jayschema
+        benchmarker("jayschema: valid document", new JaySchema(), (validator) ->
+          validator.validate(valid_doc, schema))

         # note: z-schema will not recognize the valid doc as a valid doc
-        zSchema = new Benchmark "z-schema: valid document", (bm) ->
-          bm.setup -> zValidator
-          bm.measure (validator) ->
-            for i in [1..repeats]
-              result = validator.validate(valid_doc, schema)
-        libraries.push zSchema
+        benchmarker("z-schema: valid document", zValidator, (validator) ->
+          validator.validate(valid_doc, schema))

-    # these validators work only for draft 3 of JSON Schema
-      when 3
+      when 3 # these validators work only for draft 3 of JSON Schema

         # note: amanda reports errors on integers as utc-millisec,
         # and on http://localhost:8998, due to a huge URL regex
-        amanda_bm = new Benchmark "Amanda: valid document", (bm) ->
-          bm.setup ->
-            amandaValidator = amanda("json")
-          bm.measure (validator) ->
-            for i in [1..repeats]
-              # pass Amanda an empty error-reporting function
-              result = validator.validate(valid_doc, schema, () ->)
-        libraries.push amanda_bm
+        benchmarker("Amanda: valid document", amanda("json"), (validator) ->
+          # pass Amanda an empty error-reporting function for generous benchmarking
+          validator.validate(valid_doc, schema, () ->))

-        jsv_bm = new Benchmark "JSV: valid document", (bm) ->
-          bm.setup ->
-            jsv = JSV.createEnvironment("json-schema-draft-03")
-            jsv.createSchema(schema)
-          bm.measure (validator) ->
-            for i in [1..repeats]
-              result = validator.validate(valid_doc).errors
-        libraries.push jsv_bm
+        jsv = JSV.createEnvironment("json-schema-draft-03")
+        jsv.createSchema(schema)
+        benchmarker("JSV: valid document", jsv, (validator) ->
+          validator.validate(valid_doc).errors)

-        jsonGate_bm = new Benchmark "json-gate: valid document", (bm) ->
-          bm.setup ->
-            jg = jsonGateCreateSchema(schema)
-          bm.measure (validator) ->
-            for i in [1..repeats]
-              result = validator.validate(valid_doc)
-        libraries.push jsonGate_bm
+        benchmarker("json-gate: valid document", jsonGateCreateSchema(schema), (validator) ->
+          validator.validate(valid_doc))

     results = Benchmark.compare libraries, {samples}


Make packages linkable. Fixes #10

Make packages linkable. Fixes #10

--- lib/package-panel-view.coffee
+++ lib/package-panel-view.coffee
@@ -16,7 +16,9 @@
   addPackage: (pack, timeKey) ->
     @list.append $$ ->
       @li class: 'list-item', =>
-        @span class: 'inline-block', pack.name
+        homepage = pack.metadata?.homepage
+        homepage ?= "https://atom.io/packages/#{pack.name}"
+        @a class: 'inline-block', href: homepage, pack.name
         highlightClass = 'highlight-warning'
         highlightClass = 'highlight-error' if pack[timeKey] > 25
         @span class: "inline-block #{highlightClass}", "#{pack[timeKey]}ms"


Mark multi-line doc comments as deprecated (docs highly discourage their usage)

--- grammars/rust.cson
+++ grammars/rust.cson
@@ -36,9 +36,27 @@
   'block_doc_comment': {
     'comment': 'Block documentation comment'
     'name': 'comment.block.documentation.rust'
-    'begin': '/\\*[\\*!](?![\\*/])'
-    'end': '\\*/'
-    'patterns': [
+    'begin': '(/\\*[\\*!](?![\\*/]))'
+    'end': '(\\*/)'
+    'beginCaptures': {
+      '1': { 'name': 'invalid.deprecated.rust' }
+    }
+    'endCaptures': {
+      '1': { 'name': 'invalid.deprecated.rust' }
+    }
+    'patterns': [
+      {
+        'begin': '^\\s*(\\*)(?!/)'
+        'end': '$'
+        'beginCaptures': {
+          '1': { 'name': 'invalid.deprecated.rust' }
+        }
+        'patterns': [
+          { 'include': '#block_comment' }
+          { 'include': '#block_doc_comment' }
+          { 'include': '#markdown' }
+        ]
+      }
       { 'include': '#block_comment' }
       { 'include': '#block_doc_comment' }
       { 'include': '#markdown' }


Don't sanitize width/height of img to allow manual resizing. Fix #36

--- client/lib/sanitize.coffee
+++ client/lib/sanitize.coffee
@@ -2,5 +2,5 @@
 ## https://github.com/punkave/sanitize-html

 sanitizeHtml.defaults.allowedTags.push 'img'
-sanitizeHtml.defaults.allowedAttributes.img.push 'alt', 'title'
+sanitizeHtml.defaults.allowedAttributes.img.push 'alt', 'title', 'width', 'height'
 sanitizeHtml.defaults.allowedAttributes.a.push 'title'


Refactor our helper for merging AST location data to take two locationData objects and return a new, merged one

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -294,17 +294,17 @@
     return
       loc:
         start:
-          line: first_line + 1
+          line:   first_line + 1
           column: first_column
         end:
-          line: last_line + 1
+          line:   last_line + 1
           column: last_column + 1
       range: [
         range[0]
         range[1]
       ]
       start: range[0]
-      end: range[1]
+      end:   range[1]

   # Passes each child to a function, breaking when the function returns `false`.
   eachChild: (func) ->
@@ -4166,28 +4166,24 @@
     when other     then (if options.double then "\\#{other}" else other)
   "#{options.delimiter}#{body}#{options.delimiter}"

-# Extends the location data of an AST node to include the location data from
-# another AST node.
-mergeAstLocationData = (intoNode, fromNode) ->
-  {range: intoRange} = intoNode
-  {range: fromRange} = fromNode
-  return intoNode unless intoRange and fromRange
-  if fromRange[0] < intoRange[0]
-    intoNode.range = intoRange = [
-      fromRange[0]
-      intoRange[1]
+# Take two AST nodes, or two AST nodes’ location data objects, and return a new
+# location data object that encompasses the location data of both nodes. So the
+# new `start` value will be the earlier of the two nodes’ `start` values, the
+# new `end` value will be the later of the two nodes’ `end` values, etc.
+lesser  = (a, b) -> if a < b then a else b
+greater = (a, b) -> if a > b then a else b
+mergeAstLocationData = (nodeA, nodeB) ->
+  return
+    loc:
+      start:
+        line:   lesser nodeA.loc.start.line,   nodeB.loc.start.line
+        column: lesser nodeA.loc.start.column, nodeB.loc.start.column
+      end:
+        line:   greater nodeA.loc.end.line,   nodeB.loc.end.line
+        column: greater nodeA.loc.end.column, nodeB.loc.end.column
+    range: [
+      lesser  nodeA.range[0], nodeB.range[0]
+      greater nodeA.range[1], nodeB.range[1]
     ]
-    intoNode.start = fromNode.start
-    intoNode.loc =
-      start: fromNode.loc.start
-      end: intoNode.loc.end
-  if fromRange[1] > intoRange[1]
-    intoNode.range = [
-      intoRange[0]
-      fromRange[1]
-    ]
-    intoNode.end = fromNode.end
-    intoNode.loc =
-      start: intoNode.loc.start
-      end: fromNode.loc.end
-  intoNode
+    start: lesser  nodeA.start, nodeB.start
+    end:   greater nodeA.end,   nodeB.end


switched to canonical two-space indentation in __bind helper

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -1770,16 +1770,16 @@
   # Create a function bound to the current value of "this".
   bind: '''
     (Function.prototype.bind ? Function.prototype.call.bind(Function.prototype.bind) : function(fn, me){
-		return function bound(){
-			if (!(this instanceof bound))
-				return fn.apply(me, arguments);
-			var ctor = function(){};
-			ctor.prototype = fn.prototype;
-			var obj = new ctor();
-			fn.apply(obj, arguments);
-			return obj;
-		};
-	})
+      return function bound(){
+        if (!(this instanceof bound))
+          return fn.apply(me, arguments);
+        var ctor = function(){};
+        ctor.prototype = fn.prototype;
+        var obj = new ctor();
+        fn.apply(obj, arguments);
+        return obj;
+      };
+    })
   '''

   # Discover if an item is in an array.


Fix: mentions/permissions autocomplete works in site feed

--- assets/javascripts/views/002_permissions_fields_picker.js.coffee
+++ assets/javascripts/views/002_permissions_fields_picker.js.coffee
@@ -154,7 +154,7 @@
         return callback(follows, nextPage) if follows.length

         @input.showLoading()
-        new HTTP 'GET', "#{TentStatus.config.domain_tent_api_root}/followings", params, (res, xhr) =>
+        new HTTP 'GET', "#{TentStatus.config.tent_api_root}/followings", params, (res, xhr) =>
           return callback() unless xhr.status == 200
           @input.hideLoading()


cleanup

cleanup

--- lib/status-bar-manager.coffee
+++ lib/status-bar-manager.coffee
@@ -1,14 +1,14 @@
-{Disposable, CompositeDisposable} = require 'atom'
 {ElementBuilder} = require './utils'

-ContentsByMode =
-  'insert': ["insert", "Insert"]
-  'insert.replace': ["insert", "Replace"]
-  'normal': ["normal", "Normal"]
-  'visual': ["visual", "Visual"]
-  'visual.characterwise': ["visual", "Visual Char"]
-  'visual.linewise': ["visual", "Visual Line"]
-  'visual.blockwise': ["visual", "Visual Block"]
+# [NOTE] Order is important. I depend on chrome's "keep object order".
+modeToContent =
+  "normal": "Normal"
+  "visual.characterwise": "Visual Char"
+  "visual.linewise": "Visual Line"
+  'visual.blockwise': "Visual Block"
+  'visual': "Visual"
+  'insert.replace': "Replace"
+  'insert': "Insert"

 module.exports =
 class StatusBarManager
@@ -16,20 +16,17 @@
   prefix: 'status-bar-vim-mode-plus'

   constructor: ->
-    (@container = @div(id: "#{@prefix}-container", classList: ['inline-block']))
-    .appendChild(@element = @div(id: @prefix))
+    @container = @div(id: "#{@prefix}-container", classList: ['inline-block'])
+    @container.appendChild(@element = @div(id: @prefix))

   initialize: (@statusBar) ->

   update: (mode, submode) ->
     modeString = mode
     modeString += "." + submode if submode?
-    if newContents = ContentsByMode[modeString]
-      [className, text] = newContents
-      @element.className = "#{@prefix}-#{className}"
-      @element.textContent = text
+    @element.className = "#{@prefix}-#{mode}"
+    @element.textContent = modeToContent[modeString]

-  # Private
   attach: ->
     @tile = @statusBar.addRightTile(item: @container, priority: 20)


Vue instance & component snippets

Vue instance & component snippets

Signed-off-by: Caddy Dz <cbc6199cd1cb93e9d3589fb9aa7ecaece7b37833@gmail.com>

--- src/atom/snippets.cson
+++ src/atom/snippets.cson
@@ -5,4 +5,12 @@
 '.blade.php':
   'bstext':
     'prefix' : 'bstext'
-    'body': '
\n\t$2:\n\t<input type="text" class="form-control" name="$1" id="$1">\n
\n$3'
+    'body': '
\n\t$2:\n\t<input type="text" class="form-control" name="$1" id="$1">\n
\n$3'
+'.source.js':
+  'vue.component':
+    'prefix' : 'vue.component'
+    'body': 'Vue.component (\'${1:name}\', {\n\tprops: [\n\t\t\'${2:property}\',\n\t],\n\ttemplate: `\n\t\t${3:html}\n\t`,\n\tmethods: {\n\t\t${4:method_name}() {\n\t\t\t${5:// code}\n\t\t}\n\t}\n});\n$6'
+'.source.js':
+  'new vue':
+    'prefix' : 'new vue'
+    'body': 'new Vue({\n\tel: \'${1:#app}\',\n\tdata: {\n\t\t${2:name} : \'${3:value}\',\n\t}\n});\n$7'


:poop:

:poop:

--- src/atom/window.coffee
+++ src/atom/window.coffee
@@ -44,7 +44,7 @@
     $('head').append "<style path='#{fullPath}'>#{content}</style>"

   showConsole: ->
-    $native.showDevTools()
+    atom.native.showDevTools()

   onerror: ->
     @showConsole()


set init value immediately

set init value immediately

--- src/bacon.model.coffee
+++ src/bacon.model.coffee
@@ -58,8 +58,6 @@
     model.bind = (other) ->
       this.addSyncSource(other.syncEvents())
       other.addSyncSource(this.syncEvents())
-    model.onValue()
-    model.set(initValue) if (arguments.length >= 1)
     model.lens = (lens) ->
       lens = Lens(lens)
       lensed = Model()
@@ -70,8 +68,11 @@
         valueLens.set(e, lens.get(e.value))))
       lensed
     model.syncConverter = id
+    model.onValue()
+    if (arguments.length >= 1)
+      model.apply(Bacon.once(-> initValue))
     model
-
+
   Bacon.Model.syncCount = 0

   Model.combine = (template) ->


minor correction in the aux

minor correction in the aux

--- library/membrane/auxes.coffee
+++ library/membrane/auxes.coffee
@@ -138,6 +138,7 @@
     # on how the parasiting functionality is implemented and works.
     reviewParasites: (seeds, request, callback) ->
         assert parasites = try Auxiliaries.parasite()
+        assert seeds = seeds or try @constructor.aux()
         selfomit = (par) => par.target is @constructor
         assert parasites = _.reject parasites, selfomit
         assert _.isObject polygone = _.clone seeds or {}


fix a very strange and yet-unnoticed mistake with http/https

--- library/nucleus/scaled.coffee
+++ library/nucleus/scaled.coffee
@@ -72,10 +72,10 @@
         assert _.isObject options = @resolveSslDetails()
         assert _.isString host = nconf.get "master:host"
         assert _.isNumber port = nconf.get "master:https"
-        assert registr = @makeRegistrar q, "http", undefined
-        assert selectr = @makeSelectors q, "http", undefined
-        assert forward = @makeForwarder q, "http", selectr
-        assert upgrade = @makeUpgraders q, "http", selectr
+        assert registr = @makeRegistrar q, "https", undefined
+        assert selectr = @makeSelectors q, "https", undefined
+        assert forward = @makeForwarder q, "https", selectr
+        assert upgrade = @makeUpgraders q, "https", selectr
         remove = (s) -> _.remove q, (x) -> s.uuid is x.uuid
         @secureProxy = https.createServer options, forward
         assert _.isObject @domain; @domain.add @secureProxy


fix for boolean default usage

fix for boolean default usage

--- client/Framework/components/progressbar/KDProgressBarView.coffee
+++ client/Framework/components/progressbar/KDProgressBarView.coffee
@@ -2,7 +2,7 @@
   constructor:(options = {})->
     options.cssClass = KD.utils.curryCssClass "progressbar-container", options.cssClass

-    options.determinate or= yes
+    options.determinate  ?= yes
     options.initial      ?= no
     options.title        ?= ""


Need camel case for the new UI

Need camel case for the new UI

--- vendor/assets/javascripts/fustrate/components/pagination.coffee
+++ vendor/assets/javascripts/fustrate/components/pagination.coffee
@@ -1,24 +1,24 @@
 class Fustrate.Components.Pagination extends Fustrate.Components.Base
-  constructor: ({ @current_page, @total_pages, @total_entries, @per_page }) ->
+  constructor: ({ @currentPage, @totalPages, @totalEntries, @perPage }) ->
     @base = @constructor._getPreppedPaginationURL()

   link: (text, page, options = {}) =>
     Fustrate.linkTo(text, "#{@base}page=#{page}", options)

   previousLink: =>
-    if @current_page > 1
+    if @currentPage > 1
       return "

-          #{@link('← Previous', @current_page - 1, rel: 'prev')}
+          #{@link('← Previous', @currentPage - 1, rel: 'prev')}

"

     '
← Previous
'

   nextLink: =>
-    if @current_page < @total_pages
+    if @currentPage < @totalPages
       return "

-          #{@link('Next →', @current_page + 1, rel: 'next')}
+          #{@link('Next →', @currentPage + 1, rel: 'next')}

"

     '
Next →
'
@@ -26,9 +26,9 @@
   generate: =>
     pages = []

-    if @total_pages > 1
+    if @totalPages > 1
       pages = for i in @windowedPageNumbers()
-        if i is @current_page
+        if i is @currentPage
           "
#{Fustrate.linkTo(i, '#')}
"
         else if i is 'gap'
           '
…
'
@@ -41,27 +41,27 @@
     $('
').html pages.join(' ')

   windowedPageNumbers: =>
-    window_from = @current_page - 4
-    window_to = @current_page + 4
+    window_from = @currentPage - 4
+    window_to = @currentPage + 4

-    if window_to > @total_pages
-      window_from -= window_to - @total_pages
-      window_to = @total_pages
+    if window_to > @totalPages
+      window_from -= window_to - @totalPages
+      window_to = @totalPages

     if window_from < 1
       window_to += 1 - window_from
       window_from = 1
-      window_to = @total_pages if window_to > @total_pages
+      window_to = @totalPages if window_to > @totalPages

     middle = [window_from..window_to]

     left = if 4 < middle[0] then [1, 2, 'gap'] else [1...middle[0]]

-    if @total_pages - 3 > middle.last()
-      right = [(@total_pages - 1)..@total_pages]
+    if @totalPages - 3 > middle.last()
+      right = [(@totalPages - 1)..@totalPages]
       right.unshift 'gap'
-    else if middle.last() + 1 <= @total_pages
-      right = [(middle.last() + 1)..@total_pages]
+    else if middle.last() + 1 <= @totalPages
+      right = [(middle.last() + 1)..@totalPages]
     else
       right = []


about to create a client state

about to create a client state

--- src/chat/window.coffee
+++ src/chat/window.coffee
@@ -1,6 +1,10 @@
 exports = window.chat ?= {}

 class Window
+  ##
+  # The screen will auto scroll as long as the user didn't scroll up more then
+  # this many pixels.
+  ##
   @SCROLLED_DOWN_BUFFER = 8

   constructor: (@name) ->


Simplifying Server class, implementing connect.

--- cli/src/commands/server.coffee
+++ cli/src/commands/server.coffee
@@ -3,12 +3,14 @@
 fs   = require "fs"
 path = require "path"
 pn   = path.normalize
+conn = require 'connect'

 exec = require( "child_process" ).exec

 Compiler = require './compiler'

 module.exports = class Server
+  @socket = null

   constructor:( @the, options )->
     @port = options[1] or 11235
@@ -19,67 +21,17 @@
     @compiler = new Compiler @the, true

   start_server:()->
-    @server = http.createServer( @_handler )
+    @server = conn()
+                .use(conn.static @root )
+                .use( (req, res)->
+                  res.end (fs.readFileSync "#{@root}/index.html", 'utf-8')
+                ).listen @port

-    Server.io = (require "socket.io").listen @server
-
-    @server.listen @port
-
-    Server.io.sockets.on "connection", (socket) ->
-
+    @socket = (require "socket.io").listen @server
+    @socket.sockets.on "connection", (socket) ->
       console.log 'client connected'

     console.log "#{'Server running at'.bold} http://localhost:#{@port}".grey

   close_server:()->
-    @server.close()
-
-
-  _handler:(request, response)=>
-
-    headers = request.headers
-    agent = headers['user-agent']
-    crawl = agent.indexOf( "Googlebot" ) >= 0 || agent.indexOf( "curl" ) >= 0
-
-    uri = url.parse( request.url ).pathname
-    filename = path.join( @root, uri )
-
-    fs.exists filename, (exists)=>
-
-      if !exists || fs.lstatSync( filename ).isDirectory()
-
-        filename = path.join( @root, "/index.html" )
-        file = fs.readFileSync( filename , "utf-8")
-        response.writeHead 200, {"Content-Type": "text/html"}
-
-        if crawl is true
-          cache = pn "#{@root}/static/#{request.url}/index.html"
-          if fs.existsSync cache
-            src = fs.readFileSync cache
-            response.writeHead 200, {"Content-Type": "text/html"}
-            response.write "#{src}\n"
-            response.end()
-          else
-            console.log "TODO: implement on-demmand cache"
-        else
-          response.write file
-          response.end()
-        return
-
-
-      fs.readFile filename, "binary", (err, file)->
-        if err
-          response.writeHead 500, {"Content-Type": "text/plain"}
-          response.write err + "\n"
-          response.end()
-          return
-
-        if filename.match /.js$/m
-          response.writeHead 200, {"Content-Type": "text/javascript"}
-        else if (mime = filename.match /(jpg|png|gif)$/m)
-          response.writeHead 200, {"Content-Type": "image/#{mime[1]}"}
-        else if filename.match /.css$/m
-          response.writeHead 200, {"Content-Type": "text/css"}
-
-        response.write file, "binary"
-        response.end()+    @server.close()


Remove broken throbber

Remove broken throbber

--- client/app/MainApp/main.coffee
+++ client/app/MainApp/main.coffee
@@ -58,7 +58,7 @@
                 # THE NOTIFICATION APPEARS WHEN MODAL WAS CLOSED BEFORE CONNECTION RE-ESTABLISHES
                 connectionLostNotification = new KDNotificationView
                   title    : "Server Connection Has Been Lost"
-                  content  : " Trying to reconnect..., changes will not be saved until server reconnects, please back up locally."
+                  content  : "Trying to reconnect..., changes will not be saved until server reconnects, please back up locally."
                   duration : 999999999
     ,500

@@ -66,7 +66,7 @@
   initConnectionEvents conn

   if connectionLostModalId?
-    (KDModalController.getModalById connectionLostModalId).destroy()
+    (KDModalController.getModalById connectionLostModalId)?.destroy()
     connectionLostModalId = null

     # THE MODAL APPEARS WHEN CONNECTION IS RE-ESTABLISHED


make clicks on home level of #subnav-path work, after change to nonpushstate detection

--- client/code/view/subnav.coffee
+++ client/code/view/subnav.coffee
@@ -264,7 +264,7 @@
     @$el.html("""

-
+
             #{window.user.effective.displayName or window.user.effective.shortName}’s data hub

@@ -290,7 +290,7 @@
     @$el.html("""

-
+
             #{window.user.effective.displayName or window.user.effective.shortName}’s data hub

@@ -326,7 +326,7 @@
     @$el.html("""

-
+
             #{window.user.effective.displayName or window.user.effective.shortName}’s data hub


failover -- change params

failover -- change params

--- salvus/compute.coffee
+++ salvus/compute.coffee
@@ -34,7 +34,7 @@

 # obviously don't want to trigger this too quickly, since it may mean file loss.
-AUTOMATIC_FAILOVER_TIME_S = 30
+AUTOMATIC_FAILOVER_TIME_S = 75

 SERVER_STATUS_TIMEOUT_S = 5  # 5 seconds

@@ -736,7 +736,7 @@
                 # triggers failover of project to another node.
                 misc.retry_until_success
                     f           : f
-                    start_delay : 1000
+                    start_delay : 15000
                     max_time    : AUTOMATIC_FAILOVER_TIME_S*1000
                     cb          : (err) =>
                         if err


Fix initial naming of file to use extensions

--- src/code/client.coffee
+++ src/code/client.coffee
@@ -255,8 +255,7 @@
     @_event 'newedFile', {content: ""}

   setInitialFilename: (filename) ->
-    @state.metadata.name = filename
-    @state.metadata.filename = filename
+    @state.metadata.rename filename
     @save()

   isSaveInProgress: ->


rethink: project users

rethink: project users

--- salvus/rethink.coffee
+++ salvus/rethink.coffee
@@ -1,12 +1,13 @@
 # TODO: client api -- get rid of any reference to consistency and objectify

+async = require('async')
+_ = require('underscore')

 winston = require('winston')
 winston.remove(winston.transports.Console)
 winston.add(winston.transports.Console, {level: 'debug', timestamp:true, colorize:true})

-async = require('async')
 {defaults} = misc = require('misc')
 required = defaults.required

@@ -417,6 +418,10 @@
                 opts.cb?(err)
             else
                 v = misc.dict(([r.id, {first_name:r.first_name, last_name:r.last_name}] for r in x))
+                # fill in unknown users (should never be hit...)
+                for id in opts.account_ids
+                    if not v[id]
+                        v[id] = {first_name:undefined, last_name:undefined}
                 opts.cb(err, v)

     # TODO: change to get_usernames... both here and in CLIENT code.
@@ -799,14 +804,78 @@
             path        : required
             cb          : required
         x = {}; x[opts.path] = true
-        db.table('projects').get(opts.project_id).replace(@r.row.without(public_paths:x).run(opts.cb))
-
-    # get map {project_group:[{account_id:?,first_name:?,last_name:?}], ...}
+        db.table('projects').get(opts.project_id).replace(@r.row.without(public_paths:x)).run(opts.cb)
+
+    _validate_uuids: (opts) =>
+        for k, v of opts
+            if k.slice(k.length-2) == 'id'
+                if not misc.is_valid_uuid_string(v)
+                    opts.cb("invalid #{k} -- #{v}")
+                    return false
+        return true
+
+    add_user_to_project: (opts) =>
+        opts = defaults opts,
+            project_id : required
+            account_id : required
+            group      : required  # see PROJECT_GROUPS above
+            cb         : required  # cb(err)
+        if opts.group not in misc.PROJECT_GROUPS
+            opts.cb("unknown project group '#{opts.group}'"); return
+        if not @_validate_uuids(opts) then return
+        x = {}
+        x[opts.group] = @r.row(opts.group).default([]).setInsert(opts.account_id)
+        @table('projects').get(opts.project_id).update(x).run(opts.cb)
+
+    remove_user_from_project: (opts) =>
+        opts = defaults opts,
+            project_id : required
+            account_id : required
+            group      : required  # see PROJECT_GROUPS above
+            cb         : required  # cb(err)
+
+
     get_project_users: (opts) =>
         opts = defaults opts,
             project_id : required
             groups     : PROJECT_GROUPS
-            cb         : required
+            cb         : required    # cb(err, {group:[{account_id:?,first_name:?,last_name:?}], ...})
+
+        groups = undefined
+        names  = undefined
+        async.series([
+            # get account_id's of all users
+            (cb) =>
+                @get_project_data
+                    project_id : opts.project_id
+                    columns    : opts.groups
+                    cb         : (err, _groups) =>
+                        if err
+                            cb(err)
+                        else
+                            groups = _groups
+                            for i in [0...groups.length]
+                                if not groups[i]?
+                                    groups[i] = []
+                            cb()
+            # get names of users
+            (cb) =>
+                @account_ids_to_usernames
+                    account_ids : _.flatten((v for k,v of groups))
+                    cb          : (err, _names) =>
+                        names = _names
+                        cb(err)
+        ], (err) =>
+            if err
+                opts.cb(err)
+            else
+                for group, v of groups
+                    for i in [0...v.length]
+                        account_id = v[i]
+                        x = names[account_id]
+                        v[i] = {account_id:account_id, first_name:x.first_name, last_name:x.last_name}
+                opts.cb(false, groups)
+        )

     # Set last_edited for this project to right now, and possibly update its size.
     # It is safe and efficient to call this function very frequently since it will
@@ -860,20 +929,7 @@
         else
             return null

-    add_user_to_project: (opts) =>
-        opts = defaults opts,
-            project_id : required
-            account_id : required
-            group      : required  # see PROJECT_GROUPS above
-            cb         : required  # cb(err)
-
-
-    remove_user_from_project: (opts) =>
-        opts = defaults opts,
-            project_id : required
-            account_id : required
-            group      : required  # see PROJECT_GROUPS above
-            cb         : required  # cb(err)
+

     # cb(err, true if project is public)
     project_is_public: (opts) =>


Much more conservative loading in real time map

--- client/terrain/streamer.coffee
+++ client/terrain/streamer.coffee
@@ -29,7 +29,7 @@
   ])

 # How many times higher resolutions should the maps have in relation to the terrain pxWidth
-MAP_SCALE = 1
+MAP_SCALE = 1.3

 # How many tiles should we keep in the cache
 TILE_CACHE_LIMIT = 40
@@ -37,13 +37,14 @@
 class TerrainTile
   constructor: (@bounds, @pxWidth, onload = null) ->
     @pxHeight = Math.round(@pxWidth/@meterWidth()*@meterHeight())
+    console.log "Dimensions:", @pxWidth, @pxHeight
     @resolution = @pxWidth/@meterWidth()
     @terrainImage = new Image()
     @terrainImage.onload = onload
     @terrainImage.src = "/dtm?box=#{[@bounds.min.x, @bounds.max.y, @bounds.max.x, @bounds.min.y].join(',')}&outsize=#{@pxWidth},#{@pxHeight}"
     @mapImage = new Image()
     @mapImage.onload = onload
-    @mapImage.src = "/map?box=#{[@bounds.min.x, @bounds.max.y, @bounds.max.x, @bounds.min.y].join(',')}&outsize=#{@pxWidth*MAP_SCALE},#{@pxHeight*MAP_SCALE}"
+    @mapImage.src = "/map?box=#{[@bounds.min.x, @bounds.max.y, @bounds.max.x, @bounds.min.y].join(',')}&outsize=#{Math.round(@pxWidth*MAP_SCALE)},#{Math.round(@pxHeight*MAP_SCALE)}"
   meterHeight: ->
     Math.abs(@bounds.max.y-@bounds.min.y)
   meterWidth: ->
@@ -175,9 +176,19 @@

   # Call this to change what area the streamer is showing
   setBounds: (bounds) ->
+    clearInterval(@timer) if @timer?
     @bounds = bounds
     @resetRawRez()
     @update()
+    doLoad = =>
+      # If there is no coarse data to provide adequate preview, we need to load a new atlas area
+      unless @resolution >= 0.05
+        @loadExtended(30.0, 0.05)
+      # If the effective resolution is anything less than full, we will order some more data from the server allowing for
+      # some scrolling and resizing
+      unless @resolution >= 1.0
+        @loadExtended(1.5, 1.2)
+    @timer = setTimeout(doLoad, 400)

   # Called when new data arrive or bounds are updated to redraw map and terrain
   update: ->
@@ -188,20 +199,13 @@
     @bounds = bounds if bounds?
     if @bounds?
       # This variable keeps track of the best resolution we had covering the entire output region
-      resolution = 0
+      @resolution = 0
       for tile in @relevantTiles()
         effectiveResolution = @drawTile(tile)
         # We keep track of the effective resolution of the highest resolution tile that covers the whole area
         if tile.bounds.contains(@bounds)
-          resolution = effectiveResolution
-          break if resolution >= 1.0
-      # If there is no coarse data to provide adequate preview, we need to load a new atlas area
-      unless resolution >= 0.05
-        @loadExtended(30.0, 0.3)
-      # If the effective resolution is anything less than full, we will order some more data from the server allowing for
-      # some scrolling and resizing
-      unless resolution >= 1.0
-        @loadExtended(3.0, 1.3)
+          @resolution = effectiveResolution
+          break if @resolution >= 1.0
     @onupdate() if @onupdate

 module.exports = TerrainStreamer


Using ease as default instead of ease-in-out

--- framer/Animators/BezierCurveAnimator.coffee
+++ framer/Animators/BezierCurveAnimator.coffee
@@ -24,7 +24,7 @@
 			options = {values: options}

 		@options = _.defaults options,
-			values: BezierCurveDefaults["ease-in-out"]
+			values: BezierCurveDefaults["ease"]
 			time: 1
 			precision: 1 / 1000


add edit mode functionality

add edit mode functionality

--- client/views/admin/edit.coffee
+++ client/views/admin/edit.coffee
@@ -1,17 +1,73 @@
 getPost = ->
   Post.first Session.get('postId')
-
+
+# add new lines after block elements (visual)
+formateHtml = (html) ->
+  block = /\<\/(address|article|aside|audio|blockquote|canvas|dd|div|dl|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hgroup|hr|noscript|ol|output|p|pre|section|table|tfoot|ul|video)\>(?!\n)/gmi
+  html.replace(block, (match) ->
+    "#{match}\n"
+  )
+
 Template.blogAdminEdit.rendered = ->
+  Session.set('editorTemplate', 'visualEditor')
+  Session.set('currentPost', getPost())
+
+Template.visualEditor.rendered = ->
   @editor = new MediumEditor '.editable',
     placeholder: 'Start typing...'
     buttons:
       ['bold', 'italic', 'underline', 'anchor', 'pre', 'header1', 'header2', 'orderedlist', 'unorderedlist', 'quote', 'image']

+Template.duoEditor.rendered = ->
+  @editor = new MediumEditor '.editable',
+    placeholder: 'Start typing...'
+    buttons:
+      ['bold', 'italic', 'underline', 'anchor', 'pre', 'header1', 'header2', 'orderedlist', 'unorderedlist', 'quote', 'image']
+
+  $('.html-editor').height($('.editable').height())
+  $('.editable').on('input', ->
+    $('.html-editor').val($('.editable').html().trim())
+    $('.html-editor').height($('.editable').height())
+  )
+
 Template.blogAdminEdit.helpers
   post: ->
     getPost() or {}

+  editor: ->
+    template: Session.get('editorTemplate')
+    post: Session.get('currentPost')
+
 Template.blogAdminEdit.events
+
+  'click .visual-toggle': ->
+    post = getPost()
+    post.body = $('.html-editor').val().trim()
+    Session.set('currentPost', post)
+    Session.set('editorTemplate', 'visualEditor')
+    $('.visual-toggle').addClass('selected')
+
+  'click .html-toggle': ->
+    post = getPost()
+    post.body = formateHtml($('.editable').html().trim())
+    Session.set('currentPost', post)
+    Session.set('editorTemplate', 'htmlEditor')
+    $('.html-toggle').addClass('selected')
+
+  'click .visual-html-toggle': ->
+    if $('.editable').get(0)
+      post = getPost()
+      post.body = formateHtml($('.editable').html().trim())
+    else
+      post = getPost()
+      post.body = $('.html-editor').val().trim()
+
+    Session.set('currentPost', post)
+    Session.set('editorTemplate', 'duoEditor')
+
+  'keyup .html-editor': ->
+    $('.editable').html($('.html-editor').val())
+    $('.html-editor').height($('.editable').height())

   'blur [name=title]': (e, tpl) ->
     e.preventDefault()
@@ -25,7 +81,11 @@
     e.preventDefault()
     form = $(e.currentTarget)

-    body = $('.editable', form).html().trim()
+    if $('.editable').get(0)
+      body = $('.editable', form).html().trim()
+    else
+      body = $('.html-editor').val().trim()
+
     slug = $('[name=slug]', form).val()

     if not body


log level currently not implemented

log level currently not implemented

--- src/quill.coffee
+++ src/quill.coffee
@@ -49,7 +49,6 @@

   @DEFAULTS:
     formats: ['align', 'bold', 'italic', 'strike', 'underline', 'color', 'background', 'font', 'size', 'link', 'image']
-    logLevel: false
     modules:
       'keyboard': true
       'paste-manager': true


Kill unused Range methods

Kill unused Range methods

--- src/range.coffee
+++ src/range.coffee
@@ -12,11 +12,6 @@
       if copy then object.copy() else object
     else
       new this(object.start, object.end)
-
-  @fromPointWithDelta: (pointA, rowDelta, columnDelta) ->
-    pointA = Point.fromObject(pointA)
-    pointB = new Point(pointA.row + rowDelta, pointA.column + columnDelta)
-    new this(pointA, pointB)

   @fromText: (args...) ->
     if args.length > 1
@@ -77,9 +72,6 @@
   coversSameRows: (other) ->
     @start.row == other.start.row && @end.row == other.end.row

-  add: (point) ->
-    new @constructor(@start.add(point), @end.add(point))
-
   translate: (startPoint, endPoint=startPoint) ->
     new @constructor(@start.translate(startPoint), @end.translate(endPoint))

@@ -115,14 +107,6 @@
   isEmpty: ->
     @start.isEqual(@end)

-  toDelta: ->
-    rows = @end.row - @start.row
-    if rows == 0
-      columns = @end.column - @start.column
-    else
-      columns = @end.column
-    new Point(rows, columns)
-
   getRowCount: ->
     @end.row - @start.row + 1


モジュールの記述方法を変更

モジュールの記述方法を変更

--- src/coffee/Main.coffee
+++ src/coffee/Main.coffee
@@ -1,3 +1,5 @@
-module.exports = class Main
+class Main
 	constructor: () ->
 		return
+
+module.exports = Main


Fix building false

Fix building false

--- src/coffee/main.coffee
+++ src/coffee/main.coffee
@@ -84,20 +84,15 @@
         switch config.comment.type
           when 'disqus'
             setTimeout ->
-              if W.DISQUS
-			    W.disqus_shortname = document.title
-			    W.disqus_url = location.href
+              W.disqus_identifier = document.title
+              W.disqus_url = location.href

-				DISQUS.reset {
+              if W.DISQUS then DISQUS.reset {
                   reload: true
 				}
               else
-                W.disqus_shortname = getConfig('shortname', 'comment')
-                W.disqus_identifier = document.title
-                W.disqus_url = location.href
-
                 setTimeout ->
-                  loadScript '//' + W.disqus_shortname + '.disqus.com/embed.js'
+                  loadScript '//' + getConfig('shortname', 'comment') + '.disqus.com/embed.js'
             , 1000

           when 'duoshuo'


Robot.RedisBrain extends Robot.Brain

Robot.RedisBrain extends Robot.Brain

--- src/robot.coffee
+++ src/robot.coffee
@@ -8,9 +8,9 @@
   # dispatch them to matching listeners.
   #
   # path - String directory full of Hubot scripts to load.
-  constructor: (path, name = "Hubot") ->
+  constructor: (path, name = "Hubot", brain = Robot.RedisBrain) ->
     @name        = name
-    @brain       = new Robot.Brain()
+    @brain       = new brain()
     @commands    = []
     @Response    = Robot.Response
     @listeners   = []
@@ -166,6 +166,26 @@
 class Robot.Brain
   # Represents somewhat persistent storage for the robot.
   #
+  # Returns a new Brain with no external storage.  Extend this!
+  constructor: () ->
+    @data =
+      users: { }
+
+  save: (cb) ->
+  close: ->
+
+  # Merge keys loaded from a DB against the in memory representation
+  #
+  # Returns nothing
+  #
+  # Caveats: Deeply nested structures don't merge well
+  mergeData: (data) ->
+    for k of (data or { })
+      @data[k] = data[k]
+
+class Robot.RedisBrain extends Robot.Brain
+  # Represents somewhat persistent storage for the robot.
+  #
   # Returns a new Brain that's trying to connect to redis
   #
   # Previously persisted data is loaded on a successful connection
@@ -173,8 +193,7 @@
   # Redis connects to a environmental variable REDISTOGO_URL or
   # fallsback to localhost
   constructor: () ->
-    @data =
-      users: { }
+    super()

     info = Url.parse process.env.REDISTOGO_URL || 'redis://localhost:6379'
     @client = Redis.createClient(info.port, info.hostname)
@@ -204,15 +223,6 @@
     clearInterval @saveInterval
     @save => @client.quit()

-  # Merge keys loaded from redis against the in memory representation
-  #
-  # Returns nothing
-  #
-  # Caveats: Deeply nested structures don't merge well
-  mergeData: (data) ->
-    for k of (data or { })
-      @data[k] = data[k]
-
 class Robot.Message
   # Represents an incoming message from the chat.
   #


Prepare handler for group encryption

Prepare handler for group encryption

--- js/gpg_form_encryptor.coffee
+++ js/gpg_form_encryptor.coffee
@@ -18,18 +18,26 @@
 submit_handler = (e, form) ->
   e.preventDefault()
   values = get_values(form)
-  source_encryption_names = find_names_for(form, "*[data-encrypt-source]")
-
+  single_defer = $.Deferred()
+  group_defer = $.Deferred()
+  single_encryption(form, values, (result) ->
+    console.log(result)
+    single_defer.resolve()
+  )
+  group_encryption(form, values, (result) ->
+    #console.log(result)
+    group_defer.resolve()
+  )
+  $.when(single_defer, group_defer).done(() -> # Spat defers to param list with apply
+    #form.submit()
+  )
+
+  #  source_encryption_names = find_names_for(form, "*[data-encrypt-source]")
   #$(form).find("*[data-encrypt]").each (index, element) ->
   #  n = $(element).attr("name")
   #  direct_encryption_names.push n
   ## names angelegt
   ##  arrray anlegen , duchlaufen encrypten
-  #group_encryption($(form).find("*[data-encrypt-source]"))
-  single_encryption(form, values, (result) ->
-    console.log(result)
-    form.submit()
-  )

 get_values = (form) ->
   return $(form).serializeArray()
@@ -79,6 +87,15 @@
   for i in [0..values.length-1]
     $("*[name='#{values[i].name}']").val(values[i].value)

+group_encryption = (form, values, callback) ->
+  callback()
+#  buffer = collect_sources(elements)
+
+#  write_target(buffer)
+#elements_with_name_in = (source, reference) ->
+#  return $.grep(source, (element) ->
+#    contains(element.name, reference)
+#  )
 # Future code for group encrypt function
 #find_label = (element) ->
 #  id = element.id
@@ -108,13 +125,6 @@
 #  else
 #    console.error("No target defined")

-#group_encryption = (elements) ->
-#  buffer = collect_sources(elements)
-#  write_target(buffer)
-#elements_with_name_in = (source, reference) ->
-#  return $.grep(source, (element) ->
-#    contains(element.name, reference)
-#  )


Bump up to page 6

Bump up to page 6

--- scripts/pivotal-story-tagger.coffee
+++ scripts/pivotal-story-tagger.coffee
@@ -72,7 +72,7 @@
 module.exports = (robot) ->
   robot.on 'story_create_activity', (project) ->
     PROJECT_ID = project.projectId
-    requests = [0..2].map (n) -> getPage(n, PROJECT_ID)
+    requests = [0..6].map (n) -> getPage(n, PROJECT_ID)
     robot.logger.info "Requesting pivotal stories..."
     Promise.all(requests)
     .then(parseJson)


status/table: Add Edit Grid menu item.

status/table: Add Edit Grid menu item.

--- Oghma/status/table.coffee
+++ Oghma/status/table.coffee
@@ -47,6 +47,8 @@
     visible_to.length == 0 ||
     visible_to.indexOf( O.me().gets( 'name' ) ) != -1

+  O.ui_colors.on_set( -> O.action.update_grid_controls( O ) )
+
   Ext.create( 'Oghma.Ext.Menu',
     items: [ 'Placeholder' ]
     listeners:
@@ -83,6 +85,18 @@
                 delete_table( current_table )
           )
           @add('-')
+          @add(
+            text: 'Edit Grid'
+            checked: O.action.grid_controls_visible()
+            checkHandler: ( item, checked ) =>
+              if checked
+                O.action.show_grid_controls( O )
+                O.action.return_to_origin()
+              else
+                O.action.hide_grid_controls()
+              @hide()
+          )
+          @add('-')

         for table in O.allverse.table.each()
           if is_visible( table )


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/tween.coffee
+++ src/tween.coffee
@@ -14,7 +14,7 @@
   # Add events and methods here
   self.bind "update", ->
     for property, data of I.activeTweens
-      f = Easing[data.easing]
+      f = Easing[data.easing]()
       I[property] =

   tween: (duration, properties) ->


make circle work with new legend scheme

make circle work with new legend scheme

--- bokehjs/src/coffee/renderer/glyph/circle.coffee
+++ bokehjs/src/coffee/renderer/glyph/circle.coffee
@@ -66,14 +66,14 @@

       @mask = (x[4].i for x in @index.search([x0, y0, x1, y1]))

-    _render: (ctx, indices, glyph_props) ->
+    _render: (ctx, indices, glyph_props, sx=@sx, sy=@sy, radius=@radius) ->
       for i in indices

-        if isNaN(@sx[i] + @sy[i] + @radius[i])
+        if isNaN(sx[i] + sy[i] + radius[i])
             continue

         ctx.beginPath()
-        ctx.arc(@sx[i], @sy[i], @radius[i], 0, 2*Math.PI, false)
+        ctx.arc(sx[i], sy[i], radius[i], 0, 2*Math.PI, false)

         if glyph_props.fill_properties.do_fill
           glyph_props.fill_properties.set_vectorize(ctx,i)
@@ -124,34 +124,22 @@

       return (x[4].i for x in @index.search([x0, y0, x1, y1]))

-    draw_legend: (ctx, x1, x2, y1, y2) ->
-      glyph_props = @glyph_props
-      line_props = glyph_props.line_properties
-      fill_props = glyph_props.fill_properties
-      ctx.save()
-      reference_point = @get_reference_point()
-      if reference_point?
-        glyph_settings = reference_point
-        data_r = @distance([reference_point], 'x', 'radius', 'edge')[0]
-      else
-        glyph_settings = glyph_props
-        data_r = glyph_props.select('radius', glyph_props).default
-      border = line_props.select(line_props.line_width_name, glyph_settings)
-      ctx.beginPath()
-      d = _.min([Math.abs(x2-x1), Math.abs(y2-y1)])
-      d = d - 2 * border
-      r = d / 2
-      if data_r?
-        r = if data_r > r then r else data_r
-      ctx.arc((x1 + x2) / 2.0, (y1 + y2) / 2.0, r, 2*Math.PI,false)
-      if fill_props.do_fill
-        fill_props.set(ctx, glyph_settings)
-        ctx.fill()
-      if line_props.do_stroke
-        line_props.set(ctx, glyph_settings)
-        ctx.stroke()
+    draw_legend: (ctx, x0, x1, y0, y1) ->
+      reference_point = @get_reference_point() ? 0

-      ctx.restore()
+      # using objects like this seems a little wonky, since the keys are coerced to
+      # stings, but it works
+      indices = [reference_point]
+      sx = { }
+      sx[reference_point] = (x0+x1)/2
+      sy = { }
+      sy[reference_point] = (y0+y1)/2
+      radius = { }
+      radius[reference_point] = Math.min(Math.abs(x1-x0), Math.abs(y1-y0))*0.4
+
+      console.log indices, sx, sy, radius
+
+      @_render(ctx, indices, @glyph_props, sx, sy, radius)

   class Circle extends Glyph.Model
     default_view: CircleView


fixes #29

fixes #29

--- src/utils.coffee
+++ src/utils.coffee
@@ -95,14 +95,12 @@

   removeFormatFromSubtree: (renderer, subtree, format) ->
     childNodes = Scribe.DOM.filterUneditable(subtree.childNodes)
-    value = renderer.formats[format].matchContainer(subtree)
-    ret = subtree
-    if format == value
-      ret = Scribe.DOM.unwrap(subtree)
+    if renderer.formats[format].matchContainer(subtree)
+      subtree = Scribe.DOM.unwrap(subtree)
     _.each(childNodes, (child) ->
       Scribe.Utils.removeFormatFromSubtree(renderer, child, format)
     )
-    return ret
+    return subtree

   removeExternal: (root) ->
     extNodes = _.clone(root.querySelectorAll(".#{Scribe.DOM.EXTERNAL_CLASS}"))


Fix #5: Glossa stops working when right bracket is in the search string

--- app/assets/javascripts/rglossa/components/centre/search_inputs/cwb_multiword_input.jsx.coffee
+++ app/assets/javascripts/rglossa/components/centre/search_inputs/cwb_multiword_input.jsx.coffee
@@ -65,6 +65,24 @@
       @props.handleSearch()

+  # an interval, e.g. []{1,2}
+  intervalRegexp: /\[\]\{(.+?)\}/
+
+  # an attribute/value expression such as [lemma="car" %c] or [(lemma="car" & pos="n")]
+  attributeValueRegexp: ///\[\(?
+                             ([^"]+?                      # treat quoted strings separately;
+                               (?: "[^"]*" [^\]"]*? )*? ) # they may contain right brackets
+                             (?:\s+%c)?
+                             \)?\]///
+
+  # a quoted string or a single unspecified token
+  quotedStringOrEmptyTermRegexp: /".*?"|\[\]/
+
+  splitQuery: (query) ->
+    query.match(///#{@intervalRegexp.source}
+                  |#{@quotedStringOrEmptyTermRegexp.source}
+                  |#{@attributeValueRegexp.source}///g) or ['[]']
+
   constructQueryTerms: (queryObj) ->
     queryParts = @splitQuery(queryObj.query)

@@ -74,23 +92,15 @@

     queryParts.forEach (item) =>

-      # a single unspecified token
-      if m is '[]'
-        min = 1
-        max = 1
-
-      # an interval, e.g. []{1,2}
-      else if m = item.match(/\[\]\{(.+?)\}/)
+      if m = item.match(@intervalRegexp)
         [min, max] = @handleIntervalSpecification(m)

-      # an attribute/value expression such as [lemma="car" %c] or [(lemma="car" & pos="n")]
-      else if m = item.match(/\[\(?(.+?)(?:\s+%c)?\)?\]/)
+      else if m = item.match(@attributeValueRegexp)
         dq.push @handleAttributes(m, min, max)
         min = null
         max = null

-      # just a quoted string
-      else
+      else if m = item.match(@quotedStringOrEmptyTermRegexp)
         word    = item.substring(1, item.length-1)
         isStart = /\.\+$/.test(word)
         isEnd   = /^\.\+/.test(word)
@@ -106,10 +116,6 @@
         max = null

     dq
-
-
-  splitQuery: (query) ->
-    query.match(/\[\]\{(.+?)\}|".*?"|\[[^\]]*?\]/g) or ['']

   handleIntervalSpecification: (m) ->


don’t redirect the shine output each time the punchdrunk constructor is called

--- coffeescript/punchdrunk.coffee
+++ coffeescript/punchdrunk.coffee
@@ -3,10 +3,6 @@
     game_root = config["game_root"] or "lua"
     game_code = config["game_code"]
     element = config["canvas"] or null
-
-    # Forward print() messages to the console
-    shine.stdout.write = () ->
-      console.log.apply(console, arguments)

     conf = {
       window: {},
@@ -43,3 +39,7 @@
         # this is boot.lua.json
         # it's convenient to have it embeded here because then we don't need to know its path
         vm.load({"sourceName":"@js/boot.lua","lineDefined":0,"lastLineDefined":0,"upvalueCount":0,"paramCount":0,"is_vararg":2,"maxStackSize":2,"instructions":[5,0,0,0,1,1,1,0,28,0,2,1,5,0,2,0,6,0,0,259,28,0,1,1,30,0,1,0],"constants":["require","main","love","run"],"functions":[],"linePositions":[1,1,1,3,3,3,3],"locals":[],"upvalues":[],"sourcePath":"js/boot.lua"})
+
+  # Forward print() messages to the console
+  shine.stdout.write = () ->
+    console.log.apply(console, arguments)


Center map on autocompletion selection

Center map on autocompletion selection

--- src/views.coffee
+++ src/views.coffee
@@ -65,7 +65,7 @@
                     markers = @draw_units unit_list
                     @remember_markers service_id, markers

-        draw_units: (unit_list) ->
+        draw_units: (unit_list, opts) ->
             # todo: refactor into a model/collection
             # 100 = ei tiedossa, 101 = kunnallinen, 102 = kunnan tukema, 103 = kuntayhtymä,
             # 104 = valtio, 105 = yksityinen
@@ -106,6 +106,14 @@
                     marker = event.target
                     @service_sidebar.show_details marker.unit

+
+            bounds = L.latLngBounds (m.getLatLng() for m in markers)
+            bounds = bounds.pad 0.05
+            # FIXME: map.fitBounds() maybe?
+            if opts.zoom and unit_list.length == 1
+                coords = unit_list.first().get('location').coordinates
+                @map.setView [coords[1], coords[0]], 12
+
             return markers

@@ -146,17 +154,23 @@
         autosuggest_show_details: (ev, data, _) ->
             @prevent_switch = true
             if data.object_type == 'unit'
-                @show_details new models.Unit(data)
+                @show_details new models.Unit(data),
+                    zoom: true
+                    draw_marker: true
             else if data.object_type == 'service'
                 @switch_content 'browse'
                 @service_tree.show_service(new models.Service(data))

-        show_details: (unit) ->
+        show_details: (unit, opts) ->
+            if not opts
+                opts = {}
+
             @$el.find('.container').addClass('details-open')
             @details_view.unit = unit
             @details_view.render()
-            unit_list = new models.UnitList([unit])
-            @parent.draw_units(unit_list)
+            if opts.draw_marker
+                unit_list = new models.UnitList [unit]
+                @parent.draw_units unit_list, opts

         hide_details: ->
             @$el.find('.container').removeClass('details-open')


Use nick instead of nick when broadcasting message to others

--- server/app/actors/user-actor.coffee
+++ server/app/actors/user-actor.coffee
@@ -31,6 +31,6 @@

   handleSendMessage: (ev) ->
     debug("User #{@id} sent message #{ev.data.message}")
-    @manager.globalBus.push { type: "BROADCAST", room: @room, key: "message", data: { message: "User #{@id} sent message #{ev.data.message}" } }
+    @manager.globalBus.push { type: "BROADCAST", room: @room, key: "message", data: { message: "User #{@nick} sent message #{ev.data.message}" } }

 module.exports = UserActor


Changed: `zappa.app` returns the whole context.

--- src/zappa.coffee
+++ src/zappa.coffee
@@ -367,7 +367,7 @@
           style @style if @style
         body @body

-  {id: context.id, app, io}
+  context

 # Takes a function and runs it as a zappa app. Optionally accepts a port number, and/or
 # a hostname (any order). The hostname must be a string, and the port number must be


Simplified render()

Simplified render()

--- src/zappa.coffee
+++ src/zappa.coffee
@@ -327,10 +327,6 @@
                 render.apply @, [k, v]

         render = (name,opts,next) ->
-          # Default callback to send
-          next ?= (err,str) ->
-            if err then return req.next err
-            res.send.call res, str

           # Make sure the second arg is an object.
           if typeof opts is 'function'
@@ -340,38 +336,36 @@
           if app.settings['databag']
             opts.params = data

-          apply_layout = (str,fn) ->
-            # Don't change layout: false
-            if opts.layout is false
-              fn null, str
-            else
-              # Use the default layout if one isn't given, or layout: true
-              if opts.layout is true or not opts.layout?
-                opts.layout = 'layout'
-              opts.body = str
-              res.render.call res, opts.layout, opts, fn
-
-          postrender = (str,fn) ->
-            if opts.postrender?
+          if not opts.postrender?
+            postrender = next
+          else
+            postrender = (err, str) ->
+              if err then return next err
               # Apply postrender before sending response.
               jsdom.env html: str, src: [jquery], done: (err, window) ->
-                if err then return fn err
+                if err then return next err
                 ctx.window = window
                 rendered = postrenders[opts.postrender].apply(ctx, [window.$, ctx])

                 doctype = (window.document.doctype or '') + "\n"
-                fn null, doctype + window.document.documentElement.outerHTML
-            else
-              fn null, str
-
-          report = (err) ->
-            return false if not err
-            next err
-            return true
-
-          res.render.call res, name, opts, (err,str) ->
-            report(err) or apply_layout str, (err,str) ->
-              report(err) or postrender str, next
+                html = doctype + window.document.documentElement.outerHTML
+                if next?
+                  next null, html
+                else
+                  res.send.call res, html
+
+          if opts.layout is false
+            layout = postrender
+          else
+            # Use the default layout if one isn't given, or layout: true
+            if opts.layout is true or not opts.layout?
+              opts.layout = 'layout'
+            layout = (err,str) ->
+              if err then return next err
+              opts.body = str
+              res.render.call res, opts.layout, opts, postrender
+
+          res.render.call res, name, opts, layout

         apply_helpers ctx


[client][app] changed filter controller names

--- client/landing/app/Applications/Activity.kdapplication/views/activityinnernavigation.coffee
+++ client/landing/app/Applications/Activity.kdapplication/views/activityinnernavigation.coffee
@@ -3,7 +3,7 @@

   viewAppended:->

-    filterFirstController = @setListController
+    filterController = @setListController
       type: "filterme"
       itemClass: ListGroupShowMeItem
     , @filterMenuData
@@ -11,25 +11,25 @@
     {useNeo4j} = KD.config

     if useNeo4j
-      @addSubView filterFirstController.getView()
-      filterFirstController.selectItem filterFirstController.getItemsOrdered()[0]
+      @addSubView filterController.getView()
+      filterController.selectItem filterController.getItemsOrdered()[0]

     if useNeo4j
       menudata = @followerMenuData
     else
       menudata = @showMenuData

-    filterController = @setListController
+    showMeFilterController = @setListController
       type : "showme"
       itemClass : ListGroupShowMeItem
     , menudata

     KD.getSingleton('mainController').on "AccountChanged", (account)=>
-      filterFirstController.reset()
-      filterFirstController.selectItem filterFirstController.getItemsOrdered()[0]
+      filterController.reset()
+      filterController.selectItem filterController.getItemsOrdered()[0]

-    @addSubView filterController.getView()
-    filterController.selectItem filterController.getItemsOrdered()[0]
+    @addSubView showMeFilterController.getView()
+    showMeFilterController.selectItem showMeFilterController.getItemsOrdered()[0]

     @addSubView helpBox = new HelpBox
       subtitle    : "About Your Activity Feed"


Move journal detail saving to a common function

--- app/assets/javascripts/controllers/journal_thumbnail_controller.js.coffee
+++ app/assets/javascripts/controllers/journal_thumbnail_controller.js.coffee
@@ -15,15 +15,25 @@
       nameErrors: null
       descriptionErrors: null

+  saveJournal: ->
+    @get('model').save()
+                 .then =>
+                   @setProperties(isEditing: false, uploadLogoFunction: null, logoPreview: null)
+                 .catch (response) =>
+                   @set 'nameErrors', response.errors.name?[0]
+                   @set 'descriptionErrors', response.errors.description?[0]
+
   actions:
     editJournalDetails: -> @set 'isEditing', true
     logoUploading: -> @set 'logoUploading', true

     saveJournalDetails: ->
-      #if the logo has changed save it here too.
+      # updateLogo will fire the 'logoUploaded' action from the component, thus saving the model
+      # with the new journal logo url.
       if updateLogo = @get('uploadLogoFunction')
         updateLogo()
-
+      else
+        @saveJournal()

     resetJournalDetails: ->
       @get('model').rollback()
@@ -33,11 +43,7 @@
     logoUploaded: (data) ->
       @set 'model.logoUrl', data.admin_journal.logo_url
       @set 'logoUploading', false
-      @get('model').save()
-                   .then => @set 'isEditing', false
-                   .catch (response) =>
-                     @set 'nameErrors', response.errors.name?[0]
-                     @set 'descriptionErrors', response.errors.description?[0]
+      @saveJournal()

     showPreview: (file) ->
       @set 'logoPreview', file.preview


[fix] Fixed a bug with the Show Milestone View when no progress has been made yet (2m)

--- app/assets/javascripts/houston-scheduler/views/show_milestone_view.coffee
+++ app/assets/javascripts/houston-scheduler/views/show_milestone_view.coffee
@@ -74,6 +74,7 @@
     projections.push regAll.x2 if regAll
     projections.push regLast2.x2 if regLast2
     projections.push regLast3.x2 if regLast3
+    sprints = []
     if projectedEnd = projections.max()
       lastSprint = @getEndOfSprint(projectedEnd)
       sprints = (d.sprint for d in data).sort()


this message is duplicate

this message is duplicate

--- app/assets/javascripts/views/cierres_caja/cierre_caja_form_view.js.coffee
+++ app/assets/javascripts/views/cierres_caja/cierre_caja_form_view.js.coffee
@@ -3,7 +3,6 @@
   includeForm: true

   cierreCajaSaved: (cierreCaja) ->
-    @successCallback "Cierre de caja guardado"
     @get("controller").transitionToRoute "cierres_caja.show", cierreCaja.get("id")

   saveCierreCaja: ->


为Router功能添加routeMode参数(之前提交的有BUG)

为Router功能添加routeMode参数(之前提交的有BUG)

--- src/core/router.coffee
+++ src/core/router.coffee
@@ -64,6 +64,8 @@
 	if path and path.charCodeAt(0) == 35 # `#`
 		routerMode = "hash"
 		path = path.substring(1)
+	else
+		routerMode = cola.setting("routerMode") or "hash"

 	if routerMode is "hash"
 		if path.charCodeAt(0) != 47 # `/`
@@ -254,9 +256,14 @@
 			return false
 		)

-		path = _getHashPath() or trimPath(cola.setting("defaultRouterPath"))
-		router = _findRouter(path)
-		if router then cola.setRoutePath(path, true)
+		path = _getHashPath()
+		if path
+			router = _findRouter(path)
+			if router then _switchRouter(router, path)
+		else
+			path = trimPath(cola.setting("defaultRouterPath"))
+			router = _findRouter(path)
+			if router then cola.setRoutePath(path, true)
 		return
 	, 0)
 	return


fix content status

fix content status

--- BackofficeBundle/Resources/public/coffee/FullPageFormView.coffee
+++ BackofficeBundle/Resources/public/coffee/FullPageFormView.coffee
@@ -3,6 +3,7 @@

   initialize: (options) ->
     @options = options
+    @element = options.element
     @options.currentLanguage = options.multiLanguage.language if options.multiLanguage
     @events = {}
     if options.triggers
@@ -42,4 +43,17 @@
           view = new FullPageFormView(options)
       return

+#  renderWidgetStatus: ->
+#    viewContext = this
+#    $.ajax
+#      type: "GET"
+#      url: @element.get('links')._status_list
+#      success: (response) ->
+#        widgetStatus = viewContext.renderTemplate('widgetStatus',
+#          current_status: viewContext.element.get('status')
+#          statuses: response.statuses
+#          status_change_link: viewContext.element.get('links')._self_status_change
+#        )
+#        addCustomJarvisWidget(widgetStatus)
+#        return
 )


support github show PR no-wip option. elliminate pull request with '[WIP]' in it's title.

--- scripts/github-op.coffee
+++ scripts/github-op.coffee
@@ -12,7 +12,7 @@
 #   hubot github show repos -- デフォルトの対象リポジトリ一覧表示
 #   hubot github add repo <repo> -- デフォルトの対象リポジトリに <repo> を追加
 #   hubot github remove repo <repo> -- デフォルトの対象リポジトリから <repo> を削除
-#   hubot github show {PR,pull req} [<repo>] -- GitHub の open な pull request 一覧
+#   hubot github show {PR,pull req} [no-wip] [<repo>] -- GitHub の open な pull request 一覧
 #   hubot github show issue[s] -- GitHub の open な issue 一覧
 #
 # Notes:
@@ -72,9 +72,10 @@
       robot.brain.set("github_default_target_repos", repos)
       msg.send "対象のリポジトリから #{repo} を削除しました"

-  robot.respond /github\s+show\s+(PRs?|pull\s+req(uests?)?)\s*([a-z0-9._/-]+)?$/i, (msg)->
-    repos = msg.match[3]
-    if repos
+  robot.respond /github\s+show\s+(PRs?|pull\s+req(uests?)?)(\s+no-wip)?\s*([a-z0-9._/-]+)?$/i, (msg)->
+    nowip = msg.match[3]
+    repo = msg.match[4]
+    if repo
       repos = []
       for r in repo.split(",")
         repos.push(r)
@@ -91,6 +92,8 @@
           else
             summary = "#{repo}: pull request 一覧\n"
             for pull in pulls
+              if nowip && pull.title.indexOf("[WIP]") > -1
+                continue
               mentioned = []
               pull.body.replace /\[ \]\s*(@[a-z0-9_-]+)/g, (match, nick) ->
                 mentioned.push(nick)


github-op show only non checked user as mentions, and convert github account to slack user.

--- scripts/github-op.coffee
+++ scripts/github-op.coffee
@@ -69,10 +69,20 @@
         else
           summary = "#{repo}: pull request 一覧\n"
           for pull in pulls
-            mentioned = ""
-            pull.body.replace /@[a-z0-9_-]+/g, (match) ->
-              mentioned += " <#{match}>"
+            mentioned = []
+            pull.body.replace /\[ \]\s*(@[a-z0-9_-]+)/g, (match, nick) ->
+              mentioned.push(nick)
               match
+            mentioned = mentioned.map (nick) ->
+              for _uid, user of robot.brain.users()
+                if "@#{user.githubLogin}" == nick
+                  nick = "@#{user.name}"
+                  break
+              "<#{nick}>"
+            if mentioned.length > 0
+              mentioned = ": " + mentioned.join(", ")
+            else
+              mentioned = ""
             # cannot use label for a link see: https://github.com/slackhq/hubot-slack/issues/114
-            summary += "\n\t#{pull.html_url} #{pull.title} - #{pull.user.login}: #{mentioned}\n"
+            summary += "\n\t#{pull.html_url} #{pull.title} - #{pull.user.login}#{mentioned}\n"
         msg.send summary


Clean up. The grammar would benefit from more stratification to catch common syntax errors

--- grammars/fstar.cson
+++ grammars/fstar.cson
@@ -25,7 +25,7 @@
         name: 'support.class.base.fstar'
       }
       {
-        match: '(/\\\\|\\\\/|<:|<@|[(][|]|[|][)]|u#|~>|->|<--|<-|<==>|==>|[?][.]|[.]\\[|[.]\\(|[{][:]pattern|::|:=|;;|[!][{]|\\[[|]|[|]>|[|]\\]|[~+&?$|#,.:;=\\[\\](){}-])'
+        match: '(/\\\\|\\\\/|<:|<@|[(][|]|[|][)]|u#|~>|->|<--|<-|<==>|==>|[?][.]|[.]\\[|[.]\\(|[{][:]pattern|::|:=|;;|[!][{]|\\[[|]|[|]>|[|]\\]|[~+^&?$|#,.:;=\\[\\](){}-])'
         name: 'support.class.base.fstar'
       }
       {
@@ -37,7 +37,7 @@
         name: 'constant.language.fstar'
       }
       {
-        match: '\\b(let|in|type|kind|val|rec|and|assume|admit|assert|assert_norm|squash|failwith|SMTPat|SMTPatOr|hasEq|fun|function|forall|exists|exception|by|new_effect|reify|try|synth|with|when)\\b'
+        match: '\\b(let|in|type|kind|val|rec|and|if|then|else|assume|admit|assert|assert_norm|squash|failwith|SMTPat|SMTPatOr|hasEq|fun|function|forall|exists|exception|by|new_effect|reify|try|synth|with|when)\\b'
         name: 'keyword.other.fstar'
       }
       {
@@ -146,33 +146,6 @@
         ]
       }
       {
-        begin: '\\b(if)\\b'
-        beginCaptures:
-          1: name: 'keyword.control.if.fstar'
-        end: '\\b(else)\\b'
-        endCaptures:
-          1: name: 'keyword.control.else.fstar'
-        patterns: [
-          { include: '#expr' }
-          {
-            match: '\\b(then)\\b'
-            captures:
-              1: name: 'keyword.control.then.fstar'
-          }
-        ]
-      }
-      {
-        begin: '\\b(if)\\b'
-        beginCaptures:
-          1: name: 'keyword.control.if.fstar'
-        end: '\\b(then)\\b'
-        endCaptures:
-          1: name: 'keyword.control.then.fstar'
-        patterns: [
-          { include: '#expr' }
-        ]
-      }
-      {
         match: '\\b([\\p{Lu}\\p{Lt}][\\p{Lu}\\p{Lt}\\p{Ll}\\p{Lo}\\p{Lm}\\d\'_]*)\\b(?!\\s*[.])'
         captures:
           1: name: 'support.function.constructor.fstar'
@@ -276,53 +249,47 @@
         ]
       }
       {
-        'begin': '`'
-        'beginCaptures':
-          '0':
-            'name': 'punctuation.definition.operator.begin.fstar'
-        'end': '`'
-        'endCaptures':
-          '0':
-            'name': 'punctuation.definition.operator.end.fstar'
-        'name': 'string.quoted.template.fstar'
-        'patterns': [
-          {
-            'include': '#op_names'
-          }
-        ]
-      }
-    ]
-  'string_escapes':
-    'patterns': [
-      {
-        'match': '\\\\u(?![A-Fa-f0-9]{4}|{[A-Fa-f0-9]+})[^\'"]*'
-        'name': 'invalid.illegal.unicode-escape.fstar'
-      }
-      {
-        'match': '\\\\u(?:[A-Fa-f0-9]{4}|({)([A-Fa-f0-9]+)(}))'
-        'name': 'constant.character.escape.js'
-        'captures':
-          '1':
-            'name': 'punctuation.definition.unicode-escape.begin.bracket.curly.fstar'
-          '2':
-            'patterns': [
+        begin: '`'
+        beginCaptures:
+          0: name: 'punctuation.definition.operator.begin.fstar'
+        end: '`'
+        endCaptures:
+          0: name: 'punctuation.definition.operator.end.fstar'
+        name: 'string.quoted.template.fstar'
+        patterns: [
+          { include: '#op_names' }
+        ]
+      }
+    ]
+  string_escapes:
+    patterns: [
+      {
+        match: '\\\\u(?![A-Fa-f0-9]{4}|{[A-Fa-f0-9]+})[^\'"]*'
+        name: 'invalid.illegal.unicode-escape.fstar'
+      }
+      {
+        match: '\\\\u(?:[A-Fa-f0-9]{4}|({)([A-Fa-f0-9]+)(}))'
+        name: 'constant.character.escape.js'
+        captures:
+          1: name: 'punctuation.definition.unicode-escape.begin.bracket.curly.fstar'
+          2:
+            patterns: [
               {
-                'match': '[A-Fa-f\\d]{7,}|(?!10)[A-Fa-f\\d]{6}'
-                'name': 'invalid.illegal.unicode-escape.fstar'
+                match: '[A-Fa-f\\d]{7,}|(?!10)[A-Fa-f\\d]{6}'
+                name: 'invalid.illegal.unicode-escape.fstar'
               }
             ]
-          '3':
-            'name': 'punctuation.definition.unicode-escape.end.bracket.curly.fstar'
-      }
-      {
-        'match': '\\\\(x[0-9A-Fa-f]{2}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.)'
-        'name': 'constant.character.escape.fstar'
-      }
-    ]
-  'op_names':
-    'patterns': [
-      {
-        'match': '\\bop(?:_(?:Multiply|Star|Slash|Percent|Plus|Substraction|Equals|Less|Greater|Bang|Dollar|Amp|Dot|Hat|Colon|Pipe|Question))+\\b'
-        'name': 'entity.name.tag.fstar'
-      }
-    ]
+          3: name: 'punctuation.definition.unicode-escape.end.bracket.curly.fstar'
+      }
+      {
+        match: '\\\\(x[0-9A-Fa-f]{2}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.)'
+        name: 'constant.character.escape.fstar'
+      }
+    ]
+  op_names:
+    patterns: [
+      {
+        match: '\\bop(?:_(?:Multiply|Star|Slash|Percent|Plus|Substraction|Equals|Less|Greater|Bang|Dollar|Amp|Dot|Hat|Colon|Pipe|Question))+\\b'
+        name: 'entity.name.tag.fstar'
+      }
+    ]


Luacode env support

Luacode env support

--- grammars/latex.cson
+++ grammars/latex.cson
@@ -162,7 +162,7 @@
       { include: '#envGnuplot' }
       # { include: '#metaEnvMinted' }
       # { include: '#metaEnvListing' }
-      # { include: '#envLuaCode' }
+      { include: '#envLuaCode' }
       { include: '#envVerbatim' }
       # { include: '#tikzPicture' }
       { include: '#envGeneric' }
@@ -830,7 +830,7 @@
   # Environments
   envGeneric: {
     comment: 'Matches environments not recognised by other rules. Less lenient with random formatting here, it is expected to all be on one line.'
-    contentName: 'meta.environment.general.$5.latex'
+    name: 'meta.environment.general.$5.latex'
     begin: '((\\\\)begin)(\\{)((\\w+)\\*?)(\\})(?:(\\[)(.*?)(\\]))?'
     beginCaptures:
       1:
@@ -1106,6 +1106,36 @@
         name: 'punctuation.definition.arguments.end.latex'
     patterns: [{ include: 'source.gnuplot' }]
   }
+  envLuaCode: {
+    name: 'meta.environment.embedded.$5.latex'
+    contentName: 'source.embedded.lua'
+    begin: '((\\\\)begin)(\\{)((luacode)\\*?)(\\})'
+    beginCaptures:
+      1:
+        name: 'support.function.environment.begin.latex'
+      2:
+        name: 'punctuation.definition.function.begin.latex'
+      3:
+        name: 'punctuation.definition.arguments.begin.latex'
+      4:
+        name: 'variable.parameter.environment.$5.latex'
+      # 5: Used for meta scope name (basically just non starred version)
+      6:
+        name: 'punctuation.definition.arguments.end.latex'
+    end: '((\\\\)end)(\\{)(\\4)(\\})'
+    endCaptures:
+      1:
+        name: 'support.function.environment.end.latex'
+      2:
+        name: 'punctuation.definition.function.latex'
+      3:
+        name: 'punctuation.definition.arguments.begin.latex'
+      4:
+        name: 'variable.parameter.environment.latex'
+      5:
+        name: 'punctuation.definition.arguments.end.latex'
+    patterns: [{ include: 'source.lua' }] # The l-latex package has complicated stuff here, so this might break
+  }

   # Experimental


Fixed syntax highlighting for string literals in .marko

--- grammars/marko.cson
+++ grammars/marko.cson
@@ -358,6 +358,9 @@
         include: "#expression-string-double"
       }
       {
+        include: "#expression-string-template"
+      }
+      {
         include: "#expression-group-parens"
       }
       {
@@ -396,8 +399,36 @@
         match: "\\b(out)[.](global)\\b"
       }
       {
+        captures:
+          "1":
+            name: "variable.language.js"
+          "2":
+            name: "entity.name.type"
+        match: "\\b(this)[.](input|state)\\b"
+      }
+      {
         match: "\\b(input|state|JSON|out)\\b"
         name: "entity.name.type"
+      }
+      {
+        match: "\\b(let|var)\\b"
+        name: "storage.type.var.js"
+      }
+      {
+        captures:
+          "1":
+            name: "entity.name.type"
+          "2":
+            name: "entity.name.type"
+        match: "\\b(require)[.](resolve)\\b"
+      }
+      {
+        match: "\\b(console|JSON|event|window|setTimeout|setInterval|require)\\b"
+        name: "entity.name.type"
+      }
+      {
+        match: "\\b(this|arguments)\\b"
+        name: "variable.language.js"
       }
       {
         include: "#expression-special-class"
@@ -526,6 +557,13 @@
         include: "#placeholder-patterns"
       }
     ]
+  "expression-string-escapes":
+    patterns: [
+      {
+        match: "\\\\(x\\h{2}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.)"
+        name: "constant.character.escape.js"
+      }
+    ]
   "expression-string-single":
     begin: "'"
     beginCaptures:
@@ -538,11 +576,44 @@
     name: "string.quoted.single.js"
     patterns: [
       {
-        match: "\\\\(x\\h{2}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.)"
-        name: "constant.character.escape.js"
+        include: "#expression-string-escapes"
       }
       {
         include: "#placeholder-patterns"
+      }
+    ]
+  "expression-string-template":
+    begin: "`"
+    beginCaptures:
+      "0":
+        name: "punctuation.definition.string.begin.js"
+    end: "`"
+    endCaptures:
+      "0":
+        name: "punctuation.definition.string.end.js"
+    name: "string.quoted.other.template.js"
+    patterns: [
+      {
+        include: "#expression-string-escapes"
+      }
+      {
+        begin: "\\$\\{"
+        beginCaptures:
+          "0":
+            name: "punctuation.section.embedded.begin.js"
+        contentName: "source.js"
+        end: "(\\})"
+        endCaptures:
+          "0":
+            name: "punctuation.section.embedded.end.js"
+          "1":
+            name: "source.js"
+        name: "meta.embedded.line.js"
+        patterns: [
+          {
+            include: "#expression"
+          }
+        ]
       }
     ]
   "expression-ternary-if":
@@ -668,10 +739,6 @@
         name: "keyword.control.js"
       }
       {
-        match: "\\b(let|var)\\b"
-        name: "storage.type.var.js"
-      }
-      {
         match: "(\\s+|\\b)(\\=\\>)(\\s+|\\b)"
         name: "storage.type.function.arrow.js"
       }
@@ -690,22 +757,6 @@
       {
         match: "\\bextends\\b"
         name: "entity.other.inherited-class.js"
-      }
-      {
-        captures:
-          "1":
-            name: "entity.name.type"
-          "2":
-            name: "entity.name.type"
-        match: "\\b(require)[.](resolve)\\b"
-      }
-      {
-        match: "\\b(console|JSON|event|window|setTimeout|setInterval|require)\\b"
-        name: "entity.name.type"
-      }
-      {
-        match: "\\b(this|arguments)\\b"
-        name: "variable.language.js"
       }
       {
         begin: "([\\p{L}\\p{Nl}$_][\\p{L}\\p{Nl}$\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\x{200C}\\x{200D}]*)\\s*(\\()(?=(?:[^\\(\\)]*)?\\)\\s*\\{)"


fix *regexr* functions to color regex before typing the second comma

--- grammars/stata.cson
+++ grammars/stata.cson
@@ -1,8 +1,8 @@
 'scopeName': 'source.stata'
 'name': 'Stata'
 'fileTypes': [
-  'do',
-  'ado',
+  'do'
+  'ado'
   'mata'
 ]
 'foldingStartMarker': '\\{\\s*$'
@@ -1151,7 +1151,7 @@
       }
       {
         'comment': 'color regexr with regular quotes i.e. " '
-        'match': '\\b(regexr)(\\()([^,]+)(,)\\s*(\")([^"]+)(\"(\')?)\\s*(,)([^\\)]*)(\\))'
+        'match': '\\b(regexr)(\\()([^,]+)(,)\\s*(\")([^"]+)(\"(\')?)\\s*([^\\)]*)(\\))'
         'captures':
           '1':
             'name': 'support.function.builtin.stata'
@@ -1197,9 +1197,11 @@
           '8':
             'name': 'invalid.illegal.punctuation.stata'
           '9':
-            'name': 'punctuation.definition.variable.begin.stata' # the comma
-          '10':
-            'patterns': [
+            'patterns': [
+              {
+                'match': ','
+                'name': 'punctuation.definition.variable.begin.stata' # the comma
+              }
               {
                 'include': '#string-compound'
               }
@@ -1223,12 +1225,12 @@
                 'include': '#comments-triple-slash'
               }
             ]
-          '11':
+          '10':
             'name': 'punctuation.definition.parameters.end.stata'
       }
       {
         'comment': 'color regexr with compound quotes i.e. `"text"\' '
-        'match': '\\b(regexr)(\\()([^,]+)(,)\\s*(`\")([^"]+)(\"\')\\s*(,)([^\\)]*)(\\))'
+        'match': '\\b(regexr)(\\()([^,]+)(,)\\s*(`\")([^"]+)(\"\')\\s*([^\\)]*)(\\))'
         'captures':
           '1':
             'name': 'support.function.builtin.stata'
@@ -1272,9 +1274,11 @@
           '7':
             'name': 'punctuation.definition.string.end.stata'
           '8':
-            'name': 'punctuation.definition.variable.begin.stata' # the comma
-          '9':
-            'patterns': [
+            'patterns': [
+              {
+                'match': ','
+                'name': 'punctuation.definition.variable.begin.stata' # the comma
+              }
               {
                 'include': '#string-compound'
               }
@@ -1298,7 +1302,7 @@
                 'include': '#comments-triple-slash'
               }
             ]
-          '10':
+          '9':
             'name': 'punctuation.definition.parameters.end.stata'
       }
     ]
@@ -1514,7 +1518,7 @@
       }
       {
         'comment': 'color regexr with regular quotes i.e. " '
-        'match': '\\b(ustrregexrf|ustrregexra)(\\()([^,]+)(,)\\s*(\")([^"]+)(\"(\')?)\\s*(,)([^\\),]*)([,0-9\\s]*)?\\s*(\\))'
+        'match': '\\b(ustrregexrf|ustrregexra)(\\()([^,]+)(,)\\s*(\")([^"]+)(\"(\')?)\\s*([^\\)]*)(\\))'
         'captures':
           '1':
             'name': 'support.function.builtin.stata'
@@ -1560,9 +1564,11 @@
           '8':
             'name': 'invalid.illegal.punctuation.stata'
           '9':
-            'name': 'punctuation.definition.variable.begin.stata' # the comma
-          '10':
-            'patterns': [
+            'patterns': [
+              {
+                'match': ','
+                'name': 'punctuation.definition.variable.begin.stata' # the comma
+              }
               {
                 'include': '#string-compound'
               }
@@ -1585,23 +1591,16 @@
               {
                 'include': '#comments-triple-slash'
               }
-            ]
-          '11':
-            'patterns': [
               {
                 'include': '#numbers'
               }
-              {
-                'match': ','
-                'name': 'punctuation.definition.variable.begin.stata' # the comma
-              }
-            ]
-          '12':
+            ]
+          '10':
             'name': 'punctuation.definition.parameters.end.stata'
       }
       {
         'comment': 'color regexr with compound quotes i.e. `"text"\' '
-        'match': '\\b(ustrregexrf|ustrregexra)(\\()([^,]+)(,)\\s*(`\")([^"]+)(\"\')\\s*(,)([^\\)]*)(\\))'
+        'match': '\\b(ustrregexrf|ustrregexra)(\\()([^,]+)(,)\\s*(`\")([^"]+)(\"\')\\s*([^\\)]*)(\\))'
         'captures':
           '1':
             'name': 'support.function.builtin.stata'
@@ -1645,9 +1644,11 @@
           '7':
             'name': 'punctuation.definition.string.end.stata'
           '8':
-            'name': 'punctuation.definition.variable.begin.stata' # the comma
-          '9':
-            'patterns': [
+            'patterns': [
+              {
+                'match': ','
+                'name': 'punctuation.definition.variable.begin.stata' # the comma
+              }
               {
                 'include': '#string-compound'
               }
@@ -1670,18 +1671,11 @@
               {
                 'include': '#comments-triple-slash'
               }
-            ]
-          '10':
-            'patterns': [
               {
                 'include': '#numbers'
               }
-              {
-                'match': ','
-                'name': 'punctuation.definition.variable.begin.stata' # the comma
-              }
-            ]
-          '11':
+            ]
+          '9':
             'name': 'punctuation.definition.parameters.end.stata'
       }
     ]


atom: linter-tslint: ignore *.d.ts

atom: linter-tslint: ignore *.d.ts

--- .atom/config.cson
+++ .atom/config.cson
@@ -20,8 +20,7 @@
     urls: false
     useSnippets: false
     useSnippetsAndFunction: true
-  "atom-typescript":
-    locale: "ru"
+  "atom-typescript": {}
   "auto-copyright":
     owner: "Synesis"
   "autoclose-html":
@@ -135,6 +134,7 @@
     onlyRunWhenConfig: true
   "linter-tslint":
     enableSemanticRules: true
+    ignoreTypings: true
   "linter-ui-default":
     hidePanelWhenEmpty: false
     panelHeight: 334


atom: update config for version 0.199.0

atom: update config for version 0.199.0

--- .atom/config.cson
+++ .atom/config.cson
@@ -2,7 +2,8 @@
   editor:
     fontSize: 12
     tabLength: 4
-    invisibles: {}
+    invisibles:
+      {}
     showInvisibles: true
     preferredLineLength: 100
   core:
@@ -24,4 +25,9 @@
     enabled: true
   "atom-beautify":
     _analyticsUserId: "f69d1bc0-5295-4402-bdb7-d293c000e4f0"
-  linter: {}
+  linter:
+    {}
+  "one-dark-ui":
+    layoutMode: "Compact"
+  "tree-view":
+    hideVcsIgnoredFiles: true


Use unity-ui instead of native-ui.

Use unity-ui instead of native-ui.

--- .atom/config.cson
+++ .atom/config.cson
@@ -45,7 +45,7 @@
     restorePreviousWindowsOnStart: "no"
     telemetryConsent: "no"
     themes: [
-      "native-ui"
+      "unity-ui"
       "solarized-light-syntax"
     ]
     titleBar: "custom"
@@ -113,7 +113,7 @@
     pep8ExecutablePath: "/usr/local/bin/pep8"
   "linter-ui-default":
     hidePanelWhenEmpty: true
-    panelHeight: 122
+    panelHeight: 112
     showPanel: true
     showProviderName: true
   "linter-write-good":
@@ -148,7 +148,6 @@
     locked: true
   "no-title-bar":
     automaticTrafficLightsPadding: true
-    automaticTrafficLightsPaddingOffset: 5
   nvatom:
     directory: "~/Dropbox/nv"
   "pretty-json": {}


added logic into translator

added logic into translator

--- server/Translator.coffee
+++ server/Translator.coffee
@@ -3,12 +3,12 @@
     # define the shared client-server schema
     _.extend( @,
       _id: ''
+      author_id: ''
       parent_id: undefined
       content: ''
-      instance_id: ''
     )

-    for key in [ '_id', 'parent_id', 'content', 'instance_id' ]
+    for key in [ '_id', 'author_id', 'parent_id', 'content' ]
       @[key] = either[key] if either[key]?

 class ClientPost extends SharedPost
@@ -33,10 +33,6 @@
     for post in Posts.find( 'parent_id': server.parent_id ).fetch()
       for key of post.tags
         @suggested_tags.push( key ) unless key in @my_tags
-
-    author = Meteor.users.findOne( '_id': server.author_id )
-    if author?
-      @author = _.pick( author, '_id', 'emails', 'name' )

 class ServerPost extends SharedPost
   constructor: ( client ) ->
@@ -60,11 +56,92 @@
       @author_id = user_id
     else
       _.extend( @, post )
-
+
       # check if user added a new tag
-      for tag of client.my_tags
+      for tag, weight of client.my_tags
         @tags[tag] ?= { weight: 0 }
         @tags[tag].users ?= []
         unless user_id in @tags[tag].users
           # apply the tag
-          @tags[tag].users.push( user_id )
+          @add_tag( tag, user_id )
+
+  add_tag: ( tag, user_id ) =>
+    # check if already graduated
+    already_graduated = @is_graduated( tag )
+    # add the tag
+    if not @tags[tag]?
+      @tags[tag] = {
+        users: []
+        weight: 0
+      }
+    @tags[tag].users.push( user_id )
+
+    # add user's post experience to the tag
+    @tags[tag].weight += @get_user_post_experience( user_id )
+    # check if graduated
+    graduated = @is_graduated( tag )
+
+    if not already_graduated and graduated
+      @award_points( @tags[tag].users, tag )
+      user = Users.findOne('_id': user_id)
+      tron.test( 'check_award_points', tag, user )
+    tron.test( 'check_add_tag', @, tag, user_id)
+
+
+  is_graduated: ( tag ) =>
+    # TODO: Improve this function
+    grad = false
+    if @tags[tag]?
+      grad = @tags[tag].users.length > 1
+    else grad = false
+    return grad
+
+
+  get_user_post_experience: ( user_id ) =>
+    weights = {}
+    weight_total = 0
+    # loop through tags in post (aka this)
+    for tag, obj of @tags
+      weights[tag] = obj.weight
+      weight_total += obj.weight
+    unless weight_total is 0
+      users_post_experience = 0
+      for tag, weight of weights
+        # normalize tag weights
+        weights[tag] /= weight_total
+        user = Users.findOne('_id': user_id)
+        # multiply normalize tag weight by user's tag experience
+        # sum all those up
+        if user.experience[tag]?
+          user_exp = user.experience[tag]
+        else
+          user_exp = 0  #defaults to 0
+        users_post_experience += weights[tag] * user_exp
+      # return the sum
+      return users_post_experience
+    else return 1 # TODO: Default to some function of experience
+
+  award_points: ( users, tag ) =>
+    reward = @tags[tag].weight / users.length
+    tron.test( 'check_number', reward )
+
+    for user_id in users
+      user_doc = Users.findOne( user_id )
+      # add reward to user for tag
+      q = {
+        '$set':
+          {'experience': {}}
+      }
+      exp_obj = {}
+      #copy existing tag exp
+      for u_tag, exp of user_doc.experience
+        #exp_obj['$set'][u_tag] = exp
+        exp_obj[u_tag] = exp
+      #the new tag exp, increment/insert
+      past_exp = 0
+      if exp_obj[tag]?
+        past_exp = exp_obj[tag]
+      exp_obj[tag] = reward + past_exp
+      q['$set']['experience'] = exp_obj
+      Users.update( {'_id': user_doc._id}, q )
+      tron.test( 'check_if_user_exp', user_id, tag, past_exp )


Change username to login on login page

Change username to login on login page

--- client/components/Login.coffee
+++ client/components/Login.coffee
@@ -72,7 +72,7 @@
                     <div>
                         <FieldGroup
                             id="username"
-                            label="Имя пользователя"
+                            label="Логин"
                             type="text"
                             setField={@setField}
                             state={@state}


Capitalization matters

Capitalization matters

--- _source/server/lib/_entityHelpers.coffee
+++ _source/server/lib/_entityHelpers.coffee
@@ -5,6 +5,8 @@

     if not platform
       Apollos.debug "must specify platform to translate to"
+
+    platform = platform.toUpperCase()

     if not Apollos[singular]._dictionary[platform]
       Apollos.debug "no translation found for #{platform} in #{singular}"


Build: minor refactor

Build: minor refactor

--- client/landing/gulpfile.coffee
+++ client/landing/gulpfile.coffee
@@ -1,9 +1,10 @@
 gulp       = require 'gulp'
 gutil      = require 'gulp-util'
+gulpif     = require 'gulp-if'
 coffee     = require 'gulp-coffee'
 rename     = require 'gulp-rename'
-buffer     = require 'gulp-buffer'
-stream     = require 'gulp-stream'
+gulpBuffer = require 'gulp-buffer'
+gulpStream = require 'gulp-stream'
 stylus     = require 'gulp-stylus'
 rimraf     = require 'gulp-rimraf'
 concat     = require 'gulp-concat'
@@ -24,18 +25,19 @@
 SERVER_FILE = './server/server.coffee'
 SERVER_PATH = ['./server/**/*.coffee']
 BUILD_PATH  = argv.outputDir ? './static/a/out'
+devMode     = argv.devMode?

 log = (color, message) -> gutil.log gutil.colors[color] message

 watchLogger = (color, watcher) ->
-  server = livereload()
+  server = livereload()  if devMode
   watcher.on 'change', (event) ->
     log color, "file #{event.path} was #{event.type}"
     server?.changed event.path

 gulpBrowserify = (options = {}) ->
   options.extensions or= ['.coffee']
-  options.debug       ?= yes
+  options.debug        = devMode
   b = browserify options
   b.transform coffeeify
   b.bundle()
@@ -49,10 +51,10 @@
   gulp.src STYLES_PATH
     .pipe stylus
       compress  : yes
-      # sourcemap : inline  : yes
+      sourcemap : inline  : yes  if devMode
     .pipe concat 'main.css'
     .pipe rename 'main.css'
-    # .pipe livereload()
+    .pipe gulpif devMode, livereload()
     .pipe gulp.dest "#{BUILD_PATH}/css"

@@ -83,7 +85,7 @@

 gulp.task 'sprites@1x', ->

-  spriteStream = gulp.src 'static/sprites@1x/**/*.png'
+  stream = gulp.src 'static/sprites@1x/**/*.png'
     .pipe spritesmith
       imgName   : 'sprite@1x.png'
       cssName   : 'sprite@1x.styl'
@@ -91,20 +93,20 @@
       algorithm : 'binary-tree'
       padding   : 5
       cssFormat : 'stylus'
-      cssVarMap : nameStylusVars.bind spriteStream, ''
-
-  spriteStream.css
+      cssVarMap : nameStylusVars.bind stream, ''
+
+  stream.css
     .pipe gulp.dest './app/styl/'

-  spriteStream.img
+  stream.img
     .pipe gulp.dest "#{BUILD_PATH}/images/"

-  return spriteStream
+  return stream

 gulp.task 'sprites@2x', ['sprites@1x'], ->

-  spriteStream = gulp.src 'static/sprites@2x/**/*.png'
+  stream = gulp.src 'static/sprites@2x/**/*.png'
     .pipe spritesmith
       imgName   : 'sprite@2x.png'
       cssName   : 'sprite@2x.styl'
@@ -112,15 +114,15 @@
       algorithm : 'binary-tree'
       padding   : 10
       cssFormat : 'stylus'
-      cssVarMap : nameStylusVars.bind spriteStream, '__2x'
-
-  spriteStream.css
+      cssVarMap : nameStylusVars.bind stream, '__2x'
+
+  stream.css
     .pipe gulp.dest './app/styl/'

-  spriteStream.img
+  stream.img
     .pipe gulp.dest "#{BUILD_PATH}/images/"

-  return spriteStream
+  return stream

 gulp.task 'watch-styles', -> watchLogger 'cyan', gulp.watch STYLES_PATH, ['styles-only']
@@ -130,11 +132,12 @@
   gulpBrowserify
       entries : ['./app/coffee/main.coffee']
     .pipe source 'main.js'
-    .pipe buffer()
+    .pipe gulpBuffer()
     .pipe pistachio()
-    .pipe stream()
-    # .pipe livereload()
+    .pipe gulpStream()
+    .pipe gulpif devMode, livereload()
     .pipe gulp.dest "#{BUILD_PATH}/js"
+

 gulp.task 'watch-coffee', -> watchLogger 'cyan', gulp.watch COFFEE_PATH, ['coffee']


Remove unused file

Remove unused file

--- client/landing/includes.coffee
+++ client/landing/includes.coffee
@@ -747,7 +747,6 @@
   "stylus/app.group.creation.styl",
   "stylus/app.user.styl",
   "stylus/app.markdown.styl",
-  "stylus/app.vm.styl",
   "stylus/temp.styl",
   # "stylus/app.landing.styl",
   # "stylus/app.predefined.styl",


bugfix

bugfix

--- loader.coffee
+++ loader.coffee
@@ -1,4 +1,4 @@
 if Meteor.isServer
   imgix = Npm.require('imgix-core-js')
 else
-  imgix = imgix+  window.imgix = imgix


add help command

add help command

--- scripts/bungie.coffee
+++ scripts/bungie.coffee
@@ -68,6 +68,24 @@

             robot.emit 'slack-attachment', payload

+  robot.respond /help/i, (res) ->
+    admin = process.env.ADMIN_USERNAME
+    if admin
+      admin_message = "\nFeel free to message me (@#{admin}) with any other questions about the bot."
+    else
+      admin_message = ""
+
+    payload =
+      attachments: [{
+  			"title": "Using the Gunsmith Bot",
+  			"title_link": "https://github.com/phillipspc/showoff/blob/master/README.md",
+        "text": "In #gunsmith, you can show off your weapons by messaging the bot with your gamertag, network, and weapon slot, separated by spaces. The standard usage looks like this: \n```@gunsmithbot: MyGamerTag xbox primary```\nIf you've set up your slack profile so that your *first name* matches your gamertag, you can omit this:```@gunsmithbot: playstation special```\n If your gamertag only exists on one network, that can be omitted as well:```@gunsmithbot: heavy```\n The Gunsmith Bot will always look at the *most recently played character* on your account.\n *Special note to Xbox Users:*\n If your gamertag has any spaces in it, these will need to be substituted with underscores (\"_\") in order for the bot to recognize the input properly. This is only required when inputting the gamertag manually however; spaces are fine in your slack first name.#{admin_message}\n\n _Keep that thing oiled, guardian._",
+  			"mrkdwn_in": ["text"]
+      }]
+      channel: res.message.user.name
+
+    robot.emit 'slack-attachment', payload
+

 checkNetwork = (network) ->
@@ -102,7 +120,7 @@
     networkName = if membershipType is '1' then 'xbox' else 'playstation'
     # replaces underscores with spaces (for xbox)
     displayName = displayName.split('_').join(' ') if networkName is 'xbox'
-
+
     return getPlayerId(res, membershipType, displayName, robot)
     .then (results) ->
       if !results


Move jquery hack for aloha to aloha init

--- scripts/config.coffee
+++ scripts/config.coffee
@@ -57,12 +57,6 @@
   shim:

     # ## Core Libraries
-    jquery:
-      exports: 'jQuery'
-      init: ->
-        this.jQuery.browser.version = 10000; # Hack to fix aloha-editor's version checking
-        return this.jQuery
-
     underscore:
       exports: '_'

@@ -105,6 +99,8 @@

         document.getElementsByTagName("head")[0].appendChild(script);

+        jQuery.browser.version = 10000 # Hack to fix aloha-editor's version checking
+
         return Aloha

   # Handlebars Requirejs Plugin Configuration


debug

debug

Signed-off-by: Adam Stokes <0a364f4bf549cc82d725fa7fd7ed34404be64079@ubuntu.com>

--- scripts/debpkg.coffee
+++ scripts/debpkg.coffee
@@ -25,6 +25,7 @@

 module.exports = (robot) ->
   robot.hear /debpkg\s(\w+)/i, (msg) ->
+    console.log msg.match
     pkg = msg.match[2]
     queryVersion robot, msg, urlMap.debian, pkg
     return


fixing typo

fixing typo

--- keymaps/python-debugger.cson
+++ keymaps/python-debugger.cson
@@ -7,7 +7,7 @@

 # For more detailed documentation see
 # https://atom.io/docs/latest/advanced/keymaps
-'.atom-workspace':
+'atom-workspace':
   'ctrl-alt-o': 'python-debugger:toggle'
   'f7': 'python-debugger:insert'
   'shift-f7': 'python-debugger:remove'


Revert "Update atom.config.get calls to avoid deprecation warnings"

This reverts commit 61d205c8a829da53610a90ac20b4a7a3629e9324.

--- lib/autocomplete-view.coffee
+++ lib/autocomplete-view.coffee
@@ -56,8 +56,8 @@
     false

   getCompletionsForCursorScope: ->
-    scope = @editor.scopeDescriptorForBufferPosition(@editor.getCursorBufferPosition())
-    completions = atom.config.get('editor.completions', {scope})
+    cursorScope = @editor.scopeDescriptorForBufferPosition(@editor.getCursorBufferPosition())
+    completions = atom.config.get(cursorScope, 'editor.completions')
     _.uniq(_.flatten(completions))

   buildWordList: ->


list pull requests

list pull requests

--- scripts/github.coffee
+++ scripts/github.coffee
@@ -10,7 +10,8 @@
 #   HUBOT_GITHUB_REPOS_PATH
 #
 # Commands:
-#   hubot github show:issue <options> -- List issues for a repository
+#   hubot github show:issues <options> -- List issues for a repository
+#   hubot github show:pulls <options> -- List pull requests for a repository
 #   hubot github create:issue <options> -- Create a issue for a repository
 #
 # Notes:
@@ -69,9 +70,9 @@
     "\n- Created by #{name} via Hubot"

   ## List issues for a repository
-  robot.respond /github\s+show:issues?(\s+)?(.+)?/i, (msg) ->
+  robot.respond /github\s+show:issues?(\s+.+)?/i, (msg) ->

-    option    = new CommandOption(msg.match[2])
+    option    = new CommandOption(msg.match[1])
     repo_path = option.get('repo') ? repo_path

     unless repo_path?
@@ -95,6 +96,33 @@

       msg.send summary

+  ## List issues for a repository
+  robot.respond /github\s+show:pulls?(\s+.+)?/i, (msg) ->
+
+    option    = new CommandOption(msg.match[1])
+    repo_path = option.get('repo') ? repo_path
+
+    unless repo_path?
+      msg.send "No repository specified, please provide one or set HUBOT_GITHUB_REPOS_PATH accordingly."
+      return
+
+    url   = "#{url_api_base}/repos/#{repo_path}/pulls"
+    query = option.query('state', 'head', 'base', 'sort', 'direction')
+
+    github.get "#{url}?#{query}", (issues) ->
+      if issues.length == 0
+        summary = "Achievement unlocked: open pull requests zero!"
+      else
+        if issues.length == 1
+          summary = "There's only one open pull request for #{repo_path}:"
+        else
+          summary = "I found #{issues.length} open pull requests for #{repo_path}:"
+
+        for issue in issues
+          summary = summary + "\n\t#{issue.title} (#{if issue.assignee then issue.assignee.login else 'no assignee'}) -> #{issue.html_url}"
+
+      msg.send summary
+
   ## Create an issue
   robot.respond /github\s+create:issue\s+(.+)/i, (msg) ->

@@ -112,7 +140,6 @@
     data['body']  = signature(msg.message.user.name)

     github.post url, data, (issue) ->
-      console.log(issue)
       msg.send "#{issue.title} (#{if issue.assignee then issue.assignee.login else 'no assignee'}) -> #{issue.html_url}"