class window.Evidence extends Backbone.Model

class window.OpinionatersEvidence extends Evidence

  # TODO: eventually, fetching this model should populate
  #       the collection, not the other way around
  initialize: (attributes, options) ->
    @_fact_id = options.fact_id ? @collection.fact.id

    @on 'change:users', =>
      @opinionaters().reset @get('users')

  opinionaters: ->
    @_opinionaters ?= new InteractorsPage @get('users') ? [],
      fact_id: @_fact_id
      type: @get('type')
      perPage: 7

class window.Evidence extends Backbone.Model

class window.OpinionatersEvidence extends Evidence

  # TODO: eventually, fetching this model should populate
  #       the collection, not the other way around
  initialize: (attributes, options) ->
    @_fact_id = options.fact_id ? @collection.fact.id

    @on 'change', =>
      @updateOpinionators()

    @updateOpinionators()

  opinionaters: ->
    @_opinionaters ?= new InteractorsPage null,
      fact_id: @_fact_id
      type: @get('type')
      perPage: 7

  updateOpinionators: ->
    @opinionaters().reset @opinionaters().parse(@attributes)

Use 'parse' function to set necessary properties on the page


{argv} = require 'optimist'

koding = require './bongo'
koding.connect()

AuthWorker = require './authworker'

{authWorker} = require argv.c

authWorker = new AuthWorker koding, authWorker.authResourceName
authWorker.connect()

{argv} = require 'optimist'

koding = require './bongo'
koding.connect()

AuthWorker = require './authworker'

{authWorker,librato} = require argv.c

processMonitor = (require 'processes-monitor').start
  name : "Auth Worker #{process.pid}"
  stats_id: "worker.auth." + process.pid
  interval : 30000
  librato: librato

authWorker = new AuthWorker koding, authWorker.authResourceName
authWorker.connect()

Add auth stats to librato

Add auth stats to librato


class CommitsList
  @data =
    ref: null
    limit: 0
    offset: 0
  @disable = false

  @showProgress: ->
    $('.loading').show()

  @hideProgress: ->
    $('.loading').hide()

  @init: (ref, limit) ->
    $(".day-commits-table li.commit").live 'click', (event) ->
      if event.target.nodeName != "A"
        location.href = $(this).attr("url")
        e.stopPropagation()
        return false

    @data.ref = ref
    @data.limit = limit
    @data.offset = limit

    this.initLoadMore()
    this.showProgress()

  @getOld: ->
    this.showProgress()
    $.ajax
      type: "GET"
      url: location.href
      data: @data
      complete: this.hideProgress
      success: (data) ->
        CommitsList.append(data.count, data.html)
      dataType: "json"

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

  @initLoadMore: ->
    $(document).unbind('scroll')
    $(document).endlessScroll
      bottomPixels: 400
      fireDelay: 1000
      fireOnce: true
      ceaseFire: =>
        @disable
      callback: =>
        this.getOld()

this.CommitsList = CommitsList

class CommitsList
  @data =
    ref: null
    limit: 0
    offset: 0
  @disable = false

  @showProgress: ->
    $('.loading').show()

  @hideProgress: ->
    $('.loading').hide()

  @init: (ref, limit) ->
    $("body").on "click", ".day-commits-table li.commit", (event) ->
      if event.target.nodeName != "A"
        location.href = $(this).attr("url")
        e.stopPropagation()
        return false

    @data.ref = ref
    @data.limit = limit
    @data.offset = limit

    this.initLoadMore()
    this.showProgress()

  @getOld: ->
    this.showProgress()
    $.ajax
      type: "GET"
      url: location.href
      data: @data
      complete: this.hideProgress
      success: (data) ->
        CommitsList.append(data.count, data.html)
      dataType: "json"

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

  @initLoadMore: ->
    $(document).unbind('scroll')
    $(document).endlessScroll
      bottomPixels: 400
      fireDelay: 1000
      fireOnce: true
      ceaseFire: =>
        @disable
      callback: =>
        this.getOld()

this.CommitsList = CommitsList

Replace jquery deprecated .live with .on


app = require 'app'
fs = require 'fs'
path = require 'path'
protocol = require 'protocol'

# Handles requests with 'atom' protocol.
#
# It's created by {AtomApplication} upon instantiation and is used to create a
# custom resource loader for 'atom://' URLs.
#
# The following directories are searched in order:
#   * ~/.atom/assets
#   * ~/.atom/dev/packages (unless in safe mode)
#   * ~/.atom/packages
#   * RESOURCE_PATH/node_modules
#
module.exports =
class AtomProtocolHandler
  constructor: (@resourcePath, safeMode) ->
    @loadPaths = []
    @dotAtomDirectory = path.join(app.getHomeDir(), '.atom')

    unless safeMode
      @loadPaths.push(path.join(@dotAtomDirectory, 'dev', 'packages'))

    @loadPaths.push(path.join(@dotAtomDirectory, 'packages'))
    @loadPaths.push(path.join(@resourcePath, 'node_modules'))

    @registerAtomProtocol()

  # Creates the 'atom' custom protocol handler.
  registerAtomProtocol: ->
    protocol.registerProtocol 'atom', (request) =>
      relativePath = path.normalize(request.url.substr(7))

      if relativePath.indexOf('assets/') is 0
        assetsPath = path.join(@dotAtomDirectory, relativePath)
        filePath = assetsPath if fs.statSyncNoException(assetsPath).isFile?()

      unless filePath
        for loadPath in @loadPaths
          filePath = path.join(loadPath, relativePath)
          break if fs.statSyncNoException(filePath).isFile?()

      new protocol.RequestFileJob(filePath)

app = require 'app'
fs = require 'fs'
path = require 'path'
protocol = require 'protocol'

# Handles requests with 'atom' protocol.
#
# It's created by {AtomApplication} upon instantiation and is used to create a
# custom resource loader for 'atom://' URLs.
#
# The following directories are searched in order:
#   * ~/.atom/assets
#   * ~/.atom/dev/packages (unless in safe mode)
#   * ~/.atom/packages
#   * RESOURCE_PATH/node_modules
#
module.exports =
class AtomProtocolHandler
  constructor: (resourcePath, safeMode) ->
    @loadPaths = []
    @dotAtomDirectory = path.join(app.getHomeDir(), '.atom')

    unless safeMode
      @loadPaths.push(path.join(@dotAtomDirectory, 'dev', 'packages'))

    @loadPaths.push(path.join(@dotAtomDirectory, 'packages'))
    @loadPaths.push(path.join(resourcePath, 'node_modules'))

    @registerAtomProtocol()

  # Creates the 'atom' custom protocol handler.
  registerAtomProtocol: ->
    protocol.registerProtocol 'atom', (request) =>
      relativePath = path.normalize(request.url.substr(7))

      if relativePath.indexOf('assets/') is 0
        assetsPath = path.join(@dotAtomDirectory, relativePath)
        filePath = assetsPath if fs.statSyncNoException(assetsPath).isFile?()

      unless filePath
        for loadPath in @loadPaths
          filePath = path.join(loadPath, relativePath)
          break if fs.statSyncNoException(filePath).isFile?()

      new protocol.RequestFileJob(filePath)

Remove ivar only used in constructor

Remove ivar only used in constructor


define [
    'i18next',
    'app/views/base'
],
(
    {t: t},
    {SMItemView: SMItemView}
) ->
    ServiceMapDisclaimersView: class ServiceMapDisclaimersView extends SMItemView
        template: 'description-of-service'
        className: 'content modal-dialog about'
        serializeData: ->
            lang: p13n.getLanguage()
    ServiceMapDisclaimersOverlayView: class ServiceMapDisclaimersOverlayView extends SMItemView
        template: 'disclaimers-overlay'
        serializeData: ->
            layer = p13n.get('map_background_layer')
            if layer in ['servicemap', 'accessible_map']
                copyrightLink = "https://www.openstreetmap.org/copyright"
            copyright: t "disclaimer.copyright.#{layer}"
            copyrightLink: copyrightLink
        events:
            'click #about-the-service': 'onAboutClick'
        onAboutClick: (ev) ->
            app.commands.execute 'showServiceMapDescription'

define [
    'i18next',
    'app/views/base'
],
(
    {t: t},
    {SMItemView: SMItemView}
) ->
    ServiceMapDisclaimersView: class ServiceMapDisclaimersView extends SMItemView
        template: 'description-of-service'
        className: 'content modal-dialog about'
        events:
            'click .uservoice-link': 'openUserVoice'
        openUserVoice: (ev) ->
            console.trace()
            UserVoice = window.UserVoice || [];
            UserVoice.push ['show', mode: 'contact']
        serializeData: ->
            lang: p13n.getLanguage()
    ServiceMapDisclaimersOverlayView: class ServiceMapDisclaimersOverlayView extends SMItemView
        template: 'disclaimers-overlay'
        serializeData: ->
            layer = p13n.get('map_background_layer')
            if layer in ['servicemap', 'accessible_map']
                copyrightLink = "https://www.openstreetmap.org/copyright"
            copyright: t "disclaimer.copyright.#{layer}"
            copyrightLink: copyrightLink
        events:
            'click #about-the-service': 'onAboutClick'
        onAboutClick: (ev) ->
            app.commands.execute 'showServiceMapDescription'

Make feedback link work in service description.


ProjectMonitor.Views ||= {}

class ProjectMonitor.Views.HomeView extends Backbone.View
  tagName: "ol"
  className: "projects"
  template: JST["backbone/templates/home"]

  initialize: (options) ->
    @subviews = []
    @addTileView(tile) for tile in @collection.models

    @collection.on 'reset', =>
      for view in @subviews
        view.tearDown()
      @subviews.length = 0
      for model in @collection.models
        @addTileView(model)
      @render()

    @collection.on 'add', (model) =>
      unless model.id in (view.model.id for view in @subviews)
        @addTileView(model)
        @render()

    @collection.on 'remove', (model) =>
      viewsToDelete = (view for view in @subviews when view.model.id == model.id)
      for view in viewsToDelete
        view.tearDown()
        @subviews = (v for v in @subviews when v isnt view)

  addTileView: (model) ->
    if model.get("aggregate")
      view = new ProjectMonitor.Views.AggregateProjectView(model: model)
    else
      view = new ProjectMonitor.Views.ProjectView(model: model)
    @subviews.push(view)
    @registerSubView(view)

  render: ->
    @$el.empty()
    @$el.append(subview.render().$el) for subview in @subviews
    @

ProjectMonitor.Views ||= {}

class ProjectMonitor.Views.HomeView extends Backbone.View
  tagName: "ol"
  className: "projects"
  template: JST["backbone/templates/home"]

  initialize: (options) ->
    @_addTileView(tile) for tile in @collection.models

    @collection.on 'reset', =>
      for cid,view of @subViews
        view.tearDown()
      for model in @collection.models
        @_addTileView(model)
      @render()

    @collection.on 'add', (model) =>
      unless model.id in (view.model.id for cid,view of @subViews)
        @_addTileView(model)
        @render()

    @collection.on 'remove', (model) =>
      viewsToDelete = (view for cid,view of @subViews when view.model.id == model.id)
      for view in viewsToDelete
        view.tearDown()

  _addTileView: (model) ->
    if model.get("aggregate")
      view = new ProjectMonitor.Views.AggregateProjectView(model: model)
    else
      view = new ProjectMonitor.Views.ProjectView(model: model)
    @registerSubView(view)

  render: ->
    @$el.empty()
    @$el.append(subview.render().$el) for cid,subview of @subViews
    @

Remove subviews array from home view

Remove subviews array from home view


# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/

$("document").ready ->
  $(".point1").popover
    placement: "right"
    content: "We have the technical experise to make your ideas become an amazing reality."

  $(".point2").popover
      placement: "right"
      content: "Our experience with Agile development techniques means you'll spend more time loving your product and less time in meetings."

  $(".point3").popover
      placement: "right"
      content: "We have a combined total of over 100,000 man-hours of development experience. Regardless of what you need, we can help."

  $(".point4").popover
      placement: "right"
      content: "With team members that are experienced at working at every type of business - from spry startups to large enterprises - we are able to adapt to any situation quickly."

  $('#footer-nav').localScroll()

  $('ul.portfolio-thumbs li').hover(
    -> $(".overlay", this).stop().animate({top:'0px'},{queue:false,duration:300})
    -> $(".overlay", this).stop().animate({top:'-183px'},{queue:false,duration:300})
  )

# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/

$("document").ready ->
  $(".point1").popover
    placement: "right"
    content: "We have the technical expertise to make your ideas become an amazing reality."

  $(".point2").popover
      placement: "right"
      content: "Our experience with Agile development techniques means you'll spend more time loving your product and less time in meetings."

  $(".point3").popover
      placement: "right"
      content: "We have a combined total of over 100,000 man-hours of development experience. Regardless of what you need, we can help."

  $(".point4").popover
      placement: "right"
      content: "With team members that are experienced at working at every type of business - from spry startups to large enterprises - we are able to adapt to any situation quickly."

  $('#footer-nav').localScroll()

  $('ul.portfolio-thumbs li').hover(
    -> $(".overlay", this).stop().animate({top:'0px'},{queue:false,duration:300})
    -> $(".overlay", this).stop().animate({top:'-183px'},{queue:false,duration:300})
  )

Fix embarassing typo in one of the popovers on the homepage


Backbone = require 'backbone'
mediator = require '../../../lib/mediator.coffee'
template = -> require('../templates/partials/_channel_groups.jade') arguments...
{ QUERY } = require("sharify").data

class ProfileView extends Backbone.View
  loading: false
  disabled: false
  threshold: -500
  page: 2

  initialize: ->
    @timer = setInterval @maybeLoad, 150

  maybeLoad: =>
    return false if @loading or
      @disabled or
      mediator.shared.state.get 'lightbox'

    total = document.body.scrollHeight
    scrollPos = (document.documentElement.scrollTop || document.body.scrollTop)
    progress = scrollPos + window.innerHeight * 4

    if (total - progress < @threshold)
      @loadNextPage()

  loadNextPage: ->
    @loading = true
    $.ajax
      data:
        page: @page
        q: sd.QUERY
      url: "/api/#{sd.USER.slug}/profile"
      success: (response) =>
        @page++
        @loading = false

        if response.channels.length
          $('.profile').append template
            channels: response.channels
        else
          @disabled = true

module.exports.init = ->
  new ProfileView
    el: $('.profile')

Backbone = require 'backbone'
{ QUERY, PROFILE_CHANNELS, SORT } = require("sharify").data
mediator = require '../../../lib/mediator.coffee'
ChannelGroupView = require '../../../components/channel_block_group/view.coffee'

template = -> require('../templates/partials/_channel_groups.jade') arguments...

class ProfileView extends Backbone.View
  loading: false
  disabled: false
  threshold: -500
  page: 2

  initialize: ->
    @timer = setInterval @maybeLoad, 150

  maybeLoad: =>
    return false if @loading or
      @disabled or
      mediator.shared.state.get 'lightbox'

    total = document.body.scrollHeight
    scrollPos = (document.documentElement.scrollTop || document.body.scrollTop)
    progress = scrollPos + window.innerHeight * 4

    if (total - progress < @threshold)
      @loadNextPage()

  loadNextPage: ->
    @loading = true
    $.ajax
      data:
        page: @page
        q: QUERY
        sort: SORT
      url: "/api/#{sd.USER.slug}/profile"
      success: (response) =>
        @page++
        @loading = false

        if response.channels.length
          $('.profile').append template
            channels: response.channels

          @setUpChannelGroupViews(response.channels)
        else
          @disabled = true

  setUpChannelGroupViews: (channels) ->
    for channel in channels
      view = new ChannelGroupView
        channel: channel
        el: @$(".ChannelBlockGroup[data-id=#{channel.id}]")

      view.initBlockViews()

module.exports.init = ->
  view = new ProfileView
    el: $('.profile')

  view.setUpChannelGroupViews(PROFILE_CHANNELS)

Handle random sort on the client

Handle random sort on the client


class Lanes.Components.Input extends Lanes.React.Component

    mixins: [
        Lanes.Components.Form.FieldMixin
    ]
    formGroupClass: 'input'

    getDefaultProps: ->
        type: 'text'

    propTypes:
        unlabled:  React.PropTypes.bool

    getValue: ->
        @refs.input.getValue()

    handleKeyDown: (ev) ->
        @props.onEnter() if ev.key is 'Enter'

    renderEdit: (label) ->
        value = @_getValue() or ''
        label ||= @props.label or _.field2title(@props.name)
        props = _.extend({
            ref:       'input'
            className: _.classnames('value', changeset: @state.changeset)
            label:     if @props.unlabeled then false else label
            value:     value
            onKeyDown: @handleKeyDown if @props.onEnter
            onChange:  @handleChange
        }, @props)
        if @props.inputOnly then @renderPlain(props) else @renderStyled(props, label)

    renderPlain: (props) ->
        <input {...props}/>

    renderStyled: (props, label) ->
        colProps = _.omit(props, 'name')
        <BS.Col {...colProps} >
            <BS.Input {...props} />
        </BS.Col>

class Lanes.Components.Input extends Lanes.React.Component

    NUMBER_TEST: /^-?\d+(\.\d+)?$/

    mixins: [
        Lanes.Components.Form.FieldMixin
    ]

    propTypes:
        unlabled:  React.PropTypes.bool
        onlyNumeric: React.PropTypes.bool

    getDefaultProps: ->
        type: 'text'

    getValue: ->
        @refs.input.getValue()

    handleKeyDown: (ev) ->
        @props.onEnter() if ev.key is 'Enter'

    valueInFlux: (value) ->
        type = @props.model.attributeType(@props.name)
        (@props.onlyNumeric or type is "bigdec" ) and not @NUMBER_TEST.test(value)

    validatedChangeHandler: (ev) ->
        value = ev.target.value
        if @valueInFlux(value)
            @setState(pendingValue: value)
        else
            @setState(pendingValue: false)
            @handleChange(ev)

    renderEdit: (label) ->
        value = @state.pendingValue or @props.value or @_getValue()
        label ||= @props.label or _.field2title(@props.name)
        props = _.extend({
            ref:       'input'
            className: _.classnames('value', changeset: @state.changeset)
            label:     if @props.unlabeled then false else label
            value:     value
            onKeyDown: @handleKeyDown if @props.onEnter
            onChange:  @validatedChangeHandler
        }, @props, {value: value})
        if @props.inputOnly then @renderPlain(props) else @renderStyled(props, label)

    renderPlain: (props) ->
        <input {...props}/>

    renderStyled: (props, label) ->
        colProps = _.omit(props, 'name')
        <BS.Col {...colProps} >
            <BS.Input {...props} />
        </BS.Col>

Store invalid decimal values in state, apply later

This is needed so you can type '0.' before finishing with 0.2 for instance


'use strict'

angular.module("ngFillHeight.directives")
  .directive 'ngFillHeight', ($parse) ->

    ngFillHeightLink = (scope, element, attrs) ->
      ngFillHeightOption = ($parse attrs.ngFillHeight)(scope)

      if typeof ngFillHeightOption isnt 'object'
        console.error 'The value of ngFillHeight has to be an Object'
        return

      parentObject = angular.element(ngFillHeightOption.parentSelector)
      currObject =  angular.element(element)

      ngFillHeightOption.api = {
        recalcHeight : () ->

          recurrFunc = (increment) ->
            currObject.height(currObject.height() + increment)

          recurrFunc(10) while parentObject.height() >= parentObject.prop('scrollHeight')

          recurrFunc(-1) while parentObject.height() < parentObject.prop('scrollHeight')

          return
      }

    return {
      restrict: 'A'
      link : ngFillHeightLink
    }

'use strict'

angular.module("ngFillHeight.directives")
  .directive 'ngFillHeight', ($parse) ->

    ngFillHeightLink = (scope, element, attrs) ->
      ngFillHeightOption = ($parse attrs.ngFillHeight)(scope)

      if typeof ngFillHeightOption isnt 'object'
        console.error 'The value of ngFillHeight has to be an Object'
        return

      parentObject = angular.element(ngFillHeightOption.parentSelector)
      currObject =  angular.element(element)

      ngFillHeightOption.api = {
        recalcHeight : () ->

          recurrFunc = (increment) ->
            currObject.height(currObject.height() + increment)

          recurrFunc(10) while currObject.height() < ngFillHeightOption.minHeight or parentObject.height() >= parentObject.prop('scrollHeight')

          recurrFunc(-1) while currObject.height() > ngFillHeightOption.minHeight and parentObject.height() < parentObject.prop('scrollHeight')

          return
      }

    return {
      restrict: 'A'
      link : ngFillHeightLink
    }

Add capacity to inform a minHeight property


# This function handles copying address fields from existing addresses.
copyAddress = ->
  selected_address_id = +$(this)[0].value
  match = null
  address_fields = $(this).closest('div.address-fields')
  addresses = address_fields.find('div.applicant-addresses').data('applicant-addresses')
  for address in addresses
    if +address.id == +selected_address_id
      match = address
      break
  unless match
    return
  address_fields.find('input[id$=state]').val(match.state)
  address_fields.find('input[id$=street]').val(match.street)
  address_fields.find('input[id$=city]').val(match.city)
  address_fields.find('input[id$=apt]').val(match.apt)
  address_fields.find('input[id$=zip]').val(match.zip)

$ ->
  dropdown = $('div.address-fields div.address-selector select')
  dropdown.change(copyAddress)

# This function handles copying address fields from existing addresses.
copyAddress = ->
  selected_address_id = +$(this)[0].value
  match = null
  address_fields = $(this).closest('div.address-fields')
  addresses = address_fields.find('div.applicant-addresses').data('applicant-addresses')
  for address in addresses
    if +address.id == +selected_address_id
      match = address
      break
  unless match
    return
  address_fields.find('select[id$=state]').val(match.state)
  address_fields.find('input[id$=street]').val(match.street)
  address_fields.find('input[id$=city]').val(match.city)
  address_fields.find('input[id$=apt]').val(match.apt)
  address_fields.find('input[id$=zip]').val(match.zip)

$ ->
  dropdown = $('div.address-fields div.address-selector select')
  dropdown.change(copyAddress)

Fix copying state from addresses

Fix copying state from addresses


#
# Logout helpers.
#

request = require 'superagent'
opts = require '../options'
{ parse } = require 'url'
redirectBack = require './redirectback'

@denyBadLogoutLinks = (req, res, next) ->
  if parse(req.get 'Referrer').hostname.match 'artsy.net'
    next()
  else
    next new Error "Malicious logout link."

@logout = (req, res, next) ->
  accessToken = req.user?.get('accessToken')
  req.logout()
  request
    .del("#{opts.ARTSY_URL}/api/v1/access_token")
    .set('X-Access-Token': accessToken)
    .end (error, response) ->
      if req.xhr
        res.status(200).send msg: 'success'
      else
        redirectBack req, res

#
# Logout helpers.
#

request = require 'superagent'
opts = require '../options'
{ parse } = require 'url'
redirectBack = require './redirectback'

@denyBadLogoutLinks = (req, res, next) ->
  if parse(req.get 'Referrer').hostname.match 'artsy.net'
    next()
  else
    next new Error "Malicious logout link."

@logout = (req, res, next) ->
  accessToken = req.user?.get('accessToken')
  req.logout()
  req.session = null
  request
    .del("#{opts.ARTSY_URL}/api/v1/access_token")
    .set('X-Access-Token': accessToken)
    .end (error, response) ->
      if req.xhr
        res.status(200).send msg: 'success'
      else
        redirectBack req, res

[Logout] Make sure to clear session explicitly when logging out


linterPath = atom.packages.getLoadedPackage("linter").path
Linter = require "#{linterPath}/lib/linter"

class LinterPhp extends Linter
  # The syntax that the linter handles. May be a string or
  # list/tuple of strings. Names should be all lowercase.
  @syntax: ['text.html.php', 'source.php']

  # A string, list, tuple or callable that returns a string, list or tuple,
  # containing the command line (with arguments) used to lint.
  cmd: 'php -l -n -d display_errors=On -d log_errors=Off'

  executablePath: null

  linterName: 'php'

  options: ['phpExecutablePath']

  # A regex pattern used to extract information from the executable's output.
  regex: '(Parse|Fatal) (?<error>error):(\\s*(?<type>parse|syntax) error,?)?\\s*' +
         '(?<message>(unexpected \'(?<near>[^\']+)\')?.*) ' +
         'in .*? on line (?<line>\\d+)'

  createMessage: (match) ->
    # message might be empty, we have to supply a value
    if match and match.type == 'parse' and not match.message
      message = 'parse error'
    super(match)

module.exports = LinterPhp

linterPath = atom.packages.getLoadedPackage("linter").path
Linter = require "#{linterPath}/lib/linter"
{CompositeDisposable} = require 'atom'

class LinterPhp extends Linter
  # The syntax that the linter handles. May be a string or
  # list/tuple of strings. Names should be all lowercase.
  @syntax: ['text.html.php', 'source.php']

  # A string, list, tuple or callable that returns a string, list or tuple,
  # containing the command line (with arguments) used to lint.
  cmd: 'php -l -n -d display_errors=On -d log_errors=Off'

  executablePath: null

  linterName: 'php'

  # A regex pattern used to extract information from the executable's output.
  regex: '(Parse|Fatal) (?<error>error):(\\s*(?<type>parse|syntax) error,?)?\\s*' +
         '(?<message>(unexpected \'(?<near>[^\']+)\')?.*) ' +
         'in .*? on line (?<line>\\d+)'

  constructor: (editor) ->
    super(editor)
    @disposables = new CompositeDisposable

    @disposables.add atom.config.observe 'linter-php.phpExecutablePath', =>
      @executablePath = atom.config.get 'linter-php.phpExecutablePath'

  destroy: ->
    # atom.config.unobserve 'linter-php.phpExecutablePath'
    @disposables.dispose();

  createMessage: (match) ->
    # message might be empty, we have to supply a value
    if match and match.type == 'parse' and not match.message
      message = 'parse error'
    super(match)

module.exports = LinterPhp

Revert "Use options to observe phpExecutablePath config"

This reverts commit 4f52e2ca452c18dedc15d01ffb9acdeed28d8964.


{$, $$, ScrollView} = require 'atom'
Grim = require 'grim'

module.exports =
class DeprecationCopView extends ScrollView
  @content: ->
    @div class: 'deprecation-cop pane-item', tabindex: -1, =>
      @div class: 'panel', =>
        @div class: 'panel-heading', "Deprecated calls"
        @ul outlet: 'list', class: 'list-tree has-collapsable-children'

  initialize: ({@uri}) ->
    @update()
    super()
    @on 'click', '.list-nested-item', -> $(this).toggleClass('collapsed')

  destroy: ->
    @detach()

  getUri: ->
    @uri

  getTitle: ->
    'Deprecation Cop'

  update: ->
    for method, {count, message, stackTraces} of Grim.getLog()
      @list.append $$ ->
        @li class: 'list-nested-item collapsed', =>
          @div class: 'list-item', =>
            @span class: 'text-highlight', method
            @span " (called #{count} times)"

          @ul class: 'list', =>
            for stackTrace in stackTraces
              @li class: 'list-item stack-trace padded', =>
                @span class: 'icon icon-alert'
                @span stackTrace.split("\n")[3].replace(/^\s*at\s*/, '')
                @pre stackTrace

{$, $$, ScrollView} = require 'atom'
Grim = require 'grim'

module.exports =
class DeprecationCopView extends ScrollView
  @content: ->
    @div class: 'deprecation-cop pane-item', tabindex: -1, =>
      @div class: 'panel', =>
        @div class: 'panel-heading', "Deprecated calls"
        @ul outlet: 'list', class: 'list-tree has-collapsable-children'

  initialize: ({@uri}) ->
    @update()
    super()
    @on 'click', '.list-nested-item', -> $(this).toggleClass('collapsed')

  destroy: ->
    @detach()

  getUri: ->
    @uri

  getTitle: ->
    'Deprecation Cop'

  update: ->
    methodList = []
    methodList.push [method, metadata] for method, metadata of Grim.getLog()
    methodList.sort (a, b) -> b[1].count - a[1].count

    for [method, {count, message, stackTraces}] in methodList
      @list.append $$ ->
        @li class: 'list-nested-item collapsed', =>
          @div class: 'list-item', =>
            @span class: 'text-highlight', method
            @span " (called #{count} times)"

          @ul class: 'list', =>
            for stackTrace in stackTraces
              @li class: 'list-item stack-trace padded', =>
                @span class: 'icon icon-alert'
                @span stackTrace.split("\n")[3].replace(/^\s*at\s*/, '')
                @pre stackTrace

Sort by deprecated method calls

Sort by deprecated method calls


namespace 'SensuDashboard.Views.Events', (exports) ->

  class exports.List extends SensuDashboard.Views.List

    name: 'events/list'

    initialize: ->
      @autocomplete_view = @options.autocomplete_view
      @autocomplete_view.delegate = this
      super

    itemClass: ->
      exports.ListItem

    resolvedCollection: ->
      resolved = @collection.chain()
      for token in @autocomplete_view.tokens
        model = token.object
        resolved = if model instanceof SensuDashboard.Models.Check
          resolved.filter (record) ->
            record.get('check') == model.get('name')
        else if model instanceof SensuDashboard.Models.Client
          resolved.filter (record) ->
            record.get('client') == model.get('name')
        else if _.isString(model)
          resolved.filter (record) ->
            liquidMetal.score(record.get('output'), model) > 0.7

      resolved

    renderCollection: ->
      @resolvedCollection().each (event) =>
        @renderItem(event)

    #
    # Autocomplete delegate
    #

    filtersUpdated: ->
      @render()

namespace 'SensuDashboard.Views.Events', (exports) ->

  class exports.List extends SensuDashboard.Views.List

    name: 'events/list'

    initialize: ->
      @autocomplete_view = @options.autocomplete_view
      @autocomplete_view.delegate = this
      super

    itemClass: ->
      exports.ListItem

    resolvedCollection: ->
      resolved = @collection.chain()
      for token in @autocomplete_view.tokens
        model = token.object
        resolved = if model instanceof SensuDashboard.Models.Check
          resolved.filter (record) ->
            record.get('check') == model.get('name')
        else if model instanceof SensuDashboard.Models.Client
          resolved.filter (record) ->
            record.get('client') == model.get('name')
        else if _.isString(model)
          resolved.filter (record) ->
            output = record.get('output').toLowerCase()
            output.indexOf(model.toLowerCase()) != -1

      resolved

    renderCollection: ->
      @resolvedCollection().each (event) =>
        @renderItem(event)

    #
    # Autocomplete delegate
    #

    filtersUpdated: ->
      @render()

Use strpos for faster event output filtering


angular.module('loomioApp').controller 'ProposalPieChartController', ($scope) ->

  $scope.pieChartData = [
    { value : 0, color : "#90D490" },
    { value : 0, color : "#F0BB67" },
    { value : 0, color : "#D49090" },
    { value : 0, color : "#dd0000"}
  ]

  $scope.pieChartOptions =
    animation: false
    segmentShowStroke: true
    segmentStrokeColor: "#fff"
    responsive: false

  refreshPieChartData = ->
    return unless $scope.proposal
    counts = $scope.proposal.voteCounts
    # yeah - this is done to preseve the view binding on the pieChartData
    $scope.pieChartData[0].value = counts.yes
    $scope.pieChartData[1].value = counts.abstain
    $scope.pieChartData[2].value = counts.no
    $scope.pieChartData[3].value = counts.block

  $scope.$watch 'proposal.voteCounts', ->
    refreshPieChartData()

angular.module('loomioApp').controller 'ProposalPieChartController', ($scope) ->

  $scope.pieChartData = [
    { value : 0, color : "#90D490" },
    { value : 0, color : "#F0BB67" },
    { value : 0, color : "#D49090" },
    { value : 0, color : "#dd0000" },
    { value : 0, color : "#cccccc" }
  ]

  $scope.pieChartOptions =
    animation: false
    segmentShowStroke: true
    segmentStrokeColor: "#fff"
    responsive: false

  refreshPieChartData = ->
    return unless $scope.proposal
    counts = $scope.proposal.voteCounts
    hasAnyVotes = counts.yes + counts.abstain + counts.no + counts.block > 0
    $scope.pieChartData[0].value = counts.yes
    $scope.pieChartData[1].value = counts.abstain
    $scope.pieChartData[2].value = counts.no
    $scope.pieChartData[3].value = counts.block
    $scope.pieChartData[4].value = if hasAnyVotes then 0 else 1

  $scope.$watch 'proposal.voteCounts', ->
    refreshPieChartData()

Make empty pie chart appear on proposal page


path = require 'path'
async = require 'async'
Command = require './command'
config = require './config'
fs = require './fs'

module.exports =
class RebuildModuleCache extends Command
  @commandNames: ['rebuild-module-cache']

  constructor: ->
    @atomPackagesDirectory = path.join(config.getAtomDirectory(), 'packages')

  getResourcePath: (callback) ->
    if @resourcePath
      process.nextTick => callback(@resourcePath)
    else
      config.getResourcePath (@resourcePath) => callback(@resourcePath)

  rebuild: (packageDirectory, callback) ->
    @getResourcePath (resourcePath) =>
      try
        @moduleCache ?= require(path.join(resourcePath, 'src', 'module-cache'))
        @moduleCache.create(packageDirectory)
        callback()
      catch error
        callback(error)

  run: (options) ->
    {callback} = options

    commands = []
    for packageName in fs.list(@atomPackagesDirectory)
      packageDirectory = path.join(@atomPackagesDirectory, packageName)
      continue if fs.isSymbolicLinkSync(packageDirectory)
      continue unless fs.isDirectorySync(packageDirectory)

      commands.push (callback) =>
        process.stdout.write "Rebuilding #{packageName} module cache "
        @rebuild packageDirectory, (error) =>
          if error?
            @logFailure()
          else
            @logSuccess()
          callback(error)

    async.waterfall(commands, callback)

path = require 'path'
async = require 'async'
Command = require './command'
config = require './config'
fs = require './fs'

module.exports =
class RebuildModuleCache extends Command
  @commandNames: ['rebuild-module-cache']

  constructor: ->
    @atomPackagesDirectory = path.join(config.getAtomDirectory(), 'packages')

  getResourcePath: (callback) ->
    if @resourcePath
      process.nextTick => callback(@resourcePath)
    else
      config.getResourcePath (@resourcePath) => callback(@resourcePath)

  rebuild: (packageDirectory, callback) ->
    @getResourcePath (resourcePath) =>
      try
        @moduleCache ?= require(path.join(resourcePath, 'src', 'module-cache'))
        @moduleCache.create(packageDirectory)
        callback()
      catch error
        callback(error)

  run: (options) ->
    {callback} = options

    commands = []
    fs.list(@atomPackagesDirectory).forEach (packageName) =>
      packageDirectory = path.join(@atomPackagesDirectory, packageName)
      return if fs.isSymbolicLinkSync(packageDirectory)
      return unless fs.isDirectorySync(packageDirectory)

      commands.push (callback) =>
        process.stdout.write "Rebuilding #{packageName} module cache "
        @rebuild packageDirectory, (error) =>
          if error?
            @logFailure()
          else
            @logSuccess()
          callback(error)

    async.waterfall(commands, callback)

Use forEach to iterate over package folders


class Dashing.Lecturelist extends Dashing.Widget

  ready: ->
    @currentIndex = 0
    @items = $(@node).find('li')
    @items.hide()
    @nextItem()
    @startCarousel()

  onData: (data) ->
    @currentIndex = 0
    i = 0
    if data.items.length == 0
      @set 'empty', true

  startCarousel: ->
    setInterval(@nextItem, 10000)

  nextItem: =>
    items = @get('items')
    if items
      $(items[@currentIndex]).fadeOut =>
        @currentIndex = (@currentIndex + 1) % items.length
        $(items[@currentIndex]).fadeIn()

class Dashing.Lecturelist extends Dashing.Widget

  ready: ->
    @currentIndex = 0
    @items = $(@node).find('li')
    @items.hide()
    @nextItem()
    @startCarousel()

  onData: (data) ->
    @currentIndex = 0
    i = 0
    if data.items.length == 0
      @set 'empty', true

  startCarousel: ->
    setInterval(@nextItem, 10000)

  nextItem: =>
    items = @get('items')
    if items
      $(items[@currentIndex]).fadeOut =>
        $(@node).find(".progress span:last-child").removeClass('active')
        $(@node).find(".progress span:nth-child(#{@currentIndex})").removeClass('active')
        @currentIndex = (@currentIndex + 1) % items.length
        if @currentIndex == 0
          $(@node).find(".progress span:last-child").addClass('active')
        else
          $(@node).find(".progress span:nth-child(#{@currentIndex})").addClass('active')
        $(items[@currentIndex]).fadeIn()

Change opacity of dots as lectures fade out and in


Neighborly.InfiniteScroll =
  setupScroll: ->
    _.bindAll(this, 'onScroll', 'onSuccess')
    this.$window().scroll(this.onScroll)

  fetchPage: ->
    # the isLoaderDivVisible check if the div is already in the view pane to load more content
    # the $loader.is(:visible) is here to avoid trigerring two concurrent fetchPage calls
    if this.isLoaderDivVisible() and not this.$loader.is(":visible") and not this.EOF
      this.$loader.show()
      $.get(this.path, this.filter).success this.onSuccess
      this.filter.page += 1

  onSuccess: (data) ->
    this.EOF = true  if $.trim(data) is ""
    this.$results.append data
    this.$loader.hide()
    this.$el.trigger "scroll:success", data

  $window: ->
    $(window)

  isLoaderDivVisible: ->
    this.$loaderDiv.is(":visible") and this.$window().scrollTop() + this.$window().height() > this.$loaderDiv.offset().top

  onScroll: (event) ->
    this.fetchPage()

Neighborly.InfiniteScroll =
  setupScroll: ->
    _.bindAll(this, 'onScroll', 'onSuccess')
    this.$window().scroll(this.onScroll)

  fetchPage: ->
    # the isLoaderDivVisible check if the div is already in the view pane to load more content
    # the $loader.is(:visible) is here to avoid trigerring two concurrent fetchPage calls
    if this.isLoaderDivVisible() and not this.$loader.is(":visible") and not this.EOF
      this.$loader.show()
      $.get(this.path, this.filter).success this.onSuccess
      this.filter.page += 1

  onSuccess: (data) ->
    this.EOF = true  if $.trim(data) is ""
    this.$results.append data unless this.use_custom_append? && this.use_custom_append == true
    this.$loader.hide()
    this.$el.trigger "scroll:success", data

  $window: ->
    $(window)

  isLoaderDivVisible: ->
    this.$loaderDiv.is(":visible") and this.$window().scrollTop() + this.$window().height() > this.$loaderDiv.offset().top

  onScroll: (event) ->
    this.fetchPage()

Add option to not append the data on infinite scroll module


App.Polls =
  generateToken: ->
    rand = Math.random().toString(36).substr(2) # remove `0.`
    token = rand + rand # to make it longer
    return token

  replaceToken: ->
    for link in $('.js-question-answer')
      token_param = link.search.slice(-6)
      if token_param == "token="
        link.href = link.href + @token

  initialize: ->
    @token = App.Polls.generateToken()
    App.Polls.replaceToken()

    $(".js-question-answer").on
      click: =>
        token_message = $(".js-token-message")
        if !token_message.is(':visible')
          token_message.html(token_message.html() + "
" + @token + "");
          token_message.show()
    false

App.Polls =
  generateToken: ->
    token = ''
    for n in [0..5]
      rand = Math.random().toString(36).substr(2) # remove `0.`
      token = token + rand;

    token = token.substring(0, 64)
    return token

  replaceToken: ->
    for link in $('.js-question-answer')
      token_param = link.search.slice(-6)
      if token_param == "token="
        link.href = link.href + @token

  initialize: ->
    @token = App.Polls.generateToken()
    App.Polls.replaceToken()

    $(".js-question-answer").on
      click: =>
        token_message = $(".js-token-message")
        if !token_message.is(':visible')
          token_message.html(token_message.html() + "
" + @token + "");
          token_message.show()
    false

Set token to 64 characters

Set token to 64 characters


'body':
  'meta-\\': 'tree-view:toggle'
  'meta-|': 'tree-view:reveal-active-file'

'.tree-view':
  'right': 'tree-view:expand-directory'
  'ctrl-]': 'tree-view:expand-directory'
  'left': 'tree-view:collapse-directory'
  'ctrl-[': 'tree-view:collapse-directory'
  'enter': 'tree-view:open-selected-entry'
  'm': 'tree-view:move'
  'a': 'tree-view:add'
  'delete': 'tree-view:remove'
  'backspace': 'tree-view:remove'

'.tree-view-dialog .mini.editor':
  'enter': 'core:confirm'
  'escape': 'core:cancel'

'body':
  'meta-\\': 'tree-view:toggle'
  'meta-|': 'tree-view:reveal-active-file'

'.tree-view':
  'right': 'tree-view:expand-directory'
  'ctrl-]': 'tree-view:expand-directory'
  'left': 'tree-view:collapse-directory'
  'ctrl-[': 'tree-view:collapse-directory'
  'enter': 'tree-view:open-selected-entry'
  'm': 'tree-view:move'
  'a': 'tree-view:add'
  'delete': 'tree-view:remove'
  'backspace': 'tree-view:remove'
  'k': 'core:move-up'
  'j': 'core:move-down'

'.tree-view-dialog .mini.editor':
  'enter': 'core:confirm'
  'escape': 'core:cancel'

Support moving up/down in tree view with k/j keys


# 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