authentication = require 'passport'
{Strategy} = require 'passport-linkedin'
{VIE} = require "#{__dirname}/../palsu/static/deps/vie-2.0.0.js"
vieRedis = require "#{__dirname}/../../lib/vie-redis"

exports.getAuthentication = (config) ->
  vie = new VIE
  vie.namespaces.add 'foaf', 'http://xmlns.com/foaf/0.1/'
  vie.namespaces.add 'iks', 'http://www.iks-project.eu/#'

  profileToEntity = (profile) ->
    entity = vie.entities.addOrUpdate
      '@subject': "http://linkedin.com/#user/#{profile.id}"
      '@type': 'foaf:Person'
      'foaf:name': profile.displayName
    ,
      overrideAttributes: true
    entity

  authentication.use new Strategy
    consumerKey: config.linkedin.apiKey
    consumerSecret: config.linkedin.secretKey
    callbackURL: config.linkedin.callbackURL
  , (token, tokenSecret, profile, done) ->
    done null, profileToEntity profile

  authentication.serializeUser (user, done) ->
    user.save {},
      success: ->
        done null, user.getSubjectUri()
      error: (err) -> done err, null

  authentication.deserializeUser (id, done) ->
    entity = vie.entities.addOrUpdate
      '@subject': id
    ,
      overrideAttributes: true
    entity.fetch
      success: ->
        done null, entity
      error: (err) ->
        done err, null

  authentication

authentication = require 'passport'
{Strategy} = require 'passport-linkedin'
{VIE} = require "#{__dirname}/../palsu/static/deps/vie-2.0.0.js"
vieRedis = require "#{__dirname}/../../lib/vie-redis"

exports.getAuthentication = (config) ->
  vie = new VIE
  vie.namespaces.add 'foaf', 'http://xmlns.com/foaf/0.1/'
  vie.namespaces.add 'iks', 'http://www.iks-project.eu/#'

  profileToEntity = (profile) ->
    entity = vie.entities.addOrUpdate
      '@subject': "http://linkedin.com/#user/#{profile.id}"
      '@type': 'foaf:Person'
      'foaf:name': profile.displayName
    ,
      overrideAttributes: true
    entity

  apiKey = process.env.LINKEDINAPIKEY ? config.linkedIn.apiKey
  secretKey = process.env.LINKEDINSECRETKEY ? config.linkedIn.secretKey

  authentication.use new Strategy
    consumerKey: apiKey
    consumerSecret: secretKey
    callbackURL: config.linkedin.callbackURL
  , (token, tokenSecret, profile, done) ->
    done null, profileToEntity profile

  authentication.serializeUser (user, done) ->
    user.save {},
      success: ->
        done null, user.getSubjectUri()
      error: (err) -> done err, null

  authentication.deserializeUser (id, done) ->
    entity = vie.entities.addOrUpdate
      '@subject': id
    ,
      overrideAttributes: true
    entity.fetch
      success: ->
        done null, entity
      error: (err) ->
        done err, null

  authentication

Allow reading API and secret keys from env vars


Settings = require "settings-sharelatex"
logger = require "logger-sharelatex"
_ = require "underscore"

if !Settings.analytics?.postgres?
	module.exports =
		recordEvent: (user_id, event, segmentation, callback = () ->) ->
			logger.log {user_id, event, segmentation}, "no event tracking configured, logging event"
			callback()
else
	Sequelize = require "sequelize"
	options = _.extend {logging:false}, Settings.analytics.postgres

	sequelize = new Sequelize(
		Settings.analytics.postgres.database,
		Settings.analytics.postgres.username,
		Settings.analytics.postgres.password,
		options
	)

	Event = sequelize.define("Event", {
		user_id: Sequelize.STRING,
		event: Sequelize.STRING,
		segmentation: Sequelize.JSON
	})

	module.exports =
		recordEvent: (user_id, event, segmentation = {}, callback = (error) ->) ->
			if user_id? and typeof(user_id) != "string"
				user_id = user_id.toString()
			if user_id == Settings.smokeTest?.userId
				# Don't record smoke tests analytics
				return callback()
			Event
				.create({ user_id, event, segmentation })
				.then(
					(result) -> callback(),
					(error) ->
						logger.err {err: error, user_id, event, segmentation}, "error recording analytics event"
						callback(error)
				)

		sync: () -> sequelize.sync()

Settings = require "settings-sharelatex"
logger = require "logger-sharelatex"
_ = require "underscore"

if !Settings.analytics?.postgres?
	module.exports =
		recordEvent: (user_id, event, segmentation, callback = () ->) ->
			logger.log {user_id, event, segmentation}, "no event tracking configured, logging event"
			callback()
else
	Sequelize = require "sequelize"
	options = _.extend {logging:false}, Settings.analytics.postgres

	sequelize = new Sequelize(
		Settings.analytics.postgres.database,
		Settings.analytics.postgres.username,
		Settings.analytics.postgres.password,
		options
	)

	Event = sequelize.define("Event", {
		user_id: Sequelize.STRING,
		event: Sequelize.STRING,
		segmentation: Sequelize.JSONB
	})

	module.exports =
		recordEvent: (user_id, event, segmentation = {}, callback = (error) ->) ->
			if user_id? and typeof(user_id) != "string"
				user_id = user_id.toString()
			if user_id == Settings.smokeTest?.userId
				# Don't record smoke tests analytics
				return callback()
			Event
				.create({ user_id, event, segmentation })
				.then(
					(result) -> callback(),
					(error) ->
						logger.err {err: error, user_id, event, segmentation}, "error recording analytics event"
						callback(error)
				)

		sync: () -> sequelize.sync()

Use JSONB not JSON column type

Use JSONB not JSON column type


class AsciiIo.DataUnpacker
  unpack: (base64BzippedData, callback) ->
    data = atob base64BzippedData

    if data[0] == 'B' and data[1] == 'Z'
      if window.Worker
        worker = new Worker(window.unpackWorkerPath)
        worker.onmessage = (event) => callback event.data
        worker.postMessage data
      else
        data = ArchUtils.bz2.decode data
        callback data
    else
      callback data

class AsciiIo.DataUnpacker
  unpack: (base64BzippedData, callback) ->
    data = atob base64BzippedData

    if data[0] == 'B' and data[1] == 'Z'
      if window.Worker
        worker = new Worker(window.unpackWorkerPath)
        worker.onmessage = (event) => callback event.data
        worker.postMessage data
      else
        data = ArchUtils.bz2.decode data
        callback data
    else
      data

Revert "Call the callback, really"

Revert "Call the callback, really"

This reverts commit fe3af22bf22254ce3c39112c347763451dbf2979.


Monkey.registerView 'post', '''
  div[id="monkey"]
    h1 title
    p body
    h3 "Comments"
    form[submit=postComment]
      p
        textarea[change=commentEdited]
      p
        input[type="submit" value="Post"]
    ul
      - collection comments
        - view comment
'''

Monkey.registerView 'comment', '''
  li
    p body
    p
      a[click=highlight href="#"] "Highlight"
'''

class Post extends Monkey.Model
  @property 'title'
  @property 'body'
  @collection 'comments'

class Comment extends Monkey.Model
  @property 'body'

class PostController
  postComment: (event) ->
    @model.comments.push(@newComment)
    event.preventDefault()

  commentEdited: (event) ->
    @newComment = { body: event.target.value }

class CommentController
  highlight: ->
    @view.style.backgroundColor = 'red'

Monkey.registerController 'post', PostController
Monkey.registerController 'comment', CommentController

window.aPost = new Post()

aPost.comments.update([{body: 'test'}, {body: 'another'}])

window.onload = ->
  view = Monkey.render('post', aPost)
  sandbox = document.body.appendChild(view)

Monkey.registerView 'post', '''
  div[id="monkey"]
    h1 title
    p body
    h3 "Comments"
    form[submit=postComment]
      p
        textarea[change=commentEdited]
      p
        input[type="submit" value="Post"]
    ul
      - collection comments
        - view comment
'''

Monkey.registerView 'comment', '''
  li
    p body
    p
      a[click=highlight href="#"] "Highlight"
'''

class Post extends Monkey.Model
  @property 'title'
  @property 'body'
  @collection 'comments'

class Comment extends Monkey.Model
  @property 'body'

class PostController
  postComment: (event) ->
    @model.comments.push(@newComment)
    event.preventDefault()

  commentEdited: (event) ->
    @newComment = { body: event.target.value }

class CommentController
  highlight: ->
    @view.style.backgroundColor = 'red'

Monkey.registerController 'post', PostController
Monkey.registerController 'comment', CommentController

window.aPost = new Post()

aPost.comments = [{body: 'test'}, {body: 'another'}]

window.onload = ->
  view = Monkey.render('post', aPost)
  sandbox = document.body.appendChild(view)

Update example to take advantage of improved setters


# Contributed by Jason Huggins

http: require 'http'

server: http.createServer (req, res) ->
  res.writeHeader 200, {'Content-Type': 'text/plain'}
  res.write 'Hello, World!'
  res.close()

server.listen 3000

puts "Server running at http://localhost:3000/"

# Contributed by Jason Huggins

process.mixin require 'sys'
http: require 'http'

server: http.createServer (req, res) ->
  res.writeHeader 200, {'Content-Type': 'text/plain'}
  res.write 'Hello, World!'
  res.close()

server.listen 3000

puts "Server running at http://localhost:3000/"

Include an extra line `process.mixin require 'sys'` on the top to let the .js output run using node (v0.1.31)


ZXCVBN_SRC = '//dl.dropbox.com/u/209/zxcvbn/zxcvbn.js'

# adapted from http://friendlybit.com/js/lazy-loading-asyncronous-javascript/
async_load = ->
  s = document.createElement 'script'
  s.src = ZXCVBN_SRC
  s.type = 'text/javascript'
  s.async = true
  first = document.getElementsByTagName('script')[0]
  first.parentNode.insertBefore s, first

if window.attachEvent?
  window.attachEvent 'onload', async_load
else
  window.addEventListener 'load', async_load, false

ZXCVBN_SRC = 'zxcvbn.js'

# adapted from http://friendlybit.com/js/lazy-loading-asyncronous-javascript/
async_load = ->
  s = document.createElement 'script'
  s.src = ZXCVBN_SRC
  s.type = 'text/javascript'
  s.async = true
  first = document.getElementsByTagName('script')[0]
  first.parentNode.insertBefore s, first

if window.attachEvent?
  window.attachEvent 'onload', async_load
else
  window.addEventListener 'load', async_load, false

Fix path to use local files

Fix path to use local files

Fixes #67


'use strict'

app.factory('uploadService', [
  '$q', '$sce', 'constantService', 'routerService', 'messageService',
  ($q, $sce, constants, router, messages) ->
    removedAsset: undefined

    flowOptions: () ->
      target: router.controllers.uploadChunk.route()
      method: 'octet'
      # chunkSize: 4194304
      chunkSize: 500
      # forceChunkSize: false
      forceChunkSize: true
      simultaneousUploads: 3
      testChunks: false
      chunkRetryInterval: 5000
      permanentErrors: [400, 403, 404, 409, 415, 500, 501]
      successStatuses: [200, 201, 202, 204],
      progressCallbacksInterval: 500
      prioritizeFirstAndLastChunk: false
      headers: {'x-csverf': router.http.csverf}

    upload: (volume, file) ->
      file.pause()
      router.http(router.controllers.uploadStart, volume.id,
          filename: file.name
          size: file.size
        ).then (res) ->
          file.uniqueIdentifier = res.data
          file.resume()
          return
        , (res) ->
          messages.addError
            type: 'red'
            body: constants.message('asset.upload.rejected', {sce:$sce.HTML}, file.name.substr(file.name.lastIndexOf('.')+1))
            report: res
          file.cancel()
          $q.reject(res)
])

'use strict'

app.factory('uploadService', [
  '$q', '$sce', 'constantService', 'routerService', 'messageService',
  ($q, $sce, constants, router, messages) ->
    removedAsset: undefined

    flowOptions: () ->
      target: router.controllers.uploadChunk.route()
      method: 'octet'
      chunkSize: 4194304
      # chunkSize: 500
      forceChunkSize: false
      # forceChunkSize: true
      simultaneousUploads: 3
      testChunks: false
      chunkRetryInterval: 5000
      permanentErrors: [400, 403, 404, 409, 415, 500, 501]
      successStatuses: [200, 201, 202, 204],
      progressCallbacksInterval: 500
      prioritizeFirstAndLastChunk: false
      headers: {'x-csverf': router.http.csverf}

    upload: (volume, file) ->
      file.pause()
      router.http(router.controllers.uploadStart, volume.id,
          filename: file.name
          size: file.size
        ).then (res) ->
          file.uniqueIdentifier = res.data
          file.resume()
          return
        , (res) ->
          messages.addError
            type: 'red'
            body: constants.message('asset.upload.rejected', {sce:$sce.HTML}, file.name.substr(file.name.lastIndexOf('.')+1))
            report: res
          file.cancel()
          $q.reject(res)
])

Undo some temporary settings for ngflow chunk size

We need the settings lower to get around an unresolved problem with large uploads on staging. Can reverse once debugging is done.


#= require_self
#= require_tree .

window.Neighborly =
  Common:
    initPage: ->
      that = this
      unless window.Turbolinks is undefined
        $(document).bind "page:fetch", ->
          that.Loading.show()

        $(document).bind "page:restore", ->
          that.Loading.hide()

        $(document).bind "page:change", ->
          $(window).scrollTop(0)

          try
            FB.XFBML.parse()
          try
            twttr.widgets.load()

    init: ->
      $(document).foundation('reveal', {animation: 'fadeIn', animationSpeed: 100})
      $(document).foundation()

    finish: ->

    Loading:
      show: ->
        $('#loading #back-overlay, #loading #front-overlay').fadeIn(2)
      hide: ->
        $('#loading #back-overlay, #loading #front-overlay').fadeOut(2)

#= require_self
#= require_tree .

window.Neighborly =
  Common:
    initPage: ->
      that = this
      unless window.Turbolinks is undefined
        $(document).bind "page:fetch", ->
          that.Loading.show()

        $(document).bind "page:restore", ->
          that.Loading.hide()

        $(document).bind "page:change", ->
          $(window).scrollTop(0)

          try
            FB.XFBML.parse()
          try
            twttr.widgets.load()

    init: ->
      $(document).foundation('reveal', {animation: 'fadeIn', animationSpeed: 100})
      $(document).foundation()
      $('.search-button').click ->
        if $('.discover-form-input').val() != ''
          $('form.discover-form').submit()
        else
          $('.discover-form-input').toggleClass('show').focus()

        return false

    finish: ->

    Loading:
      show: ->
        $('#loading #back-overlay, #loading #front-overlay').fadeIn(2)
      hide: ->
        $('#loading #back-overlay, #loading #front-overlay').fadeOut(2)

Add javascript for search button

Add javascript for search button


window.ReactFeedSelection = React.createClass
  displayName: 'ReactFeedSelection'

  getInitialState: ->
    feedChoice: 'global'
    feeds:
      global: new GlobalFeedActivities
      personal: new PersonalFeedActivities

  handleFeedChoiceChange: (e) ->
    console.log(e,e.target,e.target.checked, e.target.value)

    if(e.target.checked)
      @setState
        feedChoice: e.target.value

  render: ->
    _div [],
      _div [],
        _input [ type: 'radio', name: 'FeedChoice', value: 'global', id: 'FeedChoice_Global', onChange: @handleFeedChoiceChange, checked: @state.feedChoice=='global'  ]
        _label [ htmlFor: 'FeedChoice_Global' ],
          'Global'

        _input [ type: 'radio', name: 'FeedChoice', value: 'personal', id: 'FeedChoice_Personal', onChange: @handleFeedChoiceChange, checked: @state.feedChoice=='personal' ]
        _label [ htmlFor: 'FeedChoice_Personal' ],
          'Personal'

      ReactFeedActivities
        model: @state.feeds[@state.feedChoice]
        key: @state.feedChoice

window.ReactFeedSelection = React.createClass
  displayName: 'ReactFeedSelection'

  getInitialState: ->
    feedChoice: 'global'
    feeds:
      global: new GlobalFeedActivities
      personal: new PersonalFeedActivities

  handleFeedChoiceChange: (e) ->
    console.log(e,e.target,e.target.checked, e.target.value)

    if(e.target.checked)
      @setState
        feedChoice: e.target.value

  render: ->
    _div [],
      _div ['feed-selection-row'],
        _input [ 'radio-toggle-button', type: 'radio', name: 'FeedChoice', value: 'global', id: 'FeedChoice_Global', onChange: @handleFeedChoiceChange, checked: @state.feedChoice=='global'  ]
        _label [ htmlFor: 'FeedChoice_Global' ],
          'Global'

        _input [ 'radio-toggle-button', type: 'radio', name: 'FeedChoice', value: 'personal', id: 'FeedChoice_Personal', onChange: @handleFeedChoiceChange, checked: @state.feedChoice=='personal' ]
        _label [ htmlFor: 'FeedChoice_Personal' ],
          'Personal'

      ReactFeedActivities
        model: @state.feeds[@state.feedChoice]
        key: @state.feedChoice

Add classes to feed selection to allow styling


angular.module('kassa').factory('CacheService', [
  '$q'
  '$parse'
  ($q, $parse)->
    cache = {}
    [isUndefined, copy] = [angular.isUndefined, angular.copy]

    _writeToCache = (cacheObj, obj, key)->
      if isUndefined(cacheObj[key])
        cacheObj[key] = obj
      else
        #copy to ensure object references for the cached object stay intact and views are actually updated
        copy(obj, cacheObj[key])

    set = (obj, prefix, field='id')->
      $q.when(obj).then (obj)-> $parse("#{prefix}.#{obj[field]}").assign(cache, obj)

    get = (key, prefix)->
      $q.when $parse("#{prefix}.#{key}")(cache)

    getAllByPrefix = (prefix)->
      cachedData = $parse("#{prefix}")(cache)
      if isUndefined(cachedData)
        $q.when()
      else
        $q.when(_.toArray(cachedData))

    {set, get, getAllByPrefix}
])

angular.module('kassa').factory('CacheService', [
  '$q'
  '$parse'
  ($q, $parse)->
    cache = {}
    [isUndefined, copy] = [angular.isUndefined, angular.copy]

    set = (obj, prefix, field='id')->
      $q.when(obj).then (obj)-> $parse("#{prefix}.#{obj[field]}").assign(cache, obj)

    get = (key, prefix)->
      $q.when $parse("#{prefix}.#{key}")(cache)

    getAllByPrefix = (prefix)->
      cachedData = $parse("#{prefix}")(cache)
      if isUndefined(cachedData)
        $q.when()
      else
        $q.when(_.toArray(cachedData))

    {set, get, getAllByPrefix}
])

Remove accidentally left unused function from CacheService


request = require("request")
settings = require("settings-sharelatex")
logger = require("logger-sharelatex")
ErrorController = require "../Errors/ErrorController"

module.exports = WikiController =
	getPage: (req, res, next) ->
		page = req.url.replace(/^\/learn/, "").replace(/^\//, "")
		if page == ""
			page = "Main_Page"

		wikiUrl = "#{settings.apis.wiki.url}/learn-scripts/api.php"

		logger.log page: page, "proxying request to wiki"

		request {
			url: wikiUrl
			qs: {
				page: page
				action: "parse"
				format: "json"
			}
		}, (err, response, data)->
			if response?.statusCode == 404
				return ErrorController.notFound(req, res, next)
			try
				data = JSON.parse(data)
			catch err
				logger.err err:err, data:data, "error parsing data from wiki"
			logger.log data: data, "got response from wiki"
			res.render "wiki/page", {
				content: data.parse.text['*']
				title: data.parse.title
			}

request = require("request")
settings = require("settings-sharelatex")
logger = require("logger-sharelatex")
ErrorController = require "../Errors/ErrorController"

module.exports = WikiController =
	getPage: (req, res, next) ->
		page = req.url.replace(/^\/learn/, "").replace(/^\//, "")
		if page == ""
			page = "Main_Page"

		wikiUrl = "#{settings.apis.wiki.url}/learn-scripts/api.php"

		logger.log page: page, "proxying request to wiki"

		request {
			url: wikiUrl
			qs: {
				page: decodeURI(page)
				action: "parse"
				format: "json"
			}
		}, (err, response, data)->
			if response?.statusCode == 404
				return ErrorController.notFound(req, res, next)
			try
				data = JSON.parse(data)
			catch err
				logger.err err:err, data:data, "error parsing data from wiki"
			logger.log data: data, "got response from wiki"
			res.render "wiki/page", {
				content: data.parse.text['*']
				title: data.parse.title
			}

Handle encoded parameters in request URL to wiki


angular.module("admin.indexUtils").controller "ColumnsCtrl", ($scope, Columns) ->
  $scope.columns = Columns.columns
  $scope.predicate = ""
  $scope.reverse = false

angular.module("admin.indexUtils").controller "ColumnsCtrl", ($scope, Columns) ->
  $scope.columns = Columns.columns
  $scope.predicate = ""
  $scope.reverse = true

Change sorting to be done in ascending order first

Currently, we always toggle "reverse" when triggering a sort.

If "reverse" is initially set to false, triggering a sort for the first
time then toggles this to true. The effect is that, the first time that
sorting is done, the rows are sorted in reverse order.

This is not intuitive - they should be sorted in ascending order first.


module.exports = {
  options:
    helperNameSuffix: '.js'
    specNameSuffix: 'spec.coffee'
    useHelpers: true
  unit:
    specs: [
      'test/unit/**'
    ]
    helpers: [
      'test/helpers/**'
    ]
  integration:
    specs: [
      'test/integration/**'
    ]
    helpers: [
      'test/helpers/**'
    ]
}

module.exports = {
  options:
    helperNameSuffix: '.js'
    specNameSuffix: 'spec.coffee'
    stopOnFailure: true
    useHelpers: true
  unit:
    specs: [
      'test/unit/**'
    ]
    helpers: [
      'test/helpers/**'
    ]
  integration:
    specs: [
      'test/integration/**'
    ]
    helpers: [
      'test/helpers/**'
    ]
}

Stop jasmine tests at the first sign of problems

#8 QueryHandlers should also handle query transmission


Fix URL parsing for bitbucket

Fix URL parsing for bitbucket


{_, $, View} = require 'atom'

module.exports =
class WrapGuideView extends View
  @activate: ->
    atom.workspaceView.eachEditorView (editorView) ->
      if editorView.attached and editorView.getPane()
        editorView.underlayer.append(new WrapGuideView(editorView))

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

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

  getDefaultColumn: ->
    atom.config.getPositiveInt('editor.preferredLineLength', 80)

  getGuideColumn: (path) ->
    customColumns = atom.config.get('wrapGuide.columns')
    return @getDefaultColumn() unless _.isArray(customColumns)
    for customColumn in customColumns when _.isObject(customColumn)
      {pattern, column} = customColumn
      return parseInt(column) if pattern and new RegExp(pattern).test(path)
    @getDefaultColumn()

  updateGuide: ->
    column = @getGuideColumn(@editorView.getPath())
    if column > 0
      columnWidth = @editorView.charWidth * column
      if columnWidth < @editorView.layerMinWidth or columnWidth < @editorView.width()
        @css('left', columnWidth).show()
      else
        @hide()
    else
      @hide()

{_, $, View} = require 'atom'

module.exports =
class WrapGuideView extends View
  @activate: ->
    atom.workspaceView.eachEditorView (editorView) ->
      if editorView.attached and editorView.getPane()
        editorView.underlayer.append(new WrapGuideView(editorView))

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

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

  getDefaultColumn: ->
    atom.config.getPositiveInt('editor.preferredLineLength', 80)

  getGuideColumn: (path) ->
    customColumns = atom.config.get('wrap-guide.columns')
    return @getDefaultColumn() unless _.isArray(customColumns)
    for customColumn in customColumns when _.isObject(customColumn)
      {pattern, column} = customColumn
      return parseInt(column) if pattern and new RegExp(pattern).test(path)
    @getDefaultColumn()

  updateGuide: ->
    column = @getGuideColumn(@editorView.getPath())
    if column > 0
      columnWidth = @editorView.charWidth * column
      if columnWidth < @editorView.layerMinWidth or columnWidth < @editorView.width()
        @css('left', columnWidth).show()
      else
        @hide()
    else
      @hide()

Use package name as config prefix

Use package name as config prefix


class SidebarMachineBox extends KDView

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

    super options, data

    { machine } = data

    @addSubView @machineItem = new NavigationMachineItem {}, machine

    @createWorkspacesLabel()
    @createWorkspacesList()

  createWorkspacesList: ->

    { machine, workspaces } = @getData()

    @listController = new KDListViewController
      itemClass     : NavigationWorkspaceItem
      itemOptions   : { machine }

    for ws in workspaces
      @listController.addItem ws

    @addSubView @listController.getView()

  createWorkspacesLabel: ->

    @addSubView @workspacesLabel = new KDCustomHTMLView
      cssClass : 'workspaces-link'
      partial  : 'Workspaces'
      click    : =>
        modal = new MoreWorkspacesModal {}, @getData().workspaces
        modal.once 'NewWorkspaceRequested', @bound 'showAddWorkspaceInput'

  showAddWorkspaceInput: ->

    if @addWorkspaceView
      @addWorkspaceView.input.setFocus()
      return no

    { machineUId, machineLabel } = @getData().machine

    @addWorkspaceView = new AddWorkspaceView {}, { machineUId, machineLabel }
    @addWorkspaceView.once 'KDObjectWillBeDestroyed', => @addWorkspaceView = null

    @listController.getView().addSubView @addWorkspaceView

    KD.utils.wait 177, => @addWorkspaceView.input.setFocus()

class SidebarMachineBox extends KDView

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

    super options, data

    { machine } = data

    @machine = new Machine machine: KD.remote.revive machine

    @addSubView @machineItem = new NavigationMachineItem {}, @machine

    @createWorkspacesLabel()
    @createWorkspacesList()

  createWorkspacesList: ->

    { machine, workspaces } = @getData()

    @listController = new KDListViewController
      itemClass     : NavigationWorkspaceItem
      itemOptions   : { machine }

    for ws in workspaces
      @listController.addItem ws

    @addSubView @listController.getView()

  createWorkspacesLabel: ->

    @addSubView @workspacesLabel = new KDCustomHTMLView
      cssClass : 'workspaces-link'
      partial  : 'Workspaces'
      click    : =>
        modal = new MoreWorkspacesModal {}, @getData().workspaces
        modal.once 'NewWorkspaceRequested', @bound 'showAddWorkspaceInput'

  showAddWorkspaceInput: ->

    if @addWorkspaceView
      @addWorkspaceView.input.setFocus()
      return no

    { machineUId, machineLabel } = @getData().machine

    @addWorkspaceView = new AddWorkspaceView {}, { machineUId, machineLabel }
    @addWorkspaceView.once 'KDObjectWillBeDestroyed', => @addWorkspaceView = null

    @listController.getView().addSubView @addWorkspaceView

    KD.utils.wait 177, => @addWorkspaceView.input.setFocus()

Make data a Machine instance.

SidebarMachineBox: Make data a Machine instance.


module.exports = config =
	apiEndpoint: process.env.API_ENDPOINT ? 'https://api.resin.io'
	registryEndpoint: process.env.REGISTRY_ENDPOINT ? 'registry.resin.io'
	pubnub:
		subscribe_key: process.env.PUBNUB_SUBSCRIBE_KEY ? 'sub-c-bananas'
		publish_key: process.env.PUBNUB_PUBLISH_KEY ? 'pub-c-bananas'
	mixpanelToken: process.env.MIXPANEL_TOKEN ? 'bananasbananas'
	dockerSocket: process.env.DOCKER_SOCKET ? '/run/docker.sock'
	supervisorContainer:
		Volumes:
			'/boot/config.json': {}
			'/data': {}
			'/run/docker.sock': {}
			'/mnt/fib_trie': {}
		Binds: [
			'/mnt/mmcblk0p1/config.json:/boot/config.json'
			'/var/run/docker.sock:/run/docker.sock'
			'/var/lib/docker/data:/data'
			'/proc/net/fib_trie:/mnt/fib_trie'
		]

module.exports = config =
	apiEndpoint: process.env.API_ENDPOINT ? 'https://api.resin.io'
	registryEndpoint: process.env.REGISTRY_ENDPOINT ? 'registry.resin.io'
	pubnub:
		subscribe_key: process.env.PUBNUB_SUBSCRIBE_KEY ? 'sub-c-bananas'
		publish_key: process.env.PUBNUB_PUBLISH_KEY ? 'pub-c-bananas'
	mixpanelToken: process.env.MIXPANEL_TOKEN ? 'bananasbananas'
	dockerSocket: process.env.DOCKER_SOCKET ? '/run/docker.sock'
	supervisorContainer:
		Volumes:
			'/boot/config.json': {}
			'/data': {}
			'/run/docker.sock': {}
			'/mnt/fib_trie': {}
		Binds: [
			'/mnt/mmcblk0p1/config.json:/boot/config.json'
			'/var/run/docker.sock:/run/docker.sock'
			'/resin-data/resin-supervisor:/data'
			'/proc/net/fib_trie:/mnt/fib_trie'
		]

Change the data mount point to /resin-data


React = require 'react-atom-fork'
{div} = require 'reactionary-atom-fork'

module.exports = React.createClass
  getInitialState: ->
    firstRow: 0
    lastRow: 0
    totalRows: 0
    rowHeight: 0
    columnsWidth: []

  render: ->
    {firstRow, lastRow, rowHeight, columnsWidth} = @state

    console.log columnsWidth

    rows = for row in [firstRow...lastRow]
      rowData = @props.table.getRow(row)
      columns = []
      rowData.eachCell (cell,i) ->
        columns.push div className: 'table-edit-column', style: { width: columnsWidth[i] }, cell.getValue()

      div className: 'table-edit-row', style: { height: "#{rowHeight}px", top: "#{row * rowHeight}px" }, 'data-row-id': row + 1, columns

    div className: 'table-edit-content', style: { height: @getTableHeight() }, rows

  getTableHeight: ->
    @state.totalRows * @state.rowHeight

React = require 'react-atom-fork'
{div} = require 'reactionary-atom-fork'

module.exports = React.createClass
  getInitialState: ->
    firstRow: 0
    lastRow: 0
    totalRows: 0
    rowHeight: 0
    columnsWidth: []

  render: ->
    {firstRow, lastRow, rowHeight, columnsWidth} = @state

    rows = for row in [firstRow...lastRow]
      rowData = @props.table.getRow(row)
      columns = []
      rowData.eachCell (cell,i) ->
        columns.push div key: "cell-#{row}-#{i}", className: 'table-edit-column', style: { width: columnsWidth[i] }, cell.getValue()

      div key: "row-#{row}", className: 'table-edit-row', style: { height: "#{rowHeight}px", top: "#{row * rowHeight}px" }, 'data-row-id': row + 1, columns

    div className: 'table-edit-content', style: { height: @getTableHeight() }, rows

  getTableHeight: ->
    @state.totalRows * @state.rowHeight

:bug: Fix react warning when rendering collection without keys


class Postie
  listenCallback: null

  constructor: (options = {}) ->

  send: (frames, data, callback) ->
    console.log frames

  # Public: assign listener callback function
  listen: (callback) ->
    @listenCallback = callback
    @bindEventListener

  onMessage: (event) ->
    event = @parseEvent(event)
    console.log event
    @listenCallback(event)

  parseEvent: (rawEvent) ->
    new PostieEvent(rawEvent)

  bindEventListener: ->
    if typeof(window.addEventListener) is not 'undefined'
      window.addEventListener 'message', @onMessage, false
    else if typeof(window.attachEvent) is not 'undefined'
      window.attachEvent 'onmessage', @onMessage

class Postie
  listenCallback: null
  domain: '*'

  constructor: (options = {}) ->

  send: (frames, data, callback) ->
    console.log frames

  # Public: assign listener callback function
  listen: (callback) ->
    @listenCallback = callback
    @bindEventListener

  onMessage: (event) ->
    event = @parseEvent(event)
    console.log event
    @listenCallback(event)

  parseEvent: (rawEvent) ->
    new PostieEvent(rawEvent)

  bindEventListener: ->
    if typeof(window.addEventListener) is not 'undefined'
      window.addEventListener 'message', @onMessage, false
    else if typeof(window.attachEvent) is not 'undefined'
      window.attachEvent 'onmessage', @onMessage

Set cross-domain allowed to default to '*'


".source.apl":

	Function:
		prefix: "fn"
		body:   "∇${2:R ←} ${3:X} ${1:NAME} ${4:Y}\n\t$0\n∇"

	Operator:
		prefix: "op"
		body:   "∇${2:R ←} ${3:X} (${4:LOP} ${1:NAME} ${5:ROP}) ${6:Y}\n\t$0\n∇"

	Assignment:
		prefix: "a"
		body:   "${1:⎕ }← ${2:VALUE}"

	Hashbang:
		prefix: "#"
		body: "#!/usr/${3:local/bin/apl} ${2:--noCIN -f}$1"

	# GNU APL extensions
	"GNU Heredoc: Plain":
		prefix: "hd"
		body:   "${1:result} ← '<?apl' '?>' ⎕INP '${2:RAWTEXT}'\n\t$0\n${2:RAWTEXT}"

	"GNU Heredoc: HTML":
		prefix: "html"
		body:   "${1:result} ← '<?apl' '?>' ⎕INP '${2:END-}HTML'\n\t$0\n${2:END-}HTML"

# Snippets scoped to heredocs containing foreign languages (like HTML)
".heredoc.apl .embedded":
	"GNU Heredoc: Embedded APL":
		prefix: "apl"
		body:   "<?apl $0 ?>"

".source.apl":

	Function:
		prefix: "fn"
		body:   "∇${2:R ←} ${3:X} ${1:NAME} ${4:Y}\n\t$0\n∇"

	Operator:
		prefix: "op"
		body:   "∇${2:R ←} ${3:X} (${4:LOP} ${1:NAME} ${5:ROP}) ${6:Y}\n\t$0\n∇"

	Assignment:
		prefix: "a"
		body:   "${1:⎕ }← ${2:VALUE}"

	Hashbang:
		prefix: "apl"
		body: "#!/usr/bin/env apl --script\n\n$1\n\n)OFF"

	# GNU APL extensions
	"GNU Heredoc: Plain":
		prefix: "hd"
		body:   "${1:result} ← '<?apl' '?>' ⎕INP '${2:RAWTEXT}'\n\t$0\n${2:RAWTEXT}"

	"GNU Heredoc: HTML":
		prefix: "html"
		body:   "${1:result} ← '<?apl' '?>' ⎕INP '${2:END-}HTML'\n\t$0\n${2:END-}HTML"

# Snippets scoped to heredocs containing foreign languages (like HTML)
".heredoc.apl .embedded":
	"GNU Heredoc: Embedded APL":
		prefix: "apl"
		body:   "<?apl $0 ?>"

Revise hashbang snippet added in d181298

* Prefix changed to "apl". Typing this is somewhat easier than "#".
* Hashbang path changed to use /usr/bin/env, which is more portable.
* Tab-stops removed; users won't likely need to change the hashbang path
  or arguments. Having tab-stops also makes it impossible to indent code
  after insertion, as the cursor jumps back to the hashbang directive.
* An ")OFF" command has been included, necessary to terminate GNU APL.


angular.module('doubtfire.units.states.tasks.inbox', [])

#
# Task inbox for tasks to deal with
#
.config(($stateProvider) ->
  $stateProvider.state 'units/tasks/inbox', {
    parent: 'units/tasks'
    url: '/inbox/:taskId'
    templateUrl: "units/states/tasks/inbox/inbox.tpl.html"
    controller: "TaskInboxStateCtrl"
    data:
      task: "Task Inbox"
      pageTitle: "_Home_"
      roleWhitelist: ['Tutor', 'Convenor', 'Admin']
   }
)

.controller('TaskInboxStateCtrl', ($scope, Unit) ->
  $scope.taskSource = Unit.tasksForTaskInbox
  console.log("Set dataSource to ", $scope.dataSource)
)

angular.module('doubtfire.units.states.tasks.inbox', [])

#
# Task inbox for tasks to deal with
#
.config(($stateProvider) ->
  $stateProvider.state 'units/tasks/inbox', {
    parent: 'units/tasks'
    url: '/inbox/:taskId'
    templateUrl: "units/states/tasks/inbox/inbox.tpl.html"
    controller: "TaskInboxStateCtrl"
    data:
      task: "Task Inbox"
      pageTitle: "_Home_"
      roleWhitelist: ['Tutor', 'Convenor', 'Admin']
   }
)

.controller('TaskInboxStateCtrl', ($scope, Unit) ->
  $scope.taskSource = Unit.tasksForTaskInbox
)

QUALITY: Remove debug message from TaskInboxStateCtrl


define (require) ->
  Backbone = require('backbone')
  settings = require('settings')
  session = require('cs!session')
  analytics = require('cs!helpers/handlers/analytics')
  AppView = require('cs!pages/app/app')

  return new class Router extends Backbone.Router
    initialize: () ->
      @appView = new AppView()

      # Default Route
      @route '*actions', 'default', () ->
        @appView.render('error', {code: 404})

      @route '', 'index', () ->
        @appView.render('home')

      @route 'contents', 'contents', () ->
        @appView.render('contents')

      @route 'workspace', 'workspace', () ->
        @appView.render('workspace')

      # Match and extract uuid and page numbers separated by a colon
      @route /^contents\/([^:]+):?([0-9]*)/, 'media', (uuid, page) ->
        uuid = uuid.toLowerCase()
        uuid = settings.shortcodes[uuid] if settings.shortcodes[uuid]
        @appView.render('contents', {uuid: uuid, page: Number(page)})

      @route /^search/, 'search', () ->
        @appView.render('search')

      @route 'about-us', 'about-us', () ->
        @appView.render('about-us')

      @route 'people', 'people', () ->
        @appView.render('people')

    navigate: (fragment, options = {}, cb) ->
      super(arguments...)
      session.update() # Check for changes to the session status
      analytics.send() if options.analytics isnt false
      cb?()
      @trigger('navigate')

define (require) ->
  Backbone = require('backbone')
  settings = require('settings')
  session = require('cs!session')
  analytics = require('cs!helpers/handlers/analytics')
  AppView = require('cs!pages/app/app')

  return new class Router extends Backbone.Router
    initialize: () ->
      @appView = new AppView()

      # Default Route
      @route '*actions', 'default', () ->
        @appView.render('error', {code: 404})

      @route '', 'index', () ->
        @appView.render('home')

      @route 'contents', 'contents', () ->
        @appView.render('contents')

      @route 'workspace', 'workspace', () ->
        @appView.render('workspace')

      # Match and extract uuid and page numbers separated by a colon
      @route /^contents\/([^:]+):?([0-9]*)/, 'media', (uuid, page) ->
        uuid = uuid.toLowerCase()
        uuid = settings.shortcodes[uuid] if settings.shortcodes[uuid]
        @appView.render('contents', {uuid: uuid, page: Number(page)})

      @route /^(search)(?:\?q=)?(.*)/, 'search', () ->
        @appView.render('search')

      @route 'about-us', 'about-us', () ->
        @appView.render('about-us')

      @route 'people', 'people', () ->
        @appView.render('people')

    navigate: (fragment, options = {}, cb) ->
      super(arguments...)
      session.update() # Check for changes to the session status
      analytics.send() if options.analytics isnt false
      cb?()
      @trigger('navigate')

Include route and query in search params


# *************************************
#
#   Document Ready
#
# *************************************

jQuery ($) ->

  # ----- Functions ----- #

  Orientation.accordion()
  Orientation.autoSubmit()
  Orientation.dropdown()
  Orientation.headingLink()
  Orientation.search()

  Orientation.search
    $element : $( '.js-articleSearch' )
    $form    : $( '.js-articleSearch-form' )
    $input   : $( '.js-articleSearch-input' )
    $results : $( '.js-articleSearch-results' )
    callback : ( settings ) ->
      guidesElement = $( '.js-guideList' )

      if settings.$input.val()
        guidesElement.addClass( 'dn' )
        settings.$results.removeClass( 'dn' )
      else
        guidesElement.removeClass( 'dn' )
        settings.$results.addClass( 'dn' )

  # ----- Modules ----- #

  Orientation.editor.init()
  Orientation.selectText.init()
  Orientation.tableBank.init()

  # ----- Vendor ----- #

  # Bootstrap

  $( '[data-toggle="tooltip"]' ).tooltip
    container : 'body'

  # jquery-ujs

  $( '#article_tag_tokens' ).tokenInput '/tags.json',
    prePopulate       : $( '#article_tag_tokens' ).data( 'load' )
    preventDuplicates : true

# *************************************
#
#   Document Ready
#
# *************************************

jQuery ($) ->

  # ----- Functions ----- #

  Orientation.accordion()
  Orientation.autoSubmit()
  Orientation.dropdown()
  Orientation.headingLink()
  Orientation.search()
  Orientation.shortcut()

  Orientation.search
    $element : $( '.js-articleSearch' )
    $form    : $( '.js-articleSearch-form' )
    $input   : $( '.js-articleSearch-input' )
    $results : $( '.js-articleSearch-results' )
    callback : ( settings ) ->
      guidesElement = $( '.js-guideList' )

      if settings.$input.val()
        guidesElement.addClass( 'dn' )
        settings.$results.removeClass( 'dn' )
      else
        guidesElement.removeClass( 'dn' )
        settings.$results.addClass( 'dn' )

  # ----- Modules ----- #

  Orientation.editor.init()
  Orientation.selectText.init()
  Orientation.tableBank.init()

  # ----- Vendor ----- #

  # Bootstrap

  $( '[data-toggle="tooltip"]' ).tooltip
    container : 'body'

  # jquery-ujs

  $( '#article_tag_tokens' ).tokenInput '/tags.json',
    prePopulate       : $( '#article_tag_tokens' ).data( 'load' )
    preventDuplicates : true

Call Shortcut in DOM ready script

Call Shortcut in DOM ready script


class app.models.Entry extends app.Model
  # Attributes: name, type, path

  SEPARATORS_REGEXP = /\:?\ |#|::/g
  PARANTHESES_REGEXP = /\(.*?\)$/

  constructor: ->
    super
    @text = @searchValue()

  searchValue: ->
    @name
      .toLowerCase()
      .replace '...', ' '
      .replace ' event', ''
      .replace SEPARATORS_REGEXP, '.'
      .replace /\.+/g, '.'
      .replace PARANTHESES_REGEXP, ''
      .trim()

  fullPath: ->
    @doc.fullPath if @isIndex() then '' else @path

  filePath: ->
    @doc.fullPath @_filePath()

  fileUrl: ->
    @doc.fileUrl @_filePath()

  _filePath: ->
    result = @path.replace /#.*/, ''
    result += '.html' unless result[-5..-1] is '.html'
    result

  isIndex: ->
    @path is 'index'

  getType: ->
    @doc.types.findBy 'name', @type

  loadFile: (onSuccess, onError) ->
    ajax
      url: @fileUrl()
      dataType: 'html'
      success: onSuccess
      error: onError

class app.models.Entry extends app.Model
  # Attributes: name, type, path

  SEPARATORS_REGEXP = /\:?\ |#|::|->/g
  PARANTHESES_REGEXP = /\(.*?\)$/

  constructor: ->
    super
    @text = @searchValue()

  searchValue: ->
    @name
      .toLowerCase()
      .replace '...', ' '
      .replace ' event', ''
      .replace SEPARATORS_REGEXP, '.'
      .replace /\.+/g, '.'
      .replace PARANTHESES_REGEXP, ''
      .trim()

  fullPath: ->
    @doc.fullPath if @isIndex() then '' else @path

  filePath: ->
    @doc.fullPath @_filePath()

  fileUrl: ->
    @doc.fileUrl @_filePath()

  _filePath: ->
    result = @path.replace /#.*/, ''
    result += '.html' unless result[-5..-1] is '.html'
    result

  isIndex: ->
    @path is 'index'

  getType: ->
    @doc.types.findBy 'name', @type

  loadFile: (onSuccess, onError) ->
    ajax
      url: @fileUrl()
      dataType: 'html'
      success: onSuccess
      error: onError

Add "->" as a search term separator

Add "->" as a search term separator


# Config and Start SyncedCron
SyncedCron.config
	collectionName: 'rocketchat_cron_history'

generateStatistics = ->
	statistics = RocketChat.statistics.save()
	statistics.host = Meteor.absoluteUrl()
	unless RocketChat.settings.get 'Statistics_opt_out'
		HTTP.post 'https://rocket.chat/stats',
			data: statistics
	return

Meteor.startup ->
	Meteor.defer ->
		generateStatistics()

		# Generate and save statistics every hour
		SyncedCron.add
			name: 'Generate and save statistics',
			schedule: (parser) -># parser is a later.parse object
				return parser.text 'every 1 hour'
			job: generateStatistics

		SyncedCron.start()

# Config and Start SyncedCron
logger = new Logger 'SyncedCron'

SyncedCron.config
	logger: (opts) ->
		logger[opts.level].call(logger, opts.message)
	collectionName: 'rocketchat_cron_history'

generateStatistics = ->
	statistics = RocketChat.statistics.save()
	statistics.host = Meteor.absoluteUrl()
	unless RocketChat.settings.get 'Statistics_opt_out'
		HTTP.post 'https://rocket.chat/stats',
			data: statistics
	return

Meteor.startup ->
	Meteor.defer ->
		generateStatistics()

		# Generate and save statistics every hour
		SyncedCron.add
			name: 'Generate and save statistics',
			schedule: (parser) -># parser is a later.parse object
				return parser.text 'every 1 hour'
			job: generateStatistics

		SyncedCron.start()

Use RocketChat Logger as SyncedCron logger


Dashboard.PaginatedRouteMixin = Ember.Mixin.create
  actions:
    refreshPageData: (controller, page)->
      query = $.extend(@controller.get('model')['query'], { page: page })

      @store.findQuery(@controller.get('model').type, query).then (records)=>
        metadata = records.store.typeMapFor(records.type).metadata
        @controller.set('model', records)

    gotoPage: (page) ->
      @send('refreshPageData', @get('controller'), page)

    nextPage: ->
      current = @get('controller.model.meta.page') or 1
      @gotoPage current + 1

    previousPage: ->
      current = @get('controller.model.meta.page') or 0
      @gotoPage current - 1

    lastPage: ->
      last = @get('controller.model.meta.total_pages') or 1
      @gotoPage last

    firstPage: ->
      @gotoPage 1

Dashboard.PaginatedRouteMixin = Ember.Mixin.create
  actions:
    refreshPageData: (controller, page)->
      query = $.extend(@controller.get('model')['query'], { page: page })

      @store.findQuery(@controller.get('model').type, query).then (records)=>
        metadata = records.store.typeMapFor(records.type).metadata
        @controller.set('model', records)

    gotoPage: (page) ->
      @send('refreshPageData', @get('controller'), page)

    nextPage: ->
      current = @get('controller.model.meta.page') or 1
      @gotoPage current + 1

    previousPage: ->
      current = @get('controller.model.meta.page') or 2
      @gotoPage current - 1

    lastPage: ->
      last = @get('controller.model.meta.total_pages') or 1
      @gotoPage last

    firstPage: ->
      @gotoPage 1

Change default value for paginated route


class ledger.api.UnspentOutputsRestClient extends ledger.api.RestClient

  getUnspentOutputsFromAddresses: (addresses, callback) ->
    addresses = (address for address in addresses when Bitcoin.Address.validate(address) is true)
    query = _(addresses).join(',')
    @http().get(url: "blockchain/addresses/#{query}/unspents").done( (response) =>
      l response
      callback?(response)
    ).fail (xhr, status, message) =>
      callback(null, {xhr, status, message})

  getUnspentOutputsFromPaths: (addressesPaths, callback) ->
    ledger.wallet.pathsToAddresses addressesPaths, (addresses, notFound) =>
      l addressesPaths, addresses
      if notFound.length == addressesPaths.length
        callback? null, {title: 'Missing addresses', missings: notFound}
      else
        @getUnspentOutputsFromAddresses _.values(addresses), (outputs, error) =>
          return callback?(null, error) if error?
          paths = _.invert(addresses)
          for output in outputs
            output.paths = []
            output.paths.push paths[address] for address in output.addresses when paths[address]?
          callback?(outputs)

ledger.api.UnspentOutputsRestClient.instance = new ledger.api.UnspentOutputsRestClient()

class ledger.api.UnspentOutputsRestClient extends ledger.api.RestClient

  getUnspentOutputsFromAddresses: (addresses, callback) ->
    addresses = (address for address in addresses when Bitcoin.Address.validate(address) is true)
    query = _(addresses).join(',')
    @http().get
      url: "blockchain/addresses/#{query}/unspents"
      onSuccess: (response) =>
        callback?(response)
      onError: (xhr, status, message) =>
        callback(null, {xhr, status, message})

  getUnspentOutputsFromPaths: (addressesPaths, callback) ->
    ledger.wallet.pathsToAddresses addressesPaths, (addresses, notFound) =>
      if notFound.length == addressesPaths.length
        callback? null, {title: 'Missing addresses', missings: notFound}
      else
        @getUnspentOutputsFromAddresses _.values(addresses), (outputs, error) =>
          return callback?(null, error) if error?
          paths = _.invert(addresses)
          for output in outputs
            output.paths = []
            output.paths.push paths[address] for address in output.addresses when paths[address]?
          callback?(outputs)

ledger.api.UnspentOutputsRestClient.instance = new ledger.api.UnspentOutputsRestClient()

Replace promise with callbacks for UnspentOutputsRestClient


window.Cosmo =
  version: '0.1.0'

# more
# All apps should be an extended instance of this class
class Cosmo.Router
  # app regions, assumes main content goes in a div#container
  regions:
    container: $('#container')

  initialize: ->

  # do initialization stuff and then start app routing
  # TODO: Do some url management
  start: ->
    @initialize()
    @go 'home'
    this

  # Takes an object with names and jquery selectors
  addRegions: (regions) ->
    for key, value of regions
      @regions[key] = $(value)
    this

  # set the value
  # TODO: Return value or this? (to allow chaining)
  set: (key, val) ->
    if val?
      this[key] = val
    else
      this[key] = null
    this

  get: (key) -> this[key]

  # main routing function
  go: (page, args...) ->
    if this[page]?
      this[page]?(args[0], args[1])
    else
      @home()

  render: (view) -> @regions.container.html view

window.Cosmo =
  version: '0.1.0'

# All apps should be an extended instance of this class
class Cosmo.Router
  # app regions, assumes main content goes in a div#container
  regions:
    container: $('#container')

  initialize: ->

  # do initialization stuff and then start app routing
  # TODO: Do some url management
  start: ->
    @initialize()
    @go 'home'
    this

  # Takes an object with names and jquery selectors
  addRegions: (regions) ->
    for key, value of regions
      @regions[key] = $(value)
    this

  # set the value
  # TODO: Return value or this? (to allow chaining)
  set: (key, val) ->
    if val?
      this[key] = val
    else
      this[key] = null
    this

  get: (key) -> this[key]

  # main routing function
  go: (page, args...) ->
    if this[page]?
      this[page]?(args[0], args[1])
    else
      @home()

  render: (view) -> @regions.container.html view

Remove comment in branch test

Remove comment in branch test


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

  bongo = this

  (req, res, next) ->

    { channelName, queue: requestQueue } = req.body

    unless requestQueue?.length
      res.send 400
      return

    responseQueue = new Array requestQueue.length

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

    dash workQueue, -> res.send responseQueue

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

  bongo = this

  (req, res, next) ->

    { channelName, queue: requestQueue } = req.body

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

    responseQueue = new Array requestQueue.length

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

    dash workQueue, -> res.send responseQueue

Refactor deprecated method call for setting response status


#require("./webgl-example/blue-thing")

leaflet = require("leaflet")
require("./rectangle_editor")
$ = require("jquery")

tilesUrl = 'http://skogsmaskin.asuscomm.com:3001/kartverk/{z}/{x}/{y}.png'
tilesLayer = new L.TileLayer(tilesUrl);

$ ->
  map = new L.Map('map');
  map.addLayer(tilesLayer);
  map.setView(new L.LatLng(59.918893,10.739715), 10);

  do ->
    eRect = new L.RectangleEditor([[59.9,10.7],[59.928893,10.769715]],);
    map.addLayer(eRect);

    from = null
    to = null
    selectionRect = L.rectangle([[59.918893,10.739715], [59.918893,10.739715]], {color: "#ff7800", weight: 1, opacity: 0.5})
    added = false
    drawSelection = ()->
      selectionRect.setBounds([from, to])
      selectionRect.addTo(map) and added = true unless added
      console.log('draw', from, ' => ', to)

    setTo = (e)->
      console.log("omg")
      to = e.latlng
      drawSelection()

    endSelect = (e)->
      console.log('end select', e.latlng)
      map.once 'click', beginSelect
      map.off 'mouseover', setTo

    beginSelect = (e)->
      from = e.latlng
      to = e.latlng
      console.log('begin select from', from, 'to', to)
      map.once 'click', endSelect
      map.on 'mousemove', setTo

    map.once 'click', beginSelect

#require("./webgl-example/blue-thing")

leaflet = require("leaflet")
require("./rectangle_editor")
$ = require("jquery")

tilesUrl = 'http://skogsmaskin.asuscomm.com:3001/kartverk/{z}/{x}/{y}.png'
tilesLayer = new L.TileLayer(tilesUrl);

$ ->
  map = new L.Map('map');
  map.addLayer(tilesLayer);
  map.setView(new L.LatLng(59.918893,10.739715), 10);

  eRect = new L.RectangleEditor([[59.9,10.7],[59.928893,10.769715]],);
  map.addLayer(eRect);

Remove initial attempt to draw rectangle


module.exports = (name = '') ->
  { argv }     = require 'optimist'
  express      = require 'express'
  cors         = require 'cors'
  helmet       = require 'helmet'
  app          = express()

  compression = require 'compression'
  bodyParser = require 'body-parser'

  app.use compression()
  app.use bodyParser.json()
  helmet.defaults app
  app.use cors()

  KONFIG = require 'koding-config-manager'
  app.get '/version', (req, res) ->
    res.send "#{KONFIG.version}"

  app.get '/healthCheck', (req, res) ->
    res.send "#{name} is running with version: #{KONFIG.version}"

  app.listen argv.p

module.exports = (name = '', options = {}) ->
  { argv }     = require 'optimist'
  express      = require 'express'
  cors         = require 'cors'
  helmet       = require 'helmet'
  app          = express()

  compression = require 'compression'
  bodyParser = require 'body-parser'

  app.use compression()
  app.use bodyParser.json()
  helmet.defaults app
  app.use cors()

  KONFIG = require 'koding-config-manager'
  app.get '/version', (req, res) ->
    res.send "#{KONFIG.version}"

  app.get '/healthCheck', (req, res) ->
    res.send "#{name} is running with version: #{KONFIG.version}"

  app.listen options.port

Add options parameter to artifact server module

Signed-off-by: Sonmez Kartal <d2d15c3d37396a5f2a09f8b42cf5f27d43a3fbde@koding.com>


_ = require 'underscore'
React = require 'react'
KatexMixin = require './katex-mixin'

module.exports = React.createClass
  displayName: 'ArbitraryHtmlAndMath'
  propTypes:
    className: React.PropTypes.string
    html: React.PropTypes.string
    block: React.PropTypes.bool

  mixins: [KatexMixin]
  render: ->
    classes = ['has-html']
    classes.push(@props.className) if @props.className
    classes = classes.join(' ')

    if @props.block

    else

  getHTMLFromProp: ->
    {html} = @props
    if html
      __html: html

  componentDidMount: ->
    # External links should open in a new window
    root = @getDOMNode()
    links = root.querySelectorAll('a')
    _.each links, (link) ->
      link.setAttribute('target', '_blank') unless link.getAttribute('href')?[0] is '#'
    # MathML should be rendered by MathJax (if available)
    window.MathJax?.Hub.Queue(['Typeset', MathJax.Hub, root])

_ = require 'underscore'
React = require 'react'
KatexMixin = require './katex-mixin'

module.exports = React.createClass
  displayName: 'ArbitraryHtmlAndMath'
  propTypes:
    className: React.PropTypes.string
    html: React.PropTypes.string
    block: React.PropTypes.bool

  mixins: [KatexMixin]
  render: ->
    classes = ['has-html']
    classes.push(@props.className) if @props.className
    classes = classes.join(' ')

    if @props.block

    else

  getHTMLFromProp: ->
    {html} = @props
    if html
      __html: html

  componentDidMount:  -> @updateDOMNode()
  componentDidUpdate: -> @updateDOMNode()

  # Perform manipulation on HTML contained inside the components node.
  updateDOMNode: ->
    # External links should open in a new window
    root = @getDOMNode()
    links = root.querySelectorAll('a')
    _.each links, (link) ->
      link.setAttribute('target', '_blank') unless link.getAttribute('href')?[0] is '#'

    # MathML should be rendered by MathJax (if available)
    window.MathJax?.Hub.Queue(['Typeset', MathJax.Hub, root])
    # Once MathML finishes processing, manually cleanup after it to prevent
    # React "Invariant Violation" exceptions.
    # MathJax calls Queued events in order, so this should always execute after typesetting
    window.MathJax?.Hub.Queue([ ->
      for nodeId in ['MathJax_Message', 'MathJax_Hidden', 'MathJax_Font_Test']
        el = document.getElementById(nodeId)
        break unless el # the elements won't exist if MathJax didn't do anything
        # Some of the elements are wrapped by divs without selectors under body
        # Select the parentElement unless it's already directly under body.
        el = el.parentElement unless el.parentElement is document.body
        el.parentElement.removeChild(el)
    ])

Clean up stray elements created by MathJax

Otherwise React will raise "Invariant Violation" exceptions when it notices them.

Also implement componentDidUpdate so the HTML will be reprocessed if it's content is updated


InquireViaPhoneModalView = require '../inquire_via_phone/index.coffee'

module.exports = ($el) ->
  $el
    .find '.js-artwork-partner-stub-inquire-via-phone'
    .click (e) ->
      e.preventDefault()
      @modal = new InquireViaPhoneModalView
        width: '500px'
        copy:
          login: 'Log in to Artsy to call gallery'
          signup: 'Create an Artsy account to call gallery'
          register: 'Create an Artsy account to call gallery'
        artistIds: JSON.parse(e.toElement.dataset.artist_ids)
        userData:
          partner: JSON.parse(e.toElement.dataset.partner)
  $el
    .find '.js-artwork-partner-stub-phone-toggle'
    .click (e) ->
      e.preventDefault()
      $(this).hide()
      $el
        .find '.js-artwork-partner-stub-phone-toggleable'
        .show()

InquireViaPhoneModalView = require '../inquire_via_phone/index.coffee'

module.exports = ($el) ->
  $el
    .find '.js-artwork-partner-stub-inquire-via-phone'
    .click (e) ->
      e.preventDefault()
      @modal = new InquireViaPhoneModalView
        width: '500px'
        copy:
          login: 'Log in to Artsy to call gallery'
          signup: 'Create an Artsy account to call gallery'
          register: 'Create an Artsy account to call gallery'
        artistIds: JSON.parse(e.target.dataset.artist_ids)
        userData:
          partner: JSON.parse(e.target.dataset.partner)
  $el
    .find '.js-artwork-partner-stub-phone-toggle'
    .click (e) ->
      e.preventDefault()
      $(this).hide()
      $el
        .find '.js-artwork-partner-stub-phone-toggleable'
        .show()

Fix opening a Inquire via Phone modal on FireFox and IEs


Package = require 'package'
fs = require 'fs'

module.exports =
class AtomPackage extends Package
  metadata: null
  keymapsDirPath: null
  autoloadStylesheets: true

  constructor: ->
    super
    @keymapsDirPath = fs.join(@path, 'keymaps')

  load: ->
    try
      @loadMetadata()
      @loadKeymaps()
      @loadStylesheets() if @autoloadStylesheets
      rootView?.activatePackage(@name, this) if require.resolve(@path)
    catch e
      console.warn "Failed to load package named '#{@name}'", e.stack
    this

  loadMetadata: ->
    if metadataPath = fs.resolveExtension(fs.join(@path, "package"), ['cson', 'json'])
      @metadata = fs.readObject(metadataPath)

  loadKeymaps: ->
    if keymaps = @metadata?.keymaps
      keymaps = keymaps.map (relativePath) =>
        fs.resolve(@keymapsDirPath, relativePath, ['cson', 'json', ''])
      keymap.load(keymapPath) for keymapPath in keymaps
    else
      keymap.loadDirectory(@keymapsDirPath)

  loadStylesheets: ->
    for stylesheetPath in @getStylesheetPaths()
      requireStylesheet(stylesheetPath)

  getStylesheetPaths: ->
    stylesheetDirPath = fs.join(@path, 'stylesheets')
    fs.list(stylesheetDirPath)

Package = require 'package'
fs = require 'fs'

module.exports =
class AtomPackage extends Package
  metadata: null
  keymapsDirPath: null
  autoloadStylesheets: true

  constructor: ->
    super
    @keymapsDirPath = fs.join(@path, 'keymaps')

  load: ->
    try
      @loadMetadata()
      @loadKeymaps()
      @loadStylesheets() if @autoloadStylesheets
      rootView?.activatePackage(@name, this) if require.resolve(@path)
    catch e
      console.warn "Failed to load package named '#{@name}'", e.stack
    this

  loadMetadata: ->
    if metadataPath = fs.resolveExtension(fs.join(@path, "package"), ['cson', 'json'])
      @metadata = fs.readObject(metadataPath)
    @metadata ?= {}

  loadKeymaps: ->
    if keymaps = @metadata.keymaps
      keymaps = keymaps.map (relativePath) =>
        fs.resolve(@keymapsDirPath, relativePath, ['cson', 'json', ''])
      keymap.load(keymapPath) for keymapPath in keymaps
    else
      keymap.loadDirectory(@keymapsDirPath)

  loadStylesheets: ->
    for stylesheetPath in @getStylesheetPaths()
      requireStylesheet(stylesheetPath)

  getStylesheetPaths: ->
    stylesheetDirPath = fs.join(@path, 'stylesheets')
    fs.list(stylesheetDirPath)

Set metadata to empty hash when missing

Set metadata to empty hash when missing


Helper = require('hubot-test-helper')
nock = require('nock')
expect = require('chai').expect

helper = new Helper('../src/codenames.coffee')

describe 'codenames', ->
  beforeEach ->
    nock('http://codenames.clivemurray.com')
      .get('/data/prefixes.json')
      .reply(200, [{title: 'black', attributes: ['colour']}])
      .get('/data/animals.json')
      .reply(200, [{title: 'bat', attributes: ['air', 'mammal']}])
    @room = helper.createRoom(http: false)

  afterEach ->
    @room.destroy()
    nock.cleanAll()

  context 'user asks for a suggestion', ->
    beforeEach (done) ->
      @room.user.say 'alice', 'hubot suggest a name'
      setTimeout done, 100

    it 'responds to suggestions', ->
      expect(@room.messages).to.eql [
        ['alice', 'hubot suggest a name'],
        ['hubot', '@alice How about BlackBat?'],
      ]

Helper = require('hubot-test-helper')
nock = require('nock')
expect = require('chai').expect

helper = new Helper('../src/codenames.coffee')

describe 'codenames', ->
  beforeEach ->
    nock('http://codenames.clivemurray.com')
      .get('/data/prefixes.json')
      .reply(200, [{title: 'black', attributes: ['colour']}])
      .get('/data/animals.json')
      .reply(200, [{title: 'bat', attributes: ['air', 'mammal']}])
    @room = helper.createRoom(httpd: false)

  afterEach ->
    nock.cleanAll()

  context 'user asks for a suggestion', ->
    beforeEach (done) ->
      @room.user.say 'alice', 'hubot suggest a name'
      setTimeout done, 100

    it 'responds to suggestions', ->
      expect(@room.messages).to.eql [
        ['alice', 'hubot suggest a name'],
        ['hubot', '@alice How about BlackBat?'],
      ]

Disable Hubot's HTTP server during the tests


defaults = require('lodash/defaults')
shuffle = require('lodash/shuffle')

class Game.LocationGrid
  constructor: (settings) ->
    settings = defaults settings,
      x: {}
      y: {}

    settings.x = defaults settings.x,
      start: 0
      steps: 1
      stepSize: 1
      avoid: []

    settings.y = defaults settings.y,
      start: 0
      steps: 1
      stepSize: 1
      avoid: []

    xs = @_coordList(settings.x)
    ys = @_coordList(settings.y)
    coords = []
    for y in ys
      for x in xs
        xPerc = (x - settings.x.start) / (settings.x.stepSize * settings.x.steps)
        yPerc = (y - settings.y.start) / (settings.y.stepSize * settings.y.steps)
        coords.push { x, y, xPerc, yPerc }

    @freeCoords = shuffle coords

  _coordList: (listSettings) ->
    avoid = listSettings.avoid?() ? listSettings.avoid
    for i in [0...listSettings.steps] when i not in avoid
      listSettings.start + (i * listSettings.stepSize)

  getLocation: ->
    @freeCoords.pop()

defaults = require('lodash/defaults')
shuffle = require('lodash/shuffle')

class Game.LocationGrid
  constructor: (settings) ->
    settings = defaults settings,
      x: {}
      y: {}

    settings.x = defaults settings.x,
      start: 0
      steps: 1
      stepSize: 1
      avoid: []

    settings.y = defaults settings.y,
      start: 0
      steps: 1
      stepSize: 1
      avoid: []

    xs = @_coordList(settings.x)
    ys = @_coordList(settings.y)
    coords = []
    for y in ys
      for x in xs
        xPerc = (x - settings.x.start) / (settings.x.stepSize * settings.x.steps)
        yPerc = (y - settings.y.start) / (settings.y.stepSize * settings.y.steps)
        coords.push { x, y, xPerc, yPerc }

    @freeCoords = shuffle coords

  _coordList: (listSettings) ->
    avoid = listSettings.avoid?() ? listSettings.avoid
    start = listSettings.start?() ? listSettings.start
    for i in [0...listSettings.steps] when i not in avoid
      start + (i * listSettings.stepSize)

  getLocation: ->
    @freeCoords.pop()

Allow function for startposition of grid


define ['lodash', './dialogue', 'text!./template-dialogue.html'], (L, Ctrl, View) ->

  controller = (console, scope, Modals, connectTo) ->

    console.log scope.data
    scope.listName = scope.data.name
    scope.ids = scope.data.ids
    scope.type = scope.data.type
    scope.service = root: scope.data.root ? scope.data.service.root

    scope.showTemplates = ->
      console.log "showing templates at #{ scope.service.root }"

      connect = connectTo scope.service.root
      injected =
        model: -> connect.then (s) -> s.fetchModel()
        templates: -> connect.then (s) -> s.fetchTemplates()
        service: -> connect
      if list = scope.listName?
        injected.list = -> connect.then (s) -> s.fetchList list
        injected.items = -> null
      else if ids = scope.ids?
        injected.list = -> null
        injected.items = -> {ids: scope.ids, type: scope.type}

      modalInstance = Modals.open
        template: View
        controller: Ctrl
        size: 'lg'
        resolve: injected

      modalInstance.result.then (selectedTemplate) ->
        step =
          title: "Ran #{ selectedTemplate.name } over #{ scope.listName }"
          tool: 'show-table'
          data:
            service:
              root: scope.service.root
            query: selectedTemplate
        scope.appendStep data: step

  ['$log', '$scope', '$modal', 'connectTo', controller]

define ['lodash', './dialogue', 'text!./template-dialogue.html'], (L, Ctrl, View) ->

  controller = (console, scope, Modals, connectTo) ->

    console.log scope.data
    scope.listName = scope.data.name
    scope.ids = scope.data.ids
    scope.type = scope.data.type
    scope.service = root: scope.data.root ? scope.data.service.root

    scope.showTemplates = ->
      console.log "showing templates at #{ scope.service.root }"

      connect = connectTo scope.service.root
      injected =
        model: -> connect.then (s) -> s.fetchModel()
        templates: -> connect.then (s) -> s.fetchTemplates()
        service: -> connect
      if scope.listName?
        injected.list = -> connect.then (s) -> s.fetchList scope.listName
        injected.items = -> null
      else if scope.ids?
        injected.list = -> null
        injected.items = -> {ids: scope.ids, type: scope.type}

      modalInstance = Modals.open
        template: View
        controller: Ctrl
        size: 'lg'
        resolve: injected

      modalInstance.result.then (selectedTemplate) ->
        step =
          title: "Ran #{ selectedTemplate.name } over #{ scope.listName }"
          tool: 'show-table'
          data:
            service:
              root: scope.service.root
            query: selectedTemplate
        scope.appendStep data: step

  ['$log', '$scope', '$modal', 'connectTo', controller]

Fix error due to assignment in test.

Fix error due to assignment in test.


class KeypathObserver
  constructor: (@view, @model, @keypath, @callback) ->
    @interfaces = (k for k, v of @view.adapters)
    @objectPath = []
    @tokens = Rivets.KeypathParser.parse @keypath, @interfaces, @view.config.rootInterface
    @root = @tokens.shift()
    @key = @tokens.pop()
    @target = @realize()

  update: =>
    unless (next = @realize()) is @target
      @callback @target = next

  realize: =>
    current = @model

    for token, index in @tokens
      if @objectPath[index]?
        if current isnt prev = @objectPath[index]
          @view.adapters[token.interface].unsubscribe prev, token.path, @update
          @view.adapters[token.interface].subscribe current, token.path, @update
          @objectPath[index] = current
      else
        @view.adapters[token.interface].subscribe current, token.path, @update
        @objectPath[index] = current

      current = @view.adapters[token.interface].read current, token.path

    current

class KeypathObserver
  constructor: (@view, @model, @keypath, @callback) ->
    @parse()
    @objectPath = []
    @target = @realize()

  parse: =>
    interfaces = (k for k, v of @view.adapters)

    if @keypath[0] in interfaces
      root = @keypath[0]
      path = @keypath.substr 1
    else
      root = @view.config.rootInterface
      path = @keypath

    @tokens = Rivets.KeypathParser.parse path, interfaces, root
    @key = @tokens.pop()

  update: =>
    unless (next = @realize()) is @target
      prev = @target
      @target = next
      @callback @, prev

  realize: =>
    current = @model

    for token, index in @tokens
      if @objectPath[index]?
        if current isnt prev = @objectPath[index]
          @view.adapters[token.interface].unsubscribe prev, token.path, @update
          @view.adapters[token.interface].subscribe current, token.path, @update
          @objectPath[index] = current
      else
        @view.adapters[token.interface].subscribe current, token.path, @update
        @objectPath[index] = current

      current = @view.adapters[token.interface].read current, token.path

    current

Update KeypathObserver to have better state management, passing in the previous target to the callback for unbinding.


###*
IndexController. Responsible for the index view.
###
app.controller "IndexController", ($scope, $location) ->
  $scope.whatsMyName = "Welcome my friend"
  return

###*
IndexController. Responsible for the index view.
###
app.controller "IndexController", ($scope) ->
  $scope.whatsMyName = "Welcome my friend"
  return

Index does not need $location

Index does not need $location


#= require app/models/movie

class App.Collections.Movies extends Backbone.Collection
  model: App.Models.Movie
  url: '/movies'

  comparator: (model) ->
    - model.get('opening_weekend')

Sort Movies by opening weekend totals

Sort Movies by opening weekend totals

And with very little code, we can have our list of Movies sort itself by
descending opening weekend totals.


class @OverlayDimensions
  minMargin: 10

  constructor: (overlayContent) ->
    @overlayContent = overlayContent
    @totalMargin = @minMargin * 2

    @maxWidth = ->
      $(window).width() - @totalMargin

  width: ->
    @overlayContent.width()

  height: ->
    @overlayContent.height()

  margin: ->
    margin = (@maxWidth() - @width()) / 2
    return margin unless margin < 1
    @minMargin

  top: ->
    ($(window).height() - @height()) / 2

  closeButtonLeftPosition: ->
    @width() - @closeButtonOffset

class @OverlayDimensions
  minMargin: 10

  constructor: (overlayContent) ->
    @overlayContent = overlayContent
    @totalMargin = @minMargin * 2

    @maxWidth = ->
      $(window).width() - @totalMargin

  width: ->
    @overlayContent.width()

  height: ->
    @overlayContent.height()

  margin: ->
    margin = (@maxWidth() - @width()) / 2
    return margin unless margin < 1
    @minMargin

  top: ->
    window.scrollY + ($(window).height() - @height()) / 2

  closeButtonLeftPosition: ->
    @width() - @closeButtonOffset

Fix overlay positioning when scrolled down


class window.RelatedChannelsView extends Backbone.Marionette.CompositeView
  className: "related-channels-for-channel"

  template: "channels/related_channels",

  itemViewContainer: "ul",

  itemView: RelatedChannelView

  itemViewOptions: => addToCollection : @addToCollection

  templateHelpers: =>
    is_mine: this.model.get('created_by').username == currentUser.get('username')

  initialize: (options) ->
    @addToCollection = this.model.subchannels()
    @collection =  collectionDifference(new ChannelList, 'id', this.model.relatedChannels(), this.addToCollection, [@model]);

  showEmptyView: => this.$el.hide()
  closeEmptyView: => this.$el.show()

class window.RelatedChannelsView extends Backbone.Marionette.CompositeView
  className: "related-channels-for-channel"

  template: "channels/related_channels",

  itemViewContainer: "ul",

  itemView: RelatedChannelView

  itemViewOptions: => addToCollection : @addToCollection

  templateHelpers: =>
    is_mine: @model.get('created_by').username == currentUser.get('username')

  initialize: (options) ->
    @addToCollection = @model.subchannels()
    @collection =  collectionDifference(new ChannelList, 'id', @model.relatedChannels(), @addToCollection, [@model]);

  showEmptyView: => @$el.hide()
  closeEmptyView: => @$el.show()

Use @ in stead of this.

Use @ in stead of this.


Models =

  'models/cube.js':
    name: 'Cube'

  'models/sphere.js':
    name: 'Shere'

  'models/torus.js':
    name: 'Torus'

  'models/dragon.js':
    name: 'Dragon'
    scale: 1.2

  'models/hexmkii.js':
    name: 'Hex MKII'
    scale: 0.01

  'models/suzanne_low.js':
    name: 'Suzanne (low)'

  'models/suzanne_high.js':
    name: 'Suzanne (high)'

@shdr ||= {}
@shdr.Models = Models

Models =

  'models/cube.js':
    name: 'Cube'

  'models/sphere.js':
    name: 'Sphere'

  'models/torus.js':
    name: 'Torus'

  'models/dragon.js':
    name: 'Dragon'
    scale: 1.2

  'models/hexmkii.js':
    name: 'Hex MKII'
    scale: 0.01

  'models/suzanne_low.js':
    name: 'Suzanne (low)'

  'models/suzanne_high.js':
    name: 'Suzanne (high)'

@shdr ||= {}
@shdr.Models = Models

Update Shere to Sphere (typo)

Update Shere to Sphere (typo)

Corrects a typo in the editor interface


class NCopyUrlView extends JView

  constructor: ->
    super

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

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

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

    @inputUrl.setValue @publicPath

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

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

  pistachio:->
    hasNoPublicPath = @publicPath is @path

    if hasNoPublicPath
      """

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

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

class NCopyUrlView extends JView

  constructor: ->
    super

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

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

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

    @inputUrl.setValue @publicPath

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

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

  pistachio:->
    hasNoPublicPath = @publicPath is @path

    if hasNoPublicPath
      """

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

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

Fix copy file url issue

Fix copy file url issue


Set accept-cookies cookie to expire in the very far future (year 9.999).

It seems that Chrome ignores this date and instead sets the cookie to expire on 'Tue, 19 Jan 2038 03:14:07 GMT', which is the maximum date since Epoch expressed as an amount of seconds stored in a signed 32-bit integer. This shouldn't pose no problems.

Also set the Path explicitly to '/'. This should not be a problem, but it's better to be explicit.


Crafty.c 'ShootOnSight',
  remove: ->
    @unbind('GameLoop', @_checkForShot)

  shootOnSight: (options) ->
    @shootConfig =  _.defaults(options,
      targetType: 'PlayerControlledShip'
      sightAngle: 10
      shootWhenHidden: no
      cooldown: 800
    )

    @bind('GameLoop', @_checkForShot)

  _checkForShot: (fd) ->
    if @lastShotAt?
      @lastShotAt += fd.dt
      return if @lastShotAt < @shootConfig.cooldown

    self = this
    Crafty(@shootConfig.targetType).each ->
      angle = Math.atan2(self.y - @y, self.x - @x)
      angle *= 180 / Math.PI
      angle = (angle + 360) % 360
      self._shoot(angle) if Math.abs(angle - self.rotation) < self.shootConfig.sightAngle

  _shoot: (angle) ->
    return if @hidden and !@shootConfig.shootWhenHidden
    @lastShotAt = 0
    wo = @weaponOrigin ? [0, 0]
    wo[0] *= (@scale ? 1)
    wo[1] *= (@scale ? 1)

    @shootConfig.projectile(wo[0] + @x, wo[1] + @y, angle)

Crafty.c 'ShootOnSight',
  remove: ->
    @unbind('GameLoop', @_checkForShot)

  shootOnSight: (options) ->
    @shootConfig =  _.defaults(options,
      targetType: 'PlayerControlledShip'
      sightAngle: 10
      shootWhenHidden: no
      cooldown: 800
    )

    @bind('GameLoop', @_checkForShot)

  _checkForShot: (fd) ->
    if @lastShotAt?
      @lastShotAt += fd.dt
      return if @lastShotAt < @shootConfig.cooldown

    self = this
    Crafty(@shootConfig.targetType).each ->
      angle = Math.atan2(self.y - @y, self.x - @x)
      angle *= 180 / Math.PI
      angle += 180 if self.xFlipped
      angle = (angle + 360) % 360

      if Math.abs(angle - self.rotation) < self.shootConfig.sightAngle
        angle += 180 if self.xFlipped
        angle = (angle + 360) % 360
        self._shoot(angle)

  _shoot: (angle) ->
    return if @hidden and !@shootConfig.shootWhenHidden
    @lastShotAt = 0
    wo = @weaponOrigin ? [0, 0]
    wo[0] *= (@scale ? 1)
    wo[1] *= (@scale ? 1)

    @shootConfig.projectile(wo[0] + @x, wo[1] + @y, angle)

Fix shooting orientation when enemy turns around


Backbone = require 'backbone'
template = -> require('./index.jade') arguments...

module.exports = class HomePathFieldView extends Backbone.View
  className: 'Fieldset'

  events:
    'change select': 'select'

  initialize: ->
    @listenTo @model, 'change:home_path', @render

  select: (e) ->
    $target = $(e.currentTarget)
    @model.set 'home_path', $target.val()

  render: ->
    @$el.html template
      user: @model

    this

Backbone = require 'backbone'
analytics = require '../../../../lib/analytics.coffee'
template = -> require('./index.jade') arguments...

module.exports = class HomePathFieldView extends Backbone.View
  className: 'Fieldset'

  events:
    'change select': 'select'

  initialize: ->
    @listenTo @model, 'change:home_path', @render

  select: (e) ->
    $target = $(e.currentTarget)
    @model.set 'home_path', $target.val()

    analytics.track.submit 'User switched home path',
      label: 'Home path'
      value: $target.text()

  render: ->
    @$el.html template
      user: @model

    this

Add analytics to home path

Add analytics to home path


Math._random ?= Math.random

pesudoRandomValues = []

pesudoRandom = ->
  pesudoRandomValues.shift() ? Math._random()

pesudoRandom.MIN_VALUE = 0
pesudoRandom.MAX_VALUE = 0.99999999

pesudoRandom.push = ->
  pesudoRandomValues.push arguments...
  @

pesudoRandom.set = ->
  @clear()
    .push arguments...

pesudoRandom.clear = ->
  pesudoRandomValues = []
  @

pesudoRandom.attach = ->
  beforeEach -> pesudoRandom.clear()
  @

module.exports = Math.random = pesudoRandom

Modify EOL code from CRLF to LF

Modify EOL code from CRLF to LF


React = require 'react'
ClassificationsRibbon = require '../../components/classifications-ribbon'
PromiseRenderer = require '../../components/promise-renderer'
ProjectIcon = require '../../components/project-icon'

module.exports = React.createClass
  getDefaultProps: ->
    user: null

  render: ->

Your contribution stats

Users can only view their own stats.

      {if @props.profileUser is @props.user
        # TODO: Braces after "style" here confuse coffee-reactify. That's really annoying.
        centered = textAlign: 'center'

<ClassificationsRibbon user={@props.profileUser} />

          <PromiseRenderer promise={ClassificationsRibbon::getAllProjectPreferences @props.profileUser} then={(projectPreferences) =>

              {projectPreferences.map (projectPreference) =>
                <PromiseRenderer key={projectPreference.id} promise={projectPreference.get 'project'} catch={null} then={(project) =>
                  if project?

                      <ProjectIcon project={project} badge={projectPreference.activity_count} />

                  else
                    null
                } />}

          } />

      else

Sorry, we can’t show you stats for {@props.user.display_name}.
}

React = require 'react'
ClassificationsRibbon = require '../../components/classifications-ribbon'
PromiseRenderer = require '../../components/promise-renderer'
ProjectIcon = require '../../components/project-icon'

module.exports = React.createClass
  getDefaultProps: ->
    user: null

  render: ->

Your contribution stats

Users can only view their own stats.

      {if @props.profileUser is @props.user
        # TODO: Braces after "style" here confuse coffee-reactify. That's really annoying.
        centered = textAlign: 'center'

<ClassificationsRibbon user={@props.profileUser} />

          <PromiseRenderer promise={ClassificationsRibbon::getAllProjectPreferences @props.profileUser} then={(projectPreferences) =>

              {projectPreferences.map (projectPreference) =>
                <PromiseRenderer key={projectPreference.id} promise={projectPreference.get 'project'} catch={null} then={(project) =>
                  if project?

                      <ProjectIcon project={project} badge={projectPreference.activity_count} />

                  else
                    null
                } />}

          } />

      else

Sorry, we can’t show you stats for {@props.profileUser.display_name}.
}

Add Correct Display Name when Visiting Stats of Other User


class AsciiIo.DataUnpacker
  unpack: (base64BzippedData, callback) ->
    bzippedData = atob base64BzippedData

    if window.Worker
      worker = new Worker(window.unpackWorkerPath)
      worker.onmessage = (event) => callback event.data
      worker.postMessage bzippedData
    else
      data = ArchUtils.bz2.decode bzippedData
      callback data

class AsciiIo.DataUnpacker
  unpack: (base64BzippedData, callback) ->
    data = atob base64BzippedData

    if data[0] == 'B' and data[1] == 'Z'
      if window.Worker
        worker = new Worker(window.unpackWorkerPath)
        worker.onmessage = (event) => callback event.data
        worker.postMessage data
      else
        data = ArchUtils.bz2.decode data
        callback data
    else
      data

Handle both bzipped and non-bzipped data in DataUnpacker


class Written.Cursor
  constructor: (element, selection) ->
    @element = ->
      element
    @selection = selection
    children = Array.prototype.slice.call(@element().children, 0)
    @offset = selection.focusOffset

    node = selection.focusNode

    while node && !children.includes(node)
      parent = node.parentElement

      if parent
        child = node.previousSibling

        while child
          @offset += child.toString().length
          child = child.previousSibling

        if node.nodeName == 'LI'
          this.offset += Array.prototype.indexOf.call(parent.children, node)

      node = parent

    for child in @element().children
      if child == node
        break
      @offset += child.toString().length
      @offset += 1

    @currentNode = ->
      node

  offsetAt: (node) ->
    offset = @offset

    element = @element().firstElementChild
    while element && element != node
      offset -= element.toString().length
      element = element.nextElementSibling

    offset

  focus: (offset, node) =>
    if offset is undefined
      offset = @offset

    if node is undefined
      node = @element().firstElementChild

    while node.nextElementSibling && node.toString().length < offset
      offset -= node.toString().length + 1
      node = node.nextElementSibling

    range = node.getRange(Math.min(offset, node.toString().length), document.createTreeWalker(node, NodeFilter.SHOW_TEXT))
    @selection.removeAllRanges()
    @selection.addRange(range)

class Written.Cursor
  constructor: (element, selection) ->
    @element = ->
      element
    @selection = selection
    children = Array.prototype.slice.call(@element().children, 0)
    @offset = selection.focusOffset

    node = selection.focusNode

    while node && !children.includes(node)
      parent = node.parentElement

      if parent
        child = node.previousSibling

        while child
          @offset += child.toString().length
          child = child.previousSibling

        if node instanceof HTMLLIElement
          this.offset += Array.prototype.indexOf.call(parent.children, node)

      node = parent

    for child in @element().children
      if child == node
        break
      @offset += child.toString().length
      @offset += 1

    @currentNode = ->
      node

  offsetAt: (node) ->
    offset = @offset

    element = @element().firstElementChild
    while element && element != node
      offset -= element.toString().length
      element = element.nextElementSibling

    offset

  focus: (offset, node) =>
    if offset is undefined
      offset = @offset

    if node is undefined
      node = @element().firstElementChild

    while node.nextElementSibling && node.toString().length < offset
      offset -= node.toString().length + 1
      node = node.nextElementSibling

    range = node.getRange(Math.min(offset, node.toString().length), document.createTreeWalker(node, NodeFilter.SHOW_TEXT))
    @selection.removeAllRanges()
    @selection.addRange(range)

Use instanceof instead of nodeName for consistency's sake


# Include lib.
for file in ['buildable', 'arrayFilters', 'underscore']
  Ti.include('lib/' + file + '.js')

# Include expansions.
for file in ['citiesAndKnights']
  Ti.include('models/expansions/' + file + '.js')

# Include models.
for file in [
  'city',
  'game',
  'largestArmy',
  'longestRoad',
  'player',
  'settlement',
  'soldier',
  'developmentCardVictoryPoint',
  'gameSettings',
  'knight',
  'barbarians',
  'catanDefense',
  'barbariansBattleOutcome',
  'defenderOfCatanCard',
]
  Ti.include('models/' + file + '.js')

# Include lib.
for file in ['buildable', 'arrayFilters']
  Ti.include('lib/' + file + '.js')

# Include vendor.
for file in ['underscore']
  Ti.include('vendor/' + file + '.js')

# Include expansions.
for file in ['citiesAndKnights']
  Ti.include('models/expansions/' + file + '.js')

# Include models.
for file in [
  'city',
  'game',
  'largestArmy',
  'longestRoad',
  'player',
  'settlement',
  'soldier',
  'developmentCardVictoryPoint',
  'gameSettings',
  'knight',
  'barbarians',
  'catanDefense',
  'barbariansBattleOutcome',
  'defenderOfCatanCard',
]
  Ti.include('models/' + file + '.js')

Fix bug with including underscore.js.

Fix bug with including underscore.js.


Hummingbird.AnimeRoute = Ember.Route.extend
  model: (params) ->
    @store.find('fullAnime', params.id)

  saveLibraryEntry: (libraryEntry) ->
    anime = @currentModel
    Messenger().expectPromise (-> libraryEntry.save()),
      progressMessage: "Saving " + anime.get('canonicalTitle') + "..."
      successMessage: "Saved " + anime.get('canonicalTitle') + "!"

  actions:
    toggleFavorite: ->
      alert('Need to be signed in') unless @get('currentUser.isSignedIn')
      libraryEntry = @currentModel.get('libraryEntry')
      libraryEntry.set 'isFavorite', not libraryEntry.get('isFavorite')
      @saveLibraryEntry libraryEntry

    removeFromLibrary: ->
      anime = @currentModel
      libraryEntry = anime.get('libraryEntry')
      Messenger().expectPromise (-> libraryEntry.destroyRecord()),
        progressMessage: "Removing " + anime.get('canonicalTitle') + " from your library..."
        successMessage: "Removed " + anime.get('canonicalTitle') + " from your library!"

    setLibraryStatus: (newStatus) ->
      libraryEntry = @currentModel.get('libraryEntry')
      if libraryEntry
        libraryEntry.set 'status', newStatus
      else
        libraryEntry = @store.createRecord 'libraryEntry',
          status: newStatus
          anime: @currentModel
      @saveLibraryEntry libraryEntry

    setLibraryRating: (newRating) ->
      libraryEntry = @currentModel.get('libraryEntry')
      libraryEntry.set 'rating', newRating
      @saveLibraryEntry libraryEntry

Hummingbird.AnimeRoute = Ember.Route.extend
  model: (params) ->
    @store.find('fullAnime', params.id)

  saveLibraryEntry: (libraryEntry) ->
    anime = @currentModel
    Messenger().expectPromise (-> libraryEntry.save()),
      progressMessage: "Saving " + anime.get('canonicalTitle') + "..."
      successMessage: "Saved " + anime.get('canonicalTitle') + "!"

  actions:
    toggleFavorite: ->
      alert('Need to be signed in') unless @get('currentUser.isSignedIn')
      libraryEntry = @currentModel.get('libraryEntry')
      libraryEntry.set 'isFavorite', not libraryEntry.get('isFavorite')
      @saveLibraryEntry libraryEntry

    removeFromLibrary: ->
      anime = @currentModel
      libraryEntry = anime.get('libraryEntry')
      Messenger().expectPromise (-> libraryEntry.destroyRecord()),
        progressMessage: "Removing " + anime.get('canonicalTitle') + " from your library..."
        successMessage: "Removed " + anime.get('canonicalTitle') + " from your library!"

    setLibraryStatus: (newStatus) ->
      libraryEntry = @currentModel.get('libraryEntry')
      if libraryEntry
        libraryEntry.set 'status', newStatus
      else
        libraryEntry = @store.createRecord 'libraryEntry',
          status: newStatus
          anime: @currentModel
      if newStatus == "Completed"
        libraryEntry.set 'episodesWatched', libraryEntry.get('anime.episodeCount')
      @saveLibraryEntry libraryEntry

    setLibraryRating: (newRating) ->
      libraryEntry = @currentModel.get('libraryEntry')
      libraryEntry.set 'rating', newRating
      @saveLibraryEntry libraryEntry

Mark all episodes as viewed from anime pages.


behaviours = {}

share.attach = attach = (behaviour, args...) ->
  if _.isString behaviour
    options = behaviours[behaviour].options
    behaviour = behaviours[behaviour].behaviour

  if _.isFunction behaviour
    context =
      collection: @
      options: options or {}

    behaviour.apply context, args

  else
    console.warn 'Behaviour not found'

  return

class CollectionBehaviours

  @attach: (collection, args...) ->
    attach.apply collection, args

  @configure: (name, options) ->
    if name of behaviours
      behaviours[name].options = options

    else
      console.warn 'Configure failed, behaviour not found'

  @define: (name, behaviour, options) ->
    if name of behaviours and not options?.replace
      console.warn 'Behaviour already defined, use {replace: true} to override'

    else
      behaviours[name] =
        behaviour: behaviour

behaviours = {}

share.attach = attach = (behaviour, args...) ->
  if _.isString behaviour
    options = behaviours[behaviour].options
    behaviour = behaviours[behaviour].behaviour

  if _.isFunction behaviour
    context =
      collection: @
      options: options or {}

    behaviour.apply context, args

  else
    console.warn 'Behaviour not found'

  return

class CollectionBehaviours

  @attach: (collections, args...) ->
    if Match.test collections, Mongo.Collection
      collections = [collections]

    attach.apply collection, args for collection in collections

  @configure: (name, options) ->
    if name of behaviours
      behaviours[name].options = options

    else
      console.warn 'Configure failed, behaviour not found'

  @define: (name, behaviour, options) ->
    if name of behaviours and not options?.replace
      console.warn 'Behaviour already defined, use {replace: true} to override'

    else
      behaviours[name] =
        behaviour: behaviour

Support attaching behaviour to multiple collections


#= require ./abstract_attribute_binding

class Batman.DOM.EventBinding extends Batman.DOM.AbstractAttributeBinding
  onlyObserve: Batman.BindingDefinitionOnlyObserve.Data
  bindImmediately: false

  constructor: ->
    super

    callback = =>
      func = @get('filteredValue')
      target = @view.targetForKeypathBase(@functionPath)
      return func?.apply(target, arguments)

    if attacher = Batman.DOM.events[@attributeName]
      attacher(@node, callback, @view)
    else
      Batman.DOM.events.other(@node, @attributeName, callback, @view)

  _unfilteredValue: (key) ->
    if not @functionName and (index = key.lastIndexOf('.')) != -1
      @functionPath = key.substr(0, index)
      @functionName = key.substr(index + 1)
    else
      @functionPath = key

    value = super(@functionPath)
    if @functionName
      value?[@functionName]
    else
      value

#= require ./abstract_attribute_binding

class Batman.DOM.EventBinding extends Batman.DOM.AbstractAttributeBinding
  onlyObserve: Batman.BindingDefinitionOnlyObserve.Data
  bindImmediately: false

  constructor: ->
    super

    callback = =>
      func = @get('filteredValue')
      target = @view.targetForKeypathBase(@functionPath || @unfilteredKey)
      if target && @functionPath
        target = Batman.get(target, @functionPath)

      return func?.apply(target, arguments)

    if attacher = Batman.DOM.events[@attributeName]
      attacher(@node, callback, @view)
    else
      Batman.DOM.events.other(@node, @attributeName, callback, @view)

  _unfilteredValue: (key) ->
    @unfilteredKey = key
    if not @functionName and (index = key.lastIndexOf('.')) != -1
      @functionPath = key.substr(0, index)
      @functionName = key.substr(index + 1)

    value = super(@functionPath || key)
    if @functionName
      value?[@functionName]
    else
      value

Fix event bindings for variable-length keypaths for realsies.


compile = (template, type) ->
  if 0 <= index = template.indexOf ' '
    tmpl = template.substr 0, index
    attrs = template.substr index
  else
    tmpl = template

  [ name, classes... ] = tmpl.split /\.+/g
  if 0 is name.indexOf '#'
    id = name.substr 1
    name = 'div'

  if name is ''
    name = 'div'

  beginTag = "<#{name}"
  beginTag += " id='#{id}'" if id
  beginTag += " class='#{classes.join ' '}'" if classes.length
  beginTag += attrs if attrs
  beginTag += ">"
  closeTag = "</#{name}>"

  if type is '='
    (content) -> beginTag + (content or '') + closeTag
  else if type is '+'
    (content, arg0) -> #TODO add more args as necessary
      tag = replace beginTag, '{0}', arg0
      tag + content + closeTag
  else
    (contents) -> beginTag + (contents.join '') + closeTag

_templateCache = {}
Flow.HTML =
  template: (templates...) ->
    for template in templates
      if cached = _templateCache[template]
        cached
      else
        type = charAt template, 0
        if type is '=' or type is '+'
          _templateCache[template] = compile (template.substr 1), type
        else
          _templateCache[template] = compile template
  render: (name, html) ->
    el = document.createElement name
    if html
      if isString html
        el.innerHTML = html
      else
        el.appendChild html
    el

compile = (template, type) ->
  if 0 <= index = template.indexOf ' '
    tmpl = template.substr 0, index
    attrs = template.substr index
  else
    tmpl = template

  [ name, classes... ] = tmpl.split /\.+/g
  if 0 is name.indexOf '#'
    id = name.substr 1
    name = 'div'

  if name is ''
    name = 'div'

  beginTag = "<#{name}"
  beginTag += " id='#{id}'" if id
  beginTag += " class='#{classes.join ' '}'" if classes.length
  beginTag += attrs if attrs
  beginTag += ">"
  closeTag = "</#{name}>"

  if type is '='
    (content) -> beginTag + (if content isnt null and content isnt undefined then content else '') + closeTag
  else if type is '+'
    (content, arg0) -> #TODO add more args as necessary
      tag = replace beginTag, '{0}', arg0
      tag + content + closeTag
  else
    (contents) -> beginTag + (contents.join '') + closeTag

_templateCache = {}
Flow.HTML =
  template: (templates...) ->
    for template in templates
      if cached = _templateCache[template]
        cached
      else
        type = charAt template, 0
        if type is '=' or type is '+'
          _templateCache[template] = compile (template.substr 1), type
        else
          _templateCache[template] = compile template
  render: (name, html) ->
    el = document.createElement name
    if html
      if isString html
        el.innerHTML = html
      else
        el.appendChild html
    el

Handle zeros properly during templating

Handle zeros properly during templating


{ MEDIUM, CURRENT_USER } = require('sharify').data
AuthModalView = require '../../../components/auth_modal/view.coffee'
CTABarView = require '../../../components/cta_bar/view.coffee'

module.exports = (artist) ->
  return unless MEDIUM is 'search' and not CURRENT_USER?

  name = 'artist_cta'
  ctaHeadline = "Get updates on new shows and works by #{artist.get('name')}."
  modalCopy = "Get the latest new shows and works by #{artist.get('name')}."

  ctaBarView = new CTABarView
    headline: ctaHeadline
    name: name
    persist: true
    modalOptions:
      copy: modalCopy
      destination: "#{artist.href()}/follow"

  unless ctaBarView.previouslyDismissed()
    $('body').append ctaBarView.render().$el

    $('#artist-page-section-works').waypoint (direction) ->
      ctaBarView.transitionIn() if direction is 'down'
    , { offset: 'bottom-in-view' }

{ MEDIUM, CURRENT_USER } = require('sharify').data
AuthModalView = require '../../../components/auth_modal/view.coffee'
CTABarView = require '../../../components/cta_bar/view.coffee'

module.exports = (artist) ->
  return unless MEDIUM is 'search' and not CURRENT_USER?

  name = 'artist_cta'
  ctaHeadline = "Get updates on new shows and works by #{artist.get('name')}."
  modalCopy = "Get the latest new shows and works by #{artist.get('name')}."

  ctaBarView = new CTABarView
    headline: ctaHeadline
    name: name
    mode: ''
    persist: true
    modalOptions:
      copy: modalCopy
      destination: "#{artist.href()}/follow"

  unless ctaBarView.previouslyDismissed()
    $('body').append ctaBarView.render().$el

    $('#artist-page-section-works').waypoint (direction) ->
      ctaBarView.transitionIn() if direction is 'down'
    , { offset: 'bottom-in-view' }

Use correct CTA Bar on New Artist Pages

Use correct CTA Bar on New Artist Pages


storage = require 'node-persist'
storage.initSync()
config = storage.getItemSync 'config'

module.exports =
  getConfig: ->
    defaultDelay: 3
    defaultDuration: 0.5
    defaultTransition: "none"
    checkCycles: 20

Set check cycles to 2

Set check cycles to 2