#= require trix/controllers/text_controller
#= require trix/controllers/toolbar_controller
#= require trix/controllers/debug_controller

class Trix.EditorController
  constructor: (textElement, toolbarElement, inputElement, debugElement) ->
    @textController = new Trix.TextController textElement, createText(inputElement)
    @textController.delegate = this
    @toolbarController = new Trix.ToolbarController toolbarElement
    @toolbarController.delegate = this
    @debugController = new Trix.DebugController debugElement, @textController
    @debugController.render()

  createText = (inputElement) ->
    if inputElement?.value
      Trix.Text.fromJSON(inputElement.value)
    else
      new Trix.Text

  # Text controller delegate

  textControllerDidRender: ->
    @debugController.render()

  textControllerDidFocus: ->
    @toolbarController.hideDialogsThatFocus()

  textControllerDidChangeCurrentAttributes: (currentAttributes) ->
    @toolbarController.updateAttributes(currentAttributes)

  textControllerDidChangeSelection: ->
    @debugController.render()

  # Toolbar controller delegate

  toolbarDidToggleAttribute: (attributeName) ->
    @textController.toggleCurrentAttribute(attributeName)
    @textController.focus()

  toolbarDidUpdateAttribute: (attributeName, value) ->
    @textController.setCurrentAttribute(attributeName, value)
    @textController.focus()

  toolbarDidHideDialog: ->
    @textController.focus()

  toolbarWillShowDialog: ->
    @textController.lockSelection()

#= require trix/controllers/text_controller
#= require trix/controllers/toolbar_controller
#= require trix/controllers/debug_controller

class Trix.EditorController
  constructor: (textElement, toolbarElement, @inputElement, debugElement) ->
    @text = @createText()
    @textController = new Trix.TextController textElement, @text
    @textController.delegate = this
    @toolbarController = new Trix.ToolbarController toolbarElement
    @toolbarController.delegate = this
    @debugController = new Trix.DebugController debugElement, @textController
    @debugController.render()

  createText: ->
    if @inputElement?.value
      Trix.Text.fromJSON(@inputElement.value)
    else
      new Trix.Text

  saveSerializedText: ->
    @inputElement?.value = @text.asJSON()

  # Text controller delegate

  textControllerDidRender: ->
    @saveSerializedText()
    @debugController.render()

  textControllerDidFocus: ->
    @toolbarController.hideDialogsThatFocus()

  textControllerDidChangeCurrentAttributes: (currentAttributes) ->
    @toolbarController.updateAttributes(currentAttributes)

  textControllerDidChangeSelection: ->
    @debugController.render()

  # Toolbar controller delegate

  toolbarDidToggleAttribute: (attributeName) ->
    @textController.toggleCurrentAttribute(attributeName)
    @textController.focus()

  toolbarDidUpdateAttribute: (attributeName, value) ->
    @textController.setCurrentAttribute(attributeName, value)
    @textController.focus()

  toolbarDidHideDialog: ->
    @textController.focus()

  toolbarWillShowDialog: ->
    @textController.lockSelection()

Save serialized text to input element

Save serialized text to input element


CI.Versions =
  default_ruby: "1.9.3-p194"
  old_ruby: "1.8.7-p358"
  default_node: "0.8.12"
  default_php: "5.3.10-1ubuntu3.5"
  solr: "4.3.1"

CI.Versions =
  default_ruby: "1.9.3-p448"
  old_ruby: "1.8.7-p358"
  default_node: "0.8.12"
  default_php: "5.3.10-1ubuntu3.5"
  solr: "4.3.1"

Update the docs about the default version


humanizeDuration = require ".."
getSupportedLanguages = humanizeDuration.getSupportedLanguages

assert = require "assert"
fs = require "fs"
path = require "path"

describe "getLanguageSupport", ->

  definitionsPath = path.resolve(__dirname, "definitions")

  languages = []
  for file in fs.readdirSync(definitionsPath)
    if path.extname(file) is ".csv"
      languages.push path.basename(file, ".csv")

  assert.deepEqual(languages, getSupportedLanguages())

humanizeDuration = require ".."
getSupportedLanguages = humanizeDuration.getSupportedLanguages

assert = require "assert"
fs = require "fs"
path = require "path"

describe "getLanguageSupport", ->

  it "lists all supported languages", ->

    definitionsPath = path.resolve(__dirname, "definitions")

    languages = []
    for file in fs.readdirSync(definitionsPath)
      if path.extname(file) is ".csv"
        languages.push path.basename(file, ".csv")

    assert.deepEqual(languages.sort(), getSupportedLanguages().sort())

Improve resiliency of getSupportedLanguages() unit test


class ViewerAppController extends KDViewController

  KD.registerAppClass this,
    name         : "Viewer"
    route        : "/Develop"
    multiple     : yes
    openWith     : "forceNew"
    behavior     : "application"
    preCondition :

      condition  : (options, cb)->
        {path, vmName} = options
        return cb true  unless path
        path = FSHelper.plainPath path
        publicPath = path.replace \
          ///.*\/(.*\.#{KD.config.userSitesDomain})\/(.*)///, 'http://$1/$2'

        cb publicPath isnt path, {path: publicPath}

      failure    : (options, cb)->
        correctPath = \
          "/home/#{KD.nick()}/Sites/#{KD.nick()}.#{KD.config.userSitesDomain}/"
        KD.getSingleton("appManager").notify "File must be under: #{correctPath}"

  constructor:(options = {}, data)->

    options.view = new PreviewerView
      params     : options.params

    options.appInfo =
      title         : "Preview"
      cssClass      : "ace"

    super options, data

  open:(path)->
    @getView().openPath path

class ViewerAppController extends KDViewController

  KD.registerAppClass this,
    name         : "Viewer"
    route        : "/Develop"
    multiple     : yes
    openWith     : "forceNew"
    behavior     : "application"
    preCondition :

      condition  : (options, cb)->
        {path, vmName} = options
        return cb true  unless path
        path = FSHelper.plainPath path
        publicPath = path.replace \
          ///.*\/(.*\.#{KD.config.userSitesDomain})\/(.*)///, 'http://$1/$2'

        cb publicPath isnt path, {path: publicPath}

      failure    : (options, cb)->
        correctPath = \
          "/home/#{KD.nick()}/Web/"
        KD.getSingleton("appManager").notify "File must be under: #{correctPath}"

  constructor:(options = {}, data)->

    options.view = new PreviewerView
      params     : options.params

    options.appInfo =
      title         : "Preview"
      cssClass      : "ace"

    super options, data

  open:(path)->
    @getView().openPath path

Change folder to match new osKite default


uploader = new Slingshot.Upload('files')

Template.fileList.helpers
  'files': -> FileList.find {}, sort: LastModified: -1
  'date': -> moment(@LastModified).fromNow()

handleFiles = (event) ->
  if event.type == "drop"
    files = event.originalEvent.dataTransfer.files
  else
    files = event.target.files
  file = files[0]
  Session.set 'uploadingFile', file.name
  uploader.send file, (err, url) ->
    if err
      FlashMessages.sendError "An error occurred uploading the file: #{err.msg}"
    else
      Meteor.call 'listFiles'

Template.fileList.events
  'dropped #dropzone': handleFiles

  'click #dropzone': (event) ->
    event.preventDefault()
    $('#dropzoneFile').trigger('click')

  'change #dropzoneFile': handleFiles

  'click .file': (event) ->
    event.preventDefault()
    Session.set 'currentFile', @
    $('#currentFile').modal backdrop: false

Template.uploadingFile.helpers
  uploading: ->
    filename = Session.get 'uploadingFile'
    if filename and uploader.status() is 'transferring'
      name: filename
      percent_uploaded: Math.round(uploader.progress() * 100)

uploader = new Slingshot.Upload('files')

Template.fileList.helpers
  'files': -> FileList.find {}, sort: LastModified: -1
  'date': ->
    modified = moment(@LastModified)
    if modified.isBefore()
      modified.fromNow()
    else
      "a few seconds ago"

handleFiles = (event) ->
  if event.type == "drop"
    files = event.originalEvent.dataTransfer.files
  else
    files = event.target.files
  file = files[0]
  Session.set 'uploadingFile', file.name
  uploader.send file, (err, url) ->
    if err
      FlashMessages.sendError "An error occurred uploading the file: #{err.msg}"
    else
      Meteor.call 'listFiles'

Template.fileList.events
  'dropped #dropzone': handleFiles

  'click #dropzone': (event) ->
    event.preventDefault()
    $('#dropzoneFile').trigger('click')

  'change #dropzoneFile': handleFiles

  'click .file': (event) ->
    event.preventDefault()
    Session.set 'currentFile', @
    $('#currentFile').modal backdrop: false

Template.uploadingFile.helpers
  uploading: ->
    filename = Session.get 'uploadingFile'
    if filename and uploader.status() is 'transferring'
      name: filename
      percent_uploaded: Math.round(uploader.progress() * 100)

Make sure we're not in the future

Make sure we're not in the future


$ ->
  if $("#presentation-list").length > 0
    updateCurrent = ->
      window.localStorage["left-screen"] = $(".current .left").html()
      window.localStorage["right-screen"] = $(".current .right").html()

    moveToSlide = (slide) ->
      $("#presentation-list .current").removeClass("current")
      $(slide).addClass("current")
      $(document).scrollTo(slide, 500)
      updateCurrent()

    $("li:first", @).addClass("current")
    updateCurrent()

    $(document).on "keydown", (e) ->
      switch e.keyCode
        when 33, 37, 38 # Page Up, Left, Up
          e.preventDefault()
          previousItem = $("#presentation-list .current").prev()
          moveToSlide(previousItem) if previousItem?

        when 34, 39, 40 # Page Down, Right, Down
          e.preventDefault()
          nextItem = $("#presentation-list .current").next()
          moveToSlide(nextItem) if nextItem?

    $("#presentation-list li").on "click", ->
      moveToSlide(@)

  $("#presentation #left-screen, #presentation #right-screen").each ->
    updateContent = =>
      $(@).html(window.localStorage[$(@).prop("id")]).fitText 1.0,
        maxFontSize: "80px"

    setInterval updateContent, 100

$ ->
  if $("#presentation-list").length > 0
    updateCurrent = ->
      window.localStorage["left-screen"] = $(".current .left").html()
      window.localStorage["right-screen"] = $(".current .right").html()

    moveToSlide = (slide) ->
      $("#presentation-list .current").removeClass("current")
      $(slide).addClass("current")
      $(document).scrollTo(slide, 500)
      updateCurrent()

    $("li:first", @).addClass("current")
    updateCurrent()

    $(document).on "keydown", (e) ->
      switch e.keyCode
        when 33, 37, 38 # Page Up, Left, Up
          e.preventDefault()
          previousItem = $("#presentation-list .current").prev()
          moveToSlide(previousItem) if previousItem.length > 0

        when 34, 39, 40 # Page Down, Right, Down
          e.preventDefault()
          nextItem = $("#presentation-list .current").next()
          moveToSlide(nextItem) if nextItem.length > 0

    $("#presentation-list li").on "click", ->
      moveToSlide(@)

  $("#presentation #left-screen, #presentation #right-screen").each ->
    updateContent = =>
      $(@).html(window.localStorage[$(@).prop("id")]).fitText 1.0,
        maxFontSize: "80px"

    setInterval updateContent, 100

Fix bug on presentations list

Fix bug on presentations list


{$} = require 'atom-space-pen-views'
PackageManager = require '../lib/package-manager'
Q = require 'q'

describe "package updates status view", ->
  beforeEach ->
    outdatedPackage =
      name: 'out-dated'
    spyOn(PackageManager.prototype, 'loadCompatiblePackageVersion').andCallFake ->
    spyOn(PackageManager.prototype, 'getOutdated').andCallFake -> Q([outdatedPackage])
    jasmine.attachToDOM(atom.views.getView(atom.workspace))

    waitsForPromise ->
      atom.packages.activatePackage('status-bar')

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

    runs ->
      atom.packages.emitter.emit('did-activate-all')

  describe "when packages are outdated", ->
    it "adds a tile to the status bar", ->
      expect($('status-bar .package-updates-status-view').text()).toBe '1'

{$} = require 'atom-space-pen-views'
PackageManager = require '../lib/package-manager'
Q = require 'q'

describe "package updates status view", ->
  beforeEach ->
    outdatedPackage =
      name: 'out-dated'
    spyOn(PackageManager.prototype, 'loadCompatiblePackageVersion').andCallFake ->
    spyOn(PackageManager.prototype, 'getOutdated').andCallFake -> Q([outdatedPackage])
    jasmine.attachToDOM(atom.views.getView(atom.workspace))

    waitsForPromise ->
      atom.packages.activatePackage('status-bar')

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

    runs ->
      atom.packages.emitter.emit('did-activate-all')

  describe "when packages are outdated", ->
    it "adds a tile to the status bar", ->
      expect($('status-bar .package-updates-status-view').text()).toBe '1 update!'

Update spec for new status bar label

Update spec for new status bar label


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

# For more detailed documentation see
# https://atom.io/docs/latest/advanced/keymaps
'.platform-darwin atom-workspace':
  'cmd-p': 'megafinder:toggle'

'.platform-win32 atom-workspace, .platform-linux atom-workspace':
  'ctrl-alt-p': 'megafinder:toggle'

'.megafinder atom-text-editor':
  'enter': 'megafinder:confirm-selected-or-first'
  'tab': 'megafinder:autocomplete'
  'up': 'megafinder:move-cursor-up'
  'down': 'megafinder:move-cursor-down'

  # Emacs-style movement
  'ctrl-p': 'megafinder:move-cursor-up'
  'ctrl-n': 'megafinder:move-cursor-down'

  # Alternative movement keys
  'ctrl-i': 'megafinder:move-cursor-up'
  'ctrl-k': 'megafinder:move-cursor-down'

  # A more powerful version of alt-backspace
  'ctrl-l': 'megafinder:delete-path-component'

'.platform-darwin .megafinder atom-text-editor':
  'cmd-z': 'megafinder:undo'

'.platform-win32 .megafinder atom-text-editor, .platform-linux .advanced-filter-file atom-text-editor':
  'ctrl-z': 'megafinder:undo'

'.platform-darwin atom-workspace':
  'cmd-p': 'megafinder:toggle'

'.platform-win32 atom-workspace, .platform-linux atom-workspace':
  'ctrl-alt-p': 'megafinder:toggle'

'.megafinder atom-text-editor':
  'enter': 'megafinder:confirm-selected-or-first'
  'tab': 'megafinder:autocomplete'
  'up': 'megafinder:move-cursor-up'
  'down': 'megafinder:move-cursor-down'

  # Emacs-style movement
  'ctrl-p': 'megafinder:move-cursor-up'
  'ctrl-n': 'megafinder:move-cursor-down'

  # A more powerful version of alt-backspace
  'ctrl-l': 'megafinder:delete-path-component'

'.platform-darwin .megafinder atom-text-editor':
  'cmd-z': 'megafinder:undo'

'.platform-win32 .megafinder atom-text-editor, .platform-linux .advanced-filter-file atom-text-editor':
  'ctrl-z': 'megafinder:undo'

Remove keybinding cruft, make Ctrl-K work again


# variant autocompletion
$(document).ready ->
  if $("#variant_autocomplete_template").length > 0
    window.variantTemplate = Handlebars.compile($("#variant_autocomplete_template").text())
    window.variantStockTemplate = Handlebars.compile($("#variant_autocomplete_stock_template").text())
    window.variantLineItemTemplate = Handlebars.compile($("#variant_line_items_autocomplete_stock_template").text())
  return

formatVariantResult = (variant) ->
  variant.image = variant.images[0].mini_url  if variant["images"][0] isnt `undefined` and variant["images"][0].mini_url isnt `undefined`
  variantTemplate variant: variant

$.fn.variantAutocomplete = ->
  @select2
    placeholder: Spree.translations.variant_placeholder
    minimumInputLength: 3
    initSelection: (element, callback) ->
      $.get Spree.routes.variants_search + "/" + element.val(), {}, (data) ->
        callback data
    ajax:
      url: Spree.url(Spree.routes.variants_api)
      datatype: "json"
      data: (term, page) ->
        q:
          product_name_or_sku_cont: term
        token: Spree.api_key

      results: (data, page) ->
        window.variants = data["variants"]
        results: data["variants"]

    formatResult: formatVariantResult
    formatSelection: (variant) ->
      if !!variant.options_text
        variant.name + " (#{variant.options_text})"
      else
        variant.name

# variant autocompletion
$(document).ready ->
  if $("#variant_autocomplete_template").length > 0
    window.variantTemplate = Handlebars.compile($("#variant_autocomplete_template").text())
    window.variantStockTemplate = Handlebars.compile($("#variant_autocomplete_stock_template").text())
    window.variantLineItemTemplate = Handlebars.compile($("#variant_line_items_autocomplete_stock_template").text())
  return

formatVariantResult = (variant) ->
  variant.image = variant.images[0].mini_url  if variant["images"][0] isnt `undefined` and variant["images"][0].mini_url isnt `undefined`
  variantTemplate variant: variant

$.fn.variantAutocomplete = ->
  @select2
    placeholder: Spree.translations.variant_placeholder
    minimumInputLength: 3
    initSelection: (element, callback) ->
      $.get Spree.routes.variants_search + "/" + element.val(), {}, (data) ->
        callback data
    ajax:
      url: Spree.url(Spree.routes.variants_api)
      quietMillis: 200
      datatype: "json"
      data: (term, page) ->
        q:
          product_name_or_sku_cont: term
        token: Spree.api_key

      results: (data, page) ->
        window.variants = data["variants"]
        results: data["variants"]

    formatResult: formatVariantResult
    formatSelection: (variant) ->
      if !!variant.options_text
        variant.name + " (#{variant.options_text})"
      else
        variant.name

Reduce number of AJAX calls in variant autocomplete.

Fixes #6338
Fixes #5667


a = DS.attr
ETahi.TaskTemplate = DS.Model.extend
  phaseTemplate: DS.belongsTo('phaseTemplate')
  journalTaskType: DS.belongsTo('journalTaskType')
  title: Em.computed.oneWay('journalTaskType.title')

a = DS.attr
ETahi.TaskTemplate = DS.Model.extend
  phaseTemplate: DS.belongsTo('phaseTemplate')
  journalTaskType: DS.belongsTo('journalTaskType')
  title: a('string')
  template: a()

Add title and template attrs on Ember side


window.TimeAgo = React.createClass
  mixins: [SetIntervalMixin]

  getInitialState: ->
    now: Date.now()

  componentDidMount: ->
    @setInterval(@update_time, 1000)

  update_time: ->
    @setState now: Date.now()

  seconds_lapsed: ->
    (@state.now - Date.parse(@props.time)) / 1000

  showNr: (nr, unit) ->
    "#{Math.round(nr)}#{unit}"

  displayTime: (time) ->
    seconds = 1
    minutes = 60
    hours   = 60 * minutes
    days    = 24 * hours
    months  = 30* days
    years   = 365 * days
    if not time
      '?'
    else if time < 1 * minutes
      "now"
    else if time < 1*hours
      @showNr(time / minutes, 'min')
    else if time < 1*days
      @showNr(time / hours, 'h')
    else if time < 1*months
      @showNr(time / days , 'd')
    else if time < 1*years
      @showNr(time / months, 'm')
    else
      @showNr(time / years, 'y')

  render: ->
    span = _span {}, @displayTime(@seconds_lapsed())
    @transferPropsTo span

window.TimeAgo = React.createClass
  mixins: [SetIntervalMixin]

  getInitialState: ->
    now: Date.now()

  componentDidMount: ->
    @setInterval(@update_time, 1000)

  update_time: ->
    @setState now: Date.now()

  seconds_lapsed: ->
    (@state.now - Date.parse(@props.time)) / 1000

  showNr: (nr, unit) ->
    "#{Math.round(nr)}#{unit}"

  displayTime: (time) ->
    seconds = 1
    minutes = 60
    hours   = 60 * minutes
    days    = 24 * hours
    months  = 30* days
    years   = 365 * days
    if not time
      '?'
    else if time < 1 * minutes
      "now"
    else if time < 1*hours
      @showNr(time / minutes, 'm')
    else if time < 1*days
      @showNr(time / hours, 'h')
    else if time < 1*months
      @showNr(time / days , 'd')
    else if time < 1*years
      @showNr(time / months, 'mon')
    else
      @showNr(time / years, 'yr')

  render: ->
    span = _span {}, @displayTime(@seconds_lapsed())
    @transferPropsTo span

Set time ago indicators like @markijbema proposed


Tinytest.add 'passing test', (test)->
  console.log 'passing test'
  test.isTrue true

Tinytest.add 'failing test', (test)->
  console.log 'failing test'
  test.isTrue false, 'failing test message'

Tinytest.add 'throwing test', (test)->
  console.log 'throwing test'
  throw new Error('throwing test message')

Tinytest.addAsync 'async passing test', (test, done)->
  console.log 'async passing test'
  Meteor.defer ->
    test.isTrue true
    done()

Tinytest.addAsync 'async failing test', (test, done)->
  console.log 'async failing test'
  Meteor.defer ->
    test.isTrue false, 'async failing test message'
    done()

Tinytest.addAsync 'async throwing test', (test, done)->
  console.log 'async throwing test'
  Meteor.defer ->
    throw new Error('async throwing test message')
    done()

Tinytest.add 'passing test', (test)->
  console.log 'passing test'
  test.isTrue true

Tinytest.add 'failing test', (test)->
  console.log 'failing test'
  test.isTrue false, 'failing test message'

Tinytest.add 'throwing test', (test)->
  console.log 'throwing test'
  throw new Error('throwing test message')

Tinytest.addAsync 'async passing test', (test, done)->
  console.log 'async passing test'
  Meteor.defer ->
    test.isTrue true
    done()

Tinytest.addAsync 'async failing test', (test, done)->
  console.log 'async failing test'
  Meteor.defer ->
    test.isTrue false, 'async failing test message'
    done()

Tinytest.addAsync 'async throwing test', (test, done)->
  console.log 'async throwing test'
  Meteor.defer ->
    try
      throw new Error('async throwing test message')
      done()
    catch err
      test.exception(err)

Tinytest.addAsync 'async throwing test2', (test, done)->
  console.log 'async throwing test'
  Meteor.defer ->
    try
      throw new Error('async throwing test message')
      done()
    catch err
      test.exception(err)

Fix async failing test in sample app

Fix async failing test in sample app


# Description:
#   Get the CI status reported to GitHub for a repo and pull request
#
# Dependencies:
#   "githubot": "0.4.x"
#
# Configuration:
#   HUBOT_GITHUB_TOKEN
#   HUBOT_GITHUB_USER
#   HUBOT_GITHUB_API
#
# Commands:
#   hubot repo show <repo> - shows activity of repository
#
# Notes:
#   HUBOT_GITHUB_API allows you to set a custom URL path (for Github enterprise users)
#
# Author:
#   mattr-

module.exports = (robot) ->
  github = require("githubot")(robot)
  robot.respond /status (\w+\/\w+)\s*#?(\d+)/i, (msg) ->
    repo = github.qualified_repo msg.match[1]
    pr_number = msg.match[2]
    base_url = process.env.HUBOT_GITHUB_API || 'https://api.github.com'
    pull_url = "#{base_url}/repos/#{repo}/pulls/#{pr_number}"

    github.get pull_url, (pull) ->
      github.get pull.statuses_url, (status) ->
        last_status = status[0]
        msg.send "#{last_status.state} - #{last_status.target_url}"

# Description:
#   Get the CI status reported to GitHub for a repo and pull request
#
# Dependencies:
#   "githubot": "0.4.x"
#
# Configuration:
#   HUBOT_GITHUB_TOKEN
#   HUBOT_GITHUB_USER
#   HUBOT_GITHUB_API
#
# Commands:
#   hubot repo show <repo> - shows activity of repository
#
# Notes:
#   HUBOT_GITHUB_API allows you to set a custom URL path (for Github enterprise users)
#
# Author:
#   mattr-

module.exports = (robot) ->
  github = require("githubot")(robot)
  robot.respond /status (\w+\/\w+)?\s*#?(\d+)/i, (msg) ->
    repo = github.qualified_repo msg.match[1]
    pr_number = msg.match[2]
    base_url = process.env.HUBOT_GITHUB_API || 'https://api.github.com'
    pull_url = "#{base_url}/repos/#{repo}/pulls/#{pr_number}"

    github.get pull_url, (pull) ->
      github.get pull.statuses_url, (status) ->
        last_status = status[0]
        msg.send "#{last_status.state} - #{last_status.target_url}"

Allow repo to not exist :)

Allow repo to not exist :)


class Subjects
  queue: []
  api: null
  current: null
  query:
    sort: 'queued'
    page_size: "30"

  constructor: (@api, @project, @subject_set_id)->
    @query.workflow_id = @project?.links.workflows[0]
    @query.subject_set_id = @subject_set_id

  update: (opts) ->
    @[opt] = value for opt, value of opts
    @query.workflow_id = @project?.links.workflows[0]
    @query.subject_set_id = @subject_set_id

  fetch: ->
    return Promise.resolve [] unless @query.workflow_id? && @query.subject_set_id?
    @api.type('subjects')
    .get @query
    .then (newSubjects) =>
      @queue.push subject for subject in newSubjects

  flush: ->
    subject = null for subject in @queue
    @current = null
    @queue = []

  next: ->
    @current = @queue.shift()
    @fetch() if @queue.length < 2
    @current

module.exports = Subjects

class Subjects
  queue: []
  api: null
  current: null
  query:
    sort: 'queued'
    page_size: "30"

  constructor: (@api, @project, @subject_set_id)->
    @query.workflow_id = @project?.links.workflows[0]
    # @query.subject_set_id = @subject_set_id

  update: (opts) ->
    @[opt] = value for opt, value of opts
    @query.workflow_id = @project?.links.workflows[0]
    # @query.subject_set_id = @subject_set_id

  fetch: ->
    return Promise.resolve [] unless @query.workflow_id?
    @api.type('subjects')
    .get @query
    .then (newSubjects) =>
      @queue.push subject for subject in newSubjects

  flush: ->
    subject = null for subject in @queue
    @current = null
    @queue = []

  next: ->
    @current = @queue.shift()
    @fetch() if @queue.length < 2
    @current

module.exports = Subjects

Select randomly from all subject sets

Select randomly from all subject sets


INFOBAR_SIZE = 30
TILE_SIZE = 40
X_TILES = 16
Y_TILES = 12
X_TILES = 6
Y_TILES = 6
GAME_WIDTH = TILE_SIZE * X_TILES
GAME_HEIGHT = TILE_SIZE * Y_TILES + INFOBAR_SIZE

LOGGER = new Civilization.Game.Logger()

cpuCount = 2
cpuColors = [
  0x5CB85C,
  0xF0AD4E,
  0xD9534F,
  0x5BC0DE
]

cpus = []
cpus.push(new Civilization.Entity.CPU("CPU #{i}", cpuColors[i - 1])) for i in [1..cpuCount]
player = new Civilization.Entity.Player('Player 1', 0x428BCA)

LOGGER.log("#{cpus.length} CPU players have joined the game")
LOGGER.log("#{player.name} has joined the game")

Manager = new Civilization.Game.Manager(player, cpus)
document.body.appendChild(Manager.renderer.view)
Manager.start()

INFOBAR_SIZE = 30
TILE_SIZE = 40
X_TILES = 16
Y_TILES = 12
GAME_WIDTH = TILE_SIZE * X_TILES
GAME_HEIGHT = TILE_SIZE * Y_TILES + INFOBAR_SIZE

LOGGER = new Civilization.Game.Logger()

cpuCount = 2
cpuColors = [
  0x5CB85C,
  0xF0AD4E,
  0xD9534F,
  0x5BC0DE
]

cpus = []
cpus.push(new Civilization.Entity.CPU("CPU #{i}", cpuColors[i - 1])) for i in [1..cpuCount]
player = new Civilization.Entity.Player('Player 1', 0x428BCA)

LOGGER.log("#{cpus.length} CPU players have joined the game")
LOGGER.log("#{player.name} has joined the game")

Manager = new Civilization.Game.Manager(player, cpus)
document.body.appendChild(Manager.renderer.view)
Manager.start()

Revert to larger game board

Revert to larger game board


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

Inquest.Questions ||= {}

Inquest.Questions.init = ->
	new EpicEditor(
		container: $('div.epiceditor')[0],
		textarea: $($('div.epiceditor').attr('data-sync-with'))[0],
		basePath: '/assets/editor',
		clientSideStorage: false
	).load()

$ ->
	Inquest.Questions.init()

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

Inquest.Questions ||= {}
Inquest.editors ||= []

Inquest.Questions.init = ->
	Inquest.editors.push new EpicEditor(
		container: $('div.epiceditor')[0],
		textarea: $($('div.epiceditor').attr('data-sync-with'))[0],
		basePath: '/assets/editor',
		clientSideStorage: false
	).load()

$ ->
	Inquest.Questions.init()

Add all created editors to a collection of editors so they can be controlled on the page


TentAdmin.Routers.main = new class MainRouter extends Marbles.Router
  routes: {
    ""        : "root"
    "profile" : "profile"
    "apps"    : "apps"
  }

  root: =>
    @navigate('/profile', { replace: true, trigger: true })

  profile: =>
    window.scrollTo(0, 0)
    new Marbles.Views.Profile container: TentAdmin.config.container

  apps: =>
    window.scrollTo(0, 0)
    new Marbles.Views.Apps container: TentAdmin.config.container

TentAdmin.Routers.main = new class MainRouter extends Marbles.Router
  routes: {
    ""        : "root"
    "profile" : "profile"
    "apps"    : "apps"
  }

  resetScrollPosition: =>
    hash_fragment = window.location.hash
    window.scrollTo(0, 0)
    window.location.hash = hash_fragment

  root: =>
    @navigate('/profile', { replace: true, trigger: true })

  profile: =>
    new Marbles.Views.Profile container: TentAdmin.config.container
    @resetScrollPosition()

  apps: =>
    new Marbles.Views.Apps container: TentAdmin.config.container
    @resetScrollPosition()

Fix linking to id with location hash

Fix linking to id with location hash


# Exports an object that defines
#  all of the configuration needed by the projects'
#  depended-on grunt tasks.
#
# You can familiarize yourself with all of Lineman's defaults by checking out the parent file:
# https://github.com/testdouble/lineman/blob/master/config/application.coffee
#

module.exports = require(process.env['LINEMAN_MAIN']).config.extend('application', {
  removeTasks:
    common: [ "webfonts:dev", "images:dev"]
    dist: ["images:dist", "webfonts:dist", "pages:dist"]

  server:
    apiProxy:
      enabled: true
      host: 'localhost'
      port: 4567

  # enableSass: true

  # configure lineman to load additional angular related npm tasks
  loadNpmTasks: [ "grunt-ngmin" ]
  # task override configuration
  prependTasks:
    dist: ["ngmin"]         # ngmin should run in dist only

  # configuration for grunt-ngmin, this happens _after_ concat once, which is the ngmin ideal :)
  ngmin: {
    js: {
      src: "<%= files.js.concatenated %>",
      dest: "<%= files.js.concatenated %>"
    }
  },
})

# Exports an object that defines
#  all of the configuration needed by the projects'
#  depended-on grunt tasks.
#
# You can familiarize yourself with all of Lineman's defaults by checking out the parent file:
# https://github.com/testdouble/lineman/blob/master/config/application.coffee
#

module.exports = require(process.env['LINEMAN_MAIN']).config.extend('application', {
  removeTasks:
    common: [ "webfonts:dev", "images:dev"]
    dist: ["images:dist", "webfonts:dist", "pages:dist"]

  server:
    apiProxy:
      enabled: true
      host: 'localhost'
      port: 4567

  # enableSass: true

  # configure lineman to load additional angular related npm tasks
  loadNpmTasks: [ "grunt-ngmin"]

  # task override configuration
  prependTasks:
    dist: ["ngmin"]         # ngmin should run in dist only

  watch:
    scripts:
      files: ["generated/**"],
      tasks: ['copy:dev']

  copy:
    dev:
      files: [expand: true, cwd: 'generated', src: ['css/**', 'js/**', '!**/spec.js',
              '!**/*.less*', '!**/*.coffee*', '!**/*.*.map'], dest: '../lib/app/public' ]

  # configuration for grunt-ngmin, this happens _after_ concat once, which is the ngmin ideal :)
  ngmin: {
    js: {
      src: "<%= files.js.concatenated %>",
      dest: "<%= files.js.concatenated %>"
    }
  },
})

Add lineman build task to copy generated files


if typeof window isnt 'undefined'
  root = window
else
  root = global

if !root._spec_setup
  root.sinon    = require 'sinon'
  root.mockery  = require 'mockery'
  root.chai     = require 'chai'
  root.expect   = chai.expect
  root.sandbox  = sinon.sandbox.create()

  sinonChai = require 'sinon-chai'
  chai.use sinonChai

before ->
  mockery.enable useCleanCache: true
  mockery.warnOnUnregistered false
  mockery.warnOnReplace false

beforeEach ->
  root.eventric = require 'eventric'

afterEach ->
  mockery.resetCache()
  mockery.deregisterAll()
  sandbox.restore()

after ->
  mockery.disable()

root._spec_setup = true

if typeof window isnt 'undefined'
  root = window
else
  root = global

if !root._spec_setup
  root.sinon    = require 'sinon'
  root.mockery  = require 'mockery'
  root.chai     = require 'chai'
  root.expect   = chai.expect
  root.sandbox  = sinon.sandbox.create()

  sinonChai = require 'sinon-chai'
  chai.use sinonChai

before ->
  mockery.enable useCleanCache: true
  mockery.warnOnUnregistered false
  mockery.warnOnReplace false

beforeEach ->
  root.eventric = require 'eventric/src'

afterEach ->
  mockery.resetCache()
  mockery.deregisterAll()
  sandbox.restore()

after ->
  mockery.disable()

root._spec_setup = true

[EVENTRIC-43] Fix eventric include for the watcher task


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

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

  c.state = {}
  c.shutdown = ->
    c.state = {}
  noflo.helpers.WirePattern c,
    in: 'action'
    out: 'pass'
    forwardGroups: true
    async: true
  , (data, groups, out, callback) ->
    if data?.state
      # Keep track of last state
      c.state = data.state
    else
      # Warn of actions that don't contain state
      debug "#{groups.join(':')} was sent without state, using previous state"
    state = data?.state or c.state
    payload = data?.payload or data
    out.send
      state: state
      payload: payload
    do callback

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

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

  c.state = {}
  c.shutdown = ->
    c.state = {}
  noflo.helpers.WirePattern c,
    in: 'action'
    out: 'pass'
    forwardGroups: true
    async: true
  , (data, groups, out, callback) ->
    if data?.state
      # Keep track of last state
      c.state = data.state
    else
      # Warn of actions that don't contain state
      debug "#{groups.join(':')} was sent without state, using previous state"
    state = data?.state or c.state
    payload = data?.payload or data
    out.send
      action: groups.join ':'
      state: state
      payload: payload
    do callback

Send action also as property

Send action also as property


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

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

  c.inPorts.state.on 'data', (state) ->
    c.state = state

  c.state = {}
  c.shutdown = ->
    c.state = {}
  noflo.helpers.WirePattern c,
    in: 'action'
    out: 'pass'
    forwardGroups: true
    async: true
  , (data, groups, out, callback) ->
    if data?.state
      # Keep track of last state
      c.state = data.state
    else
      # Warn of actions that don't contain state
      debug "#{groups.join(':')} was sent without state, using previous state"
    state = data?.state or c.state
    payload = data?.payload or data
    out.send
      action: groups.join ':'
      state: state
      payload: payload
    do callback

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

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

  c.inPorts.state.on 'data', (state) ->
    c.state = state

  c.state = {}
  c.shutdown = ->
    c.state = {}
  noflo.helpers.WirePattern c,
    in: 'action'
    out: 'pass'
    forwardGroups: false
    async: true
  , (data, groups, out, callback) ->
    if typeof data is 'object' and data.payload and data.action
      # New-style action object
      if data.state
        # Keep track of last state
        c.state = data.state
      else
        debug "#{data.action} was sent without state, using previous state"
      out.send
        action: data.action
        state: data.state or c.state
        payload: data.payload
      do callback
      return
    # Old-style action with only payload, and action defined by brackets
    action = groups.join ':'
    debug "#{action} was sent in legacy payload-only format"
    out.send
      action: action
      state: c.state
      payload: data
    do callback

Handle new-style and legacy actions separately


_ = require 'underscore-plus'
AutocompleteView = require './autocomplete-view'

module.exports =
  configDefaults:
    includeCompletionsFromAllBuffers: false

  autocompleteViews: []
  editorSubscription: null

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

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

_ = require 'underscore-plus'
AutocompleteView = require './autocomplete-view'

module.exports =
  config:
    includeCompletionsFromAllBuffers:
      type: 'boolean'
      default: false
    whichPython:
      type: 'string'
      default: 'python'

  autocompleteViews: []
  editorSubscription: null

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

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

Update config, add default python.

Update config, add default python.


describe "Dsl",
{
	given: ->
		@target = {}
		@dsl = new Witness.Dsl @target

	when: ->
		@dsl.activate()

	then: [ # Core DSL functions are added to target object
		-> "wait" of @target
		-> "async" of @target
		-> "defineAction" of @target
		-> "defineActions" of @target
		-> typeof @target.should == "object"
	]
}

describe "Dsl",
{
	"given a DSL that will write into a target object": ->
		@target = {}
		@dsl = new Witness.Dsl @target

	"when the DSL is activated": ->
		@dsl.activate()

	"then core DSL functions are added to target object": [
		-> "wait" of @target
		-> "async" of @target
		-> "defineAction" of @target
		-> "defineActions" of @target
		-> typeof @target.should == "object"
	]
}

Add descriptions to DSL spec

Add descriptions to DSL spec


{_, Range} = require 'atom'

module.exports =
class RangeFinder

  # Public
  @rangesFor: (editor) ->
    new RangeFinder(editor).ranges()

  # Public
  constructor: (@editor) ->

  # Public
  ranges: ->
    selectionRanges = @selectionRanges()
    if _.isEmpty(selectionRanges)
      [@sortableRangeForEntireBuffer()]
    else
      _.map selectionRanges, (selectionRange) =>
        @sortableRangeFrom(selectionRange)

  # Internal
  selectionRanges: ->
    _.reject @editor.getSelectedBufferRanges(), (range) ->
      range.isEmpty()

  # Internal
  sortableRangeForEntireBuffer: ->
    @editor.getBuffer().getRange()

  # Internal
  sortableRangeFrom: (selectionRange) ->
    startRow = selectionRange.start.row
    startCol = 0
    endRow = if selectionRange.end.column == 0
      selectionRange.end.row - 1
    else
      selectionRange.end.row
    endCol = @editor.lineLengthForBufferRow(endRow)

    new Range [startRow, startCol], [endRow, endCol]

{Range} = require 'atom'

module.exports =
class RangeFinder
  # Public
  @rangesFor: (editor) ->
    new RangeFinder(editor).ranges()

  # Public
  constructor: (@editor) ->

  # Public
  ranges: ->
    selectionRanges = @selectionRanges()
    if selectionRanges.length is 0
      [@sortableRangeForEntireBuffer()]
    else
      selectionRanges.map (selectionRange) =>
        @sortableRangeFrom(selectionRange)

  # Internal
  selectionRanges: ->
    @editor.getSelectedBufferRanges().filter (range) ->
      not range.isEmpty()

  # Internal
  sortableRangeForEntireBuffer: ->
    @editor.getBuffer().getRange()

  # Internal
  sortableRangeFrom: (selectionRange) ->
    startRow = selectionRange.start.row
    startCol = 0
    endRow = if selectionRange.end.column == 0
      selectionRange.end.row - 1
    else
      selectionRange.end.row
    endCol = @editor.lineLengthForBufferRow(endRow)

    new Range [startRow, startCol], [endRow, endCol]

Use built-in array methods instead of underscore


{Subscriber} = require 'emissary'

module.exports =
class FileView extends HTMLElement
  Subscriber.includeInto(this)

  initialize: (@file) ->
    @subscribe @file, 'destroyed', => @unsubscribe()

    @classList.add('file', 'entry', 'list-item')

    @fileName = document.createElement('span')
    @appendChild(@fileName)
    @fileName.textContent = @file.name
    @fileName.setAttribute('data-name', @file.name)
    @fileName.setAttribute('data-path', @file.path)

    if @file.symlink
      @fileName.classList.add('icon-file-symlink-file')
    else
      switch @file.type
        when 'binary'     then @fileName.classList.add('icon-file-binary')
        when 'compressed' then @fileName.classList.add('icon-file-zip')
        when 'image'      then @fileName.classList.add('icon-file-media')
        when 'pdf'        then @fileName.classList.add('icon-file-pdf')
        when 'readme'     then @fileName.classList.add('icon-book')
        when 'text'       then @fileName.classList.add('icon-file-text')

    @subscribe @file, 'status-changed', => @updateStatus()
    @updateStatus()

  updateStatus: ->
    @classList.remove('status-ignored', 'status-modified',  'status-added')
    @classList.add("status-#{@file.status}") if @file.status?

  getPath: ->
    @file.path

module.exports = document.registerElement('tree-view-file', prototype: FileView.prototype, extends: 'li')

{Subscriber} = require 'emissary'

module.exports =
class FileView extends HTMLElement
  Subscriber.includeInto(this)

  initialize: (@file) ->
    @subscribe @file, 'destroyed', => @unsubscribe()

    @classList.add('file', 'entry', 'list-item')

    @fileName = document.createElement('span')
    @fileName.classList.add('name')
    @appendChild(@fileName)
    @fileName.textContent = @file.name
    @fileName.setAttribute('data-name', @file.name)
    @fileName.setAttribute('data-path', @file.path)

    if @file.symlink
      @fileName.classList.add('icon-file-symlink-file')
    else
      switch @file.type
        when 'binary'     then @fileName.classList.add('icon-file-binary')
        when 'compressed' then @fileName.classList.add('icon-file-zip')
        when 'image'      then @fileName.classList.add('icon-file-media')
        when 'pdf'        then @fileName.classList.add('icon-file-pdf')
        when 'readme'     then @fileName.classList.add('icon-book')
        when 'text'       then @fileName.classList.add('icon-file-text')

    @subscribe @file, 'status-changed', => @updateStatus()
    @updateStatus()

  updateStatus: ->
    @classList.remove('status-ignored', 'status-modified',  'status-added')
    @classList.add("status-#{@file.status}") if @file.status?

  getPath: ->
    @file.path

module.exports = document.registerElement('tree-view-file', prototype: FileView.prototype, extends: 'li')

Add name class to span

Add name class to span


{View} = require 'space-pen'
Editor = require 'editor'
$ = require 'jquery'
Point = require 'point'

module.exports =
class JumpToLineView extends View

  @activate: (rootView) -> new JumpToLineView(rootView)

  @content: ->
    @div class: 'jump-to-line', =>
      @subview 'miniEditor', new Editor(mini: true)
      @div class: 'message', outlet: 'message'

  initialize: (@rootView) ->
    @miniEditor.on 'focusout', => @detach() if @hasParent()
    @on 'core:confirm', => @confirm()
    @on 'core:cancel', => @detach() if @hasParent()

    @miniEditor.preempt 'textInput', (e) =>
      false unless e.originalEvent.data.match(/[0-9]/)

  toggle: ->
    if @hasParent()
      @detach()
    else
      @attach()

  detach: ->
    @miniEditor.setText('')
    @previouslyFocusedElement?.focus()

    super

  confirm: ->
    lineNumber = @miniEditor.getText()
    editor = rootView.getActiveEditor()

    @detach()

    return unless editor and lineNumber.length
    position = new Point(parseInt(lineNumber - 1, 0))
    editor.scrollToBufferPosition(position, center: true)
    editor.setCursorBufferPosition(position)
    editor.moveCursorToFirstCharacterOfLine()

  attach: ->
    @previouslyFocusedElement = $(':focus')
    @rootView.append(this)
    @message.text("Enter a line number 1-#{@editor.getLineCount()}")
    @miniEditor.focus()

{View} = require 'space-pen'
Editor = require 'editor'
$ = require 'jquery'
Point = require 'point'

module.exports =
class JumpToLineView extends View

  @activate: (rootView) -> new JumpToLineView(rootView)

  @content: ->
    @div class: 'jump-to-line', =>
      @subview 'miniEditor', new Editor(mini: true)
      @div class: 'message', outlet: 'message'

  initialize: (@rootView) ->
    @miniEditor.on 'focusout', => @detach() if @hasParent()
    @on 'core:confirm', => @confirm()
    @on 'core:cancel', => @detach() if @hasParent()

    @miniEditor.preempt 'textInput', (e) =>
      false unless e.originalEvent.data.match(/[0-9]/)

  toggle: ->
    if @hasParent()
      @detach()
    else
      @attach()

  detach: ->
    @miniEditor.setText('')
    @previouslyFocusedElement?.focus()

    super

  confirm: ->
    lineNumber = @miniEditor.getText()
    editor = rootView.getActiveEditor()

    @detach()

    return unless editor and lineNumber.length
    position = new Point(parseInt(lineNumber - 1, 0))
    editor.scrollToBufferPosition(position, center: true)
    editor.setCursorBufferPosition(position)
    editor.moveCursorToFirstCharacterOfLine()

  attach: ->
    @previouslyFocusedElement = $(':focus')
    @rootView.append(this)
    @message.text("Enter a line number 1-#{@rootView.getActiveEditor().getLineCount()}")
    @miniEditor.focus()

Use active editor from root view instead of ivar


describe 'Package-Deps', ->

  PackageDeps = require('../lib/main')

  describe '::packagesToInstall', ->
    it 'works', ->
      spyOn(atom.packages, 'getLoadedPackage').andReturn({
        metadata: {
          'package-deps': ['linter', 'atom-hack']
        }
      })
      atom.packages.resolvePackagePath.andCallFake((name) ->
        if name is 'linter'
          return 'some-random-stuff'
        else
          return null
      )
      retVal = PackageDeps.packagesToInstall('wow')
      expect(retVal.toEnable).toEqual(['linter'])
      expect(retVal.toInstall).toEqual(['atom-hack'])

describe 'Package-Deps', ->

  window.__steelbrain_package_deps = new Set()

  describe '::packagesToInstall', ->
    it 'works', ->
      spyOn(atom.packages, 'getLoadedPackage').andReturn({
        metadata: {
          'package-deps': ['linter', 'atom-hack']
        }
      })
      atom.packages.resolvePackagePath.andCallFake((name) ->
        if name is 'linter'
          return 'some-random-stuff'
        else
          return null
      )
      Helpers = require('../lib/helpers')
      retVal = Helpers.packagesToInstall('wow')
      expect(retVal.toEnable).toEqual(['linter'])
      expect(retVal.toInstall).toEqual(['atom-hack'])

  describe '::install', ->
    it 'works', ->
      spyOn(atom.packages, 'enablePackage').andCallFake(->)
      spyOn(atom.packages, 'activatePackage').andCallFake(->)

      # View spies
      View = require('../lib/view')
      view = {
        show: jasmine.createSpy('view.show')
        advance: jasmine.createSpy('view.advance')
      }
      spyOn(View, 'View').andReturn(view)

      # Install spies
      Helpers = require('../lib/helpers')
      spyOn(Helpers, 'installPackages').andCallFake (dependencies, successCallback) ->
        expect(dependencies).toEqual(['linter', 'linter-ruby'])
        dependencies.forEach (name) ->
          successCallback(name, true)
          atom.packages.activatePackage(name)
        return Promise.resolve()
      spyOn(Helpers, 'packagesToInstall').andReturn({
        toEnable: ['pigments'],
        toInstall: ['linter', 'linter-ruby']
      })

      # Main spies
      PackageDeps = require('../lib/main')

      waitsForPromise ->
        PackageDeps.install('some-package', true).then ->
          expect(atom.packages.enablePackage).toHaveBeenCalled()
          expect(atom.packages.enablePackage.callCount).toBe(1)
          expect(atom.packages.activatePackage).toHaveBeenCalled()
          expect(atom.packages.activatePackage.callCount).toBe(3)
          expect(view.show.callCount).toBe(1)
          expect(view.advance.callCount).toBe(2)

Add specs for main module

:new: Add specs for main module


define [
    'underscore',
    './workflows/results'
], (_, mods...) ->

    _.extend {}, mods...

define [
    'underscore'
    './workflows/results'
], (_, mods...) ->

    _.extend {}, mods...

Remove unnecessary comma in define

Remove unnecessary comma in define


Ext.apply window,
  expectToSee: (el) ->
    expect(Ext.isObject(el) || Ext.isElement(el)).to.be.ok()

  expectToNotSee: (el) ->
    expect(Ext.isString(el)).to.be.ok()

  expectDisabled: (cmp) ->
    expect(cmp.isDisabled()).to.be(true)

  expectInvisibleBodyOf: (cmp) ->
    expect(cmp.body.isVisible()).to.be false

Ext.apply window,
  expectToSee: (el) ->
    expect(Ext.isObject(el) || Ext.isElement(el)).to.be.ok()

  expectToNotSee: (el) ->
    expect(Ext.isString(el)).to.be.ok()

  expectDisabled: (cmp) ->
    throw cmp + " not found" if Ext.isString(cmp)
    expect(cmp.isDisabled()).to.be(true)

  expectInvisibleBodyOf: (cmp) ->
    throw cmp + " not found" if Ext.isString(cmp)
    expect(cmp.body.isVisible()).to.be false

Improve expectation_helper in Mocha tests


_ = require('lodash-contrib')
url = require('url')
async = require('async')
resin = require('resin-sdk')
ui = require('../ui')
log = require('../log/log')
errors = require('../errors/errors')
permissions = require('../permissions/permissions')
helpers = require('../helpers/helpers')

exports.login	= (params) ->
	async.waterfall [

		(callback) ->
			if params.credentials?
				return helpers.parseCredentials(params.credentials, callback)
			else
				return ui.widgets.login(callback)

		(credentials, callback) ->
			resin.auth.login(credentials, callback)

	], errors.handle

exports.logout = permissions.user ->
	resin.auth.logout(_.unary(errors.handle))

exports.signup = ->
	async.waterfall([

		(callback) ->
			ui.widgets.register(callback)

		(credentials, callback) ->
			resin.auth.register credentials, (error, token) ->
				return callback(error, credentials)

		(credentials, callback) ->
			credentials = _.omit(credentials, 'email')
			resin.auth.login(credentials, callback)

	], errors.handle)

exports.whoami = permissions.user ->
	resin.auth.whoami errors.handleCallback (username) ->

		if not username?
			error = new Error('Username not found')
			errors.handle(error)

		log.out(username)

_ = require('lodash-contrib')
url = require('url')
async = require('async')
resin = require('resin-sdk')
ui = require('../ui')
log = require('../log/log')
errors = require('../errors/errors')
permissions = require('../permissions/permissions')
helpers = require('../helpers/helpers')

exports.login	= (params) ->
	async.waterfall [

		(callback) ->
			if params.credentials?
				return helpers.parseCredentials(params.credentials, callback)
			else
				return ui.widgets.login(callback)

		(credentials, callback) ->
			resin.auth.login(credentials, callback)

	], errors.handle

exports.logout = permissions.user ->
	resin.auth.logout(_.unary(errors.handle))

exports.signup = ->
	async.waterfall([

		(callback) ->
			ui.widgets.register(callback)

		(credentials, callback) ->
			resin.auth.register credentials, (error, token) ->
				return callback(error, credentials)

		(credentials, callback) ->
			resin.auth.login(credentials, callback)

	], errors.handle)

exports.whoami = permissions.user ->
	resin.auth.whoami errors.handleCallback (username) ->

		if not username?
			error = new Error('Username not found')
			errors.handle(error)

		log.out(username)

Remove credentials filtering when logging in after registering


neither = require('./../lib/neither')
should = require('should')

describe 'neither', ->
  it 'should accept a boolean and return an object with nor', ->
    neither(true).should.be.an.instanceOf(neither.Neither)
    neither(true).nor.should.be.a.Function
    neither(true).test.should.be.a.Function

  describe 'nor', ->
    context 'both are false', ->
      it 'should return true', ->
        neither(false).nor(false).test().should.be.true
    context 'one is false', ->
      it 'should return false', ->
        neither(false).nor(true).test().should.be.false
    context 'both are true', ->
      it 'should return false', ->
        neither(true).nor(true).test().should.be.false

    context 'called multiple times', ->
      ( ->
        neither(true).nor(true).nor(true)
      ).should.throw('IllegalMethodException: nor cannot be called with neither/nor')

  describe '#And', ->
    it 'should delegate to indeed an and', ->
      neither(true).nor(false).And.indeed(true).and(true).test().should.be.false

  describe '#Or', ->
    it 'should delegate to indeed with an or', ->
      neither(true).nor(false).Or.else(true).or(false).test().should.be.true

  describe '#Xor', ->
    it 'should delegate to indeed with an xor', ->
      neither(true).nor(false).Xor.indeed(true).or(false).test().should.be.true

neither = require('./../lib/neither')

describe 'neither', ->
  it 'should accept a boolean and return an object with nor', ->
    neither(true).should.be.an.instanceOf(neither.Neither)
    neither(true).nor.should.be.a.Function
    neither(true).test.should.be.a.Function

  describe 'nor', ->
    context 'both are false', ->
      it 'should return true', ->
        neither(false).nor(false).test().should.be.true
    context 'one is false', ->
      it 'should return false', ->
        neither(false).nor(true).test().should.be.false
    context 'both are true', ->
      it 'should return false', ->
        neither(true).nor(true).test().should.be.false

    context 'called multiple times', ->
      ( ->
        neither(true).nor(true).nor(true)
      ).should.throw('IllegalMethodException: nor cannot be called with neither/nor')

  describe '#And', ->
    it 'should delegate to indeed an and', ->
      neither(true).nor(false).And.indeed(true).and(true).test().should.be.false

  describe '#Or', ->
    it 'should delegate to indeed with an or', ->
      neither(true).nor(false).Or.else(true).or(false).test().should.be.true

  describe '#Xor', ->
    it 'should delegate to indeed with an xor', ->
      neither(true).nor(false).Xor.indeed(true).or(false).test().should.be.true

Remove unnecessary require of should

Remove unnecessary require of should


koding = require './../bongo'

module.exports = (req, res) ->
  { JAccount, JSession } = koding.models
  {nickname} = req.params

  {clientId} = req.cookies

  JSession.fetchSession clientId, (err, result)->
    return res.status(400).end()  if err or not result

    { username } = result.session
    JAccount.one { "profile.nickname" : username }, (err, account) ->
      return res.status(400).end()  if err or not account

      unless account.can 'administer accounts'
        return res.status(403).end()

      JSession.createNewSession {
        nickname  : nickname
        # set parent group name into kookie
        groupName : result.groupName or "koding"
      }, (err, session) ->
        return res.status(400).send err.message  if err

        JSession.remove {clientId}, (err) ->
          console.error 'Could not remove session:', err  if err

          res.cookie 'clientId', session.clientId, path : '/'  if session.clientId
          res.clearCookie 'realtimeToken'
          res.status(200).send({success: yes})

koding = require './../bongo'

module.exports = (req, res) ->
  { JAccount, JSession } = koding.models
  {nickname} = req.params

  {clientId} = req.cookies

  JSession.fetchSession clientId, (err, result)->
    return res.status(400).end()  if err or not result

    { username } = result.session
    JAccount.one { "profile.nickname" : username }, (err, account) ->
      return res.status(400).end()  if err or not account

      unless account.can 'administer accounts'
        return res.status(403).end()

      JSession.createNewSession {
        username  : nickname
        # set parent group name into kookie
        groupName : result.groupName or "koding"
      }, (err, session) ->
        return res.status(400).send err.message  if err

        JSession.remove {clientId}, (err) ->
          console.error 'Could not remove session:', err  if err

          res.cookie 'clientId', session.clientId, path : '/'  if session.clientId
          res.clearCookie 'realtimeToken'
          res.status(200).send({success: yes})

Fix session user name data field

Fix session user name data field


class ResquePoller
  @INTERVAL: 2000

  constructor: (opts) ->
    @$elem      = opts.elem
    @url        = opts.url
    @intervalID = setInterval(@_poll, opts.interval || ResquePoller.INTERVAL)

  # private

  _poll: => $.getJSON @url, (resp) => @_handleResponse(resp)

  _handleResponse: (resp) ->
    return if resp.status is 'queued'
    clearInterval @intervalID if @intervalID
    switch resp.status
      when 'completed'
        @$elem.trigger 'resque:poll:stopped', resp
        @$elem.trigger 'resque:poll:success', resp
      when 'failed'
        @$elem.trigger 'resque:poll:stopped', resp
        @$elem.trigger 'resque:poll:error', resp

window.ResquePoller = ResquePoller

$ ->
  $(document).on 'ajax:before', 'form[data-resque-poll]', ->
    $(this).find('[data-resque-poll-disable-with]').each (i) ->
      $(this)
        .data('resque-poll-enable-with', $(this).val())
        .attr('disabled', 'disabled')
        .attr('value', $(this).data('resque-poll-disable-with'))

  $(document).on 'resque:poll:stopped', 'form[data-resque-poll]', ->
    $(this).find('[data-resque-poll-disable-with]').each (i) ->
      $(this)
        .removeAttr('disabled')
        .attr('value', $(this).data('resque-poll-enable-with'))

class ResquePoller
  @INTERVAL: 2000

  constructor: (opts) ->
    @$elem      = opts.elem
    @url        = opts.url
    @intervalID = setInterval(@_poll, opts.interval || ResquePoller.INTERVAL)

  # private

  _poll: => $.getJSON @url, (resp) => @_handleResponse(resp)

  _handleResponse: (resp) ->
    return if resp.status is 'queued' || resp.status is 'working'
    clearInterval @intervalID if @intervalID
    switch resp.status
      when 'completed'
        @$elem.trigger 'resque:poll:stopped', resp
        @$elem.trigger 'resque:poll:success', resp
      when 'failed'
        @$elem.trigger 'resque:poll:stopped', resp
        @$elem.trigger 'resque:poll:error', resp

window.ResquePoller = ResquePoller

$ ->
  $(document).on 'ajax:before', 'form[data-resque-poll]', ->
    $(this).find('[data-resque-poll-disable-with]').each (i) ->
      $(this)
        .data('resque-poll-enable-with', $(this).val())
        .attr('disabled', 'disabled')
        .attr('value', $(this).data('resque-poll-disable-with'))

  $(document).on 'resque:poll:stopped', 'form[data-resque-poll]', ->
    $(this).find('[data-resque-poll-disable-with]').each (i) ->
      $(this)
        .removeAttr('disabled')
        .attr('value', $(this).data('resque-poll-enable-with'))

Check for working status as well as queued


#
# Mock the Chrome extension API.
#

root = exports ? window

root.chrome = {
  extension: {
    connect: -> {
      onMessage: {
        addListener: ->
      }
      postMessage: ->
    }
    onRequest: {
      addListener: ->
    }
    sendRequest: ->
  }
}

#
# Mock the Chrome extension API.
#

root = exports ? window

root.chrome = {
  extension: {
    connect: -> {
      onMessage: {
        addListener: ->
      }
      postMessage: ->
    }
    onMessage: {
      addListener: ->
    }
    sendMessage: ->
  }
}

Add onMessage() stub to Chrome extension API mock


_ = require 'underscore-plus'
{Range} = require 'atom'

module.exports =
class Snippet
  @wordRegex: /[^\s'"<>;]+/

  name: null
  prefix: null
  body: null
  lineCount: null
  tabStops: null

  constructor: ({@name, @prefix, @bodyText, bodyTree}) ->
    @body = @extractTabStops(bodyTree)

  extractTabStops: (bodyTree) ->
    tabStopsByIndex = {}
    bodyText = []
    [row, column] = [0, 0]

    # recursive helper function; mutates vars above
    extractTabStops = (bodyTree) ->
      for segment in bodyTree
        if segment.index?
          { index, content } = segment
          index = Infinity if index == 0
          start = [row, column]
          extractTabStops(content)
          tabStopsByIndex[index] = new Range(start, [row, column])
        else if _.isString(segment)
          bodyText.push(segment)
          segmentLines = segment.split('\n')
          column += segmentLines.shift().length
          while (nextLine = segmentLines.shift())?
            row += 1
            column = nextLine.length

    extractTabStops(bodyTree)
    @lineCount = row + 1
    @tabStops = []
    for index in _.keys(tabStopsByIndex).sort()
      @tabStops.push tabStopsByIndex[index]

    bodyText.join('')

_ = require 'underscore-plus'
{Range} = require 'atom'

module.exports =
class Snippet
  @wordRegex: /[^\s'"<>;]+/

  lineCount: null
  tabStops: null

  constructor: ({@name, @prefix, @bodyText, bodyTree}) ->
    @body = @extractTabStops(bodyTree)

  extractTabStops: (bodyTree) ->
    tabStopsByIndex = {}
    bodyText = []
    [row, column] = [0, 0]

    # recursive helper function; mutates vars above
    extractTabStops = (bodyTree) ->
      for segment in bodyTree
        if segment.index?
          { index, content } = segment
          index = Infinity if index == 0
          start = [row, column]
          extractTabStops(content)
          tabStopsByIndex[index] = new Range(start, [row, column])
        else if _.isString(segment)
          bodyText.push(segment)
          segmentLines = segment.split('\n')
          column += segmentLines.shift().length
          while (nextLine = segmentLines.shift())?
            row += 1
            column = nextLine.length

    extractTabStops(bodyTree)
    @lineCount = row + 1
    @tabStops = []
    for index in _.keys(tabStopsByIndex).sort()
      @tabStops.push tabStopsByIndex[index]

    bodyText.join('')

Remove ivars set in constructor

Remove ivars set in constructor


document.addEventListener 'turbolinks:load', ->
  new AvatarCropper()

class AvatarCropper
  constructor: ->
    $('#cropbox').Jcrop
      aspectRatio: 1
      setSelect: [0, 0, 600, 600]
      boxWidth: 500
      boxHeight: 400
      onSelect: @update
      onChange: @update

  update: (coords) =>
    $('#avatar_crop_x').val(coords.x)
    $('#avatar_crop_y').val(coords.y)
    $('#avatar_crop_w').val(coords.w)
    $('#avatar_crop_h').val(coords.h)

  updatePreview: (coords) =>
    $('#preview').css
        width: Math.round(100/coords.w * $('#cropbox').width()) + 'px'
        height: Math.round(100/coords.h * $('#cropbox').height()) + 'px'
        marginLeft: '-' + Math.round(100/coords.w * coords.x) + 'px'
        marginTop: '-' + Math.round(100/coords.h * coords.y) + 'px'

document.addEventListener 'turbolinks:load', ->
  new AvatarCropper()

class AvatarCropper
  constructor: ->
    $('#cropbox').Jcrop
      aspectRatio: 1
      setSelect: [0, 0, 600, 600]
      boxWidth: window.innerWidth
      boxHeight: window.innerHeight
      onSelect: @update
      onChange: @update

  update: (coords) =>
    $('#avatar_crop_x').val(coords.x)
    $('#avatar_crop_y').val(coords.y)
    $('#avatar_crop_w').val(coords.w)
    $('#avatar_crop_h').val(coords.h)

  updatePreview: (coords) =>
    $('#preview').css
        width: Math.round(100/coords.w * $('#cropbox').width()) + 'px'
        height: Math.round(100/coords.h * $('#cropbox').height()) + 'px'
        marginLeft: '-' + Math.round(100/coords.w * coords.x) + 'px'
        marginTop: '-' + Math.round(100/coords.h * coords.y) + 'px'

Make JCrop use window height as boxWidth


NotificationPlugin = require "../../notification-plugin"

class Email extends NotificationPlugin
  SIDEKIQ_WORKER = "ErrorEmailWorker"
  SIDEKIQ_QUEUE = "error_emails"

  @receiveEvent: (config, event, callback) ->
    config.sidekiq.enqueue SIDEKIQ_WORKER, [event.trigger.type, event.error.id, config?.includeMetadata],
      retry: false
      queue: SIDEKIQ_QUEUE

    callback null

module.exports = Email

NotificationPlugin = require "../../notification-plugin"

class Email extends NotificationPlugin
  SIDEKIQ_WORKER = "ErrorEmailWorker"
  SIDEKIQ_QUEUE = "error_emails"

  @receiveEvent: (config, event, callback) ->
    sidekiq = config.sidekiq
    delete config.sidekiq

    sidekiq.enqueue SIDEKIQ_WORKER, [event.trigger.type, event.error.id, config],
      retry: false
      queue: SIDEKIQ_QUEUE

    callback null

module.exports = Email

Send entire config object into sidekiq

Send entire config object into sidekiq


# Your keymap
#
# Atom keymaps work similarly to style sheets. Just as style sheets use
# selectors to apply styles to elements, Atom keymaps use selectors to associate
# keystrokes with events in specific contexts.
#
# You can create a new keybinding in this file by typing "key" and then hitting
# tab.
#
# Here's an example taken from Atom's built-in keymap:
#
# 'atom-text-editor':
#   'enter': 'editor:newline'
#
# 'atom-workspace':
#   'ctrl-shift-p': 'core:move-up'
#   'ctrl-p': 'core:move-down'
#
# You can find more information about keymaps in these guides:
# * https://atom.io/docs/latest/customizing-atom#customizing-key-bindings
# * https://atom.io/docs/latest/advanced/keymaps
#
# This file uses CoffeeScript Object Notation (CSON).
# If you are unfamiliar with CSON, you can read more about it here:
# https://github.com/bevry/cson#what-is-cson

'atom-text-editor:not([mini])':
  'alt-s': 'sort-lines:sort'

Add keyboard shortcut for sort lines

Add keyboard shortcut for sort lines


#! /usr/bin/env coffee

yargs      = require 'yargs'
templates = require '../src/templates'

templates.show()

#! /usr/bin/env coffee

args      = require('../src/args').camelized 'template'
templates = require '../src/templates'

templates.show args.template

Use shared arg config in emplate command


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

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

  actions:
    launch: (project)->
      if window.confirm(Ember.I18n.t('projects.index.actions.are_you_sure_to_launch'))
        @send('changeState', project, 'launch')

    changeState: (project, action)->
      projectUrl = Dashboard.ApplicationAdapter.prototype.buildURL('projects', project.id)

      @get('auth').send "#{projectUrl}/#{action}",
        type: 'PUT'

      @get('target').send('refresh')

    destroy: (project)->
      if window.confirm(Ember.I18n.t('words.are_you_sure_to_delete'))
        project.destroyRecord()

        @removeObjects(project)

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

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

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

  actions:
    launch: (project)->
      if window.confirm(Ember.I18n.t('projects.index.actions.are_you_sure_to_launch'))
        @send('changeState', project, 'launch')

    changeState: (project, action)->
      projectUrl = Dashboard.ApplicationAdapter.prototype.buildURL('projects', project.id)

      $.ajax url: "#{projectUrl}/#{action}", type: 'PUT'

      @get('target').send('refresh')

    destroy: (project)->
      if window.confirm(Ember.I18n.t('words.are_you_sure_to_delete'))
        project.destroyRecord()

        @removeObjects(project)

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

Use jquery.ajax to perform state change on projects instead of auth.send


# This is a manifest file that'll be compiled into application.js, which will include all the files
# listed below.
#
# Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
# or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
#
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# compiled file.
#
# Read Sprockets README (https:#github.com/sstephenson/sprockets#sprockets-directives) for details
# about supported directives.
#
#= require jquery
#= require jquery_ujs
#= require rails-timeago
#= require jquery-notify
#= require ansi_stream
#= require_tree ./application
#= require_self

stream = new AnsiStream()

ChunkPoller.appendFormatter (chunk) ->
  stream.process(chunk)

jQuery ->
  ChunkPoller.init()

$(document).on 'click', 'a.disabled', (event) ->
  event.preventDefault()

jQuery ->
  $notificationNotice = $('.notifications')

  if $.notifyCheck() == $.NOTIFY_NOT_ALLOWED
    $button = $notificationNotice.find('button')
    $button.on 'click', ->
      $.notifyRequest()
      $notificationNotice.addClass('hidden')
    $notificationNotice.removeClass('hidden')

# This is a manifest file that'll be compiled into application.js, which will include all the files
# listed below.
#
# Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
# or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
#
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# compiled file.
#
# Read Sprockets README (https:#github.com/sstephenson/sprockets#sprockets-directives) for details
# about supported directives.
#
#= require jquery
#= require jquery_ujs
#= require rails-timeago
#= require jquery-notify
#= require ansi_stream
#= require_tree ./application
#= require_self

entityMap =
 "&": "&"
 "<": "<"
 ">": ">"
 '"': '"'
 "'": '''
 "/": '/'

escapeHtml = (string) ->
  String(string).replace(/[&<>"'\/]/g, (s) -> entityMap[s])

stream = new AnsiStream()

ChunkPoller.appendFormatter (chunk) ->
  stream.process(escapeHtml(chunk))

jQuery ->
  ChunkPoller.init()

$(document).on 'click', 'a.disabled', (event) ->
  event.preventDefault()

jQuery ->
  $notificationNotice = $('.notifications')

  if $.notifyCheck() == $.NOTIFY_NOT_ALLOWED
    $button = $notificationNotice.find('button')
    $button.on 'click', ->
      $.notifyRequest()
      $notificationNotice.addClass('hidden')
    $notificationNotice.removeClass('hidden')

Fix HTML injections in deploy output

Fix HTML injections in deploy output


_ = require 'underscore'

module.exports =
  projectPaths: null
  fuzzyFinderView: null
  loadPathsTask: null

  activate: (state) ->
    rootView.command 'fuzzy-finder:toggle-file-finder', =>
      @createView().toggleFileFinder()
    rootView.command 'fuzzy-finder:toggle-buffer-finder', =>
      @createView().toggleBufferFinder()
    rootView.command 'fuzzy-finder:find-under-cursor', =>
      @createView().findUnderCursor()

    if rootView.project.getPath()?
      LoadPathsTask = require 'fuzzy-finder/lib/load-paths-task'
      @loadPathsTask = new LoadPathsTask((paths) => @projectPaths = paths)
      @loadPathsTask.start()

    for path, lastOpened of state
      session = _.detect rootView.project.getEditSessions(), (editSession) ->
        editSession.getPath() is path
      session?.lastOpened = lastOpened

  deactivate: ->
    @loadPathsTask?.terminate()
    @loadPathsTask = null
    @fuzzyFinderView?.cancel()
    @fuzzyFinderView = null
    @projectPaths = null

  serialize: ->
    @fuzzyFinderView?.getOpenedPaths()

  createView:  ->
    unless @fuzzyFinderView
      FuzzyFinderView  = require 'fuzzy-finder/lib/fuzzy-finder-view'
      @fuzzyFinderView = new FuzzyFinderView()
      if @projectPaths? and not @fuzzyFinderView.projectPaths?
        @fuzzyFinderView.projectPaths = @projectPaths
        @fuzzyFinderView.reloadProjectPaths = false
    @fuzzyFinderView

_ = require 'underscore'

module.exports =
  projectPaths: null
  fuzzyFinderView: null
  loadPathsTask: null

  activate: (state) ->
    rootView.command 'fuzzy-finder:toggle-file-finder', =>
      @createView().toggleFileFinder()
    rootView.command 'fuzzy-finder:toggle-buffer-finder', =>
      @createView().toggleBufferFinder()
    rootView.command 'fuzzy-finder:find-under-cursor', =>
      @createView().findUnderCursor()

    if rootView.project.getPath()?
      LoadPathsTask = require 'fuzzy-finder/lib/load-paths-task'
      @loadPathsTask = new LoadPathsTask((paths) => @projectPaths = paths)
      @loadPathsTask.start()

    for editSession in rootView.project.getEditSessions()
      editSession.lastOpened = state[editSession.getPath()]

  deactivate: ->
    @loadPathsTask?.terminate()
    @loadPathsTask = null
    @fuzzyFinderView?.cancel()
    @fuzzyFinderView = null
    @projectPaths = null

  serialize: ->
    @fuzzyFinderView?.getOpenedPaths()

  createView:  ->
    unless @fuzzyFinderView
      FuzzyFinderView  = require 'fuzzy-finder/lib/fuzzy-finder-view'
      @fuzzyFinderView = new FuzzyFinderView()
      if @projectPaths? and not @fuzzyFinderView.projectPaths?
        @fuzzyFinderView.projectPaths = @projectPaths
        @fuzzyFinderView.reloadProjectPaths = false
    @fuzzyFinderView

Simplify setting lastOpened serialized value

Iterate over the project's edit sessions instead of
the stored paths in the serialized state.


# Eventful code comes here
# Program state should not be manipulated outside events files

# Please note the following conventions:
#
#  * Use jquery delegate in place of live
#    http://jupiterjs.com/news/why-you-should-never-use-jquery-live
#

# Register document events
registerDOMEvents = () ->
  state.viewport.domElement.addEventListener 'mousedown', mouseDown, true
  state.viewport.domElement.addEventListener 'mouseup', mouseUp, true
  state.viewport.domElement.addEventListener 'mousemove', mouseMove, true
  state.viewport.domElement.addEventListener 'mousewheel', mouseWheel, true
  state.viewport.domElement.addEventListener 'DOMMouseScroll', mouseWheel, true
  document.addEventListener 'keydown', keyDown, true
  window.addEventListener 'resize', windowResize, true

# Register UI controls events
registerControlEvents = () ->
  ($ '#compile').click controlsSourceCompile

# Eventful code comes here
# Program state should not be manipulated outside events files

# Please note the following conventions:
#
#  * Use jquery delegate in place of live
#    http://jupiterjs.com/news/why-you-should-never-use-jquery-live
#

# Register document events
registerDOMEvents = () ->
  state.viewport.domElement.addEventListener 'mousedown', mouseDown, true
  state.viewport.domElement.addEventListener 'mouseup', mouseUp, true
  state.viewport.domElement.addEventListener 'mousemove', mouseMove, true
  state.viewport.domElement.addEventListener 'mousewheel', mouseWheel, true
  state.viewport.domElement.addEventListener 'DOMMouseScroll', mouseWheel, true
  document.addEventListener 'keydown', keyDown, true
  window.addEventListener 'resize', windowResize, true

# Register UI controls events
registerControlEvents = () ->
  ($ '#source-compile').click controlsSourceCompile

Fix the hook for the source compile button


Subject = require 'models/subject'

module.exports = ->
  Subject.create
    id: '5077375154558fabd7000003'
    zooniverseId: 'TUTORIAL_SUBJECT'

    location: standard: [
      'images/tutorial-subject/PICT0500.JPG'
      'images/tutorial-subject/PICT0501.JPG'
      'images/tutorial-subject/PICT0502.JPG'
    ]

    coords: [2.3308, 34.8333]

    metadata:
      tutorial: true
      DateTime: [(new Date).toString()]

Subject = require 'models/subject'

module.exports = ->
  Subject.create
    id: '5077375154558fabd7000003'
    zooniverseId: 'TUTORIAL_SUBJECT'
    workflowId: '5077375154558fabd7000002'

    location: standard: [
      'images/tutorial-subject/PICT0500.JPG'
      'images/tutorial-subject/PICT0501.JPG'
      'images/tutorial-subject/PICT0502.JPG'
    ]

    coords: [2.3308, 34.8333]

    metadata:
      tutorial: true
      DateTime: [(new Date).toString()]

Add workflow ID to tutorial subject

Add workflow ID to tutorial subject


require 'coffee-cache'

beforeEach ->
  document.querySelector('#jasmine-content').innerHTML = ""

exports.keydownEvent = (keyIdentifier, {ctrl, shift, alt, cmd, which, target}={}) ->
  event = document.createEvent('KeyboardEvent')
  bubbles = true
  cancelable = true
  view = null
  keyIdentifier = keyIdentifier.toUpperCase() if /^[a-z]$/.test(keyIdentifier) and shift
  keyIdentifier = "U+#{keyIdentifier.charCodeAt(0).toString(16)}" if keyIdentifier.length is 1
  location = KeyboardEvent.DOM_KEY_LOCATION_STANDARD
  event.initKeyboardEvent('keydown', bubbles, cancelable, view,  keyIdentifier, location, ctrl, alt, shift, cmd)
  Object.defineProperty(event, 'target', get: -> target) if target?
  Object.defineProperty(event, 'which', get: -> which) if which?
  event

exports.appendContent = (element) ->
  document.querySelector('#jasmine-content').appendChild(element)
  element

require 'coffee-cache'

beforeEach ->
  document.querySelector('#jasmine-content').innerHTML = ""

exports.keydownEvent = (key, {ctrl, shift, alt, cmd, which, target}={}) ->
  event = document.createEvent('KeyboardEvent')
  bubbles = true
  cancelable = true
  view = null
  key = key.toUpperCase() if /^[a-z]$/.test(key) and shift
  if key.length is 1
    keyIdentifier = "U+#{key.charCodeAt(0).toString(16)}"
  else
    keyIdentifier = key[0].toUpperCase() + key[1..]
  location = KeyboardEvent.DOM_KEY_LOCATION_STANDARD
  event.initKeyboardEvent('keydown', bubbles, cancelable, view,  keyIdentifier, location, ctrl, alt, shift, cmd)
  Object.defineProperty(event, 'target', get: -> target) if target?
  Object.defineProperty(event, 'which', get: -> which) if which?
  event

exports.appendContent = (element) ->
  document.querySelector('#jasmine-content').appendChild(element)
  element

Make keydownEvent helper method more realistic and add some :lipstick:

The name of keyIdentifier should be captialized


# This mess is probably going to get replaced with something like:
# http://ricostacruz.com/jquery.transit/
module.exports =
  fade: ($el, options) ->
    $el.
      attr('data-state', 'fade-out').
      one($.support.transition.end, ->
        options.out() if options?.out?
        $el.
          attr('data-state', 'fade-in').
          one($.support.transition.end, ->
            options.in() if options?.in?
          ).emulateTransitionEnd 250
      ).emulateTransitionEnd 250

module.exports =
  fade: ($el, options = {}) ->
    $el.
      attr('data-state', 'fade-out').
      one($.support.transition.end, ->
        options.out() if options.out?
        $el.
          attr('data-state', 'fade-in').
          one($.support.transition.end, ->
            options.in() if options.in?
          ).emulateTransitionEnd (options.duration or 250)
      ).emulateTransitionEnd (options.duration or 250)

Allow duration to be configured

Allow duration to be configured


ROOT    = {}
sinon   = require("sinon")
assert  = require("assert")

require("../../src/environment").call(ROOT)
require("../../src/support/Widget").call(ROOT)

describe "widgets", ->
  describe "el should be overideable", ->
    before ->
      this.OptionWidget = new ROOT.Widget(
        el: "div"
      )

      this.ExtendWidget = new (ROOT.Widget.extend({
        el: "wow"
      }))

    it "should persit when passed via the constructor", ->
      assert.equal(this.OptionWidget.el, "div")

    it "should persit when passed via an extend", ->
      assert.equal(this.ExtendWidget.el, "wow")

  describe "initialize should be overideable", ->
    beforeEach ->
      this.spy = sinon.spy()

    it "should work when defined as a constructor arg", ->
      new ROOT.Widget
        initialize: this.spy

      assert(this.spy.called, true)

    it "should work when defined using the extend syntax", ->
      new (ROOT.Widget.extend
        initialize: this.spy
      )

      assert(this.spy.called, true)

ROOT    = {}
sinon   = require("sinon")
assert  = require("assert")

require("../../src/environment").call(ROOT)
require("../../src/support/Widget").call(ROOT)

describe "widgets", ->
  describe "root should be overideable", ->
    before ->
      this.OptionWidget = new ROOT.Widget(
        root: "div"
      )

      this.ExtendWidget = new (ROOT.Widget.extend({
        root: "wow"
      }))

    it "should persit when passed via the constructor", ->
      assert.equal(this.OptionWidget.root, "div")

    it "should persit when passed via an extend", ->
      assert.equal(this.ExtendWidget.root, "wow")

  describe "initialize should be overideable", ->
    beforeEach ->
      this.spy = sinon.spy()

    it "should work when defined as a constructor arg", ->
      new ROOT.Widget
        initialize: this.spy

      assert(this.spy.called, true)

    it "should work when defined using the extend syntax", ->
      new (ROOT.Widget.extend
        initialize: this.spy
      )

      assert(this.spy.called, true)

Rename unit test to update confusing test


msgflo = require 'msgflo'
path = require 'path'
chai = require 'chai' unless chai
heterogenous = require '../node_modules/msgflo/spec/heterogenous.coffee'

python = process.env.PYTHON or 'python'
participants =
  'PythonRepeat': [python, path.join __dirname, '..', 'examples', 'repeat.py']

# Note: most require running an external broker service
transports =
  'MQTT': 'mqtt://localhost'
  'AMQP': 'amqp://localhost'

transportTests = (g, address) ->

  beforeEach (done) ->
    g.broker = msgflo.transport.getBroker address
    g.broker.connect done
  afterEach (done) ->
    g.broker.disconnect done

  names = Object.keys g.commands
  names.forEach (name) ->
    heterogenous.testParticipant g, name

describe 'Participants', ->
  g =
    broker: null
    commands: participants

  Object.keys(transports).forEach (type) =>
    describe "#{type}", () ->
      address = transports[type]
      transportTests g, address

msgflo = require 'msgflo'
path = require 'path'
chai = require 'chai' unless chai
heterogenous = require '../node_modules/msgflo/spec/heterogenous.coffee'

python = process.env.PYTHON or 'python'
participants =
  'PythonRepeat': [python, path.join __dirname, '..', 'examples', 'repeat.py']

# Note: most require running an external broker service
transports =
  'MQTT': 'mqtt://localhost'
  'AMQP': 'amqp://localhost'

transportTests = (g, address) ->

  beforeEach (done) ->
    g.broker = msgflo.transport.getBroker address
    g.broker.connect done
  afterEach (done) ->
    g.broker.disconnect done

  names = Object.keys g.commands
  names.forEach (name) ->
    heterogenous.testParticipant g, name, { broker: address }

describe 'Participants', ->
  g =
    broker: null
    commands: participants

  Object.keys(transports).forEach (type) =>
    describe "#{type}", () ->
      address = transports[type]
      transportTests g, address

Fix not passing broker address

tests: Fix not passing broker address


# Select document elements using Sizzle.js.
fs = require("fs")
sizzle = fs.readFileSync(__dirname + "/../../dep/sizzle.js", "utf8")
core = require("jsdom").dom.level3.core

close = core.HTMLDocument.prototype.close
core.HTMLDocument.prototype.close = ->
  close.call this
  window = @parentWindow

  # Load Sizzle and add window.find. This only works if we parsed a document.
  if @documentElement
    ctx = process.binding("evals").Script.createContext(window)
    ctx.window = window
    ctx.document = this
    process.binding("evals").Script.runInContext sizzle, ctx
    @find = (selector, context)-> new window.Sizzle(selector, context)

# Select document elements using Sizzle.js.
fs = require("fs")
vm = process.binding("evals")
sizzle = new vm.Script(fs.readFileSync(__dirname + "/../../dep/sizzle.js", "utf8"), "sizzle.js")
core = require("jsdom").dom.level3.core

close = core.HTMLDocument.prototype.close
core.HTMLDocument.prototype.close = ->
  close.call this
  window = @parentWindow

  # Load Sizzle and add window.find. This only works if we parsed a document.
  if window && @documentElement
    ctx = process.binding("evals").Script.createContext(window)
    ctx.window = window
    ctx.document = this
    sizzle.runInContext ctx
    @find = (selector, context)-> new window.Sizzle(selector, context)

Use Script to load Sizzle.js

Use Script to load Sizzle.js


{ Model }  = require 'bongo'
{ Module } = require 'jraphical'

module.exports = class JWorkspace extends Module

  {signature, secure} = require 'bongo'
  @share()

  @set
    schema            :
      name            : String
      machineUId      : String
      rootPath        : String
      owner           : String
      layout          : Object

    sharedMethods  :
      static       :
        create     : [
          (signature Object, Function)
          (signature Object, Object, Function)
        ]
        some       :
          (signature Object, Object, Function)
      instance     :
        update     : [
          (signature Object, Function)
          (signature Object, Object, Function)
        ]
        remove     : [
          (signature Function)
          (signature Object, Function)
        ]
    sharedEvents   :
      static       : []
      instance     : []

  @create = secure (client, data, callback) ->
    data.owner = client.connection.delegate.profile.nickname
    workspace  = new JWorkspace data

    workspace.save (err) ->
      return callback err  if err
      return callback null, workspace

  some$: secure (client, query = {}, callback) ->
    query.owner = client.connection.delegate.profile.nickname
    JWorkspace.some query, callback

  update$: secure (client, data, callback) ->
    @update { $set: data }, callback

  remove$: secure (client, callback)->
    @remove callback

{ Model }  = require 'bongo'
{ Module } = require 'jraphical'

module.exports = class JWorkspace extends Module

  {signature, secure} = require 'bongo'
  @share()

  @set
    schema            :
      name            : String
      machineUId      : String
      rootPath        : String
      owner           : String
      layout          : Object

    sharedMethods  :
      static       :
        create     : signature Object, Function
        some       : signature Object, Object, Function
      instance     : []
    sharedEvents   :
      static       : []
      instance     : []

  @create = secure (client, data, callback) ->
    data.owner = client.connection.delegate.profile.nickname
    workspace  = new JWorkspace data

    workspace.save (err) ->
      return callback err  if err
      return callback null, workspace

  some$: secure (client, query = {}, callback) ->
    query.owner = client.connection.delegate.profile.nickname
    JWorkspace.some query, callback

Remove unused methods and signatures.

Workspace: Remove unused methods and signatures.


"*":
  Zen: {}
  "atom-material-ui":
    fonts: {}
  "autocomplete-plus": {}
  core:
    disabledPackages: [
      "markdown-preview"
    ]
    themes: [
      "atom-material-ui"
      "solarized-dark-syntax"
    ]
  editor:
    invisibles: {}
    softWrap: true
    softWrapAtPreferredLineLength: true
    tabLength: 4
  "exception-reporting":
    userId: "f66e1044-d904-5c31-3ec2-1cb9fec28b42"
  "file-header":
    autoAddingHeaderEnabled: false
    email: "chirag.raman@gmail.com"
    license: "Copyright (C) 2016 Multicomp Lab. All rights reserved."
    realname: "Chirag Raman"
    username: "chirag"
  linter:
    showErrorPanel: false
  "linter-jshint": {}
  minimap:
    plugins:
      "highlight-selected": true
      "highlight-selectedDecorationsZIndex": 0
  "save-session":
    dataSaveFolder: "/Users/chiragraman/.dotfiles/atom.symlink/packages/save-session/projects"
  "tool-bar":
    position: "Left"
  welcome:
    showOnStartup: false

"*":
  Zen: {}
  "atom-material-ui":
    fonts: {}
  "autocomplete-plus": {}
  core:
    customFileTypes:
      "source.cpp": [
        "h"
      ]
    disabledPackages: [
      "markdown-preview"
    ]
    themes: [
      "atom-material-ui"
      "solarized-dark-syntax"
    ]
  editor:
    invisibles: {}
    softWrap: true
    softWrapAtPreferredLineLength: true
    tabLength: 4
  "exception-reporting":
    userId: "f66e1044-d904-5c31-3ec2-1cb9fec28b42"
  "file-header":
    autoAddingHeaderEnabled: false
    email: "chirag.raman@gmail.com"
    license: "Copyright (C) 2016 Multicomp Lab. All rights reserved."
    realname: "Chirag Raman"
    username: "chirag"
  linter:
    showErrorPanel: false
  "linter-jshint": {}
  minimap:
    plugins:
      "highlight-selected": true
      "highlight-selectedDecorationsZIndex": 0
  "save-session":
    dataSaveFolder: "/Users/chiragraman/.dotfiles/atom.symlink/packages/save-session/projects"
  "tool-bar":
    position: "Left"
  welcome:
    showOnStartup: false

Update C++ custom header file type

Update C++ custom header file type


'use strict'

class Route

  ROUTES:
    root:
      title: 'Root'
      url: '/'
      partial: '#partial-home'
      init: [Account]
    notes:
      title: 'Notes'
      url: '/notes'
      partial: '#partial-note'
      init: [Note]
    about:
      title: 'About'
      url: '/about'
      partial: '#partial-home'

  constructor: ->
    navItems = document.querySelectorAll '#navigation li a'
    for item in navItems
      item.addEventListener 'click', @onClickNavItem

    window.addEventListener 'popstate', (event) =>
      state = event.state || 'root'
      @setView state

    @setView 'root'

  onClickNavItem: (event) =>
    state = event.target.id || 'root'
    window.history.pushState state, @ROUTES[state].title, @ROUTES[state].url
    @setView state

  setView: (state) ->
    partial = @ROUTES[state].partial
    Handlebars.registerPartial 'view', document.querySelector(partial).innerHTML
    template = Handlebars.compile document.querySelector('#view-template').innerHTML
    document.querySelector('#view').innerHTML = template()

    if @ROUTES[state].init?
      for klass in @ROUTES[state].init
        new klass()

window.Route = Route

'use strict'

class Route

  ROUTES:
    root:
      title: 'Root'
      url: '/'
      partial: '#partial-home'
      init: [Account]
    notes:
      title: 'Notes'
      url: '/notes'
      partial: '#partial-note'
      init: [Note]
    about:
      title: 'About'
      url: '/about'
      partial: '#partial-home'

  classPool: null

  constructor: ->
    navItems = document.querySelectorAll '#navigation li a'
    for item in navItems
      item.addEventListener 'click', @onClickNavItem

    window.addEventListener 'popstate', (event) =>
      state = event.state || 'root'
      @setView state

    @setView 'root'

  onClickNavItem: (event) =>
    state = event.target.id || 'root'
    window.history.pushState state, @ROUTES[state].title, @ROUTES[state].url
    @setView state

  setView: (state) ->
    partial = @ROUTES[state].partial
    Handlebars.registerPartial 'view', document.querySelector(partial).innerHTML
    template = Handlebars.compile document.querySelector('#view-template').innerHTML
    document.querySelector('#view').innerHTML = template()

    if @ROUTES[state].init?
      @classPool = []
      for klass in @ROUTES[state].init
        @classPool.push new klass()

    return

window.Route = Route

Add constructed instances to array

Add constructed instances to array


define ['jquery', 'semantic', 'utils', 'charts'], ($, semantic, utils, charts) ->

  currentCity = utils.getCookie('city')
  availableCity = ['Guangzhou', 'Beijing', 'Shanghai', 'Chengdu', 'Shenyang']
  currentCity = 'Guangzhou' if currentCity not in availableCity
  charts.renderChart currentCity

  # listen city click
  # FIXME: clean this quick and dirty code
  $('#Guangzhou').click () ->
    utils.setCookie 'city', 'Guangzhou', 7
    charts.renderChart 'Guangzhou'
    return false
  $('#Beijing').click () ->
    utils.setCookie 'city', 'Beijing', 7
    charts.renderChart 'Beijing'
    return false
  $('#Shanghai').click () ->
    utils.setCookie 'city', 'Shanghai', 7
    charts.renderChart 'Shanghai'
    return false
  $('#Shenyang').click () ->
    utils.setCookie 'city', 'Shenyang', 7
    charts.renderChart 'Shenyang'
    return false
  $('#Chengdu').click () ->
    utils.setCookie 'city', 'Chengdu', 7
    charts.renderChart 'Chengdu'
    return false
  $(".ui.dropdown").dropdown
    on: 'hover'
  return

define ['jquery', 'semantic', 'utils', 'charts'], ($, semantic, utils, charts) ->

  currentCity = utils.getCookie('city')
  availableCity = ['Guangzhou', 'Beijing', 'Shanghai', 'Chengdu', 'Shenyang']
  currentCity = 'Guangzhou' if currentCity not in availableCity
  charts.renderChart currentCity

  # listen city click event
  $("div.city-list").delegate "a", "click", () ->
    city = @.id
    utils.setCookie 'city', city, 7
    charts.renderChart city
    return false

  $(".ui.dropdown").dropdown
    on: 'hover'
  return

Clean up dropdown binding click event code


class VirtualizationControls extends KDButtonGroupView

  constructor:->
    options =
      cssClass      : "virt-controls"
      buttons       :
        "Start"     :
          callback  : -> log "Start machine"
        "Stop"      :
          callback  : -> log "Stop machine"
        "Turn Off"  :
          callback  : -> log "Turn off machine"

    super options

class VirtualizationControls extends KDButtonGroupView

  constructor:->
    options =
      cssClass      : "virt-controls"
      buttons       :
        "Start"     :
          callback  : ->
            KD.singletons.kiteController.run
              kiteName: 'os',
              method: 'vm.start'
        "Stop"      :
          callback  : ->
            KD.singletons.kiteController.run
              kiteName: 'os',
              method: 'vm.stop'
        "Nuke"      :
          callback  : ->
            KD.singletons.kiteController.run
              kiteName: 'os',
              method: 'vm.nuke'

    super options

Call os kite from VM buttons.

Call os kite from VM buttons.


"*":
  core:
    telemetryConsent: "limited"
  editor:
    fontFamily: "Menlo"
    invisibles: {}
    softWrap: true
  "exception-reporting":
    userId: "0f9c4bca-c1e4-c885-1374-c2dd26290ade"
  "spell-check": {}
  tabs:
    addNewTabsAtEnd: true
  welcome:
    showOnStartup: false

"*":
  core:
    telemetryConsent: "limited"
    themes: [
      "one-light-ui"
      "one-light-syntax"
    ]
  editor:
    fontFamily: "Menlo"
    invisibles: {}
    softWrap: true
  "exception-reporting":
    userId: "0f9c4bca-c1e4-c885-1374-c2dd26290ade"
  "spell-check": {}
  tabs:
    addNewTabsAtEnd: true
  welcome:
    showOnStartup: false

Switch Atom to light theme

Switch Atom to light theme


class ControlIframe
  constructor: ->
    @el = document.createElement('iframe')
    #need to append to outer document before we can access frame document.
    FactlinkJailRoot.$factlinkCoreContainer.append(@el)
    @$el = $(@el)
    @doc = @el.contentWindow.document;
    @doc.open()
    #need doctype to avoid quirks mode
    @doc.write('<!DOCTYPE html><title></title>')
    @doc.close()
    style = @doc.createElement('style')
    style.appendChild(@doc.createTextNode(FrameCss))
    @doc.head.appendChild(style)

  destroy: ->
    return unless @el
    @doc = null
    @el.parentElement?.removeChild(@el)
    @$el = @el = null

class ControlIframe
  constructor: ->
    @el = document.createElement('iframe')
    #need to append to outer document before we can access frame document.
    FactlinkJailRoot.$factlinkCoreContainer.append(@el)
    @$el = $(@el)
    @doc = @el.contentWindow.document;
    @doc.open()
    #need doctype to avoid quirks mode
    @doc.write('<!DOCTYPE html><title></title>')
    @doc.close()
    style = @doc.createElement('style')
    style.appendChild(@doc.createTextNode(FrameCss))
    @doc.head.appendChild(style)

  setContent: (contentNode) ->
    bodyEl = @doc.body
    while bodyEl.firstChild
      bodyEl.removeChild(bodyEl.firstChild)
    bodyEl.appendChild(contentNode)
  destroy: ->
    return unless @el
    @doc = null
    @el.parentElement?.removeChild(@el)
    @$el = @el = null

Implement content-setter for the control iframe