#= require jquery
#= require jquery_ujs

#= require jquery-fileupload/basic
#= require jquery-fileupload/vendor/tmpl
#= require attachments
#= require dropzone_effects

#= require bootstrap-sprockets

#= require moment
#= require moment/de
#= require bootstrap-datetimepicker

#= require marked.min
# http://github.com/toopay/bootstrap-markdown
#= require bootstrap-markdown

# NOTE: requires api controller to render markdown preview.
#       see echo-live for code
# require markdown_setup

#= require flyout
#= require cocoon

# NOTE: gem's not getting loaded for unknown reason
# require editable/bootstrap-editable
# require editable/rails

#= require jquery.autogrow
#= require clndr
#= require json3
#= require underscore

$ ->
  $('.datepicker').datetimepicker
    locale: 'de'
    pick12HourFormat: false
    minuteStepping: 15

  $('textarea').autoGrow()

  $(document).on "click", ".panel-toggle-btn", ->
    $(this).closest(".panel").toggleClass "closed"

#= require jquery
#= require jquery_ujs

#= require jquery-fileupload/basic
#= require jquery-fileupload/vendor/tmpl
#= require attachments
#= require dropzone_effects

#= require bootstrap-sprockets

#= require moment
#= require moment/de
#= require bootstrap-datetimepicker

#= require marked.min
# http://github.com/toopay/bootstrap-markdown
#= require bootstrap-markdown

# NOTE: requires api controller to render markdown preview.
#       see echo-live for code
# require markdown_setup

#= require flyout
#= require cocoon

# NOTE: gem's not getting loaded for unknown reason
# require editable/bootstrap-editable
# require editable/rails

#= require jquery.autogrow
#= require clndr
#= require json3
#= require underscore

$ ->
  $('.datepicker').datetimepicker

  $('textarea').autoGrow()

  $(document).on "click", ".panel-toggle-btn", ->
    $(this).closest(".panel").toggleClass "closed"

Remove outdated options from datetimepicker call


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

# For more detailed documentation see
# https://atom.io/docs/latest/advanced/keymaps

'.table-edit':
  'tab': 'core:move-right'
  'shift-tab': 'core:move-left'
  'cmd-shift-right': 'table-edit:select-to-end-of-line'
  'cmd-shift-left': 'table-edit:select-to-beginning-of-line'
  'cmd-shift-up': 'table-edit:select-to-beginning-of-table'
  'cmd-shift-down': 'table-edit:select-to-end-of-table'

'.table-edit atom-text-editor':
  'enter': 'core:confirm'
  'ctrl-enter': 'editor:newline'
  'tab': 'table-edit:move-right'
  'shift-tab': 'table-edit:move-left'
  'ctrl-tab': 'editor:indent'

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

# For more detailed documentation see
# https://atom.io/docs/latest/advanced/keymaps

'.table-edit':
  'tab': 'core:move-right'
  'shift-tab': 'core:move-left'
  'cmd-shift-right': 'table-edit:select-to-end-of-line'
  'cmd-shift-left': 'table-edit:select-to-beginning-of-line'
  'cmd-shift-up': 'table-edit:select-to-beginning-of-table'
  'cmd-shift-down': 'table-edit:select-to-end-of-table'
  'cmd-alt-up': 'table-edit:insert-row-before'
  'cmd-alt-down': 'table-edit:insert-row-after'
  'cmd-alt-left': 'table-edit:insert-column-before'
  'cmd-alt-right': 'table-edit:insert-column-after'

'.table-edit atom-text-editor':
  'enter': 'core:confirm'
  'ctrl-enter': 'editor:newline'
  'tab': 'table-edit:move-right'
  'shift-tab': 'table-edit:move-left'
  'ctrl-tab': 'editor:indent'

Add keybinding for row and columns insertion commands


xml = require 'xml'

module.exports = (env, callback) ->

  class Sitemap extends env.plugins.Page

    getFilename: ->
      'sitemap.xml'

    getPages: (contents) ->
      pages = []
      for filename in Object.keys contents
        content = contents[filename]
        if content instanceof env.plugins.MarkdownPage
          pages.push content
        else if content instanceof env.ContentTree
          Array::push.apply pages, @getPages content
      pages

    getView: -> (env, locals, contents, templates, callback) ->
      sitemap =
        urlset: [
          _attr:
            xmlns: 'http://www.sitemaps.org/schemas/sitemap/0.9'
        ]
      for page in @getPages contents
        url = []
        url.push loc: "#{locals.url}#{page.url}"
        if page.metadata?
          if page.metadata.date?
            url.push lastmod: page.metadata.date.toISOString()
          if page.metadata.changefreq?
            url.push changefreq: page.metadata.changefreq
          if page.metadata.priority?
            url.push priority: page.metadata.priority
        sitemap.urlset.push url: url
      callback null, new Buffer xml sitemap, declaration: true

  env.registerGenerator 'sitemap', (contents, callback) ->
    tree =
      sitemap: new Sitemap
    callback null, tree

  callback()

xml = require 'xml'

module.exports = (env, callback) ->

  class Sitemap extends env.plugins.Page

    getFilename: ->
      'sitemap.xml'

    getPages: (contents) ->
      pages = []
      for filename in Object.keys contents
        content = contents[filename]
        if content instanceof env.plugins.MarkdownPage
          pages.push content
        else if content instanceof env.ContentTree
          Array::push.apply pages, @getPages content
      pages

    getView: -> (env, locals, contents, templates, callback) ->
      sitemap =
        urlset: [
          _attr:
            xmlns: 'http://www.sitemaps.org/schemas/sitemap/0.9'
        ]
      for page in @getPages contents
        url = []
        url.push loc: "#{locals.url}#{page.url}"
        if page.metadata?
          if page.metadata.date?
            url.push lastmod: page.metadata.date.toISOString()
          if page.metadata.changefreq?
            url.push changefreq: page.metadata.changefreq
          if page.metadata.priority?
            url.push priority: page.metadata.priority
        sitemap.urlset.push url: url
      callback null, new Buffer xml sitemap, declaration: true

  env.registerGenerator 'sitemap', (contents, callback) ->
    tree =
      'sitemap.xml': new Sitemap
    callback null, tree

  callback()

Rename sitemap in content tree

Rename sitemap in content tree


path = require 'path'
fs = require 'fs-plus'

# Editor model for an image file
module.exports =
class ImageEditor
  atom.deserializers.add(this)

  @deserialize: ({filePath}) ->
    if fs.isFileSync(filePath)
      new ImageEditor(filePath)
    else
      console.warn "Could not deserialize image editor for path '#{filePath}' because that file no longer exists"

  constructor: (@filePath) ->

  serialize: ->
    {@filePath}

  getViewClass: ->
    require './image-editor-view'

  # Retrieves the filename of the open file.
  #
  # This is `'untitled'` if the file is new and not saved to the disk.
  #
  # Returns a {String}.
  getTitle: ->
    if @filePath?
      path.basename(@filePath)
    else
      'untitled'

  # Retrieves the URI of the current image.
  #
  # Returns a {String}.
  getUri: ->
    @filePath

  # Compares two {ImageEditor}s to determine equality.
  #
  # Equality is based on the condition that the two URIs are the same.
  #
  # Returns a {Boolean}.
  isEqual: (other) ->
    other instanceof ImageEditor and @getUri() is other.getUri()

path = require 'path'
fs = require 'fs-plus'

# Editor model for an image file
module.exports =
class ImageEditor
  atom.deserializers.add(this)

  @deserialize: ({filePath}) ->
    if fs.isFileSync(filePath)
      new ImageEditor(filePath)
    else
      console.warn "Could not deserialize image editor for path '#{filePath}' because that file no longer exists"

  constructor: (@filePath) ->

  serialize: ->
    {@filePath, deserializer: @constructor.name}

  getViewClass: ->
    require './image-editor-view'

  # Retrieves the filename of the open file.
  #
  # This is `'untitled'` if the file is new and not saved to the disk.
  #
  # Returns a {String}.
  getTitle: ->
    if @filePath?
      path.basename(@filePath)
    else
      'untitled'

  # Retrieves the URI of the current image.
  #
  # Returns a {String}.
  getUri: ->
    @filePath

  # Compares two {ImageEditor}s to determine equality.
  #
  # Equality is based on the condition that the two URIs are the same.
  #
  # Returns a {Boolean}.
  isEqual: (other) ->
    other instanceof ImageEditor and @getUri() is other.getUri()

Add deserializer property to serialized state


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

module.exports =
  config:
    onlyHighlightWholeWords:
      type: 'boolean'
      default: false
    hideHighlightOnSelectedWord:
      type: 'boolean'
      default: false
    ignoreCase:
      type: 'boolean'
      default: false
    lightTheme:
      type: 'boolean'
      default: false
    highlightBackground:
      type: 'boolean'
      default: false
    minimumLength:
      type: 'integer'
      default: 0

  areaView: null

  activate: (state) ->
    @areaView = new HighlightedAreaView()
    @areaView.attach()

  deactivate: ->
    @areaView.destroy()
    @areaView = null

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

module.exports =
  config:
    onlyHighlightWholeWords:
      type: 'boolean'
      default: false
    hideHighlightOnSelectedWord:
      type: 'boolean'
      default: false
    ignoreCase:
      type: 'boolean'
      default: false
    lightTheme:
      type: 'boolean'
      default: false
    highlightBackground:
      type: 'boolean'
      default: false
    minimumLength:
      type: 'integer'
      default: 0

  areaView: null

  activate: (state) ->
    @areaView = new HighlightedAreaView()
    @areaView.attach()

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

Fix undefined error on destroy

Fix undefined error on destroy


Module = require 'module'
fs = require 'fs-plus'

originalResolveFilename = Module._resolveFilename

# Precompute versions of all modules in node_modules
# Precompute the version each file is compatible

Module._resolveFilename = (relative, parent) ->
  resolved = originalResolveFilename.apply(global, arguments)
  if relative[0] isnt '.' and not fs.isAbsolute(relative) and relative isnt resolved
    console.log "#{relative} -> #{resolved}"
  resolved

Module = require 'module'
fs = require 'fs-plus'

originalResolveFilename = Module._resolveFilename

# Precompute versions of all modules in node_modules
# Precompute the version each file is compatible

getCachedModulePath = (relative, parent) ->
  return unless relative
  return unless parent?.id

  return if relative[0] is '.'
  return if relative[relative.length - 1] is '/'
  return if fs.isAbsolute(relative)

  console.log "looking up #{relative} from #{parent.id}"

  undefined

Module._resolveFilename = (relative, parent) ->
  getCachedModulePath(relative, parent) ? originalResolveFilename(relative, parent)

Add initial cache lookup method

Add initial cache lookup method


FormView = require './form-view'
Configuration = require './configuration.coffee'

class ConfigurationFormView extends FormView

  initialize: ->
    super

    @addRow @createTitleRow("Editing Tools Configuration")
    # @addRow @createFieldRow("repoUrl", "text", Configuration.labels.repoUrl)
    @addRow @createFieldRow("fullName", "text", Configuration.labels.fullName)
    @addRow @createFieldRow("email", "text", Configuration.labels.email)
    # @addRow @createFieldRow("repoOwner", "text", Configuration.labels.repoOwner)
    # @addRow @createFieldRow("username", "text", Configuration.labels.username)
    # @addRow @createFieldRow("repoUsername", "text", Configuration.labels.repoUsername)
    # @addRow @createFieldRow("password", "password", Configuration.labels.password)
    @addRow @createFieldRow("cloneDir", "directory", Configuration.labels.cloneDir)
    @addRow @createFieldRow("advancedMode", "checkbox", Configuration.labels.advancedMode)

module.exports = document.registerElement('awe-configuration-form-view', prototype: ConfigurationFormView.prototype, extends: 'div')

FormView = require './form-view'
Configuration = require './configuration.coffee'

class ConfigurationFormView extends FormView

  initialize: ->
    super

    @addRow @createTitleRow("Editing Tools Configuration")
    # @addRow @createFieldRow("repoUrl", "text", Configuration.labels.repoUrl)
    @addRow @createFieldRow("fullName", "text", Configuration.labels.fullName)
    @addRow @createFieldRow("email", "text", Configuration.labels.email)
    # @addRow @createFieldRow("repoOwner", "text", Configuration.labels.repoOwner)
    # @addRow @createFieldRow("username", "text", Configuration.labels.username)
    # @addRow @createFieldRow("repoUsername", "text", Configuration.labels.repoUsername)
    # @addRow @createFieldRow("password", "password", Configuration.labels.password)
    @addRow @createFieldRow("cloneDir", "directory", Configuration.labels.cloneDir)
    # @addRow @createFieldRow("advancedMode", "checkbox", Configuration.labels.advancedMode)

module.exports = document.registerElement('awe-configuration-form-view', prototype: ConfigurationFormView.prototype, extends: 'div')

Hide "advancedMode" option and assume it as always true


initMobileMenu = ->

  $body = $('body')
  $inputQuery = $("#search_form_query")
  $homeTemplate = $("body.template--pages-home").length
  $inputLocationContainer = $(".search_form_search_location")
  $submit = $(".main-search__submit")
  $distributor = $(".Distributor--navigation")
  smartphone = ($(window).width() < 1000)

  if $homeTemplate
    return

  if smartphone
    $inputQuery.click ->
      $inputLocationContainer.toggleClass "is-visible"
      $submit.toggleClass "is-enlarged"
      $distributor.toggleClass "is-enlarged"
      $body.toggleClass "has-enlarged-header"

$(document).on 'page:load', initMobileMenu
$(document).on 'ajax_loaded', initMobileMenu
$(document).ready initMobileMenu
$(window).resize initMobileMenu

initMobileMenu = ->

  $body = $('body')
  $inputQuery = $("#search_form_query")
  $homeTemplate = $("body.template--pages-home").length
  $inputLocationContainer = $(".search_form_search_location")
  $submit = $(".main-search__submit")
  $distributor = $(".Distributor--navigation")
  smartphone = ($(window).width() < 1000)

  if $homeTemplate
    return

  if smartphone
    $inputQuery.unbind('click').click ->
      $inputLocationContainer.toggleClass "is-visible"
      $submit.toggleClass "is-enlarged"
      $distributor.toggleClass "is-enlarged"
      $body.toggleClass "has-enlarged-header"

$(document).on 'page:load', initMobileMenu
$(document).on 'ajax_loaded', initMobileMenu
$(document).ready initMobileMenu
$(window).resize initMobileMenu

Add double click fix to last commit

Add double click fix to last commit


fs = require "fs"
path = require "path"

module.exports = (robot, scripts) ->
    scriptsPath = path.resolve(__dirname, "src")
    fs.exists scriptsPath, (exists) ->
        if exists
            for script in fs.readdirSync(scriptsPath)
                if scripts? and "*" not in scripts
                    robot.loadFile(scriptsPath, script) if script in scripts
                else
                    robot.loadFile(scriptsPath, script)

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

module.exports = (robot, scripts) ->
    scriptsPath = path.resolve(__dirname, 'src')
    if fs.existsSync scriptsPath
        for script in fs.readdirSync(scriptsPath).sort()
            if scripts? and '*' not in scripts
                robot.loadFile(scriptsPath, script) if script in scripts
            else
                robot.loadFile(scriptsPath, script)

Load / run scripts synchronously

Load / run scripts synchronously


_ = require('underscore')

httpVerbWhitelist = ['GET']

module.exports = (req, res, next) ->
  if process.env.NODE_ENV is 'production'
    httpVerbWhitelist = []

  return next() if /^\/login/.test(req.path)
  return next() if process.env.NODE_ENV == 'test'
  return next() if req.isAuthenticated() or _.contains(httpVerbWhitelist, req.method)

  return res.redirect('/login')

_ = require('underscore')

httpVerbWhitelist = ['GET']
if process.env.NODE_ENV is 'production'
  httpVerbWhitelist = []

module.exports = (req, res, next) ->
  return next() if /^\/login/.test(req.path)
  return next() if process.env.NODE_ENV == 'test'
  return next() if req.isAuthenticated() or _.contains(httpVerbWhitelist, req.method)

  return res.redirect('/login')

Stop doing hard stuff on every request

Stop doing hard stuff on every request


'.platform-darwin, .platform-win32, .platform-linux':
  'ctrl-g': 'go-to-line:toggle'

'.go-to-line .mini.editor input':
  'enter': 'core:confirm',
  'escape': 'core:cancel'

'.platform-darwin .go-to-line .mini.editor input':
  'cmd-w': 'core:cancel'

'.platform-win32 .go-to-line .mini.editor input':
  'ctrl-w': 'core:cancel'

'.platform-linux .go-to-line .mini.editor input':
  'ctrl-w': 'core:cancel'

'.platform-darwin, .platform-win32, .platform-linux':
  'ctrl-g': 'go-to-line:toggle'

'.go-to-line atom-text-editor[mini]':
  'enter': 'core:confirm',
  'escape': 'core:cancel'

'.platform-darwin .go-to-line atom-text-editor[mini]':
  'cmd-w': 'core:cancel'

'.platform-win32 .go-to-line atom-text-editor[mini]':
  'ctrl-w': 'core:cancel'

'.platform-linux .go-to-line atom-text-editor[mini]':
  'ctrl-w': 'core:cancel'

Update deprecated selectors in keymap

Update deprecated selectors in keymap


React        = require 'react'
ReactRouter  = require 'react-router'
Router       = ReactRouter.Router
Link         = ReactRouter.Link

DidYouKnowHandler = require './did_you_know_handler'
PlagiarismHandler = require './plagiarism_handler'
RecentEditsHandler = require './recent_edits_handler'

RecentActivityHandler = React.createClass(
  displayName: 'RecentActivityHandler'
  render: ->

<Link to="did-you-know">Did You Know Eligible</Link>

<Link to="plagiarism">Possible Plagiarism</Link>

<Link to="recent-edits">Recent Edits</Link>

      {@props.children}

)

module.exports = RecentActivityHandler

React        = require 'react'
ReactRouter  = require 'react-router'
Router       = ReactRouter.Router
Link         = ReactRouter.Link

DidYouKnowHandler = require './did_you_know_handler'
PlagiarismHandler = require './plagiarism_handler'
RecentEditsHandler = require './recent_edits_handler'

RecentActivityHandler = React.createClass(
  displayName: 'RecentActivityHandler'
  render: ->

<Link to="recent-activity">Did You Know Eligible</Link>

<Link to="recent-activity/plagiarism">Possible Plagiarism</Link>

<Link to="recent-activity/recent-edits">Recent Edits</Link>

      {@props.children}

)

module.exports = RecentActivityHandler

Fix routing of recent activity links

Fix routing of recent activity links


# $(document).ready ->

# 	body = $('html, body')
# 	anchorClick = (link) ->
# 		linkSplit = link.split('/').pop()
# 		$.get 'pages/' + linkSplit, (data) ->
# 			$('#content').html data
# 			return
# 		return

# 	# Scroll on top when change page
# 	# $(body).scrollTop 0
# 	# Special function for lightbox
# 	# $('.magnific-image-link').magnificPopup type: 'image'
#  	# $('.magnific-video-link').magnificPopup type: 'iframe'

# 	$('#container').on 'click', '.changepage', (e) ->
# 		window.history.pushState(null, null, $(this).attr('href'))
# 		anchorClick($(this).attr('href'))
# 		e.preventDefault()
# 		return
# 	window.addEventListener 'popstate', (e) ->
# 		anchorClick(location.pathname)
# 		return
# 	return

$(document).ready(function() {

	function anchorClick(link) {
		var linkSplit = link.split('/').pop();
		$.get('pages/' + linkSplit, function(data) {
			$('#content').html(data);
		});
	}

	$('#container').on('click', 'a', function(e) {
		window.history.pushState(null, null, $(this).attr('href'));
		anchorClick($(this).attr('href'));
		e.preventDefault();
	});

	window.addEventListener('popstate', function(e) {
		anchorClick(location.pathname);
	});

});

$(document).ready ->

  anchorClick = (link) ->
    linkSplit = link.split('/').pop()
    $.get 'pages/' + linkSplit, (data) ->
      $('#content').html data
      return
    return

  $('#container').on 'click', 'a', (e) ->
    window.history.pushState null, null, $(this).attr('href')
    anchorClick $(this).attr('href')
    e.preventDefault()
    return
  window.addEventListener 'popstate', (e) ->
    anchorClick location.pathname
    return
  return

# $(document).ready(function() {

# 	function anchorClick(link) {
# 		var linkSplit = link.split('/').pop();
# 		$.get('pages/' + linkSplit, function(data) {
# 			$('#content').html(data);
# 		});
# 	}

# 	$('#container').on('click', 'a', function(e) {
# 		window.history.pushState(null, null, $(this).attr('href'));
# 		anchorClick($(this).attr('href'));
# 		e.preventDefault();
# 	});

# 	window.addEventListener('popstate', function(e) {
# 		anchorClick(location.pathname);
# 	});

# });

Test history api with coffee

Test history api with coffee


"*":
  core:
    audioBeep: false
    closeEmptyWindows: false
    disabledPackages: [
      "language-clojure"
      "language-go"
      "language-java"
      "language-perl"
      "language-objective-c"
      "language-property-list"
      "metrics"
      "open-on-github"
      "package-generator"
      "symbols-view"
      "language-toml"
      "spell-check"
    ]
    packagesWithKeymapsDisabled: [
      "refactor"
      "es-identifier-highlight"
      "sort-lines"
      "csslint"
    ]
    telemetryConsent: "limited"
  csslint:
    hideOnNoErrors: true
  editor:
    invisibles: {}
    showInvisibles: true
  "exception-reporting":
    userId: "2ab09d56-2ab2-2fc5-7065-f68aca373175"
  "find-and-replace": {}
  linter:
    lintOnChange: false
  "linter-jscs":
    configPath: ".jscsrc"
    onlyConfig: true
  "linter-jshint":
    disableWhenNoJshintrcFileInPath: true
  "linter-ui-default":
    showPanel: true
  "spell-check": {}
  tabs:
    enableVcsColoring: true
  "tree-view":
    hideIgnoredNames: true
    hideVcsIgnoredFiles: true
  welcome:
    showOnStartup: false
  whitespace:
    ignoreWhitespaceOnCurrentLine: false

"*":
  core:
    audioBeep: false
    closeEmptyWindows: false
    disabledPackages: [
      "language-clojure"
      "language-go"
      "language-java"
      "language-perl"
      "language-objective-c"
      "language-property-list"
      "metrics"
      "open-on-github"
      "package-generator"
      "symbols-view"
      "language-toml"
      "spell-check"
      "github"
    ]
    packagesWithKeymapsDisabled: [
      "refactor"
      "es-identifier-highlight"
      "sort-lines"
      "csslint"
    ]
    telemetryConsent: "limited"
  csslint:
    hideOnNoErrors: true
  editor:
    invisibles: {}
    showInvisibles: true
  "exception-reporting":
    userId: "2ab09d56-2ab2-2fc5-7065-f68aca373175"
  "find-and-replace": {}
  linter:
    lintOnChange: false
  "linter-jscs":
    configPath: ".jscsrc"
    onlyConfig: true
  "linter-jshint":
    disableWhenNoJshintrcFileInPath: true
  "linter-ui-default":
    showPanel: true
  "spell-check": {}
  tabs:
    enableVcsColoring: true
  "tree-view":
    hideIgnoredNames: true
    hideVcsIgnoredFiles: true
  welcome:
    showOnStartup: false
  whitespace:
    ignoreWhitespaceOnCurrentLine: false

Disable github package bundled with atom since 1.18.0

No, thank you @atom.


class MainChatPanel extends JView

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

    @registerSingleton "chatPanel", @, yes

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

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

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

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

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

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

  isVisible:->
    @hasClass 'visible'

class MainChatPanel extends JView

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

    @registerSingleton "chatPanel", @, yes

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

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

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

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

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

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

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

  isVisible:->
    @hasClass 'visible'

Hide the panel when it looses focues

Hide the panel when it looses focues


async = require "async"
{assign} = require "lodash"

class PhantomTask

	constructor: (options) ->
		@jobs = []
		@scripts = []
		@options = assign {}, options
		@page = require('./webpage').create()

	add: (request, description = "Job ##{@jobs.length}", options = {}) ->

		task = require(request) options
		@jobs.push (done) =>
			console.log description
			task.apply @page, [done]
		@

	inject: (script) ->
		@scripts.push script
		@

	run: (url, callback) ->
		unless typeof callback is "function"
			callback = ->

		@page.open url, (status) =>
			unless status is "success"
				callback new Error "Failed to open page: #{src}"

			for script in [].concat(@scripts)
				@page.injectJs script

			method = if @options.parallel then "parallel" else "series"
			async[method] @jobs, (error) ->
				callback error
		@

module.exports = PhantomTask

async = require "async"
{assign} = require "lodash"

class PhantomTask

	constructor: (options) ->
		@jobs = []
		@scripts = []
		@options = assign {}, options
		@page = require('./webpage').create()

	add: (request, description = "Job ##{@jobs.length}", options = {}) ->

		task = require(request) options
		@jobs.push (done) =>
			console.log description
			task.apply @page, [done]
		@

	inject: (script) ->
		@scripts.push script
		@

	run: (url, callback) ->
		unless typeof callback is "function"
			callback = ->

		@page.open url, (status) =>
			unless status is "success"
				callback new Error "Failed to open page: #{url}"

			for script in [].concat(@scripts)
				@page.injectJs script

			method = if @options.parallel then "parallel" else "series"
			async[method] @jobs, (error) ->
				callback error
		@

module.exports = PhantomTask

Fix error message upon page load failure.


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(() ->
        timeout = Date.now()
        timeout -= Meteor.settings.inactivityTimeout || 300000
        users = _.pluck(Sessions.find({heartbeat: {$lt: timeout}}).fetch(), 'user_id')
        for id in users
            Meteor.users.update(id ,{$set: {'services.resume.loginTokens': []}})
            Sessions.remove({user_id: id})

    , 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 || 3000
    Meteor.setInterval(() ->
        timeout = Date.now()
        timeout -= Meteor.settings.inactivityTimeout || 300000
        user_ids = _.pluck(Sessions.find({heartbeat: {$lt: timeout}}).fetch(), 'user_id')
        if user_ids.length
            Meteor.users.update({_id: {$in: user_ids}} ,{$set: {'services.resume.loginTokens': []}})
            Sessions.remove({user_id: {$in: user_ids}})

    , interval)

Optimize performance by reducing amount of DB requests


`import Em from "vendor/ember"`

Component = Em.Component.extend
    initializeValue: Em.on "init", ->
        prompt = @get "content.prompt"
        if prompt
            options = @get "content.options"
            if prompt isnt options[0].label
                options.unshift
                    label: prompt
                    value: ""
        else
            options = @get "content.options"
            if @get "content.is_optional"
                if options[0].label isnt "" and not @get "content.editable"
                    options.unshift
                        label: ""
                        value: ""

    initializeTypeahead: Em.on "didInsertElement", ->
        @$().find(".typeahead").typeahead
            source: @get("content.options").map (o) -> o.label

    label: ""
    updateValue: Em.observer "label", ->
        label = @get "label"
        values = (i.value for i in @get("content.options") when i.label is label)
        if values.length is 1
            @set "content.value", values[0]
        else
            @set "content.value", label

    sanitizeValue: Em.observer "content.options", ->
        options = (o.value for o in @get "content.options")
        value = @get "content.value"
        if value not in options
            @set "content.value", options[0]

    editing: true
    actions:
        toggleEdit: ->
            @toggleProperty "editing"

`export default Component`

`import Em from "vendor/ember"`

Component = Em.Component.extend
    initializeValue: Em.on "init", ->
        prompt = @get "content.prompt"
        if prompt
            options = @get "content.options"
            if prompt isnt options[0].label
                options.unshift
                    label: prompt
                    value: ""
        else
            options = @get "content.options"
            if @get "content.is_optional"
                if options[0]?.label isnt "" and not @get "content.editable"
                    options.unshift
                        label: ""
                        value: ""

    initializeTypeahead: Em.on "didInsertElement", ->
        @$().find(".typeahead").typeahead
            source: @get("content.options").map (o) -> o.label

    label: ""
    updateValue: Em.observer "label", ->
        label = @get "label"
        values = (i.value for i in @get("content.options") when i.label is label)
        if values.length is 1
            @set "content.value", values[0]
        else
            @set "content.value", label

    sanitizeValue: Em.observer "content.options", ->
        options = (o.value for o in @get "content.options")
        value = @get "content.value"
        if value not in options
            @set "content.value", options[0]

    editing: true
    actions:
        toggleEdit: ->
            @toggleProperty "editing"

`export default Component`

Fix initialization of empty selects.

Fix initialization of empty selects.


globals = require 'globals'

module.exports = (url) ->

  return url  if globals.config.environment is 'dev'
  return url  if /p.koding.com/.test url

  # let's use DOM for parsing the url
  parser = global.document.createElement('a')
  parser.href = url

  # build our new url, example:
  # old: http://54.164.174.218:3000/kite
  # new: https://koding.com/-/prodproxy/54.164.243.111/kite
  #           or
  #      http://localhost:8090/-/prodproxy/54.164.243.111/kite

  { protocol } = global.document.location

  proxy = if globals.config.environment is 'production'
  then 'prodproxy'
  else 'devproxy'

  subdomain = if globals.config.environment is 'production'
  then 'p'
  else 'dev-p'

  return "#{protocol}//#{subdomain}.koding.com/-/#{proxy}/#{parser.hostname}#{parser.pathname}"

globals = require 'globals'

module.exports = (url) ->

  return url  if globals.config.environment is 'dev'
  return url  if /p\.koding\.com/.test url

  # let's use DOM for parsing the url
  parser = global.document.createElement('a')
  parser.href = url

  # build our new url, example:
  # old: http://54.164.174.218:3000/kite
  # new: https://koding.com/-/prodproxy/54.164.243.111/kite
  #           or
  #      http://localhost:8090/-/prodproxy/54.164.243.111/kite

  { protocol } = global.document.location

  if /\.koding\.me$/.test parser.hostname
    proxy = 'devtunnel'
    if globals.config.environment is 'production'
      proxy = 'prodtunnel'
  else
    proxy = if globals.config.environment is 'production'
    then 'prodproxy'
    else 'devproxy'

  subdomain = if globals.config.environment is 'production'
  then 'p'
  else 'dev-p'

  return "#{protocol}//#{subdomain}.koding.com/-/#{proxy}/#{parser.hostname}#{parser.pathname}"

ProxifyUrl: Use {prod, dev}tunnel for tunnelled kites


#!/usr/bin/env coffee

express        = require 'express'
path           = require 'path'
favicon        = require 'serve-favicon'
logger         = require 'morgan'
methodOverride = require 'method-override'
session        = require 'express-session'
bodyParser     = require 'body-parser'
multer         = require 'multer'
errorHandler   = require 'errorhandler'

TemperatureController = require './controllers/temperature-controller'
temperatureController = new TemperatureController()

app = express()

app.set 'port', process.env.WEATHER_SERVICE_PORT || 3000
app.set 'views', path.join(__dirname, 'views')
app.set 'view engine', 'jade'
# app.use favicon(__dirname + '/public/favicon.ico'
app.use logger('dev')
app.use methodOverride()
app.use session(resave: true, saveUninitialized: true, secret: 'uwotm8')
app.use bodyParser.json()
app.use bodyParser.urlencoded(extended: true)
app.use multer()
app.use express.static(path.join(__dirname, 'public'))

app.use errorHandler()

app.get '/temperature/c', temperatureController.celsius
app.get '/temperature/celsius', temperatureController.celsius

app.get '/temperature/f', temperatureController.fahrenheit
app.get '/temperature/fahrenheit', temperatureController.fahrenheit

app.listen app.get('port'), ->
  console.log "Express server listening on port #{app.get('port')}"

#!/usr/bin/env coffee

express        = require 'express'
path           = require 'path'
favicon        = require 'serve-favicon'
logger         = require 'morgan'
methodOverride = require 'method-override'
session        = require 'express-session'
bodyParser     = require 'body-parser'
multer         = require 'multer'
errorHandler   = require 'errorhandler'

TemperatureController = require './controllers/temperature-controller'
temperatureController = new TemperatureController()

app = express()

app.set 'port', process.env?.WEATHER_SERVICE_PORT || process.env?.PORT || 3000
app.set 'views', path.join(__dirname, 'views')
app.set 'view engine', 'jade'
# app.use favicon(__dirname + '/public/favicon.ico'
app.use logger('dev')
app.use methodOverride()
app.use session(resave: true, saveUninitialized: true, secret: 'uwotm8')
app.use bodyParser.json()
app.use bodyParser.urlencoded(extended: true)
app.use multer()
app.use express.static(path.join(__dirname, 'public'))

app.use errorHandler()

app.get '/temperature/c', temperatureController.celsius
app.get '/temperature/celsius', temperatureController.celsius

app.get '/temperature/f', temperatureController.fahrenheit
app.get '/temperature/fahrenheit', temperatureController.fahrenheit

app.listen app.get('port'), ->
  console.log "Express server listening on port #{app.get('port')}"

Allow user to specify port

Allow user to specify port


###* @jsx React.DOM ###

React = require('react/addons')
ParserMixin = require './mixins/Parser'
DisableOnSubmitMixin = require('../controls/mixins/DisableOnSubmit')

ButtonToolbar = require('react-bootstrap/ButtonToolbar')
Button = require('react-bootstrap/Button')

Form = React.createClass(
  mixins: [
    ParserMixin,
    DisableOnSubmitMixin
  ]

  propTypes:
    formDef        : React.PropTypes.object
    formData       : React.PropTypes.object
    title          : React.PropTypes.string # Title can also be set in formDef
    onEnter        : React.PropTypes.func # Callback when hitting Enter on an input
    buttons        : React.PropTypes.object

  classes: ->
    React.addons.classSet
      "form-horizontal": true
      "component-submitting" : @props.submitting
      "component-not-submitting" : !@props.submitting

  buttons: ->
    _.map @props.buttons, (props, title) =>
      props.key = title
      props.disabled = @disabled()
      React.createFactory(Button) props, title

  render: ->
    `(
      <form role="form" className={this.classes()} onChange={this.onChange}>
        {this.constructFormFromDef(this.props.formDef)}
        <ButtonToolbar>{this.buttons()}</ButtonToolbar>
      </form>
    )`
)

module.exports = Form

###* @jsx React.DOM ###

React = require('react/addons')
ParserMixin = require './mixins/Parser'
DisableOnSubmitMixin = require('../controls/mixins/DisableOnSubmit')

{ButtonToolbar, Button} = require('react-bootstrap')

Form = React.createClass(
  mixins: [
    ParserMixin,
    DisableOnSubmitMixin
  ]

  propTypes:
    formDef        : React.PropTypes.object
    formData       : React.PropTypes.object
    title          : React.PropTypes.string # Title can also be set in formDef
    onEnter        : React.PropTypes.func # Callback when hitting Enter on an input
    buttons        : React.PropTypes.object

  classes: ->
    React.addons.classSet
      "form-horizontal": true
      "component-submitting" : @props.submitting
      "component-not-submitting" : !@props.submitting

  buttons: ->
    _.map @props.buttons, (props, title) =>
      props.key = title
      props.disabled = @disabled()
      React.createFactory(Button) props, title

  render: ->
    `(
      <form role="form" className={this.classes()} onChange={this.onChange}>
        {this.constructFormFromDef(this.props.formDef)}
        <ButtonToolbar>{this.buttons()}</ButtonToolbar>
      </form>
    )`
)

module.exports = Form

Use destructuring assignment as ReactBoostrap doesn't expose modules directly anymore


class Lanes.Access.Extension extends Lanes.Extensions.Base

    identifier: "lanes-access"

    setBootstrapData: (data)->
        Lanes.current_user = new Lanes.Models.User

        Lanes.Models.Roles.all = new Lanes.Models.Roles(
            _.map( data.roles, (role)->
                { id: role.toLowerCase(), name: role }
            )
        )
        if data.user
            Lanes.current_user.set(data.user)
        if data.access
            Lanes.current_user.access_data = data.access

    onAvailable: (view)->
        this.login_dialog = new Lanes.Access.LoginDialog({ parent: view })

        # screen=Lanes.Models.Screens.all.get("user-management")
        # screen.display(view.viewport)

class Lanes.Access.Extension extends Lanes.Extensions.Base

    identifier: "lanes-access"

    setBootstrapData: (data)->
        Lanes.current_user = new Lanes.Models.User

        Lanes.Models.Roles.all = new Lanes.Models.Roles(
            _.map( data.roles, (role)->
                { id: role.toLowerCase(), name: role }
            )
        )
        if data.user
            Lanes.current_user.set(data.user)
        if data.access
            Lanes.current_user.access_data = data.access

    onAvailable: (view)->
        Lanes.Access.createLoginDialog(view)

Move loginDialog into own creation function


counterpart = require 'counterpart'
React = require 'react'
TitleMixin = require '../lib/title-mixin'
apiClient = require '../api/client'
OwnedCardList = require '../components/owned-card-list'

counterpart.registerTranslations 'en',
  projectsPage:
    title: 'All Projects'
    countMessage: 'Showing %(pageStart)s-%(pageEnd)s of %(count)s found'
    button: 'Get Started'
    notFoundMessage: 'Sorry, no projects found'

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

  mixins: [TitleMixin]

  title: 'Projects'

  listProjects: ->
    query = {include: 'avatar'}

    if !apiClient.params.admin
      query.homepage = true

    apiClient.type('projects').get Object.assign {}, query, @props.location.query

  imagePromise: (project) ->
    Promise.resolve project.avatar_src or '/assets/simple-avatar.jpg'

  cardLink: (project) ->
    link = if !!project.redirect
      project.redirect
    else
      [owner, name] = project.slug.split('/')
      "/projects/#{owner}/#{name}"

    return link

  render: ->
    <OwnedCardList
      {...@props}
      translationObjectName="projectsPage"
      listPromise={@listProjects()}
      linkTo="projects"
      cardLink={@cardLink}
      heroClass="projects-hero"
      imagePromise={@imagePromise}
      skipOwner={true} />

counterpart = require 'counterpart'
React = require 'react'
TitleMixin = require '../lib/title-mixin'
apiClient = require '../api/client'
OwnedCardList = require '../components/owned-card-list'

counterpart.registerTranslations 'en',
  projectsPage:
    title: 'All Projects'
    countMessage: 'Showing %(pageStart)s-%(pageEnd)s of %(count)s found'
    button: 'Get Started'
    notFoundMessage: 'Sorry, no projects found'

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

  mixins: [TitleMixin]

  title: 'Projects'

  listProjects: ->
    query = {include: 'avatar'}

    if !apiClient.params.admin
      query.simple = true

    apiClient.type('projects').get Object.assign {}, query, @props.location.query

  imagePromise: (project) ->
    Promise.resolve project.avatar_src or '/assets/simple-avatar.jpg'

  cardLink: (project) ->
    link = if !!project.redirect
      project.redirect
    else
      [owner, name] = project.slug.split('/')
      "/projects/#{owner}/#{name}"

    return link

  render: ->
    <OwnedCardList
      {...@props}
      translationObjectName="projectsPage"
      listPromise={@listProjects()}
      linkTo="projects"
      cardLink={@cardLink}
      heroClass="projects-hero"
      imagePromise={@imagePromise}
      skipOwner={true} />

Rename the homepage param to simple for clarity


{_, $, View} = require 'atom'

module.exports =
class WrapGuideView extends View
  @activate: ->
    atom.workspaceView.eachEditorView (editorView) ->
      if editorView.attached and editorView.getPane()
        editorView.underlayer.append(new WrapGuideView(editorView))

  @content: ->
    @div class: 'wrap-guide'

  initialize: (@editorView) ->
    @observeConfig 'editor.fontSize', => @updateGuide()
    @subscribe @editorView, 'editor:path-changed', => @updateGuide()
    @subscribe @editorView, 'editor:min-width-changed', => @updateGuide()
    @subscribe $(window), 'resize', => @updateGuide()

  getDefaultColumn: ->
    atom.config.getPositiveInt('editor.preferredLineLength', 80)

  getGuideColumn: (path) ->
    customColumns = atom.config.get('wrap-guide.columns')
    return @getDefaultColumn() unless _.isArray(customColumns)
    for customColumn in customColumns when _.isObject(customColumn)
      {pattern, column} = customColumn
      return parseInt(column) if pattern and new RegExp(pattern).test(path)
    @getDefaultColumn()

  updateGuide: ->
    column = @getGuideColumn(@editorView.getPath())
    if column > 0
      columnWidth = @editorView.charWidth * column
      if columnWidth < @editorView.layerMinWidth or columnWidth < @editorView.width()
        @css('left', columnWidth).show()
      else
        @hide()
    else
      @hide()

{_, $, View} = require 'atom'

module.exports =
class WrapGuideView extends View
  @activate: ->
    atom.workspaceView.eachEditorView (editorView) ->
      if editorView.attached and editorView.getPane()
        editorView.underlayer.append(new WrapGuideView(editorView))

  @content: ->
    @div class: 'wrap-guide'

  initialize: (@editorView) ->
    @observeConfig 'editor.fontSize', => @updateGuide()
    @subscribe @editorView, 'editor:path-changed', => @updateGuide()
    @subscribe @editorView, 'editor:min-width-changed', => @updateGuide()
    @subscribe $(window), 'resize', => @updateGuide()

  getDefaultColumn: ->
    atom.config.getPositiveInt('editor.preferredLineLength', 80)

  getGuideColumn: (path) ->
    customColumns = atom.config.get('wrap-guide.columns')
    return @getDefaultColumn() unless _.isArray(customColumns)
    for customColumn in customColumns when _.isObject(customColumn)
      {pattern, column} = customColumn
      return parseInt(column) if pattern and new RegExp(pattern).test(path)
    @getDefaultColumn()

  updateGuide: ->
    column = @getGuideColumn(@editorView.getEditor().getPath())
    if column > 0
      columnWidth = @editorView.charWidth * column
      if columnWidth < @editorView.layerMinWidth or columnWidth < @editorView.width()
        @css('left', columnWidth).show()
      else
        @hide()
    else
      @hide()

Call getPath on Editor instead of EditorView


{View} = require 'space-pen'
$ = require 'jquery'
_ = require 'underscore'

module.exports =
class WrapGuideView extends View
  @activate: ->
    rootView.eachEditor (editor) =>
      @appendToEditorPane(rootView, editor) if editor.attached

  @appendToEditorPane: (rootView, editor) ->
    if underlayer = editor.find('.underlayer')
      underlayer.append(new WrapGuideView(editor))

  @content: ->
    @div class: 'wrap-guide'

  defaultColumn: 80

  initialize: (@editor) =>
    @observeConfig 'editor.fontSize', => @updateGuide()
    @subscribe @editor, 'editor:path-changed', => @updateGuide()
    @subscribe @editor, 'editor:min-width-changed', => @updateGuide()
    @subscribe $(window), 'resize', => @updateGuide()

  getGuideColumn: (path) ->
    customColumns = config.get('wrapGuide.columns')
    return @defaultColumn unless _.isArray(customColumns)
    for customColumn in customColumns
      continue unless _.isObject(customColumn)
      pattern = customColumn['pattern']
      continue unless pattern
      return parseInt(customColumn['column']) if new RegExp(pattern).test(path)
    @defaultColumn

  updateGuide: ->
    column = @getGuideColumn(@editor.getPath(), @defaultColumn)
    if column > 0
      columnWidth = @editor.charWidth * column
      if columnWidth < @editor.layerMinWidth or columnWidth < @editor.width()
        @css('left', columnWidth).show()
      else
        @hide()
    else
      @hide()

{View} = require 'space-pen'
$ = require 'jquery'
_ = require 'underscore'

module.exports =
class WrapGuideView extends View
  @activate: ->
    rootView.eachEditor (editor) =>
      editor.underlayer.append(new WrapGuideView(editor)) if editor.attached

  @content: ->
    @div class: 'wrap-guide'

  defaultColumn: 80

  initialize: (@editor) =>
    @observeConfig 'editor.fontSize', => @updateGuide()
    @subscribe @editor, 'editor:path-changed', => @updateGuide()
    @subscribe @editor, 'editor:min-width-changed', => @updateGuide()
    @subscribe $(window), 'resize', => @updateGuide()

  getGuideColumn: (path) ->
    customColumns = config.get('wrapGuide.columns')
    return @defaultColumn unless _.isArray(customColumns)
    for customColumn in customColumns
      continue unless _.isObject(customColumn)
      pattern = customColumn['pattern']
      continue unless pattern
      return parseInt(customColumn['column']) if new RegExp(pattern).test(path)
    @defaultColumn

  updateGuide: ->
    column = @getGuideColumn(@editor.getPath(), @defaultColumn)
    if column > 0
      columnWidth = @editor.charWidth * column
      if columnWidth < @editor.layerMinWidth or columnWidth < @editor.width()
        @css('left', columnWidth).show()
      else
        @hide()
    else
      @hide()

Use underlayer property on editor

Use underlayer property on editor

Removes unneeded call to find


module.exports = (grunt) ->

  require('time-grunt')(grunt)

  require('load-grunt-config')(grunt)

  grunt.registerTask 'default', ['clean', 'amd_tamer', 'uglify']

  grunt.registerTask 'test', ['jshint']

  grunt.registerTask 'doc', ['groc']

  grunt.registerTask 'pages', ['metalsmith', 'doc']

module.exports = (grunt) ->

  require('time-grunt')(grunt)

  require('load-grunt-config')(grunt)

  grunt.registerTask 'default', ['clean', 'amd_tamer', 'uglify']

  grunt.registerTask 'test', ['jshint']

  grunt.registerTask 'doc', ['groc']

  grunt.registerTask 'pages', ['metalsmith', 'doc', 'bowercopy']

Add bowercopy task for pages

Add bowercopy task for pages


dispatcher = require './dispatcher'
invariant = require './invariant'
Action = require './action'

_id = 0

class ActionInstance

	constructor: (@type, @payload) ->
		@actionID = _id++
		Object.freeze @

	valueOf: () ->
		@payload

	getActionID: () ->
		@actionID

module.exports = class ActionCreator

	###
	# Method for dispatching an action through the dispatcher
	#
	# @param {Action} action The action to dispatch
	# @param {mixed} payload Payload for the action
	###
	dispatch: (action, payload) ->
		actionInstance = @createActionInstance action, payload
		dispatcher.dispatch actionInstance
		return actionInstance

	createActionInstance: (action, payload) ->
		invariant action instanceof Action and action?.type?,
			"The action you dispatched does not seem to be an instance of capacitor.Action"
		new ActionInstance action.type, payload

dispatcher = require './dispatcher'
invariant = require './invariant'
Action = require './action'

_actionID = 0
_requestID = 0

class ActionInstance

	constructor: (@type, @payload) ->
		@actionID = _actionID++
		Object.freeze @

	valueOf: () ->
		@payload

	getActionID: () ->
		@actionID

module.exports = class ActionCreator

	###
	# Dispatches an action through the dispatcher
	#
	# @param {Action} action The action to dispatch
	# @param {mixed} payload Payload for the action
	###
	dispatch: (action, payload) ->
		actionInstance = @createActionInstance action, payload
		dispatcher.dispatch actionInstance
		return actionInstance

	###
	# Creates an action instance for dispatching
	#
	# @param {Action} action The action to dispatch
	# @param {mixed} payload Payload for the action
	###
	createActionInstance: (action, payload) ->
		invariant action instanceof Action and action?.type?,
			"The action you dispatched does not seem to be an instance of capacitor.Action"
		new ActionInstance action.type, payload

	###
	# Generates a request id. Useful for tracking specific requests in components.
	###
	generateRequestID: () ->
		return _requestID++

Add method on ActionCreator to generate a request id


{$} = require 'atom'
UIWatcher = require './ui-watcher'

module.exports =
  activate: (state) ->
    uiWatcher = null

    # HACK: I need an actvation event when the ui or packages are all loaded.
    # It cant watch all the packages until they are all loaded.
    createUIWatcher = ->
      uiWatcher = new UIWatcher
        themeManager: atom.themes
      $(window).off 'focus', createUIWatcher
    $(window).on 'focus', createUIWatcher

    rootView.command 'dev-live-reload:reload-all', ->
      console.log 'Reloading all styles!'
      uiWatcher.reloadAll()

{$} = require 'atom'
UIWatcher = require './ui-watcher'

module.exports =
  activate: (state) ->
    return unless atom.getLoadSettings().devMode

    console.log 'Enabling live reloader...'

    uiWatcher = null

    # HACK: I need an actvation event when the ui or packages are all loaded.
    # It cant watch all the packages until they are all loaded.
    createUIWatcher = ->
      uiWatcher = new UIWatcher
        themeManager: atom.themes
      $(window).off 'focus', createUIWatcher
    $(window).on 'focus', createUIWatcher

    rootView.command 'dev-live-reload:reload-all', ->
      console.log 'Reloading all styles!'
      uiWatcher.reloadAll()

Enable only in dev mode

Enable only in dev mode


# Your snippets
#
# Atom snippets allow you to enter a simple prefix in the editor and hit tab to
# expand the prefix into a larger code block with templated values.
#
# You can create a new snippet in this file by typing "snip" and then hitting
# tab.
#
# An example CoffeeScript snippet to expand log to console.log:
#
# '.source.coffee':
#   'Console log':
#     'prefix': 'log'
#     'body': 'console.log $1'
#
# This file uses CoffeeScript Object Notation (CSON).
# If you are unfamiliar with CSON, you can read more about it here:
# https://github.com/bevry/cson#what-is-cson

'.text.html':
  'Translation Method':
    'prefix': 'trans'
    'body': '<?php echo $this->__(\'\') ?>'
  'Get URL':
    'prefix': 'geturl'
    'body': '<?php echo $this->getUrl(\'\') ?>'
  'Get Child HTML':
    'prefix': 'chtml'
    'body': '<?php echo $this->getChildHtml(\'\') ?>'

Add snippet for Magento getChildHtml method


idCounter = 0

module.exports =
class SpellCheckTask
  @handler: null
  @callbacksById: {}

  constructor: (@task) ->
    @id = idCounter++

  terminate: ->
    delete @constructor.callbacksById[@id]

  start: (buffer) ->
    # Figure out the paths since we need that for checkers that are project-specific.
    projectPath = null
    relativePath = null
    if buffer?.file?.path
      [projectPath, relativePath] = atom.project.relativizePath(buffer.file.path)

    # Submit the spell check request to the background task.
    args = {
      id: @id,
      projectPath,
      relativePath,
      text: buffer.getText()
    }
    @task?.start args, @constructor.dispatchMisspellings

  onDidSpellCheck: (callback) ->
    @constructor.callbacksById[@id] = callback

  @dispatchMisspellings: (data) =>
    @callbacksById[data.id]?(data.misspellings)

idCounter = 0
log = console.log

module.exports =
class SpellCheckTask
  @handler: null
  @callbacksById: {}
  @isBusy: false
  @queue: []

  constructor: (@task) ->
    @id = idCounter++

  terminate: ->
    delete @constructor.callbacksById[@id]

  start: (buffer) ->
    # Figure out the paths since we need that for checkers that are project-specific.
    projectPath = null
    relativePath = null
    if buffer?.file?.path
      [projectPath, relativePath] = atom.project.relativizePath(buffer.file.path)

    # Submit the spell check request to the background task.
    entry = {
      task: @task,
      args: {
        id: @id,
        projectPath,
        relativePath,
        text: buffer.getText()
      }
    }

    log('Pushing ' + @id, entry)
    queue = @constructor.queue

    if (queue.length > 0)
      for i in [0..queue.length-1]
        if (queue[i].id is @id)
          log('Ejecting previous entry at ' + i)
          queue.splice(i, 1)
          break

    queue.push(entry)
    @constructor.sendNextMaybe()

  @sendNextMaybe: ->
    if not @isBusy and @queue.length > 0
      @isBusy = true
      entry = @queue.shift()
      log('Dispatching ' + entry.args.id + ' from queue of ' + (@queue.length + 1))
      entry.task?.start entry.args, @dispatchMisspellings

  onDidSpellCheck: (callback) ->
    @constructor.callbacksById[@id] = callback

  @dispatchMisspellings: (data) =>
    log('completed ' + data.id, data)
    @callbacksById[data.id]?(data.misspellings)
    @isBusy = false
    @sendNextMaybe()

Implement a queuing system to avoid quadratic pipe reading time in node IPC.


passport = require 'passport'

exports.logInFirst = (req, res, next) ->
  if req.isAuthenticated()
    next()
  else
    res.redirect "/auth/login?next=#{req.path}"

exports.storeNext = (req, res, next) ->
  if req.query.next
    req.session.next = req.query.next

  next()

exports.login =
  passport.authenticate 'google',
    scope: ['profile', 'email']
    hd: 'andrew.cmu.edu'

exports.callback = passport.authenticate 'google'

exports.after = (req, res) ->
  if req.session.next
    next = req.session.next
    delete req.session
    res.redirect next
  else
    res.redirect '/'

exports.ensureAuthenticated = (req, res, next) ->
  if req.isAuthenticated()
    next()
  else
    res.status(401).send null

exports.ensureAdmin = (req, res, next) ->
  if req.user.isAdmin
    next()
  else
    err = new Error 'Forbidden: Admin'
    err.status 403
    err.type 'forbidden'
    next err

exports.logout = (req, res) ->
  req.logout()
  res.redirect '/'

passport = require 'passport'

exports.logInFirst = (req, res, next) ->
  if req.isAuthenticated()
    next()
  else
    res.redirect "/auth/login?next=#{req.path}"

exports.storeNext = (req, res, next) ->
  if req.query.next
    req.session.next = req.query.next

  next()

exports.login =
  passport.authenticate 'google',
    scope: ['profile', 'email']
    hd: 'andrew.cmu.edu'

exports.callback = passport.authenticate 'google'

exports.after = (req, res) ->
  if req.session.next
    next = req.session.next
    delete req.session
    res.redirect next
  else
    res.redirect '/'

exports.ensureAuthenticated = (req, res, next) ->
  if req.isAuthenticated()
    next()
  else
    res.status(401).send null

exports.ensureAdmin = (req, res, next) ->
  if req.user.isAdmin
    next()
  else
    err = new Error 'Forbidden: Admin'
    err.status = 403
    err.type = 'forbidden'
    next err

exports.logout = (req, res) ->
  req.logout()
  res.redirect '/'

Fix 'undefined is not a function' error

Fix 'undefined is not a function' error


is_page_visible = true
num_unseen_posts = 0

$(document).on 'page:change', ->
  if location.pathname == '/posts'
    App.posts = App.cable.subscriptions.create "PostsChannel",
      received: (data) ->
        $('table tbody').prepend(data['row'])

        unless is_page_visible
          num_unseen_posts++
          document.title = "(#{num_unseen_posts}*) Recent posts - metasmoke"
  else if /^\/post\/(\d*)(\/)?$/.test(location.pathname)
    App.posts = App.cable.subscriptions.create { channel: "PostsChannel", post_id: location.pathname.match(/^\/post\/(\d*)(\/)?$/)[1] },
      received: (data) ->
        $('strong.post-feedbacks').prepend(data['feedback'])
  else if App.posts
    App.posts.unsubscribe()
    App.posts = null

$(window).on 'blur', ->
  is_page_visible = false

$(window).on 'focus', ->
  if location.pathname == '/posts'
    is_page_visible = true
    num_unseen_posts = 0
    document.title = "Recent posts - metasmoke"

is_page_visible = true
num_unseen_posts = 0

$(document).on 'turbolinks:load', ->
  console.log "hi"
  if location.pathname == '/posts'
    App.posts = App.cable.subscriptions.create "PostsChannel",
      received: (data) ->
        $('table tbody').prepend(data['row'])

        unless is_page_visible
          num_unseen_posts++
          document.title = "(#{num_unseen_posts}*) Recent posts - metasmoke"
  else if /^\/post\/(\d*)(\/)?$/.test(location.pathname)
    console.log "oy"
    App.posts = App.cable.subscriptions.create { channel: "PostsChannel", post_id: location.pathname.match(/^\/post\/(\d*)(\/)?$/)[1] },
      received: (data) ->
        $('strong.post-feedbacks').prepend(data['feedback'])
  else if App.posts
    App.posts.unsubscribe()
    App.posts = null

$(window).on 'blur', ->
  is_page_visible = false

$(window).on 'focus', ->
  if location.pathname == '/posts'
    is_page_visible = true
    num_unseen_posts = 0
    document.title = "Recent posts - metasmoke"

Change websocket trigger to turbolinks:load; debugging


React = require 'react'
Router = require 'react-router'

ArticleList = require './views/article-list.cjsx'

DefaultRoute = Router.DefaultRoute
Route = Router.Route
RouteHandler = Router.RouteHandler
Link = Router.Link

App = React.createClass
	render: ->

<Link to="blog">Blog</Link>

			<RouteHandler/>

routes = <Route name="app" path="/" handler={ App }>
	<Route name="blog" path="/blog" handler={ ArticleList }/>
</Route>

Router.run routes, Router.HistoryLocation, (Handler) ->
	React.render(<Handler/>, document.body)

React = require 'react'
Router = require 'react-router'

ArticleList = require './views/article-list.cjsx'

DefaultRoute = Router.DefaultRoute
Route = Router.Route
RouteHandler = Router.RouteHandler
Link = Router.Link

App = React.createClass
	render: ->

<Link to="app">Home</Link>

			<RouteHandler/>

routes = <Route name="app" path="/" handler={ App }>
	<DefaultRoute handler={ ArticleList }/>
</Route>

Router.run routes, Router.HistoryLocation, (Handler) ->
	React.render(<Handler/>, document.body)

Set ArticleList as default route.

Set ArticleList as default route.


# Config and Start SyncedCron
logger = new Logger 'SyncedCron'

SyncedCron.config
	logger: (opts) ->
		logger[opts.level].call(logger, opts.message)
	collectionName: 'rocketchat_cron_history'

generateStatistics = ->
	statistics = RocketChat.statistics.save()
	statistics.host = Meteor.absoluteUrl()
	if RocketChat.settings.get 'Statistics_reporting'
		HTTP.post 'https://rocket.chat/stats',
			data: statistics
	return

Meteor.startup ->
	Meteor.defer ->
		generateStatistics()

		# Generate and save statistics every hour
		SyncedCron.add
			name: 'Generate and save statistics',
			schedule: (parser) -># parser is a later.parse object
				return parser.text 'every 1 hour'
			job: generateStatistics

		SyncedCron.start()

# Config and Start SyncedCron
logger = new Logger 'SyncedCron'

SyncedCron.config
	logger: (opts) ->
		logger[opts.level].call(logger, opts.message)
	collectionName: 'rocketchat_cron_history'

generateStatistics = ->
	statistics = RocketChat.statistics.save()
	statistics.host = Meteor.absoluteUrl()
	if RocketChat.settings.get 'Statistics_reporting'
		try
			HTTP.post 'https://rocket.chat/stats',
				data: statistics
		catch e
			logger.warn('Failed to send usage report')
	return

Meteor.startup ->
	Meteor.defer ->
		generateStatistics()

		# Generate and save statistics every hour
		SyncedCron.add
			name: 'Generate and save statistics',
			schedule: (parser) -># parser is a later.parse object
				return parser.text 'every 1 hour'
			job: generateStatistics

		SyncedCron.start()

Add error handling for stat server request


url = require 'url'
{$$} = require 'space-pen'
ScrollView = require 'scroll-view'
BuddyView = require './buddy-view'

module.exports =
class BuddyList extends ScrollView
  @content: ->
    @div class: 'buddy-list', tabindex: -1

  initialize: (@presence) ->
    super

    @presence.on 'person-added', -> @updateBuddies()
    @presence.on 'person-removed', -> @updateBuddies()
    @presence.on 'person-status-changed', -> @updateBuddies()

  toggle: ->
    if @hasParent()
      @detach()
    else
      @attach()

  attach: ->
    rootView.horizontal.append(this)
    @focus()
    @updateBuddies()

  updateBuddies: ->
    @empty()
    @append(new BuddyView(buddy)) for buddy in @presence.getPeople()

url = require 'url'
{$$} = require 'space-pen'
ScrollView = require 'scroll-view'
BuddyView = require './buddy-view'

module.exports =
class BuddyList extends ScrollView
  @content: ->
    @div class: 'buddy-list', tabindex: -1

  initialize: (@presence) ->
    super

    @presence.on 'person-added', => @updateBuddies()
    @presence.on 'person-removed', => @updateBuddies()
    @presence.on 'person-status-changed', => @updateBuddies()

  toggle: ->
    if @hasParent()
      @detach()
    else
      @attach()

  attach: ->
    rootView.horizontal.append(this)
    @focus()
    @updateBuddies()

  updateBuddies: ->
    @empty()
    @append(new BuddyView(buddy)) for buddy in @presence.getPeople()

Use fat arrow for callbacks

Use fat arrow for callbacks


window.StrutBuilder ||= {}

# [
#   { #slide 1
#     start: 0,
#     end: 2.5,
#     texts: [
#              {text: 'Hum', position: 1.2},
#              {text: 'bewafaa', position: 1.7}
#            ]
#   },
#   { #slide 2
#     start: 2.5,
#     end: 4.3,
#     texts: [
#              {text: 'Hargiz', position: 3.3},
#              {text: 'na they', position: 3.8}
#            ]
#   }
# ]
StrutBuilder.build = (data) ->
  slides = for slide, i  in data
    StrutBuilder.Slide.build(slide, i)
  {
    slides: slides,
    activeSlide: slides[0],
    fileName: 'presentation-unnamed',
    customStylesheet: '',
    deckVersion: '1.0',
    customBackgrounds: { bgs: [] }
  }

window.StrutBuilder ||= {}

# [
#   { #slide 1
#     start: 0,
#     end: 2.5,
#     texts: [
#              {text: 'Hum', position: 1.2},
#              {text: 'bewafaa', position: 1.7}
#            ]
#   },
#   { #slide 2
#     start: 2.5,
#     end: 4.3,
#     texts: [
#              {text: 'Hargiz', position: 3.3},
#              {text: 'na they', position: 3.8}
#            ]
#   }
# ]
StrutBuilder.build = (data) ->
  slidesdata = (slide for slide in data when slide.data.type == 'segment')
  itemdata = (item for item in data when item.data.type == 'segment_item')
  for slidedata in slidesdata
    items = for item in itemdata when (slidedata.start <= item.start and item.start < slidedata.end)
      text: item.data.note
      position: item.start
    slidedata.texts = items

  slides = for slide, i in slidesdata
    StrutBuilder.Slide.build(slide, i)
  {
    slides: slides,
    activeSlide: slides[0],
    fileName: 'presentation-unnamed',
    customStylesheet: '',
    deckVersion: '1.0',
    customBackgrounds: { bgs: [] }
  }

Convert segments and their text to words in the PPT slides


express = require 'express'
compress = require 'compression'
hbs = require 'hbs'

config = require '../config'
passport = require '../lib/auth'
User = require '../models/user'

module.exports = app = express()

hbs.registerHelper 'json', (context) ->
  new hbs.handlebars.SafeString JSON.stringify(context)

app.set 'views', "#{__dirname}/../views"
app.use compress()
app.use express.static '#{__dirname}/../public/'

# Special case for install
app.post '/login', passport.authenticate 'local',
  failureRedirect: "/#{config.buckets.adminSegment}/login"
  successRedirect: "/#{config.buckets.adminSegment}/"

app.get '/logout', (req, res) ->
  req.logout()
  res.redirect "/#{config.buckets.adminSegment}/"

app.all '*', (req, res) ->
  # This is kinda dumb, but whatever
  User.count({}).exec (err, userCount) ->
    res.send 500 if err

    res.render 'admin',
      user: req.user
      adminSegment: config.buckets.adminSegment
      needsInstall: userCount is 0

express = require 'express'
compress = require 'compression'
hbs = require 'hbs'

config = require '../config'
passport = require '../lib/auth'
User = require '../models/user'

module.exports = app = express()

hbs.registerHelper 'json', (context) ->
  new hbs.handlebars.SafeString JSON.stringify(context)

app.set 'views', "#{__dirname}/../views"
app.use compress()
app.use express.static '#{__dirname}/../public/', maxAge: 86400000 # One day

# Special case for install
app.post '/login', passport.authenticate 'local',
  failureRedirect: "/#{config.buckets.adminSegment}/login"
  successRedirect: "/#{config.buckets.adminSegment}/"

app.get '/logout', (req, res) ->
  req.logout()
  res.redirect "/#{config.buckets.adminSegment}/"

app.all '*', (req, res) ->
  # This is kinda dumb, but whatever
  User.count({}).exec (err, userCount) ->
    res.send 500 if err

    res.render 'admin',
      user: req.user
      adminSegment: config.buckets.adminSegment
      needsInstall: userCount is 0

Set cache headers for statics

Set cache headers for statics


CocoView = require 'views/kinds/CocoView'
template = require 'templates/play/level/tome/problem_alert'
{me} = require 'lib/auth'

module.exports = class ProblemAlertView extends CocoView
  className: 'problem-alert'
  template: template

  subscriptions: {}

  events:
    'click .close': 'onRemoveClicked'

  constructor: (options) ->
    super options
    @problem = options.problem

  getRenderData: (context={}) ->
    context = super context
    format = (s) -> s?.replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br>')
    message = @problem.aetherProblem.message
    age = @problem.aetherProblem.userInfo?.age
    if age?
      if /^Line \d+:/.test message
        message = message.replace /^(Line \d+)/, "$1, time #{age.toFixed(1)}"
      else
        message = "Time #{age.toFixed(1)}: #{message}"
    context.message = format message
    context.hint = format @problem.aetherProblem.hint
    context

  afterRender: ->
    super()
    @$el.addClass('alert').addClass("alert-#{@problem.aetherProblem.level}").hide().fadeIn('slow')
    @$el.addClass('no-hint') unless @problem.aetherProblem.hint
    Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'error_appear', volume: 1.0

  onRemoveClicked: ->
    @$el.remove()
    @destroy()
    #@problem.destroy()  # let's try leaving the annotations / marker ranges alone

CocoView = require 'views/kinds/CocoView'
template = require 'templates/play/level/tome/problem_alert'
{me} = require 'lib/auth'

module.exports = class ProblemAlertView extends CocoView
  className: 'problem-alert'
  template: template

  subscriptions: {}

  events:
    'click .close': 'onRemoveClicked'

  constructor: (options) ->
    super options
    @problem = options.problem

  getRenderData: (context={}) ->
    context = super context
    format = (s) -> s?.replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br>')
    context.message = format @problem.aetherProblem.message
    context.hint = format @problem.aetherProblem.hint
    context

  afterRender: ->
    super()
    @$el.addClass('alert').addClass("alert-#{@problem.aetherProblem.level}").hide().fadeIn('slow')
    @$el.addClass('no-hint') unless @problem.aetherProblem.hint
    Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'error_appear', volume: 1.0

  onRemoveClicked: ->
    @$el.remove()
    @destroy()
    #@problem.destroy()  # let's try leaving the annotations / marker ranges alone

Remove time from problem alert message

Remove time from problem alert message


@RailsAdmin ||= {}
@RailsAdmin.I18n = class Locale
  @init: (@locale, @translations)->
    moment.locale(@locale)

  @t:(key) ->
    humanize = key.charAt(0).toUpperCase() + key.replace(/_/g, " ").slice(1)
    @translations[key] || humanize

@RailsAdmin ||= {}
@RailsAdmin.I18n = class Locale
  @init: (@locale, @translations)->
    moment.locale(@locale)
    if typeof(@translations) == "string"
      @translations = JSON.parse(@translations)

  @t:(key) ->
    humanize = key.charAt(0).toUpperCase() + key.replace(/_/g, " ").slice(1)
    @translations[key] || humanize

Fix JS translations not be object but string


ETahi.MessageOverlayController = ETahi.TaskController.extend ETahi.ControllerParticipants,
  newCommentBody: ""

  message: true

  _clearNewMessage: ->
    @set('newCommentBody', "")

  commentSort: ['createdAt:asc']
  sortedComments: Ember.computed.sort('comments', 'commentSort')

  actions:
    clearMessageContent: ->
      @_clearNewMessage()

    postComment: ->
      commenter = @get('currentUser')
      commentFields =
        commenter: commenter
        messageTask: @get('model')
        body: @get('newCommentBody')
      newComment = @store.createRecord('comment', commentFields)
      newComment.save()
        .then(@_clearNewMessage.bind(@), newComment.deleteRecord)
        .then(@send('saveNewParticipant', commenter))

ETahi.MessageOverlayController = ETahi.TaskController.extend ETahi.ControllerParticipants,
  newCommentBody: ""

  message: true

  _clearNewMessage: ->
    @set('newCommentBody', "")

  commentSort: ['createdAt:asc']
  sortedComments: Ember.computed.sort('comments', 'commentSort')

  actions:
    clearMessageContent: ->
      @_clearNewMessage()

    postComment: ->
      commenter = @get('currentUser')
      commentFields =
        commenter: commenter
        messageTask: @get('model')
        body: @get('newCommentBody')
        createdAt: new Date()
      newComment = @store.createRecord('comment', commentFields)
      newComment.save()
        .then(@_clearNewMessage.bind(@), newComment.deleteRecord)
        .then(@send('saveNewParticipant', commenter))

Add initial createdAt date when saving comment.

Null dates sort differently than real ones.


describe 'HashParams', ->
    describe 'given foreground and background', ->
        params = null
        beforeEach ->
            params = new HashParams(
                new HashParams.scalar('foreground'),
                new HashParams.scalar('background'))
        it 'initializes values.foreground and .background', ->
            expect(params.values).toEqual {foreground: '', background: ''}
        describe 'setHash', ->
            it 'can set values.foreground', ->
                params.setHash '#foreground=blue'
                expect(params.values).toEqual {foreground: 'blue', background: ''}
            it 'can set values.background', ->
                params.setHash '#background=green'
                expect(params.values).toEqual {foreground: '', background: 'green'}
            it 'clears other values', ->
                params.values.foreground = 'magenta'
                params.setHash '#background=green'
                expect(params.values).toEqual {foreground: '', background: 'green'}
            it 'will not set values.foo', ->
                params.setHash '#foo=bar'
                expect(params.values).toEqual {foreground: '', background: ''}

describe 'HashParams', ->
    describe 'initial state', ->
        describe 'given foreground and background', ->
            params = null
            beforeEach ->
                params = new HashParams(
                    new HashParams.scalar('foreground'),
                    new HashParams.scalar('background'))
            it 'has empty strings for values.foreground and values.background', ->
                expect(params.values).toEqual {foreground: '', background: ''}
    describe '.setHash()', ->
        describe 'given foreground and background', ->
            params = null
            beforeEach ->
                params = new HashParams(
                    new HashParams.scalar('foreground'),
                    new HashParams.scalar('background'))
            it 'can set values.foreground', ->
                params.setHash '#foreground=blue'
                expect(params.values).toEqual {foreground: 'blue', background: ''}
            it 'can set values.background', ->
                params.setHash '#background=green'
                expect(params.values).toEqual {foreground: '', background: 'green'}
            it 'clears other values', ->
                params.values.foreground = 'magenta'
                params.setHash '#background=green'
                expect(params.values).toEqual {foreground: '', background: 'green'}
            it 'will not set values.foo', ->
                params.setHash '#foo=bar'
                expect(params.values).toEqual {foreground: '', background: ''}

Reorganize specs by public method.

Reorganize specs by public method.


Binary = require('./example');

describe 'binary', ->

  it '1 is decimal 1', ->
    expect(1).toEqual new Binary('1').toDecimal()

  xit '10 is decimal 2', ->
    expect(2).toEqual new Binary('10').toDecimal()

  xit '11 is decimal 3', ->
    expect(3).toEqual new Binary('11').toDecimal()

  xit '100 is decimal 4', ->
    expect(4).toEqual new Binary('100').toDecimal()

  xit '1001 is decimal 9', ->
    expect(9).toEqual new Binary('1001').toDecimal()

  xit '11010 is decimal 26', ->
    expect(26).toEqual new Binary('11010').toDecimal()

  xit '10001101000 is decimal 1128', ->
    expect(1128).toEqual new Binary('10001101000').toDecimal()

  xit 'carrot is decimal 0', ->
    expect(0).toEqual new Binary('carrot').toDecimal()

Binary = require('./binary');

describe 'binary', ->

  it '1 is decimal 1', ->
    expect(1).toEqual new Binary('1').toDecimal()

  xit '10 is decimal 2', ->
    expect(2).toEqual new Binary('10').toDecimal()

  xit '11 is decimal 3', ->
    expect(3).toEqual new Binary('11').toDecimal()

  xit '100 is decimal 4', ->
    expect(4).toEqual new Binary('100').toDecimal()

  xit '1001 is decimal 9', ->
    expect(9).toEqual new Binary('1001').toDecimal()

  xit '11010 is decimal 26', ->
    expect(26).toEqual new Binary('11010').toDecimal()

  xit '10001101000 is decimal 1128', ->
    expect(1128).toEqual new Binary('10001101000').toDecimal()

  xit 'carrot is decimal 0', ->
    expect(0).toEqual new Binary('carrot').toDecimal()

Fix require in binary test for coffeescript


Template.incidentTable.events
  'mouseover .incident-table tbody tr': (event, instance) ->
    if not instance.data.scrollToAnnotations
      return
    $annotation = $("span[data-incident-id=#{@_id}]")
    $("span[data-incident-id]").removeClass('viewing')
    appHeaderHeight = $('header nav.navbar').outerHeight()
    detailsHeaderHeight = $('.curator-source-details-header').outerHeight()
    headerOffset = appHeaderHeight + detailsHeaderHeight
    containerScrollTop = $('.curator-source-details-copy').scrollTop()
    annotationTopOffset = $annotation.offset().top
    countainerVerticalMidpoint = $('.curator-source-details-copy').height() / 2
    totalOffset = annotationTopOffset - headerOffset
    # Distance of scroll based on postition of text container, scroll position
    # within the text container and the container's midpoint (to position the
    # annotation in the center of the container)
    scrollDistance =  totalOffset + containerScrollTop - countainerVerticalMidpoint
    $('.curator-source-details-copy').stop().animate
      scrollTop: scrollDistance
    , 500, -> $annotation.addClass('viewing')

SCROLL_WAIT_TIME = 500

Template.incidentTable.onCreated ->
  @scrollToAnnotation = (id) =>
    intervalTime = 0
    @interval = setInterval =>
      if intervalTime >= SCROLL_WAIT_TIME
        @stopScrollingInterval()
        $annotation = $("span[data-incident-id=#{id}]")
        $("span[data-incident-id]").removeClass('viewing')
        appHeaderHeight = $('header nav.navbar').outerHeight()
        detailsHeaderHeight = $('.curator-source-details--header').outerHeight()
        headerOffset = appHeaderHeight + detailsHeaderHeight
        containerScrollTop = $('.curator-source-details--copy').scrollTop()
        annotationTopOffset = $annotation.offset().top
        countainerVerticalMidpoint = $('.curator-source-details--copy').height() / 2
        totalOffset = annotationTopOffset - headerOffset
        # Distance of scroll based on postition of text container, scroll position
        # within the text container and the container's midpoint (to position the
        # annotation in the center of the container)
        scrollDistance =  totalOffset + containerScrollTop - countainerVerticalMidpoint
        $('.curator-source-details--copy').stop().animate
          scrollTop: scrollDistance
        , 500, -> $annotation.addClass('viewing')
      intervalTime += 100
    , 100

  @stopScrollingInterval = ->
    clearInterval(@interval)

Template.incidentTable.events
  'mouseover .incident-table tbody tr': (event, instance) ->
    if not instance.data.scrollToAnnotations
      return
    instance.scrollToAnnotation(@_id)

  'mouseout .incident-table tbody tr': (event, instance) ->
    if not instance.data.scrollToAnnotations
      return
    instance.stopScrollingInterval()

Add wait time for scrolling to annotations on hover


_ = require 'lodash'
typeOf = require 'type-of'

err = require './errors'
{ availableKeys, availableFormats } = require './api_config'

RealtimeInformation    = require './services/realtime'
LocationLookup         = require './services/location'
TripPlanner            = require './services/trip'
TrafficSituation       = require './services/traffic'
DisturbanceInformation = require './services/disturbance'

checkIfKeyIsSupplied = (keys) ->
  throw new err.NoKeySuppliedError unless keys?

checkIfKeyIsObject = (keys) ->
  type = typeOf keys
  unless type is 'object' then throw new err.InvalidKeyFormatSupplied type

checkKeyNames = (keys) ->
  for key, val of keys
    if key not in availableKeys
      throw new err.InvalidKeyNameSuppliedError key

checkSuppliedFormat = (format) ->
  if format and format.toUpperCase() not in availableFormats
    throw new err.InvalidResponseFormatSuppliedError format

class SL
  constructor: (@keys, @format) ->
    checkIfKeyIsSupplied @keys
    checkIfKeyIsObject @keys
    checkKeyNames @keys
    checkSuppliedFormat @format
    @createServices()

  createServices: ->
    @realtimeInformation = new RealtimeInformation @
    @locationLookup = new LocationLookup @
    @disturbanceInformation = new DisturbanceInformation @
    @trafficSituation = new TrafficSituation @
    @tripPlanner = new TripPlanner @

module.exports = SL

_ = require 'lodash'
typeOf = require 'type-of'

err = require './errors'
{ availableKeys, availableFormats } = require './api_config'

RealtimeInformation    = require './services/realtime'
LocationLookup         = require './services/location'
TripPlanner            = require './services/trip'
TrafficSituation       = require './services/traffic'
DisturbanceInformation = require './services/disturbance'

checkIfKeyIsSupplied = (keys) ->
  throw new err.NoKeySuppliedError unless keys?

checkIfKeyIsObject = (keys) ->
  type = typeOf keys
  unless type is 'object' then throw new err.InvalidKeyFormatSupplied type

checkKeyNames = (keys) ->
  for key, val of keys
    if key not in availableKeys
      throw new err.InvalidKeyNameSuppliedError key

checkSuppliedFormat = (format) ->
  if format
    if not _(format).isString() or format.toUpperCase() not in availableFormats
      throw new err.InvalidResponseFormatSuppliedError format

class SL
  constructor: (@keys, @format) ->
    checkIfKeyIsSupplied @keys
    checkIfKeyIsObject @keys
    checkKeyNames @keys
    checkSuppliedFormat @format
    @createServices()

  createServices: ->
    @realtimeInformation = new RealtimeInformation @
    @locationLookup = new LocationLookup @
    @disturbanceInformation = new DisturbanceInformation @
    @trafficSituation = new TrafficSituation @
    @tripPlanner = new TripPlanner @

module.exports = SL

Throw error if format option is not string

Throws InvalidResponseFormatSuppliedError if the supplied format option
is not a string of either 'json' or 'xml'


# react-controllables
# ===================

invariant = require 'react/lib/invariant'

getControllableValue = (name, state, props) -> props[name] ? state[name]
capFirst = (str) -> "#{ str[...1].toUpperCase() }#{ str[1...] }"
callbackName = (prop) -> "on#{ capFirst prop }Change"

ControllablesMixin =
  getInitialState: ->
    invariant(
      !!@controllables,
      'Components that use ControllablesMixin must define a controllables array'
    )
    state = {}
    for name in @controllables
      defaultValue = @props["default#{ capFirst name }"]
      if defaultValue? then state[name] = defaultValue
    state
  getControllableValue: (name) -> getControllableValue name, @state, @props
  componentDidUpdate: (prevProps, prevState) ->
    for name in @controllables
      invariant(
        not @props[callbackName name]? or not @props[name]?,
        "It doesn't make sense to add change callbacks for controlled properties. Either remove the callback or let it manage its own state. (#{ name })"
      )
      newValue = @state[name]
      oldValue = getControllableValue name, prevState, prevProps
      if newValue isnt oldValue
        @props[callbackName name]? newValue, oldValue
    return

module.exports = ControllablesMixin

# react-controllables
# ===================

invariant = require 'react/lib/invariant'

getControllableValue = (name, state, props) -> props[name] ? state[name]
capFirst = (str) -> "#{ str[...1].toUpperCase() }#{ str[1...] }"
callbackName = (prop) -> "on#{ capFirst prop }Change"

ControllablesMixin =
  getInitialState: ->
    invariant(
      !!@controllables,
      'Components that use ControllablesMixin must define a controllables array'
    )
    state = {}
    for name in @controllables
      defaultValue = @props["default#{ capFirst name }"]
      if defaultValue? then state[name] = defaultValue
    state
  getControllableValue: (name) -> getControllableValue name, @state, @props
  componentDidUpdate: (prevProps, prevState) ->
    for name in @controllables
      newValue = @state[name]
      oldValue = getControllableValue name, prevState, prevProps
      if newValue isnt oldValue
        @props[callbackName name]? newValue, oldValue
    return

module.exports = ControllablesMixin

Remove invariant check for callbacks on controlled components

I don’t remember why I added this. React’s form components don’t do
this /:


class @WalletSendPreparingDialogViewController extends @DialogViewController

  view:
    spinnerContainer: '#spinner_container'

  onAfterRender: ->
    super
    @view.spinner = ledger.spinners.createLargeSpinner(@view.spinnerContainer[0])
    account = Account.find(index: 0).first()

    # fetch amount
    ledger.wallet.transaction.createAndPrepareTransaction @params.amount, 10000, @params.address, account, account, (transaction, error) =>
      return if not @isShown()
      @once 'dismiss', =>
        if error?
          reason = switch error.code
            when ledger.errors.NetworkError then 'network_no_response'
            when ledger.errors.NotEnoughFunds then 'unsufficient_balance'
          dialog = new WalletSendErrorDialogViewController reason: reason
          dialog.show()
        else
          dialog = new WalletSendValidationDialogViewController transaction: transaction
          dialog.show()
      @dismiss()

class @WalletSendPreparingDialogViewController extends @DialogViewController

  view:
    spinnerContainer: '#spinner_container'

  onAfterRender: ->
    super
    @view.spinner = ledger.spinners.createLargeSpinner(@view.spinnerContainer[0])
    account = Account.find(index: 0).first()

    # fetch amount
    ledger.wallet.transaction.createAndPrepareTransaction @params.amount, 10000, @params.address, account, account, (transaction, error) =>
      return if not @isShown()
      if error?
        reason = switch error.code
          when ledger.errors.NetworkError then 'network_no_response'
          when ledger.errors.NotEnoughFunds then 'unsufficient_balance'
        @once 'dismiss', =>
          dialog = new WalletSendErrorDialogViewController reason: reason
          dialog.show()
        @dismiss()
      else
        invertModes = _.invert(ledger.wallet.transaction.Transaction.ValidationModes)
        l "[PreparingTxDialog]", invertModes[transaction.getValidationMode()], transaction
        switch transaction.getValidationMode()
          when ledger.wallet.transaction.Transaction.ValidationModes.KEYCARD
            @once 'dismiss', =>
              dialog = new WalletSendValidationDialogViewController transaction: transaction
              dialog.show()
            @dismiss()
          when ledger.wallet.transaction.Transaction.ValidationModes.SECURE_SCREEN
            l "%c[M2FA] Secure screen dialog in preparation", "#888888"
            @_requestValidation(transaction)

  _requestValidation: (tx) ->
    ledger.m2fa.validateTxOnAll(tx).fail( (error) =>
      # return if not @isShown()
      @once 'dismiss', =>
        dialog = new WalletSendErrorDialogViewController(reason: error)
        dialog.show()
      @dismiss()
    ).then( () =>
      # return if not @isShown()
      @once 'dismiss', =>
        dialog = new WalletSendSuccessDialogViewController()
        dialog.show()
      @dismiss()
    ).done()

Modify dialogs when waiting for secure screen validation


Helpers = require '../lib/helpers'

describe "The Results Validation Helper", ->
  it "should throw an exception when nothing is passed.", ->
    expect( -> Helpers.validateResults()).toThrow()
  it "should throw an exception when a String is passed.", ->
    expect( -> Helpers.validateResults('String')).toThrow()

describe "The Linter Validation Helper", ->
  it "should throw an exception when nothing is passed.", ->
    expect( -> Helpers.validateLinter()).toThrow()
  it "should throw an exception when a String is passed.", ->
    expect( -> Helpers.validateLinter('String')).toThrow()

Add the same test for Linter validation.


Grim = require 'grim'
if Grim.includeDeprecations
  module.exports = require('theorist').Model
  return

PropertyAccessors = require 'property-accessors'

nextInstanceId = 1

module.exports =
class Model
  PropertyAccessors.includeInto(this)

  @resetNextInstanceId: -> nextInstanceId = 1

  constructor: (params) ->
    @assignId(params?.id)

  assignId: (id) ->
    @id ?= id ? nextInstanceId++

  @::advisedAccessor 'id',
    set: (id) -> nextInstanceId = id + 1 if id >= nextInstanceId

  destroy: ->
    return unless @isAlive()
    @alive = false
    @destroyed?()

  isAlive: -> @alive

  isDestroyed: -> not @isAlive()

Grim = require 'grim'
if Grim.includeDeprecations
  module.exports = require('theorist').Model
  return

PropertyAccessors = require 'property-accessors'

nextInstanceId = 1

module.exports =
class Model
  PropertyAccessors.includeInto(this)

  @resetNextInstanceId: -> nextInstanceId = 1

  alive: true

  constructor: (params) ->
    @assignId(params?.id)

  assignId: (id) ->
    @id ?= id ? nextInstanceId++

  @::advisedAccessor 'id',
    set: (id) -> nextInstanceId = id + 1 if id >= nextInstanceId

  destroy: ->
    return unless @isAlive()
    @alive = false
    @destroyed?()

  isAlive: -> @alive

  isDestroyed: -> not @isAlive()

Add alive ivar to light Model

Add alive ivar to light Model


SpellChecker = require 'spellchecker'

wordRegex = /(?:^|[\s\[\]"'])([a-zA-Z]+([a-zA-Z']+[a-zA-Z])?)(?=[\s\.\[\]:,"']|$)/g

module.exports = ({id, text}) ->
  row = 0
  misspellings = []
  for line in text.split('\n')
    while matches = wordRegex.exec(line)
      word = matches[1]
      continue if word in ['GitHub', 'github']
      continue unless SpellChecker.isMisspelled(word)

      startColumn = matches.index + matches[0].length - word.length
      endColumn = startColumn + word.length
      misspellings.push([[row, startColumn], [row, endColumn]])
    row++
  {id, misspellings}

SpellChecker = require 'spellchecker'

module.exports = ({id, text}) ->
  misspelledCharacterRanges = SpellChecker.checkSpelling(text)

  row = 0
  rangeIndex = 0
  characterIndex = 0
  misspellings = []
  while characterIndex < text.length and rangeIndex < misspelledCharacterRanges.length
    lineBreakIndex = text.indexOf('\n', characterIndex)
    if lineBreakIndex is -1
      lineBreakIndex = Infinity

    loop
      range = misspelledCharacterRanges[rangeIndex]
      if range and range.start < lineBreakIndex
        misspellings.push([
          [row, range.start - characterIndex],
          [row, range.end - characterIndex]
        ])
        rangeIndex++
      else
        break

    characterIndex = lineBreakIndex + 1
    row++

  {id, misspellings}

Use new bulk spell-checking method in task handler


# a number of jasmine-jquery helpers / extensions
# it totally used to do this natively, but now doesn't...

jasmine.getFixtures().findContainer = ->
  $(document.body).find("##{@containerId}")

jasmine.getFixtures().getContainer = ->
  cont = @findContainer()
  if cont.length == 0
    @createContainer_("")
    cont = @findContainer()
  cont

window.f = (html="")->
  cont = jasmine.getFixtures().getContainer();
  if html.length > 0
    cont.html(html)
  cont

#beforeEach ->
#  @addMatchers(
#    toInclude: (value)->
#      _(@).find((it)-> it == value)
#  )

# a number of jasmine-jquery helpers / extensions
# it totally used to do this natively, but now doesn't...

jasmine.getFixtures().findContainer = ->
  $(document.body).find("##{@containerId}")

jasmine.getFixtures().getContainer = ->
  cont = @findContainer()
  if cont.length == 0
    @createContainer_("")
    cont = @findContainer()
  cont

window.f = (html="")->
  cont = jasmine.getFixtures().getContainer();
  if html.length > 0
    cont.html(html)
  cont

beforeEach ->
  @addMatchers(
    toBeInstanceOf: (type)->
      @message = ->
        "Expected #{@actual} to be an instance of #{type.name}"
      @actual instanceof type
    toBeEqualTo: (other)->
      @message = ->
        "Expected #{@actual}.isEqual(#{type}) to be true"
      @actual.isEqual(other)
  )

Refactor of most of the math library pulling components out


module.exports =

  config:
    undefinedDisplay:
      type: 'string'
      default: ''

  activate: (state) ->
    atom.workspaceView.command 'table-edit:demo', => @openDemo()
    @openDemo()

  deactivate: ->

  serialize: ->

  openDemo: ->
    Table = require './table'
    TableView = require './table-view'

    table = new Table
    table.addColumn 'key'
    table.addColumn 'value', align: 'right'
    table.addColumn 'foo', align: 'right'

    for i in [0...100]
      table.addRow [
        "row#{i}"
        i * 100
        if i % 2 is 0 then 'yes' else 'no'
      ]

    tableView = new TableView(table)
    tableView.setRowHeight 30
    tableView.setRowOverdraw 4

    tableView.addClass('demo overlay from-top').height(300)
    atom.workspaceView.append(tableView)

    tableView.on 'core:cancel', -> tableView.destroy()

    tableView.focus()

module.exports =

  config:
    undefinedDisplay:
      type: 'string'
      default: ''

  activate: (state) ->
    atom.workspaceView.command 'table-edit:demo', => @openDemo()
    @openDemo()

  deactivate: ->

  serialize: ->

  openDemo: ->
    Table = require './table'
    TableView = require './table-view'

    table = new Table
    table.addColumn 'key'
    table.addColumn 'value', align: 'right'
    table.addColumn 'foo', align: 'right'

    for i in [0...100]
      table.addRow [
        "row#{i}"
        i * 100
        if i % 2 is 0 then 'yes' else 'no'
      ]

    table.clearUndoStack()

    tableView = new TableView(table)
    tableView.setRowHeight 30
    tableView.setRowOverdraw 4

    tableView.addClass('demo overlay from-top').height(300)
    atom.workspaceView.append(tableView)

    tableView.on 'core:cancel', -> tableView.destroy()

    tableView.focus()

Clear undo stack of the demo after creation


define (require) ->
  settings = require('settings')
  FooterTabView = require('cs!../inherits/tab/tab')
  template = require('hbs!./metadata-template')
  require('less!./metadata')

  return class MetadataView extends FooterTabView
    template: template
    templateHelpers: () ->
      model = super()
      model.languages = settings.languages
      model.languageName = settings.languages[model.language]
      return model

    editable:
      '.js-metadata-language-select':
        value: () -> if @media is 'book' then return 'language' else return 'currentPage.language'
        type: 'select2'
        select2:
          width: 300

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

define (require) ->
  settings = require('settings')
  FooterTabView = require('cs!../inherits/tab/tab')
  template = require('hbs!./metadata-template')
  require('less!./metadata')

  return class MetadataView extends FooterTabView
    template: template
    templateHelpers: () ->
      model = super()
      model.languages = settings.languages
      model.languageName = settings.languages[model.language]
      return model

    editable:
      '.metadata-language-select':
        value: () -> if @media is 'book' then return 'language' else return 'currentPage.language'
        type: 'select2'
        select2:
          width: 300

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

Update language select class name

Update language select class name


{Range} = require 'atom'
_ = require 'underscore-plus'
fs = require 'fs-plus'

module.exports =
class GocovParser
  constructor: (dispatch) ->
    @dispatch = dispatch

  setDataFile: (file) ->
    @dataFile = file

  # TODO disgusting - fix this parsing, regex
  rangesForFile: (file) ->
    # TODO Ensure we have a data file and it exists
    data = fs.readFileSync @dataFile, {encoding: "utf8"}

    ranges = []

    for line in data.split("\n")
      line = line.trim()
      continue if line == "mode: set" || line == ""
      [fileLinesCols, statements, count] = line.split(" ")
      [filePath, linesCols] = fileLinesCols.split(":")
      if _.endsWith(file, filePath)
        [start, end] = linesCols.split(",")
        [startRow, startCol] = start.split(".")
        [endRow, endCol] = end.split(".")

        range = new Range([parseInt(startRow)-1, parseInt(startCol)-1], [parseInt(endRow)-1, parseInt(endCol)-1])
        ranges.push range: range, count: parseInt(count)

    ranges

{Range} = require 'atom'
_ = require 'underscore-plus'
fs = require 'fs-plus'

module.exports =
class GocovParser
  constructor: (dispatch) ->
    @dispatch = dispatch

  setDataFile: (file) ->
    @dataFile = file

  # TODO disgusting - fix this parsing, regex
  rangesForFile: (file) ->
    try
      data = fs.readFileSync @dataFile, {encoding: "utf8"}
    catch error
      return []

    ranges = []

    for line in data.split("\n")
      line = line.trim()
      continue if line == "mode: set" || line == ""
      [fileLinesCols, statements, count] = line.split(" ")
      [filePath, linesCols] = fileLinesCols.split(":")
      if _.endsWith(file, filePath)
        [start, end] = linesCols.split(",")
        [startRow, startCol] = start.split(".")
        [endRow, endCol] = end.split(".")

        range = new Range([parseInt(startRow)-1, parseInt(startCol)-1], [parseInt(endRow)-1, parseInt(endCol)-1])
        ranges.push range: range, count: parseInt(count)

    ranges

Handle the error when the coverage report is not generated