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

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

    coffeelint:
      options:
        no_empty_param_list:
          level: 'error'
        max_line_length:
          level: 'ignore'

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

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

  grunt.loadNpmTasks('grunt-contrib-coffee')
  grunt.loadNpmTasks('grunt-shell')
  grunt.loadNpmTasks('grunt-coffeelint')
  grunt.loadTasks('tasks')

  grunt.registerTask 'clean', ->
    grunt.file.delete('lib') if grunt.file.exists('lib')
  grunt.registerTask('lint', ['coffeelint:src', 'coffeelint:test'])
  grunt.registerTask('default', ['coffeelint', 'coffee'])
  grunt.registerTask('test', ['default', 'lint', 'shell:test'])
  grunt.registerTask('prepublish', ['clean', 'build-grammars', 'test'])

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

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

    coffeelint:
      options:
        no_empty_param_list:
          level: 'error'
        max_line_length:
          level: 'ignore'

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

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

  grunt.loadNpmTasks('grunt-contrib-coffee')
  grunt.loadNpmTasks('grunt-shell')
  grunt.loadNpmTasks('grunt-coffeelint')
  grunt.loadTasks('tasks')

  grunt.registerTask 'clean', ->
    grunt.file.delete('lib') if grunt.file.exists('lib')
    grunt.file.delete('gen') if grunt.file.exists('gen')

  grunt.registerTask('lint', ['coffeelint:src', 'coffeelint:test'])
  grunt.registerTask('default', ['coffeelint', 'coffee'])
  grunt.registerTask('test', ['default', 'lint', 'shell:test'])
  grunt.registerTask('prepublish', ['clean', 'build-grammars', 'test'])

Delete gen/ during clean task

Delete gen/ during clean task


do ($$ = Quo) ->

    VENDORS = [ "-webkit-", "-moz-", "-ms-", "-o-", "" ]

    $$.fn.addClass = (name) ->
        @each ->
            unless _existsClass(name, @className)
                @className += " " + name
                @className = @className.trim()

    $$.fn.removeClass = (name) ->
        @each ->
            unless name
                @className = ""
            else
                @className = @className.replace(name, " ").replace(/\s+/g, " ").trim() if _existsClass(name, @className)

    $$.fn.toggleClass = (name) ->
        @each ->
            if _existsClass(name, @className)
                @className = @className.replace(name, " ")
            else
                @className += " " + name
                @className = @className.trim()

    $$.fn.hasClass = (name) ->
        _existsClass name, this[0].className

    $$.fn.style = (property, value) ->
        if value
            @each -> @style[property] = value
        else
            this[0].style[property] or _computedStyle(this[0], property)

    $$.fn.css = (property, value) -> @style property, value

    $$.fn.vendor = (property, value) ->
        @style("#{vendor}#{property}", value) for vendor in VENDORS

    _existsClass = (name, className) ->
        classes = className.split(/\s+/g)
        classes.indexOf(name) >= 0

    _computedStyle = (element, property) ->
        document.defaultView.getComputedStyle(element, "")[property]

do ($$ = Quo) ->

    VENDORS = [ "-webkit-", "-moz-", "-ms-", "-o-", "" ]

    $$.fn.addClass = (name) ->
        @each ->
            unless _existsClass(name, @className)
                @className += " " + name
                @className = @className.trim()

    $$.fn.removeClass = (name) ->
        @each ->
            unless name
                @className = ""
            else
                @className = @className.replace(name, " ").replace(/\s+/g, " ").trim() if _existsClass(name, @className)

    $$.fn.toggleClass = (name) ->
        @each ->
            if _existsClass(name, @className)
                @className = @className.replace(name, " ")
            else
                @className += " " + name
                @className = @className.trim()

    $$.fn.hasClass = (name) ->
        this.length > 0 and _existsClass name, this[0].className

    $$.fn.style = (property, value) ->
        if value
            @each -> @style[property] = value
        else if this.length > 0
            this[0].style[property] or _computedStyle(this[0], property)

    $$.fn.css = (property, value) -> @style property, value

    $$.fn.vendor = (property, value) ->
        @style("#{vendor}#{property}", value) for vendor in VENDORS

    _existsClass = (name, className) ->
        classes = className.split(/\s+/g)
        classes.indexOf(name) >= 0

    _computedStyle = (element, property) ->
        document.defaultView.getComputedStyle(element, "")[property]

Fix for when the array is empty

Fix for when the array is empty

`$$.fn.hasClass` and `$$.fn.style` will both throw exceptions if the current selection of elements is empty.


# # Kissmetrics Batch

class BatchKissmetricsClient
  @HOST: 'api.kissmetrics.com'
  @HTTP_METHOD: 'POST'
  @API_VERSION: 'v1'

  @process: (queue, apiKey, apiSecret, productGUID) =>
    http = require 'http'

    urlPath   = "#{@API_VERSION}/products/#{productGUID}/tracking/e"
    baseUrl   = "http://#{@HOST}/#{urlPath}"
    signature = @_generateSignature baseUrl, apiSecret

    request = http.request
      method: @HTTP_METHOD
      host: @HOST
      path: "#{baseUrl}?_signature=#{signature}"
      headers:
        'X-KM-ApiKey': apiKey

    requestBody = JSON.stringify {data: queue.get()}
    request.write requestBody

    queue.done()
    return request

  @_generateSignature: (baseUrl, apiSecret) =>
    crypto = require 'crypto'
    signer = crypto.createHmac 'sha256', apiSecret

    encodedRequest = [@HTTP_METHOD, encodeURIComponent baseUrl].join('&')
    encodeURIComponent signer.update(encodedRequest).digest('base64')

  constructor: (@queue) ->

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

    @queue.add data

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

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

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

    delete data.type

module.exports = BatchKissmetricsClient

# # Kissmetrics Batch

class BatchKissmetricsClient
  @HOST: 'api.kissmetrics.com'
  @HTTP_METHOD: 'POST'
  @API_VERSION: 'v1'

  @process: (queue, apiKey, apiSecret, productGUID) =>
    http = require 'http'

    urlPath   = "#{@API_VERSION}/products/#{productGUID}/tracking/e"
    baseUrl   = "http://#{@HOST}/#{urlPath}"
    signature = @_generateSignature baseUrl, apiSecret

    request = http.request
      method: @HTTP_METHOD
      host: @HOST
      path: "#{baseUrl}?_signature=#{signature}"
      headers:
        'X-KM-ApiKey': apiKey
    , ->
      queue.done()

    requestBody = JSON.stringify {data: queue.get()}
    request.write requestBody

    return request

  @_generateSignature: (baseUrl, apiSecret) =>
    crypto = require 'crypto'
    signer = crypto.createHmac 'sha256', apiSecret

    encodedRequest = [@HTTP_METHOD, encodeURIComponent baseUrl].join('&')
    encodeURIComponent signer.update(encodedRequest).digest('base64')

  constructor: (@queue) ->

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

    @queue.add data

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

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

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

    delete data.type

module.exports = BatchKissmetricsClient

Call queue.done() in the request callback


git = require '../git'
ProjectsListView = require '../views/projects-list-view'
StatusView = require '../views/status-view'

gitInit = ->
  currentFile = atom.workspace.getActiveTextEditor()?.getPath()
  if not currentFile and atom.project.getPaths().length > 1
    promise = new ProjectsListView().result.then (path) -> init(path)
  else
    init(atom.project.getPaths()[0])

init = (path) ->
  git.cmd
    args: ['init']
    cwd: path
    stdout: (data) ->
      new StatusView(type: 'success', message: data)
      atom.project.setPath(path)

module.exports = gitInit

git = require '../git'
ProjectsListView = require '../views/projects-list-view'
StatusView = require '../views/status-view'

gitInit = ->
  currentFile = atom.workspace.getActiveTextEditor()?.getPath()
  if not currentFile and atom.project.getPaths().length > 1
    promise = new ProjectsListView().result.then (path) -> init(path)
  else
    init(atom.project.getPaths()[0])

init = (path) ->
  git.cmd
    args: ['init']
    cwd: path
    stdout: (data) ->
      new StatusView(type: 'success', message: data)
      atom.project.setPaths([path])

module.exports = gitInit

Fix deprecations in git init command

Fix deprecations in git init command


class Algorhythmic
  max: 10

  compute: (array) ->
    array
    .map(@getCharInt)
    .reduce((previousInt, currentInt) =>
      (previousInt + currentInt) % @max
    , 0) + 1

  getCharInt: (char) -> parseInt char.charCodeAt(0) or 0

  convert: (string) ->
    str = string.replace(/\.(png|jpg|gif|)$/g, "")
    stringArray = str.split('')
    return @compute(stringArray)

module.exports = new Algorhythmic()

class Algorhythmic
  max: 10

  convert: (string) ->
    str = string.replace(/\.(png|jpg|gif|)$/g, "")
    stringArray = str.split('')
    return @_compute(stringArray)

  _compute: (array) ->
    array
    .map(@_getCharInt)
    .reduce((previousInt, currentInt) =>
      (previousInt + currentInt) % @max
    , 0) + 1

  _getCharInt: (char) -> parseInt char.charCodeAt(0) or 0

module.exports = new Algorhythmic()

Use convention for 'private' functions

Use convention for 'private' functions

Move them below public functions, also.


class Repo extends Backbone.Model

class RepoCollection extends Backbone.Collection

  model: Repo

  url: 'https://api.github.com/users/trabianmatt/repos'

module.exports = { Repo, RepoCollection }

class Repo extends Backbone.Model

class RepoCollection extends Backbone.Collection

  model: Repo

  url: 'https://api.github.com/users/trabian/repos'

module.exports = { Repo, RepoCollection }

Change sample Github user to 'trabian' from 'trabianmatt'


{Disposable} = require('atom')
module.exports =
  instance: null
  config:
    lintOnFly:
      title: 'Lint on fly'
      description: 'Lint files while typing, without the need to save them'
      type: 'boolean'
      default: true
    showErrorPanel:
      title: 'Show Error Panel at the bottom'
      type: 'boolean'
      default: true
    showErrorInline:
      title: 'Show Inline Tooltips'
      descriptions: 'Show inline tooltips for errors'
      type: 'boolean'
      default: true
    underlineIssues:
      title: 'Underline Issues'
      type: 'boolean'
      default: true

  activate: ->
    LinterPlus = require('./linter-plus.coffee')
    @instance = new LinterPlus()

    legacy = require('./legacy.coffee')
    for atomPackage in atom.packages.getLoadedPackages()
      if atomPackage.metadata['linter-package'] is true
        implementation = atomPackage.metadata['linter-implementation'] ? atomPackage.name
        linter = legacy(require("#{atomPackage.path}/lib/#{implementation}"))
        @consumeLinter(linter)

  consumeLinter: (linters) ->
    unless linters instanceof Array
      linters = [ linters ]

    for linter in linters
      @instance.addLinter(linter)

    new Disposable =>
      for linter in linters
        @instance.deleteLinter(linter)

  consumeStatusBar: (statusBar) ->
    @instance.views.attachBottom(statusBar)

  provideLinter: ->
    @Linter

  deactivate: ->
    @instance?.deactivate()

{Disposable} = require('atom')
module.exports =
  instance: null
  config:
    lintOnFly:
      title: 'Lint on fly'
      description: 'Lint files while typing, without the need to save them'
      type: 'boolean'
      default: true
    showErrorPanel:
      title: 'Show Error Panel at the bottom'
      type: 'boolean'
      default: true
    showErrorInline:
      title: 'Show Inline Tooltips'
      descriptions: 'Show inline tooltips for errors'
      type: 'boolean'
      default: true
    underlineIssues:
      title: 'Underline Issues'
      type: 'boolean'
      default: true

  activate: ->
    LinterPlus = require('./linter-plus.coffee')
    @instance = new LinterPlus()

    legacy = require('./legacy.coffee')
    for atomPackage in atom.packages.getLoadedPackages()
      if atomPackage.metadata['linter-package'] is true
        implementation = atomPackage.metadata['linter-implementation'] ? atomPackage.name
        linter = legacy(require("#{atomPackage.path}/lib/#{implementation}"))
        @consumeLinter(linter)

  consumeLinter: (linters) ->
    unless linters instanceof Array
      linters = [ linters ]

    for linter in linters
      @instance.addLinter(linter)

    new Disposable =>
      for linter in linters
        @instance.deleteLinter(linter)

  consumeStatusBar: (statusBar) ->
    @instance.views.attachBottom(statusBar)

  provideLinter: ->
    @instance

  deactivate: ->
    @instance?.deactivate()

Fix a critical bug in self service

:bug: Fix a critical bug in self service


class CMSimple.Panels.ImageLibrary.Selection extends Spine.Controller
  constructor: ->
    super
    @bindEvents()

  set: (img)->
    if @selectedImage
      @selectedImage.attr('src', img.src)
      snippet = Mercury.Snippet.find @selectedImage.parents('[data-snippet]').data('snippet')
      snippet.options.snippet[@selectedImage.data('snippet-image')] = img.src

    else if @region.type == 'editable'
      options = {value: img}
      @region.execCommand 'insertImage', options

  clearSelection: ->
    if @selectedImage
      @selectedImage.removeClass('selected-image')
      @selectedImage = null
      @trigger 'filter', null

  setSelection: (el)->
    @selectedImage?.removeClass('selected-image')
    @selectedImage = el
    @selectedImage.addClass('selected-image')
    if geometry = @selectedImage.data('image-geometry')
      @trigger 'filter', geometry

  bindEvents: ->
    Mercury.on 'region:focused', (event, options) =>
      @bindRegion(options.region)

  bindRegion: (region)->
    _clearSelection = => @clearSelection()
    if @region
      # unbind current region if user selected a new region
      @region.element.off 'click', _clearSelection
      @region.element.find('[data-snippet-image]').off('click')

    if @region = region
      @region.element.on 'click', _clearSelection
      @region.element.find('[data-snippet-image]').on 'click', (e)=>
        e.stopPropagation()
        @setSelection($(e.target))

class CMSimple.Panels.ImageLibrary.Selection extends Spine.Controller
  constructor: ->
    super
    @bindEvents()

  set: (img)->
    if @selectedImage
      @selectedImage.attr('src', img.src)
      @selectedImage.attr('alt', img.alt)
      snippet = Mercury.Snippet.find @selectedImage.parents('[data-snippet]').data('snippet')
      snippet.options.snippet[@selectedImage.data('snippet-image')] = img.src

    else if @region.type == 'editable'
      options = {value: img}
      @region.execCommand 'insertImage', options

  clearSelection: ->
    if @selectedImage
      @selectedImage.removeClass('selected-image')
      @selectedImage = null
      @trigger 'filter', null

  setSelection: (el)->
    @selectedImage?.removeClass('selected-image')
    @selectedImage = el
    @selectedImage.addClass('selected-image')
    if geometry = @selectedImage.data('image-geometry')
      @trigger 'filter', geometry

  bindEvents: ->
    Mercury.on 'region:focused', (event, options) =>
      @bindRegion(options.region)

  bindRegion: (region)->
    _clearSelection = => @clearSelection()
    if @region
      # unbind current region if user selected a new region
      @region.element.off 'click', _clearSelection
      @region.element.find('[data-snippet-image]').off('click')

    if @region = region
      @region.element.on 'click', _clearSelection
      @region.element.find('[data-snippet-image]').on 'click', (e)=>
        e.stopPropagation()
        @setSelection($(e.target))

Set the alt attribute on the image as well.


menu: [
  {
    label: 'File'
    submenu: [
      {label: 'Close All Tabs', command: 'tabs:close-all-tabs'}
    ]
  }
]

'context-menu':
  '.tab': [
    {label: 'Close Tab', command: 'tabs:close-tab'}
    {label: 'Close Other Tabs', command: 'tabs:close-other-tabs'}
    {label: 'Close Tabs to the Right', command: 'tabs:close-tabs-to-right'}
    {label: 'Close Saved Tabs', command: 'tabs:close-saved-tabs'}
    {label: 'Close All Tabs', command: 'tabs:close-all-tabs'}

    {type: 'separator'}

    {label: 'Split Up', command: 'tabs:split-up'}
    {label: 'Split Down', command: 'tabs:split-down'}
    {label: 'Split Left', command: 'tabs:split-left'}
    {label: 'Split Right', command: 'tabs:split-right'}
  ]
  '.tab.preview-tab': [
    {label: 'Keep Preview Tab', command: 'tabs:keep-preview-tab'}
  ]
  '.tab-bar': [
    {label: 'Reopen Closed Tab', command: 'pane:reopen-closed-item'}
  ]

Add 'Close All Tabs' to File menu

Add 'Close All Tabs' to File menu


'*':
  'editor':
    'fontFamily': 'PragmataPro'
    'showInvisibles': true
    'showIndentGuide': true
    'invisibles': {}
  'core':
    'disabledPackages': [
      'autocomplete'
      'spell-check'
    ]
    'themes': [
      'solarized-dark-ui'
      'solarized-dark-syntax'
    ]
    'projectHome': '/Users/edgard/Documents/code'
  'exception-reporting':
    'userId': '1b98cd61-c072-c275-56fd-5715ea36d0bd'
  'welcome':
    'showOnStartup': false
  'autocomplete-plus': {}
  'minimap': {}
  'git-diff':
    'showIconsInEditorGutter': true
  'spell-check': {}

'*':
  'editor':
    'fontFamily': 'PragmataPro'
    'showInvisibles': true
    'showIndentGuide': true
    'invisibles': {}
  'core':
    'disabledPackages': [
      'autocomplete'
      'spell-check'
    ]
    'themes': [
      'solarized-dark-ui'
      'solarized-dark-syntax'
    ]
    'projectHome': '/home/edgard/Documents/code'
  'exception-reporting':
    'userId': '1b98cd61-c072-c275-56fd-5715ea36d0bd'
  'welcome':
    'showOnStartup': false
  'autocomplete-plus': {}
  'minimap': {}
  'git-diff':
    'showIconsInEditorGutter': true
  'spell-check': {}

Change default project path for atom

Change default project path for atom


class PgHoffTypes
    @Type:
        'timestamp with time zone':
            format: (value)         -> return new Date(value).toLocaleString(atom.config.get('pg-hoff.locale'))
        'timestamp':
            format: (value)         -> return new Date(value).toLocaleString(atom.config.get('pg-hoff.locale'))
        'json':
            format: (text)          -> return JSON.stringify(JSON.parse(text), null, '  ')

module.exports = PgHoffTypes

timestamp =
    format: (value) -> new Date(value).toLocaleString(atom.config.get('pg-hoff.locale'))
time =
    format: (value) -> new Date('2000-01-01 ' + value).toLocaleTimeString(atom.config.get('pg-hoff.locale'))
class PgHoffTypes
    @Type:
        'timestamp with time zone': timestamp
        'timestamp without time zone': timestamp
        'time with time zone': time
        'time without time zone': time
        'json':
            format: (text)          -> return JSON.stringify(JSON.parse(text), null, '  ')

module.exports = PgHoffTypes

Add support for time data types

Add support for time data types

And remove a tiny bit of duplication.


fs = require "fs"
path = require "path"
env = (process.env.NODE_ENV or "development").toLowerCase()

if process.env.SHARELATEX_CONFIG?
	possibleConfigFiles = [process.env.SHARELATEX_CONFIG]
else
	possibleConfigFiles = [
		process.cwd() + "/config/settings.#{env}.coffee"
		path.normalize(__dirname + "/../../config/settings.#{env}.coffee")
	]

for file in possibleConfigFiles
	if fs.existsSync(file)
		module.exports = require(file)
		return

console.log "No config file could be found at: ", possibleConfigFiles
throw new Error("No config file found")

fs = require "fs"
path = require "path"
env = (process.env.NODE_ENV or "development").toLowerCase()

merge = (settings, defaults) ->
	for key, value of settings
		if typeof(value) == "object"
			defaults[key] = merge(settings[key], defaults[key] or {})
		else
			defaults[key] = value
	return defaults

defaultSettingsPath = path.normalize(__dirname + "/../../config/settings.defaults.coffee")

console.log "default settings", defaultSettingsPath
if fs.existsSync(defaultSettingsPath)
	defaults = require(defaultSettingsPath)
	settingsExist = true
else
	defaults = {}
	settingsExist = false

if process.env.SHARELATEX_CONFIG?
	possibleConfigFiles = [process.env.SHARELATEX_CONFIG]
else
	possibleConfigFiles = [
		process.cwd() + "/config/settings.#{env}.coffee"
		path.normalize(__dirname + "/../../config/settings.#{env}.coffee")
	]

for file in possibleConfigFiles
	if fs.existsSync(file)
		module.exports = merge(require(file), defaults)
		settingsExist = true
		break

if !settingsExist
	console.warn "No settings or defaults found. I'm flying blind."

module.exports = defaults

console.log "Settings", module.exports

Load and merge defaults if available

Load and merge defaults if available

Load and merge defaults if available


Q = require 'q'

{Base} = require "#{__dirname}/base"
{Wrappers} = require "#{__dirname}/helpers/wrappers"
{BeanBuilder} = require "#{__dirname}/helpers/bean_builder"

class BaseTask extends Base

  app: null

  actions: null
  actionBuilder: null

  constructor: ->
    @actions = []
    @actionBuilder = new BeanBuilder basePath: "#{__dirname}/tasks/actions", suffix: 'action'

  configure: (@app, config) ->
    super @app.getEmitter()
    @_configureActions config.actions if config.actions?.length

  _configureActions: (actions) ->
    @actions = (@_buildAction action for action in actions)
    return

  initialize: ->
    action.initialize() for action in @actions
    return

  _buildAction: (config) ->
    action = @actionBuilder.build config
    action.configure @app, @, config
    return action

  run: (data)->
    promise = null
    scope = data: data
    for action in @actions
      if promise
        promise.then (lastResult) ->
          scope.lastResult = lastResult
          Q.when action.run scope
      else
        promise = Q.when action.run scope
    return promise

exports.BaseTask = BaseTask

Q = require 'q'

{Base} = require "#{__dirname}/base"
{Wrappers} = require "#{__dirname}/helpers/wrappers"
{BeanBuilder} = require "#{__dirname}/helpers/bean_builder"

class BaseTask extends Base

  app: null

  actions: null
  actionBuilder: null

  constructor: ->
    @actions = []
    @actionBuilder = new BeanBuilder basePath: "#{__dirname}/tasks/actions", suffix: 'action'

  configure: (@app, config) ->
    super @app.getEmitter()
    @_configureActions config.actions if config.actions?.length

  _configureActions: (actions) ->
    @actions = (@_buildAction action for action in actions)
    return

  initialize: ->
    for action in @actions
      action.initialize()
    return

  _buildAction: (config) ->
    action = @actionBuilder.build config
    action.configure @app, @, config
    return action

  run: (data)->
    promise = null
    scope = data: data
    for action in @actions
      if promise
        promise.then ((lastResult) ->
          scope.lastResult = lastResult
          Q.when action.run scope
        ), ((err) =>
          @log 'warn', 'task.base', "Action #{action} performed not well.", err
        )
      else
        promise = Q.when action.run scope
    return promise

exports.BaseTask = BaseTask

Improve logging out errors when performing task's actions.


QUnit.test "test unique", (assert) ->
    input = [5, 6, 7, 5, 5, 6, 7]
    output = unique(input)
    expected = [5, 6, 7]

    assert.deepEqual( output , expected)

QUnit.test "test unique", (assert) ->
    input = [5, 6, 7, 5, 5, 6, 7]
    output = unique(input)
    expected = [5, 6, 7]

    assert.deepEqual(output , expected, "plain")

    input = [5, 6, null, 7, 5, 5, 6, 7]
    output = unique(input)
    assert.deepEqual(output, expected, "with null")

    input = [5]
    output = unique(input)
    expected = [5]
    assert.deepEqual(output, expected, "one value")

    input = [null, null, null]
    output = unique(input)
    expected = []
    assert.deepEqual(output, expected, "only nulls")

    input = [5, 6, 5, 6, 5, 6, "a"]
    output = unique(input)
    expected = [5, 6, "a"]
    assert.deepEqual(output, expected, "mixed strings/integers can give weird results (1)")

    input = [5, 6, 5, 6, 5, "6", "a"]
    output = unique(input)
    expected = [5, "6", "a"]
    assert.deepEqual(output, expected, "mixed strings/integers can give weird results (2)")

    input = ["5", 6, 5, 6, 5, "6", "a"]
    output = unique(input)
    expected = [5, "6", "a"]
    assert.deepEqual(output, expected, "mixed strings/integers can give weird results (3)")

    input = ["5", "5",  6, "5", "6", "5", "6", "a"]
    output = unique(input)
    expected = ["5", "6", "a"]
    assert.deepEqual(output, expected, "mixed strings/integers can give weird results (4)")

Add to unique unit tests

Add to unique unit tests


class @WishController extends RouteController

  waitOn: ->
    [Meteor.subscribe('wish', @params.id), subscriptionHandles['users']]

  tempalte: 'wish'

  renderTemplates:
    'nav': to: 'nav'

  notFoundTemplate: 'notFound'

  data: ->
    wish = Wishes.findOne({_id: @params.id})
    if not wish then return null
    {page: 'wish', wish: wish}

class @WishController extends RouteController

  waitOn: ->
    subs = [subscriptionHandles.users]
    if not Wishes.findOne({_id: @params.id})
      s = Meteor.subscribe('wish', @params.id)
      s.stop = ->
      subs.push(s)
    subs

  tempalte: 'wish'

  renderTemplates:
    'nav': to: 'nav'

  notFoundTemplate: 'notFound'

  data: ->
    wish = Wishes.findOne({_id: @params.id})
    if not wish then return null
    {page: 'wish', wish: wish}

Remove unneeded subscription at the wish page (if the data already exists)


module.exports = ->
  @logger = require './logger'

  @visit = (path) =>
    @driver.get "#{@host}#{path}"

  _Before = @Before
  _After  = @After

  @_inFlow = (code, callback) ->
    $.createFlow (flow) =>
      flow.execute => code.call(@)
    .then _.partial(callback, null), callback

  @Before = (code) ->
    _Before (callback) =>
      @_inFlow code, callback

  @After = (code) ->
    _After (callback) =>
      @_inFlow code, callback

  @BeforeAll = (code) ->
    unless @_ranBeforeAll
      @_ranBeforeAll = true
      @Before code

  @AfterAll = (code) ->
    unless @_ranAfterAll
      @_ranAfterAll = true
      @After code

  @Given = @When = @Then = @And = (pattern, code) =>
    @defineStep pattern, (args..., callback) =>
      start = new Date

      callforth = =>
        $
        .createFlow (flow) =>
          flow.execute => code.apply(@, args)
        .then _.partial(callback, null), (error) =>
          if new Date - start > @timeout
            callback(error)
          else
            $.delayed(1000).then -> callforth()

      callforth()

  @Before ->
    @driver = new Driver.Builder().withCapabilities(Driver.Capabilities.chrome()).build()

  @After ->
    @driver.close()
    @driver.quit()

module.exports = ->
  @logger = require './logger'

  @visit = (path) =>
    @driver.get "#{@host}#{path}"

  _Before = @Before
  _After  = @After

  @_inFlow = (code, callback) ->
    $.createFlow (flow) =>
      flow.execute => code.call(@)
    .then _.partial(callback, null),
      (err) -> throw err

  @Before = (code) ->
    _Before (callback) =>
      @_inFlow code, callback

  @After = (code) ->
    _After (callback) =>
      @_inFlow code, callback

  @BeforeAll = (code) ->
    unless @_ranBeforeAll
      @_ranBeforeAll = true
      @Before code

  @AfterAll = (code) ->
    unless @_ranAfterAll
      @_ranAfterAll = true
      @After code

  @Given = @When = @Then = @And = (pattern, code) =>
    @defineStep pattern, (args..., callback) =>
      start = new Date

      callforth = =>
        $
        .createFlow (flow) =>
          flow.execute => code.apply(@, args)
        .then _.partial(callback, null), (error) =>
          if new Date - start > @timeout
            callback(error)
          else
            $.delayed(1000).then -> callforth()

      callforth()

  @Before ->
    @driver = new Driver.Builder().withCapabilities(Driver.Capabilities.chrome()).build()

  @After ->
    @driver.close()
    @driver.quit()

Raise error on failed action in flow.

Raise error on failed action in flow.


'.workspace':
  'ctrl-g o': 'github:open'
  'ctrl-g b': 'github:blame'
  'ctrl-g h': 'github:history'
  'ctrl-g c': 'github:copy-url'

'.workspace':
  'alt-g o': 'github:open'
  'alt-g b': 'github:blame'
  'alt-g h': 'github:history'
  'alt-g c': 'github:copy-url'

Use alt instead of ctrl

Use alt instead of ctrl


chai = require 'chai'
chai.should()
chai.use require 'chai-as-promised'

require 'postmortem/register'

Nightmare = require 'joseph/nightmare'

before ->
  browser = Nightmare show: process.env.VERBOSE is 'true'
  yield browser.goto 'http://localhost:3333/'
  global.browser = browser

after ->
  yield browser.end()

chai = require 'chai'
chai.should()
chai.use require 'chai-as-promised'

require 'postmortem/register'

Nightmare = require 'joseph/nightmare'

before ->
  browser = Nightmare
    show: process.env.VERBOSE is 'true'
    # switches:
    #   'proxy-server':              'http://localhost:4010'
    #   'ignore-certificate-errors': true

  yield browser.goto 'http://localhost:3333/'
  global.browser = browser

after ->
  yield browser.end()

Add proxy settings for nightmare.

Add proxy settings for nightmare.


'use strict'

gulp = require 'gulp'
$ = (require 'gulp-load-plugins') lazy: false

paths =
  jshint: [
    './bin/*.js'
  ]
  coffeelint: [
    './gulpfile.coffee'
    './test/*.coffee'
  ]
  watch: [
    './gulpfile.coffee'
    './bin/**/*.js'
    './test/**/*.coffee'
  ]
  tests: [
    './test/**/*.coffee'
  ]

gulp.task 'jshint', ->
  gulp.src paths.jshint
    .pipe $.jshint()
    .pipe $.jshint.reporter()

gulp.task 'coffeelint', ->
  gulp.src paths.coffeelint
    .pipe $.coffeelint()
    .pipe $.coffeelint.reporter()

gulp.task 'compile', ['jshint', 'coffeelint'], ->
  gulp.src paths.tests
    .pipe $.sourcemaps.init()
    .pipe($.coffee({ bare: true }).on('error', $.util.log))
    .pipe $.sourcemaps.write()
    .pipe $.espower()
    .pipe gulp.dest('./compile')

gulp.task 'watch', ['test'], ->
  gulp.watch paths.watch, ['test']

gulp.task 'test', ['compile'], ->
  gulp.src ['./compile/**/*.js'], {cwd: __dirname}
    .pipe $.mocha()

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

'use strict'

gulp = require 'gulp'
$ = (require 'gulp-load-plugins') lazy: false

paths =
  jshint: [
    './bin/*'
  ]
  coffeelint: [
    './gulpfile.coffee'
    './test/*.coffee'
  ]
  watch: [
    './gulpfile.coffee'
    './bin/**/*.js'
    './test/**/*.coffee'
  ]
  tests: [
    './test/**/*.coffee'
  ]

gulp.task 'jshint', ->
  gulp.src paths.jshint
    .pipe $.jshint()
    .pipe $.jshint.reporter()

gulp.task 'coffeelint', ->
  gulp.src paths.coffeelint
    .pipe $.coffeelint()
    .pipe $.coffeelint.reporter()

gulp.task 'compile', ['jshint', 'coffeelint'], ->
  gulp.src paths.tests
    .pipe $.sourcemaps.init()
    .pipe($.coffee({ bare: true }).on('error', $.util.log))
    .pipe $.sourcemaps.write()
    .pipe $.espower()
    .pipe gulp.dest('./compile')

gulp.task 'watch', ['test'], ->
  gulp.watch paths.watch, ['test']

gulp.task 'test', ['compile'], ->
  gulp.src ['./compile/**/*.js'], {cwd: __dirname}
    .pipe $.mocha()

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

Enable to lint no-extension file

Enable to lint no-extension file


# Start the crash reporter before anything else.
require('crash-reporter').start(productName: 'Atom', companyName: 'GitHub')

path = require 'path'

try
  require '../src/window'
  Atom = require '../src/atom'
  window.atom = Atom.loadOrCreate('spec')

  # Show window synchronously so a focusout doesn't fire on input elements
  # that are focused in the very first spec run.
  atom.getCurrentWindow().show() unless atom.getLoadSettings().exitWhenDone

  {runSpecSuite} = require './jasmine-helper'

  # Add 'src/exports' to module search path.
  exportsPath = path.resolve(atom.getLoadSettings().resourcePath, 'exports')
  require('module').globalPaths.push(exportsPath)
  # Still set NODE_PATH since tasks may need it.
  process.env.NODE_PATH = exportsPath

  document.title = "Spec Suite"
  runSpecSuite './spec-suite', atom.getLoadSettings().logFile
catch error
  if atom?.getLoadSettings().exitWhenDone
    console.error(error.stack ? error)
    atom.exit(1)
  else
    throw error

# Start the crash reporter before anything else.
require('crash-reporter').start(productName: 'Atom', companyName: 'GitHub')

path = require 'path'

try
  require '../src/window'
  Atom = require '../src/atom'
  window.atom = Atom.loadOrCreate('spec')

  # Show window synchronously so a focusout doesn't fire on input elements
  # that are focused in the very first spec run.
  atom.getCurrentWindow().show() unless atom.getLoadSettings().exitWhenDone

  {runSpecSuite} = require './jasmine-helper'

  # Add 'exports' to module search path.
  exportsPath = path.join(atom.getLoadSettings().resourcePath, 'exports')
  require('module').globalPaths.push(exportsPath)
  # Still set NODE_PATH since tasks may need it.
  process.env.NODE_PATH = exportsPath

  document.title = "Spec Suite"
  runSpecSuite './spec-suite', atom.getLoadSettings().logFile
catch error
  if atom?.getLoadSettings().exitWhenDone
    console.error(error.stack ? error)
    atom.exit(1)
  else
    throw error

Use path.join instead of path.resolve

Use path.join instead of path.resolve

Applies commit 41ab48b to spec-bootstrap.coffee


"title": "Your Blog title"

"api":
    "baseUrl": "http://dev.julienrenaux.fr/wp-json"
    "timeout": 10000
    "maxAttempt": 3

"translation":
    "displayed" : ["en", "fr"]
    "prefered": "en"

"cordova":
    "admob":
        "enabled":false
        "android":
            "bannerID": null
            "interstitialID": null
        "ios":
            "bannerID": null
            "interstitialID": null
    "pushNotifications":
        "enabled": false
        "baseUrl": "http://yourDomain.com/pnfw"
        "android":
            "senderID": ""

"title": "Your Blog title"

"api":
    "baseUrl": "https://dev.julienrenaux.fr/wp-json"
    "timeout": 10000
    "maxAttempt": 3

"translation":
    "displayed" : ["en", "fr"]
    "prefered": "en"

"cordova":
    "admob":
        "enabled":false
        "android":
            "bannerID": null
            "interstitialID": null
        "ios":
            "bannerID": null
            "interstitialID": null
    "pushNotifications":
        "enabled": false
        "baseUrl": "http://yourDomain.com/pnfw"
        "android":
            "senderID": ""

Replace baseUrl http:// with https://

Replace baseUrl http:// with https://

Fix this error on first start :

```XMLHttpRequest cannot load https://dev.julienrenaux.fr/wp-json/... The 'Access-Control-Allow-Origin' header has a value 'http://null' that is not equal to the supplied origin. Origin 'null' is therefore not allowed access.```


{pressKey, test, testGroup, typeCharacters} = Trix.TestHelpers

testOptions =
  template: "editor_empty"
  setup: ->
    addEventListener("keydown", cancel, true)
    addEventListener "trix-before-initialize", handler = ({target}) ->
      removeEventListener("trix-before-initialize", handler)
      target.addEventListener("keydown", cancel)
  teardown: ->
    removeEventListener("keydown", cancel, true)

cancelingInCapturingPhase = false
cancelingAtTarget = false

cancel = (event) ->
  switch event.eventPhase
    when Event.CAPTURING_PHASE
      event.preventDefault() if cancelingInCapturingPhase
    when Event.AT_TARGET
      event.preventDefault() if cancelingAtTarget

testGroup "Canceled input", testOptions, ->
  test "ignoring canceled input events in capturing phase", (expectDocument) ->
    typeCharacters "a", ->
      cancelingInCapturingPhase = true
      pressKey "backspace", ->
        pressKey "return", ->
          cancelingInCapturingPhase = false
          typeCharacters "b", ->
            expectDocument "ab\n"

  test "ignoring canceled input events at target", (expectDocument) ->
    typeCharacters "a", ->
      cancelingAtTarget = true
      pressKey "backspace", ->
        pressKey "return", ->
          cancelingAtTarget = false
          typeCharacters "b", ->
            expectDocument "ab\n"

{pressKey, test, testGroup, typeCharacters} = Trix.TestHelpers

testOptions =
  template: "editor_empty"
  setup: ->
    addEventListener("keydown", cancel, true)
    addEventListener "trix-before-initialize", handler = ({target}) ->
      removeEventListener("trix-before-initialize", handler)
      target.addEventListener("keydown", cancel)
  teardown: ->
    removeEventListener("keydown", cancel, true)

cancelingInCapturingPhase = false
cancelingAtTarget = false

cancel = (event) ->
  switch event.eventPhase
    when Event::CAPTURING_PHASE
      event.preventDefault() if cancelingInCapturingPhase
    when Event::AT_TARGET
      event.preventDefault() if cancelingAtTarget

testGroup "Canceled input", testOptions, ->
  test "ignoring canceled input events in capturing phase", (expectDocument) ->
    typeCharacters "a", ->
      cancelingInCapturingPhase = true
      pressKey "backspace", ->
        pressKey "return", ->
          cancelingInCapturingPhase = false
          typeCharacters "b", ->
            expectDocument "ab\n"

  test "ignoring canceled input events at target", (expectDocument) ->
    typeCharacters "a", ->
      cancelingAtTarget = true
      pressKey "backspace", ->
        pressKey "return", ->
          cancelingAtTarget = false
          typeCharacters "b", ->
            expectDocument "ab\n"

Fix tests in IE 11

Fix tests in IE 11


keysSchema = mongoose.Schema
	user:
		type: mongoose.Schema.ObjectId
		ref: "User"
	identifier: String
	key: String
	created_at:
		type: Date
		default: Date.now
	updated_at:
		type: Date
		default: Date.now

keysSchema.static "getUserKeyWithIdentifier", (user, ident, cb) ->
	this.find
		user: user
		identifier: ident
	, (err, doc) ->
			if err
				console.log "Error getting user key by identifier"
				cb?(err)
				return true
			else
				cb?(null, doc)
				return false

	return true

keysSchema.static "createUserKeyWithIdentifier", (user, ident, key, cb) ->
	newKey = new Keys
		user: user
		identifier: ident
		key: key

	newKey.save cb

Keys = mongoose.model 'Keys', keysSchema

module.exports = exports = Keys

keysSchema = mongoose.Schema
	user:
		type: mongoose.Schema.ObjectId
		ref: "User"
	identifier: String
	key: String
	created_at:
		type: Date
		default: Date.now
	updated_at:
		type: Date
		default: Date.now

keysSchema.static "getUserKeyWithIdentifier", (user, ident, cb) ->
	this.find
		user: user.id
		identifier: ident
	, (err, doc) ->
			if err
				console.log "Error getting user key by identifier"
				cb?(err)
				return true
			else
				cb?(null, doc)
				return false

	return true

keysSchema.static "createUserKeyWithIdentifier", (user, ident, key, cb) ->
	newKey = new Keys
		user: user
		identifier: ident
		key: key

	newKey.save cb

Keys = mongoose.model 'Keys', keysSchema

module.exports = exports = Keys

Select by user.id, not the whole user object


require.config
  deps: ["main"]

  shim: {
    handlebars: exports: "Handlebars"
  }

  paths:
    # RequireJS plugins
    text: "../vendor/text/text"

    # Third party libraries
    jquery: "../vendor/jquery/jquery"
    handlebars: "../vendor/handlebars/handlebars"
    backbone: "../vendor/backbone-amd/backbone"
    underscore: "../vendor/underscore-amd/underscore"

define ->
  API_URL: "http://#{location.hostname}:4100"

require.config
  deps: ["main"]

  shim: {
    handlebars: exports: "Handlebars"
  }

  paths:
    # RequireJS plugins
    text: "../vendor/text/text"

    # Third party libraries
    jquery: "../vendor/jquery/jquery"
    handlebars: "../vendor/handlebars/handlebars"
    backbone: "../vendor/backbone-amd/backbone"
    underscore: "../vendor/underscore-amd/underscore"

define ->
  API_URL: "http://dev.api.backtick.io"

Switch over to hosted API

Switch over to hosted API


# from atom/terminal to reduce cpu usage

pty = require 'ptyw.js'

module.exports = (ptyCwd, sh, cols, rows, args) ->
  callback = @async()
  if sh
      shell = sh
  else
      shell = process.env.SHELL
      if not shell
        # Try to salvage some sort of shell to execute. Horrible code below.
        path = require 'path'
        if process.platform is 'win32'
          shell = path.resolve(process.env.SystemRoot, 'WindowsPowerShell', 'v1.0', 'powershell.exe')
        else
          shell = '/bin/sh'

  ptyProcess = pty.fork shell, args,
    name: 'xterm-256color'
    cols: cols
    rows: rows
    cwd: ptyCwd
    env: process.env

  ptyProcess.on 'data', (data) ->
    emit('term3:data', new Buffer(data).toString("base64"))

  ptyProcess.on 'exit', ->
    emit('term3:exit')
    callback()

  process.on 'message', ({event, cols, rows, text}={}) ->
    switch event
      when 'resize' then ptyProcess.resize(cols, rows)
      when 'input' then ptyProcess.write(new Buffer(text, "base64").toString("utf-8"))

# from atom/terminal to reduce cpu usage

pty = require 'ptyw.js'

module.exports = (ptyCwd, sh, cols, rows, args) ->
  callback = @async()
  if sh
      shell = sh
  else
      shell = process.env.SHELL
      if not shell
        # Try to salvage some sort of shell to execute. Horrible code below.
        path = require 'path'
        if process.platform is 'win32'
          shell = path.resolve(process.env.SystemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe')
        else
          shell = '/bin/sh'

  ptyProcess = pty.fork shell, args,
    name: 'xterm-256color'
    cols: cols
    rows: rows
    cwd: ptyCwd
    env: process.env

  ptyProcess.on 'data', (data) ->
    emit('term3:data', new Buffer(data).toString("base64"))

  ptyProcess.on 'exit', ->
    emit('term3:exit')
    callback()

  process.on 'message', ({event, cols, rows, text}={}) ->
    switch event
      when 'resize' then ptyProcess.resize(cols, rows)
      when 'input' then ptyProcess.write(new Buffer(text, "base64").toString("utf-8"))

Fix default path to powershell.

Fix default path to powershell.


{View} = require 'atom'
module.exports =
class MinimapSelectionView extends View
  decorations: []
  @content: ->
    @div class: 'minimap-selection'

  initialize: (@minimapView) ->
    @subscribe @minimapView.editorView, "selection:changed", @handleSelection
    @subscribe @minimapView.editorView, "cursor-added", @handleSelection
    @subscribe @minimapView.editorView, "cursor-moved", @handleSelection
    @subscribe @minimapView.editorView, "cursor-removed", @handleSelection

  attach: ->
    @minimapView.miniUnderlayer.append(this)
    @handleSelection()

  destroy: ->
    @detach()
    @unsubscribe()
    @minimapView = null

  handleSelection: =>
    @removeDecorations()

    {editor} = @minimapView

    return if editor.getSelections().length is 1 and editor.getLastSelection().isEmpty()

    for selection in editor.getSelections()
      @decorations.push @minimapView.decorateMarker(selection.marker, type: 'highlight-under', scope: '.editor .selection .region')

  removeDecorations: ->
    return if @decorations.length is 0
    decoration.destroy() for decoration in @decorations
    @decorations = []

{View} = require 'atom'
module.exports =
class MinimapSelectionView extends View
  decorations: []
  @content: ->
    @div class: 'minimap-selection'

  initialize: (@minimapView) ->
    @subscribe @minimapView.editorView, "selection:changed", @handleSelection
    @subscribe @minimapView.editorView, "cursor-added", @handleSelection
    @subscribe @minimapView.editorView, "cursor-moved", @handleSelection
    @subscribe @minimapView.editorView, "cursor-removed", @handleSelection

  attach: ->
    @minimapView.miniUnderlayer.append(this)
    @handleSelection()

  destroy: ->
    @detach()
    @unsubscribe()
    @minimapView = null

  handleSelection: =>
    @removeDecorations()

    {editor} = @minimapView

    return if editor.getSelections().length is 1 and editor.getLastSelection().isEmpty()

    for selection in editor.getSelections()
      @decorations.push @minimapView.decorateMarker(selection.marker, type: 'highlight-under', scope: '.minimap .editor .selection .region')

  removeDecorations: ->
    return if @decorations.length is 0
    decoration.destroy() for decoration in @decorations
    @decorations = []

Add a minimap class to the decoration scope

It’ll allow to have a specific style for the minimap selection


'scopeName': 'source.vp'
'name': 'Valkyrie Profile'
'fileTypes': ['vp']
'patterns': [
	{
		'match': '^[^\t]*$',
		'name': 'entity name function vp'
	}
]

'scopeName': 'source.vp'
'name': 'Valkyrie Profile'
'fileTypes': ['vp']
'patterns': [
	{
		'match': '^[^\t]*$',
		'name': 'entity name function vp'
	},
	{
		'match': '^\t{2}.*$'
		'name': 'constant language vp'
	}
]

Update grammar to take Windows into account


EventEmitter = require('events').EventEmitter

class Debug extends EventEmitter
  ###
    @options    Which debug details should be sent.
                  data    - dump of packet data
                  payload - details of decoded payload
  ###
  constructor: (@options) ->
    @options = @options || {}
    @options.data = @options.data || false
    @options.payload = @options.payload || false
    @options.token = @options.token || false

    @indent = '  '

  packet: (direction, packet) ->
    if @haveListeners() && @options.packet
      @log('')
      @log(direction)
      @log(packet.headerToString(@indent))

  data: (packet) ->
    if @haveListeners() && @options.data
      @log(packet.dataToString(@indent))

  payload: (generatePayloadText) ->
    if @haveListeners() && @options.payload
      @log(generatePayloadText())

  token: (token) ->
    if @haveListeners() && @options.token
      @log(token)

  # Only incur the overhead of producing formatted messages when necessary.
  haveListeners: ->
    @listeners('debug').length > 0

  log: (text) ->
    @emit('debug', text)

module.exports = Debug

EventEmitter = require('events').EventEmitter

class Debug extends EventEmitter
  ###
    @options    Which debug details should be sent.
                  data    - dump of packet data
                  payload - details of decoded payload
  ###
  constructor: (@options) ->
    @options = @options || {}
    @options.data = @options.data || false
    @options.payload = @options.payload || false
    @options.packet = @options.packet || false
    @options.token = @options.token || false

    @indent = '  '

  packet: (direction, packet) ->
    if @haveListeners() && @options.packet
      @log('')
      @log(direction)
      @log(packet.headerToString(@indent))

  data: (packet) ->
    if @haveListeners() && @options.data
      @log(packet.dataToString(@indent))

  payload: (generatePayloadText) ->
    if @haveListeners() && @options.payload
      @log(generatePayloadText())

  token: (token) ->
    if @haveListeners() && @options.token
      @log(token)

  # Only incur the overhead of producing formatted messages when necessary.
  haveListeners: ->
    @listeners('debug').length > 0

  log: (text) ->
    @emit('debug', text)

module.exports = Debug

Set a default for Connection's config.options.default .


#= require almond

window.ttm ||= {}

ttm.ClassMixer = (klass)->
  klass.build = ->
    it = new klass
    it.initialize && it.initialize.apply(it, arguments)
    it
  klass.prototype.klass = klass
  klass

define "lib/class_mixer", ->
  return ttm.ClassMixer

#= require almond

window.ttm ||= {}
window.decorators ||= {}

ttm.ClassMixer = (klass)->
  klass.build = ->
    it = new klass
    it.initialize && it.initialize.apply(it, arguments)
    it
  klass.prototype.klass = klass
  klass

define "lib/class_mixer", ->
  return ttm.ClassMixer

WIP: Implement decorator pattern for student dashboard.


require 'coffee-cache'
jasmine.getEnv().addEqualityTester(require('underscore-plus').isEqual)
require('grim').includeDeprecatedAPIs = false

Exclude deprecated APIs when running specs


AllAboard.SlidesRoute = Em.Route.extend
  model: ->
    # FIXME: So this is great, except that it doesn't work on a Board that was
    # created by the app while that same instance is still running.  It just
    # sits and hangs resolving the model.
    @modelFor("board").get("slides")

  afterModel: (slides) ->
    if slides.get("length") > 0
      @transitionTo("slide", @modelFor("board"), slides.get("firstObject"))

AllAboard.SlidesRoute = Em.Route.extend
  model: ->
    @modelFor("board").get("slides")

  afterModel: (slides) ->
    if slides.get("length") > 0
      @transitionTo("slide", @modelFor("board"), slides.get("firstObject"))

Remove a FIXME that got fixed with data beta3.


exports.config =
  files:
    javascripts:
      joinTo:
        'js/app.js': /^(vendor|bower_components|app)/

    stylesheets:
      joinTo: 'css/app.css'

    templates:
      joinTo: 'js/app.js'

  plugins:
    jshint:
      pattern: /^app\/.*\.js$/
      options:
        curly: true
        bitwise: true
      globals:
        _: true
        jQuery: true
        Zeppelin: true
        Backbone: true
      warnOnly: true

    handlebars:
      include:
        enabled: false

exports.config =
  files:
    javascripts:
      joinTo:
        'js/app.js': /^(vendor|bower_components|app)/

    stylesheets:
      joinTo: 'css/app.css'

    templates:
      joinTo: 'js/app.js'

  plugins:
    handlebars:
      include:
        enabled: false

Use .jshintrc file instead of brunch jshint plugin configurations.


# Utility functions ---------------------------------------

extend = (dest, src) ->
  dest[name] = src[name] for name in src when src[name] and !dest[name]
  dest

# Utility functions ---------------------------------------

extend = (dest, src) ->
  dest[key] = value for key, value of src when !dest[key]
  dest

Fix bug (with my understanding of CoffeeScript)


NotificationPlugin = require "../../notification-plugin"

class Email extends NotificationPlugin
  SIDEKIQ_WORKER = "ErrorEmailWorker"
  SIDEKIQ_QUEUE = "error_emails"

  @receiveEvent: (config, event, callback) ->
    sidekiq = config.sidekiq
    delete config.sidekiq

    sidekiq.enqueue SIDEKIQ_WORKER, [event.trigger.type, event.error.id, config, event.trigger.message],
      retry: false
      queue: SIDEKIQ_QUEUE

    callback null

module.exports = Email

NotificationPlugin = require "../../notification-plugin"

class Email extends NotificationPlugin
  SIDEKIQ_WORKER = "ErrorEmailWorker"
  SIDEKIQ_QUEUE = "error_emails"

  @receiveEvent: (config, event, callback) ->
    sidekiq = config.sidekiq
    delete config.sidekiq

    sidekiq.enqueue SIDEKIQ_WORKER, [event.trigger.type, event.error.id, config, event.trigger.message, event],
      retry: false
      queue: SIDEKIQ_QUEUE

    callback null

module.exports = Email

Send entire payload to Sidekiq

Send entire payload to Sidekiq


React = require 'react'
AutoSave = require '../../components/auto-save'
handleInputChange = require '../../lib/handle-input-change'

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

  getDefaultProps: ->
    project: {}

  render: ->

This page is for you to describe your research motivations and goals to the volunteers. Feel free to add detail, but try to avoid jargon. This page renders markdown, so you can format it and add images (externally hosted for now) and links. The site will show your team members with their profile pictures and roles to the side of the text.

        <AutoSave resource={@props.project}>
          Science case

          <textarea className="standard-input full" name="science_case" value={@props.project.science_case} rows="20" onChange={handleInputChange.bind @props.project} />
        </AutoSave>

React = require 'react'
AutoSave = require '../../components/auto-save'
handleInputChange = require '../../lib/handle-input-change'

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

  getDefaultProps: ->
    project: {}

  render: ->

This page is for you to describe your research motivations and goals to the volunteers. Feel free to add detail, but try to avoid jargon. This page renders markdown, so you can format it and add images via the Media Library and links. The site will show your team members with their profile pictures and roles to the side of the text.

        <AutoSave resource={@props.project}>
          Science case

          <textarea className="standard-input full" name="science_case" value={@props.project.science_case} rows="20" onChange={handleInputChange.bind @props.project} />
        </AutoSave>

Update science page help text

Update science page help text


'use strict'

Sprangular.directive 'checkoutButton', ->
  restrict: 'E'
  scope:
    user: '='
  templateUrl: 'directives/checkout_button.html'
  controller: ($scope, $location, Cart, Checkout) ->
    $scope.allowOneClick = false
    $scope.processing = false

    $scope.$watch 'user', (user) ->
      $scope.allowOneClick = user.allowOneClick

    $scope.standardCheckout = ->
      $scope.processing = true
      $location.path("/checkout")

    $scope.oneClickCheckout = ->
      $scope.processing = true

      order = Cart.current
      user = $scope.user

      order.resetAddresses(user)
      order.resetCreditCard(user)

      Checkout.update('payment')
        .then ->
          $location.path('/checkout')

    $scope.checkout = ->
      if $scope.allowOneClick
        $scope.oneClickCheckout()
      else
        $scope.standardCheckout()

'use strict'

Sprangular.directive 'checkoutButton', ->
  restrict: 'E'
  scope:
    user: '='
  templateUrl: 'directives/checkout_button.html'
  controller: ($scope, $location, Cart, Checkout) ->
    $scope.processing = false

    $scope.checkout = ->
      $scope.processing = true
      $location.path("/checkout")

Refactor checkout button to remove one-click-logic (checkout ctrl already doest it)


# 1. Don't define a test with null as one of the options
# 2. If you change a test's options, you must also change the test's name
#
# You can add overrides, which will set the option if override_p returns true.

exports = this

exports.ab_test_definitions =
  options:
    quick_setup_or_trial: ["14-day free trial.", "Run your first test with 3 clicks."]
    github_warning_modal: [true, false]
    home_red_buttons: [true, false]
    home_text_hero: [true, false]
    show_add_repos_blank_slate: [true, false] # true shows the show_add_repos_blank_slate div, false hides it
    home_extra_ctas: [true, false]
    home_cta_plan_price_trial: [true, false] # around the home button CTA, include text about plans starting from $19, and 14 day trial
    number_one_instead_of_happy_customers_callout: [true, false]
    home_speed_level: ["4-way", "8-way", "12-way"]

  overrides:
    [
      override_p: ->
        window.circleEnvironment is 'test'
      options:
        github_warning_modal: false
    ]

# 1. Don't define a test with null as one of the options
# 2. If you change a test's options, you must also change the test's name
#
# You can add overrides, which will set the option if override_p returns true.

exports = this

exports.ab_test_definitions =
  options:
    github_warning_modal: [true, false]
    home_red_buttons: [true, false]
    home_text_hero: [true, false]
    show_add_repos_blank_slate: [true, false] # true shows the show_add_repos_blank_slate div, false hides it
    home_extra_ctas: [true, false]
    home_cta_plan_price_trial: [true, false] # around the home button CTA, include text about plans starting from $19, and 14 day trial
    number_one_instead_of_happy_customers_callout: [true, false]
    home_speed_level: ["4-way", "8-way", "12-way"]

  overrides:
    [
      override_p: ->
        window.circleEnvironment is 'test'
      options:
        github_warning_modal: false
    ]

Remove quick-test-or-trial, it's not used.


module.exports = (grunt) ->
	grunt.initConfig
		clean:
			package: ['lib']
		coffee:
			options:
				bare: true
			files:
				expand: true
				cwd: 'src'
				src: ['**/*.coffee']
				dest: 'lib'
				ext: '.js'
		mochacli:
			options:
				compilers: ['coffee:coffee-script/register']
			files: ['test/brave-mouse']

	grunt.loadNpmTasks 'grunt-contrib-clean'
	grunt.loadNpmTasks 'grunt-contrib-coffee'
	grunt.loadNpmTasks 'grunt-mocha-cli'

	grunt.registerTask 'build', ['clean:package', 'coffee']
	grunt.registerTask 'default', []

module.exports = (grunt) ->
	grunt.initConfig
		clean:
			package: ['lib']
		coffee:
			options:
				bare: true
			files:
				expand: true
				cwd: 'src'
				src: ['**/*.coffee']
				dest: 'lib'
				ext: '.js'
		mochacli:
			options:
				compilers: ['coffee:coffee-script/register']
			files: ['test/brave-mouse']

	grunt.loadNpmTasks 'grunt-contrib-clean'
	grunt.loadNpmTasks 'grunt-contrib-coffee'
	grunt.loadNpmTasks 'grunt-mocha-cli'

	grunt.registerTask 'build', ['clean:package', 'coffee']
	grunt.registerTask 'test', ['mochacli']
	grunt.registerTask 'default', []

Add Grunt task alias test

Add Grunt task alias test


module.exports = class Filterable
  {secure, ObjectId} = require 'bongo'
  {permit} = require '../models/group/permissionset'

  @findSuggestions = ()->
    throw new Error "Filterable must implement static method findSuggestions!"

  @byRelevance = (client, seed, options, callback)->
    [callback, options] = [options, callback] unless callback
    {limit, blacklist, skip}  = options
    limit     ?= 10
    blacklist or= []
    blacklist = blacklist.map(ObjectId)
    cleanSeed = seed.replace(/[^\w\s-]/).trim() #TODO: this is wrong for international charsets
    startsWithSeedTest = RegExp '^'+cleanSeed, "i"
    startsWithOptions = {limit, blacklist, skip}
    @findSuggestions client, startsWithSeedTest, startsWithOptions, (err, suggestions)=>
      if err
        callback err
      else if limit is suggestions.length
          callback null, suggestions
      else
        containsSeedTest = RegExp cleanSeed, 'i'
        containsOptions =
          skip      : skip
          limit     : limit-suggestions.length
          blacklist : blacklist.concat(suggestions.map (o)-> o.getId())
        @findSuggestions client, containsSeedTest, containsOptions, (err, moreSuggestions)->
          if err
            callback err
          else
            allSuggestions = suggestions.concat moreSuggestions
            callback null, allSuggestions

  @byRelevance$ = permit 'query collection',
    success: (client, seed, options, callback)->
      @byRelevance client, seed, options, callback

module.exports = class Filterable
  {secure, ObjectId} = require 'bongo'
  {permit} = require '../models/group/permissionset'

  @findSuggestions = ()->
    throw new Error "Filterable must implement static method findSuggestions!"

  @byRelevance = (client, seed, options, callback)->
    [callback, options] = [options, callback] unless callback
    {limit, blacklist, skip}  = options
    limit     ?= 10
    blacklist or= []
    blacklist = blacklist.map(ObjectId)
    cleanSeed = seed.replace(/[^\w\s-]/).trim() #TODO: this is wrong for international charsets
    startsWithSeedTest = RegExp '^'+cleanSeed, "i"
    startsWithOptions = {limit, blacklist, skip}
    @findSuggestions client, startsWithSeedTest, startsWithOptions, (err, suggestions)=>
      if err
        callback err
      else if suggestions and limit is suggestions.length
        callback null, suggestions
      else
        containsSeedTest = RegExp cleanSeed, 'i'
        containsOptions =
          skip      : skip
          limit     : limit-suggestions.length
          blacklist : blacklist.concat(suggestions.map (o)-> o.getId())
        @findSuggestions client, containsSeedTest, containsOptions, (err, moreSuggestions)->
          if err
            callback err
          else
            allSuggestions = suggestions.concat moreSuggestions
            callback null, allSuggestions

  @byRelevance$ = permit 'query collection',
    success: (client, seed, options, callback)->
      @byRelevance client, seed, options, callback

Make sure there are suggestions

Filterable: Make sure there are suggestions


#! /usr/bin/env coffee

args      = require('../src/args').camelized 'template'
templates = require '../src/templates'

templates.show args.template

#! /usr/bin/env coffee

args      = require '../src/args'
templates = require '../src/templates'

args.withDefaultOptions('template')
  .option 'show',
    alias:    's'
    describe: 'Print the raw template jade and data to the console'
    type:     'boolean'

  .option 'new',
    alias:       'n'
    describe:    'Create a new template with the provided name'
    requiresArg: true
    type:        'string'

  .option 'delete',
    alias:       'n'
    describe:    'Delete a reference to a template (not the files themselves)'
    requiresArg: true
    type:        'string'

templates.show args.camelize().template

Add (unimplemented) options to template command


# Encapsulate the cable connection held by the consumer. This is an internal class not intended for direct user manipulation.
class Cable.Connection
  constructor: (@consumer) ->
    @open()

  send: (data) ->
    if @isOpen()
      @webSocket.send(JSON.stringify(data))
      true
    else
      false

  open: ->
    if @isOpen()
      throw new Error("Must close existing connection before opening")
    else
      @webSocket = new WebSocket(@consumer.url)
      @installEventHandlers()

  close: ->
    @webSocket?.close()

  reopen: ->
    @close()
    @open()

  isOpen: ->
    @isState("open")

  # Private

  isState: (states...) ->
    @getState() in states

  getState: ->
    return state.toLowerCase() for state, value of WebSocket when value is @webSocket?.readyState
    null

  installEventHandlers: ->
    for eventName of @events
      handler = @events[eventName].bind(this)
      @webSocket["on#{eventName}"] = handler

  events:
    message: (event) ->
      {identifier, message} = JSON.parse(event.data)
      @consumer.subscriptions.notify(identifier, "received", message)

    open: ->
      @disconnected = false
      @consumer.subscriptions.reload()

    close: ->
      @disconnect()

    error: ->
      @disconnect()

  disconnect: ->
    return if @disconnected
    @disconnected = true
    @consumer.subscriptions.notifyAll("disconnected")

  toJSON: ->
    state: @getState()

# Encapsulate the cable connection held by the consumer. This is an internal class not intended for direct user manipulation.
class Cable.Connection
  constructor: (@consumer) ->
    @open()

  send: (data) ->
    if @isOpen()
      @webSocket.send(JSON.stringify(data))
      true
    else
      false

  open: =>
    if @webSocket and not @isState("closed")
      throw new Error("Existing connection must be closed before opening")
    else
      @webSocket = new WebSocket(@consumer.url)
      @installEventHandlers()
      true

  close: ->
    @webSocket?.close()

  reopen: ->
    @close()
    @open()

  isOpen: ->
    @isState("open")

  # Private

  isState: (states...) ->
    @getState() in states

  getState: ->
    return state.toLowerCase() for state, value of WebSocket when value is @webSocket?.readyState
    null

  installEventHandlers: ->
    for eventName of @events
      handler = @events[eventName].bind(this)
      @webSocket["on#{eventName}"] = handler

  events:
    message: (event) ->
      {identifier, message} = JSON.parse(event.data)
      @consumer.subscriptions.notify(identifier, "received", message)

    open: ->
      @disconnected = false
      @consumer.subscriptions.reload()

    close: ->
      @disconnect()

    error: ->
      @disconnect()

  disconnect: ->
    return if @disconnected
    @disconnected = true
    @consumer.subscriptions.notifyAll("disconnected")

  toJSON: ->
    state: @getState()

Improve guard against opening multiple web sockets


{Emitter, CompositeDisposable} = require 'event-kit'

# The display for a selected object. i.e. the red or blue outline around the
# selected object.
#
# It basically cops the underlying object's attributes (path definition, etc.)
module.exports =
class ObjectSelection
  constructor: (@svgDocument, @options={}) ->
    @emitter = new Emitter
    @options.class ?= 'object-selection'

  on: (args...) -> @emitter.on(args...)

  setObject: (object) ->
    @_unbindObject()
    old = object
    @object = object
    @_bindObject(@object)

    @trackingObject.remove() if @trackingObject
    @trackingObject = null
    if @object
      @trackingObject = @object.cloneElement(@svgDocument)
      @trackingObject.node.setAttribute('class', @options.class + ' invisible-to-hit-test')
      @svgDocument.getToolLayer().add(@trackingObject)
      @trackingObject.back()
      @render()
    @emitter.emit 'change:object', {objectSelection: this, @object, old}

  render: =>
    @object.render(@trackingObject)

  _bindObject: (object) ->
    return unless object
    @selectedObjectSubscriptions = new CompositeDisposable
    @selectedObjectSubscriptions.add object.on('change', @render)

  _unbindObject: ->
    @selectedObjectSubscriptions?.dispose()
    @selectedObjectSubscriptions = null

{CompositeDisposable} = require 'event-kit'

# The display for a selected object. i.e. the red or blue outline around the
# selected object.
#
# It basically cops the underlying object's attributes (path definition, etc.)
module.exports =
class ObjectSelection
  constructor: (@svgDocument, @options={}) ->
    @options.class ?= 'object-selection'

  setObject: (object) ->
    return if object is @object
    @_unbindObject()
    @object = object
    @_bindObject(@object)

    @trackingObject.remove() if @trackingObject
    @trackingObject = null
    if @object
      @trackingObject = @object.cloneElement(@svgDocument)
      @trackingObject.node.setAttribute('class', @options.class + ' invisible-to-hit-test')
      @svgDocument.getToolLayer().add(@trackingObject)
      @trackingObject.back()
      @render()
    return

  render: =>
    @object.render(@trackingObject)

  _bindObject: (object) ->
    return unless object
    @selectedObjectSubscriptions = new CompositeDisposable
    @selectedObjectSubscriptions.add object.on('change', @render)

  _unbindObject: ->
    @selectedObjectSubscriptions?.dispose()
    @selectedObjectSubscriptions = null

Remove event junk from the obj selection


Promise = require 'bluebird-q'
Backbone = require 'backbone'
template = -> require('./index.jade') arguments...

module.exports = class ChannelCreateView extends Backbone.View
  events:
    'mouseover': 'focus'
    'input .js-title': 'title'
    'click .js-status': 'status'
    'click .js-create': 'create'

  initialize: ({ @user }) ->
    @listenTo @model, 'change', @render

  focus: ->
    @dom.title.focus()

  title: ->
    @model.set 'title', @dom.title.val(), silent: true

  status: (e) ->
    e.preventDefault()

    status = $(e.currentTarget).data 'value'
    @model.set 'status', status

  create: (e) ->
    e.preventDefault()

    return unless @model.has('title')

    @dom.create.text 'Creating...'

    Promise(@model.save())
      .then =>
        @dom.create.text 'Redirecting...'
        window.location.href = "/#{@model.get('user').slug}/#{@model.get('slug')}"

      .catch =>
        @dom.create.text 'Error'

  render: ->
    @$el.html template
      user: @user
      channel: @model

    @dom =
      title: @$('.js-title')
      create: @$('.js-create')

    @focus()

    this

Promise = require 'bluebird-q'
Backbone = require 'backbone'
template = -> require('./index.jade') arguments...

module.exports = class ChannelCreateView extends Backbone.View
  events:
    'mouseover': 'focus'
    'keyup .js-title': 'onKeyup'
    'input .js-title': 'title'
    'click .js-status': 'status'
    'click .js-create': 'create'

  initialize: ({ @user }) ->
    @listenTo @model, 'change', @render

  onKeyup: (e) ->
    return unless e.keyCode is 13
    @create e

  focus: ->
    @dom.title.focus()

  title: ->
    @model.set 'title', @dom.title.val(), silent: true

  status: (e) ->
    e.preventDefault()

    status = $(e.currentTarget).data 'value'
    @model.set 'status', status

  create: (e) ->
    e.preventDefault()

    return unless @model.has('title')

    @dom.create.text 'Creating...'

    Promise(@model.save())
      .then =>
        @dom.create.text 'Redirecting...'
        window.location.href = "/#{@model.get('user').slug}/#{@model.get('slug')}"

      .catch =>
        @dom.create.text 'Error'

  render: ->
    @$el.html template
      user: @user
      channel: @model

    @dom =
      title: @$('.js-title')
      create: @$('.js-create')

    @focus()

    this

Handle <enter> for creating channels

Handle <enter> for creating channels


# Like sands through the hourglass, so are the days of our lives.
startTime = Date.now()

require './window'

Atom = require './atom'
window.atom = Atom.loadOrCreate('editor')
atom.initialize()
atom.startEditorWindow()
window.atom.loadTime = Date.now() - startTime
console.log "Window load time: #{atom.getWindowLoadTime()}ms"

# Workaround for focus getting cleared upon window creation
windowFocused = (e) ->
  window.removeEventListener('focus', windowFocused)
  setTimeout (-> document.querySelector('.workspace').focus()), 0
window.addEventListener('focus', windowFocused)

Apply workaround for clearing of focus upon loading of window

After the first window focus event, the focus is getting cleared back
to document.body regardless of the prior active element. Refocusing
workspace on a delay after the first window focus event works around
the problem.


class NavigationList extends KDListView

  constructor:->
    super

    @viewWidth = 70

    @on 'ItemWasAdded', (view)=>

      view.once 'viewAppended', =>

        view._index ?= @getItemIndex view
        view.setX view._index * @viewWidth
        @_width = @viewWidth * @items.length

      lastChange = 0

      view.on 'DragInAction', (x, y)=>

        return  if x + view._x > @_width or x + view._x < 0

        if x > @viewWidth
          current = Math.floor x / @viewWidth
        else if x < -@viewWidth
          current = Math.ceil  x / @viewWidth
        else
          current = 0

        if current > lastChange
          @moveItemsTemporarily view, 1
          lastChange = current
        else if current < lastChange
          @moveItemsTemporarily view, -1
          lastChange = current

      view.on 'DragFinished', =>
        view.setX view._index * @viewWidth
        @moveItemToIndex view, view._index
        item._index = i for item, i in @items
        lastChange  = 0

  moveItemsTemporarily:(view, step)->

    newIndex = Math.max(0, Math.min(view._index + step, @items.length-1))
    return  if newIndex is view._index

    for item, index in @items
      if item._index is newIndex
        item.setX item.getRelativeX() - (step * @viewWidth)
        item._index = view._index
        view._index = newIndex
        break

class NavigationList extends KDListView

  constructor:->
    super

    @viewWidth = 70

    @on 'ItemWasAdded', (view)=>

      view.once 'viewAppended', =>

        view._index ?= @getItemIndex view
        view.setX view._index * @viewWidth
        @_width = @viewWidth * @items.length

      lastChange = 0

      view.on 'DragInAction', (x, y)=>

        return  if x + view._x > @_width or x + view._x < 0

        if x > @viewWidth
          current = Math.floor x / @viewWidth
        else if x < -@viewWidth
          current = Math.ceil  x / @viewWidth
        else
          current = 0

        if current > lastChange
          @moveItemToIndex view, view._index+1
          lastChange = current
        else if current < lastChange
          @moveItemToIndex view, view._index-1
          lastChange = current

      view.on 'DragFinished', =>
        view.setX view._index * @viewWidth
        view.setY view._y
        lastChange  = 0

  moveItemToIndex:(item, index)->
    super item, index

    for _item, index in @items
      _item._index = index
      _item.setX index * @viewWidth  unless item is _item

Fix DND, simple is better

NavigationList: Fix DND, simple is better


#= require lazysizes/plugins/rias/ls.rias
#= require lazysizes/plugins/bgset/ls.bgset
#= require lazysizes/plugins/unload/ls.unload
#= require lazysizes

#= require lazysizes/plugins/rias/ls.rias
#= require lazysizes/plugins/bgset/ls.bgset
#= require lazysizes/plugins/respimg/ls.respimg
#  require lazysizes/plugins/custommedia/ls.custommedia
#= require lazysizes

Revert "remove respimg plugin and add unload plugin"

This reverts commit f3fd6164a86c2ac25ee4156ee0adb232403d1856.


mid = new Date('02/25/14 00:00:00')
Template.agenda.day1 = ->
  @items.filter((i) -> i.time < mid).sort((a, b) -> a.time >= b.time)

Template.agenda.day2 = ->
  @items.filter((i) -> i.time > mid).sort((a, b) -> a.time >= b.time)

Template.agenda.canSee = ->
  u = User.current()
  u and (u.admin() or u.moderator())

Template.agenda.canEdit = ->
  u = User.current()
  u and u.admin()

Template.agenda.events
  'submit #add-form': (event, context) ->
    event.preventDefault()
    time = context.find('#time').value
    class1 = context.find('#class1').value
    icon1 = context.find('#icon1').value
    class2 = context.find('#class2').value
    icon2 = context.find('#icon2').value
    class3 = context.find('#class3').value
    icon3 = context.find('#icon3').value
    if not time then return
    time = new Date(time)
    if not time
      console.error('bad time string')
      return
    AgendaItem.create
      time: time
      class1: class1
      class2: class2
      class3: class3
      icon1: icon1
      icon2: icon2
      icon3: icon3

  'click #edit': ->
    Session.set('editingAgenda', not Session.get('editingAgenda'))

mid = new Date('02/25/14 00:00:00')
Template.agenda.day1 = ->
  @items.filter((i) -> i.time < mid).sort((a, b) -> a.time > b.time)

Template.agenda.day2 = ->
  @items.filter((i) -> i.time > mid).sort((a, b) -> a.time > b.time)

Template.agenda.canSee = ->
  u = User.current()
  u and (u.admin() or u.moderator())

Template.agenda.canEdit = ->
  u = User.current()
  u and u.admin()

Template.agenda.events
  'submit #add-form': (event, context) ->
    event.preventDefault()
    time = context.find('#time').value
    class1 = context.find('#class1').value
    icon1 = context.find('#icon1').value
    class2 = context.find('#class2').value
    icon2 = context.find('#icon2').value
    class3 = context.find('#class3').value
    icon3 = context.find('#icon3').value
    if not time then return
    time = new Date(time)
    if not time
      console.error('bad time string')
      return
    AgendaItem.create
      time: time
      class1: class1
      class2: class2
      class3: class3
      icon1: icon1
      icon2: icon2
      icon3: icon3

  'click #edit': ->
    Session.set('editingAgenda', not Session.get('editingAgenda'))

Change sort to use > and not >=

Change sort to use > and not >=


Sprangular.service "Orders", ($http) ->

  service =
    find: (number) ->
      $http.get("/api/orders/#{number}")
        .then (response) ->
          order = new Sprangular.Order
          order.load(response.data)

  service

Sprangular.service "Orders", ($http) ->

  service =
    find: (number) ->
      $http.get("/api/orders/#{number}")
        .then (response) ->
          Sprangular.extend(response.data, Sprangular.Order)

  service

Refactor loading of order in Orders.find()


define ->
  class Biskoto
    encode = encodeURIComponent
    decode = decodeURIComponent

    @get: (name) ->
      if document.cookie
        cookies = decode(document.cookie).split(/;\s/g)

        for cookie in cookies
          if cookie.indexOf(name) is 0
            try
              return JSON.parse(cookie.split('=')[1])
            catch err
              return cookie.split('=')[1]

      return null

    @set: (name, value, options = {}) ->
      document.cookie = "#{name}=#{JSON.stringify(value)}#{@_cookieOptions(options)}"

    @expire: (name, options = {}) ->
      options.expires = -1
      @set(name, '', options)

    ###
      options = {
        expires : Integer (seconds)
        secure  : Boolean
        domain  : String
        path    : String
      }
    ###
    @_cookieOptions: (options = {}) ->
      cookie_str = ''

      for key, value of options
        if key is 'expires'
          cookie_str += "; expires=#{@_createExpireDate(options.expires)}"
        else if key is 'domain'
          cookie_str += "; domain=#{value}"

      cookie_str += "; path=#{if options.path then options.path else '/'}"

    @_createExpireDate: (seconds) ->
      new Date(
        +new Date() + (seconds * 1000)
      ).toUTCString()

  window.Biskoto = Biskoto
  return Biskoto

define ->
  class Biskoto
    encode = encodeURIComponent
    decode = decodeURIComponent

    @get: (name) ->
      if document.cookie
        cookies = document.cookie.split(/;\s/g)

        for cookie in cookies
          if cookie.indexOf(name) is 0
            value = decode(cookie.split('=')[1])
            try
              return JSON.parse(value)
            catch err
              return value

      return null

    @set: (name, value, options = {}) ->
      document.cookie = "#{name}=#{encode( JSON.stringify(value) )}#{@_cookieOptions(options)}"

    @expire: (name, options = {}) ->
      options.expires = -1
      @set(name, '', options)

    ###
      options = {
        expires : Integer (seconds)
        secure  : Boolean
        domain  : String
        path    : String
      }
    ###
    @_cookieOptions: (options = {}) ->
      cookie_str = ''

      for key, value of options
        if key is 'expires'
          cookie_str += "; expires=#{@_createExpireDate(options.expires)}"
        else if key is 'domain'
          cookie_str += "; domain=#{value}"

      cookie_str += "; path=#{if options.path then options.path else '/'}"

    @_createExpireDate: (seconds) ->
      new Date(
        +new Date() + (seconds * 1000)
      ).toUTCString()

  return Biskoto

Fix Biskoto to be compatible with PhantomJS's escaping of cookies


noflo = require 'noflo'

class ConvertStreetLight extends noflo.Component
  icon: 'filter'
  description: 'Convert street light RGB values so we can send them to PWM'

  constructor: ->
    @inPorts = new noflo.InPorts
      colors:
        datatype: 'array'
        description: 'Street light values'
    @outPorts = new noflo.OutPorts
      street1:
        datatype: 'int'
        addressable: true
      street2:
        datatype: 'int'
        addressable: true
      street3:
        datatype: 'int'
        addressable: true
      street4:
        datatype: 'int'
        addressable: true

    @inPorts.colors.on 'data', (data) =>
      @convert data

  convertLight: (light, colors) ->
    for color, idx in colors
      @outPorts.ports["street#{light}"].send color, idx

  convert: (data) ->
    return unless data.length is 4
    for light, idx in data
      @convertLight idx+1, light

exports.getComponent = -> new ConvertStreetLight

noflo = require 'noflo'

class ConvertStreetLight extends noflo.Component
  icon: 'filter'
  description: 'Convert street light RGB values so we can send them to PWM'

  constructor: ->
    @inPorts = new noflo.InPorts
      colors:
        datatype: 'array'
        description: 'Street light values'
    @outPorts = new noflo.OutPorts
      street1:
        datatype: 'int'
        addressable: true
      street2:
        datatype: 'int'
        addressable: true
      street3:
        datatype: 'int'
        addressable: true
      street4:
        datatype: 'int'
        addressable: true

    @inPorts.colors.on 'data', (data) =>
      @convert data

  convertLight: (light, colors) ->
    for color, idx in colors
      continue unless @outPorts.ports["street#{light}"].isAttached idx
      @outPorts.ports["street#{light}"].send color, idx

  convert: (data) ->
    return unless data.length is 4
    for light, idx in data
      @convertLight idx+1, light

exports.getComponent = -> new ConvertStreetLight

Allow street lights to be not connected

Allow street lights to be not connected


"*":
  core:
    ignoredNames: [
      ".DS_Store"
      ".git"
      "vendor"
    ]
    disabledPackages: [
      "center-line"
      "branch-status"
      "open-in-atom"
      "terminal"
    ]
    themes: [
      "atom-dark-ui"
      "solarized-dark-syntax"
    ]
    destroyEmptyPanes: false
  editor:
    fontSize: 13
    autoIndentOnPaste: false
    scrollPastEnd: true
    invisibles: {}
  "spell-check":
    grammars: [
      "text.plain"
      "source.gfm"
      "text.git-commit"
    ]
  welcome:
    showOnStartup: false
  "find-and-replace":
    openProjectFindResultsInRightPane: true
  "wrap-guide":
    columns: [
      {
        pattern: "COMMIT_EDITMSG"
        column: 72
      }
    ]
  autocomplete:
    includeCompletionsFromAllBuffers: true
  "merge-conflicts":
    gitPath: "bin/git"
  "one-light-ui":
    layoutMode: "Compact"
  "one-dark-ui":
    layoutMode: "Compact"

"*":
  core:
    ignoredNames: [
      ".DS_Store"
      ".git"
      "vendor"
    ]
    disabledPackages: [
      "center-line"
      "branch-status"
      "open-in-atom"
      "terminal"
    ]
    themes: [
      "atom-dark-ui"
      "solarized-dark-syntax"
    ]
    destroyEmptyPanes: false
  editor:
    fontSize: 13
    autoIndentOnPaste: false
    scrollPastEnd: true
    invisibles: {}
  "spell-check":
    grammars: [
      "text.plain"
      "source.gfm"
      "text.git-commit"
    ]
  welcome:
    showOnStartup: false
  "find-and-replace":
    openProjectFindResultsInRightPane: true
  "wrap-guide":
    columns: [
      {
        pattern: "COMMIT_EDITMSG"
        column: 72
      }
    ]
  "merge-conflicts":
    gitPath: "bin/git"
  "one-light-ui":
    layoutMode: "Compact"
  "one-dark-ui":
    layoutMode: "Compact"

Remove setting for obsolete autocomplete package


React             = require 'react'
EnrollButton      = require '../students/enroll_button'

InlineUsers = React.createClass(
  displayName: 'InlineUsers'
  render: ->
    key = @props.title + '_' + @props.role
    last_user_index = @props.users.length - 1
    user_list = @props.users.map (user, index) ->
      link = "https://en.wikipedia.org/wiki/User:#{user.wiki_id}"
      if user.real_name?
        extra_info = " (#{user.real_name}#{if user.email? then " / " + user.email else ""})"
      else
        extra_info = ''
      extra_info = extra_info + ', ' unless index == last_user_index

      {user.wiki_id}{extra_info}

    user_list = if user_list.length > 0 then user_list else 'None'
    if @props.users.length > 0 || @props.editable
      inline_list = {@props.title}: {user_list}
    allowed = @props.role != 4 || (@props.current_user.role == 4 || @props.current_user.admin)
    button = <EnrollButton {...@props} users={@props.users} role={@props.role} key={key} inline=true allowed={allowed} show={@props.editable && allowed} />

{inline_list}{button}

)

module.exports = InlineUsers

Fix warnings caused by invalid nesting of p elems


#
# Syntho - A Web based Synthesizer
# by Michael Marner <michael@20papercups.net>
#

audioContext = new (window.AudioContext || window.webkitAudioContext)();

vco1 = audioContext.createOscillator()
vco1.type = 'square'
vco1.frequency.value = 440
vco1.start()

gate = audioContext.createGain()
gate.gain.value = 0

vco1.connect(gate)
gate.connect(audioContext.destination)

kbd = new KeyboardInput
freqMap = new FrequencyMap

callback = (message, note) ->
    if note >= 0
        vco1.frequency.value = freqMap.getFrequency(note)
        gate.gain.value = 1
    else
        gate.gain.value = 0

PubSub.subscribe('Keyboard', callback)

#
# Syntho - A Web based Synthesizer
# by Michael Marner <michael@20papercups.net>
#

audioContext = new (window.AudioContext || window.webkitAudioContext)();

vco1 = audioContext.createOscillator()
vco1.type = 'square'
vco1.frequency.value = 440
vco1.start()

gate = audioContext.createGain()
gate.gain.value = 0

vco1.connect(gate)
gate.connect(audioContext.destination)

kbd = new KeyboardInput
freqMap = new FrequencyMap

callback = (message, note) ->
    if note >= 0
        vco1.frequency.setValueAtTime(freqMap.getFrequency(note), audioContext.currentTime)
        gate.gain.value = 1
    else
        gate.gain.value = 0

PubSub.subscribe('Keyboard', callback)

Fix portamento by using setValueAtTime instead of just changing the value.


define [], () ->
 defaultRepo:
   repoUser: 'oerpub'
   repoName: 'textbook-demo'
   branch: 'master'

define [], () ->
 defaultRepo:
   repoUser: 'oerpub'
   repoName: 'textbook-demo'
   branch: ''

Set the branch to '', that makes it use the default.

It also means the repo in the url does not by default contain /branch/master.


BaseView = require 'views/base_view'

class Dialog extends BaseView
  className: 'dialog'
  tag: 'div'

  dialogTemplate: require './templates/dialog'

  initialize: (options) ->
    @parent = options.parent

  close: =>
    @remove()

  confirm: (e) =>
    unless e.type is 'keypress' and e.which isnt 13
      @confirmCallback(e)
      @close()

  events:
    'click span.window-close' : 'close'
    'click button.close' : 'close'
    'click button.confirm' : 'confirm'
    'keypress' : 'confirm'

  render: =>
    @$el.html @dialogTemplate
      title: @title
      confirmation: @confirmation
    @

  content: (content) =>
    @$('.dialog-content').html content

module.exports = Dialog

BaseView = require 'views/base_view'

class Dialog extends BaseView
  className: 'dialog'
  tag: 'div'

  dialogTemplate: require './templates/dialog'

  initialize: (options) ->
    @parent = options.parent

  close: =>
    @remove()

  confirm: (e) =>
    unless e.type is 'keypress' and e.which isnt 13
      e.preventDefault()
      @confirmCallback(e)
      @close()

  events:
    'click span.window-close' : 'close'
    'click button.close' : 'close'
    'click button.confirm' : 'confirm'
    'keypress' : 'confirm'

  render: =>
    @$el.html @dialogTemplate
      title: @title
      confirmation: @confirmation
    @

  content: (content) =>
    @$('.dialog-content').html content

module.exports = Dialog

Fix param posting in chrome

Fix param posting in chrome


window.Darkswarm = angular.module("Darkswarm", ["ngResource",
  'mm.foundation',
  'LocalStorageModule',
  'infinite-scroll',
  'angular-flash.service',
  'templates',
  'ngSanitize',
  'ngAnimate',
  'google-maps',
  'duScroll',
  'angularFileUpload',
  'angularSlideables'
  ]).config ($httpProvider, $tooltipProvider, $locationProvider, $anchorScrollProvider) ->
  $httpProvider.defaults.headers.post['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content')
  $httpProvider.defaults.headers.put['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content')
  $httpProvider.defaults.headers['common']['X-Requested-With'] = 'XMLHttpRequest'
  $httpProvider.defaults.headers.common.Accept = "application/json, text/javascript, */*"

  # This allows us to trigger these two events on tooltips
  $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' )

  # We manually handle our scrolling
  $anchorScrollProvider.disableAutoScrolling()

window.Darkswarm = angular.module("Darkswarm", ["ngResource",
  'mm.foundation',
  'LocalStorageModule',
  'infinite-scroll',
  'angular-flash.service',
  'templates',
  'ngSanitize',
  'ngAnimate',
  'google-maps',
  'duScroll',
  'angularFileUpload',
  'angularSlideables'
  ]).config ($httpProvider, $tooltipProvider, $locationProvider, $anchorScrollProvider) ->
  $httpProvider.defaults.headers['common']['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content')
  $httpProvider.defaults.headers['common']['X-Requested-With'] = 'XMLHttpRequest'
  $httpProvider.defaults.headers.common.Accept = "application/json, text/javascript, */*"

  # This allows us to trigger these two events on tooltips
  $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' )

  # We manually handle our scrolling
  $anchorScrollProvider.disableAutoScrolling()

Set auth token for all JS HTTP requests

Set auth token for all JS HTTP requests


root = exports ? this

root.team =
  paul:
    name: "Paul Bigger"
    role: "Founder"
    photo: "paul.jpg"
    github: "pbiggar"
    email: "paul@circleci.com"
    bio: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam at eros non dui sollicitudin mattis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aenean elementum mi tincidunt orci rhoncus tempus. Cras tincidunt feugiat purus a venenatis.  Morbi sollicitudin turpis sapien, sed consequat justo. Sed semper sagittis ornare. Etiam et est tortor, at tristique sem. Maecenas a quam magna."
    visible: true

root = exports ? this

root.team =
  paul:
    name: "Paul Bigger"
    role: "Founder"
    github: "pbiggar"
    email: "paul@circleci.com"
    bio: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam at eros non dui sollicitudin mattis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aenean elementum mi tincidunt orci rhoncus tempus. Cras tincidunt feugiat purus a venenatis.  Morbi sollicitudin turpis sapien, sed consequat justo. Sed semper sagittis ornare. Etiam et est tortor, at tristique sem. Maecenas a quam magna."
    visible: true

  allen:
    name: "Allen Rohner"
    role: "Founder"
    github: "arohner"
    email: "allen@circleci.com"
    bio: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam at eros non dui sollicitudin mattis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aenean elementum mi tincidunt orci rhoncus tempus. Cras tincidunt feugiat purus a venenatis.  Morbi sollicitudin turpis sapien, sed consequat justo. Sed semper sagittis ornare. Etiam et est tortor, at tristique sem. Maecenas a quam magna."
    visible: true

Add Allen's information to team

Add Allen's information to team


#= require ./add_channel_to_channels_modal_view

class window.AddChannelToChannelsButtonView extends Backbone.Marionette.Layout
  template: 'channels/add_channel_to_channels_button'

  events:
    "click .js-add-to-channel-button": "openAddToChannelModal"

  initialize: ->
    @collection = @model.getOwnContainingChannels(this)
    @bindTo @collection, "add remove reset", (channel) => @updateButton()

  onRender: ->
    @updateButton()

  updateButton: =>
    added = @collection.length > 0

    @$('.added-to-channel-button-label').toggle added
    @$('.add-to-channel-button-label').toggle not added

  openAddToChannelModal: (e) ->
    e.stopImmediatePropagation()
    e.preventDefault()

    suggestion_collection = @options.suggested_topics || new SuggestedTopics([@model.topic()])

    FactlinkApp.Modal.show 'Add to Channels',
      new AddChannelToChannelsModalView
            model: @model,
            collection: @collection
            suggestions: suggestion_collection

#= require ./add_channel_to_channels_modal_view

class window.AddChannelToChannelsButtonView extends Backbone.Marionette.Layout
  template: 'channels/add_channel_to_channels_button'

  events:
    "click .js-add-to-channel-button": "openAddToChannelModal"

  initialize: ->
    @collection = @model.getOwnContainingChannels(this)
    @bindTo @collection, "add remove reset", (channel) => @updateButton()

  onRender: ->
    @updateButton()

  updateButton: =>
    added = @collection.length > 0

    @$('.added-to-channel-button-label').toggle added
    @$('.add-to-channel-button-label').toggle not added

  topic_suggestions: ->
    @options.suggested_topics || new SuggestedTopics([@model.topic()])

  openAddToChannelModal: (e) ->
    e.stopImmediatePropagation()
    e.preventDefault()

    FactlinkApp.Modal.show 'Add to Channels',
      new AddChannelToChannelsModalView
            model: @model,
            collection: @collection
            suggestions: @topic_suggestions()

Use suggested_topics passed as parameter, and fall back to collection with the single model


checkOuterPages = (url) =>
  outerPages = ["docs", "about", "privacy", "pricing"]
  for page in outerPages
    if (url.match "^/#{page}.*")
      return "/"
  return url

CI.github =
  # we encode each parameter separately (one of them twice!) to get the right format
  authUrl: (scope=["user:email", "repo"]) =>
    destination = window.location.pathname + window.location.hash
    destination = checkOuterPages(destination)
    destination = encodeURIComponent(destination)
    csrf_token = CSRFToken
    csrf_token = encodeURIComponent(csrf_token)

    path = "https://github.com/login/oauth/authorize"
    client_id = window.renderContext.githubClientId

    l = window.location
    redirect = "#{l.protocol}//#{l.host}/auth/github?return-to=#{destination}&CSRFToken=#{csrf_token}"
    redirect = encodeURIComponent(redirect)

    url =  "#{path}?client_id=#{client_id}&redirect_uri=#{redirect}"
    if scope
      scope = scope.join ","
      scope = encodeURIComponent(scope)
      url += "&scope=#{scope}"

    url

checkOuterPages = (url) =>
  outerPages = ["docs", "about", "privacy", "pricing", "integrations", "features"]
  for page in outerPages
    if (url.match "^/#{page}.*")
      return "/"
  return url

CI.github =
  # we encode each parameter separately (one of them twice!) to get the right format
  authUrl: (scope=["user:email", "repo"]) =>
    destination = window.location.pathname + window.location.hash
    destination = checkOuterPages(destination)
    destination = encodeURIComponent(destination)
    csrf_token = CSRFToken
    csrf_token = encodeURIComponent(csrf_token)

    path = "https://github.com/login/oauth/authorize"
    client_id = window.renderContext.githubClientId

    l = window.location
    redirect = "#{l.protocol}//#{l.host}/auth/github?return-to=#{destination}&CSRFToken=#{csrf_token}"
    redirect = encodeURIComponent(redirect)

    url =  "#{path}?client_id=#{client_id}&redirect_uri=#{redirect}"
    if scope
      scope = scope.join ","
      scope = encodeURIComponent(scope)
      url += "&scope=#{scope}"

    url

Update 'outerPages' so that features/integrations pages don't redirect to themselves


this.edsc.util.url = do(window,
                        document,
                        History,
                        extend = jQuery.extend,
                        param = jQuery.param) ->

  pushPath = (path, title=document.title, data=null) ->
    History.pushState(data, title, path + window.location.search)

  savedState = null
  savedPath = null

  saveState = (path, state, push = false) ->
    paramStr = param(state)
    paramStr = '?' + paramStr if paramStr.length > 0

    if window.location.pathname != path || window.location.search != paramStr
      savedState = paramStr
      savedPath = path
      if push
        History.pushState(state, document.title, path + paramStr)
      else
        History.replaceState(state, document.title, path + paramStr)
      true
    else
      false

  # Raise a new event to avoid getting a statechange event when we ourselves change the state
  $(window).on 'statechange anchorchange', ->
    if window.location.search != savedState || window.location.pathname != savedPath
      $(window).trigger('edsc.pagechange')

  exports =
    pushPath: pushPath
    saveState: saveState

this.edsc.util.url = do(window,
                        document,
                        History,
                        extend = jQuery.extend,
                        param = jQuery.param) ->

  cleanPath = ->
    # Remove everything up to the third slash
    History.getState().cleanUrl.replace(/^[^\/]*\/\/[^\/]*/, '')

  pushPath = (path, title=document.title, data=null) ->
    # Replace everything before the first ?
    path = cleanPath().replace(/^[^\?]*/, path)
    History.pushState(data, title, path)

  savedPath = null

  saveState = (path, state, push = false) ->
    paramStr = param(state)
    paramStr = '?' + paramStr if paramStr.length > 0
    path = path + paramStr

    if cleanPath() != path
      savedPath = path
      if push
        History.pushState(state, document.title, path)
      else
        History.replaceState(state, document.title, path)
      true
    else
      false

  # Raise a new event to avoid getting a statechange event when we ourselves change the state
  $(window).on 'statechange anchorchange', ->
    if cleanPath() != savedPath
      $(window).trigger('edsc.pagechange')

  exports =
    pushPath: pushPath
    saveState: saveState

EDSC-149: Fix landing page behavior when transitioning using the enter key