# Your snippets
#
# Atom snippets allow you to enter a simple prefix in the editor and hit tab to
# expand the prefix into a larger code block with templated values.
#
# You can create a new snippet in this file by typing "snip" and then hitting
# tab.
#
# An example CoffeeScript snippet to expand log to console.log:
#
# '.source.coffee':
#   'Console log':
#     'prefix': 'log'
#     'body': 'console.log $1'
#
# Each scope (e.g. '.source.coffee' above) can only be declared once.
#
# This file uses CoffeeScript Object Notation (CSON).
# If you are unfamiliar with CSON, you can read more about it in the
# Atom Flight Manual:
# https://atom.io/docs/latest/using-atom-basic-customization#cson

'.source.js':
  'Redux container':
    'prefix': 'rcont'
    'body': """
            import { connect } from 'react-redux'

            import $1 from '../components/$1'

            export default connect()($1)

            """
  'Stylesheet (React Native)':
    'prefix': 'rnss'
    'body': """
            import { StyleSheet } from 'react-native'

            export default StyleSheet.create({
            })

            """

# Your snippets
#
# Atom snippets allow you to enter a simple prefix in the editor and hit tab to
# expand the prefix into a larger code block with templated values.
#
# You can create a new snippet in this file by typing "snip" and then hitting
# tab.
#
# An example CoffeeScript snippet to expand log to console.log:
#
# '.source.coffee':
#   'Console log':
#     'prefix': 'log'
#     'body': 'console.log $1'
#
# Each scope (e.g. '.source.coffee' above) can only be declared once.
#
# This file uses CoffeeScript Object Notation (CSON).
# If you are unfamiliar with CSON, you can read more about it in the
# Atom Flight Manual:
# https://atom.io/docs/latest/using-atom-basic-customization#cson

'.source.js':
  'Redux container':
    'prefix': 'rcont'
    'body': """
            import { connect } from 'react-redux'

            import $1 from '../components/$1'

            export default connect()($1)

            """
  'Stylesheet (React Native)':
    'prefix': 'rnss'
    'body': """
            import { StyleSheet } from 'react-native'

            export default StyleSheet.create({
              $1
            })

            """

Add a placeholder to rnss snippet

Add a placeholder to rnss snippet

That way, the cursor ends up at the right place for entering styles.


{my} = require '../my'
{draw} = require './draw'

exports.render = (root) ->
  T = root.T()
  get_style = (world) ->
    {
      border: "1px solid #{world.get('stroke')}"
      background: world.get 'fill'
      height: world.get 'height'
      width: world.get 'width'
    }
  render_children = (world) ->
    world.map_children (child) ->
      return draw(child) if child.get('path')?
      render_world(child)

  render_world = (world) ->
    dict = {
      id: world
      class: world.labels(['render', 'world'])
      style: get_style(world)
    }
    T.div dict, world.bind() -> render_children(world)

  render_world(root)

{my} = require '../my'
{draw} = require './draw'

exports.render = (root) ->
  T = root.T()
  get_style = (world) ->
    {
      border: "1px solid #{world.get('stroke')}"
      background: world.get 'fill'
      height: world.get 'height'
      width: world.get 'width'
      position: 'absolute'
      left: world.get 'x'
      top: world.get 'y'
    }
  render_children = (world) ->
    results = world.map_children (child) ->
      return draw(child) if child.get('path')?
      render_world(child)
    name = world.get('name')
    results.push T.span(name) if name?
    results
  render_world = (world) ->
    dict = {
      id: world
      class: world.labels(['render', 'world'])
      style: get_style(world)
    }
    T.div dict, render_children(world) #world.bind() ->

  render_world(root)

Use x y positioning for controls

Use x y positioning for controls


AjaxPrefix.addAjaxPrefix(jQuery, -> Courseware.prefix)

$ ->
  $.ajaxSetup
    headers : { 'X-CSRFToken': $.cookie 'csrftoken' }
    dataType: 'json'

  window.onTouchBasedDevice = ->
    navigator.userAgent.match /iPhone|iPod|iPad/i

  $('body').addClass 'touch-based-device' if onTouchBasedDevice()

  # $("a[rel*=leanModal]").leanModal()
  $('#csrfmiddlewaretoken').attr 'value', $.cookie('csrftoken')

  new Calculator
  new FeedbackForm
  if $('body').hasClass('courseware')
    Courseware.start()

  # Preserved for backward compatibility
  window.submit_circuit = (circuit_id) ->
    $("input.schematic").each (index, el) ->
      el.schematic.update_value()

    schematic_value $("#schematic_#{circuit_id}").attr("value")
    $.postWithPrefix "/save_circuit/#{circuit_id}", schematic: schematic_value, (data) ->
      alert('Saved') if data.results == 'success'

  window.postJSON = (url, data, callback) ->
    $.postWithPrefix url, data, callback

  $('#login').click ->
    $('#login_form input[name="email"]').focus()
    false

  $('#signup').click ->
    $('#signup-modal input[name="email"]').focus()
    false

AjaxPrefix.addAjaxPrefix(jQuery, -> Courseware.prefix)

$ ->
  $.ajaxSetup
    headers : { 'X-CSRFToken': $.cookie 'csrftoken' }
    dataType: 'json'

  window.onTouchBasedDevice = ->
    navigator.userAgent.match /iPhone|iPod|iPad/i

  $('body').addClass 'touch-based-device' if onTouchBasedDevice()

  # $("a[rel*=leanModal]").leanModal()
  $('#csrfmiddlewaretoken').attr 'value', $.cookie('csrftoken')

  new Calculator
  new FeedbackForm
  if $('body').hasClass('courseware')
    Courseware.start()

  # Preserved for backward compatibility
  window.submit_circuit = (circuit_id) ->
    $("input.schematic").each (index, el) ->
      el.schematic.update_value()

    schematic_value $("#schematic_#{circuit_id}").attr("value")
    $.postWithPrefix "/save_circuit/#{circuit_id}", schematic: schematic_value, (data) ->
      alert('Saved') if data.results == 'success'

  window.postJSON = (url, data, callback) ->
    $.postWithPrefix url, data, callback

  $('#login').click ->
    $('#login_form input[name="email"]').focus()
    _gaq.push(['_trackPageview', '/login'])
    false

  $('#signup').click ->
    $('#signup-modal input[name="email"]').focus()
    _gaq.push(['_trackPageview', '/signup'])
    false

Add google analytics to model signup and login panels

[# 40805407]


noflo = require 'noflo'
debug = require('debug') 'noflo-ui:store'

exports.getComponent = ->
  c = new noflo.Component
  c.inPorts.add 'action',
    datatype: 'all'
  c.inPorts.add 'state',
    datatype: 'object'
  c.outPorts.add 'pass',
    datatype: 'object'
    scoped: false

  c.state = {}
  c.tearDown = (callback) ->
    c.state = {}
    do callback
  c.forwardBrackets = {}
  c.process (input, output) ->
    if input.hasData 'state'
      c.state = input.getData 'state'
      output.done()
      return
    return unless input.hasStream 'action'
    packets = []
    brackets = []
    input.getStream('action').forEach (ip) ->
      if ip.type is 'openBracket'
        brackets.push ip.data
      if ip.type is 'closeBracket'
        brackets.pop()
      if ip.type is 'data'
        packets.push
          data: ip.data
          brackets: brackets.slice 0
    for packet in packets
      data = packet.data
      if data and typeof data is 'object' and data.payload and data.action
        # New-style action object
        if data.state
          # Keep track of last state
          c.state = data.state
        else
          debug "#{data.action} was sent without state, using previous state"
        output.send
          pass:
            action: data.action
            state: c.state
            payload: data.payload
        continue
      # Old-style action with only payload, and action defined by brackets
      action = packet.brackets.join ':'
      debug "#{action} was sent in legacy payload-only format"
      output.send
        pass:
          action: action
          state: c.state
          payload: data
    output.done()
    return

noflo = require 'noflo'
debug = require('debug') 'noflo-ui:store'

exports.getComponent = ->
  c = new noflo.Component
  c.inPorts.add 'action',
    datatype: 'all'
  c.inPorts.add 'state',
    datatype: 'object'
  c.outPorts.add 'pass',
    datatype: 'object'
    scoped: false

  c.state = {}
  c.tearDown = (callback) ->
    c.state = {}
    do callback
  c.forwardBrackets = {}
  c.process (input, output) ->
    if input.hasData 'state'
      c.state = input.getData 'state'
      output.done()
      return
    return unless input.hasStream 'action'
    packets = input.getStream('action').filter((ip) ->
      ip.type is 'data'
    ).map (ip) -> ip.data
    for data in packets
      unless data.action
        console.error 'Received action without expected payload', data
        output.done()
      if data and typeof data is 'object' and data.payload and data.action
        # New-style action object
        if data.state
          # Keep track of last state
          c.state = data.state
        else
          debug "#{data.action} was sent without state, using previous state"
        output.send
          pass:
            action: data.action
            state: c.state
            payload: data.payload
        continue
    output.done()
    return

Drop support for legacy bracketed actions


{View} = require 'atom'

module.exports = ->
  highlightSelectedPackage = atom.packages.getLoadedPackage('highlight-selected')

  highlightSelected = require (highlightSelectedPackage.path)
  HighlightedAreaView = require (highlightSelectedPackage.path + '/lib/highlighted-area-view')

  class FakeEditor
    constructor: (@minimap) ->

    getActiveMinimap: -> @minimap.getActiveMinimap()

    getActiveTextEditor: -> @getActiveMinimap().getTextEditor()

    ['markBufferRange', 'scanInBufferRange', 'getEofBufferPosition', 'getSelections', 'getLastSelection', 'bufferRangeForBufferRow', 'getTextInBufferRange'].forEach (key) ->
      FakeEditor::[key] = -> @getActiveTextEditor()[key](arguments...)

    ['decorateMarker'].forEach (key) ->
      FakeEditor::[key] = -> @getActiveMinimap()[key](arguments...)

  class MinimapHighlightSelectedView extends HighlightedAreaView
    constructor: (minimap) ->
      super
      @fakeEditor = new FakeEditor(minimap)

    getActiveEditor: -> @fakeEditor

{View} = require 'atom'

module.exports = ->
  highlightSelectedPackage = atom.packages.getLoadedPackage('highlight-selected')

  highlightSelected = require (highlightSelectedPackage.path)
  HighlightedAreaView = require (highlightSelectedPackage.path + '/lib/highlighted-area-view')

  class FakeEditor
    constructor: (@minimap) ->

    getActiveMinimap: -> @minimap.getActiveMinimap()

    getActiveTextEditor: -> @getActiveMinimap()?.getTextEditor()

    ['markBufferRange', 'scanInBufferRange', 'getEofBufferPosition', 'getSelections', 'getLastSelection', 'bufferRangeForBufferRow', 'getTextInBufferRange'].forEach (key) ->
      FakeEditor::[key] = -> @getActiveTextEditor()[key](arguments...)

    ['decorateMarker'].forEach (key) ->
      FakeEditor::[key] = -> @getActiveMinimap()[key](arguments...)

  class MinimapHighlightSelectedView extends HighlightedAreaView
    constructor: (minimap) ->
      super
      @fakeEditor = new FakeEditor(minimap)

    getActiveEditor: -> @fakeEditor

    handleSelection: ->
      return unless atom.workspace.getActiveTextEditor()?
      super

:bug: Fix error raised when typing in a mini editor


$ ->
  # Don't run on non-video pages
  return if not $('.videos').length

  nearBottom = () ->
    $(window).scrollTop() > $(document).height() - $(window).height() - 600
  page = 1
  loading = false
  infiniteScroll = every 200, () ->
    if not loading and nearBottom()
      loading = true
      $('.queue').append($('
Loading...
'))
      page++
      $.ajax(window.location.pathname + (window.location.search || '?') + "&page=#{ page }", {
        dataType: 'script',
        complete: (resp) ->
          if resp.responseText
            $('#loading').remove()
            $('.videoList').append(resp.responseText)
            affixTags($(".video[data-page='#{ page }'] .tagEntry"))
            loading = false
          else
            clearInterval(infiniteScroll)
            $('#loading').remove()
      })

$ ->
  # Don't run on non-video pages
  return if not $('.videos .videoList').length

  nearBottom = () ->
    $(window).scrollTop() > $(document).height() - $(window).height() - 600
  page = 1
  loading = false
  infiniteScroll = every 200, () ->
    if not loading and nearBottom()
      loading = true
      $('.queue').append($('
Loading...
'))
      page++
      $.ajax(window.location.pathname + (window.location.search || '?') + "&page=#{ page }", {
        dataType: 'script',
        complete: (resp) ->
          if resp.responseText
            $('#loading').remove()
            $('.videoList').append(resp.responseText)
            affixTags($(".video[data-page='#{ page }'] .tagEntry"))
            loading = false
          else
            clearInterval(infiniteScroll)
            $('#loading').remove()
      })

Return on all videos pages

Return on all videos pages


React    = require 'react'
BS       = require 'react-bootstrap'
S = require '../../helpers/string'
{TimeStore} = require '../../flux/time'
moment   = require 'moment'
TH = require '../../helpers/task'

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

  propTypes:
    event: React.PropTypes.object.isRequired

  render: ->
    due = moment(@props.event.due_at)
    now = TimeStore.getNow()

    return null if due.isAfter(now, 'd') and TH.hasLateWork(@props.event)

    # use 'day' granularity for checking if the due date is today or after today
    status = if due.isSame(now, 'd') then 'incomplete' else 'late'

    tooltip =
      <BS.Tooltip
        id="event-info-icon-#{@props.event.id}">
        <b>{S.capitalize(status)}</b>
      </BS.Tooltip>

    <BS.OverlayTrigger placement='top' overlay={tooltip}>
      <i className="info #{status}"/>
    </BS.OverlayTrigger>

React    = require 'react'
BS       = require 'react-bootstrap'
S = require '../../helpers/string'
{TimeStore} = require '../../flux/time'
moment   = require 'moment'

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

  propTypes:
    event: React.PropTypes.object.isRequired

  render: ->
    {event} = @props

    now   = TimeStore.getNow()
    dueAt = moment(event.due_at)
    isIncomplete = event.complete_exercise_count isnt event.exercise_count
    pastDue      = event.type is 'homework' and dueAt.isBefore(now, 'd')
    workedLate   = moment(event.last_worked_at).isAfter(dueAt)

    return null unless workedLate or (pastDue and isIncomplete)

    # use 'day' granularity for checking if the due date is today or after today
    status = if dueAt.isSame(now, 'd') then 'incomplete' else 'late'

    tooltip =
      <BS.Tooltip
        id="event-info-icon-#{event.id}">
        <b>{S.capitalize(status)}</b>
      </BS.Tooltip>

    <BS.OverlayTrigger placement='top' overlay={tooltip}>
      <i className="info #{status}"/>
    </BS.OverlayTrigger>

Fix logic, task lacks correct keys forTaskHelper


Ember.Handlebars.helper 'formattedTime', (time) ->
  new Handlebars.SafeString $.timeago(time)

Ember.Handlebars.helper 'formattedTime', (time) ->
  time ||= new Date()
  new Handlebars.SafeString $.timeago(time)

Set timeago to current time if no time is given

When objects are initially created by ember, they don't have a
'createdAt' attribute until the promise resolves. new Date() will result
in the same markup, so the user won't see a difference when the app
receives the server response.


#= require_self
#= require_tree .

window.Neighborly =
  Common:
    initPage: ->
      that = this
      unless window.Turbolinks is undefined
        $(document).bind "page:fetch", ->
          that.Loading.show()

        $(document).bind "page:restore", ->
          that.Loading.hide()

        $(document).bind "page:change", ->
          $(window).scrollTop(0)

          try
            FB.XFBML.parse()
          try
            twttr.widgets.load()

    init: ->
      $(document).foundation('reveal', {animation: 'fadeIn', animationSpeed: 100})
      $(document).foundation()
      $('.search-button').click ->
        if $('.discover-form-input').val() != ''
          $('form.discover-form').submit()
        else
          $('.discover-form-input').toggleClass('show').focus()

        return false

    finish: ->

    Loading:
      show: ->
        $('#loading #back-overlay, #loading #front-overlay').fadeIn(2)
      hide: ->
        $('#loading #back-overlay, #loading #front-overlay').hide()

#= require_self
#= require_tree .

window.Neighborly =
  Common:
    initPage: ->
      that = this
      unless window.Turbolinks is undefined
        $(document).bind "page:fetch", ->
          that.Loading.show()

        $(document).bind "page:restore", ->
          that.Loading.hide()

        $(document).bind "page:change", ->
          $(window).scrollTop(0)

          try
            FB.XFBML.parse()
          try
            twttr.widgets.load()

    init: ->
      $(document).foundation('reveal', {animation: 'fadeIn', animationSpeed: 100})
      $(document).foundation()

      $('.button.disabled').click ->
        return false

      $('.search-button').click ->
        if $('.discover-form-input').val() != ''
          $('form.discover-form').submit()
        else
          $('.discover-form-input').toggleClass('show').focus()

        return false

    finish: ->

    Loading:
      show: ->
        $('#loading #back-overlay, #loading #front-overlay').fadeIn(2)
      hide: ->
        $('#loading #back-overlay, #loading #front-overlay').hide()

Add default behavior on disabled buttons


class @UserList extends Backbone.Collection
  model: User

  comparator: (a, b) =>
    aAuthority = a.get('authority')
    bAuthority = b.get('authority')
    aName = a.id.toLowerCase()
    bName = b.id.toLowerCase()
    if aAuthority < bAuthority      then  1
    else if aAuthority > bAuthority then -1
    else if aName < bName           then -1
    else if aName > bName           then  1
    else                                  0

  initialize: =>

class @UserList extends Backbone.Collection
  model: User

  comparator: (a, b) =>
    aAuthority = a.get('authority')
    bAuthority = b.get('authority')
    aName = "#{a.id}".toLowerCase()
    bName = "#{b.id}".toLowerCase()
    if aAuthority < bAuthority      then  1
    else if aAuthority > bAuthority then -1
    else if aName < bName           then -1
    else if aName > bName           then  1
    else                                  0

  initialize: =>

Fix problem with numeric alt names.

Client: Fix problem with numeric alt names.

But we should probably be disallowing that anyway...


angular.module("admin.products").controller "editUnitsCtrl", ($scope, VariantUnitManager) ->

  $scope.product =
    variant_unit: angular.element('#variant_unit').val()
    variant_unit_scale: angular.element('#variant_unit_scale').val()

  $scope.variant_unit_options = VariantUnitManager.variantUnitOptions()

  if $scope.product.variant_unit == 'items'
    $scope.variant_unit_with_scale = 'items'
  else
    $scope.variant_unit_with_scale = $scope.product.variant_unit + '_' + $scope.product.variant_unit_scale

  $scope.setFields = ->
    if $scope.variant_unit_with_scale == 'items'
      variant_unit = 'items'
      variant_unit_scale = null
    else
      options = $scope.variant_unit_with_scale.split('_')
      variant_unit = options[0]
      variant_unit_scale = options[1]

    $scope.product.variant_unit = variant_unit
    $scope.product.variant_unit_scale = variant_unit_scale

angular.module("admin.products").controller "editUnitsCtrl", ($scope, VariantUnitManager) ->

  $scope.product =
    variant_unit: angular.element('#variant_unit').val()
    variant_unit_scale: angular.element('#variant_unit_scale').val()

  $scope.variant_unit_options = VariantUnitManager.variantUnitOptions()

  if $scope.product.variant_unit == 'items'
    $scope.variant_unit_with_scale = 'items'
  else
    $scope.variant_unit_with_scale = $scope.product.variant_unit + '_' + $scope.product.variant_unit_scale.replace(/\.0$/, '');

  $scope.setFields = ->
    if $scope.variant_unit_with_scale == 'items'
      variant_unit = 'items'
      variant_unit_scale = null
    else
      options = $scope.variant_unit_with_scale.split('_')
      variant_unit = options[0]
      variant_unit_scale = options[1]

    $scope.product.variant_unit = variant_unit
    $scope.product.variant_unit_scale = variant_unit_scale

Remove trailing `.0` from variant unit scale

 - As VariantUnitManager.variantUnitOptions() returns array formatted like this: `"Weight (g)", "weight_1"` and `product.variant_unit_scale` is formatted like this `weight_1.0` there is no possible match for the <select /> element
 - So, remove the trailing `.0` from `product.variant_unit_scale` to match the options


_ = require "underscore"
Fuse = require "fuse.js"

class Utils
  @robot: null

  @findRoom: (msg) ->
    room = msg.envelope.room
    if _.isUndefined(room)
      room = msg.envelope.user.reply_to
    room

  @getRoom: (context) ->
    room = @robot.adapter.client.rtm.dataStore.getChannelOrGroupByName context.message.room
    room = @robot.adapter.client.rtm.dataStore.getChannelGroupOrDMById context.message.room unless room
    room

  @getUsers: ->
    Utils.robot.adapter?.client?.rtm?.dataStore?.users or Utils.robot.brain.users()

  @lookupUserWithGithub: (github) ->
    return Promise.resolve() unless github

    findMatch = (user) ->
      name = user.name or user.login
      return unless name
      users = Utils.getUsers()
      users = _(users).keys().map (id) ->
        u = users[id]
        id: u.id
        name: u.name
        real_name: u.real_name

      f = new Fuse users,
        keys: ['real_name']
        shouldSort: yes
        verbose: no
        threshold: 0.55

      results = f.search name
      result = if results? and results.length >=1 then results[0] else undefined
      return Promise.resolve result

    if github.fetch?
      github.fetch().then findMatch
    else
      findMatch github

module.exports = Utils

_ = require "underscore"
Fuse = require "fuse.js"

class Utils
  @robot: null

  @findRoom: (msg) ->
    room = msg.envelope.room
    if _.isUndefined(room)
      room = msg.envelope.user.reply_to
    room

  @getRoom: (context) ->
    room = @robot.adapter.client.rtm.dataStore.getChannelOrGroupByName context.message.room
    room = @robot.adapter.client.rtm.dataStore.getChannelGroupOrDMById context.message.room unless room
    room = @robot.adapter.client.rtm.dataStore.getDMByUserId context.message.room unless room
    room = @robot.adapter.client.rtm.dataStore.getDMByName context.message.room unless room
    room

  @getUsers: ->
    Utils.robot.adapter?.client?.rtm?.dataStore?.users or Utils.robot.brain.users()

  @lookupUserWithGithub: (github) ->
    return Promise.resolve() unless github

    findMatch = (user) ->
      name = user.name or user.login
      return unless name
      users = Utils.getUsers()
      users = _(users).keys().map (id) ->
        u = users[id]
        id: u.id
        name: u.name
        real_name: u.real_name

      f = new Fuse users,
        keys: ['real_name']
        shouldSort: yes
        verbose: no
        threshold: 0.55

      results = f.search name
      result = if results? and results.length >=1 then results[0] else undefined
      return Promise.resolve result

    if github.fetch?
      github.fetch().then findMatch
    else
      findMatch github

module.exports = Utils

Allow for discovery of users for DMs

Allow for discovery of users for DMs


#= require ../hash/hash

class Batman.Params extends Batman.Hash
  constructor: (@hash, @navigator) ->
    super(@hash)

    @url = new Batman.UrlParams({}, @navigator, this)

  @accessor 'url', -> @url

class Batman.UrlParams extends Batman.Hash
  constructor: (@hash, @navigator, @params) ->
    super(@hash)

    @replace(@_paramsFromUri())
    @updateParams()

    @on 'change', (obj) =>
      obj.updateUrl()
      obj.updateParams()

  updateUrl: ->
    @navigator.pushState(null, '', @_pathFromParams())

  updateParams: ->
    @params.update(@toObject())

  _paramsFromUri: ->
    @_currentUri().queryParams

  _currentPath: ->
    @params.get('path')

  _currentUri: ->
    new Batman.URI(@_currentPath())

  _pathFromRoutes: ->
    route = @navigator.app.get('currentRoute')
    params =
      controller: route.controller
      action: route.action

    Batman.mixin(params, @toObject())

    @navigator.app.get('dispatcher').pathFromParams(params)

  _pathFromParams: ->
    if path = @_pathFromRoutes()
      return path

    uri = @_currentUri()
    uri.queryParams = @toObject()
    uri.toString()

#= require ../hash/hash

class Batman.Params extends Batman.Hash
  constructor: (@hash, @navigator) ->
    super(@hash)

    @url = new Batman.UrlParams({}, @navigator, this)

  @accessor 'url', -> @url

class Batman.UrlParams extends Batman.Hash
  constructor: (@hash, @navigator, @params) ->
    super(@hash)

    @replace(@_paramsFromUri())
    @_updateParams()

    @on 'change', (obj) ->
      obj._updateUrl()
      obj._updateParams()

  _updateUrl: ->
    @navigator.pushState(null, '', @_pathFromParams())

  _updateParams: ->
    @params.update(@toObject())

  _paramsFromUri: ->
    @_currentUri().queryParams

  _currentPath: ->
    @params.get('path')

  _currentUri: ->
    new Batman.URI(@_currentPath())

  _pathFromRoutes: ->
    route = @navigator.app.get('currentRoute')
    params =
      controller: route.controller
      action: route.action

    Batman.mixin(params, @toObject())

    @navigator.app.get('dispatcher').pathFromParams(params)

  _pathFromParams: ->
    if path = @_pathFromRoutes()
      return path

    uri = @_currentUri()
    uri.queryParams = @toObject()
    uri.toString()

Mark all methods as private

Mark all methods as private


_ = require 'underscore'
Backbone = require 'backbone'
Artist = require '../../../models/artist.coffee'
CurrentUser = require '../../../models/current_user.coffee'
ArtistRouter = require './router.coffee'
analytics = require './analytics.coffee'
attachArtworkModal = require '../../../components/page_modal/index.coffee'

module.exports.init = ->
  analytics.listenToEvents()

  model = new Artist sd.ARTIST
  user = CurrentUser.orNull()
  router = new ArtistRouter model: model, user: user

  Backbone.history.start pushState: true

  if user?.isAdmin?()
    attachArtworkModal '.carousel-figure, .artwork-item-image-link', {
      'artist/:id': 'close'
      'artwork/:id': 'modal'
    }

  analytics.trackArtistPageView model

_ = require 'underscore'
Backbone = require 'backbone'
Artist = require '../../../models/artist.coffee'
CurrentUser = require '../../../models/current_user.coffee'
ArtistRouter = require './router.coffee'
analytics = require './analytics.coffee'
attachArtworkModal = require '../../../components/page_modal/index.coffee'

module.exports.init = ->
  analytics.listenToEvents()

  model = new Artist sd.ARTIST
  user = CurrentUser.orNull()
  router = new ArtistRouter model: model, user: user

  Backbone.history.start pushState: true

  if user?.isAdmin?()
    selectors = [
      '.carousel-figure a[href^="/artwork"]'
      '#artwork-section .artwork-item-image-link'
    ]

    attachArtworkModal selectors.join(', '), {
      'artist/:id': 'close'
      'artwork/:id': 'modal'
    }

  analytics.trackArtistPageView model

Fix selectors for enabled test

Fix selectors for enabled test


"*":
  core:
    disabledPackages: [
      "linter-pylint"
    ]
    themes: [
      "seti-ui"
      "base16-tomorrow-dark-theme"
    ]
  editor:
    fontSize: 13
    invisibles: {}
  "exception-reporting":
    userId: "da976a4b-6a5d-3f34-7d9b-6bfdb74474ed"
  "git-diff":
    showIconsInEditorGutter: true
  "git-plus": {}
  linter: {}
  "linter-clang":
    execPath: "/usr/bin/clang"
    verboseDebug: true
  "linter-pylint":
    executable: "/usr/local/bin/pylint"
  "one-dark-ui": {}
  "vim-mode": {}
  welcome:
    showOnStartup: false
  whitespace:
    removeTrailingWhitespace: false

"*":
  core:
    disabledPackages: [
      "linter-pylint"
    ]
    themes: [
      "seti-ui"
      "base16-tomorrow-dark-theme"
    ]
  editor:
    fontSize: 13
    invisibles: {}
  "exception-reporting":
    userId: "da976a4b-6a5d-3f34-7d9b-6bfdb74474ed"
  "git-diff":
    showIconsInEditorGutter: true
  "git-plus": {}
  linter: {}
  "linter-clang":
    execPath: "/usr/bin/clang"
    verboseDebug: true
  "linter-pylint":
    executable: "/usr/local/bin/pylint"
  "one-dark-ui": {}
  "seti-ui":
    compactView: true
    themeColor: "Steel"
  "vim-mode": {}
  welcome:
    showOnStartup: false
  whitespace:
    removeTrailingWhitespace: false

Change atom seti theme colour to steel

Change atom seti theme colour to steel


window.FIXTURES = {} if window.FIXTURES is undefined

FIXTURES.user = (id = 1)->
  user:
    id: id
    bio: null
    created_at: "2014-07-04T12:42:41+00:00"
    email: "foo@bar.name"
    facebook_url: null
    linkedin_url: null
    other_url: null
    profile_type: "personal"
    twitter_url: null
    name: "Foo Bar"
    image_url: "http://neighbor.ly/uploads/user/uploaded_image/18/thumb_avatar_image.jpg"
    total_contributed: "70.0"
    admin: false
    url: "http://neighbor.ly/api/contributions/18"
    html_url: "http://neighbor.ly/neighbors/18-mrs-abigayle-gaylord"

FIXTURES.users = (page = 3)->
  users: [FIXTURES.user(1).user, FIXTURES.user(2).user]
  meta:
    page: page
    total: 2
    total_pages: 10

window.FIXTURES = {} if window.FIXTURES is undefined

FIXTURES.user = (id = 1)->
  user:
    id: id
    bio: null
    created_at: "2014-07-04T12:42:41+00:00"
    email: "foo@bar.name"
    facebook_url: null
    linkedin_url: null
    other_url: null
    profile_type: "personal"
    twitter_url: null
    name: "Foo Bar"
    image_url: "http://neighbor.ly/uploads/user/uploaded_image/18/thumb_avatar_image.jpg"
    total_contributed: "70.0"
    admin: true
    url: "http://neighbor.ly/api/contributions/18"
    html_url: "http://neighbor.ly/neighbors/18-mrs-abigayle-gaylord"

FIXTURES.users = (page = 3)->
  users: [FIXTURES.user(1).user, FIXTURES.user(2).user]
  meta:
    page: page
    total: 2
    total_pages: 10

Change user fixture to be an admin

Change user fixture to be an admin


class Batman.DOM.InsertionBinding extends Batman.DOM.AbstractBinding
  onlyObserve: Batman.BindingDefinitionOnlyObserve.Data
  bindImmediately: false

  constructor: (definition) ->
    {@invert} = definition
    super

    @placeholderNode = document.createComment("batman-insertif=\"#{@keyPath}\"")

  ready: ->
    @bind()

  dataChange: (value) ->
    view = Batman.View.viewForNode(@node, false)
    parentNode = @placeholderNode.parentNode || @node.parentNode

    if !!value is !@invert
      # Show
      view?.fire('viewWillShow')
      if not @node.parentNode?
        parentNode.insertBefore(@node, @placeholderNode)
        Batman.DOM.destroyNode(@placeholderNode)
      view?.fire('viewDidShow')
    else
      # Hide
      view?.fire('viewWillHide')
      if @node.parentNode?
        parentNode.insertBefore(@placeholderNode, @node)
        Batman.DOM.destroyNode(@node)
      view?.fire('viewDidHide')

  die: ->
    @placeholderNode = null
    super

class Batman.DOM.InsertionBinding extends Batman.DOM.AbstractBinding
  onlyObserve: Batman.BindingDefinitionOnlyObserve.Data
  bindImmediately: false

  constructor: (definition) ->
    {@invert} = definition
    super

    @placeholderNode = document.createComment("batman-insertif=\"#{@keyPath}\"")

  ready: ->
    @bind()

  dataChange: (value) ->
    view = Batman.View.viewForNode(@node, false)
    parentNode = @placeholderNode.parentNode || @node.parentNode

    if !!value is !@invert
      # Show
      view?.fire('viewWillShow')
      if not @node.parentNode?
        parentNode.insertBefore(@node, @placeholderNode)
        parentNode.removeChild(@placeholderNode)
      view?.fire('viewDidShow')
    else
      # Hide
      view?.fire('viewWillHide')
      if @node.parentNode?
        parentNode.insertBefore(@placeholderNode, @node)
        parentNode.removeChild(@node)

      view?.fire('viewDidHide')

  die: ->
    @placeholderNode = null
    super

Fix insertion bindings destroying their nodes rather than removing them


class Tenon.features.DateTimePicker
  constructor: ->
    @$els = $("[data-behaviour~='datetime-picker']")
    @format = "MMM. DD, YYYY [at] hh:mm A"
    $.each(@$els, @_formatDate)
    @_enableDateTimePicker()
    @_setupUnfocus()

  _formatDate: (i, el) =>
    $el = $(el)
    if $el.val() != '' && $el.val().match(/^\d{4}\-\d{2}\-\d{2}.*$/)
      railsFormat = "YYYY-MM-DD HH:mm:ss ZZ"
      reformatted = moment($el.val(), railsFormat).format(@format)
      $(el).val(reformatted)

  _enableDateTimePicker: =>
    @$els.datetimepicker
      pick12HourFormat: true,
      pickSeconds: false,
      format: @format

  # Blur the element when it's focused, preventing a soft-keyboard
  # from appearing
  _setupUnfocus: =>
    @$els.attr('readonly', true)

class Tenon.features.DateTimePicker
  constructor: ->
    @$els = $("[data-behaviour~='datetime-picker']")
    @format = "MMM. DD, YYYY [at] hh:mm A Z"
    $.each(@$els, @_formatDate)
    @_enableDateTimePicker()
    @_setupUnfocus()

  _formatDate: (i, el) =>
    $el = $(el)
    if $el.val() != '' && $el.val().match(/^\d{4}\-\d{2}\-\d{2}.*$/)
      railsFormat = "YYYY-MM-DD HH:mm:ss ZZ"
      reformatted = moment($el.val(), railsFormat).format(@format)
      $(el).val(reformatted)

  _enableDateTimePicker: =>
    @$els.datetimepicker
      pick12HourFormat: true,
      pickSeconds: false,
      format: @format

  # Blur the element when it's focused, preventing a soft-keyboard
  # from appearing
  _setupUnfocus: =>
    @$els.attr('readonly', true)

Add UTC offset to the datetime format

Add UTC offset to the datetime format


# Export generic testhelpers
testhelpers = require '../../../../../testhelper'

{ expect
  request
  generateUrl
  checkBongoConnectivity
  generateRequestParamsEncodeBody } = testhelpers

utils = require './utils'

testhelpers.gitlabApiUrl = generateUrl { route : '-/api/gitlab' }
testhelpers.gitlabDefaultHeaders = {
  'x-gitlab-event': 'System Hook',
  'x-gitlab-token': utils.GITLAB_TOKEN
}

testhelpers.getSampleDataFor = (event) ->

  return (require './_sampledata')[event] ? { 'event_name': event }

testhelpers.parseEvent = utils.parseEvent

module.exports = testhelpers

# Export generic testhelpers
testhelpers = require '../../../../../testhelper'

{ expect
  request
  generateUrl
  checkBongoConnectivity
  generateRequestParamsEncodeBody } = testhelpers

utils = require './utils'

testhelpers.gitlabApiUrl = generateUrl { route : '-/api/gitlab' }
testhelpers.gitlabDefaultHeaders = {
  'x-gitlab-event': 'System Hook',
  'x-gitlab-token': utils.GITLAB_TOKEN
}

testhelpers.getSampleDataFor = (event) ->

  return (require './_sampledata')[event] ? { 'event_name': event }

testhelpers.parseEvent = utils.parseEvent

testhelpers.doRequestFor = (event, callback) ->

  { scope, method } = utils.parseEvent event

  params    = generateRequestParamsEncodeBody
    url     : testhelpers.gitlabApiUrl
    headers : testhelpers.gitlabDefaultHeaders
    body    : testhelpers.getSampleDataFor event

  request.post params, callback

module.exports = testhelpers

GitLab: Test helpers for generic requests on tests


regexes =
  nonEnglishOperators: /[&|\||\=]{2}|\!\=/

module.exports = class RuleProcessor
  rule:
    name: 'prefer_english_operator'
    description: '''
      This rule prohibits &&, ||, == and !=.
      Use and, or, is, and isnt instead.
      '''
    level: 'warn'
    message: 'Don\'t use &&, ||, == and !='

  lintLine: (line, lineApi) ->
    lineTokens = lineApi.getLineTokens()

    for token in lineTokens
      if token[0] in ['COMPARE', 'LOGIC']
        location = token[2]
        substring = line[location.first_column..location.last_column]
        hasNonEnglishOperators = substring.match regexes.nonEnglishOperators
        if hasNonEnglishOperators
          return {context: "Found: #{hasNonEnglishOperators[0]}"}

module.exports = class RuleProcessor
  rule:
    name: 'prefer_english_operator'
    description: '''
    This rule prohibits &&, ||, ==, != and !.
    Use and, or, is, isnt, and not instead.
    !! for converting to a boolean is ignored.
    '''
    level: 'ignore'
    doubleNotLevel: 'ignore'
    message: 'Don\'t use &&, ||, ==, !=, or !'

  tokens: ['COMPARE', 'UNARY_MATH', 'LOGIC']
  lintToken: (token, tokenApi) ->
    config = tokenApi.config[@rule.name]
    level = config.level
    # Compare the actual token with the lexed token.
    { first_column, last_column } = token[2]
    line = tokenApi.lines[tokenApi.lineNumber]
    actual_token = line[first_column..last_column]
    context =
      switch actual_token
        when '==' then 'Replace "==" with "is"'
        when '!=' then 'Replace "!=" with "isnt"'
        when '||' then 'Replace "||" with "or"'
        when '&&' then 'Replace "&&" with "and"'
        when '!'
          # `not not expression` seems awkward, so `!!expression`
          # gets special handling.
          if tokenApi.peek(1)?[0] is 'UNARY_MATH'
            level = config.doubleNotLevel
            '"?" is usually better than "!!"'
          else if tokenApi.peek(-1)?[0] is 'UNARY_MATH'
            # Ignore the 2nd half of the double not
            undefined
          else
            'Replace "!" with "not"'
        else undefined

    if context?
      { level, context }

Update source to latest in `coffeelint`

Update source to latest in `coffeelint`


Dashboard.ProjectsTabController = Ember.Controller.extend Dashboard.SearchableBaseController,
  baseRouteName: 'projects'

  search:
    pg_search: null

Dashboard.ProjectsSearchController = Dashboard.ProjectsTabController.extend Dashboard.SearchableController,
  baseRouteName: 'projects'

Dashboard.ProjectsTabController = Ember.Controller.extend Dashboard.SearchableBaseController,
  baseRouteName: 'projects'

  search:
    query: null
    between_created_at:
      starts_at: null
      ends_at: null
    between_expires_at:
      starts_at: null
      ends_at: null
    between_online_date:
      starts_at: null
      ends_at: null

Dashboard.ProjectsSearchController = Dashboard.ProjectsTabController.extend Dashboard.SearchableController,
  baseRouteName: 'projects'

Define search fields for projects

Define search fields for projects


@connections = {}
@startStreamBroadcast = (streams) ->
	console.log 'startStreamBroadcast'

	# connections = {}

	InstanceStatus.getCollection().find().observe
		added: (record) ->
			if record.extraInformation.port is process.env.PORT
				return

			console.log 'connecting in', "localhost:#{record.extraInformation.port}"
			connections[record.extraInformation.port] = DDP.connect("localhost:#{record.extraInformation.port}", {_dontPrintErrors: true})

		removed: (record) ->
			if connections[record.extraInformation.port]?
				console.log 'disconnecting from', "localhost:#{record.extraInformation.port}"
				connections[record.extraInformation.port].disconnect()
				delete connections[record.extraInformation.port]

	broadcast = (streamName, eventName, args, userId) ->
		for port, connection of connections
			if connection.status().connected is true
				connection.call 'stream', streamName, eventName, args

	emitters = {}

	for streamName, stream of streams
		do (streamName, stream) ->
			emitters[streamName] = stream.emit
			stream.emit = (eventName, args...) ->
				broadcast streamName, eventName, args
				emitters[streamName].call {}, arguments

	Meteor.methods
		stream: (streamName, eventName, args) ->
			args.unshift eventName
			emitters[streamName]?.apply {}, args

Meteor.startup ->
	startStreamBroadcast
		'webrtc.stream': webrtc.stream

@connections = {}
@startStreamBroadcast = (streams) ->
	console.log 'startStreamBroadcast'

	# connections = {}

	InstanceStatus.getCollection().find().observe
		added: (record) ->
			if record.extraInformation.port is process.env.PORT
				return

			console.log 'connecting in', "localhost:#{record.extraInformation.port}"
			connections[record.extraInformation.port] = DDP.connect("localhost:#{record.extraInformation.port}", {_dontPrintErrors: true})

		removed: (record) ->
			if connections[record.extraInformation.port]?
				console.log 'disconnecting from', "localhost:#{record.extraInformation.port}"
				connections[record.extraInformation.port].disconnect()
				delete connections[record.extraInformation.port]

	broadcast = (streamName, eventName, args, userId) ->
		for port, connection of connections
			if connection.status().connected is true
				connection.call 'stream', streamName, eventName, args

	emitters = {}

	for streamName, stream of streams
		do (streamName, stream) ->
			emitters[streamName] = stream.emit
			stream.emit = (eventName, args...) ->
				broadcast streamName, eventName, args
				emitters[streamName].apply {}, arguments

	Meteor.methods
		stream: (streamName, eventName, args) ->
			args.unshift eventName
			emitters[streamName]?.apply {}, args

Meteor.startup ->
	startStreamBroadcast
		'webrtc.stream': webrtc.stream

Fix emitter in same server

Fix emitter in same server


module.exports = """
  fragment current on Artist {
    auction: sales(size:2, live: true, is_auction: true){
      cover_image {
        cropped(width: 150, height: 104) {
          url
        }
      }
      href
      name
      start_at
      end_at
    }
    show: partner_shows(size:2, exclude_fair_booths: true, active:true, sort:end_at_asc) {
      cover_image {
        cropped(width: 150, height: 104) {
          url
        }
      }
      href
      name
      start_at
      end_at
    }
  }
"""

module.exports = """
  fragment current on Artist {
    auction: sales(size:2, live: true, is_auction: true){
      cover_image {
        cropped(width: 150, height: 104) {
          url
        }
      }
      href
      name
      start_at
      end_at
    }
    show: partner_shows(size:2, at_a_fair: false, active:true, sort:end_at_asc) {
      cover_image {
        cropped(width: 150, height: 104) {
          url
        }
      }
      href
      name
      start_at
      end_at
    }
  }
"""

Switch exclude_fair_booths param to match an updated Gravity API


_ = require 'underscore'
React = require 'react'
classnames = require 'classnames'
{ExerciseStore} = require '../../flux/exercise'
String = require '../../helpers/string'
{ExercisePreview} = require 'openstax-react-components'

Exercise = React.createClass

  propTypes:
    exercise: React.PropTypes.object.isRequired
    ignoredTypes: React.PropTypes.object.isRequired

  renderHeader: (types) ->

      {for type in types
        className = classnames(type, {'is-ignored': @props.ignoredTypes[type]})
        {String.titleize(type)}}

  render: ->
    {exercise, ignoredTypes, show2StepPreview} = @props
    types = ExerciseStore.getExerciseTypes(exercise)
    return null if _.every( types, (pt) -> ignoredTypes[pt] )

    editUrl = exercise.url.replace(/@\d+/, '@draft')

    doQuestionsHaveFormat = ExerciseStore.doQuestionsHaveFormat('free-response', {exercise})

    if show2StepPreview
      freeResponse = _.map doQuestionsHaveFormat, (hasFreeResponse) ->

 if hasFreeResponse

    <ExercisePreview {...@props}
      className='exercise'
      header={@renderHeader(types)}
      questionFooters={freeResponse}
      displayAllTags
      displayFeedback
    >
      edit
    </ExercisePreview>

module.exports = Exercise

_ = require 'underscore'
React = require 'react'
classnames = require 'classnames'
{ExerciseStore} = require '../../flux/exercise'
String = require '../../helpers/string'
{ExercisePreview} = require 'openstax-react-components'

Exercise = React.createClass

  propTypes:
    exercise: React.PropTypes.object.isRequired
    ignoredTypes: React.PropTypes.object.isRequired

  renderHeader: (types) ->

      {for type in types
        className = classnames(type, {'is-ignored': @props.ignoredTypes[type]})
        {String.titleize(type)}}

  render: ->
    {exercise, ignoredTypes, show2StepPreview} = @props
    types = ExerciseStore.getExerciseTypes(exercise)

    return null if _.any(types) and _.every( types, (pt) -> ignoredTypes[pt] )

    editUrl = exercise.url.replace(/@\d+/, '@draft')

    doQuestionsHaveFormat = ExerciseStore.doQuestionsHaveFormat('free-response', {exercise})

    if show2StepPreview
      freeResponse = _.map doQuestionsHaveFormat, (hasFreeResponse) ->

 if hasFreeResponse

    <ExercisePreview {...@props}
      className='exercise'
      header={@renderHeader(types)}
      questionFooters={freeResponse}
      displayAllTags
      displayFeedback
    >
      edit
    </ExercisePreview>

module.exports = Exercise

Fix bug where untagged exercises would not render


{CompositeDisposable} = require 'event-kit'
{callAttachHooks} = require './space-pen-extensions'

class PanelElement extends HTMLElement
  createdCallback: ->
    @subscriptions = new CompositeDisposable

  getModel: -> @model

  setModel: (@model) ->
    view = @model.getItemView()
    @appendChild(view)
    callAttachHooks(view) # for backward compatibility with SpacePen views

    @classList.add(@model.getClassName().split(' ')...) if @model.getClassName()?
    @subscriptions.add @model.onDidChangeVisible(@visibleChanged.bind(this))
    @subscriptions.add @model.onDidDestroy(@destroyed.bind(this))

  attachedCallback: ->
    @visibleChanged(@model.isVisible())

  visibleChanged: (visible) ->
    if visible
      @style.display = null
    else
      @style.display = 'none'

  destroyed: ->
    @subscriptions.dispose()
    @parentNode?.removeChild(this)

module.exports = PanelElement = document.registerElement 'atom-panel', prototype: PanelElement.prototype

{CompositeDisposable} = require 'event-kit'
{callAttachHooks} = require './space-pen-extensions'

class PanelElement extends HTMLElement
  createdCallback: ->
    @subscriptions = new CompositeDisposable

  getModel: -> @model

  setModel: (@model) ->
    view = @model.getItemView()
    @appendChild(view)

    @classList.add(@model.getClassName().split(' ')...) if @model.getClassName()?
    @subscriptions.add @model.onDidChangeVisible(@visibleChanged.bind(this))
    @subscriptions.add @model.onDidDestroy(@destroyed.bind(this))

  attachedCallback: ->
    callAttachHooks(@model.getItemView()) # for backward compatibility with SpacePen views
    @visibleChanged(@model.isVisible())

  visibleChanged: (visible) ->
    if visible
      @style.display = null
    else
      @style.display = 'none'

  destroyed: ->
    @subscriptions.dispose()
    @parentNode?.removeChild(this)

module.exports = PanelElement = document.registerElement 'atom-panel', prototype: PanelElement.prototype

Call space pen attach hooks after attached


Q = require 'bluebird-q'
_ = require 'underscore'
StepView = require './step.coffee'
Form = require '../../form/index.coffee'
Representatives = require '../../../collections/representatives.coffee'
template = -> require('../templates/specialist.jade') arguments...

module.exports = class Specialist extends StepView
  className: 'iq-loadable is-loading'

  template: (data) ->
    template _.extend data,
      message: @inquiry.get('message')
      representative: @representative

  __events__:
    'click button': 'serialize'

  initialize: ->
    @representatives = new Representatives
    super

  setup: ->
    @__representatives__ = @representatives.fetch()
      .then => (@representative = @representatives.first())?.fetch()
      .then =>
        @render().$el.removeClass 'is-loading'

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

    form = new Form model: @inquiry, $form: @$('form')
    return unless form.isReady()

    form.state 'loading'

    @__serialize__ = Q.all [
      @inquiry.save _.extend { contact_gallery: false }, form.data()
      @user.save @inquiry.pick('name', 'email')
    ]
      .then =>
        @next()
      , (e) ->
        form.error null, e

Q = require 'bluebird-q'
_ = require 'underscore'
StepView = require './step.coffee'
Form = require '../../form/index.coffee'
Representatives = require '../../../collections/representatives.coffee'
template = -> require('../templates/specialist.jade') arguments...

module.exports = class Specialist extends StepView
  className: 'iq-loadable is-loading'

  template: (data) ->
    template _.extend data,
      message: @inquiry.get('message')
      representative: @representative

  __events__:
    'click button': 'serialize'

  initialize: ->
    @representatives = new Representatives
    super

  setup: ->
    @__representatives__ = Q @representatives.fetch()
      .then => Q (@representative = @representatives.first())?.fetch()
      .finally => @done()

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

    form = new Form model: @inquiry, $form: @$('form')
    return unless form.isReady()

    form.state 'loading'

    @__serialize__ = Q.all [
      @inquiry.save _.extend { contact_gallery: false }, form.data()
      @user.save @inquiry.pick('name', 'email')
    ]
      .then =>
        @next()
      , (e) ->
        form.error null, e

  done: ->
    @render().$el.removeClass 'is-loading'

Make representative fetch more resilient to API errors (probably only relevant for staging)


RangeFinder = require './range-finder'

module.exports =
  activate: (state) ->
    atom.workspaceView.command 'sort-lines:sort', '.editor', =>
      editor = atom.workspaceView.getActivePaneItem()
      @sortLines(editor)

  sortLines: (editor) ->
    lineRanges = RangeFinder.rangesFor(editor)
    lineRanges.forEach (lineRange) ->
      textLines = editor.getTextInBufferRange(lineRange).split("\n")
      textLines.sort (a, b) -> a.localeCompare(b)
      editor.setTextInBufferRange(lineRange, textLines.join("\n"))

RangeFinder = require './range-finder'

module.exports =
  activate: (state) ->
    atom.workspaceView.command 'sort-lines:sort', '.editor', =>
      editor = atom.workspaceView.getActivePaneItem()
      @sortLines(editor)

  sortLines: (editor) ->
    sortableRanges = RangeFinder.rangesFor(editor)
    sortableRanges.forEach (range) ->
      textLines = editor.getTextInBufferRange(range).split("\n")
      textLines.sort (a, b) -> a.localeCompare(b)
      editor.setTextInBufferRange(range, textLines.join("\n"))

Rename variable to increase clarity

Rename variable to increase clarity


"*":
  editor:
    fontFamily: "input"
    fontSize: 13
    showInvisibles: true
    showIndentGuide: true
    invisibles: {}
  core:
    themes: [
      "one-dark-ui"
      "atom-dark-syntax"
    ]
  welcome:
    showOnStartup: false
  whitespace:
    removeTrailingWhitespace: true
    ignoreWhitespaceOnCurrentLine: false
    ensureSingleTrailingNewline: true
  "exception-reporting":
    userId: "b6917109-8650-33d4-94c7-9edad015d73f"
  linter: {}
  "highlight-column": {}
"highlight-line":
  hideHighlightOnSelect: true

"*":
  editor:
    fontFamily: "input"
    fontSize: 13
    showInvisibles: true
    showIndentGuide: true
    invisibles: {}
  core:
    themes: [
      "one-dark-ui"
      "facebook-syntax"
    ]
  welcome:
    showOnStartup: false
  whitespace:
    removeTrailingWhitespace: true
    ignoreWhitespaceOnCurrentLine: false
    ensureSingleTrailingNewline: true
  "exception-reporting":
    userId: "b6917109-8650-33d4-94c7-9edad015d73f"
  linter: {}
  "highlight-column": {}
"highlight-line":
  hideHighlightOnSelect: true

Use Facebook syntax in Atom

Use Facebook syntax in Atom


Neighborly.Rewards = {} if Neighborly.Rewards is undefined

Neighborly.Rewards.Index = Backbone.View.extend
  el: '.rewards'

  initialize: ->
    this.$rewards = $(this.el)
    this.load()
    this.sortable()

  load: ->
    that = this
    $.ajax(
      url: that.$rewards.data("rewards-path")
      success: (data) ->
        $(that.el).html data
    )

  sortable: ->
    that = this
    if this.$rewards.data("can-update") is true
      this.$rewards.sortable
        axis: "y"
        placeholder: "sortable-highlight"
        start: (e, ui) ->
          ui.placeholder.height ui.item.height()

        stop: (e, ui) ->
          ui.item.effect "highlight", {}, 1000

        update: (e, ui) ->
          csrfToken = undefined
          position = undefined
          position = that.$rewards.childrend('.sortable').index(ui.item)
          csrfToken = $("meta[name='csrf-token']").attr("content")
          $.ajax
            type: "POST"
            url: ui.item.data("update-path")
            dataType: "json"
            headers:
              "X-CSRF-Token": csrfToken

            data:
              reward:
                row_order_position: position

Neighborly.Rewards = {} if Neighborly.Rewards is undefined

Neighborly.Rewards.Index = Backbone.View.extend
  el: '.rewards'

  initialize: ->
    this.$rewards = $(this.el)
    this.load()
    this.sortable()

  load: ->
    that = this
    $.ajax(
      url: that.$rewards.data("rewards-path")
      success: (data) ->
        $(that.el).html data
    )

  sortable: ->
    that = this
    if this.$rewards.data("can-update") is true
      this.$rewards.sortable
        axis: "y"
        placeholder: "sortable-highlight"
        start: (e, ui) ->
          ui.placeholder.height ui.item.height()

        stop: (e, ui) ->
          ui.item.effect "highlight", {}, 1000

        update: (e, ui) ->
          csrfToken = undefined
          position = undefined
          position = that.$rewards.find('.sortable').index(ui.item)
          csrfToken = $("meta[name='csrf-token']").attr("content")
          $.ajax
            type: "POST"
            url: ui.item.data("update-path")
            dataType: "json"
            headers:
              "X-CSRF-Token": csrfToken

            data:
              reward:
                row_order_position: position

Replace childrend by find in jquery

Replace childrend by find in jquery


ETahi.PaperIndexRoute = Ember.Route.extend
  afterModel: (model) ->
    @transitionTo('paper.edit', model) unless model.get('submitted')

  actions:
    viewCard: (task) ->
      paper = @modelFor('paper')
      redirectParams = ['paper.index', @modelFor('paper')]
      @controllerFor('application').set('overlayRedirect', redirectParams)
      @controllerFor('application').set('overlayBackground', 'paper/index')
      @transitionTo('task', paper.id, task.id)

ETahi.PaperIndexRoute = Ember.Route.extend
  afterModel: (model) ->
    @transitionTo('paper.edit', model) unless model.get('submitted')

  setupController: (controller, model) ->
    controller.set('model', model)
    controller.set 'authors', @store.all('author').filter (author) =>
      author.get('authorGroup.paper') == model

  actions:
    viewCard: (task) ->
      paper = @modelFor('paper')
      redirectParams = ['paper.index', @modelFor('paper')]
      @controllerFor('application').set('overlayRedirect', redirectParams)
      @controllerFor('application').set('overlayBackground', 'paper/index')
      @transitionTo('task', paper.id, task.id)

Add setupController hook to paper index route


class SystemSettings extends Lanes.Models.Base

    mixins: [ Lanes.Models.Mixins.FileSupport ]

    props:
        id:       {type:"integer"}
        logo:     "file"
        settings: {type: "object", required: true}

    modelTypeIdentifier: -> 'system-settings'
    url: -> Lanes.config.api_path + '/system-settings'

    initialize: ->
        @settings ||= {}
        @settings.lanes || = {}

class Config extends Lanes.Models.State

    session:
        csrf_token:  { type: 'string', setOnce: true }
        api_path:    { type: 'string', setOnce: true }
        assets_path_prefix: { type: 'string', setOnce: true }
        environment: { type: 'string', setOnce: true }
        initial_workspace_screen_id: { type: 'string', setOnce: true }
        system_settings: { type: 'state', required: true }

    derived:
        env:
            deps: ['environment'], fn: ->
                {
                    test:        this.environment == 'test',
                    development: this.environment == 'development',
                    production:  this.environment == 'production'
                }

    initialize: ->
        @system_settings = new SystemSettings

    bootstrap: (options) ->
        if options.system_settings
            @system_settings.set(options.system_settings)
            delete options.system_settings
        @set(options)
        Lanes.Extensions.setBootstrapData(options) if _.isObject(options)

Lanes.config = new Config

class SystemSettings extends Lanes.Models.Base

    mixins: [ Lanes.Models.Mixins.FileSupport ]

    props:
        id:       {type:"integer"}
        logo:     "file"
        settings: {type: "object", required: true}

    modelTypeIdentifier: -> 'system-settings'
    url: -> Lanes.config.api_path + '/system-settings'
    initialize: ->
        @on('change:settings', @setDefaultSettings)
        @setDefaultSettings()

    setDefaultSettings: ->
        @settings ||= {}
        @settings.lanes || = {}

class Config extends Lanes.Models.State

    session:
        csrf_token:  { type: 'string', setOnce: true }
        api_path:    { type: 'string', setOnce: true }
        assets_path_prefix: { type: 'string', setOnce: true }
        environment: { type: 'string', setOnce: true }
        initial_workspace_screen_id: { type: 'string', setOnce: true }
        system_settings: { type: 'state', required: true }

    derived:
        env:
            deps: ['environment'], fn: ->
                {
                    test:        this.environment == 'test',
                    development: this.environment == 'development',
                    production:  this.environment == 'production'
                }

    initialize: ->
        @system_settings = new SystemSettings

    bootstrap: (options) ->
        if options.system_settings
            @system_settings.set(options.system_settings)
            delete options.system_settings
        @set(options)
        Lanes.Extensions.setBootstrapData(options) if _.isObject(options)

Lanes.config = new Config

Make sure "lanes" config is still present after set


'use strict'

angular.module('uiApp')
  .factory 'Channel', ($q, $callback, $rootScope, Config) ->
    Channel = {}

    ChannelObject = (socket) ->
      @socket = socket
      me = this
      @socket.onmessage = (e) ->
        me.onMessage(e)
      @matrix = {}
      this

    ChannelObject::send = (data) ->
      dfd = $callback.defer()
      id = _.uniqueId()
      @matrix[id] = dfd
      payload = angular.extend(data, id: id)
      @socket.send(angular.toJson(payload))
      dfd.promise

    ChannelObject::onMessage = (e) ->
      packet = angular.fromJson(e.data)
      id = packet.id
      matrix = @matrix
      $rootScope.$apply () ->
        matrix[id].notify(packet)

    channelDeferred = $callback.defer()
    Channel.connected = () ->
      channelDeferred.promise

    getConfig = () ->
      dfd = $q.defer()

      Config.get().then (data) ->
        dfd.resolve(data.api_server)

      dfd.promise

    connect = (url) ->
      socket = new WebSocket(url)
      socket.onclose = () ->
        setTimeout((->
          connect(url)
        ), 1000)
      socket.onopen = () ->
        channel = new ChannelObject(socket)
        channelDeferred.notify(channel)

    getConfig().then (url) ->
      connect(url)

    Channel

'use strict'

angular.module('uiApp')
  .factory 'Channel', ($q, $callback, $rootScope, Config) ->
    Channel = {}

    ChannelObject = (socket) ->
      @socket = socket
      me = this
      @socket.onmessage = (e) ->
        me.onMessage(e)
      @matrix = {}
      this

    ChannelObject::send = (data) ->
      dfd = $callback.defer()
      id = _.uniqueId()
      @matrix[id] = dfd
      payload = angular.extend(data, id: id)
      @socket.send(angular.toJson(payload))
      dfd.promise

    ChannelObject::onMessage = (e) ->
      packet = angular.fromJson(e.data)
      id = packet.id
      matrix = @matrix
      $rootScope.$apply () ->
        matrix[id].notify(packet)

    channelDeferred = $q.defer()
    Channel.connected = () ->
      channelDeferred.promise

    getConfig = () ->
      dfd = $q.defer()

      Config.get().then (data) ->
        dfd.resolve(data.api_server)

      dfd.promise

    connect = (url) ->
      socket = new WebSocket(url)
      socket.onopen = () ->
        channel = new ChannelObject(socket)
        channelDeferred.resolve(channel)

    getConfig().then (url) ->
      connect(url)

    Channel

Remove channel reconnection as it causes problems


Template.oembedUrlWidget.helpers
	description: ->
		if not this.meta?
			return

		return this.meta.ogDescription or this.meta.twitterDescription or this.meta.description

	title: ->
		if not this.meta?
			return

		return this.meta.ogTitle or this.meta.twitterTitle or this.meta.title or this.meta.pageTitle

	image: ->
		if not this.meta?
			return

		return this.meta.ogImage or this.meta.twitterImage

Template.oembedUrlWidget.helpers
	description: ->
		if not this.meta?
			return

		description = this.meta.ogDescription or this.meta.twitterDescription or this.meta.description
		if not description?
			return

		return description.replace /(^“)|(”$)/g, ''

	title: ->
		if not this.meta?
			return

		return this.meta.ogTitle or this.meta.twitterTitle or this.meta.title or this.meta.pageTitle

	image: ->
		if not this.meta?
			return

		return this.meta.ogImage or this.meta.twitterImage

Remove quotes from twitter oembeds

Remove quotes from twitter oembeds


{Range} = require('atom')

Helpers = module.exports =
  validateMessages: (results) ->
    if (not results) or results.constructor.name isnt 'Array'
      throw new Error "Got invalid response from Linter, Type: #{typeof results}"
    for result in results
      unless result.type
        throw new Error "Missing type field on Linter Response, Got: #{Object.keys(result)}"
      result.range = Range.fromObject result.range if result.range?
      result.class = result.type.toLowerCase().replace(' ', '-')
      Helpers.validateMessages(result.trace) if result.trace
  validateLinter: (linter) ->
    unless linter.grammarScopes instanceof Array
      message = "grammarScopes is not an Array. (see console for more info)"
      console.warn(message)
      console.warn('grammarScopes', linter.grammarScopes)
      throw new Error(message)

    unless linter.lint?
      throw new Error("Missing linter.lint")

    if typeof linter.lint isnt 'function'
      throw new Error("linter.lint isn't a function")

    return true

{Range} = require('atom')

Helpers = module.exports =
  validateMessages: (results) ->
    if (not results) or results.constructor.name isnt 'Array'
      throw new Error "Got invalid response from Linter, Type: #{typeof results}"
    for result in results
      unless result.type
        throw new Error "Missing type field on Linter Response, Got: #{Object.keys(result)}"
      result.range = Range.fromObject result.range if result.range?
      result.class = result.type.toLowerCase().replace(' ', '-')
      Helpers.validateMessages(result.trace) if result.trace
    return # Explicit return to return undefined
  validateLinter: (linter) ->
    unless linter.grammarScopes instanceof Array
      message = "grammarScopes is not an Array. (see console for more info)"
      console.warn(message)
      console.warn('grammarScopes', linter.grammarScopes)
      throw new Error(message)

    unless linter.lint?
      throw new Error("Missing linter.lint")

    if typeof linter.lint isnt 'function'
      throw new Error("linter.lint isn't a function")

    return true

:bug: Return undefined explicitly in Helpers::validateMessages


###
A Result represents the...well...result of a Task.

Any function given to {Scriptable#addTask} is expected to return a {Result}.
###

module.exports = class Component
  constructor: (@owner) ->

  ###
  Copys all functions from the component to {#owner}.
  Ignores functions starting with an underscore and refuses to
  overwrite functions.
  ###
  mount: () =>
    for method of @
      if @owner[method]?
        console.debug 'Method %s exists already and will not be mounted on %O', method, @owner
        continue
      if method[0] is '_'
        continue
      @owner[method] = do (method) => =>
        @[method].apply @, arguments

###
A Result represents the...well...result of a Task.

Any function given to {Scriptable#addTask} is expected to return a {Result}.
###

module.exports = class Component
  constructor: (@owner) ->

  ###
  Copys all functions from the component to {#owner}.
  Ignores functions starting with an underscore and refuses to
  overwrite functions.
  ###
  mount: () =>
    for method of @
      if method[0] is '_' or      # don't copy private methods
      method == 'owner' or        # the owner property pointing to the components owner
      method == 'constructor' or  # the constructor
      method == 'mount'           # this method
        continue
      if @owner[method]?
        console.debug 'Method %s exists already and will not be mounted on %O', method, @owner
        continue
      @owner[method] = do (method) => =>
        @[method].apply @, arguments

Fix copying of constructor, mount itself and the owner property

Copying the owner property to the object it points to would be, well random...


React       = require('react/addons')
expect      = require('expect')
utils       = require('./utils')

TestUtils   = React.addons.TestUtils
Slider      = require('../slider')

describe 'Slider', ->
  before ->
    @component = TestUtils.renderIntoDocument(<Slider />)
    @component.setState({ sliderStart:  0, sliderLength: 1000 })

  describe '#events', ->
    it "sets pressed state when knob is clicked", ->
      TestUtils.Simulate.mouseDown(@component.refs.knob)
      expect(@component.state.pressed).toEqual(true)

    it "sets a proper value when the slider is clicked", ->
      TestUtils.Simulate.mouseDown(@component.refs.slider, { pageX: 800 })
      expect(@component.state.value).toEqual(80)

TestUtils   = React.addons.TestUtils
expect      = require('expect')
utils       = require('./utils')
Slider      = require('../slider')

describe 'Slider', ->
  describe '#events', ->
    slider = null

    before ->
      props = { min: -500, max: 500 }
      state = { sliderStart:  0, sliderLength: 1000 }
      slider = utils.renderComponent(Slider, props, state)

    it "sets pressed state when knob is clicked", ->
      TestUtils.Simulate.mouseDown(slider.refs.knob)
      expect(slider.state.pressed).toEqual(true)

    it "sets a proper value when the slider is clicked", ->
      TestUtils.Simulate.mouseDown(slider.refs.slider, { pageX: 200 })
      expect(slider.state.value).toEqual(-300)

Improve minimal tests for slider

Improve minimal tests for slider


grade = (student, period=(if b? then 7 else 6), messages={"A": "Excellent"}) ->
  if student.excellentWork
    "A+"
  else if student.okayStuff
    if student.triedHard then "B" else "B-"
  else
    "C"

square = (x) -> x * x

two = -> 2

math =
  root:   Math.sqrt
  square: square
  cube:   (x) -> x * square x

race = (winner, runners...) ->
  print winner, runners

class Animal extends Being
  constructor: (@name) ->

  move: (meters) ->
    alert @name + " moved #{meters}m."

hi = `function() {
  return [document.title, "Hello JavaScript"].join(": ");
}`

heredoc = """
CoffeeScript subst test #{ 010 + 0xf / 0b10 + "nested string #{ /\n/ }"}
"""

###
CoffeeScript Compiler v1.2.0
Released under the MIT License
###

OPERATOR = /// ^ (
?: [-=]>             # function
) ///

grade = (student, period=(if b? then 7 else 6), messages={"A": "Excellent"}) ->
  if student.excellentWork
    "A+"
  else if student.okayStuff
    if student.triedHard then "B" else "B-"
  else
    "C"

square = (x) -> x * x

two = -> 2

math =
  root:   Math.sqrt
  square: square
  cube:   (x) -> x * square x

race = (winner, runners...) ->
  print winner, runners

class Animal extends Being
  constructor: (@name) ->

  move: (meters) ->
    alert @name + " moved #{meters}m."

hi = `function() {
  return [document.title, "Hello JavaScript"].join(": ");
}`

heredoc = """
CoffeeScript subst test #{ 0o010 + 0xf / 0b10 + "nested string #{ /\n/ }"}
"""

###
CoffeeScript Compiler v1.2.0
Released under the MIT License
###

OPERATOR = /// ^ (
?: [-=]>             # function
) ///

Use valid CoffeeScript in example

Use valid CoffeeScript in example


"*":
  "exception-reporting":
    userId: "53c5f869-8a29-d7f5-ef48-bc598612bb8d"
  welcome:
    showOnStartup: false
  core: {}
  editor:
    invisibles: {}

"*":
  "exception-reporting":
    userId: "53c5f869-8a29-d7f5-ef48-bc598612bb8d"
  welcome:
    showOnStartup: false
  editor:
    invisibles: {}
  tabs:
    usePreviewTabs: true

Use preview tab like sublime

Use preview tab like sublime


@Pager =
  init: (@limit = 0, preload, @disable = false) ->
    @loading = $(".loading")
    if preload
      @offset = 0
      @getOld()
    else
      @offset = @limit
    @initLoadMore()

  getOld: ->
    @loading.show()
    $.ajax
      type: "GET"
      url: $(".content_list").data('href') || location.href
      data: "limit=" + @limit + "&offset=" + @offset
      complete: =>
        @loading.hide()
      success: (data) ->
        Pager.append(data.count, data.html)
      dataType: "json"

  append: (count, html) ->
    $(".content_list").append html
    if count > 0
      @offset += count
    else
      @disable = true

  initLoadMore: ->
    $(document).unbind('scroll')
    $(document).endlessScroll
      bottomPixels: 400
      fireDelay: 1000
      fireOnce: true
      ceaseFire: ->
        Pager.disable

      callback: (i) =>
        unless @loading.is(':visible')
          @loading.show()
          Pager.getOld()

@Pager =
  init: (@limit = 0, preload, @disable = false) ->
    @loading = $('.loading').first()

    if preload
      @offset = 0
      @getOld()
    else
      @offset = @limit
    @initLoadMore()

  getOld: ->
    @loading.show()
    $.ajax
      type: "GET"
      url: $(".content_list").data('href') || location.href
      data: "limit=" + @limit + "&offset=" + @offset
      complete: =>
        @loading.hide()
      success: (data) ->
        Pager.append(data.count, data.html)
      dataType: "json"

  append: (count, html) ->
    $(".content_list").append html
    if count > 0
      @offset += count
    else
      @disable = true

  initLoadMore: ->
    $(document).unbind('scroll')
    $(document).endlessScroll
      bottomPixels: 400
      fireDelay: 1000
      fireOnce: true
      ceaseFire: ->
        Pager.disable

      callback: (i) =>
        unless @loading.is(':visible')
          @loading.show()
          Pager.getOld()

Select just the first matched element

Select just the first matched element

This a generic selector. It was also affecting spinner icon of the tabs


App.CwbSearchInputsController = Em.ArrayController.extend
  needs: ['corpus', 'searches']

  corpusBinding: 'controllers.corpus.content'

  currentInterface: null

  init: ->
    # Show the preferred interface (simple, multiword or regex) for this user or corpus
    @set('currentInterface', @get('controllers.corpus.preferredSearchInterfaceVariant'))

    tags = @get('corpus.langs.firstObject.tags')
    @set 'content', [
      query: '',
      corpusShortName: @get('corpus.shortName'),
      posAttr: tags.attr,
      tags: tags.options
    ]

  isShowingSimple: (->
    @get('currentInterface') is 'simple'
  ).property('currentInterface').volatile()

  isShowingMultiword: (->
    @get('currentInterface') is 'multiword'
  ).property('currentInterface')

  isShowingRegex: (->
    @get('currentInterface') is 'regex'
  ).property('currentInterface')

  # Action handlers
  showSimple:    -> @set('currentInterface', 'simple')
  showMultiword: -> @set('currentInterface', 'multiword')
  showRegex:     -> @set('currentInterface', 'regex')

  search: (component, options = {}) ->
    options.queries = @get('content')
    @get('controllers.searches').createSearch('CwbSearch', options)

App.CwbSearchInputsController = Em.ArrayController.extend
  needs: ['corpus', 'searches']

  corpusBinding: 'controllers.corpus.content'

  currentInterface: null

  init: ->
    # Show the preferred interface (simple, multiword or regex) for this user or corpus
    @set('currentInterface', @get('controllers.corpus.preferredSearchInterfaceVariant'))

    tags = @get('corpus.langs.firstObject.tags')
    @set 'content', [
      query: '',
      corpusShortName: @get('corpus.shortName'),
      posAttr: tags?.attr,
      tags: tags?.options
    ]

  isShowingSimple: (->
    @get('currentInterface') is 'simple'
  ).property('currentInterface').volatile()

  isShowingMultiword: (->
    @get('currentInterface') is 'multiword'
  ).property('currentInterface')

  isShowingRegex: (->
    @get('currentInterface') is 'regex'
  ).property('currentInterface')

  # Action handlers
  showSimple:    -> @set('currentInterface', 'simple')
  showMultiword: -> @set('currentInterface', 'multiword')
  showRegex:     -> @set('currentInterface', 'regex')

  search: (component, options = {}) ->
    options.queries = @get('content')
    @get('controllers.searches').createSearch('CwbSearch', options)

Support corpora without a tagger config

Support corpora without a tagger config


module.exports = {

  name: "JSX"
  namespace: "jsx"
  fallback: ['js']

  ###
  Supported Grammars
  ###
  grammars: [
    "JSX"
    "JavaScript (JSX)"
    "Babel ES6 JavaScript"
  ]

  ###
  Supported extensions
  ###
  extensions: [
    "jsx",
    "js"
  ]

}

module.exports = {

  name: "JSX"
  namespace: "jsx"
  fallback: ['js']

  ###
  Supported Grammars
  ###
  grammars: [
    "JSX"
    "JavaScript (JSX)"
    "Babel ES6 JavaScript"
    "JavaScript with JSX"
  ]

  ###
  Supported extensions
  ###
  extensions: [
    "jsx",
    "js"
  ]

}

Add JavaScript with JSX grammar to JSX language


{CrudConfig, makeSimpleStore, extendConfig} = require './helpers'
_ = require 'underscore'

PerformanceConfig = {
  exports:
    getStudentOfTask: (courseId, taskId) ->
      performances = @_get(courseId)
      students = _.chain(performances)
        .pluck('students')
        .flatten(true)
        .value()

      # TODO remove when BE fixed for ids to be strings instead of numbers
      taskId = parseInt(taskId)

      _.find students, (student) ->
        taskIds = _.pluck student.data, 'id'
        _.indexOf(taskIds, taskId) > -1
}

extendConfig(PerformanceConfig, new CrudConfig())
{actions, store} = makeSimpleStore(PerformanceConfig)
module.exports = {PerformanceActions:actions, PerformanceStore:store}

{CrudConfig, makeSimpleStore, extendConfig} = require './helpers'
_ = require 'underscore'

allStudents = (performances) ->
  _.chain(performances)
    .pluck('students')
    .flatten(true)
    .value()

PerformanceConfig = {
  exports:

    getStudent: (courseId, roleId) ->
      students = allStudents @_get(courseId)
      # TODO remove parseInt when BE fixes role to be string
      _.findWhere(allStudents(@_get(courseId)), role: parseInt(roleId))

    getAllStudents: (courseId) ->
      allStudents @_get(courseId)

    getStudentOfTask: (courseId, taskId) ->
      students = allStudents @_get(courseId)

      # TODO remove when BE fixed for ids to be strings instead of numbers
      taskId = parseInt(taskId)

      _.find students, (student) ->
        taskIds = _.pluck student.data, 'id'
        _.indexOf(taskIds, taskId) > -1
}

extendConfig(PerformanceConfig, new CrudConfig())
{actions, store} = makeSimpleStore(PerformanceConfig)
module.exports = {PerformanceActions:actions, PerformanceStore:store}

Add methods to find or retrieve all students


Species.TaxonConceptSearchTextField = Em.TextField.extend
  value: ''
  currentTimeout: null

  attributeBindings: ['autocomplete']

  focusOut: (event) ->
    @.$().attr('placeholder', @get('placeholder'))
    @hideDropdown() if !@get('parentView.mousedOver')

  keyUp: (event) ->
    Ember.run.cancel(@currentTimeout)
    @currentTimeout = Ember.run.later(@, ->
      @showDropdown()
      @set('query', event.target.value)
    , 500)

  hideDropdown: () ->
    $('.search fieldset').removeClass('parent-focus parent-active')

  showDropdown: () ->
    if @.$().val().length > 2
      $('.search fieldset').addClass('parent-focus parent-active')

Species.TaxonConceptSearchTextField = Em.TextField.extend
  value: ''
  currentTimeout: null

  attributeBindings: ['autocomplete']

  focusOut: (event) ->
    @.$().attr('placeholder', @get('placeholder'))
    @hideDropdown() if !@get('parentView.mousedOver')

  keyUp: (event) ->
    Ember.run.cancel(@currentTimeout)
    if event.keyCode == 13
      @hideDropdown()
      return
    @currentTimeout = Ember.run.later(@, ->
      @showDropdown()
      @set('query', event.target.value)
    , 500)

  hideDropdown: () ->
    $('.search fieldset').removeClass('parent-focus parent-active')

  showDropdown: () ->
    if @.$().val().length > 2
      $('.search fieldset').addClass('parent-focus parent-active')

Hide suggestions dropdown if the keyUp is Enter


module.exports = {

  name: "YAML"
  namespace: "yaml"
  fallback: []
  scope: ['source.yaml']

  ###
  Supported Grammars
  ###
  grammars: [
    "YAML"
  ]

  ###
  Supported extensions
  ###
  extensions: [
    "yml",
    "yaml"
  ]

  defaultBeautifier: "align-yaml"

  options: {
    padding:
      type: 'integer'
      default: null
      minimum: 0
      description: "The amount of padding to add next to each line."
  }

}

module.exports = {

  name: "YAML"
  namespace: "yaml"
  fallback: []
  scope: ['source.yaml']

  ###
  Supported Grammars
  ###
  grammars: [
    "YAML"
  ]

  ###
  Supported extensions
  ###
  extensions: [
    "yml",
    "yaml"
  ]

  defaultBeautifier: "align-yaml"

  options: {
    padding:
      type: 'integer'
      default: 0
      minimum: 0
      description: "The amount of padding to add next to each line."
  }

}

Set default padding to 0, not null

Set default padding to 0, not null

This fixes https://github.com/Glavin001/atom-beautify/issues/1647 .


`import Ember from 'ember';`
`import Notify from 'ember-notify';`
`import CONSTANTS from '../utils/constants';`
`import SocketMixin from '../mixins/socket';`

IndexController = Ember.ArrayController.extend SocketMixin,

  needs: ['application']

  storeURL: null

  ratio: null

  model:( ->
    @store.all 'project'
  ).property()

  actions:

    submitURL: ->
      storeURL = @get "storeURL"
      if !CONSTANTS.STORE_URL_RE.test storeURL
        return Notify.error "Please enter a valid URL"
      data =
        storeURL: storeURL
      applicationAdapter = @store.adapterFor 'application'
      host = applicationAdapter.get 'host'
      namespace = applicationAdapter.get 'namespace'
      postUrl = [host, namespace, 'store_url'].join '/'
      that = @
      Ember.$.post postUrl, data
        .then ->
          that.set "storeURL", null
          Notify.success "Hang in there while we process your URL"
        .fail (xhr, message, status) ->
          Notify.error "A network error occured! Please try again later"

`export default IndexController;`

`import Ember from 'ember';`
`import Notify from 'ember-notify';`
`import CONSTANTS from '../utils/constants';`
`import SocketMixin from '../mixins/socket';`

IndexController = Ember.ArrayController.extend SocketMixin,

  needs: ['application']

  storeURL: null

  ratio: null

  model:( ->
    @store.all 'project'
  ).property()

  actions:

    submitURL: ->
      storeURL = @get "storeURL"
      if !CONSTANTS.STORE_URL_RE.test storeURL
        return Notify.error "Please enter a valid URL"
      data =
        storeURL: storeURL
      applicationAdapter = @store.adapterFor 'application'
      host = applicationAdapter.get 'host'
      namespace = applicationAdapter.get 'namespace'
      postUrl = [host, namespace, 'store_url'].join '/'
      that = @
      Ember.$.post postUrl, data
        .then ->
          that.set "storeURL", null
          Notify.success "Hang in there while we process your URL"
        .fail (xhr, message, status) ->
          if xhr.status is 401
            Notify.error xhr.responseJSON.message
          else
            Notify.error "A network error occured! Please try again later"

`export default IndexController;`

Fix scan permissions error message

Fix scan permissions error message


# General purpose utitily functions
# =================================

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

# Trim newlines from beginning and end of multi line string.
exports.trimNewLines = (str) ->
  str.replace(/^\n*/, '').replace(/\n*$/, '')

# Build an HTML file name, depending on the source path.
exports.makeDestination = makeDestination = (file, inPath) ->
  relPath = path.relative(
    path.resolve(inPath)
    path.resolve(file)
  )
  path.join(
    path.dirname(relPath)
    path.basename(relPath, path.extname(relPath)) + '.html'
  ).replace(/[\\/]/g, '-')

# Find first file matching `re` in `dir`.
exports.findFile = (dir, re) ->
  return null unless fs.statSync(dir).isDirectory()
  file = fs.readdirSync(dir).sort().filter((file) -> file.match re)?[0]
  return null unless file?
  path.join dir, file

# Make `link` objects for the menu.
exports.makeMenu = (files) ->
  menu = {}
  for file in files
    link =
      name: path.basename(file, path.extname file)
      href: 'html/' + makeDestination file
    parts = file.split('/').splice(1)
    key = if parts.length > 1 then parts[0] else './'
    if menu[key]?
      menu[key].push link
    else
      menu[key] = [ link ]
  menu

# General purpose utitily functions
# =================================

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

# Trim newlines from beginning and end of multi line string.
exports.trimNewLines = (str) ->
  str.replace(/^\n*/, '').replace(/\n*$/, '')

# Build an HTML file name, depending on the source path.
exports.makeDestination = makeDestination = (file, inPath) ->
  relPath = path.relative(
    path.resolve(inPath)
    path.resolve(file)
  )
  path.join(
    path.dirname(relPath)
    path.basename(relPath, path.extname(relPath)) + '.html'
  ).replace(/[\\/]/g, '-')

# Find first file matching `re` in `dir`.
exports.findFile = (dir, re) ->
  return null unless fs.statSync(dir).isDirectory()
  file = fs.readdirSync(dir).sort().filter((file) -> file.match re)?[0]
  return null unless file?
  path.join dir, file

# Make `link` objects for the menu.
exports.makeMenu = (files, inPath) ->
  menu = {}
  for file in files
    link =
      name: path.basename(file, path.extname file)
      href: 'html/' + makeDestination file, inPath
    parts = file.split('/').splice(1)
    key = if parts.length > 1 then parts[0] else './'
    if menu[key]?
      menu[key].push link
    else
      menu[key] = [ link ]
  menu

Fix path bug in menu generation

Fix path bug in menu generation


_ = require 'underscore-plus'
shell = require 'shell'
remote = require 'remote'
BrowserWindow = remote.require 'browser-window'

module.exports =
class WebWindow
  constructor: (url, options = {}, @openNewWindowExternally = true) ->
    _.defaults options,
      show: false
      width: 400
      height: 600
      skipTaskbar: true
      menuBarVisible: false

    @win = new BrowserWindow(options)
    @webContents = @win.webContents

    @handleEvents()
    @win.loadURL(url) # TODO: handle failed load

  handleEvents: ->
    @webContents.on 'did-finish-load', =>
      @win.show()

    if @openNewWindowExternally
      @webContents.on 'new-window', (e, url) =>
        e.preventDefault()
        @win.destroy()
        shell.openExternal(url)

_ = require 'underscore-plus'
shell = require 'shell'
remote = require 'remote'
BrowserWindow = remote.BrowserWindow

module.exports =
class WebWindow
  constructor: (url, options = {}, @openNewWindowExternally = true) ->
    _.defaults options,
      show: false
      width: 400
      height: 600
      skipTaskbar: true
      menuBarVisible: false

    @win = new BrowserWindow(options)
    @webContents = @win.webContents

    @handleEvents()
    @win.loadURL(url) # TODO: handle failed load

  handleEvents: ->
    @webContents.on 'did-finish-load', =>
      @win.show()

    if @openNewWindowExternally
      @webContents.on 'new-window', (e, url) =>
        e.preventDefault()
        @win.destroy()
        shell.openExternal(url)

Update deprecated call to remote

Update deprecated call to remote


gulp = require('gulp')
$ = require('gulp-load-plugins')
$ = $ 'rename':
  'gulp-task-listing': 'list'
path = require('path')
pkg = require('./package.json')

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

gulp.task 'list', $.list

gulp.task 'lint', ['lint-json', 'lint-coffee']

gulp.task 'lint-json', ->
  gulp.src('package.json')
    .pipe($.jsonlint())

gulp.task 'lint-coffee', ->
  gulp.src(['src/**/*.coffee', 'test/**/*.coffee'])
    .pipe($.coffeelint())
    .pipe($.coffeelint.reporter('coffeelint-stylish'))

gulp.task 'test', ['test-mocha']

gulp.task 'test-mocha', ->
  gulp.src(['test/**/*'])
    .pipe($.mocha())

gulp.task 'watch', ->
  $.watch ['src/**/*', 'test/**/*'], ->
    gulp.src(['test/**/*'])
      .pipe($.plumber(
        errorHandler: $.notify.onError("Error: <%= error.message %>")
      ))
      .pipe($.mocha(reporter: 'nyan'))

gulp = require('gulp')
$ = require('gulp-load-plugins')
$ = $ 'rename':
  'gulp-task-listing': 'list'
path = require('path')
pkg = require('./package.json')

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

gulp.task 'list', $.list

gulp.task 'lint', ['lint-json', 'lint-coffee']

gulp.task 'lint-json', ->
  gulp.src('package.json')
    .pipe($.jsonlint())

gulp.task 'lint-coffee', ->
  gulp.src(['src/**/*.coffee', 'test/**/*.coffee'])
    .pipe($.coffeelint())
    .pipe($.coffeelint.reporter('coffeelint-stylish'))

gulp.task 'test', ['test-mocha']

gulp.task 'test-mocha', ->
  gulp.src(['test/**/*.coffee'])
    .pipe($.mocha())

gulp.task 'watch', ->
  $.watch ['src/**/*', 'test/**/*'], ->
    gulp.src(['test/**/*'])
      .pipe($.plumber(
        errorHandler: $.notify.onError("Error: <%= error.message %>")
      ))
      .pipe($.mocha(reporter: 'nyan'))

Fix the wildcard in gulp test-mocha

Fix the wildcard in gulp test-mocha


'use strict'

DEFAULT       = require './Default'
CONST         = require './Constants'
existsDefault = require 'existential-default'

module.exports = (options = {}) ->
  acho = existsDefault(options, DEFAULT)
  acho.diff = [] if acho.diff
  acho[key] = value for key, value of acho

  acho.messages = do ->
    messages = {}
    for type of acho.types
      messages[type] = options.messages?[type] or []
      acho[type] = acho.generateTypeMessage type if type isnt 'line'
    messages

  acho.push = (type, messages...) ->
    message = @format messages
    @messages[type].push message
    this

  acho.add = (type, messages...) ->
    message = @format messages
    @[type] message
    @push type, message
    this

  acho

'use strict'

DEFAULT       = require './Default'
CONST         = require './Constants'
existsDefault = require 'existential-default'

Acho = (options = {}) ->
  return new Acho options unless this instanceof Acho

  acho = existsDefault(options, DEFAULT)
  acho.diff = [] if acho.diff
  acho[key] = value for key, value of acho

  acho.messages = do ->
    messages = {}
    for type of acho.types
      messages[type] = options.messages?[type] or []
      acho[type] = acho.generateTypeMessage type if type isnt 'line'
    messages

  acho.push = (type, messages...) ->
    message = @format messages
    @messages[type].push message
    this

  acho.add = (type, messages...) ->
    message = @format messages
    @[type] message
    @push type, message
    this

  acho

module.exports = Acho

Add instanceof for new keyword

Add instanceof for new keyword


module.exports =
  config:
    showIcons:
      type: 'boolean'
      default: true
    alwaysShowTabBar:
      type: 'boolean'
      default: true
      description: "Shows the Tab Bar when only 1 tab is open"
    tabScrolling:
      type: 'boolean'
      default: process.platform is 'linux'
    tabScrollingThreshold:
      type: 'integer'
      default: 120
    usePreviewTabs:
      type: 'boolean'
      default: true
      description: 'Tabs will only stay open if they are modified or double-clicked'

  activate: ->
    @tabBarViews = []

    TabBarView = require './tab-bar-view'
    _ = require 'underscore-plus'
    @paneSubscription = atom.workspace.observePanes (pane) =>
      tabBarView = new TabBarView(pane)

      paneElement = atom.views.getView(pane)
      paneElement.insertBefore(tabBarView.element, paneElement.firstChild)

      @tabBarViews.push(tabBarView)
      pane.onDidDestroy => _.remove(@tabBarViews, tabBarView)

  deactivate: ->
    @paneSubscription.dispose()
    tabBarView.remove() for tabBarView in @tabBarViews

module.exports =
  config:
    showIcons:
      type: 'boolean'
      default: true
    alwaysShowTabBar:
      type: 'boolean'
      default: true
      description: "Shows the Tab Bar when only 1 tab is open"
    tabScrolling:
      type: 'boolean'
      default: process.platform is 'linux'
    tabScrollingThreshold:
      type: 'integer'
      default: 120
    usePreviewTabs:
      type: 'boolean'
      default: false
      description: 'Tabs will only stay open if they are modified or double-clicked'

  activate: ->
    @tabBarViews = []

    TabBarView = require './tab-bar-view'
    _ = require 'underscore-plus'
    @paneSubscription = atom.workspace.observePanes (pane) =>
      tabBarView = new TabBarView(pane)

      paneElement = atom.views.getView(pane)
      paneElement.insertBefore(tabBarView.element, paneElement.firstChild)

      @tabBarViews.push(tabBarView)
      pane.onDidDestroy => _.remove(@tabBarViews, tabBarView)

  deactivate: ->
    @paneSubscription.dispose()
    tabBarView.remove() for tabBarView in @tabBarViews

Make preview tabs off by default for now


toggleTab = (targetClass) ->
    $footer = $('div.profile-footer')
    $nav = $footer.find('ul.nav')
    $nav.find('.active').removeClass 'active'
    $nav.find(".tab-#{targetClass}").addClass 'active'

toggleTabPanel = (targetClass) ->
    $('div.tab-pane.active').removeClass 'active'
    $("div.tab-pane.#{targetClass}").addClass 'active'

updateFormAction = (targetClass) ->
    $('#edit_user').attr 'action', "/users##{targetClass}"

updateLocationHash = (hash) ->
    window.location.hash = hash

$ ->
    targetClass = window.location.hash.substr(1)

    unless targetClass
        targetClass = 'account'

    toggleTab(targetClass)
    toggleTabPanel(targetClass)
    updateFormAction(targetClass)
    updateLocationHash(targetClass)

    $(".nav a").click (event) ->
        event.preventDefault()
        $this = $(event.currentTarget)
        $parent = $this.parent()
        targetClass = $this.data 'target-class'

        toggleTab(targetClass)
        toggleTabPanel(targetClass)
        updateFormAction(targetClass)
        updateLocationHash(targetClass)

initialize = () ->
    targetClass = window.location.hash.substr(1)

    unless targetClass
        targetClass = 'account'

    toggleTab(targetClass)
    toggleTabPanel(targetClass)
    updateFormAction(targetClass)
    updateLocationHash(targetClass)

    $(".nav a").click (event) ->
        event.preventDefault()
        $this = $(event.currentTarget)
        $parent = $this.parent()
        targetClass = $this.data 'target-class'

        toggleTab(targetClass)
        toggleTabPanel(targetClass)
        updateFormAction(targetClass)
        updateLocationHash(targetClass)

toggleTab = (targetClass) ->
    $footer = $('div.profile-footer')
    $nav = $footer.find('ul.nav')
    $nav.find('.active').removeClass 'active'
    $nav.find(".tab-#{targetClass}").addClass 'active'

toggleTabPanel = (targetClass) ->
    $('div.tab-pane.active').removeClass 'active'
    $("div.tab-pane.#{targetClass}").addClass 'active'

updateFormAction = (targetClass) ->
    $('#edit_user').attr 'action', "/users##{targetClass}"

updateLocationHash = (hash) ->
    window.location.hash = hash

$ ->
    $body = $('body')
    routes = ['registrations-edit', 'registrations-update']

    if _.some(routes, (route) -> $body.hasClass route)
        initialize()

Fix initialization of user profile logic


Species.EventLookup = Ember.Mixin.create
  selectedEvent: null
  selectedEventId: null

  initEventSelector: ->
    @set('selectedEvent', @get('controllers.events.content').findBy('id', @get('selectedEventId')))
    if @get('selectedEventType') == null && @get('selectedEvent')
      @set('selectedEventType', @get('controllers.events.eventTypes').findBy('id', @get('selectedEvent.type')))

  filteredEvents: ( ->
    if @get('selectedEventType')
      @get('controllers.events.content').filterBy('type', @get('selectedEventType.id'))
    else
      []
  ).property('selectedEventType.id')

  eventsDropdownVisible: ( ->
    @get('selectedEventType') != null
  ).property('selectedEventType.id')

  actions:
    handleEventTypeSelection: (eventType) ->
      @set('selectedEventType', eventType)
      if @get('selectedEventType.id') != @get('selectedEvent.type')
        @set('selectedEvent', null)
        @set('selectedEventId', null)
      if @get('selectedDocumentType.eventTypes') && @get('selectedDocumentType.eventTypes').indexOf(@get('selectedEvent.type')) < 0
        @set('selectedDocumentType', null)

    handleEventTypeDeselection: (eventType) ->
      @set('selectedEventType', null)
      @set('selectedEvent', null)
      @set('selectedEventId', null)

    handleEventSelection: (event) ->
      @set('selectedEvent', event)
      @set('selectedEventId', event.id)

    handleEventDeselection: (event) ->
      @set('selectedEvent', null)
      @set('selectedEventId', null)

Species.EventLookup = Ember.Mixin.create
  selectedEvent: null
  selectedEventId: null

  initEventSelector: ->
    @set('selectedEvent', @get('controllers.events.content').findBy('id', @get('selectedEventId')))
    if @get('selectedEventType') == null && @get('selectedEvent')
      @set('selectedEventType', @get('controllers.events.eventTypes').findBy('id', @get('selectedEvent.type')))

  filteredEvents: ( ->
    if @get('selectedEventType')
      @get('controllers.events.content').filterBy('type', @get('selectedEventType.id'))
    else
      []
  ).property('selectedEventType.id')

  eventsDropdownVisible: ( ->
    @get('selectedEventType')?
  ).property('selectedEventType.id')

  actions:
    handleEventTypeSelection: (eventType) ->
      @set('selectedEventType', eventType)
      if @get('selectedEventType.id') != @get('selectedEvent.type')
        @set('selectedEvent', null)
        @set('selectedEventId', null)
      if @get('selectedDocumentType.eventTypes') && @get('selectedDocumentType.eventTypes').indexOf(@get('selectedEvent.type')) < 0
        @set('selectedDocumentType', null)

    handleEventTypeDeselection: (eventType) ->
      @set('selectedEventType', null)
      @set('selectedEvent', null)
      @set('selectedEventId', null)

    handleEventSelection: (event) ->
      @set('selectedEvent', event)
      @set('selectedEventId', event.id)

    handleEventDeselection: (event) ->
      @set('selectedEvent', null)
      @set('selectedEventId', null)

Fix meeting dropdown appearing when no meeting type selected


App.Banners =

  update_banner: (selector, text) ->
    $(selector).html(text)

  update_style: (selector, style) ->
    $(selector).removeClass($(selector).attr("class"), true)
      .addClass(style, true)

  update_background_color: (selector, background_color) ->
    $(selector).css("background-color", background_color)

  update_font_color: (selector, font_color) ->
    $(selector).css("color", font_color)

  initialize: ->
    $("[data-js-banner-title]").on
      change: ->
        App.Banners.update_banner("#js-banner-title", $(this).val())

    $("[data-js-banner-description]").on
      change: ->
        App.Banners.update_banner("#js-banner-description", $(this).val())

    $("[name='banner[background_color]']").on
      change: ->
        App.Banners.update_background_color("#js-banner-background", $(this).val())

    $("[name='banner[font_color]']").on
      change: ->
        App.Banners.update_font_color("#js-banner-title", $(this).val())
        App.Banners.update_font_color("#js-banner-description", $(this).val())

App.Banners =

  update_banner: (selector, text) ->
    $(selector).html(text)

  update_background_color: (selector, background_color) ->
    $(selector).css("background-color", background_color)

  update_font_color: (selector, font_color) ->
    $(selector).css("color", font_color)

  initialize: ->
    $("[data-js-banner-title]").on
      change: ->
        App.Banners.update_banner("#js-banner-title", $(this).val())

    $("[data-js-banner-description]").on
      change: ->
        App.Banners.update_banner("#js-banner-description", $(this).val())

    $("[name='banner[background_color]']").on
      change: ->
        App.Banners.update_background_color("#js-banner-background", $(this).val())

    $("[name='banner[font_color]']").on
      change: ->
        App.Banners.update_font_color("#js-banner-title", $(this).val())
        App.Banners.update_font_color("#js-banner-description", $(this).val())

Remove unused JavaScript banners code

Remove unused JavaScript banners code

The banner `update_style` method isn't used since commit 82cb8d4c.