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

module.exports = (robot, scripts) ->
    scriptsPath = path.resolve(__dirname, 'src')
    fs.exists scriptsPath, (exists) ->
        if exists
            for script in fs.readdirSync(scriptsPath)
                if scripts? and '*' not in scripts
                    robot.loadFile(scriptsPath, script) if script in scripts
                else
                    robot.loadFile(scriptsPath, script)

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

module.exports = (robot, scripts) ->
    scriptsPath = path.resolve(__dirname, 'src')
    if fs.existsSync scriptsPath
        for script in fs.readdirSync(scriptsPath).sort()
            if scripts? and '*' not in scripts
                robot.loadFile(scriptsPath, script) if script in scripts
            else
                robot.loadFile(scriptsPath, script)

Load / run scripts synchronously

Load / run scripts synchronously


# RESTful data adapter for hooking angular JS into the backend API.
# Checkout "js-data-angular" docs for more info.
data = (DS) ->
  deviceMethods = {}
  DS.defineResource
    name: "device"
    endpoint: 'devices',
    basePath: '/api',
    idAttribute: "_id"
    methods: deviceMethods

  DS.defineResource
    name: "step"
    endpoint: 'steps',
    basePath: '/api',
    idAttribute: "_id"
    relations:
      belongsTo:
        sequence:
          localKey: 'sequence_id'
          localField: 'sequence'
          parent: true

  DS.defineResource
    name: "sequence"
    endpoint: 'sequences',
    basePath: '/api',
    idAttribute: "_id"
    relations:
      hasMany:
        step:
          localField: "steps"
          foreignKey: "sequence_id"

  DS.defineResource
    name: "schedule"
    endpoint: 'schedules',
    basePath: '/api',
    idAttribute: "_id"
    # relations:
    #   hasOne:
    #     sequence:
    #       localField: "sequence"
    #       foreignKey: "sequence_id"

  return DS

angular.module("FarmBot").service 'Data', [
  'DS',
  data
]

# RESTful data adapter for hooking angular JS into the backend API.
# Checkout "js-data-angular" docs for more info.
data = (DS) ->
  DS.defineResource
    name: "device"
    endpoint: 'devices',
    basePath: '/api',
    idAttribute: "_id"

  DS.defineResource
    name: "step"
    endpoint: 'steps',
    basePath: '/api',
    idAttribute: "_id"
    relations:
      belongsTo:
        sequence:
          localKey: 'sequence_id'
          localField: 'sequence'
          parent: true

  DS.defineResource
    name: "sequence"
    endpoint: 'sequences',
    basePath: '/api',
    idAttribute: "_id"
    relations:
      hasMany:
        step:
          localField: "steps"
          foreignKey: "sequence_id"

  DS.defineResource
    name: "schedule"
    endpoint: 'schedules',
    basePath: '/api',
    idAttribute: "_id"

  return DS

angular.module("FarmBot").service 'Data', [
  'DS'
  data
]

Remove boilerplate code from JS-Data code


Darkswarm.factory 'Variants', ->
  new class Variants
    variants: {}

    clear: ->
      @variants = {}

    register: (variant)->
      @variants[variant.id] ||= @extend variant

    extend: (variant)->
      variant.extended_name = @extendedVariantName(variant)
      variant.base_price_percentage = Math.round(variant.price / variant.price_with_fees * 100)
      variant.line_item ||= @lineItemFor(variant) # line_item may have been initialised in Cart#constructor
      variant.line_item.total_price = variant.price_with_fees * variant.line_item.quantity
      variant

    extendedVariantName: (variant) =>
      if variant.product_name == variant.name_to_display
        name = variant.product_name
      else
        name =  "#{variant.product_name} - #{variant.name_to_display}"
      name += " (#{variant.options_text})" if variant.options_text
      name

    lineItemFor: (variant) ->
      variant: variant
      quantity: null
      max_quantity: null

Darkswarm.factory 'Variants', ->
  new class Variants
    variants: {}

    clear: ->
      @variants = {}

    register: (variant)->
      @variants[variant.id] ||= @extend variant

    extend: (variant)->
      variant.extended_name = @extendedVariantName(variant)
      variant.base_price_percentage = Math.round(variant.price / variant.price_with_fees * 100)
      variant.line_item ||= @lineItemFor(variant) # line_item may have been initialised in Cart#constructor
      variant.line_item.total_price = variant.price_with_fees * variant.line_item.quantity
      variant

    extendedVariantName: (variant) =>
      if variant.product_name == variant.name_to_display
        name = variant.product_name
      else
        name =  "#{variant.product_name} - #{variant.name_to_display}"
      name

    lineItemFor: (variant) ->
      variant: variant
      quantity: null
      max_quantity: null

Simplify product naming in cart

Simplify product naming in cart


# Description:
#   Get most popular story from MEN
#   See also https://github.com/github/hubot-scripts/blob/master/src/scripts/chartbeat.coffee
#
# Dependencies:
#   None
#
# Configuration:
# 	HUBOT_CHARTBEAT_API_KEY
#
# Command: [not included in help script]
#   election - find election candidates
#
# Author:
#   robertocarroll

getChart = (msg, apiKey) ->
	msg.robot.http("http://api.chartbeat.com/live/toppages/v3/?apikey=#{apiKey}&host=manchestereveningnews.co.uk&section=greater-manchester-news&sort_by=social&types=1")
		.get() (err, res, body) ->
			unless res.statusCode is 200
				msg.send "Sorry there was a problem with Chartbeat. I couldn't get a story right now."
				return

			response = JSON.parse(body)
			popular = response.pages || []
			popular_article = popular[0].title.substring(0,48)+"[...]"
			popular_article_link = popular[0].path
			msg.send "The hottest story right now is: #{popular_article}. http://#{popular_article_link}"

module.exports = (robot) ->
	robot.hear /popular/i, (msg) ->
		apiKey = process.env.HUBOT_CHARTBEAT_API_KEY
		getChart(msg, apiKey)

# Description:
#   Get most popular story from MEN
#   See also https://github.com/github/hubot-scripts/blob/master/src/scripts/chartbeat.coffee
#
# Dependencies:
#   None
#
# Configuration:
# 	HUBOT_CHARTBEAT_API_KEY
#
# Command: [not included in help script]
#   election - find election candidates
#
# Author:
#   robertocarroll

getChart = (msg, apiKey) ->
	msg.robot.http("http://api.chartbeat.com/live/toppages/v3/?apikey=#{apiKey}&host=manchestereveningnews.co.uk&section=greater-manchester-news&sort_by=social&types=1")
		.get() (err, res, body) ->
			unless res.statusCode is 200
				msg.send "Sorry there was a problem with Chartbeat. I couldn't get a story right now."
				return

			response = JSON.parse(body)
			popular = response.pages || []
			popular_article = popular[0].title.substring(0,48)+"[...]"
			popular_article_link = popular[0].path
			msg.send "I bring you the hottest story right now: #{popular_article}. http://#{popular_article_link}"

module.exports = (robot) ->
	robot.hear /popular/i, (msg) ->
		apiKey = process.env.HUBOT_CHARTBEAT_API_KEY
		getChart(msg, apiKey)

Tweak to popular news phrase.

Tweak to popular news phrase.


net = require 'net'
httpProxy = require 'http-proxy'

module.exports = class LocalhostProxy
  constructor: (port) ->
    @port = port
    @remoteHost = 'http://ile.learn.co'
    @desiredPorts = ['3000', '4000', '8000', '9393']

  start: ->
    @withAvailablePort((ports) =>
      server = httpProxy.createProxyServer({target: @remoteHost + ':' + @port})

      for port in @desiredPorts
        console.log 'Port: ' + port + ' Available: ' + !!ports[port]
        if !!ports[port]
          server.listen(parseInt(port), 'localhost')
          console.log 'Listening on port: ' + port
        else
          console.log 'NOT listening on port: ' + port
    )

  withAvailablePort: (callback) ->
    ports = {}

    @desiredPorts.forEach (port) ->
      try
        server = net.createServer()
        server.listen parseInt(port), 'localhost'

        server.on 'error', (e) ->
          ports[port] = false
        server.on 'listening', (e) ->
          server.close()
          ports[port] = true
      catch error
        ports[port] = false

    allChecked = =>
      @desiredPorts.every((port) ->
        ports[port] != null
      )

    checkInterval = setInterval(=>
      console.log 'Finished checking available ports: ' + allChecked(ports)
      if allChecked()
        clearInterval(checkInterval)
        callback(ports)
    , 100)

net = require 'net'
httpProxy = require 'http-proxy'

module.exports = class LocalhostProxy
  constructor: (port) ->
    @port = port
    @remoteHost = 'http://ile.learn.co'
    @desiredPorts = ['3000', '4000', '4567', '8000', '9292', '9393']

  start: ->
    @withAvailablePort((ports) =>
      server = httpProxy.createProxyServer({target: @remoteHost + ':' + @port})

      for port in @desiredPorts
        console.log 'Port: ' + port + ' Available: ' + !!ports[port]
        if !!ports[port]
          server.listen(parseInt(port), 'localhost')
          console.log 'Listening on port: ' + port
        else
          console.log 'NOT listening on port: ' + port
    )

  withAvailablePort: (callback) ->
    ports = {}

    @desiredPorts.forEach (port) ->
      try
        server = net.createServer()
        server.listen parseInt(port), 'localhost'

        server.on 'error', (e) ->
          ports[port] = false
        server.on 'listening', (e) ->
          server.close()
          ports[port] = true
      catch error
        ports[port] = false

    allChecked = =>
      @desiredPorts.every((port) ->
        ports[port] != null
      )

    checkInterval = setInterval(=>
      console.log 'Finished checking available ports: ' + allChecked(ports)
      if allChecked()
        clearInterval(checkInterval)
        callback(ports)
    , 100)

Add a few more standard ports

Add a few more standard ports


module.exports = (grunt) ->

  grunt.initConfig
    pkg: grunt.file.readJSON("package.json")
    coffee:
      compile:
        files:
          'chrome-app/lib/pomodoro.js': ['src/*.coffee']

  grunt.loadNpmTasks "grunt-contrib-concat"
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-coffee');

  grunt.registerTask "default", ["coffee"]

module.exports = (grunt) ->

  grunt.initConfig
    pkg: grunt.file.readJSON("package.json")
    coffee:
      compile:
        files:
          'chrome-app/lib/pomodoro.js': ['src/*.coffee']
    uglify:
      build:
        src: 'chrome-app/lib/pomodoro.js',
        dest: 'chrome-app/lib/pomodoro.min.js'

  grunt.loadNpmTasks "grunt-contrib-concat"
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-coffee');

  grunt.registerTask "default", ["coffee","uglify"]

Add uglify task to grunt

Add uglify task to grunt


module.exports =
class BroadcastTarget
  editor: null
  listener: null
  isMarkdownPreview: false

  constructor: ->
    @editor = atom.workspace.activePaneItem

    @isMarkdownPreview = @editor[0]?

    @editor.on 'markdown-preview:markdown-changed', =>
      @listener?()

  setListener: (listener) ->
    @listener = listener

  getContent: ->
    return if @isMarkdownPreview then @getMarkdownPreviewContent() else @getOtherContent()

  getMarkdownPreviewContent: ->
    content = @editor[0].outerHTML
    if atom.config.get 'broadcast.getEmojisFromCheatSheetSite'
      content = content.replace /[\w-\.\/]+pngs/g, @urlToCheatSheetSite
    else
      content = content.replace /[\w-\.\/]+node_modules\/roaster\/node_modules/g, ''

  getOtherContent: ->
    content = @editor.getText?()
    .replace /</g, '<'
    .replace />/g, '>'

    return '<pre>' + content + '</pre>'

module.exports =
class BroadcastTarget
  editor: null
  listener: null
  isMarkdownPreview: false

  constructor: ->
    @editor = atom.workspace.activePaneItem

    @isMarkdownPreview = @editor[0]?

    if @isMarkdownPreview
      @editor.on 'markdown-preview:markdown-changed', =>
        @listener?()
    else
      @editor.getBuffer().on 'contents-modified', =>
        @listener?()

  setListener: (listener) ->
    @listener = listener

  getContent: ->
    return if @isMarkdownPreview then @getMarkdownPreviewContent() else @getOtherContent()

  getMarkdownPreviewContent: ->
    content = @editor[0].outerHTML
    if atom.config.get 'broadcast.getEmojisFromCheatSheetSite'
      content = content.replace /[\w-\.\/]+pngs/g, @urlToCheatSheetSite
    else
      content = content.replace /[\w-\.\/]+node_modules\/roaster\/node_modules/g, ''

  getOtherContent: ->
    content = @editor.getText?()
    .replace /</g, '<'
    .replace />/g, '>'

    return '<pre>' + content + '</pre>'

Fix an issue that non-markdown preview is not updated


RESTstop.configure()

RESTstop.add 'trips/:tiploc?', ->
  tiploc = @params.tiploc
  unless tiploc?
    return [403,
      success: false
      message:'You need to provide a tiploc as a parameter'
    ]
  schedules = getTodaysScheduleForTiploc(tiploc)
  result = getArrivalAndDepartureTimes(tiploc, schedules)
  results: result

RESTstop.add 'citysdk', ->
  node = @request.body
  tiploc = node.tiploc
  unless tiploc?
    return [403,
      message: 'You need to provide a tiploc'
    ]
  schedules = getTodaysScheduleForTiploc(tiploc)
  result = getArrivalAndDepartureTimes(tiploc, schedules)
  node.data = result
  return node

RESTstop.configure()

RESTstop.add 'trips/:tiploc?', ->
  tiploc = @params.tiploc
  unless tiploc?
    return [403,
      success: false
      message:'You need to provide a tiploc as a parameter'
    ]
  schedules = getTodaysScheduleForTiploc(tiploc)
  result = getArrivalAndDepartureTimes(tiploc, schedules)
  results: result

RESTstop.add 'citysdk', ->
  node = @request.body
  # XXX: the JSON data comes in as the only key for an empty string. So we need
  # to extract it and parse it as JSON.
  node = JSON.parse(_.keys(node)[0])
  tiploc = node.tiploc_code
  unless tiploc?
    return [403,
      message: 'You need to provide a tiploc'
    ]
  schedules = getTodaysScheduleForTiploc(tiploc)
  result = getArrivalAndDepartureTimes(tiploc, schedules)
  node.data =
    times: result
  return node

Make API work with citysdk data

Make API work with citysdk data


LineFocusStatsView = require './line-focus-stats-view'

module.exports = LineFocusStats =
  lineFocusStatsViews: null

  activate: (state) ->
    console.log 'activate LineFocusStats'
    @lineFocusStatsViews = []

    atom.commands.add 'atom-workspace',
      'line-focus-stats:toggle': => @toggle()

    # @enable()

  deactivate: ->
    @disable()

  enable: ->
    @enabled = true

    atom.workspace.observeTextEditors (editor) =>
      @lineFocusStatsViews.push(new LineFocusStatsView(editor))

  disable: ->
    while lineFocusStatsView = @lineFocusStatsViews.shift()
      lineFocusStatsView.destroy()

    @enabled = false

  toggle: ->
    console.log 'LineFocusStats was toggled!'
    if @enabled
      @disable()
    else
      @enable()

LineFocusStatsView = require './line-focus-stats-view'

module.exports = LineFocusStats =
  lineFocusStatsViews: null

  activate: (state) ->
    console.log 'activate LineFocusStats'
    @lineFocusStatsViews = []

    atom.commands.add 'atom-workspace',
      'line-focus-stats:toggle': => @toggle()

    enabled = false

  deactivate: ->
    @disable()

  enable: ->
    @enabled = true

    atom.workspace.observeTextEditors (editor) =>
      @lineFocusStatsViews.push(new LineFocusStatsView(editor))

  disable: ->
    while lineFocusStatsView = @lineFocusStatsViews.shift()
      lineFocusStatsView.destroy()

    @enabled = false

  toggle: ->
    console.log 'LineFocusStats was toggled!'
    if @enabled
      @disable()
    else
      @enable()

Modify enabled state when activated.

Modify enabled state when activated.


$ ->
  $('a[href*=#]:not([href=#])').click ->
    if location.pathname.replace(/^\//, '') == @pathname.replace(/^\//, '') and location.hostname == @hostname
      target = $(@hash)
      target = if target.length then target else $('[name=' + @hash.slice(1) + ']')
      if target.length
        $('html,body').animate { scrollTop: target.offset().top }, 1000
        return false

$ ->
  $('a[href*=#]:not([href=#])').click ->
    if location.pathname.replace(/^\//, '') == @pathname.replace(/^\//, '') and location.hostname == @hostname
      target = $(@hash)
      target = if target.length then target else $('[name=' + @hash.slice(1) + ']')
      if target.length
        $('html,body').animate { scrollTop: target.offset().top }, 500
        return false

Reduce achor navigation animation time

Reduce achor navigation animation time


_ = require "lodash"
d3Selection = require "d3-selection"
DonutChart = require "britecharts/dist/umd/donut.min"

module.exports =
  template: '

'

  props:
    initDataset:
      type: String
      required: true

  data: ->
    dataset: _.sortBy JSON.parse(@initDataset), (data) -> data.name_key

  mounted: ->
    container = d3Selection.select(".c-episode-rating-state-chart")
    containerWidth = if container.node()
      container.node().getBoundingClientRect().width
    else
      false

    donutChart = new DonutChart()

    if containerWidth
      colors = ["#FFAB40", "#bdbdbd", "#69F0AE", "#40C4FF"]

      # Remove colors which corresponded to status if its quantity is zero.
      _.forEach @dataset, (data, i) ->
        colors.splice(i, 1) if data.quantity == 0

      donutChart
        .width(containerWidth)
        .height(containerWidth - 35)
        .externalRadius(containerWidth / 2.5)
        .internalRadius(containerWidth / 5)
        .colorSchema(colors)

      container.datum(@dataset).call(donutChart)

_ = require "lodash"
d3Selection = require "d3-selection"
DonutChart = require "britecharts/dist/umd/donut.min"

module.exports =
  template: '

'

  props:
    initDataset:
      type: String
      required: true

  data: ->
    dataset: _.sortBy JSON.parse(@initDataset), (data) -> data.name_key

  mounted: ->
    container = d3Selection.select(".c-episode-rating-state-chart")
    containerWidth = if container.node()
      container.node().getBoundingClientRect().width
    else
      false

    donutChart = new DonutChart()

    if containerWidth
      colors = ["#FFAB40", "#bdbdbd", "#69F0AE", "#40C4FF"]
      removedColors = []

      # Remove colors which corresponded to status if its quantity is zero.
      _.forEach @dataset, (data, i) ->
        removedColors.push(colors[i]) if data.quantity == 0

      donutChart
        .width(containerWidth)
        .height(containerWidth - 35)
        .externalRadius(containerWidth / 2.5)
        .internalRadius(containerWidth / 5)
        .colorSchema(_.difference(colors, removedColors))

      container.datum(@dataset).call(donutChart)

Remove colors which corresponded to status if its quantity is zero (Take.3)


{
  jQuery: $
} = uploadcare

uploadcare.namespace 'utils', (ns) ->

  trackLoading = (image, src) ->
    def = $.Deferred()

    if src
      image.src = src

    if image.complete
      def.resolve(image)
    else
      $(image).one 'load', =>
        def.resolve(image)
      $(image).one 'error', =>
        def.reject(image)

    def.promise()

  ns.imageLoader = (image) ->
    # if argument is an array, treat as
    # load(['1.jpg', '2.jpg'])
    if $.isArray(image)
      return $.when.apply(null, $.map(image, ns.imageLoader))

    if image.src
      return trackLoading(image)
    else
      return trackLoading(new Image(), image)

  ns.videoLoader = (src) ->
    def = $.Deferred()
    $('<video/>')
      .on('loadeddata', def.resolve)
      .on('error', def.reject)
      .attr('src', src)
      .load()
    def.promise()

{
  jQuery: $
} = uploadcare

uploadcare.namespace 'utils', (ns) ->

  trackLoading = (image, src) ->
    def = $.Deferred()

    if src
      image.src = src

    if image.complete
      def.resolve(image)
    else
      $(image).one 'load', =>
        def.resolve(image)
      $(image).one 'error', =>
        def.reject(image)

    def.promise()

  ns.imageLoader = (image) ->
    # if argument is an array, treat as
    # load(['1.jpg', '2.jpg'])
    if $.isArray(image)
      return $.when.apply(null, $.map(image, ns.imageLoader))

    if image.src
      return trackLoading(image)
    else
      return trackLoading(new Image(), image)

  ns.videoLoader = (src) ->
    def = $.Deferred()
    $('<video/>')
      .on('loadeddata', def.resolve)
      .on('error', def.reject)
      .attr('src', src)
      .get(0)
      .load()
    def.promise()

Fix videoLoader. Run `load` for HTMLElement instead jQuery el.


'menu': [
  'label': 'Packages'
  'submenu': [
    'label': 'Markdown'
    'submenu': [
      'label': 'Toggle Preview'
      'command': 'markdown-preview:toggle'
    ]
  ]
]

'context-menu':
  '.markdown-preview':
    'Copy': 'core:copy'
    'Save As HTML\u2026': 'core:save-as'

'menu': [
  'label': 'Packages'
  'submenu': [
    'label': 'Markdown Preview'
    'submenu': [
      'label': 'Toggle Preview'
      'command': 'markdown-preview:toggle'
    ]
  ]
]

'context-menu':
  '.markdown-preview':
    'Copy': 'core:copy'
    'Save As HTML\u2026': 'core:save-as'

Use package name in menu

Use package name in menu


module.exports =
class AbstractProvider
  regex: ''
  selector: '.source.php'

  inclusionPriority: 1
  excludeLowerPriority: true

  # Build the snippet from the suggestion
  getFunctionSnippet: (word, elements) ->
    body = word + "("
    lastIndex = 0

    # Non optional elements
    for arg, index in elements.parameters
      body += ", " if index != 0
      body += "${" + (index+1) + ":" + arg + "}"
      lastIndex = index+1

    # Optional elements. One big same snippet
    if elements.optionals.length > 0
      body += "${" + (lastIndex + 1) + ":["
      body += "," if lastIndex != 0

      lastIndex += 1

      for arg, index in elements.optionals
        body += ", " if index != 0
        body += arg
      body += "]}"

    body += ")$0"

    return body

  # Return the prefix to delete if press enter
  getPrefix: (editor, bufferPosition) ->
    # Get the text for the line up to the triggered buffer position
    line = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition])

    # Match the regex to the line, and return the match
    matches = line.match(@regex)

    # Looking for the correct match
    if matches?
      for match in matches
        start = bufferPosition.column - match.length

        if start >= 0
          word = editor.getTextInBufferRange([[bufferPosition.row, bufferPosition.column - match.length], bufferPosition])
          if word == match
            return match

    return ''

module.exports =
class AbstractProvider
  regex: ''
  selector: '.source.php'

  inclusionPriority: 1
  excludeLowerPriority: true

  disableForSelector: '.source.php .comment, .source.php .string'

  # Build the snippet from the suggestion
  getFunctionSnippet: (word, elements) ->
    body = word + "("
    lastIndex = 0

    # Non optional elements
    for arg, index in elements.parameters
      body += ", " if index != 0
      body += "${" + (index+1) + ":" + arg + "}"
      lastIndex = index+1

    # Optional elements. One big same snippet
    if elements.optionals.length > 0
      body += "${" + (lastIndex + 1) + ":["
      body += "," if lastIndex != 0

      lastIndex += 1

      for arg, index in elements.optionals
        body += ", " if index != 0
        body += arg
      body += "]}"

    body += ")$0"

    return body

  # Return the prefix to delete if press enter
  getPrefix: (editor, bufferPosition) ->
    # Get the text for the line up to the triggered buffer position
    line = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition])

    # Match the regex to the line, and return the match
    matches = line.match(@regex)

    # Looking for the correct match
    if matches?
      for match in matches
        start = bufferPosition.column - match.length

        if start >= 0
          word = editor.getTextInBufferRange([[bufferPosition.row, bufferPosition.column - match.length], bufferPosition])
          if word == match
            return match

    return ''

Disable autocomplete in comment and string


# Your init script

fs = require 'fs'
path = require 'path'
DEFAULT_WORKON_HOME = path.join(process.env.HOME, '.virtualenvs')

# Get path to directory containing all Python virtualenvs
getVirtualenvHome = ->

  if process.env.WORKON_HOME
    return process.env.WORKON_HOME
  else
    return DEFAULT_WORKON_HOME

# Activate Python virtualenv for this project directory if it exists
activateVirtualenv = ->

  project = atom.project.getPaths()[0]
  if project
    virtualenv = path.join(getVirtualenvHome(), path.basename(project))
    # Do not activate virtualenv if project is itself a virtualenv
    if virtualenv isnt project
      fs.lstat(virtualenv, (err, stats) ->
        if not err and stats.isDirectory()
          process.env.PATH = [virtualenv, process.env.PATH].join ':'
          process.env.VIRTUAL_ENV = virtualenv
          console.log "Activated virtualenv at #{virtualenv}"
      )

# Explicitly define PATH and detect project virtualenvs
process.env.PATH = '/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin'
activateVirtualenv()
# Ensure that Python correctly outputs Unicode characters
process.env.PYTHONIOENCODING = 'utf_8'
# Prevent Python from generating bytecode (.pyc, .pyo) files
process.env.PYTHONDONTWRITEBYTECODE = '1'

# Your init script

fs = require 'fs'
path = require 'path'
DEFAULT_WORKON_HOME = path.join(process.env.HOME, '.virtualenvs')

# Get path to directory containing all Python virtualenvs
getVirtualenvHome = ->

  if process.env.WORKON_HOME
    return process.env.WORKON_HOME
  else
    return DEFAULT_WORKON_HOME

# Activate Python virtualenv for this project directory if it exists
activateVirtualenv = ->

  project = atom.project.getPaths()[0]
  if project
    virtualenv = path.join(getVirtualenvHome(), path.basename(project))
    # Do not activate virtualenv if project is itself a virtualenv
    if virtualenv isnt project
      fs.lstat(virtualenv, (err, stats) ->
        if not err and stats.isDirectory()
          virtualenv_bin = path.join(virtualenv, 'bin')
          process.env.PATH = [virtualenv_bin, process.env.PATH].join ':'
          process.env.VIRTUAL_ENV = virtualenv
          console.log "Activated virtualenv at #{virtualenv}"
      )

# Explicitly define PATH and detect project virtualenvs
process.env.PATH = '/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin'
activateVirtualenv()
# Ensure that Python correctly outputs Unicode characters
process.env.PYTHONIOENCODING = 'utf_8'
# Prevent Python from generating bytecode (.pyc, .pyo) files
process.env.PYTHONDONTWRITEBYTECODE = '1'

Add correct virtualenv dir to PATH

Add correct virtualenv dir to PATH


define [
	"base"
], (App) ->
	App.directive "reviewPanelSorted", ($timeout) ->
		return  {
			link: (scope, element, attrs) ->
				layout = () ->
					entries = []
					for el in element.find(".rp-entry")
						entries.push {
							el: el
							scope: angular.element(el).scope()
						}
					entries.sort (a,b) -> a.scope.entry.offset - b.scope.entry.offset

					previousBottom = 28 # This should start at the height of the toolbar
					for entry in entries
						height = $(entry.el).height()
						top = entry.scope.entry.screenPos.y
						top = Math.max(top, previousBottom + 12)
						previousBottom = top + height
						entry.scope.top = top

				scope.$watch "reviewPanel.entries", (value) ->
					return if !value?
					layout()

				scope.$on "review-panel:layout", () ->
					$timeout () ->
						layout()
		}

define [
	"base"
], (App) ->
	App.directive "reviewPanelSorted", ($timeout) ->
		return  {
			link: (scope, element, attrs) ->
				layout = () ->
					entries = []
					for el in element.find(".rp-entry")
						entries.push {
							el: el
							scope: angular.element(el).scope()
						}
					entries.sort (a,b) -> a.scope.entry.offset - b.scope.entry.offset

					previousBottom = 28 # This should start at the height of the toolbar
					for entry in entries
						height = $(entry.el).height()
						top = entry.scope.entry.screenPos.y
						top = Math.max(top, previousBottom + 12)
						previousBottom = top + height
						entry.scope.top = top

				scope.$watch "reviewPanel.entries", (value) ->
					return if !value?
					$timeout () ->
						layout()

				scope.$on "review-panel:layout", () ->
					$timeout () ->
						layout()
		}

Stop deletes overlapping comments (but leads to occasional flickering)


Neighborly.Projects.Contributions = {} if Neighborly.Projects.Contributions is undefined

Neighborly.Projects.Contributions.New =
  modules: -> []

  init: ->(
    initialize: ->
      $('.maturity-option .header').click this.boxClicked
      $('.order-size-input').on 'keyup', this.orderSizeChanged

    boxClicked: (event) ->
      $('.maturity-option').removeClass('selected')
      $box = $(event.currentTarget).parents('.maturity-option:first')
      $box.addClass('selected')

    orderSizeChanged: (event) ->
      $input = $(event.currentTarget)
      if $input.val() != ''
        $box = $(event.currentTarget).parents('.maturity-option:first')
        minimum_investment = $input.data('minimum-investment')
        amount = parseInt($input.val()) * parseFloat($input.data('minimum-investment'))
        $box.find('.value-input').val(amount)
        $box.find('.purchase-amount .amount').html(numeral(amount).format('$0,0'))
  ).initialize()

Neighborly.Projects.Contributions = {} if Neighborly.Projects.Contributions is undefined

Neighborly.Projects.Contributions.New =
  modules: -> []

  init: ->(
    initialize: ->
      $('.maturity-option .header').click this.boxClicked
      $('.order-size-input').on 'keyup input', this.orderSizeChanged

    boxClicked: (event) ->
      $('.maturity-option').removeClass('selected')
      $box = $(event.currentTarget).parents('.maturity-option:first')
      $box.addClass('selected')

    orderSizeChanged: (event) ->
      $input = $(event.currentTarget)
      if $input.val() != ''
        $box = $(event.currentTarget).parents('.maturity-option:first')
        minimum_investment = $input.data('minimum-investment')
        amount = parseInt($input.val()) * parseFloat($input.data('minimum-investment'))
        $box.find('.value-input').val(amount)
        $box.find('.purchase-amount .amount').html(numeral(amount).format('$0,0'))
  ).initialize()

Add oninput to contributions new

Add oninput to contributions new


{ flatten } = require('sdk/util/array');

class Model
  constructor : (@sources, @filters) ->
    @feeds = flatten(source.getFeeds() for source in @sources)
    feed.on("updated", @onFeedUpdated) for feed in @feeds
    @requestUpdate()

  onFeedUpdated : (feed) =>
    for item in feed.items
      console.log(item.link)
    # TODO

  requestUpdate : =>
    feed.requestUpdate() for feed in @feeds

exports.Model = Model

{ flatten } = require('sdk/util/array');
Promise = require("sdk/core/promise")

class Model
  constructor : (@sources, @filters) ->
    @feeds = flatten(source.getFeeds() for source in @sources)
    feed.on("updated", @onFeedUpdated) for feed in @feeds
    @requestUpdate()

  onFeedUpdated : (feed) =>
    @filter(item) for item in feed.items

  requestUpdate : =>
    feed.requestUpdate() for feed in @feeds

  filter : (item) =>
    # Ask every filter to check the item.
    # Pack it into a promise and let filters
    # resolve the promise upon acceptance,
    # or reject it.
    promises = for filter in @filters
      { promise, resolve, reject } = Promise.defer()
      filter.filter(item, resolve, reject)
      promise
    # Collect all promises and pack them.
    # Once all filters are done, display the item or not.
    Promise.all(promises).then (=> @display(item)), (=> @remove(item))

  display : (item) =>
    console.log("display:", item.link)

  remove : (item) =>
    console.log("remove:", item.link)

exports.Model = Model

Implement item filtering in Model class

Implement item filtering in Model class


class FocusAction
  constructor: ->
  isComplete: -> true
  isRecordable: -> false

  focusCursor: ->
    editor = atom.workspaceView.getActivePaneItem()
    editorView = atom.workspaceView.getActiveView()
    if editor? and editorView?
      cursorPosition = editor.getCursorBufferPosition()
      editorView.scrollToBufferPosition(cursorPosition)

class FocusPaneViewOnLeft extends FocusAction
  execute: ->
    atom.workspaceView.focusPaneViewOnLeft()
    @focusCursor()

class FocusPaneViewOnRight extends FocusAction
  execute: ->
    atom.workspaceView.focusPaneViewOnRight()
    @focusCursor()

class FocusPaneViewAbove extends FocusAction
  execute: ->
    atom.workspaceView.focusPaneViewAbove()
    @focusCursor()

class FocusPaneViewBelow extends FocusAction
  execute: ->
    atom.workspaceView.focusPaneViewBelow()
    @focusCursor()

class FocusPreviousPaneView extends FocusAction
  execute: ->
    atom.workspaceView.focusPreviousPaneView()
    @focusCursor()

module.exports = { FocusPaneViewOnLeft, FocusPaneViewOnRight,
  FocusPaneViewAbove, FocusPaneViewBelow, FocusPreviousPaneView }

class FocusAction
  constructor: ->
  isComplete: -> true
  isRecordable: -> false

  focusCursor: ->
    editor = atom.workspace.getActivePaneItem()
    editorView = atom.workspaceView.getActiveView()
    if editor? and editorView?
      cursorPosition = editor.getCursorBufferPosition()
      editorView.scrollToBufferPosition(cursorPosition)

class FocusPaneViewOnLeft extends FocusAction
  execute: ->
    atom.workspaceView.focusPaneViewOnLeft()
    @focusCursor()

class FocusPaneViewOnRight extends FocusAction
  execute: ->
    atom.workspaceView.focusPaneViewOnRight()
    @focusCursor()

class FocusPaneViewAbove extends FocusAction
  execute: ->
    atom.workspaceView.focusPaneViewAbove()
    @focusCursor()

class FocusPaneViewBelow extends FocusAction
  execute: ->
    atom.workspaceView.focusPaneViewBelow()
    @focusCursor()

class FocusPreviousPaneView extends FocusAction
  execute: ->
    atom.workspaceView.focusPreviousPaneView()
    @focusCursor()

module.exports = { FocusPaneViewOnLeft, FocusPaneViewOnRight,
  FocusPaneViewAbove, FocusPaneViewBelow, FocusPreviousPaneView }

Access active pane item from atom.workspace


'editor':
  'showIndentGuide': true
  'fontFamily': 'Menlo'
  'fontSize': 15
'core':
  'excludeVcsIgnoredPaths': true
  'ignoredNames': [
    '.bundle'
    '.git'
    'log'
    'repositories'
    'tmp'
    'vendor'
  ]
  'themes': [
    'unity-ui'
    'glacier-syntax'
  ]
  'hideGitIgnoredFiles': true
  'disabledPackages': [
    'background-tips'
    'exception-reporting'
    'metrics'
  ]
'spell-check':
  'grammars': [
    'text.plain'
    'source.gfm'
    'text.git-commit'
  ]
'welcome':
  'showOnStartup': false
'release-notes':
  'viewedVersion': '0.89.0'
'tree-view': {}

"*":
  editor:
    showIndentGuide: true
    fontFamily: "Menlo"
    fontSize: 15
    invisibles: {}
  core:
    excludeVcsIgnoredPaths: true
    ignoredNames: [
      ".bundle"
      ".git"
      "log"
      "repositories"
      "tmp"
      "vendor"
    ]
    themes: [
      "unity-ui"
      "glacier-syntax"
    ]
    hideGitIgnoredFiles: true
    disabledPackages: [
      "background-tips"
      "exception-reporting"
      "metrics"
    ]
  "spell-check":
    grammars: [
      "text.plain"
      "source.gfm"
      "text.git-commit"
    ]
  welcome:
    showOnStartup: false
  "release-notes":
    viewedVersion: "0.89.0"
  "tree-view": {}

Add atom and xip.io stuff.

Add atom and xip.io stuff.


window.Binco.Select2 =
  load: (selector) ->
    selector = if typeof selector == 'undefined' then '.select2-rails' else selector
    $(selector).select2()

$(document).on 'ready page:load', window.Binco.Select2.load

window.Binco.Select2 =
  load: (selector) ->
    selector = if typeof selector == 'string' then selector else '.select2-rails'
    $(selector).select2()

$(document).on 'ready page:load', window.Binco.Select2.load

Make the selector argument only work on strings


NylasStore = require 'nylas-store'
Reflux = require 'reflux'
_ = require 'underscore'
{Message,
 Actions,
 DatabaseStore,
 AccountStore,
 FocusedContentStore,
 DestroyDraftTask,
 DatabaseView} = require 'nylas-exports'

class DraftListStore extends NylasStore
  constructor: ->
    @listenTo DatabaseStore, @_onDataChanged
    @listenTo AccountStore, @_onAccountChanged

    # It's important to listen to sendDraftSuccess because the
    # _onDataChanged method will ignore our newly created draft because it
    # has its draft bit set to false (since it's now a message)!
    @listenTo Actions.sendDraftSuccess, => @_view.invalidate()
    @_createView()

  view: =>
    @_view

  _createView: =>
    account = AccountStore.current()

    if @unlisten
      @unlisten()
      @_view = null

    return unless account

    @_view = new DatabaseView Message,
      matchers: [
        Message.attributes.accountId.equal(account.id)
        Message.attributes.draft.equal(true)
      ],
      includes: [Message.attributes.body]
      orders: [Message.attributes.date.descending()]

    @unlisten = @_view.listen => @trigger({})

  _onAccountChanged: =>
    @_createView()

  _onDataChanged: (change) =>
    return unless change.objectClass is Message.name
    containsDraft = _.some(change.objects, (msg) -> msg.draft)
    return unless containsDraft and @_view
    @_view.invalidate({change: change, shallow: true})

module.exports = new DraftListStore()

NylasStore = require 'nylas-store'
Reflux = require 'reflux'
_ = require 'underscore'
{Message,
 Actions,
 DatabaseStore,
 AccountStore,
 FocusedContentStore,
 DestroyDraftTask,
 DatabaseView} = require 'nylas-exports'

class DraftListStore extends NylasStore
  constructor: ->
    @listenTo DatabaseStore, @_onDataChanged
    @listenTo AccountStore, @_onAccountChanged

    # It's important to listen to sendDraftSuccess because the
    # _onDataChanged method will ignore our newly created draft because it
    # has its draft bit set to false (since it's now a message)!
    @listenTo Actions.sendDraftSuccess, => @_view.invalidate()
    @_createView()

  view: =>
    @_view

  _createView: =>
    account = AccountStore.current()

    if @unlisten
      @unlisten()
      @_view = null

    return unless account

    @_view = new DatabaseView Message,
      matchers: [
        Message.attributes.accountId.equal(account.id)
        Message.attributes.draft.equal(true)
      ],
      includes: [Message.attributes.body]
      orders: [Message.attributes.date.descending()]

    @unlisten = @_view.listen => @trigger({})

  _onAccountChanged: =>
    @_createView()

  _onDataChanged: (change) =>
    return unless change.objectClass is Message.name
    return unless @_view
    @_view.invalidate({change: change, shallow: true})

module.exports = new DraftListStore()

Refresh view when draft becomes message

fix(drafts): Refresh view when draft becomes message


React = require 'react'
{Actions} = require 'nylas-exports'
{RetinaImg} = require 'nylas-component-kit'
AccountCommands = require '../account-commands'

class AccountSwitcher extends React.Component
  @displayName: 'AccountSwitcher'

  @propTypes:
    accounts: React.PropTypes.array.isRequired
    focusedAccounts: React.PropTypes.array.isRequired

  _makeMenuTemplate: =>
    template = AccountCommands.menuTemplate(
      @props.accounts,
      @props.focusedAccounts,
      clickHandlers: true
    )
    template = template.concat [
      {type: 'separator'}
      {label: 'Manage Accounts...', click: @_onManageAccounts}
    ]
    return template

  # Handlers

  _onManageAccounts: =>
    Actions.switchPreferencesTab('Accounts')
    Actions.openPreferences()

  _onShowMenu: =>
    remote = require('electron').remote
    Menu = remote.Menu
    menu = Menu.buildFromTemplate(@_makeMenuTemplate())
    menu.popup()

  render: =>
    <div className="account-switcher" onMouseDown={@_onShowMenu}>
      <RetinaImg
        style={width: 13, height: 14}
        name="account-switcher-dropdown.png"
        mode={RetinaImg.Mode.ContentDark} />
    </div>

module.exports = AccountSwitcher

React = require 'react'
{Actions} = require 'nylas-exports'
{RetinaImg} = require 'nylas-component-kit'
AccountCommands = require '../account-commands'

class AccountSwitcher extends React.Component
  @displayName: 'AccountSwitcher'

  @propTypes:
    accounts: React.PropTypes.array.isRequired
    focusedAccounts: React.PropTypes.array.isRequired

  _makeMenuTemplate: =>
    template = AccountCommands.menuTemplate(
      @props.accounts,
      @props.focusedAccounts,
      clickHandlers: true
    )
    template = template.concat [
      {type: 'separator'}
      {label: 'Add Account...', click: @_onAddAccount}
      {label: 'Manage Accounts...', click: @_onManageAccounts}
    ]
    return template

  # Handlers

  _onAddAccount: =>
    ipc = require('electron').ipcRenderer
    ipc.send('command', 'application:add-account')

  _onManageAccounts: =>
    Actions.switchPreferencesTab('Accounts')
    Actions.openPreferences()

  _onShowMenu: =>
    remote = require('electron').remote
    Menu = remote.Menu
    menu = Menu.buildFromTemplate(@_makeMenuTemplate())
    menu.popup()

  render: =>
    <div className="account-switcher" onMouseDown={@_onShowMenu}>
      <RetinaImg
        style={width: 13, height: 14}
        name="account-switcher-dropdown.png"
        mode={RetinaImg.Mode.ContentDark} />
    </div>

module.exports = AccountSwitcher

fix(acct-menu): Add option to add account from context menu


angular.module('loomioApp').directive 'threadPreviewCollection', ->
  scope: {query: '=', limit: '=?'}
  restrict: 'E'
  templateUrl: 'generated/components/thread_preview_collection/thread_preview_collection.html'
  replace: true
  controller: ($scope, Records, CurrentUser) ->
    $scope.lastVoteByCurrentUser = (thread) ->
      thread.activeProposal().lastVoteByUser(CurrentUser)

    $scope.markAsRead = (thread) ->
      thread.markAsRead()

    return

angular.module('loomioApp').directive 'threadPreviewCollection', ->
  scope: {query: '=', limit: '=?'}
  restrict: 'E'
  templateUrl: 'generated/components/thread_preview_collection/thread_preview_collection.html'
  replace: true
  controller: ($scope, Records, CurrentUser) ->
    $scope.lastVoteByCurrentUser = (thread) ->
      thread.activeProposal().lastVoteByUser(CurrentUser)

    return

Remove crufty mark as read method

Remove crufty mark as read method


angular.module("admin.products").directive "setOnDemand", ->
  link: (scope, element, attr) ->
    onHand = element.context.querySelector("#variant_on_hand")
    onDemand = element.context.querySelector("#variant_on_demand")

    if onDemand.checked
      onHand.disabled = 'disabled'
      onHand.dataStock = onHand.value
      onHand.value = t('admin.products.variants.infinity')

    onDemand.addEventListener 'change', (event) ->
      if onDemand.checked
        onHand.disabled = 'disabled'
        onHand.dataStock = onHand.value
        onHand.value = t('admin.products.variants.infinity')
      else
        onHand.removeAttribute('disabled')
        onHand.value = onHand.dataStock

angular.module("admin.products").directive "setOnDemand", ->
  link: (scope, element, attr) ->
    onHand = element.context.querySelector("#variant_on_hand")
    onDemand = element.context.querySelector("#variant_on_demand")

    disableOnHandIfOnDemand = ->
      if onDemand.checked
        onHand.disabled = 'disabled'
        onHand.dataStock = onHand.value
        onHand.value = t('admin.products.variants.infinity')

    disableOnHandIfOnDemand()

    onDemand.addEventListener 'change', (event) ->
      disableOnHandIfOnDemand()

      if !onDemand.checked
        onHand.removeAttribute('disabled')
        onHand.value = onHand.dataStock

Refactor and remove empty lines

Refactor and remove empty lines


fs = require 'fs-plus'
temp = require('temp').track()
path = require 'path'

DefaultFileIcons = require '../lib/default-file-icons'
FileIcons = require '../lib/file-icons'

describe 'FileIcons', ->
  afterEach ->
    FileIcons.setService(new DefaultFileIcons)

  it 'provides a default', ->
    expect(FileIcons.getService()).toBeDefined()
    expect(FileIcons.getService()).not.toBeNull()

  it 'allows the default to be overridden', ->
    service = new Object
    FileIcons.setService(service)

    expect(FileIcons.getService()).toBe(service)

  it 'allows the service to be reset to the default easily', ->
    service = new Object
    FileIcons.setService(service)
    FileIcons.resetService()

    expect(FileIcons.getService()).not.toBe(service)

  describe 'Class handling', ->
    [workspaceElement, treeView, files] = []

    beforeEach ->
      rootDirPath = fs.absolute(temp.mkdirSync('tree-view-root1'))

      for i in [1..3]
        filepath = path.join(rootDirPath, "file-#{i}.txt")
        fs.writeFileSync(filepath, "Nah")

      atom.project.setPaths([rootDirPath])
      workspaceElement = atom.views.getView(atom.workspace)
      jasmine.attachToDOM(workspaceElement)

      FileIcons.setService
        iconClassForPath: (path, file) ->
          [name, id] = path.match(/file-(\d+)\.txt$/)
          switch id
            when "1" then 'first second'
            when "2" then ['first', 'second']
            when "3" then file.constructor.name

      waitsForPromise ->
        atom.packages.activatePackage('tree-view')

      runs ->
        treeView = atom.packages.getActivePackage("tree-view").mainModule.createView()
        files = workspaceElement.querySelectorAll('li[is="tree-view-file"]')

    afterEach ->
      temp.cleanup()

    it 'allows multiple classes to be passed', ->
      expect(files[0].fileName.className).toBe('name icon first second')

    it 'allows an array of classes to be passed', ->
      expect(files[1].fileName.className).toBe('name icon first second')

    it 'passes a file reference as iconClassForPath\'s second argument', ->
      expect(files[2].fileName.className).toBe('name icon File')

Add specs for enhanced class-list handling


App.CwbSearchesController = Em.ArrayController.extend

  needs: 'corpus'

  # This will be bound to properties on views for for simple search, multiword
  # search and regex search.
  query: ''

  corpus: null
  corpusBinding: 'controllers.corpus.content'

  createCwbSearch: ->
    metadataValueIds = {}
    metadataValueIds[@get('corpus.id')] = [1,2,3]

    # TODO: Add support for simultaneous search in different "editions" within
    # the same corpus (e.g. different languages in a parallel corpus). Each
    # edition will have a distinct shortName.
    queries = [
      corpusEdition: @get('corpus.shortName')
      query:         @get('query')
    ]

    search = App.CwbSearch.createRecord(
      metadataValueIds: metadataValueIds
      queries: queries)

    @pushObject(search)

App.CwbSearchesController = Em.ArrayController.extend
  content: []

  needs: 'corpus'

  # This will be bound to properties on views for for simple search, multiword
  # search and regex search.
  query: ''

  corpus: null
  corpusBinding: 'controllers.corpus.content'

  # Action handler
  search: -> @createCwbSearch()

  createCwbSearch: ->
    metadataValueIds = {}
    metadataValueIds[@get('corpus.id')] = [1,2,3]

    # TODO: Add support for simultaneous search in different "editions" within
    # the same corpus (e.g. different languages in a parallel corpus). Each
    # edition will have a distinct shortName.
    queries = [
      corpusEdition: @get('corpus.shortName')
      query:         @get('query')
    ]

    search = App.CwbSearch.createRecord(
      metadataValueIds: metadataValueIds
      queries: queries)

    @get('store').commit()

    @pushObject(search)

Send search query to server

Send search query to server


path = require "path"
assert = require "assert"
rootRequire = require("root-require")

root = rootRequire.packpath.parent()
pkg = rootRequire("./package.json")

module.constructor.prototype.require = (modulePath) ->
  assert(modulePath, 'missing path')
  assert(typeof modulePath == 'string', 'path must be a string')

  load = (modulePath) =>
    this.constructor._load(modulePath, this)

  overridePath = pkg.browser[modulePath]

  if overridePath?
    modulePath = path.join(root, overridePath)

  return load(modulePath)

if not (global.window? and global.document?)
  jsdom = require('jsdom').jsdom

  global.document = jsdom()
  global.window = document.defaultView

  for own name, object of global.window
    if not global[name]?
      global[name] = object

Bokeh = require './main'
module.exports = Bokeh

path = require "path"
assert = require "assert"
rootRequire = require("root-require")

root = rootRequire.packpath.parent()
pkg = rootRequire("./package.json")

module.constructor.prototype.require = (modulePath) ->
  assert(modulePath, 'missing path')
  assert(typeof modulePath == 'string', 'path must be a string')

  load = (modulePath) =>
    this.constructor._load(modulePath, this)

  overridePath = pkg.browser[modulePath]

  if overridePath?
    modulePath = path.join(root, overridePath)

  return load(modulePath)

if not (global.window? and global.document?)
  jsdom = require('jsdom').jsdom

  global.document = jsdom()
  global.window = document.defaultView

  for own name, object of global.window
    if not global[name]?
      global[name] = object

Bokeh = require './main'
_ = Bokeh._

APIs = require './api'
_.extend(Bokeh, _.omit(APIs, "models"))

module.exports = Bokeh

Extend node.js' Bokeh with plotting APIs


#= require magic_word

describe 'ValidationMessage', ->
  messageBox = $('
Stuff
')
  m = new MagicWord.ValidationMessage messageBox

  describe '#constructor', ->
    it 'creates a validation message div', ->
      expect(messageBox.siblings('.validation-msg').length).toBe(1)

  describe '#successMessage', ->
    it "sets the validation message's class to success", ->
      m.successMessage()
      expect(messageBox.siblings('.validation-msg').attr('class')).toBe('validation-msg success')

  describe '#errorMessage', ->
    it "sets the validation message's class to error", ->
      m.errorMessage()
      expect(messageBox.siblings('.validation-msg').attr('class')).toBe('validation-msg error')

  describe '#resetMessage', ->
    it 'sets the given message', ->
      m.resetMessage('Your validation passed!')
      expect(messageBox.siblings('.validation-msg').text()).toBe('Your validation passed!')

#= require magic_word

describe 'ValidationMessage', ->
  beforeEach ->
    @messageBox = $('
Stuff
')
    @m = new MagicWord.ValidationMessage @messageBox

  describe '#constructor', ->
    it 'creates a validation message div', ->
      expect(@messageBox.siblings('.validation-msg').length).toBe(1)

  describe '#successMessage', ->
    it "sets the validation message's class to success", ->
      @m.successMessage()
      expect(@messageBox.siblings('.validation-msg').attr('class')).toBe('validation-msg success')

  describe '#errorMessage', ->
    it "sets the validation message's class to error", ->
      @m.errorMessage()
      expect(@messageBox.siblings('.validation-msg').attr('class')).toBe('validation-msg error')

  describe '#resetMessage', ->
    it 'sets the given message', ->
      @m.resetMessage('Your validation passed!')
      expect(@messageBox.siblings('.validation-msg').text()).toBe('Your validation passed!')

Use beforeEach block in validation message spec


class @Gmaps4Rails.Yandex.Map extends Gmaps4Rails.Common

  @include Gmaps4Rails.Interfaces.Map

  @include  Gmaps4Rails.Map
  @include  Gmaps4Rails.Yandex.Shared
  @include  Gmaps4Rails.Configuration

  CONF:
    disableDefaultUI:       false
    disableDoubleClickZoom: false
    type:                   "ROADMAP" # HYBRID, ROADMAP, SATELLITE, TERRAIN
    mapTypeControl:         null

  constructor:(map_options, controller) ->
    @controller    = controller

    defaultOptions = @setConf()
    @options  = @mergeObjects map_options, defaultOptions

    yandexOptions =
      center: @createLatLng(@options.center_latitude, @options.center_longitude)
      zoom:   @options.zoom
      behaviors: @options.behaviors

    mergedYandexOptions = @mergeObjects map_options.raw, yandexOptions

    @serviceObject = new ymaps.Map(@options.id, mergedYandexOptions)

  extendBoundsWithMarker : (marker)->
    @boundsObject.extend(@createLatLng(marker.lat,marker.lng))

  extendBoundsWithPolyline: (polyline)->

  extendBoundsWithPolygon: (polygon)->

  extendBoundsWithCircle: (circle)->

  extendBound: (bound)->

  fitBounds: ->
    @serviceObject.zoomToExtent(@boundsObject, true)

  adaptToBounds: ->
    @fitBounds()

  centerMapOnUser : (position)->
    @serviceObject.setCenter position

class @Gmaps4Rails.Yandex.Map extends Gmaps4Rails.Common

  @include Gmaps4Rails.Interfaces.Map

  @include  Gmaps4Rails.Map
  @include  Gmaps4Rails.Yandex.Shared
  @include  Gmaps4Rails.Configuration

  CONF:
    disableDefaultUI:       false
    disableDoubleClickZoom: false
    type:                   "ROADMAP" # HYBRID, ROADMAP, SATELLITE, TERRAIN
    mapTypeControl:         null

  constructor:(map_options, controller) ->
    @controller    = controller

    defaultOptions = @setConf()
    @options  = @mergeObjects map_options, defaultOptions

    yandexOptions =
      center: @createLatLng(@options.center_latitude, @options.center_longitude)
      zoom:   @options.zoom
      behaviors: @options.behaviors

    mergedYandexOptions = @mergeObjects map_options.raw, yandexOptions

    @serviceObject = new ymaps.Map(@options.id, mergedYandexOptions)

  extendBoundsWithMarkers : (marker)->
    @controller.getMapObject().setBounds(@controller.getMapObject().geoObjects.getBounds());

  extendBoundsWithPolyline: (polyline)->

  extendBoundsWithPolygon: (polygon)->

  extendBoundsWithCircle: (circle)->

  extendBound: (bound)->

  fitBounds: ->
    @serviceObject.zoomToExtent(@boundsObject, true)

  adaptToBounds: ->
    @fitBounds()

  centerMapOnUser : (position)->
    @serviceObject.setCenter position

Extend Yandex Map bounds with markers

Extend Yandex Map bounds with markers


{ formatQuestionType } = require 'meteor/gq:helpers'

Template.basic_results_info.helpers
  hasRange: ->
    @type in ['number', 'scale']

  formatQuestionType: -> formatQuestionType(@type)

Template.type_icon.helpers
  icon: ->
    type = Template.instance().data.type
    switch type
      when 'checkboxes'
        'check-square-o'
      when 'datetime'
        'clock-o'
      when 'date'
        'calendar'
      when 'longAnswer'
        'align-left'
      when 'multipleChoice'
        'list-ul'
      when 'number'
        'hashtag'
      when 'scale'
        'sliders'
      when 'shortAnswer'
        'minus'

{ formatQuestionType } = require 'meteor/gq:helpers'

Template.basic_results_info.helpers
  hasRange: ->
    @type in ['number', 'scale']

Template.type_icon.helpers
  icon: ->
    type = Template.instance().data.type
    switch type
      when 'checkboxes'
        'check-square-o'
      when 'datetime'
        'clock-o'
      when 'date'
        'calendar'
      when 'longAnswer'
        'align-left'
      when 'multipleChoice'
        'list-ul'
      when 'number'
        'hashtag'
      when 'scale'
        'sliders'
      when 'shortAnswer'
        'minus'

  formatQuestionType: -> formatQuestionType(@type)

Move formatQuestionType helper to correct template


###* @jsx React.DOM ###

window.WFplayer = React.createClass
  componentDidMount: ->
    $node = $(@getDOMNode())
    mediaObj = @props.mediaObj

    $(document).tooltip
      content: -> $node.prop('title')

    corpus_id = mediaObj.corpus_id
    line_key = mediaObj.mov.line_key
    mov = mediaObj.mov.movie_loc
    path = mediaObj.mov.path
    $("#movietitle").text(mediaObj.title)
    start = mediaObj.divs.annotation[parseInt(mediaObj.start_at)].from
    stop  = mediaObj.divs.annotation[parseInt(mediaObj.end_at)].to

    $node.find("#waveframe").attr('src', "wfplayer-#{corpus_id}-#{line_key}-#{start}-#{stop}")

  render: ->
    `<div><iframe height="370" width="100%" id="waveframe" target="_blank" className="wfplayer"></iframe></div>`

###* @jsx React.DOM ###

window.WFplayer = React.createClass
  componentDidMount: ->
    $node = $(@getDOMNode())
    mediaObj = @props.mediaObj

    $(document).tooltip
      content: -> $node.prop('title')

    corpus_id = mediaObj.corpus_id
    line_key = mediaObj.mov.line_key
    mov = mediaObj.mov.movie_loc
    path = mediaObj.mov.path
    $("#movietitle").text(mediaObj.title)
    start = mediaObj.divs.annotation[parseInt(mediaObj.start_at)].from
    stop  = mediaObj.divs.annotation[parseInt(mediaObj.end_at)].to

    $node.find("#waveframe").attr('src', "wfplayer-#{corpus_id}-#{line_key}-#{start}-#{stop}")

  render: ->
    `<div><iframe height="385" width="100%" id="waveframe" target="_blank" className="wfplayer"></iframe></div>`

Make the iframe with the player higher, to avoid scrolling even when the selection stats are displayed


behaviours = {}

share.attach = attach = (behaviour, args...) ->
  check behaviour, Match.OneOf Function, String

  if Match.test behaviour, String
    options = behaviours[behaviour].options
    behaviour = behaviours[behaviour].behaviour

  if Match.test behaviour, Function
    context =
      collection: @
      options: options or {}

    behaviour.apply context, args

  else
    console.warn 'Behaviour not found'

  return

class CollectionBehaviours

  @attach: (collections, args...) ->
    check collections, Match.OneOf Mongo.Collection, [Mongo.Collection]

    if Match.test collections, Mongo.Collection
      collections = [collections]

    attach.apply collection, args for collection in collections

  @configure: (name, options) ->
    check name, String
    check options, Object

    if name of behaviours
      behaviours[name].options = options

    else
      console.warn 'Configure failed, behaviour not found'

  @define: (name, behaviour, options) ->
    check name, String
    check behaviour, Function

    optionsPattern = Match.ObjectIncluding
      replace: Boolean

    check options, optionsPattern

    if name of behaviours and not options?.replace
      console.warn 'Behaviour already defined, use {replace: true} to override'

    else
      behaviours[name] =
        behaviour: behaviour

behaviours = {}

share.attach = attach = (behaviour, args...) ->
  check behaviour, Match.OneOf Function, String

  if Match.test behaviour, String
    options = behaviours[behaviour].options
    behaviour = behaviours[behaviour].behaviour

  if Match.test behaviour, Function
    context =
      collection: @
      options: options or {}

    behaviour.apply context, args

  else
    console.warn 'Behaviour not found'

  return

class CollectionBehaviours

  @attach: (collections, args...) ->
    check collections, Match.OneOf Mongo.Collection, [Mongo.Collection]

    if Match.test collections, Mongo.Collection
      collections = [collections]

    attach.apply collection, args for collection in collections

  @configure: (name, options) ->
    check name, String
    check options, Object

    if name of behaviours
      behaviours[name].options = options

    else
      console.warn 'Configure failed, behaviour not found'

  @define: (name, behaviour, options) ->
    check name, String
    check behaviour, Function

    optionsPattern = Match.ObjectIncluding
      replace: Boolean

    check options, Match.Optional optionsPattern

    if name of behaviours and not options?.replace
      console.warn 'Behaviour already defined, use {replace: true} to override'

    else
      behaviours[name] =
        behaviour: behaviour

Make options argument optional in define function


'name': 'Test Ruby'
'scopeName': 'test.rb'
'firstLineMatch': '^\\#!.*(?:\\s|\\/)(?:testruby)(?:$|\\s)'
'fileTypes': [
  'rb'
]
'patterns': [
  {
    'match': 'ruby'
    'name': 'meta.class.ruby'
  }
]

'name': 'Test Ruby'
'type': 'tree-sitter'
'scopeName': 'test.rb'
'parser': 'tree-sitter-ruby'

'firstLineRegex': '^\\#!.*(?:\\s|\\/)(?:testruby)(?:$|\\s)'

'fileTypes': [
  'rb'
]

Update fixture package to use a tree-sitter grammar


CompositeDisposable = null
Range = null

module.exports = TranslatorPlusDictionary =
  subscriptions: null

  activate: (state) ->
    # Initialize modules and classes
    CompositeDisposable ?= require('atom').CompositeDisposable
    Range ?= require('atom').Range

    # Initialize fields
    @subscriptions = new CompositeDisposable

    # Register the commands
    @subscriptions.add atom.commands.add 'atom-workspace', 'translator-plus-dictionary:translate': => @translate()

  deactivate: ->
    @subscriptions.dispose()

  serialize: ->

  translate: ->
    editor = atom.workspace.getActiveTextEditor()
    return unless editor?

    texts = []

    # Get texts from selected texts
    for range in editor.getSelectedBufferRanges()
      if (range.start.row == range.end.row) && (range.start.column == range.end.column)
        continue
      text = editor.getTextInRange(range)
      texts.push({
        range: range
        text: text
      })

    if texts.length == 0
      # Get words from cursor positions if no text are selected
      for cursor in editor.getCursors()
        beginWord = cursor.getBeginningOfCurrentWordBufferPosition()
        endWord = cursor.getEndOfCurrentWordBufferPosition()
        range = new Range(beginWord, endWord)

        texts.push({
          range: range
          text: editor.getTextInRange(range)
        })

CompositeDisposable = null
Range = null

module.exports = TranslatorPlusDictionary =
  subscriptions: null

  activate: (state) ->
    # Initialize modules and classes
    CompositeDisposable ?= require('atom').CompositeDisposable
    Range ?= require('atom').Range

    # Initialize fields
    @subscriptions = new CompositeDisposable

    # Register the commands
    @subscriptions.add atom.commands.add 'atom-workspace', 'translator-plus-dictionary:translate': => @translate()

  deactivate: ->
    @subscriptions.dispose()

  serialize: ->

  translate: ->
    editor = atom.workspace.getActiveTextEditor()
    return unless editor?

    targets = []

    # Get texts from selected texts
    for range in editor.getSelectedBufferRanges()
      if (range.start.row == range.end.row) && (range.start.column == range.end.column)
        continue
      text = editor.getTextInRange(range)
      targets.push({
        range: range
        text: text
      })

    if targets.length == 0
      # Get words from cursor positions if no text are selected
      for cursor in editor.getCursors()
        beginWord = cursor.getBeginningOfCurrentWordBufferPosition()
        endWord = cursor.getEndOfCurrentWordBufferPosition()
        range = new Range(beginWord, endWord)

        targets.push({
          range: range
          text: editor.getTextInRange(range)
        })

Rename a local variable in "translate" function

ref #2


tabs:
  'cmd-shift-{': (tabs) -> tabs.pane.prevTab()
  'cmd-shift-}': (tabs) -> tabs.pane.nextTab()
  'cmd-1': (tabs) -> tabs.pane.switchToTab 1
  'cmd-2': (tabs) -> tabs.pane.switchToTab 2
  'cmd-3': (tabs) -> tabs.pane.switchToTab 3
  'cmd-4': (tabs) -> tabs.pane.switchToTab 4
  'cmd-5': (tabs) -> tabs.pane.switchToTab 5
  'cmd-6': (tabs) -> tabs.pane.switchToTab 6
  'cmd-7': (tabs) -> tabs.pane.switchToTab 7
  'cmd-8': (tabs) -> tabs.pane.switchToTab 8
  'cmd-9': (tabs) -> tabs.pane.switchToTab 9

tabs:
  'cmd-w': (tabs) -> tabs.pane.closeActiveTab()
  'cmd-shift-{': (tabs) -> tabs.pane.prevTab()
  'cmd-shift-}': (tabs) -> tabs.pane.nextTab()
  'cmd-1': (tabs) -> tabs.pane.switchToTab 1
  'cmd-2': (tabs) -> tabs.pane.switchToTab 2
  'cmd-3': (tabs) -> tabs.pane.switchToTab 3
  'cmd-4': (tabs) -> tabs.pane.switchToTab 4
  'cmd-5': (tabs) -> tabs.pane.switchToTab 5
  'cmd-6': (tabs) -> tabs.pane.switchToTab 6
  'cmd-7': (tabs) -> tabs.pane.switchToTab 7
  'cmd-8': (tabs) -> tabs.pane.switchToTab 8
  'cmd-9': (tabs) -> tabs.pane.switchToTab 9

Revert "tabs dont handle cmd-w"

Revert "tabs dont handle cmd-w"

This reverts commit d06e9a1bd0ff6d649cf3a2b14b5c528137f261f7.


gulp = require 'gulp'
uglify = require 'gulp-uglify'
rename = require 'gulp-rename'

gulp.task 'compress', ->
  gulp.src 'dist/position-sticky.js'
    .pipe uglify()
    .pipe rename('position-sticky.min.js')
    .pipe gulp.dest 'dist'

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

Add compress as default task

Add compress as default task


Neighborly.Projects = {} if Neighborly.Projects is undefined
Neighborly.Projects.Updates = {} if Neighborly.Projects.Updates is undefined

Neighborly.Projects.Updates.Index =
  init: Backbone.View.extend _.extend(
    el: '.updates'
    events:
      "ajax:success form#new_update": "onCreate"
      "ajax:success .update": "onDestroy"

    onCreate: (e, data) ->
      this.$('.new_update').trigger('reset')
      @$results.prepend data

    onDestroy: (e)->
      $(e.currentTarget).remove()

    initialize: ->
      this.$loader = this.$('.updates-loading img')
      this.$loaderDiv = this.$('.updates-loading')
      this.$results = this.$('.list')
      this.path = this.$el.data('path')
      this.filter = { page: 2 }
      this.setupScroll()
      this.$el.on 'scroll:success', this.parseXFBML

    parseXFBML: ->
      FB.XFBML.parse() if this.$el.is(":visible")

    , Neighborly.InfiniteScroll)

Neighborly.Projects = {} if Neighborly.Projects is undefined
Neighborly.Projects.Updates = {} if Neighborly.Projects.Updates is undefined

Neighborly.Projects.Updates.Index =
  init: Backbone.View.extend _.extend(
    el: '.updates'
    events:
      "ajax:success form#new_update": "onCreate"
      "ajax:success .update": "onDestroy"

    onCreate: (e, data) ->
      this.$('.new_update').trigger('reset')
      @$results.prepend data
      this.parseXFBML()

    onDestroy: (e)->
      $(e.currentTarget).remove()

    initialize: ->
      _.bindAll(this, 'parseXFBML')
      this.$loader = this.$('.updates-loading img')
      this.$loaderDiv = this.$('.updates-loading')
      this.$results = this.$('.list')
      this.path = this.$el.data('path')
      this.filter = { page: 2 }
      this.setupScroll()
      this.$el.on 'scroll:success', this.parseXFBML

    parseXFBML: ->
      FB.XFBML.parse() if this.$el.is(":visible")

    , Neighborly.InfiniteScroll)

Fix parseXFBML on update index

Fix parseXFBML on update index


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

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

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

  if not fun fn
    fn = ->

  {params, options, fn}

util = require 'util'

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

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

  if not util.isFunction fn
    fn = ->

  {params, options, fn}

Replace validation to node built-in functions


{statusCreated, statusNoContent, statusOk} = require '../utils'

endpoint = (x) ->
  "/site/#{x.siteId}/deploy"

byId = (x) ->
  "#{endpoint x}/#{x.id ? x.deployId}"

upload = (x) ->
  "#{byId x}/#{x.path}"

module.exports = (blueprints) ->
  blueprints.deploy =
    create:
      url:     endpoint
      method:  'POST'
      expects: statusCreated
    update:
      url:     byId
      method:  'PATCH'
      expects: statusOk
    delete:
      url:     byId
      method:  'DELETE'
      expects: statusNoContent
    restore:
      url:     byId
      method:  'POST'
      expects: statusOk
    upload:
      url:     upload
      method:  'PUT'
      expects: statusOk
      headers:
        'Content-Type': 'application/octet-stream'
      streams: (x) -> fs.createReadStream x.absolutePath
      followRedirects: true

{statusCreated, statusNoContent, statusOk} = require '../utils'

endpoint = (x) ->
  "/site/#{x.siteId}/deploy"

byId = (x) ->
  "#{endpoint x}/#{x.id ? x.deployId}"

upload = (x) ->
  "#{byId x}/files/#{x.path}"

module.exports = (blueprints) ->
  blueprints.deploy =
    create:
      url:     endpoint
      method:  'POST'
      expects: statusCreated
    update:
      url:     byId
      method:  'PATCH'
      expects: statusOk
    delete:
      url:     byId
      method:  'DELETE'
      expects: statusNoContent
    restore:
      url:     byId
      method:  'POST'
      expects: statusOk
    upload:
      url:     upload
      method:  'PUT'
      expects: statusOk
      headers:
        'Content-Type': 'application/octet-stream'
      streams: (x) -> fs.createReadStream x.absolutePath
      followRedirects: true

Use proper path for upload.

Use proper path for upload.


Whitespace = require './whitespace'

module.exports =
  configDefaults:
    removeTrailingWhitespace: true
    ignoreWhitespaceOnCurrentLine: true
    ensureSingleTrailingNewline: true

  activate: ->
    @whitespace = new Whitespace()

  deactivate: ->
    @whitespace.destroy()

Whitespace = require './whitespace'

module.exports =
  configDefaults:
    removeTrailingWhitespace: true
    ignoreWhitespaceOnCurrentLine: true
    ignoreWhitespaceOnlyLines: false
    ensureSingleTrailingNewline: true

  activate: ->
    @whitespace = new Whitespace()

  deactivate: ->
    @whitespace.destroy()

Add ignoreWhitespaceOnlyLines as disabled by default


ETahi.Comment = DS.Model.extend
  commenter: DS.belongsTo('user')
  body: DS.attr('string')
  messageTask: DS.belongsTo('messageTask')
  createdAt: DS.attr('string')

Add date to ember comment serializers

Add date to ember comment serializers


class window.FavouriteTopicButtonView extends ActionButtonView
  mini: true

  onRender: -> @updateButton()

  initialize: ->
    @user = currentUser
    @bindTo @user.favourite_topics, 'add remove change', @updateButton, @

  templateHelpers: =>
    disabled_label: Factlink.Global.t.favourite.capitalize()
    disable_label:  Factlink.Global.t.unfavourite.capitalize()
    enabled_label:  Factlink.Global.t.favourited.capitalize()

  buttonEnabled: ->
    # @model.id is the Topic slug_title
    @model.id in @user.favourite_topics.pluck('slug_title')

  primaryAction: (e) ->
    @model.favourite()

  secondaryAction: (e) ->
    @model.unfavourite()

class window.FavouriteTopicButtonView extends ActionButtonView
  mini: true

  onRender: -> @updateButton()

  initialize: ->
    @user = currentUser
    @bindTo @user.favourite_topics, 'add remove change', @updateButton, @

  templateHelpers: =>
    disabled_label: Factlink.Global.t.favourite.capitalize()
    disable_label:  Factlink.Global.t.unfavourite.capitalize()
    enabled_label:  Factlink.Global.t.favourited.capitalize()

  buttonEnabled: ->
    @model.get('slug_title') in @user.favourite_topics.pluck('slug_title')

  primaryAction: (e) ->
    @model.favourite()

  secondaryAction: (e) ->
    @model.unfavourite()

CHeck favourited topic on 'slug_title'

CHeck favourited topic on 'slug_title'


ETahi.JournalThumbnailController = Ember.ObjectController.extend
  needs: ['application']
  currentUser: Ember.computed.alias 'controllers.application.currentUser'
  isEditing: (-> @get 'model.isDirty').property()
  thumbnailId: (-> "journal-logo-#{@get 'model.id'}").property()
  logoUploadUrl: (-> "/admin/journals/#{@get 'model.id'}/upload_logo").property()
  nameErrors: null
  descriptionErrors: null
  logoPreview: null
  journal: null

  resetErrors: ->
    @setProperties
      nameErrors: null
      descriptionErrors: null

  actions:
    editJournalDetails: -> @set 'isEditing', true
    logoUploading: -> @set 'logoUploading', true

    saveJournalDetails: ->
      #if the logo has changed save it here too.
      if updateLogo = @get('uploadLogoFunction')
        updateLogo()

    resetJournalDetails: ->
      @get('model').rollback()
      @set 'isEditing', false
      @resetErrors()

    logoUploaded: (data) ->
      @set 'model.logoUrl', data.admin_journal.logo_url
      @set 'logoUploading', false
      @get('model').save()
                   .then => @set 'isEditing', false
                   .catch (response) =>
                     @set 'nameErrors', response.errors.name?[0]
                     @set 'descriptionErrors', response.errors.description?[0]

    showPreview: (file) ->
      @set 'logoPreview', file.preview

    uploadReady: (uploadLogoFunction) ->
      @set('uploadLogoFunction', uploadLogoFunction)

ETahi.JournalThumbnailController = Ember.ObjectController.extend
  needs: ['application']
  currentUser: Ember.computed.alias 'controllers.application.currentUser'
  isEditing: (-> @get 'model.isDirty').property()
  thumbnailId: (-> "journal-logo-#{@get 'model.id'}").property()
  logoUploadUrl: (-> "/admin/journals/#{@get 'model.id'}/upload_logo").property()
  nameErrors: null
  descriptionErrors: null
  logoPreview: null
  journal: null

  resetErrors: ->
    @setProperties
      nameErrors: null
      descriptionErrors: null

  saveJournal: ->
    @get('model').save()
                 .then =>
                   @setProperties(isEditing: false, uploadLogoFunction: null, logoPreview: null)
                 .catch (response) =>
                   @set 'nameErrors', response.errors.name?[0]
                   @set 'descriptionErrors', response.errors.description?[0]

  actions:
    editJournalDetails: -> @set 'isEditing', true
    logoUploading: -> @set 'logoUploading', true

    saveJournalDetails: ->
      # updateLogo will fire the 'logoUploaded' action from the component, thus saving the model
      # with the new journal logo url.
      if updateLogo = @get('uploadLogoFunction')
        updateLogo()
      else
        @saveJournal()

    resetJournalDetails: ->
      @get('model').rollback()
      @set 'isEditing', false
      @resetErrors()

    logoUploaded: (data) ->
      @set 'model.logoUrl', data.admin_journal.logo_url
      @set 'logoUploading', false
      @saveJournal()

    showPreview: (file) ->
      @set 'logoPreview', file.preview

    uploadReady: (uploadLogoFunction) ->
      @set('uploadLogoFunction', uploadLogoFunction)

Move journal detail saving to a common function


MergeConflictsView = require './merge-conflicts-view'
SideView = require './side-view'
NavigationView = require './navigation-view'
Conflict = require './conflict'

module.exports =

  activate: (state) ->
    atom.workspaceView.command "merge-conflicts:detect", ->
      MergeConflictsView.detect()

  deactivate: ->

  serialize: ->

MergeConflictsView = require './merge-conflicts-view'
SideView = require './side-view'
NavigationView = require './navigation-view'
Conflict = require './conflict'

module.exports =

  activate: (state) ->
    atom.workspaceView.command "merge-conflicts:detect", ->
      MergeConflictsView.detect()

  deactivate: ->

  configDefaults:
    gitPath: '/usr/local/bin/git'

  serialize: ->

Make the git path a configuration setting.


class Kandan.Plugins.MeAnnounce

  @regex: /^\/me /

  @init: ()->
    Kandan.Modifiers.register @regex, (message, state)=>
      message.content = message.content.replace @regex, "#{message.user.first_name} "
      return Kandan.Helpers.Activities.build_from_base_template(message)

# Kandan.Plugins.register "Kandan.Plugins.MeAnnounce"

class Kandan.Plugins.MeAnnounce

  @regex: /^/me /

  @init: ()->
    Kandan.Modifiers.register @regex, (message, state)=>
      message.content = message.content.replace @regex, "#{message.user.first_name} "
      return Kandan.Helpers.Activities.build_from_base_template(message)

# Kandan.Plugins.register "Kandan.Plugins.MeAnnounce"

Use the escaped regex as a stopgap solution


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

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

  _Before = @Before
  _After  = @After

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

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

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

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

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

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

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

      callforth()

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

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

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

  @timeout = 5000

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

  _Before = @Before
  _After  = @After

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

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

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

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

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

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

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

      callforth()

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

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

[bugfix] Set a default timeout of 5 seconds (instead of infinite).


module.exports = {
  header: 'Help Mark Fields',
  pages: [
    {
      pageNumber: 0,
      text: "One Lorem ipsum dolor sit amet, posuere a mauris, nostra quam nonummy, facilisis amet neque. Ut lacus eros venenatis ipsum, erat ut turpis, aliquam metus vitae volutpat sit sed. Eros maecenas malesuada quam leo laoreet, vel curabitur maecenas neque quam et libero, ut libero, vitae vel eros vestibulum, sagittis etiam nunc. Nonummy risus mattis, sed nisl. Quis non quis quis ultricies non facilisi."
    },

    {
      pageNumber: 1,
      text: "Two Lorem ipsum dolor sit amet, posuere a mauris, nostra quam nonummy, facilisis amet neque. Ut lacus eros venenatis ipsum, erat ut turpis, aliquam metus vitae volutpat sit sed. Eros maecenas malesuada quam leo laoreet, vel curabitur maecenas neque quam et libero, ut libero, vitae vel eros vestibulum, sagittis etiam nunc. Nonummy risus mattis, sed nisl. Quis non quis quis ultricies non facilisi."
    },

    {
      pageNumber: 2,
      text: "Three Lorem ipsum dolor sit amet, posuere a mauris, nostra quam nonummy, facilisis amet neque. Ut lacus eros venenatis ipsum, erat ut turpis, aliquam metus vitae volutpat sit sed. Eros maecenas malesuada quam leo laoreet, vel curabitur maecenas neque quam et libero, ut libero, vitae vel eros vestibulum, sagittis etiam nunc. Nonummy risus mattis, sed nisl. Quis non quis quis ultricies non facilisi."
    }
  ]
}

module.exports = {
  header: 'Mark the fields to be transcribed',
  pages: [
    {
      pageNumber: 0,
      text: "The first step in crowdsourced transcription is to identify and mark the text that should be transcribed.

The scroll bar on the left displays all of the pages in each program. Start by locating the page in the program that lists the title, playwright, director, etc. (This may be several pages in on longer programs.) Click the button for the field you want to mark—for example, “Playwright”—and then draw a rectangle around the relevant text. Click the DONE box, then select another field to mark.

Click the X to delete your selection if you want to change it. Repeat with the next field until you have marked all of them, and then click NEXT to move on to more marking tasks. Remember that some fields may be located on different pages of the program."}

Replace dummy text with Mark workflow help text


`import Ember from "ember";`
`import { EMPLOYEE_ROLES } from "t2-people/utils/constants";`
PersonEditController = Ember.ObjectController.extend
  needs: ['application']

  employeeRoles: EMPLOYEE_ROLES

  actions:
    save: (->
      model = @get('model')
      if model.get('errors')
        model.send('becameValid')
      model.save().then (=> @transitionToRoute 'people'), ((error) ->)
    )

`export default PersonEditController;`

`import Ember from "ember";`
`import { EMPLOYEE_ROLES } from "t2-people/utils/constants";`
PersonEditController = Ember.ObjectController.extend
  needs: ['application']

  employeeRoles: EMPLOYEE_ROLES

  actions:
    save: (->
      model = @get('model')
      if model.get('isError')
        model.send('becameValid')
      model.save().then (=> @transitionToRoute 'people'), ((error) ->)
    )

`export default PersonEditController;`

Change error state check on model.

Change error state check on model.


version = {
	"pdfjs": "1.8.188"
	"moment": "2.9.0"
	"ace": "1.2.5"
}

module.exports = {
	version: version

	lib: (name) ->
		if version[name]?
			return "#{name}-#{version[name]}"
		else
			return "#{name}"
}

version = {
	"pdfjs": "1.7.225"
	"moment": "2.9.0"
	"ace": "1.2.5"
}

module.exports = {
	version: version

	lib: (name) ->
		if version[name]?
			return "#{name}-#{version[name]}"
		else
			return "#{name}"
}

Revert "update pdfjs to v1.8.188"

Revert "update pdfjs to v1.8.188"

This reverts commit 189e48dcc00f530eff674cc4a2b87c06814d45ff.


describe 'Pending', ->
    it 'Should test skipHeader option'
    it 'Should test initWith option'
    it 'Should embed plain JS files'
    it 'Should take a logging callback'
    it 'Should test verbose option'

Add two more pending tests.

Add two more pending tests.


fs = require 'fs'
require 'benchmark-helper'

describe "Editor", ->
  editor = null

  beforeEach ->
    window.rootViewParentSelector = '#jasmine-content'
    window.startup()
    editor = rootView.editor

  afterEach ->
    window.shutdown()

  benchmark "inserting and deleting a character", ->
    editor.hiddenInput.textInput('x')
    editor.backspace()

fs = require 'fs'
require 'benchmark-helper'

describe "Editor", ->
  editor = null

  beforeEach ->
    window.rootViewParentSelector = '#jasmine-content'
    window.startup()
    editor = rootView.editor

  afterEach ->
    window.shutdown()

  benchmark "inserting and deleting a character", ->
    editor.insertText('x')
    editor.backspace()

Call Editor.insertText directly instead of creating a TextInput event


express = require 'express'
bodyParser = require('body-parser')

app = express()
port = process.env.PORT || 5000

app.use express.static __dirname
   .use bodyParser.urlencoded {extended: true}
   .use bodyParser.json()

require('./routes/routes.js')(app)

app.listen(port)

console.log "Listening on port #{port}"

express = require 'express'
busyboy = require('connect-busboy')

app = express()
port = process.env.PORT || 5000

app.use express.static __dirname
   .use busyboy()

require('./routes/routes.js')(app)

app.listen(port)

console.log "Listening on port #{port}"

Use busyboy package instead of body-parser


jQuery ->
  if User.current().data_object.staff == true
    scroll(0,$(".row.osu-purple").offset().top)

jQuery ->
  if User.current().data_object.staff == true
    scroll(0,$(".row.osu-purple").offset()?.top)

Check for offset being set.

Check for offset being set.


Template.blogShowBody.rendered = ->

  # Hide draft posts from crawlers
  if not @data.published
    $('<meta>', { name: 'robots', content: 'noindex,nofollow' }).appendTo 'head'

  # Twitter
  base = "https://twitter.com/intent/tweet"
  url = encodeURIComponent location.origin + location.pathname
  author = @data.author()
  text = encodeURIComponent @data.title
  href = base + "?url=" + url + "&text=" + text

  if author.profile and author.profile.twitter
    href += "&via=" + author.profile.twitter

  $(".tw-share").attr "href", href

  # Facebook
  base = "https://www.facebook.com/sharer/sharer.php"
  url = encodeURIComponent location.origin + location.pathname
  title = encodeURIComponent @data.title
  summary = encodeURIComponent @data.excerpt()
  href = base + "?s=100&p[url]=" + url + "&p[title]=" + title + "&p[summary]=" + summary

  if @data.thumbnail()
    href += "&p[images][0]=" + encodeURIComponent @data.thumbnail()

  $(".fb-share").attr "href", href

Template.blogShowBody.rendered = ->

  # Hide draft posts from crawlers
  if not @data.published
    $('<meta>', { name: 'robots', content: 'noindex,nofollow' }).appendTo 'head'

  # Twitter
  base = "https://twitter.com/intent/tweet"
  url = encodeURIComponent location.origin + location.pathname
  author = @data.author()
  text = encodeURIComponent @data.title
  href = base + "?url=" + url + "&text=" + text

  if author.profile and author.profile.twitter
    href += "&via=" + author.profile.twitter

  $(".tw-share").attr "href", href

  # Facebook
  base = "https://www.facebook.com/sharer/sharer.php"
  url = encodeURIComponent location.origin + location.pathname
  title = encodeURIComponent @data.title
  summary = encodeURIComponent @data.excerpt()
  href = base + "?s=100&p[url]=" + url + "&p[title]=" + title + "&p[summary]=" + summary

  img = @data.thumbnail()
  if img
    if not /^http(s?):\/\/+/.test(img)
      img = location.origin + img
    href += "&p[images][0]=" + encodeURIComponent img

  $(".fb-share").attr "href", href

Handle FB share link when thumbnail URL is relative


class CB.TextButton extends CB.Control
  constructor: (frame) ->
    super(frame)
    @_labelView = new CB.LabelView()
    @_labelView.frame = new CB.Rect(0, 0, 0, 0)
    this.addSubview(@_labelView)

  @property "readonly", "labelView"

  sizeThatFits: (size) ->
    return @labelView.sizeThatFits(size)

  layoutSubviews: () ->
    @_labelView.frame = new CB.Rect(0, 0, @labelView.sizeThatFits().width, @labelView.sizeThatFits().height)

class CB.TextButton extends CB.Control
  constructor: (frame) ->
    super(frame)
    @_labelView = new CB.LabelView()
    @_labelView.frame = new CB.Rect(0, 0, 0, 0)
    this.addSubview(@_labelView)
    @cursor = "pointer"

  @property "readonly", "labelView"

  @property "cursor",
    set: (newPointer) ->
      @_pointer = newPointer
      @css("cursor", newPointer)
      return

  sizeThatFits: (size) ->
    return @labelView.sizeThatFits(size)

  layoutSubviews: () ->
    @_labelView.frame = new CB.Rect(0, 0, @labelView.sizeThatFits().width, @labelView.sizeThatFits().height)

Set default cursor pointer for TextButton


###
# Copyright 2015 ppy Pty. Ltd.
#
# This file is part of osu!web. osu!web is distributed with the hope of
# attracting more community contributions to the core ecosystem of osu!.
#
# osu!web is free software: you can redistribute it and/or modify
# it under the terms of the Affero GNU General Public License version 3
# as published by the Free Software Foundation.
#
# osu!web is distributed WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with osu!web.  If not, see <http://www.gnu.org/licenses/>.
###
{span} = React.DOM
el = React.createElement

bn = 'beatmap-discussions-post-icon'

BeatmapDiscussions.MessageIcon = React.createClass
  mixins: [React.addons.PureRenderMixin]

  render: ->
    name = switch @props.messageType
      when 'praise' then 'heart'
      when 'suggestion' then 'circle-o'
      when 'problem' then 'exclamation-circle'

    el Icon, name: name

###
# Copyright 2015 ppy Pty. Ltd.
#
# This file is part of osu!web. osu!web is distributed with the hope of
# attracting more community contributions to the core ecosystem of osu!.
#
# osu!web is free software: you can redistribute it and/or modify
# it under the terms of the Affero GNU General Public License version 3
# as published by the Free Software Foundation.
#
# osu!web is distributed WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with osu!web.  If not, see <http://www.gnu.org/licenses/>.
###
{span} = React.DOM
el = React.createElement

bn = 'beatmap-discussions-post-icon'

BeatmapDiscussions.MessageIcon = React.createClass
  mixins: [React.addons.PureRenderMixin]

  render: ->
    el Icon, name: @typeMapping[@props.messageType]

  typeMapping:
    praise: 'heart'
    suggestion: 'circle-o'
    problem: 'exclamation-circle'

Use mapping instead of switch

Use mapping instead of switch


Species.DocumentTagLookup = Ember.Mixin.create
  selectedProposalOutcome: null
  selectedProposalOutcomeId: null
  selectedReviewPhase: null
  selectedReviewPhaseId: null

  initDocumentTagsSelectors: ->
    if @get('selectedProposalOutcomeId')
      po = @get('controllers.documentTags.proposalOutcomes').findBy('id', @get('selectedProposalOutcomeId'))
      @set('selectedProposalOutcome', po)

  proposalOutcomeDropdownVisible: ( ->
    @get('selectedDocumentType.id') == 'Document::Proposal'
  ).property('selectedDocumentType')

  actions:
    handleProposalOutcomeSelection: (proposalOutcome) ->
      @set('selectedProposalOutcome', proposalOutcome)

    handleProposalOutcomeDeselection: (proposalOutcome) ->
      @set('selectedProposalOutcome', null)

Species.DocumentTagLookup = Ember.Mixin.create
  selectedProposalOutcome: null
  selectedProposalOutcomeId: null
  selectedReviewPhase: null
  selectedReviewPhaseId: null

  initDocumentTagsSelectors: ->
    if @get('selectedProposalOutcomeId')
      po = @get('controllers.documentTags.proposalOutcomes').findBy('id', @get('selectedProposalOutcomeId'))
      @set('selectedProposalOutcome', po)

  proposalOutcomeDropdownVisible: ( ->
    @get('selectedEventType.id') == 'CitesCop'
  ).property('selectedEventType')

  actions:
    handleProposalOutcomeSelection: (proposalOutcome) ->
      @set('selectedProposalOutcome', proposalOutcome)

    handleProposalOutcomeDeselection: (proposalOutcome) ->
      @set('selectedProposalOutcome', null)

Fix proposal outcome dropdown not showing up when citescop is selected


class FactlinkAppClass extends Backbone.Marionette.Application
  startAsSite: ->
    @startSiteRegions()
    @addInitializer @automaticLogoutInitializer
    @addInitializer @notificationsInitializer
    @addInitializer @scrollToTopInitializer

    @linkTarget = '_self'

    @addInitializer (options)->
      new ProfileRouter controller: new ProfileController # first, as then it doesn't match index pages such as "/m" using "/:username"
      new SearchRouter controller: new SearchController
      new ChannelsRouter controller: new ChannelsController
      new ConversationsRouter controller: new ConversationsController
      new TourRouter controller: new TourController

    @start()

  startAsClient: ->
    @startClientRegions()
    @addInitializer (options)->
      new ClientRouter controller: ClientController
    @addInitializer @clientCloseDiscussionModalInitializer
    @modal = true
    @onClientApp = true

    parent.remote.setFeatureToggles Factlink.Global.can_haz
    @start()

  isCurrentUser: (user) ->
    Factlink.Global.signed_in and user.id == currentUser.id

window.FactlinkApp = new FactlinkAppClass

class FactlinkAppClass extends Backbone.Marionette.Application
  startAsSite: ->
    @startSiteRegions()
    @addInitializer @automaticLogoutInitializer
    @addInitializer @notificationsInitializer
    @addInitializer @scrollToTopInitializer

    @linkTarget = '_self'

    @addInitializer (options)->
      new ProfileRouter controller: new ProfileController # first, as then it doesn't match index pages such as "/m" using "/:username"
      new SearchRouter controller: new SearchController
      new ChannelsRouter controller: new ChannelsController
      new ConversationsRouter controller: new ConversationsController
      new TourRouter controller: new TourController

    @start()

  startAsClient: ->
    @startClientRegions()
    @addInitializer (options)->
      new ClientRouter controller: ClientController
    @addInitializer @clientCloseDiscussionModalInitializer
    @modal = true
    @onClientApp = true

    parent.remote?.setFeatureToggles Factlink.Global.can_haz
    @start()

  isCurrentUser: (user) ->
    Factlink.Global.signed_in and user.id == currentUser.id

window.FactlinkApp = new FactlinkAppClass

Fix usage of parent.remote outside of client in client-pages.


angular.module('loomioApp').controller 'ProfilePageController', ($rootScope, Records, FormService, $location, AbilityService, ModalService, ChangePictureForm, ChangePasswordForm, DeactivateUserForm, $translate, CurrentUser, AppConfig) ->
  @user = CurrentUser

  @init = ->
    $translate.use(@user.locale)
  @init()

  @availableLocales = ->
    AppConfig.locales

  @submit = FormService.submit @, @user,
    flashSuccess: 'profile_page.messages.updated'
    submitFn: Records.users.updateProfile
    successCallback: @init

  @changePicture = ->
    ModalService.open ChangePictureForm

  @changePassword = ->
    ModalService.open ChangePasswordForm

  @deactivateUser = ->
    ModalService.open DeactivateUserForm

  @canDeactivateUser = ->
    AbilityService.canDeactivateUser()

  return

angular.module('loomioApp').controller 'ProfilePageController', ($rootScope, Records, FormService, $location, AbilityService, ModalService, ChangePictureForm, ChangePasswordForm, DeactivateUserForm, $translate, CurrentUser, AppConfig) ->
  @user = CurrentUser

  @init = =>
    $translate.use(@user.locale)
  @init()

  @availableLocales = ->
    AppConfig.locales

  @submit = FormService.submit @, @user,
    flashSuccess: 'profile_page.messages.updated'
    submitFn: Records.users.updateProfile
    successCallback: @init

  @changePicture = ->
    ModalService.open ChangePictureForm

  @changePassword = ->
    ModalService.open ChangePasswordForm

  @deactivateUser = ->
    ModalService.open DeactivateUserForm

  @canDeactivateUser = ->
    AbilityService.canDeactivateUser()

  return

Fix javascript error on profile page

Fix javascript error on profile page


qbnApp = angular.module 'qbnApp', []

class Quality
  constructor: (@name, @defaultValue) ->

  initInstance: () ->
    @value = @defaultValue

instantiate = (proto) ->
  instance = Object.create proto
  instance.initInstance()
  return instance

class Storylet
  constructor: (@title, @text, @choices) ->

class Choice
  constructor: (@text, @requirements, @nextStorylet) ->

qbnApp.controller 'QBN', ($scope) ->
  $scope.qualities = [
    instantiate new Quality 'Punchiness', 7
    instantiate new Quality 'Thinkyness', 2
  ]
  $scope.storylets = [
    new Storylet 'The Thinker\'s Apprentice', 'You do some thinking.'
    new Storylet 'Change Location', 'Yeah, but where to go?'
    new Storylet 'A Mysterious Occurrence', 'So mysterious, you don\'t even know about it.'
  ]
  $scope.eventText = 'Event'
  $scope.choices = [
    new Choice 'Choice A', 'Requires Punchiness 7'
    new Choice 'Choice B', 'Requires Thinkyness 5'
  ]
  $scope.currentStorylet = null;
  return

qbnApp = angular.module 'qbnApp', []

class Quality
  constructor: (@name, @defaultValue) ->

instantiate = (proto) ->
  instance = Object.create proto
  for key, value of proto when /^default\w{2}/.test(key)
    [_, first, rest] = /^default(\w)(\w*)/.exec(key)
    newName = first.toLowerCase() + rest
    instance[newName] = value
  return instance

class Storylet
  constructor: (@title, @text, @choices) ->

class Choice
  constructor: (@text, @requirements, @nextStorylet) ->

qbnApp.controller 'QBN', ($scope) ->
  $scope.qualities = [
    instantiate new Quality 'Punchiness', 7
    instantiate new Quality 'Thinkyness', 2
  ]
  $scope.storylets = [
    new Storylet 'The Thinker\'s Apprentice', 'You do some thinking.'
    new Storylet 'Change Location', 'Yeah, but where to go?'
    new Storylet 'A Mysterious Occurrence', 'So mysterious, you don\'t even know about it.'
  ]
  $scope.eventText = 'Event'
  $scope.choices = [
    new Choice 'Choice A', 'Requires Punchiness 7'
    new Choice 'Choice B', 'Requires Thinkyness 5'
  ]
  $scope.currentStorylet = null;
  return

Establish a Convention for Using Reflection to Automate Instantiation