module.exports = {

  name: "Python"
  namespace: "python"
  scope: ['source.python']

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

  ###
  Supported extensions
  ###
  extensions: [
    "py"
  ]

  options:
    max_line_length:
      type: 'integer'
      default: 79
      description: "set maximum allowed line length"
    indent_size:
      type: 'integer'
      default: null
      minimum: 0
      description: "Indentation size/length"
    ignore:
      type: 'array'
      default: ["E24"]
      items:
        type: 'string'
      description: "do not fix these errors/warnings"
    formater:
      type: 'string'
      default: 'autopep8'
      enum: ['autopep8', 'yapf']
      description: "formater used by pybeautifier"
    style_config:
      type: 'string'
      default: 'pep8'
      description: "formatting style used by yapf"
    sort_imports:
      type: 'boolean'
      default: false
      description: "sort imports (requires isort installed)"
    multi_line_output:
      type: 'string'
      default: 'Hanging Grid Grouped'
      enum: [
        'Grid'
        'Vertical'
        'Hanging Indent'
        'Vertical Hanging Indent'
        'Hanging Grid'
        'Hanging Grid Grouped'
        'NOQA'
      ]
      description: "defines how from imports wrap (requires isort installed)"
}

module.exports = {

  name: "Python"
  namespace: "python"
  scope: ['source.python']

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

  ###
  Supported extensions
  ###
  extensions: [
    "py"
  ]

  options:
    max_line_length:
      type: 'integer'
      default: 79
      description: "set maximum allowed line length"
    indent_size:
      type: 'integer'
      default: null
      minimum: 0
      description: "Indentation size/length"
    ignore:
      type: 'array'
      default: ["E24"]
      items:
        type: 'string'
      description: "do not fix these errors/warnings"
    formater:
      type: 'string'
      default: 'autopep8'
      enum: ['autopep8', 'yapf']
      description: "formatter used by pybeautifier"
    style_config:
      type: 'string'
      default: 'pep8'
      description: "formatting style used by yapf"
    sort_imports:
      type: 'boolean'
      default: false
      description: "sort imports (requires isort installed)"
    multi_line_output:
      type: 'string'
      default: 'Hanging Grid Grouped'
      enum: [
        'Grid'
        'Vertical'
        'Hanging Indent'
        'Vertical Hanging Indent'
        'Hanging Grid'
        'Hanging Grid Grouped'
        'NOQA'
      ]
      description: "defines how from imports wrap (requires isort installed)"
}

Fix typo in description for Python

Fix typo in description for Python


_ = require 'underscore'
React = require 'react'
BS = require 'react-bootstrap'
TutorLink = require './link'

CourseGroupingLabel = require './course-grouping-label'

NoPeriods = React.createClass

  propTypes:
    courseId: React.PropTypes.string.isRequired
    noPanel:  React.PropTypes.bool
    link:  React.PropTypes.bool

  getDefaultProps: ->
    link: true

  getMessage: ->
    [
      You have no
      <CourseGroupingLabel
        key='no-periods-label'
        lowercase
        courseId={@props.courseId} />
       in this course.
    ]

  render: ->
    {courseId, link} = @props

    if link
      message =
        <TutorLink
          className='no-periods-text'
          to='courseSettings'
          params={{courseId}}>
          {@getMessage()}
        </TutorLink>
    else
      message =

          {@getMessage()}

    if @props.noPanel
      message
    else
      <BS.Panel>
        {message}
      </BS.Panel>

module.exports = NoPeriods

_ = require 'underscore'
React = require 'react'
BS = require 'react-bootstrap'
TutorLink = require './link'
CourseGroupingLabel = require './course-grouping-label'

NoPeriods = React.createClass

  propTypes:
    courseId: React.PropTypes.string.isRequired
    noPanel:  React.PropTypes.bool
    link:  React.PropTypes.bool

  onAddSection: ->

  render: ->

        Please add at least
        one <CourseGroupingLabel courseId={@props.courseId} lowercase /> to the course.

      <BS.Button bsStyle="primary" onClick={@onAddSection}>
        Add a <CourseGroupingLabel courseId={@props.courseId} />
      </BS.Button>

module.exports = NoPeriods

Use updated wording for help

Use updated wording for help


if Meteor.isClient
  if Package.ui
    Handlebars = Package.ui.Handlebars

  Handlebars.registerHelper 't9n', (x, prefix='') ->
    T9n.get(x, prefix)

class T9n

  @get: (x, prefix='') ->
    _get(x, prefix)

  @map: (language, map) ->
    if not i18n.map[language]
      i18n.map[language] = {}
    _extend(i18n.map[language], map)
    i18n.dep.changed()

  _get = (x, prefix='') ->
    prefix = (if prefix then prefix + '.' else '')
#    console.log "_get: " + (i18n prefix + _killDots x)
    (i18n prefix + _killDots x) || x

  _killDots = (x) ->
    x.replace(/\./g, '')

  _extend = (dest, from) ->
    props = Object.getOwnPropertyNames(from)
    props.forEach (name) ->
      if (typeof from[name]) is 'object'
        if (typeof dest[name]) is 'undefined'
          dest[name] = {}
        _extend(dest[name],from[name])
      else
        destination = Object.getOwnPropertyDescriptor(from, name)
        Object.defineProperty(dest, name, destination)

@T9n = T9n

if Meteor.isClient
  if Package.ui
    Handlebars = Package.ui.Handlebars

  Handlebars.registerHelper 't9n', (x, prefix='') ->
    T9n.get(x, prefix)

class T9n

  @get: (x, prefix='') ->
    _get(x, prefix)

  @map: (language, map) ->
    if not i18n.map[language]
      i18n.map[language] = {}
    _extend(i18n.map[language], map)
    #i18n._dep.changed()

  _get = (x, prefix='') ->
    prefix = (if prefix then prefix + '.' else '')
#    console.log "_get: " + (i18n prefix + _killDots x)
    (i18n prefix + _killDots x) || x

  _killDots = (x) ->
    x.replace(/\./g, '')

  _extend = (dest, from) ->
    props = Object.getOwnPropertyNames(from)
    props.forEach (name) ->
      if (typeof from[name]) is 'object'
        if (typeof dest[name]) is 'undefined'
          dest[name] = {}
        _extend(dest[name],from[name])
      else
        destination = Object.getOwnPropertyDescriptor(from, name)
        Object.defineProperty(dest, name, destination)

@T9n = T9n

Revert "Try to put line back in"

Revert "Try to put line back in"

This reverts commit 4ebf336378519a5b7462cbafbb7391f9d6cfba46.


util = require 'util'
path = require 'path'
express = require 'express'
{spawn} = require 'child_process'

exports.removeDirectory = (destination, callback) ->
  rm = spawn 'rm', ['-R', destination]
  rm.stderr.on 'data', (data) ->
    console.log "Error on directory removal: #{data}"
  rm.on 'exit', (-> callback?())

exports.runServer = (appPath, callback = (->)) ->
  app = express.createServer()
  app.configure ->
    app.use express.static appPath
    app.set 'views', appPath
    app.set 'view options', layout: no
    app.register '.html', require 'eco'

  app.get '/', (req, res) -> res.render 'index.html'
  app.listen 8080
  app.on 'listening', ->
    callback()
  app

exports.logJSON = (object) ->
  console.log JSON.stringify object, null, 2

util = require 'util'
path = require 'path'
express = require 'express'
{spawn} = require 'child_process'

exports.runServer = (appPath, callback = (->)) ->
  app = express.createServer()
  app.configure ->
    app.use express.static appPath
    app.set 'views', appPath
    app.set 'view options', layout: no
    app.register '.html', require 'eco'

  app.get '/', (req, res) -> res.render 'index.html'
  app.listen 8080
  app.on 'listening', ->
    callback()
  app

exports.logJSON = (object) ->
  console.log JSON.stringify object, null, 2

Remove rmdir from test helpers.

Remove rmdir from test helpers.


'menu':
  [
    {
      'label': 'Packages'
      'submenu':
        [
          'label': 'Idris'
          'submenu':
            [
              {
                'label': 'Typecheck'
                'command': 'language-idris:typecheck'
              }
            ]
        ]
    }
  ]
'context-menu':
  'atom-text-editor[data-grammar~=\"idris\"]': [
    {
      'label': 'Idris'
      'submenu': [
        {
          'label': 'Typecheck'
          'command': 'language-idris:typecheck'
        }
        {
          'label': 'Type search'
          'command': 'language-idris:type-of'
        }
        {
          'label': 'Docs for'
          'command': 'language-idris:docs-for'
        }
      ]
    }
  ]

'menu':
  [
    {
      'label': 'Packages'
      'submenu':
        [
          'label': 'Idris'
          'submenu':
            [
              {
                'label': 'Typecheck'
                'command': 'language-idris:typecheck'
              }
              {
                'label': 'Open REPL'
                'command': 'language-idris:open-repl'
              }
              {
                'label': 'Apropos'
                'command': 'language-idris:apropos'
              }
              {
                'label': 'Holes'
                'command': 'language-idris:holes'
              }
              {
                'label': 'Stop Compiler'
                'command': 'language-idris:stop-compiler'
              }
            ]
        ]
    }
  ]
'context-menu':
  'atom-text-editor[data-grammar~=\"idris\"]': [
    {
      'label': 'Idris'
      'submenu': [
        {
          'label': 'Typecheck'
          'command': 'language-idris:typecheck'
        }
        {
          'label': 'Type search'
          'command': 'language-idris:type-of'
        }
        {
          'label': 'Docs for'
          'command': 'language-idris:docs-for'
        }
      ]
    }
  ]

Add additional entries to the Idris main menu

In particular: Apropos, Open REPL, Show Holes and Stop Compiler
All these are commands which are not specific to a particular location
in the idris file being edited.


angular.module("admin.indexUtils").factory 'QueryPersistence', (localStorageService)->
  new class QueryPersistence
    storageKey: ''
    storableFilters: []

    constructor: ->
      localStorageService.setStorageType("sessionStorage")

    getStoredFilters: ->
      localStorageService.get(@storageKey) || {}

    setStoredFilters: (scope) ->
      filters = {}
      for key in @storableFilters
        filters[key] = scope[key]
      localStorageService.set(@storageKey, filters)

    restoreFilters: (scope) ->
      storedFilters = @getStoredFilters()

      if storedFilters
        for k,v of storedFilters
          scope[k] = v

        return true

      false

angular.module("admin.indexUtils").factory 'QueryPersistence', (localStorageService)->
  new class QueryPersistence
    storageKey: ''
    storableFilters: []

    constructor: ->
      localStorageService.setStorageType("sessionStorage")

    getStoredFilters: ->
      localStorageService.get(@storageKey) || {}

    setStoredFilters: (scope) ->
      filters = {}
      for key in @storableFilters
        filters[key] = scope[key]
      localStorageService.set(@storageKey, filters)

    restoreFilters: (scope) ->
      storedFilters = @getStoredFilters()

      unless _.isEmpty(storedFilters)
        for k,v of storedFilters
          scope[k] = v

        return true

      false

Improve checking for presence of saved filters


path = require 'path'
vows = require 'vows'
assert = require 'assert'
coffeelint = require path.join('..', 'lib', 'coffeelint')

vows.describe('coffeelint').addBatch({

    "CoffeeLint's version number" :

        topic : coffeelint.VERSION

        'exists' : (version) ->
            assert.isString(version)

    "CoffeeLint's errors" :

        topic : coffeelint.lint """
            a = () ->\t
                1234
            """

        'are sorted by line number' : (errors) ->
            assert.isArray(errors)
            assert.lengthOf(errors, 2)
            assert.equal(errors[1].lineNumber, 2)
            assert.equal(errors[0].lineNumber, 1)

}).export(module)

path = require 'path'
vows = require 'vows'
assert = require 'assert'
coffeelint = require path.join('..', 'lib', 'coffeelint')

vows.describe('coffeelint').addBatch({

    "CoffeeLint's version number" :

        topic : coffeelint.VERSION

        'exists' : (version) ->
            assert.isString(version)

    "CoffeeLint's errors" :

        topic : coffeelint.lint """
            a = () ->\t
                1234
            """

        'are sorted by line number' : (errors) ->
            assert.isArray(errors)
            assert.lengthOf(errors, 2)
            assert.equal(errors[1].lineNumber, 2)
            assert.equal(errors[0].lineNumber, 1)

    "Errors in the source" :

        topic : '''
            fruits = [orange, apple, banana]
            switch 'a'
             when in fruits
               something
        '''

        'are reported' : (source) ->
            assert.throws -> coffeelint.lint(source)

}).export(module)

Test to ensure compilation issues are caught.


{parallel} = require 'async'
cleaner = require '../cleaner'
{wd40, browser} = require 'wd40'

base_url = process.env.CU_TEST_URL ? 'http://localhost:3001'
login_url = "#{base_url}/login"
logout_url = "#{base_url}/logout"

before (done) ->
    console.log "[scraperwiki global before]"

    parallel [
      (cb) ->
        cleaner.clear_and_set_fixtures ->
          cb()
      (cb) ->
        wd40.init ->
          browser.get base_url, ->
            cb()
    ], done

after (done) ->
    console.log "[scraperwiki global after]"
    if process.env.BROWSER_QUIT
      console.log "Quitting browser"
      return browser.quit done
    done()

{parallel} = require 'async'
cleaner = require '../cleaner'
{wd40, browser} = require 'wd40'

base_url = process.env.CU_TEST_URL ? 'http://localhost:3001'
login_url = "#{base_url}/login"
logout_url = "#{base_url}/logout"

before (done) ->
    console.log "[scraperwiki global before]"

    parallel [
      (cb) ->
        cleaner.clear_and_set_fixtures ->
          cb()
      (cb) ->
        wd40.init (err) ->
          if err
            cb new Error("wd40 init error: #{err} -- is your Selenium server running?")
            return
          browser.get base_url, ->
            cb()
    ], done

after (done) ->
    console.log "[scraperwiki global after]"
    if process.env.BROWSER_QUIT
      console.log "Quitting browser"
      return browser.quit done
    done()

Improve behaviour if there is a selenium error

Old behaviour: tests would all try to run and fail because selenium
was broken.

New behaviour: beforeall hook fails correctly and we get just
one error telling us the problem.


{$$} = require 'space-pen'
nakedLoad 'jasmine'
nakedLoad 'jasmine-html'
nakedLoad 'jasmine-focused'

$ = require 'jquery'

document.title = "Benchmark Suite"

$('head').append $$ ->
  @link rel: "stylesheet", type: "text/css", href: "static/jasmine.css"

$('body').append $$ ->
  @div id: 'jasmine_runner'
  @div id: 'jasmine-content'

jasmineEnv = jasmine.getEnv()
trivialReporter = new jasmine.TrivialReporter(document, 'jasmine_runner')

jasmineEnv.addReporter(trivialReporter)

jasmineEnv.specFilter = (spec) -> trivialReporter.specFilter(spec)

require 'benchmark-suite'
jasmineEnv.execute()

{$$} = require 'space-pen'
nakedLoad 'jasmine'
nakedLoad 'jasmine-html'
nakedLoad 'jasmine-focused'

$ = require 'jquery'

document.title = "Benchmark Suite"

$('head').append $$ ->
  @link rel: "stylesheet", type: "text/css", href: "static/jasmine.css"

$('body').append $$ ->
  @div id: 'jasmine_runner'
  @div id: 'jasmine-content'

if atom.exitOnCompletion?
  originalFinishCallback = jasmine.Runner.prototype.finishCallback
  jasmine.Runner.prototype.finishCallback = ->
    originalFinishCallback.call(this)
    $native.exit()

jasmineEnv = jasmine.getEnv()
trivialReporter = new jasmine.TrivialReporter(document, 'jasmine_runner')

jasmineEnv.addReporter(trivialReporter)

jasmineEnv.specFilter = (spec) -> trivialReporter.specFilter(spec)

require 'benchmark-suite'
jasmineEnv.execute()

Exit app if exitAppWhenFinished is set

Exit app if exitAppWhenFinished is set


class App.Views.Users.Edit extends App.View
  template: JST['application/templates/users/edit']

  events:
    'submit form' : 'onFormSubmit'

  render: ->
    @$el.html(@template(@model.attributes))
  onFormSubmit: ->
    form = @$el.find "form"
    newAttrs = @updateModelFromForm(form)
    success = =>
      gh_handle = @model.get('github_handle')
      window.getGitHubData(gh_handle) if gh_handle
      App.router.navigate("/#{@model.get('username')}", {trigger: true});
    error = -> $('.message').html("There was an issue saving your updates")
    @model.save(newAttrs, success: success, error: error)
    false

  updateModelFromForm: (form) =>
    unindexed_array = form.serializeArray()
    formData = {}
    $.map unindexed_array, (n, i) =>
      formData[n['name']] = n['value']
    formData

class App.Views.Users.Edit extends App.View
  template: JST['application/templates/users/edit']

  events:
    'submit form' : 'onFormSubmit'

  render: ->
    @$el.html(@template(@model.attributes))
  onFormSubmit: ->
    $('#get_api').attr('disabled', true)
    $('#get_api').val('Loading...')
    form = @$el.find "form"
    newAttrs = @updateModelFromForm(form)
    success = =>
      gh_handle = @model.get('github_handle')
      window.getGitHubData(gh_handle) if gh_handle
      App.router.navigate("/#{@model.get('username')}", {trigger: true});
    error = ->
      $('#get_api').attr('disabled', false)
      $('#get_api').val('Submit')
      $('.message').html("There was an issue saving your updates")
    @model.save(newAttrs, success: success, error: error)
    false

  updateModelFromForm: (form) =>
    unindexed_array = form.serializeArray()
    formData = {}
    $.map unindexed_array, (n, i) =>
      formData[n['name']] = n['value']
    formData

Disable button and change text while loading.


class @UpdateDoneViewController extends @UpdateViewController

  localizablePageSubtitle: "update.done.update_succeeded"
  localizableNextButton: "common.restore"
  navigation:
    nextRoute: "/onboarding/management/welcome"

  navigateNext: ->
    ledger.app.setExecutionMode(ledger.app.Modes.Wallet)
    super

class @UpdateDoneViewController extends @UpdateViewController

  localizablePageSubtitle: "update.done.update_succeeded"
  localizableNextButton: "common.restore"
  navigation:
    nextRoute: "/onboarding/device/connecting"

  navigateNext: ->
    @getRequest().cancel()
    ledger.app.reconnectDongleAndEnterWalletMode().then =>
      super

Reconnect dongle at the end of FUP

Reconnect dongle at the end of FUP


do ->

	mapOptions =
		center: new (google.maps.LatLng)(43.8321591, 4.3428536)
		mapTypeId: google.maps.MapTypeId.ROADMAP
		streetViewControl: false
		scrollwheel: false
		navigationControl: false
		mapTypeControl: false
		scaleControl: false
		draggable: false
		zoomControlOptions: style: google.maps.ZoomControlStyle.SMALL
		zoom: 9

	map = new (google.maps.Map)(document.getElementById('map'), mapOptions)

	marker = new (google.maps.Marker)(
		position: map.getCenter()
		map: map
		draggable:true
		animation: google.maps.Animation.DROP
		icon:
			url: window.originalImage
	)

	Caman.remoteProxy = "proxy";
	Caman "#uploaded-marker", () ->
		@brightness(5).render()		@colorize("#ff0000", 80)
		@render();

do ->

	mapOptions =
		center: new (google.maps.LatLng)(43.8321591, 4.3428536)
		mapTypeId: google.maps.MapTypeId.ROADMAP
		streetViewControl: false
		scrollwheel: false
		navigationControl: false
		mapTypeControl: false
		scaleControl: false
		draggable: false
		zoomControlOptions: style: google.maps.ZoomControlStyle.SMALL
		zoom: 9

	map = new (google.maps.Map)(document.getElementById('map'), mapOptions)

	marker = new (google.maps.Marker)(
		position: map.getCenter()
		map: map
		draggable:true
		animation: google.maps.Animation.DROP
		icon:
			url: window.originalImage
	)

	Caman.remoteProxy = "proxy";
	Caman "#uploaded-marker", () ->
		@colorize("#ff0000", 80)
		@render();

	Caman.Event.listen "processStart", () ->
		console.log 'started'

	Caman.Event.listen "processStart", () ->
		console.log "ended"

Add Caman process event listeners

Add Caman process event listeners


orm = require('orm')

module.exports = (db, callback) ->
	db.define("user", {
		id_str: String,
		screen_name: String,
		name: String,
		profile_image_url: String,
		location: String,
		favourites_count: Number,
		url: String,
		lang: String,
		statuses_count: Number,
		friends_count: Number,
		followers_count: Number
	}, {
		validations: {
			screen_name: orm.validators.unique('screen_name not unique')
		}
	})

	return callback()

orm = require('orm')

module.exports = (db, callback) ->
	db.define("user", {
		id_str: String,
		screen_name: String,
		name: String,
		description: String,
		created_at: String,
		location: String,
		profile_image_url: String,
		profile_image_url_https: String,
		url: String,
		listed_count: Number,
		favourites_count: Number,
		followers_count: Number,
		statuses_count: Number,
		friends_count: Number
	}, {
		validations: {
			screen_name: orm.validators.unique('screen_name not unique')
		}
	})

	return callback()

Adjust user model to twitterbot

Adjust user model to twitterbot


process = () =>
  if @JasmineReporter.finished
    results = @JasmineReporter.results()
    json = JSON.stringify(results)

    # attach it to the DOM, so the it can be retrieved by webdriver
    element = @document.createElement("div")
    text = @document.createTextNode(json)
    element.appendChild text
    element.id = "jasmine-results"
    @document.body.appendChild element
  else
    setTimeout process, 100

try
  @jasmine.getEnv().execute()
finally
  process()

process = () =>
  if @JasmineReporter.finished
    results = @JasmineReporter.results()
    results = (v for k,v of results)
    json = JSON.stringify(results)

    # attach it to the DOM, so the it can be retrieved by webdriver
    element = @document.createElement("div")
    text = @document.createTextNode(json)
    element.appendChild text
    element.id = "jasmine-results"
    @document.body.appendChild element
  else
    setTimeout process, 100

try
  @jasmine.getEnv().execute()
finally
  process()

Convert weird object notation to proper arrays


---
---
pow2 = (exp) ->
	base = 1
	base *= 2 while exp-- > 0
	base

update = ->
	val = 0
	for el in document.querySelectorAll '.flag-list input'
		val += pow2 el.getAttribute 'value' if el.checked
	document.querySelector('.page-heading strong').innerHTML = val
	return

document.addEventListener 'DOMContentLoaded', ->
	for el in document.querySelectorAll '.flag-list input'
		el.addEventListener 'click', update
	return

update()

---
---
pow2 = (exp) ->
	base = 1
	base *= 2 while exp-- > 0
	base

recalculate = ->
	val = 0
	for el in document.querySelectorAll '.flag-list input'
		val += pow2 el.getAttribute 'value' if el.checked
	document.querySelector('.page-heading strong').innerHTML = val
	return

document.addEventListener 'DOMContentLoaded', ->
	for el in document.querySelectorAll '.flag-list input'
		el.addEventListener 'click', recalculate
	return

recalculate()

Rename flag value recalculating function


Store = require './store'

EXAMPLE_WORKFLOW =
  id: 'GZ_MAIN_WORKFLOW'
  firstTask: 'interest'
  tasks:
    interest:
      type: 'drawing'
      question: 'Color some points'
      tools: [
        {value: 'red', label: 'Red', type: 'point', color: 'red'}
        {value: 'green', label: 'Green', type: 'point', color: 'lime'}
        {value: 'blue', label: 'Blue', type: 'point', color: 'blue'}
      ]
      required: false
      next: 'shape'

    shape:
      type: 'multiple'
      question: 'What shape is this galaxy?'
      answers: [
        {value: 'smooth', label: 'Smooth'}
        {value: 'features', label: 'Features'}
        {value: 'other', label: 'Star or artifact (done!)'}
      ]
      next: 'roundness'

    roundness:
      type: 'single'
      question: 'How round is it?'
      answers: [
        {value: 'very', label: 'Very', next: 'shape'}
        {value: 'sorta', label: 'In between'}
        {value: 'not', label: 'Cigar shaped'}
      ]
      next: null

module.exports = window.workflowStore = new Store
  root: '/workflows'
  examples: [EXAMPLE_WORKFLOW]

Store = require './store'

EXAMPLE_WORKFLOW =
  id: 'GZ_MAIN_WORKFLOW'
  firstTask: 'interest'
  tasks:
    interest:
      type: 'drawing'
      question: 'Color some points'
      tools: [
        {value: 'red', label: 'Red', type: 'point', color: 'red'}
        {value: 'green', label: 'Green', type: 'point', color: 'lime'}
        {value: 'blue', label: 'Blue', type: 'point', color: 'blue'}
      ]
      required: false
      next: 'shape'

    shape:
      type: 'multiple'
      question: 'What shape is this galaxy?'
      answers: [
        {value: 'smooth', label: 'Smooth'}
        {value: 'features', label: 'Features'}
        {value: 'other', label: 'Star or artifact'}
      ]
      next: 'roundness'

    roundness:
      type: 'single'
      question: 'How round is it?'
      answers: [
        {value: 'very', label: 'Very...', next: 'shape'}
        {value: 'sorta', label: 'In between'}
        {value: 'not', label: 'Cigar shaped'}
      ]
      next: null

module.exports = window.workflowStore = new Store
  root: '/workflows'
  examples: [EXAMPLE_WORKFLOW]

Fix text in demo workflow

Fix text in demo workflow


define ['controllers/main_controller', 'jquery', 'websocket_rails', 'underscore', 'backbone', 'marionette'], (MainController, $) ->
  # Create the single page application
  App = new Backbone.Marionette.Application();

  App.addInitializer ->
    # Get websocket connection info
    connection_params = {}
    $.get('websocket/', (data) ->
      connection_params = JSON.parse(data)
      # connect to server like normal
      dispatcher = new WebSocketRails("#{connection_params.host}:#{connection_params.port}/websocket")
      # subscribe to the channel
      channel = dispatcher.subscribe('my_game');
      channel.bind 'server_msg', (data) ->
        console.log data
    ).fail ->
      console.error 'No websocket server available'

# Prevent default clicks on links for a pushState ready app
  App.addInitializer () ->
    $(document).on 'click', 'a:not([data-bypass])', (evt) ->
      href = $(@).attr('href')
      protocol = @.protocol + '//'
      if (href && href.slice(protocol.length) != protocol)
        evt.preventDefault()
        Backbone.history.navigate(href, true)

# Create main router and start history
  App.addInitializer (options) ->
    mainController = new MainController options
    new Marionette.AppRouter(
      controller: mainController
      appRoutes:
        new: 'newGame'
    )
    Backbone.history.start({pushState: true}) if Backbone.history

  return App

define ['controllers/main_controller', 'jquery', 'websocket_rails', 'underscore', 'backbone', 'marionette'], (MainController, $) ->
  # Create the single page application
  App = new Backbone.Marionette.Application();

  App.addInitializer ->
    # Get websocket connection info
    connection_params = {}
    $.get('ws/', (connection_params) ->
      # connect to server like normal
      dispatcher = new WebSocketRails "#{connection_params.host}:#{connection_params.port}/websocket"
      # subscribe to the channel
      channel = dispatcher.subscribe 'my_game'
      channel.bind 'server_msg', (data) ->
        console.log data
    ).fail ->
      console.error 'No websocket server available'

# Prevent default clicks on links for a pushState ready app
  App.addInitializer () ->
    $(document).on 'click', 'a:not([data-bypass])', (evt) ->
      href = $(@).attr('href')
      protocol = @.protocol + '//'
      if (href && href.slice(protocol.length) != protocol)
        evt.preventDefault()
        Backbone.history.navigate(href, true)

# Create main router and start history
  App.addInitializer (options) ->
    mainController = new MainController options
    new Marionette.AppRouter(
      controller: mainController
      appRoutes:
        new: 'newGame'
    )
    Backbone.history.start({pushState: true}) if Backbone.history

  return App

[Front] Fix controller name for the ws connection params


Readline = require 'readline'

Robot    = require '../robot'
Adapter  = require '../adapter'

class Shell extends Adapter
  constructor: (robot) ->
    super robot
    @repl = null

  send: (user, strings...) ->
    console.log str for str in strings
    @repl.prompt()

  reply: (user, strings...) ->
    @send user, strings...

  run: ->
    stdin = process.openStdin()
    stdout = process.stdout

    process.on "uncaughtException", (err) =>
      @robot.logger.error "#{err}"

    if Readline.createInterface.length > 3
      @repl = Readline.createInterface stdin, null

      stdin.on "data", (buffer) =>
        @repl.write buffer
    else
      @repl = Readline.createInterface stdin, stdout, null

    @repl.on "attemptClose", =>
      @repl.close()

    @repl.on "close", =>
      process.stdout.write "\n"
      stdin.destroy()
      process.exit 0

    @repl.on "line", (buffer) =>
      user = @userForId '1', name: "Shell"
      @receive new Robot.TextMessage user, buffer
      @repl.prompt()

    @repl.setPrompt "#{@robot.name}> "
    @repl.prompt()

exports.use = (robot) ->
  new Shell robot

Readline = require 'readline'

Robot    = require '../robot'
Adapter  = require '../adapter'

class Shell extends Adapter
  send: (user, strings...) ->
    unless process.platform is "win32"
      console.log "\033[01;32m#{str}\033[0m" for str in strings
    else
      console.log "#{str}" for str in strings
    @repl.prompt()

  reply: (user, strings...) ->
    @send user, strings...

  run: ->
    stdin = process.openStdin()
    stdout = process.stdout

    process.on "uncaughtException", (err) =>
      @robot.logger.error "#{err}"

    @repl = Readline.createInterface stdin, stdout, null

    @repl.on "close", =>
      stdin.destroy()
      process.exit 0

    @repl.on "line", (buffer) =>
      @repl.close() if buffer.toLowerCase() is "exit"
      @repl.prompt()
      user = @userForId '1', name: "Shell"
      @receive new Robot.TextMessage user, buffer

    @repl.setPrompt "#{@robot.name}> "
    @repl.prompt()

exports.use = (robot) ->
  new Shell robot

Update repl prompt to work with async requests


module.exports = FLAGS =
    oF: '-Ofast'
    oS: '-Os'
    o3: ['-O3', '-march=native']
    o2: '-O2'
    o1: '-O1'
    o0: '-O0'

    g: '-g'
    pg: '-pg'
    s: '-static'
    ni: '-fno-inline'

    pca: ['-O3', '-march=native', '-mfpmath=sse', '-ffloat-store', '-Wl,-s,-O1', '-lm']

ALIAS =
    0: 'o0'
    1: 'o1'
    2: 'o2'
    3: 'o3'
    f: 'oF'

    noinline: 'ni'
    ninline: 'ni'

    static: 's'
    st: 's'

FLAGS[alias] = FLAGS[flag] for alias, flag of ALIAS

module.exports = FLAGS =
    oF: ['-Ofast', '-march=native']
    oS: '-Os'
    o3: ['-O3', '-march=native']
    o2: '-O2'
    o1: '-O1'
    o0: '-O0'

    g: '-g'
    pg: '-pg'
    s: '-static'
    ni: '-fno-inline'

    pca: ['-O3', '-march=native', '-mfpmath=sse', '-ffloat-store', '-Wl,-s,-O1', '-lm']

ALIAS =
    0: 'o0'
    1: 'o1'
    2: 'o2'
    3: 'o3'
    f: 'oF'

    noinline: 'ni'
    ninline: 'ni'

    static: 's'
    st: 's'

FLAGS[alias] = FLAGS[flag] for alias, flag of ALIAS

Add march=native flag to OFast

Add march=native flag to OFast


ETahi.QuestionComponent = Ember.Component.extend
  tagName: 'div'
  helpText: null
  displayContent: false

  model: (->
    ident = @get('ident')
    throw "you must specify an ident" unless ident

    question = @get('task.questions').findProperty('ident', ident)

    unless question
      task = @get('task')
      question = task.get('store').createRecord 'question',
        question: @get('question')
        ident: ident
        task: task
        additionalData: [{}]

      task.get('questions').pushObject(question)

    question

  ).property('task', 'ident')

  additionalData: Em.computed.alias('model.additionalData')

  change: ->
    Ember.run.debounce(this, this._saveModel, 500)

  _saveModel: ->
    @get('model').save()

ETahi.QuestionComponent = Ember.Component.extend
  tagName: 'div'
  helpText: null
  displayContent: false

  model: (->
    ident = @get('ident')
    throw "you must specify an ident" unless ident

    question = @get('task.questions').findProperty('ident', ident)

    unless question
      task = @get('task')
      question = task.get('store').createRecord 'question',
        question: @get('question')
        ident: ident
        task: task
        additionalData: [{}]

      task.get('questions').pushObject(question)

    question

  ).property('task', 'ident')

  additionalData: Em.computed.alias('model.additionalData')

  change: ->
    Ember.run.debounce(this, this._saveModel, @get('model'), 500)

  _saveModel: (model) ->
    model.save()

Fix question debounce bug that john already fixed.


"*":
  autoprefixer: {}
  core:
    themes: [
      "one-dark-ui"
      "cobalt2-syntax"
    ]
  editor:
    fontFamily: "Menlo"
    fontSize: 15
    invisibles:
      cr: " "
      eol: " "
      tab: "⇨"
    preferredLineLength: 120
    showInvisibles: true
    softWrap: true
    softWrapAtPreferredLineLength: true
  emmet: {}
  "exception-reporting":
    userId: "0f4e16a3-5040-01ba-b000-a0793a69fb44"
  "highlight-line":
    enableBackgroundColor: false
  jshint:
    supportLintingJsx: true
  "markdown-preview":
    useGitHubStyle: true
  minimap:
    plugins:
      selection: true
      selectionDecorationsZIndex: 0
  "minimap-selection":
    highlightCursorsLines: true
  "one-dark-ui": {}
  "one-light-ui": {}
  pigments: {}
  react: {}
  "tree-view":
    hideVcsIgnoredFiles: false
  welcome:
    showOnStartup: false
".angular.html.text":
  editor:
    preferredLineLength: 120
".git-commit.text":
  editor:
    preferredLineLength: 120
".git-config.source":
  editor:
    preferredLineLength: 120
".git-rebase.text":
  editor:
    preferredLineLength: 120
".js.jsx.react.source":
  editor:
    showInvisibles: true

"*":
  autoprefixer: {}
  core:
    themes: [
      "one-dark-ui"
      "cobalt2-syntax"
    ]
  editor:
    fontFamily: "Menlo"
    fontSize: 15
    invisibles:
      cr: " "
      eol: " "
      tab: "⇨"
    preferredLineLength: 120
    showInvisibles: true
    softWrap: true
    softWrapAtPreferredLineLength: true
  emmet: {}
  "exception-reporting":
    userId: "0f4e16a3-5040-01ba-b000-a0793a69fb44"
  "highlight-line":
    enableBackgroundColor: false
  jshint:
    supportLintingJsx: true
  linter:
    showErrorPanel: false
  "markdown-preview":
    useGitHubStyle: true
  minimap:
    plugins:
      selection: true
      selectionDecorationsZIndex: 0
  "minimap-selection":
    highlightCursorsLines: true
  "one-dark-ui": {}
  "one-light-ui": {}
  pigments: {}
  react: {}
  "tree-view":
    hideVcsIgnoredFiles: false
  welcome:
    showOnStartup: false
".angular.html.text":
  editor:
    preferredLineLength: 120
".git-commit.text":
  editor:
    preferredLineLength: 120
".git-config.source":
  editor:
    preferredLineLength: 120
".git-rebase.text":
  editor:
    preferredLineLength: 120
".js.jsx.react.source":
  editor:
    showInvisibles: true

Hide error panel for linter

atom: Hide error panel for linter


fs = require "fs"
path = require "path"
Coffee = require "coffee-script"
Q = require "q"
Jsdom = require "jsdom"

MARKBACK_FILE = path.join(__dirname, '../build/markback.js')

readMarkback = Q.nfcall(fs.readFile, MARKBACK_FILE).then (file) ->
  Q.fcall -> file.toString('utf-8')

convertHtml = (html) ->
  readMarkback.then (markback) ->
    deferred = Q.defer()
    Jsdom.env {
      src: markback
      html
      done: deferred.makeNodeResolver()
    }
    deferred.promise
  .then (window) ->
     Q.fcall -> window.Markback()

module.exports = {convertHtml}

fs = require "fs"
path = require "path"
Coffee = require "coffee-script"
Q = require "q"
Jsdom = require "jsdom"

MARKBACK_FILE = path.join(__dirname, '../build/markback.js')

readMarkback = Q.nfcall(fs.readFile, MARKBACK_FILE).then (file) ->
  Q.fcall -> file.toString('utf-8')

convertHtml = (html, callback = ->) ->
  readMarkback.then (markback) ->
    deferred = Q.defer()
    Jsdom.env {
      src: markback
      html
      done: deferred.makeNodeResolver()
    }
    deferred.promise
  .then (window) ->
     markdown = window.Markback()
     callback null, markdown
     Q.fcall -> markdown
  .fail (err) ->
    callback err
    throw err

module.exports = {convertHtml}

Handle callbacks as well as promises

Handle callbacks as well as promises


_  = require('lodash')
browsers = require('../browsers')

remoteReporters = ['dots']
remoteReporters.push('saucelabs') if process.env.TRAVIS_BRANCH == 'master'

remoteKarma = _.reduce(browsers, (memo, config, browser) ->
  memo[browser] =
    browsers: [browser]
    browserDisconnectTimeout: 10000
    browserDisconnectTolerance: 2
    browserNoActivityTimeout: 60000
    reporters: remoteReporters
  return memo
, {})

module.exports = (grunt) ->
  grunt.config('karma', _.extend(remoteKarma,
    options:
      configFile: 'config/karma.js'
      files: [
        'node_modules/jquery/dist/jquery.js'
        "http://localhost:#{grunt.config('port')}/test/quill.js"

        'test/fixtures/unit.html'
        'test/helpers/inject.coffee'
        'test/helpers/matchers.coffee'

        { pattern: 'test/fixtures/*.css', included: false }

        # We dont do **/*.coffee to control order of tests
        'test/unit/lib/*.coffee'
        'test/unit/core/*.coffee'
        'test/unit/modules/*.coffee'
        'test/unit/themes/*.coffee'
      ]
    coverage:
      browserNoActivityTimeout: 30000
      browsers: ['PhantomJS']
      reporters: ['coverage']
    local:
      browsers: ['Chrome', 'Firefox', 'Safari']
    server:
      autoWatch: true
      browsers: []
      singleRun: false
    test:
      browsers: ['PhantomJS']
  ))

_  = require('lodash')
browsers = require('../browsers')

remoteReporters = ['dots']
remoteReporters.push('saucelabs') if process.env.TRAVIS_BRANCH == 'master'

remoteKarma = _.reduce(browsers, (memo, config, browser) ->
  memo[browser] =
    browsers: [browser]
    browserDisconnectTimeout: 10000
    browserDisconnectTolerance: 2
    browserNoActivityTimeout: 60000
    reporters: remoteReporters
  return memo
, {})

module.exports = (grunt) ->
  grunt.config('karma', _.extend(remoteKarma,
    options:
      configFile: 'config/karma.js'
      files: [
        'node_modules/jquery/dist/jquery.js'
        "http://localhost:#{grunt.config('port')}/test/quill.js"

        'test/fixtures/unit.html'
        'test/helpers/inject.coffee'
        'test/helpers/matchers.coffee'

        { pattern: 'test/fixtures/*.css', included: false }

        # We dont do **/*.coffee to control order of tests
        'test/unit/lib/*.coffee'
        'test/unit/core/*.coffee'
        'test/unit/modules/*.coffee'
        'test/unit/themes/*.coffee'
      ]
    coverage:
      browserNoActivityTimeout: 30000
      browsers: ['PhantomJS']
      reporters: ['coverage']
    local:
      browsers: ['Chrome', 'Firefox', 'Safari']
    server:
      autoWatch: true
      browsers: []
      singleRun: false
    test:
      browsers: ['Chrome']
  ))

Switch default browser to Chrome until Phantom 2.0

Current version does not have function.bind


# noop.pw
moment = require 'moment'
{renderVita} = require './demi'
global.vita = renderVita './riptide/vita.yaml'
global._now = moment()
global.DEBUG = '-p' not in global.process.argv

module.exports = config:
  paths:
    watched: ['riptide']

  plugins:
    autoReload:
      enabled: yes
    coffeelint:
      pattern: /^riptide\/.*\.(coffee)$/
      useCoffeelintJson: yes
    jaded:
      staticPatterns: /^riptide\/markup\/([\d\w]*)\.jade$/
      globals: ['DEBUG', 'vita', '_now']
    closurecompiler:
      compilationLevel: 'SIMPLE'
    typeset:
      tweaks: disable: ['ligatures']
    stylus:
      plugins: [
        'jeet'
        'axis'
      ]

  conventions:
    ignored: [
      /node_modules\/jade/
      /(templates|partials)\/.+\.jade$/
    ]

  hooks:
    onCompile: ->
      global.vita = renderVita './riptide/vita.yaml'

  npm:
    enabled: yes

  modules:
    nameCleaner: (path) ->
      path
        .replace /^riptide\//, ''
        .replace /\.coffee/, ''

  files:
    javascripts:
      joinTo: 'js/app.js'
    stylesheets:
      joinTo: 'css/app.css'

  server:
    command: "node_modules/.bin/http-server -c-1 -p #{process.env.PORT or 8080}"

# noop.pw
moment = require 'moment'
{renderVita} = require './demi'
global.vita = renderVita './riptide/vita.yaml'
global._now = moment()
global.DEBUG = '-p' not in global.process.argv

module.exports = config:
  paths:
    watched: ['riptide']

  plugins:
    autoReload:
      enabled: yes
    coffeelint:
      pattern: /^riptide\/.*\.(coffee)$/
      useCoffeelintJson: yes
    jaded:
      staticPatterns: /^riptide\/markup\/([\d\w]*)\.jade$/
      globals: ['DEBUG', 'vita', '_now']
    closurecompiler:
      compilationLevel: 'SIMPLE'
    typeset:
      tweaks: disable: ['ligatures']
    stylus:
      plugins: [
        'jeet'
        'axis'
      ]

  conventions:
    ignored: [
      /node_modules\/jade/
      /(templates|partials)\/.+\.jade$/
    ]

  hooks:
    onCompile: ->
      global.vita = renderVita './riptide/vita.yaml'

  npm:
    enabled: yes
    styles:
      'normalize.css': ['normalize.css']

  modules:
    nameCleaner: (path) ->
      path
        .replace /^riptide\//, ''
        .replace /\.coffee/, ''

  files:
    javascripts:
      joinTo: 'js/app.js'
    stylesheets:
      joinTo: 'css/app.css'

  server:
    command: "node_modules/.bin/http-server -c-1 -p #{process.env.PORT or 8080}"

Update brunch to also include normalize.css in build


window.keymap.bindKeys '*'
  'meta-:': 'command-panel:toggle'

window.keymap.bindKeys '.command-panel .editor',
  escape: 'command-panel:toggle'
  enter: 'command-panel:execute'

window.keymap.bindKeys '.editor',
  'meta-g': 'command-panel:repeat-relative-address'
  'meta-e': 'command-panel:set-selection-as-regex-address'
  'meta-f': 'command-panel:find-in-file'

window.keymap.bindKeys '*'
  'meta-:': 'command-panel:toggle'

window.keymap.bindKeys '.command-panel .editor',
  escape: 'command-panel:toggle'
  enter: 'command-panel:execute'

window.keymap.bindKeys '.editor',
  'meta-g': 'command-panel:repeat-relative-address'
  'meta-G': 'command-panel:repeat-relative-address-in-reverse'
  'meta-e': 'command-panel:set-selection-as-regex-address'
  'meta-f': 'command-panel:find-in-file'

Implement meta-G (redo search in reverse)


Buffer = require 'buffer'
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 in an empty file", ->
    editor.insertText('x')
    editor.backspace()

  describe "when editing a ~300 line CoffeeScript file", ->
    beforeEach ->
      editor.setBuffer new Buffer(require.resolve('fixtures/medium.coffee'))

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

    benchmark "inserting and deleting a character that causes massive token changed", ->
      editor.insertText('"')
      editor.backspace()

Buffer = require 'buffer'
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 in an empty file", ->
    editor.insertText('x')
    editor.backspace()

  describe "when editing a ~300 line CoffeeScript file", ->
    beforeEach ->
      editor.setBuffer new Buffer(require.resolve('fixtures/medium.coffee'))

    describe "when the cursor is at the beginning of the file", ->
      benchmark "inserting and deleting a character at the beginning of the file", ->
        editor.insertText('x')
        editor.backspace()

      benchmark "inserting and deleting a character that causes massive re-highlighting", ->
        editor.insertText('"')
        editor.backspace()

    describe "when the cursor is at the end of the file", ->
      beforeEach ->
        editor.setCursorScreenPosition([Infinity, Infinity])

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

Add benchmark for inserting at the end of a file


$('.rating').raty
  path: '/assets'
  score: ->
    $(this).attr('data-score')
  click: (score, _) ->
    $.ajax
      type: 'post',
      url: $(this).attr('data-uri'),
      data:
        picture:
          score: score
    $(this).find('img').unbind('click')

$('.rating').raty
  path: '/assets'
  score: ->
    $(this).attr('data-score')
  click: (score, _) ->
    self = $(this)
    url  = self.attr('data-uri')

    self.find('img').unbind('click')

    $.ajax
      type: 'post'
      url: url
      data:
        picture:
          score: score
      success: ->
        $.ajax
          type: 'get'
          url: url
          success: (data) ->
            self.raty('score', data)
            self.raty('readOnly', true)

Update score and deactivate rating properly.


_ = require 'underscore-plus'
RubyMotionAutocompleteView = require './rubymotion-autocomplete-view'

module.exports =
  autocompleteViews: []
  editorSubscription: null
  snippetPrefixes: []

  activate: (state) ->
    @editorSubscription = atom.workspaceView.eachEditorView (editor) =>
      if editor.attached and not editor.mini
        autocompleteView = new RubyMotionAutocompleteView(editor)
        autocompleteView.snippetPrefixes = @snippetPrefixes
        editor.on 'editor:will-be-removed', =>
          autocompleteView.remove() unless autocompleteView.hasParent()
          _.remove(@autocompleteViews, autocompleteView)
        @autocompleteViews.push(autocompleteView)

    atom.packages.on 'snippets:loaded', =>
      @snippetPrefixes = @collectSnippets()
      @autocompleteViews.forEach (v) => v.snippetPrefixes = @snippetPrefixes

  collectSnippets: ->
    snippets = atom.syntax.propertiesForScope([".source.rubymotion"], "snippets")
    keys = []
    for item in snippets
      keys.push _.keys(item.snippets)
    _.uniq(_.flatten(keys)).sort (word1, word2) ->
      word1.toLowerCase().localeCompare(word2.toLowerCase())

  deactivate: ->
    @editorSubscription?.off()
    @editorSubscription = null
    @autocompleteViews.forEach (autocompleteView) -> autocompleteView.remove()
    @autocompleteViews = []

  serialize: ->

_ = require 'underscore-plus'
RubyMotionAutocompleteView = require './rubymotion-autocomplete-view'
Snippet = require(atom.packages.resolvePackagePath('snippets') + '/lib/snippet')
Snippets = require(atom.packages.resolvePackagePath('snippets') + '/lib/snippets')

module.exports =
  autocompleteViews: []
  editorSubscription: null
  snippetPrefixes: []

  activate: (state) ->
    @editorSubscription = atom.workspaceView.eachEditorView (editor) =>
      if editor.attached and not editor.mini
        autocompleteView = new RubyMotionAutocompleteView(editor)
        autocompleteView.snippetPrefixes = @snippetPrefixes
        editor.on 'editor:will-be-removed', =>
          autocompleteView.remove() unless autocompleteView.hasParent()
          _.remove(@autocompleteViews, autocompleteView)
        @autocompleteViews.push(autocompleteView)

    @collectSnippets (prefixes) =>
      @snippetPrefixes = prefixes
      @autocompleteViews.forEach (v) => v.snippetPrefixes = @snippetPrefixes

  collectSnippets: (callback) ->
    path = atom.packages.getActivePackage('RubyMotion').path + '/snippets/cocoatouch'
    Snippets.loadSnippetsDirectory path, ->
      snippets = atom.syntax.propertiesForScope([".source.rubymotion"], "snippets")
      keys = []
      for item in snippets
        keys.push _.keys(item.snippets)
      keys = _.uniq(_.flatten(keys)).sort (word1, word2) ->
        word1.toLowerCase().localeCompare(word2.toLowerCase())
      callback keys

  deactivate: ->
    @editorSubscription?.off()
    @editorSubscription = null
    @autocompleteViews.forEach (autocompleteView) -> autocompleteView.remove()
    @autocompleteViews = []

  serialize: ->

Load snippets for cocoatouch after loading


describe "tabs", ->
  beforeEach ->
    # run all Blacklight.onload functions
    Blacklight.activate()

  describe "dashboard tabs", ->
    beforeEach ->
      # setup the tabs like the homepage featured and recent tabs
      setFixtures '

Featured Works

Recently Uploaded

'

    describe "tabNavigation", ->

      it "It sets the first tab to active", ->
        expect($('#homeTabs li').attr('class')).toBeUndefined()
        tabNavigation();
        expect($('#homeTabs li').attr('class')).toBe('active')

  describe "dashboard tabs", ->
    beforeEach ->
      # setup the tabs like the my listing on the dashboards
      setFixtures '

                     You are currently listing your works .  You have 1 works

                       My Works

                       My Collections

'

    describe "tabNavigation", ->

      it "It does not error", ->
        tabNavigation();

describe "tabs", ->
  beforeEach ->
    # run all Blacklight.onload functions
    Blacklight.activate()

  describe "dashboard tabs", ->
    beforeEach ->
      # setup the tabs like the homepage featured and recent tabs
      setFixtures '

Featured Works

Recently Uploaded

'

    describe "tabNavigation", ->

      it "It sets the first tab to active", ->
        expect($('#homeTabs a:first').attr('class')).toBeUndefined()
        tabNavigation();
        expect($('#homeTabs a:first').attr('class')).toBe('active')

  describe "dashboard tabs", ->
    beforeEach ->
      # setup the tabs like the my listing on the dashboards
      setFixtures '

                     You are currently listing your works .  You have 1 works

                       My Works

                       My Collections

'

    describe "tabNavigation", ->

      it "It does not error", ->
        tabNavigation();

Fix selector in tabs spec

Fix selector in tabs spec


Point = require 'point'
ChildProcess = require 'child-process'

module.exports =
class TagGenerator

  constructor: (@path, @callback) ->

  parsePrefix: (section = "") ->
    if section.indexOf('class:') is 0
      section.substring(6)
    else if section.indexOf('namespace:') is 0
      section.substring(10)
    else if section.indexOf('file:') is 0
      section.substring(5)
    else if section.indexOf('signature:') is 0
      section.substring(10)
    else
      section

  parseTagLine: (line) ->
    sections = line.split('\t')
    return null if sections.length < 4

    label = sections[0]
    line = parseInt(sections[2]) - 1
    if sections.length > 5
      if prefix = @parsePrefix(sections[4])
        label = "#{prefix}::#{label}"
      if suffix = @parsePrefix(sections[5])
        label = "#{label}#{suffix}"
    else
      if suffix = @parsePrefix(sections[4])
        label = "#{label}#{suffix}"

    tag =
      position: new Point(line, 0)
      name: label

    return tag

  generate: ->
    options =
      bufferLines: true
      stdout: (data) =>
        lines = data.split('\n')
        for line in lines
          tag = @parseTagLine(line)
          @callback(tag) if tag
    command = "#{require.resolve('ctags')} --fields=+KS -nf - #{@path}"
    ChildProcess.exec(command, options)

Point = require 'point'
ChildProcess = require 'child-process'

module.exports =
class TagGenerator

  constructor: (@path, @callback) ->

  parsePrefix: (section = "") ->
    if section.indexOf('class:') is 0
      section.substring(6)
    else if section.indexOf('namespace:') is 0
      section.substring(10)
    else if section.indexOf('file:') is 0
      section.substring(5)
    else if section.indexOf('signature:') is 0
      section.substring(10)
    else if section.indexOf('struct:') is 0
      section.substring(7)
    else
      section

  parseTagLine: (line) ->
    sections = line.split('\t')
    return null if sections.length < 4

    label = sections[0]
    line = parseInt(sections[2]) - 1
    if sections.length > 5
      if prefix = @parsePrefix(sections[4])
        label = "#{prefix}::#{label}"
      if suffix = @parsePrefix(sections[5])
        label = "#{label}#{suffix}"
    else
      if suffix = @parsePrefix(sections[4])
        label = "#{label}#{suffix}"

    tag =
      position: new Point(line, 0)
      name: label

    return tag

  generate: ->
    options =
      bufferLines: true
      stdout: (data) =>
        lines = data.split('\n')
        for line in lines
          tag = @parseTagLine(line)
          @callback(tag) if tag
    command = "#{require.resolve('ctags')} --fields=+KS -nf - #{@path}"
    ChildProcess.exec(command, options)

Trim struct: prefix in display output

Trim struct: prefix in display output


class @Calendar
  options =
    month: "short"
    day: "numeric"
    year: "numeric"

  constructor: (timestamps, starting_year, starting_month, calendar_activities_path) ->
    cal = new CalHeatMap()
    cal.init
      itemName: ["contribution"]
      data: timestamps
      start: new Date(starting_year, starting_month)
      domainLabelFormat: "%b"
      id: "cal-heatmap"
      domain: "month"
      subDomain: "day"
      range: 12
      tooltip: true
      label:
        position: "top"
      legend: [
        0
        10
        20
        30
      ]
      legendCellPadding: 3
      onClick: (date, count) ->
        formated_date = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate()
        $.ajax
          url: calendar_activities_path
          data:
            date: formated_date
          cache: false
          dataType: "html"
          success: (data) ->
            $(".user-calendar-activities").html data

class @Calendar
  options =
    month: "short"
    day: "numeric"
    year: "numeric"

  constructor: (timestamps, starting_year, starting_month, calendar_activities_path) ->
    cal = new CalHeatMap()
    cal.init
      itemName: ["contribution"]
      data: timestamps
      start: new Date(starting_year, starting_month)
      domainLabelFormat: "%b"
      id: "cal-heatmap"
      domain: "month"
      subDomain: "day"
      range: 12
      tooltip: true
      label:
        position: "top"
      legend: [
        0
        10
        20
        30
      ]
      legendCellPadding: 3
      cellSize: $('.user-calendar').width() / 80
      onClick: (date, count) ->
        formated_date = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate()
        $.ajax
          url: calendar_activities_path
          data:
            date: formated_date
          cache: false
          dataType: "html"
          success: (data) ->
            $(".user-calendar-activities").html data

Make commit calendar size depend on window size

Signed-off-by: Dmitriy Zaporozhets <be23d75b156792e5acab51b196a2deb155d35d6a@gmail.com>


Q = require('q')
mongoose = require('mongoose')

log = require("#{__dirname}/../../log")

retrieve = require("#{__dirname}/../../retrieve")

Schema = mongoose.Schema
ObjectId = Schema.Types.ObjectId

BookSchema = new Schema
  isbn:
    type: String
    index:
      unique: true
    required: true
    trim: true
  title:
    type: String
    trim: true
  author:
    type: String
    trim: true
  prices: [{
    value: Number
    date: Date
  }]
  amazon:
    url: String
    image: String

BookSchema.statics.findByIsbn = (isbn) ->
  @findOne(isbn: isbn).exec()

BookSchema.statics.fetchFromAmazon = (book) ->
  retrieve.fetchFromAmazon(book.isbn)
  .then ({title, value, author, url, image}) =>
    log.verbose "received #{book.isbn}, now updating as #{title}"
    @findOneAndUpdate {isbn: book.isbn},
      title: title
      author: author
      amazon:
        url: url
        image: image
      $push:
        prices:
          value: value
          date: Date.now()
    .exec()

module.exports = mongoose.model('Book', BookSchema)

Q = require('q')
mongoose = require('mongoose')

log = require("#{__dirname}/../../log")

retrieve = require("#{__dirname}/../../retrieve")

Schema = mongoose.Schema
ObjectId = Schema.Types.ObjectId

BookSchema = new Schema
  isbn:
    type: String
    index:
      unique: true
    required: true
    trim: true
  title:
    type: String
    trim: true
  author:
    type: String
    trim: true
  prices: [{
    value: Number
    date: Date
  }]
  amazon:
    url: String
    image: String

BookSchema.statics.findByIsbn = (isbn) ->
  @findOne(isbn: isbn).exec()

BookSchema.statics.fetchFromAmazon = (book) ->
  retrieve.fetchFromAmazon(book.isbn)
  .then ({title, value, author, url, image}) =>
    log.verbose "received #{book.isbn}, now updating as #{title}"
    @findOneAndUpdate {isbn: book.isbn},
      title: title
      author: author or ''
      amazon:
        url: url
        image: image
      $push:
        prices:
          value: value
          date: Date.now()
    .exec()

module.exports = mongoose.model('Book', BookSchema)

Fix Update Book Without Author

Fix Update Book Without Author


_ = require "underscore"
Transform = require "./transform"

class Jitter extends Transform.Model
  initialize: (attrs, options) ->
    super(attrs, options)

  defaults: ->
    return _.extend({}, super(), {
      interval: 1
    })

  compute: (x) ->
    # Apply the transform to a single value
    return(x + ((Math.random() - 0.5) * @get('interval')))

  v_compute: (xs) ->
    # Apply the tranform to a vector of values
    result = new Float64Array(xs.length)
    for x, idx in xs
      result[idx] = this.compute(x)
    return result

module.exports =
  Model: Jitter

_ = require "underscore"
Transform = require "./transform"

class Jitter extends Transform.Model
  initialize: (attrs, options) ->
    super(attrs, options)

  defaults: ->
    return _.extend {}, super(), {
      interval: [ p.Number, 1]
    }

  compute: (x) ->
    # Apply the transform to a single value
    return(x + ((Math.random() - 0.5) * @get('interval')))

  v_compute: (xs) ->
    # Apply the tranform to a vector of values
    result = new Float64Array(xs.length)
    for x, idx in xs
      result[idx] = this.compute(x)
    return result

module.exports =
  Model: Jitter

Convert to using newer property initializers in the JS for Jitter


class window.Comment extends Backbone.Model

  templateHelpers: =>
    creator: @creator

  creator: -> new User(@get('created_by'))

  can_destroy: -> @creator().get('id') == currentUser.get('id')

class window.Comment extends Backbone.Model

  templateHelpers: =>
    creator: @creator

  creator: -> new User(@get('created_by')).toJSON()

  can_destroy: -> @creator().get('id') == currentUser.get('id')

Create JSON of creator in Comment templateHelper


noflo = require "noflo"
_s = require "underscore.string"
{ CacheStorage } = require "../lib/cache_storage"

class Cache extends noflo.Component

  description: _s.clean "save incoming IPs and send the saved IPs to
  port 'out' upon any data IP from 'ready'"

  constructor: ->
    @key = null
    @cache = new CacheStorage

    @inPorts =
      in: new noflo.Port
      ready: new noflo.Port
      key: new noflo.Port
      size: new noflo.Port
    @outPorts =
      out: new noflo.Port

    @inPorts.key.on "data", (@key) =>

    @inPorts.size.on "data", (size) =>
      @cache.size = size

    @inPorts.ready.on "data", (data) =>
      @cache.flushCache @outPorts.out, @key
      @outPorts.out.disconnect()
      delete @cache[@key]

    @inPorts.ready.on "disconnect", =>
      @key = null

    @inPorts.in.on "connect", =>
      @cache.connect(@key)

    @inPorts.in.on "begingroup", (group) =>
      @cache.beginGroup(group, @key)

    @inPorts.in.on "data", (data) =>
      @cache.data(data, @key)

    @inPorts.in.on "endgroup", (group) =>
      @cache.endGroup(group, @key)

    @inPorts.in.on "disconnect", =>
      @cache.disconnect(@key)
      @key = null

exports.getComponent = -> new Cache

noflo = require "noflo"
_s = require "underscore.string"
{ CacheStorage } = require "../lib/cache_storage"

class Cache extends noflo.Component

  description: _s.clean "save incoming IPs and send the saved IPs to
  port 'out' upon any data IP from 'ready'"

  constructor: ->
    @key = null
    @cache = new CacheStorage

    @inPorts =
      in: new noflo.Port
      ready: new noflo.Port
      key: new noflo.ArrayPort
      size: new noflo.Port
    @outPorts =
      out: new noflo.Port

    @inPorts.key.on "data", (@key) =>

    @inPorts.size.on "data", (size) =>
      @cache.size = size

    @inPorts.ready.on "data", (data) =>
      @cache.flushCache @outPorts.out, @key
      @outPorts.out.disconnect()
      delete @cache[@key]

    @inPorts.ready.on "disconnect", =>
      @key = null

    @inPorts.in.on "connect", =>
      @cache.connect(@key)

    @inPorts.in.on "begingroup", (group) =>
      @cache.beginGroup(group, @key)

    @inPorts.in.on "data", (data) =>
      @cache.data(data, @key)

    @inPorts.in.on "endgroup", (group) =>
      @cache.endGroup(group, @key)

    @inPorts.in.on "disconnect", =>
      @cache.disconnect(@key)
      @key = null

exports.getComponent = -> new Cache

Use ArrayPort to allow keying for 'in' and 'ready'


define [
  'analytics/compiled/BaseData'
  'timezone'
], (BaseData, tz) ->

  ##
  # Loads the participation data for the student and course. Exposes the data
  # as the 'pageViews' and 'participations' properties once loaded.
  class ParticipationData extends BaseData
    constructor: (student) ->
      course = student.get('course')
      super "/api/v1/courses/#{course.get 'id'}/analytics/users/#{student.get 'id'}/activity"

    populate: (data) ->
      @bins = []

      # maintain one unique bin per date, order of insertion into @bins
      # unimportant
      binMap = {}
      binFor = (date) =>
        if !binMap[date]?
          binMap[date] =
            date: date
            views: 0
            participations: 0
            participation_events: []
          @bins.push binMap[date]
        binMap[date]

      # sort the page view data to the appropriate bins
      for date, views of data.page_views
        # this date is the hour for the bin
        bin = binFor(Date.parse date)
        bin.views += views

      # sort the participation date to the appropriate bins
      for event in data.participations
        event.createdAt = Date.parse event.created_at
        # bin to the hour corresponding to event.createdAt, so that all
        # participations fall in the same bin as their respective page views.
        bin = binFor(tz.parse(tz.format(event.createdAt, '%F %H:00:00')))
        bin.participation_events.push event
        bin.participations += 1

define [
  'analytics/compiled/BaseData'
  'timezone'
], (BaseData, tz) ->

  ##
  # Loads the participation data for the student and course. Exposes the data
  # as the 'pageViews' and 'participations' properties once loaded.
  class ParticipationData extends BaseData
    constructor: (student) ->
      course = student.get('course')
      super "/api/v1/courses/#{course.get 'id'}/analytics/users/#{student.get 'id'}/activity"

    populate: (data) ->
      @bins = []

      # maintain one unique bin per date, order of insertion into @bins
      # unimportant
      binMap = {}
      binFor = (date) =>
        if !binMap[date]?
          binMap[date] =
            date: date
            views: 0
            participations: 0
            participation_events: []
          @bins.push binMap[date]
        binMap[date]

      # sort the page view data to the appropriate bins
      for date, views of data.page_views
        # this date is the day for the bin
        bin = binFor(Date.parse date)
        bin.views += views

      # sort the participation date to the appropriate bins
      for event in data.participations
        event.createdAt = Date.parse event.created_at
        # bin to the day corresponding to event.createdAt, so that all
        # participations fall in the same bin as their respective page views.
        bin = binFor(new Date(event.createdAt.setHours(0,0,0,0)))
        bin.participation_events.push event
        bin.participations += 1

Fix merging of participation and page views

the binning of page views and participation from the api into dates is
fixed.

closes ADMIN-1299

note: there was a change in this file from binning by day -> hour to fix
a bug that looks like was a timezone issue. See CNVS-16464 and
https://gerrit.instructure.com/#/c/44111/.  I believe this sort of
addressed the issue at the time, but was not correct.

test plan:
  - have a course with a student with page views and participation data
    on the same day (you could run @jeremy's fake_analytics.rb script to
    generate randomish data and tweak the results to get dupes)
  - go to /courses/:couse_id/analytics/users/:user_id
  - click the toggle to switch to table view
  > expect the activity by date table to have combined the views and
    participation into the same date. There should be no duplicate
    dates in the table.

Change-Id: Iff76a43c0a7c0a1d3dd5082a6bbde18407916f32
Reviewed-on: https://gerrit.instructure.com/161479
Tested-by: Jenkins
Reviewed-by: Mysti Sadler <431d062aff1e8f25416b3a7b732fe6effdb03f9a@instructure.com>
Reviewed-by: Jeremy Stanley <b3f594e10a9edcf5413cf1190121d45078c62290@instructure.com>
Reviewed-by: Carl Kibler <7c2b40129dd885a147d5b2dda56dda016b9a8f57@instructure.com>
QA-Review: Jeremy Stanley <b3f594e10a9edcf5413cf1190121d45078c62290@instructure.com>
Product-Review: Ed Schiebel <0ca8a83847a5889cf05cb219c0ab09f6b646243b@instructure.com>


{View} = require 'space-pen'
$ = require 'jquery'
Git = require 'git'
fs = require 'fs'

module.exports =
class FileView extends View

  @content: ({file} = {}) ->
    @li class: 'file entry', =>
      @span file.getBaseName(), class: 'name', outlet: 'fileName'
      @span '', class: 'highlight'

  file: null

  initialize: ({@file, @project} = {}) ->
    extension = fs.extension(@getPath())
    if fs.isReadmePath(@getPath())
      @fileName.addClass('readme-icon')
    else if fs.isCompressedExtension(extension)
      @fileName.addClass('compressed-icon')
    else if fs.isImageExtension(extension)
      @fileName.addClass('image-icon')
    else if fs.isPdfExtension(extension)
      @fileName.addClass('pdf-icon')
    else if fs.isBinaryExtension(extension)
      @fileName.addClass('binary-icon')
    else
      @fileName.addClass('text-icon')

    if git?
      git.on 'status-changed', (path, status) =>
        @updateStatus() if path is @getPath()
      git.on 'statuses-changed', =>
        @updateStatus()

    @updateStatus()

  updateStatus: ->
    @removeClass('ignored modified new')
    return unless git?

    path = @getPath()
    if git.isPathIgnored(path)
      @addClass('ignored')
    else
      status = git.statuses[path]
      if git.isStatusModified(status)
        @addClass('modified')
      else if git.isStatusNew(status)
        @addClass('new')

  getPath: ->
    @file.path

{View} = require 'space-pen'
$ = require 'jquery'
Git = require 'git'
fs = require 'fs'

module.exports =
class FileView extends View

  @content: ({file} = {}) ->
    @li class: 'file entry', =>
      @span file.getBaseName(), class: 'name', outlet: 'fileName'
      @span '', class: 'highlight'

  file: null

  initialize: ({@file, @project} = {}) ->
    extension = fs.extension(@getPath())
    if fs.isReadmePath(@getPath())
      @fileName.addClass('readme-icon')
    else if fs.isCompressedExtension(extension)
      @fileName.addClass('compressed-icon')
    else if fs.isImageExtension(extension)
      @fileName.addClass('image-icon')
    else if fs.isPdfExtension(extension)
      @fileName.addClass('pdf-icon')
    else if fs.isBinaryExtension(extension)
      @fileName.addClass('binary-icon')
    else
      @fileName.addClass('text-icon')

    if git?
      @subscribe git, 'status-changed', (path, status) =>
        @updateStatus() if path is @getPath()
      @subscribe git, 'statuses-changed', =>
        @updateStatus()

    @updateStatus()

  updateStatus: ->
    @removeClass('ignored modified new')
    return unless git?

    path = @getPath()
    if git.isPathIgnored(path)
      @addClass('ignored')
    else
      status = git.statuses[path]
      if git.isStatusModified(status)
        @addClass('modified')
      else if git.isStatusNew(status)
        @addClass('new')

  getPath: ->
    @file.path

Use subscribe instead of on

Use subscribe instead of on


class EyeTribe
  @Tracker = require('./tracker')
  @version = require('./version')

  @loop = (config, callback)->
    if typeof config == 'function'
      [callback, config] = [config, {}]
    @loopTracker = new @Tracker config
      .loop callback

module.exports = EyeTribe

class EyeTribe
  @Tracker = require('./tracker')
  @version = require('./version')
  @Protocol = require('./protocol')
  @GazeData = require('./gazedata')
  @Point2D = require('./point2d')
  @_ = require('underscore')

  @loop = (config, callback)->
    if typeof config == 'function'
      [callback, config] = [config, {}]
    @loopTracker = new @Tracker config
      .loop callback

module.exports = EyeTribe

Add references to internal modules as member variables of class EyeTribe


module.exports.pad = (n, size) ->
    n = n.toString()
    pad = ''
    size = size - n.length
    for i in [0 .. size]
        pad += ' '
    n + pad

module.exports.flatten = (arr, ret) ->
  ret ?= []
  len = arr.length
  for i in [0 .. len]
    if Array.isArray arr[i]
      exports.flatten arr[i], ret
    else
      ret.push arr[i]
  ret

path = require 'path'

module.exports =
    pad: (n, size) ->
        n = n.toString()
        pad = ''
        size = size - n.length
        for i in [0 .. size]
            pad += ' '
        n + pad
    flatten: (arr, ret) ->
        ret ?= []
        for i in [0 ... arr.length]
            if Array.isArray arr[i]
                @flatten arr[i], ret
            else
                ret.push arr[i]
        ret
    # Discovery the project root directory or return null if undiscoverable
    workspace: () ->
        dirs = require('module')._nodeModulePaths process.cwd()
        for dir in dirs
            if path.existsSync(dir) || path.existsSync(path.normalize(dir + '/../package.json'))
                return path.normalize dir + '/..'

Fix flatten function (bug introduce by coffee migration)


noflo = require 'noflo'
fbpSpec = require 'fbp-spec'

exports.getComponent = ->
  c = new noflo.Component
  c.inPorts.add 'in',
    datatype: 'object'
    description: 'Project'
  c.inPorts.add 'runtime',
    datatype: 'object'
    required: yes
  c.outPorts.add 'context',
    datatype: 'object'
  c.outPorts.add 'error',
    datatype: 'object'

  noflo.helpers.WirePattern c,
    in: 'in'
    params: ['runtime']
    out: 'context'
    async: true
  , (project, groups, out, callback) ->
    unless project
      do callback
      return
    unless c.params.runtime.isConnected()
      do callback
      return
    unless project.specs?.length
      do callback
      return
    specs = project.specs.filter (s) -> s.language is 'yaml'
    unless specs.length
      do callback
      return

    suites = []
    for s in specs
      suites = suites.concat fbpSpec.testsuite.loadYAML s.code
    unless suites.length
      do callback
      return

    # Send out the initial suites
    out.send
      suites: suites

    runner = new fbpSpec.runner.Runner c.params.runtime
    fbpSpec.runner.runAll runner, suites, ->
      # Send updated suite state
      out.send
        suites: suites
    , (err) ->
      return callback err if err
      do callback

noflo = require 'noflo'
fbpSpec = require 'fbp-spec'

exports.getComponent = ->
  c = new noflo.Component
  c.inPorts.add 'in',
    datatype: 'object'
    description: 'Project'
  c.inPorts.add 'runtime',
    datatype: 'object'
    required: yes
  c.outPorts.add 'context',
    datatype: 'object'
  c.outPorts.add 'error',
    datatype: 'object'

  noflo.helpers.WirePattern c,
    in: 'in'
    params: ['runtime']
    out: 'context'
    async: true
  , (project, groups, out, callback) ->
    unless project
      do callback
      return
    unless c.params.runtime.isConnected()
      do callback
      return
    unless project.specs?.length
      do callback
      return
    specs = project.specs.filter (s) -> s.language is 'yaml'
    unless specs.length
      do callback
      return

    suites = []
    for s in specs
      continue unless s.code.length
      try
        suite = fbpSpec.testsuite.loadYAML s.code
      catch e
        console.log e
        continue
      suites = suites.concat suite
    unless suites.length
      do callback
      return

    # Send out the initial suites
    out.send
      suites: suites

    runner = new fbpSpec.runner.Runner c.params.runtime
    fbpSpec.runner.runAll runner, suites, ->
      # Send updated suite state
      out.send
        suites: suites
    , (err) ->
      return callback err if err
      do callback

Deal with YAML parse errors

Deal with YAML parse errors


$(document).ready ->
  return unless $('#sc_memo_edit_form').length > 0

  $('.js-edit-memo').on('click', (ev) =>
    ev.preventDefault()
    originalValue = $('#memo-edit-row').data('original-value')
    $('#sc_memo_edit_form').find("[name='store_credit[memo]']").val(originalValue)
    $('#memo-display-row').addClass('hidden')
    $('#memo-edit-row').removeClass('hidden')
  )

  $('.js-save-memo').on('click', (ev) ->
    ev.preventDefault()
    $.ajax(
      $('#sc_memo_edit_form').attr('url'), {
        data: $('#sc_memo_edit_form').serialize(),
        dataType: 'json',
        method: 'put',
        complete: (xhr, status) =>
          if xhr.status is 200
            newValue = $('#sc_memo_edit_form').find("[name='store_credit[memo]']").val()
            $('#memo-edit-row').data('original-value', newValue)
            $('#memo-display-row').find('.js-memo-text').text(newValue)
            hideEditMemoForm()
            show_flash('success', xhr.responseJSON.message)
          else if xhr.status is 400
            show_flash('error', xhr.responseJSON.message)
      }
    )
  )

  $('.js-cancel-memo').on('click', (ev) =>
    ev.preventDefault()
    hideEditMemoForm()
  )

  hideEditMemoForm = ->
    $('#memo-edit-row').addClass('hidden')
    $('#memo-display-row').removeClass('hidden')

$(document).ready ->
  return unless $('#sc_memo_edit_form').length > 0

  $('.js-edit-memo').on('click', (ev) =>
    ev.preventDefault()
    originalValue = $('#memo-edit-row').data('original-value')
    $('#sc_memo_edit_form').find("[name='store_credit[memo]']").val(originalValue)
    $('#memo-display-row').addClass('hidden')
    $('#memo-edit-row').removeClass('hidden')
  )

  $('.js-save-memo').on('click', (ev) ->
    ev.preventDefault()
    Spree.ajax(
      $('#sc_memo_edit_form').attr('url'), {
        data: $('#sc_memo_edit_form').serialize(),
        dataType: 'json',
        method: 'put',
        success: ( (response) =>
          newValue = $('#sc_memo_edit_form').find("[name='store_credit[memo]']").val()
          $('#memo-edit-row').data('original-value', newValue)
          $('#memo-display-row').find('.js-memo-text').text(newValue)
          hideEditMemoForm()
          show_flash('success', response.message)
        ),
        error: ( (xhr, status) =>
          show_flash('error', xhr.responseJSON.message)
        )
      }
    )
  )

  $('.js-cancel-memo').on('click', (ev) =>
    ev.preventDefault()
    hideEditMemoForm()
  )

  hideEditMemoForm = ->
    $('#memo-edit-row').addClass('hidden')
    $('#memo-display-row').removeClass('hidden')

Switch to Spree.ajax & success / error callbacks


TentStatus.Routers.posts = new class PostsRouter extends TentStatus.Router
  routerKey: 'posts'

  routes:
    ""                 : "root"
    "profile"          : "myProfile"
    "posts"            : "index"
    "posts/:entity/:post_id" : "conversation"
    "posts/:post_id" : "conversation"

  index: =>
    unless TentStatus.config.current_entity.hostname == TentStatus.config.domain_entity.hostname
      @profile(encodeURIComponent(TentStatus.domain_entity))
      return
    @view = new TentStatus.Views.Posts
    @setCurrentAction 'index', =>
      @view.render()

  root: => @index(arguments...)

  conversation: (entity, post_id) =>
    unless post_id
      [post_id, entity] = [entity, TentStatus.config.domain_entity]
    else
      entity = new HTTP.URI decodeURIComponent(entity)
    @view = new TentStatus.Views.Conversation entity: entity, post_id: post_id

  myProfile: =>
    @profile(TentStatus.current_entity)

  profile: (entity) =>
    @view = new TentStatus.Views.Profile entity: entity

TentStatus.Routers.posts = new class PostsRouter extends TentStatus.Router
  routerKey: 'posts'

  routes:
    ""                 : "root"
    "profile"          : "myProfile"
    "posts"            : "index"
    "posts/:entity/:post_id" : "conversation"
    "posts/:post_id" : "conversation"

  index: =>
    if !TentStatus.authenticated or (TentStatus.config.current_entity.hostname != TentStatus.config.domain_entity.hostname)
      @profile(encodeURIComponent(TentStatus.domain_entity))
      return
    @view = new TentStatus.Views.Posts
    @setCurrentAction 'index', =>
      @view.render()

  root: => @index(arguments...)

  conversation: (entity, post_id) =>
    unless post_id
      [post_id, entity] = [entity, TentStatus.config.domain_entity]
    else
      entity = new HTTP.URI decodeURIComponent(entity)
    @view = new TentStatus.Views.Conversation entity: entity, post_id: post_id

  myProfile: =>
    @profile(TentStatus.current_entity)

  profile: (entity) =>
    @view = new TentStatus.Views.Profile entity: entity

Fix home page when not logged in

Fix home page when not logged in


{RootView} = require 'atom'
shell = require 'shell'

describe "link package", ->
  [editor] = []

  beforeEach ->
    atom.activatePackage('javascript-tmbundle', sync: true)
    atom.activatePackage('hyperlink-helper-tmbundle', sync: true)
    window.rootView = new RootView
    rootView.openSync('sample.js')
    atom.activatePackage('link')
    rootView.attachToDom()
    editor = rootView.getActiveView()
    editor.insertText("// http://github.com\n")

  describe "when the cursor is on a link", ->
    it "opens the link using the 'open' command", ->
      spyOn(shell, 'openExternal')
      editor.trigger('link:open')
      expect(shell.openExternal).not.toHaveBeenCalled()

      editor.setCursorBufferPosition([0,5])
      editor.trigger('link:open')

      expect(shell.openExternal).toHaveBeenCalled()
      expect(shell.openExternal.argsForCall[0][0]).toBe "http://github.com"

{RootView} = require 'atom'
shell = require 'shell'

describe "link package", ->
  [editor] = []

  beforeEach ->
    atom.activatePackage('language-javascript', sync: true)
    atom.activatePackage('language-hyperlink', sync: true)
    window.rootView = new RootView
    rootView.openSync('sample.js')
    atom.activatePackage('link')
    rootView.attachToDom()
    editor = rootView.getActiveView()
    editor.insertText("// http://github.com\n")

  describe "when the cursor is on a link", ->
    it "opens the link using the 'open' command", ->
      spyOn(shell, 'openExternal')
      editor.trigger('link:open')
      expect(shell.openExternal).not.toHaveBeenCalled()

      editor.setCursorBufferPosition([0,5])
      editor.trigger('link:open')

      expect(shell.openExternal).toHaveBeenCalled()
      expect(shell.openExternal.argsForCall[0][0]).toBe "http://github.com"

Update packages for new naming scheme

Update packages for new naming scheme


game.ToastEntity = me.ObjectEntity.extend(

  init: (x, y, settings) ->

    settings.image = 'toast'
    settings.spritewidth = '12'
    settings.spriteheight = '12'

    @parent x, y, settings

    @maxVelocity = 5
    @gravity = 0

    targetVector = new me.Vector2d(settings.target.x - x, settings.target.y - y)
    targetVector.normalize()
    targetVector.scale(new me.Vector2d(@maxVelocity, @maxVelocity))
    @setVelocity(targetVector.x, targetVector.y)

  update: ->
    @vel.x += @accel.x * me.timer.tick
    @vel.y += @accel.y * me.timer.tick
    @computeVelocity(@vel)

    @updateMovement()

)

game.ToastEntity = me.ObjectEntity.extend(

  init: (x, y, settings) ->

    settings.image = 'toast'
    settings.spritewidth = '12'
    settings.spriteheight = '12'

    @parent x, y, settings

    @maxVelocity = 5
    @gravity = 0

    targetVector = new me.Vector2d(settings.target.x - x, settings.target.y - y)
    targetVector.normalize()
    targetVector.scale(new me.Vector2d(@maxVelocity, @maxVelocity))
    @setVelocity(targetVector.x, targetVector.y)

  update: ->
    @vel.x += @accel.x * me.timer.tick
    @vel.y += @accel.y * me.timer.tick
    @computeVelocity(@vel)
    res = @updateMovement()
    @checkForEnvCollision(res)

  checkForEnvCollision: (res) ->
    me.game.remove(@) if res.x != 0 || res.y != 0

)

Remove bullets when collide with env.

Remove bullets when collide with env.


define [
  "base"
], (App) ->
  App.directive "toggleSwitch", () ->
    restrict: "E"
    scope:
      description: "@"
      labelFalse: "@"
      labelTrue: "@"
      ngModel: "="
    template: """
  <fieldset class="toggle-switch">
    <legend class="sr-only">{{description}}</legend>

    <input
      type="radio"
      name="editor-mode"
      class="toggle-switch-input"
      id="toggle-switch-false-{{$id}}"
      ng-value="false"
      ng-model="ngModel"
    >
    {{labelFalse}}

    <input
      type="radio"
      class="toggle-switch-input"
      name="editor-mode"
      id="toggle-switch-true-{{$id}}"
      ng-value="true"
      ng-model="ngModel"
    >
    {{labelTrue}}

  </fieldset>
"""

define [
  "base"
], (App) ->
  App.directive "toggleSwitch", () ->
    restrict: "E"
    scope:
      description: "@"
      labelFalse: "@"
      labelTrue: "@"
      ngModel: "="
    template: """
  <fieldset class="toggle-switch">
    <legend class="sr-only">{{description}}</legend>

    <input
      type="radio"
      name="toggle-switch-{{$id}}"
      class="toggle-switch-input"
      id="toggle-switch-false-{{$id}}"
      ng-value="false"
      ng-model="ngModel"
    >
    {{labelFalse}}

    <input
      type="radio"
      class="toggle-switch-input"
      name="toggle-switch-{{$id}}"
      id="toggle-switch-true-{{$id}}"
      ng-value="true"
      ng-model="ngModel"
    >
    {{labelTrue}}

  </fieldset>
"""

Make sure each pair of radio buttons gets a unique name attribute in the toggle switch directive.


React             = require 'react'
EnrollButton      = require '../students/enroll_button'

InlineUsers = React.createClass(
  displayName: 'InlineUsers'
  render: ->
    key = @props.title + '_' + @props.role
    user_list = @props.users.map (user) ->
      if user.real_name?
        "#{user.real_name} (#{user.wiki_id}#{if user.email? then " / " + user.email else ""})"
      else
        user.wiki_id

    user_list = user_list.join(', ')
    user_list = if user_list.length > 0 then user_list else 'None'
    inline_list = {@props.title}: {user_list} if @props.users.length > 0 || @props.editable
    allowed = @props.role != 4 || (@props.current_user.role == 4 || @props.current_user.admin)
    button = <EnrollButton {...@props} users={@props.users} role={@props.role} key={key} inline=true allowed={allowed} show={@props.editable && allowed} />

{inline_list}{button}

)

module.exports = InlineUsers

React             = require 'react'
EnrollButton      = require '../students/enroll_button'

InlineUsers = React.createClass(
  displayName: 'InlineUsers'
  render: ->
    key = @props.title + '_' + @props.role
    user_list = @props.users.map (user) ->
      link = "https://en.wikipedia.org/wiki/User:#{user.wiki_id}"
      if user.real_name?
        displayName = "#{user.real_name} (#{user.wiki_id}#{if user.email? then " / " + user.email else ""})"
      else
        displayName = user.wiki_id
      {displayName}

    # This is feels really hacky, but it works.
    last_user_index = user_list.length - 1
    inline_user_list = ([user, ', '] for user, index in user_list when index != last_user_index)
    inline_user_list.push user_list[last_user_index]

    inline_user_list = if user_list.length > 0 then inline_user_list else 'None'
    if @props.users.length > 0 || @props.editable
      inline_list = {@props.title}: {inline_user_list}
    allowed = @props.role != 4 || (@props.current_user.role == 4 || @props.current_user.admin)
    button = <EnrollButton {...@props} users={@props.users} role={@props.role} key={key} inline=true allowed={allowed} show={@props.editable && allowed} />

{inline_list}{button}

)

module.exports = InlineUsers

Make each username in InlineUsers link to the userpage


assert = require('chai').assert
_ = require('underscore')
sinon = require('sinon')
Indicator = require('../../models/indicator')
fs = require 'fs'

suite('Indicator')

test(".find reads the definition from definitions/indicators.json
and returns an indicator with the correct attributes for that ID", (done)->
  definitions = [
    {id: 1, type: 'esri'},
    {id: 5, type: 'standard'}
  ]
  readFileStub = sinon.stub(fs, 'readFile', (filename, callback) ->
    callback(null, JSON.stringify(definitions))
  )

  Indicator.find(5).then((indicator) ->
    try
      assert.isTrue readFileStub.calledWith('./definitions/indicators.json'),
        "Expected find to read the definitions file"

      assert.property indicator, 'type',
        "Expected the type property from the JSON to be populated on indicator model"

      assert.strictEqual indicator.type, 'standard',
        "Expected the type property to be populated correctly from the definition"

      done()
    catch err
      done(err)
    finally
      readFileStub.restore()
  ).fail(done)
)

test(".query loads and formats the data based on its source", ->
  indicator = new Indicator(
    source: "gdoc"
  )

  gotData = {some: 'data'}
  getDataStub = sinon.stub(indicator, 'getDataFrom', ->
    Q.fcall(-> gotData)
  )

  formatDataFrom = sinon.stub(indicator, 'formatDataFrom', ->
    Q.fcall(-> {})
  )

  assert.isTrue(
    getDataStub.calledWith(indicator.source),
    "Expected getDataFrom to be called with the indicator source"
  )

  assert.isTrue(
    formatDataFrom.calledWith(indicator.source, gotData),
    "Expected formatDataFrom to be called with the indicator source and fetched data"
  )
)

Add tests for new get/format data methods


resourceRequest = require './resource-request.coffee'
virtual = require './virtual-resource.coffee'

resources = {}

# TODO : Implement cache invalidation on a periodic basis here
exports.combine = ({resources, req, separator, contentType, prefix}) ->
  id = resources.length + (resources.href for resource in resources).join ':'

  if not resource = resources[id]
    # Create a new resource and load async
    resource = resources[id] = {
      id
      content: {href: resource.href, complete: false} for resource in resources
      deferred: []
    }

    checkComplete = ->
      # Determine if there are any incomplete operations
      for content in resource.content
        if not content.complete
          return false

      resource.content = (content.data for content in resource.content).join ''

      for deferred in resource.deferred
        deferred resource.content

    for content in resource.content
      do (content) ->
        chunks = []
        resourceRequest.get(
          content.href,
          req,
          (err, res) ->
            if err
              console.log "Unable to load combined resource #{content.href}", err
              content.data = ''
              content.complete = true
            else
              res.on 'data', (chunk) ->
                chunks.push chunk
              res.on 'end', ->
                content.data = chunks.join('') + (separator ? '')
                content.complete = true
                checkComplete()
        )

    resource.href = virtual.create
      content: (callback) ->
        resource.deferred.push callback
        checkComplete()
      contentType: contentType
      prefix: prefix

  # TODO Figure out a better way to map to the virtual middleware
  '/virtual/' + resource.href

resourceRequest = require './resource-request.coffee'
virtual = require './virtual-resource.coffee'

resources = {}

# TODO : Implement cache invalidation on a periodic basis here
exports.combine = ({resources, req, separator, contentType, prefix}) ->
  id = resources.length + (resource.href for resource in resources).join ':'

  if not resource = resources[id]
    # Create a new resource and load async
    resource = resources[id] = {
      id
      content: {href: resource.href, complete: false} for resource in resources
      deferred: []
    }

    checkComplete = ->
      # Determine if there are any incomplete operations
      for content in resource.content
        if not content.complete
          return false

      resource.content = (content.data for content in resource.content).join ''

      for deferred in resource.deferred
        deferred resource.content

    for content in resource.content
      do (content) ->
        chunks = []
        resourceRequest.get(
          content.href,
          req,
          (err, res) ->
            if err
              console.log "Unable to load combined resource #{content.href}", err
              content.data = ''
              content.complete = true
            else
              res.on 'data', (chunk) ->
                chunks.push chunk
              res.on 'end', ->
                content.data = chunks.join('') + (separator ? '')
                content.complete = true
                checkComplete()
        )

    resource.href = virtual.create
      content: (callback) ->
        resource.deferred.push callback
        checkComplete()
      contentType: contentType
      prefix: prefix

  # TODO Figure out a better way to map to the virtual middleware
  '/virtual/' + resource.href

Fix id generation within the resource combiner


# This script teaches Hubot to answer questions like "Who owns Collections Publisher?"
# and "Who owns publishing-api?"
module.exports = (robot) ->
  robot.hear /who owns (.*)\?/i, (res) ->
    # Best guess of the intended application.
    application = res.match[1].replace(/\s/g, '-').toLowerCase()
    console.log("Fetching owner for #{application}")

    robot.http("https://docs.publishing.service.gov.uk/apps/#{application}.json")
      .get() (err, response, body) ->
        if response.statusCode == 200
          data = JSON.parse(body)
          res.reply "#{data.app_name} is owned by #{data.team}"

# This script teaches Hubot to answer questions like "Who owns Collections Publisher?"
# and "Who owns publishing-api?"
module.exports = (robot) ->
  robot.hear /who owns (.*)\?/i, (res) ->
    # Best guess of the intended application.
    application = res.match[1].replace(/\s/g, '-').toLowerCase()
    console.log("Fetching owner for #{application}")

    robot.http("https://docs.publishing.service.gov.uk/apps/#{application}.json")
      .get() (err, response, body) ->
        if response.statusCode == 200
          data = JSON.parse(body)
          res.reply "#{data.app_name} is owned by #{data.product_manager} (#{data.team})"

Update product ownership to refer to Product Manager

The current state of affairs is that products are owned by product owners, and products follow them when they change team.
The team information still seems useful so we leave it in.


exports.app    = app = require('../config/app').app

exports.vows   = require 'vows'
exports.assert = require 'assert'
exports.should = require 'should'

exports.zombie         = require 'zombie'
exports.zombie.browser = new exports.zombie.Browser
  debug:      false
  runScripts: false

exports.tobi                = require 'tobi'

exports.tobi.get = (path, callback) ->
  browser = exports.tobi.createBrowser(app)
  browser.get(path, { headers: exports.headers.jsonHeaders }, callback)
  return

exports.headers             = {}
exports.headers.jsonHeaders = { 'Content-Type': 'application/json' }

Repository = require('../models/repository').Repository

exports.factories                   = {}
exports.factories.Repository        = {}
exports.factories.Repository.create = (attributes, callback) ->
  repositories = new Repository attributes
  repositories.save(callback)
  return

exports.app    = app = require('../config/app').app

exports.vows   = require 'vows'
exports.assert = require 'assert'
exports.should = require 'should'

exports.zombie         = require 'zombie'
exports.zombie.browser = new exports.zombie.Browser
  debug:      false
  runScripts: false

exports.tobi                = require 'tobi'

exports.tobi.get = (path, callback) ->
  browser = exports.tobi.createBrowser(app)
  browser.get path, { headers: exports.headers.jsonHeaders }, callback
  return

exports.headers             = {}
exports.headers.jsonHeaders = { 'Content-Type': 'application/json' }

Repository = require('../models/repository').Repository

exports.factories                   = {}
exports.factories.Repository        = {}
exports.factories.Repository.create = (attributes, callback) ->
  repositories = new Repository attributes
  repositories.save(callback)
  return

Make syntax more coffee like.

Make syntax more coffee like.


KeybindingResolverView = require '../lib/keybinding-resolver-view'
{$, RootView} = require 'atom'

describe "KeybindingResolverView", ->
  beforeEach ->
    atom.rootView = new RootView
    atom.packages.activatePackage('keybinding-resolver', immediate: true)

  describe "when the keybinding-resolver:toggle event is triggered", ->
    it "attaches and then detaches the view", ->
      expect(atom.rootView.find('.keybinding-resolver')).not.toExist()
      atom.rootView.trigger 'keybinding-resolver:toggle'
      expect(atom.rootView.find('.keybinding-resolver')).toExist()
      atom.rootView.trigger 'keybinding-resolver:toggle'
      expect(atom.rootView.find('.keybinding-resolver')).not.toExist()

  describe "when a keydown event occurs", ->
    it "displays all commands for the event", ->
      atom.keymap.bindKeys 'name', 'body', 'x': 'match-1'
      atom.keymap.bindKeys 'name', 'body', 'x': 'match-2'
      atom.keymap.bindKeys 'name', '.never-again', 'x': 'unmatch-2'

      atom.rootView.trigger 'keybinding-resolver:toggle'
      $(document).trigger keydownEvent('x', target: rootView)
      expect(atom.rootView.find('.keybinding-resolver .matched')).toHaveLength 2
      expect(atom.rootView.find('.keybinding-resolver .unmatched')).toHaveLength 1

KeybindingResolverView = require '../lib/keybinding-resolver-view'
{$, RootView} = require 'atom'

describe "KeybindingResolverView", ->
  beforeEach ->
    atom.rootView = new RootView
    atom.packages.activatePackage('keybinding-resolver', immediate: true)

  describe "when the keybinding-resolver:toggle event is triggered", ->
    it "attaches and then detaches the view", ->
      expect(atom.rootView.find('.keybinding-resolver')).not.toExist()
      atom.rootView.trigger 'keybinding-resolver:toggle'
      expect(atom.rootView.find('.keybinding-resolver')).toExist()
      atom.rootView.trigger 'keybinding-resolver:toggle'
      expect(atom.rootView.find('.keybinding-resolver')).not.toExist()

  describe "when a keydown event occurs", ->
    it "displays all commands for the event", ->
      atom.keymap.bindKeys 'name', 'body', 'x': 'match-1'
      atom.keymap.bindKeys 'name', 'body', 'x': 'match-2'
      atom.keymap.bindKeys 'name', '.never-again', 'x': 'unmatch-2'

      atom.rootView.trigger 'keybinding-resolver:toggle'
      $(document).trigger keydownEvent('x', target: atom.rootView)
      expect(atom.rootView.find('.keybinding-resolver .matched')).toHaveLength 2
      expect(atom.rootView.find('.keybinding-resolver .unmatched')).toHaveLength 1

Use atom.rootView as target value

Use atom.rootView as target value


{View} = require './space-pen-extensions'
Pane = null

### Internal ###
module.exports =
class PaneAxis extends View
  initialize: (@model) ->
    @onChildAdded(child) for child in @model.children
    @subscribe @model.children, 'changed', @onChildrenChanged

  viewForModel: (model) ->
    viewClass = model.getViewClass()
    model._view ?= new viewClass(model)

  addChild: (child, index) ->
    @model.addChild(child.model, index)

  removeChild: (child) ->
    @model.removeChild(child.model)

  onChildrenChanged:  ({index, removedValues, insertedValues}) =>
    focusedElement = document.activeElement if @hasFocus()
    @onChildRemoved(child, index) for child in removedValues
    @onChildAdded(child, index + i) for child, i in insertedValues
    focusedElement?.focus() if document.activeElement is document.body

  onChildAdded: (child, index) =>
    view = @viewForModel(child)
    @insertAt(index, view)

  onChildRemoved: (child) =>
    view = @viewForModel(child)
    view.detach()
    Pane ?= require './pane'

    if view instanceof Pane and view.model.isDestroyed()
      @getContainer()?.trigger 'pane:removed', [view]

  getContainer: ->
    @closest('.panes').view()

  getActivePaneItem: ->
    @getActivePane()?.activeItem

  getActivePane: ->
    @find('.pane.active').view() ? @find('.pane:first').view()

  insertChildBefore: (currentChild, newChild) ->
    @model.insertChildBefore(currentChild, newChild)

  insertChildAfter: (currentChild, newChild) ->
    @model.insertChildAfter(currentChild, newChild)

{View} = require './space-pen-extensions'
Pane = null

### Internal ###
module.exports =
class PaneAxis extends View
  initialize: (@model) ->
    @onChildAdded(child) for child in @model.children
    @subscribe @model.children, 'changed', @onChildrenChanged

  viewForModel: (model) ->
    viewClass = model.getViewClass()
    model._view ?= new viewClass(model)

  onChildrenChanged:  ({index, removedValues, insertedValues}) =>
    focusedElement = document.activeElement if @hasFocus()
    @onChildRemoved(child, index) for child in removedValues
    @onChildAdded(child, index + i) for child, i in insertedValues
    focusedElement?.focus() if document.activeElement is document.body

  onChildAdded: (child, index) =>
    view = @viewForModel(child)
    @insertAt(index, view)

  onChildRemoved: (child) =>
    view = @viewForModel(child)
    view.detach()
    Pane ?= require './pane'

    if view instanceof Pane and view.model.isDestroyed()
      @getContainer()?.trigger 'pane:removed', [view]

  getContainer: ->
    @closest('.panes').view()

Remove unused methods from PaneAxis

Remove unused methods from PaneAxis