:memo: Add example for drawing API

:memo: Add example for drawing API

--- src/geom/mixins/geometry.coffee
+++ src/geom/mixins/geometry.coffee
@@ -131,6 +131,8 @@

   # Paints the geometry shape in the specified canvas `context` as a line.
   #
+  # <script>drawGeometry(exampleKey, {stroke: 'highlight'})</script>
+  #
   # context - The canvas context into which draw the geometry.
   # color - The {String} color of the stroke.
   stroke: (context, color=agt.COLORS.STROKE) ->
@@ -142,6 +144,8 @@

   # Paints the geometry shape in the specified canvas `context`
   # as a plain shape.
+  #
+  # <script>drawGeometry(exampleKey, {fill: 'highlight'})</script>
   #
   # context - The canvas context into which draw the geometry.
   # color - The {String} color of the fill.


changes after code cleanup in settings.json

--- server/entry.coffee
+++ server/entry.coffee
@@ -12,7 +12,8 @@
     'Welcome to Riffyn, ' + user.profile.name

   ##On Meteor startup save okta service config including client id and secret
-  oktaService = Meteor.settings.private.oAuth.okta
+  oktaService = Meteor.settings.public.okta
+
   if oktaService
     ServiceConfiguration.configurations.upsert( { service: "okta" }, {
       $set: oktaService})
@@ -99,4 +100,4 @@
         from: "no-reply@riffyn.com",
         subject: "Riffyn forgot password",
         html: emailBody
-      })+      })


[master] change time

[master] change time

--- server/index.coffee
+++ server/index.coffee
@@ -61,7 +61,7 @@

 summaryHandler = (req, res, next) ->
   globalStart = 1433590000
-  globalEnd = 1433600000
+  globalEnd = 1433900000
   id = req.params.id
   interval = 5
   consecutive = 3


use jsonp for resolve call, fixes adding sounds in safari

--- app/views/sound_new_view.coffee
+++ app/views/sound_new_view.coffee
@@ -23,7 +23,7 @@
     e.preventDefault()
     url = @$('input[name="url"]').val()
     if url.match(/soundcloud.com/)?
-      req = $.getJSON "#{@apiUrls.resolve}?url=#{url}&client_id=#{app.apiKey}"
+      req = $.getJSON "#{@apiUrls.resolve}?url=#{url}&client_id=#{app.apiKey}&callback=?"
       req.success @create

   # TODO: refactor, views should bind to add event


Support rendering children of test component

--- test/helpers/index.coffee
+++ test/helpers/index.coffee
@@ -11,7 +11,8 @@

   render: ->
     React.createElement(@props._wrapped_component,
-      _.extend(_.omit(@props, '_wrapped_component'), ref: 'element')
+      _.extend(_.omit(@props, '_wrapped_component', 'children'), ref: 'element')
+      @props.children
     )

 Testing = {


use the correct value

use the correct value

--- src/tf.coffee
+++ src/tf.coffee
@@ -165,7 +165,7 @@
     environment = ekvs.join " "

     cmdline = "cd #{basepath}/#{projname}; #{environment} terraform #{action} -input=false -no-color"
-    cmdline = "#{cmdline} -force" if action == 'delete'
+    cmdline = "#{cmdline} -force" if action == 'destroy'
     msg.send {room: msg.message.user.name}, "```\n#{cmdline}\n```"
     exec cmdline, (error, stdout, stderr) ->
       if stderr


only render known symbols

only render known symbols

--- src/ui.coffee
+++ src/ui.coffee
@@ -182,7 +182,8 @@
       .enter().append('g')
         .attr('class', 'symbol poiSymbol')
         .each (poi) ->
-          app.symbol[poi.properties.type](d3.select(@))
+          if (drawSymbol = app.symbol[poi.properties.type])?
+            drawSymbol(d3.select(@))

     updateSymbols()


expose global map object

expose global map object

--- src/ui.coffee
+++ src/ui.coffee
@@ -346,8 +346,15 @@
     location = null
     showLocation()

+  return {
+    db: db
+    projection: projection
+    path: path
+    zoom: zoom
+  }
+

 app.load = (url) ->
   d3.json url, (error, db) ->
     if error then return console.error(error)
-    initialize(db)
+    window.map = initialize(db)


Detect global 'use strict' directives

Detect global 'use strict' directives

--- lib/assets/JavaScript.coffee
+++ lib/assets/JavaScript.coffee
@@ -186,6 +186,12 @@
               seenComments.push comment
           ), this
       ), this
+
+      # Detect global 'use strict' directives
+      if parentNode is this.parseTree and
+         node instanceof uglifyJs.AST_Directive and
+         node.value is 'use strict'
+        @strict = true

       if node instanceof uglifyJs.AST_Call
         parentParentNode = stack[stack.length - 3]


Fixes for HTMLInputElement click default action.

--- src/dom/patches.coffee
+++ src/dom/patches.coffee
@@ -38,12 +38,30 @@
             console.log "Inside default click handler"
             window = event.target.ownerDocument.parentWindow
             window.location = event.target.href if event.target.href?
-    ###
+
     core.HTMLInputElement.prototype._eventDefaults =
         click : (event) ->
             console.log "Inside overridden click handler"
             #TODO: bring this back, but just double check things.
-            event.target.click()
+           # event.target.click()
+            target = event.target
+            if target.type == 'checkbox'
+                target.checked = !target.checked
+            else if target.type == 'radio'
+                console.log('This is a radio button')
+                doc = target.ownerDocument
+                others = doc.getElementsByName(target.name)
+                for other in others
+                    if other != target && other.type == 'radio'
+                        console.log("Setting other radio to false: #{other.value}")
+                        other.checked = false
+                console.log("Setting target to true: #{target.value}")
+                target.checked = true
+            else if target.type == 'submit'
+                form = target.form
+                if form
+                  form._dispatchSubmitEvent()
+        ###
         keypress : (event) ->
             # TODO: delete/backspace etc
             # is there a way to just 'apply' the key value to the string?


Force visibility-change checking in EditorView.

When an `EditorView` becomes attached, if hardware acceleration is enabled,
the call to `pollDOM` is a no-op because there's already a refresh pending.
This can cause problems, because then the initial measurements don't happen
and (for example) the lineHeight is left as null - it'll be set on the next
rendering, but not before subscribers to `workspaceView.eachEditorView` are
notified.

--- src/editor-view.coffee
+++ src/editor-view.coffee
@@ -144,6 +144,11 @@
     return if @attached
     @attached = true
     @component.pollDOM()
+
+    # Ensure that editor measurements like lineHeight are taken here, even if pollDOM() was a no-op
+    # because an update was requested.
+    @component.checkForVisibilityChange()
+
     @focus() if @focusOnAttach

     @addGrammarScopeAttribute()


Deprecate setShowInvisibles

Deprecate setShowInvisibles

--- src/editor-view.coffee
+++ src/editor-view.coffee
@@ -313,10 +313,8 @@
     deprecate 'Use Editor::setSoftWrapped instead. You can get the editor via editorView.getModel()'
     @editor.setSoftWrapped(softWrapped)

-  # Public: Set whether invisible characters are shown.
-  #
-  # * `showInvisibles` A {Boolean} which, if `true`, show invisible characters.
   setShowInvisibles: (showInvisibles) ->
+    deprecate 'This is going away. Use atom.config.set("editor.showInvisibles", true|false) instead'
     @component.setShowInvisibles(showInvisibles)

   getText: ->


Fix typo constructor

Fix typo constructor

--- src/editor-view.coffee
+++ src/editor-view.coffee
@@ -1663,7 +1663,7 @@
   # Private:
   getCharacterWidthCache: (scopes, char) ->
     scopes ?= NoScope
-    obj = @constsructor.characterWidthCache
+    obj = @constructor.characterWidthCache
     for scope in scopes
       obj = obj[scope]
       return null unless obj?


stop when done

stop when done

--- src/layout/mixins/events.coffee
+++ src/layout/mixins/events.coffee
@@ -48,6 +48,7 @@
   done: (world, args) ->
     success = args > 0
     message = if success then 'victory' else 'failure'
+    world.send 'stop'
     alert message
     # TODO: Make this a full-fledged dialog
     # Add retry, next level, select levels, select game


modify and simplify example API

modify and simplify example API

--- src/example_api.coffee
+++ src/example_api.coffee
@@ -3,204 +3,88 @@
 exports.media_type = media_type = (name) ->
   "application/vnd.gh-knockoff.#{name}+json;version=1.0"

-exports.schema =
-  id: "gh-knockoff"
-  properties:
+
+  authenticated_user:
+    path: "/user"
+    resource: "user"
+
+  user:
+    resource: "user"
+    template: "/user/:login"

-    resource:
-      type: "object"
-      properties:
-        url:
-          type: "string"
-          format: "uri"
-
-    organization:
-      extends: {$ref: "#resource"}
-      mediaType: media_type("organization")
-      properties:
-        name: {type: "string"}
-        plan: {$ref: "#plan"}
-        projects:
-          # Here's how you describe a dictionary
-          type: "object"
-          additionalProperties: {$ref: "#project"}
-        members: {$ref: "#user_list"}
-
-    organization_list:
-      type: "object"
-      mediaType: media_type("organization_list")
-      additionalProperties: {$ref: "#organization"}
-
-
-    plan:
-      extends: {$ref: "#resource"}
-      mediaType: media_type("plan")
-      properties:
-        name: {type: "string"}
-        space: {type: "integer"}
-        bandwidth: {type: "integer"}
-
-    plan_list:
-      type: "object"
-      mediaType: media_type("plan_list")
-      additionalProperties: {$ref: "#plan"}
-
-    user:
-      extends: {$ref: "#resource"}
-      mediaType: media_type("user")
-      properties:
-        name: {type: "string"}
-        email: {type: "string"}
-
-    user_list:
-      mediaType: media_type("user_list")
-      type: "array"
-      items: {$ref: "#user"}
-
-
-    project:
-      extends: {$ref: "#resource"}
-      mediaType: media_type("project")
-      properties:
-        name: {type: "string"}
-        description: {type: "string"}
-        refs:
-          type: "object"
-          properties:
-            main: {$ref: "#branch"}
-            branches:
-              type: "object"
-              additionalProperties: {$ref: "#branch"}
-            tags:
-              type: "array"
-              items: {$ref: "#tag"}
-
-    project_list:
-      mediaType: media_type("project_list")
-      type: "array"
-      items: {$ref: "#project"}
-
-
-    reference:
-      extends: {$ref: "#resource"}
-      mediaType: media_type("reference")
-      properties:
-        name:
-          required: true
-          type: "string"
-        commit:
-          required: true
-          type: "string"
-        message:
-          required: true
-          type: "string"
-
-    branch:
-      extends: {$ref: "#reference"}
-      mediaType: media_type("branch")
-
-    tag:
-      extends: {$ref: "#reference"}
-      mediaType: media_type("tag")
-
+  user_search:
+    path: "/user"
+    resource: "user_search"
+    query: search_query
+
+  repositories:
+    description: "Repositories for the authenticated user"
+    path: "/user/repos"
+    resource: "repositories"
+
+  user_repositories:
+    path: "/user/:login/repos"
+    resource: "repositories"
+
+  repository:
+    template: "/repos/:login/:name"
+    resource: "repository"
+
+  repo_search:
+    path: "/repos"
+    query: search_query
+    resource: "repo_search"

 exports.resources =

-  organizations:
-    actions:
-
-      create:
-        method: "POST"
-        request_schema: "organization"
-        response_schema: "organization"
-        status: 201
-
-      search:
-        method: "GET"
-        response_schema: "organization_list"
-        query:
-          q:
-            required: true
-            type: "string"
-          limit:
-            type: "integer"
-          offset:
-            type: "integer"
-          sort:
-            type: "string"
-            enum: ["asc", "desc"]
-        status: 200
-
-  organization:
-    actions:
-      get:
-        method: "GET"
-        response_schema: "organization"
-        status: 200
-
-      update:
-        method: "PUT"
-        request_schema: "organization"
-        response_schema: "organization"
-        authorization: "Basic"
-        status: 200
-
-      delete:
-        method: "DELETE"
-        authorization: "Basic"
-        status: 204
-
-  plans:
-    actions:
-      list:
-        method: "GET"
-        response_schema: "plan_list"
-        status: 200
-
-
-  plan:
-    actions:
-      get:
-        method: "GET"
-        response_schema: "plan"
-        status: 200
-
-      update:
-        method: "PUT"
-        request_schema: "plan"
-        response_schema: "plan"
-        status: 200
-
   user:
     actions:
-
       get:
         method: "GET"
         response_schema: "user"
         status: 200
-
       update:
         method: "PUT"
         request_schema: "user"
         response_schema: "user"
         status: 200

-  project:
+  user_search:
     actions:
-
       get:
         method: "GET"
-        response_schema: "project"
+        response_schema: "user_list"
+        status: 200
+
+  repository:
+    actions:
+      get:
+        method: "GET"
+        response_schema: "repository"
         status: 200

       update:
         method: "PUT"
-        response_schema: "project"
+        response_schema: "repository"
         status: 200

       delete:
         method: "DELETE", status: 204
+
+  repo_search:
+    actions:
+      get:
+        method: "GET"
+        response_schema: "repository_list"
+        status: 200
+
+  repositories:
+    actions:
+      create:
+        method: "POST"
+        request_schema: "repository"
+        status: 201

   ref:
     actions:
@@ -233,29 +117,77 @@
         status: 204

-exports.paths =
-  organizations:
-    path: "/organizations"
-    publish: true
+exports.schema =
+  id: "gh-knockoff"
+  # This is the conventional place to store schema definitions,
+  # becoming official as of Draft 04
+  definitions:

-  organization:
-    path: "/organizations/:id"
+    resource:
+      type: "object"
+      properties:
+        url:
+          type: "string"
+          format: "uri"

-  plans:
-    path: "/plans"
-    publish: true
+    user:
+      extends: {$ref: "#/definitions/resource"}
+      mediaType: media_type("user")
+      properties:
+        login: {type: "string"}
+        name: {type: "string"}
+        email: {type: "string"}

-  plan:
-    path: "/plans/:id"
-
-  user:
-    path: "/users/:id"
+    user_list:
+      mediaType: media_type("user_list")
+      type: "array"
+      items: {$ref: "#/definitions/user"}

-#module.exports =
-  #media_type: media_type
-  #service_url: "http://hostname.com/"
-  #paths: paths
-  #resources: resources
-  #schema: schema
+    repository:
+      extends: {$ref: "#/definitions/resource"}
+      mediaType: media_type("repository")
+      properties:
+        name: {type: "string"}
+        description: {type: "string"}
+        refs:
+          type: "object"
+          properties:
+            main: {$ref: "#/definitions/branch"}
+            branches:
+              type: "object"
+              additionalProperties: {$ref: "#/definitions/branch"}
+            tags:
+              type: "array"
+              items: {$ref: "#/definitions/tag"}

+    repository_list:
+      mediaType: media_type("repository_list")
+      type: "array"
+      items: {$ref: "#/definitions/repository"}
+
+
+    reference:
+      extends: {$ref: "#/definitions/resource"}
+      mediaType: media_type("reference")
+      properties:
+        name:
+          required: true
+          type: "string"
+        commit:
+          required: true
+          type: "string"
+        message:
+          required: true
+          type: "string"
+
+    branch:
+      extends: {$ref: "#/definitions/reference"}
+      mediaType: media_type("branch")
+
+    tag:
+      extends: {$ref: "#/definitions/reference"}
+      mediaType: media_type("tag")
+
+
+


include Cooldown as default module

include Cooldown as default module

--- src/game_object.coffee
+++ src/game_object.coffee
@@ -161,7 +161,7 @@
       I.active = false
   }

-  defaultModules = [Bindable, Bounded, Drawable, Durable]
+  defaultModules = [Bindable, Bounded, CoolDrawable, Durable]
   modules = defaultModules.concat(I.includedModules.invoke('constantize'))
   modules = modules.without(I.excludedModules.invoke('constantize'))


Add a giphy alias for compat with slack command.

I'm trying to migrate folks from  the slack integration for giphy, and it would be helpful if that command worked here too :)

--- src/giphy-gifme.coffee
+++ src/giphy-gifme.coffee
@@ -28,6 +28,6 @@
     msg.send(response.data.image_url)

 module.exports = (robot) ->
-  robot.respond /gif me(.*)/i, (msg) ->
+  robot.respond /gif me|giphy(.*)/i, (msg) ->
     tags = msg.match[1].trim().split(', ')
     getRandomGiphyGif(msg, tags)


Hack for media upload.

Hack for media upload.

--- src/wx.coffee
+++ src/wx.coffee
@@ -47,7 +47,7 @@
   #
   # + 消息订阅专用客户端。
   # + 其余通用操作客户端；
-  {port, host, options} = redis_options ? {}
+  {port, host, options} = redis_options ? {port: null, host: null, options: null}
   redis_pubsub = redis.createClient port, host, options
   redis_client = redis.createClient port, host, options

@@ -923,14 +923,23 @@
     #
     # 直接调用`curl`命令，完成文件上传。
     upload: (type, media, callback = ->) =>
-      exec "curl -F media=@#{media} \"#{api_binary}/media/upload?access_token=#{access_token}&type=#{type}\"", (err, res) ->
+      #exec "curl -F media=@#{media} \"#{api_binary}/media/upload?access_token=#{access_token}&type=#{type}\"", (err, res) ->
+      #  return callback err if err
+      #  try
+      #    res = JSON.parse res
+      #  catch err
+      #    return callback err
+      #  return callback res if res.errcode
+      #  callback null, res
+      r = request.post
+        url      : "#{api_binary}/media/upload?access_token=#{access_token}&type=#{type}"
+        json     : true
+      , (err, res) ->
         return callback err if err
-        try
-          res = JSON.parse res
-        catch err
-          return callback err
-        return callback res if res.errcode
-        callback null, res
+        callback null, res.body
+      form = r.form()
+      form.append('media', if typeof media == 'string' then fs.createReadStream(media) else media)
+      form.append('hack', '')
       @

     # ### 文件下载


use .toString('base64') instead of requiring base64

--- src/wx.coffee
+++ src/wx.coffee
@@ -14,7 +14,6 @@
 xml2js     = require 'xml2js'
 express    = require 'express'
 async      = require 'async'
-base64     = require 'base64'

 # ### 接口地址
 #
@@ -783,7 +782,7 @@

     # 响应设备发来的消息
     device: (content) ->
-      content = base64.encode(content)
+      content = (new Buffer(content)).toString('base64')

       @send message "<MsgType><![CDATA[device_text]]></MsgType>
         <DeviceType><![CDATA[#{req.device_type}]]></DeviceType>


:memo: Tweak header comment

:memo: Tweak header comment

--- lib/image-editor-view.coffee
+++ lib/image-editor-view.coffee
@@ -1,7 +1,7 @@
 _ = require 'underscore-plus'
 {$, ScrollView} = require 'atom'

-# Renders the image in an {ImageEditor}.
+# View that renders the image of an {ImageEditor}.
 module.exports =
 class ImageEditorView extends ScrollView
   @content: ->


add link tag for root image folder path

add link tag for root image folder path

--- lib/image-helper-view.coffee
+++ lib/image-helper-view.coffee
@@ -209,7 +209,14 @@
     if rootImageFolderPath[0] != '/'
       rootImageFolderPath = '/' + rootImageFolderPath

-    copyLabel.text  "Copy image to root #{rootImageFolderPath} folder"
+    copyLabel.html  "Copy image to root <a>#{rootImageFolderPath}</a> folder"
+
+    copyLabel.find('a').on 'click', ()=>
+      try
+        atom.workspace.open('atom://config/packages/markdown-preview-enhanced', {split: 'right'})
+        @hidePanel()
+      catch e
+        @hidePanel()

 insertImageView = new InsertImageView()
 module.exports = insertImageView


add graph comments

add graph comments


help button

help button

--- electron/ui/app.coffee
+++ electron/ui/app.coffee
@@ -143,7 +143,8 @@
      callback()

    @listen 'help', ->
-    console.log ELECTRON
+    ELECTRON.shell.openExternal "http://wallapatta.github.io/"
+
    @listen 'folder', -> IPC.send 'openFolder'
    @listen 'folderOpened', (e, folders) ->
     console.log folders


Doc update

Doc update

--- src/cuffs/bindings.coffee
+++ src/cuffs/bindings.coffee
@@ -6,6 +6,10 @@
         #

         #

         #

+        #
+        # NOTE: doesn't handle type coercion so this won't work as expected:
+        #   <div data-show="showOtherSection!=true"
+        # This is because "true" will be a string rather than a bool

         @bind 'data-show'


Use the model to update the interface

Use the model to update the interface

--- lib/project-find-view.coffee
+++ lib/project-find-view.coffee
@@ -141,6 +141,9 @@
       'project-find:toggle-whole-word-option': => @toggleWholeWordOption()
       'project-find:replace-all': => @replaceAll()

+    updateInterfaceForSearching = =>
+      @setInfoMessage('Searching...')
+
     updateInterfaceForResults = (results) =>
       if results.matchCount is 0 and results.findPattern is ''
         @clearMessages()
@@ -154,6 +157,7 @@

     @subscriptions.add @model.onDidClear(resetInterface)
     @subscriptions.add @model.onDidClearReplacementState(updateInterfaceForResults)
+    @subscriptions.add @model.onDidStartSearching(updateInterfaceForSearching)
     @subscriptions.add @model.onDidFinishSearching(updateInterfaceForResults)
     @subscriptions.add @model.getFindOptions().onDidChange @updateOptionViews

@@ -231,7 +235,6 @@
     pathsPattern = @pathsEditor.getText()
     replacePattern = @replaceEditor.getText()

-    @clearMessages()
     @showResultPane().then =>
       try
         @model.search(findPattern, pathsPattern, replacePattern, options)


Fix failing slack

Fix failing slack

--- angular/core/components/install_slack/invite_form/install_slack_invite_form.coffee
+++ angular/core/components/install_slack/invite_form/install_slack_invite_form.coffee
@@ -6,7 +6,7 @@

     $scope.fetchChannels = ->
       return if $scope.channels
-      Records.identities.performCommand(Session.user().identityFor('slack')().id, 'channels').then (response) ->
+      Records.identities.performCommand(Session.user().identityFor('slack').id, 'channels').then (response) ->
         $scope.channels = response
       , (response) ->
         $scope.error = response.data.error


KiteCache: update signature

KiteCache: update signature

--- client/app/lib/kite/kitecache.coffee
+++ client/app/lib/kite/kitecache.coffee
@@ -6,7 +6,7 @@
 module.exports = class KiteCache

   storage   = LocalStorage.getStorage()
-  signature = 'KITE_'
+  signature = 'KITE_v2_'

   @generateQueryString = (options) ->


fix title of END marker

fix title of END marker

--- lib/regex-to-railroad.coffee
+++ lib/regex-to-railroad.coffee
@@ -42,9 +42,9 @@

   doEndOfString   = ->
     if opts.match /m/
-      title = "Beginning of line"
-    else
-      title = "Beginning of string"
+      title = "End of line"
+    else
+      title = "End of string"

     NonTerminal("END", title: title, class: 'zero-width-assertion')


Fix the plans-and-pricing click faux pas.

--- src/main/webapp/coffeescripts-hidden/landing.coffee
+++ src/main/webapp/coffeescripts-hidden/landing.coffee
@@ -34,7 +34,7 @@
       error : onFailure,
       crossDomain: true

-  $('body').on 'click', '.plans-and-pricing', ->
+  $('body').on 'click', 'button.plans-and-pricing', ->
     mixpanel.track("Click Plans & Pricing")

     $targetElem = $("section.plans-and-pricing > h3")


updating copy for back button

updating copy for back button

--- app/classifier/index.cjsx
+++ app/classifier/index.cjsx
@@ -423,7 +423,7 @@
     @setState backButtonWarning: false

   renderBackButtonWarning: ->
-
Going back will delete all annotations for the current task.

+
Going back will clear your work for the current task.

 module.exports = React.createClass
   displayName: 'ClassifierWrapper'


Reference instances options object

Reference instances options object

--- src/kissmetrics.coffee
+++ src/kissmetrics.coffee
@@ -55,7 +55,7 @@
   constructor: (@apiKey, @person, @options = {}) ->
     @queries = []

-    if NODEJS is on and options.batchRequests is on
+    if NODEJS is on and @options.batchRequests is on
       BatchClient = require 'kissmetrics-batch'
       @batchClient = new BatchClient options.queue

@@ -187,7 +187,7 @@
   _generateQuery: (type, data) ->
     @_validateData data

-    if options.batch
+    if @options.batchRequests
       timestamp      = Math.round((new Date).getTime() / 1000)
       batchData      = data
       batchData.type = type


Add in null check

Add in null check

--- app/coffee/DocManager.coffee
+++ app/coffee/DocManager.coffee
@@ -63,7 +63,7 @@

 	_findDocInFolder: (folder = {}, doc_id, currentPath) ->
 		for doc, i in folder.docs or []
-			if doc._id.toString() == doc_id.toString()
+			if doc._id? and doc._id.toString() == doc_id.toString()
 				return {
 					doc: doc
 					mongoPath: "#{currentPath}.docs.#{i}"


お店の詳細情報画面に引き渡す時に、お店の名前の受け渡しの所にバグがあったので修正

--- app/controllers/index.coffee
+++ app/controllers/index.coffee
@@ -120,7 +120,7 @@
     kloudService.statusesQuery(e.annotation.placeID,(statuses) ->
       Ti.API.info "statuses is #{statuses}"
       shopData =
-        shopName:e.annotation.shopName
+        shopName:e.annotation.title
         phoneNumber:e.annotation.phoneNumber
         latitude: e.annotation.latitude
         longitude: e.annotation.longitude


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_tilemap.coffee
+++ src/engine_tilemap.coffee
@@ -10,11 +10,32 @@
 ###
 Engine.Tilemap = (I, self) ->
   map = null
+  updating = false
+  clearObjects = false

   self.bind "preDraw", (canvas) ->
     map?.draw canvas

+  self.bind "update", ->
+    updating = true
+
+  self.bind "afterUpdate", ->
+    updating = false
+
+    if clearObjects
+      #TODO: Clear these out in a more graceful way, triggering unload events
+      I.objects.clear()
+      clearObjects = false
+
+  ###*
+  Loads a new may and unloads any existing map or entities.
+
+  @name loadMap
+  @methodOf Engine#
+  ###
   loadMap: (name, complete) ->
+    clearObjects = updating
+
     map = Tilemap.load
       name: name
       complete: complete


:art: clear up a var name

:art: clear up a var name

--- src/ext/svg.import.coffee
+++ src/ext/svg.import.coffee
@@ -156,19 +156,19 @@
     path: (el) -> [new Curve.Path(svgDocument, svgEl: el)]

   # create temporary div to receive svg content
-  well = document.createElement('div')
+  parentNode = document.createElement('div')
   store = {}

   # properly close svg tags and add them to the DOM
-  well.innerHTML = svgString
+  parentNode.innerHTML = svgString
     .replace(/\n/, '')
     .replace(/<(\w+)([^<]+?)\/>/g, '<$1$2></$1>')

   objects = []
-  convertNodes well.childNodes, svgDocument, 0, store, ->
+  convertNodes parentNode.childNodes, svgDocument, 0, store, ->
     nodeType = this.node.nodeName
     objects = objects.concat(IMPORT_FNS[nodeType](this)) if IMPORT_FNS[nodeType]
     null

-  well = null
+  parentNode = null
   objects


fix the unable to type chinese issue

fix the unable to type chinese issue

--- app/scripts/editor.coffee
+++ app/scripts/editor.coffee
@@ -58,8 +58,7 @@
 		result = ''
 		result += Math.random().toString(36).substr(2,1) for i in new Array(len)
 		result.toUpperCase()
-	update: ->
-		self = @
+	divsToPs: ->
 		#transform divs to ps
 		@saveSelection()
 		$('#editor div').not('[name]').each ->
@@ -68,6 +67,8 @@
 			$(@).after(el)
 			$(@).remove()
 		@restoreSelection()
+	update: ->
+		self = @
 		# giving names
 		$('#editor p,h1,h2,pre').not('[name]').each ->
 			$(@).attr 'name', self.rand()
@@ -161,6 +162,8 @@
 		$('#debug-keyup').text e.keyCode

 		switch e.keyCode
+			when 13
+				@divsToPs()
 			when 16
 				@status.shift = false
 				$('.shift').addClass('hidden')


Added GAMA-KiDS to call to action

Added GAMA-KiDS to call to action

--- app/lib/en.coffee
+++ app/lib/en.coffee
@@ -1155,7 +1155,7 @@

   home:
     headline: "Few have witnessed what you're about to see"
-    call_to_action: 'Experience a privileged glimpse of the distant universe as observed by the SDSS and the CTIO.'
+    call_to_action: 'Experience a privileged glimpse of the distant universe as observed by the SDSS, CTIO and VST.'

     galaxy_zoo:
       title: 'Classify Galaxies'


group summary: go to coding link needs to open a new path to /slug/Activity as well as hiding the landing page

--- client/landing/app/Applications/Groups.kdapplication/Views/groupsummary.coffee
+++ client/landing/app/Applications/Groups.kdapplication/Views/groupsummary.coffee
@@ -60,7 +60,9 @@
       cssClass    : "open-in-koding #{if group.privacy is 'private' then 'hidden' else ''}"
       title       : "Open the group in Koding"
       icon        : {}
-      click       : @bound "hideLandingPage"
+      click       : (event)=>
+        @hideLandingPage event
+        @lazyDomController.openPath "/#{group.slug}/Activity"

     @openInKodingLink.on "viewAppended", =>
       # set ToolTip here


setting default to true on debug verbosity

--- examples/config.coffee
+++ examples/config.coffee
@@ -31,6 +31,7 @@
     init: if production == true then false else true
   debug:
     mongo: if production == true then false else false
+    config: if production == true then false else true

 conf = new Config config


fix(database): Do not send empty error JSON

--- src/browser/database-manager.coffee
+++ src/browser/database-manager.coffee
@@ -72,7 +72,7 @@
       return

     @_query db, query, values, (err, result) ->
-      errJSONString = JSON.stringify(err)
+      errJSONString = if err then JSON.stringify(err) else null
       event.sender.send('database-result', {queryKey, errJSONString, result})

   # Resolves when a new database has been created and the initial setup


localstorage: displayEvents added, commented out prototype

--- client/landing/app/Applications/Activity.kdapplication/widgets/statuswidget.coffee
+++ client/landing/app/Applications/Activity.kdapplication/widgets/statuswidget.coffee
@@ -36,15 +36,15 @@
       name          : 'body'
       style         : 'input-with-extras'
       autogrow      : yes
-      preserveValue :
-        name        : 'status-widget'
-        saveEvents  : ['keyup','blur']
-        # displayEvents: ['focus']
-        clearEvents : 'ValidationPassed'
-        setValue    : (value)=>
-          @largeInput.setValue value
-        getValue    : =>
-          @largeInput.getValue()
+      # preserveValue :
+      #   name        : 'status-widget'
+      #   saveEvents  : ['keyup','blur']
+      #   # displayEvents: ['focus']
+      #   clearEvents : 'ValidationPassed'
+      #   setValue    : (value)=>
+      #     @largeInput.setValue value
+      #   getValue    : =>
+      #     @largeInput.getValue()
       validate      :
         rules       :
           required  : yes


disabled  embedding in status update widget

--- client/landing/app/Applications/Activity.kdapplication/widgets/statuswidget.coffee
+++ client/landing/app/Applications/Activity.kdapplication/widgets/statuswidget.coffee
@@ -16,7 +16,7 @@
         rules       :
           maxLength : 2000

-    @previousWhich = 0
+    # @previousWhich = 0

     @largeInput = new KDInputView
       cssClass      : "status-update-input"
@@ -31,13 +31,13 @@
           maxLength : 3000
         messages    :
           required  : "Please type a message..."
-      keydown:=>
-        if ($(event.which)[0] is 32) or ($(event.which)[0] is 86 and @previousWhich is 91)
-          setTimeout =>
-            firstUrl = @largeInput.getValue().match(/[a-zA-Z\d]+:\/\/(\w+:\w+@)?([a-zA-Z\d.-]+\.[A-Za-z]{2,4})(:\d+)?(\/.*)?/g)
-            @embedBox.embedUrl firstUrl?[0]
-          ,500
-        @previousWhich = $(event.which)[0]
+      # keydown:=>
+      #   if ($(event.which)[0] is 32) or ($(event.which)[0] is 86 and @previousWhich is 91)
+      #     setTimeout =>
+      #       firstUrl = @largeInput.getValue().match(/[a-zA-Z\d]+:\/\/(\w+:\w+@)?([a-zA-Z\d.-]+\.[A-Za-z]{2,4})(:\d+)?(\/.*)?/g)
+      #       @embedBox.embedUrl firstUrl?[0]
+      #     ,500
+      #   @previousWhich = $(event.which)[0]

     @cancelBtn = new KDButtonView
@@ -52,7 +52,7 @@
       title       : "Submit"
       type        : "submit"

-    @embedBox = new EmbedBox
+    # @embedBox = new EmbedBox

     @heartBox = new HelpBox
       subtitle : "About Status Updates"
@@ -138,11 +138,11 @@
       @switchToSmallView()

   pistachio:->
+    # {{> @embedBox}}
     """

{{> @smallInput}}

{{> @largeInput}}

-    {{> @embedBox}}

       {{> @labelAddTags}}


Minor revisions

Minor revisions

--- client/landing/app/Applications/Activity.kdapplication/widgets/widgetbutton.coffee
+++ client/landing/app/Applications/Activity.kdapplication/widgets/widgetbutton.coffee
@@ -42,13 +42,13 @@
   viewAppended:->
     {items, delegate} = @getOptions()

-    for title, content of items
+    for own title, content of items
       @addSubView icon = new KDCustomHTMLView
         cssClass       : "#{@utils.slugify(content.type)}"
-        _type          : content.type
-        _title         : title
+        type          : content.type
+        title         : title
         click          : ->
-          delegate.changeTab @getOption("_type"), @getOption("_title")
+          delegate.changeTab @getOption("type"), @getOption("title")

       icon.setClass "hidden" if content.disabled


Fix file tree issue for new users

Fix file tree issue for new users

--- client/landing/app/MainApp/filetree/controllers/findercontextmenucontroller.coffee
+++ client/landing/app/MainApp/filetree/controllers/findercontextmenucontroller.coffee
@@ -371,7 +371,7 @@
     fileExtension    = FSItem.getFileExtension fileView.getData().path
     appsController   = @getSingleton "kodingAppsController"
     {extensionToApp} = appsController
-    possibleApps     = extensionToApp[fileExtension] or extensionToApp.txt
+    possibleApps     = (extensionToApp[fileExtension] or extensionToApp.txt) or []
     for appName in possibleApps
       items[appName] = action: "openFileWithApp"


:lipstick: method order

:lipstick: method order

--- src/editor-component.coffee
+++ src/editor-component.coffee
@@ -46,13 +46,6 @@
         onScroll: @onHorizontalScroll
         scrollLeft: editor.getScrollLeft()
         scrollWidth: editor.getScrollWidth()
-
-  getVisibleRowRange: ->
-    visibleRowRange = @props.editor.getVisibleRowRange()
-    if @visibleRowOverrides?
-      visibleRowRange[0] = Math.min(visibleRowRange[0], @visibleRowOverrides[0])
-      visibleRowRange[1] = Math.max(visibleRowRange[1], @visibleRowOverrides[1])
-    visibleRowRange

   getInitialState: -> {}

@@ -262,24 +255,31 @@

     event.preventDefault()

+  onScreenLinesChanged: ({start, end}) ->
+    {editor} = @props
+    @requestUpdate() if editor.intersectsVisibleRowRange(start, end + 1) # TODO: Use closed-open intervals for change events
+
+  onSelectionAdded: (selection) ->
+    {editor} = @props
+    @requestUpdate() if editor.selectionIntersectsVisibleRowRange(selection)
+
+  onSelectionRemoved: (selection) ->
+    {editor} = @props
+    @requestUpdate() if editor.selectionIntersectsVisibleRowRange(selection)
+
+  getVisibleRowRange: ->
+    visibleRowRange = @props.editor.getVisibleRowRange()
+    if @visibleRowOverrides?
+      visibleRowRange[0] = Math.min(visibleRowRange[0], @visibleRowOverrides[0])
+      visibleRowRange[1] = Math.max(visibleRowRange[1], @visibleRowOverrides[1])
+    visibleRowRange
+
   clearVisibleRowOverrides: ->
     @visibleRowOverrides = null
     @forceUpdate()

   clearVisibleRowOverridesAfterDelay: null

-  onScreenLinesChanged: ({start, end}) ->
-    {editor} = @props
-    @requestUpdate() if editor.intersectsVisibleRowRange(start, end + 1) # TODO: Use closed-open intervals for change events
-
-  onSelectionAdded: (selection) ->
-    {editor} = @props
-    @requestUpdate() if editor.selectionIntersectsVisibleRowRange(selection)
-
-  onSelectionRemoved: (selection) ->
-    {editor} = @props
-    @requestUpdate() if editor.selectionIntersectsVisibleRowRange(selection)
-
   requestUpdate: ->
     @forceUpdate()


Move benchmark code to the end of the editor component

--- src/editor-component.coffee
+++ src/editor-component.coffee
@@ -683,6 +683,59 @@
   show: ->
     @setState(visible: true)

+  getFontSize: ->
+    @state.fontSize
+
+  setFontSize: (fontSize) ->
+    @setState({fontSize})
+
+  getFontFamily: ->
+    @state.fontFamily
+
+  setFontFamily: (fontFamily) ->
+    @setState({fontFamily})
+
+  setLineHeight: (lineHeight) ->
+    @setState({lineHeight})
+
+  setShowIndentGuide: (showIndentGuide) ->
+    @setState({showIndentGuide})
+
+  # Public: Defines which characters are invisible.
+  #
+  # invisibles - An {Object} defining the invisible characters:
+  #   :eol   - The end of line invisible {String} (default: `\u00ac`).
+  #   :space - The space invisible {String} (default: `\u00b7`).
+  #   :tab   - The tab invisible {String} (default: `\u00bb`).
+  #   :cr    - The carriage return invisible {String} (default: `\u00a4`).
+  setInvisibles: (invisibles={}) ->
+    defaults invisibles,
+      eol: '\u00ac'
+      space: '\u00b7'
+      tab: '\u00bb'
+      cr: '\u00a4'
+
+    @setState({invisibles})
+
+  setShowInvisibles: (showInvisibles) ->
+    @setState({showInvisibles})
+
+  screenPositionForMouseEvent: (event) ->
+    pixelPosition = @pixelPositionForMouseEvent(event)
+    @props.editor.screenPositionForPixelPosition(pixelPosition)
+
+  pixelPositionForMouseEvent: (event) ->
+    {editor} = @props
+    {clientX, clientY} = event
+
+    scrollViewClientRect = @refs.scrollView.getDOMNode().getBoundingClientRect()
+    top = clientY - scrollViewClientRect.top + editor.getScrollTop()
+    left = clientX - scrollViewClientRect.left + editor.getScrollLeft()
+    {top, left}
+
+  getModel: ->
+    @props.editor
+
   runScrollBenchmark: ->
     unless process.env.NODE_ENV is 'production'
       ReactPerf = require 'react-atom-fork/lib/ReactDefaultPerf'
@@ -717,56 +770,3 @@
                   ReactPerf.printExclusive()
                   console.log "Wasted"
                   ReactPerf.printWasted()
-
-  getFontSize: ->
-    @state.fontSize
-
-  setFontSize: (fontSize) ->
-    @setState({fontSize})
-
-  getFontFamily: ->
-    @state.fontFamily
-
-  setFontFamily: (fontFamily) ->
-    @setState({fontFamily})
-
-  setLineHeight: (lineHeight) ->
-    @setState({lineHeight})
-
-  setShowIndentGuide: (showIndentGuide) ->
-    @setState({showIndentGuide})
-
-  # Public: Defines which characters are invisible.
-  #
-  # invisibles - An {Object} defining the invisible characters:
-  #   :eol   - The end of line invisible {String} (default: `\u00ac`).
-  #   :space - The space invisible {String} (default: `\u00b7`).
-  #   :tab   - The tab invisible {String} (default: `\u00bb`).
-  #   :cr    - The carriage return invisible {String} (default: `\u00a4`).
-  setInvisibles: (invisibles={}) ->
-    defaults invisibles,
-      eol: '\u00ac'
-      space: '\u00b7'
-      tab: '\u00bb'
-      cr: '\u00a4'
-
-    @setState({invisibles})
-
-  setShowInvisibles: (showInvisibles) ->
-    @setState({showInvisibles})
-
-  screenPositionForMouseEvent: (event) ->
-    pixelPosition = @pixelPositionForMouseEvent(event)
-    @props.editor.screenPositionForPixelPosition(pixelPosition)
-
-  pixelPositionForMouseEvent: (event) ->
-    {editor} = @props
-    {clientX, clientY} = event
-
-    scrollViewClientRect = @refs.scrollView.getDOMNode().getBoundingClientRect()
-    top = clientY - scrollViewClientRect.top + editor.getScrollTop()
-    left = clientX - scrollViewClientRect.left + editor.getScrollLeft()
-    {top, left}
-
-  getModel: ->
-    @props.editor


Call the better-named method.

Call the better-named method.

--- src/git-repository.coffee
+++ src/git-repository.coffee
@@ -324,7 +324,7 @@
   # {::isStatusModified} or {::isStatusNew} to get more information.
   getPathStatus: (path) ->
     # Trigger events emitted on the async repo as well
-    @async.getPathStatus(path)
+    @async.refreshStatusForPath(path)

     repo = @getRepo(path)
     relativePath = @relativize(path)


Add support for regional coupon pricing (#4769)

* Fix working of region specific stripe coupons

* Use country basic subscription if couponID subscription not found

--- app/collections/Products.coffee
+++ app/collections/Products.coffee
@@ -9,9 +9,17 @@
   getByName: (name) -> @findWhere { name: name }

   getBasicSubscriptionForUser: (user) ->
-    countrySpecificProduct = @findWhere { name: "#{user?.get('country')}_basic_subscription" }
+    coupon = user?.get('stripe')?.couponID
+    if coupon
+      countrySpecificProduct = @findWhere { name: "#{coupon}_basic_subscription" }
+    unless countrySpecificProduct
+      countrySpecificProduct = @findWhere { name: "#{user?.get('country')}_basic_subscription" }
     return countrySpecificProduct or @findWhere({ name: 'basic_subscription' })

   getLifetimeSubscriptionForUser: (user) ->
-    countrySpecificProduct = @findWhere { name: "#{user?.get('country')}_lifetime_subscription" }
+    coupon = user?.get('stripe')?.couponID
+    if coupon
+      countrySpecificProduct = @findWhere { name: "#{coupon}_lifetime_subscription" }
+    unless countrySpecificProduct
+      countrySpecificProduct = @findWhere { name: "#{user?.get('country')}_lifetime_subscription" }
     return countrySpecificProduct or @findWhere({ name: 'lifetime_subscription' })


Don't reset non-property-setting steps (i.e. reveal)

--- app/controllers/classify.coffee
+++ app/controllers/classify.coffee
@@ -175,7 +175,7 @@
     hadStrength = @classification.get 'strength'

     properties = []
-    properties = properties.concat step.property for _, step of @steps
+    properties = properties.concat step.property for _, step of @steps when step.property

     for property in properties
       @classification.set property, null


Update client docs

Update client docs

--- src/nack/client.coffee
+++ src/nack/client.coffee
@@ -24,38 +24,50 @@
 #
 exports.Client = class Client extends Stream
   constructor: ->
+    # Initialize outgoing array to hold pending requests
     @_outgoing = []
+    # Incoming is used to point to the current response
     @_incoming = null

+    # Once we've made the connect, process the next request
     @on 'connect', => @_processRequest()
-    @on 'close',   => @_finishRequest()
-
+    # Finalize the request on close
+    @on 'close', => @_finishRequest()
+
+    # Initialize the response netstring parser
     @_initResponseParser()

   _initResponseParser: () ->
     # Initialize a Netstring stream parser
     nsStream = new ns.Stream @

+    # Listen for data and hand it to our parser
     nsStream.on 'data', (data) =>
       if @_incoming
         @_incoming._receiveData data

+    # Bubble any errors
     nsStream.on 'error', (exception) =>
       @_incoming = null
       @emit 'error', exception

   _processRequest: () ->
+    # Process the request now if the socket is open and
+    # we aren't already handling a response
     if @readyState is 'open' and !@_incoming
       if request = @_outgoing[0]
         @_incoming = new ClientResponse @, request
+        # Flush the request buffer into socket
         request.flush()
     else
+      # Try to reconnect and try again soon
       @reconnect()

   _finishRequest: () ->
     @_outgoing.shift()
     @_incoming = null

+    # Anymore requests, continue processing
     if @_outgoing.length > 0
       @_processRequest()

@@ -196,11 +208,13 @@
     while @_writeQueue and @_writeQueue.length
       data = @_writeQueue.shift()

+      # Close write socket when we see an empty netstring `0:,`
       if data is END_OF_FILE
         @socket.end data
       else
         @socket.write data

+    # Clear queue, remaining writes won't buffer
     @_writeQueue = null

     # Buffer is empty, let the world know!
@@ -263,6 +277,8 @@
       # Else its body parts
       else if data.length > 0
         @emit 'data', data
+
+      # Empty string mean EOF
       else
         @emit 'end'


Bubble pool errors to server

Bubble pool errors to server

--- src/nack/server.coffee
+++ src/nack/server.coffee
@@ -32,6 +32,9 @@
       if err
         next err

+  pool.on 'error', (error) ->
+    server.emit 'error', error
+
   origClose = server.close
   server.close = ->
     try


:shirt: Clean up `Form.NestedField` template


add safestring handling to base directive

--- src/compiler/directives/base.coffee
+++ src/compiler/directives/base.coffee
@@ -9,6 +9,7 @@
     @root = @options.root
     # Is this directive being invoked as a block helper?
     @isBlock = !!@options.fn
+    @hash = @options.hash

   # Return the block content as raw string. If given an object, use that
   # object as the context for the block content.
@@ -26,6 +27,20 @@
     hbsOutput = template(context)
     marked(hbsOutput,{})

+  # Escapes a string to make it HTML safe. See note on `safe` about marking
+  # a string as safe.
+  escape: (str) ->
+    @hbs.escape(str)
+
+  # Marks a string as HTML safe.
+  #
+  # NOTE hbs tests if a string needs escaping by testing whether it is an instance of
+  # a SafeString specific the the current instance of handlebars. This is why we need
+  # to proxy the safe string factory via the particular instance of handlebars we are using
+  # instead of the default handlebars instance.
+  safe: (str) ->
+    @hbs.safe(str)
+
   process: ->
     throw "abstract"


Prevented users receiving output from failed Slack reaction

--- src/helpers/logger.coffee
+++ src/helpers/logger.coffee
@@ -96,5 +96,5 @@
           client._apiCall 'reactions.add', params, (response) ->
             if not response.ok
               Logger.errorToSlack response.error
-              Logger.logToChannel response.error, user
+              Logger.logToChannel "I couldn't react to a message, but I tried!", user
   Logger


adding third expansion cards

adding third expansion cards

ya filthy animal

--- src/hubot-humanity.coffee
+++ src/hubot-humanity.coffee
@@ -156,7 +156,35 @@
   "When the United States raced the Soviet Union to the moon, the Mexican government funneled millions of pesos into research on _____.",
   "Why am I sticky?",
   "Why can't I sleep at night?",
-  "Why do I hurt all over?"
+  "Why do I hurt all over?",
+  # -------------------
+  # 3rd expansion black
+  # -------------------
+  'A successful job interview begins with a firm handshake and ends with _____.',
+  'After months of practice with _____, I think I\'m finally ready for _____.',
+  'And what did you bring for show and tell?',
+  'As part of his contract, Prince won\'t perform without _____ in his dressing room.',
+  'As part of his daily regimen, Anderson Cooper sets aside 15 minutes for _____.',
+  'Call the law offices of Goldstein & Goldstein, because no one should have to tolerate _____ in the workplace.',
+  'During high school I never really fit in until I found _____ club.',
+  'Finally! A service that delivers _____ right to your door.',
+  'Having problems with _____? Try _____!',
+  'Hey baby, come back to my place and I\'ll show you _____.',
+  'I\'m not like the rest of you. I\'m too rich and busy for _____.',
+  'In the seventh circle of Hell, sinners must endure _____ for all eternity.',
+  'Listen, son. If you want to get involved with _____, I won\'t stop you. Just steer clear of _____.',
+  'Loving you is easy \'cause you\'re _____.',
+  'Money can\'t buy me love, but it can buy me _____.',
+  'My gym teacher got fired for adding _____ to the obstacle course.',
+  'My life is ruled by a vicious cycle of _____ and _____.',
+  'The blind date was going horribly until we discovered our shared interest in _____.',
+  'To prepare for his upcoming role, Daniel Day-Lewis immersed himself in the world of _____.',
+  'Turns out that _____-Man was neither the hero we needed nor wanted.',
+  'What left this stain on my couch?',
+  'When you get right down to it, _____ is just _____.',
+  'With enough time and pressure, _____ will turn into _____.',
+  '_____. Awesome in theory, kind of a mess in practice.',
+  '_____: Hours of fun. Easy to use. Perfect for _____!',
 ]

 answers = [
@@ -790,7 +818,85 @@
   "World peace.",
   "Yeast.",
   "YOU MUST CONSTRUCT ADDITIONAL PYLONS.",
-  "Zeus's sexual appetites."
+  "Zeus's sexual appetites.",
+  # -------------------
+  # 3rd expansion white
+  # -------------------
+  "A PowerPoint presentation.",
+  "A black male in his early 20s, last seen wearing a hoodie.",
+  "A boo-boo.",
+  "A botched circumcision.",
+  "A cat video so cute that your eyes roll back and your spine slides out of your anus.",
+  "A cop who is also a dog.",
+  "A greased-up Matthew McConaughey.",
+  "A lamprey swimming up the toilet and latching onto your taint.",
+  "A pile of squirming bodies.",
+  "A spontaneous conga line.",
+  "A surprising amount of hair.",
+  "A vagina that leads into another dimension.",
+  "Actually getting shot, for real.",
+  "All my friends dying.",
+  "An all-midget production of Shakespeare's Richard III.",
+  "An ass disaster.",
+  "An unstoppable wave of fire ants.",
+  "Bill Clinton, naked on a bearskin rug with a saxophone.",
+  "Blood farts.",
+  "Blowing some dudes in an alley.",
+  "Buying the right pants to be cool.",
+  "Chugging a lava lamp.",
+  "Cock.",
+  "Crying into the pages of Sylvia Plath.",
+  "Demonic possession.",
+  "Disco fever.",
+  "Drinking ten 5-hour ENERGYs to get fifty continuous hours of energy.",
+  "Dying alone and in pain.",
+  "Eating Tom Selleck's mustache to gain his powers.",
+  "Filling every orifice with butterscotch pudding.",
+  "Fisting.",
+  "Flying robots that kill people.",
+  "Gay aliens",
+  "Getting your dick stuck in a Chinese finger trap with another dick.",
+  "Girls that always be textin'.",
+  "Going around punching people.",
+  "Having sex on top of a pizza.",
+  "Having shotguns for legs.",
+  "Indescribable loneliness.",
+  "Jumping out at people.",
+  "Letting everyone down.",
+  "Mufasa's death scene.",
+  "My manservant, Claude.",
+  "Not contributing to society in any meaningful way.",
+  "Not having sex.",
+  "Nothing.",
+  "Putting an entire peanut butter and jelly sandwich into the VCR.",
+  "Reverse cowgirl.",
+  "Roland the Farter, flatulist to the king.",
+  "Running naked through a mall, pissing and shitting everywhere.",
+  "Samuel L. Jackson.",
+  "Screaming like a maniac.",
+  "Self-flagellation.",
+  "Shutting the fuck up.",
+  "Slapping a racist old lady.",
+  "Sneezing, farting, and coming at the same time."
+  "Some douche with an acoustic guitar.",
+  "Some kind of bird-man.",
+  "Spending lots of money.",
+  "That ass.",
+  "The Harlem Globetrotters.",
+  "The Land of Chocolate.",
+  "The Qeusadilla Explosion Salad from Chili's.",
+  "The entire Internet.",
+  "The moist, demanding chasm of his mouth.",
+  "The primal, ball-slapping sex your parents are having right now.",
+  "The systematic destruction of an entire people and their way of life.",
+  "The thin veneer of sitautional causality that underlies porn.",
+  "The way white people is.",
+  "Three months in the hole.",
+  "Unlimited soup, salad, and breadsticks.",
+  "Velcro",
+  "Vietnam flashbacks.",
+  "Vomiting mid-blowjob.",
+  "Warm, velvety muppet sex"
 ]

 module.exports = (robot) ->


Don't send a message until the previous one finishes, guaranteeing output order same as input.

--- src/hubot/campfire.coffee
+++ src/hubot/campfire.coffee
@@ -4,12 +4,13 @@

 class Campfire extends Robot
   send: (user, strings...) ->
-    for str in strings
-      @bot.Room(user.room).speak str, (err, data) ->
+    if strings.length > 0
+      @bot.Room(user.room).speak strings.shift(), (err, data) =>
         console.log "campfire error: #{err}" if err
+        @send user, strings...

   reply: (user, strings...) ->
-    @send user, "#{user.name}: #{str}" for str in strings
+    @send user, strings.map((str) -> "#{user.name}: #{str}")...

   run: ->
     self = @


Cosmetic change.

Cosmetic change.

--- app/javascripts/services.coffee
+++ app/javascripts/services.coffee
@@ -455,7 +455,7 @@
     else
       value.toFixed(2)

-  # Makes the intensity chart  configuration for the given session.
+  # Makes the intensity chart configuration for the given session.
   #
   # The result includes two data series, Scan and Realigned.
   # The chart x-axis labels are the one-based series indexes,


fix issue about option restore

fix issue about option restore

--- coffee/controllers/optionCtrl.coffee
+++ coffee/controllers/optionCtrl.coffee
@@ -8,7 +8,7 @@
   ###
   option.getOptions (options) ->
     $scope.options = options or {}
-    analytics.setPermittion options.reportUsage
+    analytics.setPermittion $scope.options.reportUsage

     # start watch changing.
     $scope.$watch 'options.reportUsage', (newVal, oldVal) ->


Remove tutorial and hide marks buttons

Remove tutorial and hide marks buttons

--- app/classify/subject-tools.cjsx
+++ app/classify/subject-tools.cjsx
@@ -7,17 +7,4 @@
   render: ->

       {<Favourite project={@props.project} api={@props.api} subject={@props.subject} /> if @props.subject? && @props.user?}
-
-        <input type="checkbox" name="hide-old-marks" />
-
-
-          Hide old marks
-
-
-      <button name="restart-tutorial">
-
-
-          Restart tutorial
-
-      </button>


generalize "Scientist" role by changing to "Researcher" (resolves #1508)

--- app/pages/project/research.cjsx
+++ app/pages/project/research.cjsx
@@ -11,7 +11,7 @@
     owner: 'Owner'
     collaborator: 'Collaborator'
     translator: 'Translator'
-    scientist: 'Scientist'
+    scientist: 'Researcher'
     moderator: 'Moderator'
     tester: 'Tester'
     expert: 'Expert'


Collaboration: Handle editor changes in syncChange.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -795,7 +795,28 @@
     map        = @rtm.getFromModel @realTimeDoc, "#{nickname}Snapshot"
     changes    = @rtm.getFromModel @realTimeDoc, 'changes'

+
     if change.origin is nickname
+
+      if context.paneType is 'editor'
+
+        if change.type is 'NewPaneCreated'
+
+          {content, path} = context.file
+
+          string = @rtm.getFromModel @realTimeDoc, path
+
+          unless string
+            @rtm.create 'string', @realTimeDoc, path, content
+
+        else if change.type is 'ContentChange'
+          {content, path} = context.file
+          string = @rtm.getFromModel @realTimeDoc, path
+          string.setText content
+
+        delete context.file?.content?
+
+
       changes.push change

     switch change.type


Returned gamevolve.coffee to its former glory

--- client/src/app/gamEvolve.coffee
+++ client/src/app/gamEvolve.coffee
@@ -66,6 +66,7 @@
       serviceData: {}
       assets: {}
       actions: {}
+      processes: {}
       tools: {}
       evaluator: null
       log: GE.logToConsole
@@ -208,72 +209,52 @@

   return false

-# Catches all errors in the function
-# The signals parameter is only used in the "handleSignals" call
-GE.sandboxActionCall = (node, constants, bindings, methodName, signals = [], activeChildren = []) ->
-  action = constants.actions[node.action]
-  childNames = if node.children? then (child.name ? index.toString()) for index, child of node.children else []
-
-  # TODO: compile expressions ahead of time
-  evaluationContext = new GE.EvaluationContext(constants, bindings)
-
-  # Set default paramOptions
+# Set default values in paramDefs
+GE.fillParamDefDefaults = (paramDefs) ->
   # Cannot use normal "for key, value of" loop because cannot handle replace null values
-  for paramName of action.paramDefs
-    if not action.paramDefs[paramName]? then action.paramDefs[paramName] = {}
-    _.defaults action.paramDefs[paramName],
+  for paramName of paramDefs
+    if not paramDefs[paramName]? then paramDefs[paramName] = {}
+    _.defaults paramDefs[paramName],
       direction: "in"
-
+  return paramDefs
+
+# Returns an object mapping parameter namaes to their values
+# Param expressions is an object that contains 'in' and 'out' attributes
+GE.evaluateInputParameters = (evaluationContext, paramDefs, paramExpressions) ->
   evaluatedParams = {}

-  for paramName, paramOptions of action.paramDefs
+  # TODO: don't evaluate output parameters here!
+  for paramName, paramOptions of paramDefs
     # Resolve parameter value. If the layout doesn't specify a value, use the default, it it exists. Otherwise, throw exception for input values
-    if paramOptions.direction in ["in", "inout"] and node.params?.in?[paramName]
-      paramValue = node.params.in[paramName]
-    else if paramOptions.direction in ["out", "inout"] and node.params?.out?[paramName]
-      paramValue = node.params.out[paramName]
+    if paramOptions.direction in ["in", "inout"] and paramExpressions.in?[paramName]
+      paramValue = paramExpressions.in[paramName]
+    else if paramOptions.direction in ["out", "inout"] and paramExpressions.out?[paramName]
+      paramValue = paramExpressions.out[paramName]
     else if paramOptions.default?
       paramValue = paramOptions.default
     else if paramOptions.direction in ["in", "inout"]
-      throw new Error("Missing input parameter value for action: #{node.action}")
+      throw new Error("Missing input parameter value for parameter '#{paramName}'")

     try
       evaluatedParams[paramName] = evaluationContext.evaluateExpression(paramValue)
     catch error
-      throw new Error("Error evaluating the input parameter expression '#{paramValue}' for node '#{node.action}':\n#{error.stack}")
-
-  locals =
-    params: evaluatedParams
-    children: childNames
-    signals: signals
-    activeChildren: activeChildren
-    assets: constants.assets
-    tools: constants.tools
-    log: constants.log
-  try
-    methodResult = action[methodName].apply(locals)
-  catch e
-    # TODO: convert exceptions to error sigals that do not create patches
-    constants.log(GE.logLevels.ERROR, "Calling action #{node.action}.#{methodName} raised an exception #{e}. Input params were #{JSON.stringify(locals.params)}. Children are #{JSON.stringify(locals.children)}.\n#{e.stack}")
-
-  result = new GE.NodeVisitorResult(methodResult)
-
+      throw new Error("Error evaluating the input parameter expression '#{paramValue}' for parameter '#{paramName}':\n#{error.stack}")
+  return evaluatedParams
+
+# Updates the evaluation context by evaluating the output parameter expressions
+# Param expressions is an object that contains 'in' and 'out' attributes
+GE.evaluateOutputParameters = (evaluationContext, paramDefs, paramExpressions, evaluatedParams) ->
   # Only output parameters should be accessible
-  outParams = _.pick(evaluatedParams, (paramName for paramName, paramOptions of action.paramDefs when paramOptions.direction in ["out", "inout"]))
-
-  for paramName, paramValue of node.params.out
+  outParams = _.pick(evaluatedParams, (paramName for paramName, paramOptions of paramDefs when paramOptions.direction in ["out", "inout"]))
+
+  for paramName, paramValue of paramExpressions.out
     try
       outputValue = evaluationContext.evaluateExpression(paramValue, outParams)
     catch error
-      throw new Error("Error evaluating the output parameter value expression '#{paramValue}' for node '#{node.action}':\n#{error.stack}\nOutput params were #{JSON.stringify(outputParams)}.")
+      throw new Error("Error evaluating the output parameter value expression '#{paramValue}' for parameter '#{paramName}':\n#{error.stack}\nOutput params were #{JSON.stringify(outParams)}.")

     [parent, key] = GE.getParentAndKey(evaluationContext, paramName.split("."))
     parent[key] = outputValue
-
-  result.modelPatches = GE.makePatches(constants.modelData, evaluationContext.model)
-  result.servicePatches = GE.makePatches(constants.serviceData, evaluationContext.services)
-
-  return result

 GE.calculateBindingSet = (node, constants, oldBindings) ->
   bindingSet = []
@@ -328,34 +309,75 @@
 GE.visitActionNode = (node, constants, bindings) ->
   if node.action not of constants.actions then throw new Error("Cannot find action '#{node.action}'")

-  if "update" of constants.actions[node.action]
-    result = GE.sandboxActionCall(node, constants, bindings, "update")
+  action = constants.actions[node.action]
+
+  # TODO: compile expressions ahead of time
+  evaluationContext = new GE.EvaluationContext(constants, bindings)
+  GE.fillParamDefDefaults(action.paramDefs)
+  evaluatedParams = GE.evaluateInputParameters(evaluationContext, action.paramDefs, node.params)
+
+  try
+    methodResult = action.update(evaluatedParams, constants.tools, constants.log)
+  catch e
+    # TODO: convert exceptions to error sigals that do not create patches
+    constants.log(GE.logLevels.ERROR, "Calling action #{node.action}.update raised an exception #{e}. Input params were #{JSON.stringify(evaluatedParams)}. Children are #{JSON.stringify(childNames)}.\n#{e.stack}")
+
+  result = new GE.NodeVisitorResult(methodResult)
+
+  GE.evaluateOutputParameters(evaluationContext, action.paramDefs, node.params, evaluatedParams)
+
+  result.modelPatches = GE.makePatches(constants.modelData, evaluationContext.model)
+  result.servicePatches = GE.makePatches(constants.serviceData, evaluationContext.services)
+
+  return result
+
+GE.visitProcessNode = (node, constants, bindings) ->
+  if node.process not of constants.processes then throw new Error("Cannot find process '#{node.process}'")
+
+  process = constants.processes[node.process]
+  childNames = if node.children? then (child.name ? index.toString()) for index, child of node.children else []
+
+  # TODO: compile expressions ahead of time
+  evaluationContext = new GE.EvaluationContext(constants, bindings)
+  GE.fillParamDefDefaults(process.paramDefs)
+  evaluatedParams = GE.evaluateInputParameters(evaluationContext, process.paramDefs, node.params)
+
+  # check which children should be activated
+  if "listActiveChildren" of process
+    try
+      activeChildren = process.listActiveChildren(evaluatedParams, childNames, constants.tools, constants.log)
+      if not _.isArray(activeChildren) then throw new Error("Calling listActiveChildren() on node '#{node.process}' did not return an array")
+    catch e
+      # TODO: convert exceptions to error sigals that do not create patches
+      constants.log(GE.logLevels.ERROR, "Calling process #{node.process}.listActiveChildren raised an exception #{e}. Input params were #{JSON.stringify(evaluatedParams)}. Children are #{JSON.stringify(childNames)}.\n#{e.stack}")
   else
-    result = new GE.NodeVisitorResult()
-
-  if node.children?
-    # check which children should be activated
-    if "listActiveChildren" of constants.actions[node.action]
-      activeChildrenResult = GE.sandboxActionCall(node, constants, bindings, "listActiveChildren")
-      activeChildren = activeChildrenResult.result
-      if not _.isArray(activeChildren) then throw new Error("Calling listActiveChildren() on node '#{node.action}' did not return an array")
-    else
-      # By default, all children are considered active
-      activeChildren = _.range(node.children.length)
-
-    # Continue with children
-    childSignals = new Array(node.children.length)
-    for childIndex in activeChildren
-      childResult = GE.visitNode(node.children[childIndex], constants, bindings)
-      childSignals[childIndex] = childResult.result
-      result = result.appendWith(childResult)
-
-    # Handle signals
-    # TODO: if handler not defined, propogate error signals upwards? How to merge them?
-    if "handleSignals" of constants.actions[node.action]
-      errorResult = GE.sandboxActionCall(node, constants, bindings, "handleSignals", childSignals, activeChildren)
-      result = result.appendWith(errorResult)
-      result.result = errorResult # appendWith() does not affect result
+    # By default, all children are considered active
+    activeChildren = childNames
+
+  result = new GE.NodeVisitorResult()
+
+  # Continue with children
+  childSignals = new Array(node.children.length)
+  for childIndex in activeChildren
+    childResult = GE.visitNode(node.children[childIndex], constants, bindings)
+    childSignals[childIndex] = childResult.result
+    result = result.appendWith(childResult)
+
+  # Handle signals
+  # TODO: if handler not defined, propogate error signals upwards? How to merge them?
+  if "handleSignals" of process
+    try
+      signalsResult = process.handleSignals(evaluatedParams, childNames, activeChildren, childSignals, constants.tools, constants.log)
+
+      GE.evaluateOutputParameters(evaluationContext, process.paramDefs, node.params, evaluatedParams)
+      modelPatches = GE.makePatches(constants.modelData, evaluationContext.model)
+      servicePatches = GE.makePatches(constants.serviceData, evaluationContext.services)
+
+      result = result.appendWith(new GE.NodeVisitorResult(signalsResult, modelPatches, servicePatches))
+      result.result = signalsResult # appendWith() does not affect result
+    catch e
+      # TODO: convert exceptions to error sigals that do not create patches
+      constants.log(GE.logLevels.ERROR, "Calling process #{node.process}.handleSignals raised an exception #{e}. Input params were #{JSON.stringify(evaluatedParams)}. Children are #{JSON.stringify(childNames)}.\n#{e.stack}")

   return result

@@ -392,14 +414,14 @@

 GE.nodeVisitors =
   "action": GE.visitActionNode
+  "process": GE.visitProcessNode
   "foreach": GE.visitForeachNode
   "send": GE.visitSendNode

-# Constants are modelData, assets, actions, and serviceData
+# Constants are modelData, assets, actions, processes and serviceData
 GE.visitNode = (node, constants, bindings = {}) ->
   # TODO: defer action and call execution until whole tree is evaluated?
   # TODO: handle children as object in addition to array
-
   for nodeType, visitor of GE.nodeVisitors
     if nodeType of node
       return visitor(node, constants, bindings)
@@ -408,7 +430,7 @@

   return new GE.NodeVisitorResult()

-# The argument "options" can values for "node", modelData", "assets", "actions", "tools", "services", "serviceConfig", and "evaluator".
+# The argument "options" can values for "node", modelData", "assets", "actions", "processes", "tools", "services", "serviceConfig", and "evaluator".
 # By default, checks the services object for input data, visits the tree given in node, and then provides output data to services.
 # If outputServiceData is not null, the loop is not stepped, and the data is sent directly to the services. In this case, no model patches are returned.
 # Otherwise, if inputServiceData is not null, this data is used instead of asking the services.
@@ -419,6 +441,7 @@
     modelData: {}
     assets: {}
     actions: {}
+    processes: {}
     services: {}
     serviceConfig: {}
     log: null
@@ -438,6 +461,7 @@
       serviceData: options.inputServiceData
       assets: options.assets
       actions: options.actions
+      processes: options.processes
       tools: options.tools
       evaluator: options.evaluator
       log: options.log
@@ -456,13 +480,36 @@

   return modelPatches

-# Compile expression into sanboxed function that will produces an input value
-GE.compileExpression = (expressionText, evaluator) ->
+# Compile expression source into sandboxed function of (model, services, assets, tools, bindings, params)
+GE.compileExpression = (expressionText, evaluator) -> GE.compileSource("return #{expressionText};", evaluator, ["model", "services", "assets", "tools", "bindings", "params"])
+
+# Compile tool source into sandboxed function of args..., "baking in" the "tools" and "log" parameters of "context"
+GE.compileTool = (expressionText, context, args, evaluator) ->
+  source = """
+    return function(#{args.join(', ')}) {
+      var tools = context.tools;
+      var log = context.log;
+      #{expressionText}
+    };
+  """
+  return GE.compileSource(source, evaluator, ["context"])
+
+# Compile action.update() source into sandboxed function of (params, tools, log)
+GE.compileUpdate = (expressionText, evaluator) -> GE.compileSource(expressionText, evaluator, ["params", "tools", "log"])
+
+# Compile action listActiveChildren source into sandboxed function of (params, children, tools, log)
+GE.compileListActiveChildren = (expressionText, evaluator) -> GE.compileSource(expressionText, evaluator, ["params", "children", "tools", "log"])
+
+# Compile action handleSignals source into sandboxed function of (params, children, activeChildren, signals, tools, log)
+GE.compileHandleSignals = (expressionText, evaluator) -> GE.compileSource(expressionText, evaluator, ["params", "children", "activeChildren", "signals", "tools", "log"])
+
+# Compile source into sandboxed function of params
+GE.compileSource = (expressionText, evaluator, params) ->
   # Parentheses are needed around function because of strange JavaScript syntax rules
   # TODO: use "new Function" instead of eval?
   # TODO: add "use strict"?
   # TODO: detect errors
-  functionText = "(function(model, services, assets, tools, bindings, params) { return #{expressionText}; })"
+  functionText = "(function(#{params.join(', ')}) {\n#{expressionText}\n})"
   expressionFunc = evaluator(functionText)
   if typeof(expressionFunc) isnt "function" then throw new Error("Expression does not evaluate as a function")
   return expressionFunc
@@ -477,6 +524,7 @@

 # Load all the assets in the given object (name: url) and then call callback with the results, or error
 # TODO: have cache-busting be configurable
+# TODO: use promises rather than a counter
 GE.loadAssets = (assets, callback) ->
   if _.size(assets) == 0 then return callback(null, {})


add perftest for holdWhen

add perftest for holdWhen

--- performance/PerformanceTest.coffee
+++ performance/PerformanceTest.coffee
@@ -1,3 +1,4 @@
+#!/usr/bin/env coffee
 Benchmark = require('benchmark')
 Bacon = (require "../dist/Bacon").Bacon

@@ -87,6 +88,9 @@
   'EventStream.toProperty': ->
     f.withGenerator (gen) ->
       gen.stream().toProperty()
+  'EventStream.holdWhen': ->
+    f.withGenerator (gen) ->
+      gen.stream().holdWhen(gen.stream().map(false))
 }

 includeCase = (key) ->


increase lock time to 5 minutes

increase lock time to 5 minutes

--- app/coffee/LockManager.coffee
+++ app/coffee/LockManager.coffee
@@ -5,7 +5,7 @@
 module.exports = LockManager =
 	LOCK_TEST_INTERVAL: 50 # 50ms between each test of the lock
 	MAX_LOCK_WAIT_TIME: 10000 # 10s maximum time to spend trying to get the lock
-	LOCK_TTL: 10 # seconds
+	LOCK_TTL: 300 # seconds (allow 5 minutes for any operation to complete)

 	tryLock : (key, callback = (err, gotLock) ->) ->
 		rclient.set key, "locked", "EX", @LOCK_TTL, "NX", (err, gotLock)->


Fixed a single QUnit test for marking dead stones. It previously had an incorrect input SGF. I have also made the white point at [bc] rather than [cc] because that means the coordinates are not the same so the test is more likely to fail if somewhere in the code we have erroneously written pointAt y, x instead of pointAt x, y. I have tested this to make sure that it works in at least one place where pointAt is called.

--- app/coffee/tests/tests.coffee
+++ app/coffee/tests/tests.coffee
@@ -319,7 +319,7 @@
   assert.ok isPointWhiteScore($pointAt 1, 1), "(1, 1) scores for White"

 test "Form is updated with current dead stones", (assert) ->
-  setInputSgf '(;SZ[3];AB[aa][ac][bb][ac][bc]AW[cc])'
+  setInputSgf '(;SZ[3];AB[aa][ca][bb][ac][ab]AW[cb])'
   # b.b
   # .b.
   # bbw
@@ -338,8 +338,8 @@
   assert.notOk actual.match(tbPattern), "no TB property in SGF"

   # here we check for a specific coord
-  $pointAt(2, 2).click()
-  expected = /TB[\]\[\w]*\[cc]/  # TB, any number of [] and letters, [cc]
+  $pointAt(2, 1).click()
+  expected = /TB[\]\[\w]*\[bc]/  # TB, any number of [] and letters, [cc]
   actual = $('input#response').val()
   assert.ok actual.match(expected), "dead white stone found as black territory"


Dont propagate fold icon clicks to editor component

--- src/gutter-component.coffee
+++ src/gutter-component.coffee
@@ -21,7 +21,7 @@
     if gutterBackgroundColor isnt 'rbga(0, 0, 0, 0)'
       backgroundColor = gutterBackgroundColor

-    div className: 'gutter', onClick: @onClick, onMouseDown: onMouseDown,
+    div className: 'gutter', onClick: @onClick, onMouseDown: @onMouseDown,
       div className: 'line-numbers', ref: 'lineNumbers', style:
         height: Math.max(scrollHeight, scrollViewHeight)
         WebkitTransform: @getTransform()
@@ -215,6 +215,14 @@
   lineNumberNodeForScreenRow: (screenRow) ->
     @lineNumberNodesById[@lineNumberIdsByScreenRow[screenRow]]

+  onMouseDown: (event) ->
+    {editor} = @props
+    {target} = event
+    lineNumber = target.parentNode
+
+    unless target.classList.contains('icon-right') and lineNumber.classList.contains('foldable')
+      @props.onMouseDown(event)
+
   onClick: (event) ->
     {editor} = @props
     {target} = event


[Gutter] Remove emissary usage

[Gutter] Remove emissary usage

--- src/gutter-container.coffee
+++ src/gutter-container.coffee
@@ -1,12 +1,10 @@
 {Emitter} = require 'event-kit'
-{Subscriber} = require 'emissary'
 Gutter = require './gutter'

 # This class encapsulates the logic for adding and modifying a set of gutters.

 module.exports =
 class GutterContainer
-  Subscriber.includeInto(this)

   # * `textEditor` The {TextEditor} to which this {GutterContainer} belongs.
   constructor: (textEditor) ->
@@ -17,7 +15,6 @@
   destroy: ->
     @gutters = null
     @emitter.dispose()
-    @unsubscribe()

   # Creates and returns a {Gutter}.
   # * `options` An {Object} with the following fields:
@@ -84,7 +81,6 @@
     index = @gutters.indexOf(gutter)
     if index > -1
       @gutters.splice(index, 1)
-      @unsubscribe gutter
       @emitter.emit 'did-remove-gutter', gutter.name
     else
       throw new Error 'The given gutter cannot be removed because it is not ' +


timer added

timer added

--- app/modules/dbHandling.coffee
+++ app/modules/dbHandling.coffee
@@ -118,6 +118,7 @@
           if err
             console.log(err)
         )
+
         break
     if !assignExists
       fs.unlink(filePath,(err)->
@@ -245,7 +246,7 @@
       elementPos = doc.assignments.map((x) ->
         x.assignmentName
       ).indexOf(assignmentName)
-      doc.assignments[elementPos].timeSpentOnAssign = time
+      doc.assignments[elementPos].timeSpentOnAssign = formatSeconds(time)
       doc.markModified('assignments')
       doc.save()
       callback doc


Add more snippets

Add more snippets

--- snippets/ember.cson
+++ snippets/ember.cson
@@ -12,89 +12,149 @@
   #

   'Ember Route':
-    'prefix': 'ember-route'
-    'body': """
-      import Ember from 'ember';
-
-      /**
-       * ${1:[description]}
-       * @type {Ember.Route}
-       */
+    'prefix': 'route'
+    'body': """
+      import Ember from 'ember';
+
       export default Ember.Route.extend({
         model: function() {
-          ${2:// body}
+          ${1:// body}
         },
       });
     """

   'Ember Object Controller':
-    'prefix': 'ember-object-controller'
-    'body': """
-      import Ember from 'ember';
-
-      /**
-       * ${1:[description]}
-       * @type {Ember.ObjectController}
-       */
+    'prefix': 'controller'
+    'body': """
+      import Ember from 'ember';
+
       export default Ember.ObjectController.extend({
+        ${1:// body}
+      });
+    """
+
+  'Ember Array Controller':
+    'prefix': 'arr-controller'
+    'body': """
+      import Ember from 'ember';
+
+      export default Ember.ArrayController.extend({
+        ${1:// body}
+      });
+    """
+
+  'Ember Component':
+    'prefix': 'component'
+    'body': """
+      import Ember from 'ember';
+
+      export default Ember.Component.extend({
+        ${1:// body}
+      });
+    """
+
+  'Ember Mixin':
+    'prefix': 'mixin'
+    'body': """
+      import Ember from 'ember';
+
+      export default Ember.Mixin.create({
+        ${1:// body}
+      });
+    """
+
+  'Ember Generic Extend':
+    'prefix': 'extend'
+    'body': """
+      import Ember from 'ember';
+
+      export default Ember.${1:Class}.extend({
         ${2:// body}
       });
     """

-  'Ember Array Controller':
-    'prefix': 'ember-array-controller'
-    'body': """
-      import Ember from 'ember';
-
-      /**
-       * ${1:[description]}
-       * @type {Ember.ArrayController}
-       */
-      export default Ember.ArrayController.extend({
-        ${2:// body}
+  'Ember Helper':
+    'prefix': 'helper'
+    'body': """
+      import Ember from 'ember';
+
+      export default Ember.Handlebars.makeBoundHelper(function() {
+        ${1:// body}
+      });
+    """
+
+  'Ember View':
+    'prefix': 'view'
+    'body': """
+      import Ember from 'ember';
+
+      var Object = Ember.View.extend({
+        ${1:// body}
       });
     """

   'Ember Model':
-    'prefix': 'ember-model'
-    'body': """
-      import DS from 'DS';
-
-      /**
-       * ${1:[description]}
-       * @type {Ember.Model}
-       */
+    'prefix': 'model'
+    'body': """
+      import DS from 'DS';
+
       export default DS.Model.extend({
-        ${2:// body}
-      });
-    """
-
-  'Ember Generic Extend':
-    'prefix': 'ember-extend'
-    'body': """
-      import Ember from 'ember';
-
-      /**
-       * ${1:[description]}
-       * @type {Ember.{2:Class}}
-       */
-      export default Ember.${2:Class}.extend({
-        ${3:// body}
-      });
-    """
-
-  'Ember Helper':
-    'prefix': 'ember-helper'
-    'body': """
-      import Ember from 'ember';
-
-      export default Ember.Handlebars.makeBoundHelper(function() {
+        ${1:// body}
+      });
+    """
+
+  'Ember REST Adapter':
+    'prefix': 'adapter'
+    'body': """
+      import DS from 'DS';
+
+      export default DS.RESTAdapter.extend({
+        ${1:// body}
+      });
+    """
+
+  'Ember Active Model Adapter':
+    'prefix': 'active-adapter'
+    'body': """
+      import DS from 'DS';
+
+      export default DS.ActiveModelAdapter.extend({
+        ${1:// body}
+      });
+    """
+
+  'Ember REST Serializer':
+    'prefix': 'serializer'
+    'body': """
+      import DS from 'DS';
+
+      export default DS.RESTSerializer.extend({
+        ${1:// body}
+      });
+    """
+
+  'Ember Active Model Serializer':
+    'prefix': 'active-serializer'
+    'body': """
+      import DS from 'DS';
+
+      export default DS.ActiveModelSerializer.extend({
+        ${1:// body}
+      });
+    """
+
+  'Ember Transform Serializer':
+    'prefix': 'transform'
+    'body': """
+      import DS from 'DS';
+
+      export default DS.Transforms.extend({
         ${1:// body}
       });
     """

   'Ember Initializer':
-    'prefix': 'ember-initializer'
+    'prefix': 'initializer'
     'body': """
       export default {
         name: '${1:name}',
@@ -105,7 +165,7 @@
     """

   'Ember Utility':
-    'prefix': 'ember-utility'
+    'prefix': 'utility'
     'body': """
       export default function ${1:myUtility}() {
         ${2:// body}
@@ -117,7 +177,7 @@
   #

   'Ember Computed Property':
-    'prefix': 'ember-computed-property'
+    'prefix': 'property'
     'body': """
       ${1:fooCount}: function() {
         ${3:// body}
@@ -125,7 +185,7 @@
     """

   'Ember Observer':
-    'prefix': 'ember-computed-property'
+    'prefix': 'observes'
     'body': """
       ${1:fooChanged}: function() {
         ${3:// body}
@@ -133,7 +193,7 @@
     """

   'Ember Actions':
-    'prefix': 'ember-actions'
+    'prefix': 'actions'
     'body': """
       actions: {
         ${1:// body}
@@ -145,9 +205,29 @@
   #

   'Ember Find':
-    'prefix': 'ember-store-find'
+    'prefix': 'find'
     'body': "this.store.find('${1:model}');"

   'Ember Find by id':
-    'prefix': 'ember-store-find-id'
+    'prefix': 'find-id'
     'body': "this.store.find('${1:model}', params.${1:model}_id);"
+
+  'Ember Logger Debug':
+    'prefix': 'logger-debug'
+    'body': "Ember.Logger.debug('${1:message}');"
+
+  'Ember Logger Log':
+    'prefix': 'logger-log'
+    'body': "Ember.Logger.log('${1:message}');"
+
+  'Ember Logger Info':
+    'prefix': 'logger-info'
+    'body': "Ember.Logger.info('${1:message}');"
+
+  'Ember Logger Warn':
+    'prefix': 'logger-warn'
+    'body': "Ember.Logger.warn('${1:message}');"
+
+  'Ember Logger Error':
+    'prefix': 'logger-error'
+    'body': "Ember.Logger.error('${1:message}');"


ListView: Remove unregisterItem deletes item from listView tests

--- test/components/list/listviewcontroller.test.coffee
+++ test/components/list/listviewcontroller.test.coffee
@@ -288,42 +288,6 @@

         assert.isDefined viewController._e['UnregisteringItem']

-
-    context 'when it is lastToFirst', ->
-
-      it 'removes item from view list', ->
-
-        viewController = new KDListViewController
-          itemClass    : KDListItemView
-          lastToFirst  : yes
-
-        for i in [0...5]
-          viewController.addItem {title: Date.now(), id: i}, i
-
-        item = viewController.getListItems()[1]
-        viewController.unregisterItem item, 1
-
-        itemViews = viewController.getListItems().map (item) -> item.data.id
-        assert.ok _.isEqual itemViews, [0, 1, 2, 4]
-
-    context 'when it is not lastToFirst', ->
-
-      it 'removes item from view list', ->
-
-        viewController = new KDListViewController
-          itemClass    : KDListItemView
-          lastToFirst  : no
-
-        for i in [0...5]
-          viewController.addItem {title: Date.now(), id: i}, i
-
-        item = viewController.getListItems()[1]
-        viewController.unregisterItem item, 1
-
-        itemViews = viewController.getListItems().map (item) -> item.data.id
-
-        assert.deepEqual itemViews, [0, 2, 3, 4]
-
     context 'if there is data on the instance', ->

       it 'removes item from itemsIndexed', ->


update comment

update comment

--- src/lib/collection.coffee
+++ src/lib/collection.coffee
@@ -197,7 +197,7 @@
     ###*
     get item model
     @method getItemModelClass
-    @return {BaseModel}
+    @return {Function}
     ###
     getItemModelClass: ->
         @root.getModel(@constructor.itemModelName)


match trap on word boundaries only

match trap on word boundaries only

--- scripts/ackbar.coffee
+++ scripts/ackbar.coffee
@@ -18,5 +18,5 @@
 ]

 module.exports = (robot) ->
-  robot.hear /.*(trap).*/i, (msg) ->
+  robot.hear /.*\btrap\b.*/i, (msg) ->
     msg.send msg.random ackbars


Revert "Start loading albums earlier"

Revert "Start loading albums earlier"

This reverts commit f50727737a14e704fd9a7752dc411aa1c2f47c7a.

--- scripts/albums.coffee
+++ scripts/albums.coffee
@@ -198,7 +198,7 @@
     checkLoad = () ->
         return if itemsLoaded == window.files.length
         return if isLoading
-        if lastItemY - getScroll() < (viewPortHeight() / 2)
+        if lastItemY - getScroll() < viewPortHeight()
             isLoading = true
             document.querySelector('.progress').classList.remove('hidden')
             nextItem = window.files[itemsLoaded]


Minor edits

Minor edits

--- app/pages/about/about-home.cjsx
+++ app/pages/about/about-home.cjsx
@@ -8,7 +8,7 @@
    pageContent: '''
     ## What is the Zooniverse?

-    The Zooniverse is the world’s largest and most popular platform for people-powered research. This research is      made possible by volunteers—hundreds of thousands of people around the world who come together to assist           professional researchers. Our goal is to enable research that would not be possible, or practical, otherwise.      Zooniverse research results in new discoveries, datasets useful to the wider research community, and [many         publications](./#/about/publications).
+    The Zooniverse is the world’s largest and most popular platform for people-powered research. This research is made possible by volunteers—hundreds of thousands of people around the world who come together to assist professional researchers. Our goal is to enable research that would not be possible, or practical, otherwise. Zooniverse research results in new discoveries, datasets useful to the wider research community, and [many publications](./#/about/publications).

     ### At the Zooniverse, anyone can be a researcher


enhance: title for appknox and devknox

enhance: title for appknox and devknox

--- app/routes/authenticated.coffee
+++ app/routes/authenticated.coffee
@@ -2,6 +2,7 @@
 `import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';`
 `import ENV from 'irene/config/environment';`
 `import ENUMS from 'irene/enums'`
+`import config from 'irene/config/environment';`

 location = window.location

@@ -12,7 +13,12 @@
 AuthenticatedRoute = Ember.Route.extend AuthenticatedRouteMixin,

   title: (tokens) ->
-    tokens.join(' - ') + ' - Appknox'
+    Platform =
+      if config.isAppknox
+        " - Appknox"
+      else if config.isDevknox
+        " - Devknox"
+    tokens.join(' - ') + Platform

   lastTransition: null
   i18n: service()


Make sure pug plugin is backwards compatible with old sites expecting ‘jade’

--- src/plugins/pug.coffee
+++ src/plugins/pug.coffee
@@ -29,5 +29,5 @@
           callback error
     ], callback

-  env.registerTemplatePlugin '**/*.pug', PugTemplate
+  env.registerTemplatePlugin '**/*.*(pug|jade)', PugTemplate
   callback()


Fix for CoffeeScript 1.6.3 semantics

Fix for CoffeeScript 1.6.3 semantics

--- scripts/delete.coffee
+++ scripts/delete.coffee
@@ -58,8 +58,8 @@
     # console.log {age: age, token: token, channel: channel, robotId: robotId, you: you}

     loadRecentMessages(robot.http, token, channel, robotId)
-      .then (messages) -> selectMessage messages, age
-      .then (message) -> deleteMessage robot.http, token, channel, message
-      .then () -> msg.reply "Whoops, sorry about that!"
-      .catch (err) -> msg.reply err
+      .then((messages) -> selectMessage messages, age)
+      .then((message) -> deleteMessage robot.http, token, channel, message)
+      .then(() -> msg.reply "Whoops, sorry about that!")
+      .catch((err) -> msg.reply err)
       .done()


extra text for doc

extra text for doc

--- scripts/dr-mat.coffee
+++ scripts/dr-mat.coffee
@@ -47,4 +47,10 @@
     msg.send msg.random(MATT_QUOTE)

   robot.respond /awake your brothers/i, (msg)->
-    msg.send "https://secure-anchorage-54524.herokuapp.com/"+    msg.send "https://secure-anchorage-54524.herokuapp.com/"
+
+  robot.respond /my glass is empty/i, (msg)->
+    msg.send "so is mine ;p"
+
+  robot.respond /my glass is very empty/i, (msg)->
+    msg.send "Why don't you fetch something for you an me"


add 'javascript-mode' and 'coffeescript-mode'

--- scripts/editor.coffee
+++ scripts/editor.coffee
@@ -1,10 +1,12 @@
   $source = $('#source')
   $results = $('#results')
   source = CodeMirror.fromTextArea(document.getElementById('source'), {
+    mode: 'coffeescript',
     matchBrackets: true,
     smartIndent: false
   })
   results = CodeMirror.fromTextArea document.getElementById('results'), {
+    mode: 'javascript',
     matchBrackets: true,
     smartIndent: false
   }


Make components and promises immutable on container

--- source/index.coffee
+++ source/index.coffee
@@ -171,14 +171,17 @@
   class Container

     constructor: (properties = {}) ->
-      @components = new Components properties.components
-      @promises = new Promises properties.promises
-
-      @components.addListener "set", (event) =>
-        @promises.remove event.payload.key
-
-      @components.addListener "remove", (event) =>
-        @promises.remove event.payload.key
+      _components = new Components properties.components
+      _promises = new Promises properties.promises
+
+      _components.addListener "set", (event) ->
+        _promises.remove event.payload.key
+
+      _components.addListener "remove", (event) ->
+        _promises.remove event.payload.key
+
+      $.defineProperty @, "components", enumerable: true, get: -> _components
+      $.defineProperty @, "promises", enumerable: true, get: -> _promises

     createComponentPromise: (name) ->
       $.createPromise (resolve, reject) =>


regexp thing to try to make google search work

--- scripts/google.coffee
+++ scripts/google.coffee
@@ -11,5 +11,5 @@
   msg.http('http://www.google.com/search')
     .query(q: query)
     .get() (err, res, body) ->
-      cb body.match(//)?[1] || "Sorry, Google had zero results for '#{query}'"
+      cb body.match(/<a href="([^"]*)" class=l/)?[1] || "Sorry, Google had zero results for '#{query}'"


dont send a report if there is nothing to report

--- app/report.coffee
+++ app/report.coffee
@@ -9,26 +9,27 @@
     @config()

   post: (beacons, callback) ->
-    request.post(
-      HOST,
-      { json: @transform(beacons) },
-      (error, response, body) ->
-        if !error && response.statusCode == 200
-          console.log "post complete."
-        else
-          callback(error, body)
-    )
+    if @beaconData(beacons).length > 0
+      request.post(
+        HOST,
+        { json: @transform(beacons) },
+        (error, response, body) ->
+          if !error && response.statusCode == 200
+            console.log "post complete."
+          else
+            callback(error, body)
+      )

-  transform: (beacons) ->
-    beaconData = (for id, beacon of beacons
+  beaconData: (beacons) ->
+    (for id, beacon of beacons
       { uuid: beacon.id(),
       exits: beacon.counter,
       battery: beacon.batteryLevel } if beacon.counter > 0 )

-    console.log beaconData
+  transform: (beacons) ->
     { mac: ID,
     timestamp: (new Date()),
-    beacons: beaconData }
+    beacons: @beaconData(beacons) }

   config: () ->
     fs.readFile './config.json', 'utf-8', (err, data) ->


Fix previous fix -_-

Fix previous fix -_-

--- app/src/utils/formatters.coffee
+++ app/src/utils/formatters.coffee
@@ -55,7 +55,7 @@
     integerPart = []
     for digit, index in reverseIntegerPart
       integerPart.push digit
-      integerPart.push thousandSeparator if (index + 1) % 3 == 0 and (index + 1) reverseIntegerPart.length
+      integerPart.push thousandSeparator if (index + 1) % 3 == 0 and (index + 1) < reverseIntegerPart.length
     value = integerPart.reverse().join('') + decimalSeparator + fractionalPart
     # remove . if necessary
     if _.str.endsWith value, decimalSeparator


Do not init Menu lazily, otherwise empty menus would be invalid.

--- atom/browser/api/lib/menu.coffee
+++ atom/browser/api/lib/menu.coffee
@@ -26,6 +26,25 @@
 Menu = bindings.Menu
 Menu::__proto__ = EventEmitter.prototype

+Menu::_init = ->
+  @commandsMap = {}
+  @groupsMap = {}
+  @items = []
+  @delegate =
+    isCommandIdChecked: (commandId) => @commandsMap[commandId]?.checked
+    isCommandIdEnabled: (commandId) => @commandsMap[commandId]?.enabled
+    isCommandIdVisible: (commandId) => @commandsMap[commandId]?.visible
+    getAcceleratorForCommandId: (commandId) => @commandsMap[commandId]?.accelerator
+    executeCommand: (commandId) => @commandsMap[commandId]?.click()
+    menuWillShow: =>
+      # Make sure radio groups have at least one menu item seleted.
+      for id, group of @groupsMap
+        checked = false
+        for radioItem in group when radioItem.checked
+          checked = true
+          break
+        v8Util.setHiddenValue group[0], 'checked', true unless checked
+
 Menu::popup = (window) ->
   throw new TypeError('Invalid window') unless window?.constructor is BrowserWindow
   @_popup window
@@ -35,26 +54,6 @@

 Menu::insert = (pos, item) ->
   throw new TypeError('Invalid item') unless item?.constructor is MenuItem
-
-  # Create delegate.
-  unless @delegate?
-    @commandsMap = {}
-    @groupsMap = {}
-    @items = []
-    @delegate =
-      isCommandIdChecked: (commandId) => @commandsMap[commandId]?.checked
-      isCommandIdEnabled: (commandId) => @commandsMap[commandId]?.enabled
-      isCommandIdVisible: (commandId) => @commandsMap[commandId]?.visible
-      getAcceleratorForCommandId: (commandId) => @commandsMap[commandId]?.accelerator
-      executeCommand: (commandId) => @commandsMap[commandId]?.click()
-      menuWillShow: =>
-        # Make sure radio groups have at least one menu item seleted.
-        for id, group of @groupsMap
-          checked = false
-          for radioItem in group when radioItem.checked
-            checked = true
-            break
-          v8Util.setHiddenValue group[0], 'checked', true unless checked

   switch item.type
     when 'normal' then @insertItem pos, item.commandId, item.label


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/matrix.coffee
+++ src/matrix.coffee
@@ -186,7 +186,8 @@
     @name skew
     @methodOf Matrix#
     @see Matrix.skew
-    @param {Number} sx
+    @param {Number} skewX
+    @param {Number}
     ###
     skew: (skewX, skewY) ->
       @concat(Matrix.skew(skewX, skewY))


removed images

removed images

--- scripts/trello.coffee
+++ scripts/trello.coffee
@@ -72,23 +72,24 @@
     ###unless process.env.TRELLO_LIST_ID?
       msg.send "Please specify the Trello List ID in TRELLO_LIST_ID"
       return###
+    ignore = url.match(/\.(png|jpg|jpeg|gif|txt|zip|tar\.bz|js|css)/)
+    unless ignore
+      jsdom = require 'jsdom'
+      jsdom.env(
+        html: msg.match[0]
+        scripts: [
+          'http://code.jquery.com/jquery-1.7.2.min.js'
+        ]
+        done: (errors, window) ->
+          unless errors
+            $ = window.$
+            title = $('title').text()

-    jsdom = require 'jsdom'
-    jsdom.env(
-      html: msg.match[0]
-      scripts: [
-        'http://code.jquery.com/jquery-1.7.2.min.js'
-      ]
-      done: (errors, window) ->
-        unless errors
-          $ = window.$
-          title = $('title').text()
+            if title
+              url = url + ' [' + title + ']'

-          if title
-            url = url + ' [' + title + ']'
-
-          trelloIt url, '510acfdfeca4af654e004e84', msg
-    )
+            trelloIt url, '510acfdfeca4af654e004e84', msg
+      )

 trelloIt = (subject, list, msg) ->
   Trello = require("node-trello");


Looks better with only one settings title

--- components/dialogs/EditSettings.cjsx
+++ components/dialogs/EditSettings.cjsx
@@ -22,11 +22,10 @@
     render: ->
         <Modal show={@props.show} onHide={@props.onHide}>
             <Modal.Header closeButton>
-                <Modal.Title>Settings</Modal.Title>
+                <Modal.Title>Edit settings</Modal.Title>
             </Modal.Header>
             <form onSubmit={@handleSubmit}>
                 <Modal.Body>
-
Edit settings

                     <FormGroup validationState={@getValidationState()}>
                         <ControlLabel>osu!api key</ControlLabel>
                         <FormControl


Events: Fixed typo.

Events: Fixed typo.

--- engine/events.coffee
+++ engine/events.coffee
@@ -77,7 +77,7 @@

         touch_start = (event)=>
             event.preventDefault()
-            for t in event.target.touches
+            for t in event.targetTouches
                 touch = {}
                 touch.touching = true
                 touch.id = t.identifier
@@ -96,7 +96,7 @@
                 touch.movement_since_touch = 0
                 touch.touch_target = touch.target = t.target
                 @touch.touch_events[touch.id] = touch
-            @touch.touches = event.target.touches.length
+            @touch.touches = event.targetTouches.length

         if enable_touch
             root_element.addEventListener 'touchstart', touch_start, false
@@ -105,7 +105,7 @@
             event.preventDefault()
             for touch in @touch.touch_events
                 touch.touching = 0
-            for t in event.target.touches
+            for t in event.targetTouches
                 touch = @touch.touch_events[t.identifier]
                 touch.touching = true
                 touch.force = 0
@@ -113,7 +113,7 @@
                 touch.radius_y = 0
                 touch.rel_x = 0
                 touch.rel_y = 0
-            @touch.touches = event.target.touches.length
+            @touch.touches = event.targetTouches.length

         if enable_touch
             root_element.addEventListener 'touchend', touch_end, false
@@ -123,7 +123,7 @@
         touch_move = (event)=>
             event.preventDefault()

-            for t in event.target.touches
+            for t in event.targetTouches
                 touch = {}
                 touch.id = t.identifier
                 touch.touching = true
@@ -147,7 +147,7 @@
                     touch.rel_y = 0
                 touch.movement_since_touch = Math.abs(touch.rel_x) + Math.abs(touch.rel_y)
                 @touch.touch_events[touch.id] = touch
-            @touch.touches = event.target.touches.length
+            @touch.touches = event.targetTouches.length

         if enable_touch
             root_element.addEventListener 'touchmove', touch_move, false


Atom config update.

Atom config update.

--- env/atom.config.cson
+++ env/atom.config.cson
@@ -34,10 +34,18 @@
 		autoIndentOnPaste: false
 		fontSize: 17
 		invisibles:
-			{}
+			cr: "↩"
+			eol: " "
+			tab: "⇥"
+		preferredLineLength: 120
+		scrollPastEnd: true
 		showIndentGuide: true
 		showInvisibles: true
 	"event-watch":
+		notifications: [
+			"N"
+			"S"
+		]
 		schedules:
 			N: "at 7:03am and 7:34am and 8:05am and 8:41am and 9:15am and 9:44am and 10:15am and 11:15am and 12:15am and  1:15pm and 2:15pm and 3:15pm and 3:44pm and 4:19pm and 4:55pm and 5:27pm and 5:57pm and 6:30pm on Mon,Tue,Wed,Thu,Fri also at 7:30pm and 8:30pm and 9:30pm and 10:30pm and 11:30pm on Fri also at 12:30am on Sat also at 4:41pm and 5:15pm and 5:15pm and 5:49pm and 6:23pm and 6:57pm and 7:31pm and 8:05pm and 8:39pm and 9:13pm and 9:47pm and 10:21pm and 10:55pm and 11:29pm on Sat also at 12:03am on Sun"
 			S: "at 6:24am and 6:56am and 7:27am and 7:58am and 8:34am and 9:09am and  9:38am and 10:38am and 11:38am and 12:38am and 1:38pm and 2:38pm and 3:09pm and 3:38pm and 4:13pm and 4:43pm and 5:19pm and 5:51pm on Mon,Tue,Wed,Thu,Fri also at 6:53pm and 7:53pm and 8:24pm and 9:24pm and 10:24pm and 11:24pm on Fri also at 4:00pm and 4:34pm and 5:08pm and 5:42pm and 6:16pm and 6:50pm and 7:24pm and 7:58pm and 8:32pm and 9:06pm and 9:40pm and 10:14pm and 10:48pm and 11:22pm on Sat"
@@ -106,7 +114,6 @@
 		]
 	"tab-control":
 		autoSaveChanges: true
-		displayInStatusBar: true
 	"tree-view":
 		hideIgnoredNames: true
 		hideVcsIgnoredFiles: true
@@ -118,6 +125,12 @@
 "source.css.less":
 	editor:
 		tabLength: 2
+"source.gfm":
+	editor:
+		tabLength: 2
+"source.shell":
+	editor:
+		tabLength: 2
 "source.yaml":
 	editor:
 		tabLength: 2