# Plain HTML Formatter

fs = require('fs')

exports.init = ->

  extensions: ['html']
  assetType: 'html'
  contentType: 'text/html'

  compile: (path, options, cb) ->

    input = fs.readFileSync(path, 'utf8')

    # If passing optional headers for main view
    input = input.replace('<SocketStream>', options.headers) if options && options.headers

    cb(input)

# Plain HTML Formatter

fs = require('fs')

exports.init = ->

  extensions: ['html']
  assetType: 'html'
  contentType: 'text/html'

  compile: (path, options, cb) ->

    input = fs.readFileSync(path, 'utf8')

    # If passing optional headers for main view
	if options && options.headers
		input = input.replace('<SocketStream>', options.headers)
		input = input.replace('<SocketStream/>', options.headers)

    cb(input)

Make <SocketStream> tag xml compliant

Make <SocketStream> tag xml compliant


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

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

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

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

  render: ->
    {section,  onPractice} = @props

    bar = if section.current_level
      percent = Math.round((section.current_level / 1) * 100)
      color = switch
        when percent >  75 then 'high'
        when percent >= 50 then 'medium'
        else 'low'
      <BS.ProgressBar className={color} now={percent} />
    else
      Practice section

    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

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

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

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

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

  render: ->
    {section,  onPractice} = @props

    bar = if section.current_level
      percent = Math.round((section.current_level / 1) * 100)
      console.log section
      # always show at least 5% of bar, otherwise it just looks empty
      <BS.ProgressBar className={section.interpretation} now={percent} />
    else
      Practice section

    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

Switch to using interpretation property for color


ScriptView = require './script-view'

module.exports =
  scriptView: null

  activate: (state) ->
    @scriptView = new ScriptView(state.scriptViewState)

  deactivate: ->
    @scriptView.destroy()

  serialize: ->
    scriptViewState: @scriptView.serialize()

ScriptView = require './script-view'

module.exports =
  scriptView: null

  activate: (state) ->
    @scriptView = new ScriptView(state.scriptViewState)

  deactivate: ->
    @scriptView.close()

  serialize: ->
    scriptViewState: @scriptView.serialize()

Destroy no longer exists, now it's close


class Skr.Screens.TimeTracking.MiniControls extends Lanes.React.Component

    onEditEvent: ->
        @props.onEditEvent(@props.event)

    EditButton: ->
        return null unless @props.event
        <BS.Button onClick={@onEditEvent} title="Edit Time Entry">
            <LC.Icon type='pencil-square-o' 2x flush />
        </BS.Button>

    render: ->

                {@props.date.format('MMM')}
                {@props.date.format('Do')}

            <BS.Button onClick={@props.onCancel} title="Hide Controls">
                <LC.Icon type='ban' 2x flush />
            </BS.Button>
            <@EditButton />
            <BS.Button onClick={@props.onAddEntry} title="Create Time Entry">
                <LC.Icon type='plus-square' 2x flush />
            </BS.Button>

Add span tags to match old behaviour of React

This is what happens when you rely on non-specified behaviour ;)


# This provides hyperclick support for Perl 6 using hyperclick
# Please see https://atom.io/packages/hyperclick
module.exports =
class Perl6HyperclickProvider

  providerName:  'perl6-hyperclick'

  getSuggestionForWord: (editor, text, range) ->
    # Make sure the editor are valid Perl 6 editors
    return unless editor?
    grammar = editor.getGrammar()
    return unless grammar? && grammar.scopeName? && grammar.scopeName.startsWith("source.perl6")

    return {
      range: range,
      callback: () ->
        atom.notifications.addWarning("TODO perl6doc " + text)
        return
    }

# This provides hyperclick support for Perl 6 using hyperclick
# Please see https://atom.io/packages/hyperclick
module.exports =
class Perl6HyperclickProvider

  providerName:  'perl6-hyperclick'

  getSuggestionForWord: (editor, text, range) ->
    # Make sure the editor are valid Perl 6 editors
    return unless editor?
    grammar = editor.getGrammar()
    return unless grammar? && grammar.scopeName? && grammar.scopeName.startsWith("source.perl6")

    return {
      range: range,
      callback: () ->
        options =
          detail: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
          dismissable: true
        atom.notifications.addInfo("p6doc #{text}", options)
        return
    }

Fix doc with mock stuff

Fix doc with mock stuff


passport = require('./initializers/authentication')

narrativeApi = require('./routes/api/narrative')
dashboardRoutes = require('./routes/dashboard.coffee')
indicatorRoutes = require('./routes/indicators.coffee')
bookmarkRoutes = require('./routes/bookmarks.coffee')
reportRoutes = require('./routes/reports.coffee')
testRoutes = require('./routes/tests.coffee')

module.exports = exports = (app) ->
  # REST API
  app.resource 'api/narrative', narrativeApi, { format: 'json' }

  app.get "/", dashboardRoutes.index
  app.get "/dashboard", dashboardRoutes.index
  app.get "/indicators", indicatorRoutes.index
  app.get "/reports", reportRoutes.index
  app.get "/bookmarks", bookmarkRoutes.index

  app.get "/indicator/:id", indicatorRoutes.show

  app.get "/report/:id", reportRoutes.show

  # Tests
  if app.settings.env == 'test'
    app.get "/tests", testRoutes.test

passport = require('./initializers/authentication')

narrativeApi = require('./routes/api/narrative')
dashboardRoutes = require('./routes/dashboard.coffee')
indicatorRoutes = require('./routes/indicators.coffee')
bookmarkRoutes = require('./routes/bookmarks.coffee')
reportRoutes = require('./routes/reports.coffee')
testRoutes = require('./routes/tests.coffee')

module.exports = exports = (app) ->
  ensureAuthenticated = (req, res, next) ->
    return next() if app.settings.env == 'development'

    passport.
      authenticate('basic', { session: false }).
      call(@, req, res, next)

  # REST API
  app.resource 'api/narrative', narrativeApi, { format: 'json' }

  app.get "/", ensureAuthenticated, dashboardRoutes.index
  app.get "/dashboard", ensureAuthenticated, dashboardRoutes.index
  app.get "/indicators", ensureAuthenticated, indicatorRoutes.index
  app.get "/reports", ensureAuthenticated, reportRoutes.index
  app.get "/bookmarks", ensureAuthenticated, bookmarkRoutes.index

  app.get "/indicator/:id", ensureAuthenticated, indicatorRoutes.show

  app.get "/report/:id", ensureAuthenticated, reportRoutes.show

  # Tests
  if app.settings.env == 'test'
    app.get "/tests", testRoutes.test

Disable authentication in development, and make routes DRYer


".source.viml":
	editor:
		commentStart: '" '
		increaseIndentPattern: "(?:^|\\s)(?:if|else|elseif|for|function!?|while)(?:\\s|$)"
		decreaseIndentPattern: "(?:^|\\s)(?:endif|endfor|endfunction|endwhile)(?:\\s|$)"

".source.viml":
	editor:
		commentStart: '" '
		increaseIndentPattern: "(?:^|\\s)(?:if|else|elseif|for|function!?|fu|fun|func|augroup|aug|try|catch|while)(?:\\s|$)"
		decreaseIndentPattern: "(?:^|\\s)(?:endif|endfor|endf|endfun|endfunction|endtry|endwhile|(?:augroup|aug)\\.END)(?:\\s|$)"

Extend list of recognised indent/outdent keywords


Impromptu = require '../impromptu'
async = require 'async'

class Repository extends Impromptu.Cache.Global
  prepare: (method, fn) ->
    return Impromptu.Cache.Global::[method].call @, fn if @_prepared

    parts = ['root', 'branch']
    parts.push 'commit' if @options.commit

    async.map parts, (part, done) =>
      @impromptu.repository[part] done
    , (err, results) =>
      return fn err if err

      # Remove any trailing empty parts.
      while not results[results.length - 1]
        results.pop()

      # Update the name.
      results.unshift @name
      @name = results.join ':'

      @_prepared = true
      Impromptu.Cache.Global::[method].call @, fn

['run', 'get', 'set', 'unset'].forEach (method) ->
  Repository::[method] = (fn) ->
    @prepare method, fn

# Expose `Repository`.
exports = module.exports = Repository

Impromptu = require '../impromptu'
async = require 'async'

class Repository extends Impromptu.Cache.Global
  prepare: (method, fn) ->
    return Impromptu.Cache.Global::[method].call @, fn if @_prepared

    parts = ['root', 'branch']
    parts.push 'commit' if @options.commit

    async.map parts, (part, done) =>
      @impromptu.repository[part] done
    , (err, results) =>
      return fn err if err

      # Remove any trailing empty parts.
      while results.length and not results[results.length - 1]
        results.pop()

      # Update the name.
      results.unshift @name
      @name = results.join ':'

      @_prepared = true
      Impromptu.Cache.Global::[method].call @, fn

['run', 'get', 'set', 'unset'].forEach (method) ->
  Repository::[method] = (fn) ->
    @prepare method, fn

# Expose `Repository`.
exports = module.exports = Repository

Add a safety check to prevent an infinite loop.


path = require 'path'

# require('web-frame').setZoomFactor(1.15)

oldWindowDimensions = {}

atom.commands.add 'atom-workspace',
  'custom:open-todo-list': ->
    todoList = path.join(process.env.HOME, 'Dropbox/todo/todo.txt')
    atom.workspace.open(todoList)

  'custom:screenshot-prep': ->
    oldWindowDimensions = atom.getWindowDimensions()
    atom.setWindowDimensions('width': 1366, 'height': 768)

  'custom:screenshot-restore': ->
    atom.setWindowDimensions(oldWindowDimensions)

  'custom:throw-error': ->
    throw new Error

path = require 'path'

# require('web-frame').setZoomFactor(1.15)

oldWindowDimensions = {}

atom.commands.add 'atom-workspace',
  'custom:insert-timestamp': ->
    now = new Date()
    atom.workspace.getActiveTextEditor().insertText(now.toISOString())

  'custom:open-todo-list': ->
    todoList = path.join(process.env.HOME, 'Dropbox/todo/todo.txt')
    atom.workspace.open(todoList)

  'custom:screenshot-prep': ->
    oldWindowDimensions = atom.getWindowDimensions()
    atom.setWindowDimensions('width': 1366, 'height': 768)

  'custom:screenshot-restore': ->
    atom.setWindowDimensions(oldWindowDimensions)

  'custom:throw-error': ->
    throw new Error

Add custom command for inserting a date stamp


# Your keymap
#
# Atom keymaps work similarly to stylesheets. Just as stylesheets use selectors
# to apply styles to elements, Atom keymaps use selectors to associate
# keystrokes with events in specific contexts.
#
# You can create a new keybinding in this file by typing "key" and then hitting
# tab.
#
# Here's an example taken from Atom's built-in keymap:
#
# '.editor':
#   'enter': 'editor:newline'
#
# 'body':
#   'ctrl-P': 'core:move-up'
#   'ctrl-p': 'core:move-down'
#
'.editor:not(.mini)':
  'alt-cmd-Z': 'pane:show-previous-item'
'.platform-darwin':
  'ctrl-cmd-r': 'tree-view:reveal-active-file'
  'ctrl-cmd-t': 'window:run-package-specs'
'.workspace':
  'ctrl-shift-q': 'quippet:toggle'

Add better key map for Quippet

Add better key map for Quippet


module.exports =
  # Public: Determines if a string should be considered linewise or character
  #
  # text - The string to consider
  #
  # Returns 'linewise' if the string ends with a line return and 'character'
  #  otherwise.
  copyType: (text) ->
    if text.lastIndexOf("\n") is text.length - 1
      'linewise'
    else if text.lastIndexOf("\r") is text.length - 1
      'linewise'
    else
      'character'

  include: (klass, module) ->
    for key, value of module
      klass::[key] = value

module.exports =
  # Public: Determines if a string should be considered linewise or character
  #
  # text - The string to consider
  #
  # Returns 'linewise' if the string ends with a line return and 'character'
  #  otherwise.
  copyType: (text) ->
    if text.lastIndexOf("\n") is text.length - 1
      'linewise'
    else if text.lastIndexOf("\r") is text.length - 1
      'linewise'
    else
      'character'

  # Include module(object which normaly provides set of methods) to klass
  include: (klass, module) ->
    for key, value of module
      klass::[key] = value

Add description for include utility func


module.exports = ->
  { dash } = require 'bongo'

  bongo = this

  (req, res, next) ->

    {
      channelName
      queue: requestQueue
      sessionToken
      userArea
    } = req.body

    unless requestQueue?.length
      return res.status(400).end()

    responseQueue = new Array requestQueue.length

    bongo.fetchClient sessionToken, userArea, (client) ->

      unless client
        console.log "bongo.fetchClient: #{sessionToken} not found in #{userArea}"
        return res.status(500).send 'An error occcurred'

      workQueue = requestQueue.map (message, i) -> ->
        bongo.handleRequest channelName, { message, client }, (secretName, callbackId, args) ->
          bongo.scrubResponse callbackId, args, (message) ->
            responseQueue[i] = message
            workQueue.fin()

      dash workQueue, -> res.send responseQueue

module.exports = ->
  { dash } = require 'bongo'

  bongo = this

  (req, res, next) ->

    {
      channelName
      queue: requestQueue
      sessionToken
      userArea
    } = req.body

    unless requestQueue?.length
      return res.status(400).end()

    responseQueue = new Array requestQueue.length

    bongo.fetchClient sessionToken, userArea, (client) ->

      unless client
        console.log "bongo.fetchClient: #{sessionToken} not found in #{userArea}"
        return res.status(500).send 'An error occcurred'

      if sessionToken isnt client.sessionToken
        res.cookie 'clientId', client.sessionToken

      workQueue = requestQueue.map (message, i) -> ->
        bongo.handleRequest channelName, { message, client }, (secretName, callbackId, args) ->
          bongo.scrubResponse callbackId, args, (message) ->
            responseQueue[i] = message
            workQueue.fin()

      dash workQueue, -> res.send responseQueue

Set session token as client identifier cookie


Immutable = require 'immutable'
React = require 'react'
Router = require 'react-router'

utils = require '../utils.coffee'
URL = require '../url.coffee'

module.exports = React.createClass
	displayName: 'Link'
	render: ->
		props = Immutable.fromJS(@props).toJS()
		if props.lang
			props.params.lang = props.lang
		if !props.to
			if props.href
				href = props.href
			else
				href = URL.getPath props.params
			props.to = URL.getHref href, props.params
		React.createElement Router.Link, props

Immutable = require 'immutable'
React = require 'react'
Router = require 'react-router'

utils = require '../utils.coffee'
URL = require '../url.coffee'

module.exports = React.createClass
	displayName: 'Link'
	render: ->
		props = Immutable.fromJS(@props).filterNot(
			utils.keyIn 'lang', 'href'
		).toJS()
		if @props.lang
			props.params.lang = @props.lang
			props.hrefLang = @props.lang
		if !props.to
			if @props.href
				href = @props.href
			else
				href = URL.getPath props.params
				props.rel = 'alternate'
			props.to = URL.getHref href, props.params
		React.createElement Router.Link, props

Set hreflang and rel="alternate" on Link.


React = require 'react'
_ = require 'underscore'
classnames = require 'classnames'

{AnswersTable} = require './answers-table'
ArbitraryHtmlAndMath = require '../html'
FormatsListing = require './formats-listing'

FormatsListing = React.createClass
  propTypes:
    formats: React.PropTypes.arrayOf(React.PropTypes.string).isRequired

  render: ->

Formats:

      {for format in @props.formats
        {format}}

module.exports = FormatsListing

React = require 'react'
_ = require 'underscore'
classnames = require 'classnames'

{AnswersTable} = require './answers-table'
ArbitraryHtmlAndMath = require '../html'
FormatsListing = require './formats-listing'

FormatsListing = React.createClass
  propTypes:
    formats: React.PropTypes.arrayOf(React.PropTypes.string).isRequired

  render: ->

Formats:

      {for format, i in @props.formats
        {format}}

module.exports = FormatsListing

Use format index as key

Use format index as key

In some cases in the editor, formats may be duplicated before save


# Copyright 2011-2015, The Trustees of Indiana University and Northwestern
#   University.  Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
#   under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
#   CONDITIONS OF ANY KIND, either express or implied. See the License for the
#   specific language governing permissions and limitations under the License.
# ---  END LICENSE_HEADER BLOCK  ---

$ ->
  import_button_html = '
<button type="submit" name="media_object[import_bib_record]" class="btn btn-success" value="yes">Import</button>
'
  $('div.import-button').append(import_button_html)

# Copyright 2011-2015, The Trustees of Indiana University and Northwestern
#   University.  Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
#   under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
#   CONDITIONS OF ANY KIND, either express or implied. See the License for the
#   specific language governing permissions and limitations under the License.
# ---  END LICENSE_HEADER BLOCK  ---

$ ->
  import_button_html = '
<button id="media_object_bibliographic_id_btn" type="submit" name="media_object[import_bib_record]" class="btn btn-success" value="yes">Import</button>
'
  $('div.import-button').append(import_button_html)
  enable_bib_btn()
  $('#media_object_bibliographic_id').keyup -> enable_bib_btn()

enable_bib_btn = ->
    if $('#media_object_bibliographic_id').val() == ""
      $('#media_object_bibliographic_id_btn').prop('disabled', true)
    else
      $('#media_object_bibliographic_id_btn').prop('disabled', false)
    return

Disable bib import if there's no value

Disable bib import if there's no value


define [
  'jquery'
  'underscore'
  'backbone'
  'marionette'
  'cs!collections/media-types'
  'cs!views/workspace/menu/auth'
  'cs!views/workspace/menu/add'
  'cs!views/workspace/menu/toolbar-search'
  'hbs!templates/layouts/workspace/menu'
], ($, _, Backbone, Marionette, mediaTypes, AuthView, AddView, toolbarView, menuTemplate) ->

  _toolbar = null

  return new class MenuLayout extends Marionette.Layout
    template: menuTemplate

    regions:
      add: '#workspace-menu-add'
      auth: '#workspace-menu-auth'
      toolbar: '#workspace-menu-toolbar'

    onRender: () ->
      @showView(_toolbar)

    showView: (view) ->
      _toolbar = view or toolbarView

      @add.show(new AddView {collection:mediaTypes})
      @auth.show(new AuthView())
      @toolbar.show(_toolbar)

    showToolbar: (view) ->
      @showView(view or toolbarView)

define [
  'jquery'
  'underscore'
  'backbone'
  'marionette'
  'cs!collections/media-types'
  'cs!session'
  'cs!views/workspace/menu/auth'
  'cs!views/workspace/menu/add'
  'cs!views/workspace/menu/toolbar-search'
  'hbs!templates/layouts/workspace/menu'
], ($, _, Backbone, Marionette, mediaTypes, session, AuthView, AddView, toolbarView, menuTemplate) ->

  _toolbar = null

  return new class MenuLayout extends Marionette.Layout
    template: menuTemplate

    regions:
      add: '#workspace-menu-add'
      auth: '#workspace-menu-auth'
      toolbar: '#workspace-menu-toolbar'

    onRender: () ->
      @showView(_toolbar)

    showView: (view) ->
      _toolbar = view or toolbarView

      @add.show(new AddView {collection:mediaTypes})
      @auth.show(new AuthView {model: session})
      @toolbar.show(_toolbar)

    showToolbar: (view) ->
      @showView(view or toolbarView)

Include session in AuthView constructor

Include session in AuthView constructor


Settings = require 'settings-sharelatex'

module.exports = Features =
	externalAuthenticationSystemUsed: ->
		Settings.ldap? or Settings.saml? or Settings.overleaf?.oauth?

	hasFeature: (feature) ->
		switch feature
			when 'homepage'
				return Settings.enableHomepage
			when 'registration'
				return not Features.externalAuthenticationSystemUsed()
			when 'github-sync'
				return Settings.enableGithubSync
			when 'v1-return-message'
				return Settings.accountMerge? and Settings.overleaf?
			when 'v2-banner'
				return Settings.showV2Banner
			when 'custom-togglers'
				return Settings.overleaf?
			when 'templates'
				return !Settings.overleaf?
			when 'affiliations'
				return Settings?.apis?.v1?.url?
			when 'rich-text'
				isEnabled = true # Switch to false to disable
				Settings.overleaf? and isEnabled
			else
				throw new Error("unknown feature: #{feature}")

Settings = require 'settings-sharelatex'

module.exports = Features =
	externalAuthenticationSystemUsed: ->
		Settings.ldap? or Settings.saml? or Settings.overleaf?.oauth?

	hasFeature: (feature) ->
		switch feature
			when 'homepage'
				return Settings.enableHomepage
			when 'registration'
				return not Features.externalAuthenticationSystemUsed()
			when 'github-sync'
				return Settings.enableGithubSync
			when 'v1-return-message'
				return Settings.accountMerge? and Settings.overleaf?
			when 'v2-banner'
				return Settings.showV2Banner
			when 'custom-togglers'
				return Settings.overleaf?
			when 'templates'
				return !Settings.overleaf?
			when 'affiliations'
				return Settings?.apis?.v1?.url?
			when 'rich-text'
				isEnabled = true # Switch to false to disable
				Settings.overleaf? and isEnabled
			when 'redirect-sl'
				return Settings.createV1AccountOnLogin
			else
				throw new Error("unknown feature: #{feature}")

Add feature flag for redirecting SL to v2


###!
Copyright (c) 2002-2015 "Neo Technology,"
Network Engine for Objects in Lund AB [http://neotechnology.com]

This file is part of Neo4j.

Neo4j is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
###

'use strict';

# Requires jQuery
angular.module('neo4jApp.directives')
  .directive('helpTopic', ['$rootScope', 'Frame','Settings', ($rootScope, Frame, Settings) ->
    restrict: 'A'
    link: (scope, element, attrs) ->

      topic = attrs.helpTopic
      command = "help"

      if topic
        element.on 'click', (e) ->
          e.preventDefault()

          topic = topic.toLowerCase().trim().replace('-', ' ')
          Frame.create(input: "#{Settings.cmdchar}#{command} #{topic}")

          $rootScope.$apply() unless $rootScope.$$phase

  ])

###!
Copyright (c) 2002-2015 "Neo Technology,"
Network Engine for Objects in Lund AB [http://neotechnology.com]

This file is part of Neo4j.

Neo4j is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
###

'use strict';

# Requires jQuery
angular.module('neo4jApp.directives')
  .directive('helpTopic', ['$rootScope', 'Frame','Settings', ($rootScope, Frame, Settings) ->
    restrict: 'A'
    link: (scope, element, attrs) ->

      topic = attrs.helpTopic
      command = "help"

      if topic
        element.on 'click', (e) ->
          e.preventDefault()

          topic = topic.toLowerCase().trim().replace(/-/g, ' ')
          Frame.create(input: "#{Settings.cmdchar}#{command} #{topic}")

          $rootScope.$apply() unless $rootScope.$$phase

  ])

Replace all dashes in help topic name.

Replace all dashes in help topic name.


# ***** BEGIN LICENSE BLOCK *****
# Copyright (c) 2011-2012 VMware, Inc.
#
# For the license see COPYING.
# ***** END LICENSE BLOCK *****

utils = require('./utils')
transport = require('./transport')

class EventSourceReceiver extends transport.ResponseReceiver
    protocol: "eventsource"

    doSendFrame: (payload) ->
        # Beware of leading whitespace
        data = ['data: ',
                utils.escape_selected(payload, '\r\n\x00'),
                '\r\n\r\n']
        super(data.join(''))

exports.app =
    eventsource: (req, res) ->
        res.setHeader('Content-Type', 'text/event-stream; charset=UTF-8')
        res.writeHead(200)
        # Opera needs one more new line at the start.
        res.write('\r\n')

        transport.register(req, @, new EventSourceReceiver(req, res, @options))
        return true

# ***** BEGIN LICENSE BLOCK *****
# Copyright (c) 2011-2012 VMware, Inc.
#
# For the license see COPYING.
# ***** END LICENSE BLOCK *****

utils = require('./utils')
transport = require('./transport')

class EventSourceReceiver extends transport.ResponseReceiver
    protocol: "eventsource"

    doSendFrame: (payload) ->
        # Beware of leading whitespace
        data = ['data: ',
                utils.escape_selected(payload, '\r\n\x00'),
                '\r\n\r\n']
        super(data.join(''))

exports.app =
    eventsource: (req, res) ->
        if !req.headers['origin'] or req.headers['origin'] is 'null'
            origin = '*'
        else
            origin = req.headers['origin']
        res.setHeader('Content-Type', 'text/event-stream; charset=UTF-8')
        res.setHeader('Access-Control-Allow-Origin', origin)
        res.setHeader('Vary', 'Origin')
        headers = req.headers['access-control-request-headers']
        if headers
            res.setHeader('Access-Control-Allow-Headers', headers)
        res.setHeader('Access-Control-Allow-Credentials', 'true')

        res.writeHead(200)
        # Opera needs one more new line at the start.
        res.write('\r\n')

        transport.register(req, @, new EventSourceReceiver(req, res, @options))
        return true

Add CORS headers to eventsource

Add CORS headers to eventsource


"use strict"

ObjectID = require('mongodb').ObjectID

exports.param = (req, res, next, id) ->
  return res.send 400, 'invalid oid' if not /[a-f0-9]{24}/.test id
  req.id = new ObjectID id
  next()

exports.options = (req, res, next) ->
  res.setHeader 'Access-Control-Allow-Methods', 'GET, PUT, PATCH, DELETE'
  res.send()

exports.get = (req, res, next) ->
  req.col.findOne _id: req.id, (err, doc) ->
    return next err if err
    return res.json 200, documents: [doc], count: 1 if doc
    return res.json 404, error: 'Document Not Found'

exports.put = (req, res, next) ->
  res.send 501, 'Not Implemented'
  # 200, object

exports.patch = (req, res, next) ->
  res.send 501, 'Not Implemented'
  # 200, object

exports.delete = (req, res, next) ->
  res.send 501, 'Not Implemented'
  # 204

"use strict"

ObjectID = require('mongodb').ObjectID

exports.param = (req, res, next, id) ->
  return res.json 400, error: 'Invalid ObjectID' if not /[a-f0-9]{24}/.test id
  req.id = ObjectID.createFromHexString id
  next()

exports.options = (req, res, next) ->
  res.setHeader 'Access-Control-Allow-Methods', 'GET, PUT, PATCH, DELETE'
  res.send()

exports.get = (req, res, next) ->
  req.col.findOne _id: req.id, (err, doc) ->
    return next err if err
    return res.json 200, documents: [doc], count: 1 if doc
    return res.json 404, error: 'Document Not Found'

exports.put = (req, res, next) ->
  res.send 501, 'Not Implemented'
  # 200, object

exports.patch = (req, res, next) ->
  res.send 501, 'Not Implemented'
  # 200, object

exports.delete = (req, res, next) ->
  res.send 501, 'Not Implemented'
  # 204

Add new ObjectID parsing and error hanling


qbnApp = angular.module 'qbnApp', []

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

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

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

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

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

qbnApp = angular.module 'qbnApp', []

# The immutable objects that make up the game itself serve as prototypes for the objects that make
# up the mutable game state. This function takes a game definition object and makes a state
# instance of it.
#
# By convention, any property on the prototype of the form "defaultThing" will be copied on the
# instance as a property called "thing". This is to allow easy instantiation of default, starting
# values.
instantiate = (proto) ->
  instance = Object.create proto
  for key, value of proto when /^default\w{2}/.test(key)
    [_, first, rest] = /^default(\w)(\w*)/.exec(key)
    newName = first.toLowerCase() + rest
    instance[newName] = value
  return instance

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

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

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

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

Move instantiate() and Add Documentation


###
TODO: describe

call: (String) -> Object
    @param 1
    @return
resolveVendor: (String) -> Object
    @param 1
    @return
###

# Possible javascript vendor prefixes
VENDOR_PREFIXES = [
    "webkit"
    "ms"
    "moz"
    "o"
]

capitalizeString = (str) ->
    if str.length > 1
        str[0].toUpperCase() + str.slice(1)
    else
        str.toUpperCase()

# Create a copy of the global object.
globalRef = global

# Maps the given name to a set of vendor-prefixed names, then looks for these objects in the global namespace
resolveWindowObject = (name) ->
    capitalizedName = capitalizeString name
    # Build a list of possible objects, filtering down the results to those returning an actual value
    possibleObjects = (obj for obj in (globalRef[prefix + capitalizedName] for prefix in VENDOR_PREFIXES) when obj?)
    # Return either the first object or null
    possibleObjects[0] or null

# Default action, just proxy the request
windowProxy = (name) -> globalRef[name]

# Expanded action, try to directly proxy, otherwise try to resolve with vendor prefixes
windowProxy.resolveVendor = (name) ->
    globalRef[name] ? resolveWindowObject name

module.exports = windowProxy

###
TODO: describe

call: (String) -> Object
    @param 1
    @return
resolveVendor: (String) -> Object
    @param 1
    @return
###

# Possible javascript vendor prefixes
VENDOR_PREFIXES = [
    "webkit"
    "ms"
    "moz"
    "o"
]

capitalizeString = (str) ->
    if str.length > 1
        str[0].toUpperCase() + str.slice(1)
    else
        str.toUpperCase()

# Create a copy of the global object.
globalRef = global or {}

# Maps the given name to a set of vendor-prefixed names, then looks for these objects in the global namespace
resolveWindowObject = (name) ->
    capitalizedName = capitalizeString name
    # Build a list of possible objects, filtering down the results to those returning an actual value
    possibleObjects = (obj for obj in (globalRef[prefix + capitalizedName] for prefix in VENDOR_PREFIXES) when obj?)
    # Return either the first object or null
    possibleObjects[0] or null

# Default action, just proxy the request
windowProxy = (name) -> globalRef[name]

# Expanded action, try to directly proxy, otherwise try to resolve with vendor prefixes
windowProxy.resolveVendor = (name) ->
    globalRef[name] ? resolveWindowObject name

module.exports = windowProxy

Make sure global is something defined

Make sure global is something defined


@App.factory 'MnoeApiSvc', ($log, Restangular, inflector) ->
  return Restangular.withConfig((RestangularProvider) ->
    RestangularProvider.setBaseUrl('/mnoe/jpi/v1/admin')
    RestangularProvider.setDefaultHeaders({Accept: "application/json"})

    # Unwrap api response
    RestangularProvider.addResponseInterceptor(
      (data, operation, what, url, response, deferred) ->
        extractedData = null
        if (operation == 'getList')
          extractedData = data[what]
        else if (operation == 'put' || operation == 'post')
          what = inflector.singularize(what)
          extractedData = data[what]
        else
          extractedData = data
        return extractedData
    )
  )

@App.factory 'MnoeApiSvc', ($log, Restangular, inflector) ->
  return Restangular.withConfig((RestangularProvider) ->
    RestangularProvider.setBaseUrl('/mnoe/jpi/v1/admin')
    RestangularProvider.setDefaultHeaders({Accept: "application/json"})

    # Unwrap api response
    RestangularProvider.addResponseInterceptor(
      (data, operation, what, url, response, deferred) ->
        extractedData = null
        if (operation == 'getList')
          extractedData = data[what]
        else if (operation == 'get' || operation == 'put' || operation == 'post')
          what = inflector.singularize(what)
          extractedData = data[what]
        else
          extractedData = data
        return extractedData
    )
  )

Add operation GET to response interceptor unwrapper


setImmediate = process.nextTick unless setImmediate?

class Channel

  constructor: ->
    @handlers = []

  send: (args...) ->
    setImmediate => @fire args...

  fire: (message) ->
    @package message
    for handler in @handlers
      handler message

  receive: (handler) ->
    @handlers.push handler

  remove: (handler) ->
    @handlers = (_handler for _handler in @handlers when _handler isnt handler)

  forward: (channel) ->
    @receive (message) =>
      channel.fire message

  source: (block) ->
    channel = new @constructor
    channel.forward @
    block channel if block?
    channel

  package: (message) ->
    message

module.exports = Channel

setImmediate = process.nextTick unless setImmediate?

class Channel

  constructor: ->
    @handlers = []

  send: (message) ->
    setImmediate => @fire( message )

  fire: (message) ->
    @package message
    for handler in @handlers
      handler message

  receive: (handler) ->
    @handlers.push handler

  remove: (handler) ->
    @handlers = (_handler for _handler in @handlers when _handler isnt handler)

  forward: (channel) ->
    @receive (message) =>
      channel.fire message

  source: (block) ->
    channel = new @constructor
    channel.forward @
    block channel if block?
    channel

  package: (message) ->
    message

module.exports = Channel

Send and fire should have consistent interfaces


{_, $, fs, View} = require 'atom'
MatchView = require './match-view'
path = require 'path'

module.exports =
class ResultView extends View
  @content: (model, filePath, matches) ->
    iconClass = if fs.isReadmePath(filePath) then 'icon-book' else 'icon-file-text'

    @li class: 'path list-nested-item', 'data-path': _.escapeAttribute(filePath), =>
      @div outlet: 'pathDetails', class: 'path-details list-item', =>
        @span class: 'disclosure-arrow'
        @span class: iconClass + ' icon'
        @span class: 'path-name bright', filePath.replace(atom.project.getPath()+path.sep, '')
        @span outlet: 'description', class: 'path-match-number'
      @ul outlet: 'matches', class: 'matches list-tree', =>

  initialize: (@model, @filePath, result) ->
    @renderResult(result)

  renderResult: (result) ->
    selectedIndex = @matches.find('.selected').index()

    @matches.empty()

    if result
      @description.show().text("(#{matches?.length})")
    else
      @description.hide()

    matches = result?.matches
    if not matches or matches.length == 0
      @hide()
    else
      @show()
      for match in matches
        @matches.append(new MatchView(@model, {@filePath, match}))

    @matches.children().eq(selectedIndex).addClass('selected') if selectedIndex > -1

  confirm: ->
    atom.workspaceView.openSingletonSync(@filePath, split: 'left')

{_, $, fs, View} = require 'atom'
MatchView = require './match-view'
path = require 'path'

module.exports =
class ResultView extends View
  @content: (model, filePath, result) ->
    iconClass = if fs.isReadmePath(filePath) then 'icon-book' else 'icon-file-text'

    @li class: 'path list-nested-item', 'data-path': _.escapeAttribute(filePath), =>
      @div outlet: 'pathDetails', class: 'path-details list-item', =>
        @span class: 'disclosure-arrow'
        @span class: iconClass + ' icon'
        @span class: 'path-name bright', filePath.replace(atom.project.getPath()+path.sep, '')
        @span outlet: 'description', class: 'path-match-number'
      @ul outlet: 'matches', class: 'matches list-tree', =>

  initialize: (@model, @filePath, result) ->
    @renderResult(result)

  renderResult: (result) ->
    matches = result?.matches
    selectedIndex = @matches.find('.selected').index()

    @matches.empty()

    if result
      @description.show().text("(#{matches?.length})")
    else
      @description.hide()

    if not matches or matches.length == 0
      @hide()
    else
      @show()
      for match in matches
        @matches.append(new MatchView(@model, {@filePath, match}))

    @matches.children().eq(selectedIndex).addClass('selected') if selectedIndex > -1

  confirm: ->
    atom.workspaceView.openSingletonSync(@filePath, split: 'left')

Fix match count on result view

Fix match count on result view


Travis.ExpandableRecordArray = DS.RecordArray.extend
  isLoaded: false
  isLoading: false

  load: (array) ->
    @set 'isLoading', true
    self = this

    observer = ->
      if @get 'isLoaded'
        content = self.get 'content'

        array.removeObserver 'isLoaded', observer
        array.forEach (record) ->
          self.pushObject record

        self.set 'isLoading', false
        self.set 'isLoaded',  true

    array.addObserver 'isLoaded', observer

  observe: (collection, filterWith) ->
    @set 'filterWith', filterWith
    collection.addArrayObserver this,
      willChange: 'observedArrayWillChange'
      didChange: 'observedArraydidChange'

  observedArrayWillChange: (->)
  observedArraydidChange: (array, index, removedCount, addedCount) ->
    addedObjects = array.slice index, index + addedCount
    for object in addedObjects
      if @get('filterWith').call this, object
        @pushObject object

  pushObject: (record) ->
    content   = @get 'content'
    id        = record.get 'id'
    clientId  = record.get 'clientId'
    reference = @get('store').referenceForClientId(clientId)

    @addReference reference

Travis.ExpandableRecordArray = Ember.RecordArray.extend
  isLoaded: false
  isLoading: false

  load: (array) ->
    @set 'isLoading', true
    self = this

    observer = ->
      if @get 'isLoaded'
        content = self.get 'content'

        array.removeObserver 'isLoaded', observer
        array.forEach (record) ->
          self.pushObject record

        self.set 'isLoading', false
        self.set 'isLoaded',  true

    array.addObserver 'isLoaded', observer

  observe: (collection, filterWith) ->
    @set 'filterWith', filterWith
    collection.addArrayObserver this,
      willChange: 'observedArrayWillChange'
      didChange: 'observedArraydidChange'

  observedArrayWillChange: (->)
  observedArraydidChange: (array, index, removedCount, addedCount) ->
    addedObjects = array.slice index, index + addedCount
    for object in addedObjects
      if @get('filterWith').call this, object
        @pushObject object

  pushObject: (record) ->
    @get('content').pushObject(record)

Update ExpandableRecordArray to work correctly with Ember Model


Hummingbird.UserController = Ember.ObjectController.extend
  coverUpload: Ember.Object.create()
  coverUrl: Ember.computed.any('coverUpload.croppedImage', 'model.coverImageUrl')
  coverImageStyle: (->
    "background-image: url(" + @get('coverUrl') + ")"
  ).property('coverUrl')

  forumProfile: (->
    "http://forums.hummingbird.me/users/" + @get('model.username')
  ).property('model.username')

  # Legacy URLs
  feedURL: (->
    "/users/" + @get('model.username') + "/feed"
  ).property('model.username')
  libraryURL: (->
    "/users/" + @get('model.username') + "/watchlist"
  ).property('model.username')

  actions:
    coverSelected: (file) ->
      that = this
      reader = new FileReader()
      reader.onload = (e) ->
        that.set 'coverUpload.originalImage', e.target.result
        that.send 'openModal', 'crop-cover', that.get('coverUpload')
      reader.readAsDataURL(file)

Hummingbird.UserController = Ember.ObjectController.extend
  coverUpload: Ember.Object.create()
  coverUrl: Ember.computed.any('coverUpload.croppedImage', 'model.coverImageUrl')
  coverImageStyle: (->
    "background-image: url(" + @get('coverUrl') + ")"
  ).property('coverUrl')

  viewingSelf: (->
    @get('model.id') == @get('currentUser.id')
  ).property('model.id')

  forumProfile: (->
    "http://forums.hummingbird.me/users/" + @get('model.username')
  ).property('model.username')

  # Legacy URLs
  feedURL: (->
    "/users/" + @get('model.username') + "/feed"
  ).property('model.username')
  libraryURL: (->
    "/users/" + @get('model.username') + "/watchlist"
  ).property('model.username')

  actions:
    coverSelected: (file) ->
      that = this
      reader = new FileReader()
      reader.onload = (e) ->
        that.set 'coverUpload.originalImage', e.target.result
        that.send 'openModal', 'crop-cover', that.get('coverUpload')
      reader.readAsDataURL(file)

Fix viewingSelf attribute in user controller


{Testing, expect, _} = require '../helpers/component-testing'

LearningGuide = require '../../../src/flux/learning-guide'
Button = require '../../../src/components/learning-guide/practice-button'

COURSE_ID  = '1'
GUIDE_DATA = require '../../../api/courses/1/guide.json'

describe 'Learning Guide Practice Button', ->

  beforeEach ->
    LearningGuide.Student.actions.loaded(GUIDE_DATA, COURSE_ID)

  it 'can be rendered and sets the name', ->
    Testing.renderComponent( Button,
      props: { courseId: COURSE_ID, title: 'Practice moar' }
    ).then ({dom}) ->
      expect(dom.textContent).to.equal('Practice moar')

  it 'practices pages', ->
    Testing.renderComponent( Button,
      props: { courseId: COURSE_ID, title: 'Practice moar' }
    ).then ({dom}) ->
      Testing.actions.click(dom)
      expect(Testing.router.transitionTo).to.have.been.calledWith( 'viewPractice',
        { courseId: COURSE_ID }, { page_ids: ['2', '3', '5', '6'] }
      )

  it 'is disabled if no page ids exist', ->
    newdata = {"title": "Physics"}
    LearningGuide.Student.actions.loaded(newdata, COURSE_ID)
    Testing.renderComponent( Button,
      props: { courseId: COURSE_ID, title: 'title' }
    ).then ({dom, element}) ->
      expect(_.toArray dom.classList).to.include('disabled')

{Testing, expect, _} = require '../helpers/component-testing'

LearningGuide = require '../../../src/flux/learning-guide'
Button = require '../../../src/components/learning-guide/practice-button'

COURSE_ID  = '1'
GUIDE_DATA = require '../../../api/courses/1/guide.json'

describe 'Learning Guide Practice Button', ->

  beforeEach ->
    LearningGuide.Student.actions.loaded(GUIDE_DATA, COURSE_ID)

  it 'can be rendered and sets the name', ->
    Testing.renderComponent( Button,
      props: { courseId: COURSE_ID, title: 'Practice moar' }
    ).then ({dom}) ->
      expect(dom.textContent).to.equal('Practice moar')

  it 'practices pages', ->
    Testing.renderComponent( Button, props: {
      courseId: COURSE_ID, title: 'Practice moar'
      sections: GUIDE_DATA.children[0].children
    }).then ({dom}) ->
      Testing.actions.click(dom)
      expect(Testing.router.transitionTo).to.have.been.calledWith( 'viewPractice',
        { courseId: COURSE_ID }, { page_ids: ['2', '3'] }
      )

  it 'is disabled if no page ids exist', ->
    newdata = {"title": "Physics"}
    LearningGuide.Student.actions.loaded(newdata, COURSE_ID)
    Testing.renderComponent( Button,
      props: { courseId: COURSE_ID, title: 'title' }
    ).then ({dom, element}) ->
      expect(_.toArray dom.classList).to.include('disabled')

Fix to test new pages

Fix to test new pages


should = require('should')
missingSupport = require('../dist/lib/missing-support')

describe 'missing-support', ->
  describe 'filtering caniuse-db data by browser selection', ->
    it 'for browser request ie >= 7, safari >= 6, opera >= 10.1',->
      data = missingSupport(['ie >= 7', 'safari >= 6', 'opera >= 10.1']).features
      bgimgopts = data['background-img-opts']

      bgimgopts.should.have.keys('missing', 'missingData', 'caniuseData')

      missing = bgimgopts.missingData
      missing.should.have.keys('ie', 'safari', 'opera')
      missing['ie'].should.have.keys('7', '8')
      missing['safari'].should.have.keys('6', '6.1')

should = require('should')
missingSupport = require('../dist/lib/missing-support')

describe 'missing-support', ->
  it 'provides list of selected browsers', ->
    data = missingSupport(['ie >= 8'])
    data.browsers.should.containDeep [
      ['ie','8']
      ['ie','9']
      ['ie', '10']
      ['ie', '11']
    ]

  describe 'filtering caniuse-db data by browser selection', ->
    it 'for browser request ie >= 7, safari >= 6, opera >= 10.1',->
      data = missingSupport(['ie >= 7', 'safari >= 6', 'opera >= 10.1'])
        .features

      bgimgopts = data['background-img-opts']

      bgimgopts.should.have.keys('missing', 'missingData', 'caniuseData')

      missing = bgimgopts.missingData
      missing.should.have.keys('ie', 'safari', 'opera')
      missing['ie'].should.have.keys('7', '8')
      missing['safari'].should.have.keys('6', '6.1')

    it 'only yields features not supported by selected browser', ->
      data = missingSupport(['ie 8']).features
      for f, featureData of data
        featureData.missingData.should.have.keys('ie')

Add failing test for bug

Add failing test for bug


PasswordResetHandler = require("./PasswordResetHandler")
RateLimiter = require("../../infrastructure/RateLimiter")

module.exports =

	renderRequestResetForm: (req, res)->
		res.render "user/passwordReset",
			title:"Reset Password"

	requestReset: (req, res)->
		email = req.body.email.trim().toLowerCase()
		opts =
			endpointName:"auto_compile"
			timeInterval:60
			subjectName:email
			throttle: 3
		RateLimiter.addCount opts, (err, canCompile)->
			if !canCompile
				return res.send 500
			PasswordResetHandler.generateAndEmailResetToken email, (err)->
				if err?
					res.send 500, {message:err?.message}
				else
					res.send 200

	renderSetPasswordForm: (req, res)->
		res.render "user/setPassword",
			title:"Set Password"
			passwordResetToken:req.query.passwordResetToken

	setNewUserPassword: (req, res)->
		{passwordResetToken, password} = req.body
		if !password? or password.length == 0 or !passwordResetToken? or passwordResetToken.length == 0
			return res.send 500
		PasswordResetHandler.setNewUserPassword passwordResetToken?.trim(), password?.trim(), (err)->
			if err?
				res.send 500
			else
				res.send 200

PasswordResetHandler = require("./PasswordResetHandler")
RateLimiter = require("../../infrastructure/RateLimiter")

module.exports =

	renderRequestResetForm: (req, res)->
		res.render "user/passwordReset",
			title:"Reset Password"

	requestReset: (req, res)->
		email = req.body.email.trim().toLowerCase()
		opts =
			endpointName: "password_reset_rate_limit"
			timeInterval: 60
			subjectName: req.ip
			throttle: 6
		RateLimiter.addCount opts, (err, canCompile)->
			if !canCompile
				return res.send 500, { message: "Rate limit hit. Please wait a while before retrying" }
			PasswordResetHandler.generateAndEmailResetToken email, (err)->
				if err?
					res.send 500, {message:err?.message}
				else
					res.send 200

	renderSetPasswordForm: (req, res)->
		res.render "user/setPassword",
			title:"Set Password"
			passwordResetToken:req.query.passwordResetToken

	setNewUserPassword: (req, res)->
		{passwordResetToken, password} = req.body
		if !password? or password.length == 0 or !passwordResetToken? or passwordResetToken.length == 0
			return res.send 500
		PasswordResetHandler.setNewUserPassword passwordResetToken?.trim(), password?.trim(), (err)->
			if err?
				res.send 500
			else
				res.send 200

Fix password reset rate limit to work on ip, not email which changes every request


determineBrowser = ->
  [ 'chrome', 'firefox', 'safari', 'phantom_js', 'unsupported-browser' ]
  .filter( (browser) -> $('html.'+browser).length
  )[0]

extension_link_by_browser =
  firefox: 'https://static.factlink.com/extension/firefox/factlink-latest.xpi'
  safari: 'https://static.factlink.com/extension/firefox/factlink.safariextz'
  chrome: 'javascript:chrome.webstore.install()'

icon_class_by_browser =
  firefox: 'install-firefox'
  safari: 'install-safari'
  chrome: 'install-chrome'

window.ReactInstallExtensionButton = React.createClass
  displayName: 'ReactInstallExtensionButton'
  render: ->
    browserName = determineBrowser()
    extension_link = extension_link_by_browser[browserName]
    icon_class = icon_class_by_browser[browserName]
    extra_class = if @props.huge_button then 'button-huge' else null
    if document.documentElement.getAttribute('data-factlink-extension-loaded') != undefined
      _button ['button', extra_class,
        disabled: true],
        'Factlink already installed.'
    else
      _a ['button-success', extra_class, href: extension_link],
        _span [icon_class]
        'Install Factlink for ' + browserName.capitalize()

determineBrowser = ->
  [ 'chrome', 'firefox', 'safari', 'phantom_js', 'unsupported-browser' ]
  .filter( (browser) -> $('html.'+browser).length
  )[0]

extension_install_options_by_browser =
  firefox:
    href: 'https://static.factlink.com/extension/firefox/factlink-latest.xpi'
    iconClass: 'install-firefox'
  safari:
    href: 'https://static.factlink.com/extension/firefox/factlink.safariextz'
    iconClass: 'install-safari'
  chrome:
    href: 'javascript:chrome.webstore.install()'
    iconClass: 'install-chrome'

window.ReactInstallExtensionButton = React.createClass
  displayName: 'ReactInstallExtensionButton'
  render: ->
    browserName = determineBrowser()
    options = extension_install_options_by_browser[browserName]
    extra_class = if @props.huge_button then 'button-huge' else null
    if document.documentElement.getAttribute('data-factlink-extension-loaded') != undefined
      _button ['button', extra_class,
        disabled: true],
        'Factlink already installed.'
    else
      _a ['button-success', extra_class, href: options.href],
        _span [options.iconClass]
        'Install Factlink for ' + browserName.capitalize()

Simplify (if not shorten) code

Simplify (if not shorten) code


class window.SubchannelsView extends Backbone.Factlink.CompositeView
  tagName: "div"
  id: "contained-channels"
  template: "channels/subchannels"
  itemView: SubchannelItemView
  events:
    "click #more-button": "toggleMore"

  initialize: ->
    @collection.bind "remove", @renderCollection, this

  toggleMore: ->
    button = @$("#more-button .label")
    @$(".overflow").slideToggle (e) ->
      button.text (if button.text() is "more" then "less" else "more")

  appendHtml: (collectView, itemView) ->
    @$(".contained-channel-description").show()
    @$("ul").prepend itemView.el

class window.SubchannelsView extends Backbone.Factlink.CompositeView
  tagName: "div"
  id: "contained-channels"
  template: "channels/subchannels"
  itemView: SubchannelItemView
  events:
    "click #more-button": "toggleMore"

  initialize: ->
    @collection.bind "add remove reset", @updateVisibility, @
    @collection.bind "remove", @renderCollection, @

  toggleMore: ->
    button = @$("#more-button .label")
    @$(".overflow").slideToggle (e) ->
      button.text (if button.text() is "more" then "less" else "more")

  appendHtml: (collectView, itemView) ->
    @$(".contained-channel-description").show()
    @$("ul").prepend itemView.el

  updateVisibility: ->
    @$el.toggleClass 'hide', @collection.length <= 0

Hide "Composed of:" element completely if there are no containing channels.


#= require utensils/utensils
#= require utensils/bindable

class utensils.<%= file_name.camelize %>
  constructor: (@el, data) ->
    @data = if data then data else @el.data()
    @options()
    @initialize()
    @addListeners()

  options: ->

  initialize: ->

# PUBLIC #

  dispose: ->

# PROTECTED #

  addListeners: ->

  removeListeners: ->

utensils.Bindable.register '<%= file_name.dasherize %>', utensils.<%= file_name.camelize %>

#= require utensils/utensils
#= require utensils/bindable

class utensils.<%= file_name.camelize %>
  constructor: (@el, data) ->
    @data = if data then data else @el.data()
    @options()
    @initialize()
    @addListeners()

  options: ->

  initialize: ->

# PUBLIC #

  dispose: ->
    @removeListeners()

# PROTECTED #

  addListeners: ->

  removeListeners: ->

utensils.Bindable.register '<%= file_name.dasherize %>', utensils.<%= file_name.camelize %>

Add the call to removeListeners for the generator


class Cable.SubscriberManager
  constructor: (@cable) ->
    @subscribers = {}

  add: (subscriber) ->
    {identifier} = subscriber
    @subscribers[identifier] = subscriber
    @notify(subscriber, "initialized")
    if @sendCommand("subscribe", identifier)
      @notify(subscriber, "connected")

  reload: ->
    for identifier, subscriber of @subscribers
      if @sendCommand("subscribe", identifier)
        @notify(subscriber, "connected")

  remove: (subscriber) ->
    {identifier} = subscriber
    @sendCommand("unsubscribe", identifier)
    delete @subscribers[identifier]

  notifyAll: (callbackName, args...) ->
    for identifier, subscriber of @subscribers
      @notify(subscriber, callbackName, args...)

  notify: (subscriber, callbackName, args...) ->
    if typeof subscriber is "string"
      subscriber = @subscribers[subscriber]

    if subscriber
      subscriber[callbackName]?(args...)

  sendCommand: (command, identifier) ->
    return true if identifier is Cable.PING_IDENTIFIER
    @cable.send({command, identifier})

class Cable.SubscriberManager
  constructor: (@cable) ->
    @subscribers = []

  add: (subscriber) ->
    @subscribers.push(subscriber)
    @notify(subscriber, "initialized")
    if @sendCommand(subscriber, "subscribe")
      @notify(subscriber, "connected")

  reload: ->
    for subscriber in @subscribers
      if @sendCommand(subscriber, "subscribe")
        @notify(subscriber, "connected")

  remove: (subscriber) ->
    @sendCommand(subscriber, "unsubscribe")
    @subscibers = (s for s in @subscribers when s isnt subscriber)

  notifyAll: (callbackName, args...) ->
    for subscriber in @subscribers
      @notify(subscriber, callbackName, args...)

  notify: (subscriber, callbackName, args...) ->
    if typeof subscriber is "string"
      subscribers = (s for s in @subscribers when s.identifier is subscriber)
    else
      subscribers = [subscriber]

    for subscriber in subscribers
      subscriber[callbackName]?(args...)

  sendCommand: (subscriber, command) ->
    {identifier} = subscriber
    return true if identifier is Cable.PING_IDENTIFIER
    @cable.send({command, identifier})

Manage an array of subscribers since there may be more than one subscription to a channel


{Range}  = require('atom')
fuzzaldrin = require('fuzzaldrin')

module.exports =
class SnippetsProvider
  selector: '*'

  constructor: ->
    @showIcon = atom.config.get('autocomplete-plus.defaultProvider') is 'Symbol'

  getSuggestions: ({scopeDescriptor, prefix}) ->
    return unless prefix?.length
    scopeSnippets = atom.config.get('snippets', {scope: scopeDescriptor})
    @findSuggestionsForPrefix(scopeSnippets, prefix)

  findSuggestionsForPrefix: (snippets, prefix) ->
    return [] unless snippets?

    for __, snippet of snippets when snippet.prefix.lastIndexOf(prefix, 0) isnt -1
      iconHTML: if @showIcon then undefined else false
      type: 'snippet'
      text: snippet.prefix
      replacementPrefix: prefix
      rightLabel: snippet.name

  onDidInsertSuggestion: ({editor}) ->
    atom.commands.dispatch(atom.views.getView(editor), 'snippets:expand')

{Range}  = require('atom')
fuzzaldrin = require('fuzzaldrin')

module.exports =
class SnippetsProvider
  selector: '*'

  constructor: ->
    @showIcon = atom.config.get('autocomplete-plus.defaultProvider') is 'Symbol'

  getSuggestions: ({scopeDescriptor, prefix}) ->
    return unless prefix?.length
    scopeSnippets = atom.config.get('snippets', {scope: scopeDescriptor})
    @findSuggestionsForPrefix(scopeSnippets, prefix)

  findSuggestionsForPrefix: (snippets, prefix) ->
    return [] unless snippets?

    for __, snippet of snippets when snippet.prefix.lastIndexOf(prefix, 0) isnt -1
      iconHTML: if @showIcon then undefined else false
      type: 'snippet'
      text: snippet.prefix
      replacementPrefix: prefix
      rightLabel: snippet.name
      description: snippet.description
      descriptionMoreURL: snippet.descriptionMoreURL

  onDidInsertSuggestion: ({editor}) ->
    atom.commands.dispatch(atom.views.getView(editor), 'snippets:expand')

Use the description and URL when present


$(document).on "page:change", ->
  $(".data-table").each ->
    table = $(@)

    return if $.fn.dataTable.isDataTable(table)

    options =
      responsive: true
      order: [[0, "desc"]]
      pageLength: 25

    ascColumn = table.find("th.sort-asc").index()
    descColumn = table.find("th.sort-desc").index()

    if (ascColumn >= 0)
      options["order"] = [[ ascColumn, "asc" ]]

    if (descColumn >= 0)
      options["order"] = [[ descColumn, "desc" ]]

    if table.hasClass("no-paging")
      options["paging"] = false

    table.dataTable(options)

$(document).on "page:change", ->
  $(".data-table").each ->
    table = $(@)

    return if $.fn.dataTable.isDataTable(table)

    fnFooterCallback = (row, data, start, end, display) ->
      monetaryColumnIndex = 1

      # Utility function to convert string dollar amount to a number
      intVal = (i) ->
        if typeof i == 'string' then i.replace(/[\$,]/g, '') * 1 else if typeof i == 'number' then i else 0

      # Calculate the total for the current page
      pageTotal = @api().column(monetaryColumnIndex, page: 'current').data().reduce(((a, b) ->
        intVal(a) + intVal(b)
      ), 0).toFixed(2)

      # Render the pageTotal on the bottom footer.
      $(@api().column(monetaryColumnIndex).footer()).html '$'+ pageTotal

    options =
      responsive: true
      order: [[0, "desc"]]
      pageLength: 25
      fnFooterCallback: fnFooterCallback

    ascColumn = table.find("th.sort-asc").index()
    descColumn = table.find("th.sort-desc").index()

    if (ascColumn >= 0)
      options["order"] = [[ ascColumn, "asc" ]]

    if (descColumn >= 0)
      options["order"] = [[ descColumn, "desc" ]]

    if table.hasClass("no-paging")
      options["paging"] = false

    table.dataTable(options)

Add footer callback in report data tables to recalculate & render table totals on filering event


#!highlights/node_modules/coffee-script/bin/coffee

Highlights = require 'highlights'
fs = require 'fs'
path = require 'path'
highlighter = new Highlights()
highlighter.requireGrammarsSync
  modulePath: require.resolve('./atom-language-perl6/package.json')

stdin = process.openStdin()
stdin.setEncoding 'utf8'
mystderr = process.stderr
stdin.on 'data', (input) ->
    name = input.trim()
    process.exit() if name == 'exit'
    file_to_hl = path.resolve(name)
    foo = ->
      fs.readFileSync file_to_hl, 'utf8'

    html = highlighter.highlightSync
      fileContents: foo()
      scopeName: 'source.perl6fe'

    console.log html

#!highlights/node_modules/coffee-script/bin/coffee

Highlights = require 'highlights'
fs = require 'fs'
path = require 'path'
highlighter = new Highlights()
highlighter.requireGrammarsSync
  modulePath: require.resolve('./atom-language-perl6/package.json')

stdin = process.openStdin()
stdin.setEncoding 'utf8'
mystderr = process.stderr
mystdout = process.stdout
foo = (full_path) ->
  fs.readFile full_path, 'utf8', (err, file_str) ->
    if err
      console.error err
    else
      mystdout.write highlighter.highlightSync(
        fileContents: file_str
        scopeName: 'source.perl6fe'
      ) + '\n'

stdin.on 'data', (input) ->
    foo path.resolve input.trim()

Make highlights async, use process.stdout.write not console.log

console.log can do formatting of javascript data structures
Hopefully will be faster printing that way.

Not sure if making it async will make it faster, the same,
or slowing though.


define ["jquery", "lib/forms/validators", "lib/forms/error_messages"], ($, Validators, ErrorMessages) ->

  class InputValidator

    constructor: (input, label, validator) ->
      @input = input
      @label = label
      @validator = validator
      @_initialize() if @input and @label and @validator

    isValid: ->
      Validators[@name](@input, @args)

    getErrorMessage: ->
      "#{@_replace(ErrorMessages[@name], @args)} #{@label}"

    _initialize: ->
      # check for validation rules that have parameters and split them up, e.g. min(3)
      if match = @validator.match(/(.+)\((.+)\)/)
        @name = match[1]
        @args = match[2]
      else
        @name = @validator

    _replace: (text, variables) ->
      text.replace /{(\d+)}/g, (match, number) ->
        if typeof variables[number] != 'undefined' then variables[number] else match

define ["jquery", "lib/forms/validators", "lib/forms/error_messages"], ($, Validators, ErrorMessages) ->

  class InputValidator

    constructor: (input, label, validator) ->
      @input = input
      @label = label
      @validator = validator
      @_initialize() if @input and @label and @validator

    isValid: ->
      Validators[@name](@input, @args)

    getErrorMessage: ->
      "#{@_replace(ErrorMessages[@name], @args)} #{@label}"

    _initialize: ->
      # check for validation rules that have parameters and split them up, e.g. min(3)
      if match = @validator.match(/(.+?)\((.+)\)/)
        @name = match[1]
        @args = match[2]
      else
        @name = @validator

    _replace: (text, variables) ->
      text.replace /{(\d+)}/g, (match, number) ->
        if typeof variables[number] != 'undefined' then variables[number] else match

Make the match for the braces non greedy to allow for regex grouping in validator rules.


describe "ElementWrapSpec", ->
  describe "Get Tree Structure", ->
    it "should get a tree Structure of all 'components' from the passed in element", ->
      startElement = $j("

")

      structure = AppEngine.Helpers.ElementWrap.getTreeStructure(startElement)

      expect(structure.length).toEqual(2)
      expect(structure[0].el[0].id).toEqual("p1")
      expect(structure[0].children.length).toEqual(1)
      expect(structure[1].el[0].id).toEqual("p4")

  describe "createTree", ->
    it "should create a tree of components to be marked up", ->

describe "ElementWrap specs", ->
  describe "Get Tree Structure", ->
    it "should get a tree Structure of all 'components' from the passed in element", ->
      startElement = $j("

")

      structure = AppEngine.Helpers.ElementWrap.getTreeStructure(startElement)

      expect(structure.length).toEqual(2)
      expect(structure[0].el[0].id).toEqual("p1")
      expect(structure[0].children.length).toEqual(1)
      expect(structure[1].el[0].id).toEqual("p4")

  describe "createTree", ->
    it "should create a tree of components to be marked up", ->

Change name of ElementWrap Specs

Change name of ElementWrap Specs


selector = "form.neuron-form"
formHasChanged = false

$(document).on "page:load", ->
  formHasChanged = false

$(document).on "change", "#{selector} input", ->
  formHasChanged = true

$(document).on "page:before-change", ->
  confirm(
    "La información no ha sido guardada. ¿Estás seguro de continuar?"
  ) if formHasChanged

selector = "form.neuron-form"
formHasChanged = false

$(document).on "page:load", ->
  formHasChanged = false

$(document).on "change", "#{selector} input, #{selector} textarea", ->
  formHasChanged = true

$(document).on "page:before-change", ->
  confirm(
    "La información no ha sido guardada. ¿Estás seguro de continuar?"
  ) if formHasChanged

Add textarea to listen changes in neuron form


App.FoundationExtras =

  initialize: ->
    $(document).foundation()
    $(window).trigger "load.zf.sticky"
    $(window).trigger "resize"

    clearSticky = ->
      $("[data-sticky]").foundation("destroy") if $("[data-sticky]").length

    $(document).on("page:before-unload", clearSticky)

    window.addEventListener("popstate", clearSticky, false)

App.FoundationExtras =

  initialize: ->
    $(document).foundation()
    $(window).trigger "init.zf.sticky"
    $(window).trigger "resize"

    clearSticky = ->
      $("[data-sticky]").foundation("destroy") if $("[data-sticky]").length

    $(document).on("page:before-unload", clearSticky)

    window.addEventListener("popstate", clearSticky, false)

Replace load to init sticky

Replace load to init sticky


Menglifang.Widgets.DatetimePicker = Ember.TextField.extend
  resetable: true

  format: 'yyyy-mm-dd hh:ii'
  autoclose: true
  todayBtn: true
  startDate: '1949-10-01'
  minuteStep: 10
  minView: 0
  maxView: 4
  language: 'zh-CN'

  didInsertElement: ->
    options =
      format: @get('format')
      autoclose: @get('autoclose')
      todayBtn: @get('todayBtn')
      startDate: @get('startDate')
      minuteStep: @get('minuteStep')
      minView: @get('minView')
      maxView: @get('maxView')
      language: @get('language')

    @$().datetimepicker(options)

Ember.Handlebars.helper 'datetime-picker', Menglifang.Widgets.DatetimePicker

Menglifang.Widgets.DatetimePicker = Ember.TextField.extend
  resetable: true

  format: 'yyyy-mm-dd hh:ii'
  autoclose: true
  todayBtn: false
  startDate: '1949-10-01'
  minuteStep: 10
  minView: 0
  maxView: 4
  language: 'zh-CN'

  didInsertElement: ->
    options =
      format: @get('format')
      autoclose: @get('autoclose')
      todayBtn: @get('todayBtn')
      startDate: @get('startDate')
      minuteStep: @get('minuteStep')
      minView: @get('minView')
      maxView: @get('maxView')
      language: @get('language')

    @$().datetimepicker(options)

Ember.Handlebars.helper 'datetime-picker', Menglifang.Widgets.DatetimePicker

Set default value of todayBtn to false.

Set default value of todayBtn to false.


`import Em from "vendor/ember"`

Component = Em.Component.extend
    click: (evt) ->
        if evt.target.tagName is "A" and evt.target.target != '_blank'
            evt.stopPropagation()
            evt.preventDefault()
            @container.lookup("route:openxpki").sendAjax
                data:
                    page:evt.target.href.split("#")[1]
                    target:evt.target.target
        else if evt.target.tagName is "BUTTON"
            $(evt.target).addClass "btn-loading"

    buttonsWithDescription: (->
        @get("content.content.buttons").isAny "description"
    ).property "content.content.buttons.@each.description"

    actions:
        execute: (btn) ->
            if btn.action
                @container.lookup("route:openxpki").sendAjax
                    data:
                        action:btn.action
            else
                console.log('Transition');
                @container.lookup("route:openxpki").transitionTo "openxpki", btn.page

`export default Component`

`import Em from "vendor/ember"`

Component = Em.Component.extend
    click: (evt) ->
        if evt.target.tagName is "A" and evt.target.target != '_blank'
            evt.stopPropagation()
            evt.preventDefault()
            @container.lookup("route:openxpki").sendAjax
                data:
                    page:evt.target.href.split("#")[1]
                    target:evt.target.target
        else if evt.target.tagName is "BUTTON"
            $(evt.target).addClass "btn-loading"

    buttonsWithDescription: (->
        buttons = @get "content.content.buttons"
        if buttons
            buttons.isAny "description"
        else
            false
    ).property "content.content.buttons.@each.description"

    actions:
        execute: (btn) ->
            if btn.action
                @container.lookup("route:openxpki").sendAjax
                    data:
                        action:btn.action
            else
                console.log('Transition');
                @container.lookup("route:openxpki").transitionTo "openxpki", btn.page

`export default Component`

Fix property calculation in absence of buttons.


Helpers = require '../lib/helpers'

describe "The Results Validation Helper", ->
  it "should throw an exception when nothing is passed.", ->
    expect( -> Helpers.validateResults()).toThrow()
  it "should throw an exception when a String is passed.", ->
    expect( -> Helpers.validateResults('String')).toThrow()

describe "The Linter Validation Helper", ->
  it "should throw an exception when grammarScopes is not an Array.", ->
    linter = {
      grammarScopes: 'not an array'
      lint: ->
    }
    expect( -> Helpers.validateLinter(linter)).toThrow()

  it "should throw an exception when lint is missing.", ->
    linter = {
      grammarScopes: []
    }
    expect( -> Helpers.validateLinter(linter)).toThrow()

  it "should throw an exception when a lint is not a function.", ->
    linter = {
      grammarScopes: []
      lint: 'not a function'
    }
    expect( -> Helpers.validateLinter(linter)).toThrow()

  it "should return true when everything validates.", ->
    linter = {
      grammarScopes: []
      lint: ->
    }
    expect(Helpers.validateLinter(linter)).toEqual(true)

Add test for proper validated linter.

Add test for proper validated linter.


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

{CloneAssignmentLink} = require './task-dnd'

isEmpty = require 'lodash/isEmpty'
partial = require 'lodash/partial'

{PastTaskPlansActions, PastTaskPlansStore} = require '../../flux/past-task-plans'
LoadableItem = require '../loadable-item'

EmptyWarning = (props) ->
  return null unless props.isVisible

    No assignments were found

PastAssignmentsLoading = ->

      Loading past assignments...

PastAssignments = React.createClass

  propTypes:
    courseId: React.PropTypes.string.isRequired

  render: ->
    plans = PastTaskPlansStore.get(@props.courseId) or []

      <EmptyWarning isVisible={isEmpty(plans)} />

      {for plan in plans
        <CloneAssignmentLink key={plan.id} plan={plan} />}

PastAssignmentsShell = React.createClass
  render: ->
    {courseId} = @props

    <LoadableItem
      id={courseId}
      store={PastTaskPlansStore}
      actions={PastTaskPlansActions}
      load={partial(PastTaskPlansActions.load, {courseId})}
      renderLoading={ PastAssignmentsLoading }
      renderItem={-> <PastAssignments courseId={courseId} />}
    />

module.exports = PastAssignmentsShell

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

{CloneAssignmentLink} = require './task-dnd'

isEmpty = require 'lodash/isEmpty'
partial = require 'lodash/partial'

{PastTaskPlansActions, PastTaskPlansStore} = require '../../flux/past-task-plans'
LoadableItem = require '../loadable-item'

PastAssignmentsLoading = ->

      Loading past assignments...

PastAssignments = React.createClass

  propTypes:
    courseId: React.PropTypes.string.isRequired

  render: ->
    plans = PastTaskPlansStore.get(@props.courseId) or []

      {for plan in plans
        <CloneAssignmentLink key={plan.id} plan={plan} />}

PastAssignmentsShell = React.createClass
  render: ->
    {courseId} = @props

    <LoadableItem
      id={courseId}
      store={PastTaskPlansStore}
      actions={PastTaskPlansActions}
      load={partial(PastTaskPlansActions.load, {courseId})}
      renderLoading={ PastAssignmentsLoading }
      renderItem={-> <PastAssignments courseId={courseId} />}
    />

module.exports = PastAssignmentsShell

Hide empty warning on past assignments

Hide empty warning on past assignments


noflo = require "../../lib/NoFlo"

class Split extends noflo.Component
  description: "This component receives data on a single input port and sends the same data out to all connected output ports"

  constructor: ->
    @inPorts =
      in: new noflo.Port()
    @outPorts =
      out: new noflo.ArrayPort()

    @inPorts.in.on "begingroup", (group) =>
      @outPorts.out.beginGroup group
    @inPorts.in.on "data", (data) =>
      @outPorts.out.send data
    @inPorts.in.on "endgroup", =>
      @outPorts.out.endGroup()
    @inPorts.in.on "disconnect", =>
      @outPorts.out.disconnect()

exports.getComponent = ->
  new Split

noflo = require "../../lib/NoFlo"

class Split extends noflo.Component
  description: "This component receives data on a single input port and sends the same data out to all connected output ports"

  constructor: ->
    @inPorts =
      in: new noflo.Port()
    @outPorts =
      out: new noflo.ArrayPort()

    @inPorts.in.on "connect", =>
      @outPorts.out.connect()
    @inPorts.in.on "begingroup", (group) =>
      @outPorts.out.beginGroup group
    @inPorts.in.on "data", (data) =>
      @outPorts.out.send data
    @inPorts.in.on "endgroup", =>
      @outPorts.out.endGroup()
    @inPorts.in.on "disconnect", =>
      @outPorts.out.disconnect()

exports.getComponent = ->
  new Split

Connect when getting upstream connection


# Description
#   @lefay do you ...?
#
# Author:
#   lcaro

module.exports = (robot) ->
  robot.respond /do you (.+)$/i, (msg) ->
    msg.reply "yes I do in fact " + msg.match[1]

# Description
#   @lefay do you ...?
#
# Author:
#   lcaro

module.exports = (robot) ->
  robot.respond /do you (.+)\??$/i, (msg) ->
    msg.reply "yes I do in fact " + msg.match[1]

Allow question marks in do you

Allow question marks in do you


Config = require '../config'
argv = require('optimist')
  .usage('Usage: $0 --projectKey key --clientId id --clientSecret secret')
  .demand(['projectKey','clientId', 'clientSecret'])
  .argv
MarketPlaceStockUpdater = require('../main').MarketPlaceStockUpdater

Config.timeout = 60000
Config.showProgress = true

updater = new MarketPlaceStockUpdater(Config, argv.projectKey, argv.clientId, argv.clientSecret)
updater.run (msg) ->
  console.log msg
  process.exit 1 unless msg.status

Config = require '../config'
argv = require('optimist')
  .usage('Usage: $0 --projectKey key --clientId id --clientSecret secret')
  .demand(['projectKey','clientId', 'clientSecret'])
  .argv
MarketPlaceStockUpdater = require('../main').MarketPlaceStockUpdater

updater = new MarketPlaceStockUpdater(Config, argv.projectKey, argv.clientId, argv.clientSecret)
updater.run (msg) ->
  console.log msg
  process.exit 1 unless msg.status

Remove config as it shoud be set outside.


class Utils

    @getHTMLTitleFromHistoryFragment: (fragment) ->
        _.capitalize(fragment.split('\/').join(' '))

    @getShortTaskID: (taskId) ->
        split = taskId.split(/\-|\:|\./)
        if split.length > 1
            return "#{ split[0] }..."
        else
            if taskId.length > 20
                return "#{ taskId.substr(0, 20) }..."
        return taskId

    @stringJSON: (object) ->
        JSON.stringify object, null, '    '

    @viewJSON: (type, objectId) ->
        lookupObject = {}

        if type is 'task'
            lookupObject = app.allTasks
        if type is 'request'
            lookupObject = app.allRequests

        vex.dialog.alert
            contentCSS:
                width: 800
            message: "<pre>#{ lookupObject[objectId].JSONString }</pre>"

    @setupSortableTables: ->
        sortable.init()

    @humanTimeAgo: (date) ->
        return '' unless date?
        now = moment()
        time = moment(date)
        wasToday = time.date() is now.date() and Math.abs(time.diff(now)) < 86400000
        wasJustNow = Math.abs(time.diff(now)) < 120000
        """#{ time.from() } #{ if wasJustNow then '' else time.format('(' + (if wasToday then '' else 'l ') + 'h:mma)') }"""

module.exports = Utils

class Utils

    @getHTMLTitleFromHistoryFragment: (fragment) ->
        _.capitalize(fragment.split('\/').join(' '))

    @getShortTaskID: (taskId) ->
        split = taskId.split(/\-|\:|\./)
        if split.length > 1
            return "#{ split[0] }..."
        else
            return _.truncate(taskId, 20)
        return taskId

    @stringJSON: (object) ->
        JSON.stringify object, null, '    '

    @viewJSON: (type, objectId) ->
        lookupObject = {}

        if type is 'task'
            lookupObject = app.allTasks
        if type is 'request'
            lookupObject = app.allRequests

        vex.dialog.alert
            contentCSS:
                width: 800
            message: "<pre>#{ lookupObject[objectId].JSONString }</pre>"

    @setupSortableTables: ->
        sortable.init()

    @humanTimeAgo: (date) ->
        return '' unless date?
        now = moment()
        time = moment(date)
        wasToday = time.date() is now.date() and Math.abs(time.diff(now)) < 86400000
        wasJustNow = Math.abs(time.diff(now)) < 120000
        """#{ time.from() } #{ if wasJustNow then '' else time.format('(' + (if wasToday then '' else 'l ') + 'h:mma)') }"""

module.exports = Utils

Use _.string's `truncate` here instead

Use _.string's `truncate` here instead


helpers = (app) ->

  # dynamic helpers are given request and response as parameters
  app.dynamicHelpers
    flash: (req, res) -> req.flash()
    logged_in: -> true

  # static helpers take any parametes and usually format data
  app.helpers
    # Object is a mongoose model object
    urlFor: (object) ->
      if object.collection
        prefix = object.collection.name + '/'
      else
        prefix = ''

      unless object.isNew
        "/#{prefix}#{object.id}"
      else
        "/#{prefix}"

module.exports = helpers

helpers = (app) ->

  # dynamic helpers are given request and response as parameters
  app.dynamicHelpers
    flash: (req, res) -> req.flash()
    logged_in: -> true

  # static helpers take any parametes and usually format data
  app.helpers
    # Object is a mongoose model object
    urlFor: (object, path) ->
      if path
        prefix = path + '/'
      else if object.collection
        prefix = object.collection.name + '/'
      else
        prefix = ''

      unless object.isNew
        "/#{prefix}#{object.id}"
      else
        "/#{prefix}"

module.exports = helpers

Add the option to pass a path into the urlFor helper method


React = require 'react'
CropInitializer = require './initializer'
GenericTask = require '../generic'

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

  statics:
    getSVGProps: ({workflow, classification, annotation}) ->
      tasks = require '../index'
      [previousCropAnnotation] = classification.annotations.filter (anAnnotation) =>
        taskDescription = workflow.tasks[anAnnotation.task]
        TaskComponent = tasks[taskDescription.type]
        TaskComponent is this and anAnnotation.value? and anAnnotation isnt annotation
      if previousCropAnnotation?
        {x, y, width, height} = previousCropAnnotation.value
        viewBox: "#{x} #{y} #{width} #{height}"

    InsideSubject: CropInitializer

    getDefaultTask: ->
      type: 'crop'
      instruction: 'Drag to select the relevant area.'
      help: ''

    getTaskText: (task) ->
      task.instruction

    getDefaultAnnotation: ->
      value: null

    isAnnotationComplete: (task, annotation) ->
      annotation.value? or not task.required

  getDefaultProps: ->
    task: null
    annotation: null
    onChange: Function.prototype

  render: ->
    <GenericTask question={@props.task.instruction} help={@props.task.help}>
      <p>
        <button type="button" onClick={@handleClear}>Clear current crop</button>
      </p>
    </GenericTask>

  handleClear: ->
    @props.annotation.value = null
    @props.onChange()

React = require 'react'
CropInitializer = require './initializer'
GenericTask = require '../generic'

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

  statics:
    getSVGProps: ({workflow, classification, annotation}) ->
      tasks = require '../index'
      [previousCropAnnotation] = classification.annotations.filter (anAnnotation) =>
        taskDescription = workflow.tasks[anAnnotation.task]
        TaskComponent = tasks[taskDescription.type]
        TaskComponent is this and anAnnotation.value? and anAnnotation isnt annotation
      if previousCropAnnotation?
        {x, y, width, height} = previousCropAnnotation.value
        viewBox: "#{x} #{y} #{width} #{height}"

    InsideSubject: CropInitializer

    getDefaultTask: ->
      type: 'crop'
      instruction: 'Drag to select the relevant area.'
      help: ''

    getTaskText: (task) ->
      task.instruction

    getDefaultAnnotation: ->
      value: null

    isAnnotationComplete: (task, annotation) ->
      annotation.value? or not task.required

  getDefaultProps: ->
    task: null
    annotation: null
    onChange: Function.prototype

  render: ->
    <GenericTask question={@props.task.instruction} help={@props.task.help}>
      <p>
        <button type="button" disabled={not @props.annotation.value?} onClick={@handleClear}>Clear current crop</button>
      </p>
    </GenericTask>

  handleClear: ->
    @props.annotation.value = null
    @props.onChange()

Disable Clear button when there's no crop


marked = require 'marked'

renderer = new marked.Renderer()
renderer.code = ->
  ''
renderer.blockquote = ->
  ''
renderer.heading = ->
  ''
renderer.html = ->
  ''
renderer.image = ->
  ''
renderer.list = ->
  ''

markdown = (text) ->
  marked(text, renderer: renderer).replace(/<p>(.*)<\/p>/, "$1").trim()

module.exports =
  getSettingDescription: (keyPath) ->
    description = atom.config.getSchema(keyPath)?.description or ''
    markdown(description)

Make simple functions more tidy

:art: Make simple functions more tidy


class Lanes.Models.ChangeSet extends Lanes.Models.Base

    constructor: ->
        super
        this.created_at = new Date

    session:
        update: 'object'
        by: 'object'
        created_at: { type: 'date', setOnce: true }

    derived:
        record: { deps: ['collection'], fn: -> @collection.parent }
        record_name: { deps: ['record'], fn: -> _.field2title @record.api_path() }
        fields: { deps: ['update'], fn: -> _.keys(@update) }
        displayed_fields: { deps:['fields'], fn: -> _.without(@fields, 'updated_by_id', 'updated_at') }
        displayed_changes:
            deps: ['displayed_fields'], fn: ->
                _.map @displayed_fields, (field) =>
                    c = @update[field]
                    { name: field, from: c[0], to: c[1] }

    value: ->
        set = {}
        for field, change of @update
            set[field] = change[1]
        set

class ChangeSetCollection extends Lanes.Models.BasicCollection

    model: Lanes.Models.ChangeSet

    constructor: (options) ->
        super([], options)

    comparator: (a, b) ->
        if b.created_at < a.created_at then -1 else if b.created_at > a.created_at then 1 else 0

Lanes.Models.ChangeSet.Collection = ChangeSetCollection

class Lanes.Models.ChangeSet extends Lanes.Models.Base

    constructor: ->
        super
        this.created_at = new Date

    session:
        update: 'object'
        by: 'object'
        created_at: { type: 'date', setOnce: true }

    derived:
        record: { deps: ['collection'], fn: -> @collection.parent }
        record_name: { deps: ['record'], fn: -> _.field2title @record.api_path() }
        fields: { deps: ['update'], fn: -> _.keys(@update) }
        displayed_fields: { deps:['fields'], fn: -> _.without(@fields, 'updated_by_id', 'updated_at') }
        displayed_changes:
            deps: ['displayed_fields'], fn: ->
                _.map @displayed_fields, (field) =>
                    c = @update[field]
                    { name: field, from: c[0], to: c[1] }

    value: ->
        set = {}
        for field, change of @update
            set[field] = if _.isArray(change) then _.last(change) else change
        set

class ChangeSetCollection extends Lanes.Models.BasicCollection

    model: Lanes.Models.ChangeSet

    constructor: (options) ->
        super([], options)

    comparator: (a, b) ->
        if b.created_at < a.created_at then -1 else if b.created_at > a.created_at then 1 else 0

Lanes.Models.ChangeSet.Collection = ChangeSetCollection

Deal with change sets that are not arrays


# Description:
#   Listens for words and sometimes replies with an Arnie quote
#
# Dependencies:
#   None
#
# Commands:
#   None
#
# Author:
#   Casey Lawrence <cjlaw@users.noreply.github.com>
#

odds  = [1...100]

arnie_quotes = [
    'GET TO THE CHOPPA! :arnold:',
    'Your clothes, give them to me, now! :arnold:',
    'Hasta La Vista, Baby! :arnold:',
    'DDDAAANNNAAAA! :arnold:',
    'You are one ugly mother******. :arnold:',
    'It`s not a tumor! :arnold:',
    'When I said you should screw yourself. I didn`t mean it literally. :arnold:',
    'Can you hurry up. My horse is getting tired. :arnold:',
    'Are these all your lunches? You mean you eat other peoples` lunches? STOP IT!! :arnold:',
    'I`m the party pooper. :arnold:',
    'Who is your daddy and what does he do? :arnold:'
]

module.exports = (robot) ->
    robot.hear /(^|\s)arnie(\s|$|[\W])/ig, (msg) ->
        val = msg.random odds
        if val > 50
            msg.send msg.random arnie_quotes

    robot.hear /(^|\s)arnold(\s|$|[\W])/ig, (msg) ->
        val = msg.random odds
        if val > 50
            msg.send msg.random arnie_quotes

# Description:
#   Listens for words and sometimes replies with an Arnie quote
#
# Dependencies:
#   None
#
# Commands:
#   None
#
# Author:
#   Casey Lawrence <cjlaw@users.noreply.github.com>
#

odds  = [1...100]

arnie_quotes = [
    'GET TO THE CHOPPA!',
    'Your clothes, give them to me, now!',
    'Hasta La Vista, Baby!',
    'DDDAAANNNAAAA!',
    'You are one ugly mother******.',
    'It`s not a tumor!',
    'When I said you should screw yourself. I didn`t mean it literally.',
    'Can you hurry up. My horse is getting tired.',
    'Are these all your lunches? You mean you eat other peoples` lunches? STOP IT!!',
    'I`m the party pooper.',
    'Who is your daddy and what does he do?'
]

module.exports = (robot) ->
    robot.hear /(^|\s)arnie(\s|$|[\W])/ig, (msg) ->
        val = msg.random odds
        if val > 50
            msg.send msg.random arnie_quotes

    robot.hear /(^|\s)arnold(\s|$|[\W])/ig, (msg) ->
        val = msg.random odds
        if val > 50
            msg.send msg.random arnie_quotes

Remove the Arnold emoji since it's not displaying


Backbone = require 'backbone'
DropdownView = require '../../dropdown/client/dropdown_view.coffee'
mediator = require '../../../lib/mediator.coffee'

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

module.exports = class NotificationsView extends DropdownView

  initialize: ->
    super

    mediator.on 'notifications:synced', @maybeSetNotifications, @
    mediator.on 'notifications:synced', @renderFeed, @
    mediator.on 'notifications:cleared', @unsetNotifications, @

    @notifications = mediator.shared.notifications

  clearNotifications: (e)->
    @notifications.markRead()
    @$('.user-avatar').removeClass 'has-notifications'

  maybeSetNotifications: ->
    if (count = @notifications.getNumberUnread()) > 0
      @$('.notifications-menu__count').addClass '.notifications-menu__count--has-notifications'
      @$('.notifications-menu__count').text count

  unsetNotifications: ->
    @$('.user-avatar, .dropdown__link--notifications').removeClass '.notifications-menu__count--has-notifications'
    @$('.notifications-menu__count').text "0"

  renderFeed: ->
    @$('.dropdown__inner').html template
      feed: @notifications.models

Backbone = require 'backbone'
DropdownView = require '../../dropdown/client/dropdown_view.coffee'
mediator = require '../../../lib/mediator.coffee'

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

module.exports = class NotificationsView extends DropdownView

  initialize: ->
    super

    mediator.on 'notifications:synced', @maybeSetNotifications, @
    mediator.on 'notifications:synced', @renderFeed, @
    mediator.on 'notifications:cleared', @unsetNotifications, @

    @notifications = mediator.shared.notifications

  clearNotifications: (e)->
    @notifications.markRead()
    @unsetNotifications()

  openDropdown: ->
    super
    @clearNotifications()

  maybeSetNotifications: ->
    if (count = @notifications.getNumberUnread()) > 0
      @$('.notifications-menu__count').addClass 'notifications-menu__count--has-notifications'
      @$('.notifications-menu__count').text count

  unsetNotifications: ->
    @$('.notifications-menu__count').removeClass 'notifications-menu__count--has-notifications'
    @$('.notifications-menu__count').text "0"

  renderFeed: ->
    @$('.dropdown__inner').html template
      feed: @notifications.models

Fix notification class, clear on hover

Fix notification class, clear on hover


describe 'Logger', ->
  it 'expose window.log_event', ->
    expect(window.log_event).toBe Logger.log

  describe 'log', ->
    it 'send a request to log event', ->
      spyOn $, 'getWithPrefix'
      Logger.log 'example', 'data'
      expect($.getWithPrefix).toHaveBeenCalledWith '/event',
        event_type: 'example'
        event: '"data"'
        page: window.location.href

  # Broken with commit 9f75e64? Skipping for now.
  xdescribe 'bind', ->
    beforeEach ->
      Logger.bind()
      Courseware.prefix = '/6002x'

    afterEach ->
      window.onunload = null

    it 'bind the onunload event', ->
      expect(window.onunload).toEqual jasmine.any(Function)

    it 'send a request to log event', ->
      spyOn($, 'ajax')
      window.onunload()
      expect($.ajax).toHaveBeenCalledWith
        url: "#{Courseware.prefix}/event",
        data:
          event_type: 'page_close'
          event: ''
          page: window.location.href
        async: false

describe 'Logger', ->
  it 'expose window.log_event', ->
    expect(window.log_event).toBe Logger.log

  describe 'log', ->
    it 'send event to Segment.io if event is whitelisted', ->
      spyOn 'analytics.track'
      Logger.log 'seq_goto', 'data'
      expect(analytics.track).toHaveBeenCalledWith 'seq_goto', 'data'

    it 'send a request to log event', ->
      spyOn $, 'getWithPrefix'
      Logger.log 'example', 'data'
      expect($.getWithPrefix).toHaveBeenCalledWith '/event',
        event_type: 'example'
        event: '"data"'
        page: window.location.href

  # Broken with commit 9f75e64? Skipping for now.
  xdescribe 'bind', ->
    beforeEach ->
      Logger.bind()
      Courseware.prefix = '/6002x'

    afterEach ->
      window.onunload = null

    it 'bind the onunload event', ->
      expect(window.onunload).toEqual jasmine.any(Function)

    it 'send a request to log event', ->
      spyOn($, 'ajax')
      window.onunload()
      expect($.ajax).toHaveBeenCalledWith
        url: "#{Courseware.prefix}/event",
        data:
          event_type: 'page_close'
          event: ''
          page: window.location.href
        async: false

Add new Jasmine test to ensure Segment.io is called when appropriate


class Game.State extends Game.TwoWay
  constructor: () ->
    @objects = {}
    @eventCounters = {}
    super
  onEvent: (e) ->
    # Add timestamped instance to the event counters object
    if e.type not of @eventCounters
      @eventCounters[e.type] = []
    @eventCounters[e.type].push new Date()
    # relay the event upward
    @fire(e)
  addObject: (obj) ->
    if obj not instanceof Game.Object
      throw new Game.Error(Game.Error.ErrorType.NOT_OBJECT)
    if obj.getId() of @objects
      throw new Game.Error(Game.Error.ErrorType.ALREADY_ADDED)
    @objects[obj.getId()] = obj
    @listen(obj, @onEvent)
    # call object added method
    obj.added()

class Game.State extends Game.TwoWay
  constructor: () ->
    @objects = {}
    @eventCounters = {}
    @eventHandlers = {}
    super
  onEvent: (e) ->
    # Add timestamped instance to the event counters object
    if e.type not of @eventCounters
      @eventCounters[e.type] = []
    @eventCounters[e.type].push new Date()
    # relay the event upward
    @fire(e)
    # call added callbacks
    @eventHandlers[e.type].forEach (cb) -> cb()
    return
  addObject: (obj) ->
    if obj not instanceof Game.Object
      throw new Game.Error(Game.Error.ErrorType.NOT_OBJECT)
    if obj.getId() of @objects
      throw new Game.Error(Game.Error.ErrorType.ALREADY_ADDED)
    @objects[obj.getId()] = obj
    @listen(obj, @onEvent)
    # call object added method
    obj.added()
  addEventHandler: (evtType, callback) ->
    if evtType not of @eventHandlers
      @eventHandlers[evtType] = []
    if callback not in @eventHandlers[evtType]
      @eventHandlers[evtType].push callback
    return

Add custom event handlers to Game.State

Add custom event handlers to Game.State


class NCopyUrlView extends JView

  constructor: ->
    super

    @path = FSHelper.plainPath @getData().path
    @publicPath = @path.replace \
      ///.*\/(.*\.#{KD.config.userSitesDomain})\/(.*)///, 'http://$1/$2'

    @inputUrlLabel  = new KDLabelView
      cssClass      : 'public-url-label'
      title         : 'Public URL'
      click         :=>
        @focusAndSelectAll()

    @inputUrl       = new KDInputView
      label         : @inputUrlLabel
      cssClass      : 'public-url-input'

    @inputUrl.setValue @publicPath

  focusAndSelectAll:->
    @inputUrl.setFocus()
    @inputUrl.selectAll()

  viewAppended:->
    @setClass "copy-url-wrapper"
    super

  pistachio:->
    hasNoPublicPath = @publicPath is @path

    if hasNoPublicPath
      """

This #{@getData().type} can not be reached over a public URL

      """
    else
      """
      {{> @inputUrlLabel}}
      {{> @inputUrl}}
      """

class NCopyUrlView extends JView

  constructor: ->
    super

    @path = FSHelper.plainPath @getData().path
    @publicPath = @path.replace \
      ////home/(.*)/Web/(.*)///, "http://$1.#{KD.config.userSitesDomain}/$2"

    @inputUrlLabel  = new KDLabelView
      cssClass      : 'public-url-label'
      title         : 'Public URL'
      click         :=>
        @focusAndSelectAll()

    @inputUrl       = new KDInputView
      label         : @inputUrlLabel
      cssClass      : 'public-url-input'

    @inputUrl.setValue @publicPath

  focusAndSelectAll:->
    @inputUrl.setFocus()
    @inputUrl.selectAll()

  viewAppended:->
    @setClass "copy-url-wrapper"
    super

  pistachio:->
    hasNoPublicPath = @publicPath is @path

    if hasNoPublicPath
      """

This #{@getData().type} can not be reached over a public URL

      """
    else
      """
      {{> @inputUrlLabel}}
      {{> @inputUrl}}
      """

Fix copy file url issue

Fix copy file url issue