util = require 'lib/util'
network = require 'lib/network'

methodMap =
  create: 'POST'
  update: 'POST'
  delete: 'POST'
  read: 'GET'

getValue = (object, prop) ->
  if val = object?[prop]
    if _.isFunction(val) then val() else val
  else
    null

module.exports = class Sync

  constructor: (@options = {}) ->

    @options.parse ?= (response) ->
      response

  buildUrl: (url) =>

    if urlRoot = @options.urlRoot

      divider = if urlRoot.match(/\/$/) then '' else '/'

      url = url.replace /^\//, ''

      url = [urlRoot, url].join divider

    url

  sync: (method, model, options) =>

    url = @buildUrl options.url ? getValue model, 'url'

    if not options.data and model and method in ['create', 'update']

      data = JSON.stringify model.toJSON()

    if data
      contentType = 'application/json'

    network.request url,

      type: methodMap[method]

      data: data

      contentType: contentType

      timeout: options.timeout

      beforeSend: @options.beforeSend

      success: (data, status, client) =>

        data = @options.parse data

        options.success? data, status, client

util = require 'lib/util'
network = require 'lib/network'

methodMap =
  create: 'POST'
  update: 'POST'
  delete: 'POST'
  read: 'GET'

module.exports = class Sync

  constructor: (@options = {}) ->

    @options.parse ?= (response) ->
      response

  buildUrl: (url) =>

    if urlRoot = @options.urlRoot

      divider = if urlRoot.match(/\/$/) then '' else '/'

      url = url.replace /^\//, ''

      url = [urlRoot, url].join divider

    url

  sync: (method, model, options) =>

    url = @buildUrl options.url ? _.result model, 'url'

    if not options.data and model and method in ['create', 'update']

      data = JSON.stringify model.toJSON()

    if data
      contentType = 'application/json'

    network.request url,

      type: methodMap[method]

      data: data

      contentType: contentType

      timeout: options.timeout

      beforeSend: @options.beforeSend

      success: (data, status, client) =>

        data = @options.parse data

        options.success? data, status, client

Replace 'getValue' with underscore's _.result method


class Fustrate.GenericTable extends Fustrate.GenericPage
  @blankRow: null
  table: null

  initialize: =>
    super

    @reloadTable()

  reloadTable: ->

  sortRows: (rows, sortFunction = ->) ->
    ([sortFunction(row), row] for row in rows)
      .sort (x, y) -> if x[0] == y[0] then 0 else x[0] > y[0]
      .map (row) -> row[1]

  createRow: (item) =>
    @updateRow @constructor.blankRow.clone(), item

  updateRow: (row, item) ->
    row

  reloadRows: (rows, {sort} = { sort: null }) =>
    tbody = $ 'tbody', @table

    $('tr.loading', tbody).hide()

    if rows
      $('tr:not(.no-records):not(.loading)', tbody).remove()

      tbody.append if sort then @sortRows(rows, sort) else rows

    @updated()

  addRow: (row) =>
    $('tbody', @table).append row
    @updated()

  removeRow: (row) =>
    row.fadeOut =>
      row.remove()
      @updated()

  updated: =>
    $('tbody tr.no-records', @table)
      .toggle $('tbody tr:not(.no-records):not(.loading)', @table).length < 1

  # This should be fed a response from a JSON request for a paginated
  # collection.
  updatePagination: (response) =>
    $('.pagination', @root)
      .replaceWith (new Fustrate.Components.Pagination response).generate()

Add method to update pagination

Add method to update pagination

Signed-off-by: Steven Hoffman <f5bcacb8ce1651b7d1e56f2693431f20e085fdeb@valenciamgmt.com>


# See https://atom.io/docs/latest/creating-a-package#menus for more details
# 'context-menu':
#   '.overlayer':
#       'Open terminal here': 'term:open'

'menu': [
  'label': 'Packages'
  'submenu': [
    'label': 'Term 2'
    'submenu': [
      {'label': 'Open New Terminal in New Tab', 'command': 'term3:open'}
      {'label': 'Open New Terminal in Right Pane', 'command': 'term3:open-split-right'}
      {'label': 'Open New Terminal in Bottom Pane', 'command': 'term3:open-split-down'}
      {'label': 'Open New Terminal in Top Pane', 'command': 'term3:open-split-up'}
      {'label': 'Open New Terminal in Left Pane', 'command': 'term3:open-split-left'}
    ]
  ]
]
'context-menu':
  '.term': [
    {
      'label': 'Copy'
      'command': 'term3:copy'
    }
    {
      'label': 'Paste'
      'command': 'term3:paste'
    }
  ]

  '.overlayer': [
    {
      'label': 'Term3: Pipe selection to last Terminal session'
      'command': 'term3:pipe-selection'
    }
    {
      'label': 'Term3: Pipe file path to the Terminal session'
      'command': 'term3:pipe-path'
    }
  ]

# See https://atom.io/docs/latest/creating-a-package#menus for more details
# 'context-menu':
#   '.overlayer':
#       'Open terminal here': 'term:open'

'menu': [
  'label': 'Packages'
  'submenu': [
    'label': 'Term 3'
    'submenu': [
      {'label': 'Open New Terminal in New Tab', 'command': 'term3:open'}
      {'label': 'Open New Terminal in Right Pane', 'command': 'term3:open-split-right'}
      {'label': 'Open New Terminal in Bottom Pane', 'command': 'term3:open-split-down'}
      {'label': 'Open New Terminal in Top Pane', 'command': 'term3:open-split-up'}
      {'label': 'Open New Terminal in Left Pane', 'command': 'term3:open-split-left'}
    ]
  ]
]
'context-menu':
  '.term': [
    {
      'label': 'Copy'
      'command': 'term3:copy'
    }
    {
      'label': 'Paste'
      'command': 'term3:paste'
    }
  ]

  '.overlayer': [
    {
      'label': 'Term3: Pipe selection to last Terminal session'
      'command': 'term3:pipe-selection'
    }
    {
      'label': 'Term3: Pipe file path to the Terminal session'
      'command': 'term3:pipe-path'
    }
  ]

Correct package menu label to show Term 3 instead of Term 2


Ember.Application.initializer
  name: 'session'
  after: 'ember-auth-load'

  initialize: (container, application) ->
    Dashboard.Session = Ember.Object.extend(
       init: ->
        auth = container.lookup 'auth:main'

        auth.addHandler 'signInSuccess', ->
          sessionStorage.setItem('authToken', auth.get('authToken'))
          sessionStorage.setItem('authUserId', auth.get('userId'))

        authToken = sessionStorage.getItem('authToken')
        authUserId = sessionStorage.getItem('authUserId')

        if authToken != null && authUserId != null
          auth.createSession '{"access_token": "' + authToken + '", "user_id": "' + authUserId + '"}'
    ).create()

Ember.Application.initializer
  name: 'session'
  after: 'ember-auth-load'

  initialize: (container, application) ->
    Dashboard.Session = Ember.Object.extend(
       init: ->
        auth = container.lookup 'auth:main'

        auth.addHandler 'signInSuccess', ->
          sessionStorage.setItem('authToken', auth.get('authToken'))
          sessionStorage.setItem('authUserId', auth.get('userId'))

        auth.addHandler 'signOutSuccess', ->
          sessionStorage.removeItem('authToken')
          sessionStorage.removeItem('authUserId')

        authToken = sessionStorage.getItem('authToken')
        authUserId = sessionStorage.getItem('authUserId')

        if authToken != null && authUserId != null
          auth.createSession '{"access_token": "' + authToken + '", "user_id": "' + authUserId + '"}'
    ).create()

Add auth handler to remove session data when sign out


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

module.exports = (grunt) ->
  {cp, rm} = require('./task-helpers')(grunt)

  grunt.registerTask 'generate-asar', 'Generate asar archive for the app', ->
    done = @async()

    unpack = [
      '*.node'
      'ctags-darwin'
      'ctags-linux'
      'ctags-win32.exe'
      '\\.ctags'
    ]
    unpack = "{#{unpack.join(',')}}"

    appDir = grunt.config.get('atom.appDir')
    unless fs.existsSync(appDir)
      grunt.log.error 'The app has to be built before generating asar archive.'
      return done(false)

    asar.createPackageWithOptions appDir, path.resolve(appDir, '..', 'app.asar'), {unpack}, (err) ->
      return done(err) if err?

      rm appDir
      fs.renameSync path.resolve(appDir, '..', 'new-app'), appDir

      ctagsFolder = path.join("#{appDir}.asar.unpacked", 'node_modules', 'symbols-view', 'vendor')
      for ctagsFile in fs.readdirSync(ctagsFolder)
        fs.chmodSync(path.join(ctagsFolder, ctagsFile), "755")

      done()

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

module.exports = (grunt) ->
  {cp, rm} = require('./task-helpers')(grunt)

  grunt.registerTask 'generate-asar', 'Generate asar archive for the app', ->
    done = @async()

    unpack = [
      '*.node'
      '*.ctags'
      'ctags-darwin'
      'ctags-linux'
      'ctags-win32.exe'
    ]
    unpack = "{#{unpack.join(',')}}"

    appDir = grunt.config.get('atom.appDir')
    unless fs.existsSync(appDir)
      grunt.log.error 'The app has to be built before generating asar archive.'
      return done(false)

    asar.createPackageWithOptions appDir, path.resolve(appDir, '..', 'app.asar'), {unpack}, (err) ->
      return done(err) if err?

      rm appDir
      fs.renameSync path.resolve(appDir, '..', 'new-app'), appDir

      ctagsFolder = path.join("#{appDir}.asar.unpacked", 'node_modules', 'symbols-view', 'vendor')
      for ctagsFile in fs.readdirSync(ctagsFolder)
        fs.chmodSync(path.join(ctagsFolder, ctagsFile), "755")

      done()

Update .ctags pattern for unpacking

Update .ctags pattern for unpacking


kd   = require 'kd'

nick    = require 'app/util/nick'
Tracker = require 'app/util/tracker'

module.exports = (machine) ->

  return  unless analytics
  return  unless typeof analytics.user is 'function'

  { initialTurnOn } = analytics.user().traits()
  return  if initialTurnOn

  initialTurnOn = yes
  analytics.identify nick(), { initialTurnOn }

  track Tracker.BUTTON_CLICKED

  kd.singletons.computeController.once 'MachineBuilt', (event) ->

    { machineId } = event

    return  if machineId isnt machine._id

    track Tracker.MODAL_DISPLAYED

track = (action) ->
  Tracker.track action, { category: Tracker.CATEGORY_TURN_ON_VM }

kd   = require 'kd'

nick    = require 'app/util/nick'
Tracker = require 'app/util/tracker'

module.exports = (machine) ->

  return  unless analytics
  return  unless typeof analytics.user is 'function'

  { initialTurnOn } = analytics.user().traits()
  return  if initialTurnOn

  initialTurnOn = yes
  analytics.identify nick(), { initialTurnOn }
  execute machine

execute = (machine) ->

  track Tracker.BUTTON_CLICKED

  kd.singletons.computeController.once 'MachineBuilt', (event) ->

    { machineId } = event

    return  if machineId isnt machine._id

    track Tracker.MODAL_DISPLAYED

track = (action) ->
  Tracker.track action, { category: Tracker.CATEGORY_TURN_ON_VM }

Split track initial turn on function body


class @MixingpanelCookies
  constructor: ()->
    @_loadCookies()
    @cookieDefOpts =
      domain: null
      path: null
      expires_at: null

  _loadCookies: ->
    for cookie_token in document.cookie.split("; ")
      [key, value] = cookie_token.split("=")
      @[key] = value

  set: (key, value, opts = {})->
    opts  = $.extend(@cookieDefOpts, opts)
    value = value.replace(";","\\;")

    keyValue = "#{key}=#{value}"
    domain   = if opts.domain? then "; domain=#{opts.domain}" else ""
    path     = if opts.path? then "; path=#{opts.path}" else ""
    expires  = if opts.expires_at? then "; expires=#{opts.expires_at.toGMTString()}" else ""
    session  = if opts.session? then "; session=#{opts.session}" else ""

    document.cookie = (keyValue + expires + path + domain + session)
    @[key] = value

  delete: (key)->
    keyValue = "#{key}="
    expires  = "; expires=#{(new Date(0)).toGMTString()}"

    document.cookie = (keyValue + expires)
    delete @[key]

class @MixingpanelCookies
  constructor: ()->
    @_loadCookies()
    @cookieDefOpts =
      domain: null
      path: null
      expires_at: null

  _loadCookies: ->
    for cookie_token in document.cookie.split("; ")
      [key, value] = cookie_token.split("=")
      @[key] = value

  set: (key, value, opts = {})->
    opts  = $.extend(@cookieDefOpts, opts)
    value = value.replace(";","\\;")

    keyValue = "#{key}=#{value}"
    domain   = if opts.domain? then "; domain=#{opts.domain}" else ""
    path     = if opts.path? then "; path=#{opts.path}" else ""
    expires  = if opts.expires_at? then "; expires=#{opts.expires_at.toGMTString()}" else ""

    document.cookie = (keyValue + expires + path + domain + session)
    @[key] = value

  delete: (key)->
    keyValue = "#{key}="
    expires  = "; expires=#{(new Date(0)).toGMTString()}"

    document.cookie = (keyValue + expires)
    delete @[key]

Remove session from cookie set option

Remove session from cookie set option


BibtexProvider = require "./provider"

module.exports =
  config:
    bibtex:
      type: 'string'
      default: ''
    scope:
      type: 'string'
      default: '.source.gfm'
    resultTemplate:
      type: 'string'
      default: '@[key]'

  activate: ->
    @provider = new BibtexProvider()

  deactivate: ->
    @provider.registration.dispose()

  provide: ->
    return { providers: [@provider] }

BibtexProvider = require "./provider"

module.exports =
  config:
    bibtex:
      type: 'array'
      default: []
      items:
        type: 'string'
    scope:
      type: 'string'
      default: '.source.gfm'
    resultTemplate:
      type: 'string'
      default: '@[key]'

  activate: ->
    @provider = new BibtexProvider()

  deactivate: ->
    @provider.registration.dispose()

  provide: ->
    return { providers: [@provider] }

Update configuration schema to reflect that we expect an array of BibTeX file names.


root = exports ? this

root.User = class User
  @findOne: (idOrName) ->
    data = Meteor.users.findOne(idOrName)
    if not data
      data = Meteor.users.findOne({'profile.name': new RegExp('^' + idOrName + '$', 'i')})
    new User(data)

  @current: ->
    Meteor.userId() and new User(Meteor.user())

  constructor: (@data) ->

  name: -> @data.profile.name

  photoUrl: (height) ->
    if @data.services
      if @data.services.google
        picture = @data.services.google.picture
        if picture and height
          picture = "#{picture}?sz=#{height}"
      else if @data.services.github
        picture = @data.services.github.picture
        if picture and height
          picture = "#{picture}&s=#{height}"
      else if @data.services.facebook
        picture = "http://graph.facebook.com/#{@data.services.facebook.id}/picture/"
        if height
          picture = "#{picture}?height=#{height/2}"
      else if @data.services.twitter
        picture = @data.services.twitter.profile_image_url
    if not picture
      if height
        picture = Cdn.cdnify("/img/user-#{height}x#{height}.png")
      else
        picture = Cdn.cdnify('/img/user.png')
    picture

root = exports ? this

root.User = class User
  @findOne: (idOrName) ->
    data = Meteor.users.findOne(idOrName)
    if not data
      data = Meteor.users.findOne({'profile.name': new RegExp('^' + idOrName + '$', 'i')})
    new User(data)

  @current: ->
    Meteor.userId() and new User(Meteor.user())

  constructor: (@data) ->

  name: -> @data.profile.name

  photoUrl: (height) ->
    if @data.services
      if @data.services.google
        picture = @data.services.google.picture
        if picture and height
          picture = "#{picture}?sz=#{height}"
      else if @data.services.github
        picture = @data.services.github.picture
        if picture and height
          picture = "#{picture}&s=#{height}"
      else if @data.services.facebook
        picture = "http://graph.facebook.com/#{@data.services.facebook.id}/picture/"
        if height
          picture = "#{picture}?height=#{height/2}&width=#{height/2}"
      else if @data.services.twitter
        picture = @data.services.twitter.profile_image_url
    if not picture
      if height
        picture = Cdn.cdnify("/img/user-#{height}x#{height}.png")
      else
        picture = Cdn.cdnify('/img/user.png')
    picture

Fix facebook images to make sure they are squares


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

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

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

  </fieldset>
"""

Mark visible toggle switch as hidden from screen readers


define('base_layer', [], ->
  class BaseLayer
    @render: (map, opts={}) ->
      terrain = L.mapbox.tileLayer('unepwcmc.l8gj1ihl')
      satellite = L.mapbox.tileLayer('unepwcmc.lac5fjl1')

      L.control.layers({
        "Terrain": terrain,
        "Satellite": satellite
      }, null, {
        position: opts.controlPosition || 'topleft'
      }).addTo(map)

      layers = {
        "Terrain": "Terrain",
        "Satellite": "Satellite"
      }

      map.on('baselayerchange', (e) ->
        for layerName, layerId of layers
          layerControl = $("span[data-layer='#{layerId}']")

          if layerName == e.name
            layerControl.addClass("is-selected")
          else
            layerControl.removeClass("is-selected")
      )

      terrainControl = $("span[data-layer='Terrain']")
      terrainControl.addClass("is-selected")

  return BaseLayer
)

define('base_layer', [], ->
  class BaseLayer
    @render: (map, opts={}) ->
      terrain = L.mapbox.tileLayer('unepwcmc.l8gj1ihl')
      satellite = L.mapbox.tileLayer('unepwcmc.lac5fjl1')
      marine = L.mapbox.styleLayer('mapbox://styles/unepwcmc/cj63iq5m04i142rl0481raoxj')

      # add Terrain/Satellite button
      if !opts.hideControl
        L.control.layers({
          "Terrain": terrain,
          "Satellite": satellite
        }, null, {
          position: opts.controlPosition || 'topleft'
        }).addTo(map)

        layers = {
          "Terrain": "Terrain",
          "Satellite": "Satellite"
        }

        map.on('baselayerchange', (e) ->
          for layerName, layerId of layers
            layerControl = $("span[data-layer='#{layerId}']")

            if layerName == e.name
              layerControl.addClass("is-selected")
            else
              layerControl.removeClass("is-selected")
        )

        terrainControl = $("span[data-layer='Terrain']")
        terrainControl.addClass("is-selected")

      # add marine style
      if opts.style == 'marine'
        marine.addTo(map)

  return BaseLayer
)

Add option to set a style for the map and hide the terrain options


angular.module("admin.subscriptions").controller "SubscriptionController", ($scope, Subscription, SubscriptionForm, customers, schedules, paymentMethods, shippingMethods) ->
  $scope.subscription = new Subscription()
  $scope.errors = null
  $scope.save = null
  $scope.customers = customers
  $scope.schedules = schedules
  $scope.paymentMethods = paymentMethods
  $scope.shippingMethods = shippingMethods
  $scope.distributor_id = $scope.subscription.shop_id # variant selector requires distributor_id
  $scope.view = if $scope.subscription.id? then 'review' else 'details'
  $scope.nextCallbacks = {}
  $scope.backCallbacks = {}
  $scope.creditCards = []
  $scope.setView = (view) -> $scope.view = view
  $scope.stepTitleFor = (step) -> t("admin.subscriptions.steps.#{step}")
  $scope.registerNextCallback = (view, callback) => $scope.nextCallbacks[view] = callback
  $scope.registerBackCallback = (view, callback) => $scope.backCallbacks[view] = callback
  $scope.next = -> $scope.nextCallbacks[$scope.view]()
  $scope.back = -> $scope.backCallbacks[$scope.view]()

  $scope.shipAddressFromBilling = =>
    angular.extend($scope.subscription.ship_address, $scope.subscription.bill_address)

  $scope.$watch 'subscription_form', ->
    form = new SubscriptionForm($scope.subscription_form, $scope.subscription)
    $scope.errors = form.errors
    $scope.save = form.save

angular.module("admin.subscriptions").controller "SubscriptionController", ($scope, Subscription, SubscriptionForm, Customers, Schedules, PaymentMethods, ShippingMethods) ->
  $scope.subscription = new Subscription()
  $scope.errors = null
  $scope.save = null
  $scope.customers = Customers.all
  $scope.schedules = Schedules.all
  $scope.paymentMethods = PaymentMethods.all
  $scope.shippingMethods = ShippingMethods.all
  $scope.distributor_id = $scope.subscription.shop_id # variant selector requires distributor_id
  $scope.view = if $scope.subscription.id? then 'review' else 'details'
  $scope.nextCallbacks = {}
  $scope.backCallbacks = {}
  $scope.creditCards = []
  $scope.setView = (view) -> $scope.view = view
  $scope.stepTitleFor = (step) -> t("admin.subscriptions.steps.#{step}")
  $scope.registerNextCallback = (view, callback) => $scope.nextCallbacks[view] = callback
  $scope.registerBackCallback = (view, callback) => $scope.backCallbacks[view] = callback
  $scope.next = -> $scope.nextCallbacks[$scope.view]()
  $scope.back = -> $scope.backCallbacks[$scope.view]()

  $scope.shipAddressFromBilling = =>
    angular.extend($scope.subscription.ship_address, $scope.subscription.bill_address)

  $scope.$watch 'subscription_form', ->
    form = new SubscriptionForm($scope.subscription_form, $scope.subscription)
    $scope.errors = form.errors
    $scope.save = form.save

Use services to access injected data arrays


Marbles.Views.ProfileAvatar = class ProfileAvatarView extends Marbles.Views.ProfileComponent
  @template_name: '_profile_avatar'
  @view_name: 'profile_avatar'

  constructor: ->
    super

    @on 'ready', @checkImageMortality

    @render()

  checkImageMortality: =>
    img = Marbles.DOM.querySelector('img', @el)
    unless img.complete
      return setTimeout @checkImageMortality, 10

    # Fallback to default avatar if image fails to load
    if img.naturalHeight == 0 && (url = TentStatus.config.defaultAvatarURL(@get('entity')))
      img.src = url

Marbles.Views.ProfileAvatar = class ProfileAvatarView extends Marbles.Views.ProfileComponent
  @template_name: '_profile_avatar'
  @view_name: 'profile_avatar'

  constructor: ->
    super

    @on 'ready', @checkImageMortality

  checkImageMortality: =>
    img = Marbles.DOM.querySelector('img', @el)
    unless img.complete
      return setTimeout @checkImageMortality, 10

    # Fallback to default avatar if image fails to load
    if img.naturalHeight == 0 && (url = TentStatus.config.defaultAvatarURL(@get('entity')))
      img.src = url

Fix profile avatar component: Don't render until profile fetch has been attempted


DiffLine = require './diff-line'
ListItem = require '../list-item'
{git} = require '../../git'
{File} = require 'pathwatcher'

_ = require 'underscore'

##
# A DiffChunk represents one consecutive block of altered lines. The end-goal of
# breaking them out separately is to be able to stage them individually.
#
module.exports =
class DiffChunk extends ListItem
  initialize: (options) ->
    chunk = @deleteFirstLine options.chunk
    chunk = @deleteInitialWhitespace chunk
    chunk = @deleteTrailingWhitespace chunk
    @lines = _.map @splitIntoLines(chunk), (line) ->
      new DiffLine line: line

  deleteTrailingWhitespace: (chunk) ->
    chunk.replace /\s*$/, ""

  deleteFirstLine: (chunk) ->
    chunk.replace /.*?\n/, ""

  deleteInitialWhitespace: (chunk) ->
    chunk.replace /^(\s*?\n)*/, ""

  splitIntoLines: (chunk) ->
    chunk.split /\n/g

  patch: ->
    @get("header") + @get("chunk") + "\n"

  kill: ->
    (new File @patchPath()).write(@patch())
    git.git "apply --reverse #{@patchPath()}"

  stage: ->
    (new File @patchPath()).write(@patch())
    git.git "apply --cached #{@patchPath()}"

  unstage: ->
    (new File @patchPath()).write(@patch())
    git.git "apply --cached --reverse #{@patchPath()}"

  patchPath: ->
    atom.project.getRepo().getWorkingDirectory() + ".git/atomatigit_diff_patch"

path = require 'path'

DiffLine = require './diff-line'
ListItem = require '../list-item'
{git} = require '../../git'
{File} = require 'pathwatcher'

_ = require 'underscore'

##
# A DiffChunk represents one consecutive block of altered lines. The end-goal of
# breaking them out separately is to be able to stage them individually.
#
module.exports =
class DiffChunk extends ListItem
  initialize: (options) ->
    chunk = @deleteFirstLine options.chunk
    chunk = @deleteInitialWhitespace chunk
    chunk = @deleteTrailingWhitespace chunk
    @lines = _.map @splitIntoLines(chunk), (line) ->
      new DiffLine line: line

  deleteTrailingWhitespace: (chunk) ->
    chunk.replace /\s*$/, ""

  deleteFirstLine: (chunk) ->
    chunk.replace /.*?\n/, ""

  deleteInitialWhitespace: (chunk) ->
    chunk.replace /^(\s*?\n)*/, ""

  splitIntoLines: (chunk) ->
    chunk.split /\n/g

  patch: ->
    @get("header") + @get("chunk") + "\n"

  kill: ->
    (new File @patchPath()).write(@patch())
    git.git "apply --reverse #{@patchPath()}"

  stage: ->
    (new File @patchPath()).write(@patch())
    git.git "apply --cached #{@patchPath()}"

  unstage: ->
    (new File @patchPath()).write(@patch())
    git.git "apply --cached --reverse #{@patchPath()}"

  patchPath: ->
    path.join git.getPath(), ".git/atomatigit_diff_patch"

Fix junks folders created for patch file


path = require('path')

class HubotSensor
  constructor: (@freq, active = true) ->
    @_id = null
    @room = process.env.HUBOT_SENSORS_ROOM

    @setup() if active

  setup: () ->
    @enable()

  check: () ->

  fire: () ->

  notify: (text) ->
    @robot.messageRoom @room, text

  name: () ->
    @constructor.name

  info: () ->
    "Info of #{@constructor.name}"

  enable: () ->
    @_id = setInterval @_loop, @freq

  disable: () ->
    clearInterval @_id
    @_id = null

  isActive: () ->
    @_id?

  _loop: () =>
    @fire() if @check()

module.exports = HubotSensor

path = require('path')

class HubotSensor
  constructor: (@freq, active = true) ->
    @_id = null
    @room = process.env.HUBOT_SENSORS_ROOM

    @setup() if active

  setup: () ->
    @enable()

  check: () ->

  fire: () ->

  notify: (text, rooms = [@room]) ->
    @robot.messageRoom(room, text) for room in [].concat rooms

  name: () ->
    @constructor.name

  info: () ->
    "Info of #{@constructor.name}"

  enable: () ->
    @_id = setInterval @_loop, @freq

  disable: () ->
    clearInterval @_id
    @_id = null

  isActive: () ->
    @_id?

  _loop: () =>
    @fire() if @check()

module.exports = HubotSensor

Add a rooms param to notify method to notify in multiple rooms


Form = require('multiparty').Form
createWriteStream = require('fs').createWriteStream
hash = require('crypto').createHash
rand = require('crypto').pseudoRandomBytes

JFUM = module.exports = (opts) ->
  opts ?= {}

  @tmpDir = opts.tmpDir or require('os').tmpdir()
  @minFileSize = opts.minFileSize or 1
  @maxFileSize = opts.maxFileSize or 10000000000
  @acceptFileTypes = opts.acceptFileTypes or /\.(gif|jpe?g|pg)$/i

  @

JFUM.prototype.optionsHandler = (req, res, next) ->
  res.set
    'Access-Control-Allow-Origin': '*'
    'Access-Control-Allow-Methods': 'OPTIONS, POST'
    'Access-Control-Allow-Headers': 'Content-Type'
  next()

JFUM.prototype.postHandler = (req, res, next) ->
  res.set 'Access-Control-Allow-Origin', '*'

  req.jfum = null

  form = new Form()
  form.on 'error', next
  form.on 'close', next
  form.on 'part', (part) =>
    return next() if not part.filename or not @acceptFileTypes.test part.filename

    req.jfum =
      name: part.filename
      file: @tmpDir + '/jfum-' + hash('sha1').update(rand(128)).digest('hex')
      size: part.byteCount
      type: part.headers['content-type']

    write = createWriteStream req.jfum.file
    write.on 'error', next
    part.pipe write

  form.parse req

Form = require('multiparty').Form
createWriteStream = require('fs').createWriteStream
hash = require('crypto').createHash
rand = require('crypto').pseudoRandomBytes

JFUM = module.exports = (opts) ->
  opts ?= {}

  @tmpDir = opts.tmpDir or require('os').tmpdir()
  @minFileSize = opts.minFileSize or 1
  @maxFileSize = opts.maxFileSize or 10000000000
  @acceptFileTypes = opts.acceptFileTypes or /\.(gif|jpe?g|png)$/i

  @

JFUM.prototype.optionsHandler = (req, res, next) ->
  res.set
    'Access-Control-Allow-Origin': '*'
    'Access-Control-Allow-Methods': 'OPTIONS, POST'
    'Access-Control-Allow-Headers': 'Content-Type'
  next()

JFUM.prototype.postHandler = (req, res, next) ->
  res.set 'Access-Control-Allow-Origin', '*'

  req.jfum = null

  form = new Form()
  form.on 'error', next
  form.on 'close', next
  form.on 'part', (part) =>
    return next() if not part.filename or not @acceptFileTypes.test part.filename

    req.jfum =
      name: part.filename
      file: @tmpDir + '/jfum-' + hash('sha1').update(rand(128)).digest('hex')
      size: part.byteCount
      type: part.headers['content-type']

    write = createWriteStream req.jfum.file
    write.on 'error', next
    part.pipe write

  form.parse req

Fix default supported file types

Fix default supported file types


#= require gravtastic

angular.module('kassa').directive('gravatar', [
  '$parse'
  ($parse)->
    linker = ($scope, $element, $attrs)->
      email = size = null
      setGravatarSrc = (email, size)->
        if email?
          $element.attr('src', Gravtastic(email, default: 'mm', size: (size ||= 22)))

      updateEmail = (newEmail)->
        return if newEmail == email
        email = newEmail
        setGravatarSrc(email, size)
        email

      updateSize = (newSize)->
        return if newSize == size
        size = newSize
        setGravatarSrc(email, size)
        size

      $scope.$watch($attrs.gravatar, updateEmail)
      $scope.$watch($attrs.gravatarSize, updateSize)

    restrict: 'A', terminal: true, link: linker, scope: false
])

#= require gravtastic

angular.module('kassa').directive('gravatar', [
  '$parse'
  ($parse)->
    linker = ($scope, $element, $attrs)->
      email = size = null
      setGravatarSrc = (email, size)->
        email = "" unless email?
        $element.attr('src', Gravtastic(email, default: 'mm', size: (size ||= 22)))

      updateEmail = (newEmail)->
        return if newEmail == email
        email = newEmail
        setGravatarSrc(email, size)
        email

      updateSize = (newSize)->
        return if newSize == size
        size = newSize
        setGravatarSrc(email, size)
        size

      $scope.$watch($attrs.gravatar, updateEmail)
      $scope.$watch($attrs.gravatarSize, updateSize)

    restrict: 'A', terminal: true, link: linker, scope: false
])

Fix gravatar to show mystery man if no email is present


angular.module('loomioApp').factory 'MessageChannelService', ($http, Records, CommentModel, EventModel) ->
  new class MessageChannelService
    subscribeSuccess: (subscription, onMessageReceived) ->
      PrivatePub.sign(subscription)
      console.log 'subscribed to channel:', subscription.channel
      PrivatePub.subscribe subscription.channel, (data, channel) =>
        @messageReceived(data, onMessageReceived)

    subscribeTo: (channel, onMessageReceived) ->
      $http.post('/api/v1/faye/subscribe', channel: channel).then (response) =>
          subscription = response.data
          PrivatePub.sign(subscription)
          console.log 'subscribed response:', response
          PrivatePub.subscribe subscription.channel, (data, channel) =>
            @messageReceived(data, onMessageReceived)

    messageReceived: (data, onMessageReceived) ->
      console.log 'message received: ', data
      if data.memo?
        console.log 'new memo!', data.memo
        memo = data.memo
        if memo.kind == 'comment_destroyed'
          if comment = Records.comments.get(memo.data.comment_id)
            comment.destroy()
        if memo.kind == 'comment_updated'
          Records.comments.new(memo.data.comment)
          Records.import(memo.data)
        if memo.kind == 'comment_unliked'
          if comment = Records.comments.get(memo.data.comment_id)
            comment.removeLikerId(memo.data.user_id)

      if data.event?
        console.log 'new event', data.event
        Records.events.new(data.event)

      # maybe indent this one
      Records.import(data)
      onMessageReceived(data) if onMessageReceived?

angular.module('loomioApp').factory 'MessageChannelService', ($http, Records, CommentModel, EventModel) ->
  new class MessageChannelService
    subscribeSuccess: (subscription, onMessageReceived) ->
      PrivatePub.sign(subscription)
      console.log 'subscribed to channel:', subscription.channel
      PrivatePub.subscribe subscription.channel, (data, channel) =>
        @messageReceived(data, onMessageReceived)

    subscribeTo: (channel, onMessageReceived) ->
      $http.post('/api/v1/faye/subscribe', channel: channel).then (response) =>
          subscription = response.data
          PrivatePub.sign(subscription)
          console.log 'subscribed response:', response
          PrivatePub.subscribe subscription.channel, (data, channel) =>
            @messageReceived(data, onMessageReceived)

    messageReceived: (data, onMessageReceived) ->
      if data.memo?
        switch data.memo.kind
          when 'comment_destroyed'
            if comment = Records.comments.find(memo.data.comment_id)
              comment.destroy()
          when 'comment_updated'
            Records.comments.initialize(memo.data.comment)
            Records.import(memo.data)
          when 'comment_unliked'
            if comment = Records.comments.find(memo.data.comment_id)
              comment.removeLikerId(memo.data.user_id)

      if data.event?
        Records.events.initialize(data.event)

      # maybe indent this one
      Records.import(data)
      onMessageReceived(data) if onMessageReceived?

Fix live updating for discussions

Fix live updating for discussions


###
Analytics Payload Loader

This is the `analytics.js` file that loads the actual Analytics payload. It
utilizes the Friendly iFrames (FiF) technique in order to load the JavaScript
library without blocking the `window.onload` event.

@see http://goo.gl/VLDc3F More information on FiF technique
###
((url)->
  # Section 1
  iframe = document.createElement('iframe')
  iframe.src = "javascript:false"
  iframe.title = ""
  # iframe.role = "presentation" # a11y

  (iframe.frameElement or iframe).style.cssText = "width: 0; height: 0; border: 0"
  where = document.getElementsByTagName('script')
  where = where[where.length - 1]
  where.parentNode.insertBefore(iframe, where)

  # Section 2
  try
    doc = iframe.contentWindow.document
  catch err
    dom = document.domain
    iframe.src = "javascript:var d=document.open();d.domain='#{dom}';void(0);"
    doc = iframe.contentWindow.document

  doc.open()._l = ->
    js = this.createElement("script")

    this.domain = dom if(dom)
    js.id = "js-iframe-async"
    js.src = "@@base/#{url}"
    this.body.appendChild(js)

  doc.write('<body onload="window.inDapIF=true; document._l();">')
  doc.close()
)('@@payload_hash')

Add documentation for Analytics Payload Loader


init = ->
  $("[data-provider='summernote']").summernote
    height: 300
    maximumImageFileSize: 262144 # 256KB
    toolbar: [
      ['style', ['style']],
      ['font', ['bold', 'italic', 'underline', 'clear']],
      ['fontname', ['fontname']],
      ['color', ['color']],
      ['para', ['ul', 'ol', 'paragraph']],
      ['table', ['table']],
      ['insert', ['link', 'picture', 'video']],
      ['view', ['fullscreen', 'codeview', 'help']],
    ]

$(document).ready(init)

init = ->
  $("[data-provider='summernote']").summernote
    height: 300
    maximumImageFileSize: 262144 # 256KB
    callbacks: onBlurCodeview: -> $(this).val $(this).summernote('code') # Fix content not saved from codeview issue
    toolbar: [
      ['style', ['style']],
      ['font', ['bold', 'italic', 'underline', 'clear']],
      ['fontname', ['fontname']],
      ['color', ['color']],
      ['para', ['ul', 'ol', 'paragraph']],
      ['table', ['table']],
      ['insert', ['link', 'picture', 'video']],
      ['view', ['fullscreen', 'codeview', 'help']],
    ]

$(document).ready(init)

Fix content not saved from codeview

Fix content not saved from codeview


goog.provide 'app.Routes'

goog.require 'este.Routes'

class app.Routes extends este.Routes

  ###*
    @param {app.user.Store} userStore
    @constructor
    @extends {este.Routes}
  ###
  constructor: (userStore) ->
    @home = @route '/'
    @about = @route '/about'
    @songs = @route '/songs'
    @recentlyUpdatedSongs = @route '/songs/recently-updated'
    @song = @route '/:urlArtist/:urlName'

    @me = @route '/@me'
    @newSong = @route '/@me/songs/new'
    @trash = @route '/@me/songs/trash'
    @editSong = @route '/@me/songs/:id/edit'
    @mySong = @route '/@me/songs/:id'

    @api = @routes '/api',
      clientErrors: '/client-errors'
      songs: @routes '/songs',
        byUrl: '/:urlArtist/:urlName'
        id: '/:id'
        recentlyUpdated: '/recently-updated'
        search: '/search'

Add api search song route.

Add api search song route.


Flow.Browser = (_) ->
  _docs = signals []
  _hasDocs = lift _docs, (docs) -> docs.length > 0

  createDocView = ([ type, id, doc ]) ->
    _title = signal doc.title
    _date = signal new Date doc.modifiedDate
    _fromNow = lift _date, Flow.Util.fromNow

    load = ->
      _.loadNotebook id, doc

    purge = ->
      _.requestDeleteObject type, id, (error) ->
        if error
        else
          _docs.remove self

    self =
      id: id
      title: _title
      doc: doc
      date: _date
      fromNow: _fromNow
      load: load
      purge: purge

  storeNotebook = (id, doc, go) ->
    if id
      _.requestPutObject 'notebook', id, doc, (error) ->
        if error
          go error
        else
          for source, index in _docs() when source.id is id
            break
          _docs.splice index, 1, createDocView [ 'notebook', id, doc ]
          go null, id
    else
      id = uuid()
      _.requestPutObject 'notebook', id, doc, (error) ->
        if error
          go error
        else
          _docs.push createDocView [ 'notebook', id, doc ]
          go null, id

  loadNotebooks = ->
    _.requestObjects 'notebook', (error, objs) ->
      if error
        debug error
      else
        #XXX sort
        _docs map objs, createDocView

  link _.ready, ->
    loadNotebooks()

  link _.storeNotebook, storeNotebook

  docs: _docs
  hasDocs: _hasDocs
  loadNotebooks: loadNotebooks

Flow.Browser = (_) ->
  _docs = signals []

  _sortedDocs = lift _docs, (docs) ->
    sortBy docs, (doc) -> -doc.date().getTime()

  _hasDocs = lift _docs, (docs) -> docs.length > 0

  createDocView = ([ type, id, doc ]) ->
    _title = signal doc.title
    _date = signal new Date doc.modifiedDate
    _fromNow = lift _date, Flow.Util.fromNow

    load = ->
      _.loadNotebook id, doc

    purge = ->
      _.requestDeleteObject type, id, (error) ->
        if error
          debug error
        else
          _docs.remove self

    self =
      id: id
      title: _title
      doc: doc
      date: _date
      fromNow: _fromNow
      load: load
      purge: purge

  storeNotebook = (id, doc, go) ->
    if id
      _.requestPutObject 'notebook', id, doc, (error) ->
        if error
          go error
        else
          for source, index in _docs() when source.id is id
            break
          _docs.splice index, 1, createDocView [ 'notebook', id, doc ]
          go null, id
    else
      id = uuid()
      _.requestPutObject 'notebook', id, doc, (error) ->
        if error
          go error
        else
          _docs.push createDocView [ 'notebook', id, doc ]
          go null, id

  loadNotebooks = ->
    _.requestObjects 'notebook', (error, objs) ->
      if error
        debug error
      else
        #XXX sort
        _docs map objs, createDocView

  link _.ready, ->
    loadNotebooks()

  link _.storeNotebook, storeNotebook

  docs: _sortedDocs
  hasDocs: _hasDocs
  loadNotebooks: loadNotebooks

Sort files by modification time

Sort files by modification time


module.exports = ->
  # Project configuration
  @initConfig
    pkg: @file.readJSON 'package.json'

    # BDD tests on Node.js
    mochaTest:
      nodejs:
        src: ['spec/*.coffee']
        options:
          reporter: 'spec'

    # Coding standards
    coffeelint:
      components:
        files:
          src: ['spec/*.coffee', 'src/*.coffee', 'src/runtimes/*.coffee', 'src/utils/*.coffee']
        options:
          max_line_length:
            value: 100
            level: 'warn'

    # Protocol tests
    shell:
      msgflo:
        command: 'node bin/msgflo'
        options:
          async: true
      fbp_test:
        command: 'fbp-test --colors'

  # Grunt plugins used for building

  # Grunt plugins used for testing
  @loadNpmTasks 'grunt-mocha-test'
  @loadNpmTasks 'grunt-coffeelint'
  @loadNpmTasks 'grunt-shell-spawn'

  # Our local tasks
  @registerTask 'fbp-test', [
    'shell:msgflo'
    'shell:fbp_test'
    'shell:msgflo:kill'
  ]

  @registerTask 'build', 'Build the chosen target platform', (target = 'all') =>
    # nothing

  @registerTask 'test', 'Build and run automated tests', (target = 'all') =>
    @task.run 'coffeelint'
    @task.run 'build'
    @task.run 'mochaTest'
#    @task.run 'fbp-test'

  @registerTask 'default', ['test']

module.exports = ->
  # Project configuration
  @initConfig
    pkg: @file.readJSON 'package.json'

    # BDD tests on Node.js
    mochaTest:
      nodejs:
        src: ['spec/*.coffee']
        options:
          reporter: 'spec'
          grep: process.env.TESTS

    # Coding standards
    coffeelint:
      components:
        files:
          src: ['spec/*.coffee', 'src/*.coffee', 'src/runtimes/*.coffee', 'src/utils/*.coffee']
        options:
          max_line_length:
            value: 100
            level: 'warn'

    # Protocol tests
    shell:
      msgflo:
        command: 'node bin/msgflo'
        options:
          async: true
      fbp_test:
        command: 'fbp-test --colors'

  # Grunt plugins used for building

  # Grunt plugins used for testing
  @loadNpmTasks 'grunt-mocha-test'
  @loadNpmTasks 'grunt-coffeelint'
  @loadNpmTasks 'grunt-shell-spawn'

  # Our local tasks
  @registerTask 'fbp-test', [
    'shell:msgflo'
    'shell:fbp_test'
    'shell:msgflo:kill'
  ]

  @registerTask 'build', 'Build the chosen target platform', (target = 'all') =>
    # nothing

  @registerTask 'test', 'Build and run automated tests', (target = 'all') =>
    @task.run 'coffeelint'
    @task.run 'build'
    @task.run 'mochaTest'
#    @task.run 'fbp-test'

  @registerTask 'default', ['test']

Grunt: Allow to grep for tests using TESTS envvar


Pancakes.ApplicationRoute = Ember.Route.extend
  renderTemplate: (controller, model) ->
    @_super(controller, model)

    @render 'study_locations',
      into: 'application',
      outlet: 'studyLocations',
      controller: 'studyLocations'

  setupController: (controller) ->
    @_super(controller)

    @controllerFor('studyLocations').setProperties
      editable: false
      available: Pancakes.StudyLocation.find()

  redirect: ->
    @transitionTo 'event_searches.new'

# vim:ts=2:sw=2:et:tw=78

Pancakes.ApplicationRoute = Ember.Route.extend
  renderTemplate: (controller, model) ->
    @_super(controller, model)

    @render 'study_locations',
      into: 'application',
      outlet: 'studyLocations',
      controller: 'studyLocations'

  setupController: (controller) ->
    @_super(controller)

    @controllerFor('studyLocations').setProperties
      editable: false
      available: Pancakes.StudyLocation.find()

# vim:ts=2:sw=2:et:tw=78

Revert "Redirect / to /event_searches/new."

This reverts commit 0de8612155705207d54facc388020df1ceb82141.

The scope of the hook is too broad; it applies to _every_ route below
the application route.  A different approach is needed.


{open} = require 'io/file'

class Downloader
  constructor: (@url, @file, @callback) ->
    @_downloader = Cc["@mozilla.org/network/downloader;1"].createInstance()
    @_ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService)

    @_downloader.QueryInterface Ci.nsIDownloader
    @_downloader.init this, @file

  @::__defineGetter__ 'file', ->
    @_file

  @::__defineSetter__ 'file', (file) ->
    if file.path?
      @_file = file
    else
      @_file = open file

  #
  # Starts the download. This is the only public method for this class.
  #
  download: ->
    @_httpChannel = ioService.newChannel @url, '', null
    @_httpChannel.QueryInterface Ci.nsIHttpChannel
    @_httpChannel.asyncOpen @_downloader, null

  @download: (url, file, callback) ->
    (new Downloader url, file, callback).download()

  #
  # nsIDownloader delegate callback function.
  #
  onDownloadComplete: (dl, req, ctxt, status, file) ->
     try
       match = @_httpChannel.getResponseHeader('Content-Disposition').match(/filename="([^"]+)"/i)
       suggestedFilename = match?[1]
     catch err
       match = url.match(/.+\/([^\/\?]+)/)
       suggestedFilename = match?[1]

     wesabe.callback @callback, req.status is 0, [file, suggestedFilename]

     delete @_httpChannel
     delete @_downloader
     delete @_ioService
     delete @callback

module.exports = Downloader

{open} = require 'io/file'

class Downloader
  constructor: (@url, @file, @callback) ->
    @_downloader = Cc["@mozilla.org/network/downloader;1"].createInstance()
    @_ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService)

    @_downloader.QueryInterface Ci.nsIDownloader
    @_downloader.init this, @file

  @::__defineGetter__ 'file', ->
    @_file

  @::__defineSetter__ 'file', (file) ->
    if file.path?
      @_file = file
    else
      @_file = open file

  #
  # Starts the download. This is the only public method for this class.
  #
  download: ->
    @_httpChannel = @_ioService.newChannel @url, '', null
    @_httpChannel.QueryInterface Ci.nsIHttpChannel
    @_httpChannel.asyncOpen @_downloader, null

  @download: (url, file, callback) ->
    (new Downloader url, file, callback).download()

  #
  # nsIDownloader delegate callback function.
  #
  onDownloadComplete: (dl, req, ctxt, status, file) ->
     try
       match = @_httpChannel.getResponseHeader('Content-Disposition').match(/filename="([^"]+)"/i)
       suggestedFilename = match?[1]
     catch err
       match = url.match(/.+\/([^\/\?]+)/)
       suggestedFilename = match?[1]

     wesabe.callback @callback, req.status is 0, [file, suggestedFilename]

     delete @_httpChannel
     delete @_downloader
     delete @_ioService
     delete @callback

module.exports = Downloader

Fix regression when downloading a specific URL.

Copy-paste restructuring error. It used to be all in one function, now it's an ivar.


@XBlock =
  runtime: {}

  initializeBlock: (element) ->
    $element = $(element)
    children = @initializeBlocks($element)
    version = $element.data("runtime-version")
    initFnName = $element.data("init")
    if version? and initFnName?
      runtime = @runtime["v#{version}"](element, children)
      initFn = window[initFnName]
      block = initFn(runtime, element) ? {}
    else
      elementTag = $('<div>').append($element.clone()).html();
      console.log("Block #{elementTag} is missing data-runtime-version or data-init, and can't be initialized")
      block = {}

    block.element = element
    block.name = $element.data("name")

    block

  initializeBlocks: (element) ->
    $(element).immediateDescendents(".xblock").map((idx, elem) =>
      @initializeBlock elem
    ).toArray()

@XBlock =
  runtime: {}

  initializeBlock: (element) ->
    $element = $(element)
    children = @initializeBlocks($element)
    version = $element.data("runtime-version")
    initFnName = $element.data("init")
    if version? and initFnName?
      runtime = @runtime["v#{version}"](element, children)
      initFn = window[initFnName]
      block = initFn(runtime, element) ? {}
    else
      elementTag = $('<div>').append($element.clone()).html();
      console.log("Block #{elementTag} is missing data-runtime-version or data-init, and can't be initialized")
      block = {}

    block.element = element
    block.name = $element.data("name")

    $element.trigger("xblock-initialized")
    $element.data("initialized", true)
    block

  initializeBlocks: (element) ->
    $(element).immediateDescendents(".xblock").map((idx, elem) =>
      @initializeBlock elem
    ).toArray()

Add information to xblocks about when they complete initialization that's visible in javascript


request   = require 'supertest'
assert    = require 'assert'

req = request require '../../coffee/server'

describe '/CloudHealthCheck', ->
  it 'should return OK for MongoDB and Redis', (done) ->
    req.get '/CloudHealthCheck'
      .expect 200
      .expect (res) ->
        assert.deepEqual res.body, message: 'System OK'
      .end done

request   = require 'supertest'
assert    = require 'assert'

req = request require '../../coffee/server'

describe '/CloudHealthCheck', ->
  it 'should return 200 OK for GET request', (done) ->
    req.get '/CloudHealthCheck'
      .expect 200
      .expect (res) ->
        assert.deepEqual res.body, message: 'System OK'
      .end done

  it 'should return 200 OK for HEAD request', (done) ->
    req.get('/CloudHealthCheck').expect(200).end done

Add integration test for HEAD /CloudHealthCheck


expect = require('chai').expect
fs     = require('fs')

Parser = require('../lib/parser')

describe 'Parser', ->
  dummy  = fs.createReadStream('./test/fixtures/dummy.html')
  parser = new Parser(dummy)

  it 'lists available years', (done) ->
    parser.getYears (err, years) ->
      expect(err).to.be.null
      expect(years).to.be.eql years: ['2001', '2002', '2003', '2004']
      done()

expect = require('chai').expect
fs     = require('fs')

Parser = require('../lib/parser')

describe 'Parser', ->
  beforeEach ->
    @dummy  = fs.createReadStream('./test/fixtures/dummy.html')
    @parser = new Parser(@dummy)

  it 'lists available years', (done) ->
    @parser.getYears (err, years) ->
      expect(err).to.be.null
      expect(years).to.be.eql years: ['2001', '2002', '2003', '2004']
      done()

Use properties instead of local variables


try
  require '../src/window'
  Atom = require '../src/atom'
  window.atom = new Atom()
  window.atom.show() unless atom.getLoadSettings().exitWhenDone
  {runSpecSuite} = require './jasmine-helper'

  document.title = "Spec Suite"
  runSpecSuite './spec-suite'
catch error
  unless atom.getLoadSettings().exitWhenDone
    atom.getCurrentWindow().setSize(800, 600)
    atom.getCurrentWindow().center()
    atom.openDevTools()

  console.error(error.stack ? error)
  atom.exit(1) if atom.getLoadSettings().exitWhenDone

try
  require '../src/window'
  Atom = require '../src/atom'
  window.atom = new Atom()
  window.atom.show() unless atom.getLoadSettings().exitWhenDone
  {runSpecSuite} = require './jasmine-helper'

  document.title = "Spec Suite"
  runSpecSuite './spec-suite'
catch error
  if atom?.getLoadSettings().exitWhenDone
    atom.exit(1)
  else
    throw error

Remove window handling in catch block

Remove window handling in catch block

The catch block in index.html takes care of this


{CompositeDisposable} = require 'atom'
RestructuredPreviewView = require './restructured-preview-view'
url = require 'url'

module.exports = RestructuredPreview =
  restructuredPreviewView: null
  modalPanel: null
  subscriptions: null

  activate: (state) ->
    @subscriptions = new CompositeDisposable
    @subscriptions.add atom.commands.add 'atom-workspace', 'restructured-preview:show-preview': => @toggle()

    atom.workspace.addOpener( (uri) ->
      if /^restructured-preview:.*.rst$/.test(uri)
        view = new RestructuredPreviewView(uri)
        result = url.parse(uri)
        child = require('child_process').exec(
          "python #{__dirname}/rst2html.py #{result.pathname}", (error, stdout, stderr) ->
            view.setText(stdout)
        )

        console.log "#{view}"
        return view
    )

  deactivate: ->
    @subscriptions.dispose()

  serialize: ->

  toggle: ->
    editor = atom.workspace.getActiveTextEditor()
    path = editor.getPath()
    atom.workspace.open("restructured-preview://#{path}")

{CompositeDisposable} = require 'atom'
RestructuredPreviewView = require './restructured-preview-view'
url = require 'url'

module.exports = RestructuredPreview =
  restructuredPreviewView: null
  modalPanel: null
  subscriptions: null

  activate: (state) ->
    @subscriptions = new CompositeDisposable
    @subscriptions.add atom.commands.add 'atom-workspace', 'restructured-preview:show-preview': => @toggle()

    atom.workspace.addOpener( (uri) ->
      if /^restructured-preview:.*.rst$/.test(uri)
        view = new RestructuredPreviewView(uri)
        result = url.parse(uri)
        child = require('child_process').exec(
          "python #{__dirname}/rst2html.py #{result.pathname}", (error, stdout, stderr) ->
            view.setText(stdout)
        )

        console.log "#{view}"
        return view
    )

  deactivate: ->
    @subscriptions.dispose()

  serialize: ->

  toggle: ->
    editor = atom.workspace.getActiveTextEditor()
    path = editor.getPath()
    options =
      split: 'right'
      searchAllPanes: true
    atom.workspace.open("restructured-preview://#{path}", options)

Split to the right when opening preview

Split to the right when opening preview


App.Forms =

  disableEnter: ->
    $('form.js-enter-disabled').on('keyup keypress', (event) ->
      if event.which == 13
        e.preventDefault()
    )

  submitOnChange: ->
    $('.js-submit-on-change').unbind('change').on('change', ->
      $(this).closest('form').submit()
      false
    )

  toggleLink: ->
    $('.js-toggle-link').unbind('click').on('click', ->
      $($(this).data('toggle-selector')).toggle("down")
      if $(this).data('toggle-text') isnt undefined
        toggle_txt = $(this).text()
        $(this).text( $(this).data('toggle-text')  )
        $(this).data('toggle-text', toggle_txt)

      false
    )

  synchronizeInputs: ->
    $("[name='progress_bar[percentage]']").on
      input: ->
        $("[name='#{this.name}']").val($(this).val())

  hideOrShowFieldsAfterSelection: ->
    $("[name='progress_bar[kind]']").on
      change: ->
        title_field = $("[name^='progress_bar'][name$='[title]']").parent()

        if this.value == "primary"
          title_field.hide()
        else
          title_field.show()

    $("[name='progress_bar[kind]']").change()

  initialize: ->
    App.Forms.disableEnter()
    App.Forms.submitOnChange()
    App.Forms.toggleLink()
    App.Forms.synchronizeInputs()
    App.Forms.hideOrShowFieldsAfterSelection()
    false

App.Forms =

  disableEnter: ->
    $('form.js-enter-disabled').on('keyup keypress', (event) ->
      if event.which == 13
        e.preventDefault()
    )

  submitOnChange: ->
    $('.js-submit-on-change').unbind('change').on('change', ->
      $(this).closest('form').submit()
      false
    )

  toggleLink: ->
    $('.js-toggle-link').unbind('click').on('click', ->
      $($(this).data('toggle-selector')).toggle("down")
      if $(this).data('toggle-text') isnt undefined
        toggle_txt = $(this).text()
        $(this).text( $(this).data('toggle-text')  )
        $(this).data('toggle-text', toggle_txt)

      false
    )

  synchronizeInputs: ->
    $("[name='progress_bar[percentage]']").on
      input: ->
        $("[name='#{this.name}']").val($(this).val())

    $("[name='progress_bar[percentage]'][type='range']").trigger("input")

  hideOrShowFieldsAfterSelection: ->
    $("[name='progress_bar[kind]']").on
      change: ->
        title_field = $("[name^='progress_bar'][name$='[title]']").parent()

        if this.value == "primary"
          title_field.hide()
        else
          title_field.show()

    $("[name='progress_bar[kind]']").change()

  initialize: ->
    App.Forms.disableEnter()
    App.Forms.submitOnChange()
    App.Forms.toggleLink()
    App.Forms.synchronizeInputs()
    App.Forms.hideOrShowFieldsAfterSelection()
    false

Synchronize percentage for new progress bars

According to the HTML specification:

> The default value is the minimum plus half the difference between the
> minimum and the maximum, unless the maximum is less than the minimum,
> in which case the default value is the minimum.

So for new progress bars, we had a numeric value of `nil` and a range
value of `50`, meaning the input fields weren't in sync.

Manually triggering the event on the progress, while not an ideal
solution (ideally we would be able to define `0` as default), sets the
value of the numeric field.


jQuery ->
  $(".clickable").live "click", (e) ->
    window.location.href = $(this).find("a.primary-link").attr("href")

  $(".collapsible")
    .hover ->
      $(this).find(".collapse").slideDown()
    , ->
      $(this).find(".collapse").slideUp()
    .find(".collapse").hide()

  $(":input.date").dateinput
    format: "dddd, dd mmmm yyyy"

jQuery ->
  # Crude way to make large blocks .clickable by definiting a.primary-link in them
  $(".clickable").live "click", (e) ->
    window.location.href = $(this).find("a.primary-link").attr("href")

  # When .collapsible item is hovered in/out the .collapse elements inside
  # expand and collapse
  $(".collapsible")
    .hover ->
      $(this).find(".collapse").slideDown()
    , ->
      $(this).find(".collapse").slideUp()
    .find(".collapse").hide()

  # Apply date selector to all date inputs
  $(":input.date").dateinput
    format: "dddd, dd mmmm yyyy"

  # When a select box is changed search for other selects that
  # are linked via the autoset and autoset-param data attributes
  # and update them with the new value.
  $(document).on "change", "select", ->
    source_select = $ this
    $("select[data-autoset='##{this.id}']").each ->
      target_select = $ this
      param = target_select.data("autoset-param")
      new_value = source_select.find("option:selected").data(param)
      console.info source_select, target_select, param, new_value
      target_select.val(new_value)

  # When a select box is changed find any dependent elements and
  # hide or show based on whether the new value is blank or not.
  $(document).on "change", "select", ->
    source_select = $ this
    $("*[data-dependent='##{this.id}']").each ->
      target = $ this
      if source_select.val() != ""
        target.show()
      else
        target.hide()

Add autoset and dependent methods to select inputs.

* Autoset allows one select to influence another.
* Dependent shows or hides elements based on the value of a select.


".source.ruby":
  "Hashrocket":
    prefix: "="
    body: " => "

  "Context block":
    prefix: "context"
    body: """
      context "$1" do
        $2
      end
    """

  "Test block":
    prefix: "test"
    body: """
      test "$1" do
        $2
      end
    """

".source.coffee":
  "Describe block":
    prefix: "de"
    body: """
      describe "${1:description}", ->
        ${2:body}
    """
  "It block":
    prefix: "i"
    body: """
      it "$1", ->
        $2
    """
  "Before each":
    prefix: "be"
    body: """
      beforeEach ->
        $1
    """
  "After each":
    prefix: "af"
    body: """
      afterEach ->
        $1
    """
  "Expectation":
    prefix: "ex"
    body: "expect($1).to$2"
  "Console log":
    prefix: "log"
    body: "console.log $1"
  "Range array":
    prefix: "ra"
    body: "[[$1, $2], [$3, $4]]"
  "Point array":
    prefix: "pt"
    body: "[$1, $2]"
  "Key-value pair":
    prefix: ":"
    body: '${1:"${2:key}"}: ${3:value}'
  "Create Jasmine spy":
    prefix: "spy"
    body: 'jasmine.createSpy("${1:description}")$2'

".source.ruby":
  "Hashrocket":
    prefix: "="
    body: " => "

  "Curly braces block":
    prefix: "{"
    body: "{ |${1:variable}| $2 "

  "Context block":
    prefix: "context"
    body: """
      context "$1" do
        $2
      end
    """

  "Test block":
    prefix: "test"
    body: """
      test "$1" do
        $2
      end
    """

".source.coffee":
  "Describe block":
    prefix: "de"
    body: """
      describe "${1:description}", ->
        ${2:body}
    """
  "It block":
    prefix: "i"
    body: """
      it "$1", ->
        $2
    """
  "Before each":
    prefix: "be"
    body: """
      beforeEach ->
        $1
    """
  "After each":
    prefix: "af"
    body: """
      afterEach ->
        $1
    """
  "Expectation":
    prefix: "ex"
    body: "expect($1).to$2"
  "Console log":
    prefix: "log"
    body: "console.log $1"
  "Range array":
    prefix: "ra"
    body: "[[$1, $2], [$3, $4]]"
  "Point array":
    prefix: "pt"
    body: "[$1, $2]"
  "Key-value pair":
    prefix: ":"
    body: '${1:"${2:key}"}: ${3:value}'
  "Create Jasmine spy":
    prefix: "spy"
    body: 'jasmine.createSpy("${1:description}")$2'

Add Atom snippet for Ruby curly braces block


React = require 'react'
BS = require 'react-bootstrap'

ChapterSectionType = require './chapter-section-type'

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

  propTypes:
    section:  ChapterSectionType.isRequired
    onPractice: React.PropTypes.func

  render: ->
    {section, chapter, onPractice} = @props
    level = section.current_level
    percent = Math.round((level / 1) * 100)
    color = switch
      when percent >  75 then 'high'
      when percent >= 50 then 'medium'
      else 'low'

    bar = <BS.ProgressBar className={color} now={percent} />
    if onPractice
      <BS.Button onClick={-> onPractice(section)} block>{bar}</BS.Button>
    else
      bar

React = require 'react'
BS = require 'react-bootstrap'

ChapterSectionType = require './chapter-section-type'

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

  propTypes:
    section:  ChapterSectionType.isRequired
    onPractice: React.PropTypes.func

  render: ->
    {section, chapter, onPractice} = @props
    level = section.current_level
    percent = Math.round((level / 1) * 100)
    color = switch
      when percent >  75 then 'high'
      when percent >= 50 then 'medium'
      else 'low'

    bar = <BS.ProgressBar className={color} now={percent} />
    if onPractice
      tooltip = <BS.Tooltip>Click to practice</BS.Tooltip>
      <BS.OverlayTrigger placement='bottom' overlay={tooltip}>
        <BS.Button onClick={-> onPractice(section)} block>{bar}</BS.Button>
      </BS.OverlayTrigger>
    else
      bar

Add "Click to practice" tooltip to practice buttons


define [
	"base"
], (App) ->
	App.controller "FreeTrialModalController", ($scope, abTestManager, sixpack, event_tracking)->

		$scope.buttonClass = "btn-primary"

		$scope.startFreeTrial = (source, couponCode) ->
			event_tracking.sendMB "subscription-start-trial", { source }

			w = window.open()
			sixpack.convert "track-changes-discount", ->
				ga?('send', 'event', 'subscription-funnel', 'upgraded-free-trial', source)
				url = "/user/subscription/new?planCode=#{$scope.startTrialPlanCode}&ssp=true"
				if couponCode?
					url = "#{url}&cc=#{couponCode}"
				$scope.startedFreeTrial = true
				w.location = url

define [
	"base"
], (App) ->
	App.controller "FreeTrialModalController", ($scope, abTestManager, sixpack, event_tracking)->

		$scope.buttonClass = "btn-primary"

		$scope.startFreeTrial = (source, couponCode) ->
			event_tracking.sendMB "subscription-start-trial", { source, plan: $scope.startTrialPlanCode }

			w = window.open()
			sixpack.convert "track-changes-discount", ->
				ga?('send', 'event', 'subscription-funnel', 'upgraded-free-trial', source)
				url = "/user/subscription/new?planCode=#{$scope.startTrialPlanCode}&ssp=true"
				if couponCode?
					url = "#{url}&cc=#{couponCode}"
				$scope.startedFreeTrial = true
				w.location = url

Add plan to event tracking

Add plan to event tracking


#= require dashing.js
#= require_tree ./lib
#= require_tree ./dashing
#= require_tree ../../widgets

Dashing.on 'ready', ->
  Dashing.widget_margins ||= [5, 5]
  Dashing.widget_base_dimensions ||= [300, 360]
  Dashing.numColumns ||= 4

  contentWidth = (Dashing.widget_base_dimensions[0] + Dashing.widget_margins[0] * 2) * Dashing.numColumns

  Batman.setImmediate ->
    $('.gridster').width(contentWidth)
    $('.gridster ul:first').gridster
      widget_margins: Dashing.widget_margins
      widget_base_dimensions: Dashing.widget_base_dimensions
      avoid_overlapped_widgets: !Dashing.customGridsterLayout

    window.grid = $('.gridster ul:first').gridster().data('gridster')
    window.grid.disable()

#= require dashing.js
#= require_tree ./lib
#= require_tree ./dashing
#= require_tree ../../widgets

$ ->
  Dashing.widget_margins ||= [5, 5]
  Dashing.widget_base_dimensions ||= [300, 360]
  Dashing.numColumns ||= 4

  contentWidth = (Dashing.widget_base_dimensions[0] + Dashing.widget_margins[0] * 2) * Dashing.numColumns

  Batman.setImmediate ->
    $('.gridster').width(contentWidth)
    $('.gridster ul:first').gridster
      widget_margins: Dashing.widget_margins
      widget_base_dimensions: Dashing.widget_base_dimensions
      avoid_overlapped_widgets: !Dashing.customGridsterLayout

    window.grid = $('.gridster ul:first').gridster().data('gridster')
    window.grid.disable()

Fix notification window not hiding on start


# Parses a structure of errors that came from the server
angular.module("admin.utils").factory "ErrorsParser", ->
  new class ErrorsParser
    toString: (errors, defaultContent = "") =>
      return defaultContent unless errors?

      errorsString = ""
      if errors.length > 0
        # it is an array of errors
        errorsString = errors.join("\n")
      else
        # it is a hash of errors
        keys = Object.keys(errors)
        for key in keys
          errorsString += errors[key].join("\n") + "\n"

      this.defaultIfEmpty(errorsString, defaultContent)

    defaultIfEmpty: (content, defaultContent) =>
      return defaultContent if content == ""
      content

# Parses a structure of errors that came from the server
angular.module("admin.utils").factory "ErrorsParser", ->
  new class ErrorsParser
    toString: (errors, defaultContent = "") =>
      return defaultContent unless errors?

      errorsString = ""
      if errors.length > 0
        # it is an array of errors
        errorsString = errors.join("\n") + "\n"
      else
        # it is a hash of errors
        keys = Object.keys(errors)
        for key in keys
          errorsString += errors[key].join("\n") + "\n"

      this.defaultIfEmpty(errorsString, defaultContent)

    defaultIfEmpty: (content, defaultContent) =>
      return defaultContent if content == ""
      content

Add traling breakline to case where errors come in a array


Sprangular.directive 'addressForm', ->
  restrict: 'E'
  templateUrl: 'addresses/form.html'
  scope:
    address: '='
    countries: '='
    disabled: '='
    submitted: '='
  controller: ($scope) ->
    $scope.selectedCountry = null
    $scope.hasErrors = false

    $scope.$watchGroup ['address.firstname', 'address.lastname', 'address.address1', 'address.address2', 'address.city', 'address.stateId', 'address.countryId', 'address.zipcode', 'address.phone'], ->
      return unless $scope.submitted

      address = $scope.address
      address.validate()
      errors = address.errors
      $scope.hasErrors = errors && Object.keys(errors).length > 0

    $scope.$watch 'address.countryId', (newCountryId) ->
      return unless newCountryId

      address = $scope.address

      $scope.selectedCountry = _.find($scope.countries, (country) -> country.id == newCountryId)
      address.country = $scope.selectedCountry

      if address.state && address.state.country_id != newCountryId
        address.stateId = null
        address.state = null

    $scope.$watch 'address.stateId', (newStateId) ->
      return unless newStateId
      state = _.find($scope.selectedCountry.states, (state) -> state.id == newStateId)
      $scope.address.state = state

  link: (element, attrs) ->
    attrs.disabled = false unless attrs.disabled?

Sprangular.directive 'addressForm', ->
  restrict: 'E'
  templateUrl: 'addresses/form.html'
  scope:
    address: '='
    countries: '='
    disabled: '='
    submitted: '='
  controller: ($scope, Account) ->
    $scope.user = Account.user
    $scope.selectedCountry = null
    $scope.hasErrors = false

    $scope.$watchGroup ['address.firstname', 'address.lastname', 'address.address1', 'address.address2', 'address.city', 'address.stateId', 'address.countryId', 'address.zipcode', 'address.phone'], ->
      return unless $scope.submitted

      address = $scope.address
      address.validate()
      errors = address.errors
      $scope.hasErrors = errors && Object.keys(errors).length > 0

    $scope.$watch 'address.countryId', (newCountryId) ->
      return unless newCountryId

      address = $scope.address

      $scope.selectedCountry = _.find($scope.countries, (country) -> country.id == newCountryId)
      address.country = $scope.selectedCountry

      if address.state && address.state.country_id != newCountryId
        address.stateId = null
        address.state = null

    $scope.$watch 'address.stateId', (newStateId) ->
      return unless newStateId
      state = _.find($scope.selectedCountry.states, (state) -> state.id == newStateId)
      $scope.address.state = state

  link: (element, attrs) ->
    attrs.disabled = false unless attrs.disabled?

Add $scope.user to address-form directive


$ ->
  hashTabs = ->
    _element: ""

    element: (element) ->
      this._element = element

    updateTab: (updateHash=true)->
      this.updateTabLink()
      this.updateTabContainer()
      $('body').scrollTop(0)
      if updateHash
        this.updateURLHash()

    updateTabLink: ->
      this._element.parent().addClass('is-active').siblings('.is-active').removeClass('is-active')

    updateTabContainer: ->
      dataTab = this._element.data('tab')
      $("[data-content='" + dataTab + "']").addClass('is-active').show().siblings().removeClass('is-active').hide()

    updateURLHash: ->
      window.location.hash = this._element.data('tab')

  if $('[data-tab]').length
    $(window).on 'load', ->
      if window.location.hash != ""
        hash = window.location.hash.replace("#", "")
        tabs = new hashTabs()
        tabs.element $("[data-tab='"+hash+"']")
        tabs.updateTab()
      else
        tabs = new hashTabs()
        tabs.element $("[data-tab]").first()
        tabs.updateTab(updateHash=false)

    $(window).on 'hashchange', ->
      hash = window.location.hash.replace("#", "")
      tabs = new hashTabs()
      tabs.element $("[data-tab='"+hash+"']")
      tabs.updateTab()

    $('[data-tab]').on 'click', (e) ->
      tabs = new hashTabs()
      tabs.element $(this)
      tabs.updateTab()
      e.preventDefault()

$ ->
  hashTabs = ->
    _element: ""

    element: (element) ->
      this._element = element

    updateTab: (updateHash=true)->
      this.updateTabLink()
      this.updateTabContainer()
      $('body').scrollTop(0)
      if updateHash
        this.updateURLHash()

    updateTabLink: ->
      this._element.parent().addClass('is-active').siblings('.is-active').removeClass('is-active')

    updateTabContainer: ->
      dataTab = this._element.data('tab')
      contentTab = $("[data-content='" + dataTab + "']")
      $('[data-content]').not(contentTab).removeClass('is-active')
      contentTab.each ->
        $(this).addClass('is-active')

    updateURLHash: ->
      window.location.hash = this._element.data('tab')

  if $('[data-tab]').length
    $(window).on 'load', ->
      if window.location.hash != ""
        hash = window.location.hash.replace("#", "")
        tabs = new hashTabs()
        tabs.element $("[data-tab='"+hash+"']")
        tabs.updateTab()
      else
        tabs = new hashTabs()
        tabs.element $("[data-tab]").first()
        tabs.updateTab(updateHash=false)

    $(window).on 'hashchange', ->
      hash = window.location.hash.replace("#", "")
      tabs = new hashTabs()
      tabs.element $("[data-tab='"+hash+"']")
      tabs.updateTab()

    $('[data-tab]').on 'click', (e) ->
      tabs = new hashTabs()
      tabs.element $(this)
      tabs.updateTab()
      e.preventDefault()

Add possibility to show N content with the same data tab


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

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

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

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

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

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

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

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

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

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

Make empty pie chart appear on proposal page


angular.module('doubtfire.tasks.task-definition-selector',[])

#
# A switch that that the selection of a specified task definition
# Only handles task definition - not tasks in a project
#
.directive('taskDefinitionSelector', ->
  replace: true
  restrict: 'E'
  templateUrl: 'tasks/task-definition-selector/task-definition-selector.tpl.html'
  scope:
    # Unit required
    unit: "="
    # What to do when definition is changed
    onSelectDefinition: "="
    # Use ng-model to select task
    ngModel: '=?'
    # Provide a btn-style to force the colour to change`
    buttonStyle: '@'
    # Clearable
    showClear: '=?'
  controller: ($scope, groupService) ->
    $scope.buttonStyle ?= 'default'
    $scope.groupSetName = (id) ->
      groupService.groupSetName(id, $scope.unit)

    $scope.hideGroupSetName = $scope.unit.group_sets?.length is 0

    $scope.selectedDefinition = null

    $scope.setSelectedDefinition = (taskDef) ->
      $scope.selectedDefinition = taskDef
      if $scope.onSelectDefinition? && _.isFunction($scope.onSelectDefinition)
        $scope.onSelectDefinition(taskDef)
      if $scope.ngModel? && _.isObject($scope.ngModel)
        $scope.ngModel = taskDef
)

angular.module('doubtfire.tasks.task-definition-selector',[])

#
# A switch that that the selection of a specified task definition
# Only handles task definition - not tasks in a project
#
.directive('taskDefinitionSelector', ->
  replace: true
  restrict: 'E'
  templateUrl: 'tasks/task-definition-selector/task-definition-selector.tpl.html'
  scope:
    # Unit required
    unit: "="
    # What to do when definition is changed
    onSelectDefinition: "="
    # Use ng-model to select task
    ngModel: '=?'
    # Provide a btn-style to force the colour to change`
    buttonStyle: '@'
    # Clearable
    showClear: '=?'
  controller: ($scope, groupService) ->
    $scope.buttonStyle ?= 'default'
    $scope.groupSetName = (id) ->
      groupService.groupSetName(id, $scope.unit)

    $scope.hideGroupSetName = $scope.unit.group_sets?.length is 0

    $scope.selectedDefinition = null

    $scope.setSelectedDefinition = (taskDef) ->
      $scope.selectedDefinition = taskDef
      $scope.ngModel = taskDef
      if $scope.onSelectDefinition? && _.isFunction($scope.onSelectDefinition)
        $scope.onSelectDefinition(taskDef)
)

FIX: Fix ng-model attribute update for task def selector


if Meteor.isServer
	url = Npm.require('url')

RocketChat.settings.get 'Site_Url', (key, value) ->
	if value?.trim() isnt ''
		__meteor_runtime_config__.ROOT_URL = value
		if Meteor.absoluteUrl.defaultOptions?.rootUrl?
			Meteor.absoluteUrl.defaultOptions.rootUrl = value

		if Meteor.isServer
			RocketChat.hostname = url.parse(value).hostname

if Meteor.isServer
	url = Npm.require('url')

RocketChat.settings.get 'Site_Url', (key, value) ->
	if value?.trim() isnt ''
		__meteor_runtime_config__.ROOT_URL = value
		__meteor_runtime_config__.DDP_DEFAULT_CONNECTION_URL = value
		if Meteor.absoluteUrl.defaultOptions?.rootUrl?
			Meteor.absoluteUrl.defaultOptions.rootUrl = value

		if Meteor.isServer
			RocketChat.hostname = url.parse(value).hostname

Set DDP_DEFAULT_CONNECTION_URL to the same value of ROOT_URL


@Curri.RequestsNumber =
  poll: ->
    setTimeout @request, 10000

  request: ->
    $.ajax
      url: $('#requesters_link').attr('href')
      dataType: 'JSON'
      success: Curri.RequestsNumber.updateRequesters

  updateRequesters: (data) ->
    $requestsLink = $('#requesters_link')
    reqLimit = $requestsLink.data("reqlimit")
    oldReqNum = $requestsLink.data("requests")
    newReqNum = JSON.parse(data.requesters).length

    if newReqNum != oldReqNum
      # Update the data for next poll
      $requestsLink.data("requests", newReqNum)

      # Update the visual
      if newReqNum > 0
        $('.nav-help').toggleClass('active', true)
        $reqNumb = $('.req-num').show()
        if newReqNum <= reqLimit
          $reqNumb.text(newReqNum)
        else
          $reqNumb.text(reqLimit + "+")
      else
        $('.req-num').hide()
        $('.nav-help').removeClass('active')

    Curri.RequestsNumber.poll()

@Curri.RequestsNumber =
  poll: ->
    setTimeout @request, 10000

  request: ->
    $.ajax
      url: $('#requesters_link').attr('href')
      dataType: 'JSON'
      success: Curri.RequestsNumber.updateRequesters

  updateRequesters: (data) ->
    $requestsLink = $('#requesters_link')
    reqLimit = $requestsLink.data("reqlimit")
    oldReqNum = $requestsLink.data("requests")
    newReqNum = JSON.parse(data.requesters).length

    if newReqNum != oldReqNum
      # Reset title
      document.title = document.title.replace(/\(\d+\)\s/, '')

      # Update the data for next poll
      $requestsLink.data("requests", newReqNum)

      # Update the visual
      if newReqNum > 0
        $('.nav-help').toggleClass('active', true)
        $reqNumb = $('.req-num').show()
        document.title = "(#{newReqNum}) #{document.title}"
        if newReqNum <= reqLimit
          $reqNumb.text(newReqNum)
        else
          $reqNumb.text(reqLimit + "+")
      else
        $('.req-num').hide()
        $('.nav-help').removeClass('active')

    Curri.RequestsNumber.poll()

Document title changes to notify teachers of requesters - like twitter and gmail


React        = require 'react/addons'
Router       = require 'react-router'
RouteHandler = Router.RouteHandler
Link         = Router.Link

DidYouKnowHandler = require './did_you_know_handler'
MainspaceHandler  = require './mainspace_handler'
PlagiarismHandler = require './plagiarism_handler'

RecentActivityHandler = React.createClass(
  displayName: 'RecentActivityHandler'
  render: ->

<Link to="did-you-know">Did You Know Eligible</Link>

<Link to="mainspace">Mainspace Eligible</Link>

<Link to="plagiarism">Plagiarism Flag</Link>

      <RouteHandler {...@props} />

)

module.exports = RecentActivityHandler

React        = require 'react/addons'
Router       = require 'react-router'
RouteHandler = Router.RouteHandler
Link         = Router.Link

DidYouKnowHandler = require './did_you_know_handler'
MainspaceHandler  = require './mainspace_handler'
PlagiarismHandler = require './plagiarism_handler'

RecentActivityHandler = React.createClass(
  displayName: 'RecentActivityHandler'
  render: ->

<Link to="did-you-know">Did You Know Eligible</Link>

<Link to="plagiarism">Plagiarism Flag</Link>

      <RouteHandler {...@props} />

)

module.exports = RecentActivityHandler

Remove link to placeholder 'Mainspace' recent activity feed


mkdirp = require 'mkdirp'
fs = require 'fs'

#
# Convenient method for writing a file on
# a path that might not exist. This function
# will create all folders provided in the
# path to the file.
#
#
#    writeToFile('/tmp/hi/folder/file.js', "console.log('hi')")
#
# will create a file at /tmp/hi/folder/file.js with provided content
#
writeToFile = (file, content) ->
  try
    fs.writeFileSync(file, content)
  catch e
    if e.code == 'ENOENT' or e.code == 'EBADF'
      splitted = file.split('/')
      mkdirp.sync(splitted.splice(0, splitted.length-1).join('/'), 0o0777)

      # Retry!
      writeToFile(file, content)
    else
      console.log e

normalizeUrl = (url) ->
  protocol = url.match(/^(http|https):\/\//)?[0]
  protocol = '' unless protocol?
  url = url.replace(protocol, '')
  url = url.replace('//', '/')
  return protocol + url

exports.writeToFile = writeToFile
exports.normalizeUrl = normalizeUrl

mkdirp = require 'mkdirp'
fs = require 'fs'

#
# Convenient method for writing a file on
# a path that might not exist. This function
# will create all folders provided in the
# path to the file.
#
#
#    writeToFile('/tmp/hi/folder/file.js', "console.log('hi')")
#
# will create a file at /tmp/hi/folder/file.js with provided content
#
writeToFile = (file, content) ->
  try
    fs.writeFileSync(file, content)
  catch e
    if e.code == 'ENOENT' or e.code == 'EBADF'
      splitted = file.split('/')
      mkdirp.sync(splitted.splice(0, splitted.length-1).join('/'), 0o0777)

      # Retry!
      writeToFile(file, content)
    else
      console.log e

normalizeUrl = (url) ->
  protocol = url.match(/^(http|https):\/\//)?[0]
  protocol = '' unless protocol?
  url = url.replace(protocol, '')
  url = url.replace('\\', '/')
  url = url.replace('//', '/')
  return protocol + url

exports.writeToFile = writeToFile
exports.normalizeUrl = normalizeUrl

Fix url normalization for Windows

Fix url normalization for Windows

When bundle-up is called from Windows, it uses the file path to generate
the compiled assets url, resulting in things like
"/\generated\bundle\asdfg.css".

This little patch fixes that.


PreviewOpener = require '../../lib/openers/preview-opener'

describe "PreviewOpener", ->
  describe "open", ->
    it "invokes the callback with an exit code equal to `1` because the file is not found", ->
      exitCode = null
      opener = new PreviewOpener()
      opener.open 'dummy-file-name.pdf', (code) -> exitCode = code

      waitsFor ->
        exitCode > 0

      runs ->
        expect(exitCode).toEqual(1)

PreviewOpener = require '../../lib/openers/preview-opener'

describe "PreviewOpener", ->
  describe "open", ->
    # FIXME: Horrible test. Needs to be fixed, or removed entirely.
    it "invokes the callback with an exit code equal to `1` because the file is not found", ->
      exitCode = null
      opener = new PreviewOpener()
      opener.open 'dummy-file-name.pdf', (code) -> exitCode = code

      waitsFor ->
        exitCode > 0

      runs ->
        expect(exitCode).toEqual(1)

Add note about horribly bad test

Add note about horribly bad test


App.CorpusResultRoute = Em.Route.extend

  model: (params) ->
    {search_id: searchId, page_no: @pageNo} = params

    # Get the search model subclass for the search engine used by the current
    # corpus and find the record with the given search_id
    searchModelClass = @controllerFor('corpus').get('searchModelClass')
    search = searchModelClass.find(searchId)
    @controllerFor('search').set('model', search)

    search

  serialize: (params) ->
    [search, @pageNo] = params
    {search_id: search.get('id'), page_no: @pageNo}

  setupController: (controller, model) ->
    # When the route is loaded via a URL, `model` is the actual search model, but when we
    # transition here from a different route, the "model" is actually an array containing the model
    # and the page number...
    model = model[0] if Em.isArray(model)
    @controllerFor('resultTable').set('content', model.getResultPage(@pageNo))

  renderTemplate: ->
    searchEngine = @controllerFor('corpus').get('searchEngine')
    @render("#{searchEngine}/result")
    @render('corpus/result/num_hits', outlet: 'numHits', controller: 'search')

  events:
    metadataSelectionsChanged: ->
      # FIXME: Select the appropriate controller based on the type of search we're doing
      @controllerFor('cwbSearchInputs').search()

App.CorpusResultRoute = Em.Route.extend

  model: (params) ->
    {search_id: searchId, page_no: @pageNo} = params

    # Get the search model subclass for the search engine used by the current
    # corpus and find the record with the given search_id
    searchModelClass = @controllerFor('corpus').get('searchModelClass')
    search = searchModelClass.find(searchId)
    @controllerFor('search').set('model', search)

    search

  serialize: (params) ->
    [search, @pageNo] = params
    {search_id: search.get('id'), page_no: @pageNo}

  setupController: (controller, model) ->
    # When the route is loaded via a URL, `model` is the actual search model, but when we
    # transition here from a different route, the "model" is actually an array containing the model
    # and the page number...
    model = model[0] if Em.isArray(model)
    @controllerFor('resultTable').set('content', model.getResultPage(@pageNo))

    # FIXME: Select the appropriate controller based on the type of search we're doing
    @controllerFor('cwbResultToolbar').setCurrentPageNo(@pageNo)

  renderTemplate: ->
    searchEngine = @controllerFor('corpus').get('searchEngine')
    @render("#{searchEngine}/result")
    @render('corpus/result/num_hits', outlet: 'numHits', controller: 'search')

  events:
    metadataSelectionsChanged: ->
      # FIXME: Select the appropriate controller based on the type of search we're doing
      @controllerFor('cwbSearchInputs').search()

Set the correct page number on the toolbar controller


Wheelmap.ApplicationRoute = Ember.Route.extend
  signedIn: false

  beforeModel: (transition)->
    signUrl = $.cookie('sign_url')

    if signUrl?
      transition.abort()

      $.cookie('sign_url', null)
      window.location.hash = signUrl

  actions:
    authenticate: (transition)->
      self = @

      unless self.signedIn
        transition.abort()

        $.ajax('/users/signed_in').done (signedIn)->
          if signedIn
            self.signedIn = true
            transition.retry()
          else
            params = {}

            $.each transition.params, ->
              $.extend params, @

            $.cookie('sign_url', transition.router.recognizer.generate(transition.targetName, params))
            window.location = '/users/sign_in'

Wheelmap.ApplicationRoute = Ember.Route.extend
  signedIn: false

  beforeModel: (transition)->
    signUrl = $.cookie('sign_url')
    data = JSON.parse $.cookie("flash")

    if signUrl? && !data.error && !data.alert && !data.notice
      transition.abort()

      $.cookie('sign_url', null)
      window.location.hash = signUrl

  actions:
    authenticate: (transition)->
      self = @

      unless self.signedIn
        transition.abort()

        $.ajax('/users/signed_in').done (signedIn)->
          if signedIn
            self.signedIn = true
            transition.retry()
          else
            params = {}

            $.each transition.params, ->
              $.extend params, @

            $.cookie('sign_url', transition.router.recognizer.generate(transition.targetName, params))
            window.location = '/users/sign_in'

Fix autoredirect when login failed.

Fix autoredirect when login failed.


minimatch = require 'minimatch'

module.exports = (env, callback) ->

  filter = (contents, pattern) ->
    list(contents).filter (entry) ->
        if pattern? then minimatch(entry.filename, pattern) else true

  list = (contents) ->
    entries = []
    for key, value of contents._
      switch key
        when 'directories'
          subcontents = value.map (directory) -> list directory
          entries = [].concat.apply entries, subcontents
        else
          entries = entries.concat value
    entries.sort (a, b) -> a.filename.localeCompare b.filename

  env.helpers.contents =
    filter: filter
    list: list

  callback()

minimatch = require 'minimatch'

module.exports = (env, callback) ->

  filter = (contents, pattern) ->
    list(contents).filter (entry) ->
      minimatch(entry.filename, pattern)

  list = (contents) ->
    entries = []
    for key, value of contents._
      switch key
        when 'directories'
          subcontents = value.map (directory) -> list directory
          entries = [].concat.apply entries, subcontents
        else
          entries = entries.concat value
    entries.sort (a, b) -> a.filename.localeCompare b.filename

  env.helpers.contents =
    filter: filter
    list: list

  callback()

Remove non-mandatory check from filter function


StreamServer = require './stream_server'
Bits = require './bits'
logger = require './logger'

Bits.set_warning_fatal true
logger.setLevel logger.LEVEL_INFO

streamServer = new StreamServer

process.on 'SIGINT', =>
  console.log 'Got SIGINT'
  streamServer.stop ->
    process.kill process.pid, 'SIGTERM'

process.on 'uncaughtException', (err) ->
  streamServer.stop()
  throw err

streamServer.start()

url = require 'url'

StreamServer = require './stream_server'
Bits = require './bits'
logger = require './logger'

Bits.set_warning_fatal true
logger.setLevel logger.LEVEL_INFO

streamServer = new StreamServer
streamServer.setLivePathConsumer (uri, callback) ->
  pathname = url.parse(uri).pathname[1..]

  isAuthorized = true

  if isAuthorized
    callback null # Accept access
  else
    callback new Error 'Unauthorized access' # Deny access

process.on 'SIGINT', =>
  console.log 'Got SIGINT'
  streamServer.stop ->
    process.kill process.pid, 'SIGTERM'

process.on 'uncaughtException', (err) ->
  streamServer.stop()
  throw err

streamServer.start()

Add an example of setLivePathConsumer()

Add an example of setLivePathConsumer()


# modules
express = require("express");
app = express();
colors = require 'colors'
db = require './models/db'
http = require './services/http'
logger = require './services/logger'
# config
net = require './config/networking'

# DATABASE
# --------
do db.syncSchemas

# EXPRESS BOOTSTRAP
# -----------------
# add middlewares
http.addThirdPartyMiddlewares app
http.tuneResponses app
# def routing
api = require './api'
api app
# listen
app.listen process.env.PORT
logger.info "HTTP REST API listening on port #{process.env.PORT}".green

# SHOW INDEX
# -------------------
app.use(express.static(__dirname + '/index'));
app.get '/', (req, res) ->
  res.sendFile '/index.html'

# SOCKET.IO BOOTSTRAP
# -------------------

# todo ;p

# modules
express = require("express");
app = express();
colors = require 'colors'
db = require './models/db'
http = require './services/http'
logger = require './services/logger'
# config
net = require './config/networking'

# DATABASE
# --------
do db.syncSchemas

# EXPRESS BOOTSTRAP
# -----------------
# add middlewares
http.addThirdPartyMiddlewares app
http.tuneResponses app
# def routing
api = require './api'
api app
# listen
port = process.env.PORT || 3000
app.listen port
logger.info "HTTP REST API listening on port #{port}".green

# SHOW INDEX
# -------------------
app.use(express.static(__dirname + '/index'));
app.get '/', (req, res) ->
  res.sendFile '/index.html'

# SOCKET.IO BOOTSTRAP
# -------------------

# todo ;p

Use 3000 if PORT not defined

Use 3000 if PORT not defined


$ ->
  # enable chosen js
  $('.chosen-select').chosen
    no_results_text: 'No results matched'
    width: '400px'
    placeholder_text_multiple: 'Select languages...'

$ ->
  # enable chosen js
  $('.chosen-select').chosen
    no_results_text: 'No results matched'
    width: '400px'
    placeholder_text_multiple: 'Select languages you can read and speak'

Add text in chosen select

Add text in chosen select


class Cloudsdale.Collections.CloudsUsers extends Backbone.Collection

  model: Cloudsdale.Models.User
  url: -> "/v1/#{@topic.type}s/#{@topic.id}/users/online.json"

  topic: 'cloud'

  initialize: (args,options) ->

    args    ||= {}
    options ||= {}

    @topic = options.topic

    this

  cachedFetch: (options) ->
    options = {} unless options
    session.get('users').findOrInitialize [],
      specific_endpoint: true
      url: @url()
      success: (collection, resp, xhr) =>
        $.each resp.result, (i,user) => @findOrInitialize(user)
        options.success(collection, resp, xhr) if options.success

      error: (resp, xhr,_options) =>
        options.error(resp, xhr,_options) if options.error

    this

  listen: ->
    nfc.on "#{@topic.type}s:#{@topic.id}:users:*", (payload) =>
      @findOrInitialize(payload)

  findOrInitialize: (args) ->
    user = session.get('users').findOrInitialize(args)
    user.set(args)
    @add(user)

class Cloudsdale.Collections.CloudsUsers extends Backbone.Collection

  model: Cloudsdale.Models.User
  url: ->
    if @topic.get('member_count') > 100
      return "/v1/#{@topic.type}s/#{@topic.id}/users/online.json"
    else
      return "/v1/#{@topic.type}s/#{@topic.id}/users.json"

  topic: 'cloud'

  initialize: (args,options) ->

    args    ||= {}
    options ||= {}

    @topic = options.topic

    this

  cachedFetch: (options) ->
    options = {} unless options
    session.get('users').findOrInitialize [],
      specific_endpoint: true
      url: @url()
      success: (collection, resp, xhr) =>
        $.each resp.result, (i,user) => @findOrInitialize(user)
        options.success(collection, resp, xhr) if options.success

      error: (resp, xhr,_options) =>
        options.error(resp, xhr,_options) if options.error

    this

  listen: ->
    nfc.on "#{@topic.type}s:#{@topic.id}:users:*", (payload) =>
      @findOrInitialize(payload)

  findOrInitialize: (args) ->
    user = session.get('users').findOrInitialize(args)
    user.set(args)
    @add(user)

Return all users on small clouds.

Return all users on small clouds.


class window.FavouriteTopicButtonView extends ActionButtonView
  mini: true

  onRender: -> @updateButton()

  initialize: ->
    @bindTo @model, 'change', @updateButton, @
    @user = currentUser

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

  buttonEnabled: ->
    @user.favourite_topics.contains @model

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

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

class window.FavouriteTopicButtonView extends ActionButtonView
  mini: true

  onRender: -> @updateButton()

  initialize: ->
    @bindTo @model, 'change', @updateButton, @
    @user = currentUser

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

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

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

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

Fix check for Topic in FavouriteTopicCollection

Can't this be done nicer?

favourite_topics.contains @model doesn't seem to work (I guess because the @model has more attributes than the Topics in FavTopicCollection).

@mark @janpaul123


'use strict'

{
	setupEditors
	setupSamples
	setupCompilers
	setupRunButton
	saveProgram
	loadProgram
} = window.viz

grid = null

run = (editors, compiler) ->
	saveProgram editors

	playfield = new bef.Playfield()
	playfield.fromString editors.source.getValue(), 16, 10

	lazyRuntime = new bef.LazyRuntime()
	lazyRuntime.execute(
		playfield
		{ jumpLimit: 1000, compiler }
		editors.input.getValue()
	)

	stringedStack = lazyRuntime.programState.stack.join ' '
	stringedOutput = lazyRuntime.programState.outRecord.join ' '

	editors.output.setValue "Stack: #{stringedStack}\nOutput: #{stringedOutput}", 1

	grid?.destroy()
	grid = new viz.Grid playfield, lazyRuntime.pathSet, document.getElementById 'can'
	grid.setListener (path) ->
		editors.js.setValue path?.code ? ''

do ->
	compiler = bef.StackingCompiler

	editors = setupEditors()

	setupSamples window.befSample, editors

	setupCompilers (_compiler) ->
		compiler = _compiler
		run editors, compiler

	setupRunButton ->
		run editors, compiler

	loadProgram editors

	run editors, compiler

'use strict'

{
	setupEditors
	setupSamples
	setupCompilers
	setupRunButton
	saveProgram
	loadProgram
} = window.viz

grid = null

run = (editors, compiler) ->
	saveProgram editors

	playfield = new bef.Playfield()
	playfield.fromString editors.source.getValue(), 16, 10

	lazyRuntime = new bef.LazyRuntime()
	lazyRuntime.execute(
		playfield
		{ jumpLimit: 1000, compiler }
		editors.input.getValue()
	)

	stringedStack = lazyRuntime.programState.stack.join ' '
	stringedOutput = lazyRuntime.programState.outRecord.join ' '

	editors.output.setValue "Stack: #{stringedStack}\nOutput: #{stringedOutput}", 1

	grid?.destroy()
	grid = new viz.Grid playfield, lazyRuntime.pathSet, document.getElementById 'can'
	grid.setListener (path) ->
		code = path?.code ? ''
		editors.js.setValue code, -1
		return

do ->
	compiler = bef.StackingCompiler

	editors = setupEditors()

	setupSamples window.befSample, editors

	setupCompilers (_compiler) ->
		compiler = _compiler
		run editors, compiler

	setupRunButton ->
		run editors, compiler

	loadProgram editors

	run editors, compiler

Stop selecting the js output

Stop selecting the js output