namespace 'Exo.Widgets.AutocompleteTokenField', (exports) ->

  exports.ReplaceIdStrategy = (tokens) ->
    if id = tokens[0]?.id
      @originalInput.value = id

  exports.ArrayStrategy = (tokens) ->
    inputName = @originalInput.name + "[]"
    @$el.find('[type="hidden"]').remove()
    for token in tokens
      input = document.createElement("input")
      input.name = inputName
      input.value = token.get("query")
      input.type = "hidden"
      @el.appendChild(input)
    return

  exports.NestedAttributesStrategy = (instance, tokens) ->

  klass = class extends Exo.Views.AutocompleteTokenField

    constructor: (el, options = {}) ->
      @serializeStrategy = options.serializeStrategy || exports.NestedAttributesStrategy

      options.input = el[0]
      super(options)

    initialize: (options) ->
      super

      @source ||= new Backbone.Collection
      @setSource(@source)

      @render()

      unless @originalInput.value == ""
        @insertToken(@source.get(@originalInput.value))

      if options.tokens
        @setToken(options.tokens)

      @on "add remove", (tokens) =>
        tokens = if tokens instanceof Array then tokens else [tokens]
        @serializeStrategy.call(this, tokens)

  Exo.Widget.register("autoCompleteTokenField", klass)

namespace 'Exo.Widgets.AutocompleteTokenField', (exports) ->

  # TODO add docs for all of these!!!

  exports.ReplaceIdStrategy = (tokens) ->
    if id = tokens[0]?.id
      @originalInput.value = id

  exports.ArrayStrategy = (tokens) ->
    inputName = @originalInput.name + "[]"
    @$el.find('[type="hidden"]').remove()
    for token in tokens
      input = document.createElement("input")
      input.name = inputName
      input.value = token.get("query")
      input.type = "hidden"
      @el.appendChild(input)
    return

  exports.NestedAttributesStrategy = (tokens) ->
    token = tokens[0]

    if !token.isNew()
      # If token is an existing object than convert nested attributes to ID attribute instead
      #
      # Example:
      #
      # employee[employer_attributes][name]
      #
      # becomes
      #
      # employee[employer_id]

      replaceNestedAttributesRegex = /_attributes](\[\w+\])+$/

      @originalInput.name = @originalInput.name.replace(replaceNestedAttributesRegex, "_id]")
      @originalInput.value = token.id
    else
      @originalInput.value = token.get("query")

  klass = class extends Exo.Views.AutocompleteTokenField
    constructor: (el, options = {}) ->
      @serializeStrategy = options.serializeStrategy || exports.ArrayStrategy

      options.input = el[0]
      super(options)

    initialize: (options) ->
      super

      @source ||= new Backbone.Collection
      @setSource(@source)

      @render()

      unless @originalInput.value == ""
        @insertToken(@source.get(@originalInput.value))

      if options.tokens
        @setToken(options.tokens)

      @on "add remove", (tokens) =>
        tokens = if tokens instanceof Array then tokens else [tokens]
        @serializeStrategy.call(this, tokens, options)

  Exo.Widget.register("autoCompleteTokenField", klass)

Implement NestedAttributesStrategy for autocompleteTokenField widget


class Cfp.Views.RankingView extends Backbone.View
  el: '#ranking'

  events:
    'change #rank' : 'changeRank'

  changeRank: ->
    rank = new Cfp.Models.Rank
    rank.save
      value: @selectedRank()

  selectedRank: =>
    @$('#rank').val()

class Cfp.Views.RankingView extends Backbone.View
  el: '#ranking'

  events:
    'change #rank' : 'changeRank'

  changeRank: ->
    rank = new Cfp.Models.Rank
    rank.save
      value: @selectedRank()

  selectedRank: =>
    @$('#rank:checked').val()

Fix for bad asset compilation on production, this MUST BE REMOVED in the future


Base = require("./Base")

module.exports = class Mercurial extends Base

  type: 'mercurial'

  _doCommit: (message) -> @_exec(['hg', 'commit', '-m', message])

  _doPull: -> @_exec(['hg', 'pull'])

  _doPush: -> @_exec(['hg', 'push'])

  _doUpdate: (revision) -> @_exec(['hg', 'update', revision])

  _doMerge: (revision) -> @_exec(['hg', 'merge', '-r', revision])

  _doStatus: -> @_exec(['hg', 'status'])

  _doBranch: -> @_exec(['hg', 'branch'])

Base = require("./Base")

module.exports = class Mercurial extends Base

  type: 'mercurial'

  _doCommit: (message) -> @_exec(['hg', 'commit', '-m', message])

  _doPull: -> @_exec(['hg', 'pull'])

  _doPush: -> @_exec(['hg', 'push'])

  _doUpdate: (revision) -> @_exec(['hg', 'update', revision])

  _doMerge: (revision) -> @_exec(['hg', 'merge', revision])

  _doStatus: -> @_exec(['hg', 'status'])

  _doBranch: -> @_exec(['hg', 'branch'])

Remove -r flag from hg merge command

Remove -r flag from hg merge command


define [
	"base"
], (App) ->
	App.directive "reviewPanelToggle", () ->
		restrict: "E"
		scope:
			onToggle: '&'
			ngModel: '='
			disabled: '=?'
			onDisabledClick: '=?'
		link: (scope) ->
			if !scope.disabled?
				scope.disabled = false
			scope.onChange = (args...) ->
				scope.onToggle({ isOn: scope.localModel })
			scope.handleClick = () ->
				if scope.disabled
					scope.onDisabledClick()
			scope.localModel = scope.ngModel
			scope.$watch "ngModel", (value) ->
				scope.localModel = value

		template: """

	<input id="rp-toggle-{{$id}}" ng-disabled="disabled" type="checkbox" class="rp-toggle-hidden-input" ng-model="localModel" ng-change="onChange()" />

"""

define [
	"base"
], (App) ->
	App.directive "reviewPanelToggle", () ->
		restrict: "E"
		scope:
			onToggle: '&'
			ngModel: '='
			valWhenUndefined: '=?'
			disabled: '=?'
			onDisabledClick: '=?'
		link: (scope) ->
			if !scope.disabled?
				scope.disabled = false
			scope.onChange = (args...) ->
				scope.onToggle({ isOn: scope.localModel })
			scope.handleClick = () ->
				if scope.disabled
					scope.onDisabledClick()
			scope.localModel = scope.ngModel
			scope.$watch "ngModel", (value) ->
				if scope.valWhenUndefined? and !value?
					value = scope.valWhenUndefined
				scope.localModel = value

		template: """

	<input id="rp-toggle-{{$id}}" ng-disabled="disabled" type="checkbox" class="rp-toggle-hidden-input" ng-model="localModel" ng-change="onChange()" />

"""

Support a default value when undefined in the review panel toggles.


expect = require('chai').expect
algorhythm = require('../../lib/algorhythmic')

describe 'Algorhythmic', ->
  it 'works', ->
    expect(algorhythm).to.be.ok

expect = require('chai').expect
algorhythm = require('../../lib/algorhythmic')

describe 'Algorhythmic', ->
  describe 'bucketing an identifier', ->
    bucket = randomString = null

    it 'buckets an empty string', ->
      expect(algorhythm.convert('')).to.be.ok

    it 'buckets a single character string', ->
      expect(algorhythm.convert('a')).to.be.ok

    it 'always converts a string to the same bucket', ->
      for run in [1..100]
        bucket = algorhythm.convert('foo')

        expect(bucket).to.equal(5)

    it 'always buckets within the given range', ->
      for run in [1..100]
        randomString = Math.random().toString(30).substring(2)
        bucket = algorhythm.convert(randomString)

        expect(bucket).to.be.within(1,10)

Add some tests around the bucketing

Add some tests around the bucketing


exports.config =
  paths:
    watched:['client']
  files:
    javascripts:
      joinTo: 'app.js'
    stylesheets:
      joinTo: 'app.css'
    templates:
      joinTo: 'app.js'

exports.config =
  sourceMaps: false
  paths:
    watched:['client']
  files:
    javascripts:
      joinTo:
        'javascripts/app.js': /^client(\/|\\)app/
        'javascripts/vendor.js': /^(bower_components|(client(\/|\\)vendor))/
    stylesheets:
      joinTo: 'stylesheets/app.css'
    templates:
      default: 'hbs'
      joinTo: 'javascripts/app.js'

Update the conf to work.

Update the conf to work.


App.ResultToolbarController = Em.ArrayController.extend
  content: []
  searchBinding: 'currentSearchController.content'

  pageSize: 15

  _currentPageNo: 0
  currentPageNo: ((key, value) ->
    if value?
      numPages = @get('numPages')
      @_currentPageNo = if value < 0 then 0 else (if value >= numPages then numPages - 1 else value)

    @_currentPageNo
  ).property()

  showPreviousPage: ->
    @set('currentPageNo', @get('currentPageNo') - 1)

  showNextPage: ->
    @set('currentPageNo', @get('currentPageNo') + 1)
    App.router.send('goToPage')

  showFirstPage: ->
    @set('currentPageNo', 0)

  showLastPage: ->
    @set('currentPageNo', @get('numPages') - 1)

  numPages: (->
    numHits = @get('search.numHits')
    if numHits is 0 then 0 else Math.floor((numHits - 1) / @pageSize) + 1
  ).property('search')

  hasMoreThanOnePage: (->
    @get('numPages') > 1
  ).property('numPages')

  isShowingFirstPage: (->
    @get('currentPageNo') is 0
  ).property('currentPageNo')

  isShowingLastPage: (->
    @get('currentPageNo') is @get('numPages') - 1
  ).property('currentPageNo', 'numPages')

App.ResultToolbarController = Em.ArrayController.extend
  content: []
  searchBinding: 'currentSearchController.content'

  pageSize: 15

  _currentPageNo: 0
  currentPageNo: ((key, value) ->
    if value?
      numPages = @get('numPages')
      @_currentPageNo = if value < 0 then 0 else (if value >= numPages then numPages - 1 else value)

    @_currentPageNo
  ).property()

  showPreviousPage: ->
    @set('currentPageNo', @get('currentPageNo') - 1)
    App.router.send('goToPage')

  showNextPage: ->
    @set('currentPageNo', @get('currentPageNo') + 1)
    App.router.send('goToPage')

  showFirstPage: ->
    @set('currentPageNo', 0)
    App.router.send('goToPage')

  showLastPage: ->
    @set('currentPageNo', @get('numPages') - 1)
    App.router.send('goToPage')

  numPages: (->
    numHits = @get('search.numHits')
    if numHits is 0 then 0 else Math.floor((numHits - 1) / @pageSize) + 1
  ).property('search')

  hasMoreThanOnePage: (->
    @get('numPages') > 1
  ).property('numPages')

  isShowingFirstPage: (->
    @get('currentPageNo') is 0
  ).property('currentPageNo')

  isShowingLastPage: (->
    @get('currentPageNo') is @get('numPages') - 1
  ).property('currentPageNo', 'numPages')

Send events when traversing result pages


ETahi.InlineEditBodyPartComponent = Em.Component.extend
  editing: false
  snapshot: []
  confirmDelete: false

  createSnapshot: (->
    @set('snapshot', Em.copy(@get('block'), true))
  ).observes('editing')

  hasContent: true
  # hasContent: Em.computed.notEmpty('bodyPart.value')

  hasNoContent: Em.computed.not('hasContent')

  actions:
    toggleEdit: ->
      @sendAction('cancel', @get('block'), @get('snapshot')) if @get('editing')
      @toggleProperty 'editing'

    deleteBlock: ->
      @sendAction('delete', @get('block'))

    save: ->
      if @get('hasContent')
        @sendAction('save', @get('block'))
        @toggleProperty 'editing'

    confirmDeletion: ->
      @set('confirmDelete', true)

    cancelDestroy: ->
      @set('confirmDelete', false)

    addItem: ->
      @sendAction('addItem', @get('block'))

ETahi.InlineEditBodyPartComponent = Em.Component.extend
  editing: false
  snapshot: []
  confirmDelete: false

  createSnapshot: (->
    @set('snapshot', Em.copy(@get('block'), true))
  ).observes('editing')

  hasContent: (->
    @get('block').any(@_isEmpty)
  ).property('block.@each.value')

  hasNoContent: Em.computed.not('hasContent')

  _isEmpty: (item) ->
    item && !Ember.isEmpty(item.value)

  actions:
    toggleEdit: ->
      @sendAction('cancel', @get('block'), @get('snapshot')) if @get('editing')
      @toggleProperty 'editing'

    deleteBlock: ->
      @sendAction('delete', @get('block'))

    save: ->
      if @get('hasContent')
        @sendAction('save', @get('block'))
        @toggleProperty 'editing'

    confirmDeletion: ->
      @set('confirmDelete', true)

    cancelDestroy: ->
      @set('confirmDelete', false)

    addItem: ->
      @sendAction('addItem', @get('block'))

Disable saving when items are all empty

Disable saving when items are all empty


Sprangular.directive 'shippingRateSelection', ->
  restrict: 'E'
  templateUrl: 'shipping/rates.html'
  scope:
    order: '='

  controller: ($scope, Checkout, Env) ->
    $scope.loading = false
    $scope.address = {}
    $scope.currencySymbol = Env.currency.symbol

    $scope.$watch 'order.shippingRate', (rate, oldRate) ->
      return if !oldRate || rate.id == oldRate.id

      order = $scope.order

      if rate
        order.shipTotal = rate.cost
      else
        order.shipTotal = 0

      order.updateTotals()

    $scope.$watch('order.shippingAddress', ->
      $scope.address = $scope.order.shippingAddress
    , true)

    validateAddress = (address) ->
      $scope.isValid = address.firstname && address.lastname && address.city && address.address1 && address.zipcode && address.country && address.state && address.phone

    $scope.$watch('address', validateAddress, true)

    # use $scope.$watchGroup when its released
    $scope.$watch 'address.country.id + address.state.id + address.zipcode + isValid', (oldValue, newValue) ->
      return if $scope.loading || oldValue == newValue || !$scope.isValid

      $scope.loading = true

      Checkout.setDelivery().then ->
        $scope.loading = false

    validateAddress($scope.address)

Sprangular.directive 'shippingRateSelection', ->
  restrict: 'E'
  templateUrl: 'shipping/rates.html'
  scope:
    order: '='

  controller: ($scope, Checkout, Env) ->
    $scope.loading = false
    $scope.address = {}
    $scope.currencySymbol = Env.currency.symbol

    $scope.$watch 'order.shippingRate', (rate, oldRate) ->
      return if !oldRate || rate.id == oldRate.id

      order = $scope.order

      if rate
        order.shipTotal = rate.cost
      else
        order.shipTotal = 0

      order.updateTotals()

      Checkout.setDelivery()

    $scope.$watch('order.shippingAddress', ->
      $scope.address = $scope.order.shippingAddress
    , true)

Refactor shipping-rate-selection directive to remove annoying refresh issue


{View} = require 'atom'
ImageEditor = require './image-editor'

module.exports =
class ImageEditorStatusView extends View
  @content: ->
    @div class: 'status-image inline-block', =>
      @span class: 'image-size', outlet: 'imageSizeStatus'

  initialize: (@statusBar) ->
    @attach()

    @subscribe atom.workspaceView, 'pane-container:active-pane-item-changed', =>
      @updateImageSize()

  attach: ->
    @statusBar.appendLeft this

  afterAttach: ->
    @updateImageSize()

  getImageSize: ({originalHeight, originalWidth}) ->
    @imageSizeStatus.text("#{originalWidth}x#{originalHeight}").show()

  updateImageSize: ->
    editor = atom.workspaceView.getActivePaneItem()
    if editor instanceof ImageEditor
      view = atom.workspaceView.getActiveView()
      if view.loaded
        @getImageSize(view)
      else # wait for image to load before getting originalWidth and originalHeight
        view.image.load =>
          # Make sure view is still active since load is async
          @getImageSize(view) if view is atom.workspaceView.getActiveView()
    else
      @imageSizeStatus.hide()

{View} = require 'atom'
ImageEditor = require './image-editor'

module.exports =
class ImageEditorStatusView extends View
  @content: ->
    @div class: 'status-image inline-block', =>
      @span class: 'image-size', outlet: 'imageSizeStatus'

  initialize: (@statusBar) ->
    @attach()

    @subscribe atom.workspaceView, 'pane-container:active-pane-item-changed', =>
      @updateImageSize()

  attach: ->
    @statusBar.appendLeft this

  afterAttach: ->
    @updateImageSize()

  getImageSize: ({originalHeight, originalWidth}) ->
    @imageSizeStatus.text("#{originalWidth}x#{originalHeight}").show()

  updateImageSize: ->
    editor = atom.workspaceView.getActivePaneItem()
    if editor instanceof ImageEditor
      view = atom.workspaceView.getActiveView()
      if view.loaded
        @getImageSize(view)
      else
        # Wait for image to load before getting originalWidth and originalHeight
        view.image.load =>
          # Make sure view is still active since load is async
          @getImageSize(view) if view is atom.workspaceView.getActiveView()
    else
      @imageSizeStatus.hide()

Move comment to next line

:lipstick: Move comment to next line


class Tenon.features.QuickSearch
  constructor: ->
    @$content = $('.toolbox #quick-search-container')
    @$toggle = $('#quick-search-toggle')
    @openClass = 'open'
    @$field = $('#quick-search')

    # listeners
    $(document).on('keyup', '#quick-search', $.debounce(500, @_submitSearch))

    @$toggle.on('click', @toggleNav)
    # $(document).on('click', '.nav-holder.mobile-open a', @_closeNav)

  _submitSearch: (e) =>
    @$list = $(@$field.data('record-list'))
    params = {q: @$field.val()}
    new Tenon.features.RecordList(@$list, params: params, clear: true)

  toggleNav: (e) =>
    e.preventDefault()
    @$toggle.find('i').toggleClass('fa-search fa-times')
    if @$toggle.hasClass(@openClass)
      @_closeNav()
    else
      @_openNav()

  _closeNav: () =>
    @$field.val('').keyup() # clear quick search and submit it when closing the toolbox
    @$toggle.removeClass(@openClass)
    $('header').css('border-bottom-width', '1px')
    @$content.removeClass(@openClass)

  _openNav: () =>
    @$toggle.addClass(@openClass)
    $('header').css('border-bottom-width', '0px')
    @$content.addClass(@openClass)
    @$content.find('input')[0].focus()

class Tenon.features.QuickSearch
  constructor: ->
    @$content = $('.toolbox #quick-search-container')
    @$toggle = $('#quick-search-toggle')
    @openClass = 'open'

    # listeners
    $(document).on('keyup', '#quick-search', $.debounce(500, @_submitSearch))

    @$toggle.on('click', @toggleNav)
    # $(document).on('click', '.nav-holder.mobile-open a', @_closeNav)

  _submitSearch: (e) =>
    # Need to set this here so that if #quick-search is drawn after initial
    # DOM-ready we still have access to it
    @$field = $('#quick-search')

    @$list = $(@$field.data('record-list'))
    params = {q: @$field.val()}
    new Tenon.features.RecordList(@$list, params: params, clear: true)

  toggleNav: (e) =>
    e.preventDefault()
    @$toggle.find('i').toggleClass('fa-search fa-times')
    if @$toggle.hasClass(@openClass)
      @_closeNav()
    else
      @_openNav()

  _closeNav: () =>
    if @$field
      @$field.val('').keyup() # clear quick search and submit it when closing the toolbox
    @$toggle.removeClass(@openClass)
    $('header').css('border-bottom-width', '1px')
    @$content.removeClass(@openClass)

  _openNav: () =>
    @$toggle.addClass(@openClass)
    $('header').css('border-bottom-width', '0px')
    @$content.addClass(@openClass)
    @$content.find('input')[0].focus()

Fix QuickSearch for quick searches that are drawn after DOMReady


Bacon = require 'bacon'
$ = require 'jquery'

class NCN
  constructor: (@host = '') ->
  request = (method) -> (path, data) ->
    Bacon.fromPromise(
      $.ajax @host + path,
        data: data
        type: method
        dataType: 'json')
  get: request 'GET'
  post: request 'POST'
  delete: request 'DELETE'
  put: request 'PUT'

module.exports = NCN

Bacon = require 'bacon'
$ = require 'jquery'

class NCN
  constructor: (@host = '') ->
    @toilets = new NCN @host + '/toilets'
  request = (method) -> (path, data) ->
    [path, data] = [@host, path] unless data?
    Bacon.fromPromise(
      $.ajax @host + path,
        data: data
        type: method
        dataType: 'json')
  get: request 'GET'
  post: request 'POST'
  delete: request 'DELETE'
  put: request 'PUT'

module.exports = NCN

Make path an optional parameter

Make path an optional parameter


App.Followable =

  update: (followable_id, button) ->
    $("#" + followable_id + " .js-follow").html(button)
    # Temporary line. Waiting for issue resolution: https://github.com/consul/consul/issues/1736
    initialize_modules()

Remove uneeded initialization after ajax refresh.


module.exports = (grunt) ->
  require('load-grunt-tasks') grunt

  grunt.initConfig
    clean:
      folder: 'build'

    sass:
      options:
        sourceMap: true
      dist:
        files: 'build/main.css': 'stylesheets/main.sass'

    watch:
      files: ['stylesheets/**/*.sass', 'stylesheets/**/*.scss']
      tasks: ['sass']

  grunt.registerTask 'default', ['clean', 'sass']

module.exports = (grunt) ->
  require('load-grunt-tasks') grunt

  grunt.initConfig
    clean:
      folder: 'build'

    sass:
      options:
        sourceMap: true
        includePaths: ['bower_components']
      dist:
        files: 'build/main.css': 'stylesheets/main.sass'

    watch:
      files: ['stylesheets/**/*.sass', 'stylesheets/**/*.scss']
      tasks: ['sass']

  grunt.registerTask 'default', ['clean', 'sass']

Add includePaths to sass options.

Add includePaths to sass options.


Path = require('path')
fs = require('fs')

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

    coffee:
      compile:
        files:
          'js/odometer.js': 'coffee/odometer.coffee'
          'js/landing-page.js': 'coffee/landing-page.coffee'

    watch:
      coffee:
        files: ['coffee/odometer.coffee', 'sass/*']
        tasks: ["coffee", "uglify", "compass"]

    uglify:
      options:
        banner: "/*! <%= pkg.name %> <%= pkg.version %> */\n"

      dist:
        src: 'odometer.js'
        dest: 'odometer.min.js'

    compass:
      dist:
        options:
          sassDir: 'sass'
          cssDir: 'css'

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

  grunt.registerTask 'default', ['coffee', 'uglify', 'compass']

Path = require('path')
fs = require('fs')

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

    coffee:
      compile:
        files:
          'js/odometer.js': 'coffee/odometer.coffee'
          'js/landing-page.js': 'coffee/landing-page.coffee'

    watch:
      coffee:
        files: ['coffee/odometer.coffee', 'coffee/landing-page.coffee', 'sass/*']
        tasks: ["coffee", "uglify", "compass"]

    uglify:
      options:
        banner: "/*! <%= pkg.name %> <%= pkg.version %> */\n"

      dist:
        src: 'odometer.js'
        dest: 'odometer.min.js'

    compass:
      dist:
        options:
          sassDir: 'sass'
          cssDir: 'css'

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

  grunt.registerTask 'default', ['coffee', 'uglify', 'compass']

Add watcher for landing page coffeescript


'menu': [
  'label': 'Edit'
  'submenu': [
    'label': 'Select Encoding'
    'command': 'encoding-selector:show'
  ]
]

'context-menu':
  '.overlayer': [
    'label': 'Change Encoding'
    'command': 'encoding-selector:show'
  ]

'menu': [
  'label': 'Edit'
  'submenu': [
    'label': 'Select Encoding'
    'command': 'encoding-selector:show'
  ]
]

'context-menu':
  'atom-text-editor': [
    'label': 'Change Encoding'
    'command': 'encoding-selector:show'
  ]

Add context menu to atom-text-editor selector


path = require 'path'

_ = require 'underscore-plus'
optimist = require 'optimist'

config = require './config'
Command = require './command'
Install = require './install'

module.exports =
class Rebuild extends Command
  @commandNames: ['rebuild']

  constructor: ->
    @atomNodeDirectory = path.join(config.getAtomDirectory(), '.node-gyp')
    @atomNpmPath = require.resolve('npm/bin/npm-cli')

  parseOptions: (argv) ->
    options = optimist(argv)
    options.usage """

      Usage: apm rebuild

      Rebuild all the modules currently installed in the node_modules folder
      in the current working directory.
    """
    options.alias('h', 'help').describe('help', 'Print this usage message')

  showHelp: (argv) -> @parseOptions(argv).showHelp()

  run: ({callback}) ->
    new Install().installNode (error) =>
      if error?
        callback(error)
      else
        process.stdout.write 'Rebuilding modules '

        rebuildArgs = ['rebuild']
        rebuildArgs.push("--target=#{config.getNodeVersion()}")
        rebuildArgs.push("--arch=#{config.getNodeArch()}")
        env = _.extend({}, process.env, HOME: @atomNodeDirectory)
        env.USERPROFILE = env.HOME if config.isWin32()

        @fork @atomNpmPath, rebuildArgs, {env}, (code, stderr='') ->
          if code is 0
            process.stdout.write '\u2713\n'.green
            callback()
          else
            process.stdout.write '\u2717\n'.red
            callback(stderr)

path = require 'path'

_ = require 'underscore-plus'
optimist = require 'optimist'

config = require './config'
Command = require './command'
Install = require './install'

module.exports =
class Rebuild extends Command
  @commandNames: ['rebuild']

  constructor: ->
    @atomNodeDirectory = path.join(config.getAtomDirectory(), '.node-gyp')
    @atomNpmPath = require.resolve('npm/bin/npm-cli')

  parseOptions: (argv) ->
    options = optimist(argv)
    options.usage """

      Usage: apm rebuild

      Rebuild all the modules currently installed in the node_modules folder
      in the current working directory.
    """
    options.alias('h', 'help').describe('help', 'Print this usage message')

  showHelp: (argv) -> @parseOptions(argv).showHelp()

  run: ({callback}) ->
    new Install().installNode (error) =>
      if error?
        callback(error)
      else
        process.stdout.write 'Rebuilding modules '

        rebuildArgs = ['rebuild']
        rebuildArgs.push("--target=#{config.getNodeVersion()}")
        rebuildArgs.push("--arch=#{config.getNodeArch()}")
        env = _.extend({}, process.env, HOME: @atomNodeDirectory)
        env.USERPROFILE = env.HOME if config.isWin32()

        @fork @atomNpmPath, rebuildArgs, {env}, (code, stderr='') =>
          if code is 0
            @logSuccess()
            callback()
          else
            @logFailure()
            callback(stderr)

Use log helpers in Rebuild command

Use log helpers in Rebuild command


Meteor.startup ->
  if Meteor.isServer

    lib = require './lib.coffee'
    keys = require '/server/utils/deploykeys.coffee'

    authenticationHandler = ->
      if (key = @params.query?['api-key']) or (key = @request.headers?['api-key'])
        if (apiKey = APIKeys.findOne key: key)
          user = Meteor.users.findOne(apiKey.owner)
          # @authenticatedUSer = user
          @next()
        else if(key in keys())
          user = "deploy"
          @next()
        else
          lib.endWithError @response, 401, "Not authenticated"
      else lib.endWithError @response, 401, "No API key provided"

    Router.onBeforeAction authenticationHandler, only:
      [
        'api/v2/apps/details'
        'api/v2/apps/dockerCompose'
        'api/v2/apps/bigboatCompose'
        'api/v2/apps/byName'
        'api/v2/apps'
        'api/v2/instances'
        'api/v2/instances/single'
        'api/v2/status'
      ]

Meteor.startup ->
  if Meteor.isServer

    lib = require './lib.coffee'
    keys = require '/server/utils/deploykeys.coffee'

    authenticationHandler = ->
      if (key = @params.query?['api-key']) or (key = @request.headers?['api-key'])
        if (apiKey = APIKeys.findOne key: key)
          user = Meteor.users.findOne(apiKey.owner)
          if user
            @next()
          else
            lib.endWithError @response, 401, "Not authenticated"
        else if(key in keys())
          user = "deploy"
          @next()
        else
          lib.endWithError @response, 401, "Not authenticated"
      else lib.endWithError @response, 401, "No API key provided"

    Router.onBeforeAction authenticationHandler, only:
      [
        'api/v2/apps/details'
        'api/v2/apps/dockerCompose'
        'api/v2/apps/bigboatCompose'
        'api/v2/apps/byName'
        'api/v2/apps'
        'api/v2/instances'
        'api/v2/instances/single'
        'api/v2/status'
      ]

Check for, and deny anonymous api_keys

Check for, and deny anonymous api_keys


webpack = require "webpack"
option  = require "./gulp.coffee"

module.exports =
    watchDelay  : 500

    output      :
        filename            : "[name].js"
        sourceMapFilename   : "map/[file].map"
        publicPath          : "/js/"

    devtool     : "#source-map"
    resolve     :
        root            : [
            "#{option.sourceDir}/renderer/coffee"
            "#{option.sourceDir}/renderer/js"
        ]
        extensions      : ["", ".coffee", ".js"]
        modulesDirectories  : [
            "bower_components"
            "node_modules"
        ]
        alias               :
            bower   : "bower_components"

    module                  :
        loaders     : [
            {test: /\.html$/,   loader: "html-loader"}
            {test: /\.coffee$/, loader: "coffee-loader"}
            {test: /\.jade$/,   loader: "jade-loader"}
            {test: /\.styl$/,   loader: "css-loader!stylus-loader"}
            {test: /\.cson$/,   loader: "cson-loader"}
            {test: /\.json$/,   loader: "json-loader"}
        ]

    plugins         : [
        new webpack.ResolverPlugin(new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin("package.json", [ "main" ]))
        new webpack.ResolverPlugin(new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin("bower.json", [ "main" ]))
        new webpack.ResolverPlugin(new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin("component.json", [ "main" ]))
        new webpack.optimize.AggressiveMergingPlugin
        new webpack.optimize.DedupePlugin
    ]

webpack = require "webpack"
option  = require "./gulp.coffee"

module.exports =
    watchDelay  : 500

    output      :
        filename            : "[name].js"
        sourceMapFilename   : "map/[file].map"
        publicPath          : "/js/"

    devtool     : "#source-map"

    target      : "atom"

    resolve     :
        root            : [
            "#{option.sourceDir}/renderer/coffee"
            "#{option.sourceDir}/renderer/js"
        ]
        extensions      : ["", ".coffee", ".js"]
        modulesDirectories  : [
            "bower_components"
            "node_modules"
        ]
        alias               :
            bower   : "bower_components"

    module                  :
        loaders     : [
            {test: /\.html$/,   loader: "html-loader"}
            {test: /\.coffee$/, loader: "coffee-loader"}
            {test: /\.jade$/,   loader: "jade-loader"}
            {test: /\.styl$/,   loader: "css-loader!stylus-loader"}
            {test: /\.cson$/,   loader: "cson-loader"}
            {test: /\.json$/,   loader: "json-loader"}
        ]

    plugins         : [
        new webpack.ResolverPlugin(new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin("package.json", [ "main" ]))
        new webpack.ResolverPlugin(new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin("bower.json", [ "main" ]))
        new webpack.ResolverPlugin(new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin("component.json", [ "main" ]))
        new webpack.optimize.AggressiveMergingPlugin
        new webpack.optimize.DedupePlugin
    ]

Change webkit buils target to Electron(atom-shell)


class SidebarController extends KDViewController

  constructor:->
    super

    mainController = KD.getSingleton 'mainController'
    mainController.on 'ManageRemotes', -> new ManageRemotesModal
    mainController.on 'ManageDatabases', -> new ManageDatabasesModal
    mainController.on 'AccountChanged', @bound 'accountChanged'
    mainController.ready @bound 'accountChanged'

  accountChanged:(account)->
    account or= KD.whoami()
    {profile} = account
    sidebar   = @getView()

    {
     avatar, finderHeader, navController
     avatarAreaIconMenu, finderController
     footerMenuController, resourcesController
    } = sidebar

    avatar.setData account
    finderHeader.setData account
    # temp fix
    # this should be done on framework level
    # check comments on KDObject::setData
    avatar.render()
    finderHeader.render()

    navController.reset()
    footerMenuController.reset()
    @resetAdminNavItems()

    avatarAreaIconMenu.accountChanged account

    # get registered users vms all ready to go (don't for guests right now):
    finderController.reset()      if "registered" is account.type

    resourcesController.reset()

  resetAdminNavItems:->
    # return unless KD.isLoggedIn()

    KD.whoami().fetchRole? (err, role)=>
      if role is "super-admin"
        @getView().navController.addItem
          title    : "Admin Panel"
          type     : "admin"
          loggedIn : yes
          callback : -> new AdminModal

class SidebarController extends KDViewController

  constructor:->
    super

    mainController = KD.getSingleton 'mainController'
    mainController.on 'ManageRemotes', -> new ManageRemotesModal
    mainController.on 'ManageDatabases', -> new ManageDatabasesModal
    mainController.on 'AccountChanged', @bound 'accountChanged'
    mainController.ready @bound 'accountChanged'

  accountChanged:(account)->
    account or= KD.whoami()
    {profile} = account
    sidebar   = @getView()

    {
     avatar, finderHeader, navController
     avatarAreaIconMenu, finderController
     footerMenuController, resourcesController
    } = sidebar

    avatar.setData account
    finderHeader.setData account
    # temp fix
    # this should be done on framework level
    # check comments on KDObject::setData
    avatar.render()
    finderHeader.render()

    navController.reset()
    footerMenuController.reset()
    @resetAdminNavItems()

    avatarAreaIconMenu.accountChanged account

    if KD.isLoggedIn()
      # get registered users vms
      # allready to go (don't for guests right now):
      finderController.reset()
      resourcesController.reset()

  resetAdminNavItems:->
    # return unless KD.isLoggedIn()

    KD.whoami().fetchRole? (err, role)=>
      if role is "super-admin"
        @getView().navController.addItem
          title    : "Admin Panel"
          type     : "admin"
          loggedIn : yes
          callback : -> new AdminModal

Fix the logic in SidebarController which triggers the FinderController reset to auto-mount vms


Chamber.Views.Messages ||= {}

class Chamber.Views.Messages.IndexView extends Backbone.View
  template: JST["backbone/templates/messages/index"]

  initialize: () ->
    #@options.messages.bind('reset', @addAll)
    @options.messages.bind('add', @addOne)

  #addAll: () =>
  #  @options.messages.each(@addOne)

  addOne: (message) =>
    view = new Chamber.Views.Messages.MessageView({model : message})
    $("tbody", @el).append(view.render().el)

  render: =>
    $(@el).html(@template(messages: @options.messages.toJSON()))
    return this

Chamber.Views.Messages ||= {}

class Chamber.Views.Messages.IndexView extends Backbone.View
  template: JST["backbone/templates/messages/index"]

  initialize: () ->
    #@options.messages.bind('reset', @addAll)
    @options.messages.bind('add', @addOne)

  #addAll: () =>
  #  @options.messages.each(@addOne)

  addOne: (message) =>
    view = new Chamber.Views.Messages.MessageView({model : message})
    $("tbody", @el).append(view.render().el)

    $('html, body').animate({
       scrollTop: $(document).height()-$(window).height()},
       200
    )

  render: =>
    $(@el).html(@template(messages: @options.messages.toJSON()))

    return this

Add scroll at new message

Add scroll at new message


addBinding = ViewModel.addBinding

addBinding
  name: 'text'
  autorun: (c, bindArg) ->
    bindArg.element.text bindArg.getVmValue()
    return

addBinding
  name: 'default'
  bind: (bindArg) ->
    bindArg.element.on bindArg.bindName, (event) ->
      if ~bindArg.bindValue.indexOf(')', bindArg.bindValue.length - 1)
        bindArg.getVmValue()
      else
        bindArg.setVmValue(event)
      return
    return

addBinding
  name: 'toggle'
  events:
    click: (event, bindArg) ->
      value = bindArg.getVmValue()
      bindArg.setVmValue(!value)
      return

addBinding
  name: 'if'
  autorun: (c, bindArg) ->
    if bindArg.getVmValue()
      bindArg.element.show()
    else
      bindArg.element.hide()
    return

addBinding
  name: 'value'
  selector: 'input'
  events:
    'input propertychange': (event, bindArg) ->
      bindArg.setVmValue bindArg.element.val()
  autorun: (c, bindArg) ->
    bindArg.element.val bindArg.getVmValue()

addBinding = ViewModel.addBinding

addBinding
  name: 'default'
  bind: (bindArg) ->
    bindArg.element.on bindArg.bindName, (event) ->
      if ~bindArg.bindValue.indexOf(')', bindArg.bindValue.length - 1)
        bindArg.getVmValue()
      else
        bindArg.setVmValue(event)
      return
    return

addBinding
  name: 'toggle'
  events:
    click: (event, bindArg) ->
      value = bindArg.getVmValue()
      bindArg.setVmValue(!value)
      return

addBinding
  name: 'if'
  autorun: (c, bindArg) ->
    if bindArg.getVmValue()
      bindArg.element.show()
    else
      bindArg.element.hide()
    return

addBinding
  name: 'value'
  selector: 'input'
  events:
    'input propertychange': (event, bindArg) ->
      bindArg.setVmValue bindArg.element.val()
      return
  autorun: (c, bindArg) ->
    bindArg.element.val bindArg.getVmValue()
    return

addBinding
  name: 'text'
  autorun: (c, bindArg) ->
    bindArg.element.text bindArg.getVmValue()
    return

addBinding
  name: 'html'
  autorun: (c, bindArg) ->
    bindArg.element.html bindArg.getVmValue()
    return

Add value, text, and html

Add value, text, and html


AuthModalView = require '../../../../components/auth_modal/view.coffee'
{ templateMap } = require '../../../../components/auth_modal/maps.coffee'
mediator = require '../../../../lib/mediator.coffee'

templateMap['phone_number'] = -> require('./templates/phone_number.jade') arguments...

module.exports = class InquireViaPhoneModalView extends AuthModalView
  template: ->
    templateToRender = if sd.CURRENT_USER then 'phone_number' else @state.get('mode')
    templateMap[templateToRender] arguments...

  onSubmitSuccess: (model, response, options) =>
    analyticsHooks.trigger "auth:#{@state.get 'mode'}"
    @reenableForm null, reset: false

    if response.error?
      mediator.trigger 'auth:error', _s.capitalize response.error
    else
      Cookies.set('destination', @destination, expires: 60 * 60 * 24) if @destination

      switch @state.get('mode')
        when 'login'
          Cookies.set('signed_in', true, expires: 60 * 60 * 24 * 7)
        when 'register'
          mediator.trigger 'auth:sign_up:success'
        when 'forgot'
          mediator.trigger 'auth:change:mode', 'reset'

      @state.set 'mode', 'phone_number'

AuthModalView = require '../../../../components/auth_modal/view.coffee'
{ templateMap } = require '../../../../components/auth_modal/maps.coffee'
mediator = require '../../../../lib/mediator.coffee'

templateMap['phone_number'] = -> require('./templates/phone_number.jade') arguments...

module.exports = class InquireViaPhoneModalView extends AuthModalView
  initialize: (options) ->
    super
    mediator.on 'modal:closed', @refreshPageIfLoggedIn

  template: ->
    templateToRender = if sd.CURRENT_USER then 'phone_number' else @state.get('mode')
    templateMap[templateToRender] arguments...

  refreshPageIfLoggedIn: =>
    if @loggedIn
      location.reload()

  onSubmitSuccess: (model, response, options) =>
    @loggedIn = true

    analyticsHooks.trigger "auth:#{@state.get 'mode'}"
    @reenableForm null, reset: false

    if response.error?
      mediator.trigger 'auth:error', _s.capitalize response.error
    else
      Cookies.set('destination', @destination, expires: 60 * 60 * 24) if @destination

      switch @state.get('mode')
        when 'login'
          Cookies.set('signed_in', true, expires: 60 * 60 * 24 * 7)
        when 'register'
          mediator.trigger 'auth:sign_up:success'
        when 'forgot'
          mediator.trigger 'auth:change:mode', 'reset'

      @state.set 'mode', 'phone_number'

Refresh the whole page after logging in on the Inquire via Phone modal


`import BaseRoute from './_base'`
`import ajax from 'ic-ajax'`

Route = BaseRoute.extend
  model: ->
    mePath = '/api/me'

    # return the current user as the model if authenticated, otherwise a blank object
    new Ember.RSVP.Promise (resolve, reject) =>
      ajax
          url: mePath
          dataType: 'json'
        .then (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)
            ajax
              url: mePath
              method: 'post'
          ,
            (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'
        return false
      else
        return true
`export default Route`

`import BaseRoute from './_base'`
`import ajax from 'ic-ajax'`

Route = BaseRoute.extend
  model: ->
    mePath = '/api/me'

    # return the current user as the model if authenticated, otherwise a blank object
    new Ember.RSVP.Promise (resolve, reject) =>
      ajax
          url: mePath
          dataType: 'json'
        .then (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
              ajax
                url: mePath
                method: 'post'
            resolve(user)
          ,
            (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'
        return false
      else
        return true
`export default Route`

Update user only on successful signin

Update user only on successful signin


React = require 'react'
BS = require 'react-bootstrap'
{CourseStore} = require '../../flux/course'

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

  contextTypes:
    router: React.PropTypes.func

  getDefaultProps: ->
    bsStyle: 'primary'

  propTypes:
    courseId:  React.PropTypes.string
    chapterId: React.PropTypes.number
    sectionId: React.PropTypes.number
    page:      React.PropTypes.string
    unstyled:  React.PropTypes.bool
    bsStyle:   React.PropTypes.string

  render: ->
    courseId = @props.courseId or @context.router.getCurrentParams().courseId
    # the router is smart enough to figure out which props are present and return the best route
    linkType = if @props.page then 'viewReferenceBookPage' else
      if @props.section then 'viewReferenceBookSection' else 'viewReferenceBook'
    course = CourseStore.get(@props.courseId)
    link = @context.router.makeHref( linkType, bookId: course.book_id, cnxId: @props.page, section:@props.section )
    linkProps = {target:'_blank', className:'view-reference-guide', href: link}
    text = @props.children or 'Browse the Book'
    if @props.unstyled
      <a {...linkProps}>{text}</a>
    else
      <BS.Button bsStyle={@props.bsStyle} {...linkProps}>{text}</BS.Button>

React = require 'react'
BS = require 'react-bootstrap'
{CourseStore} = require '../../flux/course'

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

  contextTypes:
    router: React.PropTypes.func

  getDefaultProps: ->
    bsStyle: 'primary'

  propTypes:
    courseId:  React.PropTypes.string
    chapterId: React.PropTypes.number
    sectionId: React.PropTypes.number
    page:      React.PropTypes.string
    unstyled:  React.PropTypes.bool
    bsStyle:   React.PropTypes.string

  render: ->
    course = CourseStore.get(
      @props.courseId or @context.router.getCurrentParams().courseId
    )
    # the router is smart enough to figure out which props are present and return the best route
    linkType = if @props.page then 'viewReferenceBookPage' else
      if @props.section then 'viewReferenceBookSection' else 'viewReferenceBook'
    link = @context.router.makeHref( linkType, bookId: course.book_id, cnxId: @props.page, section:@props.section )
    linkProps = {target:'_blank', className:'view-reference-guide', href: link}
    text = @props.children or 'Browse the Book'
    if @props.unstyled
      <a {...linkProps}>{text}</a>
    else
      <BS.Button bsStyle={@props.bsStyle} {...linkProps}>{text}</BS.Button>

Read book_id from course and use that for link


React = require 'react'

NewCourseRegistration = require './new-registration'
ModifyCourseRegistration = require './modify-registration'

UserStatus = require '../user/status-mixin'
Course = require './model'

CourseRegistration = React.createClass

  propTypes:
    collectionUUID: React.PropTypes.string.isRequired

  mixins: [UserStatus]

  render: ->
    course = @getUser().getCourse(@props.collectionUUID)
    body = if course?.isRegistered()
      <ModiyCourseRegistration {...@props} />
    else
      <NewCourseRegistration {...@props} />

    <div className="row">
      {body}
    </div>

module.exports = CourseRegistration

React = require 'react'

NewCourseRegistration = require './new-registration'
ModifyCourseRegistration = require './modify-registration'

UserStatus = require '../user/status-mixin'
Course = require './model'

CourseRegistration = React.createClass

  propTypes:
    collectionUUID: React.PropTypes.string.isRequired

  mixins: [UserStatus]

  render: ->
    course = @getUser().getCourse(@props.collectionUUID)
    body = if course?.isRegistered()
      <ModifyCourseRegistration {...@props} course={course} />
    else
      <NewCourseRegistration {...@props} />

    <div className="row">
      {body}
    </div>

module.exports = CourseRegistration

Modify needs to know current course

Modify needs to know current course


'global':
  'editor':
    'showInvisibles': true
    'preferredLineLength': 120
    'softWrapped': true
    'showIndentGuide': true
    'fontSize': 12
    'invisibles': {}
  'core':
    'disabledPackages': [
      'metrics'
    ]
    'themes': [
      'atom-dark-ui'
      'glitterbomb'
    ]
  'exception-reporting':
    'userId': '2f6c1fe2-cfdb-87de-4712-11b4b505fed4'
  'welcome':
    'showOnStartup': false
  'atom-beautify':
    '_analyticsUserId': '1d4703af-c0bf-4e61-8894-f8865aaabf2b'

'global':
  'editor':
    'showInvisibles': true
    'preferredLineLength': 120
    'softWrapped': true
    'showIndentGuide': true
    'fontSize': 12
    'invisibles': {}
  'core':
    'disabledPackages': [
      'metrics'
    ]
  'exception-reporting':
    'userId': '2f6c1fe2-cfdb-87de-4712-11b4b505fed4'
  'welcome':
    'showOnStartup': false
  'atom-beautify':
    '_analyticsUserId': '1d4703af-c0bf-4e61-8894-f8865aaabf2b'

Remove glitterbomb as default template in atom


# App
# ===
# Highest-level application script.

# Dependencies
# ------------
# Configure RequireJS. Dependency references default to the `lib` directory.
# We're using CoffeeScript, so our `app` package code is in the compiled js
# directory `release`.
requirejs.config
  baseUrl: 'lib'
  paths:
    app: '../release'
requirejs [
  'phaser'
  'app/game'
],
(Phaser, MorningStroll) ->

  'use strict'

  # Global flags
  # ------------

  window.DEBUG = on

  # Main
  # ----

  # Run our game.
  game = new MorningStroll()
  game.start()

  # Debug
  # -----
  # Block of additions for debugging the app. Exposes classes and instances as
  # globals.
  if window.DEBUG is on
    window.Phaser = Phaser
    window.game = game
    console.info game

# App
# ===
# Highest-level application script.

# Dependencies
# ------------
# Configure RequireJS. Dependency references default to the `lib` directory.
# We're using CoffeeScript, so our `app` package code is in the compiled js
# directory `release`.
requirejs.config
  baseUrl: 'lib'
  paths:
    app: '../release'
  shim:
    phaser:
      exports: 'Phaser'
requirejs [
  'phaser'
  'app/game'
],
(Phaser, MorningStroll) ->

  'use strict'

  # Global flags
  # ------------

  window.DEBUG = on

  # Main
  # ----

  # Run our game.
  game = new MorningStroll()
  game.start()

  # Debug
  # -----
  # Block of additions for debugging the app. Exposes classes and instances as
  # globals.
  if window.DEBUG is on
    window.Phaser = Phaser
    window.game = game
    console.info game

Fix AMD and Phaser integration.

Fix AMD and Phaser integration.


# Let people know of emergent / constant questions
#
# hot topic anything = whatever you want to rememeber
# forget hot topic anything
# hot topics

module.exports = (robot) ->
  robot.brain.data.hot_topics or= {}

  robot.respond /hot topic (.*) = (.*)/i, (msg) ->
    robot.brain.data.hot_topics[msg.match[1].toLowerCase()] = msg.match[2]
    msg.send "OK I'll remember that and let people know"

  robot.respond /forget hot topic (.*)/i, (msg) ->
    if robot.brain.data.hot_topics[msg.match[1].toLowerCase()]?
      delete robot.brain.data.hot_topics[msg.match[1].toLowerCase()]
      msg.send "OK forgot it."
    else
      msg.send "Didn't know anything about that anyway."

  robot.respond /hot topics/i, (msg) ->
    robot.brain.data.hot_topics or= {}
    memories = for phrase, memory of robot.brain.data.hot_topics
      "#{phrase} -> #{memory}"
    msg.send memories.join("\n")

  robot.hear /(.+)/i, (msg) ->
    text = msg.message.text
    unless text.match 'hot topic'
      memories = for phrase, memory of robot.brain.data.hot_topics when text.toLowerCase().match(phrase)
        memory
      msg.send memories.join("\n")

# Let people know of emergent / constant questions
#
# hot topic anything = whatever you want to rememeber
# forget hot topic anything
# hot topics

module.exports = (robot) ->
  robot.brain.data.hot_topics or= {}

  robot.respond /hot topic (.*) = (.*)/i, (msg) ->
    robot.brain.data.hot_topics[msg.match[1].toLowerCase()] = msg.match[2]
    msg.send "OK, I'll remember that and let people know."

  robot.respond /forget hot topic (.*)/i, (msg) ->
    if robot.brain.data.hot_topics[msg.match[1].toLowerCase()]?
      delete robot.brain.data.hot_topics[msg.match[1].toLowerCase()]
      msg.send "OK, forgot it."
    else
      msg.send "Didn't know anything about that anyway."

  robot.respond /hot topics/i, (msg) ->
    robot.brain.data.hot_topics or= {}
    memories = for phrase, memory of robot.brain.data.hot_topics
      "#{phrase} -> #{memory}"
    msg.send memories.join("\n")

  robot.hear /(.+)/i, (msg) ->
    text = msg.message.text
    check = (text, phrase) ->
      r = phrase.match /^\/(.*)\/$/
      if r
        phrase = RegExp(r[1], 'i')
      !!text.toLowerCase().match(phrase)

    unless text.match 'hot topic'
      memories = for phrase, memory of robot.brain.data.hot_topics when check(text, phrase)
        memory
      msg.send memories.join("\n")

Allow regexp in hot topics

Allow regexp in hot topics


dgram = require "dgram"

class ListenServer
  passphrase: ''
  handleMessage: (msg, rinfo) ->
    try
      data = JSON.parse(msg)
      if data and @checkPassphrase(data)
        @incomingData(data)

  checkPassphrase: (data) ->
    if @passphrase == ''
      true
    else
      data.passphrase and data.passphrase == @passphrase

  incomingData: (data) ->
    if data.objectType
      @mongoConnection.collection "fnf-#{data.objectType}", (err, conn) ->
        coll.insert data, {safe:true}, (err) ->

  start: (port) ->
    server = dgram.createSocket("udp4")
    server.on("listening", @onSocketListen)
    server.on("message", @onSocketMessage)
    server.listenServer = this
    server.bind(port)
    @server = server
    this

  stop: ->
    @server.close()

  onSocketListen: ->
    console.log "server listening #{@address().address}:#{@address().port}"

  onSocketMessage: (msg, rinfo) ->
    # Handle the message in the scope of ListenServer, not the Socket
    # This makes it easier to test by spying/stubbing handleMessage instead
    @listenServer.handleMessage(msg, rinfo)

module.exports = ListenServer

dgram = require "dgram"

class ListenServer
  passphrase: ''
  handleMessage: (msg, rinfo) ->
    try
      data = JSON.parse(msg)
      if data and @checkPassphrase(data)
        @incomingData(data)

  checkPassphrase: (data) ->
    if @passphrase == ''
      true
    else
      data.passphrase and data.passphrase == @passphrase

  incomingData: (data) ->
    if data.objectType
      @mongoConnection.collection "fnf-#{data.objectType}", (err, conn) ->
        coll.insert data, {safe:true}, (err) ->

  start: (port) ->
    server = dgram.createSocket("udp4")
    # server.on("listening", @onSocketListen)
    server.on("message", @onSocketMessage)
    server.listenServer = this
    server.bind(port)
    @server = server
    this

  stop: ->
    @server.close()

  # onSocketListen: ->
  #   console.log "server listening #{@address().address}:#{@address().port}"

  onSocketMessage: (msg, rinfo) ->
    # Handle the message in the scope of ListenServer, not the Socket
    # This makes it easier to test by spying/stubbing handleMessage instead
    @listenServer.handleMessage(msg, rinfo)

module.exports = ListenServer

Comment out listening event for now

Comment out listening event for now


# fillOptions = () ->
#   $('#selectLocation').

Template.locationEdit.created = () ->
  Meteor.call 'getOptions', (error, result) ->
    locationsData = JSON.parse result
    locations = locationsData.locations
    for location in locations
      $('#selectLocation').append '<option>'+location.name+'</option>'

Template.locationEdit.events
  'submit form': (e) ->
    e.preventDefault()

    locationProperty = $(e.target).find('[name=location]').val()

    Meteor.users.update {_id: Meteor.userId()}, {$set: {"profile.location": locationProperty}}, (error) ->
      if error
        console.log 'Error!'

      else
        alert 'Update Successful!'

fillOptions = (name) ->
  $('#selectLocation').append '<option>' + name + '</option>'

Template.locationEdit.created = () ->
  Meteor.call 'getOptions', (error, result) ->
    locationsData = JSON.parse result
    locations = locationsData.locations
    for location in locations
      fillOptions location.name

Template.locationEdit.events
  'submit form': (e) ->
    e.preventDefault()

    locationProperty = $(e.target).find('[name=location]').val()

    Meteor.users.update {_id: Meteor.userId()}, {$set: {"profile.location": locationProperty}}, (error) ->
      if error
        console.log 'Error!'

      else
        alert 'Update Successful!'

Add fillingOptions as its own method

Add fillingOptions as its own method


# medellin coords 6.29, -75.54
map_center = new google.maps.LatLng(19.311143,-39.199219)
marker = null
map = null

initialize_map = ->
  mapOptions =
    zoom: 2,
    scrollwheel: false,
    mapTypeId: google.maps.MapTypeId.ROADMAP,
    center: map_center

  map = new google.maps.Map(document.getElementById('mapa'), mapOptions)
  loadUsersData()

loadUsersData = ->
  $.getJSON '/users_map.json', (data) ->
    $.each data, (key, value) ->
      user_location = new google.maps.LatLng(value.latitude, value.longitude)
      addMarker(user_location)

addMarker = (place) ->
  marker = new google.maps.Marker
    map: map,
    draggable:true,
    animation: google.maps.Animation.DROP,
    position: place

  google.maps.event.addListener(marker, 'click', bounceOnce)

bounceOnce = ->
  marker.setAnimation(google.maps.Animation.BOUNCE)
  setInterval(stopBounce, 1000)

stopBounce = ->
  marker.setAnimation(null)

toggleBounce = ->
  if marker.getAnimation() != null
    marker.setAnimation(null)
  else
    marker.setAnimation(google.maps.Animation.BOUNCE)

$(document).ready ->
  initialize_map()

# medellin coords 6.29, -75.54
map_center = new google.maps.LatLng(19.311143,-39.199219)
marker = null
map = null

initialize_map = ->
  mapOptions =
    zoom: 2,
    scrollwheel: false,
    draggable: true,
    panControl: true,
    mapTypeId: google.maps.MapTypeId.ROADMAP,
    center: map_center

  map = new google.maps.Map(document.getElementById('mapa'), mapOptions)
  loadUsersData()

loadUsersData = ->
  $.getJSON '/users_map.json', (data) ->
    $.each data, (key, value) ->
      user_location = new google.maps.LatLng(value.latitude, value.longitude)
      addMarker(user_location)

addMarker = (place) ->
  marker = new google.maps.Marker
    map: map,
    draggable:true,
    animation: google.maps.Animation.DROP,
    position: place

  google.maps.event.addListener(marker, 'click', bounceOnce)

bounceOnce = ->
  marker.setAnimation(google.maps.Animation.BOUNCE)
  setInterval(stopBounce, 1000)

stopBounce = ->
  marker.setAnimation(null)

toggleBounce = ->
  if marker.getAnimation() != null
    marker.setAnimation(null)
  else
    marker.setAnimation(google.maps.Animation.BOUNCE)

$(document).ready ->
  initialize_map()

Add additional controls to the index page Map


loadPpt = ->
  pptData = StrutBuilder.build(JSON.parse(localStorage.regions))
    #[
      #{ #slide 1
        #start: 0,
        #end: 2.5,
        #texts: [
                 #{text: 'Hum', position: 1.2},
                 #{text: 'bewafaa', position: 1.7}
               #]
      #},
      #{ #slide 2
        #start: 2.5,
        #end: 4.3,
        #texts: [
                 #{text: 'Hargiz', position: 3.3},
                 #{text: 'na they', position: 3.8}
               #]
      #}
    #]
  localStorage['strut-presentation-unnamed.strut'] = JSON.stringify(pptData)
  $('#editor').attr('src', '/editor/')

$ ->
  $('#ppt-loader').on 'click', loadPpt

loadPpt = ->
  pptData = StrutBuilder.build(JSON.parse(localStorage.regions))
    #[
      #{ #slide 1
        #start: 0,
        #end: 2.5,
        #texts: [
                 #{text: 'Hum', position: 1.2},
                 #{text: 'bewafaa', position: 1.7}
               #]
      #},
      #{ #slide 2
        #start: 2.5,
        #end: 4.3,
        #texts: [
                 #{text: 'Hargiz', position: 3.3},
                 #{text: 'na they', position: 3.8}
               #]
      #}
    #]
  localStorage['Strut_sessionMeta'] = '{"generator_index":0,"lastPresentation":"presentation-unnamed.strut"}'
  localStorage['strut-presentation-unnamed.strut'] = JSON.stringify(pptData)
  $('#editor').attr('src', '/editor/')

$ ->
  $('#ppt-loader').on 'click', loadPpt

Add strut session initial data, for fixing first-load bug


StockStore = require './stock_store.coffee'

ArticleStore = new StockStore(
  sortKey: 'title'
  sortAsc: true
  descKeys:
    character_sum: true
    view_count: true
  modelKey: 'article'
)

module.exports = ArticleStore.store

StockStore = require './stock_store.coffee'

ArticleStore = new StockStore(
  sortKey: 'character_sum'
  descKeys:
    character_sum: true
    view_count: true
  modelKey: 'article'
)

module.exports = ArticleStore.store

Change default sort to character_sum for Articles


$ = require 'jquery'
FinishGame =
  submitResults: (state) ->
    window.onbeforeunload = ->
    gameParams = {rounds: state.round.get('rounds').toJS()}
    gameId = state.game.get('id')
    $.ajax
      url: "/games/#{gameId}/finish"
      data: { game: gameParams }
      method: 'PUT'
      headers:
        'X-CSRF-Token': localStorage.getItem('csrf-token')
    .done (response) ->
      window.location = response.redirectUrl
    .fail ->
      window.onbeforeunload = -> 'Your game has not been saved!'
      alert 'Error finishing game!'

module.exports = FinishGame

$ = require 'jquery'
FinishGame =
  submitResults: (state) ->
    window.onbeforeunload = ->
    $('#finish-game-button').html('Finishing Game...')
    $('#finish-game-button').attr('disabled', true)

    gameParams = {rounds: state.round.get('rounds').toJS()}
    gameId = state.game.get('id')
    $.ajax
      url: "/games/#{gameId}/finish"
      data: { game: gameParams }
      method: 'PUT'
      headers:
        'X-CSRF-Token': localStorage.getItem('csrf-token')
    .done (response) ->
      window.location = response.redirectUrl
    .fail ->
      window.onbeforeunload = -> 'Your game has not been saved!'
      alert 'Error finishing game!'

module.exports = FinishGame

Disable finish game on click

Disable finish game on click


#= require trix/utilities/object

class Trix.Attachment extends Trix.Object
  constructor: (@file) ->
    super

Call super so we get an id

Call super so we get an id


'use strict'

angular.module('ccApp.controllers')
.controller 'MainCtrl', ($scope, _, questions, scores) ->

  questionsByCategory = _.groupBy questions, (question) -> question.tags[0]

  $scope.questions = questions
  $scope.questionGroups = questionsByCategory
  $scope.sections = _.keys(questionsByCategory)

  $scope.totalsPerSection = scores.totals(questions)
  $scope.resultsPerSection = scores.userScores(questions)

  $scope.generateReport = () ->
    $scope.resultsPerSection = scores.userScores(questions)

'use strict'

angular.module('ccApp.controllers')
.controller 'MainCtrl', ($scope, _, questions, scores) ->

  questionsByCategory = _.groupBy questions, (question) -> question.tags[0]

  $scope.questions = questions
  $scope.questionGroups = questionsByCategory
  $scope.sections = _.keys(questionsByCategory)

  $scope.totalsPerSection = scores.totals(questions)

  updateResults = () ->
    $scope.resultsPerSection = scores.userScores(questions)

  $scope.$watch(
    'questions',
    updateResults,
    true
  )

  $scope.generateReport = () ->
    # TODO: add report generation

Add watch on questions, so that scores are updated in real time


PEG = require 'pegjs'
{fs} = require 'atom'
grammarSrc = fs.readFileSync(require.resolve('./snippet-body.pegjs'), 'utf8')
module.exports = PEG.buildParser(grammarSrc, trackLineAndColumn: true)

try
  parser = require './snippet-body'
catch
  {fs} = require 'atom'
  PEG = require 'pegjs'

  grammarSrc = fs.readFileSync(require.resolve('./snippet-body.pegjs'), 'utf8')
  parser = PEG.buildParser(grammarSrc, trackLineAndColumn: true)

module.exports = parser

Use pre-compiled parser when available

Use pre-compiled parser when available


module.exports =
  config:
    autocompleteBrackets:
      type: 'boolean'
      default: true
    autocompleteSmartQuotes:
      type: 'boolean'
      default: true
    wrapSelectionsInBrackets:
      type: 'boolean'
      default: true

  activate: ->
    atom.workspace.observeTextEditors (editor) ->
      editorElement = atom.views.getView(editor)
      BracketMatcherView = require './bracket-matcher-view'
      new BracketMatcherView(editor, editorElement)
      BracketMatcher = require './bracket-matcher'
      new BracketMatcher(editor, editorElement)

BracketMatcherView = null
BracketMatcher = null

module.exports =
  config:
    autocompleteBrackets:
      type: 'boolean'
      default: true
    autocompleteSmartQuotes:
      type: 'boolean'
      default: true
    wrapSelectionsInBrackets:
      type: 'boolean'
      default: true

  activate: ->
    atom.workspace.observeTextEditors (editor) ->
      editorElement = atom.views.getView(editor)

      BracketMatcherView ?= require './bracket-matcher-view'
      new BracketMatcherView(editor, editorElement)

      BracketMatcher ?= require './bracket-matcher'
      new BracketMatcher(editor, editorElement)

Use ?= to assign deferred requires

:art: Use ?= to assign deferred requires


class Water.TreesController extends Backbone.Router
  routes:
    "/tree/*path": "fetch_tree"
    "/blob/*path": "fetch_blob"
    "*anything": "root"

  fetch_tree: (path) ->
    @fetcher.fetch("trees", path)
  fetch_blob: (path) ->
    @fetcher.fetch("blobs", path)
  root: () ->
    @fetcher.fetch("trees", "")

# Takes care of routing.
# Relays data to breadcrumbs and tree_fetcher models

class Water.TreesController extends Backbone.Router
  initialize: (params) =>
    @fetcher = params.fetcher
    @breadcrumbs = params.breadcrumbs
  routes:
    "/tree/*path": "fetch_tree"
    "/blob/*path": "fetch_blob"
    "*anything": "root"

  fetch_tree: (path) ->
    @breadcrumbs.path(path)
    @fetcher.fetch("trees", path)
  fetch_blob: (path) ->
    @breadcrumbs.push(["/blob"].concat(path.split("/")))
    @fetcher.fetch("blobs", path)
  root: () ->
    @breadcrumbs.root()
    @fetcher.fetch("trees", "")

Add breadcrumb methods in router

Add breadcrumb methods in router


###
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# File: modules/projects.coffee
###

module = angular.module("taigaProject", [])

Add copiright and license terms

Add copiright and license terms


class Utils

    getHTMLTitleFromHistoryFragment: (fragment) ->
        _.capitalize(fragment.split('\/').join(' '))

    stringJSON: (object) ->
        JSON.stringify object, null, '    '

    viewJSON: (object) ->
        vex.dialog.alert
            contentCSS:
                width: 800
            message: "<pre>#{ utils.stringJSON object }</pre>"

    getAcrossCollections: (collectionStrings, id) ->
        model = undefined
        _.each collectionStrings, (collectionString) ->
            collection = app.collections[collectionString]
            model = collection.get(id) ? model
        return model

module.exports = new Utils

class Utils

    getHTMLTitleFromHistoryFragment: (fragment) ->
        _.capitalize(fragment.split('\/').join(' '))

    stringJSON: (object) ->
        JSON.stringify object, null, '    '

    viewJSON: (object) ->
        objectClone = _.extend {}, object
        delete objectClone.JSONString
        vex.dialog.alert
            contentCSS:
                width: 800
            message: "<pre>#{ utils.stringJSON objectClone }</pre>"

    getAcrossCollections: (collectionStrings, id) ->
        model = undefined
        _.each collectionStrings, (collectionString) ->
            collection = app.collections[collectionString]
            model = collection.get(id) ? model
        return model

module.exports = new Utils

Remove JSONString property when displaying JSON


$ ->
  scan_request_option_id = '#entire_no'
  scan_request_container_class = '#holding-request-option-offsite-scan'
  # Select scan request if any of the text inputs have a value
  $(document).on 'keypress', scan_request_container_class + ' fieldset input', ->
    $(this).closest('fieldset').prev('label').find('input').first().prop('checked', true)
  # Clear the text inputs for scan requests if option is unselected
  $(document).on 'change', 'input[type="radio"]', ->
    if !$(scan_request_option_id).is(':checked')
      $(scan_request_container_class + ' fieldset input').val('')

$ ->
  scan_request_option_id = '#entire_no'
  scan_request_container_class = '[id^=holding-request-option][id$=-scan]'
  # Select scan request if any of the text inputs have a value
  $(document).on 'keypress', scan_request_container_class + ' fieldset input', ->
    $(this).closest('fieldset').prev('label').find('input').first().prop('checked', true)
  # Clear the text inputs for scan requests if option is unselected
  $(document).on 'change', 'input[type="radio"]', ->
    if !$(scan_request_option_id).is(':checked')
      $(scan_request_container_class + ' fieldset input').val('')

Change holding request JS to select scans regardless of holding status div id


commandDisposable = null
grammarListView = null
grammarStatusView = null

module.exports =
  config:
    showOnRightSideOfStatusBar:
      type: 'boolean'
      default: true

  activate: ->
    commandDisposable = atom.commands.add('atom-text-editor', 'grammar-selector:show', createGrammarListView)

  deactivate: ->
    commandDisposable?.dispose()
    commandDisposable = null

    grammarStatusView?.destroy()
    grammarStatusView = null

    grammarListView?.destroy()
    grammarListView = null

  consumeStatusBar: (statusBar) ->
    GrammarStatusView = require './grammar-status-view'
    grammarStatusView = new GrammarStatusView().initialize(statusBar)
    grammarStatusView.attach()

createGrammarListView = ->
  unless grammarListView?
    GrammarListView = require './grammar-list-view'
    grammarListView = new GrammarListView()
  grammarListView.toggle()

commandDisposable = null
grammarListView = null
grammarStatusView = null

module.exports =
  config:
    showOnRightSideOfStatusBar:
      type: 'boolean'
      default: true
      description: 'Show the active pane item\'s langauge on the right side of Atom\'s status bar, instead of the left.'

  activate: ->
    commandDisposable = atom.commands.add('atom-text-editor', 'grammar-selector:show', createGrammarListView)

  deactivate: ->
    commandDisposable?.dispose()
    commandDisposable = null

    grammarStatusView?.destroy()
    grammarStatusView = null

    grammarListView?.destroy()
    grammarListView = null

  consumeStatusBar: (statusBar) ->
    GrammarStatusView = require './grammar-status-view'
    grammarStatusView = new GrammarStatusView().initialize(statusBar)
    grammarStatusView.attach()

createGrammarListView = ->
  unless grammarListView?
    GrammarListView = require './grammar-list-view'
    grammarListView = new GrammarListView()
  grammarListView.toggle()

Add description for config setting

Add description for config setting


module.exports = s =

  # Convenience method: wrap services in complete middleware stack
  # accepts {serviceName: serviceDef}
  # returns {serviceName: serviceDef} (wrapped)
  create: (defs, jargon=[], policy=[], resolvers={}) ->
    services = s.process defs, jargon
    withPolicy = s.applyPolicy services, policy
    final = s.applyDependencies withPolicy, resolvers

    return final

  # loads services from the file system (assumed to be in separate files)
  # accepts (serviceLocation)
  # returns {serviceName: serviceDef}
  load: require './getServices'

  # processes service definitions into functions
  # accepts (services, jargon)
  # returns {serviceName: service}
  process: require './wrapServicesInMiddleware'

  # wraps services with access/lookup policy
  # accepts (services, policy)
  # returns {serviceName: wrappedService}
  applyPolicy: require './applyPolicy'

  # looks up and requires dependencies according to
  # the resolvers data structure.
  # accepts (services, resolvers)
  # returns {serviceName: wrappedService}
  applyDependencies: require './applyDependencies'

  # prints out the stack of filters applied to each service
  # accepts (services)
  # returns {serviceName: filterStack}
  print: require './printFilters'

  # exposes the 'graph' submodule, which includes functions to
  # take a set of services and return information about the graphs
  # graphs induced by the various dependency types, especially
  # the 'services' dependencyType.
  graph: require './graph'

  # Export 'errors' module for extensions to depend upon.
  errors: = require './errors'

module.exports = s =

  # Convenience method: wrap services in complete middleware stack
  # accepts {serviceName: serviceDef}
  # returns {serviceName: serviceDef} (wrapped)
  create: (defs, jargon=[], policy=[], resolvers={}) ->
    services = s.process defs, jargon
    withPolicy = s.applyPolicy services, policy
    final = s.applyDependencies withPolicy, resolvers

    return final

  # loads services from the file system (assumed to be in separate files)
  # accepts (serviceLocation)
  # returns {serviceName: serviceDef}
  load: require './getServices'

  # processes service definitions into functions
  # accepts (services, jargon)
  # returns {serviceName: service}
  process: require './wrapServicesInMiddleware'

  # wraps services with access/lookup policy
  # accepts (services, policy)
  # returns {serviceName: wrappedService}
  applyPolicy: require './applyPolicy'

  # looks up and requires dependencies according to
  # the resolvers data structure.
  # accepts (services, resolvers)
  # returns {serviceName: wrappedService}
  applyDependencies: require './applyDependencies'

  # prints out the stack of filters applied to each service
  # accepts (services)
  # returns {serviceName: filterStack}
  print: require './printFilters'

  # exposes the 'graph' submodule, which includes functions to
  # take a set of services and return information about the graphs
  # graphs induced by the various dependency types, especially
  # the 'services' dependencyType.
  graph: require './graph'

  # Export 'errors' module for extensions to depend upon.
  errors: require './errors'

Fix syntax error in export

Fix syntax error in export


'use strict'

async = require 'async'
sysPath = require 'path'
inflection = require 'inflection'
GeneratedFile = require './generated_file'

getGeneratedFilesPaths = (sourceFile, joinConfig) ->
  sourceFileJoinConfig = joinConfig[inflection.pluralize sourceFile.type] or {}
  Object.keys(sourceFileJoinConfig).filter (generatedFilePath) ->
    checker = sourceFileJoinConfig[generatedFilePath]
    checker sourceFile.path

getFiles = (fileList, config, joinConfig, minifiers) ->
  map = {}
  fileList.files.forEach (file) =>
    paths = getGeneratedFilesPaths file, joinConfig
    paths.forEach (path) =>
      map[path] ?= []
      map[path].push file

  Object.keys(map).map (generatedFilePath) =>
    sourceFiles = map[generatedFilePath]
    fullPath = sysPath.join config.paths.public, generatedFilePath
    new GeneratedFile fullPath, sourceFiles, config, minifiers

# * plugins - hashmap of plugins from package.json.
module.exports = write = (fileList, config, joinConfig, plugins, startTime, callback) ->
  minifiers = plugins.filter (plugin) -> !!plugin.minify
  files = getFiles fileList, config, joinConfig, minifiers
  changed = files.filter (generatedFile) ->
    generatedFile.sourceFiles.some (sourceFile) ->
      sourceFile.cache.compilationTime > startTime
  async.forEach changed, ((file) -> file.write callback), (error) ->
    return callback error if error?
    callback null, changed

'use strict'

async = require 'async'
sysPath = require 'path'
inflection = require 'inflection'
GeneratedFile = require './generated_file'

getGeneratedFilesPaths = (sourceFile, joinConfig) ->
  sourceFileJoinConfig = joinConfig[inflection.pluralize sourceFile.type] or {}
  Object.keys(sourceFileJoinConfig).filter (generatedFilePath) ->
    checker = sourceFileJoinConfig[generatedFilePath]
    checker sourceFile.path

getFiles = (fileList, config, joinConfig, minifiers) ->
  map = {}
  fileList.files.forEach (file) =>
    paths = getGeneratedFilesPaths file, joinConfig
    paths.forEach (path) =>
      map[path] ?= []
      map[path].push file

  Object.keys(map).map (generatedFilePath) =>
    sourceFiles = map[generatedFilePath]
    fullPath = sysPath.join config.paths.public, generatedFilePath
    new GeneratedFile fullPath, sourceFiles, config, minifiers

# * plugins - hashmap of plugins from package.json.
module.exports = write = (fileList, config, joinConfig, plugins, startTime, callback) ->
  minifiers = plugins.filter (plugin) -> !!plugin.minify
  files = getFiles fileList, config, joinConfig, minifiers
  changed = files.filter (generatedFile) ->
    generatedFile.sourceFiles.some (sourceFile) ->
      sourceFile.cache.compilationTime > startTime
  async.forEach changed, ((file, next) -> file.write next), (error) ->
    return callback error if error?
    callback null, changed

Fix calling callback after writing of every file.


spawn = require("child_process").spawn
exec  = require("child_process").exec
logger = require "logger-sharelatex"

module.exports = DockerRunner =
  _docker: 'docker'
  _baseCommand: ['run', '--rm=true', '-t', '-v', '$COMPILE_DIR:/source', '--name=texlive-$PROJECT_ID', 'texlive']

  run: (project_id, command, directory, timeout, callback = (error) ->) ->
    logger.log project_id: project_id, command: command, directory: directory, "running docker command"

    if command[0] != 'latexmk'
      throw 'Invalid command'

    docker_cmd = (arg.replace('$COMPILE_DIR', directory).replace('$PROJECT_ID', project_id) \
      for arg in DockerRunner._baseCommand)
    docker_cmd = docker_cmd.concat (arg.replace('$COMPILE_DIR', '/source') for arg in command.slice(1))

    proc = spawn DockerRunner._docker, docker_cmd, stdio: "inherit", cwd: directory
    timer = setTimeout timeout, () ->
      logger.warn "timeout achieved, stopping docker instance"
      exec 'docker', ['stop', "texlive-#{project_id}"]
      callback 'Compilation failed, timeout'
    proc.on "close", () ->
      clearTimeout timer
      callback()

spawn = require("child_process").spawn
exec  = require("child_process").exec
logger = require "logger-sharelatex"
Settings = require "settings-sharelatex"

module.exports = DockerRunner =
  _docker: 'docker'
  _baseCommand: ['run', '--rm=true', '-t', '-v', '$COMPILE_DIR:/source', '--name=texlive-$PROJECT_ID']

  run: (project_id, command, directory, timeout, callback = (error) ->) ->
    logger.log project_id: project_id, command: command, directory: directory, "running docker command"

    if command[0] != 'latexmk'
      throw 'Invalid command'

    docker_cmd = (arg.replace('$COMPILE_DIR', directory).replace('$PROJECT_ID', project_id) \
      for arg in DockerRunner._baseCommand)
    docker_cmd.push Settings.clsi?.docker?.image or "texlive"
    docker_cmd = docker_cmd.concat (arg.replace('$COMPILE_DIR', '/source') for arg in command.slice(1))

    proc = spawn DockerRunner._docker, docker_cmd, stdio: "inherit", cwd: directory
    timer = setTimeout timeout, () ->
      logger.warn "timeout achieved, stopping docker instance"
      exec 'docker', ['stop', "texlive-#{project_id}"]
      callback {timedout: true}
    proc.on "close", () ->
      clearTimeout timer
      callback()

Implement docker based latex compilation.

Implement a docker based command runner that runs compilation in a docker container, for added isolation and security.

This implementation also supports timeouts.


bindArrows = ->
  Mousetrap.bind 'left', ->
    if jekyll_page_previous? and not window.disable_keyboard_nav
      Turbolinks.visit(jekyll_page_previous)

  Mousetrap.bind 'right', ->
    if jekyll_page_next? and not window.disable_keyboard_nav
      Turbolinks.visit(jekyll_page_next)

# Mousetrap.bind 'up', ->
#   if 'jekyll_page_up' of window
#     window.location.href = jekyll_page_up

Mousetrap.bind 'e d i t o r', ->
  if localStorage.debug_mode is "yes"
    # Disable
    localStorage.debug_mode = "no"
    $('[data-debug-toggle]').hide()
  else
    # Enable
    localStorage.debug_mode = "yes"
    $('[data-debug-toggle]').show()

window.disable_keyboard_nav = false

$(window).load ->
  # DO NOT DO ON EVERY TURBONAV
  if localStorage.debug_mode is "yes"
    $('[data-debug-toggle]').show()

  bindArrows()

document.addEventListener 'turbolinks:visit', ->
  window.jekyll_page_up = null
  window.jekyll_page_previous = null
  window.jekyll_page_next = null

bindArrows = ->
  Mousetrap.bind 'left', ->
    if jekyll_page_previous? and not window.disable_keyboard_nav
      Turbolinks.visit(jekyll_page_previous)

  Mousetrap.bind 'right', ->
    if jekyll_page_next? and not window.disable_keyboard_nav
      Turbolinks.visit(jekyll_page_next)

# Mousetrap.bind 'up', ->
#   if 'jekyll_page_up' of window
#     window.location.href = jekyll_page_up

Mousetrap.bind 'e d i t o r', ->
  if localStorage.debug_mode is "yes"
    # Disable
    localStorage.debug_mode = "no"
    $('[data-debug-toggle]').hide()
  else
    # Enable
    localStorage.debug_mode = "yes"
    $('[data-debug-toggle]').show()

window.disable_keyboard_nav = false

$(window).load ->
  # DO NOT DO ON EVERY TURBONAV
  bindArrows()

$(document).ready ->
  if localStorage.debug_mode is "yes"
    $('[data-debug-toggle]').show()

document.addEventListener 'turbolinks:visit', ->
  window.jekyll_page_up = null
  window.jekyll_page_previous = null
  window.jekyll_page_next = null

Fix debug mode toggle not persisting between turbonav bug


# Add new migrations here.
# I wasn't able to get automatic path globed `require` to work with gulp.
migrations = [
  require "./01_base"
  require "./02_add_initial_values"
]

module.exports =
  migrations: migrations
  update: (data) ->
    for m in migrations
      if m.update
        data = m.update(data)
    data

  latestVersion: ->
    @lastMigration().version

  lastMigration: ->
    _.max migrations, (m) -> m.version

# Add new migrations here.
# I wasn't able to get automatic path globed `require` to work with gulp.
migrations = [
  require "./01_base"
]

module.exports =
  migrations: migrations
  update: (data) ->
    for m in migrations
      if m.update
        data = m.update(data)
    data

  latestVersion: ->
    @lastMigration().version

  lastMigration: ->
    _.max migrations, (m) -> m.version

Fix tests:, rm non-existant migration.

Fix tests:, rm non-existant migration.


class LoginAppsController extends AppController

  KD.registerAppClass this,
    name         : "Login"
    route        : [
      '/:name?/Login'
      '/:name?/Redeem'
      '/:name?/Register'
      '/:name?/Recover'
      '/:name?/ResendToken'
    ]
    hiddenHandle : yes
    labels       : ['Redeem', 'Register', 'Recover', 'ResendToken']
    preCondition :
      condition  : (options, cb)-> cb not KD.isLoggedIn()
      failure    : -> KD.getSingleton('router').handleRoute "/Activity"

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

    options.view    = new LoginView
      testPath      : "landing-login"
    options.appInfo =
      name          : "Login"

    super options, data

  handleQuery: (query) ->

    {currentPath} = KD.getSingleton 'router'
    form = 'login'

    s = (exp, tf)-> if currentPath.match exp then form = tf
    s /\/Login$/       , 'login'
    s /\/Redeem$/      , 'redeem'
    s /\/Register$/    , 'register'
    s /\/Recover$/     , 'recover'
    s /\/ResendToken$/ , 'resendEmail'

    @getView().animateToForm form

class LoginAppsController extends AppController

  KD.registerAppClass this,
    name         : "Login"
    route        : [
      '/:name?/Login'
      '/:name?/Redeem'
      '/:name?/Register'
      '/:name?/Recover'
      '/:name?/ResendToken'
    ]
    hiddenHandle : yes
    labels       : ['Redeem', 'Register', 'Recover', 'ResendToken']
    preCondition :
      condition  : (options, cb)-> cb not KD.isLoggedIn()
      failure    : -> KD.getSingleton('router').handleRoute "/Activity"

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

    options.view    = new LoginView
      testPath      : "landing-login"
    options.appInfo =
      name          : "Login"

    super options, data

  appIsShown: (params)->
    @handleRoute "/#{params.label}"

  handleQuery: (label)->
    {currentPath} = KD.getSingleton 'router'
    @handleRoute currentPath

  handleRoute: (route)->
    form = 'login'
    s = (exp, tf)-> if route.match exp then form = tf
    s /\/Login$/       , 'login'
    s /\/Redeem$/      , 'redeem'
    s /\/Register$/    , 'register'
    s /\/Recover$/     , 'recover'
    s /\/ResendToken$/ , 'resendEmail'

    @utils.defer =>
      @getView().animateToForm form

Login: Handle all type of calling for the app, you can either use router or applicationManager


app = require '../../test.js'
_ = require 'underscore'

module.exports =

  Backbone: app.Backbone

  chai: require 'chai'

  require: (path) ->
    app.require path

app = require '../../test.js'
_ = require 'underscore'

module.exports =

  Backbone: app.Backbone

  chai: require 'chai'

  require: (path) ->
    app.stitchRequire path

Use stitchRequire stitch identifier in test helper


class MainChatPanel extends JView

  constructor:->
    super
      cssClass : 'main-chat-panel visible'

    @registerSingleton "chatPanel", @, yes

    @header = new MainChatHeader
    @conversationList = new ChatConversationListView
    @conversationListController = new ChatConversationListController
      view : @conversationList

  createConversation:(data)->
    # Data includes chatChannel and the conversation
    @conversationListController.addItem data

  viewAppended:->
    @addSubView @header
    @addSubView @conversationList
    @conversationListController.loadItems()

  show:->
    @setClass 'visible'
    @emit 'PanelVisibilityChanged', true

  hide:->
    @unsetClass 'visible'
    @emit 'PanelVisibilityChanged', false

  toggle:->
    @toggleClass 'visible'
    @emit 'PanelVisibilityChanged', @isVisible()

  isVisible:->
    @hasClass 'visible'

class MainChatPanel extends JView

  constructor:->
    super
      cssClass : 'main-chat-panel'

    @registerSingleton "chatPanel", @, yes

    @header = new MainChatHeader
    @conversationList = new ChatConversationListView
    @conversationListController = new ChatConversationListController
      view : @conversationList

    @on 'PanelVisibilityChanged', (visible)=>
      if visible
        @getSingleton('windowController').addLayer @
        @once 'ReceivedClickElsewhere', @bound 'hide'

  createConversation:(data)->
    # Data includes chatChannel and the conversation
    @conversationListController.addItem data

  viewAppended:->
    @addSubView @header
    @addSubView @conversationList
    @conversationListController.loadItems()
    @show()

  show:->
    @setClass 'visible'
    @emit 'PanelVisibilityChanged', true

  hide:->
    @unsetClass 'visible'
    @emit 'PanelVisibilityChanged', false

  toggle:->
    @toggleClass 'visible'
    @emit 'PanelVisibilityChanged', @isVisible()

  isVisible:->
    @hasClass 'visible'

Hide the panel when it looses focues

Hide the panel when it looses focues


ETahi.AdminRoute = ETahi.AuthorizedRoute.extend
  actions:
    viewUserDetails: (user) ->
      @controllerFor('adminJournalUser').set('model', user)
      @render 'userDetailOverlay',
        into: 'application'
        outlet: 'overlay'
        controller: 'adminJournalUser'

    didTransition: ->
      $('html').attr('screen', 'admin')

    willTransition: ->
      $('html').attr('screen', '')

ETahi.AdminRoute = ETahi.AuthorizedRoute.extend
  beforeModel: ->
    Ember.$.ajax '/admin/journals/authorization',
      headers:
        'Tahi-Authorization-Check': true

  actions:
    viewUserDetails: (user) ->
      @controllerFor('adminJournalUser').set('model', user)
      @render 'userDetailOverlay',
        into: 'application'
        outlet: 'overlay'
        controller: 'adminJournalUser'

    didTransition: ->
      $('html').attr('screen', 'admin')

    willTransition: ->
      $('html').attr('screen', '')

Add authorization check on admin route.

Add authorization check on admin route.

Since we're loading the model asynchronously a 403
from the server won't trigger the error state and the
subsequent transition back to the dashboard.


$ ->
  $('#currency').change ->
    $.ajax(
      type: 'POST'
      url: $(this).data('href')
      data:
        currency: $(this).val()
    ).done ->
      window.location.reload()

$ ->
  $('#currency').on 'change', ->
    $.ajax(
      type: 'POST'
      url: $(this).data('href')
      data:
        currency: $(this).val()
    ).done ->
      window.location.reload()

Use jQuery "on" for event hook.

Use jQuery "on" for event hook.


describe 'Custom Element Base', ->
  registerCustomElement = require('../base.min.js')
  {getCustomElement} = require('./helper')

  it 'fires the events in order', ->
    lastRan = null
    element = new (registerCustomElement(getCustomElement({
      name: 'x-custom-element'
      created: -> lastRan = 'created'
      initialize: -> lastRan = 'initialize'
      attached: ->
        expect(lastRan).toBe('initialize')
        lastRan = 'attached'
      detached: -> lastRan = 'detached'
      attributeChanged: -> lastRan = 'attributeChanged'
    })))
    expect(lastRan).toBe('created')
    element.setAttribute('a-b', 'c')
    expect(lastRan).toBe('attributeChanged')
    document.body.appendChild(element)
    expect(lastRan).toBe('attached')
    element.remove()
    expect(lastRan).toBe('detached')

describe 'Custom Element Base', ->
  registerCustomElement = require('../base.min.js')
  {getCustomElement} = require('./helper')

  it 'fires the events in order', ->
    lastRan = null
    element = new (registerCustomElement(getCustomElement({
      name: 'x-spec-event-order'
      created: -> lastRan = 'created'
      initialize: -> lastRan = 'initialize'
      attached: ->
        expect(lastRan).toBe('initialize')
        lastRan = 'attached'
      detached: -> lastRan = 'detached'
      attributeChanged: -> lastRan = 'attributeChanged'
    })))
    expect(lastRan).toBe('created')
    element.setAttribute('a-b', 'c')
    expect(lastRan).toBe('attributeChanged')
    document.body.appendChild(element)
    expect(lastRan).toBe('attached')
    element.remove()
    expect(lastRan).toBe('detached')

  it 'does not fire attributeChanged on config change', ->
    executed = false
    element = new (registerCustomElement(getCustomElement({
      name: 'x-spec-attributechanged-fired'
      attributeChanged: -> executed = true
      config: {asd: {type: Boolean, default: false}}
    })))
    element.setAttribute('asd', false)
    expect(executed).toBe(false)
    element.setAttribute('asd-a', false)
    expect(executed).toBe(true)

:new: Add a spec about attribute change firing


define [
	"base"
], (App) ->
	App.directive "reviewPanelToggle", () ->
		restrict: "E"
		scope:
			onToggle: '&'
			ngModel: '='
			disabled: '=?'
			onDisabledClick: '=?'
		link: (scope) ->
			if !scope.disabled?
				scope.disabled = false
			scope.onChange = (args...) ->
				scope.onToggle({ isOn: scope.localModel })
			scope.handleClick = () ->
				if scope.disabled
					scope.onDisabledClick()
			scope.localModel = scope.ngModel
			scope.$watch "ngModel", (value) ->
				scope.localModel = value

		template: """

	<input id="rp-toggle-{{$id}}" ng-disabled="disabled" type="checkbox" class="rp-toggle-hidden-input" ng-model="localModel" ng-change="onChange()" />

"""

define [
	"base"
], (App) ->
	App.directive "reviewPanelToggle", () ->
		restrict: "E"
		scope:
			onToggle: '&'
			ngModel: '='
			valWhenUndefined: '=?'
			disabled: '=?'
			onDisabledClick: '=?'
		link: (scope) ->
			if !scope.disabled?
				scope.disabled = false
			scope.onChange = (args...) ->
				scope.onToggle({ isOn: scope.localModel })
			scope.handleClick = () ->
				if scope.disabled
					scope.onDisabledClick()
			scope.localModel = scope.ngModel
			scope.$watch "ngModel", (value) ->
				if scope.valWhenUndefined? and !value?
					value = scope.valWhenUndefined
				scope.localModel = value

		template: """

	<input id="rp-toggle-{{$id}}" ng-disabled="disabled" type="checkbox" class="rp-toggle-hidden-input" ng-model="localModel" ng-change="onChange()" />

"""

Support a default value when undefined in the review panel toggles.


TimeEntry = require '../../models/time_entry'
should = require 'should'

makeT = (offset = 10) ->
  t = new TimeEntry
    start: new Date() - 5
    end: new Date() - offset
    duration: 60 * 60
    message: 'message goes here'
    userId: 'someuserID'
    projectId: 'someprojectID'

describe 'TimeEntry Model', ->
  it 'should be able to create a new instance', ->
    t = do makeT
    t.should.not.be.equal undefined
  it 'should validate', ->
    t = do makeT
    start = new Date t.start
    end = new Date t.end
    start.should.be.instanceof Date
    end.should.be.instanceof Date
    t.message.should.be.equal 'message goes here'
    t.validate().should.be.true

TimeEntry = require '../../models/time_entry'
should = require 'should'

makeT = (offset = 10) ->
  t = new TimeEntry
    start: new Date() - 5
    end: new Date() - offset
    message: 'message goes here'
    userId: 'someuserID'
    projectId: 'someprojectID'

describe 'TimeEntry Model', ->
  it 'should be able to create a new instance', ->
    t = do makeT
    t.should.not.be.equal undefined
  it 'should validate', ->
    t = do makeT
    start = new Date t.start
    end = new Date t.end
    start.should.be.instanceof Date
    end.should.be.instanceof Date
    t.message.should.be.equal 'message goes here'
    t.validate().should.be.true

  it 'should not validate', ->
    t = new TimeEntry
      start: 'not a date'
      end: 'not a date either'
      message: 'he he i have invalid dates'
      userId: 'jokster1'
      projectId: 'alternate time continuum'
    t.validate().should.be.false

Add a validation checker for time entry

Add a validation checker for time entry


module.exports = (grunt) ->
  grunt.initConfig
    nodewebkit:
      options:
        build_dir: './builds'
        version: '0.8.3'
        mac: true
        win: true
        linux32: false
        linux64: false
      src: [ './src/**/*' ]

  grunt.loadNpmTasks 'grunt-node-webkit-builder'
  grunt.registerTask 'default', ['nodewebkit']

module.exports = (grunt) ->
  grunt.initConfig
    nodewebkit:
      options:
        build_dir: './builds'
        version: '0.8.4'
        mac: true
        win: true
        linux32: false
        linux64: false
      src: [ './src/**/*' ]

  grunt.loadNpmTasks 'grunt-node-webkit-builder'
  grunt.registerTask 'default', ['nodewebkit']

Update node-webkit version for builder

Update node-webkit version for builder


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

# For more detailed documentation see
# https://atom.io/docs/latest/advanced/keymaps
'.editor':
  'cmd-shift-c': 'coffee-compile:compile'

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

# For more detailed documentation see
# https://atom.io/docs/latest/advanced/keymaps
'.platform-darwin .editor':
  'cmd-shift-c': 'coffee-compile:compile'
'.platform-win32 .editor, .platform-linux .editor':
  'ctrl-alt-b': 'coffee-compile:compile'

Add keybindings for win32 and linux

Add keybindings for win32 and linux


window.GiftCards =
  _bindLookupGiftCard: ->
    $(document).on('submit', '#lookup-redemption-code', (event) ->
      event.preventDefault()
      window.location.href = $(this).attr('action') + '/' + $(this).find('#gift_card_redemption_code').val()
    )

  init: ->
    @_bindLookupGiftCard()

  setAdminDatepicker: () ->
    $(document).ready(() ->
      $(".giftcard-datepicker").datepicker("setDate", new Date($(".giftcard-datepicker").val().split("-")))
      $(".giftcard-datepicker").datepicker("option", "minDate", new Date())
    )

window.GiftCards =
  _bindLookupGiftCard: ->
    $(document).on('submit', '#lookup-redemption-code', (event) ->
      event.preventDefault()
      window.location.href = $(this).attr('action') + '/' + $(this).find('#gift_card_redemption_code').val()
    )

  init: ->
    @_bindLookupGiftCard()

  setAdminDatepicker: () ->
    $(document).ready(() ->
      if $(".giftcard-datepicker").val()
        $(".giftcard-datepicker").datepicker("setDate", new Date($(".giftcard-datepicker").val().split("-")))
        $(".giftcard-datepicker").datepicker("option", "minDate", new Date())
    )

Set datepicker when there is a datepicker on the page


vorpal = require 'vorpal'
chalk = vorpal().chalk
_ = require 'underscore'

awkward = vorpal()
       .delimiter process.env.USER+'@awk'
       .localStorage 'awkward@iostreamer'
       .history 'awkward@iostreamer/history'

global.chalk = chalk
global.awkward = awkward
global._ = _

require('./terminal/main')(awkward)

awkward.exec 'repl'

awkward.show().parse process.argv

vorpal = require 'vorpal'
chalk = vorpal().chalk
_ = require 'underscore'
os = require 'os'

user = process.env.USER
user = process.env.USERNAME if (os.platform() is "win32")

awkward = vorpal()
       .delimiter user + '@awk'
       .localStorage 'awkward@iostreamer'
       .history 'awkward@iostreamer/history'

global.chalk = chalk
global.awkward = awkward
global._ = _

require('./terminal/main')(awkward)

awkward.exec 'repl'

awkward.show().parse process.argv

Fix undefined username in Windows

Fix undefined username in Windows


# Description:
#  Listens for ac webhooks and post in chat
#
# Dependencies:
#   None
#
# Configuration:
#   None
#
# Commands:
#   None
#
# Notes:
#   None
#
# Author:
#   digitalsadhu
module.exports = (robot) ->

  robot.router.get "/hubot/ac-webhooks", (req, res) ->
    robot.send "ac webhooks url hit!"

# Description:
#  Listens for ac webhooks and post in chat
#
# Dependencies:
#   None
#
# Configuration:
#   None
#
# Commands:
#   None
#
# Notes:
#   None
#
# Author:
#   digitalsadhu
module.exports = (robot) ->

  robot.router.post "/hubot/ac-webhooks", (req, res) ->
    console.log req.body
    user = {}
    user.room = process.env['HUBOT_CAMPFIRE_ROOMS'].split(',')[0]
    robot.send user, 'ac webhooks url hit!'
    res.end '{"success":true}'

Add post hook endpoint and console logging


React = require 'react'

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

  render: ->

      •
      •
      •

React = require 'react'

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

  render: ->
    # TODO
    <i className="fa fa-spin fa-spinner"></i>

Simplify loading indicator for now

Simplify loading indicator for now