window.Tahi = {}
Tahi.papers =
  init: ->
    $('#add_author').on 'click', (e) ->
      e.preventDefault()
      $('<li class="author">').appendTo $('ul.authors')

$(document).ready ->
  if $("[contenteditable]").length > 0
    for elementId in ['body_editable', 'abstract_editable']
      CKEDITOR.inline elementId,
        extraPlugins: 'sharedspace'
        removePlugins: 'floatingspace,resize'
        sharedSpaces:
          top: 'ckeditor-toolbar'
        toolbar: [
          [ 'Styles', 'Format', 'FontSize' ]
          [ 'Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', '-', 'RemoveFormat' ]
          [ 'NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', 'Blockquote', 'CreateDiv']
          [ 'PasteFromWord' ],
          [ 'Link', 'Unlink', 'Anchor']
          [ 'Find', 'Replace', '-', 'Scayt', '-', 'ShowBlocks' ]
        ]

  $('#save_button').on 'click', (e) ->
    e.preventDefault()
    $.ajax
      url: $(this).data('url') + '.json'
      method: "PUT"
      data:
        paper:
          title: $.trim($('#title_editable').text())
          body: CKEDITOR.instances.body_editable.getData()
          abstract: CKEDITOR.instances.abstract_editable.getData()
          short_title: $.trim($('#short_title_editable').text())
      success:
        window.location = "/"
  Tahi.papers.init()

window.Tahi = {}
Tahi.papers =
  init: ->
    $('#add_author').on 'click', (e) ->
      e.preventDefault()
      $('<li class="author">').appendTo $('ul.authors')

$(document).ready ->
  if $("[contenteditable]").length > 0
    for elementId in ['body_editable', 'abstract_editable']
      CKEDITOR.inline elementId,
        extraPlugins: 'sharedspace'
        removePlugins: 'floatingspace,resize'
        sharedSpaces:
          top: 'ckeditor-toolbar'
        toolbar: [
          [ 'Styles', 'Format', 'FontSize' ]
          [ 'Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', '-', 'RemoveFormat' ]
          [ 'NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', 'Blockquote', 'CreateDiv', 'Table']
          [ 'PasteFromWord' ],
          [ 'Link', 'Unlink', 'Anchor']
          [ 'Find', 'Replace', '-', 'Scayt', '-', 'ShowBlocks' ]
        ]

  $('#save_button').on 'click', (e) ->
    e.preventDefault()
    $.ajax
      url: $(this).data('url') + '.json'
      method: "PUT"
      data:
        paper:
          title: $.trim($('#title_editable').text())
          body: CKEDITOR.instances.body_editable.getData()
          abstract: CKEDITOR.instances.abstract_editable.getData()
          short_title: $.trim($('#short_title_editable').text())
      success:
        window.location = "/"
  Tahi.papers.init()

Add tables to CKEditor toolbar

Add tables to CKEditor toolbar

We want the editor to support tables.


RocketChat.setUsername = (user, username) ->
	username = s.trim username
	if not user or not username
		return false

	if not /^[0-9a-zA-Z-_.]+$/.test username
		return false

	# User already has desired username, return
	if user.username is username
		return user

	# Check username availability
	unless RocketChat.checkUsernameAvailability username
		return false

	previousUsername = user.username

	# Username is available; if coming from old username, update all references
	if previousUsername
		RocketChat.models.Messages.updateAllUsernamesByUserId user._id, username
		RocketChat.models.Messages.updateUsernameOfEditByUserId user._id, username

		RocketChat.models.Messages.findByMention(previousUsername).forEach (msg) ->
			updatedMsg = msg.msg.replace(new RegExp("@#{previousUsername}", "ig"), "@#{username}")
			RocketChat.models.Messages.updateUsernameAndMessageOfMentionByIdAndOldUsername msg._id, previousUsername, username, updatedMsg

		RocketChat.models.Rooms.replaceUsername previousUsername, username
		RocketChat.models.Rooms.replaceUsernameOfUserByUserId user._id, username

		RocketChat.models.Subscriptions.setUserUsernameByUserId user._id, username
		RocketChat.models.Subscriptions.setNameForDirectRoomsWithOldName previousUsername, username

	# Set new username
	RocketChat.models.Users.setUsername user._id, username
	user.username = username
	return user

RocketChat.setUsername = (user, username) ->
	username = s.trim username
	if not user or not username
		return false

	if not /^[0-9a-zA-Z-_.]+$/.test username
		return false

	# User already has desired username, return
	if user.username is username
		return user

	# Check username availability
	unless RocketChat.checkUsernameAvailability username
		return false

	previousUsername = user.username

	# If first time setting username, send Enrollment Email
	if not previousUsername and RocketChat.settings.get 'Accounts_Enrollment_Email'
		Accounts.sendEnrollmentEmail(user._id)

	# Username is available; if coming from old username, update all references
	if previousUsername
		RocketChat.models.Messages.updateAllUsernamesByUserId user._id, username
		RocketChat.models.Messages.updateUsernameOfEditByUserId user._id, username

		RocketChat.models.Messages.findByMention(previousUsername).forEach (msg) ->
			updatedMsg = msg.msg.replace(new RegExp("@#{previousUsername}", "ig"), "@#{username}")
			RocketChat.models.Messages.updateUsernameAndMessageOfMentionByIdAndOldUsername msg._id, previousUsername, username, updatedMsg

		RocketChat.models.Rooms.replaceUsername previousUsername, username
		RocketChat.models.Rooms.replaceUsernameOfUserByUserId user._id, username

		RocketChat.models.Subscriptions.setUserUsernameByUserId user._id, username
		RocketChat.models.Subscriptions.setNameForDirectRoomsWithOldName previousUsername, username

	# Set new username
	RocketChat.models.Users.setUsername user._id, username
	user.username = username
	return user

Send e-mail on first username set

Send e-mail on first username set


Meteor.methods
	restart_server: ->
		if not Meteor.userId()
			throw new Meteor.Error 'invalid-user', "[methods] restart_server -> Invalid user"

		unless RocketChat.authz.hasRole( Meteor.userId(), 'admin') is true
			throw new Meteor.Error 'not-authorized', '[methods] restart_server -> Not authorized'

		Meteor.setTimeout ->
			process.exit(0)
		, 2000

		return {} =
			message: "The_server_will_restart_in_s_seconds"
			params: [2]

Meteor.methods
	restart_server: ->
		if not Meteor.userId()
			throw new Meteor.Error 'invalid-user', "[methods] restart_server -> Invalid user"

		unless RocketChat.authz.hasRole( Meteor.userId(), 'admin') is true
			throw new Meteor.Error 'not-authorized', '[methods] restart_server -> Not authorized'

		Meteor.setTimeout ->
			process.exit(1)
		, 2000

		return {} =
			message: "The_server_will_restart_in_s_seconds"
			params: [2]

Change to process.exit(1) to restart server


# @todo: build main switch/accordeon for filter-form

initFilterForm = ->

  filterExpand = ->

    $filterForm = $('.filter-form')
    startHeight = $('.col-form-inner:first').find('.radio_buttons').height() * 3.5
    expandLabel = 'Mehr Filter anzeigen'
    collapseLabel = 'Weniger Filter anzeigen'

    $filterForm
            .addClass 'filter-form--isCollapsed'
            .height startHeight

    if !$('.filter-form__expander').length
      $filterForm.prepend '
' + expandLabel + '
'

    $expander = $('.filter-form__expander')

    $expander.on 'click', ->

      if $filterForm.hasClass('filter-form--isCollapsed')
        $expander.html collapseLabel
        $filterForm
            .css 'height', 'auto'
            .removeClass 'filter-form--isCollapsed'
      else
        $expander.html expandLabel
        $filterForm
              .css 'height', startHeight
              .addClass 'filter-form--isCollapsed'

  filterExpand()

$(document).ready initFilterForm
$(document).on 'page:load', initFilterForm
#$(window).on 'resize', initFilterForm

# @todo: build main switch/accordeon for filter-form

initFilterForm = ->

  filterExpand = ->

    $filterForm = $('.filter-form')
    startHeight = $('.col-form-inner:first').find('.radio_buttons').height() * 3.5
    expandLabel = 'Mehr Filter anzeigen'
    collapseLabel = 'Weniger Filter anzeigen'

    $filterForm
            .addClass 'filter-form--isCollapsed'
            .height startHeight

    if !$('.filter-form__expander').length
      $filterForm.prepend '
' + expandLabel + '
'
      $filterForm.prepend '
' + expandLabel + '
'

    $expander = $('.filter-form__expander')

    $expander.attr 'aria-expanded', false

    $expander.on 'click', ->

      if $filterForm.hasClass('filter-form--isCollapsed')
        $expander.html collapseLabel
        $expander
            .html collapseLabel
            .attr 'aria-expanded', true

        $filterForm
            .css 'height', 'auto'
            .removeClass 'filter-form--isCollapsed'
      else
        $expander.html expandLabel
        $expander
            .html expandLabel
            .attr 'aria-expanded', false

        $filterForm
              .css 'height', startHeight
              .addClass 'filter-form--isCollapsed'

  filterExpand()

$(document).ready initFilterForm
$(document).on 'page:load', initFilterForm
#$(window).on 'resize', initFilterForm

Add WAI/ARIA states for filter accordeon


#!/usr/bin/env coffee
http = require 'http'
util = require 'util'
crypto = require 'crypto'

name = process.argv[2]

version = process.argv[3] ? "latest"

deps = []

hash = crypto.createHash 'sha256'

http.get "http://registry.npmjs.org/#{name}", (res) ->
  res.setEncoding()
  val = ""
  res.on 'data', (chunk) ->
    val += chunk
  res.on 'end', ->
    pkginfo = JSON.parse val
    version = pkginfo['dist-tags'].stable ? pkginfo['dist-tags'].latest if version is "latest"
    http.get "http://registry.npmjs.org/#{name}/-/#{name}-#{version}.tgz", (res) ->
      res.on 'data', (chunk) ->
        hash.update chunk
      res.on 'end', ->
        process.stdout.write """
                             "#{name}" = buildNodePackage rec {
                               name = "#{name}-#{version}";
                               src = fetchurl {
                                 url = "http://registry.npmjs.org/#{name}/-/${name}.tgz";
                                 sha256 = "#{hash.digest('hex')}";
                               };
                               deps = [ #{("self.\"#{dep}\"" for dep in deps).join " "} ];
                             };
                             """
    deps = (key for key, value of (pkginfo.versions[version].dependencies ? {}))

#!/usr/bin/env coffee
http = require 'http'
util = require 'util'
crypto = require 'crypto'

name = process.argv[2]

version = process.argv[3] ? "latest"

deps = []

hash = crypto.createHash 'sha256'

http.get "http://registry.npmjs.org/#{name}", (res) ->
  res.setEncoding()
  val = ""
  res.on 'data', (chunk) ->
    val += chunk
  res.on 'end', ->
    pkginfo = JSON.parse val
    version = pkginfo['dist-tags'].stable ? pkginfo['dist-tags'].latest if version is "latest"
    http.get "http://registry.npmjs.org/#{name}/-/#{name}-#{version}.tgz", (res) ->
      res.on 'data', (chunk) ->
        hash.update chunk
      res.on 'end', ->
        process.stdout.write """
                             #{}  "#{name}" = self."#{name}-#{version}";

                               "#{name}-#{version}" = buildNodePackage rec {
                                 name = "#{name}-#{version}";
                                 src = fetchurl {
                                   url = "http://registry.npmjs.org/#{name}/-/${name}.tgz";
                                   sha256 = "#{hash.digest('hex')}";
                                 };
                                 deps = [
                             #{("      self.\"#{dep}#{if ver is "*" then "" else "-#{ver}"}\"" for dep, ver of deps).join "\n"}
                                 ];
                               };
                             """
    deps = pkginfo.versions[version].dependencies ? {}

Put versions in to generated attribute names, indent generated code


'body':
  'meta-t': 'fuzzy-finder:toggle-file-finder'
  'meta-b': 'fuzzy-finder:toggle-buffer-finder'
  'ctrl-.': 'fuzzy-finder:find-under-cursor'
  'meta-T': 'fuzzy-finder:toggle-git-status-finder'

'body':
  'meta-t': 'fuzzy-finder:toggle-file-finder'
  'meta-b': 'fuzzy-finder:toggle-buffer-finder'
  'ctrl-.': 'fuzzy-finder:find-under-cursor'
  'meta-B': 'fuzzy-finder:toggle-git-status-finder'

Update status finder keybinding to meta-B


FactlinkJailRoot.showProxyMessage = ->
  content = """

      Factlink demo page

Get the extension to add discussions on every site

Or install Factlink on your own site

  """

  frame = new FactlinkJailRoot.ControlIframe content

  frame.$el.css
    top: '10px'
    left: 'auto'
    right: '10px'
    position: 'fixed'

  frame.fadeIn()

FactlinkJailRoot.showProxyMessage = ->
  content = """

      Factlink demo page

Get the extension to add discussions on every site

Or install Factlink on your own site

  """

  frame = new FactlinkJailRoot.ControlIframe content

  frame.$el.css
    top: '10px'
    left: '10px'
    position: 'fixed'

  frame.fadeIn()

Put notice on the left, on the right didn't *feel* right (pun intended).

If someone who owns a site opens Factlink in the proxy and is all like, what the heck is this doing on my site, they should always see some kind of explanation. Hiding away the explanation under the sidebar seems therefore not a good idea.


module.exports = class
  @_addition: (a, b) -> (a + b)
  @_multiplication: (a, b) -> (a * b)

  @sum: (array) =>
    array.reduce(@_addition, 0)

  @product: (array) =>
    array.reduce(@_multiplication, 0)

module.exports = class
  @_addition: (a, b) -> (a + b)
  @_multiplication: (a, b) -> (a * b)

  @sum: (array) =>
    array.reduce(@_addition, 0)

  @reverseSum: (array) =>
    array.reverse().reduce(@_addition, 0)

  @product: (array) =>
    array.reduce(@_multiplication, 0)

  @reverseProduct: (array) =>
    array.reverse().reduce(@_multiplication, 0)

Add some more hashing functions

Add some more hashing functions


# Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
# See the LICENCE file in the repository root for full licence text.

import * as React from 'react'
import { div } from 'react-dom-factories'
import { nextVal } from 'utils/seq'
el = React.createElement

bn = 'notification-banner-v2'
notificationBanners = document.getElementsByClassName('js-notification-banners')

export class NotificationBanner extends React.PureComponent
  constructor: (props) ->
    super props

    @eventId = "notification-banner-#{nextVal()}"
    @createPortalContainer()

  componentDidMount: =>
    $(document).on "turbolinks:before-cache.#{@eventId}", @removePortalContainer

  componentWillUnmount: =>
    $(document).off ".#{@eventId}"
    @removePortalContainer()

  render: =>
    notification =
      div className: "#{bn} #{bn}--#{@props.type}",
        div className: "#{bn}__col #{bn}__col--icon"
        div className: "#{bn}__col #{bn}__col--label",
          div className: "#{bn}__type", @props.type
          div className: "#{bn}__text", @props.title
        div className: "#{bn}__col",
          div className: "#{bn}__text", @props.message
    ReactDOM.createPortal notification, @portalContainer

  removePortalContainer: =>
    @portalContainer.remove()

  createPortalContainer: =>
    @portalContainer = document.createElement 'div'
    notificationBanners[0].appendChild @portalContainer

# Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
# See the LICENCE file in the repository root for full licence text.

import * as React from 'react'
import { div } from 'react-dom-factories'
import { nextVal } from 'utils/seq'
el = React.createElement

bn = 'notification-banner-v2'
notificationBanners = document.getElementsByClassName('js-notification-banners')

export class NotificationBanner extends React.PureComponent
  constructor: (props) ->
    super props

    @eventId = "notification-banner-#{nextVal()}"
    @createPortalContainer()

  componentDidMount: =>
    $(document).on "turbolinks:before-cache.#{@eventId}", @removePortalContainer

  componentWillUnmount: =>
    $(document).off ".#{@eventId}"
    @removePortalContainer()

  render: =>
    notification =
      div className: "#{bn} #{bn}--#{@props.type}",
        div className: "#{bn}__col #{bn}__col--icon"
        div className: "#{bn}__col #{bn}__col--label",
          div className: "#{bn}__type", @props.type
          div className: "#{bn}__text", @props.title
        div className: "#{bn}__col",
          div className: "#{bn}__text", @props.message
    ReactDOM.createPortal notification, @portalContainer

  removePortalContainer: =>
    @portalContainer.remove()

  createPortalContainer: =>
    @portalContainer = document.createElement 'div'
    notificationBanners = (window.newBody ? document.body).querySelector('.js-notification-banners')
    notificationBanners.appendChild @portalContainer

Use newBody for attaching portals

Use newBody for attaching portals


# Preamble
_.mixin(_.str.exports())
#rx =  require('../../reactive-coffee/src/reactive')
rx = require 'reactive-coffee'

# Dependencies

{my} = require('./my')
{god} = require('./god')
{config} = require('./config')
{draw} = require('./draw')
{test} = require('./test') if my.test

world = god(rx, config)

main = ->
  $('body').append(
    draw world.find_child('layout')
  )

# Instantiate our main view
$(main)
# Run Tests
test(rx) if my.test

# Preamble
_.mixin(_.str.exports())
#rx =  require('../../reactive-coffee/src/reactive')
rx = require 'reactive-coffee'

# Dependencies

{my} = require('./my')
{god} = require('./god')
{config} = require('./config')
{draw} = require('./draw')
{test} = require('./test') if my.test

world = god(rx, config)

div = rx.rxt.tags.div
main = ->
  $('body').append(
    div {id: "controls"}
    div {id: "grid"}
    div {id: "inspector"}
    draw world.find_child('layout')
    #render world.find_child('ui')
  )

# Instantiate our main view
$(main)
# Run Tests
test(rx) if my.test

Create divs for each section"

Create divs for each section"


Lint = require '../lib/lint'

# Use the command `window:run-package-specs` (cmd-alt-ctrl-p) to run specs.
#
# To run a specific `it` or `describe` block add an `f` to the front (e.g. `fit`
# or `fdescribe`). Remove the `f` to unfocus the block.

describe 'Lint', ->
  it 'MUST have specs :)'

Lint = require '../lib/atom-lint'

# Use the command `window:run-package-specs` (cmd-alt-ctrl-p) to run specs.
#
# To run a specific `it` or `describe` block add an `f` to the front (e.g. `fit`
# or `fdescribe`). Remove the `f` to unfocus the block.

describe 'atom-lint', ->
  it 'MUST have specs :)'

Fix forgotten renaming in spec

Fix forgotten renaming in spec


class TopFactEvidenceLayoutView extends Backbone.Marionette.Layout
  template: 'evidence/top_fact_evidence_layout'

  regions:
    contentRegion: '.js-content-region'

  templateHelpers:
    type_css: ->
      switch @type
        when 'believe' then 'supporting'
        when 'disbelieve' then 'weakening'
        when 'doubt' then 'unsure'

    is_unsure: -> @type == 'doubt'

  onRender: ->
    @contentRegion.show new InteractingUsersView model: @model

class window.TopFactEvidenceView extends Backbone.Marionette.CompositeView
  className: 'top-fact-evidence'
  template: 'evidence/top_fact_evidence'
  itemView: TopFactEvidenceLayoutView
  itemViewContainer: '.js-evidence-item-view-container'

class TopFactEvidenceLayoutView extends Backbone.Marionette.Layout
  template: 'evidence/top_fact_evidence_layout'

  regions:
    contentRegion: '.js-content-region'

  templateHelpers:
    type_css: ->
      switch @type
        when 'believe' then 'supporting'
        when 'disbelieve' then 'weakening'
        when 'doubt' then 'unsure'

    is_unsure: -> @type == 'doubt'

  onRender: ->
    @$el.toggle @model.get('impact') != ''
    @contentRegion.show new InteractingUsersView model: @model

class window.TopFactEvidenceView extends Backbone.Marionette.CompositeView
  className: 'top-fact-evidence'
  template: 'evidence/top_fact_evidence'
  itemView: TopFactEvidenceLayoutView
  itemViewContainer: '.js-evidence-item-view-container'

  initialize: ->
    @bindTo @collection, 'change:impact', @render

Update when impact changes and hide ones without impact


'use strict'

app.directive 'assetDisplay', [
  'constantService', '$timeout',
  (constants, $timeout) ->
    restrict: 'E'
    templateUrl: 'asset/display.html'
    scope:
      assetFn: '&asset'
    link: ($scope) ->
      asset = $scope.assetFn()
      $scope.asset = asset.inContext()
      $scope.readable = $scope.asset.checkPermission(constants.permission.VIEW)
      if $scope.asset != asset
        $scope.clip = asset.segment.relativeTo($scope.asset.segment)
      return
]

'use strict'

app.directive 'assetDisplay', [
  'constantService',
  (constants) ->
    restrict: 'E'
    templateUrl: 'asset/display.html'
    scope:
      assetFn: '&asset'
    link: ($scope) ->
      asset = $scope.assetFn()
      $scope.asset = asset.inContext()
      $scope.readable = $scope.asset.checkPermission(constants.permission.VIEW)
      if $scope.asset != asset
        $scope.clip = asset.segment.relativeTo($scope.asset.segment)
      return
]

Remove unused service dependency from assetDisplay


Model = require 'lib/model'

module.exports = class Template extends Model
  urlRoot: '/api/templates'
  idAttribute: 'filename'
  defaults:
    filename: ''
    contents: ''

Add defaults to File client model

Add defaults to File client model


Keymap = require 'keymap'
$ = require 'jquery'
_ = require 'underscore'

module.exports =
class App
  keymap: null
  windows: null
  tabText: null

  constructor: (@loadPath, nativeMethods)->
    @windows = []
    @setUpKeymap()
    @tabText = "  "

  setUpKeymap: ->
    @keymap = new Keymap()
    $(document).on 'keydown', (e) => @keymap.handleKeyEvent(e)
    @keymap.bindDefaultKeys()

  open: (url) ->
    $native.open url

  quit: ->
    $native.terminate null

  windowOpened: (window) ->
    @windows.push window

  windowClosed: (window) ->
    index = @windows.indexOf(window)
    @windows.splice(index, 1) if index >= 0

Keymap = require 'keymap'
$ = require 'jquery'
_ = require 'underscore'
require 'underscore-extensions'

module.exports =
class App
  keymap: null
  windows: null
  tabText: null

  constructor: (@loadPath, nativeMethods)->
    @windows = []
    @setUpKeymap()
    @tabText = "  "

  setUpKeymap: ->
    @keymap = new Keymap()
    $(document).on 'keydown', (e) => @keymap.handleKeyEvent(e)
    @keymap.bindDefaultKeys()

  open: (url) ->
    $native.open url

  quit: ->
    $native.terminate null

  windowOpened: (window) ->
    @windows.push window

  windowClosed: (window) ->
    _.remove(@windows, window)

Use _.remove to remove windows from atom.windows array


# I love this program.
#
# Atom will evaluate this file each time a new window is opened. It is run
# after packages are loaded/activated and after the previous editor state
# has been restored.
#
# An example hack to log to the console when each text editor is saved.
#
# atom.workspace.observeTextEditors (editor) ->
#   editor.onDidSave ->
#     console.log "Saved! #{editor.getPath()}"

# Hide scope-related notices shortly after displaying them
atom.workspace.notificationManager.onDidAddNotification (popup) ->
	if /^\s*Scopes at Cursor/.test(popup.message)
		setTimeout (->
			popup.dismiss()
		), atom.config.get("popupDismissDelay") || 1000

# Crude debugging method to see what events we can hook into
prot = atom.emitter.constructor.prototype
emit = prot.emit
global.traceEmissions = (active) ->
	if active
		prot.emit = () ->
			console.trace arguments
			emit.apply @, arguments
	else
		prot.emit = emit

# Disable that useless pending item feature
atom.workspace.onDidAddPaneItem ({pane}) -> pane.setPendingItem(null)

global.fi = atom.packages.loadedPackages["file-icons"]

Store a global reference to File-Icons package


{expect} = require 'chai'

# For some reason, this navbar component test will have warnings about setting
# state when component is unmounted if it's after some of the other specs.
# The tests still run and progress just fine despite the warnings, but for now,
# I'm leaving this test here.
# TODO figure out why.
require './components/course-listing.spec'
require './components/navbar.spec'
require './components/task-plan/homework/homework-plan.spec'
require './components/task-plan/homework/exercise-summary.spec'
require './components/task-plan/footer.spec'

require './components/task.spec'
require './components/task-homework.spec'
require './components/task-homework-past-due.spec'
require './components/practice.spec'
require './components/learning-guide.spec'
require './components/course-periods-nav.spec'
require './components/course-calendar.spec'
require './components/student-dashboard.spec'
require './components/reference-book.spec'
require './crud-store.spec'
require './task-store.spec'
require './loadable.spec'
require './teacher-task-plan-store.spec'
require './step-panel-policy.spec'
require './time.spec'
require './current-user-store.spec'
require './course-listing-store.spec'
require './app-config.spec'

# # This should be done **last** because it starts up the whole app
require './router.spec'

{expect} = require 'chai'

# For some reason, this navbar component test will have warnings about setting
# state when component is unmounted if it's after some of the other specs.
# The tests still run and progress just fine despite the warnings, but for now,
# I'm leaving this test here.
# TODO figure out why.
require './components/course-listing.spec'
require './components/navbar.spec'
require './components/homework/homework-plan.spec'
require './components/homework/exercise-summary.spec'
require './components/reading-plan.spec'

require './components/task.spec'
require './components/task-homework.spec'
require './components/task-homework-past-due.spec'
require './components/practice.spec'
require './components/learning-guide.spec'
require './components/course-periods-nav.spec'
require './components/course-calendar.spec'
require './components/student-dashboard.spec'
require './components/reference-book.spec'
require './crud-store.spec'
require './task-store.spec'
require './loadable.spec'
require './teacher-task-plan-store.spec'
require './step-panel-policy.spec'
require './time.spec'
require './current-user-store.spec'
require './course-listing-store.spec'
require './app-config.spec'

# # This should be done **last** because it starts up the whole app
require './router.spec'

Revert "adding in footer tests"

Revert "adding in footer tests"

This reverts commit 1fb940d58d5d73703317828d3ec70b2fb5af6813.


$(document).on 'turbolinks:load', ->
  page_id = $('.content').data('page-id')

  if page_id
    App.updates = App.cable.subscriptions.create { channel: "PageUpdateChannel", page_id: page_id },
      connected: ->
        # Called when the subscription is ready for use on the server
        console.log "Connected to " + App.updates.identifier

      disconnected: ->
        # Called when the subscription has been terminated by the server
        console.log "Disconnected from " + App.updates.identifier

      received: (data) ->
        # Called when there's incoming data on the websocket for this channel
        console.log "Received data from " + App.updates.identifier
        $('.content').html(data.html)
        new Init()

ActionCable: Fix missing time formatting on receiving update


Q = require 'q'
v = require 'validator'
m = require '../messages'
u = require '../utils'

exports.format = (model, property, options) ->
    d = Q.defer()
    if options.constructor is RegExp
        options =
            "with": options

    if !options.message?
        options.message = m.messages.invalid

    if u.isBlank model[property]()
        if not options.allowBlank
            model.addError property, options.message
    else if options['with'] && !options['with'].test model[property]()
        model.addError property, options.message
    else if options.without && options.without.test model[property]()
        model.addError property, options.message

    d.resolve()

    d.promise

Q = require 'q'
v = require 'validator'
m = require '../messages'
u = require '../utils'

exports.format = (model, property, options) ->
    d = Q.defer()
    if options.constructor is RegExp
        options =
            "with": options

    if !options.message?
        options.message = m.messages.invalid

    #Ensure regexp is valid
    regexpTest = options['with'].test
    if regexpTest is undefined
      throw {code: 500, message: "The specified regexp #{options['with']} is invalid.", status: "failure"}

    if u.isBlank model[property]()
        if not options.allowBlank
            model.addError property, options.message
    else if options['with'] && not options['with'].test model[property]()
        model.addError property, options.message
    else if options.without && options.without.test model[property]()
        model.addError property, options.message

    d.resolve()

    d.promise

Format Validator: Throw exception if regexp is invalid.


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

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

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

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

Remove Change Encoding from mini editors


# Tub class
YAML = require 'yamljs'
class TubConfig
  constructor: (tub, appdir, yml) ->
    # body...
    @dir = appdir
    @tub = tub
    @file = "#{@dir}/.jakhu/#{@tub}tub_config.yml"
    @yml = yml
    @yaml = {}
    fs.openSync @file
  create: (args) ->
    # body...
    @yaml.name = @tub
    @yaml.language = @yml.language
    @yaml.dir = "/app"
    @yaml.app = @yml.name
  write: (args) ->
    # body...
    @sy = YAML.stringify(@yaml)
    fs.appendFileSync @file, @sy

genTubConfig = (tub, appdir, yml) ->
  # body...
  tc = new DockerFileDefault(tub, appdir, yml)
  tc.create()
  tc.write()

module.exports = {genTubConfig: genTubConfig}

# Tub class
YAML = require 'yamljs'
class TubConfig
  constructor: (tub, appdir, yml) ->
    # body...
    @dir = appdir
    @tub = tub
    @file = "#{@dir}/.jakhu/#{@tub}tub_config.yml"
    @yml = yml
    @yaml = {}
    fs.openSync @file
  create: (args) ->
    # body...
    @yaml.name = @tub
    @yaml.language = @yml.language
    @yaml.dir = "/app"
    @yaml.app = @yml.name
  write: (args) ->
    # body...
    @sy = YAML.stringify(@yaml)
    fs.appendFileSync @file, @sy

genTubConfig = (tub, appdir, yml) ->
  # body...
  tc = new TubConfig(tub, appdir, yml)
  tc.create()
  tc.write()

module.exports = {genTubConfig: genTubConfig}

Fix bug to use correct constructor

Fix bug to use correct constructor


define [
    'underscore'
    'marionette'
    '../core'
    'tpl!templates/query/loader.html'
], (_, Marionette, c, templates...) ->

    templates = _.object ['loader'], templates

    class QueryLoader extends Marionette.ItemView
        className: 'query-loader'

        template: templates.loader

        ui:
            loadingMessage: '.loading-message'
            errorMessage: '.error-message'

        initialize: ->
            @data = {}

            if not (@data.context = @options.context)
                throw new Error 'context model required'
            if not (@data.view = @options.view)
                throw new Error 'view model required'
            if not (@data.queries = @options.queries)
                throw new Error 'queries collection required'

            @on 'router:load', @onRouterLoad

            @data.queries.on 'sync', @loadRequestedQuery

        onRouterLoad: (router, fragment, args...) =>
            @requestedQueryId = null
            if args? and args[0]?
                @requestedQueryId = parseInt(args[0])

        loadRequestedQuery: =>
            if @requestedQueryId?
                query = _.findWhere(@data.queries.models, {'id': @requestedQueryId})

                delete @requestedQueryId

                if query?
                    @data.view.save('json', query.get('view_json'))
                    @data.context.save('json', query.get('context_json'), reset: true)
                    c.router.navigate('results', trigger: true)
                else
                    @ui.loadingMessage.hide()
                    @ui.errorMessage.show()

    { QueryLoader }

define [
    'underscore'
    'marionette'
    '../core'
    'tpl!templates/query/loader.html'
], (_, Marionette, c, templates...) ->

    templates = _.object ['loader'], templates

    class QueryLoader extends Marionette.ItemView
        className: 'query-loader'

        template: templates.loader

        ui:
            loadingMessage: '.loading-message'
            errorMessage: '.error-message'

        initialize: ->
            @data = {}

            if not (@data.context = @options.context)
                throw new Error 'context model required'
            if not (@data.view = @options.view)
                throw new Error 'view model required'
            if not (@data.queries = @options.queries)
                throw new Error 'queries collection required'

            @on 'router:load', @onRouterLoad

            @data.queries.on 'sync', @loadRequestedQuery

        onRouterLoad: (router, fragment, id) =>
            @requestedQueryId = parseInt(id) or null

        loadRequestedQuery: =>
            if @requestedQueryId?
                query = _.findWhere(@data.queries.models, {'id': @requestedQueryId})

                delete @requestedQueryId

                if query?
                    @data.view.save('json', query.get('view_json'))
                    @data.context.save('json', query.get('context_json'), reset: true)
                    c.router.navigate('results', trigger: true)
                else
                    @ui.loadingMessage.hide()
                    @ui.errorMessage.show()

    { QueryLoader }

Use simpler method for retrieving the requested query ID


module.exports = (grunt) ->

   # Task definitions
   grunt.initConfig({

      less:
         dist:
            files:
               "playing-cards.css": "playing-cards.less"

      cssmin:
         dist:
            src: "playing-cards.css"
            expand: true
            ext: ".min.css"

      copy:
         dist:
            files: [
               expand: true
               src: "imgs/*"
               dest: "tmp/"
            ]

      svgmin:
         dist:
            files: [
               expand: true
               cwd: "tmp/imgs/"
               src: ["*.svg"]
               dest: "tmp/imgs/"
            ]

      dataUri:
         dist:
            src: "*.css"
            dest: "."
            options:
               target: "tmp/imgs/*.svg"
               baseDir: "tmp/"

      clean: ["tmp"]

      watch:
         less:
            files: ["*.less"],
            tasks: ["default"]
   })

   grunt.loadNpmTasks "grunt-contrib-cssmin"
   grunt.loadNpmTasks "grunt-contrib-less"
   grunt.loadNpmTasks "grunt-contrib-copy"
   grunt.loadNpmTasks "grunt-svgmin"
   grunt.loadNpmTasks "grunt-data-uri"
   grunt.loadNpmTasks "grunt-contrib-clean"
   grunt.loadNpmTasks "grunt-contrib-watch"

   grunt.registerTask("default", ["less:dist", "cssmin:dist", "copy:dist", "svgmin:dist", "dataUri:dist", "clean"])

module.exports = (grunt) ->

   # Task definitions
   grunt.initConfig({

      less:
         dist:
            files:
               "playing-cards.css": "playing-cards.less"

      cssmin:
         dist:
            src: "playing-cards.css"
            expand: true
            ext: ".min.css"

      copy:
         dist:
            files: [
               expand: true
               src: "imgs/*"
               dest: "tmp/"
            ]

      svgmin:
         dist:
            files: [
               expand: true
               cwd: "tmp/imgs/"
               src: ["*.svg"]
               dest: "tmp/imgs/"
            ]

      dataUri:
         dist:
            src: "*.css"
            dest: "."
            options:
               target: "tmp/imgs/*.svg"
               baseDir: "tmp/"

      clean: ["tmp"]

      watch:
         less:
            files: ["*.less", "imgs/*.svg"],
            tasks: ["default"]
   })

   grunt.loadNpmTasks "grunt-contrib-cssmin"
   grunt.loadNpmTasks "grunt-contrib-less"
   grunt.loadNpmTasks "grunt-contrib-copy"
   grunt.loadNpmTasks "grunt-svgmin"
   grunt.loadNpmTasks "grunt-data-uri"
   grunt.loadNpmTasks "grunt-contrib-clean"
   grunt.loadNpmTasks "grunt-contrib-watch"

   grunt.registerTask("default", ["less:dist", "cssmin:dist", "copy:dist", "svgmin:dist", "dataUri:dist", "clean"])

Make watch monitor for svg changes too

Make watch monitor for svg changes too


seconds = 1000

module.exports = EventLoopMonitor =
	monitor: (logger) ->
		interval = setInterval () ->
			EventLoopMonitor.Delay()
		, 1 * seconds
		Metrics = require "./metrics"
		Metrics.registerDestructor () ->
			clearInterval(interval)

	Delay: () ->
		Metrics = require "./metrics"
		t1 = process.hrtime()
		setImmediate () ->
			delta = process.hrtime(t1)
			responseTime = delta[0]*1e6 + delta[1]*1e-3
			Metrics.timing("event-loop-microsec", responseTime)

module.exports = EventLoopMonitor =
	monitor: (logger, interval = 1000, log_threshold = 100) ->
		Metrics = require "./metrics"

		previous = Date.now()
		intervalId = setInterval () ->
			now = Date.now()
			offset = now - previous - interval
			if offset > log_threshold
				logger.warn {offset: offset}, "slow event loop"
			previous = now
			Metrics.timing("event-loop-millsec", offset)
		, interval

		Metrics.registerDestructor () ->
			clearInterval(intervalId)

Monitor event loop by looking for skew

Monitor event loop by looking for skew

If we monitor with setImmediate, we miss big blocking loops. For example,
suppose we have 1000 1ms loops then a single bad 1000ms loop. setImmediate
will only be called at the right time 1/1000 of the time (it has to be the
loop just before the bad one). So this monitoring method gives a good average
if the std dev is low, but doesn't pick up spikes.

Instead, we can monitor the skew from the expected time between setIntervals.
In the case above, with a setInterval for 1000ms, we will pick up a skew
proportional to the amount of time that it overlaps the bad loop. So 50%
change of picking up skew > 500ms, and thus getting a good sense of any
spikes.


traverse  = require 'traverse'

module.exports.create = (KONFIG, options = {}) ->
  env = ''

  add = (name, value) ->
    env += "export #{name}=${#{name}:-#{value}}\n"

  traverse.paths(KONFIG).forEach (path) ->
    node = traverse.get KONFIG, path
    return  if typeof node is 'object'
    add "KONFIG_#{path.join('_')}".toUpperCase(), node

  add 'KITE_HOME', '$KONFIG_KITEHOME'
  add 'GOPATH', '$KONFIG_PROJECTROOT/go'
  add 'GOBIN', '$GOPATH/bin'

  return env

traverse  = require 'traverse'

module.exports.create = (KONFIG, options = {}) ->
  env = ''

  add = (name, value) ->
    env += "export #{name}=${#{name}:-#{value}}\n"

  traverse.paths(KONFIG).forEach (path) ->
    node = traverse.get KONFIG, path
    return  if typeof node is 'object'
    add "KONFIG_#{path.join('_')}".toUpperCase(), node

  add 'ENV_JSON_FILE', "$KONFIG_PROJECTROOT/#{options.envFileName}.json"
  add 'KONFIG_JSON', '$(cat $ENV_JSON_FILE)'

  add 'KITE_HOME', '$KONFIG_KITEHOME'
  add 'GOPATH', '$KONFIG_PROJECTROOT/go'
  add 'GOBIN', '$GOPATH/bin'

  return env

Read JSON environment file into KONFIG_JSON environment variable

Signed-off-by: Sonmez Kartal <d2d15c3d37396a5f2a09f8b42cf5f27d43a3fbde@koding.com>


{View} = require 'space-pen'
FileView = require './file-view'

module.exports =
class DirectoryView extends View
  @content: (archivePath, entry) ->
    @li class: 'list-nested-item entry', =>
      @span class: 'list-item', =>
        @span entry.getName(), class: 'icon icon-file-directory'
      @ol class: 'list-tree', outlet: 'entries'

  initialize: (archivePath, entry) ->
    for child in entry.children
      if child.isDirectory()
        @entries.append(new DirectoryView(archivePath, child))
      else
        @entries.append(new FileView(archivePath, child))

{View} = require 'space-pen'
FileView = require './file-view'

module.exports =
class DirectoryView extends View
  @content: (archivePath, entry) ->
    @li class: 'list-nested-item entry', =>
      @span class: 'list-item', =>
        @span entry.getName(), class: 'directory icon icon-file-directory'
      @ol class: 'list-tree', outlet: 'entries'

  initialize: (archivePath, entry) ->
    for child in entry.children
      if child.isDirectory()
        @entries.append(new DirectoryView(archivePath, child))
      else
        @entries.append(new FileView(archivePath, child))

Add back directory class to directory view


authenticateToken = (token) ->
  env_token = process.env.AUTH_TOKEN

  if env_token? && token?
    return env_token == token

  return false

module.exports = (req, res, next) ->
  if _.contains(["POST", "DELETE"], req.method)
    unless authenticateToken(req.query.token || null)
      return res.send(401, 'Unauthorised')

  next()

authenticateToken = (token) ->
  env_token = process.env.AUTH_TOKEN

  if env_token? && token?
    return env_token == token

  return false

module.exports = (req, res, next) ->
  unless authenticateToken(req.query.token || null)
    return res.send(401, 'Unauthorised')

  next()

Remove unnecessary method check for token auth

Since we're not using express-resource for uses anymore, we can just
apply the token auth middleware directly to the routes we want


'use strict'

###*
 # @ngdoc overview
 # @name blogarashikecomApp
 # @description
 # # blogarashikecomApp
 #
 # Main module of the application.
###
angular
  .module 'arashike-blog', [
    'ngAnimate'
    'ngAria'
    'ngCookies'
    'ngMessages'
    'ngResource'
    'ngRoute'
    'ngSanitize'
    'ngTouch'
    'ngMaterial'
  ]
  .value 'apiEndpoint', 'https://api.github.com'
  .value 'gistUrl', 'https://gist.github.com'
  .value 'authorName', 'JumpeiArashi'
  .value 'gistUsername', 'JumpeiArashi'
  .config ($routeProvider) ->
    $routeProvider
      .when '/',
        templateUrl: 'views/top.html'
        controller: 'TopCtrl'
        controllerAs: 'top'
      .when '/articles/:gistId',
        templateUrl: 'views/articledetail.html'
        controller: 'ArticleDetailCtrl'
        controllerAs: 'articleDetail'
      .otherwise
        redirectTo: '/'

'use strict'

###*
 # @ngdoc overview
 # @name blogarashikecomApp
 # @description
 # # blogarashikecomApp
 #
 # Main module of the application.
###
angular
  .module 'arashike-blog', [
    'ngAnimate'
    'ngAria'
    'ngCookies'
    'ngMessages'
    'ngResource'
    'ngRoute'
    'ngSanitize'
    'ngTouch'
    'ngMaterial'
  ]
  .value 'apiEndpoint', 'https://api.github.com'
  .value 'gistUrl', 'https://gist.github.com'
  .value 'authorName', 'JumpeiArashi'
  .value 'gistUsername', 'JumpeiArashi-blog'
  .config ($routeProvider) ->
    $routeProvider
      .when '/',
        templateUrl: 'views/top.html'
        controller: 'TopCtrl'
        controllerAs: 'top'
      .when '/articles/:gistId',
        templateUrl: 'views/articledetail.html'
        controller: 'ArticleDetailCtrl'
        controllerAs: 'articleDetail'
      .otherwise
        redirectTo: '/'

Change gist user to JumpeiArashi-blog

Change gist user to JumpeiArashi-blog


bongo = require 'bongo'

{secure} = bongo

JMachine = require './computeproviders/machine'

module.exports = class SharedMachine extends bongo.Base

  @share()

  setUsers = (client, uid, options, callback) ->

    options.permanent = yes
    JMachine.shareByUId client, uid, options, callback

bongo = require 'bongo'

{secure} = bongo

JMachine = require './computeproviders/machine'

module.exports = class SharedMachine extends bongo.Base

  @share()

  @add = secure (client, uid, target, callback) ->

    asUser  = yes
    options = {target, asUser}
    setUsers client, uid, options, callback

  setUsers = (client, uid, options, callback) ->

    options.permanent = yes
    JMachine.shareByUId client, uid, options, callback

Define shared machine user add method

Define shared machine user add method


BaseStackEditorView = require './basestackeditorview'

module.exports = class VariablesEditorView extends BaseStackEditorView

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

    unless options.content
      options.content = """
        # This is a YAML file which you can define
        # key-value pairs like;
        #
        #   foo: bar
        #

      """
      options.contentType = 'yaml'

    super options, data

BaseStackEditorView = require './basestackeditorview'

module.exports = class VariablesEditorView extends BaseStackEditorView

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

    unless options.content
      options.content = """
        # Define custom variables here
        # You can define your custom variables, and use them in your stack template.
        #
        # This is a YAML file which you can define key-value pairs like;
        #
        #   foo: bar
        #

      """
      options.contentType = 'yaml'

    super options, data

Update yaml content with information.

VariablesEditorView: Update yaml content with information.


class LoggerFactory

  instance = null
  @get: ->
    instance ?= new LoggerFactory

  createLogger: (namespace, defaultLevel = 'info')->
    log.debug 'LoggerFactory.createLogger()', arguments
    expect(namespace).to.be.a('string').that.is.ok
    prefix = namespace + ':'
    expect(Loglevel).to.be.a 'function'
    logger = Loglevel(prefix)
    logger.setLevel(defaultLevel)
    return logger

  createPackageLogger: (packageName, defaultLevel)->
    return @createLogger(packageName, defaultLevel)

  createAppLogger: (appName, defaultLevel)->
    return @createLogger(appName, defaultLevel)

loglevel = LoggerFactory.get()

class LoggerFactory

  instance = null
  @get: ->
    instance ?= new LoggerFactory

  createLogger: (namespace, defaultLevel = 'info')->
    log.debug 'LoggerFactory.createLogger()', arguments
    if namespace?
      expect(namespace).to.be.a('string')
      prefix = namespace + ':'
    expect(Loglevel).to.be.a 'function'
    logger = Loglevel(prefix)
    logger.setLevel(defaultLevel)
    return logger

  createPackageLogger: (packageName, defaultLevel)->
    return @createLogger(packageName, defaultLevel)

  createAppLogger: (appName, defaultLevel)->
    return @createLogger(appName, defaultLevel)

loglevel = LoggerFactory.get()

Make namespace optional in log factory

Make namespace optional in log factory


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

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

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

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

Resolve keybinding conflict with joining lines


define (require) ->
  settings = require('cs!settings')
  BaseView = require('cs!helpers/backbone/views/base')
  template = require('hbs!./latest-template')
  require('less!./latest')

  return class LatestView extends BaseView
    template: template
    templateHelpers:
      url: () ->
        id = @model.get('id').split('@', 1)[0] # Remove the version from the id
        return "#{settings.root}content/#{id}"

    initialize: () ->
      super()
      @listenTo(@model, 'change:isLatest', @render)

define (require) ->
  settings = require('cs!settings')
  BaseView = require('cs!helpers/backbone/views/base')
  template = require('hbs!./latest-template')
  require('less!./latest')

  return class LatestView extends BaseView
    template: template
    templateHelpers:
      url: () ->
        id = @model.get('id').split('@', 1)[0] # Remove the version from the id
        return "#{settings.root}contents/#{id}"

    initialize: () ->
      super()
      @listenTo(@model, 'change:isLatest', @render)

Convert urls from /content to /contents

Convert urls from /content to /contents


define (require) ->
  $ = require('jquery')
  router = require('cs!router')
  analytics = require('cs!helpers/handlers/analytics')
  BaseView = require('cs!helpers/backbone/views/base')
  template = require('hbs!./leaf-template')
  require('less!./leaf')

  return class TocNodeView extends BaseView
    template: template
    tagName: 'li'
    itemViewContainer: '.subcollection'

    events:
      'click a': 'loadPage'

    initialize: () ->
      super()
      @listenTo(@model, 'change:active change:page', @render)

    loadPage: (e) ->
      e.preventDefault()
      $a = $(e.currentTarget)

      @model.get('book').setPage($a.data('page'))
      route = $a.attr('href')
      router.navigate(route) # Update browser URL to reflect the new route
      analytics.send() # Send the analytics information for the new route

define (require) ->
  $ = require('jquery')
  router = require('cs!router')
  analytics = require('cs!helpers/handlers/analytics')
  BaseView = require('cs!helpers/backbone/views/base')
  template = require('hbs!./leaf-template')
  require('less!./leaf')

  return class TocNodeView extends BaseView
    template: template
    templateHelpers: () ->
      book = @model.get('book').toJSON()
      # Ensure the version number is appended to the book id
      if book.id.indexOf('@') is -1
        book.id = "#{book.id}@#{book.version}"
      return {book: book}

    tagName: 'li'
    itemViewContainer: '.subcollection'

    events:
      'click a': 'loadPage'

    initialize: () ->
      super()
      @listenTo(@model, 'change:active change:page', @render)

    loadPage: (e) ->
      e.preventDefault()
      $a = $(e.currentTarget)

      @model.get('book').setPage($a.data('page'))
      route = $a.attr('href')
      router.navigate(route) # Update browser URL to reflect the new route
      analytics.send() # Send the analytics information for the new route

Include version numbers in ToC links

Include version numbers in ToC links


CronJob = require('cron').CronJob

module.exports = (robot) ->
  jobList = [
    { time: '30 10 * * 1-5', msg: '@channel Tea Time!' }
    { time: '00 17 * * 5', msg: '@channel Beer & Pokdeng Time!' }
  ]

  # for job in jobList
  #   cronJob = new CronJob job.time, =>
  #     robot.messageRoom '#general', job.msg
  #   , null, false, 'Asia/Bangkok'
  #
  #   cronJob.start()

  cronJob = new CronJob '30 10 * * 1-5', =>
    robot.messageRoom '#general', '@channel Tea Time!'
  , null, false, 'Asia/Bangkok'

  cronJob.start()

  cronJob = new CronJob '00 17 * * 5', =>
    robot.messageRoom '#general', '@channel Beer & Pokdeng Time!'
  , null, false, 'Asia/Bangkok'

  cronJob.start()

  cronJob = new CronJob '00 18 * * 1-5', =>
    robot.messageRoom '#general', '@here Hey, turn off air filter, NOW!!!'
  , null, false, 'Asia/Bangkok'

  cronJob.start()

Add turn off air filter

Add turn off air filter


if Meteor.isServer
   @Sessions = new Meteor.Collection("session_timeouts")
   Meteor.methods( {
      session_heartbeat: ->
         user_id = Meteor.userId()
         return unless user_id
         old_session = Sessions.findOne({user_id: user_id})
         if old_session?
            Sessions.update({user_id: user_id}, {$set: {heartbeat: Date.now()}})
         else
            Sessions.insert({user_id: user_id, heartbeat: Date.now()})
   })

   interval = Meteor.settings.purgeInterval || 3000
   Meteor.setInterval(() ->
      loggedOutUsersIds = _.pluck(Meteor.users.find('services.resume.forceLogout': true).fetch(), '_id')
      if loggedOutUsersIds?.length
         Meteor.users.update({_id: {$in: loggedOutUsersIds}},
            {$set: {'services.resume.loginTokens': []},
            $unset: {'services.resume.forceLogout': ''}})

      timeout = Date.now()
      timeout -= Meteor.settings.inactivityTimeout || 300000
      usersToLogOut = _.pluck(Sessions.find({heartbeat: {$lt: timeout}}).fetch(), 'user_id')
      if usersToLogOut?.length
         Meteor.users.update({_id: {$in: usersToLogOut}}, {$set: {'services.resume.forceLogout': true}})
         Sessions.remove({user_id: {$in: usersToLogOut}})

   , interval)

if Meteor.isServer
   @Sessions = new Meteor.Collection("session_timeouts")
   Meteor.methods( {
      session_heartbeat: ->
         user_id = Meteor.userId()
         return unless user_id
         old_session = Sessions.findOne({user_id: user_id})
         if old_session?
            Sessions.update({user_id: user_id}, {$set: {heartbeat: Date.now()}})
         else
            Sessions.insert({user_id: user_id, heartbeat: Date.now()})
   })

   interval = Meteor.settings.purgeInterval || 60 * 1000
   Meteor.setInterval(() ->
      loggedOutUsersIds = _.pluck(Meteor.users.find('services.resume.forceLogout': true).fetch(), '_id')
      if loggedOutUsersIds?.length
         Meteor.users.update({_id: {$in: loggedOutUsersIds}},
            {$set: {'services.resume.loginTokens': []},
            $unset: {'services.resume.forceLogout': ''}})

      timeout = Date.now()
      timeout -= Meteor.settings.inactivityTimeout || 15 * 60 * 1000
      usersToLogOut = _.pluck(Sessions.find({heartbeat: {$lt: timeout}}).fetch(), 'user_id')
      if usersToLogOut?.length
         Meteor.users.update({_id: {$in: usersToLogOut}}, {$set: {'services.resume.forceLogout': true}})
         Sessions.remove({user_id: {$in: usersToLogOut}})

   , interval)

Increase timeouts to decrease server resources consumption


define [
	"base"
	"ide/colors/ColorManager"
], (App, ColorManager) ->
	App.controller "ChatMessageController", ["$scope", "ide", ($scope, ide) ->
		$scope.hue = (user) ->
			ColorManager.getHueForUserId(user.id)
	]

define [
	"base"
	"ide/colors/ColorManager"
], (App, ColorManager) ->
	App.controller "ChatMessageController", ["$scope", "ide", ($scope, ide) ->
		$scope.hue = (user) ->
			if !user?
				return 0
			else
				return ColorManager.getHueForUserId(user.id)
	]

Add in null check for user when getting hue in chat


{$$, BufferedProcess, SelectListView} = require 'atom'
SelectStageHunks = require './select-stage-hunks-view'
git = require '../git'

module.exports =
class SelectStageHunkFile extends SelectListView

  initialize: (items) ->
    super
    @addClass 'overlay from-top'

    @setItems items
    atom.workspaceView.append this
    @focusFilterEditor()

  getFilterKey: -> 'path'

  viewForItem: (item) ->
    $$ ->
      @li =>
        @div class: 'pull-right', =>
          @span class: 'inline-block highlight', item.mode
        @span class: 'text-warning', item.path

  confirmed: ({path}) ->
    @cancel()
    git.diff(
      (data) -> new SelectStageHunks(data),
      path
    )

{BufferedProcess} = require 'atom'
{$$, SelectListView} = require 'atom-space-pen-views'
SelectStageHunks = require './select-stage-hunks-view'
git = require '../git'

module.exports =
class SelectStageHunkFile extends SelectListView

  initialize: (items) ->
    super
    @show()

    @setItems items
    @focusFilterEditor()

  getFilterKey: -> 'path'

  show: ->
    @panel ?= atom.workspace.addModalPanel(item: this)
    @panel.show()

  cancelled: -> @hide()

  hide: ->
    @panel?.hide()

  viewForItem: (item) ->
    $$ ->
      @li =>
        @div class: 'pull-right', =>
          @span class: 'inline-block highlight', item.mode
        @span class: 'text-warning', item.path

  confirmed: ({path}) ->
    @cancel()
    git.diff(
      (data) -> new SelectStageHunks(data),
      path
    )

Upgrade stage hunk file view

Upgrade stage hunk file view


# The changelog not only lists the significant changes in the interface,
# but provides a mechanism to hook into the tour or display a help menu.
# The changelog is an array of _version_ objects. Each version has a summary
# and an array of changes.
define ->

    return [
        version: '2.0.0'
        changes: [
            summary: 'Initial Release'
        ]
    ]

# The changelog not only lists the significant changes in the interface,
# but provides a mechanism to hook into the tour or display a help menu.
# The changelog is an array of _version_ objects. Each version has a summary
# and an array of changes.
define ->

    return [
        version: '2.0.1'
        changes: []
    ,
        version: '2.0.0'
        changes: [
            summary: 'Initial Release'
        ]
    ]

Add change log entry for 2.0.1

Add change log entry for 2.0.1


window.StrutBuilder ||= {}

StrutBuilder.Textbox = {}

StrutBuilder.Textbox.build = (data, i) ->
  {
    TextBox: {},
    x: 341,
    y: 65 * (i+1),
    scale: {"x":1, "y":1},
    type: "TextBox",
    text: "" + data.text + "",
    size: 72,
    selected:false
  }

window.StrutBuilder ||= {}

StrutBuilder.Textbox = {}
faces = [ 'Lato', 'League Gothic, sans-serif', 'Droid Sans Mono, monospace', 'Ubuntu, sans-serif', 'Abril Fatface, cursive', 'Hammersmith One, sans-serif', 'Fredoka One, cursive', 'Gorditas, cursive', 'Press Start 2P, cursive' ]
randomFace = ->
  faces[Math.floor(Math.random() * faces.length)]
StrutBuilder.Textbox.build = (data, i) ->
  face = randomFace()
  text = if Math.random() > 0.5
    '' + data.text + ''
  else
    data.text
  text = if Math.random() > 0.5
    '' + text + ''
  else
    text
  loop
    color = tinycolor('hsl(0,0.9,0.3)').spin(Math.floor(Math.random() * 360))
    break if color.isDark()
  {
    TextBox: {},
    x: 341,
    y: 65 * (i+1),
    scale: {"x":1, "y":1},
    type: "TextBox",
    text: "" + data.text + "",
    size: 72,
    selected:false
  }

Add font face, bold/italic randomization


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

SRC = 'src'
DEST = 'out'

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

gulp.task 'haml', ->
  gulp.src "#{SRC}/haml/**/*.haml"
    .pipe haml()
    .pipe gulp.dest DEST

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

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

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

gulp.task 'serve', ->
  serve DEST

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

SRC = 'src'
DEST = 'out'

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

gulp.task 'haml', ->
  gulp.src "#{SRC}/haml/**/*.haml"
    .pipe haml()
    .pipe gulp.dest DEST

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

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

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

gulp.task 'serve', ->
  serve DEST

Move Bower Dependencies Out of scripts/

Move Bower Dependencies Out of scripts/

Since they're not all scripts.


Encoder = require 'htmlencode'

module.exports = (text = '') ->

  input = Encoder.htmlDecode text

  return text  unless (/^>/gm).test input

  val = ''

  for line in input.split '\n'
    line += '\n'  if line[0] is '>'
    val  += "#{line}\n"

  return val

Encoder = require 'htmlencode'

module.exports = (text = '') ->

  input = Encoder.htmlDecode text

  return text  unless (/^>/gm).test input

  val = ''

  for line in input.split '\n'
    line = if line[0] is '>'
    then ">#{Encoder.XSSEncode line.substring 1}"
    else Encoder.XSSEncode line

    val  += "#{line}\n"

  return val

Apply XSS encoding in format quotes utility function


class Lanes.Components.ToggleField extends Lanes.React.Component
    mixins: [ Lanes.Components.Form.FieldMixin ]

    formGroupClass: 'toggle'

    renderDisplayValue: ->
        <Lanes.Vendor.ReactToggle
            defaultChecked={!!@props.model[@props.name]}
            disabled={true}
        />

    handleToggleChange: (ev) ->
        @props.model[@props.name] = ev.target.checked
        null

    renderEdit: (label) ->
        props = _.omit(@props, 'label')
        toggle = <Lanes.Vendor.ReactToggle
            defaultChecked={!!@props.model[@props.name]}
            onChange={@handleToggleChange}
            {...props} />
        if @props.unstyled
            toggle
        else
            colProps = _.omit(@props, 'name', 'label')
            <BS.Col {...colProps}>

                    {label}

                        {toggle}

            </BS.Col>

class Lanes.Components.ToggleField extends Lanes.React.Component
    mixins: [ Lanes.Components.Form.FieldMixin ]

    formGroupClass: 'toggle'

    renderDisplayValue: ->
        <Lanes.Vendor.ReactToggle
            defaultChecked={!!@props.model[@props.name]}
            checked={!!@props.model[@props.name]}
            disabled={true}
        />

    handleToggleChange: (ev) ->
        @props.model[@props.name] = ev.target.checked
        null

    renderEdit: (label) ->
        props = _.omit(@props, 'label')
        toggle = <Lanes.Vendor.ReactToggle
            onChange={@handleToggleChange}
            checked={!!@props.model[@props.name]}
            defaultChecked={!!@props.model[@props.name]}
            />
        if @props.unstyled
            toggle
        else
            colProps = _.omit(@props, 'name', 'label')
            <BS.Col {...colProps}>

                    {label}

                        {toggle}

            </BS.Col>

Set checked property on togglefield

Set checked property on togglefield


Router.map ->

  @route "entrySignIn",
    path: "/sign-in"
    onBeforeRun: ->
      Session.set('entryError', undefined)
      Session.set('buttonText', 'in')

  @route "entrySignUp",
    path: "/sign-up"
    onBeforeRun: ->
      Session.set('entryError', undefined)
      Session.set('buttonText', 'up')

  @route "entryForgotPassword",
    path: "/forgot-password"
    onBeforeRun: ->
      Session.set('entryError', undefined)

  @route 'entrySignOut',
    path: '/sign-out'
    before: ->
      if AccountsEntry.config.homeRoute
        Meteor.logout()
        Router.go AccountsEntry.config.homeRoute
      @stop()

Router.map ->

  @route "entrySignIn",
    path: "/sign-in"
    onBeforeRun: ->
      Session.set('entryError', undefined)
      Session.set('buttonText', 'in')

  @route "entrySignUp",
    path: "/sign-up"
    onBeforeRun: ->
      Session.set('entryError', undefined)
      Session.set('buttonText', 'up')

  @route "entryForgotPassword",
    path: "/forgot-password"
    onBeforeRun: ->
      Session.set('entryError', undefined)

  @route 'entrySignOut',
    path: '/sign-out'
    before: ->
      if Session.get('entrySettings').homeRoute
        Meteor.logout()
        Router.go Session.get('entrySettings').homeRoute
      @stop()

Fix undefined error in sign-out route

Fix undefined error in sign-out route


Router.map ->

  @route 'blogIndex',
    path: '/blog'
    before: ->
      if Blog.settings.blogIndexTemplate
        @template = Blog.settings.blogIndexTemplate
    waitOn: ->
      Meteor.subscribe 'posts'
    data: ->
      posts: Post.where published: true

  @route 'blogShow',
    path: '/blog/:slug'
    notFoundTemplate: 'blogNotFound'
    before: ->
      if Blog.settings.blogShowTemplate
        @template = Blog.settings.blogShowTemplate
      Session.set 'postSlug', @params.slug
    waitOn: ->
      [ Meteor.subscribe 'posts'
        Meteor.subscribe 'users' ]
    data: ->
      Post.first slug: @params.slug

  @route 'blogAdmin',
    path: '/admin/blog'
    waitOn: ->
      Meteor.subscribe 'posts'
    before: ->
      if Meteor.loggingIn()
        return @stop()

      if not Meteor.user()
        return @redirect('/blog')

  @route 'blogAdminNew',
    path: '/admin/blog/new'
    before: ->
      if Meteor.loggingIn()
        return @stop()

      if not Meteor.user()
        return @redirect('/blog')

  @route 'blogAdminEdit',
    path: '/admin/blog/edit/:slug'
    waitOn: ->
      Meteor.subscribe 'posts'
    data: ->
      Post.first slug: @params.slug
    before: ->
      if Meteor.loggingIn()
        return @stop()

      if not Meteor.user()
        return @redirect('/blog')

      Session.set 'postSlug', @params.slug

Router.map ->

  @route 'blogIndex',
    path: '/blog'
    before: ->
      if Blog.settings.blogIndexTemplate
        @template = Blog.settings.blogIndexTemplate
    waitOn: ->
      [ Meteor.subscribe 'posts'
        Meteor.subscribe 'users' ]
    data: ->
      posts: Post.where published: true

  @route 'blogShow',
    path: '/blog/:slug'
    notFoundTemplate: 'blogNotFound'
    before: ->
      if Blog.settings.blogShowTemplate
        @template = Blog.settings.blogShowTemplate
      Session.set 'postSlug', @params.slug
    waitOn: ->
      [ Meteor.subscribe 'posts'
        Meteor.subscribe 'users' ]
    data: ->
      Post.first slug: @params.slug

  @route 'blogAdmin',
    path: '/admin/blog'
    waitOn: ->
      [ Meteor.subscribe 'posts'
        Meteor.subscribe 'users' ]
    before: ->
      if Meteor.loggingIn()
        return @stop()

      if not Meteor.user()
        return @redirect('/blog')

  @route 'blogAdminNew',
    path: '/admin/blog/new'
    before: ->
      if Meteor.loggingIn()
        return @stop()

      if not Meteor.user()
        return @redirect('/blog')

  @route 'blogAdminEdit',
    path: '/admin/blog/edit/:slug'
    waitOn: ->
      [ Meteor.subscribe 'posts'
        Meteor.subscribe 'users' ]
    data: ->
      Post.first slug: @params.slug
    before: ->
      if Meteor.loggingIn()
        return @stop()

      if not Meteor.user()
        return @redirect('/blog')

      Session.set 'postSlug', @params.slug

Fix bug where every blog author was 'Mystery author' on blog index page


class WebpackConfig

  getDefaultConfiguration: ->
    module:
      loaders: [
        {test: /\.coffee$/i, loader: 'coffee-loader'}
      ]
    resolve:
      extensions: ['', '.js', '.coffee']

module.exports = new WebpackConfig

webpackStream = require 'webpack-stream'

NormalModuleReplacementPlugin = webpackStream.webpack.NormalModuleReplacementPlugin

class WebpackConfig

  getDefaultConfiguration: ->
    module:
      loaders: [
        {test: /\.coffee$/i, loader: 'coffee-loader'}
      ]
      noParse: [
        /node_modules\/sinon\/pkg\/.*/
      ]
    plugins: [
      new NormalModuleReplacementPlugin /^sinon$/, process.cwd() + '/node_modules/sinon/pkg/sinon.js'
    ]

    resolve:
      extensions: ['', '.js', '.coffee']

module.exports = new WebpackConfig

Use current stable version of Sinon.JS, require browser packaged version when using webpack, fix usage of sinon-chai


SpellCheckView = null

module.exports =
  configDefaults:
    grammars: [
      'text.plain'
      'source.gfm'
      'text.git-commit'
    ]

  createView: (editorView) ->
    SpellCheckView ?= require './spell-check-view'
    new SpellCheckView(editorView)

  activate: ->
    atom.workspaceView.eachEditorView (editorView) =>
      if editorView.attached and editorView.getPane()?
        editorView.underlayer.append(@createView(editorView))

SpellCheckView = null

module.exports =
  configDefaults:
    grammars: [
      'text.plain'
      'source.gfm'
      'text.git-commit'
    ]

  activate: ->
    @editorSubscription = atom.workspaceView.eachEditorView(addViewToEditor)

  deactivate: ->
    @editorSubscription?.off()

addViewToEditor = (editorView) ->
  if editorView.attached and editorView.getPane()?
    SpellCheckView ?= require './spell-check-view'
    editorView.underlayer.append(new SpellCheckView(editorView))

Implement deactivate that offs subscription


class PartyListView extends Backbone.View
    el: $('div.party-list')
    el_gov: $('ul.party-list-government')
    el_oppo: $('ul.party-list-opposition')

    render: ->
        @collection.forEach @render_one, @
        return @

    render_one: (party) ->
        if party.get "governing_party"
            el = @el_gov
        else
            el = @el_oppo
        party_view = new PartyListItemView
            model: party
            # Directly render into the container ul on the Django template
            # Will this break any event listeners?
            el: el
        party_view.render().el

    initialize: ->
        @collection = new PartyList
        @collection.comparator = 'member_count'
        # This gets triggered when the content has fetch'ed from JSON
        @listenTo @collection, "reset", @render
        @collection.fetch
            reset: true

class @PartyListItemView extends Backbone.View
    template: _.template $('#party-list-item-template').html()

    render: ->
        template_variables = @model.toJSON()
        # This is a view-specific transformation, would seem out of place in model
        if template_variables.governing_party
            template_variables.party_governing_status = "party-government"
        else
            template_variables.party_governing_status = "party-opposition"
        template_variables.view_url = @model.get_view_url()
        # Appending not a good idea?
        @$el.append @template template_variables
        return @

# Initialize the page contents
@party_list_view = new PartyListView

class PartyListView extends Backbone.View
    el: $('div.party-list')
    el_gov: $('ul.party-list-government')
    el_oppo: $('ul.party-list-opposition')

    render: ->
        @collection.forEach @render_one, @
        return @

    render_one: (party) ->
        if party.get "governing_party"
            el = @el_gov
        else
            el = @el_oppo
        party_view = new PartyListItemView
            model: party
            # Directly render into the container ul on the Django template
            # Will this break any event listeners?
            el: el
        party_view.render().el

    initialize: ->
        @collection = new PartyList
        @collection.comparator = (model) -> return -model.get('member_count')
        # This gets triggered when the content has fetch'ed from JSON
        @listenTo @collection, "reset", @render
        @collection.fetch
            reset: true

class @PartyListItemView extends Backbone.View
    template: _.template $('#party-list-item-template').html()

    render: ->
        template_variables = @model.toJSON()
        # This is a view-specific transformation, would seem out of place in model
        if template_variables.governing_party
            template_variables.party_governing_status = "party-government"
        else
            template_variables.party_governing_status = "party-opposition"
        template_variables.view_url = @model.get_view_url()
        # Appending not a good idea?
        @$el.append @template template_variables
        return @

# Initialize the page contents
@party_list_view = new PartyListView

Sort party list in descending order

Sort party list in descending order


store = new FS.Store.FileSystem "images",
	path: "~/uploads"
	fileKeyMaker: (fileObj) ->
		filename = fileObj.name()
		filenameInStore = fileObj.name({store: 'images'})

		return filenameInStore || filename

@Images = new FS.Collection "images",
	stores: [store]

@Images.allow
	insert: ->
		return true
	update: ->
		return true

uploadPath = "~/uploads"

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

		return filenameInStore || filename

@Images = new FS.Collection "images",
	stores: [store]

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

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

			collectionName = opts.collectionName

			collection = FS._collections['images']

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

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

Create url for avatar download

Create url for avatar download


GitHubFile  = require './github-file'

module.exports =
  configDefaults:
    includeLineNumbersInUrls: true

  activate: ->
    return unless atom.project.getRepo()?

    atom.workspaceView.eachPaneView (pane) ->
      pane.command 'open-on-github:file', ->
        if itemPath = getActivePath()
          GitHubFile.fromPath(itemPath).open(getSelectedRange())

      pane.command 'open-on-github:blame', ->
        if itemPath = getActivePath()
          GitHubFile.fromPath(itemPath).blame(getSelectedRange())

      pane.command 'open-on-github:history', ->
        if itemPath = getActivePath()
          GitHubFile.fromPath(itemPath).history()

      pane.command 'open-on-github:copy-url', ->
        if itemPath = getActivePath()
          GitHubFile.fromPath(itemPath).copyUrl(getSelectedRange())

      pane.command 'open-on-github:branch-compare', ->
        if itemPath = atom.project.getPath()
          GitHubFile.fromPath(itemPath).openBranchCompare()

getActivePath = ->
  atom.workspace.getActivePaneItem()?.getPath?()

getSelectedRange = ->
  atom.workspace.getActivePaneItem()?.getSelectedBufferRange?()

GitHubFile  = require './github-file'

module.exports =
  configDefaults:
    includeLineNumbersInUrls: true

  activate: ->
    return unless atom.project.getRepo()?

    atom.workspace.observePanes (pane) ->
      atom.commands.add atom.views.getView(pane),
        'open-on-github:file': ->
          if itemPath = getActivePath()
            GitHubFile.fromPath(itemPath).open(getSelectedRange())

        'open-on-github:blame': ->
          if itemPath = getActivePath()
            GitHubFile.fromPath(itemPath).blame(getSelectedRange())

        'open-on-github:history': ->
          if itemPath = getActivePath()
            GitHubFile.fromPath(itemPath).history()

        'open-on-github:copy-url': ->
          if itemPath = getActivePath()
            GitHubFile.fromPath(itemPath).copyUrl(getSelectedRange())

        'open-on-github:branch-compare': ->
          if itemPath = atom.project.getPath()
            GitHubFile.fromPath(itemPath).openBranchCompare()

getActivePath = ->
  atom.workspace.getActivePaneItem()?.getPath?()

getSelectedRange = ->
  atom.workspace.getActivePaneItem()?.getSelectedBufferRange?()

Use atom.commands to register commands

Use atom.commands to register commands


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

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

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

    tween = createjs.Tween.get($this).to( {counter: $target_count}, 10000 , createjs.Ease.quintOut)

    tween.addEventListener 'change', (event) ->
      $this.text(Math.round($this.counter).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","))
      return

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

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

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

    tween = createjs.Tween.get($this).to( {counter: $target_count}, 10000 , createjs.Ease.quintOut)

    tween.addEventListener 'change', (event) ->
      $this.text(Math.round($this.counter).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","))
      return

Remove setting count to zero, it makes it cluncky


# Placeholder Polyfill
# https://github.com/mathiasbynens/jquery-placeholder
$(window).load ->
  $("input, textarea").placeholder()

# Sammy
(($) ->
  circle = $.sammy("body", ->

    # Page
    class Page
      constructor: (@name, @title) ->

      render: ->
        document.title = "Circle - " + @title
        $("body").attr("id",@name).html HAML['header'](renderContext)
        $("body").append HAML[@name](renderContext)
        $("body").append HAML['footer'](renderContext)

      display: ->
        if HAML? and HAML[@name]?
          @render()
        else
          self = this
          $.getScript "assets/views/outer/#{@name}/#{@name}.hamlc", -> self.render()

    # Pages
    home = new Page("home", "Continuous Integration made easy")
    about = new Page("about", "About Us")

    # Navigation
    @get "/", (context) -> home.display()
    @get "#/about", (context) -> about.display()

  )

  # Run the application
  $ -> circle.run "#/"
) jQuery

# Placeholder Polyfill
# https://github.com/mathiasbynens/jquery-placeholder
$(window).load ->
  $("input, textarea").placeholder()

# Sammy
(($) ->
  circle = $.sammy("body", ->

    # Page
    class Page
      constructor: (@name, @title) ->

      render: ->
        document.title = "Circle - " + @title
        $("body").attr("id",@name).html HAML['header'](renderContext)
        $("body").append HAML[@name](renderContext)
        $("body").append HAML['footer'](renderContext)

      load: (show) ->
        self = this
        if show?
          $.getScript "assets/views/outer/#{@name}/#{@name}.hamlc", ->
            self.render()
        else
          $.getScript "assets/views/outer/#{@name}/#{@name}.hamlc"

      display: ->
        if HAML? and HAML[@name]?
          @render()
        else
          @load(true)

    # Pages
    home = new Page("home", "Continuous Integration made easy")
    about = new Page("about", "About Us")

    # Navigation
    @get "/", (context) -> home.display()
    @get "#/about", (context) -> about.display()

  )

  # Run the application
  $ -> circle.run "#/"

) jQuery

Load method for future preloading

Load method for future preloading


path = require 'path'
optimist = require 'optimist'
async = require 'async'
archive = require './ls-archive'

module.exports = ->
  files = optimist.usage('Usage: lsa [file ...]') .demand(1).argv._
  queue = async.queue (archivePath, callback) ->
    do (archivePath) ->
      archive.list archivePath, (error, files) ->
        if error?
          console.error("Error reading: #{file}")
        else
          console.log("#{archivePath} (#{files.length})")
          for file, index in files
            if index is files.length - 1
              prefix = '\u2514\u2500\u2500 '
            else
              prefix = '\u251C\u2500\u2500 '
            console.log "#{prefix}#{file.getPath()}"
          console.log()
        callback()

  files.forEach (file) -> queue.push(path.resolve(process.cwd(), file))

path = require 'path'
optimist = require 'optimist'
async = require 'async'
archive = require './ls-archive'

module.exports = ->
  files = optimist.usage('Usage: lsa [file ...]') .demand(1).argv._
  queue = async.queue (archivePath, callback) ->
    do (archivePath) ->
      archive.list archivePath, (error, files) ->
        if error?
          console.error("Error reading: #{archivePath}")
        else
          console.log("#{archivePath} (#{files.length})")
          for file, index in files
            if index is files.length - 1
              prefix = '\u2514\u2500\u2500 '
            else
              prefix = '\u251C\u2500\u2500 '
            console.log "#{prefix}#{file.getPath()}"
          console.log()
        callback()

  files.forEach (file) -> queue.push(path.resolve(process.cwd(), file))

Use correct path in error message

Use correct path in error message


class window.FavouriteTopicButtonView extends ActionButtonView
  mini: true

  onRender: -> @updateButton()

  initialize: ->
    @bindTo @model, 'change', @updateButton, @
    @user = currentUser

  templateHelpers: =>
    disabled_label: Factlink.Global.t.follow.capitalize()
    disable_label:  Factlink.Global.t.unfollow.capitalize()
    enabled_label:  Factlink.Global.t.following.capitalize()

  buttonEnabled: ->
    @user.favourite_topics.contains @model

  primaryAction: (e) ->
    @model.favourite()

  secondaryAction: (e) ->
    @model.unfavourite()

class window.FavouriteTopicButtonView extends ActionButtonView
  mini: true

  onRender: -> @updateButton()

  initialize: ->
    @bindTo @model, 'change', @updateButton, @
    @user = currentUser

  templateHelpers: =>
    disabled_label: Factlink.Global.t.favourite.capitalize()
    disable_label:  Factlink.Global.t.unfavourite.capitalize()
    enabled_label:  Factlink.Global.t.favourited.capitalize()

  buttonEnabled: ->
    @user.favourite_topics.contains @model

  primaryAction: (e) ->
    @model.favourite()

  secondaryAction: (e) ->
    @model.unfavourite()

Use correct labels for 'Favourite' button


# Description:
#   A simple interaction with the built in HTTP Daemon
#
# Dependencies:
#   None
#
# Configuration:
#   None
#
# Commands:
#   None
#
# URLS:
#   /hubot/version
#   /hubot/ping
#   /hubot/time
#   /hubot/info
#   /hubot/ip

spawn = require('child_process').spawn

module.exports = (robot) ->

  robot.router.get "/hubot/version", (req, res) ->
    res.end robot.version

  robot.router.post "/hubot/ping", (req, res) ->
    res.end "PONG"

  robot.router.get "/hubot/time", (req, res) ->
    res.end "Server time is: #{new Date()}"

  robot.router.get "/hubot/info", (req, res) ->
    child = spawn('/bin/sh', ['-c', "echo I\\'m $LOGNAME@$(hostname):$(pwd) \\($(git rev-parse HEAD)\\)"])

    child.stdout.on 'data', (data) ->
      res.end "#{data.toString().trim()} running node #{process.version} [pid: #{process.pid}]"
      child.stdin.end()

  robot.router.get "/hubot/ip", (req, res) ->
    robot.http('http://checkip.dyndns.org').get() (err, r, body) ->
      res.end body

# Description:
#   A simple interaction with the built in HTTP Daemon
#
# Dependencies:
#   None
#
# Configuration:
#   None
#
# Commands:
#   None
#
# URLS:
#   /hubot/version
#   /hubot/ping
#   /hubot/time
#   /hubot/info
#   /hubot/ip

spawn = require('child_process').spawn

module.exports = (robot) ->

  robot.router.get "/hubot/version", (req, res) ->
    res.end robot.version

  robot.router.post "/hubot/ping", (req, res) ->
    res.end "PONG"

  robot.router.get "/hubot/time", (req, res) ->
    res.end "Server time is: #{new Date()}"

  robot.router.get "/hubot/info", (req, res) ->
    child = spawn('/bin/sh', ['-c', "echo I\\'m $LOGNAME@$(hostname):$(pwd) \\($(git rev-parse HEAD)\\)"])

    child.stdout.on 'data', (data) ->
      res.end "#{data.toString().trim()} running node #{process.version} [pid: #{process.pid}]"
      child.stdin.end()

  robot.router.get "/hubot/ip", (req, res) ->
    robot.http('http://ifconfig.me/ip').get() (err, r, body) ->
      res.end body

Change url for ip route

Change url for ip route


Point = require './point'

SpliceArrayChunkSize = 100000

MULTI_LINE_REGEX_REGEX = /[\r\n\[]/

module.exports =
  spliceArray: (originalArray, start, length, insertedArray=[]) ->
    if insertedArray.length < SpliceArrayChunkSize
      originalArray.splice(start, length, insertedArray...)
    else
      removedValues = originalArray.splice(start, length)
      for chunkStart in [0..insertedArray.length] by SpliceArrayChunkSize
        chunkEnd = chunkStart + SpliceArrayChunkSize
        chunk = insertedArray.slice(chunkStart, chunkEnd)
        originalArray.splice(start + chunkStart, 0, chunk...)
      removedValues

  newlineRegex: /\r\n|\n|\r/g

  normalizePatchChanges: (changes) ->
    changes.map (change) -> {
      start: Point.fromObject(change.newStart)
      oldExtent: Point.fromObject(change.oldExtent)
      newExtent: Point.fromObject(change.newExtent)
      newText: change.newText
    }

  regexIsSingleLine: (regex) ->
    not MULTI_LINE_REGEX_REGEX.test(regex.source)

Point = require './point'

SpliceArrayChunkSize = 100000

MULTI_LINE_REGEX_REGEX = /\r|\n|^\[\^|[^\\]\[\^/

module.exports =
  spliceArray: (originalArray, start, length, insertedArray=[]) ->
    if insertedArray.length < SpliceArrayChunkSize
      originalArray.splice(start, length, insertedArray...)
    else
      removedValues = originalArray.splice(start, length)
      for chunkStart in [0..insertedArray.length] by SpliceArrayChunkSize
        chunkEnd = chunkStart + SpliceArrayChunkSize
        chunk = insertedArray.slice(chunkStart, chunkEnd)
        originalArray.splice(start + chunkStart, 0, chunk...)
      removedValues

  newlineRegex: /\r\n|\n|\r/g

  normalizePatchChanges: (changes) ->
    changes.map (change) -> {
      start: Point.fromObject(change.newStart)
      oldExtent: Point.fromObject(change.oldExtent)
      newExtent: Point.fromObject(change.newExtent)
      newText: change.newText
    }

  regexIsSingleLine: (regex) ->
    not MULTI_LINE_REGEX_REGEX.test(regex.source)

Make multi-line regex test more strict

Make multi-line regex test more strict

Regex patterns can only match across line boundaries if they contain a
carriage return, a newline, or a negated character class.


define [
  'jquery'
  'underscore'
  'backbone'
  'jade.templates'
  'mixen'
  'mixens/BaseViewMixen'
  'mixens/CollectionViewMixen'
], ($, _, Backbone, jade, Mixen, BaseView, CollectionView) ->
  class IssuesListView extends Mixen(CollectionView, BaseView)
    template: jade.issues

    initialize: (options) ->
      super

    render: =>
      context = @getRenderContext()
      data =
        issues: _.map @collection.models, (val) -> val.toJSON()

      context = _.extend context, data

      console.log context

      @$el.html @template context

      @

define [
  'jquery'
  'underscore'
  'backbone'
  'jade.templates'
  'mixen'
  'mixens/BaseViewMixen'
  'mixens/CollectionViewMixen'
], ($, _, Backbone, jade, Mixen, BaseView, CollectionView) ->
  class IssuesListView extends Mixen(CollectionView, BaseView)
    template: jade.issues

    initialize: (options) ->
      super

    render: =>
      context = @getRenderContext()
      data =
        issues: _.map @collection.models, (val) -> val.toJSON()

      context = _.extend context, data

      @$el.html @template context

      @

Remove rouge console.log file from the issues list view


exports.Util =
  last: (arr) -> arr[arr.length - 1]

  isString: (s) -> typeof s == 'string' || s instanceof String

  defineNonEnumerable: (obj, k, v) ->
    Object.defineProperty obj, k,
      value: v
      writable: true
      enumerable: false
      configurable: true

# polyfill
if Map?

  if not Map.prototype.items
    Map.prototype.items = () ->
      results = []
      i = this.iterator();
      size = this.size()
      if size
        for num in [1..size]
          results.push(i.next())
      results

  exports.Map = Map

else
  class exports.Map
    constructor: ->
      @cache = Object.create null
      @proto_cache = undefined
      @proto_set = false

    get: (key) ->
      key = key.toString()
      return @cache[key] unless key is '__proto__'
      return @proto_cache

    has: (key) ->
      key = key.toString()
      return key of @cache unless key is '__proto__'
      return @proto_set

    set: (key, value) ->
      unless key.toString() is '__proto__'
        @cache[key] = value
      else
        @proto_cache = value
        @proto_set = true
      value

    items: ->
      items = ([k,v] for k, v of @cache)
      items.push ['__proto__', @proto_cache] if @proto_set
      items

exports.Util =
  last: (arr) -> arr[arr.length - 1]

  isString: (s) -> typeof s == 'string' || s instanceof String

  defineNonEnumerable: (obj, k, v) ->
    Object.defineProperty obj, k,
      value: v
      writable: true
      enumerable: false
      configurable: true

class exports.Map
  constructor: ->
    @cache = Object.create null
    @proto_cache = undefined
    @proto_set = false

  get: (key) ->
    key = key.toString()
    return @cache[key] unless key is '__proto__'
    return @proto_cache

  has: (key) ->
    key = key.toString()
    return key of @cache unless key is '__proto__'
    return @proto_set

  set: (key, value) ->
    unless key.toString() is '__proto__'
      @cache[key] = value
    else
      @proto_cache = value
      @proto_set = true
    value

  items: ->
    items = ([k,v] for k, v of @cache)
    items.push ['__proto__', @proto_cache] if @proto_set
    items

Stop trying to make Map a polyfill.

Stop trying to make Map a polyfill.

ES6's map does provide items(), but it returns an iterator. It only really makes
sense to use it if we have `for-of` loops, so there's little point in trying to
fill in that behavior at the moment.


".source.js":
  "Promise":
    prefix: "promise",
    body: """
    new Promise((resolve, reject) => {
    \t${0}
    });
    """
  "Promise.then":
    prefix: "then"
    body: """
    then((${2:value}) => {
    \t${0}
    });
    """
  "Promise.catch":
    prefix: "catch"
    body: """
    catch(${2:(err)} => {
    \t${0}
    });
    """

".source.js":
  "Promise":
    prefix: "promise",
    body: """
    new Promise((resolve, reject) => {
    \t${0}
    });
    """
  "Promise.ptorotype.then":
    prefix: "then"
    body: """
    then(${1:(${2:value}) => {
    \t${0}
    \\}})
    """
  "Promise.ptorotype.catch":
    prefix: "catch"
    body: """
    catch(${1:(${2:value}) => {
    \t${0}
    \\}})
    """

  "Promise.all":
    prefix: "all"
    body: "Promise.all(${1:iterable})"
  "Promise.all +":
    prefix: "Promise.all"
    body: "Promise.all(${1:iterable})"

  "Promise.race":
    prefix: "race"
    body: "Promise.race(${1:iterable})"
  "Promise.race +":
    prefix: "Promise.race"
    body: "Promise.race(${1:iterable})"

  "Promise.resolve":
    prefix: "resolve"
    body: "Promise.resolve(${1:value})"
  "Promise.resolve +":
    prefix: "Promise.resolve"
    body: "Promise.resolve(${1:value})"

  "Promise.reject":
    prefix: "reject"
    body: "Promise.reject(${1:value})"
  "Promise.reject +":
    prefix: "Promise.reject"
    body: "Promise.reject(${1:value})"

Add Promise.all/race/resolve/reject & improve then/catch


menu: [
  {
    label: 'View'
    submenu: [
      {
        label: 'Toggle Treeview'
        command: 'tree-view:toggle'
      }
    ]
  }
]

'context-menu':
  '.tree-view':
    'Add File': 'tree-view:add'
    'Rename': 'tree-view:move'
    'Delete': 'tree-view:remove'

'menu': [
  {
    'label': 'View'
    'submenu': [
      {
        'label': 'Toggle Treeview'
        'command': 'tree-view:toggle'
      }
    ]
  }
]

'context-menu':
  '.tree-view':
    'Add File': 'tree-view:add'
    'Rename': 'tree-view:move'
    'Delete': 'tree-view:remove'

Switch cson keys to be single quoted

Switch cson keys to be single quoted


$( ->
  $modal = $('.js-feedback-modal')
  $iframe = $('.js-feedback-modal iframe')
  iframeIsLoaded = false

  openFeedbackFrame = ->
    iframeIsLoaded = true
    $modal.fadeIn('fast')
    $iframe[0].contentWindow.updateHeight()

  $(".js-feedback-modal-link").on('click', ->
    if !$iframe.attr('src')
      $iframe.attr('src', $iframe.attr('data-src'))
      $iframe.load(openFeedbackFrame)
    else if(iframeIsLoaded)
      openFeedbackFrame()
  )
  $(".js-feedback-modal-layer").on('click', ->
    $modal.fadeOut('fast', ->
      if iframeIsLoaded
        $iframe[0].contentWindow.onCloseFrame()
    )
  )
)

$ ->
  $modal = $('.js-feedback-modal')
  $iframe = $('.js-feedback-modal iframe')
  iframeIsLoaded = false

  openFeedbackFrame = ->
    iframeIsLoaded = true
    $modal.fadeIn('fast')
    $iframe[0].contentWindow.updateHeight()

  $(".js-feedback-modal-link").on 'click', ->
    if !$iframe.attr('src')
      $iframe.attr('src', $iframe.attr('data-src'))
      $iframe.load(openFeedbackFrame)
    else if iframeIsLoaded
      openFeedbackFrame()

  $(".js-feedback-modal-layer").on 'click', ->
    $modal.fadeOut 'fast', ->
      if iframeIsLoaded
        $iframe[0].contentWindow.onCloseFrame()

Remove some parens per @markijbema's suggestion.


Backbone.Factlink ||= {}

class TooltipCreator
  constructor: (@_$offsetParent, @_positioning, @_tooltipViewFactory)->

  createTooltip: ($target) ->
    @_positionedRegion().bindToElement $target, @_$offsetParent
    @_positionedRegion().show @_tooltipView()
    @_positionedRegion().updatePosition()

    @_positionedRegion().$el

  removeTooltip: ->
    @_positionedRegion().resetFade()

  _tooltipView: ->
    tooltipOptions = _.extend {},
      contentView: @_tooltipViewFactory(),
      @_positioning
    new PopoverView tooltipOptions

  _positionedRegion: ->
    return @__positionedRegion if @__positionedRegion?

    region_options = _.extend {fadeTime: 100} , @_positioning
    @__positionedRegion = new Backbone.Factlink.PositionedRegion region_options

Backbone.Factlink.makeTooltipForView = (parentView, options) ->
  _.defaults options, $offsetParent: parentView.$el

  tooltipCreator = new TooltipCreator options.$offsetParent,
    options.positioning, options.tooltipViewFactory

  tooltipOpener = new Backbone.Factlink.TooltipOpener
    el: parentView.$(options.selector)
    tooltipCreator: tooltipCreator

  tooltipOpener.render()

  parentView.on 'close', => tooltipOpener.close()

Backbone.Factlink ||= {}

class TooltipCreator
  constructor: (@_$offsetParent, @_positioning, @_tooltipViewFactory)->

  createTooltip: ($target) ->
    @_ensureRegion()
    @_positionedRegion.bindToElement $target, @_$offsetParent
    @_positionedRegion.show @_tooltipView()
    @_positionedRegion.updatePosition()

    @_positionedRegion.$el

  removeTooltip: ->
    @_positionedRegion.resetFade() if @_positionedRegion

  _tooltipView: ->
    tooltipOptions = _.extend {},
      contentView: @_tooltipViewFactory(),
      @_positioning
    new PopoverView tooltipOptions

  _ensureRegion: ->
    if !@_positionedRegion?
      region_options = _.extend { fadeTime: 100 }, @_positioning
      @_positionedRegion = new Backbone.Factlink.PositionedRegion region_options

Backbone.Factlink.makeTooltipForView = (parentView, options) ->
  _.defaults options, $offsetParent: parentView.$el

  tooltipCreator = new TooltipCreator options.$offsetParent,
    options.positioning, options.tooltipViewFactory

  tooltipOpener = new Backbone.Factlink.TooltipOpener
    el: parentView.$(options.selector)
    tooltipCreator: tooltipCreator

  tooltipOpener.render()

  parentView.on 'close', => tooltipOpener.close()

Fix multiple tooltips in stream; don't clean up positionedRegion if it wasn't created.


module.exports = (grunt) ->
  grunt.initConfig
    pkgFile: 'package.json'

    files:
      adapter: ['src/adapter.js']

    build:
      adapter: '<%= files.adapter %>'

    # JSHint options
    # http://www.jshint.com/options/
    jshint:
      adapter:
        files:
          src: '<%= files.adapter %>'
        options:
          browser: true,
          es5: true,
          strict: false
          undef: false
          camelcase: false

      options:
        quotmark: 'single'
        camelcase: true
        strict: true
        trailing: true
        curly: true
        eqeqeq: true
        immed: true
        latedef: true
        newcap: true
        noarg: true
        sub: true
        undef: true
        boss: true
        globals: {}

    testacular:
      adapter:
        configFile: 'testacular.conf.js'
        autoWatch: false
        singleRun: true
        reporters: ['dots']

  grunt.loadTasks 'tasks'
  grunt.loadTasks '../testacular/tasks'
  grunt.loadNpmTasks 'grunt-contrib-jshint'
  grunt.loadNpmTasks 'gruntacular'

  grunt.registerTask 'default', ['build', 'jshint', 'test']
  grunt.registerTask 'test', ['testacular']

  # TODO(vojta): release task
  # grunt.registerTask 'release', 'Build, bump and publish to NPM.', (type) ->
  #   grunt.task.run [
  #     'build',
  #     "bump:#{type||'patch'}",
  #     'npm-publish'
  #   ]

module.exports = (grunt) ->
  grunt.initConfig
    pkgFile: 'package.json'

    files:
      adapter: ['src/adapter.js']

    build:
      adapter: '<%= files.adapter %>'

    # JSHint options
    # http://www.jshint.com/options/
    jshint:
      adapter:
        files:
          src: '<%= files.adapter %>'
        options:
          browser: true,
          es5: true,
          strict: false
          undef: false
          camelcase: false

      options:
        quotmark: 'single'
        camelcase: true
        strict: true
        trailing: true
        curly: true
        eqeqeq: true
        immed: true
        latedef: true
        newcap: true
        noarg: true
        sub: true
        undef: true
        boss: true
        globals: {}

    testacular:
      adapter:
        configFile: 'testacular.conf.js'
        autoWatch: false
        singleRun: true
        reporters: ['dots']

  grunt.loadTasks 'tasks'
  # grunt.loadTasks '../testacular/tasks'
  grunt.loadNpmTasks 'grunt-contrib-jshint'
  grunt.loadNpmTasks 'gruntacular'

  grunt.registerTask 'default', ['build', 'jshint', 'test']
  grunt.registerTask 'test', ['testacular']

  # TODO(vojta): release task
  # grunt.registerTask 'release', 'Build, bump and publish to NPM.', (type) ->
  #   grunt.task.run [
  #     'build',
  #     "bump:#{type||'patch'}",
  #     'npm-publish'
  #   ]

Comment out my local tasks

Comment out my local tasks