window.coviolations ?=
    views: {}
    models: {}
    plotting: {}
    push: {}

$ ->
    app = window.coviolations

    class app.models.UserProject extends Backbone.Model
        ### User project model ###
        urlRoot: '/api/v1/userprojects/'

    class app.models.UserProjectCollection extends Backbone.Collection
        ### User project collection ###
        url: '/api/v1/userprojects/'
        model: app.models.UserProject

    class app.models.Task extends Backbone.Model
        ### Task model ###
        urlRoot: '/api/v1/tasks/'

    class app.models.TaskCollection extends Backbone.Collection
        ### Task collection ###
        url: '/api/v1/tasks/'
        model: app.models.Task

window.coviolations ?=
    views: {}
    models: {}
    plotting: {}
    push: {}

$ ->
    app = window.coviolations

    class app.models.UserProject extends Backbone.Model
        ### User project model ###
        urlRoot: '/api/v1/projects/project/'

    class app.models.UserProjectCollection extends Backbone.Collection
        ### User project collection ###
        url: '/api/v1/projects/project/'
        model: app.models.UserProject

    class app.models.Task extends Backbone.Model
        ### Task model ###
        urlRoot: '/api/v1/tasks/task/'

    class app.models.TaskCollection extends Backbone.Collection
        ### Task collection ###
        url: '/api/v1/tasks/task/'
        model: app.models.Task

Change api urls in client side

Change api urls in client side


Route = Ember.Route.extend
  model: ->
    # return the current user as the model if authenticated, otherwise a blank object
    new Promise (resolve, reject) =>
      promise = $.ajax
        url: '/api/me'
        dataType: 'json'
      promise.done (result) =>
        user = Ember.Object.create(result)
        if result.isAnon
          @set 'session.isAnon', true
          @set 'session.currentUser', null
        else
          @set 'session.isAnon', false
          @set 'session.currentUser', user
        resolve(user)
      promise.fail (result) ->
        reject()
  clearSession: ->
    @set 'session.currentUser', undefined
    @set 'session.isAnon', true
    @set 'settings.editMode', false
  actions:
    toggleEditMode: ->
      @toggleProperty 'settings.editMode'
      @send 'flushUpdateQueue'
    flushUpdateQueue: ->
      return unless @get('session.currentUser.isAdmin')
      updateQueue = @get('updateQueue')
      while updateQueue.get('length')
        section = updateQueue.popObject()
        section.save()
    error: (result, transition) ->
      if result.status is 403
        @clearSession()
        @transitionTo 'signout'
`export default Route`

Route = Ember.Route.extend
  model: ->
    # return the current user as the model if authenticated, otherwise a blank object
    new Ember.RSVP.Promise (resolve, reject) =>
      promise = $.ajax
        url: '/api/me'
        dataType: 'json'
      promise.done (result) =>
        user = Ember.Object.create(result)
        if result.isAnon
          @set 'session.isAnon', true
          @set 'session.currentUser', null
        else
          @set 'session.isAnon', false
          @set 'session.currentUser', user
        resolve(user)
      promise.fail (result) ->
        reject()
  clearSession: ->
    @set 'session.currentUser', undefined
    @set 'session.isAnon', true
    @set 'settings.editMode', false
  actions:
    toggleEditMode: ->
      @toggleProperty 'settings.editMode'
      @send 'flushUpdateQueue'
    flushUpdateQueue: ->
      return unless @get('session.currentUser.isAdmin')
      updateQueue = @get('updateQueue')
      while updateQueue.get('length')
        section = updateQueue.popObject()
        section.save()
    error: (result, transition) ->
      if result.status is 403
        @clearSession()
        @transitionTo 'signout'
`export default Route`

Return proper Ember.RSVP.Promise to avoid troubles on browsers


{Task} = require 'atom'
ctags = require 'ctags'
fs = require 'fs-plus'

handlerPath = require.resolve('./load-tags-handler')

module.exports =
  getTagsFile: ->
    tagsFile = atom.project.resolve("tags")
    return tagsFile if fs.isFileSync(tagsFile)

    tagsFile = atom.project.resolve(".tags")
    return tagsFile if fs.isFileSync(tagsFile)

    tagsFile = atom.project.resolve("TAGS")
    return tagsFile if fs.isFileSync(tagsFile)

  find: (editor, callback) ->
    word = editor.getTextInRange(editor.getCursor().getCurrentWordBufferRange())
    tagsFile = @getTagsFile()

    if word?.length > 0 and tagsFile
      ctags.findTags(tagsFile, word, callback)
    else
      process.nextTick -> callback(null, [])

  getAllTags: (callback) ->
    projectTags = []
    task = Task.once handlerPath, atom.project.getPath(), -> callback(projectTags)
    task.on 'tags', (paths) -> projectTags.push(paths...)
    task

{Task} = require 'atom'
ctags = require 'ctags'
fs = require 'fs-plus'

handlerPath = require.resolve('./load-tags-handler')

module.exports =
  getTagsFile: ->
    tagsFile = atom.project.resolve("tags")
    return tagsFile if fs.isFileSync(tagsFile)

    tagsFile = atom.project.resolve(".tags")
    return tagsFile if fs.isFileSync(tagsFile)

    tagsFile = atom.project.resolve("TAGS")
    return tagsFile if fs.isFileSync(tagsFile)

  find: (editor, callback) ->
    range = editor.getCursor().getCurrentWordBufferRange()
    symbol = getSymbolFromRange(editor, range)

    tagsFile = @getTagsFile()

    if symbol?.length > 0 and tagsFile
      ctags.findTags(tagsFile, symbol, callback)
    else
      process.nextTick -> callback(null, [])

  getAllTags: (callback) ->
    projectTags = []
    task = Task.once handlerPath, atom.project.getPath(), -> callback(projectTags)
    task.on 'tags', (paths) -> projectTags.push(paths...)
    task

getSymbolFromRange = (editor, range) ->
  word = editor.getTextInRange(range)
  charAfterWord = getCharAfterWord(editor, range)

  if charAfterWord == "?" or charAfterWord == "!"
    return word + charAfterWord
  else
    return word

getCharAfterWord = (editor, range) ->
  return editor.getTextInRange([[range.end.row, range.end.column], [range.end.row, range.end.column + 1]])

Add support for Ruby bang and question methods

* The ::find method of tag-reader.coffee uses getCurrentWordBufferRange()
  to pull in the current word to pull in the current tag. Passing in a
  custom regexp to getCurrentWordBufferRange() determines what constitutes
  a word.

* Obtain the charAfterWord by incrementing the row by one. If the character
  is a question mark or exclamation point, append charAfterWord to word
  and search the whole thing.


ledger.api ?= {}

class ledger.api.HttpClient extends @HttpClient
  constructor: () -> super

  authenticated: -> ledger.api.authenticated(@_baseUrl)

class ledger.api.RestClient
  @API_BASE_URL: ledger.config.restClient.baseUrl

  @singleton: () -> @instance ||= new @()

  http: () ->
    @_client ||= @_httpClientFactory()
    @_client.setHttpHeader 'X-Ledger-Locale', chrome.i18n.getUILanguage()
    @_client.setHttpHeader 'X-Ledger-Platform', 'chrome'
    @_client

  networkErrorCallback: (callback) ->
    errorCallback = (xhr, status, message) ->
        callback(null, {xhr, status, message, code: ledger.errors.NetworkError})
    errorCallback

  _httpClientFactory: -> new ledger.api.HttpClient(ledger.config.restClient.baseUrl)

class ledger.api.AuthRestClient extends ledger.api.RestClient
  _httpClientFactory: -> super().authenticated(@baseUrl)

@testRestClientAuthenticate = ->
  f = ->
    ledger.app.wallet.getBitIdAddress (address) ->
      r = new ledger.api.AuthRestClient()
      r.http().get
        url: "accountsettings/#{address}"
        onSuccess: () -> l arguments
        onError: () ->
          e arguments
  ledger.app.wallet.getState (state) ->
    if state is ledger.wallet.States.LOCKED
      ledger.app.wallet.unlockWithPinCode '0000', f
    else
      do f

ledger.api ?= {}

class ledger.api.HttpClient extends @HttpClient
  constructor: () -> super

  authenticated: -> ledger.api.authenticated(@_baseUrl)

class ledger.api.RestClient
  @API_BASE_URL: ledger.config.restClient.baseUrl

  @singleton: () -> @instance ||= new @()

  http: () ->
    @_client ||= @_httpClientFactory()
    @_client.setHttpHeader 'X-Ledger-Locale', chrome.i18n.getUILanguage()
    @_client.setHttpHeader 'X-Ledger-Platform', 'chrome'
    @_client.setHttpHeader 'X-Ledger-Environment', ledger.env
    @_client

  networkErrorCallback: (callback) ->
    errorCallback = (xhr, status, message) ->
        callback(null, {xhr, status, message, code: ledger.errors.NetworkError})
    errorCallback

  _httpClientFactory: -> new ledger.api.HttpClient(ledger.config.restClient.baseUrl)

class ledger.api.AuthRestClient extends ledger.api.RestClient
  _httpClientFactory: -> super().authenticated(@baseUrl)

@testRestClientAuthenticate = ->
  f = ->
    ledger.app.wallet.getBitIdAddress (address) ->
      r = new ledger.api.AuthRestClient()
      r.http().get
        url: "accountsettings/#{address}"
        onSuccess: () -> l arguments
        onError: () ->
          e arguments
  ledger.app.wallet.getState (state) ->
    if state is ledger.wallet.States.LOCKED
      ledger.app.wallet.unlockWithPinCode '0000', f
    else
      do f

Add 'X-Ledger-Environment' in HTTP requests header


Darkswarm.filter "localizeCurrency", (currencyConfig)->
  # Convert number to string currency using injected currency configuration.
  (amount) ->
    # Set country code (eg. "US").
    currency_code = if currencyConfig.display_currency then " " + currencyConfig.currency else ""
    # Set decimal points,  2 or 0 if hide_cents.
    decimals = if currencyConfig.hide_cents == "true" then 0 else 2
    # Set wether the currency symbol appears first
    sign_first =  currencyConfig.symbol_position == 'before'
    # We need to use parseFloat as the amount should come in as a string.
    amount = parseFloat(amount)

    # Build the final price string.
    I18n.toCurrency(amount, {precision: decimals, unit: currencyConfig.symbol, sign_first: sign_first}) + currency_code

Darkswarm.filter "localizeCurrency", (currencyConfig)->
  # Convert number to string currency using injected currency configuration.
  (amount) ->
    # Set country code (eg. "US").
    currency_code = if currencyConfig.display_currency then " " + currencyConfig.currency else ""
    # Set decimal points, 2 or 0 if hide_cents.
    decimals = if currencyConfig.hide_cents == "true" then 0 else 2
    # Set format if the currency symbol should come after the number, otherwise (default) use the locale setting.
    format = if currencyConfig.symbol_position == "after" then "%n %u" else undefined
    # We need to use parseFloat as the amount should come in as a string.
    amount = parseFloat(amount)

    # Build the final price string.
    I18n.toCurrency(amount, {precision: decimals, unit: currencyConfig.symbol, format: format}) + currency_code

Fix wrong way to force currency symbol after the amount


'use strict';
util           = require 'util'
{EventEmitter} = require 'events'
elasticsearch  = require 'elasticsearch'
_              = require 'lodash'
debug          = require('debug')('meshblu-elasticsearch')
request = require 'request'

MESSAGE_SCHEMA =
  type: 'object'
  properties:
    exampleBoolean:
      type: 'boolean'
      required: true
    exampleString:
      type: 'string'
      required: true

OPTIONS_SCHEMA =
  type: 'object'
  properties:
    firstExampleOption:
      type: 'string'
      required: true

class Plugin extends EventEmitter
  constructor: ->
    @options = {}
    @messageSchema = MESSAGE_SCHEMA
    @optionsSchema = OPTIONS_SCHEMA
    @elasticsearch = new elasticsearch.Client host: 'localhost:9200'

  onMessage: (message) =>
    debug 'onMessage', message
    return if message.topic == 'device-status'

    topic = _.snakeCase message.topic

    @elasticsearch.create
      index: "event_#{topic}"
      type: 'event'
      body: message.payload

  onConfig: (device) =>
    @setOptions device.options

  setOptions: (options={}) =>
    @options = options

module.exports =
  messageSchema: MESSAGE_SCHEMA
  optionsSchema: OPTIONS_SCHEMA
  Plugin: Plugin

'use strict';
util           = require 'util'
{EventEmitter} = require 'events'
elasticsearch  = require 'elasticsearch'
_              = require 'lodash'
debug          = require('debug')('meshblu-elasticsearch')
request = require 'request'

MESSAGE_SCHEMA =
  type: 'object'
  properties:
    exampleBoolean:
      type: 'boolean'
      required: true
    exampleString:
      type: 'string'
      required: true

OPTIONS_SCHEMA =
  type: 'object'
  properties:
    firstExampleOption:
      type: 'string'
      required: true

class Plugin extends EventEmitter
  constructor: ->
    @options = {}
    @messageSchema = MESSAGE_SCHEMA
    @optionsSchema = OPTIONS_SCHEMA

    @elasticsearch = new elasticsearch.Client
      host:     process.env.ELASTICSEARCH_HOST
      hostname: process.env.ELASTICSEARCH_HOSTNAME ? 'localhost'
      port:     process.env.ELASTICSEARCH_PORT ? '9200'

  onMessage: (message) =>
    debug 'onMessage', message
    return if message.topic == 'device-status'

    topic = _.snakeCase message.topic

    @elasticsearch.create
      index: "event_#{topic}"
      type: 'event'
      body: message.payload

  onConfig: (device) =>
    @setOptions device.options

  setOptions: (options={}) =>
    @options = options

module.exports =
  messageSchema: MESSAGE_SCHEMA
  optionsSchema: OPTIONS_SCHEMA
  Plugin: Plugin

Allow overriding of elasticsearch config via environment


module.exports = (Impromptu, register, system) ->
  register 'pwd',
    update: ->
      process.env.PWD

  register 'prettyPwd',
    update: ->
      cwd = process.env.PWD
      if cwd.indexOf process.env.HOME == 0
        cwd = '~' + cwd.slice process.env.HOME.length
      cwd

  register 'user',
    update: (done) ->
      Impromptu.exec 'whoami', done

  register 'host',
    update: (done) ->
      Impromptu.exec 'hostname', done

  register 'shortHost',
    update: (done) ->
      system.host (err, host) ->
        done err, host.split('.', 1)[0]

module.exports = (Impromptu, register, system) ->
  register 'pwd',
    update: ->
      process.env.PWD

  register 'prettyPwd',
    update: ->
      cwd = process.env.PWD
      if cwd.indexOf(process.env.HOME) == 0
        cwd = '~' + cwd.slice process.env.HOME.length
      cwd

  register 'user',
    update: (done) ->
      Impromptu.exec 'whoami', done

  register 'host',
    update: (done) ->
      Impromptu.exec 'hostname', done

  register 'shortHost',
    update: (done) ->
      system.host (err, host) ->
        done err, host.split('.', 1)[0]

Fix logic/syntax error checking for child paths of home directory

Fixes #5


{$} = require 'atom'

module.exports =
  configDefaults:
    enabled: false

  activate: ->
    atom.workspaceView.on 'focusout', ".editor:not(.mini)", (event) =>
      editor = event.targetView()?.getModel()
      @autosave(editor)

    atom.workspaceView.on 'pane:before-item-destroyed', (event, paneItem) =>
      @autosave(paneItem)

    $(window).preempt 'beforeunload', =>
      for pane in atom.workspaceView.getPanes()
        @autosave(paneItem) for paneItem in pane.getItems()

  autosave: (paneItem) ->
    return unless atom.config.get('autosave.enabled')
    return unless paneItem?.getUri?()?
    return unless paneItem?.isModified?()

    paneItem?.save?()

{$} = require 'atom'

module.exports =
  configDefaults:
    enabled: false

  activate: ->
    atom.workspaceView.on 'focusout', ".editor:not(.mini)", (event) =>
      editor = $(event.target).closest('.editor').view()?.getModel()
      @autosave(editor)

    atom.workspaceView.on 'pane:before-item-destroyed', (event, paneItem) =>
      @autosave(paneItem)

    $(window).preempt 'beforeunload', =>
      for pane in atom.workspaceView.getPanes()
        @autosave(paneItem) for paneItem in pane.getItems()

  autosave: (paneItem) ->
    return unless atom.config.get('autosave.enabled')
    return unless paneItem?.getUri?()?
    return unless paneItem?.isModified?()

    paneItem?.save?()

Make focusout handling compatible with React editor

Because the input element isn't handled by jQuery, it's not possible
to retrieve the containing view with Event::targetView. This approach is
wordier but can retrieve the view correctly.


{$, EditorView, View} = require 'atom'
utils = require './Utils'

module.exports=
class PackageListCustomListView extends View

  @content: ->
    @div class: 'overlay from-top', =>
      @subview 'gistUrl', new EditorView(mini: true, placeholderText: 'ID of Gist with packages list.')
      @div class: 'block', =>
        @span class: 'pull-left', =>
          @button class: 'btn btn-success inline-block-tight gp-confirm-button', click: 'confirmList', 'Confirm'
        @span class: 'pull-right', =>
          @button class: 'btn btn-error inline-block-tight gp-cancel-button', click: 'abort', 'Cancel'

  confirmList: ->
    wasListLoaded = utils.loadPackageListFromGIst @gistUrl.text()
    @detach() if wasListLoaded

  initialize: ->
    @on 'core:cancel', => @detach()
    @on 'core:confirm', => @confirmList()
    atom.workspaceView.append this

  abort: ->
    @detach()

{$, EditorView, View} = require 'atom'
utils = require './Utils'

module.exports=
class PackageListCustomListView extends View

  @content: ->
    @div class: 'overlay from-top', =>
      @subview 'gistUrl', new EditorView(mini: true, placeholderText: 'ID of Gist with packages list.')
      @div class: 'block', =>
        @span class: 'pull-left', =>
          @button class: 'btn btn-success inline-block-tight gp-confirm-button', click: 'confirmList', 'Confirm'
        @span class: 'pull-right', =>
          @button class: 'btn btn-error inline-block-tight gp-cancel-button', click: 'abort', 'Cancel'

  confirmList: ->
    utils.loadPackageListFromGIst @gistUrl.text()
    @detach()

  initialize: ->
    @on 'core:cancel', => @detach()
    @on 'core:confirm', => @confirmList()
    atom.workspaceView.append this

  abort: ->
    @detach()

Detach custom list window even if downloading fails


window.UsersComponent = class UsersComponent
  @initialize: ->
    @addEventListeners()

  @addEventListeners: ->
    @userDetailsTooltip()

  @userDetailsTooltip: ->
    tooltipClass = '.information-tooltip'
    $(document).on('click', "#{tooltipClass}-trigger", (ev) ->
      ev.preventDefault()
      $this = $(@)
      if ($this.find('.fa-align-left').length)
        $("#{tooltipClass}-trigger").not(@).siblings(tooltipClass).hide()
        $("#{tooltipClass}-trigger").not(@).html(' details')
        $this.html(' close')
        $this.siblings(tooltipClass).show()
      else
        $this.html(' details')
        $this.siblings(tooltipClass).hide()
    )

$(document).ready -> UsersComponent.initialize()

window.UsersComponent = class UsersComponent
  @initialize: ->
    @addEventListeners()

  @addEventListeners: ->
    @userDetailsTooltip()
    #Enables search through users table
    enableSearch()

  @userDetailsTooltip: ->
    tooltipClass = '.information-tooltip'
    $(document).on('click', "#{tooltipClass}-trigger", (ev) ->
      ev.preventDefault()
      $this = $(@)
      if ($this.find('.fa-align-left').length)
        $("#{tooltipClass}-trigger").not(@).siblings(tooltipClass).hide()
        $("#{tooltipClass}-trigger").not(@).html(' details')
        $this.html(' close')
        $this.siblings(tooltipClass).show()
      else
        $this.html(' details')
        $this.siblings(tooltipClass).hide()
    )

$(document).ready -> UsersComponent.initialize()

Enable search in users table

Enable search in users table


Walrus = require '../lib/walrus'

describe 'Walrus.Parser', ->

  it 'should be defined', -> expect( Walrus.Parser ).toBeDefined( )

  describe '#parse', ->

    it 'should be defined', -> expect( Walrus.Parser.parse ).toBeDefined( )

    it 'should parse all examples correctly', ->

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

      test = ( basename ) ->

        text = fs.readFileSync "#{basename}.wal",  'utf8'
        json = fs.readFileSync "#{basename}.js",   'utf8'
        html = fs.readFileSync "#{basename}.html", 'utf8'

        Walrus.Parser.parser.yy = Walrus.AST

        tmpl = Walrus.Parser.parse text

        console.log tmpl
        console.log eval( "(#{json})" )
        console.log tmpl.compile( eval( "(#{json})" ) )

        expect( tmpl.compile eval( "(#{json})" ) ).toEqual html

      for file in fs.readdirSync './spec/examples'

        test "./spec/examples/#{path.basename file, '.wal'}" if path.extname( file ) is '.wal'

Walrus = require '../lib/walrus'

describe 'Walrus.Parser', ->

  it 'should be defined', -> expect( Walrus.Parser ).toBeDefined( )

  describe '#parse', ->

    it 'should be defined', -> expect( Walrus.Parser.parse ).toBeDefined( )

    fs    = require 'fs'
    path  = require 'path'
    specs = './spec/examples'

    Walrus.Parser.parser.yy = Walrus.AST

    for file in fs.readdirSync specs when path.extname( file ) is '.wal'

      do ( file ) ->

        base = path.basename file, '.wal'

        text = fs.readFileSync "#{specs}/#{base}.wal",  'utf8'
        json = fs.readFileSync "#{specs}/#{base}.js",   'utf8'
        html = fs.readFileSync "#{specs}/#{base}.html", 'utf8'

        tmpl = Walrus.Parser.parse text

        console.log '*****'
        console.log file
        console.log tmpl
        console.log eval( "(#{json})" )
        console.log tmpl.compile( eval( "(#{json})" ) )

        it "should pass the #{base} example", -> expect( tmpl.compile eval( "(#{json})" ) ).toEqual html

Refactor parser specs to gain one expectation per spec


require('./spec_helper')
spy = require('through2-spy')

describe 'smoke test', ->
  createFS = require('../index')
  coffee = require('gulp-coffee')

  it 'should mock gulp', (done) ->
    fs = createFS
          src:
            coffee:
              'sample.coffee': """
                console.log 'Hello world'
              """
              'another.coffee': """
                fib = (n) ->
                  switch n
                    when 0, 1
                      1
                    else
                      fib(n) + fib(n-1)
              """

    fs.createReadStream 'src/coffee'
      .pipe coffee
        bare: true
      .pipe fs.createWriteStream('dest/js', true)
      .onFinished done, (folder) ->
        console.log fs.directory  # Display whole tree of files
        folder.should.equal fs.openFolder('dest/js')
        folder['sample.js'].should.not.be.null
        folder['another.js'].should.not.be.null

require('./spec_helper')
spy = require('through2-spy')

describe 'smoke test', ->
  createFS = require('../index')
  coffee = require('gulp-coffee')

  it 'should mock gulp', (done) ->
    fs = createFS
          src:
            coffee:
              'sample.coffee': """
                console.log 'Hello world'
              """
              'another.coffee': """
                fib = (n) ->
                  switch n
                    when 0, 1
                      1
                    else
                      fib(n) + fib(n-1)
              """

    fs.src 'src/coffee/*.coffee'
      .pipe coffee
        bare: true
      .pipe fs.dest 'dest/js'
      .onFinished done, (folder) ->
        # console.log fs.directory  # Display whole tree of files
        folder.should.equal fs.openFolder('dest/js')
        folder['sample.js'].should.not.be.null
        folder['another.js'].should.not.be.null

Update smoke test with latest API

Update smoke test with latest API


describe "Welcome", ->
  editor = null

  beforeEach ->
    waitsForPromise ->
      atom.packages.activatePackage("welcome")
    waitsFor ->
      editor = atom.workspace.getActiveTextEditor()

  describe "when activated for the first time", ->
    it "shows the welcome buffer", ->
      expect(editor.getText()).toMatch(/Welcome to Atom/)

  describe "when activated again", ->
    beforeEach ->
      atom.workspace.getActivePane().destroy()
      atom.packages.deactivatePackage("welcome")

      waitsForPromise ->
        atom.packages.activatePackage("welcome")
      waits(200)

    it "doesn't show the welcome buffer", ->
      expect(atom.workspace.getPaneItems().length).toBe(0)

  describe "the welcome:show-welcome-buffer command", ->
    workspaceElement = null

    beforeEach ->
      workspaceElement = atom.views.getView(atom.workspace)

    it "shows the welcome buffer", ->
      atom.workspace.getActivePane().destroy()

      atom.commands.dispatch(workspaceElement, 'welcome:show-welcome-buffer')

      waitsFor ->
        editor = atom.workspace.getActiveTextEditor()

      runs ->
        expect(editor.getText()).toMatch(/Welcome to Atom/)

describe "Welcome", ->
  editor = null

  beforeEach ->
    spyOn(atom.workspace, 'open').andCallThrough()

    waitsForPromise ->
      atom.packages.activatePackage("welcome")

    waitsFor ->
      editor = atom.workspace.getActiveTextEditor()

  describe "when activated for the first time", ->
    it "shows the welcome buffer", ->
      expect(atom.workspace.open).toHaveBeenCalled()
      expect(editor.getText()).toMatch(/Welcome to Atom/)

  describe "when activated again", ->
    beforeEach ->
      atom.workspace.getActivePane().destroy()
      atom.packages.deactivatePackage("welcome")
      atom.workspace.open.reset()

      waitsForPromise ->
        atom.packages.activatePackage("welcome")

    it "doesn't show the welcome buffer", ->
      expect(atom.workspace.open).not.toHaveBeenCalled()

  describe "the welcome:show-welcome-buffer command", ->
    workspaceElement = null

    beforeEach ->
      workspaceElement = atom.views.getView(atom.workspace)

    it "shows the welcome buffer", ->
      atom.workspace.getActivePane().destroy()

      atom.commands.dispatch(workspaceElement, 'welcome:show-welcome-buffer')

      waitsFor ->
        editor = atom.workspace.getActiveTextEditor()

      runs ->
        expect(editor.getText()).toMatch(/Welcome to Atom/)

Speed up test suite more

Speed up test suite more


twilio = require("twilio")
bot = require("../bot")
config = require("../config")
TWILIO_PHONE = config.twilio.phone
TWILIO_ACCOUNT_SID = config.twilio.accountSid
TWILIO_AUTH_TOKEN = config.twilio.authToken
if not (TWILIO_PHONE and TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN)
  throw new Error "Please configure Twilio"
client = twilio(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)

module.exports = receive: (req, res) ->

  # console.log(req, res);

  # Respond empty to SMS.
  # Empty responses are not sent as SMS to sender.
  twiml = new twilio.TwimlResponse()
  res.send(twiml)

  from = req.body.From
  message = req.body.Body
  bot.generateResponse
    conversation: from
    message: message
    meta: req.body
  , (error, result) ->

    # console.log(error, result);
    if error
      body = error.message
    else
      body = result.response.plain

    client.sendMessage({
      to: from
      from: TWILIO_PHONE
      body: body
    }, (err, responseData) ->
        # console.log(err, responseData)
    )

twilio = require("twilio")
bot = require("../bot")
config = require("../config")
TWILIO_PHONE = config.twilio.phone
TWILIO_ACCOUNT_SID = config.twilio.accountSid
TWILIO_AUTH_TOKEN = config.twilio.authToken
if not (TWILIO_PHONE and TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN)
  throw new Error "Please configure Twilio"
client = twilio(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)

module.exports = receive: (req, res) ->

  # console.log(req, res);

  # Respond empty to SMS.
  # Empty responses are not sent as SMS to sender.
  twiml = new twilio.TwimlResponse()
  res.send(twiml)

  from = req.body.From
  message = req.body.Body
  bot.generateResponse
    conversation: from
    message: message
    meta: req.body
  , (error, result) ->

    # console.log(error, result);
    if error
      body = "An error occurred: #{error.message}"
    else
      body = result.response.plain

    client.sendMessage({
      to: from
      from: TWILIO_PHONE
      body: body
    }, (err, responseData) ->
        # console.log(err, responseData)
    )

Add prefix to error messages

Add prefix to error messages

"An error occurred: #{ERROR}"


Crafty.c 'ViewportRelativeMotion',
  init: ->

  remove: ->

  viewportRelativeMotion: ({ x, y, speed }) ->
    @_startLocation = { x, y }
    @_speed = speed
    @_initialViewport =
      x: (Crafty.viewport.width / 4)
    @_location =
      sx: x + ((x - @_initialViewport.x) * (@_speed - 1))
      sy: y
      dx: @dx ? 0
      dy: @dy ? 0

    shifted = (@_initialViewport.x - Crafty.viewport._x) * (@_speed - 1)
    newX = @_location.sx - shifted + @dx
    newY = @_location.sy - (Crafty.viewport._y * (1 - @_speed)) + @dy
    @_location.x = newX
    @_location.y = newY
    @attr @_location

    @motion = Crafty.bind 'ViewportScroll', =>
      shifted = (@_initialViewport.x - Crafty.viewport._x) * (@_speed - 1)
      newX = @_location.sx - shifted + @dx
      newY = @_location.sy - (Crafty.viewport._y * (1 - @_speed)) + @dy
      @attr x: newX, y: newY
    this

  remove: ->
    Crafty.unbind 'CameraScroll', @motion

Crafty.c 'ViewportRelativeMotion',
  init: ->

  remove: ->

  viewportRelativeMotion: ({ x, y, speed }) ->
    @_startLocation = { x, y }
    @_speed = speed
    @_initialViewport =
      x: (Crafty.viewport.width / 4)
    @_location =
      sx: x + ((x - @_initialViewport.x) * (@_speed - 1))
      sy: y
      dx: @dx ? 0
      dy: @dy ? 0

    shifted = (@_initialViewport.x - Crafty.viewport._x) * (@_speed - 1)
    newX = @_location.sx - shifted + @_location.dx
    newY = @_location.sy - (Crafty.viewport._y * (1 - @_speed)) + @_location.dy
    @_location.x = newX
    @_location.y = newY
    @attr @_location

    @motion = Crafty.bind 'ViewportScroll', =>
      shifted = (@_initialViewport.x - Crafty.viewport._x) * (@_speed - 1)
      newX = @_location.sx - shifted + @dx
      newY = @_location.sy - (Crafty.viewport._y * (1 - @_speed)) + @dy
      @attr x: newX, y: newY
    this

  remove: ->
    Crafty.unbind 'CameraScroll', @motion

Fix issue of initial placement of items

Fix issue of initial placement of items


packageJSON = require('../../package.json')

exports.version =
	signature: 'version'
	description: 'output the version number'
	action: ->
		console.log(packageJSON.version)

packageJSON = require('../../package.json')

exports.version =
	signature: 'version'
	description: 'output the version number'
	action: ->
		console.log("#{packageJSON.name}: #{packageJSON.version}")
		console.log("node: #{process.version}")

Add node version to version command output


exports.config =
  # See http://brunch.readthedocs.org/en/latest/config.html for documentation.
  paths:
    public: 'public'
  files:
    javascripts:
      joinTo:
        'js/app.js': /^app/

    stylesheets:
      joinTo:
        'ss/app.css': /^app\/styles/

  plugins:
    sass:
      mode: 'native'

Configure sass to use libsass

Configure sass to use libsass


###
| Storyboard
| (c) Guillermo Grau Panea 2016
| License: MIT
###
k = require './constants'

# Enable chalk colors in the browser (we'll handle the conversion)
if k.IS_BROWSER
  process.env.COLORTERM = true

hub = require './hub'
stories = require './stories'
consoleListener = require './listeners/console'

mainStory = stories.createStory []
hub.init {mainStory}
hub.addListener consoleListener

# Make sure a record is created
mainStory.changeTitle 'ROOT STORY'

module.exports = {
  mainStory,
  addListener: hub.addListener,
  getListeners: hub.getListeners,
}

###
| Storyboard
| (c) Guillermo Grau Panea 2016
| License: MIT
###
k = require './constants'

# Chalk is disabled by default in the browser. Override
# this default (we'll handle ANSI code conversion ourselves
# when needed)
chalk = require 'chalk'
chalk.enabled = true

mainStory = require './stories'
hub = require './hub'
hub.init {mainStory}

hub.addListener require './listeners/console'
if k.IS_BROWSER
  hub.addListener require './listeners/wsClient'

# Make sure a record is created for the root story
mainStory.logStory 'CREATED'

module.exports = {
  mainStory,
  addListener: hub.addListener,
  getListeners: hub.getListeners,
}

Add wsClient listener by default in browser. More robust way to enable chalk in browser


module.exports = (grunt) ->
  grunt.loadNpmTasks 'grunt-contrib-coffee'
  grunt.loadNpmTasks 'grunt-contrib-concat'
  grunt.loadNpmTasks 'grunt-chmod'

  grunt.initConfig
    meta:
      shebang: '#!/usr/bin/env node'

    coffee:
      default:
        options:
          bare: true
        files:
          'summon': 'summon.coffee'

    # Prepend the node shebang line
    concat:
      options:
        banner: '<%= meta.shebang %>\n'
      dist:
        src: 'summon'
        dest: 'summon'

    # Make sure the file is executable
    chmod:
      options:
        mode: '755'
      target:
        src: 'summon'

  grunt.registerTask 'default', ['coffee', 'concat', 'chmod']

module.exports = (grunt) ->
  grunt.loadNpmTasks 'grunt-contrib-coffee'
  grunt.loadNpmTasks 'grunt-contrib-concat'
  grunt.loadNpmTasks 'grunt-chmod'

  grunt.initConfig
    meta:
      shebang: '#!/usr/bin/env node'
      hacking: """
        // Please don't edit this file directly. It is compiled from the CoffeeScript
        // in summon.coffee. If you edit the CoffeeScript source and compile its changes,
        // please keep the commits to this file separate. See the README for instructions
        // on setting up a pre-commit hook to help with that.
      """

    coffee:
      default:
        options:
          bare: true
        files:
          'summon': 'summon.coffee'

    # Prepend the node shebang line
    concat:
      options:
        banner: '<%= meta.shebang %>\n\n<%= meta.hacking %>\n\n'
      dist:
        src: 'summon'
        dest: 'summon'

    # Make sure the file is executable
    chmod:
      options:
        mode: '755'
      target:
        src: 'summon'

  grunt.registerTask 'default', ['coffee', 'concat', 'chmod']

Add info to compiled summon file about editing/commiting it.


module.exports = (grunt) ->

    grunt.initConfig

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

        meta:
            banner: '/*! <%= pkg.name %> <%= pkg.version %> | (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %> | <%= pkg.license %> License */\n'

        coffee:
            compile:
                expand: true
                cwd: 'coffee/'
                src: ['**/*.coffee']
                dest: 'js/'
                ext: '.js'

        uglify:
            options:
                banner: '<%= meta.banner %>'
            dist:
                files:
                    'js/uTip.min.js': ['js/uTip.js']

        watch:
            files: 'coffee/**/*.coffee'
            tasks: ['compile']

    grunt.loadNpmTasks 'grunt-contrib-coffee'
    grunt.loadNpmTasks 'grunt-contrib-uglify'
    grunt.loadNpmTasks 'grunt-contrib-watch'

    grunt.registerTask 'compile', ['coffee', 'uglify']
    grunt.registerTask 'default', ['compile', 'watch']

module.exports = (grunt) ->

    grunt.initConfig

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

        meta:
            banner: '/*! <%= pkg.name %> <%= pkg.version %> | (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %> | <%= pkg.license %> License */\n'

        coffee:
            compile:
                expand: true
                cwd: 'coffee/'
                src: ['**/*.coffee']
                dest: 'js/'
                ext: '.js'

        uglify:
            options:
                banner: '<%= meta.banner %>'
            dist:
                files:
                    'js/uTip.min.js': ['js/uTip.js']

        watch:
            files: 'coffee/**/*.coffee'
            tasks: ['compile']

    grunt.loadNpmTasks 'grunt-contrib-coffee'
    grunt.loadNpmTasks 'grunt-contrib-uglify'
    grunt.loadNpmTasks 'grunt-contrib-watch'

    grunt.registerTask 'compile', ['coffee', 'uglify']
    grunt.registerTask 'dev', ['compile', 'watch']
    grunt.registerTask 'default', ['default']

Create "dev" grunt task and set "default" as alias


Node = require '../node'

module.exports = class Folder extends Node
  get: (cb) -> @api.get(@nodePath, {}, cb.bind(@))
  create: (cb) -> @api.post(@nodePath, {}, cb.bind(@))
  delete: (cb) -> @api.delete(@nodePath, {}, cb.bind(@))
  move: (to, cb) -> @api.post("#{@nodePath}/move", { to: to }, cb.bind(@))
  rename: @move

  color: (color, cb) -> @api.put("#{@nodePath}/color", { color: color }, cb.bind(@))

Node = require '../node'

module.exports = class Folder extends Node
  get: (cb) -> @api.get(@nodePath, {}, cb.bind(@))
  create: (cb) -> @api.post(@nodePath, {}, cb.bind(@))
  delete: (cb) -> @api.delete(@nodePath, {}, cb.bind(@))
  move: (to, cb) -> @api.post("#{@nodePath}/move", { to: to }, cb.bind(@))
  rename: @move

  changeColor: (color, cb) -> @api.put("#{@nodePath}/color", { color: color }, cb.bind(@))

Change to changeColor to avoid property conflict


class window.FactRelation extends Backbone.Model

  defaults:
    evidence_type: 'FactRelation'

  setOpinion: (type) ->
    $.ajax
      url: @url() + "/opinion/" + type
      success: (data) =>
        mp_track "Evidence: opinionate",
          type: type
          evidence_id: @id

        @set data

      type: "post"

  removeOpinion: ->
    $.ajax
      url: "#{@url()}/opinion"
      type: "delete"
      success: (data) =>
        @set data

  getFact: ->
    return @_fact if @_fact?

    @_fact = new Fact(@get('from_fact'))
    @on 'change', =>
      @_fact.set @get('from_fact')
    @_fact

  believe: -> @setOpinion "believes"
  disbelieve: -> @setOpinion "disbelieves"

  isBelieving: -> @get('current_user_opinion') == 'believes'
  isDisBelieving: -> @get('current_user_opinion') == 'disbelieves'

  current_opinion: -> @get('current_user_opinion')

  creator: -> new User(@get('created_by'))

  can_destroy: -> @get 'can_destroy?'

  urlRoot: -> @collection.factRelationsUrl()

  validate: (attributes) ->
    unless attributes.evidence_id? or /^.*\S.*$/.test(attributes.displaystring)
      'Should have either an evidence_id or a displaystring'

class window.FactRelation extends Backbone.Model

  defaults:
    evidence_type: 'FactRelation'
    opinions:
      formatted_relevance: "?"

  setOpinion: (type) ->
    $.ajax
      url: @url() + "/opinion/" + type
      success: (data) =>
        mp_track "Evidence: opinionate",
          type: type
          evidence_id: @id

        @set data

      type: "post"

  removeOpinion: ->
    $.ajax
      url: "#{@url()}/opinion"
      type: "delete"
      success: (data) =>
        @set data

  getFact: ->
    return @_fact if @_fact?

    @_fact = new Fact(@get('from_fact'))
    @on 'change', =>
      @_fact.set @get('from_fact')
    @_fact

  believe: -> @setOpinion "believes"
  disbelieve: -> @setOpinion "disbelieves"

  isBelieving: -> @get('current_user_opinion') == 'believes'
  isDisBelieving: -> @get('current_user_opinion') == 'disbelieves'

  current_opinion: -> @get('current_user_opinion')

  creator: -> new User(@get('created_by'))

  can_destroy: -> @get 'can_destroy?'

  urlRoot: -> @collection.factRelationsUrl()

  validate: (attributes) ->
    unless attributes.evidence_id? or /^.*\S.*$/.test(attributes.displaystring)
      'Should have either an evidence_id or a displaystring'

Set also placeholder when adding a fact relation


reloadStylesheets = ->
  queryString = '?reload=' + new Date().getTime()
  el.href = el.href.replace(/\?.*|$/, queryString) for el in document.querySelectorAll('link[rel="stylesheet"]')

connect = (opts={}) ->
  # if user is running mozilla then use it's built-in WebSocket
  window.WebSocket ||= window.MozWebSocket

  connection = new WebSocket('ws://' + (opts.host || document.domain || 'localhost') + ':'+(opts.port || 8081))

  connection.onopen = ->
    console.log("Connected to watcher")

  connection.onerror = ->
    console.log("Unable to connect to watcher")

  connection.onmessage = (message) ->
    switch message.data
      when 'reload-css'
        reloadStylesheets()
      when 'reload-js'
        window.location.reload()

connect()

reloadStylesheets = ->
  queryString = '?reload=' + new Date().getTime()
  el.href = el.href.replace(/\?.*|$/, queryString) for el in document.querySelectorAll('link[rel="stylesheet"]')

connect = (opts={}) ->
  # if user is running mozilla then use it's built-in WebSocket
  WebSocket = window.WebSocket || window.MozWebSocket

  return unless WebSocket

  connection = new WebSocket('ws://' + (opts.host || document.domain || 'localhost') + ':'+(opts.port || 8081))

  connection.onopen = ->
    console.log("Connected to watcher")

  connection.onerror = ->
    console.log("Unable to connect to watcher")

  connection.onmessage = (message) ->
    switch message.data
      when 'reload-css'
        reloadStylesheets()
      when 'reload-js'
        window.location.reload()

connect()

Return silently if no WebSockets support


`// @ngInject`
angular.module('cobudget').config (RestangularProvider, config) ->
  RestangularProvider.setBaseUrl(config.apiEndpoint)
  RestangularProvider.setDefaultHttpFields
    withCredentials: true
  RestangularProvider.setDefaultHeaders
    Accept: "application/json"

  RestangularProvider.setResponseInterceptor (data, operation, what, url, response, deferred) ->
    if operation is "get"
      # Trim the 's' off so we can reference the singular root note name
      # This is a total hack and won't work for resources like "people"
      return response.data[what.substring(0, what.length-1)]
    if operation is "getList"
      return response.data[what]

  RestangularProvider.setRequestInterceptor (element, operation, what, url) ->
    if operation is "post"
      # Add singular root node to element
      newElem = {}
      newElem[what.substring(0, what.length-1)] = element
      return newElem
    if operation is "update"
      # Trim the 's' off so we can reference the singular root note name
      # This is a total hack and won't work for resources like "people"
      return response.data[what.substring(0, what.length-1)]

Revert "Revert "Add root node to POST contributions/""

This reverts commit cffb43d652f57fa64f7bfc630d214f33206ddae0.


ETahi.EventStreamActions = {
  created: (esData) ->
    Ember.run =>
      if esData.task
        phaseId = esData.task.phase_id
        taskId = esData.task.id
        @store.pushPayload('task', esData)
        task = @store.findTask(taskId)
        phase = @store.getById('phase', phaseId)
        phase.get('tasks').addObject(task)
        task.triggerLater('didLoad')
  updated: (esData)->
    Ember.run =>
      if esData.task
        phaseId = esData.task.phase_id
        taskId = esData.task.id
        # This is an ember bug.  A task's phase needs to be notified that the other side of
        # the hasMany relationship has changed via set.  Simply loading the updated task into the store
        # won't trigger the relationship update.
        @store.pushPayload('task', esData)
        task = @store.findTask(taskId)
        task.triggerLater('didLoad')
  destroy: (esData)->
    (esData.task_ids).forEach (taskId) =>
      task = @store.findTask(taskId)
      #phase = task.get('phase')
      #phase.get('tasks').removeObject(task)
      task.deleteRecord()
      task.triggerLater('didDelete')
}

ETahi.EventStreamActions = {
  created: (esData) ->
    Ember.run =>
      if esData.task
        phaseId = esData.task.phase_id
        taskId = esData.task.id
        @store.pushPayload('task', esData)
        task = @store.findTask(taskId)
        phase = @store.getById('phase', phaseId)
        phase.get('tasks').addObject(task)
        task.triggerLater('didLoad')
  updated: (esData)->
    Ember.run =>
      if esData.task
        taskId = esData.task.id
        # This is an ember bug.  A task's phase needs to be notified that the other side of
        # the hasMany relationship has changed via set.  Simply loading the updated task into the store
        # won't trigger the relationship update.
        @store.pushPayload('task', esData)
        task = @store.findTask(taskId)
        task.triggerLater('didLoad')
  destroy: (esData)->
    (esData.task_ids).forEach (taskId) =>
      task = @store.findTask(taskId)
      #phase = task.get('phase')
      #phase.get('tasks').removeObject(task)
      task.deleteRecord()
      task.triggerLater('didDelete')
}

Remove unused local var in event stream actions


require('./styles/index.scss')

require('leaflet/dist/leaflet.css')
require('leaflet')

findClosestTo = (origin, parsed) ->
  minDistance = 1000
  found = null
  for element in parsed.elements
    elemLatLng = new L.LatLng(element.lat, element.lon)
    if origin.distanceTo(elemLatLng) < minDistance
      found = elemLatLng
  found

document.addEventListener('DOMContentLoaded', ->
  map = L.map('map').setView([25.1701, 121.5948], 13)

  L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png',
    attribution: '© OpenStreetMap contributors'
  ).addTo(map)

  map.on('click', (ev) ->
    latlng = ev.latlng
    fetch("http://overpass-api.de/api/interpreter?data=[out:json];node(around:100,#{latlng.lat},#{latlng.lng});out;")
      .then((response) => response.text())
      .then((body) =>
        found = findClosestTo(latlng, JSON.parse(body))
        L.marker(found).addTo(map)
      )
  )
)

require('./styles/index.scss')

require('leaflet/dist/leaflet.css')
require('leaflet')

overpass_query = (latlng) ->
  lat = latlng.lat
  lng = latlng.lng
  """
  [out:json];
  way(around:100,#{lat},#{lng})->.ways;
  node(around:100,#{lat},#{lng})->.nodes;
  .ways >->.way_nodes;
  node.nodes.way_nodes;
  out;
  """

findClosestTo = (origin, parsed) ->
  minDistance = 1000
  found = null
  debugger
  for element in parsed.elements
    elemLatLng = new L.LatLng(element.lat, element.lon)
    if origin.distanceTo(elemLatLng) < minDistance
      found = elemLatLng
  found

document.addEventListener('DOMContentLoaded', ->
  map = L.map('map').setView([25.1701, 121.5948], 13)

  L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png',
    attribution: '© OpenStreetMap contributors'
  ).addTo(map)

  map.on('click', (ev) ->
    latlng = ev.latlng
    # TODO: handle no nodes returned
    fetch("http://overpass-api.de/api/interpreter?data=#{overpass_query(latlng)}")
      .then((response) => response.text())
      .then((body) =>
        found = findClosestTo(latlng, JSON.parse(body))
        L.marker(found).addTo(map)
      )
  )
)

Include only nodes being part of ways

Include only nodes being part of ways


angular.module 'module.media', [
    'restangular'
    'ui.router'
    'service.auth'
    'service.overlay'
    'module.common'
]
.config [
    '$stateProvider',
    ($stateProvider) ->
        $stateProvider
        .state 'media',
            url: '/m/:hash'
            templateUrl: 'modules/media/media.tpl.html'
            controller: 'mediaViewController'
            resolve:
                user: [
                    'authService',
                    (authService) ->
                        authService.userAsync()
                ]
                media: [
                    'Restangular', '$stateParams'
                    (Restangular, $stateParams) ->
                        Restangular.one 'media', $stateParams.hash
                        .get()
                ]
                character: [
                    'Restangular', 'media',
                    (Restangular, media) ->
                        if media.character?
                            Restangular.one 'characters', media.character
                            .one 'brief'
                            .get()
                        else
                            null
                ]
]
.controller 'mediaViewController', [
    '$scope', '$state', '$stateParams', 'user', 'media', 'character', 'authService',
    ($scope, $state, $stateParams, user, media, character, authService) ->
        $scope.media = media
        $scope.character = character
        $scope.type = if media.locator.indexOf('.jpg') > 0 then 'image' else 'movie'
        $scope.mediaDate = moment($scope.media.timestamp).format('lll')
        $scope.src = hubEnv.remoteMediaLocation + $scope.media.locator

        $scope.userCanEdit = user.ulevel >= authService.userLevels().editor
]

angular.module 'module.media', [
    'restangular'
    'ui.router'
    'service.auth'
    'service.overlay'
    'module.common'
]
.config [
    '$stateProvider',
    ($stateProvider) ->
        $stateProvider
        .state 'media',
            url: '/m/:hash'
            templateUrl: 'modules/media/media.tpl.html'
            controller: 'mediaViewController'
            resolve:
                user: [
                    'authService',
                    (authService) ->
                        authService.userAsync()
                ]
                media: [
                    'Restangular', '$stateParams'
                    (Restangular, $stateParams) ->
                        Restangular.one 'media', $stateParams.hash
                        .get()
                ]
                character: [
                    'Restangular', 'media',
                    (Restangular, media) ->
                        if media.character?
                            Restangular.one 'characters', media.character
                            .one 'brief'
                            .get()
                        else
                            null
                ]
]
.controller 'mediaViewController', [
    '$scope', '$state', '$stateParams', 'user', 'media', 'character', 'authService',
    ($scope, $state, $stateParams, user, media, character, authService) ->
        $scope.media = media
        $scope.character = character
        $scope.type = if media.locator.indexOf('.jpg') > 0 then 'image' else 'movie'
        $scope.mediaDate = moment($scope.media.timestamp).format('lll')
        $scope.src = hubEnv.remoteMediaLocation + $scope.media.locator

        $scope.userCanEdit = user?.ulevel >= authService.userLevels().editor
]

Fix broken media view when not logged in


kd = require 'kd'
globals = require 'globals'
isLoggedIn = require './isLoggedIn'

trackEligible = ->

  return analytics? and globals.config.logToExternal

# Access control wrapper around segmentio object.
module.exports = exports = (args...) ->

  return  unless trackEligible()

  # send event#action as event for GA
  if args.length > 1
    {action} = args[1]
    args[1].event = args[0]  unless args[1].event

  # if event#action, send that or fallback to event
  event = if action? then action else args[0]
  analytics.track event, args[1]

exports.alias = (args...) ->

  return  unless trackEligible()
  analytics.alias args...

Remove obsolete module require statement


settings =
  # # set/comment in to use actual BE
  # baseUrl: 'http://localhost:3001'

  endpoints:
    'exercise.*.send.save':
      url: 'api/steps/{id}'
      method: 'PATCH'
      completedEvent: 'exercise.{id}.receive.save'

    'exercise.*.send.complete':
      url: 'api/steps/{id}/completed'
      method: 'PUT'
      completedEvent: 'exercise.{id}.receive.complete'

    'exercise.*.send.fetch':
      url: 'api/steps/{id}'
      method: 'GET'
      completedEvent: 'exercise.{id}.receive.fetch'

    'task.*.send.fetch':
      url: 'api/tasks/{id}'
      method: 'GET'
      completedEvent: 'task.{id}.receive.fetch'

    'task.*.send.fetchByModule':
      url: 'api/cc/tasks/{collectionUUID}/{moduleUUID}'
      method: 'GET'
      completedEvent: 'task.{collectionUUID}/{moduleUUID}.receive.fetchByModule'

module.exports = settings

settings =
  # to customize, set environmental var BASE_URL when building or running webpack-dev-server
  # Currently is set on an endpoint by endpoint basis until all are implemented by BE
  # baseUrl: process?.env?.BASE_URL

  endpoints:
    'exercise.*.send.save':
      url: 'api/steps/{id}'
      method: 'PATCH'
      completedEvent: 'exercise.{id}.receive.save'

    'exercise.*.send.complete':
      url: 'api/steps/{id}/completed'
      method: 'PUT'
      completedEvent: 'exercise.{id}.receive.complete'

    'exercise.*.send.fetch':
      url: 'api/steps/{id}'
      method: 'GET'
      completedEvent: 'exercise.{id}.receive.fetch'

    'task.*.send.fetch':
      url: 'api/tasks/{id}'
      method: 'GET'
      completedEvent: 'task.{id}.receive.fetch'

    'task.*.send.fetchByModule':
      url: 'api/cc/tasks/{collectionUUID}/{moduleUUID}'
      method: 'GET'
      baseUrl: process?.env?.BASE_URL
      completedEvent: 'task.{collectionUUID}/{moduleUUID}.receive.fetchByModule'

module.exports = settings

Document baseURL and set it for tasks

Document baseURL and set it for tasks


async = require 'async'

{Boutique} = require './lib/boutique'
{serializers} = require './lib/serializers'
{selectFormat} = require './lib/formatselection'

formats =
  'application/json':
    lib: require './lib/formats/json'
    serialize: serializers.json

represent = (ast, contentType, options, cb) ->
  key = selectFormat contentType, Object.keys formats

  if key
    {lib, serialize} = formats[key]

    async.waterfall [
        (next) ->
          format = new lib.Format options
          boutique = new Boutique format
          boutique.represent ast, impl, next
      ,
        (obj, next) ->
          serialize obj, next

    ], cb
  else
    cb new Error "Content-Type '#{contentType}' is not implemented."

module.exports = {
  represent
}

async = require 'async'

{Boutique} = require './lib/boutique'
{serializers} = require './lib/serializers'
{selectFormat} = require './lib/formatselection'

formats =
  'application/json':
    lib: require './lib/formats/json'
    serialize: serializers.json

represent = (ast, contentType, options, cb) ->
  if typeof options is 'function' then cb = options
  key = selectFormat contentType, Object.keys formats

  if key
    {lib, serialize} = formats[key]

    async.waterfall [
        (next) ->
          format = new lib.Format options
          boutique = new Boutique format
          boutique.represent ast, impl, next
      ,
        (obj, next) ->
          serialize obj, next

    ], cb
  else
    cb new Error "Content-Type '#{contentType}' is not implemented."

module.exports = {
  represent
}

Support for optional number of arguments.


Cloudsdale.MessageController = Ember.Controller.extend

  content: null

  text: ( () ->
    text = @content.get('content') || ""
    text = text.replace(/\s/g, ' ')
    text = text.replace(/(\\r\\n|\\n|\\r)/g, '<br/>')
    return text
  ).property('content.content')

  avatar: ( () ->
    @content.get('author').get('avatar') if @content.get('author')
  ).property('content.author.avatar')

  name: ( () ->
    @content.get('author').get('displayName') if @content.get('author')
  ).property('content.author.displayName')

  handle: ( () ->
    @content.get('author').get('username') if @content.get('author')
  ).property('content.author.username')

  createdAt: ( () ->
    console.log @content.get('createdAt')
    @content.get('createdAt')
  ).property('content.createdAt')

  updatedAt: ( () ->
    @content.get('updatedAt')
  ).property('content.updatedAt')

  timestamp: ( () ->
    @content.get('timestamp')
  ).property('content.timestamp')

Cloudsdale.MessageController = Ember.Controller.extend

  content: null

  text: ( () ->
    text = @content.get('content') || ""
    text = text.replace(/\s(?=\s)/g, ' ')
    text = text.replace(/(\\r\\n|\\n|\\r)/g, '<br/>')
    return text
  ).property('content.content')

  avatar: ( () ->
    @content.get('author').get('avatar') if @content.get('author')
  ).property('content.author.avatar')

  name: ( () ->
    @content.get('author').get('displayName') if @content.get('author')
  ).property('content.author.displayName')

  handle: ( () ->
    @content.get('author').get('username') if @content.get('author')
  ).property('content.author.username')

  createdAt: ( () ->
    console.log @content.get('createdAt')
    @content.get('createdAt')
  ).property('content.createdAt')

  updatedAt: ( () ->
    @content.get('updatedAt')
  ).property('content.updatedAt')

  timestamp: ( () ->
    @content.get('timestamp')
  ).property('content.timestamp')

Use regex lookahead to sort out the issue with breaking words.


###
  Toggles the activation state of a survey based on the 'active' ReactiveVar
  on the instance
  @param [Object] instance, Blaze instance
###
activate = (instance) ->
  activeState = instance.active
  activeState.set not activeState.get()
  props =
    active: activeState.get()
  instance.data.survey.save(props)
    .then (survey) ->
      instance.activating.set true
      survey.setUserACL(activeState.get())
    .then ->
      instance.activating.set false
      instance.data.surveyState.set activeState.get()
      state = (if activeState.get() then "activated" else "deactivated")
      toastr.success("You have " + state + " your survey.")
    .fail (error) ->
      toastr.error error.message

module.exports = activate

###
  Toggles the activation state of a survey based on the 'active' ReactiveVar
  on the instance
  @param [Object] instance, Blaze instance
###
activate = (instance) ->
  activeState = instance.active
  activeState.set not activeState.get()
  props =
    active: activeState.get()
  instance.activating.set true
  instance.data.survey.save(props)
    .then (survey) ->
      instance.activating.set false
      instance.data.surveyState.set activeState.get()
      state = (if activeState.get() then "activated" else "deactivated")
      toastr.success("You have " + state + " your survey.")
    .fail (error) ->
      toastr.error error.message

module.exports = activate

Remove call to setACL method when survey is activated


Improve Table of Contents nested list handling


# # Kissmetrics Batch

class BatchKissmetricsClient
  @HOST: 'api.kissmetrics.com'

  constructor: (@queue) ->
    @_validate_queue @queue

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

    @queue.add data

  get: ->
    @queue.get()

  process: ->
    queue = @get()

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

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

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

  _validate_queue: ->
    for method in ['add', 'get']
      unless typeof @queue[method] is 'function'
        throw new Error "Missing method: #{required_method}"

module.exports = BatchKissmetricsClient

# # Kissmetrics Batch

class BatchKissmetricsClient
  @HOST: 'api.kissmetrics.com'

  constructor: (@queue) ->
    @_validate_queue @queue

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

    @queue.add data

  get: ->
    @queue.get()

  process: ->
    queue = @get()

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

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

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

    delete data.type

  _validate_queue: ->
    for method in ['add', 'get']
      unless typeof @queue[method] is 'function'
        throw new Error "Missing method: #{required_method}"

module.exports = BatchKissmetricsClient

Delete data.type once it's used to transform batch data


packages: [
  "atom-beautify"
  "atomic-chrome"
  "close-tags"
  "date"
  "file-icons"
  "git-history"
  "git-plus"
  "git-time-machine"
  "language-eco"
  "language-elixir"
  "language-haml"
  "language-rspec"
  "linter"
  "linter-write-good"
  "merge-conflicts"
  "package-sync"
  "rails-snippets"
  "rails-transporter"
  "ruby-test"
  "split-diff"
]

packages: [
  "atom-beautify"
  "atomic-chrome"
  "close-tags"
  "date"
  "file-icons"
  "git-history"
  "git-plus"
  "git-time-machine"
  "language-eco"
  "language-elixir"
  "language-haml"
  "language-rspec"
  "linter"
  "linter-write-good"
  "merge-conflicts"
  "package-sync"
  "pane-layout-plus"
  "rails-snippets"
  "rails-transporter"
  "ruby-test"
  "split-diff"
]

Add back Atom `pane-layout-plus` package

To get keyboard navigation among panes with CTRL + <number>


class window.UserStatisticsView extends Backbone.Marionette.ItemView
  className: 'statistics'
  template: 'users/statistics'

  initialize: ->
    @listenTo @model, 'change', @render

Update statistics when model changes

Update statistics when model changes


Template.reactiveTable.onRendered ->
  if @data.settings.keyboardFocus
    attrs = tabindex: '0'
    @$('tbody > tr').attr(attrs)

Template.reactiveTable.events
  'click tbody > tr': (event, instance) ->
    instance.$(event.currentTarget).blur()

Template.reactiveTable.onRendered ->
  if @data.settings.keyboardFocus
    @autorun =>
      if @context.ready.get()
        Meteor.defer =>
          @$('tbody > tr').attr(tabindex: '0')

Template.reactiveTable.events
  'click tbody > tr': (event, instance) ->
    instance.$(event.currentTarget).blur()

Add tabindex attr to table rows after table is loaded


###
# Invite is a named function that will replace /invite commands
# @param {Object} message - The message object
###

class Invite
	constructor: (command, params, item) ->
		if command isnt 'invite' or not Match.test params, String
			return

		username = params.trim()
		if username is ''
			return

		username = username.replace('@', '')

		user = Meteor.users.findOne({ username: username })
		currentUser = Meteor.users.findOne Meteor.userId()

		if not user?
			console.log 'notify user_doesnt_exist'
			RocketChat.Notifications.notifyUser Meteor.userId(), 'message', {
				_id: Random.id()
				rid: item.rid
				ts: new Date
				msg: TAPi18n.__('User_doesnt_exist', { postProcess: 'sprintf', sprintf: [ username ] }, currentUser.language)
			}
			return

		# cancel if the user is already in this room
		if RocketChat.models.Rooms.findOneByIdContainigUsername(item.rid, user.username)?
			RocketChat.Notifications.notifyUser Meteor.userId(), 'message', {
				_id: Random.id()
				rid: item.rid
				ts: new Date
				msg: TAPi18n.__('Username_is_already_in_here', { postProcess: 'sprintf', sprintf: [ username ] }, currentUser.language)
			}
			return

		Meteor.runAsUser user._id, ->
			Meteor.call 'joinRoom', item.rid

RocketChat.slashCommands.add 'invite', Invite

###
# Invite is a named function that will replace /invite commands
# @param {Object} message - The message object
###

class Invite
	constructor: (command, params, item) ->
		if command isnt 'invite' or not Match.test params, String
			return

		username = params.trim()
		if username is ''
			return

		username = username.replace('@', '')

		user = Meteor.users.findOne({ username: username })
		currentUser = Meteor.users.findOne Meteor.userId()

		if not user?
			console.log 'notify user_doesnt_exist'
			RocketChat.Notifications.notifyUser Meteor.userId(), 'message', {
				_id: Random.id()
				rid: item.rid
				ts: new Date
				msg: TAPi18n.__('User_doesnt_exist', { postProcess: 'sprintf', sprintf: [ username ] }, currentUser.language)
			}
			return

		# cancel if the user is already in this room
		if RocketChat.models.Rooms.findOneByIdContainigUsername(item.rid, user.username)?
			RocketChat.Notifications.notifyUser Meteor.userId(), 'message', {
				_id: Random.id()
				rid: item.rid
				ts: new Date
				msg: TAPi18n.__('Username_is_already_in_here', { postProcess: 'sprintf', sprintf: [ username ] }, currentUser.language)
			}
			return

		Meteor.call 'addUserToRoom',
			rid: item.rid
			username: user.username

RocketChat.slashCommands.add 'invite', Invite

Change /invite to use addUserToRoom instead joinRoom


_       = require 'lodash'
request = require 'request'
deepPluck = require 'deep-pluck'

isError = (errorCode) ->
  if errorCode? and errorCode.toString() isnt '0' then true else false

createErrorMsg = (body, codeKey, msgKey) ->
  [code, msg] = [deepPluck(body, codeKey)[0], deepPluck(body, msgKey)[0]]
  if isError(code) then "#{code} - #{msg}" else null

parseResponse = (body) ->
  body = JSON.parse body
  if body.StatusCode?
    [err, data] = [createErrorMsg(body, 'StatusCode', 'Message'), body.ResponseData]
  else
    [err, data] = [createErrorMsg(body, 'errorCode', 'errorText'), body]
  [err, data]

module.exports = (url, raw, deferred) ->
  request.get { url }, (err, response, body) ->
    unless err or raw
      [err, body] = parseResponse body
    if err then deferred.reject err
    else deferred.resolve body

_         = require 'lodash'
request   = require 'request'
keyfinder = require 'keyfinder'

isError = (errorCode) ->
  if errorCode? and errorCode.toString() isnt '0' then true else false

createErrorMsg = (body, codeKey, msgKey) ->
  [code, msg] = [keyfinder(body, codeKey)[0], keyfinder(body, msgKey)[0]]
  if isError(code) then "#{code} - #{msg}" else null

parseResponse = (body) ->
  body = JSON.parse body
  if body.StatusCode?
    [err, data] = [createErrorMsg(body, 'StatusCode', 'Message'), body.ResponseData]
  else
    [err, data] = [createErrorMsg(body, 'errorCode', 'errorText'), body]
  [err, data]

module.exports = (url, raw, deferred) ->
  request.get { url }, (err, response, body) ->
    unless err or raw
      [err, body] = parseResponse body
    if err then deferred.reject err
    else deferred.resolve body

Replace deep-pluck with keyfinder module


Spine = require 'spine'

class Toolbox extends Spine.Controller

  events:
    'click .tool' : 'selection'
    'click .remove-tools': 'clearDashboard'

  constructor: ->
    super

  render: =>
    @html require('views/toolbox')(@) if @el.html

  clearDashboard: (e) =>
    @trigger 'remove-all-tools'

  selection: (e) =>
    e.preventDefault()
    selected = $(e.currentTarget).attr('data-id')
    @trigger 'add-new-tool', selected

module.exports = Toolbox

Add spine require to toolbox to make it pass tests on linux


angular.module("grumbles")
  .controller "FormCtrl", ($http, Output) ->
    @submit = ->
      return unless @command

      split_command = @command.split " "

      verb         = split_command[0]
      entity       = split_command[1]
      other_entity = split_command[2]

      path  = "/#{verb}/#{entity}"
      path += "/#{other_entity}" if other_entity?

      $http.post(path)
        .success (data, status, headers, config) ->
          Output.add data

        .error (data, status, headers, config) ->
          console.error "Everything sucks ...", data

    this

angular.module("grumbles")
  .controller "FormCtrl", ($http, Output) ->
    @submit = ->
      return unless @command

      split_command = @command.split " "

      verb         = split_command[0]
      entity       = split_command[1]
      other_entity = split_command[2]

      path  = "/#{verb}/#{entity}"
      path += "/#{other_entity}" if other_entity?

      $http.post(path)
        .success (data, status, headers, config) =>
          Output.add data
          @command = ""

        .error (data, status, headers, config) ->
          console.error "Everything sucks ...", data

    this

Reset command input on successful commands


$ ->
  window.initPickers = ->
    base_options =
      format: "yyyy-mm-dd hh:ii"
      autoclose: true
      todayBtn: true
      viewSelect: 'month'
      minView: 'day'
      language: I18n.locale

    search_form_options = _.defaults({pickerPosition: "bottom-left datetimepicker-bottom-left-custom"}, base_options)
    search_form_options_gt = _.defaults({initialDate: new Date(new Date().setHours(0,0,0,0))}, search_form_options)
    search_form_options_lt = _.defaults({initialDate: new Date(new Date().setHours(23,59,59,999))}, search_form_options)
    $('#search_form input.datepicker[name*="_at_gteq]"]').datetimepicker search_form_options_gt
    $('#search_form input.datepicker[name*="_at_lteq]"]').datetimepicker search_form_options_lt

    date_picker_options = _.defaults({format: "yyyy-mm-dd", minView: 2}, base_options)
    $('.simple_form input.date_picker').datetimepicker date_picker_options

    $('.simple_form input.datetime_picker').datetimepicker base_options

    time_picker_options = _.defaults({format: "hh:ii"}, base_options)
    $('.simple_form input.time_picker').datetimepicker time_picker_options

  initPickers()

$ ->
  window.initPickers = ->
    base_options =
      format: "yyyy-mm-dd hh:ii"
      autoclose: true
      todayBtn: true
      viewSelect: 'month'
      minView: 'day'
      language: I18n.locale
      weekStart: 1

    search_form_options = _.defaults({pickerPosition: "bottom-left datetimepicker-bottom-left-custom"}, base_options)
    search_form_options_gt = _.defaults({initialDate: new Date(new Date().setHours(0,0,0,0))}, search_form_options)
    search_form_options_lt = _.defaults({initialDate: new Date(new Date().setHours(23,59,59,999))}, search_form_options)
    $('#search_form input.datepicker[name*="_at_gteq]"]').datetimepicker search_form_options_gt
    $('#search_form input.datepicker[name*="_at_lteq]"]').datetimepicker search_form_options_lt

    date_picker_options = _.defaults({format: "yyyy-mm-dd", minView: 2}, base_options)
    $('.simple_form input.date_picker').datetimepicker date_picker_options

    $('.simple_form input.datetime_picker').datetimepicker base_options

    time_picker_options = _.defaults({format: "hh:ii"}, base_options)
    $('.simple_form input.time_picker').datetimepicker time_picker_options

  initPickers()

Set datepicker start weekday to Monday

Set datepicker start weekday to Monday


scrollbarWidth = 0
FactlinkJailRoot.loaded_promise.then ->
  # Create the measurement nod; see http://davidwalsh.name/detect-scrollbar-width
  scrollDiv = document.createElement("div");
  $(scrollDiv).css(
    width: "100px"
    height: "100px"
    overflow: "scroll"
    position: "absolute"
    top: "-9999px"
  )
  document.body.appendChild(scrollDiv)

  scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth

  document.body.removeChild(scrollDiv)
  console.log 'scrollbar width: ', scrollbarWidth

FactlinkJailRoot.on 'modalOpened', ->
  document.documentElement.setAttribute('data-factlink-suppress-scrolling', '')
FactlinkJailRoot.on 'modalClosed', -> document.documentElement.removeAttribute('data-factlink-suppress-scrolling')

Implement a scrollbar width detector

Implement a scrollbar width detector


goog.provide 'app.react.pages.Home'

class app.react.pages.Home

  ###*
    @param {app.Routes} routes
    @param {app.songs.Store} songsStore
    @param {app.react.Touch} touch
    @constructor
  ###
  constructor: (routes, songsStore, touch) ->
    {div,ul,li} = React.DOM
    {a} = touch.scroll 'a'

    @create = React.createClass

      render: ->
        div className: 'home',
          ul null, songsStore.all().map (song, i) ->
            li key: i,
              a href: routes.mySong.createUrl(song),
                "#{song.name} [#{song.artist}]"
          a
            className: 'btn btn-default'
            href: routes.myNewSong.createUrl()
          , Home.MSG_ADD_NEW_SONG

  @MSG_ADD_NEW_SONG: goog.getMsg 'add new song'

goog.provide 'app.react.pages.Home'

class app.react.pages.Home

  ###*
    @param {app.Routes} routes
    @param {app.songs.Store} songsStore
    @param {app.react.Touch} touch
    @constructor
  ###
  constructor: (routes, songsStore, touch) ->
    {div,ul,li,br} = React.DOM
    {a,button} = touch.scroll 'a', 'button'

    @create = React.createClass

      render: ->
        div className: 'home',
          ul null, songsStore.all().map (song, i) ->
            li key: i,
              a href: routes.mySong.createUrl(song),
                "#{song.name} [#{song.artist}]"
          a
            className: 'btn btn-default'
            href: routes.myNewSong.createUrl()
          , Home.MSG_ADD_NEW_SONG
          button
            className: 'btn btn-default'
            href: routes.myNewSong.createUrl()
            onPointerUp: @onResetPointerUp
          , Home.MSG_RESET

      onResetPointerUp: (e) ->
        reset = confirm 'Do you really want reset everything?'
        return if !reset
        window.localStorage.removeItem 'songary::songs'
        window.location.reload true

  @MSG_ADD_NEW_SONG: goog.getMsg 'add new song'
  @MSG_RESET: goog.getMsg 'reset'

Add dev reset storage button.

Add dev reset storage button.


[
	{
		title: ".atom"
		icon: "atom-icon"
		paths: [
			"~/.atom"
		]
		devMode: true
	}
	{
		title: ".files"
		icon: "terminal-icon"
		paths: [
			"~/.files"
		]
		devMode: true
	}
	{
		title: "language-roff"
		icon: "manpage-icon"
		paths: [
			"~/Labs/language-roff"
		]
		devMode: true
	}
	{
		title: "file-icons"
		icon: "atom-icon"
		paths: [
			"~/Labs/file-icons"
		]
		devMode: true
	}
	{
		title: "Utils"
		icon: "icon-tools"
		paths: [
			"~/Labs/Utils"
		]
		devMode: true
	}
	{
		title: "YASR"
		icon: "emacs-icon"
		paths: [
			"~/Labs/YASR"
		]
		devMode: true
	}
]

[
	{
		title: ".atom"
		icon: "atom-icon"
		paths: [
			"~/.atom"
		]
		devMode: true
	}
	{
		title: ".files"
		icon: "terminal-icon"
		paths: [
			"~/.files"
		]
		devMode: true
	}
	{
		title: "language-roff"
		icon: "manpage-icon"
		paths: [
			"~/Labs/language-roff"
		]
		devMode: true
	}
	{
		title: "file-icons"
		icon: "atom-icon"
		paths: [
			"~/Labs/file-icons/atom"
			"~/Labs/file-icons/source"
		]
		devMode: true
	}
	{
		title: "Utils"
		icon: "icon-tools"
		paths: [
			"~/Labs/Utils"
		]
		devMode: true
	}
	{
		title: "YASR"
		icon: "emacs-icon"
		paths: [
			"~/Labs/YASR"
		]
		devMode: true
	}
]

Update project paths for File-Icons

Update project paths for File-Icons


#
# main.coffee
#
# Role: start the app
#
# Responsibility:
# * load top-level modules
# * initalize game based on query string
# * render and append to body of HTML
# * run tests if local
#

# Preamble
# TODO: server-side include underscore, underscore.string, jquery

{my} = require './my'
sys = require 'sys'

if not my.online
  rx = require './render/rx_mock'
else
  _.mixin(_.str.exports())
  if my.reactive_debug
    rx = require '../../reactive-coffee/src/reactive'
    console.log "WARNING: Loading local copy of reactive-coffee"
  else
    rx = require 'reactive-coffee'

# Dependencies

{game} = require './game'
{render} = require './render'

world = game(rx, {file: 'example', level: 0})

main = ->
  $('body').append(
    render(world) if my.online
  )

# Instantiate our main view
$(main)
# Run Tests
{test} = require('./test') if my.test
test(rx) if my.test

#
# main.coffee
#
# Role: start the app
#
# Responsibility:
# * load top-level modules
# * initalize game based on query string
# * render and append to body of HTML
# * run tests if local
#

# Preamble
# TODO: server-side include underscore, underscore.string, jquery

{my} = require './my'
sys = require 'sys'
queryString = require 'query-string'

if not my.online
  rx = require './render/rx_mock'
else
  _.mixin(_.str.exports())
  if my.reactive_debug
    rx = require '../../reactive-coffee/src/reactive'
    console.log "WARNING: Loading local copy of reactive-coffee"
  else
    rx = require 'reactive-coffee'

# Dependencies

{game} = require './game'
{render} = require './render'

default_query = {file: 'example', level: 0}
parsed_query = queryString.parse(location.search)

world = game rx, my.extend(default_query, parsed_query)

main = ->
  $('body').append(
    render(world) if my.online
  )

# Instantiate our main view
$(main)
# Run Tests
{test} = require('./test') if my.test
test(rx) if my.test

Use query string to select game

Use query string to select game


"use strict"

os = require 'os'

exports.info = (req, res, next) ->
  return res.status(403).end() if req.usr not in ['DNT', 'Pingdom']

  res.json 200,
    app:
      uptime: process.uptime()
      memory: process.memoryUsage()
    os:
      uptime: os.uptime()
      loadavg: os.loadavg()
      totalmem: os.totalmem()
      freemem: os.freemem()

exports.gc = (req, res, next) ->
  return res.status(403).end() if req.usr isnt 'DNT'

  global.gc() if typeof global.gc is 'function'
  res.end()

"use strict"

os = require 'os'

exports.info = (req, res, next) ->
  return res.status(403).end() if req.usr isnt 'DNT'

  res.json 200,
    app:
      uptime: process.uptime()
      memory: process.memoryUsage()
    os:
      uptime: os.uptime()
      loadavg: os.loadavg()
      totalmem: os.totalmem()
      freemem: os.freemem()

exports.gc = (req, res, next) ->
  return res.status(403).end() if req.usr isnt 'DNT'

  global.gc() if typeof global.gc is 'function'
  res.end()

Remove access for pingdom for normal sys info call


"*":
  editor:
    showIndentGuide: true
    fontFamily: "Inconsolata"
    invisibles: {}
    scrollPastEnd: true
    softWrap: true
    softWrapAtPreferredLineLength: true
    preferredLineLength: 100
  core:
    ignoredNames: [
      ".bundle"
      ".git"
      "log"
      "repositories"
      "tmp"
      "vendor"
    ]
    hideGitIgnoredFiles: true
    disabledPackages: [
      "background-tips"
      "exception-reporting"
      "metrics"
      "latexer"
    ]
  "spell-check":
    grammars: [
      "text.plain"
      "source.gfm"
      "text.git-commit"
    ]
  welcome:
    showOnStartup: false
  "release-notes":
    viewedVersion: "0.89.0"
  "autocomplete-bibtex":
    bibtex: [
      "/Users/briandanielak/Documents/bibtex/2015-10-21-zotero-library/2015-10-21-zotero-library.bib"
    ]
  "autocomplete-plus": {}
  Zen:
    softWrap: true
    typewriter: true
    minimap: true
    width: 140
  "markdown-table-formatter": {}
  latex: {}
  "make-runner": {}

Add make-runner package to Atom

Add make-runner package to Atom


Base = require './base.coffee'
Backbone = require 'backbone'
Backbone.LocalStorage = require 'backbone.localstorage'
Block = require '../models/block.coffee'

module.exports = class RecentConnections extends Backbone.Collection
  model: Block

  localStorage: new Backbone.LocalStorage RecentConnections

  shove: (model) ->
    @create(model.toJSON())

  unshove: (model) ->
    @sync('delete', @get model.id)

Base = require './base.coffee'
Backbone = require 'backbone'
Backbone.LocalStorage = require 'backbone.localstorage'
Block = require '../models/block.coffee'

module.exports = class RecentConnections extends Backbone.Collection
  model: Block

  localStorage: new Backbone.LocalStorage RecentConnections

  shove: (model) ->
    @create(model.toJSON())
    @unshift @pop()

  unshove: (model) ->
    @sync('delete', @get model.id)

Move created record to the top of the stack


define ['backbone', 'utils', 'backboneAnalytics'], (Backbone, Utils) ->
  Backbone.Router.extend

    initialize: () ->
      @route '', 'homeView'
      @route /([^\/]+)\/([^\/]+)\/([^\/]+)\/([^\/]+)\/([^\/]+)(?:\/([^\/]+))?(?:\/([^\/]+))?\/?/, 'routesView'

    homeView: () =>
      Reitti.Event.trigger 'home'

    routesView: (from, to, departArrive, datetime, transportTypes, routeIndex, legIndex) =>
      routeIndex = 0 if !routeIndex or routeIndex is ''
      Reitti.Event.trigger 'routes:find',
        from: Utils.decodeURIComponent(from)
        to: Utils.decodeURIComponent(to)
        date: if datetime is 'now' then 'now' else Utils.parseDateTimeFromMachines(datetime)
        arrivalOrDeparture: departArrive
        transportTypes: transportTypes.split(',')
        routeIndex: parseInt(routeIndex, 10)
        legIndex: if legIndex? then parseInt(legIndex, 10) else undefined

    navigateToRoutes: (params) ->
      path = [Utils.encodeURIComponent(params.from),
              Utils.encodeURIComponent(params.to),
              params.arrivalOrDeparture,
              if params.date is 'now' then 'now' else Utils.formatDateTimeForMachines(params.date),
              params.transportTypes.join(',')]
      if params.routeIndex?
        path.push params.routeIndex
        if params.legIndex? then path.push params.legIndex

      @navigate '/' + path.join('/'), trigger: true

define ['backbone', 'utils', 'backboneAnalytics'], (Backbone, Utils) ->
  Backbone.Router.extend

    initialize: () ->
      @route '', 'homeView'
      @route /([^\/]+)\/([^\/]+)\/([^\/]+)\/([^\/]+)\/([^\/]+)(?:\/([^\/]+))?(?:\/([^\/]+))?\/?/, 'routesView'

    homeView: () =>
      Reitti.Event.trigger 'home'

    routesView: (from, to, departArrive, datetime, transportTypes, routeIndex, legIndex) =>
      routeIndex = 0 if !routeIndex or routeIndex is ''
      Reitti.Event.trigger 'routes:find',
        from: Utils.decodeURIComponent(from)
        to: Utils.decodeURIComponent(to)
        date: if datetime is 'now' then 'now' else Utils.parseDateTimeFromMachines(datetime)
        arrivalOrDeparture: departArrive
        transportTypes: transportTypes.split(',')
        routeIndex: parseInt(routeIndex, 10)
        legIndex: if legIndex? and legIndex.length then parseInt(legIndex, 10) else undefined

    navigateToRoutes: (params) ->
      path = [Utils.encodeURIComponent(params.from),
              Utils.encodeURIComponent(params.to),
              params.arrivalOrDeparture,
              if params.date is 'now' then 'now' else Utils.formatDateTimeForMachines(params.date),
              params.transportTypes.join(',')]
      if params.routeIndex?
        path.push params.routeIndex
        if params.legIndex? then path.push params.legIndex

      @navigate '/' + path.join('/'), trigger: true

Check that legIndex isn't empty.

Check that legIndex isn't empty.


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

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

    coffeelint:
      options:
        max_line_length:
          level: 'ignore'

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

    shell:
      rebuild:
        command: 'npm build .'
        options:
          stdout: true
          stderr: true
          failOnError: true

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

  grunt.loadNpmTasks('grunt-contrib-coffee')
  grunt.loadNpmTasks('grunt-shell')
  grunt.loadNpmTasks('grunt-coffeelint')
  grunt.registerTask 'clean', ->
    rm = require('rimraf').sync
    rm('lib')
    rm('build')
  grunt.registerTask('lint', ['coffeelint'])
  grunt.registerTask('default', ['lint', 'coffee', 'shell:rebuild'])
  grunt.registerTask('test', ['default', 'shell:test'])
  grunt.registerTask('prepublish', ['clean', 'coffee', 'lint', 'shell:rebuild'])

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

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

    coffeelint:
      options:
        max_line_length:
          level: 'ignore'

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

    shell:
      rebuild:
        command: 'npm build .'
        options:
          stdout: true
          stderr: true
          failOnError: true

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

  grunt.loadNpmTasks('grunt-contrib-coffee')
  grunt.loadNpmTasks('grunt-shell')
  grunt.loadNpmTasks('grunt-coffeelint')
  grunt.registerTask 'clean', ->
    rm = require('rimraf').sync
    rm('lib')
    rm('build')
  grunt.registerTask('lint', ['coffeelint'])
  grunt.registerTask('default', ['lint', 'coffee', 'shell:rebuild'])
  grunt.registerTask('test', ['default', 'shell:test'])
  grunt.registerTask('prepublish', ['clean', 'coffee', 'lint', 'shell:rebuild'])

Tweak test command for Windows

Tweak test command for Windows


# Hay que bindear tanto cuando se carga la página como cuando turbolinks la
# pide (el page:change)
bindearTodo = ->
  # mejora el estilo default de los file uploaders
  $("form.carta :file").filestyle(
    buttonText: 'Subir',
    classText: 'span9 filestyle',
    classButton: 'span3 filestyle'
  )

$(document)
  .on 'page:change', ->
    bindearTodo()

# TODO bindear 36 = inicio y 35 = fin para primera y última carta
# TODO testear que estén los tags
$(document)
  .on 'keydown', (tecla) ->
    Turbolinks.visit((
      switch tecla.which
        when 37 then $('#anterior')
        when 39 then $('#siguiente')
      ).attr('href'))

jQuery ->
  bindearTodo()

# Hay que bindear tanto cuando se carga la página como cuando turbolinks la
# pide (el page:change)
bindearTodo = ->
  # mejora el estilo default de los file uploaders
  $("form.carta :file").filestyle(
    buttonText: 'Subir',
    classText: 'span9 filestyle',
    classButton: 'span3 filestyle'
  )

$(document)
  .on 'page:change', ->
    bindearTodo()

# TODO bindear 36 = inicio y 35 = fin para primera y última carta
# TODO testear que estén los tags
$(document)
  .on 'keydown', (tecla) ->
    Turbolinks.visit((
      unless tecla.altKey
        switch tecla.which
          when 37 then $('#anterior')
          when 39 then $('#siguiente')
        ).attr('href'))

jQuery ->
  bindearTodo()

Fix conflicto con alt + cursor

Fix conflicto con alt + cursor


# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/

roundUpBy = (value, round_to) ->
  return round_to * Math.ceil(value / round_to)

$ ->
  animateMetric = $(".metric-box strong").each (index) ->
    $this = $(this)
    starting_point = 0
    $target_count = parseInt($this.text().replace(/\,/g, ''))

    # set the test to 0
    $this.counter = starting_point

    tween = createjs.Tween.get($this).to( {counter: $target_count}, (1200 + index * 1000) , createjs.Ease.quintOut)

    tween.addEventListener 'change', (event) ->
      # if the number is under 90 of the target, increment number by thousands
      if $this.counter < $target_count * 0.7
        $this.counter = roundUpBy($this.counter, 1000)
      else if $this.counter < $target_count * 0.8
        $this.counter = roundUpBy($this.counter, 100)
      else if $this.counter < $target_count * 0.99
        $this.counter = roundUpBy($this.counter, 10)
      $this.text(Math.round($this.counter).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","))
      return

# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/

roundUpBy = (value, round_to) ->
  return round_to * Math.ceil(value / round_to)

$ ->
  animateMetric = $(".metric-box strong").each (index) ->
    $this = $(this)
    starting_point = 0
    $target_count = parseInt($this.text().replace(/\,/g, ''))

    # set the test to 0
    $this.counter = starting_point

    tween = createjs.Tween.get($this).to( {counter: $target_count}, (1200 + index * 800) , createjs.Ease.quintOut)

    tween.addEventListener 'change', (event) ->
      # if the number is under 90 of the target, increment number by thousands
      if $this.counter < $target_count * 0.7
        $this.counter = roundUpBy($this.counter, 1000)
      else if $this.counter < $target_count * 0.8
        $this.counter = roundUpBy($this.counter, 100)
      else if $this.counter < $target_count * 0.99
        $this.counter = roundUpBy($this.counter, 10)
      $this.text(Math.round($this.counter).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","))
      return

Make the stepped animation finish closer together


fs = require 'fs'
path = require 'path'
yeoman = require 'yeoman-generator'
semver = require 'semver'

module.exports = class SlideGenerator extends yeoman.generators.NamedBase

    files: ->
        appPath = process.cwd()
        fullPath = path.join appPath, '/slides/list.json'
        list = require fullPath

        if @options.markdown
            @filename = "#{@_.slugify(@name)}.md"
            @log.info 'Using Markdown.'

            if @options.notes
                @template 'slide-withnotes.md', "slides/#{@filename}"
            else
                @template 'slide.md', "slides/#{@filename}"

        else
            @filename = "#{@_.slugify(@name)}.html"
            @log.info 'Using HTML.'

            if @options.notes
                @template 'slide-withnotes.html', "slides/#{@filename}"
            else
                @template 'slide.html', "slides/#{@filename}"

        if @options.markdown and @options.attributes
            list.push filename: @filename, attr: 'data-background': '#ff0000'
        else
            list.push @filename

        fs.writeFileSync fullPath, JSON.stringify list, null, 4

fs = require 'fs'
path = require 'path'
yeoman = require 'yeoman-generator'
semver = require 'semver'

module.exports = class SlideGenerator extends yeoman.generators.NamedBase
    constructor: (args, options, config) ->
        super args, options, config
        @option 'notes',
            desc: 'Include speaker notes'
            type: Boolean
            default: false
        @option 'markdown',
            desc: 'Use markdown'
            type: Boolean
            default: false
        @option 'attributes',
            desc: 'Include (data-)attributes on markdown section. This only works combinded with --markdown.'
            type: Boolean
            default: false

    files: ->
        appPath = process.cwd()
        fullPath = path.join appPath, '/slides/list.json'
        list = require fullPath

        if @options.markdown
            @filename = "#{@_.slugify(@name)}.md"
            @log.info 'Using Markdown.'

            if @options.notes
                @template 'slide-withnotes.md', "slides/#{@filename}"
            else
                @template 'slide.md', "slides/#{@filename}"

        else
            @filename = "#{@_.slugify(@name)}.html"
            @log.info 'Using HTML.'

            if @options.notes
                @template 'slide-withnotes.html', "slides/#{@filename}"
            else
                @template 'slide.html', "slides/#{@filename}"

        if @options.markdown and @options.attributes
            list.push filename: @filename, attr: 'data-background': '#ff0000'
        else
            list.push @filename

        fs.writeFileSync fullPath, JSON.stringify list, null, 4

Declare options for --help usage.

Declare options for --help usage.


'.platform-darwin .editor':
  'ctrl-shift-u': 'encoding-selector:show'

'.platform-win32 .editor':
  'ctrl-shift-u': 'encoding-selector:show'

'.platform-linux .editor':
  'ctrl-u': 'encoding-selector:show'

'.platform-darwin atom-text-editor':
  'ctrl-shift-u': 'encoding-selector:show'

'.platform-win32 atom-text-editor':
  'ctrl-shift-u': 'encoding-selector:show'

'.platform-linux atom-text-editor':
  'ctrl-u': 'encoding-selector:show'

Fix deprecated selectors in keymap

Fix deprecated selectors in keymap


"*":
  "atom-ctags": {}
  "autocomplete-plus":
    enableAutoActivation: false
  core:
    audioBeep: false
    disabledPackages: [
      "exception-reporting"
    ]
    ignoredNames: [
      ".bundle"
      ".git"
      "log"
      "repositories"
      "tmp"
    ]
    themes: [
      "atom-dark-ui"
      "solarized-dark-syntax"
    ]
  editor:
    fontFamily: "Menlo"
    invisibles: {}
    scrollPastEnd: true
    showIndentGuide: true
    softWrapAtPreferredLineLength: true
  "git-diff-details":
    closeAfterCopy: true
  "git-history":
    showDiff: false
  "git-plus":
    openInPane: false
    verboseCommit: true
    wordDiff: false
  "merge-conflicts": {}
  "ruby-test":
    rspecFileCommand: "spring rspec --tty {relative_path}"
    rspecSingleCommand: "spring rspec --tty {relative_path}:{line_number}"
    shell: "zsh"
  welcome:
    showOnStartup: false

"*":
  "atom-ctags": {}
  "autocomplete-plus":
    enableAutoActivation: false
  core:
    audioBeep: false
    disabledPackages: [
      "exception-reporting"
    ]
    ignoredNames: [
      ".bundle"
      ".git"
      "log"
      "repositories"
      "tmp"
    ]
    themes: [
      "atom-dark-ui"
      "solarized-dark-syntax"
    ]
  editor:
    fontFamily: "Menlo"
    invisibles: {}
    scrollPastEnd: true
    showIndentGuide: true
    softWrapAtPreferredLineLength: true
  "git-diff-details":
    closeAfterCopy: true
  "git-history":
    showDiff: false
  "git-plus":
    openInPane: false
    verboseCommit: true
    wordDiff: false
  "merge-conflicts": {}
  "ruby-test":
    rspecFileCommand: "spring rspec --tty {relative_path}"
    rspecSingleCommand: "spring rspec --tty {relative_path}:{line_number}"
    shell: "zsh"
    specFramework: "rspec"
  welcome:
    showOnStartup: false

Add Atom ruby-test preferred spec framework


Rubocop = require '../../lib/linter/rubocop'

describe 'Rubocop', ->
  rubocop = null

  beforeEach ->
    rubocop = new Rubocop('/path/to/target.rb')

  describe 'buildCommand', ->
    originalRubocopPath = atom.config.get('atom-lint.rubocop.path')

    afterEach ->
      atom.config.set('atom-lint.rubocop.path', originalRubocopPath)

    describe 'when the target file path is "/path/to/target.rb"', ->
      describe 'and config "atom-lint.rubocop.path" is "/path/to/rubocop"', ->
        it 'returns ["/path/to/rubocop", "--format", "json", "/path/to/target.rb"]', ->
          atom.config.set('atom-lint.rubocop.path', '/path/to/rubocop')
          expect(rubocop.buildCommand())
            .toEqual(['/path/to/rubocop', '--format', 'json', '/path/to/target.rb'])

      describe 'and config "atom-lint.rubocop.path" is not set', ->
        it 'returns ["rubocop", "--format", "json", "/path/to/target.rb"]', ->
          atom.config.set('atom-lint.rubocop.path', null)
          expect(rubocop.buildCommand())
            .toEqual(['rubocop', '--format', 'json', '/path/to/target.rb'])

Rubocop = require '../../lib/linter/rubocop'

describe 'Rubocop', ->
  rubocop = null

  beforeEach ->
    rubocop = new Rubocop('/path/to/target.rb')

  describe 'buildCommand', ->
    originalRubocopPath = atom.config.get('atom-lint.rubocop.path')

    afterEach ->
      atom.config.set('atom-lint.rubocop.path', originalRubocopPath)

    describe 'when the target file path is "/path/to/target.rb"', ->
      describe 'and config "atom-lint.rubocop.path" is "/path/to/rubocop"', ->
        it 'returns ["/path/to/rubocop", "--format", "json", "/path/to/target.rb"]', ->
          atom.config.set('atom-lint.rubocop.path', '/path/to/rubocop')
          expect(rubocop.buildCommand())
            .toEqual(['/path/to/rubocop', '--force-exclusion', '--format', 'json', '/path/to/target.rb'])

      describe 'and config "atom-lint.rubocop.path" is not set', ->
        it 'returns ["rubocop", "--format", "json", "/path/to/target.rb"]', ->
          atom.config.set('atom-lint.rubocop.path', null)
          expect(rubocop.buildCommand())
            .toEqual(['rubocop', '--force-exclusion', '--format', 'json', '/path/to/target.rb'])

Fix spec file to enable --force-exclusion option in RuboCop


{fork} = require('child_process')
prerenderUrl = 'http://127.0.0.1:' + (process.env.PORT or 3000)

module.exports = (app) ->
  # Start up prerender server
  child = fork('server/prerender/server')
  process.on 'exit', ->
    child.kill()

  # Set up Prerender middleware and link to server
  middleware = require('prerender-node')
  middleware.set('prerenderServiceUrl', prerenderUrl)
  app.use(middleware)

{fork} = require('child_process')
prerenderUrl = 'http://127.0.0.1:' + (process.env.PORT or 3000)

module.exports = (app) ->
  # Check if Prerender is available
  try
    require.resolve('prerender')
    require.resolve('prerender-node')
  catch
    return

  # Start up prerender server
  child = fork('server/prerender/server')
  process.on 'exit', ->
    child.kill()

  # Set up Prerender middleware and link to server
  middleware = require('prerender-node')
  middleware.set('prerenderServiceUrl', prerenderUrl)
  app.use(middleware)

Check if pretender is available

Check if pretender is available


_ = require('lodash')
Promise = require('bluebird')

IS_BROWSER = window?

if (IS_BROWSER)
	# The browser mock assumes global fetch prototypes exist
	realFetchModule = require('fetch-ponyfill')({ Promise })
	global.Promise = Promise
	global.Headers = realFetchModule.Headers
	global.Request = realFetchModule.Request
	global.Response = realFetchModule.Response

fetchMock = require('fetch-mock').sandbox(Promise)
fetchMock.patch = (matcher, response, options) ->
	options = _.assign({}, options, { method: 'PATCH' })
	@mock(matcher, response, options)

utils = require('../lib/utils')
utils.fetch = fetchMock.fetchMock

getToken = require('resin-token')
getRequest = require('../lib/request')

dataDirectory = null
if not IS_BROWSER
	settings = require('resin-settings-client')
	dataDirectory = settings.get('dataDirectory')

getCustomRequest = (opts) ->
	opts = _.assign({}, opts, { dataDirectory, debug: false, isBrowser: IS_BROWSER })
	getRequest(opts)

module.exports = ->
	IS_BROWSER: IS_BROWSER
	fetchMock: fetchMock
	token: getToken({ dataDirectory })
	request: getCustomRequest()
	getCustomRequest: getCustomRequest

_ = require('lodash')
Promise = require('bluebird')

IS_BROWSER = window?

if (IS_BROWSER)
	# The browser mock assumes global fetch prototypes exist
	realFetchModule = require('fetch-ponyfill')({ Promise })
	global.Promise = Promise
	global.Headers = realFetchModule.Headers
	global.Request = realFetchModule.Request
	global.Response = realFetchModule.Response

fetchMock = require('fetch-mock').sandbox(Promise)
fetchMock.fetchMock.Promise = Promise
fetchMock.patch = (matcher, response, options) ->
	options = _.assign({}, options, { method: 'PATCH' })
	@mock(matcher, response, options)

utils = require('../lib/utils')
utils.fetch = fetchMock.fetchMock

getToken = require('resin-token')
getRequest = require('../lib/request')

dataDirectory = null
if not IS_BROWSER
	settings = require('resin-settings-client')
	dataDirectory = settings.get('dataDirectory')

getCustomRequest = (opts) ->
	opts = _.assign({}, opts, { dataDirectory, debug: false, isBrowser: IS_BROWSER })
	getRequest(opts)

module.exports = ->
	IS_BROWSER: IS_BROWSER
	fetchMock: fetchMock
	token: getToken({ dataDirectory })
	request: getCustomRequest()
	getCustomRequest: getCustomRequest

Fix bug in fetch-mock that breaks custom Promise usage