View = require './view'

RequestTasks = require '../collections/RequestTasks'

class RequestView extends View

    template: require './templates/request'

    initialize: =>
        @request = app.allRequests[@options.requestId]

        @requestTasks = new RequestTasks [], requestId: @options.requestId
        @requestTasks.fetch().done => @render fetchDone = true

    render: (fetchDone = false) =>
        if not @request
            vex.dialog.alert("
Could not open a request by that ID.

#{ @options.requestId }
")
            return

        context =
            request: @request
            fetchDone: fetchDone
            requestTasksActive: _.filter(@requestTasks.toJSON(), (t) -> t.isActive)
            requestTasksHistorical: _.first(_.filter(@requestTasks.toJSON(), (t) -> not t.isActive), 100)
            requestTasksScheduled: _.filter(app.collections.tasksScheduled.toJSON(), (t) => t.requestId is @options.requestId)

        @$el.html @template context

        @setupEvents()

        utils.setupSortableTables()

    setupEvents: ->
        @$el.find('.view-json').unbind('click').click (event) ->
            utils.viewJSON 'task', $(event.target).data('task-id')

module.exports = RequestView

View = require './view'

RequestTasks = require '../collections/RequestTasks'

class RequestView extends View

    template: require './templates/request'

    initialize: =>
        @request = app.allRequests[@options.requestId]

        @requestTasks = new RequestTasks [], requestId: @options.requestId
        @requestTasks.fetch().done =>
            @fetchDone = true
            @render()

    render: =>
        if not @request
            vex.dialog.alert("
Could not open a request by that ID.

#{ @options.requestId }
")
            return

        context =
            request: @request
            fetchDone: @fetchDone
            requestTasksActive: _.filter(@requestTasks.toJSON(), (t) -> t.isActive)
            requestTasksHistorical: _.first(_.filter(@requestTasks.toJSON(), (t) -> not t.isActive), 100)
            requestTasksScheduled: _.filter(app.collections.tasksScheduled.toJSON(), (t) => t.requestId is @options.requestId)

        @$el.html @template context

        @setupEvents()

        utils.setupSortableTables()

    setupEvents: ->
        @$el.find('.view-json').unbind('click').click (event) ->
            utils.viewJSON 'task', $(event.target).data('task-id')

module.exports = RequestView

Fix logic bug with fetchDone state

Fix logic bug with fetchDone state


window.app = _.extend {}, Backbone.Events,
  Models: {}
  Collections: {}
  Views: {}
  Routers: {}

  init: ->
    @auth()

  auth: ->
    new this.Views.Authenticate()

  ready: ->
    $('#app').show()
    @repositories = new this.Collections.Repositories()
    @notifications = new this.Collections.Notifications()

    new this.Views.Lists(repositories: @repositories)
    new this.Routers.Notifications(
      notifications: @notifications,
      repositories: @repositories
    )

    new this.Views.Shortcuts(
      repositories: @repositories,
      notifications: @notifications
    )

    Backbone.history.start() unless History.started

    Backbone.history.navigate 'all', trigger: true

  isDevelopment: ->
    localStorage['dev']?

  update: ->
    applicationCache.update() unless applicationCache.status == applicationCache.UNCACHED

$ ->
  app.init()

$.ajaxSetup
  headers:
    'Accept': 'application/vnd.github.v3.html+json'

  # the Notifications API makes heavy use of the If-Modified-Since header for
  # determining what to respond with. This disables any HTTP caching until
  # proper local caching is implemented.
  cache: false

# Update app cache every 60 seconds and when leaving the page
setInterval app.update, 60 * 1000
$(window).on 'beforeunload', app.update

$(window).ajaxError (ev, xhr) ->
  # unset the token if the API responds with a 401, and try to re-authenticate.
  if xhr.status is 401
    localStorage['token'] = ""
    window.app.auth()

window.app = _.extend {}, Backbone.Events,
  Models: {}
  Collections: {}
  Views: {}
  Routers: {}

  init: ->
    @auth()

  auth: ->
    new this.Views.Authenticate()

  ready: ->
    $('#app').show()
    @repositories = new this.Collections.Repositories()
    @notifications = new this.Collections.Notifications()

    new this.Views.Lists(repositories: @repositories)
    new this.Routers.Notifications(
      notifications: @notifications,
      repositories: @repositories
    )

    new this.Views.Shortcuts(
      repositories: @repositories,
      notifications: @notifications
    )

    Backbone.history.start() unless Backbone.History.started

    Backbone.history.navigate 'all', trigger: true

  isDevelopment: ->
    localStorage['dev']?

  update: ->
    applicationCache.update() unless applicationCache.status == applicationCache.UNCACHED

$ ->
  app.init()

$.ajaxSetup
  headers:
    'Accept': 'application/vnd.github.v3.html+json'

  # the Notifications API makes heavy use of the If-Modified-Since header for
  # determining what to respond with. This disables any HTTP caching until
  # proper local caching is implemented.
  cache: false

# Update app cache every 60 seconds and when leaving the page
setInterval app.update, 60 * 1000
$(window).on 'beforeunload', app.update

$(window).ajaxError (ev, xhr) ->
  # unset the token if the API responds with a 401, and try to re-authenticate.
  if xhr.status is 401
    localStorage['token'] = ""
    window.app.auth()

Fix reference to backbone history

Fix reference to backbone history


files = '

  fastdev
  compile

'.trim().split(/[^a-zA-Z\/\.]+/).map (s)-> './'+s

module.exports = (tintan)->

  require(file) tintan for file in files

  namespace 'tintan', ->

    task 'build', ->
      console.log 'building '

files = '

  fastdev
  compile

'.trim().split(/[^a-zA-Z\/\.]+/).map (s)-> './'+s

module.exports = (tintan)->

  require(file) tintan for file in files

  namespace 'tintan', ->

    task 'build', ['^compile'], ->
      console.log 'done'.green + ' building ' + tintan.constructor.appXML.name()

Add compile task as prereq of tintan:build


{exec} = require 'child_process'
path   = require 'path'
fs     = require 'fs'

REFRESH_RATE = 500        # in msecs
RUN_PATH     = path.join __dirname, "run_test.coffee"
seen         = {}         # cache of file names
shouldRun    = true       # if a change in file was detected
canRun       = true       # if tests are running right now, don't run.

runTest = (curr, prev) ->
  if curr.mtime > prev.mtime
    console.log "[beantest] Change in file detected. Rerunning tests...\n"
    shouldRun = true

@beantest = ->
  if shouldRun and canRun
    exec "coffee #{RUN_PATH}", (error, stdout, stderr) ->
      console.log(if error then stderr else stdout)
      canRun = true
    shouldRun = canRun = false
  exec 'find . | grep "\\.coffee$"', (error, stdout, stderr) ->
    fileNames = stdout.split('\n')
    for fileName in fileNames
      if fileName not of seen
        seen[fileName] = true
        fs.watchFile fileName, { interval: REFRESH_RATE }, runTest
    setTimeout @beantest, REFRESH_RATE

{exec} = require 'child_process'
path   = require 'path'
fs     = require 'fs'

REFRESH_RATE = 500        # in msecs
RUN_PATH     = path.join __dirname, "run_test.coffee"
seen         = {}         # cache of file names
shouldRun    = true       # if a change in file was detected
canRun       = true       # if tests are running right now, don't run.

runTest = (curr, prev) ->
  if curr.mtime > prev.mtime
    console.log "[beantest] Change in file detected. Rerunning tests...\n"
    shouldRun = true

beantest = ->
  if shouldRun and canRun
    exec "coffee #{RUN_PATH}", (error, stdout, stderr) ->
      console.log(if error then stderr else stdout)
      canRun = true
    shouldRun = canRun = false
  exec 'find . | grep "\\.coffee$"', (error, stdout, stderr) ->
    fileNames = stdout.split('\n')
    for fileName in fileNames
      if fileName not of seen
        seen[fileName] = true
        fs.watchFile fileName, { interval: REFRESH_RATE }, runTest
    setTimeout beantest, REFRESH_RATE

@beantest = beantest

Fix silly 'this' context error.

Fix silly 'this' context error.


_ = require('underscore')

class DeployMonitor

  timeout = 10000

  constructor: ()->
    @_deploy = null
    @_chat = null
    @_currentTimeout = 0

  setDeploy: (deploy, chat)->
    @_deploy = deploy
    @_chat = chat
    @_deploy.on('change', ()=>
      @_resetTimeoutMonitor()
      @_timeoutMonitor()
    )
    @_deploy.on('close', ()=>
      @.removeDeploy()
    )
    @_deploy.on('error', ()=>
      @.removeDeploy()
    )

  hasDeploy: ()->
    return null != @_deploy

  getDeploy: ()->
    return @_deploy

  removeDeploy: ()->
    @_deploy = null

  _timeoutMonitor: _.debounce(()->
    if !@.hasDeploy()
      return
    @_currentTimeout += timeout
    @_chat.send "Running #{@_currentTimeout}ms: #{getLastText(@_deploy.data.output)}"
    @_timeoutMonitor()
  , timeout)

  _resetTimeoutMonitor: ()->
    if @_currentTimeout > 0
      @_chat.send "Continuing..."
    @_currentTimeout = 0

  getLastText = (text)->
    textLines = text.split(/\r?\n/)
    n = textLines.length - 1
    while(!textLines[n].trim() && n > 0)
      n--
    return textLines[n]

module.exports = DeployMonitor

_ = require('underscore')

class DeployMonitor

  timeout = 10000

  constructor: ()->
    @_deploy = null
    @_chat = null
    @_currentTimeout = 0

  setDeploy: (deploy, chat)->
    @_deploy = deploy
    @_chat = chat
    @_deploy.on('change', ()=>
      @_resetTimeoutMonitor()
      @_timeoutMonitor()
    )
    @_deploy.on('close', ()=>
      @.removeDeploy()
    )
    @_deploy.on('error', ()=>
      @.removeDeploy()
    )

  hasDeploy: ()->
    return null != @_deploy

  getDeploy: ()->
    return @_deploy

  removeDeploy: ()->
    @_deploy.removeAllEventListeners()
    @_deploy = null
    @_chat = null

  _timeoutMonitor: _.debounce(()->
    if !@.hasDeploy()
      return
    @_currentTimeout += timeout
    @_chat.send "Running #{@_currentTimeout}ms: #{getLastText(@_deploy.data.output)}"
    @_timeoutMonitor()
  , timeout)

  _resetTimeoutMonitor: ()->
    if @_currentTimeout > 0
      @_chat.send "Continuing..."
    @_currentTimeout = 0

  getLastText = (text)->
    textLines = text.split(/\r?\n/)
    n = textLines.length - 1
    while(!textLines[n].trim() && n > 0)
      n--
    return textLines[n]

module.exports = DeployMonitor

Clean all the data of deploy job when `removeDeploy`.


define (require) ->
  _ = require('underscore')
  BaseView = require('cs!helpers/backbone/views/base')
  BookPopoverView = require('cs!./popovers/book/book')
  template = require('hbs!./header-template')
  require('less!./header')

  return class MediaHeaderView extends BaseView
    template: template
    templateHelpers: () ->
      currentPage = @model.get('currentPage')

      if currentPage
        currentPage = currentPage.toJSON()
        currentPage.encodedTitle = encodeURI(currentPage.title)
      else
        currentPage = {
          title: 'Untitled'
          encodedTitle: 'Untitled'
          authors: []
        }

      downloads = @model.get('downloads')
      pageDownloads = currentPage?.get?('downloads')

      return {
        currentPage: currentPage
        hasDownloads: (_.isArray(downloads) and downloads?.length) or
          (_.isArray(pageDownloads) and pageDownloads?.length)
      }

    regions:
      'button': '.info .btn'

    events:
      'click .summary h5': 'toggleSummary'

    initialize: () ->
      super()
      @listenTo(@model, 'change:downloads change:buyLink changePage', @render)
      @listenTo(@model, 'change:edit', @toggleEdit)

    onRender: () ->
      @regions.button.append new BookPopoverView
        model: @model
        owner: @$el.find('.info .btn')

    toggleSummary: (e) ->
      $summary = @$el.find('.summary')

      $summary.find('h5').toggleClass('active')
      @$el.find('.abstract').toggle()

    toggleEdit: () ->
      @$el.children('.media-header').children('h2').attr('contenteditable', @model.get('edit'))

define (require) ->
  _ = require('underscore')
  EditableView = require('cs!helpers/backbone/views/editable')
  BookPopoverView = require('cs!./popovers/book/book')
  template = require('hbs!./header-template')
  require('less!./header')

  return class MediaHeaderView extends EditableView
    template: template
    templateHelpers: () ->
      currentPage = @model.get('currentPage')

      if currentPage
        currentPage = currentPage.toJSON()
        currentPage.encodedTitle = encodeURI(currentPage.title)
      else
        currentPage = {
          title: 'Untitled'
          encodedTitle: 'Untitled'
          authors: []
        }

      downloads = @model.get('downloads')
      pageDownloads = currentPage?.get?('downloads')

      return {
        currentPage: currentPage
        hasDownloads: (_.isArray(downloads) and downloads?.length) or
          (_.isArray(pageDownloads) and pageDownloads?.length)
      }

    editable: '.media-header > h2'

    regions:
      'button': '.info .btn'

    events:
      'click .summary h5': 'toggleSummary'

    initialize: () ->
      super()
      @listenTo(@model, 'change:downloads change:buyLink changePage', @render)

    onRender: () ->
      @regions.button.append new BookPopoverView
        model: @model
        owner: @$el.find('.info .btn')

    toggleSummary: (e) ->
      $summary = @$el.find('.summary')

      $summary.find('h5').toggleClass('active')
      @$el.find('.abstract').toggle()

Convert to using editable view

Convert to using editable view


# 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'
  'cmd-alt-up': 'table-edit:insert-row-before'
  'cmd-alt-down': 'table-edit:insert-row-after'
  'cmd-alt-left': 'table-edit:insert-column-before'
  'cmd-alt-right': 'table-edit:insert-column-after'

'.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'
  'cmd-alt-up': 'table-edit:insert-row-before'
  'cmd-alt-down': 'table-edit:insert-row-after'
  'cmd-alt-backspace': 'table-edit:delete-row'
  'cmd-alt-left': 'table-edit:insert-column-before'
  'cmd-alt-right': 'table-edit:insert-column-after'
  'cmd-alt-ctrl-backspace': 'table-edit:delete-column'

'.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 keybindings for deletion commands

Add keybindings for deletion commands


crypto = require 'crypto'
Reporter = require './reporter'

module.exports =
  activate: ({sessionLength}) ->
    if atom.config.get('metrics.userId')
      @begin(sessionLength)
    else
      @getUserId (userId) -> atom.config.set('metrics.userId', userId)
      @begin(sessionLength)

  serialize: ->
    sessionLength: Date.now() - @sessionStart

  begin: (sessionLength) ->
    @sessionStart = Date.now()

    Reporter.sendEvent('window', 'ended', sessionLength) if sessionLength
    Reporter.sendEvent('window', 'started')
    atom.workspaceView.on 'pane:item-added', (event, item) ->
      Reporter.sendPaneItem(item)

    if atom.getLoadSettings().shellLoadTime?
      # Only send shell load time for the first window
      Reporter.sendTiming('shell', 'load', atom.getLoadSettings().shellLoadTime)

    process.nextTick ->
      # Wait until window is fully bootstrapped before sending the load time
      Reporter.sendTiming('core', 'load', atom.getWindowLoadTime())

  getUserId: (callback) ->
    require('getmac').getMac (error, macAddress) =>
      if error?
        callback require('guid').raw()
      else
        callback crypto.createHash('sha1').update(macAddress, 'utf8').digest('hex')

crypto = require 'crypto'
Reporter = require './reporter'

module.exports =
  activate: ({sessionLength}) ->
    if atom.config.get('metrics.userId')
      @begin(sessionLength)
    else
      @getUserId (userId) -> atom.config.set('metrics.userId', userId)
      @begin(sessionLength)

  serialize: ->
    sessionLength: Date.now() - @sessionStart

  begin: (sessionLength) ->
    @sessionStart = Date.now()

    Reporter.sendEvent('window', 'ended', sessionLength) if sessionLength
    Reporter.sendEvent('window', 'started')
    atom.workspaceView.on 'pane:item-added', (event, item) ->
      Reporter.sendPaneItem(item)

    if atom.getLoadSettings().shellLoadTime?
      # Only send shell load time for the first window
      Reporter.sendTiming('shell', 'load', atom.getLoadSettings().shellLoadTime)

    process.nextTick ->
      # Wait until window is fully bootstrapped before sending the load time
      Reporter.sendTiming('core', 'load', atom.getWindowLoadTime())

  getUserId: (callback) ->
    require('getmac').getMac (error, macAddress) =>
      if error?
        callback require('node-uuid').v4()
      else
        callback crypto.createHash('sha1').update(macAddress, 'utf8').digest('hex')

Replace guid package call with node-uuid call


Spine = require('spine')

class window.Task extends Spine.Model

  # Set model properties
  @configure 'Task',
    'name',
    'date',
    'notes',
    'completed',
    'priority',
    'list'

  @extend @Local

  @active: (list) =>
    @select (task) ->
      !task.completed and (if list then (task.list is list) else yes)

  @completed: (list) =>
    @select (task) ->
      task.completed and (task.list is list if list)

  @list: (listId) =>
    return [] unless listId
    if listId is "all" then return @byPriority()
    @byPriority().filter (task) ->
        task.list is listId

  @byPriority: ->
    @all().sort (a, b) ->
      diff = a.priority - b.priority
      if diff is 0
        # If the priorities are the same
        # then sort by name
        b.name.localeCompare(a.name)
      else diff

  @filter: (query) ->
    return all() unless query
    query = query.toLowerCase()
    @select (item) ->
      item.name?.toLowerCase().indexOf(query) > -1

  @tag: (tag) ->
    return [] unless tag
    tag = tag.toLowerCase()
    @select (item) ->
      item.name?.toLowerCase().indexOf('#'+tag) > -1

module.exports = Task

Spine = require('spine')

class window.Task extends Spine.Model

  # Set model properties
  @configure 'Task',
    'name',
    'date',
    'notes',
    'completed',
    'priority',
    'list'

  @extend @Local

  @active: (list) =>
    @select (task) ->
      !task.completed and (if list then (task.list is list) else yes)

  @completed: (list) =>
    @select (task) ->
      task.completed and (task.list is list if list)

  @list: (listId) =>
    return [] unless listId
    if listId is "all" then return @byPriority()
    @byPriority().filter (task) ->
        task.list is listId

  @byPriority: ->
    @all().sort (a, b) ->
      diff = a.priority - b.priority
      if diff is 0
        # If the priorities are the same
        # then sort by name
        b.name.localeCompare(a.name)
      else diff

  @filter: (query) ->
    return all() unless query
    query = query.toLowerCase().split(" ")
    results = []
    @select (item) ->
      matches = yes
      for word in query
        regex = new RegExp(word, "i")
        if not item.name?.match(regex) then matches = no
      results.push(item) unless matches is no
    return results

  @tag: (tag) ->
    return [] unless tag
    tag = tag.toLowerCase()
    @select (item) ->
      item.name?.toLowerCase().indexOf('#'+tag) > -1

module.exports = Task

Make search better by ignoring word order


url = require 'url'
fs = require 'fs-plus'

MarkdownPreviewView = require './markdown-preview-view'

module.exports =
  activate: ->
    atom.workspaceView.command 'markdown-preview:show', =>
      @show()

    atom.workspace.registerOpener (uriToOpen) ->
      {protocol, host, pathname} = url.parse(uriToOpen)
      pathname = decodeURI(pathname) if pathname
      return unless protocol is 'markdown-preview:'

      if host is 'editor'
        new MarkdownPreviewView(editorId: pathname.substring(1))
      else
        new MarkdownPreviewView(filePath: pathname)

  show: ->
    editor = atom.workspace.getActiveEditor()
    return unless editor?

    previousActivePane = atom.workspace.getActivePane()
    uri = "markdown-preview://editor/#{editor.id}"
    atom.workspace.open(uri, split: 'right', searchAllPanes: true).done (markdownPreviewView) ->
      if markdownPreviewView instanceof MarkdownPreviewView
        markdownPreviewView.renderMarkdown()
        previousActivePane.activate()

url = require 'url'
fs = require 'fs-plus'

MarkdownPreviewView = require './markdown-preview-view'

module.exports =
  configDefaults:
    grammars: [
      'source.gfm'
      'text.plain'
      'text.plain.null-grammar'
    ]
  activate: ->
    atom.workspaceView.command 'markdown-preview:show', =>
      @show()

    atom.workspace.registerOpener (uriToOpen) ->
      {protocol, host, pathname} = url.parse(uriToOpen)
      pathname = decodeURI(pathname) if pathname
      return unless protocol is 'markdown-preview:'

      if host is 'editor'
        new MarkdownPreviewView(editorId: pathname.substring(1))
      else
        new MarkdownPreviewView(filePath: pathname)

  show: ->
    editor = atom.workspace.getActiveEditor()
    return unless editor?

    grammars = atom.config.get('markdown-preview.grammars') ? []
    return unless editor.getGrammar().scopeName in grammars

    previousActivePane = atom.workspace.getActivePane()
    uri = "markdown-preview://editor/#{editor.id}"
    atom.workspace.open(uri, split: 'right', searchAllPanes: true).done (markdownPreviewView) ->
      if markdownPreviewView instanceof MarkdownPreviewView
        markdownPreviewView.renderMarkdown()
        previousActivePane.activate()

Make whitelisted grammars a config setting


'.platform-darwin, .platform-darwin .simple-flake8 .editor':
  'cmd-shift-8': 'simple-flake8:toggle'

'.platform-win32, .platform-win32 .simple-flake8 .editor':
  'ctrl-shift-8': 'simple-flake8:toggle'

'.platform-linux, .platform-linux .simple-flake8 .editor':
  'ctrl-shift-8': 'simple-flake8:toggle'

'.platform-darwin atom-text-editor, .platform-darwin .simple-flake8':
  'cmd-shift-8': 'simple-flake8:toggle'

'.platform-win32 atom-text-editor, .platform-win32 .simple-flake8':
  'ctrl-shift-8': 'simple-flake8:toggle'

'.platform-linux atom-text-editor, .platform-linux .simple-flake8':
  'ctrl-shift-8': 'simple-flake8:toggle'

Fix deprecation of editor class in keymap.


import {GROUPS} from '../../client/lib/informaticsGroups'
import {START_SUBMITS_DATE} from '../api/dashboard'
import send from '../metrics/graphite'
import Result from "../models/result"

export default sendMetrics = () ->
    queries =
        ok: {ok: 1, lastSubmitTime: {$gt: START_SUBMITS_DATE}},
        ps: {ps: 1}
    metrics = {}
    for key, query of queries
        for group, _ of GROUPS
            query["userList"] = group
            metrics["#{key}.#{group}"] = (await Result.find(query)).length
    await send(metrics)

import {GROUPS} from '../../client/lib/informaticsGroups'
import {START_SUBMITS_DATE} from '../api/dashboard'
import send from '../metrics/graphite'
import Result from "../models/result"

export default sendMetrics = () ->
    queries =
        ok: {ok: 1, lastSubmitTime: {$gt: START_SUBMITS_DATE}},
        ps: {ps: 1}
    metrics = {}
    for key, query of queries
        query["total"] = 1
        for group, _ of GROUPS
            query["userList"] = group
            metrics["#{key}.#{group}"] = (await Result.find(query)).length
    await send(metrics)

Send metrics only for problems, not for tables


### logger.coffee ###

colors = require 'colors'
winston = require 'winston'
util = require 'util'

class cli extends winston.Transport

  name: 'cli'

  constructor: (options) ->
    super(options)
    @quiet = options.quiet or false

  log: (level, msg, meta, callback) ->
    if level == 'error'
      process.stderr.write "\n  error".red + " #{ msg }\n"
      if @level == 'verbose' && meta?
        if meta.stack?
          stack = meta.stack.substr meta.stack.indexOf('\n') + 1
          process.stderr.write stack + "\n\n"
        for key, value of meta
          if key in ['message', 'stack']
            continue
          pval = util.inspect(value, false, 2, true).replace(/\n/g, '\n    ')
          process.stderr.write "    #{ key }: #{ pval }\n"
      else
        process.stderr.write "\n"
    else if !@quiet
      switch level
        when 'verbose'
          msg = msg.yellow
      if meta
        msg += util.format ' %j', meta
      process.stdout.write "  #{ msg }\n"

    @emit 'logged'
    callback null, true

transports = exports.transports = [
  new cli {level: 'info'}
]

exports.logger = new winston.Logger
  exitOnError: true
  transports: transports

### logger.coffee ###

colors = require 'colors'
winston = require 'winston'
util = require 'util'

class cli extends winston.Transport

  name: 'cli'

  constructor: (options) ->
    super(options)
    @quiet = options.quiet or false

  log: (level, msg, meta, callback) ->
    if level == 'error'
      process.stderr.write "\n  error".red + " #{ msg }\n"
      if @level == 'verbose' && meta?
        if meta.stack?
          stack = meta.stack.substr meta.stack.indexOf('\n') + 1
          process.stderr.write stack + "\n\n"
        for key, value of meta
          if key in ['message', 'stack']
            continue
          pval = util.inspect(value, false, 2, true).replace(/\n/g, '\n    ')
          process.stderr.write "    #{ key }: #{ pval }\n"
      else
        process.stderr.write "\n"
    else if !@quiet
      switch level
        when 'verbose'
          msg = "verbose: #{ msg }"
      if meta
        msg += util.format ' %j', meta
      process.stdout.write "  #{ msg }\n"

    @emit 'logged'
    callback null, true

transports = exports.transports = [
  new cli {level: 'info'}
]

exports.logger = new winston.Logger
  exitOnError: true
  transports: transports

Remove ugly yellow color from verbose log messages


window.Toast = {}
toastQueue = []
showing = false

Toast.processToast = () ->
  if toastQueue.length > 0
    toast = toastQueue.shift()
    $("#toasts").find(':first-child').remove()
    if $(toast).data("toast-type") == "error"
      Toast.toastError $(toast).text()
    else
      Toast.toastMessage $(toast).text()

Toast.toastMessage = (message) ->
  if !showing
    $('#toast').text message
    $('#toast').addClass 'active'
    showing = true
    setTimeout(timeout, 5000)
  else
    toastQueue.push("
#{message}
")

Toast.toastError = (message) ->
  if !showing
    $('#toast').text(message)
    $('#toast').addClass 'error'
    $('#toast').addClass 'active'
    showing = true
    setTimeout(timeout, 5000)
  else
    toastQueue.push("
#{message}
")

timeout = () ->
  $('#toast').removeClass 'active'
  $('#toast').removeClass 'error'
  setTimeout( () =>
    showing = false
    Toast.processToast()
  , 200);

$(document).on "ready page:load", ->
  toastQueue = $.makeArray $('#toasts').children()
  Toast.processToast()

window.Toast = {}
toastQueue = []
showing = false

Toast.processToast = () ->
  if toastQueue.length > 0
    toast = toastQueue.shift()
    $("#toasts").find(':first-child').remove()
    if $(toast).data("toast-type") == "error"
      Toast.toastError $(toast).text()
    else
      Toast.toastMessage $(toast).text()

Toast.toastMessage = (message) ->
  if !showing
    $('#toast').text message
    $('#toast').addClass 'active'
    showing = true
    setTimeout(timeout, 5000)
  else
    toastQueue.push("
#{message}
")

Toast.toastError = (message) ->
  if !showing
    $('#toast').addClass 'error'
    $('#toast').text(message)
    $('#toast').addClass 'active'
    showing = true
    setTimeout(timeout, 5000)
  else
    toastQueue.push("
#{message}
")

timeout = () ->
  $('#toast').removeClass 'active'
  $('#toast').removeClass 'error'
  setTimeout( () =>
    showing = false
    Toast.processToast()
  , 200);

$(document).on "ready page:load", ->
  toastQueue = $.makeArray $('#toasts').children()
  Toast.processToast()

Add error class first for toasts

Add error class first for toasts


"use strict"

filter = require './filter'
search = require './search'
beautify = require './beautify'
validate = require './validate'

defaults =
  version: 1.1
  retweets: true
  mentions: true
  default_profile_images: true
  count: true
  lang: []
  mute: []
  spam: []
  media:
    width: 500
    height: 500
    class: "twtcst_image"

oauth_error = """You must specify oauth in your options object:
oauth = {
  consumer_key: ''
  consumer_secret: ''
  token: ''
  token_secret: ''
}
"""

parse = (words, oauth, options) ->
  if not words or words.length < 1
    throw new Error "Set words for search"

  unless oauth.consumer_key and oauth.consumer_secret and oauth.token and oauth.token_secret
    throw new Error oauth_error

  result = JSON.parse JSON.stringify defaults

  result[key] = value for own key, value of options

  if result.hash_length
    result.hash_length = parseInt(result.hash_length, 10) || 1

  result.words = words
  result.oauth = oauth

  result

module.exports = (words, oauth = {}, options = {}) ->

  options = parse words, oauth, options

  if options.count
    counter = require('./counter')(options.count_file)
  else
    counter = null

  beautify = beautify options
  validate = validate options

  filter: filter options, beautify, validate, counter
  search: search options, beautify, validate, counter

"use strict"

filter = require './filter'
search = require './search'
beautify = require './beautify'
validate = require './validate'

defaults =
  version: 1.1
  retweets: true
  mentions: true
  default_profile_images: true
  count: true
  lang: []
  mute: []
  spam: []
  media:
    width: 500
    height: 500
    class: "twtcst_image"

oauth_error = """You must specify oauth in your options object:
oauth = {
  consumer_key: ''
  consumer_secret: ''
  token: ''
  token_secret: ''
}
"""

parse = (words, oauth, options) ->
  if not words or words.length < 1
    throw new Error "Set words for search"

  unless oauth.consumer_key and oauth.consumer_secret and oauth.token and oauth.token_secret
    throw new Error oauth_error

  result = JSON.parse JSON.stringify defaults

  result[key] = value for own key, value of options

  if result.hash_length
    result.hash_length = parseInt(result.hash_length, 10) || 1

  result.words = words
  result.oauth = oauth

  result

module.exports = (words, oauth = {}, options = {}) ->

  options = parse words, oauth, options

  if options.count
    counter = require('./counter')(options.count_file)
  else
    counter = null

  _beautify = beautify options
  _validate = validate options

  filter: filter options, _beautify, _validate, counter
  search: search options, _beautify, _validate, counter

Allow multiple using validate and beautify


config = require './config'

updateRoleStats = (cfg, stats) ->
  if stats.average
    delete cfg.p if cfg.p
    cfg.process = stats.average
    delete cfg.stddev if cfg.stddev # no longer valid
  if stats.stddev
    cfg.stddev = stats.stddev

updateStats = (cfg, stats) ->
  for rolename, rolestats of stats
    roleconfig = cfg[rolename]
    continue if not roleconfig

    updateRoleStats roleconfig, rolestats
  return cfg

collectStdin = (callback) ->
  data = ""

  process.stdin.on 'data', (chunk) ->
    data += chunk.toString()
  process.stdin.on 'end', () ->
    return callback null, data

transformFile = (filepath, transformFunc, callback) ->
  fs = require 'fs'

  fs.readFile filepath, { encoding: 'utf-8' }, (err, contents) ->
    return callback err if err
    transformed = transformFunc contents
    fs.writeFile filepath, transformed, { encoding: 'utf-8', flag: 'w+' }, (err) ->
      return callback err

exports.main = main = () ->

  configfile = process.argv[2]
  throw new Error 'no config file specified' if not configfile

  collectStdin (err, data) ->
    stats = JSON.parse data
    transform = (contents) ->
      cfg = config.parseOnly contents
      cfg = updateStats cfg, stats
      return config.serialize cfg

    transformFile configfile, transform, (err) ->
      throw err if err
      console.log 'Updated', configfile
      process.exit(0)

main() if not module.parent

config = require './config'

updateRoleStats = (cfg, stats) ->
  if stats.average
    delete cfg.p if cfg.p
    cfg.processing = stats.average
    delete cfg.stddev if cfg.stddev # no longer valid
  if stats.stddev
    cfg.stddev = stats.stddev

updateStats = (cfg, stats) ->
  for rolename, rolestats of stats
    roleconfig = cfg[rolename]
    continue if not roleconfig

    updateRoleStats roleconfig, rolestats
  return cfg

collectStdin = (callback) ->
  data = ""

  process.stdin.on 'data', (chunk) ->
    data += chunk.toString()
  process.stdin.on 'end', () ->
    return callback null, data

transformFile = (filepath, transformFunc, callback) ->
  fs = require 'fs'

  fs.readFile filepath, { encoding: 'utf-8' }, (err, contents) ->
    return callback err if err
    transformed = transformFunc contents
    fs.writeFile filepath, transformed, { encoding: 'utf-8', flag: 'w+' }, (err) ->
      return callback err

exports.main = main = () ->

  configfile = process.argv[2]
  throw new Error 'no config file specified' if not configfile

  collectStdin (err, data) ->
    stats = JSON.parse data
    transform = (contents) ->
      cfg = config.parseOnly contents
      cfg = updateStats cfg, stats
      return config.serialize cfg

    transformFile configfile, transform, (err) ->
      throw err if err
      console.log 'Updated', configfile
      process.exit(0)

main() if not module.parent

Fix wrong key being used

guv-update-workerstats: Fix wrong key being used [major]

The tool was using the 'process' config key instead of 'processing'
This would have caused the default (or old value in file) being
used instead, leading to wrong scaling parameters.


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

async = require 'async'

module.exports = (grunt) ->
  {spawn} = require('./task-helpers')(grunt)

  grunt.registerTask 'run-specs', 'Run the specs', ->
    passed = true
    done = @async()
    atomPath = path.resolve('atom.sh')
    apmPath = path.resolve('node_modules/.bin/apm')

    queue = async.queue (packagePath, callback) ->
      options =
        cmd: apmPath
        args: ['test', '-p', atomPath]
        opts:
          cwd: packagePath
      grunt.log.writeln("Launching #{path.basename(packagePath)} specs.")
      spawn options, (error, results, code) ->
        passed = passed and code is 0
        callback()

    modulesDirectory = path.resolve('node_modules')
    for packageDirectory in fs.readdirSync(modulesDirectory)
      packagePath = path.join(modulesDirectory, packageDirectory)
      continue unless grunt.file.isDir(path.join(packagePath, 'spec'))
      try
        {engines} = grunt.file.readJSON(path.join(packagePath, 'package.json')) ? {}
        queue.push(packagePath) if engines.atom?

    queue.concurrency = 1
    queue.drain = -> done(passed)

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

async = require 'async'

module.exports = (grunt) ->
  {spawn} = require('./task-helpers')(grunt)

  grunt.registerTask 'run-specs', 'Run the specs', ->
    passed = true
    done = @async()
    atomPath = path.resolve('atom.sh')
    apmPath = path.resolve('node_modules/.bin/apm')

    queue = async.queue (packagePath, callback) ->
      options =
        cmd: apmPath
        args: ['test', '--path', atomPath]
        opts:
          cwd: packagePath
      grunt.log.writeln("Launching #{path.basename(packagePath)} specs.")
      spawn options, (error, results, code) ->
        passed = passed and code is 0
        callback()

    modulesDirectory = path.resolve('node_modules')
    for packageDirectory in fs.readdirSync(modulesDirectory)
      packagePath = path.join(modulesDirectory, packageDirectory)
      continue unless grunt.file.isDir(path.join(packagePath, 'spec'))
      try
        {engines} = grunt.file.readJSON(path.join(packagePath, 'package.json')) ? {}
        queue.push(packagePath) if engines.atom?

    queue.concurrency = 1
    queue.drain = -> done(passed)

Use long opts for clarity

:lipstick: Use long opts for clarity


Promise = require 'bluebird-q'
{ API_URL } = require('sharify').data
{ track } = require '../../../../lib/analytics.coffee'
Serializer = require '../../../../components/form/serializer.coffee'

module.exports = ($el) ->
  $form = $el.find '.js-form'
  $submit = $el.find '.js-submit'
  $errors = $el.find '.js-errors'

  $form.on 'submit', (e) ->
    e.preventDefault()

    serializer = new Serializer $form

    label = $submit.text()

    $submit
      .prop 'disabled', true
      .text 'Sending...'

    Promise $.ajax
      url: "#{API_URL}/invitees/invite"
      type: 'POST'
      data: serializer.data()

    .then ->
      $form.trigger 'reset'

      $submit
        .prop 'disabled', false
        .text 'Sent!'

      setTimeout (-> $submit.text label), 2500

      track.submit 'Invitation sent from user'

    .catch ({ responseJSON: { message, description }}) ->
      $errors
        .show()
        .html """
          #{message}<br>
          #{description}
        """

      $submit
        .prop 'disabled', false
        .text 'Error'

      setTimeout ->
        $submit.text label
        $errors.empty()
      , 5000

      track.error 'Invitation not sent, try again.'

Promise = require 'bluebird-q'
{ API_URL } = require('sharify').data
{ track } = require '../../../../lib/analytics.coffee'
Serializer = require '../../../../components/form/serializer.coffee'

module.exports = ($el) ->
  $form = $el.find '.js-form'
  $submit = $el.find '.js-submit'
  $errors = $el.find '.js-errors'

  submissionTimeout = null;

  $form.on 'submit', (e) ->
    e.preventDefault()

    submissionTimeout && clearTimeout(submissionTimeout);

    serializer = new Serializer $form

    label = $submit.text()

    $submit
      .prop 'disabled', true
      .text 'Sending...'

    Promise $.ajax
      url: "#{API_URL}/invitees/invite"
      type: 'POST'
      data: serializer.data()

    .then ->
      $form.trigger 'reset'

      $submit
        .prop 'disabled', false
        .text 'Sent!'

      submissionTimeout = setTimeout (-> $submit.text label), 2500

      track.submit 'Invitation sent from user'

    .catch ({ responseJSON: { message, description }}) ->
      $errors
        .show()
        .html """
          #{message}

          #{description}
        """

      $submit
        .prop 'disabled', false
        .text 'Error'

      submissionTimeout = setTimeout ->
        $submit.text label
        $errors.empty()
      , 5000

      track.error 'Invitation not sent, try again.'

    .catch ->
      $errors
        .show()
        .html """
          Something went wrong, please contact info@are.na if the problem persists.
        """

      $submit
        .prop 'disabled', false
        .text label

      submissionTimeout = setTimeout ->
        $errors.empty()
      , 15000

      track.error 'Invitation not sent: server error. Try again.'

Add catch all error handling in case of AJAX fail response

- Add `fail` block to form submission request promise chain.
- Add timeout tracking so that old requests dont clear timeouts set by subsequent requests.


uploadPath = "/var/www/rocket.chat/uploads"

store = new FS.Store.FileSystem "avatars",
	path: uploadPath
	fileKeyMaker: (fileObj) ->
		filename = fileObj.name()
		filenameInStore = fileObj.name({store: 'avatars'})

		return filenameInStore || filename

@Avatars = new FS.Collection "avatars",
	stores: [store]

@Avatars.allow
	insert: ->
		return true
	update: ->
		return true
	download: ->
		return true

Meteor.startup ->
	if Meteor.isServer
		FS.HTTP.mount ['/avatar/:filename'], ->
			self = this
			opts = FS.Utility.extend({}, self.query || {}, self.params || {})

			collectionName = opts.collectionName

			collection = FS._collections['avatars']

			file = if collection? then collection.findOne({ "copies.avatars.key": opts.filename }) else null

			return {
				collection: collection
				file: file
				storeName: 'avatars'
				download: opts.download
				filename: opts.filename
			}

storeType = 'GridFS'
if Meteor.settings?.public?.avatarStore?.type?
	storeType = Meteor.settings.public.avatarStore.type

store = undefined

beforeWrite = (fileObj) ->
	fileObj._setInfo 'avatars', 'storeType', storeType, true

if storeType is 'FileSystem'
	path = "~/uploads"
	if Meteor.settings?.public?.avatarStore?.path?
		path = Meteor.settings.public.avatarStore.path
	console.log path

	store = new FS.Store.FileSystem "avatars",
		path: path
		beforeWrite: beforeWrite
		fileKeyMaker: (fileObj) ->
			filename = fileObj.name()
			filenameInStore = fileObj.name({store: 'avatars'})

			return filenameInStore || filename
else
	store = new FS.Store.GridFS "avatars",
		beforeWrite: beforeWrite

@Avatars = new FS.Collection "avatars",
	stores: [store]

@Avatars.allow
	insert: ->
		return true
	update: ->
		return true
	download: ->
		return true

Meteor.startup ->
	if Meteor.isServer
		FS.HTTP.mount ['/avatar/:filename'], ->
			self = this
			opts = FS.Utility.extend({}, self.query || {}, self.params || {})

			collectionName = opts.collectionName

			collection = FS._collections['avatars']

			file = if collection? then collection.findOne({ "copies.avatars.name": opts.filename, "copies.avatars.storeType": storeType }) else null

			return {
				collection: collection
				file: file
				storeName: 'avatars'
				download: opts.download
				filename: opts.filename
			}

Add GridFS as default avatar storage

Add GridFS as default avatar storage


core = require './core'
Input = require './Input'
{p, span, label} = require './DOM'

ActivityIndicator = require './ActivityIndicator'

Form =
  Input: core.createComponent 'rui.Form.Input',
    getInitialState: ->
      uniqueId: core.uniqueId()

    render: ->
      p className: 'rui-form-p', [
        label htmlFor: @state.uniqueId, @props.label if @props.label?
        span [
          @transferPropsTo Input
            onError: (err) =>
              @setState error: err
          span className: 'rui-form-error', @state.error if @state.error?
          span className: 'rui-form-help', @props.help
        ]
      ]

module.exports = Form

core = require './core'
Input = require './Input'
{p, span, label} = require './DOM'

ActivityIndicator = require './ActivityIndicator'

Form =
  Input: core.createComponent 'rui.Form.Input',
    getInitialState: ->
      uniqueId: core.uniqueId()

    render: ->
      p className: 'rui-form-p', [
        label htmlFor: @state.uniqueId, @props.label if @props.label?
        span [
          @transferPropsTo Input
            id: @state.uniqueId
            onError: (err) =>
              @setState error: err
          span className: 'rui-form-error', @state.error if @state.error?
          span className: 'rui-form-help', @props.help
        ]
      ]

module.exports = Form

Fix missing label reference to input field


`import Pathology from "./pathology.data.coffee"`

###*
 * The clinical Encounter REST data object extension utility.
 *
 * @module clinical
 * @class ClinicalEncounter
 * @static
###
ClinicalEncounter =
  ###*
   * @method extend
   * @param encounter the clinical encounter
   * @param subject the parent subject REST object
   * @return the augmented clinical encounter object
  ###
  extend: (encounter, subject) ->
    ###*
     * @method isBiopsy
     * @return whether the encounter class is `Biopsy`
    ###
    encounter.isBiopsy = -> @_cls is 'Biopsy'

    ###*
     * @method isSurgery
     * @return whether the encounter class ends in `Surgery`
    ###
    encounter.isSurgery = -> @_cls.endsWith('Surgery')

    # Add the virtual properties.
    Object.defineProperties encounter,
      ###*
       * 'Surgery' for a surgery encounter,
       *  otherwise the encounter class
       *
       * @property title
      ###
      title:
        get: ->
          if @isSurgery() then 'Surgery' else @_cls

    # Extend the pathology object.
    if encounter.pathology
      Pathology.extend(encounter.pathology)

    # Return the augmented clinical encounter object.
    encounter

`export { ClinicalEncounter as default }`

`import Pathology from "./pathology.data.coffee"`
`import Breast from "./breast.coffee"`

###*
 * The clinical Encounter REST data object extension utility.
 *
 * @module clinical
 * @class ClinicalEncounter
 * @static
###
ClinicalEncounter =
  ###*
   * @method extend
   * @param encounter the clinical encounter
   * @param subject the parent subject REST object
   * @return the augmented clinical encounter object
  ###
  extend: (encounter, subject) ->
    ###*
     * @method isBiopsy
     * @return whether the encounter class is `Biopsy`
    ###
    encounter.isBiopsy = -> @_cls is 'Biopsy'

    ###*
     * @method isSurgery
     * @return whether the encounter class ends in `Surgery`
    ###
    encounter.isSurgery = -> @_cls.endsWith('Surgery')

    # Add the virtual properties.
    Object.defineProperties encounter,
      ###*
       * 'Surgery' for a surgery encounter,
       *  otherwise the encounter class
       *
       * @property title
      ###
      title:
        get: ->
          if @isSurgery() then 'Surgery' else @_cls

    # Extend the pathology object.
    if encounter.pathology
      Pathology.extend(encounter.pathology)

    # Breast augments clinical encounters with virtual properties.
    if encounter.subject.collection is 'Breast'
      Breast.extend(encounter)

    # Return the augmented clinical encounter object.
    encounter

`export { ClinicalEncounter as default }`

Extend clinical for breast tumors.

Extend clinical for breast tumors.


Tasks = require './Tasks'
TaskScheduled = require '../models/TaskScheduled'

class TasksScheduled extends Tasks

    model: TaskScheduled

    url: "#{ env.SINGULARITY_BASE }/#{ constants.apiBase }/tasks/scheduled"

    parse: (tasks) ->
        _.each tasks, (task, i) =>
            task.JSONString = utils.stringJSON task
            task.id = @parsePendingId task.pendingTaskId
            task.requestId = task.pendingTaskId.requestId
            task.name = task.id
            task.nextRunAt = task.pendingTaskId.nextRunAt
            task.nextRunAtHuman = utils.humanTimeSoon task.nextRunAt
            task.schedule = task.request.schedule
            tasks[i] = task
            app.allTasks[task.id] = task

        tasks

    parsePendingId: (pendingTaskId) ->
        "#{ pendingTaskId.requestId }-#{ pendingTaskId.nextRunAt }-#{ pendingTaskId.instanceNo }"

    comparator: 'nextRunAt'

module.exports = TasksScheduled

Tasks = require './Tasks'
TaskScheduled = require '../models/TaskScheduled'

class TasksScheduled extends Tasks

    model: TaskScheduled

    url: "#{ env.SINGULARITY_BASE }/#{ constants.apiBase }/tasks/scheduled"

    parse: (tasks) ->
        _.each tasks, (task, i) =>
            task.JSONString = utils.stringJSON task
            if not task.pendingTaskId?
                task.pendingTaskId = task.pendingTask.pendingTaskId
            task.id = @parsePendingId task.pendingTaskId
            task.requestId = task.pendingTaskId.requestId
            task.name = task.id
            task.nextRunAt = task.pendingTaskId.nextRunAt
            task.nextRunAtHuman = utils.humanTimeSoon task.nextRunAt
            task.schedule = task.request.schedule
            tasks[i] = task
            app.allTasks[task.id] = task

        tasks

    parsePendingId: (pendingTaskId) ->
        "#{ pendingTaskId.requestId }-#{ pendingTaskId.nextRunAt }-#{ pendingTaskId.instanceNo }"

    comparator: 'nextRunAt'

module.exports = TasksScheduled

Fix scheduled task property changes

Fix scheduled task property changes


Report = require('../models/report.coffee').model

exports.index = (req, res) ->
  Report.find (err, reports) ->
    if !err?
      res.render "reports/index",
        reportsJSON: JSON.stringify(reports)
        title: "Report show page"

exports.show = (req, res) ->
  reportId = req.params.id

  Report.findOne(reportId, (err, report) ->
    if err?
      console.error err
      return res.render(500, "Could not retrieve report")

    if report?
      res.render "reports/show", reportData: JSON.stringify report
    else
      res.render(404)
  )

exports.new = (req, res) ->
  res.render "reports/show"

exports.present = (req, res) ->
  reportId = req.params.id

  Report.findOne(reportId, (err, report)->
    if err? or !report?
      console.error err
      return res.render(500, "Could not retrieve report")

    res.render "reports/present", reportData: JSON.stringify(report)
  )

Report = require('../models/report.coffee').model

exports.index = (req, res) ->
  Report.find (err, reports) ->
    if !err?
      res.render "reports/index",
        reportsJSON: JSON.stringify(reports)
        title: "Report show page"

exports.show = (req, res) ->
  reportId = req.params.id

  Report.findOne(reportId, (err, report) ->
    if err?
      console.error err
      return res.render(500, "Could not retrieve report")

    if report?
      report.toObjectWithNestedPage().then( (reportObject) ->
        res.render "reports/show", reportData: JSON.stringify reportObject
      ).fail( (err) ->
        console.error err
        res.render(500)
      )
    else
      res.render(404)
  )

exports.new = (req, res) ->
  res.render "reports/show"

exports.present = (req, res) ->
  reportId = req.params.id

  Report.findOne(reportId, (err, report)->
    if err? or !report?
      console.error err
      return res.render(500, "Could not retrieve report")

    res.render "reports/present", reportData: JSON.stringify(report)
  )

Make report page fetch page association

Make report page fetch page association


require('../src/jquery.turbolinks.coffee')

chai      = require('chai')
sinon     = require('sinon')
sinonChai = require('sinon-chai')
jQuery    = require('jquery')

chai.should()
chai.use(sinonChai)

describe 'jQuery Turbolinks', ->

  it '''
       should trigger callbacks passed to
       `jQuery()` and `jQuery.ready()` when page:change
       event fired
     ''', ->
       jQuery(callback1 = sinon.spy())
       jQuery(callback2 = sinon.spy())

       jQuery(document).trigger('page:change')

       callback1.should.have.been.calledTwice
       callback2.should.have.been.calledTwice

jsdom = require('jsdom').jsdom

global.document = jsdom()
global.window   = document.createWindow()

require('../src/jquery.turbolinks.coffee')

chai      = require('chai')
sinon     = require('sinon')
sinonChai = require('sinon-chai')
$         = require('jquery')

chai.should()
chai.use(sinonChai)

describe '$ Turbolinks', ->

  callback1 = callback2 = null

  beforeEach ->
     $(callback1 = sinon.spy())
     $(callback2 = sinon.spy())

  it '''
       should trigger callbacks passed to
       `$()` and `$.ready()` when page:load
       event fired
     ''', ->
       $(document).trigger('page:load')

       callback1.should.have.been.calledOnce
       callback2.should.have.been.calledOnce

  describe '$.bindReady', ->

    it 'should unbind default (page:load) event', ->
       $.bindReady('random_event_name')

       $(document).trigger('page:load')

       callback1.should.have.not.been.called
       callback2.should.have.not.been.called

    it 'should bind ready to passed function', ->
       $.bindReady('page:change')

       $(document)
         .trigger('page:load')
         .trigger('page:change')

       callback1.should.have.been.calledOnce
       callback2.should.have.been.calledOnce

Update test suite, now it works properly


module.exports = (grunt) ->
  grunt.initConfig
    pkg: grunt.file.readJSON('package.json')

    coffee:
      glob_to_multiple:
        expand: true
        cwd: 'src'
        src: ['*.coffee']
        dest: 'lib'
        ext: '.js'

    coffeelint:
      options:
        no_empty_param_list:
          level: 'error'
        max_line_length:
          level: 'ignore'

      src: ['src/*.coffee']
      test: ['spec/*.coffee']

    shell:
      test:
        command: 'node node_modules/.bin/jasmine-focused --captureExceptions --coffee spec'
        options:
          stdout: true
          stderr: true
          failOnError: true

  grunt.loadNpmTasks('grunt-contrib-coffee')
  grunt.loadNpmTasks('grunt-shell')
  grunt.loadNpmTasks('grunt-coffeelint')
  grunt.loadTasks('tasks')

  grunt.registerTask 'clean', ->
    grunt.file.delete('lib') if grunt.file.exists('lib')
  grunt.registerTask('lint', ['coffeelint:src', 'coffeelint:test'])
  grunt.registerTask('default', ['coffeelint', 'coffee'])
  grunt.registerTask('test', ['default', 'lint', 'shell:test'])
  grunt.registerTask('prepublish', ['clean', 'build-grammars', 'test'])

module.exports = (grunt) ->
  grunt.initConfig
    pkg: grunt.file.readJSON('package.json')

    coffee:
      glob_to_multiple:
        expand: true
        cwd: 'src'
        src: ['*.coffee']
        dest: 'lib'
        ext: '.js'

    coffeelint:
      options:
        no_empty_param_list:
          level: 'error'
        max_line_length:
          level: 'ignore'

      src: ['src/*.coffee']
      test: ['spec/*.coffee']

    shell:
      test:
        command: 'node node_modules/.bin/jasmine-focused --captureExceptions --coffee spec'
        options:
          stdout: true
          stderr: true
          failOnError: true

  grunt.loadNpmTasks('grunt-contrib-coffee')
  grunt.loadNpmTasks('grunt-shell')
  grunt.loadNpmTasks('grunt-coffeelint')
  grunt.loadTasks('tasks')

  grunt.registerTask 'clean', ->
    grunt.file.delete('lib') if grunt.file.exists('lib')
    grunt.file.delete('gen') if grunt.file.exists('gen')

  grunt.registerTask('lint', ['coffeelint:src', 'coffeelint:test'])
  grunt.registerTask('default', ['coffeelint', 'coffee'])
  grunt.registerTask('test', ['default', 'lint', 'shell:test'])
  grunt.registerTask('prepublish', ['clean', 'build-grammars', 'test'])

Delete gen/ during clean task

Delete gen/ during clean task


do ($$ = Quo) ->

    VENDORS = [ "-webkit-", "-moz-", "-ms-", "-o-", "" ]

    $$.fn.addClass = (name) ->
        @each ->
            unless _existsClass(name, @className)
                @className += " " + name
                @className = @className.trim()

    $$.fn.removeClass = (name) ->
        @each ->
            unless name
                @className = ""
            else
                @className = @className.replace(name, " ").replace(/\s+/g, " ").trim() if _existsClass(name, @className)

    $$.fn.toggleClass = (name) ->
        @each ->
            if _existsClass(name, @className)
                @className = @className.replace(name, " ")
            else
                @className += " " + name
                @className = @className.trim()

    $$.fn.hasClass = (name) ->
        _existsClass name, this[0].className

    $$.fn.style = (property, value) ->
        if value
            @each -> @style[property] = value
        else
            this[0].style[property] or _computedStyle(this[0], property)

    $$.fn.css = (property, value) -> @style property, value

    $$.fn.vendor = (property, value) ->
        @style("#{vendor}#{property}", value) for vendor in VENDORS

    _existsClass = (name, className) ->
        classes = className.split(/\s+/g)
        classes.indexOf(name) >= 0

    _computedStyle = (element, property) ->
        document.defaultView.getComputedStyle(element, "")[property]

do ($$ = Quo) ->

    VENDORS = [ "-webkit-", "-moz-", "-ms-", "-o-", "" ]

    $$.fn.addClass = (name) ->
        @each ->
            unless _existsClass(name, @className)
                @className += " " + name
                @className = @className.trim()

    $$.fn.removeClass = (name) ->
        @each ->
            unless name
                @className = ""
            else
                @className = @className.replace(name, " ").replace(/\s+/g, " ").trim() if _existsClass(name, @className)

    $$.fn.toggleClass = (name) ->
        @each ->
            if _existsClass(name, @className)
                @className = @className.replace(name, " ")
            else
                @className += " " + name
                @className = @className.trim()

    $$.fn.hasClass = (name) ->
        this.length > 0 and _existsClass name, this[0].className

    $$.fn.style = (property, value) ->
        if value
            @each -> @style[property] = value
        else if this.length > 0
            this[0].style[property] or _computedStyle(this[0], property)

    $$.fn.css = (property, value) -> @style property, value

    $$.fn.vendor = (property, value) ->
        @style("#{vendor}#{property}", value) for vendor in VENDORS

    _existsClass = (name, className) ->
        classes = className.split(/\s+/g)
        classes.indexOf(name) >= 0

    _computedStyle = (element, property) ->
        document.defaultView.getComputedStyle(element, "")[property]

Fix for when the array is empty

Fix for when the array is empty

`$$.fn.hasClass` and `$$.fn.style` will both throw exceptions if the current selection of elements is empty.


# # Kissmetrics Batch

class BatchKissmetricsClient
  @HOST: 'api.kissmetrics.com'
  @HTTP_METHOD: 'POST'
  @API_VERSION: 'v1'

  @process: (queue, apiKey, apiSecret, productGUID) =>
    http = require 'http'

    urlPath   = "#{@API_VERSION}/products/#{productGUID}/tracking/e"
    baseUrl   = "http://#{@HOST}/#{urlPath}"
    signature = @_generateSignature baseUrl, apiSecret

    request = http.request
      method: @HTTP_METHOD
      host: @HOST
      path: "#{baseUrl}?_signature=#{signature}"
      headers:
        'X-KM-ApiKey': apiKey

    requestBody = JSON.stringify {data: queue.get()}
    request.write requestBody

    queue.done()
    return request

  @_generateSignature: (baseUrl, apiSecret) =>
    crypto = require 'crypto'
    signer = crypto.createHmac 'sha256', apiSecret

    encodedRequest = [@HTTP_METHOD, encodeURIComponent baseUrl].join('&')
    encodeURIComponent signer.update(encodedRequest).digest('base64')

  constructor: (@queue) ->

  add: (timestamp, data) ->
    data.timestamp = timestamp
    @_transformData data

    @queue.add data

  _transformData: (data) ->
    data.identity = data._p
    delete data._p

    if data.type is 'record'
      data.event = data._n
      delete data._n

    if data.type is 'alias'
      data.alias = data._n
      delete data._n

    delete data.type

module.exports = BatchKissmetricsClient

# # Kissmetrics Batch

class BatchKissmetricsClient
  @HOST: 'api.kissmetrics.com'
  @HTTP_METHOD: 'POST'
  @API_VERSION: 'v1'

  @process: (queue, apiKey, apiSecret, productGUID) =>
    http = require 'http'

    urlPath   = "#{@API_VERSION}/products/#{productGUID}/tracking/e"
    baseUrl   = "http://#{@HOST}/#{urlPath}"
    signature = @_generateSignature baseUrl, apiSecret

    request = http.request
      method: @HTTP_METHOD
      host: @HOST
      path: "#{baseUrl}?_signature=#{signature}"
      headers:
        'X-KM-ApiKey': apiKey
    , ->
      queue.done()

    requestBody = JSON.stringify {data: queue.get()}
    request.write requestBody

    return request

  @_generateSignature: (baseUrl, apiSecret) =>
    crypto = require 'crypto'
    signer = crypto.createHmac 'sha256', apiSecret

    encodedRequest = [@HTTP_METHOD, encodeURIComponent baseUrl].join('&')
    encodeURIComponent signer.update(encodedRequest).digest('base64')

  constructor: (@queue) ->

  add: (timestamp, data) ->
    data.timestamp = timestamp
    @_transformData data

    @queue.add data

  _transformData: (data) ->
    data.identity = data._p
    delete data._p

    if data.type is 'record'
      data.event = data._n
      delete data._n

    if data.type is 'alias'
      data.alias = data._n
      delete data._n

    delete data.type

module.exports = BatchKissmetricsClient

Call queue.done() in the request callback


git = require '../git'
ProjectsListView = require '../views/projects-list-view'
StatusView = require '../views/status-view'

gitInit = ->
  currentFile = atom.workspace.getActiveTextEditor()?.getPath()
  if not currentFile and atom.project.getPaths().length > 1
    promise = new ProjectsListView().result.then (path) -> init(path)
  else
    init(atom.project.getPaths()[0])

init = (path) ->
  git.cmd
    args: ['init']
    cwd: path
    stdout: (data) ->
      new StatusView(type: 'success', message: data)
      atom.project.setPath(path)

module.exports = gitInit

git = require '../git'
ProjectsListView = require '../views/projects-list-view'
StatusView = require '../views/status-view'

gitInit = ->
  currentFile = atom.workspace.getActiveTextEditor()?.getPath()
  if not currentFile and atom.project.getPaths().length > 1
    promise = new ProjectsListView().result.then (path) -> init(path)
  else
    init(atom.project.getPaths()[0])

init = (path) ->
  git.cmd
    args: ['init']
    cwd: path
    stdout: (data) ->
      new StatusView(type: 'success', message: data)
      atom.project.setPaths([path])

module.exports = gitInit

Fix deprecations in git init command

Fix deprecations in git init command


class Algorhythmic
  max: 10

  compute: (array) ->
    array
    .map(@getCharInt)
    .reduce((previousInt, currentInt) =>
      (previousInt + currentInt) % @max
    , 0) + 1

  getCharInt: (char) -> parseInt char.charCodeAt(0) or 0

  convert: (string) ->
    str = string.replace(/\.(png|jpg|gif|)$/g, "")
    stringArray = str.split('')
    return @compute(stringArray)

module.exports = new Algorhythmic()

class Algorhythmic
  max: 10

  convert: (string) ->
    str = string.replace(/\.(png|jpg|gif|)$/g, "")
    stringArray = str.split('')
    return @_compute(stringArray)

  _compute: (array) ->
    array
    .map(@_getCharInt)
    .reduce((previousInt, currentInt) =>
      (previousInt + currentInt) % @max
    , 0) + 1

  _getCharInt: (char) -> parseInt char.charCodeAt(0) or 0

module.exports = new Algorhythmic()

Use convention for 'private' functions

Use convention for 'private' functions

Move them below public functions, also.


class Repo extends Backbone.Model

class RepoCollection extends Backbone.Collection

  model: Repo

  url: 'https://api.github.com/users/trabianmatt/repos'

module.exports = { Repo, RepoCollection }

class Repo extends Backbone.Model

class RepoCollection extends Backbone.Collection

  model: Repo

  url: 'https://api.github.com/users/trabian/repos'

module.exports = { Repo, RepoCollection }

Change sample Github user to 'trabian' from 'trabianmatt'


{Disposable} = require('atom')
module.exports =
  instance: null
  config:
    lintOnFly:
      title: 'Lint on fly'
      description: 'Lint files while typing, without the need to save them'
      type: 'boolean'
      default: true
    showErrorPanel:
      title: 'Show Error Panel at the bottom'
      type: 'boolean'
      default: true
    showErrorInline:
      title: 'Show Inline Tooltips'
      descriptions: 'Show inline tooltips for errors'
      type: 'boolean'
      default: true
    underlineIssues:
      title: 'Underline Issues'
      type: 'boolean'
      default: true

  activate: ->
    LinterPlus = require('./linter-plus.coffee')
    @instance = new LinterPlus()

    legacy = require('./legacy.coffee')
    for atomPackage in atom.packages.getLoadedPackages()
      if atomPackage.metadata['linter-package'] is true
        implementation = atomPackage.metadata['linter-implementation'] ? atomPackage.name
        linter = legacy(require("#{atomPackage.path}/lib/#{implementation}"))
        @consumeLinter(linter)

  consumeLinter: (linters) ->
    unless linters instanceof Array
      linters = [ linters ]

    for linter in linters
      @instance.addLinter(linter)

    new Disposable =>
      for linter in linters
        @instance.deleteLinter(linter)

  consumeStatusBar: (statusBar) ->
    @instance.views.attachBottom(statusBar)

  provideLinter: ->
    @Linter

  deactivate: ->
    @instance?.deactivate()

{Disposable} = require('atom')
module.exports =
  instance: null
  config:
    lintOnFly:
      title: 'Lint on fly'
      description: 'Lint files while typing, without the need to save them'
      type: 'boolean'
      default: true
    showErrorPanel:
      title: 'Show Error Panel at the bottom'
      type: 'boolean'
      default: true
    showErrorInline:
      title: 'Show Inline Tooltips'
      descriptions: 'Show inline tooltips for errors'
      type: 'boolean'
      default: true
    underlineIssues:
      title: 'Underline Issues'
      type: 'boolean'
      default: true

  activate: ->
    LinterPlus = require('./linter-plus.coffee')
    @instance = new LinterPlus()

    legacy = require('./legacy.coffee')
    for atomPackage in atom.packages.getLoadedPackages()
      if atomPackage.metadata['linter-package'] is true
        implementation = atomPackage.metadata['linter-implementation'] ? atomPackage.name
        linter = legacy(require("#{atomPackage.path}/lib/#{implementation}"))
        @consumeLinter(linter)

  consumeLinter: (linters) ->
    unless linters instanceof Array
      linters = [ linters ]

    for linter in linters
      @instance.addLinter(linter)

    new Disposable =>
      for linter in linters
        @instance.deleteLinter(linter)

  consumeStatusBar: (statusBar) ->
    @instance.views.attachBottom(statusBar)

  provideLinter: ->
    @instance

  deactivate: ->
    @instance?.deactivate()

Fix a critical bug in self service

:bug: Fix a critical bug in self service


class CMSimple.Panels.ImageLibrary.Selection extends Spine.Controller
  constructor: ->
    super
    @bindEvents()

  set: (img)->
    if @selectedImage
      @selectedImage.attr('src', img.src)
      snippet = Mercury.Snippet.find @selectedImage.parents('[data-snippet]').data('snippet')
      snippet.options.snippet[@selectedImage.data('snippet-image')] = img.src

    else if @region.type == 'editable'
      options = {value: img}
      @region.execCommand 'insertImage', options

  clearSelection: ->
    if @selectedImage
      @selectedImage.removeClass('selected-image')
      @selectedImage = null
      @trigger 'filter', null

  setSelection: (el)->
    @selectedImage?.removeClass('selected-image')
    @selectedImage = el
    @selectedImage.addClass('selected-image')
    if geometry = @selectedImage.data('image-geometry')
      @trigger 'filter', geometry

  bindEvents: ->
    Mercury.on 'region:focused', (event, options) =>
      @bindRegion(options.region)

  bindRegion: (region)->
    _clearSelection = => @clearSelection()
    if @region
      # unbind current region if user selected a new region
      @region.element.off 'click', _clearSelection
      @region.element.find('[data-snippet-image]').off('click')

    if @region = region
      @region.element.on 'click', _clearSelection
      @region.element.find('[data-snippet-image]').on 'click', (e)=>
        e.stopPropagation()
        @setSelection($(e.target))

class CMSimple.Panels.ImageLibrary.Selection extends Spine.Controller
  constructor: ->
    super
    @bindEvents()

  set: (img)->
    if @selectedImage
      @selectedImage.attr('src', img.src)
      @selectedImage.attr('alt', img.alt)
      snippet = Mercury.Snippet.find @selectedImage.parents('[data-snippet]').data('snippet')
      snippet.options.snippet[@selectedImage.data('snippet-image')] = img.src

    else if @region.type == 'editable'
      options = {value: img}
      @region.execCommand 'insertImage', options

  clearSelection: ->
    if @selectedImage
      @selectedImage.removeClass('selected-image')
      @selectedImage = null
      @trigger 'filter', null

  setSelection: (el)->
    @selectedImage?.removeClass('selected-image')
    @selectedImage = el
    @selectedImage.addClass('selected-image')
    if geometry = @selectedImage.data('image-geometry')
      @trigger 'filter', geometry

  bindEvents: ->
    Mercury.on 'region:focused', (event, options) =>
      @bindRegion(options.region)

  bindRegion: (region)->
    _clearSelection = => @clearSelection()
    if @region
      # unbind current region if user selected a new region
      @region.element.off 'click', _clearSelection
      @region.element.find('[data-snippet-image]').off('click')

    if @region = region
      @region.element.on 'click', _clearSelection
      @region.element.find('[data-snippet-image]').on 'click', (e)=>
        e.stopPropagation()
        @setSelection($(e.target))

Set the alt attribute on the image as well.


menu: [
  {
    label: 'File'
    submenu: [
      {label: 'Close All Tabs', command: 'tabs:close-all-tabs'}
    ]
  }
]

'context-menu':
  '.tab': [
    {label: 'Close Tab', command: 'tabs:close-tab'}
    {label: 'Close Other Tabs', command: 'tabs:close-other-tabs'}
    {label: 'Close Tabs to the Right', command: 'tabs:close-tabs-to-right'}
    {label: 'Close Saved Tabs', command: 'tabs:close-saved-tabs'}
    {label: 'Close All Tabs', command: 'tabs:close-all-tabs'}

    {type: 'separator'}

    {label: 'Split Up', command: 'tabs:split-up'}
    {label: 'Split Down', command: 'tabs:split-down'}
    {label: 'Split Left', command: 'tabs:split-left'}
    {label: 'Split Right', command: 'tabs:split-right'}
  ]
  '.tab.preview-tab': [
    {label: 'Keep Preview Tab', command: 'tabs:keep-preview-tab'}
  ]
  '.tab-bar': [
    {label: 'Reopen Closed Tab', command: 'pane:reopen-closed-item'}
  ]

Add 'Close All Tabs' to File menu

Add 'Close All Tabs' to File menu


'*':
  'editor':
    'fontFamily': 'PragmataPro'
    'showInvisibles': true
    'showIndentGuide': true
    'invisibles': {}
  'core':
    'disabledPackages': [
      'autocomplete'
      'spell-check'
    ]
    'themes': [
      'solarized-dark-ui'
      'solarized-dark-syntax'
    ]
    'projectHome': '/Users/edgard/Documents/code'
  'exception-reporting':
    'userId': '1b98cd61-c072-c275-56fd-5715ea36d0bd'
  'welcome':
    'showOnStartup': false
  'autocomplete-plus': {}
  'minimap': {}
  'git-diff':
    'showIconsInEditorGutter': true
  'spell-check': {}

'*':
  'editor':
    'fontFamily': 'PragmataPro'
    'showInvisibles': true
    'showIndentGuide': true
    'invisibles': {}
  'core':
    'disabledPackages': [
      'autocomplete'
      'spell-check'
    ]
    'themes': [
      'solarized-dark-ui'
      'solarized-dark-syntax'
    ]
    'projectHome': '/home/edgard/Documents/code'
  'exception-reporting':
    'userId': '1b98cd61-c072-c275-56fd-5715ea36d0bd'
  'welcome':
    'showOnStartup': false
  'autocomplete-plus': {}
  'minimap': {}
  'git-diff':
    'showIconsInEditorGutter': true
  'spell-check': {}

Change default project path for atom

Change default project path for atom


class PgHoffTypes
    @Type:
        'timestamp with time zone':
            format: (value)         -> return new Date(value).toLocaleString(atom.config.get('pg-hoff.locale'))
        'timestamp':
            format: (value)         -> return new Date(value).toLocaleString(atom.config.get('pg-hoff.locale'))
        'json':
            format: (text)          -> return JSON.stringify(JSON.parse(text), null, '  ')

module.exports = PgHoffTypes

timestamp =
    format: (value) -> new Date(value).toLocaleString(atom.config.get('pg-hoff.locale'))
time =
    format: (value) -> new Date('2000-01-01 ' + value).toLocaleTimeString(atom.config.get('pg-hoff.locale'))
class PgHoffTypes
    @Type:
        'timestamp with time zone': timestamp
        'timestamp without time zone': timestamp
        'time with time zone': time
        'time without time zone': time
        'json':
            format: (text)          -> return JSON.stringify(JSON.parse(text), null, '  ')

module.exports = PgHoffTypes

Add support for time data types

Add support for time data types

And remove a tiny bit of duplication.


fs = require "fs"
path = require "path"
env = (process.env.NODE_ENV or "development").toLowerCase()

if process.env.SHARELATEX_CONFIG?
	possibleConfigFiles = [process.env.SHARELATEX_CONFIG]
else
	possibleConfigFiles = [
		process.cwd() + "/config/settings.#{env}.coffee"
		path.normalize(__dirname + "/../../config/settings.#{env}.coffee")
	]

for file in possibleConfigFiles
	if fs.existsSync(file)
		module.exports = require(file)
		return

console.log "No config file could be found at: ", possibleConfigFiles
throw new Error("No config file found")

fs = require "fs"
path = require "path"
env = (process.env.NODE_ENV or "development").toLowerCase()

merge = (settings, defaults) ->
	for key, value of settings
		if typeof(value) == "object"
			defaults[key] = merge(settings[key], defaults[key] or {})
		else
			defaults[key] = value
	return defaults

defaultSettingsPath = path.normalize(__dirname + "/../../config/settings.defaults.coffee")

console.log "default settings", defaultSettingsPath
if fs.existsSync(defaultSettingsPath)
	defaults = require(defaultSettingsPath)
	settingsExist = true
else
	defaults = {}
	settingsExist = false

if process.env.SHARELATEX_CONFIG?
	possibleConfigFiles = [process.env.SHARELATEX_CONFIG]
else
	possibleConfigFiles = [
		process.cwd() + "/config/settings.#{env}.coffee"
		path.normalize(__dirname + "/../../config/settings.#{env}.coffee")
	]

for file in possibleConfigFiles
	if fs.existsSync(file)
		module.exports = merge(require(file), defaults)
		settingsExist = true
		break

if !settingsExist
	console.warn "No settings or defaults found. I'm flying blind."

module.exports = defaults

console.log "Settings", module.exports

Load and merge defaults if available

Load and merge defaults if available

Load and merge defaults if available


Q = require 'q'

{Base} = require "#{__dirname}/base"
{Wrappers} = require "#{__dirname}/helpers/wrappers"
{BeanBuilder} = require "#{__dirname}/helpers/bean_builder"

class BaseTask extends Base

  app: null

  actions: null
  actionBuilder: null

  constructor: ->
    @actions = []
    @actionBuilder = new BeanBuilder basePath: "#{__dirname}/tasks/actions", suffix: 'action'

  configure: (@app, config) ->
    super @app.getEmitter()
    @_configureActions config.actions if config.actions?.length

  _configureActions: (actions) ->
    @actions = (@_buildAction action for action in actions)
    return

  initialize: ->
    action.initialize() for action in @actions
    return

  _buildAction: (config) ->
    action = @actionBuilder.build config
    action.configure @app, @, config
    return action

  run: (data)->
    promise = null
    scope = data: data
    for action in @actions
      if promise
        promise.then (lastResult) ->
          scope.lastResult = lastResult
          Q.when action.run scope
      else
        promise = Q.when action.run scope
    return promise

exports.BaseTask = BaseTask

Q = require 'q'

{Base} = require "#{__dirname}/base"
{Wrappers} = require "#{__dirname}/helpers/wrappers"
{BeanBuilder} = require "#{__dirname}/helpers/bean_builder"

class BaseTask extends Base

  app: null

  actions: null
  actionBuilder: null

  constructor: ->
    @actions = []
    @actionBuilder = new BeanBuilder basePath: "#{__dirname}/tasks/actions", suffix: 'action'

  configure: (@app, config) ->
    super @app.getEmitter()
    @_configureActions config.actions if config.actions?.length

  _configureActions: (actions) ->
    @actions = (@_buildAction action for action in actions)
    return

  initialize: ->
    for action in @actions
      action.initialize()
    return

  _buildAction: (config) ->
    action = @actionBuilder.build config
    action.configure @app, @, config
    return action

  run: (data)->
    promise = null
    scope = data: data
    for action in @actions
      if promise
        promise.then ((lastResult) ->
          scope.lastResult = lastResult
          Q.when action.run scope
        ), ((err) =>
          @log 'warn', 'task.base', "Action #{action} performed not well.", err
        )
      else
        promise = Q.when action.run scope
    return promise

exports.BaseTask = BaseTask

Improve logging out errors when performing task's actions.


QUnit.test "test unique", (assert) ->
    input = [5, 6, 7, 5, 5, 6, 7]
    output = unique(input)
    expected = [5, 6, 7]

    assert.deepEqual( output , expected)

QUnit.test "test unique", (assert) ->
    input = [5, 6, 7, 5, 5, 6, 7]
    output = unique(input)
    expected = [5, 6, 7]

    assert.deepEqual(output , expected, "plain")

    input = [5, 6, null, 7, 5, 5, 6, 7]
    output = unique(input)
    assert.deepEqual(output, expected, "with null")

    input = [5]
    output = unique(input)
    expected = [5]
    assert.deepEqual(output, expected, "one value")

    input = [null, null, null]
    output = unique(input)
    expected = []
    assert.deepEqual(output, expected, "only nulls")

    input = [5, 6, 5, 6, 5, 6, "a"]
    output = unique(input)
    expected = [5, 6, "a"]
    assert.deepEqual(output, expected, "mixed strings/integers can give weird results (1)")

    input = [5, 6, 5, 6, 5, "6", "a"]
    output = unique(input)
    expected = [5, "6", "a"]
    assert.deepEqual(output, expected, "mixed strings/integers can give weird results (2)")

    input = ["5", 6, 5, 6, 5, "6", "a"]
    output = unique(input)
    expected = [5, "6", "a"]
    assert.deepEqual(output, expected, "mixed strings/integers can give weird results (3)")

    input = ["5", "5",  6, "5", "6", "5", "6", "a"]
    output = unique(input)
    expected = ["5", "6", "a"]
    assert.deepEqual(output, expected, "mixed strings/integers can give weird results (4)")

Add to unique unit tests

Add to unique unit tests


class @WishController extends RouteController

  waitOn: ->
    [Meteor.subscribe('wish', @params.id), subscriptionHandles['users']]

  tempalte: 'wish'

  renderTemplates:
    'nav': to: 'nav'

  notFoundTemplate: 'notFound'

  data: ->
    wish = Wishes.findOne({_id: @params.id})
    if not wish then return null
    {page: 'wish', wish: wish}

class @WishController extends RouteController

  waitOn: ->
    subs = [subscriptionHandles.users]
    if not Wishes.findOne({_id: @params.id})
      s = Meteor.subscribe('wish', @params.id)
      s.stop = ->
      subs.push(s)
    subs

  tempalte: 'wish'

  renderTemplates:
    'nav': to: 'nav'

  notFoundTemplate: 'notFound'

  data: ->
    wish = Wishes.findOne({_id: @params.id})
    if not wish then return null
    {page: 'wish', wish: wish}

Remove unneeded subscription at the wish page (if the data already exists)


module.exports = ->
  @logger = require './logger'

  @visit = (path) =>
    @driver.get "#{@host}#{path}"

  _Before = @Before
  _After  = @After

  @_inFlow = (code, callback) ->
    $.createFlow (flow) =>
      flow.execute => code.call(@)
    .then _.partial(callback, null), callback

  @Before = (code) ->
    _Before (callback) =>
      @_inFlow code, callback

  @After = (code) ->
    _After (callback) =>
      @_inFlow code, callback

  @BeforeAll = (code) ->
    unless @_ranBeforeAll
      @_ranBeforeAll = true
      @Before code

  @AfterAll = (code) ->
    unless @_ranAfterAll
      @_ranAfterAll = true
      @After code

  @Given = @When = @Then = @And = (pattern, code) =>
    @defineStep pattern, (args..., callback) =>
      start = new Date

      callforth = =>
        $
        .createFlow (flow) =>
          flow.execute => code.apply(@, args)
        .then _.partial(callback, null), (error) =>
          if new Date - start > @timeout
            callback(error)
          else
            $.delayed(1000).then -> callforth()

      callforth()

  @Before ->
    @driver = new Driver.Builder().withCapabilities(Driver.Capabilities.chrome()).build()

  @After ->
    @driver.close()
    @driver.quit()

module.exports = ->
  @logger = require './logger'

  @visit = (path) =>
    @driver.get "#{@host}#{path}"

  _Before = @Before
  _After  = @After

  @_inFlow = (code, callback) ->
    $.createFlow (flow) =>
      flow.execute => code.call(@)
    .then _.partial(callback, null),
      (err) -> throw err

  @Before = (code) ->
    _Before (callback) =>
      @_inFlow code, callback

  @After = (code) ->
    _After (callback) =>
      @_inFlow code, callback

  @BeforeAll = (code) ->
    unless @_ranBeforeAll
      @_ranBeforeAll = true
      @Before code

  @AfterAll = (code) ->
    unless @_ranAfterAll
      @_ranAfterAll = true
      @After code

  @Given = @When = @Then = @And = (pattern, code) =>
    @defineStep pattern, (args..., callback) =>
      start = new Date

      callforth = =>
        $
        .createFlow (flow) =>
          flow.execute => code.apply(@, args)
        .then _.partial(callback, null), (error) =>
          if new Date - start > @timeout
            callback(error)
          else
            $.delayed(1000).then -> callforth()

      callforth()

  @Before ->
    @driver = new Driver.Builder().withCapabilities(Driver.Capabilities.chrome()).build()

  @After ->
    @driver.close()
    @driver.quit()

Raise error on failed action in flow.

Raise error on failed action in flow.


'.workspace':
  'ctrl-g o': 'github:open'
  'ctrl-g b': 'github:blame'
  'ctrl-g h': 'github:history'
  'ctrl-g c': 'github:copy-url'

'.workspace':
  'alt-g o': 'github:open'
  'alt-g b': 'github:blame'
  'alt-g h': 'github:history'
  'alt-g c': 'github:copy-url'

Use alt instead of ctrl

Use alt instead of ctrl


chai = require 'chai'
chai.should()
chai.use require 'chai-as-promised'

require 'postmortem/register'

Nightmare = require 'joseph/nightmare'

before ->
  browser = Nightmare show: process.env.VERBOSE is 'true'
  yield browser.goto 'http://localhost:3333/'
  global.browser = browser

after ->
  yield browser.end()

chai = require 'chai'
chai.should()
chai.use require 'chai-as-promised'

require 'postmortem/register'

Nightmare = require 'joseph/nightmare'

before ->
  browser = Nightmare
    show: process.env.VERBOSE is 'true'
    # switches:
    #   'proxy-server':              'http://localhost:4010'
    #   'ignore-certificate-errors': true

  yield browser.goto 'http://localhost:3333/'
  global.browser = browser

after ->
  yield browser.end()

Add proxy settings for nightmare.

Add proxy settings for nightmare.


'use strict'

gulp = require 'gulp'
$ = (require 'gulp-load-plugins') lazy: false

paths =
  jshint: [
    './bin/*.js'
  ]
  coffeelint: [
    './gulpfile.coffee'
    './test/*.coffee'
  ]
  watch: [
    './gulpfile.coffee'
    './bin/**/*.js'
    './test/**/*.coffee'
  ]
  tests: [
    './test/**/*.coffee'
  ]

gulp.task 'jshint', ->
  gulp.src paths.jshint
    .pipe $.jshint()
    .pipe $.jshint.reporter()

gulp.task 'coffeelint', ->
  gulp.src paths.coffeelint
    .pipe $.coffeelint()
    .pipe $.coffeelint.reporter()

gulp.task 'compile', ['jshint', 'coffeelint'], ->
  gulp.src paths.tests
    .pipe $.sourcemaps.init()
    .pipe($.coffee({ bare: true }).on('error', $.util.log))
    .pipe $.sourcemaps.write()
    .pipe $.espower()
    .pipe gulp.dest('./compile')

gulp.task 'watch', ['test'], ->
  gulp.watch paths.watch, ['test']

gulp.task 'test', ['compile'], ->
  gulp.src ['./compile/**/*.js'], {cwd: __dirname}
    .pipe $.mocha()

gulp.task 'default', ['test']

'use strict'

gulp = require 'gulp'
$ = (require 'gulp-load-plugins') lazy: false

paths =
  jshint: [
    './bin/*'
  ]
  coffeelint: [
    './gulpfile.coffee'
    './test/*.coffee'
  ]
  watch: [
    './gulpfile.coffee'
    './bin/**/*.js'
    './test/**/*.coffee'
  ]
  tests: [
    './test/**/*.coffee'
  ]

gulp.task 'jshint', ->
  gulp.src paths.jshint
    .pipe $.jshint()
    .pipe $.jshint.reporter()

gulp.task 'coffeelint', ->
  gulp.src paths.coffeelint
    .pipe $.coffeelint()
    .pipe $.coffeelint.reporter()

gulp.task 'compile', ['jshint', 'coffeelint'], ->
  gulp.src paths.tests
    .pipe $.sourcemaps.init()
    .pipe($.coffee({ bare: true }).on('error', $.util.log))
    .pipe $.sourcemaps.write()
    .pipe $.espower()
    .pipe gulp.dest('./compile')

gulp.task 'watch', ['test'], ->
  gulp.watch paths.watch, ['test']

gulp.task 'test', ['compile'], ->
  gulp.src ['./compile/**/*.js'], {cwd: __dirname}
    .pipe $.mocha()

gulp.task 'default', ['test']

Enable to lint no-extension file

Enable to lint no-extension file


# Start the crash reporter before anything else.
require('crash-reporter').start(productName: 'Atom', companyName: 'GitHub')

path = require 'path'

try
  require '../src/window'
  Atom = require '../src/atom'
  window.atom = Atom.loadOrCreate('spec')

  # Show window synchronously so a focusout doesn't fire on input elements
  # that are focused in the very first spec run.
  atom.getCurrentWindow().show() unless atom.getLoadSettings().exitWhenDone

  {runSpecSuite} = require './jasmine-helper'

  # Add 'src/exports' to module search path.
  exportsPath = path.resolve(atom.getLoadSettings().resourcePath, 'exports')
  require('module').globalPaths.push(exportsPath)
  # Still set NODE_PATH since tasks may need it.
  process.env.NODE_PATH = exportsPath

  document.title = "Spec Suite"
  runSpecSuite './spec-suite', atom.getLoadSettings().logFile
catch error
  if atom?.getLoadSettings().exitWhenDone
    console.error(error.stack ? error)
    atom.exit(1)
  else
    throw error

# Start the crash reporter before anything else.
require('crash-reporter').start(productName: 'Atom', companyName: 'GitHub')

path = require 'path'

try
  require '../src/window'
  Atom = require '../src/atom'
  window.atom = Atom.loadOrCreate('spec')

  # Show window synchronously so a focusout doesn't fire on input elements
  # that are focused in the very first spec run.
  atom.getCurrentWindow().show() unless atom.getLoadSettings().exitWhenDone

  {runSpecSuite} = require './jasmine-helper'

  # Add 'exports' to module search path.
  exportsPath = path.join(atom.getLoadSettings().resourcePath, 'exports')
  require('module').globalPaths.push(exportsPath)
  # Still set NODE_PATH since tasks may need it.
  process.env.NODE_PATH = exportsPath

  document.title = "Spec Suite"
  runSpecSuite './spec-suite', atom.getLoadSettings().logFile
catch error
  if atom?.getLoadSettings().exitWhenDone
    console.error(error.stack ? error)
    atom.exit(1)
  else
    throw error

Use path.join instead of path.resolve

Use path.join instead of path.resolve

Applies commit 41ab48b to spec-bootstrap.coffee


{pressKey, test, testGroup, typeCharacters} = Trix.TestHelpers

testOptions =
  template: "editor_empty"
  setup: ->
    addEventListener("keydown", cancel, true)
    addEventListener "trix-before-initialize", handler = ({target}) ->
      removeEventListener("trix-before-initialize", handler)
      target.addEventListener("keydown", cancel)
  teardown: ->
    removeEventListener("keydown", cancel, true)

cancelingInCapturingPhase = false
cancelingAtTarget = false

cancel = (event) ->
  switch event.eventPhase
    when Event.CAPTURING_PHASE
      event.preventDefault() if cancelingInCapturingPhase
    when Event.AT_TARGET
      event.preventDefault() if cancelingAtTarget

testGroup "Canceled input", testOptions, ->
  test "ignoring canceled input events in capturing phase", (expectDocument) ->
    typeCharacters "a", ->
      cancelingInCapturingPhase = true
      pressKey "backspace", ->
        pressKey "return", ->
          cancelingInCapturingPhase = false
          typeCharacters "b", ->
            expectDocument "ab\n"

  test "ignoring canceled input events at target", (expectDocument) ->
    typeCharacters "a", ->
      cancelingAtTarget = true
      pressKey "backspace", ->
        pressKey "return", ->
          cancelingAtTarget = false
          typeCharacters "b", ->
            expectDocument "ab\n"

{pressKey, test, testGroup, typeCharacters} = Trix.TestHelpers

testOptions =
  template: "editor_empty"
  setup: ->
    addEventListener("keydown", cancel, true)
    addEventListener "trix-before-initialize", handler = ({target}) ->
      removeEventListener("trix-before-initialize", handler)
      target.addEventListener("keydown", cancel)
  teardown: ->
    removeEventListener("keydown", cancel, true)

cancelingInCapturingPhase = false
cancelingAtTarget = false

cancel = (event) ->
  switch event.eventPhase
    when Event::CAPTURING_PHASE
      event.preventDefault() if cancelingInCapturingPhase
    when Event::AT_TARGET
      event.preventDefault() if cancelingAtTarget

testGroup "Canceled input", testOptions, ->
  test "ignoring canceled input events in capturing phase", (expectDocument) ->
    typeCharacters "a", ->
      cancelingInCapturingPhase = true
      pressKey "backspace", ->
        pressKey "return", ->
          cancelingInCapturingPhase = false
          typeCharacters "b", ->
            expectDocument "ab\n"

  test "ignoring canceled input events at target", (expectDocument) ->
    typeCharacters "a", ->
      cancelingAtTarget = true
      pressKey "backspace", ->
        pressKey "return", ->
          cancelingAtTarget = false
          typeCharacters "b", ->
            expectDocument "ab\n"

Fix tests in IE 11

Fix tests in IE 11


keysSchema = mongoose.Schema
	user:
		type: mongoose.Schema.ObjectId
		ref: "User"
	identifier: String
	key: String
	created_at:
		type: Date
		default: Date.now
	updated_at:
		type: Date
		default: Date.now

keysSchema.static "getUserKeyWithIdentifier", (user, ident, cb) ->
	this.find
		user: user
		identifier: ident
	, (err, doc) ->
			if err
				console.log "Error getting user key by identifier"
				cb?(err)
				return true
			else
				cb?(null, doc)
				return false

	return true

keysSchema.static "createUserKeyWithIdentifier", (user, ident, key, cb) ->
	newKey = new Keys
		user: user
		identifier: ident
		key: key

	newKey.save cb

Keys = mongoose.model 'Keys', keysSchema

module.exports = exports = Keys

keysSchema = mongoose.Schema
	user:
		type: mongoose.Schema.ObjectId
		ref: "User"
	identifier: String
	key: String
	created_at:
		type: Date
		default: Date.now
	updated_at:
		type: Date
		default: Date.now

keysSchema.static "getUserKeyWithIdentifier", (user, ident, cb) ->
	this.find
		user: user.id
		identifier: ident
	, (err, doc) ->
			if err
				console.log "Error getting user key by identifier"
				cb?(err)
				return true
			else
				cb?(null, doc)
				return false

	return true

keysSchema.static "createUserKeyWithIdentifier", (user, ident, key, cb) ->
	newKey = new Keys
		user: user
		identifier: ident
		key: key

	newKey.save cb

Keys = mongoose.model 'Keys', keysSchema

module.exports = exports = Keys

Select by user.id, not the whole user object


require.config
  deps: ["main"]

  shim: {
    handlebars: exports: "Handlebars"
  }

  paths:
    # RequireJS plugins
    text: "../vendor/text/text"

    # Third party libraries
    jquery: "../vendor/jquery/jquery"
    handlebars: "../vendor/handlebars/handlebars"
    backbone: "../vendor/backbone-amd/backbone"
    underscore: "../vendor/underscore-amd/underscore"

define ->
  API_URL: "http://#{location.hostname}:4100"

require.config
  deps: ["main"]

  shim: {
    handlebars: exports: "Handlebars"
  }

  paths:
    # RequireJS plugins
    text: "../vendor/text/text"

    # Third party libraries
    jquery: "../vendor/jquery/jquery"
    handlebars: "../vendor/handlebars/handlebars"
    backbone: "../vendor/backbone-amd/backbone"
    underscore: "../vendor/underscore-amd/underscore"

define ->
  API_URL: "http://dev.api.backtick.io"

Switch over to hosted API

Switch over to hosted API


# from atom/terminal to reduce cpu usage

pty = require 'ptyw.js'

module.exports = (ptyCwd, sh, cols, rows, args) ->
  callback = @async()
  if sh
      shell = sh
  else
      shell = process.env.SHELL
      if not shell
        # Try to salvage some sort of shell to execute. Horrible code below.
        path = require 'path'
        if process.platform is 'win32'
          shell = path.resolve(process.env.SystemRoot, 'WindowsPowerShell', 'v1.0', 'powershell.exe')
        else
          shell = '/bin/sh'

  ptyProcess = pty.fork shell, args,
    name: 'xterm-256color'
    cols: cols
    rows: rows
    cwd: ptyCwd
    env: process.env

  ptyProcess.on 'data', (data) ->
    emit('term3:data', new Buffer(data).toString("base64"))

  ptyProcess.on 'exit', ->
    emit('term3:exit')
    callback()

  process.on 'message', ({event, cols, rows, text}={}) ->
    switch event
      when 'resize' then ptyProcess.resize(cols, rows)
      when 'input' then ptyProcess.write(new Buffer(text, "base64").toString("utf-8"))

# from atom/terminal to reduce cpu usage

pty = require 'ptyw.js'

module.exports = (ptyCwd, sh, cols, rows, args) ->
  callback = @async()
  if sh
      shell = sh
  else
      shell = process.env.SHELL
      if not shell
        # Try to salvage some sort of shell to execute. Horrible code below.
        path = require 'path'
        if process.platform is 'win32'
          shell = path.resolve(process.env.SystemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe')
        else
          shell = '/bin/sh'

  ptyProcess = pty.fork shell, args,
    name: 'xterm-256color'
    cols: cols
    rows: rows
    cwd: ptyCwd
    env: process.env

  ptyProcess.on 'data', (data) ->
    emit('term3:data', new Buffer(data).toString("base64"))

  ptyProcess.on 'exit', ->
    emit('term3:exit')
    callback()

  process.on 'message', ({event, cols, rows, text}={}) ->
    switch event
      when 'resize' then ptyProcess.resize(cols, rows)
      when 'input' then ptyProcess.write(new Buffer(text, "base64").toString("utf-8"))

Fix default path to powershell.

Fix default path to powershell.


{View} = require 'atom'
module.exports =
class MinimapSelectionView extends View
  decorations: []
  @content: ->
    @div class: 'minimap-selection'

  initialize: (@minimapView) ->
    @subscribe @minimapView.editorView, "selection:changed", @handleSelection
    @subscribe @minimapView.editorView, "cursor-added", @handleSelection
    @subscribe @minimapView.editorView, "cursor-moved", @handleSelection
    @subscribe @minimapView.editorView, "cursor-removed", @handleSelection

  attach: ->
    @minimapView.miniUnderlayer.append(this)
    @handleSelection()

  destroy: ->
    @detach()
    @unsubscribe()
    @minimapView = null

  handleSelection: =>
    @removeDecorations()

    {editor} = @minimapView

    return if editor.getSelections().length is 1 and editor.getLastSelection().isEmpty()

    for selection in editor.getSelections()
      @decorations.push @minimapView.decorateMarker(selection.marker, type: 'highlight-under', scope: '.editor .selection .region')

  removeDecorations: ->
    return if @decorations.length is 0
    decoration.destroy() for decoration in @decorations
    @decorations = []

{View} = require 'atom'
module.exports =
class MinimapSelectionView extends View
  decorations: []
  @content: ->
    @div class: 'minimap-selection'

  initialize: (@minimapView) ->
    @subscribe @minimapView.editorView, "selection:changed", @handleSelection
    @subscribe @minimapView.editorView, "cursor-added", @handleSelection
    @subscribe @minimapView.editorView, "cursor-moved", @handleSelection
    @subscribe @minimapView.editorView, "cursor-removed", @handleSelection

  attach: ->
    @minimapView.miniUnderlayer.append(this)
    @handleSelection()

  destroy: ->
    @detach()
    @unsubscribe()
    @minimapView = null

  handleSelection: =>
    @removeDecorations()

    {editor} = @minimapView

    return if editor.getSelections().length is 1 and editor.getLastSelection().isEmpty()

    for selection in editor.getSelections()
      @decorations.push @minimapView.decorateMarker(selection.marker, type: 'highlight-under', scope: '.minimap .editor .selection .region')

  removeDecorations: ->
    return if @decorations.length is 0
    decoration.destroy() for decoration in @decorations
    @decorations = []

Add a minimap class to the decoration scope

It’ll allow to have a specific style for the minimap selection


'scopeName': 'source.vp'
'name': 'Valkyrie Profile'
'fileTypes': ['vp']
'patterns': [
	{
		'match': '^[^\t]*$',
		'name': 'entity name function vp'
	}
]

'scopeName': 'source.vp'
'name': 'Valkyrie Profile'
'fileTypes': ['vp']
'patterns': [
	{
		'match': '^[^\t]*$',
		'name': 'entity name function vp'
	},
	{
		'match': '^\t{2}.*$'
		'name': 'constant language vp'
	}
]

Update grammar to take Windows into account