app = require 'app'
fs = require 'fs'
path = require 'path'
protocol = require 'protocol'

# Handles requests with 'atom' protocol.
#
# It's created by {AtomApplication} upon instantiation, and is used to create a
# custom resource loader by adding the 'atom' custom protocol.
module.exports =
class AtomProtocolHandler
  constructor: (@resourcePath) ->
    @loadPaths = [
      path.join(app.getHomeDir(), '.atom', 'dev', 'packages')
      path.join(app.getHomeDir(), '.atom', 'packages')
      path.join(@resourcePath, 'node_modules')
    ]

    @registerAtomProtocol()

  # Creates the 'atom' custom protocol handler.
  registerAtomProtocol: ->
    protocol.registerProtocol 'atom', (request) =>
      relativePath = path.normalize(request.url.substr(7))
      for loadPath in @loadPaths
        filePath = path.join(loadPath, relativePath)
        break if fs.statSyncNoException(filePath).isFile?()
      return new protocol.RequestFileJob(filePath)

app = require 'app'
fs = require 'fs'
path = require 'path'
protocol = require 'protocol'

# Handles requests with 'atom' protocol.
#
# It's created by {AtomApplication} upon instantiation and is used to create a
# custom resource loader for 'atom://' URLs.
#
# The following directories are searched in order:
#   * ~/.atom/assets
#   * ~/.atom/dev/packages
#   * ~/.atom/packages
#   * RESOURCE_PATH/node_modules
#
module.exports =
class AtomProtocolHandler
  constructor: (@resourcePath) ->
    @loadPaths = [
      path.join(app.getHomeDir(), '.atom', 'dev', 'packages')
      path.join(app.getHomeDir(), '.atom', 'packages')
      path.join(@resourcePath, 'node_modules')
    ]

    @registerAtomProtocol()

  # Creates the 'atom' custom protocol handler.
  registerAtomProtocol: ->
    protocol.registerProtocol 'atom', (request) =>
      relativePath = path.normalize(request.url.substr(7))

      if relativePath.indexOf('assets/') is 0
        assetsPath = path.join(app.getHomeDir(), '.atom', relativePath)
        if fs.statSyncNoException(assetsPath).isFile?()
          filePath = assetsPath

      unless filePath
        for loadPath in @loadPaths
          filePath = path.join(loadPath, relativePath)
          break if fs.statSyncNoException(filePath).isFile?()

      new protocol.RequestFileJob(filePath)

Load atom://assets/ urls from ~/.atom/assets


Client = require('raven').Client

Client.prototype.captureDocumentError = (req, err) ->
  @captureMessage "#{req.type}: #{err.details[0].message}",
    level: 'warning'
    extra: req: req, err: err

module.exports = new Client process.env.SENTRY_DNS

module.exports.patchGlobal (id, err) ->
  console.error 'Uncaught Exception'
  console.error err.message
  console.error err.stack
  process.exit 1

Client = require('raven').Client

Client.prototype.captureDocumentError = (req, err) ->
  @captureMessage "#{req.type}: #{err.details[0].message}",
    level: 'warning'
    extra: req: req, err: err

module.exports = new Client process.env.SENTRY_DNS

if process.env.NODE_ENV isnt 'development'
  module.exports.patchGlobal (id, err) ->
    console.error 'Uncaught Exception'
    console.error err.message
    console.error err.stack
    process.exit 1

Disable logging to Sentry in development


app = require 'app'
fs = require 'fs'
path = require 'path'
protocol = require 'protocol'

# Handles requests with 'atom' protocol.
#
# It's created by {AtomApplication} upon instantiation and is used to create a
# custom resource loader for 'atom://' URLs.
#
# The following directories are searched in order:
#   * ~/.atom/assets
#   * ~/.atom/dev/packages (unless in safe mode)
#   * ~/.atom/packages
#   * RESOURCE_PATH/node_modules
#
module.exports =
class AtomProtocolHandler
  constructor: (resourcePath, safeMode) ->
    @loadPaths = []
    @dotAtomDirectory = path.join(app.getHomeDir(), '.atom')

    unless safeMode
      @loadPaths.push(path.join(@dotAtomDirectory, 'dev', 'packages'))

    @loadPaths.push(path.join(@dotAtomDirectory, 'packages'))
    @loadPaths.push(path.join(resourcePath, 'node_modules'))

    @registerAtomProtocol()

  # Creates the 'atom' custom protocol handler.
  registerAtomProtocol: ->
    protocol.registerProtocol 'atom', (request) =>
      relativePath = path.normalize(request.url.substr(7))

      if relativePath.indexOf('assets/') is 0
        assetsPath = path.join(@dotAtomDirectory, relativePath)
        filePath = assetsPath if fs.statSyncNoException(assetsPath).isFile?()

      unless filePath
        for loadPath in @loadPaths
          filePath = path.join(loadPath, relativePath)
          break if fs.statSyncNoException(filePath).isFile?()

      new protocol.RequestFileJob(filePath)

app = require 'app'
fs = require 'fs'
path = require 'path'
protocol = require 'protocol'

# Handles requests with 'atom' protocol.
#
# It's created by {AtomApplication} upon instantiation and is used to create a
# custom resource loader for 'atom://' URLs.
#
# The following directories are searched in order:
#   * ~/.atom/assets
#   * ~/.atom/dev/packages (unless in safe mode)
#   * ~/.atom/packages
#   * RESOURCE_PATH/node_modules
#
module.exports =
class AtomProtocolHandler
  constructor: (resourcePath, safeMode) ->
    @loadPaths = []

    unless safeMode
      @loadPaths.push(path.join(process.env.ATOM_HOME, 'dev', 'packages'))

    @loadPaths.push(path.join(process.env.ATOM_HOME, 'packages'))
    @loadPaths.push(path.join(resourcePath, 'node_modules'))

    @registerAtomProtocol()

  # Creates the 'atom' custom protocol handler.
  registerAtomProtocol: ->
    protocol.registerProtocol 'atom', (request) =>
      relativePath = path.normalize(request.url.substr(7))

      if relativePath.indexOf('assets/') is 0
        assetsPath = path.join(process.env.ATOM_HOME, relativePath)
        filePath = assetsPath if fs.statSyncNoException(assetsPath).isFile?()

      unless filePath
        for loadPath in @loadPaths
          filePath = path.join(loadPath, relativePath)
          break if fs.statSyncNoException(filePath).isFile?()

      new protocol.RequestFileJob(filePath)

Use ATOM_HOME env var in protocol handler

Closes #5501


H2O.PredictOutput = (_, prediction) ->
  { frame, model } = prediction

  _predictionTable = _.inspect 'prediction', prediction

  inspect = ->
    #XXX get this from prediction table
    _.insertAndExecuteCell 'cs', "inspect predict #{stringify model.key}, #{stringify frame.key.name}"

  predictionTable: _predictionTable
  inspect: inspect
  template: 'flow-predict-output'

H2O.PredictOutput = (_, prediction) ->
  { frame, model } = prediction

  _predictionTable = _.inspect 'prediction', prediction

  inspect = ->
    #XXX get this from prediction table
    _.insertAndExecuteCell 'cs', "inspect getPrediction #{stringify model.key}, #{stringify frame.key.name}"

  predictionTable: _predictionTable
  inspect: inspect
  template: 'flow-predict-output'

Use getPrediction() instead of predict() during inspection


"*":
  Zen:
    softWrap: false
    width: 160
  core:
    disabledPackages: [
      "notebook"
      "language-gfm"
      "wrap-guide"
    ]
    telemetryConsent: "limited"
    themes: [
      "atom-dark-ui"
      "atom-dark-syntax"
    ]
  "default-language":
    defaultLanguage: "Pandoc markdown"
  editor:
    fontSize: 16
    showIndentGuide: true
    softWrap: true
    tabLength: 4
  "exception-reporting":
    userId: "9d2cc651-880b-ad49-90cb-e8e61ea9a2b1"
  "markdown-preview":
    breakOnSingleNewline: true
  nvatom:
    directory: "~/Dropbox/notes"
  welcome:
    showOnStartup: false
".gfm.nvatom.source":
  editor:
    tabLength: 4
".gfm.source":
  editor:
    tabLength: 4

"*":
  Zen:
    softWrap: false
    width: 160
  core:
    disabledPackages: [
      "notebook"
      "language-gfm"
      "wrap-guide"
    ]
    telemetryConsent: "limited"
    themes: [
      "one-dark-ui"
      "solarized-dark-syntax"
    ]
  "default-language":
    defaultLanguage: "Pandoc markdown"
  editor:
    fontSize: 16
    showIndentGuide: true
    softWrap: true
    tabLength: 4
  "exception-reporting":
    userId: "9d2cc651-880b-ad49-90cb-e8e61ea9a2b1"
  "markdown-preview":
    breakOnSingleNewline: true
  nvatom:
    directory: "~/Dropbox/notes"
  welcome:
    showOnStartup: false
".gfm.nvatom.source":
  editor:
    tabLength: 4
".gfm.source":
  editor:
    tabLength: 4

Set Solarized Dark as editor theme and One Dark as UI theme


_ = require 'underscore'
React = require 'react'
EventEmitter2 = require 'eventemitter2'
Course = require '../course/model'
api = require '../api'

BLANK_USER =
  is_admin: false
  is_content_analyst: false
  is_customer_service: false
  name: null
  profile_url: null

User =

  channel: new EventEmitter2 wildcard: true

  update: (data) ->
    _.extend(this, data.user)
    @courses = _.map data.courses, (course) -> new Course(course)
    @channel.emit('change')

  logout: ->
    _.extend(this, BLANK_USER)
    @channel.emit('change')

  getCourse: (collectionUUID) ->
    _.findWhere @courses, ecosystem_book_uuid: collectionUUID

  ensureStatusLoaded: ->
    api.channel.emit("user.send.statusUpdate") unless @isLoggedIn()

  isLoggedIn: -> !!@profile_url

api.channel.on 'user.receive.*', ({data}) ->
  User.update(loaded: true)
  if data.access_token
    api.channel.emit('set.access_token', data.access_token)
  User.endpoints = data.endpoints
  if data.user then User.update(data.user) else User.logout()

# start out as a blank user
_.extend(User, BLANK_USER)

module.exports = User

_ = require 'underscore'
React = require 'react'
EventEmitter2 = require 'eventemitter2'
Course = require '../course/model'
api = require '../api'

BLANK_USER =
  is_admin: false
  is_content_analyst: false
  is_customer_service: false
  name: null
  profile_url: null
  courses: []

User =

  channel: new EventEmitter2 wildcard: true

  update: (data) ->
    _.extend(this, data.user)
    @courses = _.map data.courses, (course) => new Course(@, course)
    @channel.emit('change')

  logout: ->
    _.extend(this, BLANK_USER)
    @channel.emit('change')

  getCourse: (collectionUUID) ->
    _.findWhere( @courses, ecosystem_book_uuid: collectionUUID )

  findOrCreateCourse: (collectionUUID) ->
    @getCourse(collectionUUID) or (
      course = new Course(@, ecosystem_book_uuid: collectionUUID)
      @courses.push(course)
      course
    )

  ensureStatusLoaded: ->
    api.channel.emit("user.send.statusUpdate") unless @isLoggedIn()

  isLoggedIn: -> !!@profile_url

  onCourseUpdate: (course) ->
    @channel.emit('change')

api.channel.on 'user.receive.*', ({data}) ->
  User.update(loaded: true)
  if data.access_token
    api.channel.emit('set.access_token', data.access_token)
  User.endpoints = data.endpoints
  if data.user then User.update(data.user) else User.logout()

# start out as a blank user
_.extend(User, BLANK_USER)

module.exports = User

Add findOrCreate and a method for course updates


fs   = require 'fs'
path = require 'path'

vm   = require './vm'
walk = require './walk'
{debounce, parseArgs} = require './utils'

module.exports = parseArgs (basePath, opts, cb) ->
  {relative, excluded} = opts
  opts.patch          ?= true
  opts.recurse        ?= true
  opts.watchSymlink   ?= false

  modules              = {}
  watching             = {}

  watch = (dir, isModule) ->
    return modules[dir] = true if isModule
    return if watching[dir]

    onchange = (event, filename) ->
      filename = path.join dir, filename

      return if excluded filename

      fs.stat filename, (err, stats) ->
        # ignore non-existent files
        return if err? or not stats?

        # watch new directory created
        if stats.isDirectory()
          watch filename

        # watch real dir of symbolic link
        else if stats.isSymbolicLink() and opts.watchSymlink
          fs.readLink filename, (err, realPath) ->
            watch path.dirname realPath

        # file changed
        else
          # Callback with file, stats object, module boolean
          cb filename, stats, modules[filename] ? false

    watching[dir] = fs.watch dir, (debounce 500, onchange)

  watch basePath

  if opts.recurse
    walk basePath, opts, (filename, stats) ->
      if excluded filename
        return

      watch filename if stats.isDirectory()

  if opts.patch
    vm (filename, stats) ->
      if excluded filename
        return

      watch filename, true if stats.isDirectory()

  watch

fs   = require 'fs'
path = require 'path'

vm   = require './vm'
walk = require './walk'
{debounce, parseArgs} = require './utils'

module.exports = parseArgs (basePath, opts, cb) ->
  {relative, excluded} = opts
  opts.patch          ?= false
  opts.recurse        ?= true
  opts.watchSymlink   ?= false

  modules              = {}
  watching             = {}

  watch = (dir, isModule) ->
    return modules[dir] = true if isModule
    return if watching[dir]

    onchange = (event, filename) ->
      filename = path.join dir, filename

      return if excluded filename

      fs.stat filename, (err, stats) ->
        # ignore non-existent files
        return if err? or not stats?

        # watch new directory created
        if stats.isDirectory()
          watch filename

        # watch real dir of symbolic link
        else if stats.isSymbolicLink() and opts.watchSymlink
          fs.readLink filename, (err, realPath) ->
            watch path.dirname realPath

        # file changed
        else
          # Callback with file, stats object, module boolean
          cb filename, stats, modules[filename] ? false

    watching[dir] = fs.watch dir, (debounce 500, onchange)

  watch basePath

  if opts.recurse
    walk basePath, opts, (filename, stats) ->
      if excluded filename
        return

      watch filename if stats.isDirectory()

  if opts.patch
    vm (filename, stats) ->
      if excluded filename
        return

      watch filename, true if stats.isDirectory()

  watch

Disable vm patching by default.

Disable vm patching by default.


angular.module('doubtfire.projects.states.dashboard.directives.task-dashboard.directives.task-plagiarism-card', [])
#
# plagiarism of task information
#
.directive('taskPlagiarismCard', ->
  restrict: 'E'
  templateUrl: 'projects/states/dashboard/directives/task-dashboard/directives/task-plagiarism-card/task-plagiarism-card.tpl.html'
  scope:
    task: '='
  controller: ($scope, PlagiarismReportModal) ->
    $scope.viewReport = -> PlagiarismReportModal.show($scope.task)
)

angular.module('doubtfire.projects.states.dashboard.directives.task-dashboard.directives.task-plagiarism-card', [])
#
# plagiarism of task information
#
.directive('taskPlagiarismCard', ->
  restrict: 'E'
  templateUrl: 'projects/states/dashboard/directives/task-dashboard/directives/task-plagiarism-card/task-plagiarism-card.tpl.html'
  scope:
    task: '='
  controller: ($scope, PlagiarismReportModal) ->
    $scope.viewReport = -> PlagiarismReportModal.show
)

FIX: Use function pointer for plagiarism view report


class @Overlay
  constructor: (selector, options) ->
    @selector = selector
    @options = options
    self = this
    @overlay = $("

")

    $(@selector).click (event) ->
      event.preventDefault()
      self.open(this)

    $(document).on 'keyup', (event) ->
      return unless event.which == 27
      self.close()

  open: (target) ->
    overlayContent = $(target).find('.js-overlay').remove()

    @overlay.append(overlayContent)
    $('body').append(@overlay)

    maxWidth = $(window).width() - 20
    maxHeight = $(window).height() - 20

    commentWidth = 309
    maxPhotoWidth = 1024
    maxPhotoHeight = 768
    totalWidth = maxPhotoWidth + commentWidth

    if totalWidth > maxWidth
      width = maxWidth
    else
      width = totalWidth

    if maxPhotoHeight > maxHeight
      height = maxHeight
    else
      height = maxPhotoHeight

    heightRatio = height / maxPhotoHeight
    widthRatio = width / maxPhotoWidth

    if heightRatio > widthRatio
      height = maxPhotoHeight * widthRatio
    else if widthRatio > heightRatio
      width = (maxPhotoWidth * heightRatio) + commentWidth

    margin = (maxWidth - width) / 2

    top = window.scrollY + 10

    @overlay.css('left', margin)
    @overlay.css('top', top)
    @overlay.width(width)
    @overlay.height(height)

    $('body').addClass('scroll-lock')

    overlayContent.show()

  close: ->
    @overlay.hide()
    $('body').removeClass('scroll-lock')

class @Overlay
  constructor: (selector, options) ->
    @selector = selector
    @options = options
    self = this
    @overlay = $("

")
    $('body').append(@overlay)

    $(@selector).click (event) ->
      event.preventDefault()
      self.open(this)

    $(document).on 'keyup', (event) ->
      return unless event.which == 27
      self.close()

  open: (target) ->
    overlayContent = $(target).find('.js-overlay').clone()

    @overlay.append(overlayContent)

    maxWidth = $(window).width() - 20
    maxHeight = $(window).height() - 20

    commentWidth = 309
    maxPhotoWidth = 1024
    maxPhotoHeight = 768
    totalWidth = maxPhotoWidth + commentWidth

    if totalWidth > maxWidth
      width = maxWidth
    else
      width = totalWidth

    if maxPhotoHeight > maxHeight
      height = maxHeight
    else
      height = maxPhotoHeight

    heightRatio = height / maxPhotoHeight
    widthRatio = width / maxPhotoWidth

    if heightRatio > widthRatio
      height = maxPhotoHeight * widthRatio
    else if widthRatio > heightRatio
      width = (maxPhotoWidth * heightRatio) + commentWidth

    margin = (maxWidth - width) / 2

    top = window.scrollY + 10

    @overlay.css('left', margin)
    @overlay.css('top', top)
    @overlay.width(width)
    @overlay.height(height)
    @overlay.show()

    $('body').addClass('scroll-lock')

    overlayContent.show()

  close: ->
    @overlay.empty().hide()
    $('body').removeClass('scroll-lock')

Allow overlay to be opened multiple times


class NavigationMachineItem extends JView

  {Running, Stopped} = Machine.State

  stateClasses  = ''
  stateClasses += "#{state.toLowerCase()} " for state in Object.keys Machine.State

  constructor:(options = {}, data)->

    machine            = data
    @alias             = machine.getName()
    path               = KD.utils.groupifyLink "/IDE/VM/#{machine.uid}"

    options.tagName    = 'a'
    options.cssClass   = "vm #{machine.status.state.toLowerCase()} #{machine.provider}"
    options.attributes =
      href             : path
      title            : "Open IDE for #{@alias}"

    super options, data

    @machine   = @getData()

    @label     = new KDCustomHTMLView
      partial  : @alias

    @progress  = new KDProgressBarView
      cssClass : 'hidden'

    { computeController } = KD.singletons

    computeController.on "public-#{@machine._id}", (event)=>

      {percentage, status} = event

      if percentage?

        if @progress.bar

          @progress.show()
          @progress.updateBar percentage

          if percentage is 100
            KD.utils.wait 1000, @progress.bound 'hide'

      else

        @progress.hide()

      if status?

        @unsetClass stateClasses
        @setClass status.toLowerCase()

  pistachio:->

    return """

      {{> @label}}

      {{> @progress}}
    """

class NavigationMachineItem extends JView

  {Running, Stopped} = Machine.State

  stateClasses  = ''
  stateClasses += "#{state.toLowerCase()} " for state in Object.keys Machine.State

  constructor:(options = {}, data)->

    machine            = data
    @alias             = machine.getName()
    path               = KD.utils.groupifyLink "/IDE/VM/#{machine.uid}"

    options.tagName    = 'a'
    options.cssClass   = "vm #{machine.status.state.toLowerCase()} #{machine.provider}"
    options.attributes =
      href             : path
      title            : "Open IDE for #{@alias}"

    super options, data

    @machine   = @getData()

    @label     = new KDCustomHTMLView
      partial  : @alias

    @progress  = new KDProgressBarView
      cssClass : 'hidden'

    { computeController } = KD.singletons

    computeController.on "public-#{@machine._id}", (event)=>

      {percentage, status} = event

      if percentage?

        if @progress.bar

          @progress.show()
          @progress.updateBar percentage

          if percentage is 100
            KD.utils.wait 1000, @progress.bound 'hide'

      else

        @progress.hide()

      if status?

        @unsetClass stateClasses
        @setClass status.toLowerCase()

    @machine.on 'MachineDomainUpdated', @bound 'updateLabel'

  updateLabel: ->

    @label.updatePartial @machine.getName()

  pistachio:->

    return """

      {{> @label}}

      {{> @progress}}
    """

NavigationMachineItem: Update machine label on UI when machine domain updated.


window.ReactFeedSelection = React.createClass
  displayName: 'ReactFeedSelection'
  mixins: [UpdateOnSignInOrOutMixin]

  getInitialState: ->
    feedChoice: 'global'
    feeds:
      global: new GlobalFeedActivities
      personal: new PersonalFeedActivities

  _handleFeedChoiceChange: (e) ->
    if(e.target.checked)
      @setState
        feedChoice: e.target.value

  render: ->
    _div [],
      if currentSession.signedIn()
        _div ['feed-selection-row'],
          _input [ 'radio-toggle-button', type: 'radio', name: 'FeedChoice', value: 'global', id: 'FeedChoice_Global', onChange: @_handleFeedChoiceChange, checked: @state.feedChoice=='global'  ]
          _label [ htmlFor: 'FeedChoice_Global' ],
            'Global'

          _input [ 'radio-toggle-button', type: 'radio', name: 'FeedChoice', value: 'personal', id: 'FeedChoice_Personal', onChange: @_handleFeedChoiceChange, checked: @state.feedChoice=='personal' ]
          _label [ htmlFor: 'FeedChoice_Personal' ],
            'Personal'

          _div ['feed-selection-install-extension-button'],
            ReactInstallExtensionOrBookmarklet()

      ReactFeedActivitiesAutoLoading
        model: @state.feeds[@state.feedChoice]
        key: @state.feedChoice

window.ReactFeedSelection = React.createClass
  displayName: 'ReactFeedSelection'
  mixins: [UpdateOnSignInOrOutMixin]

  getInitialState: ->
    feedChoice: 'global'
    feeds:
      global: new GlobalFeedActivities
      personal: new PersonalFeedActivities

  _handleFeedChoiceChange: (e) ->
    if(e.target.checked)
      @setState
        feedChoice: e.target.value

  render: ->
    _div [],
      if currentSession.signedIn()
        _div ['feed-selection-row'],
          _input [ 'radio-toggle-button', type: 'radio', name: 'FeedChoice', value: 'global', id: 'FeedChoice_Global', onChange: @_handleFeedChoiceChange, checked: @state.feedChoice=='global'  ]
          _label [ htmlFor: 'FeedChoice_Global' ],
            'Global'

          _input [ 'radio-toggle-button', type: 'radio', name: 'FeedChoice', value: 'personal', id: 'FeedChoice_Personal', onChange: @_handleFeedChoiceChange, checked: @state.feedChoice=='personal' ]
          _label [ htmlFor: 'FeedChoice_Personal' ],
            'Personal'

          if !window.is_kennisland
            _div ['feed-selection-install-extension-button'],
              ReactInstallExtensionOrBookmarklet()

      ReactFeedActivitiesAutoLoading
        model: @state.feeds[@state.feedChoice]
        key: @state.feedChoice

Hide browser extension button on feed when kennisland


angular.module('loomioApp').factory 'GroupRecordsInterface', (BaseRecordsInterface, GroupModel) ->
  class GroupRecordsInterface extends BaseRecordsInterface
    model: GroupModel

    fetchByParent: (parentGroup) ->
      @fetch
        path: "#{parentGroup.id}/subgroups"
        cacheKey: "subgroupsFor#{parentGroup.key}"

    archive: (group) ->
      @restfulClient.postMember group.id, "archive"

angular.module('loomioApp').factory 'GroupRecordsInterface', (BaseRecordsInterface, GroupModel) ->
  class GroupRecordsInterface extends BaseRecordsInterface
    model: GroupModel

    fetchByParent: (parentGroup) ->
      @fetch
        path: "#{parentGroup.id}/subgroups"
        cacheKey: "subgroupsFor#{parentGroup.key}"

    archive: (group) =>
      @restfulClient.patchMember group.id, "archive"

Make archive group work again

Make archive group work again


# Description:
#   今日も一日頑張るぞい！
#
# Commands:
#   hubot zoi - 今日も一日頑張る
#
# Author:
#   Uchio KONDO

request = require 'request'
uri     = 'http://zoi.herokuapp.com/js/services.js'

getZois = (cb) ->
  request(uri, (err, response, body) ->
    if err
      cb.onError(err)
    else
      zois = body.match(/image: 'https:\/\/pbs.twimg.com\/media\/.+.jpg:large'/mg).
                  map((l) -> l.match("'(.+)'")[1])
      (cb.onZoi || cb.onSuccess)(zois)
  )

module.exports = (robot) ->
  robot.respond /zoi/i, (msg) ->
    getZois
      onZoi: (zois) -> msg.send msg.random zois
      onError: (err) -> msg.send "頑張れなかった...: #{err}"

# Description:
#   今日も一日頑張るぞい！
#
# Commands:
#   hubot zoi - 今日も一日頑張る
#
# Author:
#   Uchio KONDO

request = require 'request'
uri     = 'http://zoi.herokuapp.com/js/services.js'

getZois = (cb) ->
  request(uri, (err, response, body) ->
    if err
      cb.onError(err)
    else
      zois = body.match(/image: 'https:\/\/pbs.twimg.com\/media\/.+.jpg:large'/mg).
                  map((l) -> l.match("'(https.+\.jpg):large'")[1])
      (cb.onZoi || cb.onSuccess)(zois)
  )

module.exports = (robot) ->
  robot.respond /zoi/i, (msg) ->
    getZois
      onZoi: (zois) -> msg.send msg.random zois
      onError: (err) -> msg.send "頑張れなかった...: #{err}"

Remove :large suffix to usefully expand on limechat


# Source: https://github.com/lukehoban/atom-ide-flow/blob/master/lib/tooltip-view.coffee
{$, View} = require 'atom'

module.exports = (Main)->
  class TooltipView extends View
    @content: ->
      @div class: 'ide-hack-tooltip'

    initialize: (@rect, text = null) ->
      @text(text) if text?
      $(document.body).append this
      @updatePosition()

    # update tooltip text
    updateText: (text) ->
      @text(text)
      @updatePosition()

    # smart position update
    updatePosition: ->
      coords = [@rect.right, @rect.bottom, undefined]
      offset = 10

      # x axis adjust
      if coords[0] + this[0].offsetWidth >= $(document.body).width()
        coords[0] = $(document.body).width() - this[0].offsetWidth - offset
      if coords[0] < 0
        this.css({ 'white-space': 'pre-wrap' })
        coords[0] = offset
        coords[2] = offset

      # y axis adjust
      if coords[1] + this[0].offsetHeight >= $(document.body).height()
        coords[1] = @rect.top - this[0].offsetHeight

      this.css({ left: coords[0], top: coords[1], right: coords[2] })

# Source: https://github.com/lukehoban/atom-ide-flow/blob/master/lib/tooltip-view.coffee
{$, View} = require 'atom'

module.exports = (Main)->
  class TooltipView extends View
    @content: ->
      @div class: 'ide-hack-tooltip'

    initialize: (@rect, @LeMessage) ->
      @append LeMessage
      $(document.body).append this
      @updatePosition()

    # update tooltip text
    updateText: (text) ->
      @empty()
      @append @LeMessage
      @updatePosition()

    # smart position update
    updatePosition: ->
      coords = [@rect.right, @rect.bottom, undefined]
      offset = 10

      # x axis adjust
      if coords[0] + this[0].offsetWidth >= $(document.body).width()
        coords[0] = $(document.body).width() - this[0].offsetWidth - offset
      if coords[0] < 0
        this.css({ 'white-space': 'pre-wrap' })
        coords[0] = offset
        coords[2] = offset

      # y axis adjust
      if coords[1] + this[0].offsetHeight >= $(document.body).height()
        coords[1] = @rect.top - this[0].offsetHeight

      this.css({ left: coords[0], top: coords[1], right: coords[2] })

Append HTML instead of Text

:bug: Append HTML instead of Text


# Default callbacks for internal storage of received packets
class CableNotifications.Store.DefaultCallbacks
  constructor: (@collections) ->

  # Callbacks
  ##################################################

  collection_remove: (packet, collection) ->
    console.warn 'Method not implemented: collection_remove '

  create: (packet, collection) ->
    fields = packet.data
    collection.insert(fields)

  update: (packet, collection) ->
    collection.update({id: packet.id}, packet.data)

  update_many: (packet, collection) ->
    _.each packet.data, (fields) ->
      collection.update({id: fields.id}, fields)

  upsert_many: (packet, collection) ->
    _.each packet.data, (fields) ->
      collection.upsert({id: fields.id}, fields)

  destroy: (packet, collection) ->
    collection.remove({id: packet.id})

  error: (packet, collection) ->
    console.error "[#{packet.cmd}]: #{packet.error}"

# Default callbacks for internal storage of received packets
class CableNotifications.Store.DefaultCallbacks
  constructor: (@collections) ->

  # Callbacks
  ##################################################

  collection_remove: (packet, collection) ->
    console.warn 'Method not implemented: collection_remove '

  create: (packet, collection) ->
    fields = packet.data
    collection.create(fields)

  update: (packet, collection) ->
    collection.update({id: packet.id}, packet.data)

  update_many: (packet, collection) ->
    _.each packet.data, (fields) ->
      collection.update({id: fields.id}, fields)

  upsert_many: (packet, collection) ->
    _.each packet.data, (fields) ->
      collection.upsert({id: fields.id}, fields)

  destroy: (packet, collection) ->
    collection.destroy({id: packet.id})

  error: (packet, collection) ->
    console.error "[#{packet.cmd}]: #{packet.error}"

Fix default_callback wrong method names

Fix default_callback wrong method names


Range = require 'range'
Point = require 'point'

module.exports =
class Fold
  @idCounter: 1

  renderer: null
  startRow: null
  endRow: null

  constructor: (@renderer, @startRow, @endRow) ->
    @id = @constructor.idCounter++

  destroy: ->
    @renderer.destroyFold(this)

  getRange: ->
    throw "Don't worry about this yet -- sobo"

  getBufferDelta: ->
    new Point(@endRow - @startRow + 1, 0)

  handleBufferChange: (event) ->
    oldStartRow = @startRow

    { oldRange } = event
    if oldRange.start.row <= @startRow and oldRange.end.row >= @endRow
      @renderer.unregisterFold(oldStartRow, this)
      return

    changeInsideFold = @startRow <= oldRange.start.row and @endRow >= oldRange.end.row
    @startRow = @updateAnchorRow(@startRow, event)
    @endRow = @updateAnchorRow(@endRow, event)

    if @startRow != oldStartRow
      @renderer.unregisterFold(oldStartRow, this)
      @renderer.registerFold(@startRow, this)

    changeInsideFold

  updateAnchorRow: (row, event) ->
    { newRange, oldRange } = event
    return row if row < oldRange.start.row

    deltaFromOldRangeEndRow = row - oldRange.end.row
    newRange.end.row + deltaFromOldRangeEndRow

  compare: (other) ->
    other
    # startComparison = @start.compare(other.start)
    # if startComparison == 0
    #   other.end.compare(@end)
    # else
    #   startComparison

Range = require 'range'
Point = require 'point'

module.exports =
class Fold
  @idCounter: 1

  renderer: null
  startRow: null
  endRow: null

  constructor: (@renderer, @startRow, @endRow) ->
    @id = @constructor.idCounter++

  destroy: ->
    @renderer.destroyFold(this)

  getBufferDelta: ->
    new Point(@endRow - @startRow + 1, 0)

  handleBufferChange: (event) ->
    oldStartRow = @startRow

    { oldRange } = event
    if oldRange.start.row <= @startRow and oldRange.end.row >= @endRow
      @renderer.unregisterFold(oldStartRow, this)
      return

    changeInsideFold = @startRow <= oldRange.start.row and @endRow >= oldRange.end.row
    @startRow = @updateAnchorRow(@startRow, event)
    @endRow = @updateAnchorRow(@endRow, event)

    if @startRow != oldStartRow
      @renderer.unregisterFold(oldStartRow, this)
      @renderer.registerFold(@startRow, this)

    changeInsideFold

  updateAnchorRow: (row, event) ->
    { newRange, oldRange } = event
    return row if row < oldRange.start.row

    deltaFromOldRangeEndRow = row - oldRange.end.row
    newRange.end.row + deltaFromOldRangeEndRow

Remove unused methods from Fold

Remove unused methods from Fold


moment = require 'moment'
twix = require 'twix'
_ = require 'underscore'

durations =
  create: (startTime, endTime) ->
    moment(startTime).twix(endTime)

moment = require 'moment'
twix = require 'twix'
_ = require 'underscore'
{TimeStore} = require '../flux/time'

module.exports =
  create: (startTime, endTime) ->
    moment(startTime).twix(endTime)

  isPastDue: ({due_at}) ->
    moment(TimeStore.getNow()).isAfter(due_at)

Add isPastDue method and export methods

Add isPastDue method and export methods

I don't think this actually worked before? It didn't export anything


requirejs.config({
  paths: {
    fabric: [
      'lib/fabric']
  }
})

define ['game', 'synchronizedtime', 'position', 'lib/fabric'], (Game, SynchronizedTime, Position) -> # Fabric is deliberately not set as an argument
  console.log "Fabric: ", fabric
  console.log "Position: ", new Position([1,2], 0, 5)
  canvas = new fabric.Canvas('canvas', {renderOnAddRemove: false})
  window.canvas = canvas

  game = new Game(canvas)
  game.start()
  setInterval((->
    SynchronizedTime.time += 1
    game.timer_tick()
    ),
    1000
  )

requirejs.config({
  paths: {
    fabric: [
      'lib/fabric']
  }
})

define ['game', 'synchronizedtime', 'position', 'lib/fabric'], (Game, SynchronizedTime, Position) -> # Fabric is deliberately not set as an argument
  console.log "Fabric: ", fabric
  console.log "Position: ", new Position([1,2], 0, 5)
  canvas = new fabric.Canvas('canvas', {renderOnAddRemove: false})
  window.canvas = canvas

  game = new Game(canvas)
  game.browser = true
  game.start()
  setInterval((->
    SynchronizedTime.time += 1
    game.timer_tick()
    ),
    1000
  )

Set browser to true on Game class when in browser


loadFragment = (locale, university, controller, param, modal) ->
  controller ?= ""
  param      ?= ""

  url = "/#{locale}/#{university}"
  if controller
    url += "/#{controller}"
    if param
      url += "/#{param}"

  url += ".fragment"

  contentWrap = $('#page-content')
  cached.getDOM url, (error, dom) ->
    initDom dom.clone().appendTo(contentWrap.empty())
  return true

testEnvironment = () -> $('body').attr('data-env') == 'test'

init_routing = () ->
  return if not History.enabled or testEnvironment()
  crossroads.addRoute "/{locale}/{university}/:controller:/:param:", loadFragment
  # crossroads.routed.add(console.log, console);

  $('a').live 'click', (ev) ->
    $this = $(this)
    href = $this.attr 'href'
    return false if $this.hasClass 'js-modal' or href == "#"
    if href.indexOf('http://') == 0 or $this.hasClass("js-reload")
      return true
    else
      navigate href
      return false

  History.Adapter.bind window, 'statechange', () ->
    navigate History.getState().hash

navigate = (url) ->
  History.pushState null, null, url
  crossroads.parse url

$(init_routing)

loadFragment = (locale, university, controller, param, modal) ->
  controller ?= ""
  param      ?= ""
  getParams  = null

  url = "/#{locale}/#{university}"
  if controller
    url += "/#{controller}"
    if param
      rest = param.split('?')
      getParams = rest[1]
      url += "/#{rest[0]}"
  url += ".fragment"
  if getParams
    url += "?#{getParams}"

  contentWrap = $('#page-content')
  cached.getDOM url, (error, dom) ->
    initDom dom.clone().appendTo(contentWrap.empty())
  return true

testEnvironment = () -> $('body').attr('data-env') == 'test'

init_routing = () ->
  return  if not History.enabled or testEnvironment()
  crossroads.addRoute "/{locale}/{university}/:controller:/:param:", loadFragment
  # crossroads.routed.add(console.log, console);

  $('a').live 'click', (ev) ->
    $this = $(this)
    href = $this.attr 'href'
    return false if $this.hasClass 'js-modal' or href == "#"
    if href.indexOf('http://') == 0 or $this.hasClass("js-reload")
      return true
    else
      navigate href
      return false

  History.Adapter.bind window, 'statechange', () ->
    navigate History.getState().hash

navigate = (url) ->
  History.pushState null, null, url
  crossroads.parse url

$(init_routing)

Support get parameters for fragment routing


{makeElement, defer} = Trix

prototypes =
  cursorTarget:
    makeElement
      tagName: "span"
      textContent: Trix.ZERO_WIDTH_SPACE
      data:
        trixSelection: true
        trixCursorTarget: true
        trixSerialize: false

Trix.extend
  selectionElements:
    selector: "[data-trix-selection]"

    cssText: """
      font-size: 0 !important;
      padding: 0 !important;
      margin: 0 !important;
      border: none !important;
    """

    create: (name) ->
      prototypes[name].cloneNode(true)

{makeElement, defer} = Trix

prototypes =
  cursorTarget:
    makeElement
      tagName: "span"
      textContent: Trix.ZERO_WIDTH_SPACE
      data:
        trixSelection: true
        trixCursorTarget: true
        trixSerialize: false

Trix.extend
  selectionElements:
    selector: "[data-trix-selection]"

    cssText: """
      font-size: 0 !important;
      padding: 0 !important;
      margin: 0 !important;
      border: none !important;
      line-height: 0 !important;
    """

    create: (name) ->
      prototypes[name].cloneNode(true)

Make sure selection elements have 0 line height to not offset line height.


define (require, exports, module) ->
  # This object stores generic information about the app
  # that might need to be available globally.
  config = {}

  # `set` is a shortcut for setting a bunch of properties by passing in an object.
  # Order isn't guaranteed. Call it multiple times if order is important.
  config.set = (options) ->
    for own key, value of options
      throw new Error 'Don\'t overwrite "set" in config.' if key is 'set'
      config[key] = value

  # Determine if we're running on a development server.
  config.set
    dev: +location.port > 1023 or !!~location.hostname.indexOf '.dev'
    demo: !!~location.href.indexOf 'demo'

  # Default host and API proxy path
  config.set
    apiHost: 'http://www.batdetective.org'
    proxyPath: '/_ouroboros_api/proxy'

  # TODO: What if dev Ouroboros isn't on 3000?
  config.set apiHost: "http://#{location.hostname}:3000" if config.dev

  config.set apiHost: "https://dev.zooniverse.org" if config.demo

  module.exports = config

define (require, exports, module) ->
  # This object stores generic information about the app
  # that might need to be available globally.
  config = {}

  # `set` is a shortcut for setting a bunch of properties by passing in an object.
  # Order isn't guaranteed. Call it multiple times if order is important.
  config.set = (options) ->
    for own key, value of options
      throw new Error 'Don\'t overwrite "set" in config.' if key is 'set'
      config[key] = value

  # Determine if we're running on a development server.
  config.set
    dev: +location.port > 1023 or !!~location.hostname.indexOf '.dev'
    demo: !!~location.href.indexOf 'demo'

  # Default host and API proxy path
  config.set
    apiHost: 'http://www.batdetective.org'
    proxyPath: '/_ouroboros_api/proxy'

  # TODO: What if dev Ouroboros isn't on 3000?
  if config.dev
    config.set
      apiHost: 'https://dev.zooniverse.org'
      proxyPath: '/proxy'

  config.set apiHost: "https://dev.zooniverse.org" if config.demo

  module.exports = config

Use dev.zooniverse.org for local development


# An animation sequence is an array of frames where each frame includes:
# * A pattern
# * A duration (ms)
class AnimationSequence

  # Initializes an animation sequence with a pattern and a duration for that pattern
  # @param pattern A single pattern or array of patterns
  # @param duration A single duration or array of durations
  constructor: (pattern, duration) ->
    @patterns = [].concat(pattern)
    @durations = [].concat(duration)

  # Returns a concatted animation sequence
  concat: (animationSequence) ->
    newPatterns = @patterns.concat(animationSequence.patterns)
    newDurations = @durations.concat(animationSequence.durations)
    return new AnimationSequence(newPatterns, newDurations)

  # Sets the speed for animation interpolation
  setSpeed: (@animationSpeed) ->

  # Interpolates between two frames
  # @param pattern The next pattern to animate to.
  # @param duration The duration that the interpolation should take
  interpolate: (pattern, duration) ->
    # TODO

Add extra line at end of file

Add extra line at end of file


JSV = require("JSV").JSV

class SchemaValidator

  constructor: (@schema_manager) ->
    @jsv = JSV.createEnvironment("json-schema-draft-03")
    for schema in @schema_manager.schemas
      @jsv.createSchema(schema, false, schema.id)

  validate: (identifier, data) ->
    schema = @schema_manager.find(identifier)
    if schema
      result = @jsv.findSchema(schema.id).validate data, schema
      if result.errors
        result.description = {}
        for error in result.errors
          data_property = @format_name(error.uri)
          result.description[data_property] = @format_error(error)
      result
    else
      throw "unknown schema id: #{id}"

  format_name: (data_uri) ->
    [root, data_property] = data_uri.split("#/")
    data_property || "<root>"
    data_property
      .replace /(\d+)\//g, (match, p1) -> "[#{p1}]/"
      .replace(/\//g, ".")

  format_error: (error) ->
    [root, property] = error.uri.split("#/")
    schema_property = error.schemaUri.split(":")[2]
    property ||= "<root>"
    out =
      message: error.message
      schema: schema_property

    switch error.message
      when "Instance is not a required type"
        out.types = error.details
      when "Number is greater than the required maximum value"
        out.maximum = error.details
      when "Number is less than the required minimum value"
        out.minimum = error.details

    out

module.exports = SchemaValidator

JSV = require("JSV").JSV

class SchemaValidator

  constructor: (@schema_manager) ->
    @jsv = JSV.createEnvironment("json-schema-draft-03")
    for schema in @schema_manager.schemas
      @jsv.createSchema(schema, false, schema.id)

  validate: (identifier, data) ->
    schema = @schema_manager.find(identifier)
    if schema
      result = @jsv.findSchema(schema.id).validate data, schema
      if result.errors
        result.description = {}
        for error in result.errors
          data_property = @format_name(error.uri)
          result.description[data_property] = @format_error(error)
      result
    else
      throw "unknown schema id: #{id}"

  format_name: (data_uri) ->
    [root, data_property] = data_uri.split("#/")
    data_property ?= "<root>"
    data_property
      .replace /(\d+)\//g, (match, p1) -> "[#{p1}]/"
      .replace(/\//g, ".")

  format_error: (error) ->
    [root, property] = error.uri.split("#/")
    schema_property = error.schemaUri.split(":")[2]
    property ||= "<root>"
    out =
      message: error.message
      schema: schema_property

    switch error.message
      when "Instance is not a required type"
        out.types = error.details
      when "Number is greater than the required maximum value"
        out.maximum = error.details
      when "Number is less than the required minimum value"
        out.minimum = error.details

    out

module.exports = SchemaValidator

Set default data property in schema validation when formatting


completionDelay = 100

exports.triggerAutocompletion = (editor, moveCursor = true, char = 'f') ->
  if moveCursor
    editor.moveToBottom()
    editor.moveToBeginningOfLine()
  editor.insertText(char)
  exports.waitForAutocomplete()

exports.waitForAutocomplete = ->
  advanceClock(completionDelay)
  waitsFor 'autocomplete to show', (done) ->
    setImmediate ->
      advanceClock(10)
      setImmediate ->
        advanceClock(10)
        done()

exports.buildIMECompositionEvent = (event, {data, target} = {}) ->
  event = new CustomEvent(event, {bubbles: true})
  event.data = data
  Object.defineProperty(event, 'target', {get: -> target})
  event

exports.buildTextInputEvent = ({data, target}) ->
  event = new CustomEvent('textInput', {bubbles: true})
  event.data = data
  Object.defineProperty(event, 'target', {get: -> target})
  event

completionDelay = 100

beforeEach ->
  spyOn(atom.views, 'readDocument').andCallFake (fn) -> fn()
  spyOn(atom.views, 'updateDocument').andCallFake (fn) -> fn()

exports.triggerAutocompletion = (editor, moveCursor = true, char = 'f') ->
  if moveCursor
    editor.moveToBottom()
    editor.moveToBeginningOfLine()
  editor.insertText(char)
  exports.waitForAutocomplete()

exports.waitForAutocomplete = ->
  advanceClock(completionDelay)
  waitsFor 'autocomplete to show', (done) ->
    setImmediate ->
      advanceClock(10)
      setImmediate ->
        advanceClock(10)
        done()

exports.buildIMECompositionEvent = (event, {data, target} = {}) ->
  event = new CustomEvent(event, {bubbles: true})
  event.data = data
  Object.defineProperty(event, 'target', {get: -> target})
  event

exports.buildTextInputEvent = ({data, target}) ->
  event = new CustomEvent('textInput', {bubbles: true})
  event.data = data
  Object.defineProperty(event, 'target', {get: -> target})
  event

Make updateDocument and readDocument sync


# Keybindings require three things to be fully defined: A selector that is
# matched against the focused element, the keystroke and the command to
# execute.
#
# Below is a basic keybinding which registers on all platforms by applying to
# the root workspace element.

# For more detailed documentation see
# https://atom.io/docs/latest/advanced/keymaps

'.table-edit':
  'tab': 'core:move-right'
  'shift-tab': 'core:move-left'

'.table-edit atom-text-editor':
  'enter': 'core:confirm'
  'ctrl-enter': 'editor:newline'
  'tab': 'table-edit:move-right'
  'shift-tab': 'table-edit:move-left'
  'ctrl-tab': 'editor:indent'

# Keybindings require three things to be fully defined: A selector that is
# matched against the focused element, the keystroke and the command to
# execute.
#
# Below is a basic keybinding which registers on all platforms by applying to
# the root workspace element.

# For more detailed documentation see
# https://atom.io/docs/latest/advanced/keymaps

'.table-edit':
  'tab': 'core:move-right'
  'shift-tab': 'core:move-left'
  'cmd-shift-right': 'table-edit:select-to-end-of-line'
  'cmd-shift-left': 'table-edit:select-to-beginning-of-line'
  'cmd-shift-up': 'table-edit:select-to-beginning-of-table'
  'cmd-shift-down': 'table-edit:select-to-end-of-table'

'.table-edit atom-text-editor':
  'enter': 'core:confirm'
  'ctrl-enter': 'editor:newline'
  'tab': 'table-edit:move-right'
  'shift-tab': 'table-edit:move-left'
  'ctrl-tab': 'editor:indent'

Add keymap for new selection expansion

Add keymap for new selection expansion


define [
  "chaplin"
  "lib/utils"
], (Chaplin, utils) ->
  "use strict"

  class Model extends Chaplin.Model

    urlRoot: utils.bnwUrl "/api"

    apiCall: (url = @url(), data = undefined) ->
      reqData = if data? then data else @query
      $.ajax
        url: url
        type: if data? then "POST" else "GET"
        data: reqData
        # This should actually be auto-detectable (backend do return
        # correct Content-type header) but jQuery sucks.
        dataType: "json"

define [
  "jquery"
  "chaplin"
  "lib/utils"
], ($, Chaplin, utils) ->
  "use strict"

  class Model extends Chaplin.Model

    urlRoot: utils.bnwUrl "/api"

    apiCall: (url = @url(), data = undefined) ->
      deferred = $.Deferred()
      promise = deferred.promise()
      reqData = if data? then data else @query
      jqxhr = $.ajax
        url: url
        type: if data? then "POST" else "GET"
        data: reqData
        # This should actually be auto-detectable (backend do return
        # correct Content-type header) but jQuery sucks.
        dataType: "json"
      jqxhr.done (data) =>
        # XXX: Not sure if GC will correctly clean up callbacks on 'done'
        deferred.resolve data unless @disposed
      promise

Fix errors appearing after long requests


express = require 'express'
stylus = require 'stylus'
assets = require 'connect-assets'

app = express.createServer()
app.use assets()
app.set 'view engine', 'jade'

app.get '/', (req, resp) -> resp.render 'index'

app.get '/:filename', (req, resp) ->
  content = req.query['content']
  if (content and content.length > 1024)
    resp.json({error: 'File content is longer than 1024 characters and was truncated.. Please use POST instead of GET'}, 406)
  else
    saveit(req, resp)

app.post '/:filename', saveit

saveit = (req, resp) ->
  content = req.query["content"]
  unless (content)
    resp.json({error: 'File content is missing'}, 406)
  else
    resp.attachment(req.params.filename)
    resp.send(content)

app.listen process.env.PORT or 4000, -> console.log 'Listening...'

express = require 'express'
stylus = require 'stylus'
assets = require 'connect-assets'

app = express.createServer()
app.use assets()
app.set 'view engine', 'jade'

app.get '/', (req, resp) -> resp.render 'index'

app.get '/:filename.:extension', (req, resp) ->
  content = req.param['content']
  if (content and content.length > 1024)
    resp.json({error: 'File content is longer than 1024 characters and was truncated.. Please use POST instead of GET'}, 406)
  else
    saveit(req, resp)

app.post '/:filename.:extension', saveit

saveit = (req, resp) ->
  content = req.param.content
  unless (content)
    resp.json({error: 'File content is missing'}, 406)
  else
    resp.attachment(req.param.filename)
    resp.send(content)

app.listen process.env.PORT or 4000, -> console.log 'Listening...'

Improve how params and routes are looked up


angular.module('gi.security').controller 'usersController'
, ['$scope', '$location', 'User', 'Auth'
, ($scope, $location, User, Auth) ->

  $scope.newUser = User.create()
  $scope.currentView = 'list'

  $scope.getData = () ->
    User.query (results) ->
      $scope.users = results
      $scope.selectedUser = $scope.users[0]

  $scope.deleteUser = (id) ->
    User.delete {id: id}
    , () ->
      $scope.getData()

  $scope.saveUser = (user) ->
    User.save user, () ->
      $scope.getData()

  $scope.getUsers = () ->
    $scope.users = User.query()

  $scope.deleteUser = (id) ->
    User.delete {id: id}
    , () ->
      $scope.getUsers()

  $scope.selectUser = (user) ->
    $scope.selectedUser = user

  $scope.show = (view) ->
    $scope.currentView = view

  Auth.isAdmin().then (isAdmin) ->
    if isAdmin
      $scope.getData()
    else
      $location.path '/login'

]

angular.module('gi.security').controller 'usersController'
, ['$scope', '$location', 'User', 'Auth'
, ($scope, $location, User, Auth) ->

  $scope.newUser = {}
  $scope.currentView = 'list'

  $scope.getData = () ->
    User.query (results) ->
      $scope.users = results
      $scope.selectedUser = $scope.users[0]

  $scope.deleteUser = (id) ->
    User.delete {id: id}
    , () ->
      $scope.getData()

  $scope.saveUser = (user) ->
    User.save user, () ->
      $scope.getData()

  $scope.getUsers = () ->
    $scope.users = User.query()

  $scope.deleteUser = (id) ->
    User.delete {id: id}
    , () ->
      $scope.getUsers()

  $scope.selectUser = (user) ->
    $scope.selectedUser = user

  $scope.show = (view) ->
    $scope.currentView = view

  Auth.isAdmin().then (isAdmin) ->
    if isAdmin
      $scope.getData()
    else
      $location.path '/login'

]

Remove deprecated User.create{} method call


crypto = require 'crypto'
_ = require 'underscore'

Singleton = require 'singleton'
Riak = require "#{__dirname}/../db/riak"
TimeEntry = require "#{__dirname}/../models/time_entry"

class TimeEntriesController extends Singleton
  bucket: 'time_entries'
  idGen: ()->
    sha = crypto.createHash 'sha1'
    _.each arguments, (d)->
      sha.update JSON.stringify d
    sha.digest 'hex'

  create: (time_entry, callback)->
    if time_entry.userId and time_entry.projectId
      id = @idGen time_entry

      handler = (err, obj, meta) ->
        if err
          callback err
        else
          te = new TimeEntry obj
          te.id = id
          callback null, te

      console.log id, @bucket
      Riak.getClient().save @bucket, id, time_entry,
        {
          index:
            userId: time_entry.userId
            projectId: time_entry.projectId
        }, handler

module.exports = TimeEntriesController.get()

crypto = require 'crypto'
_ = require 'underscore'

Singleton = require 'singleton'
Riak = require "#{__dirname}/../db/riak"
TimeEntry = require "#{__dirname}/../models/time_entry"

class TimeEntriesController extends Singleton
  bucket: 'time_entries'
  idGen: ()->
    sha = crypto.createHash 'sha1'
    _.each arguments, (d)->
      sha.update JSON.stringify d
    sha.digest 'hex'

  create: (time_entry, callback)->
    if time_entry.userId and time_entry.projectId
      time_entry.id = @idGen time_entry

      handler = (err, obj, meta) ->
        console.log err, obj, meta
        if err
          callback err
        else
          callback null, time_entry

      Riak.getClient().save @bucket, time_entry.id, time_entry,
        {
          index:
            userId: time_entry.userId
            projectId: time_entry.projectId
        }, handler

module.exports = TimeEntriesController.get()

Send back the original time entry

Send back the original time entry


require ['jquery', 'lib/core/base', 'flamsteed', 'lib/utils/scroll_perf', 'polyfills/function_bind', 'trackjs'], ($, Base, _FS, ScrollPerf) ->
  $ ->
    base = new Base()
    new ScrollPerf
    window.lp.fs = new _FS({events: window.lp.fs.buffer, u: $.cookies.get('lpUid')})
    require ['sailthru'], ->
      Sailthru.setup(domain: 'horizon.lonelyplanet.com')

require ['jquery', 'lib/core/base', 'flamsteed', 'lib/utils/scroll_perf', 'polyfills/function_bind', 'trackjs'], ($, Base, _FS, ScrollPerf) ->
  $ ->
    base = new Base()
    new ScrollPerf

    # Currently we can't serve Flamsteed over https because of f.staticlp.com
    # New community is using this file rather than app_secure_core
    # Trello card: https://trello.com/c/2RCd59vk/201-move-f-staticlp-com-off-cloudfront-and-on-to-fastly-so-we-can-serve-over-https
    unless window.location.protocol is "https:"
      window.lp.fs = new _FS({events: window.lp.fs.buffer, u: $.cookies.get('lpUid')})

    require ['sailthru'], ->
      Sailthru.setup(domain: 'horizon.lonelyplanet.com')

Remove flamsteed from secure pages

Remove flamsteed from secure pages


$ ->
    $body = $('body')
    bodyClass = $body.attr 'class'
    routes = ['startups-new', 'startups-edit']

    if bodyClass in ['startups-new', 'startups-edit']
        $markets = $("#startup_market_list")

        $('.upload-photo').click (event) ->
          event.preventDefault()
          $('#startup_image').click()

        $('#startup_image').change (event) ->
          if @files && @files[0]
              reader = new FileReader()

              reader.onload = (e) ->
                $('img.startuplogo').attr('src', e.target.result)

              reader.readAsDataURL(@files[0])

        $('.reset-photo').click (event) ->
          event.preventDefault()
          # TODO: Set a flag to delete image
          $('form').submit()

        $markets.tokenInput "http://api.angel.co/1/search?type=MarketTag",
          crossDomain: true,
          queryParam: "query",
          prePopulate: $markets.data('pre'),
          theme: "facebook",
          tokenLimit: 3,
          tokenValue: "name"
    else if bodyClass is 'startups-index'
        $(".startupcard").equalHeights()

$ ->
    $body = $('body')
    bodyClass = $body.attr 'class'
    routes = ['startups-new', 'startups-edit']

    if bodyClass in ['startups-new', 'startups-edit']
        $markets = $("#startup_market_list")

        $('.upload-photo').click (event) ->
          event.preventDefault()
          $('#startup_image').click()

        $('#startup_image').change (event) ->
          if @files && @files[0]
              reader = new FileReader()

              reader.onload = (e) ->
                $('img.startuplogo').attr('src', e.target.result)

              reader.readAsDataURL(@files[0])

        $('.reset-photo').click (event) ->
          event.preventDefault()
          $('input#startup_default_logo').val('set_default')
          $('img.startuplogo').attr('src', '/assets/fallback/thumb_startup-default.png')

        $markets.tokenInput "http://api.angel.co/1/search?type=MarketTag",
          crossDomain: true,
          queryParam: "query",
          prePopulate: $markets.data('pre'),
          theme: "facebook",
          tokenLimit: 3,
          tokenValue: "name"
    else if bodyClass is 'startups-index'
        $(".startupcard").equalHeights()

Add reset company image behavior for Startups.coffee


define [
    '../../core'
    'tpl!templates/views/columns.html'
    'plugins/jquery-ui'
], (c, templates...) ->

define [
    '../../core'
    'tpl!templates/views/columns-avail.html'
    'tpl!templates/views/columns-select.html'
    'tpl!templates/views/columns-selected.html'
    'plugins/jquery-ui'
], (c, templates...) ->

Include templates in column.coffee deps.


root = exports ? this

class DateChooser

  constructor: (chooser) ->
    @chooser = chooser
    selectors = chooser.find(".date-chooser-select") # Today/Tomorrow selection
    initialDate = selectors.data("initial-date")
    @dateDisplay = chooser.find(".date-chooser-display")
    @values = chooser.find(".date-chooser-values")
    @today = selectors.find(".today")
    @tomorrow = selectors.find(".tomorrow")

    switch initialDate
      when "today"
        @enableLink @tomorrow, @today
      when "tomorrow"
        @enableLink @today, @tomorrow
      else
        @enableLink @today, @tomorrow
        @enableLink @tomorrow, @today

  enableLink: (selector, otherSelector) =>
    selector.html( "<a href>" + selector.text() + "</a>" )
    selector.on "click", (event) =>
      @toggleDate( selector, otherSelector )
      event.preventDefault()

  toggleDate: (selector, otherSelector) =>
    selector.html( selector.text() )
    @setDate selector
    @enableLink otherSelector, selector

  setDate: (selector) =>
    day = selector.data("day")
    month = selector.data("month")
    year = selector.data("year")
    display = selector.data("date-display")
    @values.find(".day").eq(0).val(day)
    @values.find(".month").eq(0).val(month)
    @values.find(".year").eq(0).val(year)
    @dateDisplay.text(display)

root.DateChooser = DateChooser

root = exports ? this

class DateChooser

  constructor: (chooser) ->
    @chooser = chooser
    selectors = chooser.find(".date-chooser-select") # Today/Tomorrow selection
    initialDate = selectors.data("initial-date")
    @dateDisplay = chooser.find(".date-chooser-display")
    @values = chooser.find(".date-chooser-values")
    @today = selectors.find(".today")
    @tomorrow = selectors.find(".tomorrow")

    @today.on "click", (event) =>
      @toggleDate( @today, @tomorrow )
      event.preventDefault()

    @tomorrow.on "click", (event) =>
      @toggleDate( @tomorrow, @today )
      event.preventDefault()

    switch initialDate
      when "today"
        @enableLink @tomorrow
      when "tomorrow"
        @enableLink @today
      else
        @enableLink @today
        @enableLink @tomorrow

  enableLink: (selector) =>
    selector.html( "<a href>" + selector.text() + "</a>" )

  toggleDate: (selector, otherSelector) =>
    selector.html( selector.text() )
    @setDate selector
    @enableLink otherSelector

  setDate: (selector) =>
    day = selector.data("day")
    month = selector.data("month")
    year = selector.data("year")
    display = selector.data("date-display")
    @values.find(".day").eq(0).val(day)
    @values.find(".month").eq(0).val(month)
    @values.find(".year").eq(0).val(year)
    @dateDisplay.text(display)

root.DateChooser = DateChooser

Add on click event only once to each toggle element.


describe 'Linter Behavior', ->
  linter = null
  linterState = null
  bottomContainer = null
  trigger = (el, name) ->
    event = document.createEvent('HTMLEvents');
    event.initEvent(name, true, false);
    el.dispatchEvent(event);

  beforeEach ->
    waitsForPromise ->
      atom.packages.activatePackage('linter').then ->
        linter = atom.packages.getActivePackage('linter').mainModule.instance
        linterState = linter.state
        bottomContainer = linter.views.bottomContainer

  describe 'Bottom Tabs', ->
    it 'defaults to file tab', ->
      expect(linterState.scope).toBe('File')

    it 'changes tab on click', ->
      trigger(bottomContainer.getTab('Project'), 'click')
      expect(linterState.scope).toBe('Project')

    it 'toggles panel visibility on click', ->
      expect(linter.views.panel.panelVisibility).toBe(true)
      trigger(bottomContainer.getTab('File'), 'click')
      expect(linter.views.panel.panelVisibility).toBe(false)
      trigger(bottomContainer.getTab('File'), 'click')
      expect(linter.views.panel.panelVisibility).toBe(true)

:new: Add spec for visibility being toggled on tab double click


{CompositeDisposable} = require 'event-kit'

class MinimapHighlightSelected
  constructor: ->
    @subscriptions = new CompositeDisposable

  activate: (state) ->
    @highlightSelectedPackage = atom.packages.getLoadedPackage('highlight-selected')
    @minimapPackage = atom.packages.getLoadedPackage('minimap')

    return @deactivate() unless @highlightSelectedPackage? and @minimapPackage?

    @MinimapHighlightSelectedView = require('./minimap-highlight-selected-view')()

    @minimap = require @minimapPackage.path
    @highlightSelected = require @highlightSelectedPackage.path

    @minimap.registerPlugin 'highlight-selected', this

  deactivate: ->
    @deactivatePlugin()
    @minimapPackage = null
    @highlightSelectedPackage = null
    @highlightSelected = null
    @minimap = null

  isActive: -> @active
  activatePlugin: ->
    return if @active
    return unless @minimap.active

    @active = true

    @createViews()

    @subscriptions.add @minimap.onDidActivate @createViews
    @subscriptions.add @minimap.onDidDeactivate @destroyViews

  deactivatePlugin: ->
    return unless @active

    @active = false
    @destroyViews()
    @subscriptions.dispose()

  createViews: =>
    return if @viewsCreated

    @viewsCreated = true
    @view = new @MinimapHighlightSelectedView(@minimap)
    @view.handleSelection()

  destroyViews: =>
    return unless @viewsCreated
    @viewsCreated = false
    @view.removeMarkers()
    @view.destroy()

module.exports = new MinimapHighlightSelected

{CompositeDisposable} = require 'event-kit'

class MinimapHighlightSelected
  constructor: ->
    @subscriptions = new CompositeDisposable

  activate: (state) ->
    @highlightSelectedPackage = atom.packages.getLoadedPackage('highlight-selected')
    @minimapPackage = atom.packages.getLoadedPackage('minimap')

    return @deactivate() unless @highlightSelectedPackage? and @minimapPackage?

    @MinimapHighlightSelectedView = require('./minimap-highlight-selected-view')()

    @minimap = require @minimapPackage.path
    @highlightSelected = require @highlightSelectedPackage.path

    return @deactivate() unless @minimap.versionMatch('3.x')

    @minimap.registerPlugin 'highlight-selected', this

  deactivate: ->
    @deactivatePlugin()
    @minimapPackage = null
    @highlightSelectedPackage = null
    @highlightSelected = null
    @minimap = null

  isActive: -> @active
  activatePlugin: ->
    return if @active
    return unless @minimap.active

    @active = true

    @createViews()

    @subscriptions.add @minimap.onDidActivate @createViews
    @subscriptions.add @minimap.onDidDeactivate @destroyViews

  deactivatePlugin: ->
    return unless @active

    @active = false
    @destroyViews()
    @subscriptions.dispose()

  createViews: =>
    return if @viewsCreated

    @viewsCreated = true
    @view = new @MinimapHighlightSelectedView(@minimap)
    @view.handleSelection()

  destroyViews: =>
    return unless @viewsCreated
    @viewsCreated = false
    @view.removeMarkers()
    @view.destroy()

module.exports = new MinimapHighlightSelected

Add version test for minimap v3

Add version test for minimap v3


require 'atom'
require 'window'
{createPeer, connectDocument} = require './session-utils'
{createSite, Document} = require 'telepath'

window.setDimensions(x: 0, y: 0, width: 800, height: 800)
atom.show()

peer = createPeer()
{sessionId} = atom.getLoadSettings()
connection = peer.connect(sessionId, reliable: true)
connection.on 'open', ->
  console.log 'connection opened'
  connection.once 'data', (data) ->
    console.log 'received document'
    atom.windowState = Document.deserialize(createSite(peer.id), data)
    connectDocument(atom.windowState, connection)
    window.setUpEnvironment('editor')
    window.startEditorWindow()

require 'atom'
require 'window'
$ = require 'jquery'
{$$} = require 'space-pen'
{createPeer, connectDocument} = require './session-utils'
{createSite, Document} = require 'telepath'

window.setDimensions(width: 350, height: 100)
window.setUpEnvironment('editor')
{sessionId} = atom.getLoadSettings()

loadingView = $$ ->
  @div style: 'margin: 10px; text-align: center', =>
    @div "Joining session #{sessionId}"
$(window.rootViewParentSelector).append(loadingView)
atom.show()

peer = createPeer()
connection = peer.connect(sessionId, reliable: true)
connection.on 'open', ->
  console.log 'connection opened'
  connection.once 'data', (data) ->
    loadingView.remove()
    console.log 'received document'
    atom.windowState = Document.deserialize(createSite(peer.id), data)
    connectDocument(atom.windowState, connection)
    window.startEditorWindow()

Add loading view when joining session

Add loading view when joining session


define (require) ->
  _ = require('underscore')
  settings = require('settings')

  shortcodes = settings.shortcodes
  inverseShortcodes = _.invert(shortcodes)

  return new class LinksHelper
    getPath: (page, data) ->
      url = settings.root

      switch page
        when 'contents'
          uuid = data.model.getVersionedId()
          uuid = inverseShortcodes[uuid] if inverseShortcodes[uuid]
          url += "contents/#{uuid}"
          url += ":#{data.page}" if data.page

      return url

    serializeQuery: (query) ->
      queryString = {}

      query.split('?').pop().split('&').forEach (prop) ->
        item = prop.split('=')
        queryString[decodeURIComponent(item.shift())] = decodeURIComponent(item.shift())

      return queryString

    param: (obj) ->
      str = []

      for p of obj
        if obj.hasOwnProperty(p)
          str.push("#{encodeURIComponent(p)}=#{encodeURIComponent(obj[p])}")

      return str.join("&")

Add methods to serialize and parameterize query strings


utils = window.Curve

module.exports =
class PointerTool
  constructor: (svg, {@selectionModel, @selectionView}={}) ->
    @_evrect = svg.node.createSVGRect();
    @_evrect.width = @_evrect.height = 1;

  activate: ->
    svg.on 'click', @onClick
    svg.on 'mousemove', @onMouseMove

  deactivate: ->
    svg.off 'click', @onClick
    svg.off 'mousemove', @onMouseMove

  onClick: (e) =>
    obj = @_hitWithIntersectionList(e)
    @selectionModel.setSelected(obj)
    return false if obj

  onMouseMove: (e) =>
    @selectionModel.setPreselected(@_hitWithIntersectionList(e))
    # @selectionModel.setPreselected(@_hitWithTarget(e))

  _hitWithTarget: (e) ->
    obj = null
    obj = utils.getObjectFromNode(e.target) if e.target != svg.node
    obj

  _hitWithIntersectionList: (e) ->
    @_evrect.x = e.clientX
    @_evrect.y = e.clientY
    nodes = svg.node.getIntersectionList(@_evrect, null)

    obj = null
    if nodes.length
      for i in [nodes.length-1..0]
        clas = nodes[i].getAttribute('class')
        continue if clas and clas.indexOf('invisible-to-hit-test') > -1
        obj = utils.getObjectFromNode(nodes[i])
        break

    obj

$ = require 'jquery'

utils = window.Curve

module.exports =
class PointerTool
  constructor: (svg, {@selectionModel, @selectionView}={}) ->
    @_evrect = svg.node.createSVGRect();
    @_evrect.width = @_evrect.height = 1;

  activate: ->
    svg.on 'click', @onClick
    svg.on 'mousemove', @onMouseMove

  deactivate: ->
    svg.off 'click', @onClick
    svg.off 'mousemove', @onMouseMove

  onClick: (e) =>
    obj = @_hitWithIntersectionList(e)
    @selectionModel.setSelected(obj)
    return false if obj

  onMouseMove: (e) =>
    @selectionModel.setPreselected(@_hitWithIntersectionList(e))
    # @selectionModel.setPreselected(@_hitWithTarget(e))

  _hitWithTarget: (e) ->
    obj = null
    obj = utils.getObjectFromNode(e.target) if e.target != svg.node
    obj

  _hitWithIntersectionList: (e) ->
    {left, top} = $(svg.node).offset()
    @_evrect.x = e.clientX - left
    @_evrect.y = e.clientY - top
    nodes = svg.node.getIntersectionList(@_evrect, null)

    obj = null
    if nodes.length
      for i in [nodes.length-1..0]
        clas = nodes[i].getAttribute('class')
        continue if clas and clas.indexOf('invisible-to-hit-test') > -1
        obj = utils.getObjectFromNode(nodes[i])
        break

    obj

Make event positions relative to node offset


module.exports = (grunt) ->

  # Project configuration.
  grunt.initConfig

    shell:
      'compile-client-side':
        options: stdout: true
        command: 'coffee -o ./ -c src/'
      'compile-server-side':
        options: stdout: true
        command: 'coffee -o public/scripts/compiled -c public/scripts/src/'
      'compile-css':
        options: stdout: true
        command: 'stylus public/styles/'

    concat:
      options:
        separator: ';'
      dist:
        src: ['public/scripts/compiled/*.js', 'public/scripts/compiled/views/*.js']
        dest: 'public/scripts/out.js'

    uglify:
      build:
        src: 'public/scripts/out.js'
        dest: 'public/scripts/out.min.js'

    clean:
      js: ['*.js', 'public/scripts/compiled/', 'public/scripts/*.js']
      css: 'public/styles/main.css'
      components: 'public/components'
      npm: 'node_modules'

  # Load the plugin that provides the 'uglify' task.
  grunt.loadNpmTasks 'grunt-contrib-uglify'
  grunt.loadNpmTasks 'grunt-contrib-concat'
  grunt.loadNpmTasks 'grunt-contrib-clean'
  grunt.loadNpmTasks 'grunt-shell'

  # Default task(s).
  grunt.registerTask 'default', ['compile', 'concat', 'uglify']
  grunt.registerTask 'compile', ['shell:compile-server-side', 'shell:compile-client-side']

module.exports = (grunt) ->

  # Project configuration.
  grunt.initConfig

    shell:
      'compile-client-side':
        options: stdout: true
        command: 'coffee -o ./ -c src/'
      'compile-server-side':
        options: stdout: true
        command: 'coffee -o public/scripts/compiled -c public/scripts/src/'
      'compile-css':
        options: stdout: true
        command: 'stylus public/styles/'

    concat:
      options:
        separator: ';'
      dist:
        src: ['public/scripts/compiled/*.js', 'public/scripts/compiled/views/*.js']
        dest: 'public/scripts/out.js'

    uglify:
      build:
        src: 'public/scripts/out.js'
        dest: 'public/scripts/out.min.js'

    clean:
      js: ['*.js', 'public/scripts/compiled/', 'public/scripts/*.js']
      css: 'public/styles/main.css'
      components: 'public/components'
      npm: 'node_modules'

  # Load the plugin that provides the 'uglify' task.
  grunt.loadNpmTasks 'grunt-contrib-uglify'
  grunt.loadNpmTasks 'grunt-contrib-concat'
  grunt.loadNpmTasks 'grunt-contrib-clean'
  grunt.loadNpmTasks 'grunt-shell'

  # Default task(s).
  grunt.registerTask 'default', ['compile', 'concat', 'uglify']
  grunt.registerTask 'compile', ['shell:compile-server-side', 'shell:compile-client-side', 'shell:compile-css']

Add compile-css to grunt compile task.

Add compile-css to grunt compile task.


{$, EditorView, View} = require 'atom'
path = require 'path'

module.exports =
class Dialog extends View
  @content: ({prompt} = {}) ->
    @div class: 'tree-view-dialog overlay from-top', =>
      @div class: 'block', =>
        @label prompt, class: 'icon', outlet: 'promptText'
        @subview 'miniEditor', new EditorView(mini: true)
        @div class: 'error-message', outlet: 'errorMessage'

  initialize: ({initialPath, @onConfirm, select, iconClass} = {}) ->
    @promptText.addClass(iconClass) if iconClass
    @on 'core:confirm', => @onConfirm(@miniEditor.getText())
    @on 'core:cancel', => @cancel()
    @miniEditor.hiddenInput.on 'focusout', => @remove()

    @miniEditor.setText(initialPath)

    if select
      extension = path.extname(initialPath)
      baseName = path.basename(initialPath)
      if baseName is extension
        selectionEnd = initialPath.length
      else
        selectionEnd = initialPath.length - extension.length
      range = [[0, initialPath.length - baseName.length], [0, selectionEnd]]
      @miniEditor.setSelectedBufferRange(range)

  attach: ->
    atom.workspaceView.append(this)
    @miniEditor.focus()

  close: ->
    @remove()
    atom.workspaceView.focus()

  cancel: ->
    @remove()
    $('.tree-view').focus()

  showError: (message) ->
    @errorMessage.text(message)
    @flashError()

{$, EditorView, View} = require 'atom'
path = require 'path'

module.exports =
class Dialog extends View
  @content: ({prompt} = {}) ->
    @div class: 'tree-view-dialog overlay from-top', =>
      @div class: 'block', =>
        @label prompt, class: 'icon', outlet: 'promptText'
        @subview 'miniEditor', new EditorView(mini: true)
        @div class: 'error-message', outlet: 'errorMessage'

  initialize: ({initialPath, @onConfirm, select, iconClass} = {}) ->
    @promptText.addClass(iconClass) if iconClass
    @on 'core:confirm', => @onConfirm(@miniEditor.getText())
    @on 'core:cancel', => @cancel()
    @miniEditor.hiddenInput.on 'focusout', => @remove()
    @miniEditor.getBuffer().on 'changed', => @showError()

    @miniEditor.setText(initialPath)

    if select
      extension = path.extname(initialPath)
      baseName = path.basename(initialPath)
      if baseName is extension
        selectionEnd = initialPath.length
      else
        selectionEnd = initialPath.length - extension.length
      range = [[0, initialPath.length - baseName.length], [0, selectionEnd]]
      @miniEditor.setSelectedBufferRange(range)

  attach: ->
    atom.workspaceView.append(this)
    @miniEditor.focus()

  close: ->
    @remove()
    atom.workspaceView.focus()

  cancel: ->
    @remove()
    $('.tree-view').focus()

  showError: (message='') ->
    @errorMessage.text(message)
    @flashError() if message

Clear error when buffer changes

Clear error when buffer changes


PARAM_TOKEN = /[?|&]/g
VALUE_TOKEN = "="

class HashParameters
  constructor: ->
    @parameters = {}

  decode: (string) ->
    decodeURIComponent(string)

  encode: (string) ->
    encodeURIComponent(string)

  fromLocationHash: ->
    @parameters = {}
    hash = @readHash()

    keyPairs = hash.split PARAM_TOKEN
    _.each keyPairs, (pair) =>
      if pair.match VALUE_TOKEN
        [key, value] = pair.split VALUE_TOKEN
        @parameters[key] = @decode value

  updateLocationhash: ->
    keys = _.keys @parameters
    strings = _.map keys, (key) =>
      value = @parameters[key]
      [key, @encode(value)].join VALUE_TOKEN
    @writeHash strings.join('&')

  setParam: (key,value) ->
    @parameters[key] = value
    @updateLocationhash()

  getParam: (key) ->
    @fromLocationHash()
    return @parameters[key]

  clearParam: (key) ->
    delete @parameters[key]
    @updateLocationhash()

  writeHash: (string) ->
    if window and window.location
      if string.length < 1
        window.location.hash = ""
      else
        window.location.hash = "?#{string}"

  readHash: ->
    if window and window.location
      # remove the leading slash
      hash = window.top.location.hash.substring(1)
    else
      ""

module.exports = new HashParameters()

PARAM_TOKEN = /[?|&]/g
VALUE_TOKEN = "="

class HashParameters
  constructor: ->
    @parameters = {}

  decode: (string) ->
    decodeURIComponent(string)

  encode: (string) ->
    encodeURIComponent(string)

  fromLocationHash: ->
    @parameters = {}
    hash = @readHash()

    keyPairs = hash.split PARAM_TOKEN
    _.each keyPairs, (pair) =>
      if pair.match VALUE_TOKEN
        [key, value] = pair.split VALUE_TOKEN
        @parameters[key] = @decode value

  updateLocationhash: ->
    keys = _.keys @parameters
    strings = _.map keys, (key) =>
      value = @parameters[key]
      [key, @encode(value)].join VALUE_TOKEN
    @writeHash strings.join('&')

  setParam: (key,value) ->
    @parameters[key] = value
    @updateLocationhash()

  getParam: (key) ->
    @fromLocationHash()
    return @parameters[key]

  clearParam: (key) ->
    delete @parameters[key]
    @updateLocationhash()

  writeHash: (string) ->
    if window and window.location
      if string.length < 1
        window.location.hash = ""
      else
        window.location.hash = "?#{string}"

  readHash: ->
    try
      if window and window.top.location
        # remove the leading slash
        hash = window.top.location.hash.substring(1)
    catch     # x-origin exception
      if window and window.location
        hash = window.location.hash.substring(1)
    return hash or ""

module.exports = new HashParameters()

Fix x-origin exception when loading from sage.concord.org


'context-menu':
  '.overlayer':
    'Correct spelling': 'spell-check:correct-misspelling'

'context-menu':
  '.overlayer':
    'Correct Spelling': 'spell-check:correct-misspelling'

Use title case in menu

Use title case in menu


# Your init script
#
# Atom will evaluate this file each time a new window is opened. It is run
# after packages are loaded/activated and after the previous editor state
# has been restored.
#

fileTypes = {
  "GitHub Markdown": (editor) ->
    editor.setSoftWrap(true)
    editor.setTabLength(4)

  "Java": (editor) ->
    editor.setTabLength(4)

  "Python": (editor) ->
    editor.setTabLength(4)
}

# Apply grammar-specific settings.
#
# editor - Editor to which to apply the grammar-specific settings.
applySyntaxSettings = (editor) ->
  func(editor) for name, func of fileTypes when name is editor.getGrammar().name

# Executes for each and every Editor, past and future.
atom.workspace.eachEditor (editor) ->
  applySyntaxSettings(editor)

  editor.on 'grammar-changed', ->
    applySyntaxSettings(editor)

# Your init script
#
# Atom will evaluate this file each time a new window is opened. It is run
# after packages are loaded/activated and after the previous editor state
# has been restored.
#

fileTypes = {
  "GitHub Markdown": (editor) ->
    editor.setSoftWrap(true)
    editor.setTabLength(4)

  "Java": (editor) ->
    editor.setTabLength(4)

  "Python": (editor) ->
    editor.setTabLength(4)
}

# Apply grammar-specific settings.
#
# editor - Editor to which to apply the grammar-specific settings.
applyGrammarSettings = (editor) ->
  func(editor) for name, func of fileTypes when name is editor.getGrammar().name

# Executes for each and every Editor, past and future.
atom.workspace.eachEditor (editor) ->
  applyGrammarSettings(editor)

  editor.on 'grammar-changed', ->
    applyGrammarSettings(editor)

Rename function to match standard

Rename function to match standard


# Keybindings require three things to be fully defined: A selector that is
# matched against the focused element, the keystroke and the command to
# execute.
#
# Below is a basic keybinding which registers on all platforms by applying to
# the root workspace element.

# For more detailed documentation see
# https://atom.io/docs/latest/advanced/keymaps

'.table-edit atom-text-editor':
  'enter': 'core:confirm'
  'cmd-enter': 'editor:newline'

Add binding for enter key in table view editor


exec = require("child_process").exec
path = require('path')

module.exports =
  activate: ->
    atom.workspaceView.command "open-terminal-here:open", (event) =>
      @open()

  open: ->
    filepath = atom.workspaceView.find('.tree-view .selected')?.view()?.getPath?()
    if filepath
      dirpath = path.dirname(filepath)
      exec "open -a Terminal.app #{dirpath}" if dirpath?

exec = require("child_process").exec
path = require('path')

module.exports =
  configDefaults: {
    app: 'Terminal.app'
    args: ''
  },
  activate: ->
    atom.workspaceView.command "open-terminal-here:open", (event) =>
      @open()

  open: ->
    filepath = atom.workspaceView.find('.tree-view .selected')?.view()?.getPath?()
    if filepath
      dirpath = path.dirname(filepath)
      app = atom.config.get('open-terminal-here.app')
      args = atom.config.get('open-terminal-here.args')
      exec "open -a #{app} #{args} #{dirpath}" if dirpath?

Add configuration option to change which app to load


_             = require 'underscore'
Backbone      = require 'backbone'
FeedbackView  = require '../../feedback/view.coffee'

module.exports = class FooterView extends Backbone.View
  events:
    'click .mlf-feedback': 'feedback'

  feedback: (e) ->
    e.preventDefault()

    # FeedbackView inherits ModalView which auto-renders
    # and disposes of itself on close
    new FeedbackView(width: '500px')

_             = require 'underscore'
Backbone      = require 'backbone'
FeedbackView  = require '../../feedback/view.coffee'
mediator      = require '../../../lib/mediator.coffee'

module.exports = class FooterView extends Backbone.View
  events:
    'click .mlf-feedback': 'feedback'

  initialize: (options) ->
    mediator.on 'open:feedback', @openFeedback, this

  feedback: (e) ->
    e.preventDefault()
    mediator.trigger 'open:feedback'

  openFeedback: ->
    new FeedbackView(width: '500px')

Allow for feedback open to be triggered through the mediator


# depends:
# python.install
# prosilver?
#
# after:
# casper.registration_agreement
#
# phpbb_version:
# >=3.1.0

d = ->
  console.log arguments...

base = global.wolis.config.test_url

casper.start base + '/ucp.php?mode=register&agreed=1', ->
  @test.assertExists '#tz_date'
  tz_value = @evaluate ->
    document.querySelector('#tz_date').value
  # Should match system time zone
  @test.assertMatch tz_value, /^GMT-05:00/

casper.run ->
  @test.done()

# depends:
# python.install
# prosilver?
#
# after:
# casper.registration_agreement
#
# phpbb_version:
# >=3.1.0

d = ->
  console.log arguments...

base = global.wolis.config.test_url

casper.start base + '/ucp.php?mode=register&agreed=1', ->
  @test.assertExists '#tz_date'
  tz_value = @evaluate ->
    document.querySelector('#tz_date').value
  # Should match system time zone
  # System time zone is our time zone
  offset_hours = (new Date()).getTimezoneOffset() / -60
  sign = if offset_hours < 0 then '-' else '+'
  offset = Math.abs(offset_hours)
  if offset < 10
    offset = '0' + offset
  # XXX does not handle fractional hour offsets
  @test.assertMatch tz_value, new RegExp("^GMT#{sign}#{offset}:00")

casper.run ->
  @test.done()

Make tz selection test non-system-specific


Hummingbird.LoadMoreComponent = Ember.Component.extend
  checkInView: ->
    elementBottom = @$().offset().top + @$().height()
    windowBottom = window.scrollY + $(window).height()
    if elementBottom < windowBottom + 200
      @sendAction()

  didInsertElement: ->
    $(window).bind 'scroll.loadmore', $.proxy(@checkInView, this)

  willClearRender: ->
    $(window).unbind 'scroll.loadmore'

Hummingbird.LoadMoreComponent = Ember.Component.extend
  checkInView: ->
    elementBottom = @$().offset().top + @$().height()
    windowBottom = window.scrollY + $(window).height()
    if elementBottom < windowBottom + 200
      @sendAction()

  didInsertElement: ->
    $(window).bind 'scroll.loadmore', $.proxy(@checkInView, this)
    @checkInView()

  willClearRender: ->
    $(window).unbind 'scroll.loadmore'

Check if the loading indicator is visible on didInsertElement.


fs = require 'fs'
path = require 'path'

module.exports =
    selector: '.source.lua'
    disableForSelector: '.source.lua .comment, .source.lua .string'

    inclusionPriority: 10
    excludeLowerPriority: true

    getSuggestions: ( { editor, bufferPosition, scopeDescriptor, prefix, activatedManually } ) ->
        if prefix.length is 0
            return []

        suggestions = @findSuggestions( @completions, prefix )
        suggestions

    findSuggestions: ( completions, prefix ) ->
        suggestions = []
        for item in completions
            if @compareStrings( item.displayText, prefix )
                suggestions.push( {
                        displayText: item.displayText
                        snippet: item.snippet
                        type: item.type
                    } )

        suggestions

    loadCompletions: ->
        @completions = {}
        fs.readFile path.resolve( __dirname, '..', './snippets/love-completions.json' ), ( error, data ) =>
            @completions = JSON.parse( data ) unless error?
            return

    compareStrings: ( a, b ) ->
        a[0].toLowerCase() is b[0].toLowerCase()

fs = require 'fs'
path = require 'path'

module.exports =
    selector: '.source.lua'
    disableForSelector: '.source.lua .comment, .source.lua .string'

    inclusionPriority: 10
    excludeLowerPriority: true

    getSuggestions: ( { editor, bufferPosition, scopeDescriptor, prefix, activatedManually } ) ->
        if prefix.length is 0
            return []

        suggestions = @findSuggestions( @completions, prefix )
        suggestions

    findSuggestions: ( completions, prefix ) ->
        suggestions = []
        for item in completions
            if @compareStrings( item.displayText, prefix )
                suggestions.push( @buildSuggestion( item ) )

        suggestions

    buildSuggestion: ( item ) ->
        suggestion =
            displayText: item.displayText
            snippet: item.snippet
            type: item.type
        return suggestion

    loadCompletions: ->
        @completions = {}
        fs.readFile path.resolve( __dirname, '..', './snippets/love-completions.json' ), ( error, data ) =>
            @completions = JSON.parse( data ) unless error?
            return

    compareStrings: ( a, b ) ->
        a[0].toLowerCase() is b[0].toLowerCase()

Add separate function for building each suggestion


define ['mediator', 'lib/route'], (mediator, Route) ->
  'use strict'

  class Router # This class does not inherit from Backbone’s router

    constructor: ->
      # Create a Backbone.History instance
      @createHistory()

    createHistory: ->
      Backbone.history or= new Backbone.History

    startHistory: ->
      # Start the Backbone.History instance to start routing
      # This should be called after all routes have been registered
      Backbone.history.start pushState: true

    stopHistory: ->
      Backbone.history.stop()

    # Connect an address with a controller action
    # Directly create a route on the Backbone.History instance
    match: (pattern, target, options = {}) =>

      # Create a route
      route = new Route pattern, target, options

      # Register the route at the Backbone.History instance
      Backbone.history.route route, route.handler

    # Route a given URL path manually, return whether a route matched
    # This looks quite like Backbone.History::loadUrl but it
    # accepted an absolute URL with a leading slash (e.g. /foo)
    # and passes the changeURL param to the callback function
    route: (path) =>
      #console.debug 'Router#route', path

      # Remove leading hash or slash
      path = path.replace /^(\/#|\/)/, ''
      for handler in Backbone.history.handlers
        if handler.route.test(path)
          handler.callback path, changeURL: true
          return true
      false

    # Change the current URL, add a history entry.
    # Do not trigger any routes (which is Backbone’s
    # default behavior, but added for clarity)
    changeURL: (url) ->
      Backbone.history.navigate url, trigger: false

define ['mediator', 'lib/route'], (mediator, Route) ->
  'use strict'

  class Router # This class does not inherit from Backbone’s router

    constructor: ->
      # Create a Backbone.History instance
      @createHistory()

    createHistory: ->
      Backbone.history or= new Backbone.History

    startHistory: ->
      # Start the Backbone.History instance to start routing
      # This should be called after all routes have been registered
      Backbone.history.start pushState: true

    stopHistory: ->
      Backbone.history.stop()

    # Connect an address with a controller action
    # Directly create a route on the Backbone.History instance
    match: (pattern, target, options = {}) =>

      # Create a route
      route = new Route pattern, target, options

      # Register the route at the Backbone.History instance
      Backbone.history.route route, route.handler

    # Route a given URL path manually, return whether a route matched
    # This looks quite like Backbone.History::loadUrl but it
    # accepts an absolute URL with a leading slash (e.g. /foo)
    # and passes the changeURL param to the callback function
    route: (path) =>
      #console.debug 'Router#route', path

      # Remove leading hash or slash
      path = path.replace /^(\/#|\/)/, ''
      for handler in Backbone.history.handlers
        if handler.route.test(path)
          handler.callback path, changeURL: true
          return true
      false

    # Change the current URL, add a history entry.
    # Do not trigger any routes (which is Backbone’s
    # default behavior, but added for clarity)
    changeURL: (url) ->
      Backbone.history.navigate url, trigger: false

Fix a typo in a comment

Fix a typo in a comment


angular.module('fileManager')
.directive('breadcrumb', ($location)->
  return {
    restrict: 'E'
    scope: true
    template: '
' +
                '
' +
                  '{{piece.value}}' +
                  '{{piece.value}}' +
                '
' +
              '
'
    link: (scope, element, attrs) ->
        #scope.$on('$routeChangeSuccess', ->
        path = $location.path()

        breadcrumb = path.split('/').splice(2)

        result = []
        link   = ''
        for piece in breadcrumb
          link += '/' + piece
          add=
            link: link.replace('#', '%23')
            value: piece
          result.push(add)

        scope.breadcrumb = result
        #)
  }
)

angular.module('fileManager')
.directive('breadcrumb', ($stateParams)->
  return {
    restrict: 'E'
    template: """

{{ piece.value }}

              """

    link: (scope, element, attrs) ->
      scope.$watch($stateParams, ->
        path = "/#{$stateParams.path}"
        path = path.split('/')

        result = []
        link   = ''
        for piece in path
          if piece isnt ''
            link += '/' + piece
            add=
              path: link.replace('#', '%23')
              value: piece
            result.push(add)

        scope.breadcrumb = result
      )

  }
)

Update the breadcrumb directive to use the new router and use the actual design.


module.exports = (grunt) ->

  grunt.initConfig

    pkg: grunt.file.readJSON 'package.json'

    clean:
      main: 'target/src'
      test: 'target/test'

    coffeelint:
      options:
        configFile: 'coffeelint.json'
      main: 'src/**/*.coffee'
      test: 'test/**/*.coffee'
      build: 'gruntfile.coffee'

    coffee:
      main:
        expand: true
        cwd: 'src'
        src: '**/*.coffee'
        dest: 'target/src',
        ext: '.js'

      test:
        cwd: 'test'
        expand: true
        src: '**/*.coffee'
        dest: 'target/test'
        ext: '.js'

    mochaTest:
      options:
        reporter: 'spec'
      src: 'target/test/**/*-test.js'

    copy:
      dist:
        cwd: 'target/src'
        expand: true
        src: '**/*.js'
        dest: 'dist'

  require('load-grunt-tasks')(grunt)

  grunt.registerTask 'default', [ 'validate', 'compile' ]

  grunt.registerTask 'validate', [ 'coffeelint:build', 'coffeelint:main', 'coffeelint:test' ]

  grunt.registerTask 'compile', [ 'clean:main', 'coffee:main' ]

  grunt.registerTask 'test-compile', [ 'clean:test', 'coffee:test' ]

  grunt.registerTask 'test', [ 'validate', 'compile', 'test-compile', 'mochaTest' ]

module.exports = (grunt) ->

  grunt.initConfig

    pkg: grunt.file.readJSON 'package.json'

    clean:
      main: 'target/src'
      test: 'target/test'

    coffeelint:
      options:
        configFile: 'coffeelint.json'
      main: 'src/**/*.coffee'
      test: 'test/**/*.coffee'
      build: 'gruntfile.coffee'

    coffee:
      options:
        bare: true
        sourceMap: true
        sourceMapDir: './target/.sourcemap'
      main:
        expand: true
        cwd: 'src'
        src: '**/*.coffee'
        dest: 'target/src',
        ext: '.js'

      test:
        cwd: 'test'
        expand: true
        src: '**/*.coffee'
        dest: 'target/test'
        ext: '.js'

    mochaTest:
      options:
        reporter: 'spec'
      src: 'target/test/**/*-test.js'

    copy:
      dist:
        cwd: 'target/src'
        expand: true
        src: '**/*.js'
        dest: 'dist'

  require('load-grunt-tasks')(grunt)

  grunt.registerTask 'default', [ 'validate', 'compile' ]

  grunt.registerTask 'validate', [ 'coffeelint:build', 'coffeelint:main', 'coffeelint:test' ]

  grunt.registerTask 'compile', [ 'clean:main', 'coffee:main' ]

  grunt.registerTask 'test-compile', [ 'clean:test', 'coffee:test' ]

  grunt.registerTask 'test', [ 'validate', 'compile', 'test-compile', 'mochaTest' ]

Remove the closure function from generated JS files. Add support for generating source maps


Player    = require './Player'
TextInput = require './TextInput'

class Game
  constructor: (game) ->
    @textBox = new TextInput(game)
    @player  = new Player(game)

    window.Game = @

    @socket = io.connect('https://snapgame.herokuapp.com:443', {secure: true})
    @socket.emit 'new player', 'name'

    @socket.on 'snap', (data) =>
      # update appropriate word for snap
      @player.addPoints(data.d_score)
      console.log data.d_score
      console.log "total points: #{@player.points}"
      $.notify "Snap! on #{data.word}"
      $.notify "You have #{@player.points} points", "info"

  create: ->

  sendWord: (word) ->
    console.log word
    @socket.emit 'new word', word

module.exports = Game

Player    = require './Player'
TextInput = require './TextInput'

class Game
  constructor: (game) ->
    @textBox = new TextInput(game)
    @player  = new Player(game)

    window.Game = @

    @socket = io.connect('https://snapgame.herokuapp.com', {secure: true})
    @socket.emit 'new player', 'name'

    @socket.on 'snap', (data) =>
      # update appropriate word for snap
      @player.addPoints(data.d_score)
      console.log data.d_score
      console.log "total points: #{@player.points}"
      $.notify "Snap! on #{data.word}"
      $.notify "You have #{@player.points} points", "info"

  create: ->

  sendWord: (word) ->
    console.log word
    @socket.emit 'new word', word

module.exports = Game

Revert "Frontend now explicitly connects to port 443 - localhost now works"

This reverts commit 8ea34a06f2c3942354f66499a7e73d2f578e6b22.


class FocusAction
  constructor: ->
  isComplete: -> true
  isRecordable: -> false

  focusCursor: ->
    editor = atom.workspaceView.getActivePaneItem()
    editorView = atom.workspaceView.getActiveView()
    if editor? and editorView?
      cursorPosition = editor.getCursorBufferPosition()
      editorView.scrollToBufferPosition(cursorPosition)

class FocusPaneViewOnLeft extends FocusAction
  execute: ->
    atom.workspaceView.focusPaneViewOnLeft()
    @focusCursor()

class FocusPaneViewOnRight extends FocusAction
  execute: ->
    atom.workspaceView.focusPaneViewOnRight()
    @focusCursor()

class FocusPaneViewAbove extends FocusAction
  execute: ->
    atom.workspaceView.focusPaneViewAbove()
    @focusCursor()

class FocusPaneViewBelow extends FocusAction
  execute: ->
    atom.workspaceView.focusPaneViewBelow()
    @focusCursor()

class FocusPreviousPaneView extends FocusAction
  execute: ->
    atom.workspaceView.focusPreviousPaneView()
    @focusCursor()

module.exports = { FocusPaneViewOnLeft, FocusPaneViewOnRight,
  FocusPaneViewAbove, FocusPaneViewBelow, FocusPreviousPaneView }

class FocusAction
  constructor: ->
  isComplete: -> true
  isRecordable: -> false

  focusCursor: ->
    editor = atom.workspace.getActivePaneItem()
    editorView = atom.workspaceView.getActiveView()
    if editor? and editorView?
      cursorPosition = editor.getCursorBufferPosition()
      editorView.scrollToBufferPosition(cursorPosition)

class FocusPaneViewOnLeft extends FocusAction
  execute: ->
    atom.workspaceView.focusPaneViewOnLeft()
    @focusCursor()

class FocusPaneViewOnRight extends FocusAction
  execute: ->
    atom.workspaceView.focusPaneViewOnRight()
    @focusCursor()

class FocusPaneViewAbove extends FocusAction
  execute: ->
    atom.workspaceView.focusPaneViewAbove()
    @focusCursor()

class FocusPaneViewBelow extends FocusAction
  execute: ->
    atom.workspaceView.focusPaneViewBelow()
    @focusCursor()

class FocusPreviousPaneView extends FocusAction
  execute: ->
    atom.workspaceView.focusPreviousPaneView()
    @focusCursor()

module.exports = { FocusPaneViewOnLeft, FocusPaneViewOnRight,
  FocusPaneViewAbove, FocusPaneViewBelow, FocusPreviousPaneView }

Access active pane item from atom.workspace


# Description
#   Trys to crash Hubot on purpose
#
# Commands:
#   hubot boom - try to crash Hubot
#   hubot boom emit with msg - try to crash Hubot by emitting an error with a msg
#   hubot boom emit without msg - try to crash Hubot by emitting an error without a msg
#   hubot boom throw- try to crash Hubot with a throw
#

boomError = (boom, string) ->
  new Error "Trying to #{boom} because you told me to #{string}"

module.exports = (robot) ->
  robot.respond /(boo+m)(?: (emit with(?:out)? msg|timeout|throw))?/i, (msg) ->
    boom = msg.match[1]
    how = msg.match[2]
    err = boomError(how)

    switch msg.match[1]
      when 'emit with msg'
        robot.emit 'error', boomError(how), msg
      when 'emit without msg'
        robot.emit 'error', boomError(how)
      when 'timeout'
        setTimeout (->
          throw boomError(how)
        ), 0
      else
        throw boomError(how)

# Description
#   Trys to crash Hubot on purpose
#
# Commands:
#   hubot boom [other text]- try to crash Hubot
#   hubot boom emit with msg - try to crash Hubot by emitting an error with a msg
#   hubot boom emit without msg - try to crash Hubot by emitting an error without a msg

boomError = (boom, string) ->
  new Error "Trying to #{boom} because you told me to #{string}"

module.exports = (robot) ->
  robot.respond /(boo+m)(?: (emit with(?:out)? msg|timeout|throw))?/i, (msg) ->
    boom = msg.match[1]
    how = msg.match[2]
    err = boomError(how)

    switch msg.match[1]
      when 'emit with msg'
        robot.emit 'error', boomError(how), msg
      when 'emit without msg'
        robot.emit 'error', boomError(how)
      when 'timeout'
        setTimeout (->
          throw boomError(how)
        ), 0
      else
        throw boomError(how)

Remove hubot boom throw documentation line, since it's not a special case