SymbolsView = require './symbols-view'
TagGenerator = require './tag-generator'

module.exports =
class FileView extends SymbolsView
  constructor: (@stack) ->
    super
  initialize: ->
    super

    @cachedTags = {}

    @subscribe atom.project.eachBuffer (buffer) =>
      @subscribe buffer, 'reloaded saved destroyed path-changed', =>
        delete @cachedTags[buffer.getPath()]

      @subscribe buffer, 'destroyed', =>
        @unsubscribe(buffer)

    @subscribe atom.workspace.eachEditor (editor) =>
      @subscribe editor, 'grammar-changed', =>
        delete @cachedTags[editor.getPath()]

      @subscribe editor, 'destroyed', =>
        @unsubscribe(editor)

  toggle: ->
    if @hasParent()
      @cancel()
    else if filePath = @getPath()
      @populate(filePath)
      @attach()

  getPath: -> atom.workspace.getActiveEditor()?.getPath()

  getScopeName: -> atom.workspace.getActiveEditor()?.getGrammar()?.scopeName

  populate: (filePath) ->
    @list.empty()
    @setLoading('Generating symbols\u2026')
    if tags = @cachedTags[filePath]
      @maxItem = Infinity
      @setItems(tags)
    else
      @generateTags(filePath)

  generateTags: (filePath) ->
    new TagGenerator(filePath, @getScopeName()).generate().done (tags) =>
      @cachedTags[filePath] = tags
      @maxItem = Infinity
      @setItems(tags)

  afterTagOpen: (previous) ->
    @stack.push previous

SymbolsView = require './symbols-view'
TagGenerator = require './tag-generator'

module.exports =
class FileView extends SymbolsView
  initialize: ->
    super

    @cachedTags = {}

    @subscribe atom.project.eachBuffer (buffer) =>
      @subscribe buffer, 'reloaded saved destroyed path-changed', =>
        delete @cachedTags[buffer.getPath()]

      @subscribe buffer, 'destroyed', =>
        @unsubscribe(buffer)

    @subscribe atom.workspace.eachEditor (editor) =>
      @subscribe editor, 'grammar-changed', =>
        delete @cachedTags[editor.getPath()]

      @subscribe editor, 'destroyed', =>
        @unsubscribe(editor)

  toggle: ->
    if @hasParent()
      @cancel()
    else if filePath = @getPath()
      @populate(filePath)
      @attach()

  getPath: -> atom.workspace.getActiveEditor()?.getPath()

  getScopeName: -> atom.workspace.getActiveEditor()?.getGrammar()?.scopeName

  populate: (filePath) ->
    @list.empty()
    @setLoading('Generating symbols\u2026')
    if tags = @cachedTags[filePath]
      @maxItem = Infinity
      @setItems(tags)
    else
      @generateTags(filePath)

  generateTags: (filePath) ->
    new TagGenerator(filePath, @getScopeName()).generate().done (tags) =>
      @cachedTags[filePath] = tags
      @maxItem = Infinity
      @setItems(tags)

  afterTagOpen: (previous) ->
    @stack.push previous

Remove ctor handled in superclass

Remove ctor handled in superclass


Meteor.subscribe 'authors';
Meteor.subscribe 'Posts';
Meteor.subscribe 'Comments';

Template['posts'].helpers
  'posts': -> Posts.find({}, sort: {createdAt: -1})

Template.post.helpers
  'comments': -> Comments.find({post_id: @post._id})

# Template['posts'].events

Template['new-post'].events
  'submit .new-post': (e) ->
    e.preventDefault()
    Posts.insert
      'title': e.target.title.value
      'content': e.target.content.value
      'author': Meteor.userId()
      'createdAt': new Date()

Template.new_comment.events
  'submit .new_comment': (e) ->
    e.preventDefault
    Comments.insert
      'title': e.target.title.value
      'content': e.target.content.value
      'author': e.target.author.value
      'post_id': e.target.post_id.value
      'createdAt': new Date()

Meteor.subscribe 'authors';
Meteor.subscribe 'Posts';
Meteor.subscribe 'Comments';

Template['posts'].helpers
  'posts': -> Posts.find({}, sort: {createdAt: -1})

Template.post.helpers
  'comments': -> Comments.find({post_id: @post._id})

# Template['posts'].events

Template['new-post'].events
  'submit .new-post': (e) ->
    e.preventDefault()
    Posts.insert
      'title': e.target.title.value
      'content': e.target.content.value
      'author': Meteor.userId()
      'createdAt': new Date()

Template.new_comment.events
  'submit .new_comment': (e) ->
    e.preventDefault()
    Comments.insert
      'title': e.target.title.value
      'content': e.target.content.value
      'author': e.target.author.value
      'post_id': e.target.post_id.value
      'createdAt': new Date()

Fix on preventDefault method call

Fix on preventDefault method call


Dialog = require './dialog'
Project = require './project'
projects = require './projects'
path = require 'path'
changeCase = require 'change-case'

module.exports =
class SaveDialog extends Dialog
  filePath: null

  constructor: () ->
    firstPath = atom.project.getPaths()[0]
    title = path.basename(firstPath)

    if atom.config.get('project-manager.prettifyTitle')
      title = changeCase.titleCase(title)

    super
      prompt: 'Enter name of project'
      input: title
      select: true
      iconClass: 'icon-arrow-right'

    projects.getCurrent (project) =>
      if project.props.paths[0] is firstPath
        @showError "This project is already saved as #{project.props.title}"

  onConfirm: (title) ->
    if title
      properties =
        title: title
        paths: atom.project.getPaths()

      project = new Project(properties)
      project.save()

      @close()
    else
      @showError('You need to specify a name for the project')

Dialog = require './dialog'
Project = require './project'
projects = require './projects'
path = require 'path'
changeCase = require 'change-case'

module.exports =
class SaveDialog extends Dialog
  filePath: null

  constructor: () ->
    firstPath = atom.project.getPaths()[0]
    title = path.basename(firstPath)

    if atom.config.get('project-manager.prettifyTitle')
      title = changeCase.titleCase(title)

    super
      prompt: 'Enter name of project'
      input: title
      select: true
      iconClass: 'icon-arrow-right'

    projects.getCurrent (project) =>
      if project.rootPAth is firstPath
        @showError "This project is already saved as #{project.props.title}"

  onConfirm: (title) ->
    if title
      properties =
        title: title
        paths: atom.project.getPaths()

      project = new Project(properties)
      project.save()

      @close()
    else
      @showError('You need to specify a name for the project')

Use method to check root path

Use method to check root path


# Argumenta instance
argumenta = require '../app/argumenta'

# Site index
exports.index = (req, res) ->
  argumenta.users.latest {limit: 10}, (err, users) ->
    argumenta.repos.latest {limit: 20}, (err, repos) ->
      res.reply 'index',
        latest_users: users
        latest_repos: repos

# Argumenta instance
argumenta = require '../app/argumenta'

# Site index
exports.index = (req, res) ->
  argumenta.users.latest {limit: 10}, (err, users) ->
    argumenta.repos.latest {limit: 20, metadata: true}, (err, repos) ->
      res.reply 'index',
        latest_users: users
        latest_repos: repos

Include metadata for repos on homepage

Include metadata for repos on homepage


AutoUpdater = process.atomBinding('auto_updater').AutoUpdater
EventEmitter = require('events').EventEmitter

AutoUpdater::__proto__ = EventEmitter.prototype

autoUpdater = new AutoUpdater
autoUpdater.on 'update-downloaded-raw', (args...) ->
  args[3] = new Date(args[3])  # releaseDate
  @emit 'update-downloaded', args..., => @quitAndInstall()

autoUpdater.quitAndInstall = ->
  # Do the restart after all windows have been closed.
  app = require 'app'
  app.removeAllListeners 'window-all-closed'
  app.once 'window-all-closed', AutoUpdater::quitAndInstall.bind(this)

  # Tell all windows to remove beforeunload handler and then close itself.
  ipc = require 'ipc'
  BrowserWindow = require 'browser-window'
  ipc.sendChannel win.getProcessId(), win.getRoutingId(), 'ATOM_SHELL_SILENT_CLOSE' for win in BrowserWindow.getAllWindows()

module.exports = autoUpdater

AutoUpdater = process.atomBinding('auto_updater').AutoUpdater
EventEmitter = require('events').EventEmitter

AutoUpdater::__proto__ = EventEmitter.prototype

autoUpdater = new AutoUpdater
autoUpdater.on 'update-downloaded-raw', (args...) ->
  args[3] = new Date(args[3])  # releaseDate
  @emit 'update-downloaded', args..., => @quitAndInstall()

autoUpdater.quitAndInstall = ->
  # If we don't have any window then quitAndInstall immediately.
  BrowserWindow = require 'browser-window'
  windows = BrowserWindow.getAllWindows()
  if windows.length is 0
    AutoUpdater::quitAndInstall.call this
    return

  # Do the restart after all windows have been closed.
  app = require 'app'
  app.removeAllListeners 'window-all-closed'
  app.once 'window-all-closed', AutoUpdater::quitAndInstall.bind(this)

  # Tell all windows to remove beforeunload handler and then close itself.
  ipc = require 'ipc'
  ipc.sendChannel win.getProcessId(), win.getRoutingId(), 'ATOM_SHELL_SILENT_CLOSE' for win in windows

module.exports = autoUpdater

Fix quitAndInstall when there is no window.


Emmet = require '../lib/emmet'

describe "Emmet", ->
  it "has one valid test", ->
    true

Emmet = require 'emmet/lib/emmet'
RootView = require 'root-view'
Buffer = require 'text-buffer'
Editor = require 'editor'

describe "Emmet", ->
  [buffer, editor, editSession] = []
  keymap = null

  beforeEach ->
    window.rootView = new RootView
    rootView.open('sample.js')

    editor = rootView.getActiveView()
    editSession = rootView.getActivePaneItem()
    buffer = editor.getBuffer()

    atom.activatePackage("emmet")

    rootView.simulateDomAttachment()
    rootView.enableKeymap()

  describe "emmet:expand-abbreviation", ->
    htmlSolution = null

    beforeEach ->
      buffer.setText('')
      editor.insertText "#header>ul#nav>li*4>a"
      htmlSolution = '
\n
\n

\n

\n

\n

\n
\n
'

    it "expands HTML abbreviations via commands", ->
      editor.trigger "emmet:expand-abbreviation"
      expect(editor.getText()).toBe htmlSolution

    it "expands HTML abbreviations via keybindings", ->
      editor.trigger keydownEvent('e', shiftKey: true, metaKey: true, target: editor[0])
      expect(editor.getText()).toBe htmlSolution

Write a test for expand-abbreviation

Write a test for expand-abbreviation


params = (options) ->
  url: "https://stream.twitter.com/1.1/statuses/filter.json"
  oauth: options.oauth
  form:
    include_entities: true
    track: options.words.join ','

request = require 'request'

makeRequest = (validate, beautify, fn) ->
  req = request.post params, (error, response, body) ->
    fn error, false if error

  req.on 'end', () ->
    req = makeRequest validate, beautify, fn

  req.on 'data', (buffer) ->
    try
      tweet = JSON.parse buffer.toString()
      if tweet.disconnect
        req = makeRequest validate, beautify, fn
    catch error
      tweet = false

    if tweet and validate tweet
      tweet = beautify tweet, false if beautify

      fn null, tweet

  req

module.exports = (options) ->
  params = params options

  (validate, beautify, fn) ->
    req = makeRequest validate, beautify, fn

params = (options) ->
  url: "https://stream.twitter.com/1.1/statuses/filter.json"
  oauth: options.oauth
  form:
    include_entities: true
    track: options.words.join ','

request = require 'request'

makeRequest = (validate, beautify, fn) ->
  stream = request.post params, (error, response, body) ->
    fn error, false if error

  stream.on 'end', ->
    makeRequest validate, beautify, fn
    return

  stream.on 'data', (buffer) ->
    try
      tweet = JSON.parse buffer.toString()
      if tweet and tweet.disconnect
        makeRequest validate, beautify, fn
        return
    catch error
      tweet = false

    if tweet and validate tweet
      tweet = beautify tweet, false if beautify

      fn null, tweet
      return
  return

module.exports = (options) ->
  params = params options

  (validate, beautify, fn) ->
    makeRequest validate, beautify, fn
    return

Add more control on return

Add more control on return


# Tool to query and render GUID conversions via REST API

delay = (ms, func) -> setTimeout func, ms

class window.GuidTool
    # Initialize on page load
    @init: =>
        $('#guid').select()

        delay 2000, =>
            $('.nav, #footer').fadeIn()

        $('#submit').click (event) =>
            event.preventDefault()
            GuidTool.query $('#guid').val()
            $('#guid').val('').focus()

    # Query the server for a given GUID
    @query: (guid) =>
        $.ajax
            url: '/'
            type: 'post'
            data:
                guid: guid
            success: (data, status, xhr) =>
                # Check for success and show conversions or error
                if data.status is 'success'
                    html = '
Int:' + data.int + '
Hex:' + data.hex + '
B64:' + data.b64 + '
'
                else
                    guid = "no input given" if guid is ''
                    html = '
Invalid input: ' + guid + '
'

                $('#results').prepend(html)

# Tool to query and render GUID conversions via REST API

delay = (ms, func) -> setTimeout func, ms

class window.GuidTool
    # Initialize on page load
    @init: =>
        $('#guid').select()

        delay 2000, =>
            $('.nav, #footer').fadeIn()

        $('#submit').click (event) =>
            event.preventDefault()
            GuidTool.query $('#guid').val()
            $('#guid').val('').focus()

    # RFC 4122 Section 4.4 GUID generation from pseudo-random numbers
    @generate: =>
        'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace /[xy]/g, (c) =>
            r = Math.random() * 16 | 0
            v = if c is 'x' then r else r & 0x3 | 0x8
            return v.toString 16

    # Query the server for a given GUID
    @query: (guid) =>
        $.ajax
            url: '/'
            type: 'post'
            data:
                guid: guid
            success: (data, status, xhr) =>
                # Check for success and show conversions or error
                if data.status is 'success'
                    html = '
Int:' + data.int + '
Hex:' + data.hex + '
B64:' + data.b64 + '
'
                else
                    guid = "no input given" if guid is ''
                    html = '
Invalid input: ' + guid + '
'

                $('#results').prepend(html)

Add method to generate UUID from javascript


_ = require 'underscore'

exports.throttle = (getIdentifier, timeout = 1000) ->
  cache = {}
  throttleInner = (req, resp, next) ->
    identifier = getIdentifier(arguments)

    unless identifier of cache
       cache[identifier] = _.throttle(next, timeout, {trailing: false})

    cache[identifier]()

  return throttleInner

_ = require 'underscore'

callNext = (next) ->
  next()

exports.throttle = (getIdentifier, timeout = 1000) ->
  cache = {}
  throttleInner = (req, resp, next) ->
    identifier = getIdentifier(arguments)

    unless identifier of cache
       cache[identifier] = _.throttle(callNext, timeout, {trailing: false})

    cache[identifier](next)

  return throttleInner

Add indirection so that correct next is called.


webpackConfig = require('./webpack.config.coffee')
webpackConfig.entry = {}

module.exports = (config)  ->
  config.set
    frameworks: ['jasmine']
    browsers: ['Chrome']

    files: [
      './spec/**/*.coffee'
    ]

    preprocessors:
      './spec/helpers/**/*': ['webpack']
      './spec/**/*': ['webpack']

    webpack: webpackConfig

    webpackMiddleware:
      noInfo: true

webpackConfig = require('./webpack.config.coffee')
webpackConfig.entry = {}
webpackConfig.plugins = []

module.exports = (config)  ->
  config.set
    frameworks: ['jasmine']
    browsers: ['Chrome']

    files: [
      './spec/**/*.coffee'
    ]

    preprocessors:
      './spec/helpers/**/*': ['webpack']
      './spec/**/*': ['webpack']

    webpack: webpackConfig

    webpackMiddleware:
      noInfo: true

Remove common chunks plugin when testing


gulp       = require 'gulp'
del        = require 'del'
haml       = require 'gulp-haml'
sourcemaps = require 'gulp-sourcemaps'
coffee     = require 'gulp-coffee'
serve      = require './serve.coffee'
bower      = require 'main-bower-files'

SRC = 'src'
DEST = 'out'
HAML_PATH = "#{SRC}/haml/**/*.haml"
COFFEE_PATH = "#{SRC}/coffee/**/*.coffee"

gulp.task 'default', ['haml', 'coffee', 'bower']

gulp.task 'haml', ->
  gulp.src HAML_PATH
    .pipe haml()
    .pipe gulp.dest DEST

gulp.task 'coffee', ->
  gulp.src COFFEE_PATH
    .pipe sourcemaps.init()
    .pipe coffee()
    .pipe sourcemaps.write()
    .pipe gulp.dest "#{DEST}/scripts"

gulp.task 'bower', ->
  gulp.src bower()
    .pipe gulp.dest "#{DEST}/external"

gulp.task 'clean', (callback) ->
  del DEST, callback

gulp.task 'watch', ['haml', 'coffee'], ->
  gulp.watch HAML_PATH, ['haml']
  gulp.watch COFFEE_PATH, ['coffee']
  return

gulp.task 'serve', ->
  serve DEST

gulp       = require 'gulp'
del        = require 'del'
haml       = require 'gulp-haml'
sourcemaps = require 'gulp-sourcemaps'
coffee     = require 'gulp-coffee'
serve      = require './serve.coffee'
bower      = require 'main-bower-files'

SRC = 'src'
DEST = 'out'
HAML_PATH = "#{SRC}/haml/**/*.haml"
COFFEE_PATH = "#{SRC}/coffee/**/*.coffee"

gulp.task 'build', ['haml', 'coffee', 'bower']

gulp.task 'haml', ->
  gulp.src HAML_PATH
    .pipe haml()
    .pipe gulp.dest DEST

gulp.task 'coffee', ->
  gulp.src COFFEE_PATH
    .pipe sourcemaps.init()
    .pipe coffee()
    .pipe sourcemaps.write()
    .pipe gulp.dest "#{DEST}/scripts"

gulp.task 'bower', ->
  gulp.src bower()
    .pipe gulp.dest "#{DEST}/external"

gulp.task 'clean', (callback) ->
  del DEST, callback

gulp.task 'watch', ['build'], ->
  gulp.watch HAML_PATH, ['haml']
  gulp.watch COFFEE_PATH, ['coffee']
  return

gulp.task 'serve', ->
  serve DEST

gulp.task 'default', ['watch', 'serve']

Watch & Serve by Default

Watch & Serve by Default

The old default task is now "build".


".text.html":
  "polymer element":
    "prefix": "pe"
    "body": """
    <link rel="import" href="${1:../bower_components}/polymer/polymer.html">

    <dom-module id="$2">
      <style>
        :host {
          display: block;
        }
      </style>
      <template>
        $4
      </template>
    </dom-module>

    <script>
      Polymer({
        is: '$2',
        $3
      });
    </script>
    """
  "polymer element no template":
    "prefix": "pen"
    "body": """
    <link rel="import" href="${1:../bower_components}/polymer/polymer.html">

    <script>
      Polymer({
        is: '$2',
        $3
      });
    </script>
    """

".text.html":
  "polymer element":
    "prefix": "pe"
    "body": """
    <link rel="import" href="${1:../bower_components}/polymer/polymer.html">

    <dom-module id="$2">
      <style>
        :host {
          $5
        }
      </style>
      <template>
        $4
      </template>
      <script>
        Polymer({
          is: '$2',
          $3
        });
      </script>
    </dom-module>
    """
  "polymer element no template":
    "prefix": "pen"
    "body": """
    <link rel="import" href="${1:../bower_components}/polymer/polymer.html">

    <script>
      Polymer({
        is: '$2',
        $3
      });
    </script>
    """

Remove opinion about element display style

This one is give or take.


request = require 'request'

module.exports =
  class Reporter
    constructor: ->
      @request = request

    send: (eventType, data) ->
      params =
        timestamp: new Date().getTime() / 1000
        dimensions: data

      requestOptions =
        method: 'POST'
        url: "https://collector.githubapp.com/atom/#{eventType}"
        headers:
          'Content-Type' : 'application/vnd.github-octolytics+json'
        body: JSON.stringify(params)

      @request requestOptions, -> # Callback prevents errors from going to the console

request = require 'request'

module.exports =
  class Reporter
    constructor: ->
      @request = request

    send: (eventType, data) ->
      params =
        timestamp: new Date().getTime() / 1000
        context: data

      requestOptions =
        method: 'POST'
        url: "https://collector.githubapp.com/atom/#{eventType}"
        headers:
          'Content-Type' : 'application/vnd.github-octolytics+json'
        body: JSON.stringify(params)

      @request requestOptions, -> # Callback prevents errors from going to the console

Store data in context instead of dimensions


class Aggregate

  constructor: (@_context, @_eventric, @_name, Root) ->
    @_domainEvents = []

    if !Root
      @root = {}
    else
      @root = new Root

    @root.$emitDomainEvent = @emitDomainEvent

  emitDomainEvent: (domainEventName, domainEventPayload) =>
    DomainEventClass = @_context.getDomainEvent domainEventName
    if !DomainEventClass
      err = "Tried to emitDomainEvent '#{domainEventName}' which is not defined"
      @_eventric.log.error err
      throw new Error err

    aggregate =
      id: @id
      name: @_name
    domainEvent = @_context.createDomainEvent domainEventName, DomainEventClass, domainEventPayload, aggregate
    @_domainEvents.push domainEvent

    @_handleDomainEvent domainEventName, domainEvent
    @_eventric.log.debug "Created and Handled DomainEvent in Aggregate", domainEvent
    # TODO: do a rollback if something goes wrong inside the handle function

  _handleDomainEvent: (domainEventName, domainEvent) ->
    if @root["handle#{domainEventName}"]
      @root["handle#{domainEventName}"] domainEvent, ->

    else
      @_eventric.log.debug "Tried to handle the DomainEvent '#{domainEventName}' without a matching handle method"

  getDomainEvents: =>
    @_domainEvents

  applyDomainEvents: (domainEvents) ->
    @_applyDomainEvent domainEvent for domainEvent in domainEvents

  _applyDomainEvent: (domainEvent) ->
    @_handleDomainEvent domainEvent.name, domainEvent

module.exports = Aggregate

class Aggregate

  constructor: (@_context, @_eventric, @_name, AggregateClass) ->
    @_domainEvents = []
    @instance = new AggregateClass
    @instance.$emitDomainEvent = @emitDomainEvent

  emitDomainEvent: (domainEventName, domainEventPayload) =>
    DomainEventClass = @_context.getDomainEvent domainEventName
    if !DomainEventClass
      throw new Error "Tried to emitDomainEvent '#{domainEventName}' which is not defined"

    aggregate =
      id: @id
      name: @_name
    domainEvent = @_context.createDomainEvent domainEventName, DomainEventClass, domainEventPayload, aggregate
    @_domainEvents.push domainEvent

    @_handleDomainEvent domainEventName, domainEvent
    @_eventric.log.debug "Created and Handled DomainEvent in Aggregate", domainEvent

  _handleDomainEvent: (domainEventName, domainEvent) ->
    if @instance["handle#{domainEventName}"]
      @instance["handle#{domainEventName}"] domainEvent
    else
      @_eventric.log.debug "Tried to handle the DomainEvent '#{domainEventName}' without a matching handle method"

  getDomainEvents: =>
    @_domainEvents

  applyDomainEvents: (domainEvents) ->
    @_applyDomainEvent domainEvent for domainEvent in domainEvents

  _applyDomainEvent: (domainEvent) ->
    @_handleDomainEvent domainEvent.name, domainEvent

module.exports = Aggregate

Aggregate: Rename plain object member variable from "root" to "instance" in order to not conflict with DDD terminology


I18n = (args...) ->
  m = chrome.i18n.getMessage(args...)
  return escapeHtml(m) if m != ""
  m = id.replace(/(.)([A-Z][a-z])/g, (m, p1, p2) -> "#{p1} #{p2.toLowerCase()}")
  m = m.replace(/_/g, ' ')
  escapeHtml(m)

$(->
  pat = /__MSG_([A-Za-z0-9]+)__/
  $("a,button").each((i, v) ->
    e = $(v)
    title = e.attr?("title")
    if title
      newTitle = title.replace(pat, (m, p1) -> I18n(p1))
      e.attr("title", newTitle) if title != newTitle
    html = e.html()
    return true unless html
    newHtml = html.replace(pat, (m, p1) -> I18n(p1))
    e.html(newHtml) if html != newHtml
  )
)

I18n = (args...) ->
  m = chrome.i18n.getMessage(args...)
  return escapeHtml(m) if m != ""
  m = args[0].replace(/(.)([A-Z][a-z])/g, (m, p1, p2) -> "#{p1} #{p2.toLowerCase()}")
  m = m.replace(/_/g, ' ')
  escapeHtml(m)

$(->
  pat = /__MSG_([A-Za-z0-9]+)__/
  $("a,button").each((i, v) ->
    e = $(v)
    title = e.attr?("title")
    if title
      newTitle = title.replace(pat, (m, p1) -> I18n(p1))
      e.attr("title", newTitle) if title != newTitle
    html = e.html()
    return true unless html
    newHtml = html.replace(pat, (m, p1) -> I18n(p1))
    e.html(newHtml) if html != newHtml
  )
)

Fix I18n error when ID not defined in messages.json


class Component

    constructor: ->

    update: ->

    stringify: ->
        JSON.stringify(@spec)

class $blab.Table extends Component

    constructor: (@spec, sheet) ->

        randPos = (range, offset)->
            "#{Math.round((Math.random()*range+offset)*100)}%"
        @spec.x ?= randPos(0.8, 0.1)
        @spec.y ?= randPos(0.8, 0.1)

        @sheet = sheet[@spec.id]

        container = $("##{@spec.id}")
        container.append("

")
        container.css("position", "absolute")
        container.css("left", @spec.x)
        container.css("top", @spec.y)

        hot = $("##{@spec.id} .hot")
        @defaults =
            data: @sheet.spec.data
            afterChange: (change, source) =>
                $blab.compute() if source is "edit" and @sheet.spec.compute
            columns: ({type: 'numeric'} for k in [1..@sheet.spec.data[0].length])
            rowHeaders: @sheet.spec.rowHeaders
            colHeaders: @sheet.spec.colHeaders
            contextMenu: false
        @table = new Handsontable hot[0], $.extend({}, @defaults, @spec)

    update: ->
        @table.loadData @sheet.spec.data
        @table.render()

class $blab.Table extends $blab.Component

    constructor: (@spec, sheet, file) ->

        @sheet = sheet[@spec.id]

        container = $("##{@spec.containerId}")
        container.append("

")

        hot = $("#Table-#{@spec.id}")
        hot.css("position", "absolute")
        hot.css("left", @spec.x)
        hot.css("top", @spec.y)

        defaults =
            data: @sheet.data
            afterChange: (change, source) =>
                $blab.compute() if source is "edit" and @spec.compute
            columns: ({type: 'numeric'} for k in [1..@sheet.data[0].length])
            rowHeaders: @sheet.rowHeaders
            colHeaders: @sheet.colHeaders
            contextMenu: false

        @table = new Handsontable hot[0], $.extend({}, defaults, @spec)

    update: ->
        @table.loadData @sheet.data
        @table.render()

Remove local Component class. Refactor.

Remove local Component class. Refactor.


angular.module 'ws.helper', []

  .factory 'WebSocketOptions', ->
    options = {}
    return options

  .factory 'UserWebSocket', [
    '$timeout'
    'WebSocketOptions'
    ($timeout, options) ->
      class UserWebSocket

        constructor: (url) ->
          @url = url
          @handlers = {}
          @connect() if options.autoConnect ? true

        register: (handlers...) ->
          @handlers[handler.protocol] = handler for handler in handlers
          return

        connect: =>
          @socket = new WebSocket @url
          @socket.onmessage = (event) =>
            msg = JSON.parse(event.data)
            handler = @handlers[msg.protocol]
            handler.onmessage(msg) if handler?
            return

          @socket.onclose = (event) =>
            $timeout @connect, 10000
            return

          return

        send: (msg) ->
          @socket.send JSON.stringify(msg) if @readyState() is WebSocket.OPEN
          return

        readyState: ->
          @socket?.readyState ? WebSocket.CLOSED

      return new UserWebSocket options.url
    ]

angular.module 'ws.helper', []

  .factory 'WebSocketOptions', ->
    options = {}
    return options

  .factory 'UserWebSocket', [
    '$timeout'
    'WebSocketOptions'
    ($timeout, options) ->
      class UserWebSocket

        constructor: (url) ->
          @url = url
          @handlers = {}
          $timeout @connect, 2000 if options.autoConnect ? true

        register: (handlers...) ->
          @handlers[handler.protocol] = handler for handler in handlers
          return

        connect: =>
          @socket = new WebSocket @url
          @socket.onmessage = (event) =>
            msg = JSON.parse(event.data)
            handler = @handlers[msg.protocol]
            handler.onmessage(msg) if handler?
            return

          @socket.onclose = (event) =>
            $timeout @connect, 10000
            return

          return

        send: (msg) ->
          @socket.send JSON.stringify(msg) if @readyState() is WebSocket.OPEN
          return

        readyState: ->
          @socket?.readyState ? WebSocket.CLOSED

      return new UserWebSocket options.url
    ]

Put a 2-second delay before websocket connecting


# Your Adapter should define:
# * QueryDefaults - an object of defaults to send as query parameters

class Embeditor.Adapter
    className: "Adapter"

    constructor: (@element, @options={}) ->
        @adapter        = Embeditor.Adapters[@className]
        @href           = @element.attr('href')
        @dataOptions    = @_extractData()
        @queryParams    = @_buildParams(@dataOptions, options)

    swap: ->
        return

    embed: (html) ->
        @wrapper = $("<div />", class: @options.wrapperClass)
        @wrapper.html(html)
        @element.after @wrapper

    _extractData: ->
        dataOptions = {}

        for key,val of @element.data()
            # Make sure we care about this attribute
            if @adapter.QueryDefaults?[key]
                dataOptions[key] = val

        dataOptions

    # We're combining a few things (in order of precedence):
    # 1. The `data-attributes` of the placeholder,
    # 2. The adapter-specific options specified at Embeditor
    #    initialization,
    # 3. The global options specified at Embeditor initialization,
    # 4. This adapter's default options (fallback options).
    _buildParams: (dataOptions, options) ->
        _.defaults(dataOptions,
            @adapter.query,
            options['query'],
            @adapter.QueryDefaults
        )

# Your Adapter SHOULD define:
# * @QueryDefaults - The default parameters if no others are passed in.

class Embeditor.Adapter
    className: "Adapter"

    @QueryDefaults = {}

    constructor: (@element, @options={}) ->
        @adapter        = Embeditor.Adapters[@className]
        @href           = @element.attr('href')
        @dataOptions    = @_extractData()
        @queryParams    = @_buildParams(@dataOptions, options)

    swap: ->
        return

    embed: (html) ->
        @wrapper = $("<div />", class: @options.wrapperClass)
        @wrapper.html(html)
        @element.after @wrapper

    _extractData: ->
        dataOptions = {}

        for key,val of @element.data()
            # Make sure we care about this attribute
            if @adapter.QueryDefaults?[key]
                dataOptions[key] = val

        dataOptions

    # We're combining a few things (in order of precedence):
    # 1. The `data-attributes` of the placeholder,
    # 2. The adapter-specific options specified at Embeditor
    #    initialization,
    # 3. The global options specified at Embeditor initialization,
    # 4. This adapter's default options (fallback options).
    _buildParams: (dataOptions, options) ->
        _.defaults(dataOptions,
            @adapter.query,
            options['query'],
            @adapter.QueryDefaults
        )

Add some documentatio and blank QueryDefaults object in Adapter


HighlightedAreaView = require './highlighted-area-view'

module.exports =
  activate: (state) ->
    @areas = []
    atom.workspaceView.eachEditorView (editorView) =>
      area = new HighlightedAreaView(editorView)
      area.attach()
      @areas.push = area

  deactivate: =>
    for area in @areas
      area.destroy()

HighlightedAreaView = require './highlighted-area-view'
areas = []

module.exports =
  activate: (state) ->
    atom.workspaceView.eachEditorView (editorView) ->
      area = new HighlightedAreaView(editorView)
      area.attach()
      areas.push = area

  deactivate: ->
    for area in areas
      area.destroy()

Fix being able to uninstall package

Fix being able to uninstall package


Guid = require 'guid'
Reporter = require './reporter'

module.exports =
  activate: ({sessionLength}) ->
    atom.config.set('metrics.userId', Guid.raw()) unless atom.config.get('metrics.userId')
    @sessionStart = Date.now()

    Reporter.sendEvent('ended', sessionLength) if sessionLength
    Reporter.sendEvent('started')
    Reporter.sendTiming('core-load', atom.getWindowLoadTime())
    Reporter.sendTiming('shell-load', atom.getLoadSettings().shellLoadTime)
    atom.workspaceView.on 'editor:attached', (event, editorView) ->
      Reporter.sendView('EditorView')

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

Guid = require 'guid'
Reporter = require './reporter'

module.exports =
  activate: ({sessionLength}) ->
    atom.config.set('metrics.userId', Guid.raw()) unless atom.config.get('metrics.userId')
    @sessionStart = Date.now()

    Reporter.sendEvent('ended', sessionLength) if sessionLength
    Reporter.sendEvent('started')
    Reporter.sendTiming('core-load', atom.getWindowLoadTime())
    Reporter.sendTiming('shell-load', atom.getLoadSettings().shellLoadTime)
     atom.workspaceView.on 'pane:item-added', (event, item) ->
        name = item.getViewClass?().name ? item.constructor.name
        Reporter.sendView(name)

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

Use pane:item-added instead of editor:attached

Now there is info about what pane item is added. Also we don't get
events for mini-editors being attached.


flat = (obj, newObj = {}, path = '') ->
	for key, value of obj
		if _.isObject value
			flat value, newObj, key + '.'
		else
			newObj[path + key] = value

	return newObj

Meteor.startup ->
	l = {}
	errors = []

	for lang, value of TAPi18next.options.resStore
		l[lang] = flat value

	for lang, value of l
		for subLang, subValue of l when subLang isnt lang
			for key, translation of subValue
				if not value[key]?
					errors.push "#{lang}: no value found for key #{key} from #{subLang}"

	if errors.length > 0
		len = 0
		for error in errors
			if error.length > len
				len = error.length

		console.log s.rpad('', len + 4, '=').red
		for error in errors
			console.log "| #{error} |".red
		console.log s.rpad('', len + 4, '=').red

flat = (obj, newObj = {}, path = '') ->
	for key, value of obj
		if _.isObject value
			flat value, newObj, key + '.'
		else
			newObj[path + key] = value

	return newObj

Meteor.startup ->
	l = {}
	errors = []

	for lang, value of TAPi18next.options.resStore
		l[lang] = flat value

	for lang, value of l
		for subLang, subValue of l when subLang isnt lang
			for key, translation of subValue
				if not value[key]?
					errors.push "#{lang}: no value found for key #{key} from #{subLang}"

	if errors.length > 0
		len = 0
		for error in errors
			if error.length > len
				len = error.length

		console.log s.rpad('', len + 4, '=').red
		for error in errors
			console.log "|".red, s.rpad("#{error}", len).red,  "|".red
		console.log s.rpad('', len + 4, '=').red

Fix log of language validation

Fix log of language validation


class Visualization extends Backbone.Model
  paramRoot: 'visualization'
  defaults:
    parameters: null

module.exports = Visualization

class Visualization extends Backbone.Model
  paramRoot: 'visualization'
  urlRoot: '/api/visualizations'
  defaults:
    parameters: null

module.exports = Visualization

Add urlRoot parameter to Visualization model in order to update model parameters via api


{Disposable, CompositeDisposable} = require 'event-kit'

ContentsByMode =
  insert:  ["status-bar-vim-mode-insert", "Insert"]
  command: ["status-bar-vim-mode-command", "Command"]
  visual:  ["status-bar-vim-mode-visual", "Visual"]

module.exports =
class StatusBarManager
  constructor: ->
    @element = document.createElement("div")
    @element.id = "status-bar-vim-mode"
    @element.classList.add("inline-block")

  initialize: ->
    @disposables = new CompositeDisposable
    unless @attach()
      @disposables.add atom.packages.onDidActivateAll => @attach()
    @disposables

  update: (currentMode) ->
    for mode, [klass, html] of ContentsByMode
      if mode is currentMode
        @element.classList.add(klass)
        @element.innerHTML = html
      else
        @element.classList.remove(klass)

  # Private

  attach: ->
    statusBar = atom.workspaceView?.statusBar
    if statusBar?
      statusBar.prependRight(@element)
      @disposables.add =>
        @element.parentNode?.removeChild(@element)
      true
    else
      false

{Disposable, CompositeDisposable} = require 'event-kit'

ContentsByMode =
  insert:  ["status-bar-vim-mode-insert", "Insert"]
  command: ["status-bar-vim-mode-command", "Command"]
  visual:  ["status-bar-vim-mode-visual", "Visual"]

module.exports =
class StatusBarManager
  constructor: ->
    @element = document.createElement("div")
    @element.id = "status-bar-vim-mode"
    @element.classList.add("inline-block")

  initialize: ->
    @disposables = new CompositeDisposable
    unless @attach()
      @disposables.add atom.packages.onDidActivateAll => @attach()
    @disposables

  update: (currentMode) ->
    for mode, [klass, html] of ContentsByMode
      if mode is currentMode
        @element.classList.add(klass)
        @element.innerHTML = html
      else
        @element.classList.remove(klass)

  # Private

  attach: ->
    statusBar = atom.workspaceView?.statusBar
    if statusBar?
      statusBar.prependRight(@element)
      @disposables.add new Disposable =>
        @element.parentNode?.removeChild(@element)
      true
    else
      false

Fix error when deactivating package

Fix error when deactivating package


Spine = require('spine')

class Actions extends Spine.Controller

  elements:
    '.deal': 'deal_button'
    '.continue': 'continue_button'
    '.surrender': 'surrender_button'
    '.bet': 'bet_buttons'
    '.action': 'action_buttons'

  events:
    'click .deal': 'deal'

  constructor: ->
    super
    @html require('views/actions')()
    Spine.bind 'result', @finished
    @setup()

  setup: =>
    show @bet_buttons
    hide @action_buttons
    hide @continue_button

  deal: =>
    hide @bet_buttons
    hide @deal_button
    show @action_buttons

  finished: =>
    hide @action_buttons
    show @continue_button

  # Private

  hide = (selection) ->
    selection.hide()

  show = (selection) ->
    enable selection.show()

  disable = (selection) ->
    selection.attr('disabled', true)

  enable = (selection) ->
    selection.removeAttr('disabled')

module.exports = Actions

Spine = require('spine')
Player = require('models/player')
$ = Spine.$

class Actions extends Spine.Controller

  elements:
    '.deal': 'deal_button'
    '.continue': 'continue_button'
    '.surrender': 'surrender_button'
    '.bet': 'bet_buttons'
    '.action': 'action_buttons'

  events:
    'click .deal': 'deal'

  constructor: ->
    super
    @html require('views/actions')()
    Spine.bind 'result', @finished
    Player.bind 'change', @bank_check
    @setup()

  setup: =>
    show @bet_buttons
    hide @action_buttons
    hide @continue_button

  deal: =>
    hide @bet_buttons
    hide @deal_button
    show @action_buttons

  finished: =>
    hide @action_buttons
    show @continue_button

  bank_check: (player) =>
    for el in @bet_buttons.find('button')
      bet_button = $(el)
      (disable bet_button) if bet_button.data('amount') > player.pot.size

  # Private

  hide = (selection) ->
    selection.hide()

  show = (selection) ->
    enable selection.show()

  disable = (selection) ->
    selection.attr('disabled', true)

  enable = (selection) ->
    selection.removeAttr('disabled')

module.exports = Actions

Disable bet buttons when you don't have that much to bet


'.platform-darwin .editor':
  'cmd-r': 'symbols-view:toggle-file-symbols'
  'cmd-alt-down': 'symbols-view:go-to-declaration'
  'cmd-alt-up': 'symbols-view:return-from-declaration'

'.platform-win32 .editor':
  'ctrl-r': 'symbols-view:toggle-file-symbols'

'.platform-linux .editor':
  'ctrl-r': 'symbols-view:toggle-file-symbols'
  'ctrl-alt-down': 'symbols-view:go-to-declaration'
  'ctrl-alt-up': 'symbols-view:return-from-declaration'

'.platform-darwin':
  'cmd-R': 'symbols-view:toggle-project-symbols'

'.platform-win32, .platform-linux':
  'ctrl-R': 'symbols-view:toggle-project-symbols'

'.platform-darwin atom-text-editor':
  'cmd-r': 'symbols-view:toggle-file-symbols'
  'cmd-alt-down': 'symbols-view:go-to-declaration'
  'cmd-alt-up': 'symbols-view:return-from-declaration'

'.platform-win32 atom-text-editor':
  'ctrl-r': 'symbols-view:toggle-file-symbols'

'.platform-linux atom-text-editor':
  'ctrl-r': 'symbols-view:toggle-file-symbols'
  'ctrl-alt-down': 'symbols-view:go-to-declaration'
  'ctrl-alt-up': 'symbols-view:return-from-declaration'

'.platform-darwin':
  'cmd-R': 'symbols-view:toggle-project-symbols'

'.platform-win32, .platform-linux':
  'ctrl-R': 'symbols-view:toggle-project-symbols'

Replace .editor -> atom-text-editor in keymap


$ = window.$ = require("jquery")
Bacon = window.Bacon = require("baconjs")
_ = require("lodash")
$.fn.asEventStream = Bacon.$.asEventStream
examples = require("./examples.coffee")

$code = $("#code #editor")
$run = $(".run")

runBus = new Bacon.Bus()

codeMirror = CodeMirror.fromTextArea $code.get(0), {
  lineNumbers: true
  mode: "javascript"
  theme: "solarized dark",
  extraKeys: {
    "Ctrl-Enter": -> runBus.push()
  }
}
initialCode = localStorage.code || examples.first
codeMirror.setValue(initialCode)
codeP = Bacon.fromEventTarget(codeMirror, "change")
  .map(".getValue")
  .toProperty(initialCode)
enabledP = Bacon.constant(true)

codeP.onValue (code) ->
  localStorage.code = code

evalE = codeP.filter(enabledP).sampledBy($run.asEventStream("click").doAction(".preventDefault").merge(runBus))

evalCode = (code) ->
  window.frameLoaded = (frame) ->
    scriptElem = frame.document.createElement "script"
    scriptElem.type = "text/javascript"
    scriptElem.text = code
    frame.document.body.appendChild scriptElem
  $("#game").attr("src", "game.html")

evalE.onValue(evalCode)

codeMirror.focus()
$("body").css("opacity", 1)

$ = window.$ = require("jquery")
Bacon = window.Bacon = require("baconjs")
_ = require("lodash")
$.fn.asEventStream = Bacon.$.asEventStream
examples = require("./examples.coffee")

$code = $("#code #editor")
$run = $(".run")

runBus = new Bacon.Bus()

codeMirror = CodeMirror.fromTextArea $code.get(0), {
  lineNumbers: true
  mode: "javascript"
  theme: "solarized dark",
  extraKeys: {
    "Ctrl-Enter": -> runBus.push()
  }
}
initialCode = localStorage.code || examples.first
codeMirror.setValue(initialCode)
codeP = Bacon.fromEventTarget(codeMirror, "change")
  .map(".getValue")
  .toProperty(initialCode)
enabledP = Bacon.constant(true)

codeP.onValue (code) ->
  localStorage.code = code

evalE = codeP.filter(enabledP).sampledBy($run.asEventStream("click").doAction(".preventDefault").merge(runBus))

evalCode = (code) ->
  window.frameLoaded = (frame) ->
    frame.eval(code)
  $("#game").attr("src", "game.html")

evalE.onValue(evalCode)

codeMirror.focus()
$("body").css("opacity", 1)

Revert "use script elem instead of eval"

This reverts commit c6e6adf9eb37da77fa01b0c856ead2e426463530.


class Creator

  update: (el) =>
    model = el.getAttribute(ObserveJS.attributeName)
    if model?
      @create(el, model)
    else
      @destroy(el)

  create: (el) =>
    model = el.getAttribute(ObserveJS.attributeName)
    if ObserveJS.cache[model]?
      if el.instance?
        el.instance.loaded()
        return

      el.instance = new ObserveJS.cache[model](el)

      el.instance.element = ->
        el

      el.instance.on = (event, target, callback) ->
        if callback?
          el.instance.on.events.push([event, target, callback])
        else
          callback = target
          target = el
        target.addEventListener(event, callback)

      el.instance.on.events = []

      if el.instance.loaded?
        el.instance.loaded()

    else
      throw "error: #{model} is not registered. Add your model with ObserveJS.Models.add(#{model})"

  destroy: (el) =>
    el.instance.on.events?.forEach (event) ->
      event[1].removeEventListener(event[0], event[2])

@ObserveJS.Creator = new Creator()

class Creator

  update: (el) =>
    model = el.getAttribute(ObserveJS.attributeName)
    if model?
      @create(el, model)
    else
      @destroy(el)

  create: (el) =>
    model = el.getAttribute(ObserveJS.attributeName)
    if ObserveJS.cache[model]?
      if el.instance?
        el.instance.loaded()
        return

      el.instance = new ObserveJS.cache[model](el)

      el.instance.element = ->
        el

      el.instance.on = (event, target, callback) ->
        if callback?
          el.instance.on.events.push([event, target, callback])
        else
          callback = target
          target = el
        target.addEventListener(event, callback)

      el.instance.on.events = []

      if el.instance.loaded?
        el.instance.loaded()

    else
      throw "error: #{model} is not registered. Add your model with ObserveJS.Models.add(#{model})"

  destroy: (el) =>
    el.instance.on.events?.forEach (event) ->
      event[1].removeEventListener(event[0], event[2])

    delete el.instance

@ObserveJS.Creator = new Creator()

Delete object to avoid leakage

Delete object to avoid leakage


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