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

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

    coffeelint:
      options:
        max_line_length:
          level: 'ignore'

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

    cpplint:
      files: ['src/**/*.cc']
      reporter: 'spec'
      verbosity: 1
      filters:
        build:
          include: false
          namespaces: false
        legal:
          copyright: false
        readability:
          braces: false

    shell:
      rebuild:
        command: 'npm build .'
        options:
          stdout: true
          stderr: true
          failOnError: true

      test:
        command: 'jasmine-focused --captureExceptions --coffee spec/'
        options:
          stdout: true
          stderr: true
          failOnError: true

  grunt.loadNpmTasks('grunt-contrib-coffee')
  grunt.loadNpmTasks('grunt-shell')
  grunt.loadNpmTasks('grunt-coffeelint')
  grunt.loadNpmTasks('node-cpplint')
  grunt.registerTask('lint', ['coffeelint', 'cpplint'])
  grunt.registerTask('default', ['lint', 'coffee', 'shell:rebuild'])
  grunt.registerTask('test', ['default', 'shell:test'])
  grunt.registerTask 'clean', ->
    rm = require('rimraf').sync
    rm('lib')
    rm('build')

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

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

    coffeelint:
      options:
        max_line_length:
          level: 'ignore'

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

    cpplint:
      files: ['src/**/*.cc']
      reporter: 'spec'
      verbosity: 1
      filters:
        build:
          include: false
          namespaces: false
        legal:
          copyright: false
        readability:
          braces: false

    shell:
      rebuild:
        command: 'npm build .'
        options:
          stdout: true
          stderr: true
          failOnError: true

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

  grunt.loadNpmTasks('grunt-contrib-coffee')
  grunt.loadNpmTasks('grunt-shell')
  grunt.loadNpmTasks('grunt-coffeelint')
  grunt.loadNpmTasks('node-cpplint')
  grunt.registerTask('lint', ['coffeelint', 'cpplint'])
  grunt.registerTask('default', ['lint', 'coffee', 'shell:rebuild'])
  grunt.registerTask('test', ['default', 'shell:test'])
  grunt.registerTask 'clean', ->
    rm = require('rimraf').sync
    rm('lib')
    rm('build')

Fix running specs on Windows.

Fix running specs on Windows.


_         = require 'underscore'
sd        = require('sharify').data
Backbone  = require 'backbone'

Artworks            = require '../../../collections/artworks.coffee'
LayeredSearchView   = require('./layered-search.coffee').LayeredSearchView
SaleView            = require './sale.coffee'

module.exports = class BelowTheFold extends Backbone.View
  initialize: (options) ->
    { @saved, @artwork } = options

    $.when.apply(null, @artwork.fetchRelatedCollections()).then =>
      # Find the first related collection that has any results
      availableFeature = _.find @artwork.relatedCollections, (xs) -> xs.length
      if availableFeature
        @["setup#{availableFeature.kind}"]()
      else
        # Always fallback to layered search if there are no features available
        @setupLayeredSearch()

  setupLayeredSearch: ->
    new LayeredSearchView el: @$el, artwork: @artwork
    @done()

  setupSales: ->
    new SaleView el: @$el, sale: @artwork.sales.first(), saved: @saved
    @done()

  setupFairs: ->
    throw 'I don\'t know how to do this yet!'

  setupShows: ->
    throw 'I don\'t know how to do this yet!'

  done: ->
    @$el.attr 'data-state', 'fade-in'

_         = require 'underscore'
sd        = require('sharify').data
Backbone  = require 'backbone'

Artworks            = require '../../../collections/artworks.coffee'
LayeredSearchView   = require('./layered-search.coffee').LayeredSearchView
SaleView            = require './sale.coffee'

module.exports = class BelowTheFold extends Backbone.View
  initialize: (options) ->
    { @saved, @artwork } = options

    $.when.apply(null, @artwork.fetchRelatedCollections()).then =>
      # Find the first related collection that has any results
      @availableFeature = _.find @artwork.relatedCollections, (xs) -> xs.length
      if @availableFeature
        @["setup#{@availableFeature.kind}"]()
      else
        # Always fallback to layered search if there are no features available
        @setupLayeredSearch()

  setupLayeredSearch: (options = {}) ->
    { @fair } = options
    new LayeredSearchView el: @$el, artwork: @artwork, fair: @fair
    @done()

  setupSales: ->
    new SaleView el: @$el, sale: @artwork.sales.first(), saved: @saved
    @done()

  setupFairs: ->
    fair = @availableFeature.first()
    @setupLayeredSearch fair: fair

  setupShows: ->
    throw 'I don\'t know how to do this yet!'

  done: ->
    @$el.attr 'data-state', 'fade-in'

Enable the fair setup and pass in the fair to the layered search view


class PanelView extends HTMLElement
  constructor: ->
module.exports = PanelView

class PanelView extends HTMLElement
  constructor: ->
  registerModel: (Model)->
    @Model = Model
    @Model.View = this
  createdCallback: ->
    this.id = 'linter-panel'
  render: (Messages)->

module.exports = PanelView

Add basic methods to PanelView

:new: Add basic methods to PanelView


define (require) ->
  Backbone = require('backbone')
  settings = require('cs!settings')

  SEARCH_URI = "#{location.protocol}//#{settings.cnxarchive.host}:#{settings.cnxarchive.port}/search"

  return class SearchResults extends Backbone.Model
    url: () -> "#{SEARCH_URI}#{@query}"

    defaults:
      query:
        limits: []
        sort: []
      results:
        items: []
        total: 0

    initialize: (options = {}) ->
      @query = options.query or ''
      @fetch
        success: () => @set('loaded', true)

    parse: (response, options) ->
      response = super(arguments...)

      authors = new Backbone.Collection()
      _.each response.results.limits, (limit) ->
        if limit.author then authors.add(limit.author)

      _.each response.results.items, (item) ->
        _.each item.authors, (author, index) ->
          item.authors[index] = authors.get(author).toJSON()

      return response

define (require) ->
  Backbone = require('backbone')
  settings = require('cs!settings')

  SEARCH_URI = "#{location.protocol}//#{settings.cnxarchive.host}:#{settings.cnxarchive.port}/search"

  return class SearchResults extends Backbone.Model
    url: () -> "#{SEARCH_URI}#{@query}"

    defaults:
      query:
        limits: []
        sort: []
      results:
        items: []
        total: 0

    initialize: (options = {}) ->
      @query = options.query or ''
      @fetch
        success: () => @set('loaded', true)

    parse: (response, options) ->
      response = super(arguments...)

      authors = new Backbone.Collection()
      _.each response.results.limits, (limit) ->
        if limit.author then authors.add(limit.author)

      _.each response.results.items, (item) ->
        _.each item.authors, (author, index) ->
          item.authors[index] = authors.get(author).toJSON()

      #response.queryFormatted = _.cloneDeep(response.query)
      response.queryFormatted = JSON.parse(JSON.stringify(response.query)) # HACK to deep clone
      _.each response.queryFormatted.limits, (limit) ->
        if limit.authorID
          author = authors.get(limit.authorID).toJSON()
          limit.authorID = "#{author.fullname} (#{author.id})"

      return response

Format search query to display authorID with author's full name


#= require clipboard

$ ->
  clipboard = new Clipboard '.js-clipboard-trigger',
    text: (trigger) ->
      $target = $(trigger.nextElementSibling || trigger.previousElementSibling)
      $target.data('clipboard-text') || $target.text().trim()

  clipboard.on 'success', (e) ->
    $(e.trigger).
      tooltip(trigger: 'manual', placement: 'auto bottom', title: 'Copied!').
      tooltip('show')

    # Clear the selection and blur the trigger so it loses its border
    e.clearSelection()
    $(e.trigger).blur()

    # Manually hide the tooltip after 1 second
    setTimeout(->
      $(e.trigger).tooltip('hide')
    , 1000)

#= require clipboard

$ ->
  clipboard = new Clipboard '.js-clipboard-trigger',
    text: (trigger) ->
      $target = $(trigger.nextElementSibling || trigger.previousElementSibling)
      $target.data('clipboard-text') || $target.text().trim()

  clipboard.on 'success', (e) ->
    $(e.trigger).
      tooltip(trigger: 'manual', placement: 'auto bottom', title: 'Copied!').
      tooltip('show').
      one('mouseleave', -> $(this).tooltip('hide'))

    # Clear the selection and blur the trigger so it loses its border
    e.clearSelection()
    $(e.trigger).blur()

  # Safari doesn't support `execCommand`, so instead we inform the user to
  # copy manually.
  #
  # See http://clipboardjs.com/#browser-support
  clipboard.on 'error', (e) ->
    if /Mac/i.test(navigator.userAgent)
      title = "Press ⌘-C to copy"
    else
      title = "Press Ctrl-C to copy"

    $(e.trigger).
      tooltip(trigger: 'manual', placement: 'auto bottom', html: true, title: title).
      tooltip('show').
      one('mouseleave', -> $(this).tooltip('hide'))

Add a fallback for Safari copy-to-clipboard

Also, hide the tooltip in a less stupid way.

Closes #3547


".source.viml":
	editor:
		commentStart: '" '
		increaseIndentPattern: "(?:^|\\s)(?:if|else|elseif|for|function!?|while)(?:\\s|$)"
		decreaseIndentPattern: "(?:^|\\s)(?:endif|endfor|endfunction|endwhile)(?:\\s|$)"

Add editor settings for adjusting indent/outdent


$ ->

  $("#slider").slider
    value: 1
    min: 1
    max: 3
    slide: (event, ui) ->
      num = ui.value+1
      verbosity = while num -= 1 then 'v'
      $("#job_verbosity").val verbosity.join('')

  if $('#job-results').size() > 0

    div = $ '#job-results'
    project_id = div.data 'project_id'
    job_id = div.data 'job_id'

    get_job_status = ->
      $.getJSON "/projects/#{project_id}/jobs/#{job_id}", (data) ->
        if data.results == null
          setTimeout get_job_status, 3000
        else
          div.removeClass('hide').html data.results

          title = div.siblings('h3')
          title.find('img').remove()
          text = title.text().replace /Running/, 'Task'
          text = text.replace /\.\.\./, 'completed'
          title.text text

          $('.alert-message').remove()

          if data.success
            $('.tabs .pull-right').html 'SUCCESS'
          else
            $('.tabs .pull-right').html 'FAILED'

    setTimeout get_job_status, 3000

$ ->

  $("#slider").slider
    value: 3
    min: 1
    max: 3
    slide: (event, ui) ->
      num = ui.value+1
      verbosity = while num -= 1 then 'v'
      $("#job_verbosity").val verbosity.join('')

  if $('#job-results').size() > 0

    div = $ '#job-results'
    project_id = div.data 'project_id'
    job_id = div.data 'job_id'

    get_job_status = ->
      $.getJSON "/projects/#{project_id}/jobs/#{job_id}", (data) ->
        if data.results == null
          setTimeout get_job_status, 3000
        else
          div.removeClass('hide').html data.results

          title = div.siblings('h3')
          title.find('img').remove()
          text = title.text().replace /Running/, 'Task'
          text = text.replace /\.\.\./, 'completed'
          title.text text

          $('.alert-message').remove()

          if data.success
            $('.tabs .pull-right').html 'SUCCESS'
          else
            $('.tabs .pull-right').html 'FAILED'

    setTimeout get_job_status, 3000

Set default vebosity to 3

Set default vebosity to 3


$( ->

  $('a.pdf-languages-trigger').click ->
    $(this).toggleClass 'active'
    $('.download-languages').toggleClass 'active'

	categories = [
		{ href: '#tab-essentials-content', contentSelector: '.tab-essentials-content', anchorSelector: '[href="#tab-essentials-content"]' }
		{ href: '#tab-useful-content', contentSelector: '.tab-useful-content', anchorSelector: '[href="#tab-useful-content"]' }
		{ href: '#tab-personal-content', contentSelector: '.tab-personal-content', anchorSelector: '[href="#tab-personal-content"]' }
	]

	showActive categories

	window.onhashchange = -> showActive categories

)

categories = [
	{ href: '#tab-essentials-content', contentSelector: '.tab-essentials-content', anchorSelector: '[href="#tab-essentials-content"]' }
	{ href: '#tab-useful-content', contentSelector: '.tab-useful-content', anchorSelector: '[href="#tab-useful-content"]' }
	{ href: '#tab-personal-content', contentSelector: '.tab-personal-content', anchorSelector: '[href="#tab-personal-content"]' }
]

$(document).ready ->

  $('a.pdf-languages-trigger').click ->
    $(this).toggleClass 'active'
    $('.download-languages').toggleClass 'active'

  $('.tab-useful-content').hide()
  $('.tab-personal-content').hide()

	showActive categories

window.onhashchange = -> showActive categories

Hide useful and personal supplies when there's no hash in the url.


# Secure version of the ledger.storage.ChromeStore. This store uses AES encryption for storing keys and data
class @ledger.storage.SecureStore extends ledger.storage.ChromeStore

  # @param [String] name The store name
  # @param [String] key The secure key used to encrypt and decrypt data with AES
  constructor: (name, key) ->
    super(name)
    @_aes = new ledger.crypto.AES(key)
    @_hasCalledKeys = no
    @_keysCache = []

  # @see Store.keys
  keys: (cb) ->
    if @_hasCalledKeys
      _.defer => cb?(@_keysCache)
    else
      super (encrypted_keys) =>
        @_keysCache = _.uniq(@_keysCache.concat(_.compact(Try(=> @_aes.decrypt(encrypted_key)).orNull() for encrypted_key in encrypted_keys)))
        @_hasCalledKeys = yes
        cb?(@_keysCache)

  _preprocessKey: (key) -> super(@_aes.encrypt(key))
  _deprocessKey: (raw_key) ->
    key = Try(=> @_aes.decrypt(super(raw_key))).orNull()
    @_keysCache.push key unless _(@_keysCache).contains(key)
    key

  _preprocessValue: (value) -> Try(=> @_aes.encrypt(super(value))).orNull()
  _deprocessValue: (raw_value) -> Try(=> super(@_aes.decrypt(raw_value))).orNull()

# Secure version of the ledger.storage.ChromeStore. This store uses AES encryption for storing keys and data
class @ledger.storage.SecureStore extends ledger.storage.ChromeStore

  # @param [String] name The store name
  # @param [String] key The secure key used to encrypt and decrypt data with AES
  constructor: (name, key) ->
    super(name)
    @_aes = new ledger.crypto.AES(key)
    @_hasCalledKeys = no
    @_keysCache = []

  # @see Store.keys
  keys: (cb) ->
    if @_hasCalledKeys
      _.defer => cb?(@_keysCache)
    else
      super (encrypted_keys) =>
        @_keysCache = _.uniq(@_keysCache.concat(_.compact(Try(=> @_aes.decrypt(encrypted_key)).orNull() for encrypted_key in encrypted_keys)))
        @_hasCalledKeys = yes
        cb?(@_keysCache)

  _preprocessKey: (key) ->
    @_keysCache.push key unless _(@_keysCache).contains(key)
    super(@_aes.encrypt(key))

  _deprocessKey: (raw_key) ->
    key = Try(=> @_aes.decrypt(super(raw_key))).orNull()
    @_keysCache.push key unless _(@_keysCache).contains(key)
    key

  _preprocessValue: (value) -> Try(=> @_aes.encrypt(super(value))).orNull()
  _deprocessValue: (raw_value) -> Try(=> super(@_aes.decrypt(raw_value))).orNull()

Fix secure store get keys

Fix secure store get keys


class Link
  constructor: (@page, @$link) ->

  allows_process: (event) ->
    !(this._cross_origin_link(event.currentTarget) ||
      this._non_standard_click(event))

  process: ->
    type = if (@$link.data('push') == 'partial')
      'partial'
    else
      'template'

    @page.load(@$link.attr("href"), @$link.data('target'), type)

  # We split host because IE returns host with port and other browsers not
  #
  _cross_origin_link: (link) ->
    (location.protocol != link.protocol) ||
    (location.host.split(':')[0] != link.host.split(':')[0])

  _non_standard_click: (event) ->
    event.metaKey || event.ctrlKey || event.shiftKey || event.altKey

window._Wiselinks = {} if window._Wiselinks == undefined
window._Wiselinks.Link = Link

class Link
  constructor: (@page, @$link) ->

  allows_process: (event) ->
    !(this._cross_origin_link(event.currentTarget) ||
      this._non_standard_click(event))

  process: ->
    type = if (@$link.data('push') == 'partial')
      'partial'
    else
      'template'

    @page.load(@$link.attr("href"), @$link.data('target'), type)

  # We split host because IE returns host with port and other browsers not
  #
  _cross_origin_link: (link) ->
    (location.protocol != link.protocol) ||
    (location.port != link.port) ||
    (location.host.split(':')[0] != link.host.split(':')[0])

  _non_standard_click: (event) ->
    event.metaKey || event.ctrlKey || event.shiftKey || event.altKey

window._Wiselinks = {} if window._Wiselinks == undefined
window._Wiselinks.Link = Link

Check for port change during link processing


Meteor.startup ->
	Migrations.add
		version: 7
		up: ->

			console.log 'Populate urls in messages'
			query = ChatMessage.find({ 'urls.0': { $exists: true }, urls: { $type: 2 } })
			count = query.count()
			query.forEach (message, index) ->
				console.log "#{index + 1} / #{count}"

				message.urls = message.urls.map (url) ->
					if _.isString url
						return {url: url}
					return url

				OEmbed.RocketUrlParser message

			console.log 'End'

Meteor.startup ->
	Migrations.add
		version: 7
		up: ->

			console.log 'Populate urls in messages'
			query = ChatMessage.find({ 'urls.0': { $exists: true } })
			count = query.count()
			query.forEach (message, index) ->
				console.log "#{index + 1} / #{count}"

				message.urls = message.urls.map (url) ->
					if _.isString url
						return {url: url}
					return url

				OEmbed.RocketUrlParser message

			console.log 'End'

Update migration 7 to interate over all messages with urls


'atom-workspace':
  'cmd-shift-c': 'git:toggle-git-panel'

'.git-StagingView':
  'left': 'git:focus-diff-view'

'.git-CommitView-editor atom-text-editor:not([mini])':
  'cmd-enter': 'git:commit'
  'ctrl-enter': 'git:commit'

'.git-FilePatchView':
  '/': 'git:toggle-patch-selection-mode'
  'tab': 'git:select-next-hunk'
  'shift-tab': 'git:select-previous-hunk'
  'right': 'git:focus-git-panel'

'.git-Prompt-input':
  'enter': 'core:confirm'
  'esc': 'core:cancel'

'.platform-darwin':
  'cmd-shift-c': 'git:toggle-git-panel'

'.platform-win32, .platform-linux':
  'alt-shift-c': 'git:toggle-git-panel'

'.git-StagingView':
  'left': 'git:focus-diff-view'

'.git-CommitView-editor atom-text-editor:not([mini])':
  'cmd-enter': 'git:commit'
  'ctrl-enter': 'git:commit'

'.git-FilePatchView':
  '/': 'git:toggle-patch-selection-mode'
  'tab': 'git:select-next-hunk'
  'shift-tab': 'git:select-previous-hunk'
  'right': 'git:focus-git-panel'

'.git-Prompt-input':
  'enter': 'core:confirm'
  'esc': 'core:cancel'

Add menu and Win32/Linux keybindings

Add menu and Win32/Linux keybindings


{Tool} = require './base'
{createShape} = require '../core/shapes'

module.exports = class Pan extends Tool

  name: 'Pan'
  iconName: 'pan'
  usesSimpleAPI: false

  didBecomeActive: (lc) ->
    unsubscribeFuncs = []
    @unsubscribe = =>
      for func in unsubscribeFuncs
        func()

    unsubscribeFuncs.push lc.on 'lc-pointerdown', ({rawX, rawY}) =>
      @oldPosition = lc.position
      @pointerStart = {x: rawX, y: rawY}

    unsubscribeFuncs.push lc.on 'lc-pointerdrag', ({rawX, rawY}) =>
      dp = {
        x: (rawX - @pointerStart.x),
        y: (rawY - @pointerStart.y)
      }
      lc.setPan(@oldPosition.x + dp.x, @oldPosition.y + dp.y)

  willBecomeInactive: (lc) ->
    @unsubscribe()

{Tool} = require './base'
{createShape} = require '../core/shapes'

module.exports = class Pan extends Tool

  name: 'Pan'
  iconName: 'pan'
  usesSimpleAPI: false

  didBecomeActive: (lc) ->
    unsubscribeFuncs = []
    @unsubscribe = =>
      for func in unsubscribeFuncs
        func()

    unsubscribeFuncs.push lc.on 'lc-pointerdown', ({rawX, rawY}) =>
      @oldPosition = lc.position
      @pointerStart = {x: rawX, y: rawY}

    unsubscribeFuncs.push lc.on 'lc-pointerdrag', ({rawX, rawY}) =>
      # okay, so this is really bad:
      # lc.position is "buggy screen coordinates": correct on non-retina,
      # probably wrong on retina. compensate here; in v0.5 we should put the
      # offset in drawing coordinates.
      dp = {
        x: (rawX - @pointerStart.x) * lc.backingScale,
        y: (rawY - @pointerStart.y) * lc.backingScale
      }
      lc.setPan(@oldPosition.x + dp.x, @oldPosition.y + dp.y)

  willBecomeInactive: (lc) ->
    @unsubscribe()

Fix pan tool on retina

Fix pan tool on retina


# Description:
#   To create something that's… that's genuinely new,
#   you have to… to start again.
#
# Dependencies:
#   None
#
# Configuration:
#   None
#
# Commands:
#   <anything Ivey> - Display an Ive
#
# Author:
#   arbales

ives = [
  "http://www.blogcdn.com/www.engadget.com/media/2012/03/jony-ive-10-20-09.jpg",
  "http://betanews.com/wp-content/uploads/media/60/6057.png",
  "http://www.socialgazelle.com/wp-content/uploads/2012/05/jonathan-ive.jpg",
  "http://www3.pcmag.com/media/images/238530-jonathan-ive.jpg",
  "http://farm5.static.flickr.com/4089/5213864251_6a686844ca.jpg",
  "http://www.ipadforums.net/wallpapers/data/500/jonathan_ive.JPG",
  "http://www.wearesuperfamous.com/wp-content/jonny_ive11.jpg"
]

module.exports = (robot) ->
  robot.hear /.*(essential|elegant|efficient|experience|incredible|meticulous|unibody|from the ground up).*/i, (msg) ->
    msg.send msg.random ives

# Description:
#   To create something that's… that's genuinely new,
#   you have to… to start again.
#
# Dependencies:
#   None
#
# Configuration:
#   None
#
# Commands:
#   <anything Ivey> - Display an Ive
#
# Author:
#   arbales

ives = [
  "http://www.blogcdn.com/www.engadget.com/media/2012/03/jony-ive-10-20-09.jpg",
  "http://betanews.com/wp-content/uploads/media/60/6057.png",
  "http://www.socialgazelle.com/wp-content/uploads/2012/05/jonathan-ive.jpg",
  "http://www3.pcmag.com/media/images/238530-jonathan-ive.jpg",
  "http://farm5.static.flickr.com/4089/5213864251_6a686844ca.jpg",
  "http://www.ipadforums.net/wallpapers/data/500/jonathan_ive.JPG",
  "http://www.wearesuperfamous.com/wp-content/jonny_ive11.jpg"
]

module.exports = (robot) ->
  robot.hear /(essential|elegant|efficient|experience|incredible|meticulous|unibody|from the ground up)/i, (msg) ->
    msg.send msg.random ives

Remove superflous additions to Jony's regex per @tombell.


Page  = require 'controllers/page'
Api = require 'zooniverse/lib/api'

class HomePage extends Page
  el: $('.home')
  className: 'home'
  template: require 'views/home'

  elements:
    '.participants': 'participants'
    '.images': 'images'

  constructor: ->
    super
    @html @template

    Api.current.get '/projects/spacewarp', (project) =>
      @participants.html @formatNumber project.user_count
      @images.html @formatNumber project.classification_count

  formatNumber: (n) ->
    n.toString().replace /(\d)(?=(\d{3})+(?!\d))/g, '$1,'

module.exports = HomePage

Page  = require 'controllers/page'
Api = require 'zooniverse/lib/api'

FILMING = true

class HomePage extends Page
  el: $('.home')
  className: 'home'
  template: require 'views/home'

  elements:
    '.participants': 'participants'
    '.images': 'images'

  constructor: ->
    super
    @html @template

    Api.current.get '/projects/spacewarp', (project) =>
      if FILMING
        @participants.html 0
        @images.html @formatNumber 0
      else
        @participants.html @formatNumber project.user_count
        @images.html @formatNumber project.classification_count

  formatNumber: (n) ->
    n.toString().replace /(\d)(?=(\d{3})+(?!\d))/g, '$1,'

module.exports = HomePage

Set metrics to zero for now

Set metrics to zero for now


Import = require '../lib/import'
CONS = require '../lib/constants'
fs = require 'fs'
argv = require('optimist')
  .usage('Usage: $0 --projectKey key --clientId id --clientSecret secret --csv file --language lang --publish')
  .default('lang', 'en')
  .describe('projectKey', 'your SPHERE.IO project-key')
  .describe('clientId', 'your OAuth client id for the SPHERE.IO API')
  .describe('clientSecret', 'your OAuth client secret for the SPHERE.IO API')
  .describe('csv', 'CSV file containing products to import')
  .describe('language', 'Default language to using during import')
  .describe('publish', 'When given, all changes will be published immediately')
  .demand(['projectKey', 'clientId', 'clientSecret', 'csv'])
  .argv

timeout = argv.timeout
timeout or= 60000

CONS.DEFAULT_LANGUAGE = argv.language

options =
  config:
    project_key: argv.projectKey
    client_id: argv.clientId
    client_secret: argv.clientSecret
  timeout: timeout
  show_progress: true

importer = new Import options
importer.publishProducts = argv.publish

fs.readFile argv.csv, 'utf8', (err, content) ->
  if err
    console.error "Problems on reading file '#{argv.csv}': " + err
    process.exit 2

  importer.import content, (result) ->
    console.log result.message
    process.exit 0 if result.status is true
    process.exit 1

Import = require '../lib/import'
package_json = require '../package.json'
CONS = require '../lib/constants'
fs = require 'fs'
argv = require('optimist')
  .usage('Usage: $0 --projectKey key --clientId id --clientSecret secret --csv file --language lang --publish')
  .default('lang', 'en')
  .describe('projectKey', 'your SPHERE.IO project-key')
  .describe('clientId', 'your OAuth client id for the SPHERE.IO API')
  .describe('clientSecret', 'your OAuth client secret for the SPHERE.IO API')
  .describe('csv', 'CSV file containing products to import')
  .describe('language', 'Default language to using during import')
  .describe('publish', 'When given, all changes will be published immediately')
  .demand(['projectKey', 'clientId', 'clientSecret', 'csv'])
  .argv

timeout = argv.timeout
timeout or= 60000

CONS.DEFAULT_LANGUAGE = argv.language

options =
  config:
    project_key: argv.projectKey
    client_id: argv.clientId
    client_secret: argv.clientSecret
  timeout: timeout
  show_progress: true
  user_agent: "#{package_json.name} - #{package_json.version}"

importer = new Import options
importer.publishProducts = argv.publish

fs.readFile argv.csv, 'utf8', (err, content) ->
  if err
    console.error "Problems on reading file '#{argv.csv}': " + err
    process.exit 2

  importer.import content, (result) ->
    console.log result.message
    process.exit 0 if result.status is true
    process.exit 1

Send name and version as user agent.

Send name and version as user agent.


FS = require 'fs'
Q = require 'q'

###
Class for generating JSON product type representations from CSV files.
###
class ProductTypeGenerator

  _options = {}

  constructor: (options = {}) ->
    @_options = options

  ###
  Creates sphere product type representation files using JSON format.
  @param {array} types Entire types CSV as an array of records.
  @param {array} attributes Entire attributes CSV as an array of records.
  @param {function} callback The callback function to be invoked when the method finished its work.
  @return Result of the given callback
  ###
  run: (types, attributes, callback) ->
    callback true

module.exports = ProductTypeGenerator

FS = require 'fs'
Q = require 'q'

###
Class for generating JSON product type representations from CSV files.
###
class ProductTypeGenerator

  constructor: (options = {}) ->
    @_options = options

  ###
  Creates sphere product type representation files using JSON format.
  @param {array} types Entire types CSV as an array of records.
  @param {array} attributes Entire attributes CSV as an array of records.
  @param {function} callback The callback function to be invoked when the method finished its work.
  @return Result of the given callback
  ###
  run: (types, attributes, callback) ->
    callback true

module.exports = ProductTypeGenerator

Remove explicit variable declaration as its done by cs anyhow.


MinimapSelectionView = require './minimap-selection-view'

module.exports =

  active: false
  views: {}

  activate: (state) ->
    try
      atom.packages.activatePackage('minimap').then (minimapPackage) =>
        @minimap = minimapPackage.mainModule
        return @deactivate() unless @minimap.versionMatch('>= 3.5.0')

        @minimap.registerPlugin 'selection', this
    catch
      @deactivate

  deactivate: ->
    @minimap.unregisterPlugin 'selection'
    @minimap = null

  isActive: -> @active

  activatePlugin: ->
    return if @active
    @active = true

    @subscription = @minimap.observeMinimaps (o) =>
      minimap = o.view ? o
      selectionView = new MinimapSelectionView(minimap)

      @views[minimap.getTextEditor().id] = selectionView

  deactivatePlugin: ->
    return unless @active
    view.destroy() for id,view of @views
    @active = false
    @views = {}

    @subscription.off()

MinimapSelectionView = require './minimap-selection-view'

module.exports =

  active: false
  views: {}

  activate: (state) ->
    try
      atom.packages.activatePackage('minimap').then (minimapPackage) =>
        @minimap = minimapPackage.mainModule
        return @deactivate() unless @minimap.versionMatch('>= 3.5.0')

        @minimap.registerPlugin 'selection', this
    catch
      @deactivate

  deactivate: ->
    @minimap.unregisterPlugin 'selection'
    @minimap = null

  isActive: -> @active

  activatePlugin: ->
    return if @active
    @active = true

    @subscription = @minimap.observeMinimaps (o) =>
      minimap = o.view ? o
      selectionView = new MinimapSelectionView(minimap)

      @views[minimap.getTextEditor().id] = selectionView

  deactivatePlugin: ->
    return unless @active
    view.destroy() for id,view of @views
    @active = false
    @views = {}

    @subscription.dispose()

Remove call to deprecated method

:bug: Remove call to deprecated method


data =
  metrics:
    anon:
      colour: 'orange', number: 'loading...', title: 'Anonymous Visitors'
    ,
    normal:
      colour: 'blue', number: 'loading...', title: 'Normal Visitors'
    ,
    premium:
      colour: 'green', number: 'loading...', title: 'Premium Visitors'

source = $('#metric-block-template').html()
template = Handlebars.compile source
$('.metric-channel-blocks').html template(data)

socket = io.connect "http://#{window.location.hostname}"
socket.emit 'ready'
socket.on 'metric', (payload) ->
  for key of payload.metrics
    data.metrics[key].number = payload.metrics[key]

  $('.metric-channel-blocks').html template(data)

data =
  metrics:
    anon:
      colour: 'orange', number: '...', title: 'Anonymous Visitors'
    ,
    normal:
      colour: 'blue', number: '...', title: 'Logged-in Visitors'
    ,
    premium:
      colour: 'green', number: '...', title: 'Premium Visitors'

source = $('#metric-block-template').html()
template = Handlebars.compile source
$('.metric-channel-blocks').html template(data)

socket = io.connect "http://#{window.location.hostname}"
socket.emit 'ready'
socket.on 'metric', (payload) ->
  for key of payload.metrics
    data.metrics[key].number = payload.metrics[key]

  $('.metric-channel-blocks').html template(data)

Rename normal users to logged-in users, loading now ...


Add comment heading to document ready

Add comment heading to document ready


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

MESSAGES = {
  student: [

Tutor shows your weakest topics so you can practice to improve.

Try to get all of your topics to green!

  ]
  teacher: [

Tutor shows the weakest topics for each period.

Students may need your help in those areas.

  ]
  teacher_student: [

Tutor shows the weakest topics for the student.

They may need your help in those areas.

  ]
}

module.exports = React.createClass

  displayName: 'LearningGuideInfoLink'
  type: React.PropTypes.oneOf(['student', 'teacher', 'teacher_student']).isRequired

  render: ->
    tooltip =
    <BS.Tooltip className='info-link-tooltip' html='true'>

The performance forecast is an estimate of your understanding of a topic.

It is personalized display based on your answers to reading questions,
      homework problems, and previous practices.

      {MESSAGES[@props.type]}
    </BS.Tooltip>

    <BS.OverlayTrigger placement='right' overlay={tooltip}>

    </BS.OverlayTrigger>

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

MESSAGES = {
  student: [

The performance forecast is an estimate of your understanding of a topic.

        It is personalized display based on your answers to reading questions,
        homework problems, and previous practices.

  ]
  teacher: [

The performance forecast is an estimate of each period's understanding of a topic.

        It is personalized display based on their answers to reading questions,
        homework problems, and previous practices.

  ]
  teacher_student: [

The performance forecast is an estimate of each student's understanding of a topic.

        It is personalized display based on their answers to reading questions,
        homework problems, and previous practices.

  ]
}

module.exports = React.createClass

  displayName: 'LearningGuideInfoLink'
  type: React.PropTypes.oneOf(['student', 'teacher', 'teacher_student']).isRequired

  render: ->
    tooltip =
    <BS.Tooltip className='info-link-tooltip' html='true'>
      {MESSAGES[@props.type]}
    </BS.Tooltip>

    <BS.OverlayTrigger placement='right' overlay={tooltip}>

    </BS.OverlayTrigger>

Set messages to correct phrases

Set messages to correct phrases


Trix.galleryFilter = (snapshot) ->
  filter = new Filter snapshot
  filter.perform()
  filter.getSnapshot()

class Filter
  constructor: (snapshot) ->
    {@document, @selectedRange} = snapshot

  perform: ->
    @removeGalleryAttribute()
    @applyGalleryAttribute()

  getSnapshot: ->
    {@document, @selectedRange}

  # Private

  removeGalleryAttribute: ->
    for range in @findRangesOfGalleryBlocks()
      @document = @document.removeAttributeAtRange("gallery", range)

  applyGalleryAttribute: ->
    offset = 0
    for range in @findRangesOfGalleryPieces() when range[1] - range[0] > 1
      range[0] += offset
      range[1] += offset

      unless @document.getCharacterAtPosition(range[1]) is "\n"
        @document = @document.insertBlockBreakAtRange(range[1])
        offset++

      unless range[0] is 0
        unless @document.getCharacterAtPosition(range[0] - 1) is "\n"
          @document = @document.insertBlockBreakAtRange(range[0])
          offset++

      @document = @document.applyBlockAttributeAtRange("gallery", true, range)

  findRangesOfGalleryBlocks: ->
    @document.findRangesForBlockAttribute("gallery")

  findRangesOfGalleryPieces: ->
    @document.findRangesForTextAttribute("presentation", withValue: "gallery")

Trix.galleryFilter = (snapshot) ->
  filter = new Filter snapshot
  filter.perform()
  filter.getSnapshot()

class Filter
  constructor: (snapshot) ->
    {@document, @selectedRange} = snapshot

  perform: ->
    @removeGalleryAttribute()
    @applyGalleryAttribute()

  getSnapshot: ->
    {@document, @selectedRange}

  # Private

  removeGalleryAttribute: ->
    for range in @findRangesOfGalleryBlocks()
      @document = @document.removeAttributeAtRange("gallery", range)

  applyGalleryAttribute: ->
    offset = 0
    for range in @findRangesOfGalleryPieces() when range[1] - range[0] > 1
      range[0] += offset
      range[1] += offset

      unless @document.getCharacterAtPosition(range[1]) is "\n"
        @document = @document.insertBlockBreakAtRange(range[1])
        @moveSelectedRangeForward() if range[1] < @selectedRange[1]
        offset++

      unless range[0] is 0
        unless @document.getCharacterAtPosition(range[0] - 1) is "\n"
          @document = @document.insertBlockBreakAtRange(range[0])
          @moveSelectedRangeForward() if range[0] < @selectedRange[0]
          offset++

      @document = @document.applyBlockAttributeAtRange("gallery", true, range)

  findRangesOfGalleryBlocks: ->
    @document.findRangesForBlockAttribute("gallery")

  findRangesOfGalleryPieces: ->
    @document.findRangesForTextAttribute("presentation", withValue: "gallery")

  moveSelectedRangeForward: ->
    @selectedRange[0] += 1
    @selectedRange[1] += 1

Adjust selection when inserting gallery block breaks


#= require ./abstract_binding

class Batman.DOM.RouteBinding extends Batman.DOM.AbstractBinding
  onAnchorTag: false
  onlyObserve: Batman.BindingDefinitionOnlyObserve.Data

  @accessor 'dispatcher', ->
    @view.lookupKeypath('dispatcher') || Batman.App.get('current.dispatcher')

  bind: ->
    if (@node.nodeName is 'A' || @node.nodeName is 'a')
      @onAnchorTag = true

    super

    return if @onAnchorTag && @node.getAttribute('target')
    Batman.DOM.events.click(@node, @routeClick)

  routeClick: (node, event) =>
    return if event.__batmanActionTaken
    event.__batmanActionTaken = true
    params = @pathFromValue(@get('filteredValue'))
    Batman.redirect(params) if params?

  dataChange: (value) ->
    if value
      path = @pathFromValue(value)

    if @onAnchorTag
      if path and Batman.navigator
        path = Batman.navigator.linkTo(path)
      else
        path = "#"

      @node.href = path

  pathFromValue: (value) ->
    if value
      if value.isNamedRouteQuery
        value.get('path')
      else
        @get('dispatcher')?.pathFromParams(value)

#= require ./abstract_binding

class Batman.DOM.RouteBinding extends Batman.DOM.AbstractBinding
  onAnchorTag: false
  onlyObserve: Batman.BindingDefinitionOnlyObserve.Data

  @accessor 'dispatcher', ->
    @view.lookupKeypath('dispatcher') || Batman.App.get('current.dispatcher')

  bind: ->
    if @node.nodeName in ['a','A']
      @onAnchorTag = true

    super

    return if @onAnchorTag && @node.getAttribute('target')
    Batman.DOM.events.click(@node, @routeClick)

  routeClick: (node, event) =>
    return if event.__batmanActionTaken
    event.__batmanActionTaken = true
    params = @pathFromValue(@get('filteredValue'))
    Batman.redirect(params) if params?

  dataChange: (value) ->
    if value
      path = @pathFromValue(value)

    if @onAnchorTag
      if path and Batman.navigator
        path = Batman.navigator.linkTo(path)
      else
        path = "#"

      @node.href = path

  pathFromValue: (value) ->
    if value
      if value.isNamedRouteQuery
        value.get('path')
      else
        @get('dispatcher')?.pathFromParams(value)

Switch to using in so coffeescript can optimize


class @Overlay
  constructor: (contentSelector, options) ->
    @contentSelector = contentSelector
    @options = options || {}
    defaults =
      domType: OverlayDomWrapper
      dimensionsType: OverlayDimensions
    @options = $.extend(defaults, @options)
    @dom = new @options.domType()
    $('body').append(@dom.el).append(@dom.mask)

    @createDimensions = ->
      new @options.dimensionsType(@originalContent)

  open: (target) ->
    @prepareOpen(target)
    @setDimensions()
    @show()

  prepareOpen: (target = document) ->
    @dom.mask.show()
    @lockScroll()
    @originalContent = $(target).find(@contentSelector)
    @overlayContent = @originalContent.clone()
    @dom.el.append(@overlayContent)
    @dimensions = @createDimensions()

  setDimensions: ->
    @dom.el.css('left', @dimensions.margin())
    @dom.el.css('top', @dimensions.top())
    @dom.el.width(@dimensions.width())
    @dom.closeButton().css('left', @dimensions.closeButtonLeftPosition())

  show: ->
    @overlayContent.show()
    @dom.el.show()
    @overlayContent.trigger('overlay:show')

  setMaskHeight: ->
    @dom.mask.height($(document).height())

  lockScroll: ->
    $('body').addClass('scroll-lock') unless window.mobileLayout()

  close: ->
    @dom.el.empty().hide()
    @dom.mask.hide()
    $('body').removeClass('scroll-lock')

class @Overlay
  constructor: (contentSelector, options) ->
    @contentSelector = contentSelector
    @options = options || {}
    defaults =
      domType: OverlayDomWrapper
      dimensionsType: OverlayDimensions
    @options = $.extend(defaults, @options)
    @dom = new @options.domType()
    $('body').append(@dom.el).append(@dom.mask)

    @createDimensions = ->
      new @options.dimensionsType(@originalContent)

  open: (target) ->
    @prepareOpen(target)
    @setDimensions()
    @show()

  prepareOpen: (target = document) ->
    @dom.mask.show()
    @lockScroll()
    @originalContent = $(target).find(@contentSelector)
    @overlayContent = @originalContent.clone()
    @dom.el.append(@overlayContent)
    @dimensions = @createDimensions()

  setDimensions: ->
    @dom.el.css('left', @dimensions.margin())
    @dom.el.css('top', @dimensions.top())
    @dom.el.width(@dimensions.width())
    @dom.mask.height('100%')
    @dom.closeButton().css('left', @dimensions.closeButtonLeftPosition())

  show: ->
    @overlayContent.show()
    @dom.el.show()
    @overlayContent.trigger('overlay:show')

  setMaskHeight: ->
    @dom.mask.height($(document).height())

  lockScroll: ->
    $('body').addClass('scroll-lock') unless window.mobileLayout()

  close: ->
    @dom.el.empty().hide()
    @dom.mask.hide()
    $('body').removeClass('scroll-lock')

Reset mask height on close

Reset mask height on close

Otherwise it will never shrink after opening long content


#= require env
#= require jquery
#= require jquery/jquery-migrate
#= require jquery/jquery.cookies.js
#= require i18n.js
#= require bootstrap
#= require bootstrap-select
#= require custom-bootstrap.js.coffee
#= require function
#= require map-overlay.js
#= require handlebars
#= require ember
#= require ember-data
#= require spin
#= require leaflet/leaflet-src
#= require leaflet/leaflet.locate
#= require ember-leaflet
#= require jquery/jquery.magnific-popup.js
#= require responsive-popover
#= require dropzone
#= require_self
#= require wheelmap

I18n.defaultLocale = 'de'
I18n.locale = $('html').attr('lang')
I18n.fallbacks = true

Ember.Application.reopen
  # Transition end event
  customEvents: (()->
    customEvents = {}

    if $.support.transition?
      customEvents[$.support.transition.end] = 'transitionEnd'

    customEvents
  ).property()

Wheelmap = @Wheelmap = Ember.Application.create
  # LOG_TRANSITIONS: true
  rootElement: '#wheelmap'

DS.RESTAdapter.reopen
  buildURL: (type, id)->
    # Needed for not having wrong cached urls
    @_super(type, id) + '.json'

Wheelmap.Router.reopen
  rootURL: '/map/'

#= require env
#= require jquery
#= require jquery/jquery-migrate
#= require jquery/jquery.cookies.js
#= require i18n.js
#= require bootstrap
#= require bootstrap-select
#= require custom-bootstrap.js.coffee
#= require function
#= require map-overlay.js
#= require handlebars
#= require ember
#= require ember-data
#= require spin
#= require leaflet/leaflet-src
#= require leaflet/leaflet.locate
#= require ember-leaflet
#= require jquery/jquery.magnific-popup.js
#= require responsive-popover
#= require dropzone
#= require_self
#= require wheelmap

I18n.defaultLocale = 'en'
I18n.locale = $('html').attr('lang')
I18n.fallbacks = true

Ember.Application.reopen
  # Transition end event
  customEvents: (()->
    customEvents = {}

    if $.support.transition?
      customEvents[$.support.transition.end] = 'transitionEnd'

    customEvents
  ).property()

Wheelmap = @Wheelmap = Ember.Application.create
  # LOG_TRANSITIONS: true
  rootElement: '#wheelmap'

DS.RESTAdapter.reopen
  buildURL: (type, id)->
    # Needed for not having wrong cached urls
    @_super(type, id) + '.json'

Wheelmap.Router.reopen
  rootURL: '/map/'

Change default local for JS I18n.

Change default local for JS I18n.


express = require('express')

module.exports = ->
  router = express.Router()
  router.get '/framestream', (req, res) ->
    req.socket.setTimeout Infinity

    messageCount = 1
    subscriberCount = 0

    subscriber = require('./redis_client')()

    subscriber.subscribe 'updates'

    subscriber.on 'error', (err) ->
      console.error('Redis Error: ' + err)

    subscriber.on 'subscribe', (err) ->
      subscriberCount++

    subscriber.on "message", (channel, message) ->
      messageCount++
      res.write 'id: ' + messageCount + '\n'
      res.write 'data: ' + message + '\n\n'

    res.writeHead 200,
      'Content-Type': 'text/event-stream'
      'Cache-Control': 'no-cache'
      'Connection': 'keep-alive'

    res.write '\n'

    req.on 'close', ->
      subscriberCount--
      subscriber.unsubscribe()
      subscriber.quit()
  router

express = require('express')

module.exports = ->
  router = express.Router()
  router.get '/framestream', (req, res) ->
    req.socket.setTimeout 1000*1000

    messageCount = 1
    subscriberCount = 0

    subscriber = require('./redis_client')()

    subscriber.subscribe 'updates'

    subscriber.on 'error', (err) ->
      console.error('Redis Error: ' + err)

    subscriber.on 'subscribe', (err) ->
      subscriberCount++

    subscriber.on "message", (channel, message) ->
      messageCount++
      res.write 'id: ' + messageCount + '\n'
      res.write 'data: ' + message + '\n\n'

    res.writeHead 200,
      'Content-Type': 'text/event-stream'
      'Cache-Control': 'no-cache'
      'Connection': 'keep-alive'

    res.write '\n'

    req.on 'close', ->
      subscriberCount--
      subscriber.unsubscribe()
      subscriber.quit()
  router

Set finite timeout for framestream

Set finite timeout for framestream


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

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

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

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

  defaultColumn: 80

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

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

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

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

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

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

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

  defaultColumn: 80

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

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

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

Set left as number without px suffix

Set left as number without px suffix


'context-menu':
  '.tree-view':
    'Add file': 'tree-view:add'

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

Add rename and delete menu items

Add rename and delete menu items


Neighborly.Users ?= {}

Neighborly.Users.Payments =
  modules: -> []
  init: Backbone.View.extend
    el: '.user-payments-content'

    initialize: ->
      $accounts = $(".account-method")

      for account in $accounts
        $account = $(account)
        if $account.data('path')
          $.get($account.data('path')).success (data) =>
            $account.html data
            Initjs.initializePartial()

            $('.use-existing-item input:not(#payment_show_bank_existing)')
              .attr('disabled', '1')

Disable inputs in "My bank account" configuration form under settings


define ['lib/amd/Backbone','neo4j/webadmin/modules/databrowser/views/PropertyContainerView'], (Backbone, PropertyContainerView) ->

  describe "PropertyContainerView", ->
    it "recognizes non-quoted strings as strings", ->
      pcv = new PropertyContainerView(template:null)

      expect(pcv.shouldBeConvertedToString "a").toBe(true)
      expect(pcv.shouldBeConvertedToString "åäö").toBe(true)
      expect(pcv.shouldBeConvertedToString "åäö #$ asd  ").toBe(true)
      expect(pcv.shouldBeConvertedToString ";åäö #$ asd  ").toBe(true)

      expect(pcv.shouldBeConvertedToString "1").toBe(false)
      expect(pcv.shouldBeConvertedToString "12").toBe(false)
      expect(pcv.shouldBeConvertedToString "12.523").toBe(false)

      expect(pcv.shouldBeConvertedToString "['1','2','3']").toBe(false)
      expect(pcv.shouldBeConvertedToString "[1,2,3]").toBe(false)

      expect(pcv.shouldBeConvertedToString '"a quoted string"').toBe(false)

define ['lib/amd/Backbone','neo4j/webadmin/modules/databrowser/views/PropertyContainerView'], (Backbone, PropertyContainerView) ->

  # TODO: Refactor out the "shouldBeConvertedToString" into it's own class
  describe "PropertyContainerView", ->
    it "recognizes ascii characters as strings", ->
      pcv = new PropertyContainerView(template:null)

      expect(pcv.shouldBeConvertedToString "a").toBe(true)
      expect(pcv.shouldBeConvertedToString "abcd123 ").toBe(true)

    it "recognizes odd characters as strings", ->
      pcv = new PropertyContainerView(template:null)

      expect(pcv.shouldBeConvertedToString "åäö").toBe(true)
      expect(pcv.shouldBeConvertedToString "åäö #$ asd  ").toBe(true)
      expect(pcv.shouldBeConvertedToString ";åäö #$ asd  ").toBe(true)

    it "recognizes valid JSON values as not being strings", ->
      pcv = new PropertyContainerView(template:null)

      expect(pcv.shouldBeConvertedToString "1").toBe(false)
      expect(pcv.shouldBeConvertedToString "12").toBe(false)
      expect(pcv.shouldBeConvertedToString "12.523").toBe(false)

      expect(pcv.shouldBeConvertedToString "['1','2','3']").toBe(false)
      expect(pcv.shouldBeConvertedToString "[1,2,3]").toBe(false)

      expect(pcv.shouldBeConvertedToString '"a quoted string"').toBe(false)

Split webadmin string recognizer into multiple tests.


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

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

'.platform-linux':
  'ctrl-i': 'go-to-line:toggle'

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

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

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

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

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

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

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

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

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

:penguin: Use ctrl-g for linux. Join selectors with the same keybinding.


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

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

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

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

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

'.table-edit':
  'tab': 'table-edit:move-right'
  'shift-tab': 'table-edit:move-left'

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

Add binding for moving left and right with tab


noflo = require 'noflo'
unless noflo.isBrowser()
  chai = require 'chai' unless chai
  ReadFile = require '../components/ReadFile.coffee'
else
  ReadFile = require 'noflo-browserfile/components/ReadFile.js'

describe 'ReadFile component', ->
  c = null
  ins = null
  out = null
  beforeEach ->
    c = ReadFile.getComponent()
    ins = noflo.internalSocket.createSocket()
    out = noflo.internalSocket.createSocket()
    c.inPorts.in.attach ins
    c.outPorts.out.attach out

  describe 'when instantiated', ->
    it 'should have an input port', ->
      chai.expect(c.inPorts.in).to.be.an 'object'
    it 'should have an output port', ->
      chai.expect(c.outPorts.out).to.be.an 'object'

noflo = require 'noflo'
unless noflo.isBrowser()
  chai = require 'chai'
  path = require 'path'
  baseDir = path.resolve __dirname, '../'
else
  baseDir = 'noflo-browserfile'

describe 'ReadFile component', ->
  c = null
  ins = null
  out = null
  before (done) ->
    @timeout 4000
    loader = new noflo.ComponentLoader baseDir
    loader.load 'browserfile/ReadFile', (err, instance) ->
      return done err if err
      c = instance
      ins = noflo.internalSocket.createSocket()
      c.inPorts.file.attach ins
      done()
  beforeEach ->
    out = noflo.internalSocket.createSocket()
    c.outPorts.out.attach out
  afterEach ->
    c.outPorts.out.detach out

  describe 'when instantiated', ->
    it 'should have an file port', ->
      chai.expect(c.inPorts.file).to.be.an 'object'
    it 'should have an output port', ->
      chai.expect(c.outPorts.out).to.be.an 'object'

Use ComponentLoader to get instance

Use ComponentLoader to get instance


import * as Preact from 'preact'

VNode = Preact.h('a', null).constructor

export createElement = (element, props, children...) ->
  if props? && (Array.isArray(props) || typeof props != 'object' || props instanceof VNode)
    children.unshift props
    props = {}

  Preact.h element, props, ...children

export class Component extends Preact.Component
  constructor: ->
    super()

import * as Preact from 'preact'

export createElement = (element, props, children...) ->
  if props? && (Array.isArray(props) || typeof props != 'object' || props.props?)
    children.unshift props
    props = {}

  Preact.h element, props, ...children

export class Component extends Preact.Component
  constructor: ->
    super()

Fix workaround for new Preact 10 release

There is VNode class anymore.


# modules
Game = require '../models/game'

# CONTROLLER
# ----------
module.exports = (app) ->

    # GET /api/games
    app.get '/api/games/', 'games.list', (req, res) ->
        list = []
        Game.findAll().then (games) ->
            list.push formatGame(game) for game in games
            res.json list

    # GET /api/games/:id
    app.get '/api/games/:id', 'games.get', (req, res) ->
        res.findOrFail Game, req.params.id, (game) ->
            res.json formatGame(game)

# METHODS
# -------

formatGame = (game) ->
    result = game
    return result

# modules
Game = require '../models/game'

gameController = require '../controllers/game_controller'

# CONTROLLER
# ----------
module.exports = (app) ->

    # GET /api/games
    app.get '/api/games/', 'games.list', (req, res) ->
        list = []
        Game.findAll().then (games) ->
            list.push formatGame(game) for game in games
            res.json list

    # GET /api/games/current
    app.get '/api/games/current', 'games.current', (req, res) ->
        gameController.currentGame (currentGame) ->
            if currentGame
                res.json formatGame(currentGame)
            else res.notFoundError()

    # GET /api/games/:id
    app.get '/api/games/:id', 'games.get', (req, res) ->
        res.findOrFail Game, req.params.id, (game) ->
            res.json formatGame(game)

# METHODS
# -------

formatGame = (game) ->
    result = game
    return result

Add api getter for current game

Add api getter for current game


angular.module("doubtfire.common.pdf-viewer", [])
.directive('pdfViewer', ->
  restrict: 'E'
  templateUrl: 'common/pdf-panel/pdf-panel.tpl.html'
  scope:
    pdfUrl: '='
  controller: ($scope, $sce, $timeout) ->
    $scope.isSafari = navigator.userAgent.indexOf("Safari") > 0 && navigator.userAgent.indexOf("Chrome") == -1
    # Watch the URL, and hide the view if it hasn't loaded
    $scope.$watch 'pdfUrl', (newUrl) ->
      return unless newUrl?
      $scope.shouldShowIframe = false
      # Add a timeout to reset the iframe
      $timeout (nowLoading) ->
        $scope.shouldShowIframe = true
      $scope.googleDocsUrl = $sce.trustAsResourceUrl "https://docs.google.com/gview?url=#{newUrl}&embedded=true"
)

angular.module("doubtfire.common.pdf-viewer", [])
#
# Basic PDF viewer
#
.directive('pdfViewer', ->
  restrict: 'E'
  templateUrl: 'common/pdf-viewer/pdf-viewer.tpl.html'
  scope:
    pdfUrl: '='
  controller: ($scope, $sce, $timeout) ->
    $scope.isSafari = navigator.userAgent.indexOf("Safari") > 0 && navigator.userAgent.indexOf("Chrome") == -1
    # Watch the URL, and hide the view if it hasn't loaded
    $scope.$watch 'pdfUrl', (newUrl) ->
      return unless newUrl?
      $scope.shouldShowIframe = false
      # Add a timeout to reset the iframe
      $timeout (nowLoading) ->
        $scope.shouldShowIframe = true
      $scope.googleDocsUrl = $sce.trustAsResourceUrl "https://docs.google.com/gview?url=#{newUrl}&embedded=true"
)

Add correct template URL to PDF viewer

FIX: Add correct template URL to PDF viewer


describe 'Indie', ->
  Validate = require('../lib/validate')
  Indie = require('../lib/indie')
  indie = null

  beforeEach ->
    indie?.dispose()
    indie = new Indie({})

  describe 'Validations', ->
    it 'just cares about a name', ->
      linter = {}
      Validate.linter(linter, true)
      expect(linter.name).toBe(null)
      linter.name = 'a'
      Validate.linter(linter, true)
      expect(linter.name).toBe('a')
      linter.name = 2
      expect ->
        Validate.linter(linter, true)
      .toThrow()

  describe 'constructor', ->
    it 'sets a scope for message registry to know', ->
      expect(indie.scope).toBe('project')

  describe '{set, delete}Messages', ->
    it 'notifies the event listeners of the change', ->
      listener = jasmine.createSpy('indie.listener')
      messages = [{}]
      indie.onDidUpdateMessages(listener)
      indie.setMessages(messages)
      expect(listener).toHaveBeenCalled()
      expect(listener.calls.length).toBe(1)
      expect(listener).toHaveBeenCalledWith(messages)
      indie.deleteMessages()
      expect(listener.calls.length).toBe(2)
      expect(listener.mostRecentCall.args[0] instanceof Array)
      expect(listener.mostRecentCall.args[0].length).toBe(0)

  describe 'dispose', ->
    it 'triggers the onDidDestroy event', ->
      listener = jasmine.createSpy('indie.destroy')
      indie.onDidDestroy(listener)
      indie.disposeS()
      expect(listener).toHaveBeenCalled()

Add more specs for indie

:new: Add more specs for indie


# For more information see: http://emberjs.com/guides/routing/
ETahi.Router.map ()->
  @route('flow_manager')
  @resource 'paper', { path: '/papers/:paper_id' }, ->
    @route('edit')
    @route('manage')
    @route('submit')

  @route('task', {path: '/papers/:paper_id/tasks/:task_id'})
  @route('paper_new', { path: '/papers/new' })
  @route('signin', {path: '/users/sign_in'})
  @route('signup', {path: '/users/sign_up'})
  @route('profile', {path: '/profile'})

  @resource('affiliation')
  @resource('author')

  @resource 'admin', ->
    @resource 'journal', path: '/journals/:journal_id', ->
      @resource 'manuscript_manager_template', path: '/manuscript_manager_templates', ->
        @route('new')
        @route('edit', path: '/:template_id/edit')

ETahi.Router.reopen
  rootURL: '/'
  location: 'history'

# For more information see: http://emberjs.com/guides/routing/
ETahi.Router.map ()->
  @route('flow_manager')
  @resource 'paper', { path: '/papers/:paper_id' }, ->
    @route('edit')
    @route('manage')
    @route('submit')

  @route('task', {path: '/papers/:paper_id/tasks/:task_id'})
  @route('paper_new', { path: '/papers/new' })
  @route('signin', {path: '/users/sign_in'})
  @route('signup', {path: '/users/sign_up'})
  @route('profile', {path: '/profile'})

  @resource('affiliation')
  @resource('author')

  @resource 'admin', ->
    @resource 'journal', path: '/journals/:journal_id', ->
      @resource 'manuscript_manager_template', path: '/manuscript_manager_templates', ->
        @route('new')
        @route('edit', path: '/:template_id/edit')

if window.history and window.history.pushState
  ETahi.Router.reopen
    rootURL: '/'
    location: 'history'

Add a guard to pushState for friendly browsers only


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

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

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

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

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

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

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

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

    tween = createjs.Tween.get($this).to( {counter: $target_count}, (1000 + index * 1000) , createjs.Ease.quintOut)

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

Make the each metric animation finish at different times

The animation of a number finishes 1 second after the one before it.


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

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

module.exports = {genTubConfig: genTubConfig}

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

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

module.exports = {genTubConfig: genTubConfig}

Fix bug to use fs

Fix bug to use fs


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

module.exports =
  list: (archivePath, callback) ->
    switch path.extname(archivePath)
      when '.tar' then @listTar(archivePath, callback)
      when '.gz' then @listGzip(archivePath, callback)
      when '.zip' then @listZip(archivePath, callback)
      else callback([])

  listZip: (archivePath, callback) ->
    unzip = require 'unzip'
    paths = []
    fileStream = fs.createReadStream(archivePath)
    zipStream = fileStream.pipe(unzip.Parse())
    zipStream.on 'entry', (entry) ->
      paths.push(entry.path)
      entry.autodrain()
    zipStream.on 'close', ->
      callback(paths)

  listGzip: (archivePath, callback) ->
    if path.extname(path.basename(archivePath, '.gz')) isnt '.tar'
      callback([])
      return

    zlib = require 'zlib'
    gzipStream = fs.createReadStream(archivePath).pipe(zlib.createGunzip())
    @readTarStream(gzipStream, callback)

  listTar: (archivePath, callback) ->
    fileStream = fs.createReadStream(archivePath)
    @readTarStream(fileStream, callback)

  readTarStream: (inputStream, callback) ->
    tar = require 'tar'
    paths = []
    tarStream = inputStream.pipe(tar.Parse())
    tarStream.on 'entry', (entry) -> paths.push(entry.props.path)
    tarStream.on 'end', -> callback(paths)

Move function body to same line as .on() call


RenameModel = require './atom-ternjs-rename'
RenameView = require './atom-ternjs-rename-view'

module.exports =
class ViewManager

    renamePanel: null
    renameModel: null
    manager: null

    constructor: (manager) ->
        @manager = manager
        @registerViewProvider()

    registerViewProvider: ->
        atom.views.addViewProvider
            modelConstructor: RenameModel
            viewConstructor: RenameView

    showRename: ->
        if @renamePanel
            @renamePanel.show()
            return
        @renameModel = new RenameModel(@manager)
        @renamePanel = atom.workspace.addBottomPanel item: @renameModel

    hideRename: ->
        @renamePanel?.hide()
        @focusEditor()

    focusEditor: ->
        editor = atom.workspace.getActiveTextEditor()
        return unless editor
        view = atom.views.getView(editor)
        view?.focus?()

RenameModel = require './atom-ternjs-rename'
RenameView = require './atom-ternjs-rename-view'

module.exports =
class ViewManager

    renamePanel: null
    renameModel: null
    manager: null

    constructor: (manager) ->
        @manager = manager
        @registerViewProvider()

    registerViewProvider: ->
        atom.views.addViewProvider
            modelConstructor: RenameModel
            viewConstructor: RenameView

    showRename: ->
        if @renamePanel
            @renamePanel.show()
            return
        @renameModel = new RenameModel(@manager)
        @renamePanel = atom.workspace.addBottomPanel item: @renameModel

    hideRename: ->
        return unless @renamePanel?.isVisible()
        @renamePanel.hide()
        @focusEditor()

    focusEditor: ->
        editor = atom.workspace.getActiveTextEditor()
        return unless editor
        view = atom.views.getView(editor)
        view?.focus?()

Fix atom freezes when moving cursor fast with key


{View} = require 'atom'
ImageEditor = require './image-editor'

module.exports =
class ImageEditorStatusView extends View
  @content: ->
    @div class: 'status-image inline-block', =>
      @span class: 'image-size', outlet: 'imageSizeStatus'

  initialize: (filePath, image) ->
    @filePath = filePath
    @image = image
    if @filePath and @image
      @attach()

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

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

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

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

{View} = require 'atom'
ImageEditor = require './image-editor'

module.exports =
class ImageEditorStatusView extends View
  @content: ->
    @div class: 'status-image inline-block', =>
      @span class: 'image-size', outlet: 'imageSizeStatus'

  initialize: (filePath, image) ->
    @filePath = filePath
    @image = image
    if @filePath and @image
      @attach()

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

      @subscribe atom.workspaceView, 'pane:before-item-destroyed', =>
        editor = atom.workspaceView.getActivePaneItem()
        if editor instanceof ImageEditor and @filePath is editor.filePath
          @detach()

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

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

Fix bug detaching on pane close

Fix bug detaching on pane close


{View} = require 'atom'

# Status bar view for the soft wrap indicator.
module.exports =
class SoftWrapIndicatorView extends View
  @content: ->
    @div class: 'inline-block', =>
      @a 'Wrap', class: 'soft-wrap-indicator', outlet: 'light'

  # Initializes the view by subscribing to various events.
  #
  # statusBar - {StatusBar} of the application
  initialize: (@statusBar) ->
    @subscribe @statusBar, 'active-buffer-changed', @update

    atom.workspace.eachEditor (editor) =>
      @subscribe editor.displayBuffer, 'soft-wrap-changed', @update

    @subscribe this, 'click', => @getActiveEditor()?.toggleSoftWrap()

  # Internal: Executes after the view is added to the status bar.
  afterAttach: ->
    @update()

  # Gets the currently active `Editor`.
  #
  # Returns the {Editor} that is currently active or `null` if there is not one active.
  getActiveEditor: ->
    atom.workspace.getActiveEditor()

  # Updates the indicator based on the current state of the application.
  update: =>
    if @getActiveEditor()?.getSoftWrap()
      @light.addClass('lit').show()
    else if @getActiveEditor()?
      @light.removeClass('lit').show()
    else
      @light.hide()

  # Tear down any state and detach.
  destroy: ->
    @remove()

{View} = require 'atom'

# Public: Status bar view for the soft wrap indicator.
module.exports =
class SoftWrapIndicatorView extends View
  @content: ->
    @div class: 'inline-block', =>
      @a 'Wrap', class: 'soft-wrap-indicator', outlet: 'light'

  # Public: Initializes the view by subscribing to various events.
  #
  # statusBar - {StatusBar} of the application
  initialize: (@statusBar) ->
    @subscribe @statusBar, 'active-buffer-changed', @update

    atom.workspace.eachEditor (editor) =>
      @subscribe editor.displayBuffer, 'soft-wrap-changed', @update

    @subscribe this, 'click', => @getActiveEditor()?.toggleSoftWrap()

  # Internal: Executed by the framework after the view is added to the status bar.
  afterAttach: ->
    @update()

  # Internal: Gets the currently active `Editor`.
  #
  # Returns the {Editor} that is currently active or `null` if there is not one active.
  getActiveEditor: ->
    atom.workspace.getActiveEditor()

  # Internal: Updates the indicator based on the current state of the application.
  update: =>
    if @getActiveEditor()?.getSoftWrap()
      @light.addClass('lit').show()
    else if @getActiveEditor()?
      @light.removeClass('lit').show()
    else
      @light.hide()

  # Internal: Tear down any state and detach.
  destroy: ->
    @remove()

:memo: Update documentation to current standards


path = require 'path'
rek = require 'rekuire'

module.exports.config =
  framework: 'custom'
  frameworkPath: require.resolve 'protractor-cucumber-framework'

  # Capabilities to be passed to the webdriver instance.
  capabilities:
    'chromeOptions':
      args: ['--test-type']   # Disable the "unsupported flag" prompt. See: https://github.com/angular/protractor/issues/835

  cucumberOpts:
    require: [rek.path 'GeneralStepDefs']
    tags: []
    format: 'pretty'

  onPrepare: ->
    # Support a non-angular site
    browser.ignoreSynchronization = true
    browser.manage().timeouts().setScriptTimeout 5000
    browser.manage().timeouts().implicitlyWait 5000

path = require 'path'
rek = require 'rekuire'

module.exports.config =
  framework: 'custom'
  frameworkPath: require.resolve 'protractor-cucumber-framework'

  # Capabilities to be passed to the webdriver instance.
  capabilities:
    'chromeOptions':
      args: ['--test-type']   # Disable the "unsupported flag" prompt. See: https://github.com/angular/protractor/issues/835

  cucumberOpts:
    require: [rek.path 'GeneralStepDefs']
    format: 'pretty'

  onPrepare: ->
    # Support a non-angular site
    browser.ignoreSynchronization = true
    browser.manage().timeouts().setScriptTimeout 5000
    browser.manage().timeouts().implicitlyWait 5000

Remove default value for tags

Remove default value for tags


'use strict'

app = angular.module('holmesApp', [
  'ngCookies',
  'ngSanitize',
  'ngRoute',
  'angularMoment',
  'restangular'
])
  .config ($routeProvider, RestangularProvider, ConfigConst) ->
    $routeProvider
      .when '/',
        redirectTo: '/domains'
      .when '/domains',
        templateUrl: 'views/domains.html'
        controller: 'DomainsCtrl'
      .when '/domains/:domainId',
        templateUrl: 'views/domain.html'
        controller: 'DomainCtrl'
      .when '/domains/:domainId/reviews/:reviewId',
        templateUrl: 'views/reviews.html'
        controller: 'ReviewsCtrl'
      .when '/violations/:violationKey',
        templateUrl: 'views/violation.html'
        controller: 'ViolationCtrl'
      .otherwise
        redirectTo: '/'
    RestangularProvider.setBaseUrl(ConfigConst.baseUrl)

  .run(($rootScope, $window) ->
    $rootScope.$on('$viewContentLoaded', ->
      $window.scrollTo(0, 0)
    )
  )

Reset scroll to 0 after changing pages

Reset scroll to 0 after changing pages


fs = require 'fs'
print = (s) -> fs.write "/dev/stderr", s, 'w'

page = new WebPage()
page.onConsoleMessage = (msg) -> console.error msg

timeoutId = null
deferTimeout = ->
  clearTimeout timeoutId if timeoutId
  timeoutId = setTimeout ->
    console.error "Timeout"
    phantom.exit 1
  , 3000

page.open phantom.args[0], ->
  deferTimeout()

  setInterval ->
    tests = page.evaluate ->
      tests = document.getElementById('qunit-tests')?.children
      return unless tests
      for test in tests when test.className isnt 'running' and not test.recorded
        test.recorded = true
        if test.className is 'pass'
          '.'
        else if test.className is 'fail'
          'F'

    return unless tests
    for test in tests when test
      deferTimeout()
      print test

    result = page.evaluate ->
      result = document.getElementById('qunit-testresult')
      tests  = document.getElementById('qunit-tests').children

      if result.innerText.match /completed/
        console.error ""

        for test in tests when test.className is 'fail'
          console.error test.innerText

        console.error result.innerText
        return parseInt result.getElementsByClassName('failed')[0].innerText

      return

    phantom.exit result if result?
  , 100

fs = require 'fs'

page = new WebPage()
page.onConsoleMessage = (msg) -> console.error msg

timeoutId = null
deferTimeout = ->
  clearTimeout timeoutId if timeoutId
  timeoutId = setTimeout ->
    console.error "Timeout"
    phantom.exit 1
  , 3000

page.open phantom.args[0], ->
  deferTimeout()

  setInterval ->
    tests = page.evaluate ->
      tests = document.getElementById('qunit-tests')?.children
      return unless tests
      for test in tests when test.className isnt 'running' and not test.recorded
        test.recorded = true
        if test.className is 'pass'
          '.'
        else if test.className is 'fail'
          'F'

    return unless tests
    for test in tests when test
      deferTimeout()
      console.error test

    result = page.evaluate ->
      result = document.getElementById('qunit-testresult')
      tests  = document.getElementById('qunit-tests').children

      if result.innerText.match /completed/
        console.error ""

        for test in tests when test.className is 'fail'
          console.error test.innerText

        console.error result.innerText
        return parseInt result.getElementsByClassName('failed')[0].innerText

      return

    phantom.exit result if result?
  , 100

Use console.error instead of /dev/stderr


describe "Basic setup", ->
  it "should add chosen to jQuery object", ->
    expect(jQuery.fn.chosen).toBeDefined()

  it "should create very basic chosen", ->
    tmpl = "
      <select data-placeholder='Choose a Country...'>
        <option value=''></option>
        <option value='United States'>United States</option>
        <option value='United Kingdom'>United Kingdom</option>
        <option value='Afghanistan'>Afghanistan</option>
      </select>
    "
    div = $("<div>").html(tmpl)
    select = div.find("select")
    expect(select.size()).toBe(1)
    select.chosen()
    # very simple check that the necessary elements have been created
    ["container", "container-single", "single", "default"].forEach (clazz)->
      el = div.find(".chosen-#{clazz}")
      expect(el.size()).toBe(1)

Add simple test that constructs a chosen entirely in memory


'.workspace':
  'ctrl-shift-9': 'github:toggle-git-panel'
  'ctrl-9': 'github:toggle-git-panel-focus'

'.github-StagingView':
  'left': 'github:focus-diff-view'

'.github-CommitView-editor atom-text-editor:not([mini])':
  'cmd-enter': 'github:commit'
  'ctrl-enter': 'github:commit'

'.github-FilePatchView':
  '/': 'github:toggle-patch-selection-mode'
  'tab': 'github:select-next-hunk'
  'shift-tab': 'github:select-previous-hunk'
  'right': 'github:focus-git-panel'

'.github-Prompt-input':
  'enter': 'core:confirm'
  'esc': 'core:cancel'

'.workspace':
  'ctrl-9': 'github:toggle-git-panel'
  'ctrl-shift-9': 'github:toggle-git-panel-focus'

'.github-StagingView':
  'left': 'github:focus-diff-view'

'.github-CommitView-editor atom-text-editor:not([mini])':
  'cmd-enter': 'github:commit'
  'ctrl-enter': 'github:commit'

'.github-FilePatchView':
  '/': 'github:toggle-patch-selection-mode'
  'tab': 'github:select-next-hunk'
  'shift-tab': 'github:select-previous-hunk'
  'right': 'github:focus-git-panel'

'.github-Prompt-input':
  'enter': 'core:confirm'
  'esc': 'core:cancel'

Swap ctrl-9 and ctrl-shift-9 <_< >_>

Swap ctrl-9 and ctrl-shift-9 <_< >_>


isActive = (type, inverse = false) ->
  name = 'is'
  name = name + 'Not' if inverse
  name = name + _.capitalize type

  (view) ->
    unless view instanceof Spacebars.kw
      throw new Error "#{name} options must be key value pair such " +
        "as {{#{name} regex='route/path'}}. You passed: " +
        "#{JSON.stringify view}"

    pattern =
      className: Match.Optional String
      regex: String

    check view.hash, pattern

    route = Router.current().route

    return false unless route

    {className, regex} = view.hash

    className ?= if inverse then 'disabled' else 'active'

    isPath = true if type is 'path'

    test = testExp route, regex, isPath

    test = not test if inverse

    if test then className else false

testExp = (route, exp, isPath = false) ->
  if isPath
    pattern = route.path()

  else
    pattern = route.name

  re = new RegExp exp, 'i'

  re.test pattern

helpers =
  isActiveRoute: isActive 'route'

  isActivePath: isActive 'path'

  isNotActiveRoute: isActive 'route', true

  isNotActivePath: isActive 'path', true

Template.registerHelper name, func for own name, func of helpers

isActive = (type, inverse = false) ->
  name = 'is'
  name = name + 'Not' if inverse
  name = name + 'Active' + _.capitalize type

  (view) ->
    unless view instanceof Spacebars.kw
      throw new Error "#{name} options must be key value pair such " +
        "as {{#{name} regex='route/path'}}. You passed: " +
        "#{JSON.stringify view}"

    pattern =
      className: Match.Optional String
      regex: String

    check view.hash, pattern

    route = Router.current().route

    return false unless route

    {className, regex} = view.hash

    className ?= if inverse then 'disabled' else 'active'

    isPath = true if type is 'path'

    test = testExp route, regex, isPath

    test = not test if inverse

    if test then className else false

testExp = (route, exp, isPath = false) ->
  if isPath
    pattern = route.path()

  else
    pattern = route.name

  re = new RegExp exp, 'i'

  re.test pattern

helpers =
  isActiveRoute: isActive 'route'

  isActivePath: isActive 'path'

  isNotActiveRoute: isActive 'route', true

  isNotActivePath: isActive 'path', true

Template.registerHelper name, func for own name, func of helpers

Fix name of helper in error message

Fix name of helper in error message


class ComputeEventListener extends KDObject

  constructor:(options = {})->

    super
      interval : options.interval ? 4000

    @kloud          = KD.singletons.kontrol.getKite
      name          : "kloud"
      environment   : "vagrant"

    @listeners      = []
    @tickInProgress = no
    @running        = no
    @timer          = null

  listen:->

    return  if @running
    @running = yes

    @timer = KD.utils.repeat @getOption('interval'), @bound 'tick'

  stop:->

    return  unless @running
    @running = no
    KD.utils.killWait @timer

  addListener:(type, eventId)->

    @listeners.push { type, eventId }

  tick:->

    return  unless @listeners.length
    return  if @tickInProgress
    @tickInProgress = yes

    @kloud.event(@listeners)

    .then (res)=>

      log res

    .catch (err)=>

      warn "Eventer error:", err
      @stop()

    .finally =>

      @tickInProgress = no

class ComputeEventListener extends KDObject

  constructor:(options = {})->

    super
      interval : options.interval ? 4000

    @kloud          = KD.singletons.kontrol.getKite
      name          : "kloud"
      environment   : "vagrant"

    @listeners      = []
    @tickInProgress = no
    @running        = no
    @timer          = null

  start:->

    return  if @running
    @running = yes

    @tick()
    @timer = KD.utils.repeat @getOption('interval'), @bound 'tick'

  stop:->

    return  unless @running
    @running = no
    KD.utils.killWait @timer

  addListener:(type, eventId)->

    @listeners.push { type, eventId }
    @start()  unless @running

  tick:->

    return  unless @listeners.length
    return  if @tickInProgress
    @tickInProgress = yes

    {computeController} = KD.singletons
    @kloud.event(@listeners)

    .then (responses)=>

      activeListeners = []
      responses.forEach (res)=>

        if res.err
          warn "Error on '#{res.event_id}':", res.err

        else

          [type, eventId] = res.event.eventId.split '-'

          if res.event.percentage < 100
            activeListeners.push { type, eventId }

          log "#{res.event.eventId}", res.event

          computeController.emit "public-#{eventId}", res.event
          computeController.emit "#{res.event.eventId}", res.event

      @listeners = activeListeners
      @tickInProgress = no

    .catch (err)=>

      @tickInProgress = no
      warn "Eventer error:", err
      @stop()

ComputeEventListener: Use start, emit over ComputeController, start polling when
requested, stop when not needed


DS.RESTAdapter.configure 'plurals',
  corpus:     'corpora'
  search:     'searches'
  cwb_search: 'cwb_searches'
  metadata_category: 'metadata_categories'

App.Adapter = DS.RESTAdapter.extend

  buildURL: (record, suffix) ->
    url = @_super(record, suffix)
    url = "/search_engines#{url}" unless record.search(/_search$/) is -1
    url

  didCreateRecord: (store, type, record, json) ->
    @_super(store, type, record, json)

    if record.setupResultPages?
      root = @rootForType(type)
      record.setupResultPages(json[root])

  didFindRecord: (store, type, payload, id) ->
    @_super(store, type, payload, id)

    record = type.find(id)
    if record.setupResultPages?
      root = @rootForType(type)
      record.setupResultPages(payload[root])

# Arrays and hashes should just be passed along to be (de)serialized by the
# standard mechanisms. Ember Data does not include these attribute types (yet)
# because of complications wrt knowing when an attribute becomes dirty, but we
# define them ourselves and just make sure to dirty the object ourselves when
# needed.
App.Adapter.registerTransform 'array',
  deserialize: (serialized) ->
    serialized

  serialize: (deserialized) ->
    deserialized

App.Adapter.registerTransform 'hash',
  deserialize: (serialized) ->
    serialized

  serialize: (deserialized) ->
    deserialized

DS.RESTAdapter.configure 'plurals',
  corpus:     'corpora'
  search:     'searches'
  cwb_search: 'cwb_searches'
  metadata_category: 'metadata_categories'

App.Adapter = DS.RESTAdapter.extend

  buildURL: (record, suffix) ->
    url = @_super(record, suffix)
    unless record.search(/_search$/) is -1
      parts = url.split('/')
      lastElm = parts.length - 1
      url = parts[0...lastElm].join('/') + '/search_engines/' + parts[lastElm]
    url

  didCreateRecord: (store, type, record, json) ->
    @_super(store, type, record, json)

    if record.setupResultPages?
      root = @rootForType(type)
      record.setupResultPages(json[root])

  didFindRecord: (store, type, payload, id) ->
    @_super(store, type, payload, id)

    record = type.find(id)
    if record.setupResultPages?
      root = @rootForType(type)
      record.setupResultPages(payload[root])

# Arrays and hashes should just be passed along to be (de)serialized by the
# standard mechanisms. Ember Data does not include these attribute types (yet)
# because of complications wrt knowing when an attribute becomes dirty, but we
# define them ourselves and just make sure to dirty the object ourselves when
# needed.
App.Adapter.registerTransform 'array',
  deserialize: (serialized) ->
    serialized

  serialize: (deserialized) ->
    deserialized

App.Adapter.registerTransform 'hash',
  deserialize: (serialized) ->
    serialized

  serialize: (deserialized) ->
    deserialized

Fix buildURL wrt running the app on a prefix


gulp    = require "gulp"
gutil   = require "gulp-util"
coffee  = require "gulp-coffee"
del     = require "del"
path    = require "path"
{spawn} = require "child_process"

prefix      = gutil.env.prefix || "./build"
resourceDir = path.join prefix, "Resources"

gulp.task "clean", (done) ->
  del(prefix, done)

gulp.task "js", ->
  gulp.src(["./Resources/**/*.js"])
    .pipe(gulp.dest(resourceDir))

gulp.task "coffee", ->
  gulp.src("./Resources/**/*.coffee")
    .pipe(coffee(bare: true))
    .pipe(gulp.dest(resourceDir))

gulp.task "tiapp", ->
  gulp.src(["./tiapp.xml"])
    .pipe(gulp.dest(prefix))

gulp.task "precompile", ["js", "coffee", "tiapp"]

gulp.task "build", ["precompile"], (done) ->
  spawn(
    "./node_modules/.bin/titanium"
    ["build", "--project-dir", prefix].concat(process.argv)
    cwd:   "./"
    stdio: "inherit"
  )
  .on("close", done)

gulp.task "default", ["build"]

gulp    = require "gulp"
gutil   = require "gulp-util"
coffee  = require "gulp-coffee"
del     = require "del"
path    = require "path"
{spawn} = require "child_process"

prefix      = gutil.env.prefix || "./build"
resourceDir = path.join prefix, "Resources"

gulp.task "clean", (done) ->
  del(prefix, done)

gulp.task "js", ->
  gulp.src("./Resources/**/*.js")
    .pipe(gulp.dest(resourceDir))

gulp.task "coffee", ->
  gulp.src("./Resources/**/*.coffee")
    .pipe(coffee(bare: true))
    .pipe(gulp.dest(resourceDir))

gulp.task "resources", ->
  gulp.src(["./Resources/**/*", "!./Resources/**/*.js", "!./Resources/**/*.coffee"])
    .pipe(gulp.dest(resourceDir))

gulp.task "tiapp", ->
  gulp.src("./tiapp.xml")
    .pipe(gulp.dest(prefix))

gulp.task "precompile", ["js", "coffee", "resources", "tiapp"]

gulp.task "build", ["precompile"], (done) ->
  spawn(
    "./node_modules/.bin/titanium"
    ["build", "--project-dir", prefix].concat(process.argv)
    cwd:   "./"
    stdio: "inherit"
  )
  .on("close", done)

gulp.task "default", ["build"]

Add non-code reesources to build

Add non-code reesources to build

Was missing things like images and stuff.


"*":
  "exception-reporting":
    userId: "1e725750-c66d-33e4-9fce-f627ac32bde5"
  "release-notes":
    viewedVersion: "0.94.0"
  welcome:
    showOnStartup: false
  metrics:
    userId: "c2f08243ecb4a9bc44bd9e46d4f067086d9357e3"
  editor:
    fontSize: 11
    showInvisibles: true
    showIndentGuide: true
    invisibles: {}
  core:
    disabledPackages: [
      "metrics"
      "select-rectangle"
      "vim-mode"
    ]
    themes: [
      "atom-dark-ui-slim"
      "atom-dark-syntax"
    ]
  linter:
    showErrorInline: true
  "go-plus":
    goPath: "~/.go"
  "tree-view": {}
  "autocomplete-plus": {}

"*":
  "exception-reporting":
    userId: "1e725750-c66d-33e4-9fce-f627ac32bde5"
  "release-notes":
    viewedVersion: "0.94.0"
  welcome:
    showOnStartup: false
  metrics:
    userId: "c2f08243ecb4a9bc44bd9e46d4f067086d9357e3"
  editor:
    fontSize: 11
    showInvisibles: true
    showIndentGuide: true
    invisibles: {}
  core:
    disabledPackages: [
      "metrics"
      "select-rectangle"
      "vim-mode"
    ]
    themes: [
      "atom-dark-ui-slim"
      "twilight-syntax"
    ]
  linter:
    showErrorInline: true
  "go-plus":
    goPath: "~/.go"
  "tree-view": {}
  "autocomplete-plus": {}

Use twilight syntax theme for atom

Use twilight syntax theme for atom


angular.module('loomioApp').factory '$exceptionHandler', ($log, AppConfig) ->
  if !AppConfig.errbit.key?
    return ->

  client = new airbrakeJs.Client
    projectId:  AppConfig.errbit.key
    projectKey: AppConfig.errbit.key
    reporter:   'xhr'
    host:       AppConfig.errbit.url

  client.addFilter (notice) ->
    notice.context.environment = AppConfig.environment
    if notice.errors[0].type == ""
      null
    else
      notice

  (exception, cause) ->
    $log.error(exception)
    client.notify
      error: exception,
      params:
        current_user_id: AppConfig.currentUserId
        angular_cause: cause

angular.module('loomioApp').factory '$exceptionHandler', ($log, AppConfig) ->
  if !AppConfig.errbit.key?
    return ->

  client = new airbrakeJs.Client
    projectId:  AppConfig.errbit.key
    projectKey: AppConfig.errbit.key
    reporter:   'xhr'
    host:       AppConfig.errbit.url

  client.addFilter (notice) ->
    notice.context.environment = AppConfig.environment
    if notice.errors[0].type == ""
      null
    else
      notice

  (exception, cause) ->
    $log.error(exception)
    client.notify
      error: exception,
      params:
        version:         AppConfig.version
        current_user_id: AppConfig.currentUserId
        angular_cause: cause

Add version to client exceptions

Add version to client exceptions


'use strict'

module.exports = angular.module('astrifex.maker', [])
    .config(($stateProvider) ->
      $stateProvider.state 'maker',
      url: ''
      templateUrl: 'app/maker/layout.html'
      controller: 'makerController'
      return
).controller('makerController', require('./makerController'))

'use strict'

module.exports = angular.module('astrifex.maker', ['ui.router'])
    .config(($stateProvider) ->
      $stateProvider.state 'maker',
      url: ''
      templateUrl: 'app/maker/layout.html'
      controller: 'makerController'
      return
).controller('makerController', require('./makerController'))

Declare ui.router dependency in astrifex.maker module


casper = require('casper').create()

d = ->
  console.log arguments...

casper.start 'http://func/ucp.php?mode=register', ->
  @test.assertHttpStatus 200
  @test.assertExists 'input[value="I agree to these terms"]'

  @click 'input[value="I agree to these terms"]'

casper.then ->
  @test.assertHttpStatus 200
  @test.assertDoesntExist 'input[value="I agree to these terms"]'

casper.run()

casper = require('casper').create()

d = ->
  console.log arguments...

casper.start 'http://func/ucp.php?mode=register', ->
  @test.assertHttpStatus 200
  @test.assertExists 'input[value="I agree to these terms"]'

  @click 'input[value="I agree to these terms"]'

casper.then ->
  @test.assertHttpStatus 200
  @test.assertDoesntExist 'input[value="I agree to these terms"]'

  @test.assertExists '#tz_date'
  tz_value = @evaluate ->
    document.querySelector('#tz_date').value
  # Should match system time zone
  @test.assertMatch tz_value, /^GMT-05:00/

casper.run()

Add a test for correct initial value of timezone selector in registration form


noflo = require 'noflo'

class Gate extends noflo.Component
  description: 'This component forwards received packets when the gate is open'

  constructor: ->
    @open = false

    @inPorts =
      in: new noflo.Port 'all'
      open: new noflo.Port 'bang'
      close: new noflo.Port 'bang'
    @outPorts =
      out: new noflo.Port 'all'

    @inPorts.in.on 'connect', =>
      return unless @open
      @outPorts.out.connect()
    @inPorts.in.on 'begingroup', (group) =>
      return unless @open
      @outPorts.out.beginGroup group
    @inPorts.in.on 'data', (data) =>
      return unless @open
      @outPorts.out.send data
    @inPorts.in.on 'endgroup', =>
      return unless @open
      @outPorts.out.endGroup()
    @inPorts.in.on 'disconnect', =>
      return unless @open
      @outPorts.out.disconnect()

    @inPorts.open.on 'data', =>
      @open = true
    @inPorts.close.on 'data', =>
      @open = false

exports.getComponent = -> new Gate

noflo = require 'noflo'

class Gate extends noflo.Component
  description: 'This component forwards received packets when the gate is open'

  constructor: ->
    @open = false

    @inPorts =
      in: new noflo.Port 'all'
      open: new noflo.Port 'bang'
      close: new noflo.Port 'bang'
    @outPorts =
      out: new noflo.Port 'all'

    @inPorts.in.on 'connect', =>
      return unless @open
      @outPorts.out.connect()
    @inPorts.in.on 'begingroup', (group) =>
      return unless @open
      @outPorts.out.beginGroup group
    @inPorts.in.on 'data', (data) =>
      return unless @open
      @outPorts.out.send data
    @inPorts.in.on 'endgroup', =>
      return unless @open
      @outPorts.out.endGroup()
    @inPorts.in.on 'disconnect', =>
      return unless @open
      @outPorts.out.disconnect()

    @inPorts.open.on 'data', =>
      @open = true
    @inPorts.close.on 'data', =>
      @open = false
      @outPorts.out.disconnect()

exports.getComponent = -> new Gate

Send disconnect on gate close

Send disconnect on gate close


gulp       = require 'gulp'
del        = require 'del'
haml       = require 'gulp-haml'
sourcemaps = require 'gulp-sourcemaps'
coffee     = require 'gulp-coffee'

SRC = 'src'
DEST = 'out'

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

gulp.task 'haml', ->
  gulp.src "#{SRC}/haml/**/*.haml"
    .pipe haml compiler: 'visionmedia'
    .pipe gulp.dest DEST

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

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

gulp       = require 'gulp'
del        = require 'del'
haml       = require 'gulp-haml'
sourcemaps = require 'gulp-sourcemaps'
coffee     = require 'gulp-coffee'

SRC = 'src'
DEST = 'out'

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

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

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

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

Switch Back to Default HAML Compiler

Switch Back to Default HAML Compiler

The output of the other one was getting wonky.