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


define [
    'jquery'
    './utils/numbers'
    './utils/url'
    './utils/version'
], ($, mods...) ->

    # Convenience method for getting a value using the dot-notion for
    # accessing nested structures.
    getDotProp = (obj, key) ->
        toks = key.split('.')
        for tok in toks
            if not (obj = obj[tok])?
                return
        return obj

    # Convenience method for setting a value using the dot-notion for
    # accessing nested structures.
    setDotProp = (obj, key, value) ->
        if typeof key is 'object'
            # Second argument is a boolean to whether or not to replace
            # the options
            if value is true
                return $.extend(true, {}, key)
            return $.extend(true, obj, key)

        toks = key.split('.')
        last = toks.pop()
        for tok in toks
            if not obj[tok]?
                obj[tok] = {}
            obj = obj[tok]
        obj[last] = value
        return

    $.extend { getDotProp, setDotProp }, mods...

define [
    'jquery'
    './utils/numbers'
    './utils/url'
    './utils/version'
], ($, mods...) ->

    # Convenience method for getting a value using the dot-notion for
    # accessing nested structures.
    getDotProp = (obj, key) ->
        toks = key.split('.')
        for tok in toks
            if not (obj = obj[tok])?
                return
        return obj

    # Convenience method for setting a value using the dot-notion for
    # accessing nested structures.
    setDotProp = (obj, key, value) ->
        if typeof key is 'object'
            # Second argument is a boolean to whether or not to replace
            # the options
            if value is true
                return $.extend(true, {}, key)
            return $.extend(true, obj, key)

        toks = key.split('.')
        last = toks.pop()
        for tok in toks
            if not obj[tok]?
                obj[tok] = {}
            obj = obj[tok]
        obj[last] = value
        return

    pprint = (obj) ->
        console.log(JSON.stringify(obj, null, 4))

    $.extend { getDotProp, setDotProp, pprint }, mods...

Add pprint utility function for JSON-based structures


util = require 'util'

exports.is =
  fun: util.isFunction
  str: util.isString
  arr: util.isArray
  obj: util.isObject

exports.normalize = (params, options, fn) ->
  if util.isFunction params
    fn = params
    params = null
    options = null
  else if util.isFunction options
    fn = options
    options = null

  if not util.isFunction fn
    fn = ->

  {params, options, fn}

fun = (f) -> typeof f is 'function'
str = (s) -> typeof s is 'string'
arr = (a) -> a instanceof Array
obj = (o) -> o instanceof Object and not fun(o) and not arr(o)

exports.is =
  fun: fun
  str: str
  arr: arr
  obj: obj

exports.normalize = (params, options, fn) ->
  if fun params
    fn = params
    params = null
    options = null
  else if fun options
    fn = options
    options = null

  if not fun fn
    fn = ->

  {params, options, fn}

Revert "Replace validation to node built-in functions"

This reverts commit 9e4ff9f9d1dfa10098ec9fdbcd44420e2af64332.


_ = require 'underscore'
{ Cities, FeaturedCities } = require 'places'
PartnerShows = require '../../../../collections/partner_shows.coffee'
RelatedShowsView = require './view.coffee'

module.exports = (type, show) ->

  el = $('.js-related-shows')

  city = _.findWhere(Cities, name: show.formatCity())

  criteria =
    sort: 'end_at'
    size: 20
    displayable: true

  relatedShows = new PartnerShows

  switch type
    when 'fair'
      data =  _.extend criteria, {
        fair_id: show.related().fair.get('_id')
      }
      title = "More Booths from #{show.related().fair.get('name')}"
    when 'gallery'
      data = _.extend criteria, {
        sort: '-start_at'
        status: "upcoming"
      }
      relatedShows.url = "#{show.related().partner.url()}/shows"
      title = "Other Shows from #{show.partnerName()}"
    when 'featured'
      data = _.extend criteria, {
        featured: true
        status: 'running'
      }
      el = $('.js-featured-shows')
      title = "Featured Shows"
    when 'city'
      data = _.extend criteria, {
        near: show.location().getMapsLocation()
        status: 'running'
      }
      title = "Current Shows in #{show.formatCity()}"

  new RelatedShowsView
    collection: relatedShows
    title: title
    el: el
    show: show
    city: city

  relatedShows.fetch
    data: data
    success: ->
      relatedShows.getShowsRelatedImages()

_ = require 'underscore'
{ Cities, FeaturedCities } = require 'places'
PartnerShows = require '../../../../collections/partner_shows.coffee'
RelatedShowsView = require './view.coffee'

module.exports = (type, show) ->

  el = $('.js-related-shows')

  city = _.findWhere(Cities, name: show.formatCity())

  criteria =
    sort: 'end_at'
    size: 20
    displayable: true

  relatedShows = new PartnerShows

  switch type
    when 'fair'
      data =  _.extend criteria, {
        fair_id: show.related().fair.get('_id')
      }
      title = "More Booths from #{show.related().fair.get('name')}"
    when 'gallery'
      data = _.extend criteria, {
        sort: '-start_at'
      }
      relatedShows.url = "#{show.related().partner.url()}/shows"
      title = "Other Shows from #{show.partnerName()}"
    when 'featured'
      data = _.extend criteria, {
        featured: true
        status: 'running'
      }
      el = $('.js-featured-shows')
      title = "Featured Shows"
    when 'city'
      data = _.extend criteria, {
        near: show.location().getMapsLocation()
        status: 'running'
      }
      title = "Current Shows in #{show.formatCity()}"

  new RelatedShowsView
    collection: relatedShows
    title: title
    el: el
    show: show
    city: city

  relatedShows.fetch
    data: data
    success: ->
      relatedShows.getShowsRelatedImages()

Include past shows in related shows on gallery pages


{Task} = require 'atom'
ctags = require 'ctags'
async = require 'async'
getTagsFile = require "./get-tags-file"

handlerPath = require.resolve './load-tags-handler'

module.exports =
  find: (editor, callback) ->
    symbol = editor.getSelectedText()

    unless symbol
      cursor = editor.getLastCursor()
      scopes = cursor.getScopeDescriptor().getScopesArray()
      rubyScopes = scopes.filter (scope) -> /^source\.ruby($|\.)/.test(scope)
      wordRegex = /[a-zA-Z_][\w!?]*/g if rubyScopes.length

      range = cursor.getCurrentWordBufferRange({wordRegex})
      symbol = editor.getTextInRange(range)

    unless symbol
      return process.nextTick -> callback(null, [])

    allTags = []

    async.each(
      atom.project.getPaths(),
      (projectPath, done) ->
        tagsFile = getTagsFile(projectPath)
        return done() unless tagsFile?
        ctags.findTags tagsFile, symbol, (err, tags=[]) ->
          tag.directory = projectPath for tag in tags
          allTags = allTags.concat(tags)
          done(err)
      (err) -> callback(err, allTags)
    )

  getAllTags: (callback) ->
    projectTags = []
    task = Task.once handlerPath, atom.project.getPaths(), -> callback(projectTags)
    task.on 'tags', (tags) -> projectTags.push(tags...)
    task

{Task} = require 'atom'
ctags = require 'ctags'
async = require 'async'
getTagsFile = require "./get-tags-file"

handlerPath = require.resolve './load-tags-handler'

module.exports =
  find: (editor, callback) ->
    symbol = editor.getSelectedText()

    unless symbol
      cursor = editor.getLastCursor()
      scopes = cursor.getScopeDescriptor().getScopesArray()
      rubyScopes = scopes.filter (scope) -> /^source\.ruby($|\.)/.test(scope)
      wordRegex = /[\w!?]*/g if rubyScopes.length

      range = cursor.getCurrentWordBufferRange({wordRegex})
      symbol = editor.getTextInRange(range)

    unless symbol
      return process.nextTick -> callback(null, [])

    allTags = []

    async.each(
      atom.project.getPaths(),
      (projectPath, done) ->
        tagsFile = getTagsFile(projectPath)
        return done() unless tagsFile?
        ctags.findTags tagsFile, symbol, (err, tags=[]) ->
          tag.directory = projectPath for tag in tags
          allTags = allTags.concat(tags)
          done(err)
      (err) -> callback(err, allTags)
    )

  getAllTags: (callback) ->
    projectTags = []
    task = Task.once handlerPath, atom.project.getPaths(), -> callback(projectTags)
    task.on 'tags', (tags) -> projectTags.push(tags...)
    task

Fix go-to-declaration for various cursor position within Ruby identifier

Even though technically the Ruby "word" can only start with a letter or
underscore, in practice it's not a good idea to define such regexp here,
because due to Atom's implementation of `editor.scanInBufferRange`, the
"word" under cursor will be looked up twice: once considering everything
up to the cursor position, and the second time considering everything
from the cursor position to the end of file.

So, if the cursor was positioned between "c" and "!" in `abc!`
identifier, the go-to-declaration for `abc!` symbol would fail.


window.bsat.utils.readyOrPageChange ->
  #
  # Setup sidebar open/close state when the user
  # clicks on sidebar toggle button. The sidebar
  # gets closed on small devices using media queries,
  # so we need to take this into account.
  #
  $('#sidebar-toggle-button').click (e) ->
    e.preventDefault()
    if $(window).width() <= 768
      $('body').toggleClass('sidebar-open')
      $('body').removeClass('sidebar-close')
    else
      $('body').removeClass('sidebar-open')
      $('body').toggleClass('sidebar-close')

  #
  # Detect if there is no sidebar
  #
  unless $('#sidebar')[0]
    $('body').addClass('no-sidebar')

  #
  # Use IScroll for scrolling the sidebar
  #
  for selector in ['#sidebar']
    new IScroll(selector, {
      scrollbars: true,
      fadeScrollbars: true,
      mouseWheel: true
    }) if $(selector)[0]

window.bsat.utils.readyOrPageChange ->
  #
  # Setup sidebar open/close state when the user
  # clicks on sidebar toggle button. The sidebar
  # gets closed on small devices using media queries,
  # so we need to take this into account.
  #
  $('#sidebar-toggle-button').click (e) ->
    e.preventDefault()
    if $(window).width() <= 768
      $('body').toggleClass('sidebar-open')
      $('body').removeClass('sidebar-close')
    else
      $('body').removeClass('sidebar-open')
      $('body').toggleClass('sidebar-close')

  #
  # Detect if there is no sidebar
  #
  unless $('#sidebar')[0]
    $('body').addClass('no-sidebar')

  #
  # Use IScroll for scrolling the sidebar
  #
  selector = '#sidebar'
  new IScroll(selector, {
    scrollbars: true,
    fadeScrollbars: true,
    mouseWheel: true,
    click: true
  }) if $(selector)[0]

Enable click support on mobile devices.

Enable click support on mobile devices.


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

fileTypes =
  '.swig' : 'text.html.twig'

atom.workspace.observeTextEditors (editor) ->
  scopeName = fileTypes[extname editor.getPath()]
  return unless scopeName?
  g =  atom.grammars.grammarForScopeName scopeName
  return unless g?
  editor.setGrammar g

process.env.PATH = ["/usr/local/bin", process.env.PATH].join(":")

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

process.env.PATH = ["/usr/local/bin", process.env.PATH].join(":")

Remove deprecated swig file type

Remove deprecated swig file type


commander = require 'commander'
meshblu   = require 'meshblu'

class KeygenCommand
  parseOptions: =>
    commander
      .parse process.argv

  run: =>
    @parseOptions()

    @config = {server: 'meshblu.octoblu.com', port: 80, uuid: 'wrong'}
    @conn = meshblu.createConnection @config
    @conn.on 'notReady', @onReady

  onReady: (credentials) =>
    @conn.register {}, (credentials) =>
      @config.uuid = credentials.uuid
      @config.token = credentials.token
      console.log JSON.stringify(@config, null, 2)
      process.exit 0

(new KeygenCommand()).run()

commander = require 'commander'
_         = require 'lodash'
meshblu   = require 'meshblu'
url       = require 'url'

DEFAULT_HOST = 'meshblu.octoblu.com'
DEFAULT_PORT = 80

class KeygenCommand
  parseOptions: =>
    commander
      .option '-s, --server <host[:port]>', 'Meshblu host'
      .parse process.argv

  parseConfig: =>
    unless commander.server?
      return {server: DEFAULT_HOST, port: DEFAULT_PORT}

    server = commander.server
    unless _.startsWith server, 'ws'
      protocol = if port == 443 then 'wss://' else 'ws://'
      server = protocol + server

    {hostname, port} = url.parse server
    port ?= 80
    {server: hostname, port: port}

  run: =>
    @parseOptions()
    @config = @parseConfig()
    @config.uuid = 'wrong' # to force a notReady
    @conn = meshblu.createConnection @config
    @conn.on 'notReady', @onReady

  onReady: (credentials) =>
    @conn.register {}, (credentials) =>
      @config.uuid = credentials.uuid
      @config.token = credentials.token
      console.log JSON.stringify(@config, null, 2)
      process.exit 0

(new KeygenCommand()).run()

Add optional server and port

Add optional server and port


GitHubFile  = require './github-file'

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

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

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

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

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

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

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

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

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

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

GitHubFile  = require './github-file'

module.exports =
  config:
    includeLineNumbersInUrls:
      default: true
      type: 'boolean'
      description: 'Include the line range selected in the editor when opening or copying URLs to the clipboard. When opened in the browser, the GitHub page will automatically scroll to the selected line range.'

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

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

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

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

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

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

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

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

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

Add description for config setting

Add description for config setting


fs = require('fs')

isFile= (name,foundCB)->
  try
    fs.stat name, (err,stats)->
      foundCB(!err && stats.isFile())

isFileSync= (name)->
  try
    fs.statSync(name).isFile()
  catch e
    throw e unless e.code == 'ENOENT'
    false

moduleExtensions= ['.js','.coffee','.json']

hasAnExtension= (name,extensions,foundCB)->
  i= -1
  looper= (found)->
    return foundCB(extensions[i]) if found
    i+= 1
    return foundCB(false) unless i<extensions.length
    isFile(name+extensions[i],looper)
  looper(false) #initiate the process

module.exports=
  # is the given name that of a regular file?
  isFile: isFile
  isFileSync: isFileSync

  # does a file with one of the given extensions exist?
  hasAnExtension: hasAnExtension

  # Would adding the proper extension to the given name find a file whose extension
  # suggested that it could be a module?
  isModule: (name,foundCB)->
    hasAnExtension(name,moduleExtensions,foundCB)
  isModuleSync: (name)->
    return true for ext in moduleExtensions when isFileSync(name+ext)
    false

fs = require('fs')

isFile= (name,foundCB)->
  fs.stat name, (err,stats)->
    foundCB(!err && stats.isFile())

isFileSync= (name)->
  try
    fs.statSync(name).isFile()
  catch e
    throw e unless e.code == 'ENOENT'
    false

moduleExtensions= ['.js','.coffee','.json']

hasAnExtension= (name,extensions,foundCB)->
  i= -1
  looper= (found)->
    return foundCB(extensions[i]) if found
    i+= 1
    return foundCB(false) unless i<extensions.length
    isFile(name+extensions[i],looper)
  looper(false) #initiate the process

module.exports=
  # is the given name that of a regular file?
  isFile: isFile
  isFileSync: isFileSync

  # does a file with one of the given extensions exist?
  hasAnExtension: hasAnExtension

  # Would adding the proper extension to the given name find a file whose extension
  # suggested that it could be a module?
  isModule: (name,foundCB)->
    hasAnExtension(name,moduleExtensions,foundCB)
  isModuleSync: (name)->
    return true for ext in moduleExtensions when isFileSync(name+ext)
    false

Remove try without a catch

Remove try without a catch


define (require) ->
  $ = require('jquery')
  Backbone = require('backbone')
  router = require('cs!router')
  analytics = require('cs!helpers/handlers/analytics') # Setup Analytics Handler

  # The root URI prefixed on all non-external AJAX and Backbone URIs
  root = '/'

  init = (options = {}) ->
    # Append /test to the root if the app is in test mode
    if options.test
      root += 'test/'

    external = new RegExp('^((f|ht)tps?:)?//')

    # Catch internal application links and let Backbone handle the routing
    $(document).on 'click', 'a:not([data-bypass])', (e) ->
      href = $(this).attr('href')

      # Only handle links intended to be processed by Backbone
      if e.isDefaultPrevented() or href.charAt(0) is '#' or /^mailto:.+/.test(href) then return

      e.preventDefault()

      if external.test(href)
        window.open(href, '_blank')
      else
        router.navigate(href, {trigger: true})

    Backbone.history.start
      pushState: true
      root: root

    # Prefix all non-external AJAX requests with the root URI
    $.ajaxPrefilter (options, originalOptions, jqXHR) ->
      if not external.test(options.url)
        options.url = root + options.url

      return

  return {init: init}

define (require) ->
  $ = require('jquery')
  Backbone = require('backbone')
  router = require('cs!router')
  analytics = require('cs!helpers/handlers/analytics') # Setup Analytics Handler

  # The root URI prefixed on all non-external AJAX and Backbone URIs
  root = '/'

  init = (options = {}) ->
    # Append /test to the root if the app is in test mode
    if options.test
      root += 'test/'

    external = new RegExp('^((f|ht)tps?:)?//')

    # Catch internal application links and let Backbone handle the routing
    $(document).on 'click', 'a:not([data-bypass])', (e) ->
      href = $(this).attr('href')

      # Only handle links intended to be processed by Backbone
      if e.isDefaultPrevented() or href.charAt(0) is '#' or /^mailto:.+/.test(href) then return

      e.preventDefault()

      if external.test(href)
        window.open(href, '_blank')
      else
        router.navigate(href, {trigger: true})

    Backbone.history.start
      pushState: true
      root: root

    # Force Backbone to register the full path including the query in its history
    if location.search
      router.navigate(location.pathname + location.search, {replace: true})

    # Prefix all non-external AJAX requests with the root URI
    $.ajaxPrefilter (options, originalOptions, jqXHR) ->
      if not external.test(options.url)
        options.url = root + options.url

      return

  return {init: init}

Include query strings in backbone history


###
PDFImage - embeds images in PDF documents
By Devon Govett
###

fs = require 'fs'
Data = require './data'
JPEG = require './image/jpeg'
PNG = require './image/png'

class PDFImage
  @open: (src, label) ->
    if Buffer.isBuffer(src)
      data = src
    else
      if src[0..4] is 'data:' and src.indexOf(';base64,') > -1
        base64String = src.split(';base64,')[1]
        data = new Buffer(base64String, 'base64')

      else
        data = fs.readFileSync src
        return unless data

    if data[0] is 0xff and data[1] is 0xd8
      return new JPEG(data, label)

    else if data[0] is 0x89 and data.toString('ascii', 1, 4) is 'PNG'
      return new PNG(data, label)

    else
      throw new Error 'Unknown image format.'

module.exports = PDFImage

###
PDFImage - embeds images in PDF documents
By Devon Govett
###

fs = require 'fs'
Data = require './data'
JPEG = require './image/jpeg'
PNG = require './image/png'

class PDFImage
  @open: (src, label) ->
    if Buffer.isBuffer(src)
      data = src
    else
      if match = /^data:.+;base64,(.*)$/.exec(src)
        data = new Buffer(match[1], 'base64')

      else
        data = fs.readFileSync src
        return unless data

    if data[0] is 0xff and data[1] is 0xd8
      return new JPEG(data, label)

    else if data[0] is 0x89 and data.toString('ascii', 1, 4) is 'PNG'
      return new PNG(data, label)

    else
      throw new Error 'Unknown image format.'

module.exports = PDFImage

Use a regex to capture the base64 encoded string


class ChatConversationListItemTitle extends JView

  constructor:(options = {}, data)->
    options.cssClass = 'chat-item'
    # data = [nick for nick in data when nick isnt KD.nick()].first
    super

  viewAppended:->

    invitees = @getData()
    @accounts = []

    for invitee in invitees
      KD.remote.cacheable invitee, (err, account)=>
        warn err  if err
        @accounts.push account?.first or Object
        @setTemplate @pistachio()  if @accounts.length is @getData().length

  getName:(index)->
    "#{@accounts[index].profile.firstName} #{@accounts[index].profile.lastName}"

  pistachio:->

    @setClass 'multiple'  if @accounts.length > 1

    @avatar  = new AvatarView
      size   : {width: 30, height: 30}
      origin : @accounts.first

    @participants = switch @accounts.length
      when 1 then @getName 0
      when 2 then "#{@getName(0)} and #{@getName(1)}"
      else "#{@getName(0)}, #{@getName(1)} and #{data.length - 2} more."

    """

        {{> @avatar}}

#{@participants}

    """

class ChatConversationListItemTitle extends JView

  constructor:(options = {}, data)->
    options.cssClass = 'chat-item'
    # data = [nick for nick in data when nick isnt KD.nick()].first
    super

  viewAppended:->

    invitees = @getData()
    @accounts = []

    for invitee in invitees
      KD.remote.cacheable invitee, (err, account)=>
        warn err  if err
        @accounts.push account?.first or Object
        @setTemplate @pistachio()  if @accounts.length is @getData().length

  getName:(index)->
    "#{@accounts[index].profile.firstName} #{@accounts[index].profile.lastName}"

  pistachio:->

    @setClass 'multiple'  if @accounts.length > 1

    @avatar  = new AvatarView
      size   : {width: 30, height: 30}
      origin : @accounts.first

    @participants = switch @accounts.length
      when 1 then @getName 0
      when 2 then "#{@getName(0)} and #{@getName(1)}"
      else "#{@getName(0)}, #{@getName(1)} and #{@accounts.length - 2} more."

    """

        {{> @avatar}}

#{@participants}

    """

Check accounts length since its the data


fs = require 'graceful-fs'
AssetWatcher = require './Asset'

class TemplateWatcher extends AssetWatcher
    constructor: (@config)->
        super()

    pattern: -> super ["**/template.jade"]
    getPaths: -> ['/templates.js', "/templates-#{@hash()}.js"]

    getShortPath: (path)->
        @pathpart(path)
        .substr(1)
        .replace('.jade', '')
        .replace('/template', '')

    getModuleName: (shortPath)->
        module = shortPath.replace(/\//g, '.') + '.template'
        if moduleRoot = @getModuleRoot()
            module = "#{moduleRoot}.#{module}"
        module

    getModuleRoot: -> @config.templateModuleRoot

    stripNewlines: (content)->
        content.replace(/\r?\n/g, '\\n\' +\n    \'')

    # Normalize backslashes and strip newlines.
    escapeContent: (content)->
        @stripNewlines(content)
        .replace(/\\/g, '\\\\')
        .replace(/'/g, '\\\'')

    render: (code, path)->
        options = filename: path
        content = require('jade').render(code, options)
        @wrap path, content

    wrap: (path, content)->
        shortPath = @getShortPath path
        module = @getModuleName shortPath

        """
        angular.module('#{module}', [])
        .run(function($templateCache){
            $templateCache.put('#{shortPath}', '#{@escapeContent(content)}');
        });
        """

module.exports = TemplateWatcher

fs = require 'graceful-fs'
AssetWatcher = require './Asset'

class TemplateWatcher extends AssetWatcher
    constructor: (@config)->
        super()

    pattern: -> super ["**/template.jade"]
    getPaths: -> ['/templates.js', "/templates-#{@hash()}.js"]

    getShortPath: (path)->
        @pathpart(path)
        .substr(1)
        .replace('.jade', '')
        .replace('/template', '')

    getModuleName: (shortPath)->
        module = shortPath.replace(/[\/\\]/g, '.') + '.template'
        if moduleRoot = @getModuleRoot()
            module = "#{moduleRoot}.#{module}"
        module

    getModuleRoot: -> @config.templateModuleRoot

    stripNewlines: (content)->
        content.replace(/\r?\n/g, '\\n\' +\n    \'')

    # Normalize backslashes and strip newlines.
    escapeContent: (content)->
        @stripNewlines(content)
        .replace(/\\/g, '\\\\')
        .replace(/'/g, '\\\'')

    render: (code, path)->
        options = filename: path
        content = require('jade').render(code, options)
        @wrap path, content

    wrap: (path, content)->
        shortPath = @getShortPath path
        module = @getModuleName shortPath

        """
        angular.module('#{module}', [])
        .run(function($templateCache){
            $templateCache.put('#{shortPath}', '#{@escapeContent(content)}');
        });
        """

module.exports = TemplateWatcher

Fix for windows template paths.

Fix for windows template paths.


'use strict'

app.directive 'pdfViewer', [
  () ->
    restrict: 'E'
    scope:
      asset:  '=asset'
    link: ($scope, $elem) ->
      pdfElement = $($elem)[0]
      objectTag = document.createElement('object')
      objectTag.setAttribute('data', $scope.asset.downloadRoute(true))
      pdfElement.appendChild(objectTag)
      return

]

'use strict'

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

]

Fix width on PDF viewer

Fix width on PDF viewer


moment    = require('lib/moment')

exports.isDev = (req) ->
  # We are on DEV when Host has string like
  # '/dev.' or '/staging.' or '.local' or '.dev' or 'localhost'
  found = req.headers.Host.search(/(\/dev\.|\/staging\.|\.local$|\.dev$|localhost)/) > -1
  return found

exports.prettyDate = (date) ->
  moment.utc(date).local().format('MMM Do YYYY')

exports.halfDate = (date) ->
  moment.utc(date).local().format('MMM D')

exports.isItFresh = (date) ->
  if moment.utc().eod() < moment.utc(date).add('days', 30).eod()
    return true
  return false

exports.capitalize = (str) ->
  str ?= ''
  str.charAt(0).toUpperCase() + str.slice(1)

moment    = require('lib/moment')

exports.isDev = (req) ->
  # We are on DEV when Host has string like
  # '/dev.' or '/staging.' or '.local' or '.dev' or 'localhost'
  re = /(\/dev\.|\/staging\.|\.local$|\.dev$|localhost)/
  return re.test(req.headers.Host)

exports.prettyDate = (date) ->
  moment.utc(date).local().format('MMM Do YYYY')

exports.halfDate = (date) ->
  moment.utc(date).local().format('MMM D')

exports.isItFresh = (date) ->
  if moment.utc().eod() < moment.utc(date).add('days', 30).eod()
    return true
  return false

exports.capitalize = (str) ->
  str ?= ''
  str.charAt(0).toUpperCase() + str.slice(1)

Change isDev util function to use re.test() instead of search()


program = require "commander"
fs      = require "fs"
path    = require "path"

bang = process.env.HOME + "/.bang"
data = {}

if path.existsSync bang
  data = JSON.parse(fs.readFileSync bang)

save = ->
  console.log "save called"
  fs.writeFileSync bang, JSON.stringify(data)

get = (key) ->
  console.log data[key] if data[key]

set = (key, value) ->
  data[key] = value
  save()

remove = ->
  delete data[key] if data[key]
  save()

exports.start = ->
  program.version("0.0.1")
          .usage("[options] [key] [value]")
          .option("-d, --delete", "delete the specified key")
          .parse(process.argv)

  [key, value] = program.args

  if key and program.delete
    remove key
  else if key and value
    set key, value
  else if key
    get key
  else
    console.log program.helpInformation()

program = require "commander"
fs      = require "fs"
path    = require "path"

bang = process.env.HOME + "/.bang"
data = {}

if path.existsSync bang
  data = JSON.parse(fs.readFileSync bang)

save = ->
  fs.writeFileSync bang, JSON.stringify(data)

get = (key) ->
  console.log data[key] if data[key]

set = (key, value) ->
  data[key] = value
  save()

remove = ->
  delete data[key] if data[key]
  save()

exports.start = ->
  program.version("0.0.1")
          .usage("[options] [key] [value]")
          .option("-d, --delete", "delete the specified key")
          .parse(process.argv)

  [key, value] = program.args

  if key and program.delete
    remove key
  else if key and value
    set key, value
  else if key
    get key
  else
    console.log program.helpInformation()

Remove debug message from save method.

Remove debug message from save method.


_ = require 'underscore'
Backbone = require 'backbone'
{ API_URL } = require('sharify').data
UserInterest = require '../models/user_interest.coffee'

module.exports = class UserInterests extends Backbone.Collection
  model: UserInterest

  url: ->
    if (id = @collectorProfile?.id)?
      "#{API_URL}/api/v1/collector_profile/#{id}/user_interest"
    else
      "#{API_URL}/api/v1/me/user_interest"

  initialize: (models, { @collectorProfile } = {}) -> #

  parse: (response) ->
    _.filter response, (obj) ->
      not _.isEmpty(obj.interest)

  fetch: (options = {}) ->
    options.url = "#{@url()}/artists" # Temporary hack for this non-RESTful endpoint

    if @collectorProfile?
      options.data = _.extend options.data or {}, @collectorProfile.pick('anonymous_session_id')

    super options

  comparator: (userInterest) ->
    -Date.parse(userInterest.get 'updated_at')

  findByInterestId: (id) ->
    @find (userInterest) ->
      userInterest.related().interest.id is id

  alreadyInterested: (interest) ->
    @findByInterestId(interest.id)?

  addInterest: (interest) ->
    return if @alreadyInterested interest

    @unshift
      interest_id: interest.id
      interest: interest.attributes

_ = require 'underscore'
Backbone = require 'backbone'
{ API_URL } = require('sharify').data
UserInterest = require '../models/user_interest.coffee'

module.exports = class UserInterests extends Backbone.Collection
  model: UserInterest

  interestType: 'Artist' # Should/will be configurable

  url: ->
    if @collectorProfile?
      "#{API_URL}/api/v1/user_interests"
    else
      "#{API_URL}/api/v1/me/user_interest/artists"

  initialize: (models, { @collectorProfile } = {}) ->
    @model::urlRoot = if @collectorProfile?
      "#{API_URL}/api/v1/user_interest"
    else
      "#{API_URL}/api/v1/me/user_interest"

  parse: (response) ->
    _.filter response, (obj) ->
      not _.isEmpty(obj.interest)

  fetch: (options = {}) ->
    if @collectorProfile?
      options.data = _.extend options.data or {}, @owner(),
        interest_type: @interestType
    super options

  comparator: (userInterest) ->
    -Date.parse(userInterest.get 'updated_at')

  findByInterestId: (id) ->
    @find (userInterest) ->
      userInterest.related().interest.id is id

  alreadyInterested: (interest) ->
    @findByInterestId(interest.id)?

  owner: ->
    if @collectorProfile?
      owner_id: @collectorProfile.id, owner_type: 'CollectorProfile'

  addInterest: (interest) ->
    return if @alreadyInterested interest

    @unshift _.extend {
      interest_type: @interestType
      interest_id: interest.id
      interest: interest.attributes
    }, @owner()

Use updated endpoint and fix for legacy endpoint


window.UsersComponent = class UsersComponent
  @initialize: ->
    @addEventListeners()

  @addEventListeners: ->
    @userDetailsTooltip()
    @respondentsTable()
    #Enables search through users table
    enableSearch()

  @userDetailsTooltip: ->
    tooltipClass = '.information-tooltip'
    $(document).on('click', "#{tooltipClass}-trigger", (ev) ->
      ev.preventDefault()
      $this = $(@)
      if ($this.find('.fa-align-left').length)
        $("#{tooltipClass}-trigger").not(@).siblings(tooltipClass).hide()
        $("#{tooltipClass}-trigger").not(@).html(' details')
        $this.html(' close')
        $this.siblings(tooltipClass).show()
      else
        $this.html(' details')
        $this.siblings(tooltipClass).hide()
    )

  @respondentsTable: ->
    $('.delegate-box').on('click', ->
      $('.respondents-list').slideToggle()
    )

$(document).ready -> UsersComponent.initialize()

window.UsersComponent = class UsersComponent
  @initialize: ->
    @addEventListeners()

  @addEventListeners: ->
    @userDetailsTooltip()
    @respondentsTable()
    #Enables search through users table
    enableSearch()

  @userDetailsTooltip: ->
    tooltipClass = '.information-tooltip'
    $(document).on('click', "#{tooltipClass}-trigger", (ev) ->
      ev.preventDefault()
      $this = $(@)
      if ($this.find('.fa-align-left').length)
        $("#{tooltipClass}-trigger").not(@).siblings(tooltipClass).hide()
        $("#{tooltipClass}-trigger").not(@).html(' details')
        $this.html(' close')
        $this.siblings(tooltipClass).show()
      else
        $this.html(' details')
        $this.siblings(tooltipClass).hide()
    )

  @respondentsTable: ->
    $('.delegate-box').on('click', ->
      if $(@).attr('checked')
        $('.respondents-list').slideDown()
      else
        $('.respondents-list').slideUp()
    )

$(document).ready -> UsersComponent.initialize()

Fix checkbox to slide correctly only when ticked


"use strict"

ObjectID = require('mongodb').ObjectID
Moniker = require('moniker')

rand = (min, max) -> Math.floor (Math.random() * (max - min + 1) + min)

lisence = [
  "CC BY-NC-ND 3.0 NO"
  "CC BY-NC 3.0 NO"
  "CC BY-ND 3.0 NO"
  "CC BY 3.0 NO"
]

provider = [
  "DNT"
  "NRK"
  "TURAPP"
]

tags = [
  'Sted'
  'Hytte'
]

module.exports = (num) ->
  now = new Date().getTime()
  past = now - 100000000000
  num = num or 100
  ret = []

  for i in [1..num]
    d1 = rand(past, now)
    d2 = rand(d1, now)

    ret.push
      _id: new ObjectID()
      opprettet: new Date(d1).toISOString()
      endret: new Date(d2).toISOString()
      tilbyder: provider[rand(0, provider.length-1)]
      lisens: lisence[rand(0, lisence.length-1)]
      navn: Moniker.choose()
      tags: [tags[rand(0, tags.length-1)]]
      privat:
        foo: Moniker.choose()
        bar: Moniker.choose()

  ret

"use strict"

ObjectID = require('mongodb').ObjectID
Moniker = require('moniker')

rand = (min, max) -> Math.floor (Math.random() * (max - min + 1) + min)

lisence = [
  "CC BY-NC-ND 3.0 NO"
  "CC BY-NC 3.0 NO"
  "CC BY-ND 3.0 NO"
  "CC BY 3.0 NO"
]

provider = [
  "DNT"
  "NRK"
  "TURAPP"
]

statuses = [
  "Offentlig"
  "Privat"
  "Kladd"
  "Slettet"
]

tags = [
  'Sted'
  'Hytte'
]

module.exports = (num) ->
  now = new Date().getTime()
  past = now - 100000000000
  num = num or 100
  ret = []

  for i in [1..num]
    d1 = rand(past, now)
    d2 = rand(d1, now)

    ret.push
      _id: new ObjectID()
      opprettet: new Date(d1).toISOString()
      endret: new Date(d2).toISOString()
      tilbyder: provider[rand(0, provider.length-1)]
      lisens: lisence[rand(0, lisence.length-1)]
      status: statuses[rand(0, statuses.length-1)]
      navn: Moniker.choose()
      tags: [tags[rand(0, tags.length-1)]]
      privat:
        foo: Moniker.choose()
        bar: Moniker.choose()

  ret

Add support for document status in data gen


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

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

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

  url: ->
    '/facts/#{@fact.id}/interactors'

Revert "Added url for NDPEvidenceCollection"

This reverts commit 4cbcb9b3947112bc980da95cc7078bbae0291882.


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

module.exports =
class TabView extends View
  @content: ->
    @li class: 'tab sortable', =>
      @div class: 'title', outlet: 'title'
      @div class: 'close-icon'

  initialize: (@item, @pane) ->
    @item.on? 'title-changed', =>
      @updateTitle()
      @updateTooltip()

    @item.on? 'modified-status-changed', =>
      @updateModifiedStatus()

    @updateTitle()
    @updateModifiedStatus()
    @updateTooltip()

  updateTooltip: ->
    @destroyTooltip()

    if itemPath = @item.getPath?()
      @setTooltip
        title: itemPath
        delay:
          show: 2000
          hide: 100
        placement: 'bottom'

  beforeRemove: ->
    @destroyTooltip()

  updateTitle: ->
    return if @updatingTitle
    @updatingTitle = true

    title = @item.getTitle()
    useLongTitle = false
    for tab in @getSiblingTabs()
      if tab.item.getTitle() is title
        tab.updateTitle()
        useLongTitle = true
    title = @item.getLongTitle?() ? title if useLongTitle

    @title.text(title)
    @updatingTitle = false

  getSiblingTabs: ->
    @siblings('.tab').views()

  updateModifiedStatus: ->
    if @item.isModified?()
      @addClass('modified') unless @isModified
      @isModified = true
    else
      @removeClass('modified') if @isModified
      @isModified = false

{$, View} = require 'atom'
_ = require 'underscore-plus'
path = require 'path'

module.exports =
class TabView extends View
  @content: ->
    @li class: 'tab sortable', =>
      @div class: 'title', outlet: 'title'
      @div class: 'close-icon'

  initialize: (@item, @pane) ->
    @item.on? 'title-changed', =>
      @updateTitle()
      @updateTooltip()

    @item.on? 'modified-status-changed', =>
      @updateModifiedStatus()

    @updateTitle()
    @updateModifiedStatus()
    @updateTooltip()

  updateTooltip: ->
    @destroyTooltip()

    if itemPath = @item.getPath?()
      @setTooltip
        title: _.escape(itemPath)
        delay:
          show: 2000
          hide: 100
        placement: 'bottom'

  beforeRemove: ->
    @destroyTooltip()

  updateTitle: ->
    return if @updatingTitle
    @updatingTitle = true

    title = @item.getTitle()
    useLongTitle = false
    for tab in @getSiblingTabs()
      if tab.item.getTitle() is title
        tab.updateTitle()
        useLongTitle = true
    title = @item.getLongTitle?() ? title if useLongTitle

    @title.text(title)
    @updatingTitle = false

  getSiblingTabs: ->
    @siblings('.tab').views()

  updateModifiedStatus: ->
    if @item.isModified?()
      @addClass('modified') unless @isModified
      @isModified = true
    else
      @removeClass('modified') if @isModified
      @isModified = false

Use _.escape to escape tooltip titles

Use _.escape to escape tooltip titles


"*":
  "activate-power-mode":
    autoToggle: false
    comboMode:
      enabled: false
    plugins:
      playAudio: true
      screenShake: true
  "autocomplete-plus":
    confirmCompletion: "enter"
    useCoreMovementCommands: false
  "autocomplete-python-jedi":
    fuzzyMatcher: false
    useSnippets: "required"
  core:
    audioBeep: false
    automaticallyUpdate: false
    closeDeletedFileTabs: true
    disabledPackages: [
      "exception-reporting"
      "markdown-preview"
    ]
    telemetryConsent: "limited"
  editor:
    invisibles: {}
    showIndentGuide: true
  "git-plus":
    experimental:
      stageFilesBeta: false
    general: {}
  linter: {}
  "linter-ui-default": {}
  "tree-view": {}
  "vim-mode-plus": {}
  welcome:
    showOnStartup: false

"*":
  "activate-power-mode":
    autoToggle: false
    comboMode:
      enabled: false
    plugins:
      playAudio: true
      screenShake: true
  "autocomplete-plus":
    confirmCompletion: "enter"
    useCoreMovementCommands: false
  "autocomplete-python-jedi":
    fuzzyMatcher: false
    useSnippets: "required"
  core:
    audioBeep: false
    automaticallyUpdate: false
    closeDeletedFileTabs: true
    disabledPackages: [
      "exception-reporting"
      "markdown-preview"
    ]
    telemetryConsent: "limited"
  editor:
    invisibles: {}
    showIndentGuide: true
    softWrap: false
    softWrapAtPreferredLineLength: false
  "git-plus":
    experimental:
      stageFilesBeta: false
    general: {}
  linter: {}
  "linter-ui-default": {}
  "tree-view": {}
  "vim-mode-plus": {}
  welcome:
    showOnStartup: false
".asciidoc.source":
  editor:
    softWrap: true
    softWrapAtPreferredLineLength: true
".md.text":
  editor:
    softWrap: true
    softWrapAtPreferredLineLength: true
".gfm.source":
  editor:
    softWrap: true
    softWrapAtPreferredLineLength: true
".plain.text":
  editor:
    softWrap: true
    softWrapAtPreferredLineLength: true

Enable soft wrap in Atom for text files.

Markdown reference:
https://discuss.atom.io/t/soft-wrap-support-but-only-for-markdown/46374/2


{ isListAscending } = require './is-list-ascending.coffee'

_binarySearch = (sortedList, needle, start, end)->

  return -1 if end < start

  mid = Math.floor (start + ((end - start) / 2))

  return mid if sortedList[mid] is needle

  if sortedList[mid] > needle

    return _binarySearch sortedList, needle, start, mid - 1

  else

    return _binarySearch sortedList, needle, mid + 1, end

@binarySearch = (sortedList, needle)->
  throw new Error 'Unsorted List' unless isListAscending sortedList
  return _binarySearch sortedList, needle, 0, sortedList.length-1

{ isListAscending } = require './is-list-ascending.coffee'

_binarySearch = (sortedList, needle)->
  return -1 if sortedList.length is 0

  mid = Math.floor (sortedList.length / 2)
  return mid if sortedList[mid] is needle

  if sortedList[mid] > needle
    return _binarySearch sortedList[0..mid - 1], needle
  else
    res = _binarySearch sortedList[mid+1..sortedList.length], needle
    return (if res is -1 then -1 else res + mid + 1)

@binarySearch = (sortedList, needle)->
  throw new Error 'Unsorted List' unless isListAscending sortedList
  return _binarySearch sortedList, needle

Revert "Revert "CHECKPOINT re-implemented binarySearch using slices""

This reverts commit 71ac993cfc9b521cbb2492d2ce31943d08bb84ab.


_ = require 'underscore'
Backbone = require 'backbone'
template = require './templates/index.coffee'
Scrollbar = require '../scrollbar/index.coffee'

module.exports = class Modalize extends Backbone.View
  className: 'modalize'

  defaults: dimensions: width: '400px'

  events:
    'click .js-modalize-backdrop': 'maybeClose'
    'click .js-modalize-close': 'close'

  initialize: (options = {}) ->
    { @subView, @dimensions } = _.defaults options, @defaults
    @scrollbar = new Scrollbar

  state: (state, callback = $.noop) ->
    _.defer =>
      @$el
        .attr 'data-state', state
        .one $.support.transition.end, callback
        .emulateTransitionEnd 250

  render: ->
    unless @__rendered__
      @$el.html template()
      @__rendered__ = true
    @postRender()
    this

  postRender: ->
    unless @__postRendered__
      @$('.js-modalize-dialog').css @dimensions
      @$('.js-modalize-body').html @subView.render().$el
      @scrollbar.disable()
      @state 'open'
      @__postRendered__ = true
    else
      @subView.render().$el

  maybeClose: (e) ->
    @close() if $(e.target).hasClass('js-modalize-backdrop')

  close: (callback) ->
    @scrollbar.reenable()
    @state 'close', =>
      @subView?.remove?()
      @remove()
      callback?()

_ = require 'underscore'
Backbone = require 'backbone'
template = require './templates/index.coffee'
Scrollbar = require '../scrollbar/index.coffee'

module.exports = class Modalize extends Backbone.View
  className: 'modalize'

  defaults: dimensions: width: '400px'

  events:
    'click .js-modalize-backdrop': 'maybeClose'
    'click .js-modalize-close': 'close'

  initialize: (options = {}) ->
    { @subView, @dimensions } = _.defaults options, @defaults
    @scrollbar = new Scrollbar
    $(window).on 'keyup.modalize', @escape

  state: (state, callback = $.noop) ->
    _.defer =>
      @$el
        .attr 'data-state', state
        .one $.support.transition.end, callback
        .emulateTransitionEnd 250

  render: ->
    unless @__rendered__
      @$el.html template()
      @__rendered__ = true
    @postRender()
    this

  postRender: ->
    unless @__postRendered__
      @$('.js-modalize-dialog').css @dimensions
      @$('.js-modalize-body').html @subView.render().$el
      @scrollbar.disable()
      @state 'open'
      @__postRendered__ = true
    else
      @subView.render().$el

  escape: (e) =>
    @close() if e.which is 27

  maybeClose: (e) ->
    @close() if $(e.target).hasClass('js-modalize-backdrop')

  close: (callback) ->
    $(window).off 'keyup.modalize'
    @scrollbar.reenable()
    @state 'close', =>
      @subView?.remove?()
      @remove()
      callback?()

Support ESC key for closing modalize modals