'use strict'

app = angular
  .module('taarifaWaterpointsApp', [
    'ngResource',
    'ngRoute',
    'leaflet-directive',
    'dynform',
    'angular-flash.service',
    'angular-flash.flash-alert-directive',
    'gettext',
    'ui.bootstrap'
  ])
  .config ($routeProvider, $httpProvider, flashProvider) ->
    $routeProvider
      .when '/',
        templateUrl: 'views/main.html'
        controller: 'MapCtrl'
      .when '/waterpoints/edit/:id',
        templateUrl: 'views/edit.html'
        controller: 'WaterpointEditCtrl'
      .when '/waterpoints/new',
        templateUrl: 'views/edit.html'
        controller: 'WaterpointCreateCtrl'
      .when '/requests/new',
        templateUrl: 'views/edit.html'
        controller: 'RequestCreateCtrl'
      .when '/dashboard',
        templateUrl: 'views/dashboard.html'
        controller: 'DashboardCtrl'
      .otherwise
        redirectTo: '/'
    $httpProvider.defaults.headers.patch =
      'Content-Type': 'application/json;charset=utf-8'
    flashProvider.errorClassnames.push 'alert-danger'

app.run (gettextCatalog,$rootScope) ->
    gettextCatalog.currentLanguage = 'en'

    # gettextCatalog.debug = true
    $rootScope.fields = {
        catalog: gettextCatalog,
        languages: {
            "en": "English",
            "sw_TZ": "Swahili"
        }
    }

'use strict'

app = angular
  .module('taarifaWaterpointsApp', [
    'ngResource',
    'ngRoute',
    'leaflet-directive',
    'dynform',
    'angular-flash.service',
    'angular-flash.flash-alert-directive',
    'gettext',
    'ui.bootstrap'
  ])
  .config ($routeProvider, $httpProvider, flashProvider) ->
    $routeProvider
      .when '/',
        templateUrl: 'views/main.html'
        controller: 'MapCtrl'
      .when '/waterpoints/edit/:id',
        templateUrl: 'views/edit.html'
        controller: 'WaterpointEditCtrl'
      .when '/waterpoints/new',
        templateUrl: 'views/edit.html'
        controller: 'WaterpointCreateCtrl'
      .when '/requests',
        templateUrl: 'views/requests.html'
        controller: 'RequestTriageCtrl'
      .when '/requests/new',
        templateUrl: 'views/edit.html'
        controller: 'RequestCreateCtrl'
      .when '/dashboard',
        templateUrl: 'views/dashboard.html'
        controller: 'DashboardCtrl'
      .otherwise
        redirectTo: '/'
    $httpProvider.defaults.headers.patch =
      'Content-Type': 'application/json;charset=utf-8'
    flashProvider.errorClassnames.push 'alert-danger'

app.run (gettextCatalog,$rootScope) ->
    gettextCatalog.currentLanguage = 'en'

    # gettextCatalog.debug = true
    $rootScope.fields = {
        catalog: gettextCatalog,
        languages: {
            "en": "English",
            "sw_TZ": "Swahili"
        }
    }

Add route /requests for requests view

Add route /requests for requests view


path = require 'path'
Q = require 'q'
SymbolsView = require './symbols-view'
TagReader = require './tag-reader'

module.exports =
class GoToView extends SymbolsView
  toggle: ->
    if @panel.isVisible()
      @cancel()
    else
      @populate()

  beforeRemove: ->
    @deferredFind?.resolve([])

  findTag: (editor) ->
    @deferredFind?.resolve([])

    deferred = Q.defer()
    TagReader.find editor, (error, matches=[]) -> deferred.resolve(matches)
    @deferredFind = deferred
    @deferredFind.promise

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

    @findTag(editor).then (matches) =>
      tags = []
      for match in matches
        position = @getTagLine(match)
        continue unless position
        tags.push
          file: match.file
          name: path.basename(match.file)
          position: position

      if tags.length is 1
        @openTag(tags[0])
      else if tags.length > 0
        @setItems(tags)
        @attach()

path = require 'path'
Q = require 'q'
SymbolsView = require './symbols-view'
TagReader = require './tag-reader'

module.exports =
class GoToView extends SymbolsView
  toggle: ->
    if @panel.isVisible()
      @cancel()
    else
      @populate()

  detached: ->
    @deferredFind?.resolve([])

  findTag: (editor) ->
    @deferredFind?.resolve([])

    deferred = Q.defer()
    TagReader.find editor, (error, matches=[]) -> deferred.resolve(matches)
    @deferredFind = deferred
    @deferredFind.promise

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

    @findTag(editor).then (matches) =>
      tags = []
      for match in matches
        position = @getTagLine(match)
        continue unless position
        tags.push
          file: match.file
          name: path.basename(match.file)
          position: position

      if tags.length is 1
        @openTag(tags[0])
      else if tags.length > 0
        @setItems(tags)
        @attach()

Use ::detached instead of ::beforeRemove hook


AppSettingsStore = require('../../stores/app-settings-store').store

migration =
  version: "1.22.0"
  description: "Add simulation type"
  date: "2018-03-30"

  doUpdate: (data) ->
    data.settings ?= {}

    # previous complexities were:
    #  0: diagram only
    #  1: basic relationships
    #  2: expanded relationships
    #  3: collectors
    #
    # map these to
    #  0: simulation: diagramOnly / complexity: basic
    #  1: simulation: static      / complexity: basic
    #  2: simulation: static      / complexity: expanded
    #  3: simulation: time        / complexity: expanded

    previousComplexity = data.settings.complexity or 2

    if previousComplexity == 0
      data.settings.simulationType = AppSettingsStore.SimulationType.diagramOnly
      data.settings.complexity     = AppSettingsStore.Complexity.basic
    else if previousComplexity == 1
      data.settings.simulationType = AppSettingsStore.SimulationType.static
      data.settings.complexity     = AppSettingsStore.Complexity.basic
    else if previousComplexity == 2
      data.settings.simulationType = AppSettingsStore.SimulationType.static
      data.settings.complexity     = AppSettingsStore.Complexity.expanded
    else if previousComplexity == 3
      data.settings.simulationType = AppSettingsStore.SimulationType.time
      data.settings.complexity     = AppSettingsStore.Complexity.expanded

module.exports = _.mixin migration, require './migration-mixin'

AppSettingsStore = require('../../stores/app-settings-store').store

migration =
  version: "1.22.0"
  description: "Add simulation type"
  date: "2018-03-30"

  doUpdate: (data) ->
    data.settings ?= {}

    # previous complexities were:
    #  0: diagram only
    #  1: basic relationships
    #  2: expanded relationships
    #  3: collectors
    #
    # map these to
    #  0: simulation: diagramOnly / complexity: basic
    #  1: simulation: static      / complexity: basic
    #  2: simulation: static      / complexity: expanded
    #  3: simulation: time        / complexity: expanded

    previousComplexity = if data.settings.complexity? then data.settings.complexity else 2

    if previousComplexity == 0
      data.settings.simulationType = AppSettingsStore.SimulationType.diagramOnly
      data.settings.complexity     = AppSettingsStore.Complexity.basic
    else if previousComplexity == 1
      data.settings.simulationType = AppSettingsStore.SimulationType.static
      data.settings.complexity     = AppSettingsStore.Complexity.basic
    else if previousComplexity == 2
      data.settings.simulationType = AppSettingsStore.SimulationType.static
      data.settings.complexity     = AppSettingsStore.Complexity.expanded
    else if previousComplexity == 3
      data.settings.simulationType = AppSettingsStore.SimulationType.time
      data.settings.complexity     = AppSettingsStore.Complexity.expanded

module.exports = _.mixin migration, require './migration-mixin'

Fix case where importing complexity = 0

Fix case where importing complexity = 0

[#156480860]


define (require) ->
  settings = require('settings')
  FooterTabView = require('cs!../inherits/tab/tab')
  template = require('hbs!./metadata-template')
  require('less!./metadata')

  return class MetadataView extends FooterTabView
    template: template
    templateHelpers: () ->
      model = super()
      model.languages = settings.languages
      return model

    editable:
      '.js-metadata-language-select':
        value: if @media is 'book' then 'currentPage.language' else 'language'
        type: 'select2'
        select2:
          width: 300

    initialize: () ->
      super()
      @listenTo(@model, 'change:editable', @render)

define (require) ->
  settings = require('settings')
  FooterTabView = require('cs!../inherits/tab/tab')
  template = require('hbs!./metadata-template')
  require('less!./metadata')

  return class MetadataView extends FooterTabView
    template: template
    templateHelpers: () ->
      model = super()
      model.languages = settings.languages
      return model

    editable:
      '.js-metadata-language-select':
        value: () -> if @media is 'book' then return 'language' else return 'currentPage.language'
        type: 'select2'
        select2:
          width: 300

    initialize: () ->
      super()
      @listenTo(@model, 'change:editable', @render)

Use a function to determine value

Use a function to determine value


request = require 'request'
_       = require 'lodash'

class StockController
  lastTradePrice: (req, res) =>
    @request req.params.symbol, (error, response, body) =>
      return res.status(500).send(error) if error?
      res.status(response.statusCode).send(body)

  request: (symbol, callback=->) =>
    options =
      url: 'http://download.finance.yahoo.com/d/quotes.csv'
      qs:
        s: symbol
        f: 'l1'
    request options, callback

module.exports = StockController

request = require 'request'
_       = require 'lodash'

class StockController
  lastTradePrice: (req, res) =>
    @request req.params.symbol, (error, response, body) =>
      return res.status(500).send(error) if error?
      res.status(response.statusCode).send price: parseFloat(body)

  request: (symbol, callback=->) =>
    options =
      url: 'http://download.finance.yahoo.com/d/quotes.csv'
      qs:
        s: symbol
        f: 'l1'
    request options, callback

module.exports = StockController

Return JSON object instead of a primitive


NotificationPlugin = require '../../notification-plugin'
Handlebars = require 'handlebars'
url = require 'url'

module.exports = class BugHerd extends NotificationPlugin

  # Resolve the bugs rest api endpoint
  tasksUrl = (config) ->
    "https://www.bugherd.com/api_v2/projects/#{config.projectId}/tasks.json"

  # Parses the user's input of tags for the task
  parseTags = (tags) -> tags?.split /, */

  # Build the request payload
  @requestPayload = (config, event) ->
    task:
      description: @textBody event
      priority: config.priority
      tag_names: parseTags config.tags
      status: config.status

  # Compose the really simple message body
  @textBody = Handlebars.compile(
    """
      {{#if error.message}}{{error.message}}{{/if}}
      ({{error.exceptionClass}} in {{error.context}})

      Stacktrace:{{#each error.stacktrace}}{{#if inProject}}
      {{file}}:{{lineNumber}} - {{method}}{{/if}}{{/each}}
    """
  )

  # Receive the configuration & event payload
  @receiveEvent = (config, event, callback) ->
    return if event?.trigger?.type == 'reopened'
    console.log config, event

    @request
      .post tasksUrl config
      .auth config.apiKey, 'x'
      .send @requestPayload config, event
      .on 'error', (err) ->
        callback err
      .end (res) ->
        return callback res.error if res.error

        callback null,
          id: res?.body?.task?.id
          url: res?.body?.task?.admin_link

NotificationPlugin = require '../../notification-plugin'
Handlebars = require 'handlebars'
url = require 'url'

module.exports = class BugHerd extends NotificationPlugin

  # Resolve the bugs rest api endpoint
  tasksUrl = (config) ->
    "https://www.bugherd.com/api_v2/projects/#{config.projectId}/tasks.json"

  # Parses the user's input of tags for the task
  parseTags = (tags) -> tags?.split /, */

  # Build the request payload
  @requestPayload = (config, event) ->
    task:
      description: @textBody event
      priority: config.priority
      tag_names: parseTags config.tags
      status: config.status

  # Compose the really simple message body
  @textBody = Handlebars.compile(
    """
      {{#if error.message}}{{error.message}}{{/if}}
      ({{error.exceptionClass}} in {{error.context}})

      {{error.url}}

      Stacktrace:{{#each error.stacktrace}}{{#if inProject}}
      {{file}}:{{lineNumber}} - {{method}}{{/if}}{{/each}}
    """
  )

  # Receive the configuration & event payload
  @receiveEvent = (config, event, callback) ->
    return if event?.trigger?.type == 'reopened'
    console.log config, event

    @request
      .post tasksUrl config
      .auth config.apiKey, 'x'
      .send @requestPayload config, event
      .on 'error', (err) ->
        callback err
      .end (res) ->
        return callback res.error if res.error

        callback null,
          id: res?.body?.task?.id
          url: res?.body?.task?.admin_link

Add error url to BugHerd

Add error url to BugHerd


# Your init script
#
# Atom will evaluate this file each time a new window is opened. It is run
# after packages are loaded/activated and after the previous editor state
# has been restored.
#
# An example hack to make opened Markdown files always be soft wrapped:
#
# path = require 'path'
#
atom.workspaceView.eachEditorView (editorView) ->
  editor = editorView.getEditor()
  if path.extname(editor.getPath()) is '.md'
    editor.setSoftWrap(true)

# Your init script
#
# Atom will evaluate this file each time a new window is opened. It is run
# after packages are loaded/activated and after the previous editor state
# has been restored.
#

# Make opened Markdown files always be soft wrapped:
path = require 'path'

atom.workspaceView.eachEditorView (editorView) ->
  editor = editorView.getEditor()
  if path.extname(editor.getPath()) is '.md'
    editor.setSoftWrap(true)

Fix autosoft wrap for md files in Atom

Fix autosoft wrap for md files in Atom


module.exports = (robot) ->
  robot.respond /hello/, (msg) ->
    msg.reply "hello!"

  robot.hear /orly/, ->
    msg.send "yarly"

  cuteMe = (msg) ->
    msg.http("http://api.dailycute.com.net/v1/posts/all")
      .get() (body) ->
        results = JSON.parse(body)
        msg.send (msg.random results.posts).image_src

Add API call to Daily Cute and pick image using msg.random.


# Description:
#   The Adventures of Captain Quail, now in you chat!
#
# Dependencies:
#   "htmlparser": "1.7.6"
#   "soupselect": "0.2.0"
#
# Configuration:
#   None
#
# Commands:
#   hubot cq - The latest Adventures of Captain Quail
#
# Author:
#   phildini

htmlparser = require "htmlparser"
Select = require("soupselect").select

module.exports = (robot) ->
    robot.respond /cq/i, (msg) ->
        msg.http("http://www.captainquail.com/comic/")
            .get() (err, res, body) ->
                handler = new htmlparser.DefaultHandler()
                parser = new htmlparser.Parser(handler)
                parser.parseComplete(body)

                img = Select handler.dom, "img.comic"
                title = Select handler.dom, "a.content-link"
                link = title[0].attribs
                comic = img[0].attribs

                msg.send link.href
                msg.send comic.alt

# Description:
#   The Adventures of Captain Quail, now in you chat!
#
# Dependencies:
#   "htmlparser": "1.7.6"
#   "soupselect": "0.2.0"
#
# Configuration:
#   None
#
# Commands:
#   hubot cq - The latest Adventures of Captain Quail
#
# Author:
#   phildini

htmlparser = require "htmlparser"
Select = require("soupselect").select

module.exports = (robot) ->
    robot.respond /cq/i, (msg) ->
        msg.http("http://www.captainquail.com/comic/")
            .get() (err, res, body) ->
                handler = new htmlparser.DefaultHandler()
                parser = new htmlparser.Parser(handler)
                parser.parseComplete(body)

                img = Select handler.dom, "img.comic"
                title = Select handler.dom, "a.content-link"
                link = title[0].attribs
                comic = img[0].attribs

                msg.send link.href
                msg.send comic.src
                msg.send comic.alt

Add a link to the image to display the comic.


###
Module to fetch meetup events

@author Snorre Magnus Davøen
###

###
{
  "title": "Event title",
  "source": "http://url.com",
  "date": [timestamp],
  "location": {
    "address": "Addressen til greien",
    "name": "hvis det er et navn på stedet (f.eks Kvarteret/Landmark)",
    "lon": 10
    "lat": 10
  }
}
###
mu = require 'meetup-api'

# Check if this is a Facebook url
# @param [string] url
exports.canHandle = (url) ->
  return false
# Import and url
# @param [object] query
# @option query [string] url
# @option query [function] onSuccess
# @option query [function] onError
exports.handle = (query) ->

###
Module to fetch meetup events

@author Snorre Magnus Davøen
###

###
{
  "title": "Event title",
  "source": "http://url.com",
  "date": [timestamp],
  "location": {
    "address": "Addressen til greien",
    "name": "hvis det er et navn på stedet (f.eks Kvarteret/Landmark)",
    "lon": 10
    "lat": 10
  }
}
###

muAPIKey = process.env.MU_API_KEY

unless muAPIKey
  console.error 'MU_API_KEY environment variable missing'
  process.exit(1)

mu = (require 'meetup-api') key: muAPIKey

muRegex = /// meetup.com\/      # Match meetup.com/
              [A-Za-z09\-]*\/   # Match Letter-109-MeetupGroup
              events\/          # Match events/
              (\d{9,})\/? ///   # Match 123456789 (least 9 digits)

getEvents = (query) ->
  mu.getEvents
    event_id: query.ids
    (error, events) ->
      if error
        query.onError
          error: error
          module: 'meetup'
      else
        query.onSuccess events

#getEvents
#  ids: [221806583]
#  onSuccess: (event) -> console.log event
#  onError: (error) -> console.log error

# Check if this is a Meetup url
# @param [string] url
exports.canHandle = (url) ->
  if url.match muRegex is not null then true else false

# Import and url
# @param [object] query
# @option query [string] url
# @option query [function] onSuccess
# @option query [function] onError
exports.handle = (query) ->
  id = (muRegex.exec query.url)[1]
  console.log id

Add getEvents and Regex stuff

Add getEvents and Regex stuff

Added function getEvents to get any number of events based on a query
object’s ids property. The value should be an array of event ids.

Added rmulti-line regular expression that matches Meetup events, and
captures the id.

Need to implement an event parsing function that parses the JSON
response from the Meetup api.


express = require("express")
routes = require("./routes")
app = module.exports = express.createServer()
app.configure ->
  app.set "views", __dirname + "/views"
  app.set "view engine", "jade"
  app.use express.bodyParser()
  app.use express.methodOverride()
  app.use app.router
  app.use express.static(__dirname + "/assets")
  app.use require('connect-assets')()

app.configure "development", ->
  app.use express.errorHandler(
    dumpExceptions: true
    showStack: true
  )

app.configure "production", ->
  app.use express.errorHandler()

app.get "/", routes.index

models = require './models'
rest = require "./routes/rest"

rest.createRoutesFor "character", models.Character, app

app.listen process.env.port, ->
  console.log "Express server listening on port %d in %s mode", app.address().port, app.settings.env

express = require("express")
routes = require("./routes")
app = module.exports = express.createServer()
app.configure ->
  app.set "views", __dirname + "/views"
  app.set "view engine", "jade"
  app.use express.bodyParser()
  app.use express.methodOverride()
  app.use app.router
  app.use express.static(__dirname + "/assets")
  app.use require('connect-assets')()

app.configure "development", ->
  app.use express.errorHandler(
    dumpExceptions: true
    showStack: true
  )

app.configure "production", ->
  app.use express.errorHandler()

app.get "/", routes.index

models = require './models'
rest = require "./routes/rest"

rest.createRoutesFor "character", models.Character, app

app.listen process.env.PORT, ->
  console.log "Express server listening on port %d in %s mode", app.address().port, app.settings.env

Fix typo: PORT is capitalized

Fix typo: PORT is capitalized


{View} = require 'atom'
ImageEditor = require './image-editor'

module.exports =
class ImageEditorStatusView extends View
  @content: ->
    @div class: 'status-image inline-block', =>
      @span class: 'image-size', outlet: 'imageSizeStatus'

  initialize: (filePath, image) =>
    @filePath = filePath
    @image = image
    if @filePath and @image
      @attach()

      @subscribe atom.workspaceView, 'pane-container:active-pane-item-changed', =>
        editor = atom.workspaceView.getActivePaneItem()
        if editor instanceof ImageEditor and @filePath is editor.filePath
          @imageSizeStatus.parent().show()
        else
          @imageSizeStatus.parent().hide()

  attach: =>
    statusBar = atom.workspaceView.statusBar
    if statusBar
      statusBar.appendLeft this
      @getImageSize()

  getImageSize: =>
    imageWidth = @image.width()
    imageHeight = @image.height()
    @imageSizeStatus.text("#{imageWidth} px x #{imageHeight} px").show()

{View} = require 'atom'
ImageEditor = require './image-editor'

module.exports =
class ImageEditorStatusView extends View
  @content: ->
    @div class: 'status-image inline-block', =>
      @span class: 'image-size', outlet: 'imageSizeStatus'

  initialize: (filePath, image) =>
    @filePath = filePath
    @image = image
    if @filePath and @image
      @attach()

      @subscribe atom.workspaceView, 'pane-container:active-pane-item-changed', =>
        editor = atom.workspaceView.getActivePaneItem()
        if editor instanceof ImageEditor and @filePath is editor.filePath
          @imageSizeStatus.parent().show()
        else
          @imageSizeStatus.parent().hide()

  attach: =>
    statusBar = atom.workspaceView.statusBar
    if statusBar
      statusBar.appendLeft this
      @getImageSize()

  getImageSize: =>
    imageWidth = @image.width()
    imageHeight = @image.height()
    @imageSizeStatus.text("#{imageWidth}px x #{imageHeight}px").show()

Remove spaces from image size view

Remove spaces from image size view


##= require_self
##= require ./Import
##= require ./ApiInfo
##= require ./ChooseRecords
##= require ./ViewRecords

class Skr.Screens.FreshBooksImport extends Skr.Screens.Base
    modelForAccess: 'invoice'
    getInitialState: -> isEditing: true
    dataObjects:
        import: ->
            new Skr.Screens.FreshBooksImport.Import

    jobStatus: ->
        return null unless @import.job.isExecuting
        message = if @import.stage is 'complete'
            "Importing records from Fresh Books"
        else
            "Loading record summaries from Fresh Books"
        <LC.JobStatus job={@import.job} onlyExecuting
            message={message} />

    render: ->
        <LC.ScreenWrapper identifier="fresh-books-import">
            <Skr.Screens.FreshBooksImport.ApiInfo import={@import} />
            {@jobStatus()}
            <Skr.Screens.FreshBooksImport.ChooseRecords import={@import} />
            <Skr.Screens.FreshBooksImport.ViewRecords import={@import} />
        </LC.ScreenWrapper>

##= require_self
##= require ./Import
##= require ./ApiInfo
##= require ./ChooseRecords
##= require ./ViewRecords

class Skr.Screens.FreshBooksImport extends Skr.Screens.Base
    modelForAccess: 'invoice'
    getInitialState: -> isEditing: true
    dataObjects:
        import: ->
            new Skr.Screens.FreshBooksImport.Import

    JobStatus: ->
        return null unless @import.job.isExecuting
        message = if @import.stage is 'complete'
            "Importing records from Fresh Books"
        else
            "Loading record summaries from Fresh Books"
        <LC.JobStatus job={@import.job} onlyExecuting
            message={message} />

    render: ->
        <LC.ScreenWrapper identifier="fresh-books-import">
            <LC.ErrorDisplay model={@import} />

            <Skr.Screens.FreshBooksImport.ApiInfo import={@import} />
            <@JobStatus />
            <Skr.Screens.FreshBooksImport.ChooseRecords import={@import} />
            <Skr.Screens.FreshBooksImport.ViewRecords import={@import} />
        </LC.ScreenWrapper>

Use ErroDisplay and render using function component


mongoose = require('mongoose')

checkinSchema = new mongoose.Schema
    user: String
    session: Number
    checkinTime: Date
    deleted: { type: Boolean, default: false }
    deletedTime: Date

checkinSchema.methods.upsert = () ->
    # https://jira.mongodb.org/browse/SERVER-14322
    try
        @checkinTime = new Date()
        @update(this, {upsert: true})
    catch
        logger.info "Could not upsert a checkin"

checkinSchema.methods.markDeleted = () ->
    @deleted = true
    @deletedTime = new Date()
    @update(this)

checkinSchema.statics.findBySession = (session) ->
    Checkin.find
        session: session
        deleted: false

checkinSchema.statics.findByUser = (user) ->
    Checkin.find
        user: user
        deleted: false

checkinSchema.index({ deleted: 1, session : 1 })
checkinSchema.index({ deleted: 1, user : 1 })

Checkin = mongoose.model('Checkins', checkinSchema);

export default Checkin

export MAX_CHECKIN_PER_SESSION = [1, 2]

mongoose = require('mongoose')

checkinSchema = new mongoose.Schema
    user: String
    session: Number
    checkinTime: Date
    deleted: { type: Boolean, default: false }
    deletedTime: Date

checkinSchema.methods.upsert = () ->
    # https://jira.mongodb.org/browse/SERVER-14322
    try
        @checkinTime = new Date()
        @update(this, {upsert: true})
    catch
        logger.info "Could not upsert a checkin"

checkinSchema.methods.markDeleted = () ->
    @deleted = true
    @deletedTime = new Date()
    @update(this)

checkinSchema.statics.findBySession = (session) ->
    Checkin.find
        session: session
        deleted: false

checkinSchema.statics.findByUser = (user) ->
    Checkin.find
        user: user
        deleted: false

checkinSchema.index({ deleted: 1, session : 1 })
checkinSchema.index({ deleted: 1, user : 1 })

Checkin = mongoose.model('Checkins', checkinSchema);

export default Checkin

export MAX_CHECKIN_PER_SESSION = [13, 21]

Set proper amount for checkins

Set proper amount for checkins


define [
  'jquery'
  'underscore'
  'backbone'
  'marionette'
  'cs!app'
  'cs!collections/content'
  'cs!views/layouts/workspace'
  'less!styles/main.less'
], ($, _, Backbone, Marionette, app, content, WorkspaceLayout) ->

  return new class Router extends Marionette.AppRouter
    # Show Workspace
    # -------
    # Show the workspace listing and update the URL
    workspace: () ->
      if not @layout
        @layout = new WorkspaceLayout()
        app.main.show(@layout)
      else
        # load default views
        @layout.showViews()

    # Edit existing content
    # -------
    # Start editing an existing piece of content and update the URL.
    edit: (model) ->
      if typeof model is 'string'
        model = content.get(model)

      # Redirect to workspace if model does not exist
      if not model
        require ['cs!routers/router'], (router) ->
          router.navigate('/', {trigger: true, replace: true});

      if not @layout
        @layout = new WorkspaceLayout({model: model})
        app.main.show(@layout)
      else
        # load editor views
        @layout.showViews({model: model})

define [
  'jquery'
  'underscore'
  'backbone'
  'marionette'
  'cs!app'
  'cs!collections/content'
  'cs!views/layouts/workspace'
  'less!styles/main.less'
], ($, _, Backbone, Marionette, app, content, WorkspaceLayout) ->

  return new class Router extends Marionette.AppRouter
    # Show Workspace
    # -------
    # Show the workspace listing and update the URL
    workspace: () ->
      if not @layout
        @layout = new WorkspaceLayout()
        app.main.show(@layout)
      else
        # load default views
        @layout.showViews()

    # Edit existing content
    # -------
    # Start editing an existing piece of content and update the URL.
    edit: (model) ->
      if typeof model is 'string'
        model = content.get(model)

      # Redirect to workspace if model does not exist
      if not model
        require ['cs!routers/router'], (router) ->
          router.navigate('/', {trigger: true, replace: true});

      if not @layout
        @layout = new WorkspaceLayout({model: model})
        app.main.show(@layout)

      # load editor views
      @layout.showViews({model: model})

Edit content on load instead of workspace


ParticipantHeads = require 'activity/views/privatemessage/participantheads'
IDEChatMessageParticipantAvatar = require './idechatmessageparticipantavatar'

module.exports = class IDEChatParticipantHeads extends ParticipantHeads

  setDefaultListTitle: ->

    @setOption 'moreListTitle', 'Other Participants'

  setVideoListTitle: ->

    @setOption 'moreListTitle', 'Inctive Participants'

  updateParticipants: (participantMap, state) ->

    @updatePreviewAvatars participantMap.preview, state
    @updateExtras participantMap.hidden, state

  updatePreviewAvatars: (participants, state) ->

    { selectedParticipant, talkingParticipants, videoActive } = state

    avatars = participants.toJS().map (participant) =>
      { nickname } = participant.profile
      cssClasses = []
      if videoActive
        if nickname is selectedParticipant
          cssClasses.push 'is-selectedParticipant'
        if nickname in talkingParticipants
          cssClasses.push 'is-talkingParticipant'

      options = { cssClass: cssClasses.join(' '), size: { width: 25, height: 25 } }
      avatar = new IDEChatMessageParticipantAvatar options, participant
      @forwardEvent avatar, 'ParticipantSelected'

    @previewContainer.destroySubViews()
    @previewContainer.addSubView avatar  for avatar in avatars

ParticipantHeads = require 'activity/views/privatemessage/participantheads'
IDEChatMessageParticipantAvatar = require './idechatmessageparticipantavatar'

module.exports = class IDEChatParticipantHeads extends ParticipantHeads

  setDefaultListTitle: ->

    @setOption 'moreListTitle', 'Other Participants'

  setVideoListTitle: ->

    @setOption 'moreListTitle', 'Inctive Participants'

  updateParticipants: (participantMap, state) ->

    @updatePreviewAvatars participantMap.preview, state
    @updateExtras participantMap.hidden, state

  updatePreviewAvatars: (participants, state) ->

    { selectedParticipant, talkingParticipants, videoActive } = state

    avatars = participants.toJS().map (participant) =>
      { nickname } = participant.profile
      cssClasses = []
      if videoActive
        if nickname is selectedParticipant
          cssClasses.push 'is-selectedParticipant'
        if nickname in talkingParticipants
          cssClasses.push 'is-talkingParticipant'

      options =
        cssClass : cssClasses.join ' '
        size     : { width: 25, height: 25 }

      avatar = new IDEChatMessageParticipantAvatar options, participant
      @forwardEvent avatar, 'ParticipantSelected'

    @previewContainer.destroySubViews()
    @previewContainer.addSubView avatar  for avatar in avatars

Improve object notation to increase readability


# http://ogp.me/

OgpParser = exports? and exports or @OgpParser = {}

# expects a "dom" object created by cheerio
OgpParser.execute = ($) ->
  title:    $("meta[property='og:title']").first().attr("content")
  summary:  $("meta[property='og:description']").first().attr("content")
  image:    $("meta[property='og:image']").first().attr("content")
  language: $("meta[property='og:locale']").first().attr("content")
  site: $("meta[property='og:site_name']").first().attr("content")

Add og:site_name to opengraph parser

Add og:site_name to opengraph parser


Dialog = require './dialog'
Project = require './project'
projects = require './projects'
path = require 'path'

module.exports =
class SaveDialog extends Dialog
  filePath: null

  constructor: () ->
    firstPath = atom.project.getPaths()[0]
    title = path.basename(firstPath)
    if atom.config.get('project-manager.prettifyTitle')
      title = @prettifyTitle(title)

    super
      prompt: 'Enter name of project'
      input: title
      select: true
      iconClass: 'icon-arrow-right'

    projects.getCurrent (project) =>
      if project.props.paths[0] is firstPath
        @showError "This project is already saved as #{project.props.title}"

  onConfirm: (title) ->
    if title
      properties =
        title: title
        paths: atom.project.getPaths()

      project = new Project(properties)
      project.save()

      @close()
    else
      @showError('You need to specify a name for the project')

  prettifyTitle: (title) ->
    title = title.replace(/[^\w\s]/gi, ' ')
    title = title.charAt(0).toUpperCase() + title.slice(1)

Dialog = require './dialog'
Project = require './project'
projects = require './projects'
path = require 'path'
changeCase = require 'change-case'

module.exports =
class SaveDialog extends Dialog
  filePath: null

  constructor: () ->
    firstPath = atom.project.getPaths()[0]
    title = path.basename(firstPath)

    if atom.config.get('project-manager.prettifyTitle')
      title = changeCase.titleCase(title)

    super
      prompt: 'Enter name of project'
      input: title
      select: true
      iconClass: 'icon-arrow-right'

    projects.getCurrent (project) =>
      if project.props.paths[0] is firstPath
        @showError "This project is already saved as #{project.props.title}"

  onConfirm: (title) ->
    if title
      properties =
        title: title
        paths: atom.project.getPaths()

      project = new Project(properties)
      project.save()

      @close()
    else
      @showError('You need to specify a name for the project')

Use Change case to prettify

Use Change case to prettify


http = require 'http'
nack = require 'nack/pool'

{BufferedReadStream} = require 'nack/buffered'

idle = 1000 * 60 * 15

exports.Server = class Server
  constructor: (@configuration) ->
    @applications = {}
    @server = http.createServer (req, res) =>
      @onRequest req, res

  listen: (port) ->
    @server.listen port

  close: ->
    @server.close()

  applicationForConfig: (config) ->
    @applications[config] ?= nack.createPool config, size: 3, idle: idle

  onRequest: (req, res) ->
    reqBuf = new BufferedReadStream req
    host = req.headers.host.replace /:.*/, ""
    @configuration.findPathForHost host, (path) =>
      if app = @applicationForConfig path
        app.proxyRequest reqBuf, res
        reqBuf.flush()
      else
        @respondWithError res, "unknown host #{req.headers.host}"

  respondWithError: (res, err) ->
    res.writeHead 500, "Content-Type": "text/html"
    res.write "
500 Internal Server Error

#{err}
"
    res.end()

http = require 'http'

{createPool}         = require 'nack/pool'
{logStream}          = require 'nack/logger'
{BufferedReadStream} = require 'nack/buffered'

idle = 1000 * 60 * 15

exports.Server = class Server
  constructor: (@configuration) ->
    @applications = {}
    @server = http.createServer (req, res) =>
      @onRequest req, res

  listen: (port) ->
    @server.listen port

  close: ->
    @server.close()

  createApplicationPool: (config) ->
    pool = createPool config, size: 3, idle: idle

    pool.on 'spawn', () ->
      logStream pool.stdout
      logStream pool.stderr

    pool

  applicationForConfig: (config) ->
    if config
      @applications[config] ?= @createApplicationPool config

  onRequest: (req, res) ->
    reqBuf = new BufferedReadStream req
    host = req.headers.host.replace /:.*/, ""
    @configuration.findPathForHost host, (path) =>
      if app = @applicationForConfig path
        app.proxyRequest reqBuf, res
      else
        @respondWithError res, "unknown host #{req.headers.host}"
      reqBuf.flush()

  respondWithError: (res, err) ->
    res.writeHead 500, "Content-Type": "text/html"
    res.write "
500 Internal Server Error

#{err}
"
    res.end()

Fix application pool creation and log child output


ledger.fup ?= {}
ledger.fup.utils ?= {}

_.extend ledger.fup.utils,

  compareVersions: (v1, v2) ->
    new ledger.utils.ComparisonResult v1, v2, (v1, v2) ->
      if v1[0] == v2[0] and v1[1] == v2[1]
        0
      else if v1[0] == 0 and v2[0] != 0
        -1
      else if v1[1] > v2[1]
        1

  versionToString: (v) ->
    return null unless v?
    version = v[1]
    info = if v[0] is 0 then "HW1" else "Ledger"
    "#{info} #{(version >> 16) & 0xff}.#{(version >> 8) & 0xff}.#{version & 0xff}"

Add a method to stringify dongle versions


kd              = require 'kd'
React           = require 'kd-react'
immutable       = require 'immutable'
ActivityFlux    = require 'activity/flux'
ChatPane        = require 'activity/components/chatpane'

module.exports = class PublicChatPane extends React.Component

  @defaultProps =
    thread   : immutable.Map()
    messages : immutable.List()
    padded   : no

  componentDidMount: ->

    @createModalContainer()

  createModalContainer: ->

    ModalContainer = document.createElement 'div'
    ModalContainer.setAttribute 'class', 'PublicChatPane-ModalContainer hidden'
    document.body.appendChild ModalContainer

  channel: (key) -> @props.thread?.getIn ['channel', key]

  onSubmit: ({ value }) ->

    return  unless body = value
    name = @channel 'name'

    unless body.match ///\##{name}///
      body += " ##{name} "

    ActivityFlux.actions.message.createMessage @channel('id'), body

    @moveScrollToBottom()

  moveScrollToBottom: ->

    ChatPaneWrapper = @refs.ChatPaneWrapper.getDOMNode()
    InfiniteScroll = ChatPaneWrapper.querySelector '.InfiniteScroll'
    InfiniteScroll.scrollTop = InfiniteScroll.scrollHeight

  onScrollThresholdReached: ->

    from = @props.messages.first().get('createdAt')
    ActivityFlux.actions.message.loadMessages @channel('id'), from

  render: ->
    <div ref="ChatPaneWrapper">
      <ChatPane
        thread={@props.thread}
        className="PublicChatPane"
        messages={@props.messages}
        onSubmit={@bound 'onSubmit'}
        onScrollThresholdReached={@bound 'onScrollThresholdReached'}
      />
    </div>

kd              = require 'kd'
React           = require 'kd-react'
immutable       = require 'immutable'
ActivityFlux    = require 'activity/flux'
ChatPane        = require 'activity/components/chatpane'

module.exports = class PublicChatPane extends React.Component

  @defaultProps =
    thread   : immutable.Map()
    messages : immutable.List()
    padded   : no

  channel: (key) -> @props.thread?.getIn ['channel', key]

  onSubmit: ({ value }) ->

    return  unless body = value
    name = @channel 'name'

    unless body.match ///\##{name}///
      body += " ##{name} "

    ActivityFlux.actions.message.createMessage @channel('id'), body

    @moveScrollToBottom()

  moveScrollToBottom: ->

    ChatPaneWrapper = @refs.ChatPaneWrapper.getDOMNode()
    InfiniteScroll = ChatPaneWrapper.querySelector '.InfiniteScroll'
    InfiniteScroll.scrollTop = InfiniteScroll.scrollHeight

  onScrollThresholdReached: ->

    from = @props.messages.first().get('createdAt')
    ActivityFlux.actions.message.loadMessages @channel('id'), from

  render: ->
    <div ref="ChatPaneWrapper">
      <ChatPane
        thread={@props.thread}
        className="PublicChatPane"
        messages={@props.messages}
        onSubmit={@bound 'onSubmit'}
        onScrollThresholdReached={@bound 'onScrollThresholdReached'}
      />
    </div>

Remove unused function and its call

activity: Remove unused function and its call


# Wrapping a string in this class gives you a prettier way to test
# for equality. The value returned by `Bullet.env` is wrapped
# in a StringInquirer object so instead of calling this:
#
#     Bullet.env == "production"
#
# you can call this:
#
#     Bullet.env.production?  #=> true
#     Bullet.env.staging?     #=> true
#

class StringInquirer

  constructor: (env) ->
    @[env] = 'true'

module.exports = StringInquirer

# Wrapping a string in this class gives you a prettier way to test
# for equality. The value returned by `Bullet.env` is wrapped
# in a StringInquirer object so instead of calling this:
#
#     Bullet.env == "production"
#
# you can call this:
#
#     Bullet.env.production?  #=> true
#     Bullet.env.staging?     #=> false
#

class StringInquirer

  constructor: (env) ->
    @[env] = 'true'

module.exports = StringInquirer

Fix a typo in stringInquirer

Fix a typo in stringInquirer


passport = require "passport"

route =
	verb: "post"
	path: "/login"
	fn: (req, res, next) ->
		auth = passport.authenticate "local", (err, user, info) ->
			if err then return next err
			else if !user
				json =
					reason:
						info.message
				return res.status(401).json json
			else req.logIn user, (err) ->
				redir = req.session?.returnTo
				if err then return next err
				else if redir then return res.redirect redir
				else return res.status(200).json { data: state: "home" }
		return auth req, res, next

module.exports = route

passport = require "passport"

route =
	verb: "post"
	path: "/login"
	fn: (req, res, next) ->
		auth = passport.authenticate "local", (err, user, info) ->
			if err then return next err
			else if !user
				json =
					reason:
						info.message
				return res.status(401).json json
			else req.logIn user, (err) ->
				redir = req.session?.returnTo
				if redir then delete req.session.returnTo
				if err then return next err
				else if redir then return res.redirect redir
				else return res.status(200).json { data: state: "home" }
		return auth req, res, next

module.exports = route

Remove returnTo after using it

Remove returnTo after using it

Ensures that Passport doesn't pick it up again and use it.


mongoose = require 'mongoose'
config = require '../../server_config'
PrepaidSchema = new mongoose.Schema {}, {strict: false, minimize: false,read:config.mongo.readpref}

PrepaidSchema.index({code: 1}, { unique: true })

PrepaidSchema.statics.generateNewCode = (done) ->
  tryCode = ->
    code = _.sample("abcdefghijklmnopqrstuvwxyz0123456789", 8).join('')
    Prepaid.findOne code: code, (err, prepaid) ->
      return done() if err
      return done(code) unless prepaid
      tryCode()
  tryCode()

PrepaidSchema.pre('save', (next) ->
  @set('exhausted', @get('maxRedeemers') <= _.size(@get('redeemers')))
  next()
)

module.exports = Prepaid = mongoose.model('prepaid', PrepaidSchema)

mongoose = require 'mongoose'
config = require '../../server_config'
PrepaidSchema = new mongoose.Schema {}, {strict: false, minimize: false,read:config.mongo.readpref}

PrepaidSchema.index({code: 1}, { unique: true })
PrepaidSchema.index({'redeemers.userID': 1})

PrepaidSchema.statics.generateNewCode = (done) ->
  tryCode = ->
    code = _.sample("abcdefghijklmnopqrstuvwxyz0123456789", 8).join('')
    Prepaid.findOne code: code, (err, prepaid) ->
      return done() if err
      return done(code) unless prepaid
      tryCode()
  tryCode()

PrepaidSchema.pre('save', (next) ->
  @set('exhausted', @get('maxRedeemers') <= _.size(@get('redeemers')))
  next()
)

module.exports = Prepaid = mongoose.model('prepaid', PrepaidSchema)

Add redeemers.userID index to prepaids collection


# Cameras by road that they are on view.
view roads: ->
    div 'data-role': 'page', id: 'roads', ->
        div 'data-role': 'header', 'data-position': 'fixed', ->
            h1 'Cameras By Road'
        div 'data-role': 'content', ->
            ul id: 'road-list', 'data-role': 'listview', 'data-filter': true, 'data-filter-placeholder': 'Filter roads...', ->
                for road in JSON.parse @roads
                    if road.directions.length is 0 or road.cameraCount is 1
                        # A road list item that does not have cameras that can
                        # be laid out directionally.  This could be because
                        # there is only a single camera, or because the item
                        # represents more than one road.
                        li -> a href: "/roads/#{road.roadId}", -> road.name

                    else if road.directions.length is 2
                        # A road list item that represents a road with cameras
                        # that can be laid out directionally (N, S, E, W).
                        li ->
                            for direction in road.directions
                                a 'data-theme': 'c',
                                  href: "/roads/#{road.roadId}/#{direction}",
                                  -> "#{road.name} #{direction}"

# Cameras by road that they are on view.
view roads: ->
    div 'data-role': 'page', id: 'roads', ->
        div 'data-role': 'header', 'data-position': 'fixed', ->
            h1 'Cameras By Road'
        div 'data-role': 'content', ->
            ul id: 'road-list', 'data-role': 'listview', 'data-filter': true, 'data-filter-placeholder': 'Filter roads...', ->
                for road in JSON.parse @roads
                    if road.directions.length is 0 or road.cameraCount is 1
                        # A road list item that does not have cameras that can
                        # be laid out directionally.  This could be because
                        # there is only a single camera, or because the item
                        # represents more than one road.
                        li -> a href: "/roads/#{road.roadId}", -> road.name

                    else if road.directions.length is 2
                        # A road list item that represents a road with cameras
                        # that can be laid out directionally (N, S, E, W).
                        li ->
                            for direction in road.directions
                                a 'data-theme': 'c',
                                  href: "/roads/#{road.roadId}/#{direction}",
                                  ->
                                    h1 "#{road.name} #{direction}"
                                    p "#{road.cameraCount} Cameras"

Add camera count to road list.

Add camera count to road list.


# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in home.js.

$ ->
		data = [2,3,4,5,5,7,6,5,4,2,2,3,4,5,5,7,9,5,3,2,2,3,2,3,4,5,5,7,6,5,4,2,2,4,2,2,3,4]
		console.log(data);
  $('.inlinesparkline').sparkline data,
  	type: "discrete"
  	lineColor: "#2d4611"

  $('ul#user-grid li').mouseover ->
  	name = $(this).data('name')
  	$('#user-name').html(name)

  $('ul#user-grid li').mouseout ->
  	$('#user-name').html("")

# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in home.js.

$ ->
  data = [2,3,4,5,5,7,6,5,4,2,2,3,4,5,5,7,9,5,3,2,2,3,2,3,4,5,5,7,6,5,4,2,2,4,2,2,3,4]
  console.log(data);
  $('.inlinesparkline').sparkline data,
    type: "discrete"
    lineColor: "#2d4611"

  #$('ul#user-grid li').mouseover ->
  #  name = $(this).data('name')
  #  $('#user-name').html(name)

  #$('ul#user-grid li').mouseout ->
  #  $('#user-name').html("")

Comment out parts of Nick's javascript that isn't needed

- Remove the name showing up on the app show page when hovering over a
  person's image


$(document).ready ->

  $('.ajax-hijacker').on 'click', 'a', (e) ->

    target = $(this).closest('.ajax-hijacker').data('target')

    href = $(this).attr('href')

    if href == '#'
      return

    e.preventDefault()
    e.stopPropagation()

    options = {
      url: href,
      success: (result) ->
        $(target).html(result)
        $(document).trigger('ajaxAction')
    }

    if type = $(this).data('method')
      options.type = type

    $.ajax options

  $('.ajax-hijacker').on 'submit', 'form', (e) ->
    e.preventDefault()
    e.stopPropagation()

    target = $(this).closest('.ajax-hijacker').data('target')

    action = $(this).attr('action')
    method = $(this).attr('method')

    data = $(this).serialize()

    options = {
      type: method,
      url: action,
      data: data,
      success: (response) ->
        $(target).html(response)
        $(document).trigger('ajaxAction')
    }

    $.ajax(options)

$(document).ready ->

  $('.ajax-hijacker').on 'click', 'a', (e) ->

    target = $(this).closest('.ajax-hijacker').data('target')

    href = $(this).attr('href')

    if href == '#'
      return

    e.preventDefault()
    e.stopPropagation()

    options = {
      url: href
      complete: (result) ->
        $(target).html(result.responseText)
        $(document).trigger('ajaxAction')
    }

    if type = $(this).data('method')
      options.type = type

    $.ajax options

  $('.ajax-hijacker').on 'submit', 'form', (e) ->
    e.preventDefault()
    e.stopPropagation()

    target = $(this).closest('.ajax-hijacker').data('target')

    action = $(this).attr('action')
    method = $(this).attr('method')

    data = $(this).serialize()

    options = {
      type: method
      url: action
      data: data
      complete: (result) ->
        $(target).html(result.responseText)
        $(document).trigger('ajaxAction')
    }

    $.ajax(options)

Handle for ajax server error

Handle for ajax server error


angular.module('Sprangular.StaticContent')
  .config ($routeProvider) ->
    $routeProvider.when '/pages/:slug*',
      controller: 'PageShowCtrl'
      templateUrl: 'pages/show.html'
      resolve:
        page: (StaticContent, $route)->
          slug = $route.current.params.slug
          StaticContent.find(slug)

Sprangular.StaticContent =
  addRoutes: (routeProvider) ->
    routeProvider.when '/:slug*',
      controller: 'PageShowCtrl'
      templateUrl: 'pages/show.html'
      resolve:
        page: (StaticContent, $route, $window)->
          slug = $route.current.params.slug

          StaticContent.find(slug).then (->), ->
            $window.location.href = "/not-found"

angular.module('Sprangular.StaticContent')
  .config ($routeProvider) ->
    $routeProvider.when '/pages/:slug*',
      controller: 'PageShowCtrl'
      templateUrl: 'pages/show.html'
      resolve:
        page: (StaticContent, $route)->
          slug = $route.current.params.slug
          StaticContent.find(slug)

Sprangular.StaticContent =
  addRoutes: (routeProvider) ->
    routeProvider.when '/:slug*',
      controller: 'PageShowCtrl'
      templateUrl: 'pages/show.html'
      resolve:
        page: (StaticContent, $route, $window)->
          slug = $route.current.params.slug

          StaticContent.find(slug).then ((data) -> data), ->
            $window.location.href = "/not-found"

Fix issue with then() returning nil on successful fetch


'use strict'

app = angular
  .module('taarifaWaterpointsApp', [
    'ngResource',
    'ngRoute',
    'leaflet-directive',
    'dynform',
    'angular-flash.service',
    'angular-flash.flash-alert-directive',
    'gettext',
    'ui.bootstrap'
  ])
  .config ($routeProvider, $httpProvider, flashProvider) ->
    $routeProvider
      .when '/',
        templateUrl: 'views/main.html'
        controller: 'MapCtrl'
      .when '/waterpoints/edit/:id',
        templateUrl: 'views/edit.html'
        controller: 'WaterpointEditCtrl'
      .when '/waterpoints/new',
        templateUrl: 'views/edit.html'
        controller: 'WaterpointCreateCtrl'
      .when '/requests',
        templateUrl: 'views/requests.html'
        controller: 'RequestTriageCtrl'
      .when '/requests/new',
        templateUrl: 'views/edit.html'
        controller: 'RequestCreateCtrl'
      .when '/dashboard',
        templateUrl: 'views/dashboard.html'
        controller: 'DashboardCtrl'
      .otherwise
        redirectTo: '/'
    $httpProvider.defaults.headers.patch =
      'Content-Type': 'application/json;charset=utf-8'
    flashProvider.errorClassnames.push 'alert-danger'

app.run (gettextCatalog,$rootScope) ->
    gettextCatalog.currentLanguage = 'en'

    # gettextCatalog.debug = true
    $rootScope.fields = {
        catalog: gettextCatalog,
        languages: {
            "en": "English",
            "sw_TZ": "Swahili"
        }
    }

'use strict'

app = angular
  .module('taarifaWaterpointsApp', [
    'ngResource',
    'ngRoute',
    'leaflet-directive',
    'dynform',
    'angular-flash.service',
    'angular-flash.flash-alert-directive',
    'gettext',
    'ui.bootstrap'
  ])
  .config ($routeProvider, $httpProvider, flashProvider) ->
    $routeProvider
      .when '/',
        templateUrl: 'views/main.html'
        controller: 'MapCtrl'
      .when '/waterpoints/edit/:id',
        templateUrl: 'views/edit.html'
        controller: 'WaterpointEditCtrl'
      .when '/waterpoints/new',
        templateUrl: 'views/edit.html'
        controller: 'WaterpointCreateCtrl'
      .when '/requests',
        templateUrl: 'views/requests.html'
        controller: 'RequestListCtrl'
      .when '/requests/:id',
        templateUrl: 'views/triage.html'
        controller: 'RequestTriageCtrl'
      .when '/requests/new',
        templateUrl: 'views/edit.html'
        controller: 'RequestCreateCtrl'
      .when '/dashboard',
        templateUrl: 'views/dashboard.html'
        controller: 'DashboardCtrl'
      .otherwise
        redirectTo: '/'
    $httpProvider.defaults.headers.patch =
      'Content-Type': 'application/json;charset=utf-8'
    flashProvider.errorClassnames.push 'alert-danger'

app.run (gettextCatalog,$rootScope) ->
    gettextCatalog.currentLanguage = 'en'

    # gettextCatalog.debug = true
    $rootScope.fields = {
        catalog: gettextCatalog,
        languages: {
            "en": "English",
            "sw_TZ": "Swahili"
        }
    }

Add routes for requests list and triage views


isObject = require 'is-object'

find = (haystack, needle, memo = []) ->
  if needle of haystack
    memo.push haystack[needle]

  if isObject haystack
    for val in (i for i of haystack)
      find(haystack[val], needle, memo) if isObject haystack[val]

  return memo

keyfinder = (obj, predicate) ->
  find obj, predicate

module.exports = (obj, predicate) ->
  find obj, predicate

isObject = require 'is-object'

find = (haystack, needle, memo = []) ->
  if needle and isObject haystack
    if needle of haystack
      memo.push haystack[needle]
    for val in (i for i of haystack)
      find(haystack[val], needle, memo) if isObject haystack[val]

  return memo

keyfinder = (obj, predicate) ->
  find obj, predicate

module.exports = (obj, predicate) ->
  find obj, predicate

Check if haystack is object before search


module.exports = (robot) ->
        robot.respond /educast (.*)/i, (res) ->
                res.send "https://beta.educast.pro/search/?q=#{res.match[1]}"

# Description:
#   Search from to educast
#
# Dependencies:
#   None
#
# Configuration:
#   None
#
# Commands:
#   hubot educast <keyword> - Search from educast

module.exports = (robot) ->
        robot.respond /educast\s+(.*)/i, (res) ->
                keyword = encodeURIComponent(res.match[1])
                res.send "https://beta.educast.pro/search/?q=#{keyword}"

Add usage. Fix command. encode uri

Add usage. Fix command. encode uri


xml = require 'xml'

# generate device type string
makeDeviceType = (config) ->
    type = [
        config.specs.upnp.prefix
        config.device.type
        config.device.version
    ]
    type.join ':'

# build device description element
buildDescription = (config, callback) ->

    # generate namespace string
    makeNS = ->
        ns = [
            config.specs.upnp.prefix
            'device'
            config.specs.upnp.version.split('.')[0]
            config.specs.upnp.version.split('.')[1]
        ]
        ns.join '-'

    # build spec version element
    buildSpecVersion = ->
        major = config.specs.upnp.version.split('.')[0]
        minor = config.specs.upnp.version.split('.')[1]
        [
            { major: major }
            { minor: minor }
        ]

    # build device element
    buildDevice = ->
        [
            { deviceType: makeDeviceType(config) }
            { friendlyName: config.app.name }
            { manufacturer: config.app.name }
            { modelName: config.app.name + ' Media Server' }
            { UDN: config.device.uuid }
        ]

    xml [
        root: [
            _attr:
                xmlns: makeNS()
            { specVersion: buildSpecVersion() }
            { device: buildDevice() }
        ]
    ]

exports.buildDescription = buildDescription
exports.makeDeviceType = makeDeviceType

xml = require 'xml'

# generate device type string
makeDeviceType = (config) ->
    type = [
        config.specs.upnp.prefix
        config.device.type
        config.device.version
    ]
    type.join ':'

# build device description element
buildDescription = (config, callback) ->

    # generate namespace string
    makeNS = ->
        ns = [
            config.specs.upnp.prefix
            'device'
            config.specs.upnp.version.split('.')[0]
            config.specs.upnp.version.split('.')[1]
        ]
        ns.join '-'

    # build spec version element
    buildSpecVersion = ->
        major = config.specs.upnp.version.split('.')[0]
        minor = config.specs.upnp.version.split('.')[1]
        [
            { major: major }
            { minor: minor }
        ]

    # build device element
    buildDevice = ->
        name = config.app.name.substr(0, 64)
        [
            { deviceType: makeDeviceType(config) }
            { friendlyName: name }
            { manufacturer: name }
            { modelName: name.substr(0, 32) }
            { UDN: config.device.uuid }
        ]

    xml [
        root: [
            _attr:
                xmlns: makeNS()
            { specVersion: buildSpecVersion() }
            { device: buildDevice() }
        ]
    ]

exports.buildDescription = buildDescription
exports.makeDeviceType = makeDeviceType

Add max lengths to Device names as per spec


angular.module 'qbn.save', ['qbn.quality']
  .factory 'savedGame', (qualities) ->
    storageName = 'qbnSave'
    api =
      load: () ->
        save = JSON.parse localStorage.getItem storageName
        if save?
          for savedQuality in save.qualities
            quality = qualities.lookup savedQuality.id
            quality.load savedQuality
        return [save?.storylet?.id, save?.storylet?.isFrontal]
      save: (storylet, isFrontal) ->
        save =
          qualities: qualities.saveAll()
        if storylet?
          save.storylet =
            id: storylet
            isFrontal: isFrontal
        localStorage.setItem storageName, JSON.stringify save
        return
      erase: () ->
        localStorage.clear()
        return
    return Object.freeze api

angular.module 'qbn.save', ['qbn.quality']
  .factory 'savedGame', (qualities) ->
    storageName = 'qbnSave'
    api =
      load: () ->
        save = JSON.parse localStorage.getItem storageName
        return undefined if not save?
        for savedQuality in save.qualities
          quality = qualities.lookup savedQuality.id
          quality.load savedQuality
        return [save.storylet?.id, save.storylet?.isFrontal]
      save: (storylet, isFrontal) ->
        save =
          qualities: qualities.saveAll()
        if storylet?
          save.storylet =
            id: storylet
            isFrontal: isFrontal
        localStorage.setItem storageName, JSON.stringify save
        return
      erase: () ->
        localStorage.clear()
        return
    return Object.freeze api

Return Undefined if There is No Save to Load


".source.js":
  "Promise":
    prefix: "promise",
    body: """
    new Promise((resolve, reject) => {
    \t${0}
    });
    """
  "Promise.prototype.then":
    prefix: "then"
    body: """
    then(${1:(${2:value}) => {
    \t${0}
    \\}})
    """
  "Promise.prototype.catch":
    prefix: "catch"
    body: """
    catch(${1:(${2:err}) => {
    \t${0}
    \\}})
    """

  "Promise.all":
    prefix: "all"
    body: "Promise.all(${1:iterable})"
  "Promise.all +":
    prefix: "Promise.all"
    body: "Promise.all(${1:iterable})"

  "Promise.race":
    prefix: "race"
    body: "Promise.race(${1:iterable})"
  "Promise.race +":
    prefix: "Promise.race"
    body: "Promise.race(${1:iterable})"

  "Promise.resolve":
    prefix: "resolve"
    body: "Promise.resolve(${1:value})"
  "Promise.resolve +":
    prefix: "Promise.resolve"
    body: "Promise.resolve(${1:value})"

  "Promise.reject":
    prefix: "reject"
    body: "Promise.reject(${1:value})"
  "Promise.reject +":
    prefix: "Promise.reject"
    body: "Promise.reject(${1:value})"

".source.js":
  "new Promise":
    prefix: "Promise",
    body: """
    new Promise((resolve, reject) => {
    \t${0}
    });
    """
  "new Promise +":
    prefix: "new Promise",
    body: """
    new Promise((resolve, reject) => {
    \t${0}
    });
    """

  "Promise.prototype.then":
    prefix: "then"
    body: """
    then(${1:(${2:value}) => {
    \t${0}
    \\}})
    """
  "Promise.prototype.catch":
    prefix: "catch"
    body: """
    catch(${1:(${2:err}) => {
    \t${0}
    \\}})
    """

  "Promise.all":
    prefix: "all"
    body: "Promise.all(${1:iterable})"
  "Promise.all +":
    prefix: "Promise.all"
    body: "Promise.all(${1:iterable})"

  "Promise.race":
    prefix: "race"
    body: "Promise.race(${1:iterable})"
  "Promise.race +":
    prefix: "Promise.race"
    body: "Promise.race(${1:iterable})"

  "Promise.resolve":
    prefix: "resolve"
    body: "Promise.resolve(${1:value})"
  "Promise.resolve +":
    prefix: "Promise.resolve"
    body: "Promise.resolve(${1:value})"

  "Promise.reject":
    prefix: "reject"
    body: "Promise.reject(${1:err})"
  "Promise.reject +":
    prefix: "Promise.reject"
    body: "Promise.reject(${1:err})"

Clean up some promise snippets

Clean up some promise snippets


'use strict';

initSelect2 = (inputs, extra = {}) ->
  inputs.each ->
    item = $(this)
    # reading from data allows <input data-select2='{"tags": ['some']}'> to be passed to select2
    options = $.extend(allowClear: true, extra, item.data('select2'))
    # because select2 reads from input.data to check if it is select2 already
    item.data('select2', null)
    item.select2(options)

$(document).on 'has_many_add:after', '.has_many_container', (e, fieldset) ->
  initSelect2(fieldset.find('.select2-input'))

$(document).ready ->
  initSelect2($(".select2-input"), placeholder: "")
  return

'use strict';

initSelect2 = (inputs, extra = {}) ->
  inputs.each ->
    item = $(this)
    # reading from data allows <input data-select2='{"tags": ['some']}'> to be passed to select2
    options = $.extend(allowClear: true, extra, item.data('select2'))
    # because select2 reads from input.data to check if it is select2 already
    item.data('select2', null)
    item.select2(options)

$(document).on 'has_many_add:after', '.has_many_container', (e, fieldset) ->
  initSelect2(fieldset.find('.select2-input'))

$(document).on 'ready page:load turbolinks:load', ->
  initSelect2($(".select2-input"), placeholder: "")
  return

Make the gem compatible with Turbolinks and Turbolinks Classic


namespace 'SensuDashboard.Views.Events', (exports) ->

  class exports.List extends SensuDashboard.Views.List

    name: 'events/list'

    initialize: ->
      @autocomplete_view = @options.autocomplete_view
      @autocomplete_view.delegate = this
      super

    itemClass: ->
      exports.ListItem

    resolvedCollection: ->
      resolved = @collection.chain()
      for token in @autocomplete_view.tokens
        model = token.object
        resolved = if model instanceof SensuDashboard.Models.Check
          resolved.filter (record) ->
            record.get('check') == model.get('name')
        else if model instanceof SensuDashboard.Models.Client
          resolved.filter (record) ->
            record.get('client') == model.get('name')
        else if _.isString(model)
          resolved.filter (record) ->
            output = record.get('output').toLowerCase()
            output.indexOf(model.toLowerCase()) != -1

      resolved

    renderCollection: ->
      @resolvedCollection().each (event) =>
        @renderItem(event)

    #
    # Autocomplete delegate
    #

    filtersUpdated: ->
      @render()

namespace 'SensuDashboard.Views.Events', (exports) ->

  class exports.List extends SensuDashboard.Views.List

    name: 'events/list'

    initialize: ->
      @autocomplete_view = @options.autocomplete_view
      @autocomplete_view.delegate = this
      super

    itemClass: ->
      exports.ListItem

    resolvedCollection: ->
      resolved = @collection.chain()
      for token in @autocomplete_view.tokens
        model = token.object
        resolved = if model instanceof SensuDashboard.Models.Check
          resolved.filter (record) ->
            record.get('check') == model.get('name')
        else if model instanceof SensuDashboard.Models.Client
          resolved.filter (record) ->
            record.get('client') == model.get('name')
        else if _.isString(model)
          resolved.filter (record) ->
            output = record.get('output').toLowerCase()
            result = output.indexOf(model.toLowerCase()) != -1
            result || record.get('check').toLowerCase() == model.toLowerCase()

      resolved

    renderCollection: ->
      @resolvedCollection().each (event) =>
        @renderItem(event)

    #
    # Autocomplete delegate
    #

    filtersUpdated: ->
      @render()

Allow event filtering by check name

Allow event filtering by check name


@ignore = []

@record_is_ignored = (name) ->
  ($.inArray name, ignore) isnt -1

render_field = ->
  if field.attr
    v = xml.attr name
  else
    x = xml.children field.name
    v = x.text()
  with_mixin {xml: x, value: v}, fieldview

@render_record = ->
  for f in schema
    if not record_is_ignored f.name
      with_mixin {field: f}, render_field

@ignore = []

@record_is_ignored = (name) ->
  ($.inArray name, ignore) isnt -1

render_field = ->
  if field.attr
    x = xml
    v = xml.attr name
  else
    x = xml.children field.name
    v = x.text()
  with_mixin {parent: xml, xml: x, value: v}, fieldview

@render_record = ->
  for f in schema
    if not record_is_ignored f.name
      with_mixin {field: f}, render_field

Set XML for attribute nodes.

Set XML for attribute nodes.


{SelectListView} = require 'atom-space-pen-views'

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

module.exports =
class PodFilePane extends SelectListView
 initialize: ->
   super
   @addClass('overlay from-top')
   @setItems([])
   @panel ?= atom.workspace.addModalPanel(item: this)
   @panel.hide()

 viewForItem: (item) ->
   "
#{path.basename(path.dirname(item))}/#{path.basename(item)}
"

 confirmed: (item) ->
   atom.workspace.open(item)

 cancelled: ->
   @panel.hide()

  getElement: ->
    @element

  toggle: (onPath) =>
    @loadItemsForPath(onPath)

    if @panel.isVisible()
      @panel.hide()
    else
      @panel.show()
      @focusFilterEditor()

  loadItemsForPath: (onPath) =>
    componentFolder = path.dirname(onPath)

    fs.readdir componentFolder, (err, files) =>
      @setItems files.map((file) => "#{componentFolder}/#{file}")

{SelectListView} = require 'atom-space-pen-views'

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

module.exports =
class PodFilePane extends SelectListView
 initialize: ->
   super
   @addClass('overlay from-top')
   @setItems([])
   @panel ?= atom.workspace.addModalPanel(item: this)
   @panel.hide()

 viewForItem: (item) ->
   "
#{path.basename(path.dirname(item))}/#{path.basename(item)}
"

 confirmed: (item) ->
   atom.workspace.open(item)

 cancelled: ->
   @panel.hide()

  getElement: ->
    @element

  toggle: (onPath) =>
    @loadItemsForPath(onPath)

    if @panel.isVisible()
      @panel.hide()
    else
      @panel.show()
      @focusFilterEditor()

  loadItemsForPath: (onPath) =>
    componentFolder = path.dirname(onPath)

    fs.readdir componentFolder, (err, files) =>
      if files
        @setItems files.map((file) => "#{componentFolder}/#{file}")

Fix a crash when cmd+shift+m failed to load files


#
# Wrap .js into .bat
# http://www.dostips.com/forum/viewtopic.php?p=37780#p37780
#
fs = require "fs"
iconv = require "iconv-lite"
ini = require '../package'

module.exports =
me = (options)->

me::apply = (compiler)->
  compiler.plugin "done", (compilation)->
    for k, z of compilation.compilation.assets
      dst = z.existsAt
      continue unless /[.]js$/.test dst
      fs.unlink dst
      dst = dst.replace /[.].*?$/, '.bat'
      fs.writeFile dst, toANSI """
0</*! ::
@echo off
REM See #{ini.homepage}
cscript //nologo //e:javascript "%~f0" %*
goto :EOF */0;
#{do z.source}

      """
    return

toANSI = (s)->
  iconv.encode s, 'cp1251'

#
# Wrap .js into .bat
# http://www.dostips.com/forum/viewtopic.php?p=37780#p37780
#
fs = require "fs"
iconv = require "iconv-lite"
ini = require '../package'

module.exports =
me = (options)->

me::apply = (compiler)->
  compiler.plugin "done", (compilation)->
    for k, z of compilation.compilation.assets
      dst = z.existsAt
      continue unless /[.]js$/.test dst
      fs.unlink dst
      dst = dst.replace /[.].*?$/, '.bat'
      fs.writeFile dst, toANSI """
0</*! :: See #{ini.homepage}
@echo off
cscript //nologo //e:javascript "%~f0" %*
goto :EOF */0;
#{do z.source}

      """
    return

toANSI = (s)->
  iconv.encode s, 'cp1251'

Move URL to first line

Move URL to first line


uuid      = require 'node-uuid'
WebSocket = require 'ws'

class DeviceManagerSocketClient
  constructor: (@options={}) ->
    @messageCallbacks = {}

  connect: (callback=->) =>
    @connection = new WebSocket "ws://#{@options.host}:#{@options.port}"

    if @connection.on?
      @connection.on 'open', callback
      @connection.on 'error', console.error
      @connection.on 'message', @onMessage
    else
      @connection.onopen = callback
      @connection.onerror = => console.error 'connection error', arguments
      @connection.onmessage = @onMessage

  addDevice: (data, callback=->) =>
    @sendMessage 'addDevice', data, callback

  removeDevice: =>
    @sendMessage 'removeDevice', data, callback

  startDevice: =>
    @sendMessage 'startDevice', data, callback

  stopDevice: =>
    @sendMessage 'stopDevice', data, callback

  onMessage: (message) =>
    message = JSON.parse message
    @messageCallbacks[message.id]?(message.error, message.data)
    delete @messageCallbacks[message.id]

  sendMessage: (action, data, callback=->) =>
    message = {action: action, id: uuid.v4(), data: data}
    @connection.send JSON.stringify message
    @messageCallbacks[message.id] = callback

module.exports = DeviceManagerSocketClient

uuid      = require 'node-uuid'
WebSocket = require 'ws'

class DeviceManagerSocketClient
  constructor: (@options={}) ->
    @messageCallbacks = {}

  connect: (callback=->) =>
    @connection = new WebSocket "ws://#{@options.host}:#{@options.port}"

    if @connection.on?
      @connection.on 'open', callback
      @connection.on 'error', console.error
      @connection.on 'message', @onMessage
    else
      @connection.onopen = callback
      @connection.onerror = => console.error 'connection error', arguments
      @connection.onmessage = @onMessage

  addDevice: (data, callback=->) =>
    @sendMessage 'addDevice', data, callback

  removeDevice: (data, callback=->) =>
    @sendMessage 'removeDevice', data, callback

  startDevice: (data, callback=->) =>
    @sendMessage 'startDevice', data, callback

  stopDevice: (data, callback=->) =>
    @sendMessage 'stopDevice', data, callback

  onMessage: (wholeMessage) =>
    message = JSON.parse wholeMessage.data
    @messageCallbacks[message.id]?(message.error, message.data)
    delete @messageCallbacks[message.id]

  sendMessage: (action, data, callback=->) =>
    message = {action: action, id: uuid.v4(), data: data}
    @connection.send JSON.stringify message
    @messageCallbacks[message.id] = callback

module.exports = DeviceManagerSocketClient

Add params to other methods

Add params to other methods


jQuery ->
  $(".best_in_place").best_in_place().bind "ajax:success", ->
    $(this).closest(".video-container").find("iframe").attr "src", $(this).html() if $(this).hasClass("video-related")

  # handle updating the new file for upload display
  $("label.file-upload-label input").livequery ->
    $(this).on "change", ->
      fileName = $(this).val().replace(/^.*[\\\/]/, '')
      fileText = "<span class='file-preview-text'>New File: </span>" + fileName
      $(this).closest(".new-file-select").find(".selected-file-preview").html fileText

# handle 401 responses (unauthorized) when AJAX requests
$.ajaxSetup statusCode:
  401: ->
    location.href = "/users/sign_in"

Add JavaScript Redirect for 401 (Unauth)

Add a JavaScript catch for if the user receives a 401 response when
attempting to click an AJAX-enabled link (i.e. signed out, not
authorized to edit, etc).


React = require 'react'
Router = require 'react-router'
LateIcon = require '../late-icon'
TaskHelper = require '../../helpers/task'

module.exports = {

  propTypes:
    courseId: React.PropTypes.string.isRequired

    task: React.PropTypes.shape(
      status:          React.PropTypes.string
      due_at:          React.PropTypes.string
      last_worked_at:  React.PropTypes.string
      type:            React.PropTypes.string
    ).isRequired

    student: React.PropTypes.shape(
      name: React.PropTypes.string
      role: React.PropTypes.number
    ).isRequired

  renderLink: ({message}) ->
    <Router.Link className={"task-result #{@props.task.type} #{@props.className}"} to='viewTaskStep'
      params={courseId: @props.courseId, id: @props.task.id, stepIndex: 1}>
      <span>{message}</span>
      <LateIcon {...@props}/>
    </Router.Link>

}

React = require 'react'
Router = require 'react-router'
LateIcon = require '../late-icon'
TaskHelper = require '../../helpers/task'

module.exports = {

  propTypes:
    courseId: React.PropTypes.string.isRequired

    task: React.PropTypes.shape(
      status:          React.PropTypes.string
      due_at:          React.PropTypes.string
      last_worked_at:  React.PropTypes.string
      type:            React.PropTypes.string
    ).isRequired

  renderLink: ({message}) ->
    <Router.Link className={"task-result #{@props.task.type} #{@props.className}"} to='viewTaskStep'
      params={courseId: @props.courseId, id: @props.task.id, stepIndex: 1}>
      <span>{message}</span>
      <LateIcon {...@props}/>
    </Router.Link>

}

Remove unused student prop from mixin

Remove unused student prop from mixin


{View} = require 'atom'

# Status bar view for the indentation indicator.
module.exports =
class IndentationIndicatorView extends View
  @content: ->
    @div class: 'inline-block', =>
      @span 'foo:42', class: 'indentation-indicator', outlet: 'text'

  # Public: Initializes the view by subscribing to various events.
  #
  # statusBar - {StatusBar} of the application
  initialize: (@statusBar) ->
    @subscribe @statusBar, 'active-buffer-changed', @update

  # Internal: Creates the text for the indicator.
  #
  # softTabs - A {Boolean} indicating whether soft tabs are enabled.
  # length - The {Number} of spaces that a tab is considered equivalent to.
  #
  # Returns the {String} containing the text for the indicator.
  formatText: (softTabs, length) ->
    type = if softTabs then "Spaces" else "Tabs"
    "#{type}:#{length}"

  # Internal: Gets the currently active `Editor`.
  #
  # Returns the {Editor} that is currently active or `null` if there is not one active.
  getActiveEditor: ->
    atom.workspace.getActiveEditor()

  # Internal: Updates the indicator based on the current state of the application.
  update: =>
    editor = @getActiveEditor()
    if editor?
      @text.text(@formatText(editor.getSoftTabs(), editor.getTabLength())).show()
    else
      @text.hide()

  # Public: Tear down any state and detach.
  destroy: ->
    @remove()

{View} = require 'atom'

# Status bar view for the indentation indicator.
module.exports =
class IndentationIndicatorView extends View
  @content: ->
    @div class: 'indentation-indicator inline-block', =>
      @span 'foo:42', outlet: 'text'

  # Public: Initializes the view by subscribing to various events.
  #
  # statusBar - {StatusBar} of the application
  initialize: (@statusBar) ->
    @subscribe @statusBar, 'active-buffer-changed', @update

  # Internal: Creates the text for the indicator.
  #
  # softTabs - A {Boolean} indicating whether soft tabs are enabled.
  # length - The {Number} of spaces that a tab is considered equivalent to.
  #
  # Returns the {String} containing the text for the indicator.
  formatText: (softTabs, length) ->
    type = if softTabs then "Spaces" else "Tabs"
    "#{type}:#{length}"

  # Internal: Gets the currently active `Editor`.
  #
  # Returns the {Editor} that is currently active or `null` if there is not one active.
  getActiveEditor: ->
    atom.workspace.getActiveEditor()

  # Internal: Updates the indicator based on the current state of the application.
  update: =>
    editor = @getActiveEditor()
    if editor?
      @text.text(@formatText(editor.getSoftTabs(), editor.getTabLength())).show()
    else
      @text.hide()

  # Public: Tear down any state and detach.
  destroy: ->
    @remove()

Move class to the outer `div`

Move class to the outer `div`


{View} = require 'space-pen'
$ = require 'jquery'
_ = require 'underscore'

module.exports =
class WrapGuide extends View
  @activate: (rootView, state) ->
    rootView.eachEditor (editor) =>
      @appendToEditorPane(rootView, editor) if editor.attached

  @appendToEditorPane: (rootView, editor) ->
    if underlayer = editor.pane()?.find('.underlayer')
      underlayer.append(new WrapGuide(rootView, editor))

  @content: ->
    @div class: 'wrap-guide'

  defaultColumn: 80

  initialize: (@rootView, @editor) =>
    @observeConfig 'editor.fontSize', => @updateGuide()
    @subscribe @editor, 'editor:path-changed', => @updateGuide()
    @subscribe @editor, 'editor:min-width-changed', => @updateGuide()
    @subscribe $(window), 'resize', => @updateGuide()

  getGuideColumn: (path) ->
    customColumns = config.get('wrapGuide.columns')
    return @defaultColumn unless _.isArray(customColumns)
    for customColumn in customColumns
      continue unless _.isObject(customColumn)
      regex = customColumn['pattern']
      continue unless regex
      return parseInt(customColumn['column']) if new RegExp(regex).test(path)
    @defaultColumn

  updateGuide: ->
    column = @getGuideColumn(@editor.getPath(), @defaultColumn)
    if column > 0
      columnWidth = @editor.charWidth * column
      if columnWidth < @editor.layerMinWidth or columnWidth < @editor.width()
        @css('left', "#{columnWidth}px").show()
      else
        @hide()
    else
      @hide()

{View} = require 'space-pen'
$ = require 'jquery'
_ = require 'underscore'

module.exports =
class WrapGuide extends View
  @activate: (rootView, state) ->
    rootView.eachEditor (editor) =>
      @appendToEditorPane(rootView, editor) if editor.attached

  @appendToEditorPane: (rootView, editor) ->
    if underlayer = editor.pane()?.find('.underlayer')
      underlayer.append(new WrapGuide(rootView, editor))

  @content: ->
    @div class: 'wrap-guide'

  defaultColumn: 80

  initialize: (@rootView, @editor) =>
    @observeConfig 'editor.fontSize', => @updateGuide()
    @subscribe @editor, 'editor:path-changed', => @updateGuide()
    @subscribe @editor, 'editor:min-width-changed', => @updateGuide()
    @subscribe $(window), 'resize', => @updateGuide()

  getGuideColumn: (path) ->
    customColumns = config.get('wrapGuide.columns')
    return @defaultColumn unless _.isArray(customColumns)
    for customColumn in customColumns
      continue unless _.isObject(customColumn)
      pattern = customColumn['pattern']
      continue unless pattern
      return parseInt(customColumn['column']) if new RegExp(pattern).test(path)
    @defaultColumn

  updateGuide: ->
    column = @getGuideColumn(@editor.getPath(), @defaultColumn)
    if column > 0
      columnWidth = @editor.charWidth * column
      if columnWidth < @editor.layerMinWidth or columnWidth < @editor.width()
        @css('left', "#{columnWidth}px").show()
      else
        @hide()
    else
      @hide()

Rename variable to pattern to shadow config key path


noflo = require 'noflo'
{QueueComponent} = require './QueueComponent'

class SendMessage extends QueueComponent
  constructor: ->
    do @basePortSetup
    mq = null

    groups = []

    @inPorts.in = new noflo.ArrayPort

    @inPorts.in.on 'connect', =>
      mq = @getMQ()

    @inPorts.in.on 'begingroup', (group) =>
      groups.push group

    @inPorts.in.on 'data', (data) =>
      return unless mq
      mq.publish groups.join(':'), data

    @inPorts.in.on 'endgroup', =>
      groups.pop()

    @inPorts.in.on 'disconnect', =>
      do mq.disconnect if mq
      mq = null

exports.getComponent = -> new SendMessage

noflo = require 'noflo'
{QueueComponent} = require './QueueComponent'

class SendMessage extends QueueComponent
  constructor: ->
    do @basePortSetup
    mq = null

    groups = []

    @inPorts.in = new noflo.ArrayPort

    @inPorts.in.on 'connect', =>
      mq = @getMQ()

    @inPorts.in.on 'begingroup', (group) =>
      groups.push group

    @inPorts.in.on 'data', (data) =>
      return unless mq
      return mq.publish groups.join(':'), data if mq.pub.connected
      mq.pub.once 'connect', ->
        mq.publish groups.join(':'), data

    @inPorts.in.on 'endgroup', =>
      groups.pop()

    @inPorts.in.on 'disconnect', =>
      do mq.disconnect if mq
      mq = null

exports.getComponent = -> new SendMessage

Deal with Redis connection possibly being slow


path = require 'path'
fs = require 'fs-extra'
request = require 'request'
Decompress = require 'decompress'
injectPackage = null
workDir = null

installPackage = (owner, name, version, callback) ->
  tarballUrl = 'https://github.com/' + owner + '/' + name + '/archive/master.tar.gz'
  tarballPath = path.join(workDir, 'name.tar.gz')
  r = request(tarballUrl)
  r.on 'end', ->
    decompress = new Decompress()
    decompress.src tarballPath
    decompress.dest path.join(workDir, 'node_modules', name)
    decompress.use(Decompress.targz({ strip: 1 }))
    decompress.run (error) ->
      if error
        throw error

      fs.unlinkSync tarballPath
      injectPackage name, version
      callback()

  r.pipe(fs.createWriteStream(tarballPath))

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

  grunt.registerTask 'install-unpublished-packages', 'Installs unpublished packages', ->
    workDir = grunt.config.get 'workDir'
    done = @async()

    installPackage 'spark', 'welcome-spark', '0.19.0', ->
      installPackage 'spark', 'feedback-spark', '0.34.0', ->
        installPackage 'spark', 'release-notes-spark', '0.36.0', ->
          installPackage 'spark', 'language-spark', '0.3.0', ->
            done()

path = require 'path'
fs = require 'fs-extra'
request = require 'request'
Decompress = require 'decompress'
injectPackage = null
workDir = null

installPackage = (owner, name, version, callback) ->
  tarballUrl = 'https://github.com/' + owner + '/' + name + '/archive/master.tar.gz'
  tarballPath = path.join(workDir, 'name.tar.gz')
  r = request(tarballUrl)
  r.on 'end', ->
    decompress = new Decompress()
    decompress.src tarballPath
    decompress.dest path.join(workDir, 'node_modules', name)
    decompress.use(Decompress.targz({ strip: 1 }))
    decompress.run (error) ->
      if error
        throw error

      fs.unlinkSync tarballPath
      injectPackage name, version
      callback()

  r.pipe(fs.createWriteStream(tarballPath))

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

  grunt.registerTask 'install-unpublished-packages', 'Installs unpublished packages', ->
    workDir = grunt.config.get 'workDir'
    done = @async()

    installPackage 'spark', 'welcome-spark', '0.19.0', ->
      installPackage 'spark', 'feedback-spark', '0.35.0', ->
        installPackage 'spark', 'release-notes-spark', '0.36.0', ->
          installPackage 'spark', 'language-spark', '0.3.0', ->
            done()

Hide Send Feedback from status bar

Hide Send Feedback from status bar


class IDE.StatusBarMenu extends KDContextMenu

  constructor: (options = {}) ->

    menuItems         = @getMenuItems options.paneType
    {delegate}        = options
    options.menuWidth = 220
    options.x         = delegate.getX()
    options.cssClass  = 'status-bar-menu'

    super options, menuItems

    @on 'ContextMenuItemReceivedClick', (view, event) =>
      if event.target.classList.contains 'kdlistitemview'
        @destroy()

  getMenuItems: (paneType) ->
    appManager = KD.getSingleton 'appManager'
    items      = {}

    @addEditorMenuItems  items, appManager  if paneType is 'editor'
    @addDefaultMenuItems items, appManager

    return items

  addEditorMenuItems: (items, appManager) ->
    items.Save        = callback: -> appManager.tell 'IDE', 'saveFile'
    items['Save All'] = callback: -> appManager.tell 'IDE', 'saveAllFiles'
    items.customView  = @syntaxSelector = new IDE.SyntaxSelectorMenuItem

  addDefaultMenuItems: (items, appManager) ->
    items.separator   = type: 'separator'
    items.Shortcuts   = callback: -> appManager.tell 'IDE', 'showShortcutsView'
    items.Contribute  = callback: -> KD.utils.createExternalLink 'https://github.com/koding/IDE'
    items.Quit        = callback: ->
      appManager.quitByName 'IDE'
      KD.getSingleton('router').handleRoute '/Activity'

class IDE.StatusBarMenu extends KDContextMenu

  constructor: (options = {}) ->

    menuItems         = @getMenuItems options.paneType
    {delegate}        = options
    options.menuWidth = 220
    options.x         = delegate.getX()
    options.cssClass  = 'status-bar-menu'

    super options, menuItems

    @on 'ContextMenuItemReceivedClick', (view, event) =>
      if event.target.classList.contains 'kdlistitemview'
        @destroy()

  getMenuItems: (paneType) ->
    appManager = KD.getSingleton 'appManager'
    items      = {}

    @addEditorMenuItems  items, appManager  if paneType is 'editor'
    @addDefaultMenuItems items, appManager

    return items

  addEditorMenuItems: (items, appManager) ->
    items.Save        = callback: -> appManager.tell 'IDE', 'saveFile'
    items['Save All'] = callback: -> appManager.tell 'IDE', 'saveAllFiles'
    items.customView  = @syntaxSelector = new IDE.SyntaxSelectorMenuItem
    items.separator   = type: 'separator'

  addDefaultMenuItems: (items, appManager) ->
    items.Shortcuts   = callback: -> appManager.tell 'IDE', 'showShortcutsView'
    items.Contribute  = callback: -> KD.utils.createExternalLink 'https://github.com/koding/IDE'
    items.Quit        = callback: ->
      appManager.quitByName 'IDE'
      KD.getSingleton('router').handleRoute '/Activity'

StatusBarMenu: Move separator from defaults to editor menu items.


$(document).ready( ->
  root = exports ? this
  mapDiv = $("#map")
  if mapDiv?
    lat = mapDiv.data("lat")
    lon = mapDiv.data("lon")
    host = mapDiv.data('host')
    key = mapDiv.data('key')
    attribution = mapDiv.data("attribution")

    zoom = 15

    map = L.map("map",
      center: [
        lat
        lon
      ]
      zoom: zoom
    )
    map.scrollWheelZoom.disable()

    L.control.scale(
      position: "topright"
      imperial: false
    ).addTo map

    map.attributionControl.setPrefix ""

    if(L.Browser.retina)
      tp = "lr"
    else
      tp = "ls"

    # L.tileLayer(host+'/'+tp+"/{z}/{x}/{y}?apikey=#{key}",
    L.tileLayer(host+"/{z}/{x}/{y}.png",
      attribution: $('<div />').html(attribution).text(),
      maxZoom: 18
    ).addTo map

    schools = $('.school')
    schools.each (i) ->
      school = schools.eq(i)
      lat = school.data('lat')
      if lat?
        lon = school.data('lon')
        options = { stroke: false, color: '#3f3', fillOpacity: 0.1 }
        marker = L.marker([lat,lon], options)
        # circle.setRadius(20)
        marker.addTo(map)
        marker.bindPopup("<p>" + school.parent().html() + "</p>")

)

$(document).ready( ->
  root = exports ? this
  mapDiv = $("#map")
  if mapDiv?
    lat = mapDiv.data("lat")
    lon = mapDiv.data("lon")
    host = mapDiv.data('host')
    key = mapDiv.data('key')
    attribution = mapDiv.data("attribution")

    zoom = 15

    L.Icon.Default.imagePath = '../images'

    map = L.map("map",
      center: [
        lat
        lon
      ]
      zoom: zoom
    )
    map.scrollWheelZoom.disable()

    L.control.scale(
      position: "topright"
      imperial: false
    ).addTo map

    map.attributionControl.setPrefix ""

    if(L.Browser.retina)
      tp = "lr"
    else
      tp = "ls"

    # L.tileLayer(host+'/'+tp+"/{z}/{x}/{y}?apikey=#{key}",
    L.tileLayer(host+"/{z}/{x}/{y}.png",
      attribution: $('<div />').html(attribution).text(),
      maxZoom: 18
    ).addTo map

    schools = $('.school')
    schools.each (i) ->
      school = schools.eq(i)
      lat = school.data('lat')
      if lat?
        lon = school.data('lon')
        options = { stroke: false, color: '#3f3', fillOpacity: 0.1 }
        marker = L.marker([lat,lon], options)
        # circle.setRadius(20)
        marker.addTo(map)
        marker.bindPopup("<p>" + school.parent().html() + "</p>")

)

Set image path for leaflet map js

Set image path for leaflet map js

Fix bug with map icon images not being found.


React = require 'react'
Select = require 'react-select'
apiClient = require '../api/client'
debounce = require 'debounce'

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

  getDefaultProps: ->
    multi: false
    project: null

  getInitialState: ->
    collections: []

  searchCollections: (value, callback) ->
    query =
      page_size: 20
      favorite: false
      current_user_roles: 'owner,collaborator'
    query.search = "#{value}" unless value is ''

    apiClient.type('collections').get query
      .then (collections) ->

        opts = collections.map (collection) ->
          {
            value: collection.id,
            label: collection.display_name,
            collection: collection
          }

        callback null, {
          options: opts
        }

  addSelected:(collIds) ->
    options = @refs.collectionSelect.state.options
    lastId = collIds.split(",").slice(-1)[0]
    collection = options.filter (col) ->
      col.value == lastId
    @setState({ collections: @state.collections.slice().concat(collection), value: collIds })

  getSelected: ->
    @state.collections

  render: ->
    <Select
      ref="collectionSelect"
      multi={@props.multi}
      name="collids"
      value={this.state.value}
      placeholder="Collection Name"
      searchPromptText="Type to search Collections"
      className="collection-search"
      closeAfterClick={true}
      onChange={@addSelected}
      asyncOptions={debounce(@searchCollections, 200)} />

React = require 'react'
Select = require 'react-select'
apiClient = require '../api/client'
debounce = require 'debounce'

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

  getDefaultProps: ->
    multi: false
    project: null

  searchCollections: (value, callback) ->
    query =
      page_size: 20
      favorite: false
      current_user_roles: 'owner,collaborator'
    query.search = "#{value}" unless value is ''

    apiClient.type('collections').get query
      .then (collections) ->

        opts = collections.map (collection) ->
          {
            value: collection.id,
            label: collection.display_name,
            collection: collection
          }

        callback null, {
          options: opts
        }

  getSelected: ->
    @refs.collectionSelect.state.values

  render: ->
    <Select
      ref="collectionSelect"
      multi={@props.multi}
      name="collids"
      placeholder="Collection Name"
      searchPromptText="Type to search Collections"
      className="collection-search"
      closeAfterClick={true}
      asyncOptions={debounce(@searchCollections, 200)} />

Clean up collections manager react-select

- Closes #2040


###
Typekit
###

try
    Typekit.load()

###
Google Analytics
###

GOOGLE_ANALYTICS =
    account: 'UA-12933299-1'
    domain: 'stdout.be'

_gaq = _gaq or []
_gaq.push ['_setAccount', GOOGLE_ANALYTICS.account]
_gaq.push ['_trackPageview']
_gaq.push ['_setDomainName', GOOGLE_ANALYTICS.domain]

do ->
    https = 'https:' is document.location.protocol
    ga = document.createElement 'script'
    ga.src = (if https then 'https://ssl' else 'http://www') + '.google-analytics.com/ga.js'
    ga.setAttribute 'async', 'async'
    document.documentElement.firstChild.appendChild ga

###
Typekit
###

try
    Typekit.load()

###
Google Analytics
###

GOOGLE_ANALYTICS =
    account: 'UA-12933299-1'
    domain: 'stdout.be'

_gaq = window._gaq ?= []
_gaq.push ['_setAccount', GOOGLE_ANALYTICS.account]
_gaq.push ['_trackPageview']
_gaq.push ['_setDomainName', GOOGLE_ANALYTICS.domain]

do ->
    https = 'https:' is document.location.protocol
    ga = document.createElement('script')
    ga.type = 'text/javascript'
    ga.async = true
    ga.src = (if https then 'https://ssl' else 'http://www') + '.google-analytics.com/ga.js'
    s = document.getElementsByTagName('script')[0]
    s.parentNode.insertBefore ga, s

Update Google Analytics tracking script.


class Presentation
  constructor: ->
    @$presentation = $('.presentation')
    @$current      = $('section').first()
    @y             = 0

    @$presentation.css('transition-duration', '1s')
    @bind()
    @resize()

  bind: ->
    @bindKeys()
    @bindGestures()
    @bindResize()

  bindKeys: ->
    key('right, down, space, return', @next)
    key('left, up, backspace', @prev)

  bindGestures: ->
    Hammer(document).on('swipeleft swipeup', @next)
    Hammer(document).on('swiperight swipedown', @prev)

  bindResize: ->
    $(window).resize(@resize)

  resize: =>
    min = Math.min($(window).width(), $(window).height())
    $('section').css('font-size', "#{min * 0.4}%")

  prev: =>
    $prev = @$current.prev('section')
    if $prev.length
      @y -= $prev.outerHeight()
      $('.presentation').css('transform', "translateY(-#{@y}px)")
      @$current = $prev

  next: =>
    $next = @$current.next('section')
    if $next.length
      @y += $next.outerHeight()
      $('.presentation').css('transform', "translateY(-#{@y}px)")
      @$current = $next

presentation = new Presentation()

class Presentation
  constructor: ->
    @$presentation = $('.presentation')
    @$current      = $('section').first()
    @y             = 0

    @$presentation.css('transition-duration', '1s')
    @bind()
    @resize()

  bind: ->
    @bindKeys()
    @bindGestures()
    @bindResize()

  bindKeys: ->
    key('right, down, space, return', @next)
    key('left, up, backspace', @prev)

  bindGestures: ->
    Hammer(document).on('swipeleft swipeup', @next)
    Hammer(document).on('swiperight swipedown', @prev)

  bindResize: ->
    $(window).resize(@resize)

  resize: =>
    min = Math.min($(window).width(), $(window).height())
    $('section').css('font-size', "#{min * 0.4}%")

  prev: =>
    $prev = @$current.prev('section')
    if $prev.length
      @y -= $prev.outerHeight()
      $('.presentation').css('transform', "translateY(-#{@y}px)")
      @$current = $prev

  next: =>
    $next = @$current.next('section')
    if $next.length
      @y += @$current.outerHeight()
      $('.presentation').css('transform', "translateY(-#{@y}px)")
      @$current = $next

presentation = new Presentation()

Fix issue with translateY calculation when going next.


class window.Topic extends Backbone.Model
  newChannelForUser: (user) ->
    new Channel
      title: this.get 'title'
      slug_title: this.get 'slug_title'
      username: user.get 'username'

  withCurrentOrCreatedChannelFor: (user, options)->
    if ch = @existingChannelFor(user)
      options.success?(ch)
    else
      ch = @newChannelForUser(user)
      user.channels.add(ch)
      console.info "saving channel #{ch.get 'title'} to #{ch.url()}"
      ch.save({},
        success: (m,r)-> options.success?(m,r)
        error:   (m,r)->
          user.channels.remove(ch)
          options.error?(m,r)
      )

  existingChannelFor: (user)->
    user.channels.getBySlugTitle(@get 'slug_title')

class window.Topic extends Backbone.Model

  idAttribute: 'slug_title'

  newChannelForUser: (user) ->
    new Channel
      title: this.get 'title'
      slug_title: this.get 'slug_title'
      username: user.get 'username'

  withCurrentOrCreatedChannelFor: (user, options)->
    if ch = @existingChannelFor(user)
      options.success?(ch)
    else
      ch = @newChannelForUser(user)
      user.channels.add(ch)
      console.info "saving channel #{ch.get 'title'} to #{ch.url()}"
      ch.save({},
        success: (m,r)-> options.success?(m,r)
        error:   (m,r)->
          user.channels.remove(ch)
          options.error?(m,r)
      )

  existingChannelFor: (user)->
    user.channels.getBySlugTitle(@get 'slug_title')

Add 'slug_title' as idAttribute to Topic

This will prevent duplicate Topics in collections when added