Template.admin.servers = ->
  servers.find()
Template.admin.events
  "click .servt tr": ->
    Router.go Router.routes["adminServer"].path {id: @_id}
Template.adminServer.events
  "click .sdBtn": ->
    id = @_id
    console.log "Requesting host shutdown for "+id
    Meteor.call "shutdownHost", id, (err, res)->
      if err?
        $.pnotify
          title: "Failed to Shutdown Host"
          text: err.reason
          type: "error"
      else
        $.pnotify
          title: "Command Sent"
          text: "Server told to shut down."
          type: "success"

Template.admin.servers = ->
  servers.find()
Template.admin.events
  "click .servt tr": ->
    Router.go Router.routes["adminServer"].path {id: @_id}
Template.adminServer.events
  "click .sdBtn": ->
    id = @_id
    if !confirm 'Are you sure you want to shut down '+@ip+"?"
      return
    console.log "Requesting host shutdown for "+id
    Meteor.call "shutdownHost", id, (err, res)->
      if err?
        $.pnotify
          title: "Failed to Shutdown Host"
          text: err.reason
          type: "error"
      else
        $.pnotify
          title: "Command Sent"
          text: "Server told to shut down."
          type: "success"

Add confirm for server shutdown

Add confirm for server shutdown


'.editor':
  'ctrl-j': 'symbols-view:toggle-file-symbols'
  'meta-.': 'symbols-view:go-to-declaration'

'body':
  'ctrl-J': 'symbols-view:toggle-project-symbols'

'.editor':
  'meta-r': 'symbols-view:toggle-file-symbols'
  'meta-.': 'symbols-view:go-to-declaration'

'body':
  'meta-R': 'symbols-view:toggle-project-symbols'

Change interferring keybindings when toggeling

Fixes #6


editor = new Voltage.Editor

Deps.autorun ->
  if Meteor.user()
    Meteor.call 'isVoltageAuthorized', (err, authorized) ->
      Session.set 'isVoltageAuthorized', authorized
  else
    Session.set 'isVoltageAuthorized', false

Template.voltagePage.helpers
  isVoltageAuthorized: ->
    editor.renderData()
    Session.get 'isVoltageAuthorized'

Template.voltagePage.rendered = ->
  slug = window.location.pathname.replace(/\//g, '-').slice(1)

  $('.voltage').addClass slug

  options =
    update: (markdown) =>
      page = Page.first name: slug

      if page
        page.update text: markdown
      else
        Page.create
          text: markdown
          name: slug
    html: () =>
      page = Page.first name: slug

      if page
        return marked page.text

      ''
    markdown: () =>
      page = Page.first name: slug

      if page
        return page.text

      ''

  editor.init(options).renderData()

  # anchor fragment support
  hash = document.location.hash.substr(1)
  if hash && !Template.voltagePage.scrolled
    scroller = ->
      $("html, body").stop()

    Meteor.setTimeout(->
      elem = $('#' + hash)
      if elem.length
        scroller().scrollTop(elem.offset().top)
        Template.voltagePage.scrolled = true
    , 0)

Template.voltagePage.destroyed = ->
  delete Template.voltagePage.scrolled

Template.voltageEditor.rendered = ->
  editor.renderEditor()

editor = new Voltage.Editor

Deps.autorun ->
  if Meteor.user()
    Meteor.call 'isVoltageAuthorized', (err, authorized) ->
      Session.set 'isVoltageAuthorized', authorized
  else
    Session.set 'isVoltageAuthorized', false

Template.voltagePage.helpers
  isVoltageAuthorized: ->
    editor.renderData()
    Session.get 'isVoltageAuthorized'

Template.voltagePage.rendered = ->
  slug = window.location.pathname.replace(/\//g, '-').slice(1)

  $('.voltage').addClass slug

  options =
    update: (markdown) =>
      page = Page.first name: slug

      if page
        page.update text: markdown
      else
        Page.create
          text: markdown
          name: slug
    html: () =>
      page = Page.first name: slug

      if page
        return page.html || marked page.text

      ''
    markdown: () =>
      page = Page.first name: slug

      if page
        return page.text

      ''

  editor.init(options).renderData()

  # anchor fragment support
  hash = document.location.hash.substr(1)
  if hash && !Template.voltagePage.scrolled
    scroller = ->
      $("html, body").stop()

    Meteor.setTimeout(->
      elem = $('#' + hash)
      if elem.length
        scroller().scrollTop(elem.offset().top)
        Template.voltagePage.scrolled = true
    , 0)

Template.voltagePage.destroyed = ->
  delete Template.voltagePage.scrolled

Template.voltageEditor.rendered = ->
  editor.renderEditor()

Check for rendered HTML before rendering in client


#!/usr/bin/env coffee

RSS = require 'rss'
escape = require 'escape-html'
request = require 'request'

API_ENDPOINT = 'https://api.imgur.com/3/'

get = (clientId, path, query, cb) ->
  request {
    url: API_ENDPOINT + path
    method: 'get'
    qs: query
    json: true
    headers:
      Authorization: "Client-ID #{ clientId }"
  }, cb

exports.album2rss = (clientId, albumId, cb) ->
  return cb "Must specify a client ID" unless clientId
  return cb "Must specify an album ID" unless albumId

  get clientId, "album/#{ albumId }", null, (err, res, body) ->
    return cb err if err
    return cb String(body?.data?.error) unless body?.success

    feed = new RSS(title: body.data.title, site_url: body.data.link, generator: 'imgur2rss')

    body.data.images.sort (a, b) -> a.datetime - b.datetime

    for image in body.data.images.slice 0, 10
      feed.item
        title: image.title ? image.id
        url: "http://imgur.com/#{ image.id }"
        date: new Date(image.datetime * 1000)
        description: """<img src="#{ image.link }" alt="#{ image.title ? image.id }"/>"""

    cb null, feed.xml(indent: true)

#!/usr/bin/env coffee

RSS = require 'rss'
escape = require 'escape-html'
request = require 'request'

API_ENDPOINT = 'https://api.imgur.com/3/'

get = (clientId, path, query, cb) ->
  request {
    url: API_ENDPOINT + path
    method: 'get'
    qs: query
    json: true
    headers:
      Authorization: "Client-ID #{ clientId }"
  }, cb

exports.album2rss = (clientId, albumId, cb) ->
  return cb "Must specify a client ID" unless clientId
  return cb "Must specify an album ID" unless albumId

  get clientId, "album/#{ albumId }", null, (err, res, body) ->
    return cb err if err
    return cb String(body?.data?.error) unless body?.success

    body.data.images.sort (a, b) -> a.datetime - b.datetime

    feed = new RSS(
      title: body.data.title
      site_url: body.data.link
      generator: 'imgur2rss'
      ttl: 60 * 12
    )

    for image in body.data.images.slice 0, 10
      feed.item
        title: image.title ? image.id
        url: "http://imgur.com/#{ image.id }"
        date: new Date(image.datetime * 1000)
        description: """<img src="#{ image.link }" alt="#{ image.title ? image.id }"/>"""
    cb null, feed.xml(indent: true)

Set feed TTL to 12 hours.

Set feed TTL to 12 hours.


ETahi.FunderController = Ember.ObjectController.extend ETahi.SavesDelayed,
  allAuthors: null
  fundedAuthors: Em.computed.alias('model.authors')
  addingAuthor: null

  initAuthors: (->
    @set('allAuthors', @get('task.paper.authors'))
  ).on('init').observes('task.paper.authors')

  actions:
    funderDidChange: ->
      #saveModel is implemented in ETahi.SavesDelayed
      @send('saveModel')

    startAddingAuthor: ->
      author = @store.createRecord('author', position: 0)
      group.get('authors').pushObject(author)
      @set('addingAuthor', author)

    finishAddingAuthor: ->
      author = @get("addingAuthor")
      if author?.get("firstName") && author?.get("lastName")
        author.save().then =>
          @get('allAuthors').pushObject(author)
          @get('fundedAuthors').pushObject(author)
          @set('addingAuthor', null)

    cancelAddingAuthor: ->
      @get('addingAuthor').deleteRecord()
      @set('addingAuthor', null)

    removeFunder: ->
      @get('model').destroyRecord()

ETahi.FunderController = Ember.ObjectController.extend ETahi.SavesDelayed,
  allAuthors: null
  fundedAuthors: Em.computed.alias('model.authors')
  addingAuthor: null

  initAuthors: (->
    @set('allAuthors', @get('task.paper.authors'))
  ).on('init').observes('task.paper.authors')

  actions:
    funderDidChange: ->
      #saveModel is implemented in ETahi.SavesDelayed
      @send('saveModel')

    startAddingAuthor: ->
      author = @store.createRecord('author', position: 0)
      @set('addingAuthor', author)

    finishAddingAuthor: ->
      author = @get("addingAuthor")
      if author?.get("firstName") && author?.get("lastName")
        author.save().then =>
          @get('allAuthors').pushObject(author)
          @get('fundedAuthors').pushObject(author)
          @set('addingAuthor', null)

    cancelAddingAuthor: ->
      @get('addingAuthor').deleteRecord()
      @set('addingAuthor', null)

    removeFunder: ->
      @get('model').destroyRecord()

Remove unused group from funding disclosure


class GameState
  customers: []
  agents: []

  requestQueues: {}

  tickables: []
  tick: 0

  money: 1000000
  reputation: 0.5
  agent_spawner = AgentSpawner()

  addAgent: (agent) ->
    @agents.append(agent)

  fireAgent: (agent) ->
    @agents.pop(agent)

  calculateReputation: ->
    totalWorth = 0
    totalRep = 0
    for customer in @customers
      totalRep += customer.mood * customer.worth
      totalWorth += customer.worth
    if totalWorth != 0
      @reputation = totalRep / totalWorth

  constructor: ->
    @level = new Level

  numberOfRequests: ->
    requests = 0
    for queue of @requestQueues
      requests += @requestQueues[queue].length()
    requests

  toString: ->
    "Customers: " + @customers.length +
    "\nRequests: " + @numberOfRequests() +
    "\nReputation: " + (@reputation * 100) + "%"

class GameState
  customers: []
  agents: []

  requestQueues: {}

  tickables: []
  tick: 0

  money: 1000000
  reputation: 0.5
  agentSpawner: AgentSpawner()

  addAgent: (agent) ->
    @agents.append(agent)

  fireAgent: (agent) ->
    @agents.pop(agent)

  calculateReputation: ->
    totalWorth = 0
    totalRep = 0
    for customer in @customers
      totalRep += customer.mood * customer.worth
      totalWorth += customer.worth
    if totalWorth != 0
      @reputation = totalRep / totalWorth

  constructor: ->
    @level = new Level

  numberOfRequests: ->
    requests = 0
    for queue of @requestQueues
      requests += @requestQueues[queue].length()
    requests

  toString: ->
    "Customers: " + @customers.length +
    "\nRequests: " + @numberOfRequests() +
    "\nReputation: " + (@reputation * 100) + "%"

Fix agentSpawner declaration in game state


define [
	"base"
], (App) ->
	App.controller "AutoCompileOnboardingController", ($scope) ->
		recompileBtn = angular.element('#recompile')
		popover = angular.element('.onboarding__autocompile')
		{ top, left } = recompileBtn.offset()

		# If pdf panel smaller than recompile button + popover, show to left.
		# Otherwise show to right
		if $scope.ui.pdfWidth < 475
			$scope.placement = 'left'
			popover.offset({
				top: top,
				left: left - popover.width()
			})
		else
			$scope.placement = 'right'
			angular.element('.onboarding__autocompile').offset({
				top: top,
				left: left + recompileBtn.width()
			})

		$scope.dismiss = () ->
			$scope.onboarding.autoCompile = 'dismissed'
			event_tracking.sendMB "shown-autocompile-onboarding"

define [
	"base"
], (App) ->
	App.controller "AutoCompileOnboardingController", ($scope, event_tracking) ->
		recompileBtn = angular.element('#recompile')
		popover = angular.element('.onboarding__autocompile')
		{ top, left } = recompileBtn.offset()

		# If pdf panel smaller than recompile button + popover, show to left.
		# Otherwise show to right
		if $scope.ui.pdfWidth < 475
			$scope.placement = 'left'
			popover.offset({
				top: top,
				left: left - popover.width()
			})
		else
			$scope.placement = 'right'
			angular.element('.onboarding__autocompile').offset({
				top: top,
				left: left + recompileBtn.width()
			})

		$scope.dismiss = () ->
			$scope.onboarding.autoCompile = 'dismissed'
			event_tracking.sendMB "shown-autocompile-onboarding"

Fix event tracking not being injecting

Fix event tracking not being injecting


angular.module('loomioApp').controller 'ProfilePageController', ($rootScope, Records, FormService, $location, AbilityService, ModalService, ChangePictureForm, ChangePasswordForm, DeactivateUserForm, $translate, Session, AppConfig, DeactivationModal) ->
  $rootScope.$broadcast('currentComponent', { page: 'profilePage'})

  @init = =>
    return unless AbilityService.isLoggedIn()
    @user = Session.user()
    $translate.use(@user.locale)
  @init()

  @availableLocales = ->
    AppConfig.locales

  @submit = FormService.submit @, @user,
    flashSuccess: 'profile_page.messages.updated'
    submitFn: Records.users.updateProfile
    successCallback: @init

  @changePicture = ->
    ModalService.open ChangePictureForm

  @changePassword = ->
    ModalService.open ChangePasswordForm

  @deactivateUser = ->
    ModalService.open DeactivationModal

  return

angular.module('loomioApp').controller 'ProfilePageController', ($rootScope, Records, FormService, $location, AbilityService, ModalService, ChangePictureForm, ChangePasswordForm, DeactivateUserForm, $translate, Session, AppConfig, DeactivationModal) ->
  $rootScope.$broadcast('currentComponent', { page: 'profilePage'})

  @init = =>
    return unless AbilityService.isLoggedIn()
    @user = Session.user().clone()
    $translate.use(@user.locale)
    @submit = FormService.submit @, @user,
      flashSuccess: 'profile_page.messages.updated'
      submitFn: Records.users.updateProfile
      successCallback: @init
  @init()

  @availableLocales = ->
    AppConfig.locales

  @changePicture = ->
    ModalService.open ChangePictureForm

  @changePassword = ->
    ModalService.open ChangePasswordForm

  @deactivateUser = ->
    ModalService.open DeactivationModal

  return

Clone user properly for profile page

Clone user properly for profile page


requirejs.config
  baseUrl: '/scripts'
  paths:
    'underscore': '../bower_components/underscore/underscore'
    'jquery': '../bower_components/jquery/jquery'
    'backbone': '../bower_components/backbone/backbone'
    'backbone.routefilter': '../bower_components/backbone-route-filter/backbone-route-filter'
    'backbone.stickit': '../bower_components/backbone.stickit/backbone.stickit'
    'backbone-validtion': '../bower_components/backbone-validation/dist/backbone-validation-amd'
    'backbone.babysitter': '../bower_components/backbone.babysitter/lib/backbone.babysitter'
    'backbone.wreqr': '../bower_components/backbone.wreqr/lib/backbone.wreqr'
    'marionette': '../bower_components/marionette/lib/core/amd/backbone.marionette'
    'bootstrap': '../bower_components/bootstrap/dist/js/bootstrap'

    'jade': '../vendor/scripts/runtime'
  shim:
    'underscore':
      exports: '_'
    'jquery':
      exports: '$'
    'backbone':
      deps: ['underscore', 'jquery', 'bootstrap']
      exports: 'Backbone'
    'backbone.routefilter':
      deps: ['backbone']
    'marionette':
      deps: ['backbone', 'backbone.babysitter', 'backbone.wreqr', 'backbone.routefilter', 'backbone.stickit', 'backbone-validtion']
      exports: 'Marionette'
    'bootstrap':
      deps: ['jquery']
    'templates':
      deps: ['jade']

requirejs.config
  baseUrl: '/scripts'
  paths:
    'underscore': '../bower_components/underscore/underscore'
    'jquery': '../bower_components/jquery/jquery'
    'backbone': '../bower_components/backbone/backbone'
    'backbone.routefilter': '../bower_components/backbone-route-filter/backbone-route-filter'
    'backbone.stickit': '../bower_components/backbone.stickit/backbone.stickit'
    'backbone-validtion': '../bower_components/backbone-validation/dist/backbone-validation-amd'
    'backbone.babysitter': '../bower_components/backbone.babysitter/lib/backbone.babysitter'
    'backbone.wreqr': '../bower_components/backbone.wreqr/lib/backbone.wreqr'
    'marionette': '../bower_components/marionette/lib/core/amd/backbone.marionette'
    'bootstrap': '../bower_components/bootstrap/dist/js/bootstrap'

    'jade': '../vendor/scripts/runtime'
  shim:
    'underscore':
      exports: '_'
    'jquery':
      exports: '$'
    'backbone':
      deps: ['underscore', 'jquery', 'bootstrap']
    'backbone.routefilter':
      deps: ['backbone']
    'marionette':
      deps: ['backbone', 'backbone.babysitter', 'backbone.wreqr', 'backbone.routefilter', 'backbone.stickit', 'backbone-validtion']
    'bootstrap':
      deps: ['jquery']
    'templates':
      deps: ['jade']

Remove exports for Backbonejs & Marionettejs since we use AMD versions


@['pages#home'] = (data) ->
  handler = Gmaps.build('Google')
  handler.buildMap
    provider:
      center:
        lat: 39
        lng: -101
      zoom: 4
    internal:
      id: 'home-map-jumbotron'
    ->
      handler.addMarkers markersJSON

@['pages#sponsor'] = (data) ->
  handler = Gmaps.build('Google')
  handler.buildMap
    provider:
      center:
        lat: 39
        lng: -101
      zoom: 3
    internal:
      id: 'sponsor-clubs-map'
    ->
      handler.addMarkers markersJSON

@['pages#home'] = (data) ->
  handler = Gmaps.build('Google')
  handler.buildMap
    provider:
      center:
        lat: 39
        lng: -101
      zoom: 4
      scrollwheel: false
    internal:
      id: 'home-map-jumbotron'
    ->
      handler.addMarkers markersJSON

@['pages#sponsor'] = (data) ->
  handler = Gmaps.build('Google')
  handler.buildMap
    provider:
      center:
        lat: 39
        lng: -101
      zoom: 3
      scrollwheel: false
    internal:
      id: 'sponsor-clubs-map'
    ->
      handler.addMarkers markersJSON

Revert "show scrollbar on maps"

Revert "show scrollbar on maps"

This reverts commit 54267e3a2556919b841cd1c668b55c73e1d8a374.


packages: [
  "editorconfig"
  "file-icons"
  "highlight-selected"
  "linter"
  "linter-coffeelint"
  "linter-csslint"
  "linter-flake8"
  "linter-htmlhint"
  "linter-js-yaml"
  "linter-jshint"
  "linter-jsonlint"
  "linter-less"
  "linter-php"
  "linter-scss-lint"
  "linter-shellcheck"
  "linter-xmllint"
  "minimap"
  "omni-ruler"
  "package-sync"
  "pigments"
  "pretty-json"
  "sort-lines"
  "terminal-plus"
]

packages: [
  "editorconfig"
  "file-icons"
  "highlight-selected"
  "linter"
  "linter-coffeelint"
  "linter-csslint"
  "linter-flake8"
  "linter-htmlhint"
  "linter-js-yaml"
  "linter-jshint"
  "linter-jsonlint"
  "linter-less"
  "linter-php"
  "linter-scss-lint"
  "linter-shellcheck"
  "linter-xmllint"
  "minimap"
  "omni-ruler"
  "package-sync"
  "pigments"
  "pretty-json"
  "sort-lines"
]

Remove terminal-plus package from Atom

Remove terminal-plus package from Atom


packages: [
  "advanced-open-file"
  "atom-react-native-autocomplete"
  "auto-indent"
  "autocomplete-modules"
  "emmet"
  "expand-region"
  "file-icons"
  "git-diff-popup"
  "git-plus"
  "git-time-machine"
  "hyperclick"
  "incremental-search"
  "js-hyperclick"
  "language-javascript-jsx"
  "linter"
  "linter-eslint"
  "linter-rubocop"
  "linter-sass-lint"
  "list-edit"
  "markdown-scroll-sync"
  "merge-conflicts"
  "package-sync"
  "project-manager"
  "react"
  "react-es6-snippets"
  "relative-numbers"
  "ruby-test"
  "sort-lines"
  "split-diff"
  "synced-sidebar"
  "terminal-plus"
  "toggle-quotes"
  "vim-mode"
  "vim-surround"
]

packages: [
  "advanced-open-file"
  "atom-react-native-autocomplete"
  "auto-indent"
  "autocomplete-modules"
  "emmet-simplified"
  "expand-region"
  "file-icons"
  "git-diff-popup"
  "git-plus"
  "git-time-machine"
  "hyperclick"
  "incremental-search"
  "js-hyperclick"
  "language-javascript-jsx"
  "linter"
  "linter-eslint"
  "linter-rubocop"
  "linter-sass-lint"
  "list-edit"
  "markdown-scroll-sync"
  "merge-conflicts"
  "package-sync"
  "project-manager"
  "react"
  "react-es6-snippets"
  "relative-numbers"
  "ruby-test"
  "sort-lines"
  "split-diff"
  "synced-sidebar"
  "terminal-plus"
  "toggle-quotes"
  "vim-mode"
  "vim-surround"
]

Switch from emmet to emmet-simplified

Switch from emmet to emmet-simplified

This avoids numerous key-binding conflicts


# node.js child-process
# http://nodejs.org/docs/v0.6.3/api/child_processes.html

$ = require 'jquery'
_ = require 'underscore'

module.exports =
class ChildProccess
  @exec: (command, options={}) ->
    deferred = $.Deferred()

    if options.bufferLines
      options.stdout = @bufferLines(options.stdout) if options.stdout
      options.stderr = @bufferLines(options.stderr) if options.stderr

    $native.exec command, options, (exitStatus, stdout, stdin) ->
      if error != 0
        error = new Error("Exec failed (#{exitStatus}) command '#{command}'")
        error.exitStatus = exitStatus
        deferred.reject(error)
      else
        deferred.resolve(stdout, stdin)

    deferred

  @bufferLines: (callback) ->
    buffered = ""
    (data) ->
      buffered += data
      lastNewlineIndex = buffered.lastIndexOf('\n')
      if lastNewlineIndex >= 0
        callback(buffered.substring(0, lastNewlineIndex + 1))
        buffered = buffered.substring(lastNewlineIndex + 1)

# node.js child-process
# http://nodejs.org/docs/v0.6.3/api/child_processes.html

$ = require 'jquery'
_ = require 'underscore'

module.exports =
class ChildProccess
  @exec: (command, options={}) ->
    deferred = $.Deferred()

    if options.bufferLines
      options.stdout = @bufferLines(options.stdout) if options.stdout
      options.stderr = @bufferLines(options.stderr) if options.stderr

    $native.exec command, options, (exitStatus, stdout, stderr) ->
      if exitStatus != 0
        error = new Error("Exec failed (#{exitStatus}) command '#{command}'")
        error.exitStatus = exitStatus
        deferred.reject(error)
      else
        deferred.resolve(stdout, stderr)

    deferred

  @bufferLines: (callback) ->
    buffered = ""
    (data) ->
      buffered += data
      lastNewlineIndex = buffered.lastIndexOf('\n')
      if lastNewlineIndex >= 0
        callback(buffered.substring(0, lastNewlineIndex + 1))
        buffered = buffered.substring(lastNewlineIndex + 1)

Fix the $native.exec callback in ChildProcess.exec

It's stderr, not stdin. Also we have to look at exitStatus, not error to see if the process failed. None of this is tested, which is bad.


class exports.BrunchApplication
  constructor: ->
    jQuery =>
      @initialize this
      Backbone.history.start()

  initialize: ->
    null

class exports.BrunchApplication
  constructor: ->
    $ =>
      @initialize this
      Backbone.history.start()

  initialize: ->
    null

Use jQuery symbol in template instead of name.

This would allow to support zepto etc.


module.exports =
  """
  query user($id: ID! $per: Int, $page: Int, $perBlocks: Int, $q: String,  $sort: SearchSorts, $seed: Int) {
    user(id: $id) {
      contents(per: $per, type: "channel", page: $page, q: $q, sort_by: $sort, seed: $seed) {
        title(truncate: 50)
        updated_at(relative: true)
        user {
          name
        }
        id
        href
        kind {
          __typename
          ... on Channel {
            visibility
            counts {
              blocks
            }
            blocks(per: $perBlocks, sort_by: UPDATED_AT, direction: DESC, type: BLOCK) {
              ... blockThumb
            }
          }
        }
      }
    }
  }

  #{require '../../../components/block_v2/queries/block.coffee'}
  """

module.exports =
  """
  query user($id: ID! $per: Int, $page: Int, $perBlocks: Int, $q: String,  $sort: SearchSorts, $seed: Int) {
    user(id: $id) {
      contents(per: $per, type: "channel", page: $page, q: $q, sort_by: $sort, seed: $seed) {
        title(truncate: 50)
        updated_at(relative: true)
        user {
          name
        }
        id
        href
        kind {
          __typename
          ... on Channel {
            visibility
            counts {
              blocks
            }
            blocks(per: $perBlocks, sort_by: POSITION, direction: DESC, type: BLOCK) {
              ... blockThumb
            }
          }
        }
      }
    }
  }

  #{require '../../../components/block_v2/queries/block.coffee'}
  """

Sort blocks in channels view by position


packages: [
  "advanced-open-file"
  "atom-alignment"
  "atom-beautify"
  "atom-transpose"
  "change-case"
  "color-picker"
  "docblockr"
  "editorconfig"
  "emmet"
  "expand-region"
  "git-blame"
  "language-apache"
  "language-applescript"
  "language-awk"
  "language-babel"
  "language-diff"
  "language-env"
  "language-gitattributes"
  "language-gitignore"
  "language-har"
  "language-hosts"
  "language-ini"
  "language-javascript-jsx"
  "language-latex"
  "language-liquid"
  "language-lisp"
  "language-pegjs"
  "language-pip"
  "language-prolog"
  "language-sca"
  "language-svg"
  "language-tcl"
  "language-viml"
  "language-x86"
  "linter"
  "linter-coffeescript"
  "linter-eslint"
  "linter-flake8"
  "linter-jsonlint"
  "linter-php"
  "linter-shellcheck"
  "linter-ui-default"
  "monokai"
  "package-sync"
  "plist-converter"
  "resize-indent"
  "sort-lines"
  "ssh-config"
  "svg-preview"
  "tabs-to-spaces"
  "teletype"
]

packages: [
  "advanced-open-file"
  "atom-alignment"
  "atom-beautify"
  "atom-transpose"
  "change-case"
  "color-picker"
  "docblockr"
  "editorconfig"
  "emmet"
  "expand-region"
  "git-blame"
  "language-apache"
  "language-applescript"
  "language-awk"
  "language-babel"
  "language-diff"
  "language-env"
  "language-gitattributes"
  "language-gitignore"
  "language-har"
  "language-hosts"
  "language-ini"
  "language-javascript-jsx"
  "language-latex"
  "language-liquid"
  "language-lisp"
  "language-pegjs"
  "language-pip"
  "language-prolog"
  "language-sca"
  "language-svg"
  "language-tcl"
  "language-tex"
  "language-viml"
  "language-x86"
  "linter"
  "linter-coffeescript"
  "linter-eslint"
  "linter-flake8"
  "linter-jsonlint"
  "linter-php"
  "linter-shellcheck"
  "linter-ui-default"
  "monokai"
  "package-sync"
  "plist-converter"
  "resize-indent"
  "sort-lines"
  "ssh-config"
  "svg-preview"
  "tabs-to-spaces"
  "teletype"
]

Add LaTeX grammar to Atom

Add LaTeX grammar to Atom


".source.ruby":
  "Hashrocket":
    prefix: "="
    body: " => "

".source.coffee":
  "Describe block":
    prefix: "de"
    body: """
      describe "${1:description}", ->
        ${2:body}
    """
  "It block":
    prefix: "i"
    body: """
      it "$1", ->
        $2
    """
  "Before each":
    prefix: "be"
    body: """
      beforeEach ->
        $1
    """
  "After each":
    prefix: "af"
    body: """
      afterEach ->
        $1
    """
  "Expectation":
    prefix: "ex"
    body: "expect($1).to$2"
  "Console log":
    prefix: "log"
    body: "console.log $1"
  "Range array":
    prefix: "ra"
    body: "[[$1, $2], [$3, $4]]"
  "Point array":
    prefix: "pt"
    body: "[$1, $2]"
  "Key-value pair":
    prefix: ":"
    body: '${1:"${2:key}"}: ${3:value}'
  "Create Jasmine spy":
    prefix: "spy"
    body: 'jasmine.createSpy("${1:description}")$2'

".source.ruby":
  "Hashrocket":
    prefix: "="
    body: " => "

  "Test block":
    prefix: "test"
    body: """
      test "$1" do
        $2
      end
    """

".source.coffee":
  "Describe block":
    prefix: "de"
    body: """
      describe "${1:description}", ->
        ${2:body}
    """
  "It block":
    prefix: "i"
    body: """
      it "$1", ->
        $2
    """
  "Before each":
    prefix: "be"
    body: """
      beforeEach ->
        $1
    """
  "After each":
    prefix: "af"
    body: """
      afterEach ->
        $1
    """
  "Expectation":
    prefix: "ex"
    body: "expect($1).to$2"
  "Console log":
    prefix: "log"
    body: "console.log $1"
  "Range array":
    prefix: "ra"
    body: "[[$1, $2], [$3, $4]]"
  "Point array":
    prefix: "pt"
    body: "[$1, $2]"
  "Key-value pair":
    prefix: ":"
    body: '${1:"${2:key}"}: ${3:value}'
  "Create Jasmine spy":
    prefix: "spy"
    body: 'jasmine.createSpy("${1:description}")$2'

Add Ruby snippet for test blocks

Add Ruby snippet for test blocks


angular.module('kassa').directive('kassaStateToggle',[
  '$location'
  ($location)->
    linker = ($scope, $element, $attrs)->
      stateKey = $attrs.kassaStateToggle
      defaultValue = $attrs.kassaStateDefault || false

      unless $location.search()[stateKey]?
       $location.search stateKey, defaultValue

      isInVisibleState = (key)->
        stateValue = $location.search()[key]
        stateValue == "true"

      $element.on 'click', ->
        $scope.$apply ->
          value = String(!isInVisibleState(stateKey))
          $location.search(stateKey, value).replace()

      $scope.isInVisibleState = isInVisibleState

    restrict: 'A', scope: false, link: linker
])

angular.module('kassa').directive('kassaStateToggle',[
  '$location'
  ($location)->
    linker = ($scope, $element, $attrs)->
      stateKey = $attrs.kassaStateToggle
      defaultValue = $attrs.kassaStateDefault || false

      unless $location.search()[stateKey]?
       $location.search(stateKey, defaultValue).replace()

      isInVisibleState = (key)->
        stateValue = $location.search()[key]
        stateValue == "true"

      $element.on 'click', ->
        $scope.$apply ->
          value = String(!isInVisibleState(stateKey))
          $location.search(stateKey, value).replace()

      $scope.isInVisibleState = isInVisibleState

    restrict: 'A', scope: false, link: linker
])

Fix kassaStateToggle adding an extra history token on defaultValue


{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: (filePath, image) =>
    @filePath = filePath
    @image = image
    if @filePath and @image
      @attach()

      @subscribe atom.workspaceView, 'pane-container:active-pane-item-changed', =>
        editor = atom.workspaceView.getActivePaneItem()
        if editor instanceof ImageEditor and @filePath is editor.filePath
          @imageSizeStatus.parent().show()
        else
          @imageSizeStatus.parent().hide()

  attach: =>
    statusBar = atom.workspaceView.statusBar
    if statusBar
      statusBar.appendLeft this
      @getImageSize()

  getImageSize: =>
    imageWidth = @image.width()
    imageHeight = @image.height()
    @imageSizeStatus.text("#{imageWidth}px x #{imageHeight}px").show()

{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: (filePath, image) =>
    @filePath = filePath
    @image = image
    if @filePath and @image
      @attach()

      @subscribe atom.workspaceView, 'pane-container:active-pane-item-changed', =>
        editor = atom.workspaceView.getActivePaneItem()
        if editor instanceof ImageEditor and @filePath is editor.filePath
          @imageSizeStatus.parent().show()
        else
          @imageSizeStatus.parent().hide()

      @subscribe atom.workspaceView, 'pane:before-item-destroyed', =>
        @detach()

  attach: =>
    statusBar = atom.workspaceView.statusBar
    if statusBar
      statusBar.appendLeft this
      @getImageSize()

  getImageSize: =>
    imageWidth = @image.width()
    imageHeight = @image.height()
    @imageSizeStatus.text("#{imageWidth}px x #{imageHeight}px").show()

Detach from status bar when pane removed


React = require 'react'
createReactClass = require 'create-react-class'
CroppedImage = require '../../../components/cropped-image'

ArticleListItem = createReactClass
  getDefaultProps: ->
    icon: null
    title: ''
    onClick: ->

  render: ->
    <button type="button" className="field-guide-editor-article-button" onClick={@props.onClick}>
      <CroppedImage className="field-guide-editor-article-button-icon" src={@props.icon} aspectRatio={1} width="3em" height="3em" style={borderRadius: '50%', verticalAlign: 'middle'} />{' '}
      {@props.title}
    </button>

module.exports = ArticleListItem

React = require 'react'
createReactClass = require 'create-react-class'
CroppedImage = require('../../../components/cropped-image').default

ArticleListItem = createReactClass
  getDefaultProps: ->
    icon: null
    title: ''
    onClick: ->

  render: ->
    <button type="button" className="field-guide-editor-article-button" onClick={@props.onClick}>
      <CroppedImage className="field-guide-editor-article-button-icon" src={@props.icon} aspectRatio={1} width="3em" height="3em" style={borderRadius: '50%', verticalAlign: 'middle'} />{' '}
      {@props.title}
    </button>

module.exports = ArticleListItem

Fix CroppedImage require in ArticleListItem


`import Ember from 'ember'`
`import TravisRoute from 'travis/routes/basic'`
`import config from 'travis/config/environment'`

Route = TravisRoute.extend
  queryParams:
    filter: { replace: true }
  model: ->
    apiEndpoint = config.apiEndpoint
    $.ajax(apiEndpoint + '/v3/repos?repository.active=true', {
      headers: {
        Authorization: 'token ' + @auth.token()
      }
    }).then (response) ->
      response.repositories.sortBy('last_build.finished_at').filter( (repo) ->
        repo.last_build
      ).map( (repo) ->
        Ember.Object.create(repo)
      )

`export default Route`

`import Ember from 'ember'`
`import TravisRoute from 'travis/routes/basic'`
`import config from 'travis/config/environment'`

Route = TravisRoute.extend
  queryParams:
    filter: { replace: true }
  model: ->
    apiEndpoint = config.apiEndpoint
    $.ajax(apiEndpoint + '/v3/repos?repository.active=true', {
      headers: {
        Authorization: 'token ' + @auth.token()
      }
    }).then (response) ->
      response.repositories.sortBy('last_build.finished_at').filter( (repo) ->
        repo.last_build
      ).sort( (a, b) ->
        if !a.last_build.finished_at || a.last_build.finished_at > b.last_build.finished_at
          return -1
        else if !b.last_build.finished_at || b.last_build.finished_at > a.last_build.finished_at
          return 1
        else
          return 0
      ).map( (repo) ->
        Ember.Object.create(repo)
      )

`export default Route`

Sort repos explicitly on dashboard

Sort repos explicitly on dashboard


###
Register all the editors.

One you add a new built in editor, it should go here.
###

# Require each module, which loads a file editor.  These call register_file_editor.
# This should be a comprehensive list of all React editors

# require('./editor_terminal')
require('./chat/register')
require('./editor_archive')
#require('./course/main')
require('./course-coffee/main')
require('./editor_pdf')
require('./stopwatch/register')
require('./jupyter/register')
require('./tasks/register')

require('./media-viewer/register')

# Public editors
#require('./public/editor_image')

# Raw data editors
require('./editor-data/generic')

# All the non-react editors.
require('./editor').register_nonreact_editors()

require('./frame-editors/register')

###
Register all the editors.

One you add a new built in editor, it should go here.
###

# Require each module, which loads a file editor.  These call register_file_editor.
# This should be a comprehensive list of all React editors

# require('./editor_terminal')
require('./chat/register')
require('./editor_archive')
require('./course/main')
#require('./course-coffee/main')
require('./editor_pdf')
require('./stopwatch/register')
require('./jupyter/register')
require('./tasks/register')

require('./media-viewer/register')

# Public editors
#require('./public/editor_image')

# Raw data editors
require('./editor-data/generic')

# All the non-react editors.
require('./editor').register_nonreact_editors()

require('./frame-editors/register')

Switch to ts course with one line

Switch to ts course with one line


ETahi.UploadManuscriptOverlayController = ETahi.TaskController.extend
  manuscriptUploadUrl: (->
    "/papers/#{@get('litePaper.id')}/upload"
  ).property('litePaper.id')

  isUploading: false
  isProcessing: false
  uploadProgress: 0
  showProgress: true

  actions:
    uploadStarted: ->
      @set('isUploading', true)

    uploadProgress: (data) ->
      progress = Math.round(data.loaded * 100 / data.total)
      @set('uploadProgress', progress)
      if progress >= 100
        @setProperties(showProgress: false, isProcessing: true)

    uploadError: (message) ->
      @set('uploadError', message)

    uploadFinished: (data) ->
      @store.pushPayload(data)
      @set('isUploading', false)
      @set('completed', true)
      @get('model').save().then(=> @send('closeAction'))

ETahi.UploadManuscriptOverlayController = ETahi.TaskController.extend
  manuscriptUploadUrl: (->
    "/papers/#{@get('litePaper.id')}/upload"
  ).property('litePaper.id')

  isUploading: false
  isProcessing: false
  uploadProgress: 0
  showProgress: true

  isEditable: (->
    !@get('paper.lockedBy') && (@get('isUserEditable') || @get('isCurrentUserAdmin'))
  ).property('paper.lockedBy', 'isUserEditable', 'isCurrentUserAdmin')

  actions:
    uploadStarted: ->
      @set('isUploading', true)

    uploadProgress: (data) ->
      progress = Math.round(data.loaded * 100 / data.total)
      @set('uploadProgress', progress)
      if progress >= 100
        @setProperties(showProgress: false, isProcessing: true)

    uploadError: (message) ->
      @set('uploadError', message)

    uploadFinished: (data) ->
      @store.pushPayload(data)
      @set('isUploading', false)
      @set('completed', true)
      @get('model').save().then(=> @send('closeAction'))

Disable upload manuscript overlay when the paper is being edited.


define [
  'controllers/node_tool'
  'models/commands/commander'
], (NodeTool, Commander)  ->

  class MockContainer
    addNode: ->

  class MockDrawing extends MockContainer
    select: (item) ->
      @selection = [item]

    clearSelection: ->
      @selection = []

  describe 'NodeTool', ->
    beforeEach ->
      @commander = new Commander
      @drawing = new MockDrawing
      @tool = new NodeTool(commander: @commander, drawing: @drawing)
      @tool.setParameter("shape", "a-shape")

    it 'creates a new node', ->
      spyOn(@drawing, 'addNode')

      @tool.onMouseDown({point: "dummy-position"})

      expect(@drawing.addNode).toHaveBeenCalledWith(
        shape : 'a-shape'
        position : "dummy-position"
      )

define [
  'controllers/node_tool'
  'models/commands/commander'
], (NodeTool, Commander)  ->

  class MockContainer
    addNode: ->

  class MockDrawing extends MockContainer
    select: (item) ->
      @selection = [item]

    clearSelection: ->
      @selection = []

  describe 'NodeTool', ->
    beforeEach ->
      @commander = new Commander
      @drawing = new MockDrawing
      @tool = new NodeTool(commander: @commander, drawing: @drawing)
      @tool.setParameter("shape", "a-shape")

    it 'creates a new node', ->
      spyOn(@drawing, 'addNode')

      @tool.onMouseDown(
        point:
          x: 1
          y: 2
      )

      expect(@drawing.addNode).toHaveBeenCalledWith(
        shape: 'a-shape'
        position:
          x: 1
          y: 2
      )

Fix spec broken in previous commit (bdcf0c64).


editorModule "Attachments", template: "editor_with_image"

editorTest "moving an image by drag and drop", (expectDocument) ->
  typeCharacters "!", ->
    moveCursor direction: "right", times: 1, (coordinates) ->
      img = document.activeElement.querySelector("img")
      triggerEvent(img, "mousedown")
      after 1, ->
        dragToCoordinates coordinates, ->
          expectDocument "!a#{Trix.OBJECT_REPLACEMENT_CHARACTER}b\n"

editorTest "removing an image", (expectDocument) ->
  after 20, ->
    clickElement getFigure(), ->
      closeButton = getFigure().querySelector(".attachment__remover")
      clickElement closeButton, ->
        expectDocument "ab\n"

getFigure = ->
  getDocumentElement().querySelector("figure")

editorModule "Attachments", template: "editor_with_image"

editorTest "moving an image by drag and drop", (expectDocument) ->
  typeCharacters "!", ->
    moveCursor direction: "right", times: 1, (coordinates) ->
      img = document.activeElement.querySelector("img")
      triggerEvent(img, "mousedown")
      after 1, ->
        dragToCoordinates coordinates, ->
          expectDocument "!a#{Trix.OBJECT_REPLACEMENT_CHARACTER}b\n"

editorTest "removing an image", (expectDocument) ->
  after 20, ->
    clickElement getFigure(), ->
      closeButton = getFigure().querySelector(".attachment__remover")
      clickElement closeButton, ->
        expectDocument "ab\n"

editorTest "editing an image caption", (expectDocument) ->
  clickElement findElement("figure"), ->
    clickElement findElement("figcaption"), ->
      ok findElement("textarea")
      findElement("textarea").focus()
      findElement("textarea").value = "my caption"
      pressKey "return", ->
        ok not findElement("textarea")
        expectAttributes [2, 3], caption: "my caption"
        expectDocument "ab#{Trix.OBJECT_REPLACEMENT_CHARACTER}\n"

getFigure = ->
  findElement("figure")

findElement = (selector) ->
  getDocumentElement().querySelector(selector)

Test editing image attachment caption

Test editing image attachment caption


Walrus = require '../../bin/walrus'

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

TestHelpers =

  read : ( filename ) -> fs.readFileSync filename, 'utf8' if path.existsSync filename

  pass : ( specs, suffix='' ) ->

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

      do ( file ) =>

        base = path.basename file, '.wal'

        spec = "#{specs}/#{base}#{suffix}.html"

        text = @read "#{specs}/#{base}.wal"
        json = @read "#{specs}/#{base}.js"
        html = @read spec

        # if we can't find the suffixed version, try and find
        # one without the suffix and use that instead.
        if not html
          spec = "#{specs}/#{base}.html"
          html = @read spec

        # if _that_ one doesn't exist, throw a helpful error.
        throw "Can't find example html at #{specs}/#{base}#{suffix}.html or #{specs}/#{base}.html" if not html

        tmpl = Walrus.Parser.parse text

        it "should pass the #{base}#{suffix} example", ( done ) ->

          comp = tmpl.compile( eval( "(#{json})" ) )

          if comp is html
            done( )
          else

            cmd = """
            printf "#{comp}" | diff --unified #{spec} -
            """

            exec cmd, ( error, stdout, stderr ) ->

              done new Error "Expected did not match actual:\n" + stdout

module.exports = TestHelpers

Walrus = require '../../bin/walrus'

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

TestHelpers =

  read : ( filename ) -> fs.readFileSync filename, 'utf8' if path.existsSync filename

  pass : ( specs, suffix='' ) ->

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

      do ( file ) =>

        base = path.basename file, '.wal'

        spec = "#{base}#{suffix}"

        text = @read "#{specs}/#{base}.wal"
        json = @read "#{specs}/#{base}.js"
        html = @read "#{specs}/#{spec}.html"

        # if we can't find the suffixed version, try and find
        # one without the suffix and use that instead.
        if not html
          spec = base
          html = @read "#{specs}/#{spec}.html"

        # if _that_ one doesn't exist, throw a helpful error.
        throw "Can't find example html at #{specs}/#{base}#{suffix}.html or #{specs}/#{base}.html" if not html

        tmpl = Walrus.Parser.parse text

        it "should pass the #{spec} example", ( done ) ->

          comp = tmpl.compile( eval( "(#{json})" ) )

          if comp is html
            done( )
          else

            cmd = """
            printf "#{comp}" | diff --unified #{specs}/#{spec}.html -
            """

            exec cmd, ( error, stdout, stderr ) ->

              done new Error "Expected did not match actual:\n" + stdout

module.exports = TestHelpers

Make test helpers print out the correct spec name


packages: [
  "advanced-open-file"
  "atom-alignment"
  "atom-beautify"
  "atom-transpose"
  "change-case"
  "color-picker"
  "docblockr"
  "editorconfig"
  "emmet"
  "expand-region"
  "language-apache"
  "language-applescript"
  "language-awk"
  "language-babel"
  "language-diff"
  "language-env"
  "language-gitattributes"
  "language-gitignore"
  "language-har"
  "language-hosts"
  "language-ini"
  "language-javascript-jsx"
  "language-latex"
  "language-liquid"
  "language-lisp"
  "language-pegjs"
  "language-pip"
  "language-prolog"
  "language-sca"
  "language-svg"
  "language-tcl"
  "language-viml"
  "language-x86"
  "linter"
  "linter-coffeescript"
  "linter-eslint"
  "linter-flake8"
  "linter-jsonlint"
  "linter-php"
  "linter-shellcheck"
  "linter-ui-default"
  "monokai"
  "package-sync"
  "resize-indent"
  "sort-lines"
  "ssh-config"
  "svg-preview"
  "swap-selection"
  "tabs-to-spaces"
  "teletype"
]

packages: [
  "advanced-open-file"
  "atom-alignment"
  "atom-beautify"
  "atom-transpose"
  "change-case"
  "color-picker"
  "docblockr"
  "editorconfig"
  "emmet"
  "expand-region"
  "language-apache"
  "language-applescript"
  "language-awk"
  "language-babel"
  "language-diff"
  "language-env"
  "language-gitattributes"
  "language-gitignore"
  "language-har"
  "language-hosts"
  "language-ini"
  "language-javascript-jsx"
  "language-latex"
  "language-liquid"
  "language-lisp"
  "language-pegjs"
  "language-pip"
  "language-prolog"
  "language-sca"
  "language-svg"
  "language-tcl"
  "language-viml"
  "language-x86"
  "linter"
  "linter-coffeescript"
  "linter-eslint"
  "linter-flake8"
  "linter-jsonlint"
  "linter-php"
  "linter-shellcheck"
  "linter-ui-default"
  "monokai"
  "package-sync"
  "resize-indent"
  "sort-lines"
  "ssh-config"
  "svg-preview"
  "tabs-to-spaces"
  "teletype"
]

Uninstall swap-selection package for atom


class Game.State extends Game.TwoWay
  constructor: () ->
    @objects = {}
    @eventCounters = {}
    @eventHandlers = {}
    super
  onEvent: (e) ->
    # Add timestamped instance to the event counters object
    if e.type not of @eventCounters
      @eventCounters[e.type] = []
    @eventCounters[e.type].push new Date()
    # relay the event upward
    @fire(e)
    # call added callbacks
    @eventHandlers[e.type].forEach (cb) -> cb()
    return
  addObject: (obj) ->
    if obj not instanceof Game.Object
      throw new Game.GameError(Game.Error.ErrorType.NOT_OBJECT)
    if obj.getId() of @objects
      throw new Game.GameError(Game.Error.ErrorType.ALREADY_ADDED)
    @objects[obj.getId()] = obj
    @listen(obj, @onEvent)
    # call object added method
    obj.added()
  addEventHandler: (evtType, callback) ->
    if evtType not of @eventHandlers
      @eventHandlers[evtType] = []
    if callback not in @eventHandlers[evtType]
      @eventHandlers[evtType].push callback
    return

class Game.State extends Game.TwoWay
  constructor: () ->
    @objects = {}
    @eventCounters = {}
    @eventHandlers = {}
    super
  onEvent: (e) ->
    # Add timestamped instance to the event counters object
    if e.type not of @eventCounters
      @eventCounters[e.type] = []
    @eventCounters[e.type].push new Date()
    # relay the event upward
    @fire(e)
    # call added callbacks
    @eventHandlers[e.type]?.forEach (cb) -> cb()
    return
  addObject: (obj) ->
    if obj not instanceof Game.Object
      throw new Game.GameError(Game.Error.ErrorType.NOT_OBJECT)
    if obj.getId() of @objects
      throw new Game.GameError(Game.Error.ErrorType.ALREADY_ADDED)
    @objects[obj.getId()] = obj
    @listen(obj, @onEvent)
    # call object added method
    obj.added()
  addEventHandler: (evtType, callback) ->
    if evtType not of @eventHandlers
      @eventHandlers[evtType] = []
    if callback not in @eventHandlers[evtType]
      @eventHandlers[evtType].push callback
    return

Fix bug in event handlers calling

Fix bug in event handlers calling


module.exports = (robot) ->
  names = ['Dana', 'Freia', 'Jhishan', 'Kyle', 'Brenton', 'Terri', 'Abhi', 'Drew']
  robot.hear /Resume queen, who is next?/i, (res) ->
    res.send res.random names

module.exports = (robot) ->
  names = ['Dana', 'Freia', 'Jhishan', 'Kyle', 'Brenton', 'Terri', 'Abhi', 'Drew']
  robot.hear /Resume queen, who is next\?/i, (res) ->
    res.send res.random names

Add \ before question mark

Add \ before question mark


module.exports = (app, server) ->
  authMiddleware = require('./middleware/authentication')
  models  = require '../models'
  app.io = require('socket.io')(server)
  redisClient = require('./redis_client')()

  respondSocketError = (next, err) ->
    console.error "Socket failed to connect", err
    next(new Error('not found'))

  validateLeagueAuth = (socket, next) ->
    unless socket.handshake.query.league_id
      respondSocketError(next)
    else
      models.League.findOne(where: {id: socket.handshake.query.league_id})
        .then (league) ->
          if (league)
            authMiddleware.getOrCreateUserFromToken(socket.handshake.query.id_token, redisClient)
              .then (user) ->
                next()
              .catch respondSocketError.bind(null, next)
          else
            respondSocketError(next)
        .catch respondSocketError.bind(null, next)

  app.io.use validateLeagueAuth

  app.io.sockets.on 'connection', (socket) ->
    socket.join(socket.handshake.query.league_id);
    socket.on 'message', (data) ->
      socket.broadcast.to(socket.handshake.query.league_id).emit('message', data)

module.exports = (app, server) ->
  authMiddleware = require('./middleware/authentication')
  models  = require '../models'
  app.io = require('socket.io')(server)
  redisClient = require('./redis_client')()

  respondSocketError = (next, err) ->
    console.error "Socket failed to connect", err
    next(new Error('not found'))

  validateLeagueAuth = (socket, next) ->
    unless socket.handshake.query.league_id
      respondSocketError(next)
    else
      models.League.findOne(where: {id: socket.handshake.query.league_id})
        .then (league) ->
          if league
            if league.public
              next()
            else
              authMiddleware.getOrCreateUserFromToken(socket.handshake.query.id_token, redisClient)
                .then (user) ->
                  next()
                .catch respondSocketError.bind(null, next)
          else
            respondSocketError(next)
        .catch respondSocketError.bind(null, next)

  app.io.use validateLeagueAuth

  app.io.sockets.on 'connection', (socket) ->
    socket.join(socket.handshake.query.league_id);
    socket.on 'message', (data) ->
      socket.broadcast.to(socket.handshake.query.league_id).emit('message', data)

Fix video indicator not showing anonymously on public leagues


DiffChunk = require './diff-chunk'
List = require '../list'
_ = require 'underscore'

##
# A diff is a whole-file diff, and is broken into a list of chunks. End-game
# here is to be able to stage individual chunks, not just the whole diff.
#

module.exports =
class Diff extends List
  model: DiffChunk
  is_sublist: true
  selected_index: -1

  removeHeader: (diff) ->
    # Remove first two lines, which name the file
    @header = diff.match(/^(.*?\n){2}/)[0]
    diff.replace /^(.*?\n){2}/, ""

  splitChunks: (diff) ->
    # We'll treat "@@ " a the beginning of a line as characteristic of the start
    # of a chunk.
    diff.split /(?=^@@ )/gm

  constructor: (diff) ->
    @giftDiff = diff
    @raw = diff.diff
    diff = @removeHeader @raw
    chunks = @splitChunks diff
    chunks = _.map chunks, (chunk) => chunk: chunk, header: @header
    super chunks

    @select -1

  chunks: ->
    @models

DiffChunk = require './diff-chunk'
List = require '../list'
_ = require 'underscore'

##
# A diff is a whole-file diff, and is broken into a list of chunks. End-game
# here is to be able to stage individual chunks, not just the whole diff.
#

module.exports =
class Diff extends List
  model: DiffChunk
  is_sublist: true
  selected_index: -1

  removeHeader: (diff) ->
    # Remove first two lines, which name the file
    @header = diff.match(/^(.*?\n){2}/)[0]
    diff.replace /^(.*?\n){2}/, ""

  splitChunks: (diff) ->
    # We'll treat "@@ " a the beginning of a line as characteristic of the start
    # of a chunk.
    diff.split /(?=^@@ )/gm

  constructor: (diff) ->
    @giftDiff = diff
    @raw = diff?.diff
    diff = @removeHeader @raw
    chunks = @splitChunks diff
    chunks = _.map chunks, (chunk) => chunk: chunk, header: @header
    super chunks

    @select -1

  chunks: ->
    @models

Fix type error upon completed commit

Fix type error upon completed commit


# 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
'.workspace':
  'ctrl-alt-o': 'rails:toggle'

'.editor.vim-mode.command-mode:not(.insert-mode)':
  'g m': 'rails:go-to-model'
  'g v': 'rails:go-to-view'
  'g c': 'rails:go-to-controller'
  'g s': 'rails:go-to-test'
  'g h': 'rails:go-to-helper'

# 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
'.atom-workspace':
  'ctrl-alt-o': 'rails:toggle'

'.atom-text-editor.vim-mode.command-mode:not(.insert-mode)':
  'g m': 'rails:go-to-model'
  'g v': 'rails:go-to-view'
  'g c': 'rails:go-to-controller'
  'g s': 'rails:go-to-test'
  'g h': 'rails:go-to-helper'

Fix deprecated selectors in the keymap

Fix deprecated selectors in the keymap


chai   = require("chai")
should = chai.should()
mock   = require("TinyMockJS").mock
Logger = require("../src/Logger").Logger
fs     = require("fs")

describe "Logger", ->
  describe "constructor(options)", ->
    it "retrieves the log file name from 'options'", ->
      mock (options) ->
        options.expects("get_log_filename")
        logger = new Logger(options)

  describe "write(message)", ->

    logger = undefined

    beforeEach ->
      options = { get_log_filename: -> "log.txt" }
      logger = new Logger(options)

    it "writes 'message' to 'log_filename' that was passed to the constructor", ->
      mock ->
        fs.expects("appendFileSync").args("log.txt", "a message")
        logger.log("a message")

    it "does not eat exceptions thrown by fs.appendFileSync", ->
      mock ->
        fs.expects("appendFileSync").args("log.txt", "a message").throws(new Error("an error"))
        (-> logger.log("a message") ).should.throw("an error")

chai   = require("chai")
should = chai.should()
mock   = require("TinyMockJS")
Logger = require("../src/Logger").Logger
fs     = require("fs")

describe "Logger", ->
  describe "constructor(options)", ->
    it "retrieves the log file name from 'options'", ->
      mock (options) ->
        options.expects("get_log_filename")
        logger = new Logger(options)

  describe "write(message)", ->

    logger = undefined

    beforeEach ->
      options = { get_log_filename: -> "log.txt" }
      logger = new Logger(options)

    it "writes 'message' to 'log_filename' that was passed to the constructor", ->
      mock ->
        fs.expects("appendFileSync").args("log.txt", "a message")
        logger.log("a message")

    it "does not eat exceptions thrown by fs.appendFileSync", ->
      mock ->
        fs.expects("appendFileSync").args("log.txt", "a message").throws(new Error("an error"))
        (-> logger.log("a message") ).should.throw("an error")

Change 'mock = require("TinyMockJS").mock' to 'mock = require("TinyMockJS")'


window.Sounds2 = class
  sounds:
    walk: [0, 0.2]
    chop: [1, 1.2]
    throw: [2, 2.1]
    snowball_hit: [3, 3.4]
    drop: [4, 4.2]
    pickup: [5, 5.2]
    deliver: [6, 6.5]

  constructor: ->
    @players = []

    for i in [0 .. 9] # create 10 players by default
      @.createPlayer()

  createPlayer: ->
    player = document.createElement("audio");
    player.src = '/sounds.mp3'

    $('body').append(player)

    player.pause()

    player.addEventListener('timeupdate',
      ()=>
        @.onPlayerTimeUpdate(player)
      false
    )

    @players.push(player)

    player

  getFreePlayer: ->
    _.detect(@players, (p)-> not p.stop_at? ) || @.createPlayer()

  playSound: (key)->
    player = @.getFreePlayer()

    player.src = player.src
    player.currentTime = @.sounds[key][0]
    player.stop_at = @.sounds[key][1]

    console.log('start:', player.currentTime)

    player.play()

  # binds to player
  onPlayerTimeUpdate: (player)->
    if player.currentTime > player.stop_at
      player.pause()

      delete player.stop_at

      console.log('done')

window.Sounds2 = class
  sounds:
    walk: [0, 0.2]
    chop: [1, 1.2]
    throw: [2, 2.1]
    snowball_hit: [3, 3.4]
    drop: [4, 4.2]
    pickup: [5, 5.2]
    deliver: [6, 6.5]

  constructor: ->
    @players = []

    for i in [0 .. 9] # create 10 players by default
      @.createPlayer()

  createPlayer: ->
    player = document.createElement("audio");
    player.src = '/sounds.mp3'

    $('body').append(player)

    player.pause()

    player.addEventListener('timeupdate',
      ()=>
        @.onPlayerTimeUpdate(player)
      false
    )

    @players.push(player)

    player

  getFreePlayer: ->
    _.detect(@players, (p)-> not p.stop_at? ) || @.createPlayer()

  playSound: (key)->
    player = @.getFreePlayer()

    if player.readyState == player.HAVE_ENOUGH_DATA
      player.currentTime = @.sounds[key][0]
      player.stop_at = @.sounds[key][1]

      player.play()

  # binds to player
  onPlayerTimeUpdate: (player)->
    if player.currentTime > player.stop_at
      player.pause()

      delete player.stop_at

Make sounds work both in chrome an firefox


module.exports =
  openPath: (path, callback) ->
    waitsForPromise -> atom.workspace.open(path)

    runs ->
      callback(atom.views.getView(atom.workspace.getActivePaneItem()))

  rowRangeFrom: (marker) ->
    [marker.getTailBufferPosition().row, marker.getHeadBufferPosition().row]

module.exports =
  openPath: (path, callback) ->
    workspaceElement = atom.views.getView(atom.workspace)
    jasmine.attachToDOM(workspaceElement)

    waitsForPromise -> atom.workspace.open(path)

    runs ->
      callback(atom.views.getView(atom.workspace.getActivePaneItem()))

  rowRangeFrom: (marker) ->
    [marker.getTailBufferPosition().row, marker.getHeadBufferPosition().row]

Attach the workspace to the DOM in specs.


gulp = require 'gulp'
jshint = require 'gulp-jshint'
coffeelint = require 'gulp-coffeelint'
mocha = require 'gulp-mocha'
runSequence = require 'run-sequence'

# compilers
coffee = require 'gulp-coffee'

gulp.task 'coffee', ->
  gulp.src './src/*.coffee'
    .pipe coffee()
    .pipe gulp.dest './lib'

gulp.task 'coffeelint', ->
  gulp.src ['./src/*.coffee', './test/*.coffee']
    .pipe coffeelint()
    .pipe coffeelint.reporter()

gulp.task 'jshint', ->
  gulp.src ['./bin/*.js', './lib/*.js']
    .pipe jshint()
    .pipe jshint.reporter()

gulp.task 'watch', ->
  gulp.watch './src/*.coffee', ['coffee']

gulp.task 'mocha', ->
  gulp.src './test/*.coffee'
    .pipe mocha {reporter: 'spec'}

gulp.task 'default', ->
  runSequence ['coffeelint', 'jshint'], 'coffee', 'mocha'

gulp = require 'gulp'
jshint = require 'gulp-jshint'
coffeelint = require 'gulp-coffeelint'
mocha = require 'gulp-mocha'
runSequence = require 'run-sequence'

# compilers
coffee = require 'gulp-coffee'

gulp.task 'coffee', ->
  gulp.src './src/*.coffee'
    .pipe coffeelint()
    .pipe coffeelint.reporter()
    .pipe coffee()
    .pipe gulp.dest './lib'

gulp.task 'coffeelint', ->
  gulp.src ['./src/*.coffee', './test/*.coffee']
    .pipe coffeelint()
    .pipe coffeelint.reporter()

gulp.task 'jshint', ->
  gulp.src ['./bin/*.js', './lib/*.js']
    .pipe jshint()
    .pipe jshint.reporter()

gulp.task 'watch', ->
  gulp.watch './src/*.coffee', ['coffee']

gulp.task 'mocha', ->
  gulp.src './test/*.coffee'
    .pipe mocha {reporter: 'spec'}

gulp.task 'default', ->
  runSequence ['coffeelint', 'jshint'], 'coffee', 'mocha'

Add coffeelint in coffee task

Add coffeelint in coffee task


if Meteor.isCordova
	document.addEventListener 'deviceready', ->
		if device?.platform.toLowerCase() isnt 'android'
			cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
			cordova.plugins.Keyboard.disableScroll(true);

	window.addEventListener 'native.keyboardshow', ->
		if device?.platform.toLowerCase() isnt 'android'
			$('.main-content').css 'height', window.innerHeight

	window.addEventListener 'native.keyboardhide', ->
		if device?.platform.toLowerCase() isnt 'android'
			$('.main-content').css 'height', window.innerHeight

if Meteor.isCordova
	document.addEventListener 'deviceready', ->
		if device?.platform.toLowerCase() isnt 'android'
			cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
			cordova.plugins.Keyboard.disableScroll(true);

	window.addEventListener 'native.keyboardshow', ->
		if device?.platform.toLowerCase() isnt 'android'
			if Meteor.userId()?
				$('.main-content').css 'height', window.innerHeight
			else
				$(document.body).css 'height', window.innerHeight
				$(document.body).css 'overflow', 'scroll'

	window.addEventListener 'native.keyboardhide', ->
		if device?.platform.toLowerCase() isnt 'android'
			if Meteor.userId()?
				$('.main-content').css 'height', window.innerHeight
			else
				$(document.body).css 'height', window.innerHeight
				$(document.body).css 'overflow', 'visible'

Fix scroll of login window

Fix scroll of login window


logger = require "logger-sharelatex"

module.exports =
	startDrain: (io, rate) ->
		# Clear out any old interval
		clearInterval @interval
		if rate == 0
			return
		@interval = setInterval () =>
			@reconnectNClients(io, rate)
		, 1000

	RECONNECTED_CLIENTS: {}
	reconnectNClients: (io, N) ->
		drainedCount = 0
		for client in io.sockets.clients()
			if !@RECONNECTED_CLIENTS[client.id]
				@RECONNECTED_CLIENTS[client.id] = true
				logger.log {client_id: client.id}, "Asking client to reconnect gracefully"
				client.emit "reconnectGracefully"
				drainedCount++
			if drainedCount == N
				break
		if drainedCount < N
			logger.log "All clients have been told to reconnectGracefully"

logger = require "logger-sharelatex"

module.exports =
	startDrain: (io, rate) ->
		# Clear out any old interval
		clearInterval @interval
		if rate == 0
			return
		@interval = setInterval () =>
			@reconnectNClients(io, rate)
		, 1000

	RECONNECTED_CLIENTS: {}
	reconnectNClients: (io, N) ->
		drainedCount = 0
		for client in io.sockets.clients()
			if !@RECONNECTED_CLIENTS[client.id]
				@RECONNECTED_CLIENTS[client.id] = true
				logger.log {client_id: client.id}, "Asking client to reconnect gracefully"
				client.emit "reconnectGracefully"
				drainedCount++
			haveDrainedNClients = (drainedCount == N)
			if haveDrainedNClients
				break
		if drainedCount < N
			logger.log "All clients have been told to reconnectGracefully"

Make breakout logic after draining N clients more clear


_ = require "underscore"
Promise = require "bluebird"
stream = require "readable-stream"
Match = require "mtr-match"
ActivityTask = require "../ActivityTask"
Read = require "./Read"
Save = require "./Save"

class Download extends ActivityTask
  constructor: (options, dependencies) ->
    Match.check dependencies, Match.ObjectIncluding
      read: Read
      save: Save
    super
    @save.input = @read.output = new stream.PassThrough({objectMode: true})
  execute: ->
    Promise.join(@read.execute(), @save.execute())

module.exports = Download

_ = require "underscore"
Promise = require "bluebird"
stream = require "readable-stream"
Match = require "mtr-match"
ActivityTask = require "../ActivityTask"
Read = require "./Read"
Save = require "./Save"

class Download extends ActivityTask
  constructor: (options, dependencies) ->
    Match.check dependencies, Match.ObjectIncluding
      read: Read
      save: Save
    dependencies.save.input = dependencies.read.output = new stream.PassThrough({objectMode: true})
    super

  execute: ->
    Promise.join(@read.execute(), @save.execute())

module.exports = Download

Split options into options and dependencies


'use strict'

app.directive 'pdfViewer', [
  () ->
    restrict: 'E'
    transclude: true
    scope:
      asset:  '=asset'
    link: ($scope, $elem, $attrs,_Controller, $transclude) ->
      pdfElement = $elem[0]
      objectTag = document.createElement('object')
      objectTag.setAttribute('data', $scope.asset.downloadRoute(true))
      objectTag.setAttribute('width', '80%')
      objectTag.setAttribute('type', $scope.asset.format.mimetype)
      $transclude (elem) ->
        objectTag.appendChild elem[0]
      pdfElement.appendChild(objectTag)
      return

]

'use strict'

app.directive 'pdfViewer', [
  () ->
    restrict: 'E'
    transclude: true
    scope:
      asset:  '=asset'
    link: ($scope, $elem, $attrs,_Controller, $transclude) ->
      pdfElement = $elem[0]
      objectTag = document.createElement('object')
      objectTag.setAttribute('data', $scope.asset.downloadRoute(true))
      objectTag.setAttribute('width', '80%')
      objectTag.setAttribute('type', $scope.asset.format.mimetype)
      $transclude (elem) ->
        _.each elem, (i) -> objectTag.appendChild i
      pdfElement.appendChild(objectTag)
      return

]

Allow for multiple-element case in the transclude of pdf


# Given an array of actions, the actions will be called serially.
# The first action is called.  If it calls the 'next' function that is passed to it
# the next action in the series is called.  This continues until either one of the
# actions does not call 'next' or until the end of the list is reached.
_ = require('underscore')._
actionOrMetaString= require('../actionOrMetaString')

exports = module.exports = action_series= (silo)->
  action_series_handler= (req,res,next)->
    return next() unless silo
    if _.isFunction(silo)
      silo= [silo]
    i= 0
    nextActionHandler= (err)=>
      next(err) if err
      try
        actor= silo[i++]
        if (actor)
          actionOrMetaString(actor)(req,res,nextActionHandler)
        else
          next()
      catch e
        next(e)
    nextActionHandler()

# Given an array of actions, the actions will be called serially.
# The first action is called.  If it calls the 'next' function that is passed to it
# the next action in the series is called.  This continues until either one of the
# actions does not call 'next' or until the end of the list is reached.
_ = require('underscore')._
actionOrMetaString= require('../actionOrMetaString')

exports = module.exports = action_series= (silo)->
  action_series_handler= (req,res,next)->
    return next() unless silo
    if _.isFunction(silo)
      silo= [silo]
    i= 0
    nextActionHandler= (err)=>
      return next(err) if err
      try
        actor= silo[i++]
        if (actor)
          actionOrMetaString(actor)(req,res,nextActionHandler)
        else
          next()
      catch e
        next(e)
    nextActionHandler()

Resolve "headers already sent" problems on errors.


$ ->
  # Refresh if a page is loaded directly
  pageLoad()

  # Refresh if a page is loaded via turbolinks
  $(document).on "page:load", pageLoad

pageLoad = ->
  # Steps to do on a page load (with turbolinks)

  # Refresh all ratings on the current page
  refreshRatings()

  # Refresh for the new event divs that are shown on a idea show
  $(".idea-overview-title").on "ajax:success", (event) ->
    refreshRatings()

refreshRatings = ->
  $(".rating").each (index,value) ->
    div_id = $(this).attr("id")
    #alert $(this).attr("data-url")
    #TODO: make this generic so it will update idea-* or event-*
    $.ajax
      url: $(this).attr("data-url"),
      type: 'GET',
      dataType: 'json'
      success: (data, textStatus, jqXHR) ->
        reaction = data['reaction']
        $("##{div_id}").raty
          path: "/assets"
          score: reaction
          click: (score,evt) ->
            $.ajax
              url: $(this).attr("data-url"),
              type: 'PATCH',
              dataType: 'json',
              data: { event: { reaction: score } }

$ ->
  # Refresh if a page is loaded directly
  pageLoad()

  # Refresh if a page is loaded via turbolinks
  $(document).on "page:load", pageLoad

pageLoad = ->
  # Steps to do on a page load (with turbolinks)

  # Refresh all ratings on the current page
  refreshRatings()

  # Refresh for the new event divs that are shown on a idea show
  $(".idea-overview-title").on "ajax:success", (event) ->
    refreshRatings()

refreshRatings = ->
  $(".rating").each (index,value) ->
    div_id = $(this).attr("id")
    #alert $(this).attr("data-url")
    #TODO: make this generic so it will update idea-* or event-*
    $.ajax
      url: $(this).attr("data-url"),
      type: 'GET',
      dataType: 'json'
      success: (data, textStatus, jqXHR) ->
        reaction = data['reaction']
        $("##{div_id}").raty
          path: "/assets"
          score: reaction
          readOnly: $("##{div_id}").attr("data-readonly")
          click: (score,evt) ->
            $.ajax
              url: $(this).attr("data-url"),
              type: 'PATCH',
              dataType: 'json',
              data: { event: { reaction: score } }

Set reaction stars as read only depending on data-readonly attribute.

Fixes #10


class window.NDPEvidenceCollection extends Backbone.Collection
  initialize: (models, options) ->
    @on 'change', @sort, @
    @fact = options.fact

  constructor: (models, options) ->
    super
    unless models and models.length > 0
      @reset [
        new OpinionatersEvidence {type: 'believe'},    collection: this
        new OpinionatersEvidence {type: 'disbelieve'}, collection: this
        new OpinionatersEvidence {type: 'doubt'},      collection: this
      ]

  comparator: (item) -> - item.get('impact')

class window.NDPEvidenceCollection extends Backbone.Collection
  initialize: (models, options) ->
    @on 'change', @sort, @
    @fact = options.fact

  constructor: (models, options) ->
    super
    unless models and models.length > 0
      @reset [
        new OpinionatersEvidence({type: 'believe'   }, fact: @fact),
        new OpinionatersEvidence({type: 'disbelieve'}, fact: @fact),
        new OpinionatersEvidence({type: 'doubt'     }, fact: @fact)
      ]

  comparator: (item) -> - item.get('impact')

Revert "Indeed don't pass the fact but the collection"

This reverts commit dbff5c17b0e8211986e2a0a773e4cdec0afe4880.


stripeCallback = (status, response) ->
  if response.error
    anchortabSite.validationError("#card-number", response.error.message)
  else
    $("#stripe-token").val(response.id)
    $(".stripe-form").submit()

$(document).ready ->
  anchortabSite.event("stripe-form-ready")

  $(".plan-selection").on 'change', (event) ->
    if $(".plan-selection").find(":selected").data("has-trial")
      $(".billing-information").hide()
    else
      $(".billing-information").show()

  $(".stripe-form .submit").on "click", (event) ->
    return if $(".plan-selection").find(":selected").data("has-trial")

    event.preventDefault()
    event.stopPropagation()

    anchortabSite.event("validate-stripe-form",
      stripeCallback: stripeCallback
    )

  $(".plan-selection").change()

stripeCallback = (status, response) ->
  if response.error
    anchortabSite.validationError("#card-number", response.error.message)
  else
    $("#stripe-token").val(response.id)
    $(".stripe-form").submit()

$(document).ready ->
  anchortabSite.event("stripe-form-ready")

  $(".plan-selection").on 'change', (event) ->
    if $(".plan-selection").find(":selected").data("has-trial")
      $(".billing-information").hide()
    else
      $(".billing-information").show()

  $(".stripe-form .submit").on "click", (event) ->
    return if $(".plan-selection").find(":selected").data("has-trial")

    event.preventDefault()
    event.stopPropagation()

    anchortabSite.event("validate-stripe-form",
      stripeCallback: stripeCallback
    )

  if window.location.hash != ""
    hash = window.location.hash.replace("#", "")
    $("option[value=" + hash + "]").attr("selected", "selected")

  $(".plan-selection").change()

Select a plan based on a hash provided in URL.


React = require 'react'
BS = require 'react-bootstrap'

ChapterSectionType = require './chapter-section-type'

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

  propTypes:
    section:  React.PropTypes.object.isRequired
    onPractice: React.PropTypes.func

  render: ->
    {section,  onPractice} = @props

    bar = if section.current_level
      percent = Math.round((section.current_level / 1) * 100)
      # always show at least 5% of bar, otherwise it just looks empty
      <BS.ProgressBar className={section.interpretation} now={Math.max(percent, 5)} />
    else
      Practice section

    if onPractice
      tooltip = <BS.Tooltip>Click to practice</BS.Tooltip>
      <BS.OverlayTrigger placement='bottom' overlay={tooltip}>
        <BS.Button onClick={-> onPractice(section)} block>{bar}</BS.Button>
      </BS.OverlayTrigger>
    else
      bar

React = require 'react'
BS = require 'react-bootstrap'

ChapterSectionType = require './chapter-section-type'

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

  propTypes:
    section:  React.PropTypes.object.isRequired
    onPractice: React.PropTypes.func

  render: ->
    {section,  onPractice} = @props

    bar = if section.current_level
      percent = Math.round((section.current_level / 1) * 100)
      # always show at least 5% of bar, otherwise it just looks empty
      <BS.ProgressBar className={section.interpretation} now={Math.max(percent, 5)} />
    else
      Practice more to get forecast

    if onPractice
      tooltip = <BS.Tooltip>Click to practice</BS.Tooltip>
      <BS.OverlayTrigger placement='bottom' overlay={tooltip}>
        <BS.Button onClick={-> onPractice(section)} block>{bar}</BS.Button>
      </BS.OverlayTrigger>
    else
      bar

Change empty progress bar wording

Change empty progress bar wording


FactlinkApp.module "DiscussionModalOnFrontend", (DiscussionModalOnFrontend, MyApp, Backbone, Marionette, $, _) ->

  background_page_url = null

  DiscussionModalOnFrontend.addInitializer ->
    background_page_url = currentUser.streamLink()

    FactlinkApp.vent.on 'close_discussion_modal', ->
      Backbone.history.navigate background_page_url, false
      FactlinkApp.discussionModalRegion.close()

  DiscussionModalOnFrontend.openDiscussion = (fact, url) ->
    Backbone.history.navigate fact.get('url'), false
    background_page_url = Backbone.history.getFragment(url) if url?
    newClientModal = new DiscussionModalContainer
    FactlinkApp.discussionModalRegion.show newClientModal
    newClientModal.mainRegion.show new NDPDiscussionView model: fact

  # HACK: This assumes that we use "navigate url, true" for all url changes that
  # leave the main regions alone (sidebar, mainRegion, etc)
  DiscussionModalOnFrontend.loadUrlStopRouting = (fragment) ->
    FactlinkApp.discussionModalRegion.close()

    if fragment == background_page_url
      background_page_url = null
      true
    else
      background_page_url = null
      false

FactlinkApp.module "DiscussionModalOnFrontend", (DiscussionModalOnFrontend, MyApp, Backbone, Marionette, $, _) ->

  background_page_url = null

  DiscussionModalOnFrontend.addInitializer ->
    background_page_url = currentUser.streamLink()

    FactlinkApp.vent.on 'close_discussion_modal', ->
      Backbone.history.navigate background_page_url, false
      FactlinkApp.discussionModalRegion.close()

  DiscussionModalOnFrontend.openDiscussion = (fact, url=background_page_url) ->
    Backbone.history.navigate fact.get('url'), false
    background_page_url = Backbone.history.getFragment(url)

    newClientModal = new DiscussionModalContainer
    FactlinkApp.discussionModalRegion.show newClientModal
    newClientModal.mainRegion.show new NDPDiscussionView model: fact

  # HACK: This assumes that we use "navigate url, true" for all url changes that
  # leave the main regions alone (sidebar, mainRegion, etc)
  DiscussionModalOnFrontend.loadUrlStopRouting = (fragment) ->
    FactlinkApp.discussionModalRegion.close()

    if fragment == background_page_url
      background_page_url = null
      true
    else
      background_page_url = null
      false

Use clearer default for url

Use clearer default for url


cp = require('child_process')

getBranchFromRef = (ref) ->
  refParts = ref.split("/")
  return refParts[refParts.length-1]

exports.index = (req, res) ->
  parsedPayload = JSON.parse(req.body.payload)

  console.log "Got deploy message from #{parsedPayload.ref}"
  unless getBranchFromRef(parsedPayload.ref) is "deploy"
    console.log "Ignoring, only deploys on pushes from deploy"
    return res.send 500, "Only commits from deploy branch are accepted"

  console.log "Forking #{process.cwd()}/bin/deploy.coffee"
  cp.fork("#{process.cwd()}/bin/deploy.coffee")
  console.log "Forked!"

  res.send 200

cp = require('child_process')

getBranchFromRef = (ref) ->
  refParts = ref.split("/")
  return refParts[refParts.length-1]

exports.index = (req, res) ->
  parsedPayload = JSON.parse(req.body.payload)

  console.log "Got deploy message from #{parsedPayload.ref}"
  unless getBranchFromRef(parsedPayload.ref) is "deploy"
    console.log "Ignoring, only deploys on pushes from deploy"
    return res.send 500, "Only commits from deploy branch are accepted"

  console.log "Forking #{process.cwd()}/bin/deploy.coffee"

  deployProcess = cp.fork("#{process.cwd()}/bin/deploy.coffee")
  theProcess.stdout.on('data', (data) ->
    console.log(data.toString())
  )
  theProcess.stderr.on('data', (data) ->
    console.log("Error: #{data.toString()}")
  )

  console.log "Forked!"

  res.send 200

Add error logging to process forking

Add error logging to process forking


_ = require "underscore"
LayoutDOM = require "./layout_dom"
p = require "../../core/properties"

class BaseBox extends LayoutDOM.Model
  type: "BaseBox"

  @define {
      children: [ p.Array, [] ]
    }

module.exports =
  Model: BaseBox

_ = require "underscore"
LayoutDOM = require "./layout_dom"
p = require "../../core/properties"

class BaseBox extends LayoutDOM.Model
  type: "BaseBox"

  @define {
      children: [ p.Array, [] ]
    }

  get_layoutable_children: () ->
    return @get('children')

  get_edit_variables: () ->
    edit_variables = super()
    # Go down the children to pick up any more constraints
    for child in @get_layoutable_children()
      edit_variables = edit_variables.concat(child.get_edit_variables())
    return edit_variables

  get_constraints: () ->
    constraints = super()
    # Go down the children to pick up any more constraints
    for child in @get_layoutable_children()
      constraints = constraints.concat(child.get_constraints())
    return constraints

module.exports =
  Model: BaseBox

Make sure VBox and HBox still work

Make sure VBox and HBox still work

Not adding tests because about to remove HBox and VBox


gulp = require 'gulp'
coffee = require 'gulp-coffee'
mocha = require 'gulp-mocha'

src = './src/*.coffee'
test = './test/*.coffee'

gulp.task('compile', ->
  gulp.src(src)
    .pipe(coffee())
    .pipe(gulp.dest('./src'))
)

gulp.task('test', ->
  gulp.src(test)
    .pipe(mocha({ reporter: 'list' }))
)

gulp.task('watch', ->
  gulp.watch([src, test], ['test'])
)

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

gulp = require 'gulp'
coffee = require 'gulp-coffee'
mocha = require 'gulp-mocha'

src = './src/*.coffee'
test = './test/*.coffee'

gulp.task('compile', ->
  gulp.src(src)
    .pipe(coffee())
    .pipe(gulp.dest('./src'))
)

gulp.task('test', ->
  gulp.src(test)
    .pipe(mocha({ reporter: 'list' }))
)

gulp.task('watch', ->
  gulp.watch([src, test], ['test'])
)

gulp.task('default', ['compile', 'test'])

Make default gulp task compile and test

Make default gulp task compile and test


path = require 'path'
_ = require 'underscore-plus'
ImageEditor = require './image-editor'

module.exports =
  activate: ->
    atom.project.registerOpener(openUri)

  deactivate: ->
    atom.project.unregisterOpener(openUri)

# Files with these extensions will be opened as images
imageExtensions = ['.gif', '.ico', '.jpeg', '.jpg', '.png']
openUri = (uriToOpen) ->
  uriExtension = path.extname(uriToOpen)
  if _.include(imageExtensions, uriExtension)
    new ImageEditor(uriToOpen)

path = require 'path'
_ = require 'underscore-plus'
ImageEditor = require './image-editor'

module.exports =
  activate: ->
    atom.workspace.registerOpener(openUri)

  deactivate: ->
    atom.workspace.unregisterOpener(openUri)

# Files with these extensions will be opened as images
imageExtensions = ['.gif', '.ico', '.jpeg', '.jpg', '.png']
openUri = (uriToOpen) ->
  uriExtension = path.extname(uriToOpen)
  if _.include(imageExtensions, uriExtension)
    new ImageEditor(uriToOpen)

Use opener methods from workspace instead of project


RubocopAutoCorrect = require './rubocop-auto-correct'

module.exports =
  config:
    rubocopCommandPath:
      description: 'If command doesnot work, please input rubocop full path. example: /Users/<username>/.rbenv/shims/rubocop)'
      type: 'string'
      default: 'rubocop'
    autoRun:
      description: 'When you save the buffer, Automatically run Rubocop auto correct, But, need to run manually once at window'
      type: 'boolean'
      default: false
    notification:
      description: 'If you want to disable notification, Please remove the check'
      type: 'boolean'
      default: true
    correctFile:
      description: 'When enabled, correct directly in the file (Don\'t need to save)'
      type: 'boolean'
      default: false

  activate: ->
    @rubocopAutoCorrect = new RubocopAutoCorrect()

  deactivate: ->
    @rubocopAutoCorrect?.destroy()
    @rubocopAutoCorrect = null

RubocopAutoCorrect = require './rubocop-auto-correct'

module.exports =
  config:
    rubocopCommandPath:
      description: 'If the command does not work, please input rubocop full path here. Example: /Users/<username>/.rbenv/shims/rubocop)'
      type: 'string'
      default: 'rubocop'
    autoRun:
      description: 'When you save the buffer, automatically it runs Rubocop auto correct. You need to run manually once at window before you use the option.'
      type: 'boolean'
      default: false
    notification:
      description: 'When this option is disabled, you do not receive any notifications even thought a file is corrected.'
      type: 'boolean'
      default: true
    correctFile:
      description: 'You can correct a file directly if you enable this option. You do not need to save file after correcting it.'
      type: 'boolean'
      default: false

  activate: ->
    @rubocopAutoCorrect = new RubocopAutoCorrect()

  deactivate: ->
    @rubocopAutoCorrect?.destroy()
    @rubocopAutoCorrect = null

Correct the descriptions of the options

Correct the descriptions of the options


module.exports = {

  name: "UX Markup"
  namespace: "ux"
  fallback: ['xml']

  ###
  Supported Grammars
  ###
  grammars: [
    "UX"
  ]

  ###
  Supported extensions
  ###
  extensions: [
    "ux"
  ]

  defaultBeautifier: "Pretty Diff"
}

module.exports = {

  name: "UX Markup"
  namespace: "ux"
  fallback: ['html']

  ###
  Supported Grammars
  ###
  grammars: [
    "UX"
  ]

  ###
  Supported extensions
  ###
  extensions: [
    "ux"
  ]

  defaultBeautifier: "Pretty Diff"
}

Change fallback for UX Markup from xml back to html


should = require 'should'
KDCustomHTMLView = require '../../lib/core/customhtmlview'

describe 'KDCustomHTMLView', ->
  it 'exists', ->
    KDCustomHTMLView.should.exist

  describe 'constructor', ->
    it 'should instantiate without error', ->
      router = new KDCustomHTMLView
      router.should.exist

should = require 'should'
KDCustomHTMLView = require '../../lib/core/customhtmlview'

describe 'KDCustomHTMLView', ->
  it 'exists', ->
    KDCustomHTMLView.should.exist

  describe 'constructor', ->
    it 'should instantiate without error', ->
      view = new KDCustomHTMLView
      view.should.exist

    it 'should set tag name', ->
      htmlView = new KDCustomHTMLView 'koding'
      htmlView.tagName.should.equal 'koding'

    it 'should set link attributes', ->
      htmlView = new KDCustomHTMLView {
        tagName: 'a'
      }

      htmlView.options.attributes.href.should.equal '#'

Add more tests to custom html view

Add more tests to custom html view


# detect tweet URL and send tweet content
module.exports = (robot) ->
	robot.hear /https?:\/\/(mobile\.)?twitter\.com\/.*?\/status\/([0-9]+)/i, (msg) ->
		msg.http("https://api.twitter.com/1/statuses/show/#{msg.match[2]}.json").get() (err, res, body) ->
			return if err or (res.statusCode != 200)

			tweet = JSON.parse(body)

			msg.send "@#{tweet.user.name}: #{tweet.text}"

# detect tweet URL and send tweet content
module.exports = (robot) ->
	robot.hear /https?:\/\/(mobile\.)?twitter\.com\/.*?\/status\/([0-9]+)/i, (msg) ->
		msg.http("https://api.twitter.com/1/statuses/show/#{msg.match[2]}.json").get() (err, res, body) ->
			return if err or (res.statusCode != 200)

			tweet = JSON.parse(body)

			msg.send "@#{tweet.user.screen_name}: #{tweet.text}"

Use screen_name instead of name

Use screen_name instead of name


# Copyright (c) 2015 Markus Kohlhase <mail@markus-kohlhase.de>

React     = require "react"
PureMixin = require "react-pure-render/mixin"

{ NAMES, CSS_CLASSES } = require "../constants/Categories"

{ div, p, h3, button, span, i } = React.DOM

module.exports = React.createClass

  displayName: "EntryDetails"

  mixins: [ PureMixin ]

  render: ->
    { entry } = @props
    clz = CSS_CLASSES[entry.categories?[0]]
    div className: "entry-detail #{clz}",
      div className: "category",
        span null, NAMES[entry.categories?[0]]
      div null,
        h3 null, entry.title
        p null, entry.description
        p null, entry.homepage
        p null, entry.phone
        div null,
          button
            onClick: @props.onClose
            className: "pure-button",
              i className: "fa fa-chevron-left"
              "zurück"
          button
            onClick: @props.onEdit
            className: "pure-button",
              i className: "fa fa-pencil"
              "bearbeiten"

# Copyright (c) 2015 Markus Kohlhase <mail@markus-kohlhase.de>

React     = require "react"
PureMixin = require "react-pure-render/mixin"

{ NAMES, CSS_CLASSES } = require "../constants/Categories"

{ div, p, h3, button, span, i, a } = React.DOM

module.exports = React.createClass

  displayName: "EntryDetails"

  mixins: [ PureMixin ]

  render: ->
    { entry } = @props
    clz = CSS_CLASSES[entry.categories?[0]]
    div className: "entry-detail #{clz}",
      div className: "category",
        span null, NAMES[entry.categories?[0]]
      div null,
        h3 null, entry.title
        p null, entry.description
        p null, a href: entry.homepage, entry.homepage
        p null, entry.phone
        div null,
          button
            onClick: @props.onClose
            className: "pure-button",
              i className: "fa fa-chevron-left"
              "zurück"
          button
            onClick: @props.onEdit
            className: "pure-button",
              i className: "fa fa-pencil"
              "bearbeiten"

fix(details): Make Homepage field clickable/a hyperlink


import logger from '../log'
request = require('request-promise-native')

sleep = (milliseconds) ->
    return new Promise((resolve) -> setTimeout(resolve, milliseconds))

export default download = (href, jar, options) ->
    logger.debug "Downloading", href
    if not jar
        jar = request.jar()
    delay = 5
    for i in [1..10]
        try
            page = await request({
                options...,
                url: href
                jar: jar,
                gzip: true,
                timeout: options?.timeout || 7 * 1000
            })
            return page
        catch e
            logger.info "Error downloading " + href + " " + i + " will re-download"
            logger.info e.message
            await sleep(delay)
            delay *= 2
    throw "Can't download"

import logger from '../log'
request = require('request-promise-native')

sleep = (milliseconds) ->
    return new Promise((resolve) -> setTimeout(resolve, milliseconds))

export default download = (href, jar, options) ->
    logger.debug "Downloading", href
    if not jar
        jar = request.jar()
    delay = 5
    for i in [1..8]
        try
            page = await request({
                options...,
                url: href
                jar: jar,
                gzip: true,
                timeout: options?.timeout || 7 * 1000
            })
            return page
        catch e
            logger.info "Error downloading " + href + " " + i + " will re-download"
            logger.info e.message
            await sleep(delay)
            delay *= 2
    throw "Can't download"

Decrease number of attemts to make sure to fin in browser timeout


AccountsEntry =
  settings:
    wrapLinks: true
    homeRoute: '/home'
    dashboardRoute: '/dashboard'
    passwordSignupFields: 'EMAIL_ONLY'

  config: (appConfig) ->
    @settings = _.extend(@settings, appConfig)

    i18n.setDefaultLanguage "en_US"
    if appConfig.language
      i18n.setLanguage appConfig.language

    if appConfig.signUpTemplate
      Router.routes = _.reject Router.routes, (e, i) ->
        e.name is 'entrySignUp'

      Router.map ->
        @route 'signUp',
          path: 'sign-up',
          template: appConfig.signUpTemplate

@AccountsEntry = AccountsEntry

AccountsEntry =
  settings:
    wrapLinks: true
    homeRoute: '/home'
    dashboardRoute: '/dashboard'
    passwordSignupFields: 'EMAIL_ONLY'

  config: (appConfig) ->
    @settings = _.extend(@settings, appConfig)

    i18n.setDefaultLanguage "en"
    if appConfig.language
      i18n.setLanguage appConfig.language

    if appConfig.signUpTemplate
      Router.routes = _.reject Router.routes, (e, i) ->
        e.name is 'entrySignUp'

      Router.map ->
        @route 'signUp',
          path: 'sign-up',
          template: appConfig.signUpTemplate

@AccountsEntry = AccountsEntry

Update language to match default

Update language to match default

`en_US` != `en`

It may be better to reject this fix and change english.coffee, but I'm unsure if you want to use 2-char or 4-char codes.

Cheers


###
#    Copyright 2015-2017 ppy Pty. Ltd.
#
#    This file is part of osu!web. osu!web is distributed with the hope of
#    attracting more community contributions to the core ecosystem of osu!.
#
#    osu!web is free software: you can redistribute it and/or modify
#    it under the terms of the Affero GNU General Public License version 3
#    as published by the Free Software Foundation.
#
#    osu!web is distributed 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 osu!web.  If not, see <http://www.gnu.org/licenses/>.
###

class @FormToggle
  constructor: ->
    $(document).on 'change', '.js-form-toggle--input', @togglePollForm

  togglePollForm: (e) =>
    show = e.currentTarget.checked
    id = e.currentTarget.dataset.formToggleId
    $form = $(".js-form-toggle--form[data-form-toggle-id='#{id}']")

    direction = if show then 'Down' else 'Up'
    $form.stop()["slide#{direction}"]()

###
#    Copyright 2015-2017 ppy Pty. Ltd.
#
#    This file is part of osu!web. osu!web is distributed with the hope of
#    attracting more community contributions to the core ecosystem of osu!.
#
#    osu!web is free software: you can redistribute it and/or modify
#    it under the terms of the Affero GNU General Public License version 3
#    as published by the Free Software Foundation.
#
#    osu!web is distributed 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 osu!web.  If not, see <http://www.gnu.org/licenses/>.
###

class @FormToggle
  constructor: ->
    addEventListener 'turbolinks:load', @sync
    $(document).on 'change', '.js-form-toggle--input', @onChange

  onChange: (e) =>
    @toggle e.currentTarget

  sync: =>
    inputs = document.getElementsByClassName('js-form-toggle--input')

    @toggle(input) for input in inputs

  toggle: (input) ->
    id = input.dataset.formToggleId
    show = input.checked

    $form = $(".js-form-toggle--form[data-form-toggle-id='#{id}']")

    direction = if show then 'Down' else 'Up'
    $form.stop()["slide#{direction}"]()

Synchronize the fancy toggle its related form on load

Because some browsers restore previous state on reload.


# See https://atom.io/docs/latest/hacking-atom-package-word-count#menus for more details
'context-menu':
  'atom-text-editor': [
    {
      'label': 'Toggle titanium'
      'command': 'titanium:toggle'
    }
  ]
'menu': [
  {
    'label': 'Packages'
    'submenu': [
      'label': 'titanium'
      'submenu': [
        {
          'label': 'Toggle'
          'command': 'titanium:toggle'
        }
      ]
    ]
  }
]

'context-menu':
  'atom-text-editor': [
    {
      'label': 'Alloy'
      'submenu': [
        {
          'label': 'Open view'
          'command': 'titanium:openView'
        }
        {
          'label': 'Open style'
          'command': 'titanium:openStyle'
        }
        {
          'label': 'Open controller'
          'command': 'titanium:openController'
        }
        {
          'label': 'Open related'
          'command': 'titanium:openAllRelatedFiles'
        }
        {
          'label': 'Clode related'
          'command': 'titanium:closeAllRelatedPanes'
        }
      ]
    }
  ]

Add context menu items to open related files


Backbone = require 'backbone'
cookies = require 'cookies-js'
{ keys, each } = require 'underscore'

module.exports = class State extends Backbone.Model
  defaults:
    view_mode: 'grid'
    lightbox: false

  initialize: ->
    # set values from cookies
    each keys(@defaults), (key) =>
      @set(key, val) if val = cookies.get key

    @on 'change:view_mode', @setCookie

  setCookie: (model, value)->
    cookies.set keys(model.changed)[0], value

Backbone = require 'backbone'
cookies = require 'cookies-js'
{ keys, each } = require 'underscore'

module.exports = class State extends Backbone.Model
  defaults:
    view_mode: 'grid'
    lightbox: false
    sort: 'updated_at'

  initialize: ->
    # set values from cookies
    each keys(@defaults), (key) =>
      @set(key, val) if val = cookies.get key

    @on 'change:view_mode', @setCookie
    @on 'change:sort', @setCookie

  setCookie: (model, value)->
    cookies.set keys(model.changed)[0], value

Add default sort and sets cookie when sort changes


class AddWorkspaceView extends KDCustomHTMLView

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

    options.cssClass = 'add-workpace-view'

    super options, data

    @addSubView new KDCustomHTMLView tagName: 'figure'

    @addSubView @input  = new KDInputView
      type     : 'text'
      keydown  : @bound 'handleKeyDown'

    @addSubView @cancel = new KDCustomHTMLView
      cssClass : 'cancel'
      click    : @bound 'destroy'

  handleKeyDown: (event) ->

    if event.which is 13
      data           = @getData()
      options        =
        name         : @input.getValue()
        machineUId   : data.machineUId
        machineLabel : data.machineLabel

      {activitySidebar} = KD.getSingleton 'mainView'

      activitySidebar.createNewWorkspace options

class AddWorkspaceView extends KDCustomHTMLView

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

    options.cssClass = 'add-workpace-view'

    super options, data

    @addSubView new KDCustomHTMLView tagName: 'figure'

    @addSubView @input  = new KDInputView
      type     : 'text'
      keydown  : @bound 'handleKeyDown'

    @addSubView @cancel = new KDCustomHTMLView
      cssClass : 'cancel'
      click    : @bound 'destroy'

  click: ->
    return no

  handleKeyDown: (event) ->

    if event.which is 13
      data           = @getData()
      options        =
        name         : @input.getValue()
        machineUId   : data.machineUId
        machineLabel : data.machineLabel

      {activitySidebar} = KD.getSingleton 'mainView'

      activitySidebar.createNewWorkspace options

AddWorkspaceView: Return no when clicked to prevent node changing on sidebar.


App.todo = App.cable.subscriptions.create "TodoChannel",
  connected: ->
    # Called when the subscription is ready for use on the server

  disconnected: ->
    # Called when the subscription has been terminated by the server

App.todo = App.cable.subscriptions.create {
  channel: "TodoChannel"
  },
  connected: ->
    # Called when the subscription is ready for use on the server

  disconnected: ->
    # Called when the subscription has been terminated by the server

Send explicit parameters in channel subscription


App.UsersIndexView = Ember.View.extend
  layoutName: "settings"
  classNames: ["settings"]

App.UsersNewView = Ember.View.extend
  layoutName: "settings"
  classNames: ["settings"]

App.UserEditView = Ember.View.extend
  layoutName: "settings"
  classNames: ["settings"]

App.RoomsView = Ember.View.extend
  layoutName: "settings"
  classNames: ["settings"]

App.UsersIndexView = Ember.View.extend
  layoutName: "settings"
  classNames: ["settings"]

App.UsersNewView = Ember.View.extend
  layoutName: "settings"
  classNames: ["settings"]

App.UserEditView = Ember.View.extend
  layoutName: "settings"
  classNames: ["settings"]

App.RoomsIndexView = Ember.View.extend
  layoutName: "settings"
  classNames: ["settings"]

App.RoomsNewView = Ember.View.extend
  layoutName: "settings"
  classNames: ["settings"]

App.RoomEditView = Ember.View.extend
  layoutName: "settings"
  classNames: ["settings"]

Define views for Room management

Define views for Room management