React             = require 'react/addons'
Editable          = require '../high_order/editable'

List              = require '../common/list'
Revision          = require './revision'
RevisionStore     = require '../../stores/revision_store'
ServerActions     = require '../../actions/server_actions'

getState = ->
  revisions: RevisionStore.getModels()

RevisionList = React.createClass(
  displayName: 'RevisionList'
  render: ->
    elements = @props.revisions.map (revision) =>
      <Revision revision={revision} key={revision.id} {...@props} />

    keys =
      'rating_num':
        'label': I18n.t('revision.class')
        'desktop_only': true
      'title':
        'label': I18n.t('revision.title')
        'desktop_only': false
      'edited_by':
        'label': I18n.t('revision.edited_by')
        'desktop_only': true
      'characters':
        'label': I18n.t('revision.chars_added')
        'desktop_only': true
      'date':
        'label': I18n.t('revision.date_time')
        'desktop_only': true
        'info_key': 'revisions.time_doc'

    <List
      elements={elements}
      keys={keys}
      table_key='revisions'
      store={RevisionStore}
    />
)

module.exports = Editable(RevisionList, [RevisionStore], ServerActions.saveRevisions, getState)

React             = require 'react/addons'
Editable          = require '../high_order/editable'

List              = require '../common/list'
Revision          = require './revision'
RevisionStore     = require '../../stores/revision_store'
ServerActions     = require '../../actions/server_actions'

getState = ->
  revisions: RevisionStore.getModels()

RevisionList = React.createClass(
  displayName: 'RevisionList'
  render: ->
    elements = @props.revisions.map (revision) =>
      <Revision revision={revision} key={revision.id} {...@props} />

    keys =
      'rating_num':
        'label': I18n.t('revisions.class')
        'desktop_only': true
      'title':
        'label': I18n.t('revisions.title')
        'desktop_only': false
      'edited_by':
        'label': I18n.t('revisions.edited_by')
        'desktop_only': true
      'characters':
        'label': I18n.t('revisions.chars_added')
        'desktop_only': true
      'date':
        'label': I18n.t('revisions.date_time')
        'desktop_only': true
        'info_key': 'revisions.time_doc'

    <List
      elements={elements}
      keys={keys}
      table_key='revisions'
      store={RevisionStore}
    />
)

module.exports = Editable(RevisionList, [RevisionStore], ServerActions.saveRevisions, getState)

Fix message keys in RevisionList

Fix message keys in RevisionList

Resolves #471


module.exports = (e, dataTransfer, iconAndSize) ->
  image = iconAndSize[0]
  if image instanceof Image && !image.complete
    # Early exit; not loaded image will not work and more importantly it craches Safari 6
    return

  if dataTransfer.setDragImage
    if image instanceof HTMLElement
      # Chrome hack; position the drag element where the UA are suppose to position it.
      # This since UA's requires it to be in the DOM and Chrome requires it to be in view…
      iconAndSize[0] = image = image.cloneNode true
      image.style.position = "absolute"
      image.style.left = "#{e.clientX - iconAndSize[1]}px"
      image.style.top = "#{e.clientY - iconAndSize[2]}px"
      document.body.appendChild image
      # Remove it from the DOM at the next run loop
      setTimeout(->
        document.body.removeChild image
      , 0)

    dataTransfer.setDragImage.apply dataTransfer, iconAndSize

module.exports = (e, dataTransfer, iconAndSize) ->
  image = iconAndSize[0]
  if image instanceof Image && !image.complete
    # Early exit; not loaded image will not work and more importantly it craches Safari 6
    return

  if dataTransfer.setDragImage
    if image instanceof HTMLElement
      # Chrome hack; position the drag element where the UA are suppose to position it.
      # This since UA's requires it to be in the DOM and Chrome requires it to be in view…
      iconAndSize[0] = image = image.cloneNode true
      image.style.position = "fixed"
      image.style.left = "#{e.clientX - iconAndSize[1]}px"
      image.style.top = "#{e.clientY - iconAndSize[2]}px"
      document.body.appendChild image
      # Remove it from the DOM at the next run loop
      setTimeout(->
        document.body.removeChild image
      , 0)

    dataTransfer.setDragImage.apply dataTransfer, iconAndSize

Update chrome hack to use position: fixed

For reasons unknown, we were experiencing UI bugginess with the drag image
after updating jQuery.

Chaging the position to fixed instead of absolute fixed the problem.


# Public: GrammarUtils.OperatingSystem - a module which exposes different
# platform related helper functions.
module.exports =
  isDarwin: ->
    @platform() is 'darwin'

  isWindows: ->
    @platform() is 'win32'

  isLinux: ->
    @platform() is 'linux'

  platform: ->
    process.platform

  release: ->
    process.release

Enable the reading of the OS release number


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

describe 'third-party module', ->
  fixtures = path.join __dirname, 'fixtures'

  xdescribe 'unzip', ->
    unzip = require 'unzip'

    it 'fires close event', (done) ->
      fs.createReadStream(path.join(fixtures, 'zip', 'a.zip'))
        .pipe(unzip.Parse())
        .on('close', done)

  describe 'runas', ->
    it 'can be required in renderer', ->
      require 'runas'

    it 'can be required in node binary', (done) ->
      runas = path.join fixtures, 'module', 'runas.js'
      child = require('child_process').fork runas
      child.on 'message', (msg) ->
        assert.equal msg, 'ok'
        done()

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

describe 'third-party module', ->
  fixtures = path.join __dirname, 'fixtures'

  describe 'runas', ->
    it 'can be required in renderer', ->
      require 'runas'

    it 'can be required in node binary', (done) ->
      runas = path.join fixtures, 'module', 'runas.js'
      child = require('child_process').fork runas
      child.on 'message', (msg) ->
        assert.equal msg, 'ok'
        done()

Remove the unzip spec completely.

Remove the unzip spec completely.


React = require 'react'

module.exports = React.PropTypes.shape(
  title:                    React.PropTypes.string
  children:                 React.PropTypes.array
  chapter_section:          React.PropTypes.array
  current_level:            React.PropTypes.number
  questions_answered_count: React.PropTypes.number
)

React = require 'react'

module.exports = React.PropTypes.shape(
  title:                    React.PropTypes.string
  children:                 React.PropTypes.array
  chapter_section:          React.PropTypes.array
  clue:                     React.PropTypes.object
  questions_answered_count: React.PropTypes.number
)

Add clue parameter to validation

Add clue parameter to validation


class Article
   constructor: ->

   set_title: (@title) ->

   set_content: (@content) ->

   set_date: (@date) ->

   set_section: (@section) ->

   parse: (html) ->
      page = $(html)
      @set_title page.find("#articleContent h1").text()
      raw_content = page.find("#masterContent .ArticleContent_Inner")
      @set_content _.chain(raw_content).map(
                                            (paragraph) -> $(paragraph).find("p").removeAttr("class").prop("outerHTML")
                                             ).reduce(
                                                (memo, obj) -> memo + obj
                                             ).value()

root = exports ? this
root.Article = Article

class Article
   constructor: ->

   set_title: (@title) ->

   set_content: (@content) ->

   set_date: (@date) ->

   set_section: (@section) ->

   parse: (html) ->
      page = $(html)
      @set_title page.find("#articleContent h1").text()
      raw_content = page.find("#masterContent .ArticleContent_Inner")
      @set_content _.chain(raw_content).map(
                                            (paragraph) -> $(paragraph).removeAttr("class").prop("outerHTML")
                                             ).reduce(
                                                (memo, obj) -> memo + obj
                                             ).value()

root = exports ? this
root.Article = Article

Insert subtitle to the content

Insert subtitle to the content

Signed-off-by: Lincoln Lee <46f1a0bd5592a2f9244ca321b129902a06b53e03@lincoln.hk>


$(document).ready ->

  $('#messages-kiitos li').on 'click', (event) ->
    event.preventDefault()
    id = $(@).data('id')
    $.get "messages/#{id}"

$(document).ready ->

  $('#messages-kiitos .list-group li').on 'click', (event) ->
    event.preventDefault()
    id = $(@).data('id')
    $.get "messages/#{id}"

Implement 'recent' link on messages section


#<< AppEngine/Helpers/Helpers

class StrictObject extends Backbone.Events
  @expectedParameters: []
  @applyParameters: []
  @isAbstract: -> @ == StrictObject

  constructor: (conf) ->
    #check that this is not an abstract class
    if @__proto__.constructor.isAbstract()
      throw new Error "Unable to create instance of Abstract class '#{@.__proto__.constructor.getName()}'"

    #Check that all config params were passed through correctly
    AppEngine.Helpers.assertParametersExist @.__proto__.constructor.expectedParameters, conf
    AppEngine.Helpers.applyToObject @__proto__.constructor.applyParameters, conf, @

#<< AppEngine/Helpers/Helpers

class StrictObject extends Backbone.Events
  @expectedParameters: []
  @applyParameters: []
  @isAbstract: -> @ == StrictObject

  constructor: (conf) ->
    #check that this is not an abstract class
    if @__proto__.constructor.isAbstract()
      throw new Error "Unable to create instance of Abstract class '#{@.__proto__.constructor.getName()}'"

    #Check that all config params were passed through correctly
    if @.__proto__.constructor.expectedParameters.length > 0
      if conf
        AppEngine.Helpers.assertParametersExist @.__proto__.constructor.expectedParameters, conf
      else
        throw new Error "StrictObject: There are required parameters but the options are not defined"

    AppEngine.Helpers.applyToObject @__proto__.constructor.applyParameters, conf, @ if conf

Fix parameter asserting when the options passed through are undefined


'menu': [
  {
    'label': 'Edit'
    'submenu': [
      'label': 'Lines'
      'submenu': [
        { 'label': 'Sort', 'command': 'sort-lines:sort' }
        { 'label': 'Reverse Sort', 'command': 'sort-lines:reverse-sort' }
        { 'label': 'Unique', 'command': 'sort-lines:unique' }
        { 'label': 'Case-insensitive Sort', 'command': 'sort-lines:case-insensitive-sort' }
      ]
    ]
  }
]

'menu': [
  {
    'label': 'Edit'
    'submenu': [
      'label': 'Lines'
      'submenu': [
        { 'label': 'Sort', 'command': 'sort-lines:sort' }
        { 'label': 'Reverse Sort', 'command': 'sort-lines:reverse-sort' }
        { 'label': 'Unique', 'command': 'sort-lines:unique' }
        { 'label': 'Sort (Case Insentivive)', 'command': 'sort-lines:case-insensitive-sort' }
      ]
    ]
  }
]

Tweak menu label for case insensitive sort


Decrease threads number in order to show threads working


#= require trix/views/text_view
#= require fixtures

module "Trix.TextView"

test "#createElementsForText", ->
  text = fixture("plain")
  elements = getElementsForText(text)
  equal elements.length, 1, "one element for plain string"

  el = elements[0]
  equal el.tagName.toLowerCase(), "span", "container element is a span"
  equal el.trixPosition, 0, "container element has a trixPosition"
  equal el.childNodes.length, 1, "container element has one child node"

  node = el.firstChild
  equal node.nodeType, Node.TEXT_NODE, "child node is a text node"
  equal node.data, text.toString(), "child node contains text string"
  equal node.trixPosition, 0, "child node has a trixPosition property"
  equal node.trixLength, text.getLength(), "child node has a trixLength property"

  text = Trix.Text.textForStringWithAttributes(text.toString() + "\n")
  elements = getElementsForText(text)
  equal elements.length, 2, "two elements for string ending with a newline"
  equal elements[0].lastChild.tagName.toLowerCase(), "br", "container element's last child is a BR"
  equal elements[1].tagName.toLowerCase(), "br", "last element is an extra BR"

# Helpers

getElementsForText = (text) ->
  element = document.createElement("div")
  textView = new Trix.TextView element, text
  textView.createElementsForText()

#= require trix/views/text_view
#= require fixtures

module "Trix.TextView"

test "#createElementsForText", ->
  text = fixture("plain")
  elements = getElementsForText(text)
  equal elements.length, 1, "one element for plain string"

  el = elements[0]
  equal el.tagName.toLowerCase(), "span", "container element is a span"
  equal el.trixPosition, 0, "container element has a trixPosition"
  equal el.childNodes.length, 1, "container element has one child node"

  node = el.firstChild
  equal node.nodeType, Node.TEXT_NODE, "child node is a text node"
  equal node.data, text.toString(), "child node contains text string"
  equal node.trixPosition, 0, "child node has a trixPosition property"
  equal node.trixLength, text.getLength(), "child node has a trixLength property"

  text = Trix.Text.textForStringWithAttributes(fixture("plain").toString() + "\n")
  elements = getElementsForText(text)
  equal elements.length, 2, "two elements for string ending with a newline"
  equal elements[0].lastChild.tagName.toLowerCase(), "br", "container element's last child is a BR"
  equal elements[1].tagName.toLowerCase(), "br", "last element is an extra BR"

# Helpers

getElementsForText = (text) ->
  element = document.createElement("div")
  textView = new Trix.TextView element, text
  textView.createElementsForText()

Load fixture instead of relying on exitisting var


'*':
  'editor':
    'fontFamily': 'Fira Mono OT'
    'showIndentGuide': true
    'tabLength': 4
    'softWrap': true
    'invisibles': {}
    'zoomFontWhenCtrlScrolling': false
  'core':
    'disabledPackages': [
      'language-objective-c'
      'archive-view'
      'autosave'
      'bookmarks'
      'language-clojure'
      'language-perl'
      'language-php'
      'language-toml'
      'snippets'
      'welcome'
      'spell-check'
    ]
    'themes': [
      'seti-ui'
      'seti-syntax'
    ]
    'projectHome': '/home/ai/Dev'
    'autoHideMenuBar': true
  'exception-reporting':
    'userId': '51ccaaae-04bc-1ed3-3445-f9bd1a2f3a71'
  'welcome':
    'showOnStartup': false
  'markdown-preview':
    'breakOnSingleNewline': true
  'spell-check': {}
'.ruby.source':
  'editor':
    'tabLength': 2
'.coffee.source':
  'editor':
    'tabLength': 2
'.sass.source':
  'editor':
    'tabLength': 2

Change tab size for Ruby, Coffee and Sass


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

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

jQuery ->
  notificationNotice = $('.notifications')
  btn = notificationNotice.find('button')

  if $.notifyCheck() == $.NOTIFY_NOT_ALLOWED
    btn.click () =>
      $.notifyRequest()
      notificationNotice.hide()
  else
    notificationNotice.hide()

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

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

jQuery ->
  notificationNotice = $('.notifications')
  btn = notificationNotice.find('button')

  if $.notifyCheck() == $.NOTIFY_NOT_ALLOWED
    btn.click () =>
      $.notifyRequest()
      notificationNotice.hide()
  else
    notificationNotice.hide()

Add jQuery notify back in

Add jQuery notify back in


Meteor.methods
	setAvatarFromService: (dataURI, service) ->
		if not Meteor.userId()
			throw new Meteor.Error 203, '[methods] typingStatus -> Usuário não logado'

		user = Meteor.user()

		{image, contentType} = RocketFile.dataURIParse dataURI

		rs = RocketFile.bufferToStream new Buffer(image, 'base64')
		ws = RocketFileAvatarInstance.createWriteStream user.username, contentType
		ws.on 'end', Meteor.bindEnvironment ->
			Meteor.users.update {_id: user._id}, {$set: {avatarOrigin: service}}

		rs.pipe(ws)

	resetAvatar: (image, service) ->
		if not Meteor.userId()
			throw new Meteor.Error 203, '[methods] typingStatus -> Usuário não logado'

		user = Meteor.user()

		Avatars.remove {'copies.avatars.name': user.username}

		Meteor.users.update user._id, {$unset: {avatarOrigin: 1}}

Meteor.methods
	setAvatarFromService: (dataURI, service) ->
		if not Meteor.userId()
			throw new Meteor.Error 203, '[methods] typingStatus -> Usuário não logado'

		user = Meteor.user()

		{image, contentType} = RocketFile.dataURIParse dataURI

		rs = RocketFile.bufferToStream new Buffer(image, 'base64')
		ws = RocketFileAvatarInstance.createWriteStream user.username, contentType
		ws.on 'end', Meteor.bindEnvironment ->
			Meteor.users.update {_id: user._id}, {$set: {avatarOrigin: service}}

		rs.pipe(ws)

	resetAvatar: (image, service) ->
		if not Meteor.userId()
			throw new Meteor.Error 203, '[methods] typingStatus -> Usuário não logado'

		user = Meteor.user()

		RocketFileAvatarInstance.deleteFile user.username

		Meteor.users.update user._id, {$unset: {avatarOrigin: 1}}

Change method 'resetAvatar' to use RocketFile


$ ->
  $.rails.allowAction = (element) ->
    message = element.data('confirm')
    fire = $.rails.fire
    if message and fire(element, 'confirm')
      title = if $(document.body).hasClass('videos') then 'Delete this video' else 'Delete your account'
      dialog = new Dialog(title, message, confirmLabel: '<i class="icon-trash" role="presentation" aria-hidden="true"></i>Yes')
      dialog.onConfirm () ->
        callback = fire(element, 'confirm:complete', [true])
        if (callback)
          allowActionOrig = $.rails.allowAction
          $.rails.allowAction = () -> true
          element.trigger('click')
          $.rails.allowAction = allowActionOrig
      dialog.onClose () ->
        element.focus()
      dialog.show()
    not message or false

$ ->
  $.rails.allowAction = (element) ->
    message = element.data('confirm')
    fire = $.rails.fire
    if message and fire(element, 'confirm')
      title = if $(document.body).hasClass('videos') then 'Delete this video' else 'Delete your account'
      dialog = new Dialog(title, message, confirmLabel: '<i class="icon-trash" role="presentation" aria-hidden="true"></i>Yes')
      dialog.onConfirm () ->
        callback = fire(element, 'confirm:complete', [true])
        if (callback)
          allowActionOrig = $.rails.allowAction
          $.rails.allowAction = () -> true
          element.trigger('click')
          $.rails.allowAction = allowActionOrig
          dialog.destroy()
      dialog.onClose () ->
        element.focus()
      dialog.show()
    not message or false

Destroy confirm dialog when user has confirmed


_ = require 'underscore'
BufferedProcess = require 'buffered-process'
$ = require 'jquery'

module.exports =
class LoadPathsTask
  constructor: (@callback) ->

  start: ->
    rootPath = project.getPath()
    ignoredNames = config.get('fuzzyFinder.ignoredNames') ? []
    ignoredNames = ignoredNames.concat(config.get('core.ignoredNames') ? [])
    ignoreGitIgnoredFiles =  config.get('core.hideGitIgnoredFiles')

    command = require.resolve 'nak'
    args = ['-l', rootPath]
    args.unshift('--addVCSIgnores') if config.get('nak.addVCSIgnores')
    args.unshift('-d', ignoredNames.join(',')) if ignoredNames.length > 0

    paths = []
    deferred = $.Deferred()
    exit = (code) =>
      if code is -1
        deferred.reject({command, code})
      else
        @callback(paths)
        deferred.resolve()
    stdout = (data) ->
      paths.push.apply(paths, _.compact(data.split('\n')))

    @process = new BufferedProcess({command, args, stdout, exit})
    deferred

  abort: ->
    if @process?
      @process.kill()
      @process = null

_ = require 'underscore'
BufferedProcess = require 'buffered-process'
$ = require 'jquery'

module.exports =
class LoadPathsTask
  constructor: (@callback) ->

  start: ->
    rootPath = project.getPath()
    ignoredNames = config.get('fuzzyFinder.ignoredNames') ? []
    ignoredNames = ignoredNames.concat(config.get('core.ignoredNames') ? [])
    ignoreGitIgnoredFiles =  config.get('core.hideGitIgnoredFiles')

    command = require.resolve 'nak'
    args = ['-l', rootPath]
    args.unshift('--addVCSIgnores') if config.get('nak.addVCSIgnores')
    args.unshift('-d', ignoredNames.join(',')) if ignoredNames.length > 0

    paths = []
    deferred = $.Deferred()
    exit = (code) =>
      if code is -1
        deferred.reject({command, code})
      else
        @callback(paths)
        deferred.resolve()
    stdout = (data) ->
      paths.push(_.compact(data.split('\n'))...)

    @process = new BufferedProcess({command, args, stdout, exit})
    deferred

  abort: ->
    if @process?
      @process.kill()
      @process = null

Use splat instead of apply

Use splat instead of apply


ViewRegistry = require '../src/view-registry'
Panel = require '../src/panel'
PanelContainer = require '../src/panel-container'

describe "PanelContainer", ->
  [container] = []

  class TestPanelItem
    constructior: ->

  beforeEach ->
    viewRegistry = new ViewRegistry
    container = new PanelContainer({viewRegistry})

  describe "::addPanel(panel)", ->
    it 'emits an onDidAddPanel event with the index the panel was inserted at', ->
      container.onDidAddPanel addPanelSpy = jasmine.createSpy()

      panel1 = new Panel(item: new TestPanelItem(), location: 'left')
      container.addPanel(panel1)
      expect(addPanelSpy).toHaveBeenCalledWith({panel: panel1, index: 0})

      panel2 = new Panel(item: new TestPanelItem(), location: 'left')
      container.addPanel(panel2)
      expect(addPanelSpy).toHaveBeenCalledWith({panel: panel2, index: 1})

  describe "when a panel is destroyed", ->
    it 'emits an onDidRemovePanel event with the index of the removed item', ->
      container.onDidRemovePanel removePanelSpy = jasmine.createSpy()

      panel1 = new Panel(item: new TestPanelItem(), location: 'left')
      container.addPanel(panel1)
      panel2 = new Panel(item: new TestPanelItem(), location: 'left')
      container.addPanel(panel2)

      expect(removePanelSpy).not.toHaveBeenCalled()

      panel2.destroy()
      expect(removePanelSpy).toHaveBeenCalledWith({panel: panel2, index: 1})

      panel1.destroy()
      expect(removePanelSpy).toHaveBeenCalledWith({panel: panel1, index: 0})

ViewRegistry = require '../src/view-registry'
Panel = require '../src/panel'
PanelContainer = require '../src/panel-container'

describe "PanelContainer", ->
  [container] = []

  class TestPanelItem
    constructior: ->

  beforeEach ->
    viewRegistry = new ViewRegistry
    container = new PanelContainer({viewRegistry})

  describe "::addPanel(panel)", ->
    it 'emits an onDidAddPanel event with the index the panel was inserted at', ->
      container.onDidAddPanel addPanelSpy = jasmine.createSpy()

      panel1 = new Panel(item: new TestPanelItem())
      container.addPanel(panel1)
      expect(addPanelSpy).toHaveBeenCalledWith({panel: panel1, index: 0})

      panel2 = new Panel(item: new TestPanelItem())
      container.addPanel(panel2)
      expect(addPanelSpy).toHaveBeenCalledWith({panel: panel2, index: 1})

  describe "when a panel is destroyed", ->
    it 'emits an onDidRemovePanel event with the index of the removed item', ->
      container.onDidRemovePanel removePanelSpy = jasmine.createSpy()

      panel1 = new Panel(item: new TestPanelItem())
      container.addPanel(panel1)
      panel2 = new Panel(item: new TestPanelItem())
      container.addPanel(panel2)

      expect(removePanelSpy).not.toHaveBeenCalled()

      panel2.destroy()
      expect(removePanelSpy).toHaveBeenCalledWith({panel: panel2, index: 1})

      panel1.destroy()
      expect(removePanelSpy).toHaveBeenCalledWith({panel: panel1, index: 0})

Remove location junk from panel creation


###
HTML Renderer

@namespace Atoms.Core
@class Attributes

@author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
###
"use strict"

Atoms.Core.Attributes =

  ###
  Set the parent instance to current instance.
  @method setParent
  ###
  scaffold: ->
    # Assign Parrent
    @parent = {}
    if @attributes?.parent?
      @parent = @attributes.parent
      delete @attributes.parent

    # Assign Container
    @container = @attributes.container or @parent.el or document.body
    delete @attributes.container

    # Assign Entity
    if @attributes?.entity?
      @entity = @attributes.entity
      delete @attributes.entity

  chemistry: (children) ->
    children = @attributes.children or @default.children
    for item, index in children
      for key of item when not @available or key in @available
        base = key.split(".")
        type = base[0]
        class_name = base[1]
        if Atoms[type][class_name]?
          attributes = item[key]
          if @default?.children?[index]?[key]?
            attributes = Atoms.Core.Helper.mix item[key], @default.children?[index]?[key]
          @children.push @instance type, class_name, attributes

  instance: (type, class_name, attributes) ->
    attributes.parent = @
    new Atoms[type][class_name] attributes

###
HTML Renderer

@namespace Atoms.Core
@class Attributes

@author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
###
"use strict"

Atoms.Core.Attributes =

  ###
  Set the parent instance to current instance.
  @method setParent
  ###
  scaffold: ->
    # Assign Parrent
    @parent = {}
    if @attributes?.parent?
      @parent = @attributes.parent
      delete @attributes.parent

    # Assign Container
    @container = @attributes.container or @parent.el or document.body
    delete @attributes.container

    # Assign Entity
    if @attributes?.entity?
      @entity = @attributes.entity
      delete @attributes.entity

  chemistry: (children) ->
    children = @attributes.children or @default.children
    for item, index in children
      for key of item when not @constructor.available or key in @constructor.available
        base = key.split(".")
        type = base[0]
        class_name = base[1]
        if Atoms[type][class_name]?
          attributes = item[key]
          if @default?.children?[index]?[key]?
            attributes = Atoms.Core.Helper.mix item[key], @default.children?[index]?[key]
          @children.push @instance type, class_name, attributes

  instance: (type, class_name, attributes={}) ->
    if @__available type, class_name
      attributes.parent = attributes.parent or @
      new Atoms[type][class_name] attributes
    else
      throw "Instance no available in current container."

  __available: (type, class_name) ->
    instance = Atoms[type][class_name]
    instance = instance.base or instance.name
    (not @constructor.available or "#{type}.#{instance}" in @constructor.available)

Check available when creates a new instance


path = require 'path'
Module = require 'module'
ModuleCache = require '../src/module-cache'

describe 'ModuleCache', ->
  beforeEach ->
    spyOn(Module, '_findPath').andCallThrough()

  it 'resolves atom shell module paths without hitting the filesystem', ->
    require.resolve('shell')
    expect(Module._findPath.callCount).toBe 0

  it 'resolves relative core paths without hitting the filesystem', ->
    ModuleCache.add atom.getLoadSettings().resourcePath, {
      _atomModuleCache:
        extensions:
          '.json': [
            path.join('spec', 'fixtures', 'module-cache', 'file.json')
          ]
    }
    expect(require('./fixtures/module-cache/file.json').foo).toBe 'bar'
    expect(Module._findPath.callCount).toBe 0

path = require 'path'
Module = require 'module'
fs = require 'fs-plus'
temp = require 'temp'
ModuleCache = require '../src/module-cache'

describe 'ModuleCache', ->
  beforeEach ->
    spyOn(Module, '_findPath').andCallThrough()

  it 'resolves atom shell module paths without hitting the filesystem', ->
    require.resolve('shell')
    expect(Module._findPath.callCount).toBe 0

  it 'resolves relative core paths without hitting the filesystem', ->
    ModuleCache.add atom.getLoadSettings().resourcePath, {
      _atomModuleCache:
        extensions:
          '.json': [
            path.join('spec', 'fixtures', 'module-cache', 'file.json')
          ]
    }
    expect(require('./fixtures/module-cache/file.json').foo).toBe 'bar'
    expect(Module._findPath.callCount).toBe 0

  it 'resolves module paths to a compatible version provided by core', ->
    packagePath = fs.realpathSync(temp.mkdirSync('atom-package'))
    ModuleCache.add packagePath, {
      _atomModuleCache:
        folders: [{
          paths: [
            ""
          ]
          dependencies:
            'underscore-plus': '*'
        }]
    }
    ModuleCache.add atom.getLoadSettings().resourcePath, {
      _atomModuleCache:
        dependencies: [{
          name: 'underscore-plus'
          version: require('underscore-plus/package.json').version
          path: path.join('node_modules', 'underscore-plus', 'lib', 'underscore-plus.js')
        }]
    }

    indexPath = path.join(packagePath, 'index.js')
    fs.writeFileSync indexPath, """
      exports.load = function() {require('underscore-plus');};
    """

    packageMain = require(indexPath)
    Module._findPath.reset()
    packageMain.load()
    expect(Module._findPath.callCount).toBe 0

Add spec for resolving compatible module paths


# Define the Configuration
docpadConfig = {
	templateData:
		site:
			title: "Dorian Patterson"

		getPreparedTitle: -> if @document.title then "#{@document.title} | #{@site.title}" else @site.title
}

# Export the Configuration
module.exports = docpadConfig

# Define the Configuration
docpadConfig = {
	templateData:
		site:
			title: "Curly Hair Coder"

		getPreparedTitle: -> if @document.title then "#{@document.title} | #{@site.title}" else @site.title
		collections:
			pages: ->
				@getCollection("html").findAllLive({isPage:true})
}

# Export the Configuration
module.exports = docpadConfig

Change site title and add collection function.


{Document, Point, Range, Site} = require 'telepath'
_ = require 'underscore-plus'

module.exports =
  _: _
  BufferedNodeProcess: require '../src/buffered-node-process'
  BufferedProcess: require '../src/buffered-process'
  Directory: require '../src/directory'
  Document: Document
  File: require '../src/file'
  fs: require '../src/fs-utils'
  Git: require '../src/git'
  Point: Point
  Range: Range
  Site: Site

# The following classes can't be used from a Task handler and should therefore
# only be exported when not running as a child node process
unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE
  {$, $$, $$$, View} = require '../src/space-pen-extensions'

  module.exports.$ = $
  module.exports.$$ = $$
  module.exports.$$$ = $$$
  module.exports.Editor = require '../src/editor'
  module.exports.pathForRepositoryUrl = require('../src/project').pathForRepositoryUrl
  module.exports.RootView = require '../src/root-view'
  module.exports.SelectList = require '../src/select-list'
  module.exports.ScrollView = require '../src/scroll-view'
  module.exports.Task = require '../src/task'
  module.exports.View = View

{Document, Point, Range, Site} = require 'telepath'
_ = require 'underscore-plus'

module.exports =
  _: _
  apm: require 'atom-package-manager'
  BufferedNodeProcess: require '../src/buffered-node-process'
  BufferedProcess: require '../src/buffered-process'
  Directory: require '../src/directory'
  Document: Document
  File: require '../src/file'
  fs: require '../src/fs-utils'
  Git: require '../src/git'
  Point: Point
  Range: Range
  Site: Site

# The following classes can't be used from a Task handler and should therefore
# only be exported when not running as a child node process
unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE
  {$, $$, $$$, View} = require '../src/space-pen-extensions'

  module.exports.$ = $
  module.exports.$$ = $$
  module.exports.$$$ = $$$
  module.exports.Editor = require '../src/editor'
  module.exports.pathForRepositoryUrl = require('../src/project').pathForRepositoryUrl
  module.exports.RootView = require '../src/root-view'
  module.exports.SelectList = require '../src/select-list'
  module.exports.ScrollView = require '../src/scroll-view'
  module.exports.Task = require '../src/task'
  module.exports.View = View

Add apm to public API

Add apm to public API


$ ->
  $(".js-emoji").each ->
    $(@).html(emojione.toImage($(@).text()))

window.emojify = ->
  $(".js-emoji").each ->
    $(@).html(emojione.toImage($(@).text()))

$ -> emojify()

Make an emojify global function

Make an emojify global function


class CallbackDataSource extends DataSource

  constructor: (form_field, source) ->
    super(form_field, source)
    @results = {}

  search: (chosen, response_cb) ->
    ds = this
    # Call @source function, setting this to the chosen object
    # @param request
    # @param response_cb
    @source.call chosen, chosen.get_search_request(), (data) ->
      ds.did_search(chosen, response_cb, data)

  did_search: (chosen, response_cb, data) ->
    results = {}

    # Keep only results that have been chosen
    for value in chosen.choices()
      results[value] = @results[value]
    @results = results

    # Merge with new results
    options = (this.add_option child for child in data)

    response_cb options

  add_option: (child) ->
    item = @results[child.value] || child

    item.array_index = item.value
    item.html = item.label || item if not item.html?
    item.selected = item.selected || false
    item.disabled = item.disabled || false

    @results[item.value] = item
    item

  get_option_element: (array_index) ->
    this.get_option_element_by_value(array_index)

  get_item: (array_index) ->
    @results[array_index]

class CallbackDataSource extends DataSource

  constructor: (form_field, source) ->
    super(form_field, source)
    @results = {}

  search: (chosen, response_cb) ->
    ds = this
    # Call @source function, setting this to the chosen object
    # @param request
    # @param response_cb
    @source.call chosen, chosen.get_search_request(), (data) ->
      ds.did_search(chosen, response_cb, data)

  did_search: (chosen, response_cb, data) ->
    results = {}

    # Keep only results that have been chosen
    for value in chosen.choices()
      results[value] = @results[value]
    @results = results

    # Merge with new results
    options = (this.add_option child for child in data)

    response_cb options

  add_option: (child) ->
    item = @results[child.value] || child

    item.array_index = item.value
    item.text = item.label || item if not item.text?
    item.html = item.text if not item.html?
    item.selected = item.selected || false
    item.disabled = item.disabled || false

    @results[item.value] = item
    item

  get_option_element: (array_index) ->
    this.get_option_element_by_value(array_index)

  get_item: (array_index) ->
    @results[array_index]

Fix issue where item.text is not defined as expected


class TourUserView extends ActionButtonView
  template: 'tour/interesting_user'
  className: 'tour-interesting-user'
  templateHelpers: =>
    disabled_label: Factlink.Global.t.follow_user.capitalize()
    disable_label:  Factlink.Global.t.unfollow.capitalize()
    enabled_label:  Factlink.Global.t.following.capitalize()

  templateHelpers: =>
    disabled_label: Factlink.Global.t.follow_user.capitalize()
    disable_label:  Factlink.Global.t.unfollow.capitalize()
    enabled_label:  Factlink.Global.t.following.capitalize()

  enableHoverState: ->
    return if @justClicked
    super
    @$el.addClass 'hover'

  disableHoverState: ->
    super
    @$el.removeClass 'hover'

  updateButton: ->
    super
    @$el.toggleClass 'secondary', @buttonEnabled()

  buttonEnabled: ->
    @model.followers.followed_by_me()

  primaryAction: ->
    @model.follow()
    # TODO: favourite
    @updateButton()

  secondaryAction: ->
    @model.unfollow()
    @updateButton()

class window.TourUsersListView extends Backbone.Marionette.CollectionView
  itemView: TourUserView
  className: 'tour-interesting-users-list'

class TourUserView extends ActionButtonView
  template: 'tour/interesting_user'
  className: 'tour-interesting-user'
  templateHelpers: =>
    disabled_label: Factlink.Global.t.follow_user.capitalize()
    disable_label:  Factlink.Global.t.unfollow.capitalize()
    enabled_label:  Factlink.Global.t.following.capitalize()

  enableHoverState: ->
    return if @justClicked
    super
    @$el.addClass 'hover'

  disableHoverState: ->
    super
    @$el.removeClass 'hover'

  updateButton: ->
    super
    @$el.toggleClass 'secondary', @buttonEnabled()

  buttonEnabled: ->
    @model.followers.followed_by_me()

  primaryAction: ->
    @model.follow()
    # TODO: favourite
    @$el.removeClass 'hover'
    @updateButton()

  secondaryAction: ->
    @model.unfollow()
    @$el.removeClass 'hover'
    @updateButton()

class window.TourUsersListView extends Backbone.Marionette.CollectionView
  itemView: TourUserView
  className: 'tour-interesting-users-list'

Fix button interaction logic: disable hoverstate on click.


define [
  "backbone"
  "sp-utils-middleware"
  "common"
  "view/page"
  "view/modal"
],(
  Backbone
  Middleware
  common
  Page
  Modal
)->

  showPage=(View,options={},callback)->
    common.app.content.show View, options, callback

  class MiddlewareRouter extends Middleware
    auth:(async,args)->
      async.resolve "auth"

  middleware = new MiddlewareRouter

  Router = Backbone.Router.extend

    routes:
      "":"index"
      "!/rooms(/:id)": "rooms"
      "!/roomadd/:token": "roomAdd"
      "!/404": "error404"
      "*default":"default_router"

    index: middleware.wrap ->
      view = showPage Page.IndexPage

    rooms: middleware.wrap (id)->
      auto_id = common.user.get 'active_room_id' unless id?
      if auto_id?
        @navigate "!/rooms/#{auto_id}", {trigger: true}
        return
      view = showPage Page.MainPage
      view.setRoom id if id?

    roomAdd: middleware.wrap (token)->
      common.api.post_rooms_join(token).done (data)->
        Backbone.trigger "rooms:needUpdate"
        common.router "!/rooms/#{data.id}", {trigger:true}

    error404: middleware.wrap ->
      showPage Page.Error404Page

    default_router:->
      @navigate "!/404", {trigger:true,replace:true}

define [
  "backbone"
  "sp-utils-middleware"
  "common"
  "view/page"
  "view/modal"
],(
  Backbone
  Middleware
  common
  Page
  Modal
)->

  showPage=(View,options={},callback)->
    common.app.content.show View, options, callback

  class MiddlewareRouter extends Middleware
    auth:(async,args)->
      async.resolve "auth"

  middleware = new MiddlewareRouter

  Router = Backbone.Router.extend

    routes:
      "":"index"
      "!/rooms(/:id)": "rooms"
      "!/roomadd/:token": "roomAdd"
      "!/404": "error404"
      "*default":"default_router"

    index: middleware.wrap ->
      view = showPage Page.IndexPage

    rooms: middleware.wrap (id)->
      auto_id = common.user.get 'active_room_id' unless id?
      if auto_id?
        @navigate "!/rooms/#{auto_id}", {trigger: true}
        return
      view = showPage Page.MainPage
      view.setRoom id if id?

    roomAdd: middleware.wrap (token)->
      common.api.post_rooms_join(token)
        .done (data)->
          Backbone.trigger "rooms:needUpdate"
          common.router.navigate "!/rooms/#{data.id}", {trigger:true}
        .fail -> common.router.navigate "#", {trigger: true}

    error404: middleware.wrap ->
      showPage Page.Error404Page

    default_router:->
      @navigate "!/404", {trigger:true,replace:true}

Fix add room route work

Fix add room route work


$(document).ready ->

  $('#query').autocompleter {
    source: '/autocomplete/keywords',
    offset: 'results',
    highlightMatches: true
  }

  $('#location').autocompleter {
    source: '/autocomplete/places',
    offset: 'results',
    minLength: 4,
    highlightMatches: true
  }

  $('#flash a.close').click(sweet_hide)

  fix_lang_menu()

sweet_hide = ->
  alert_box = $(this).parent()
  properties = {
    height: 'toggle',
    marginBottom: 'toggle',
    marginTop: 'toogle',
    opacity: 'toggle',
    paddingBottom: 'toggle',
    paddingTop: 'toogle'
  }
  alert_box.animate properties, 'fast', check_flash

check_flash = ->
  flash_area = $('#flash')
  flash_area.slideUp() if $('li', flash_area).length?

fix_lang_menu = ->
  lang_menu = $('li.lang').first()
  lang_submenu = $('ul', lang_menu).first()
  lang_menu_w = lang_menu.width()
  lang_submenu_w = lang_submenu.width()
  left_margin = lang_menu_w - (lang_submenu_w + 1)
  lang_submenu.css 'margin-left', left_margin + 'px'

$(document).ready ->

  $('#query').autocompleter {
    source: '/autocomplete/keywords',
    offset: 'results',
    highlightMatches: true
  }

  $('#location').autocompleter {
    source: '/autocomplete/places',
    offset: 'results',
    minLength: 4,
    highlightMatches: true
  }

  $('#flash a.close').click(sweet_hide)

  fix_lang_menu()

sweet_hide = ->
  alert_box = $(this).parent()
  properties = {
    height: 'toggle',
    marginBottom: 'toggle',
    marginTop: 'toogle',
    opacity: 'toggle',
    paddingBottom: 'toggle',
    paddingTop: 'toogle'
  }
  alert_box.animate properties, 'fast', check_flash

check_flash = ->
  flash_area = $('#flash')
  flash_area.slideUp() if not flash_area.find('li:visible').length

fix_lang_menu = ->
  lang_menu = $('li.lang').first()
  lang_submenu = $('ul', lang_menu).first()
  lang_menu_w = lang_menu.width()
  lang_submenu_w = lang_submenu.width()
  left_margin = lang_menu_w - (lang_submenu_w + 1)
  lang_submenu.css 'margin-left', left_margin + 'px'

Fix flash message dismiss bug

Fix flash message dismiss bug


{$$, BufferedProcess, SelectListView} = require 'atom'

git = require '../git'
StatusView = require './status-view'
CherryPickSelectCommits = require './cherry-pick-select-commits-view'

module.exports =
class CherryPickSelectBranch extends SelectListView

  initialize: (items, @currentHead) ->
    super
    @addClass 'overlay from-top'
    @setItems items

    atom.workspaceView.append this
    @focusFilterEditor()

  viewForItem: (item) ->
    $$ ->
      @li item

  confirmed: (item) ->
    @cancel()
    args = [
      'log'
      '--cherry-pick'
      '-z'
      '--format=%H%n%an%n%ar%n%s'
      "#{@currentHead}...#{item}"
    ]

    git.cmd
      args: args
      stdout: (data) ->
        @save ?= ''
        @save += data
      exit: (exit) ->
        if exit is 0 and @save?
          new CherryPickSelectCommits(@save.split('\0')[...-1])
          @save = null
        else
          new StatusView(type: 'warning', message: "No commits available to cherry-pick.")

{BufferedProcess} = require 'atom'
{$$, SelectListView} = require 'atom-space-pen-views'

git = require '../git'
StatusView = require './status-view'
CherryPickSelectCommits = require './cherry-pick-select-commits-view'

module.exports =
class CherryPickSelectBranch extends SelectListView

  initialize: (items, @currentHead) ->
    super
    @show()
    @setItems items

    @focusFilterEditor()

  show: ->
    @panel ?= atom.workspace.addModalPanel(item: this)
    @panel.show()

  cancelled: -> @hide()

  hide: ->
    @panel?.hide()

  viewForItem: (item) ->
    $$ ->
      @li item

  confirmed: (item) ->
    @cancel()
    args = [
      'log'
      '--cherry-pick'
      '-z'
      '--format=%H%n%an%n%ar%n%s'
      "#{@currentHead}...#{item}"
    ]

    git.cmd
      args: args
      stdout: (data) ->
        @save ?= ''
        @save += data
      exit: (exit) ->
        if exit is 0 and @save?
          new CherryPickSelectCommits(@save.split('\0')[...-1])
          @save = null
        else
          new StatusView(type: 'warning', message: "No commits available to cherry-pick.")

Upgrade cherry pick select branch

Upgrade cherry pick select branch


class TerminalPane extends Pane

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

    options.cssClass  = "terminal-pane terminal"
    options.delay    ?= 0

    super options, data

    @createWebTermView()
    @webterm.on "WebTermConnected", (@remote) =>
      @emit "WebtermCreated"
      @onWebTermConnected()

  createWebTermView: ->
    @webterm           = new WebTermView
      cssClass         : "webterm"
      advancedSettings : no
      delegate         : this
      mode             : @getMode()

    @webterm.connectToTerminal()
    # WebTermView.setTerminalTimeout null, 15000, handler, handler

  getMode: -> "create"

  onWebTermConnected: ->
    {command} = @getOptions()
    @runCommand command if command

  runCommand: (command, callback) ->
    return unless command
    if @remote
      if callback
        @webterm.once "WebTermEvent", callback
        command += ";echo $?|kdevent"
      return @remote.input "#{command}\n"

    if not @remote and not @triedAgain
      @utils.wait 2000, =>
        @runCommand command
        @triedAgain = yes

  pistachio: ->
    """
      {{> @header}}
      {{> @webterm}}
    """

class TerminalPane extends Pane

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

    options.cssClass  = "terminal-pane terminal"
    options.delay    ?= 0

    super options, data

    @createWebTermView()

  createWebTermView: ->

    KD.singletons.vmController.fetchDefaultVm (err, vm)=>

      @webterm           = new WebTermView {
        cssClass         : "webterm"
        advancedSettings : no
        delegate         : this
        mode             : @getMode()
        vm
      }

      @addSubView @header
      @addSubView @webterm

      @webterm.connectToTerminal()

      @webterm.on "WebTermConnected", (@remote) =>
        @emit "WebtermCreated"
        @onWebTermConnected()

      # WebTermView.setTerminalTimeout null, 15000, handler, handler

  getMode: -> "create"

  onWebTermConnected: ->
    {command} = @getOptions()
    @runCommand command if command

  runCommand: (command, callback) ->
    return unless command
    if @remote
      if callback
        @webterm.once "WebTermEvent", callback
        command += ";echo $?|kdevent"
      return @remote.input "#{command}\n"

    if not @remote and not @triedAgain
      @utils.wait 2000, =>
        @runCommand command
        @triedAgain = yes

  # pistachio: ->
  #   """
  #     {{> @header}}
  #     {{> @webterm}}
  #   """

Fix TerminalPane until we support for multiple vms


#= require ./top/navbar
#= require ./centre/main_area

###* @jsx React.DOM ###

window.App = React.createClass
  render: ->
    `<span>
      <Navbar/>
      <MainArea/>
    </span>`

#= require ../store
#= require ../statechart
#= require ./top/navbar
#= require ./centre/main_area

###* @jsx React.DOM ###

models = [
  'corpora'
]

states =
  start: {}
  results: {}

window.App = React.createClass
  getInitialState: ->
    store: new Store(models, (store) => @setState(store: store))
    statechart: new Statechart('Main', states, 'start', (sc) => @setState(statechart: sc))

  render: ->
    `<span>
      <Navbar />
      <MainArea statechart={this.state.statechart} />
    </span>`

Add store and statechart to the App component


SpellCheckView = null

module.exports =
  config:
    grammars:
      type: 'array'
      default: [
        'source.asciidoc'
        'source.gfm'
        'text.git-commit'
        'text.plain'
        'text.plain.null-grammar'
      ]

  activate: ->
    @disposable = atom.workspace.observeTextEditors(addViewToEditor)

  deactivate: ->
    @disposable.dispose()

addViewToEditor = (editor) ->
  SpellCheckView ?= require './spell-check-view'
  new SpellCheckView(editor)

SpellCheckView = null

module.exports =
  config:
    grammars:
      type: 'array'
      default: [
        'source.asciidoc'
        'source.gfm'
        'text.git-commit'
        'text.plain'
        'text.plain.null-grammar'
      ]
      description: 'List of scopes for languages which will be checked for misspellings. See [the README](https://github.com/atom/spell-check#spell-check-package-) for more information on finding the correct scope for a specific language.'

  activate: ->
    @disposable = atom.workspace.observeTextEditors(addViewToEditor)

  deactivate: ->
    @disposable.dispose()

addViewToEditor = (editor) ->
  SpellCheckView ?= require './spell-check-view'
  new SpellCheckView(editor)

Add description for config setting

Add description for config setting


class VirtualizationController extends KDController

  constructor:->
    super

    @kc = KD.singletons.kiteController

    @lastState =
      state    : 'STOPPED'

  run:(command, callback, emitStateChanged=yes)->
    @kc.run
      kiteName : 'os'
      method   : command
    , @_cbWrapper callback, emitStateChanged

  start:(callback)->
    @run 'vm.start', callback

  stop:(callback)->
    @run 'vm.stop', callback

  reinitialize:(callback)->
    @run 'vm.reinitialize', callback

  info:(callback)->
    @run 'vm.info', (err, info)=>
      unless err then @lastState = info
      else warn "[VM]", err

      @emit 'StateChanged', err, info
      callback? err, info
    , no

  _cbWrapper:(callback, emitStateChanged)->
    return callback  unless emitStateChanged
    return (rest...)=>
      @info callback? rest...

  askToTurnOn:(appName='', callback)->

    content = """To #{if appName then 'run' else 'do this'} #{appName}
                 you need to turn on your VM first, you can do that by
                 clicking 'Turn ON VM' button below."""

    modal = new KDModalView
      title          : "Your VM is turned off"
      content        : "

#{content}

"
      height         : "auto"
      overlay        : yes
      buttons        :
        'Turn ON VM' :
          style      : "modal-clean-green"
          callback   : =>
            @start =>
              modal.destroy()
              if appName
                @once 'StateChanged', ->
                  appManager.open appName
        Cancel       :
          style      : "modal-clean-gray"
          callback   : ->
            modal.destroy()
            callback?()

    modal.once 'KDModalViewDestroyed', -> callback?()

Add askToTurnOn helper method to turn VM on globally


_ = require 'underscore'

View = require 'lib/view'
tpl = require 'templates/users/edit'

mediator = require('chaplin').mediator

module.exports = class EditUserView extends View
  template: tpl
  autoRender: yes

  events:
    'submit form': 'submitForm'
    'click [href="#remove"]': 'clickRemove'

  getTemplateData: ->
    _.extend super,
      currentUser: mediator.user?.toJSON()

  submitForm: (e) ->
    e.preventDefault()

    data = @$el.formParams(no)
    @model.save(data, wait: yes).fail(@renderServerErrors).done =>
      @dispose()

  clickRemove: (e) ->
    e.preventDefault()
    @model.destroy(wait: yes).done =>
      toastr.success 'User has been removed.'
      @dispose()

_ = require 'underscore'

View = require 'lib/view'
tpl = require 'templates/users/edit'

mediator = require('chaplin').mediator

module.exports = class EditUserView extends View
  template: tpl
  autoRender: yes

  events:
    'submit form': 'submitForm'
    'click [href="#remove"]': 'clickRemove'

  getTemplateData: ->
    _.extend super,
      currentUser: mediator.user?.toJSON()

  submitForm: (e) ->
    e.preventDefault()

    data = @$el.formParams(no)
    @model.save(data, wait: yes).fail(@renderServerErrors).done =>
      @dispose()

  clickRemove: (e) ->
    e.preventDefault()
    if confirm 'Are you sure?'
      @model.destroy(wait: yes).done =>
        toastr.success 'User has been removed.'
        @dispose()

Add confirm to user delete

Add confirm to user delete


###
Requires https://github.com/andialbrecht/sqlparse
###
getCmd = (inputPath, outputPath, options) ->
  path = options.sqlformat_path
  optionsStr = " --indent_width={0} --keywords={1} --identifiers={2} --reindent"
  optionsStr = optionsStr.replace("{0}", options.indent_size)
		                     .replace("{1}", options.keywords)
      					         .replace("{2}", options.identifiers)
  if path
    # Use absolute path
    "python \"" + path + "\" \"" + inputPath + "\" " + optionsStr
  else
    # Use command available in $PATH
    "sqlformat \"" + inputPath + "\" " + optionsStr

"use strict"
cliBeautify = require("./cli-beautify")
isStdout = true
module.exports = cliBeautify(getCmd, isStdout)

###
Requires https://github.com/andialbrecht/sqlparse
###
getCmd = (inputPath, outputPath, options) ->
  path = options.sqlformat_path

  optionsStr = "--reindent"
  if options.indent_size?
    optionsStr += " --indent_width=#{options.indent_size}"
  if options.keywords?
    optionsStr += " --keywords=#{options.keywords}"
  if options.identifiers
    optionsStr += " --identifiers=#{options.identifiers}"

  if path
    # Use absolute path
    "python \"" + path + "\" \"" + inputPath + "\" " + optionsStr
  else
    # Use command available in $PATH
    "sqlformat \"" + inputPath + "\" " + optionsStr

"use strict"
cliBeautify = require("./cli-beautify")
isStdout = true
module.exports = cliBeautify(getCmd, isStdout)

Improve SQL Beautifier to support missing config options.

Fixes #67.


Helper = require('hubot-test-helper')
chai = require 'chai'

expect = chai.expect

helper = new Helper('../scripts/hello.coffee')

describe 'Training 1', ->
  beforeEach ->
    @room = helper.createRoom()

  afterEach ->
    @room.destroy()

  it 'start Training 1', ->
    @room.user.say('miura', '@customer 三浦と申します。監視ソフトウェアの構築をしていました。').then =>
      expect(@room.messages).to.eql [
        ['miura', '@customer 三浦と申します。監視ソフトウェアの構築をしていました。']
        ['hubot', '@miura はじめまして、三浦さん。これからよろしくお願いします。']
      ]

Helper = require('hubot-test-helper')
chai = require 'chai'

expect = chai.expect

helper = new Helper('../scripts/training1st.coffee')

describe 'The 1st day of Training', ->
  beforeEach ->
    @room = helper.createRoom()

  afterEach ->
    @room.destroy()

  it 'should reply greeting to trainee', ->
    @room.user.say('miura', '@hubot 三浦と申します。監視ソフトウェアの構築をしていました。').then =>
      expect(@room.messages).to.eql [
        ['miura', '@hubot 三浦と申します。監視ソフトウェアの構築をしていました。']
        ['hubot', '@miura はじめまして、三浦さん。これからよろしくお願いします。']
      ]

Change description on tests of training.


os = require("os")

module.exports.monitor = (logger) ->
	return (req, res, next) ->
		Metrics = require("./metrics")
		startTime = new Date()
		end = res.end
		res.end = () ->
			end.apply(this, arguments)
			responseTime = new Date() - startTime
			routePath = req.route.path.toString().replace(/\//g, '_').replace(/\:/g, '').slice(1)
			key = "http-requests.#{routePath}.#{req.method}.#{res.statusCode}"

			Metrics.timing(key, responseTime)
			logger.log
				req:
					url: req.originalUrl || req.url
					method: req.method
					referrer: req.headers['referer'] || req.headers['referrer']
					"remote-addr": req.ip || req.socket?.socket?.remoteAddress || req.socket?.remoteAddress
					"user-agent": req.headers["user-agent"]
					"content-length": req.headers["content-length"]
				res:
					"content-length": res._headers?["content-length"]
					statusCode: res.statusCode
				"response-time": responseTime
				"http request"
		next()

os = require("os")

module.exports.monitor = (logger) ->
	return (req, res, next) ->
		Metrics = require("./metrics")
		startTime = new Date()
		end = res.end
		res.end = () ->
			end.apply(this, arguments)
			responseTime = new Date() - startTime
			if req.route?.path?
				routePath = req.route.path.toString().replace(/\//g, '_').replace(/\:/g, '').slice(1)
				key = "http-requests.#{routePath}.#{req.method}.#{res.statusCode}"

				Metrics.timing(key, responseTime)
				logger.log
					req:
						url: req.originalUrl || req.url
						method: req.method
						referrer: req.headers['referer'] || req.headers['referrer']
						"remote-addr": req.ip || req.socket?.socket?.remoteAddress || req.socket?.remoteAddress
						"user-agent": req.headers["user-agent"]
						"content-length": req.headers["content-length"]
					res:
						"content-length": res._headers?["content-length"]
						statusCode: res.statusCode
					"response-time": responseTime
					"http request"

		next()

Check that req.route.path is set

Check that req.route.path is set


BaseModel = require './base'
crypto = require 'crypto'
uuid = require 'uuid'
_ = require 'underscore'

class UserCredential extends BaseModel
  constructor: (options) ->
    super options

    @required = ['userid', 'secret']

    if not @secret
      @genSecret()

  genSecret: () ->
    sha = crypto.createHash 'sha256'
    sha.update "#{uuid.v1()}"
    @secret = sha.digest 'hex'

module.exports = UserCredential

BaseModel = require './base'
crypto = require 'crypto'
uuid = require 'uuid'
_ = require 'underscore'

class UserCredential extends BaseModel
  constructor: (options) ->
    super options

    @required = ['userid', 'secret', 'user_identifier']

    if not @secret
      @genSecret()

    if not @user_identifier
      @genIdentifier()

  genSecret: () ->
    sha = crypto.createHash 'sha256'
    sha.update "#{uuid.v1()}"
    @secret = sha.digest 'hex'

  genIdentifier: () ->
    @user_identifier = uuid.v1()

module.exports = UserCredential

Add user_identifier to UserCredential Model


Reflux = require 'reflux'
{api} = require '../api/client'
classifierActions = require '../actions/classifier-actions'
workflowStore = require './workflow-store'

module.exports = Reflux.createStore
  subjects: []
  subject: null

  init: ->
    @listenTo workflowStore, @changeWorkflow
    @listenTo classifierActions.moveToNextSubject, @next

  getInitialState: ->
    @subjects
    @subject

  next: ->
    if @subjects.length == 0
      query =
        workflow_id: workflowStore.data.id
        sort: 'queued'

      api.type('subjects').get query
        .then (subjects) =>
          return unless subjects.length > 0
          @subjects = subjects
        .then =>
          @loadSubjectImage()
    else
      @loadSubjectImage()

  loadSubjectImage: ->
    @subject = @subjects.shift()
    @generateHttpsUrl()
    image = new Image()
    image.src = @subject.locations[0]['image/jpeg']
    image.onload = =>
      @subject.srcWidth = image.naturalWidth
      @trigger @subject

  generateHttpsUrl: ->
    # If it's not an https url, we replace the s3 domain name with a secure one
    url = @subject.locations[0]['image/jpeg']
    if url.substr(0, 5) != 'https'
      filename = url.replace /https?:\/\/[^\/]+/i, ''
      @subject.locations[0]['image/jpeg'] = 'https://zooniverse-export.s3.amazonaws.com' + filename

  changeWorkflow: ->
    @subject = null
    @subjects.length = 0
    @trigger @subject
    @next()

Reflux = require 'reflux'
{api} = require '../api/client'
classifierActions = require '../actions/classifier-actions'
workflowStore = require './workflow-store'

module.exports = Reflux.createStore
  subjects: []
  subject: null

  init: ->
    @listenTo workflowStore, @changeWorkflow
    @listenTo classifierActions.moveToNextSubject, @next

  getInitialState: ->
    @subjects
    @subject

  next: ->
    if @subjects.length == 0
      query =
        workflow_id: workflowStore.data.id

      api.get('/subjects/queued', query)
        .then (subjects) =>
          return unless subjects.length > 0
          @subjects = subjects
        .then =>
          @loadSubjectImage()
    else
      @loadSubjectImage()

  loadSubjectImage: ->
    @subject = @subjects.shift()
    @generateHttpsUrl()
    image = new Image()
    image.src = @subject.locations[0]['image/jpeg']
    image.onload = =>
      @subject.srcWidth = image.naturalWidth
      @trigger @subject

  generateHttpsUrl: ->
    # If it's not an https url, we replace the s3 domain name with a secure one
    url = @subject.locations[0]['image/jpeg']
    if url.substr(0, 5) != 'https'
      filename = url.replace /https?:\/\/[^\/]+/i, ''
      @subject.locations[0]['image/jpeg'] = 'https://zooniverse-export.s3.amazonaws.com' + filename

  changeWorkflow: ->
    @subject = null
    @subjects.length = 0
    @trigger @subject
    @next()

Switch to using new subject selection URL


FactlinkJailRoot.createFactFromSelection = (current_user_opinion) ->
  success = ->
    FactlinkJailRoot.createButton.hide()
    FactlinkJailRoot.off 'modalOpened', success

  selInfo =
    text: window.document.getSelection().toString()
    title: window.document.title

  text = selInfo.text
  siteUrl = FactlinkJailRoot.siteUrl()
  siteTitle = selInfo.title

  FactlinkJailRoot.on 'modalOpened', success
  FactlinkJailRoot.factlinkCoreEnvoy 'prepareNewFactlink',
    text, siteUrl, siteTitle, current_user_opinion

FactlinkJailRoot.textSelected = ->
  # At least 4 words of at least 2 characters
  /(\w{2,}[\s-_&\/#%.,;:!()]+){4}/.test window.document.getSelection().toString()

FactlinkJailRoot.createFactFromSelection = (current_user_opinion) ->
  success = ->
    window.document.getSelection().removeAllRanges()
    FactlinkJailRoot.createButton.hide()
    FactlinkJailRoot.off 'modalOpened', success

  selInfo =
    text: window.document.getSelection().toString()
    title: window.document.title

  text = selInfo.text
  siteUrl = FactlinkJailRoot.siteUrl()
  siteTitle = selInfo.title

  FactlinkJailRoot.on 'modalOpened', success
  FactlinkJailRoot.factlinkCoreEnvoy 'prepareNewFactlink',
    text, siteUrl, siteTitle, current_user_opinion

FactlinkJailRoot.textSelected = ->
  # At least 4 words of at least 2 characters
  /(\w{2,}[\s-_&\/#%.,;:!()]+){4}/.test window.document.getSelection().toString()

Remove selection when added a discussion


#= require jquery
#= require bootstrap-fix
#= require jquery_ujs
#= require jquery-ui.js
#= require jquery.cookie
#= require jquery.titlealert
#= require jquery.mousewheel
#= require jquery.expanding
#= require jquery.iframe-transport
#= require simply-scroll
# require jquery.purr
#= require hamlcoffee
#= require date
#= require faye
#= require twitter-text
#= require autolink
#= require select2
# require pjax
# require jquery.tokeninput
# require jquery.daterangepicker
# require best_in_place
# require rails.validations
# require rails.validations.custom
# require rails.validations.formbuilder
# require farbtastic
# require showdown
# require highcharts
#
# require underscore
#= require underscore-1.3.3
#= require backbone
#= require backbone.getset
#
#= require cloudsdale
#
#= require_tree ../templates/
#= require_tree ./models
#= require_tree ./collections
#= require_tree ./views
#= require_tree ./routers
#= require_tree ./modules

#= require jquery
#= require bootstrap
#= require jquery_ujs
#= require jquery-ui.js
#= require jquery.cookie
#= require jquery.titlealert
#= require jquery.mousewheel
#= require jquery.expanding
#= require jquery.iframe-transport
#= require simply-scroll
# require jquery.purr
#= require hamlcoffee
#= require date
#= require faye
#= require twitter-text
#= require autolink
#= require select2
# require pjax
# require jquery.tokeninput
# require jquery.daterangepicker
# require best_in_place
# require rails.validations
# require rails.validations.custom
# require rails.validations.formbuilder
# require farbtastic
# require showdown
# require highcharts
#
# require underscore
#= require underscore-1.3.3
#= require backbone
#= require backbone.getset
#
#= require cloudsdale
#
#= require_tree ../templates/
#= require_tree ./models
#= require_tree ./collections
#= require_tree ./views
#= require_tree ./routers
#= require_tree ./modules

Use actual bootstrap javascript, not some weird homebrew version? :3


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

root = exports ? this

# Formats a Bug's file name, taking into account special files.
#
# @param [Object] bug The Bug.
# @return [String] The abbreviated file name.
#
root.formatBugFile = (bug) ->
  #TODO don't guess, record this information
  if bug.file =~ /^\[S\] /
    "<simple blamer>"
  else
    parts = bug.file.split('/')
    parts[parts.length  - 1]

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

root = exports ? this

# Formats a Bug's file name, taking into account special files.
#
# @param [Object] bug The Bug.
# @return [String] The abbreviated file name.
#
root.formatBugFile = (bug) ->
  #TODO don't guess, record this information
  if bug.file.match(/^\[S\] /)
    "<simple blamer>"
  else
    parts = bug.file.split('/')
    parts[parts.length  - 1]

Fix bug list display issue

Fix bug list display issue


#= require dashboard
#= require mock-ajax

describe "Dashboard - getData()", ->
  beforeEach ->
    jasmine.Ajax.useMock()
    @dashboard = Dashboard

  describe "when the report is ready on the first iteration", ->
    it "should return the dashboard data", ->
      that = this

      @dashboard.getData 'lesson_leaders', {}, (data) ->
        that.data = data

      request = mostRecentAjaxRequest()
      expect(request.url).toBe('/dashboard/queries')
      expect(request.method).toBe('POST')
      request.response { status: 200, responseText: '{"id": "REPORT_ID" }' }

      waits(1000)

      runs ->
        request = mostRecentAjaxRequest()
        expect(request.url).toBe('/dashboard/queries/REPORT_ID/ready')
        expect(request.method).toBe('GET')
        request.response { status: 200, responseText: '{"is_ready": true, "data":"DATA" }' }

        expect(that.data).toEqual 'DATA'

#= require lib/dashboard_poller
#= require mock-ajax

describe "Dashboard - getData()", ->
  beforeEach ->
    jasmine.Ajax.useMock()
    @dashboard = ttm.lib.DashboardPoller.build()

  describe "when the report is ready on the first iteration", ->
    it "should return the dashboard data", ->
      that = this

      @dashboard.getData 'lesson_leaders', {}, (data) ->
        that.data = data

      request = mostRecentAjaxRequest()
      expect(request.url).toBe('/dashboard/queries')
      expect(request.method).toBe('POST')
      request.response { status: 200, responseText: '{"id": "REPORT_ID" }' }

      waits(1000)

      runs ->
        request = mostRecentAjaxRequest()
        expect(request.url).toBe('/dashboard/queries/REPORT_ID/ready')
        expect(request.method).toBe('GET')
        request.response { status: 200, responseText: '{"is_ready": true, "data":"DATA" }' }

        expect(that.data).toEqual 'DATA'

Move the poller into the lib and mix it with the other classmixer stuffs. Update the test.


React = require 'react'
ReactRedux = require 'react-redux'
moment = require 'moment'

createFactory = require '../create-factory.coffee'
Elements = require '../elements.coffee'
selectors = require '../selectors/app-selectors.coffee'
userActions = require '../actions/user-actions.coffee'
URL = require '../url.coffee'

{ nav, ul, li } = Elements

Link = createFactory ReactRedux.connect(selectors.linkSelector)(
	require './link.coffee'
)
LangPicker = createFactory ReactRedux.connect(selectors.langPickerSelector)(
	require './lang-picker.coffee'
)

module.exports = React.createClass
	displayName: 'SiteMenu'
	handleLogout: ->
		@props.dispatch userActions.logout()
	render: ->
		{ home, admin, newArticle, logout } = @props.localeStrings
		newUrl = '/' + moment().format('YYYY/MM/DD') + '/untitled/new'
		ulArgs = [
			className: 'list-inline'
			li Link href: '/', home
			li Link href: '/login', admin
		]
		if @props.login.isLoggedIn
			logoutUrl = URL.getUserPath(@props.login.user._id) + '/logout'
			ulArgs.push(
				li key: 'new-article', Link href: newUrl, newArticle
				li key: 'logout', Link(
					href: logoutUrl
					onClick: @handleLogout
					logout
				)
			)
		nav(
			{ className: 'site-menu' }
			LangPicker className: 'site-menu__lang-picker'
			ul ulArgs
		)

React = require 'react'
ReactRedux = require 'react-redux'
moment = require 'moment'

createFactory = require '../create-factory.coffee'
Elements = require '../elements.coffee'
selectors = require '../selectors/app-selectors.coffee'
userActions = require '../actions/user-actions.coffee'
URL = require '../url.coffee'

{ nav, ul, li } = Elements

Link = createFactory ReactRedux.connect(selectors.linkSelector)(
	require './link.coffee'
)
LangPicker = createFactory ReactRedux.connect(selectors.langPickerSelector)(
	require './lang-picker.coffee'
)

module.exports = React.createClass
	displayName: 'SiteMenu'
	handleLogout: ->
		@props.dispatch userActions.logout()
	render: ->
		{ home, admin, newArticle, logout } = @props.localeStrings
		newUrl = '/' + moment().format('YYYY/MM/DD') + '/untitled/new'
		ulArgs = [
			className: 'list-inline'
			li Link href: '/', home
			li Link href: '/login', admin
		]
		if @props.login.isLoggedIn
			ulArgs.push(
				li key: 'new-article', Link href: newUrl, newArticle
				li key: 'logout', Link(
					href: '/logout'
					onClick: @handleLogout
					logout
				)
			)
		nav(
			{ className: 'site-menu' }
			LangPicker className: 'site-menu__lang-picker'
			ul ulArgs
		)

Remove duplicate user path from logout link.


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

  getInitialState: ->
    now: Date.now()

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

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

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

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

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

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

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

  getInitialState: ->
    now: Date.now()

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

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

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

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

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

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

Make a difference between minutes and months. They both used to have an "m" as indicator


should   = require 'should'
fixtures = require './fixtures'
git      = require '../src'
Blob     = require '../src/blob'

describe "Blob", ->
  describe "constructor", ->
    repo = fixtures.branched
    blob = new Blob repo, {name: "X", mode: "Y", id: "abc"}
    it "assigns @name", ->
      blob.name.should.eql "X"
    it "assigns @mode", ->
      blob.mode.should.eql "Y"
    it "assigns @id", ->
      blob.id.should.eql "abc"

  describe "#data", ->
    describe "of a file off the root", ->
      repo = git "#{__dirname}/fixtures/branched"
      data = null
      before (done) ->
        repo.tree().blobs (err, blobs) ->
          blobs[0].data (err, _data) ->
            data = _data
            done err

      it "is a string", ->
        data.should.be.a "string"
        data.should.include "Bla"

    describe "of a file in a subdir", ->
      repo = git "#{__dirname}/fixtures/branched"
      data = null
      before (done) ->
        repo.tree().trees (err, trees) ->
          trees[0].blobs (err, blobs) ->
            blobs[0].data (err, _data) ->
              data = _data
              done err

      it "is a string", ->
        data.should.be.a "string"
        data.should.include "!!!"

should   = require 'should'
fixtures = require './fixtures'
git      = require '../src'
Blob     = require '../src/blob'

describe "Blob", ->
  describe "constructor", ->
    repo = fixtures.branched
    blob = new Blob repo, {name: "X", mode: "Y", id: "abc"}
    it "assigns @name", ->
      blob.name.should.eql "X"
    it "assigns @mode", ->
      blob.mode.should.eql "Y"
    it "assigns @id", ->
      blob.id.should.eql "abc"

  describe "#data", ->
    describe "of a file off the root", ->
      repo = git "#{__dirname}/fixtures/branched"
      data = null
      before (done) ->
        repo.tree().blobs (err, blobs) ->
          blobs[0].data (err, _data) ->
            data = _data
            done err

      it "is a string", ->
        data.should.be.type "string"
        data.should.include "Bla"

    describe "of a file in a subdir", ->
      repo = git "#{__dirname}/fixtures/branched"
      data = null
      before (done) ->
        repo.tree().trees (err, trees) ->
          trees[0].blobs (err, blobs) ->
            blobs[0].data (err, _data) ->
              data = _data
              done err

      it "is a string", ->
        data.should.be.type "string"
        data.should.include "!!!"

Test fix: corrected use of should to assert typeof


ThemeWatcher = require './theme-watcher'
BaseThemeWatcher = require './base-theme-watcher'

module.exports =
class UIWatcher
  constructor: ({@themeManager}) ->
    @baseTheme = new BaseThemeWatcher()
    for theme in @themeManager.getActiveThemes()
      @createThemeWatcher(theme)
    @themeManager.on 'theme-activated', (theme) =>
      @createThemeWatcher(theme)

  createThemeWatcher: (theme) ->
    watcher = new ThemeWatcher(theme)
    watcher.on 'globals-changed', @reloadAll
    watcher

  reloadAll: =>
    @baseTheme.reloadStylesheet()
    theme.load() for theme in atom.themes.getThemes()

BaseThemeWatcher = require './base-theme-watcher'
ThemeWatcher = require './theme-watcher'
PackageWatcher = require './package-watcher'

module.exports =
class UIWatcher
  constructor: ({@themeManager}) ->
    @watchers = []
    @baseTheme = @createWatcher(BaseThemeWatcher)
    @watchThemes()
    @watchPackages()

  watchThemes: ->
    for theme in @themeManager.getActiveThemes()
      @createWatcher(ThemeWatcher, theme)

    @themeManager.on 'theme-activated', (theme) =>
      @createWatcher(ThemeWatcher, theme)

  watchPackages: ->
    for pack in atom.getActivePackages()
      console.log pack
      @createWatcher(PackageWatcher, pack) if PackageWatcher.supportsPackage(pack)

  createWatcher: (type, object) ->
    watcher = new type(object)
    watcher.on 'globals-changed', @reloadAll
    watcher.on 'destroyed', =>
      @watchers = _.without(@watchers, watcher)
    @watchers.push(watcher)
    watcher

  reloadAll: =>
    @baseTheme.reloadStylesheet()
    theme.load() for theme in atom.themes.getActiveThemes()
    pack.reloadStylesheets() for pack in atom.getActivePackages() when PackageWatcher.supportsPackage(pack)

Refactor ui watcher to use packageWatcher


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

# Your keymap
#
# Atom keymaps work similarly to style sheets. Just as style sheets use
# selectors to apply styles to elements, Atom keymaps use selectors to associate
# keystrokes with events in specific contexts. Unlike style sheets however,
# each selector can only be declared once.
#
# You can create a new keybinding in this file by typing "key" and then hitting
# tab.
#
# Here's an example taken from Atom's built-in keymap:
#
# 'atom-text-editor':
#   'enter': 'editor:newline'
#
# 'atom-workspace':
#   'ctrl-shift-p': 'core:move-up'
#   'ctrl-p': 'core:move-down'
#
# You can find more information about keymaps in these guides:
# * http://flight-manual.atom.io/using-atom/sections/basic-customization/#_customizing_keybindings
# * http://flight-manual.atom.io/behind-atom/sections/keymaps-in-depth/
#
# If you're having trouble with your keybindings not working, try the
# Keybinding Resolver: `Cmd+.` on macOS and `Ctrl+.` on other platforms. See the
# Debugging Guide for more information:
# * http://flight-manual.atom.io/hacking-atom/sections/debugging/#check-the-keybindings
#
# This file uses CoffeeScript Object Notation (CSON).
# If you are unfamiliar with CSON, you can read more about it in the
# Atom Flight Manual:
# http://flight-manual.atom.io/using-atom/sections/basic-customization/#_cson

Update to Atom 1.15.0 default

Update to Atom 1.15.0 default


Polymer
  is: 'league-controller'
  properties:
    league: Object
    leagueId:
      type: String,
      observer: '_leagueIdChanged'
    leagueAttrs: Object
    players:
      type: Object
      observer: '_onPlayersChange'
    playerModels: Array

  newPlayer: ->
    event.preventDefault()
    league = @league
    errorsEl = Polymer.dom(this).node.querySelector("#errors")
    playerInput = Polymer.dom(this).node.querySelector("#playerName")
    opts =
      name: playerInput.value
      LeagueId: league.id
    player = new Player(opts)
    player.setApiClient(@$.api)
    player.save player.attributes,
      success: (model) ->
        league.get('Players').add(model)
        playerInput.value = ""
        errorsEl.errors = []
      error: (data, response) ->
        errorsEl.errors = [response.responseJSON.error.errors[0]]

  playerEditClick: (event) ->
    event.preventDefault()
    console.log "TODO"

  playerRemoveClick: (event) ->
    event.preventDefault()
    playerId = event.target.getAttribute("data-player")
    player = @league.get('Players').get(playerId)
    player.setApiClient(@$.api)
    player.destroy(wait: true)

  _leagueIdChanged: ->
    if @leagueId
      @$.api.findOrFetchModel(League, @leagueId)
        .then (league) =>
          @league = league
          @fire('iron-signal', {name: "league", data: league})

Polymer
  is: 'league-controller'
  properties:
    league: Object
    leagueId:
      type: String,
      observer: '_leagueIdChanged'
    leagueAttrs: Object
    players:
      type: Object
      observer: '_onPlayersChange'
    playerModels: Array

  newPlayer: ->
    event.preventDefault()
    league = @league
    errorsEl = Polymer.dom(this).node.querySelector("#errors")
    playerInput = Polymer.dom(this).node.querySelector("#playerName")
    opts =
      name: playerInput.value
      LeagueId: league.id
    player = new Player(opts)
    player.setApiClient(@$.api)
    player.save player.attributes,
      success: (model) ->
        league.get('Players').add(model)
        playerInput.value = ""
        errorsEl.errors = []
      error: (data, response) ->
        errorsEl.errors = [response.responseJSON.error.errors[0]]

  playerEditClick: (event) ->
    event.preventDefault()
    console.log "TODO"

  playerRemoveClick: (event) ->
    event.preventDefault()
    playerId = event.target.getAttribute("data-player")
    player = @league.get('Players').get(playerId)
    if window.confirm("Deleting player #{player.get('name')}. Are you sure?")
      player.setApiClient(@$.api)
      player.destroy(wait: true)

  _leagueIdChanged: ->
    if @leagueId
      @$.api.findOrFetchModel(League, @leagueId)
        .then (league) =>
          @league = league
          @fire('iron-signal', {name: "league", data: league})

Add confirmation to player delete

Add confirmation to player delete


Marionette  = require "marionette"

CommentCollectionView   = require "./comment/collectionView"
PlayingNotifierView     = require "./nowPlaying/nowPlayingView"

module.exports =
class NcoMainLayout extends Marionette.LayoutView
    template    : require "./view.jade"

    regions     :
        comment     : "#nco-comments"
        notify      : ".NcoNotifier"

    ui          : null

    events      : null

    onShow      : ->
        @comment.show new CommentCollectionView
        @notify.show new PlayingNotifierView

Marionette  = require "marionette"

CommentCollectionView   = require "./comment/collectionView"
NotifyView = require "./notify/NotifyView"

module.exports =
class NcoMainLayout extends Marionette.LayoutView
    template    : require "./view.jade"

    regions     :
        comment     : "#nco-comments"
        notify      : ".NcoNotifier"

    ui          : null

    events      : null

    onShow      : ->
        @comment.show new CommentCollectionView
        @notify.show new NotifyView

Replace notify view to NotifyView

[Dev] Replace notify view to NotifyView


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

UserStatus = require '../user/status'
{ExerciseStep} = require '../exercise'

STEP_ID = '4571'

Demo = React.createClass
  displayName: 'Demo'
  render: ->
    demos =
      exercise: <ExerciseStep id={STEP_ID}/>

    demos = _.map(demos, (demo, name) ->
      <BS.Row>
        <BS.Col xs={12}>

{"#{name}"}

{demo}

        </BS.Col>
      </BS.Row>
    )
    <BS.Grid className='demo'>
      <UserStatus />
      {demos}
    </BS.Grid>

module.exports = Demo

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

UserStatus = require '../user/status'
UserLoginButton = require '../user/login-button'
UserLogin = require '../user/login'

{ExerciseStep} = require '../exercise'

STEP_ID = '4571'

Demo = React.createClass
  displayName: 'Demo'

  onAttemptLogin: ->
    @setState(displayLogin: true)

  onLoginComplete: ->
    @setState(displayLogin: false)

  render: ->
    if @state?.displayLogin
      return <UserLogin onComplete={@onLoginComplete} />

    demos =
      exercise: <ExerciseStep id={STEP_ID}/>

    demos = _.map(demos, (demo, name) ->
      <BS.Row>
        <BS.Col xs={12}>

{"#{name}"}

{demo}

        </BS.Col>
      </BS.Row>
    )
    <BS.Grid className='demo'>
      <UserStatus />
      <UserLoginButton onAttemptLogin={@onAttemptLogin} />
      {demos}
    </BS.Grid>

module.exports = Demo

Switch out view to login and back on completion


'context-menu':
  '.tab': [
    {label: 'Close Tab', command: 'tabs:close-tab'}
    {label: 'Close Other Tabs', command: 'tabs:close-other-tabs'}
    {label: 'Close Tabs to the Right', command: 'tabs:close-tabs-to-right'}
    {label: 'Close Saved Tabs', command: 'tabs:close-saved-tabs'}
    {label: 'Close All Tabs', command: 'tabs:close-all-tabs'}

    {type: 'separator'}

    {label: 'Split Up', command: 'tabs:split-up'}
    {label: 'Split Down', command: 'tabs:split-down'}
    {label: 'Split Left', command: 'tabs:split-left'}
    {label: 'Split Right', command: 'tabs:split-right'}
  ]

  '.tab-bar': [
    {label: 'Reopen Closed Tab', command: 'pane:reopen-closed-item'}
  ]

'context-menu':
  'tabs-tab': [
    {label: 'Close Tab', command: 'tabs:close-tab'}
    {label: 'Close Other Tabs', command: 'tabs:close-other-tabs'}
    {label: 'Close Tabs to the Right', command: 'tabs:close-tabs-to-right'}
    {label: 'Close Saved Tabs', command: 'tabs:close-saved-tabs'}
    {label: 'Close All Tabs', command: 'tabs:close-all-tabs'}

    {type: 'separator'}

    {label: 'Split Up', command: 'tabs:split-up'}
    {label: 'Split Down', command: 'tabs:split-down'}
    {label: 'Split Left', command: 'tabs:split-left'}
    {label: 'Split Right', command: 'tabs:split-right'}
  ]

  'tabs-bar': [
    {label: 'Reopen Closed Tab', command: 'pane:reopen-closed-item'}
  ]

Update menus to use custom tag selectors


Volontariat.ArgumentsIndexRoute = Ember.Route.extend
  model: (params) ->
    @controllerFor('arguments.index').set 'page', parseInt(params.page)
    @controllerFor('arguments.index').set 'thingId', @modelFor('thing').id

    @store.find 'argument', thing_id: @modelFor('thing').id, page: params.page

  setupController: (controller, model) ->
    controller.send('goToPageWithoutRedirect', controller.get('page'))
    controller.set('model', model)

  actions:

    openModal: (modalName) ->
      @render modalName,
        into: "arguments.index"
        outlet: "modal"
      $('#modal').modal('show')

    closeModal: ->
      @disconnectOutlet
        outlet: "modal"
        parentView: "arguments.index"
      $('#modal').modal('hide')

Volontariat.ArgumentsIndexRoute = Ember.Route.extend
  model: (params) ->
    @controllerFor('arguments.index').set 'page', parseInt(params.page)
    @controllerFor('arguments.index').set 'thingName', @modelFor('thing')._data.name

    @store.find 'argument', thing_id: @modelFor('thing').id, page: params.page

  setupController: (controller, model) ->
    controller.send('goToPageWithoutRedirect', controller.get('page'))
    controller.set('model', model)

  actions:

    openModal: (modalName) ->
      @render modalName,
        into: "arguments.index"
        outlet: "modal"
      $('#modal').modal('show')
      $('#modal').removeClass('hide')

    closeModal: ->
      @disconnectOutlet
        outlet: "modal"
        parentView: "arguments.index"
      $('#modal').modal('hide')
      $('#modal').addClass('hide')
      $('body').removeClass('modal-open')
      $('.modal-backdrop').remove()

Change thing by ID to thing by name route and close new argument modal right.


StockXmlImport = require('../main').StockXmlImport
fs = require 'fs'
argv = require('optimist')
  .usage('Usage: $0 --projectKey key --clientId id --clientSecret secret --xmlfile file')
  .demand(['projectKey', 'clientId', 'clientSecret', 'xmlfile'])
  .argv

options =
  config:
    project_key: argv.projectKey
    client_id: argv.clientId
    client_secret: argv.clientSecret

stockxmlimport = new StockXmlImport options

fs.readFile argv.xmlfile, 'utf8', (err, content) ->
  if err
    console.error "Problems on reading file '#{argv.xmlfile}': " + err
    process.exit 2
  stockxmlimport.run content, (result) ->
    console.log result
    process.exit 1 unless result.status

StockXmlImport = require('../main').StockXmlImport
fs = require 'fs'
argv = require('optimist')
  .usage('Usage: $0 --projectKey key --clientId id --clientSecret secret --xmlfile file --timeout timeout')
  .demand(['projectKey', 'clientId', 'clientSecret', 'xmlfile'])
  .argv

timeout = argv.timeout
timeout or= 60000

options =
  config:
    project_key: argv.projectKey
    client_id: argv.clientId
    client_secret: argv.clientSecret
  timeout: timeout

stockxmlimport = new StockXmlImport options

fs.readFile argv.xmlfile, 'utf8', (err, content) ->
  if err
    console.error "Problems on reading file '#{argv.xmlfile}': " + err
    process.exit 2
  stockxmlimport.run content, (result) ->
    console.log result
    process.exit 1 unless result.status

Enable to set timeout as command line option.


fs = require 'fs'
package_json = require '../package.json'
StockXmlImport = require('../main').StockXmlImport
argv = require('optimist')
  .usage('Usage: $0 --projectKey key --clientId id --clientSecret secret --xmlfile file --timeout timeout')
  .default('timeout', 300000)
  .describe('projectKey', 'your SPHERE.IO project-key')
  .describe('clientId', 'your OAuth client id for the SPHERE.IO API')
  .describe('clientSecret', 'your OAuth client secret for the SPHERE.IO API')
  .describe('xmlfile', 'XML file containing inventory information to import')
  .describe('timeout', 'Set timeout for requests')
  .demand(['projectKey', 'clientId', 'clientSecret', 'xmlfile'])
  .argv

options =
  config:
    project_key: argv.projectKey
    client_id: argv.clientId
    client_secret: argv.clientSecret
  timeout: argv.timeout
  user_agent: "#{package_json.name} - #{package_json.version}"

stockxmlimport = new StockXmlImport options

fs.readFile argv.xmlfile, 'utf8', (err, content) ->
  if err
    console.error "Problems on reading file '#{argv.xmlfile}': " + err
    process.exit 2
  stockxmlimport.run content, (result) ->
    console.log result
    process.exit 1 unless result.status

fs = require 'fs'
package_json = require '../package.json'
StockXmlImport = require('../main').StockXmlImport
argv = require('optimist')
  .usage('Usage: $0 --projectKey [key] --clientId [id] --clientSecret [secret] --xmlfile [file] --timeout [timeout]')
  .default('timeout', 300000)
  .describe('projectKey', 'your SPHERE.IO project-key')
  .describe('clientId', 'your OAuth client id for the SPHERE.IO API')
  .describe('clientSecret', 'your OAuth client secret for the SPHERE.IO API')
  .describe('xmlfile', 'XML file containing inventory information to import')
  .describe('timeout', 'Set timeout for requests')
  .demand(['projectKey', 'clientId', 'clientSecret', 'xmlfile'])
  .argv

options =
  config:
    project_key: argv.projectKey
    client_id: argv.clientId
    client_secret: argv.clientSecret
  timeout: argv.timeout
  user_agent: "#{package_json.name} - #{package_json.version}"

stockxmlimport = new StockXmlImport options

fs.readFile argv.xmlfile, 'utf8', (err, content) ->
  if err
    console.error "Problems on reading file '#{argv.xmlfile}': " + err
    process.exit 2
  stockxmlimport.run content, (result) ->
    console.log result
    process.exit 1 unless result.status

Add some layaut to command line usage.

Add some layaut to command line usage.


NodeEditView= React.createFactory require '../node-edit-view'
LinkEditView= React.createFactory require '../link-edit-view'

{div, i} = React.DOM

module.exports = React.createClass

  displayName: 'InspectorPanelView'

  getInitialState: ->
    expanded: true

  collapse: ->
    @setState {expanded: false}

  expand: ->
    @setState {expanded: true}

  render: ->
    className = "wireframe-inspector-panel"
    if @state.expanded is false
      className = "#{className} inspector-panel-collapsed"
    (div {className: className},
      (div {className: 'inspector-panel-toggle'},
        if @state.expanded is true
          (i
            className: 'fa fa-chevron-right'
            onClick: @collapse
          )
        else
          (i
            className: 'fa fa-chevron-left'
            onClick: @expand
          )
      )
      (div {className: "inspector-panel-content"},
        (NodeEditView {node: @props.node, onNodeChanged: @props.onNodeChanged, protoNodes: @props.protoNodes})
        (LinkEditView {link: @props.selectedConnection, onLinkChanged: @props.onLinkChanged})
      )
    )

NodeEditView= React.createFactory require '../node-edit-view'
LinkEditView= React.createFactory require '../link-edit-view'

{div, i} = React.DOM

module.exports = React.createClass

  displayName: 'InspectorPanelView'

  getInitialState: ->
    expanded: true

  collapse: ->
    @setState {expanded: false}

  expand: ->
    @setState {expanded: true}

  render: ->
    className = "wireframe-inspector-panel"
    if @state.expanded is false
      className = "#{className} inspector-panel-collapsed"
    (div {className: className},
      (div {className: 'inspector-panel-toggle'},
        if @state.expanded is true
          (i
            className: 'fa fa-chevron-right'
            onClick: @collapse
          )
        else
          (i
            className: 'fa fa-chevron-left'
            onClick: @expand
          )
      )
      (div {className: "inspector-panel-content"},
        (NodeEditView {node: @props.node, onNodeChanged: @props.onNodeChanged, protoNodes: @props.protoNodes})
        (LinkEditView {link: @props.link, onLinkChanged: @props.onLinkChanged})
      )
    )

Correct bad property name mapping in inspector-panel


fsUtils = require 'fs-utils'
path = require 'path'
_ = require 'underscore'
archive = require 'ls-archive'
File = require 'file'

module.exports=
class ArchiveEditSession
  registerDeserializer(this)

  @activate: ->
    Project = require 'project'
    Project.registerOpener (filePath) ->
      new ArchiveEditSession(filePath) if archive.isPathSupported(filePath)

  @deserialize: ({path}={}) ->
    path = project.resolve(path)
    if fsUtils.isFileSync(path)
      new ArchiveEditSession(path)
    else
      console.warn "Could not build archive edit session for path '#{path}' because that file no longer exists"

  constructor: (@path) ->
    @file = new File(@path)

  destroy: ->
    @file?.off()

  serialize: ->
    deserializer: 'ArchiveEditSession'
    path: @getUri()

  getViewClass: ->
    require './archive-view'

  getTitle: ->
    if archivePath = @getPath()
      path.basename(archivePath)
    else
      'untitled'

  getUri: -> project?.relativize(@getPath()) ? @getPath()

  getPath: -> @path

  isEqual: (other) ->
    other instanceof ArchiveEditSession and @getUri() is other.getUri()

fsUtils = require 'fs-utils'
path = require 'path'
_ = require 'underscore'
archive = require 'ls-archive'
File = require 'file'

module.exports=
class ArchiveEditSession
  registerDeserializer(this)
  @version: 1

  @activate: ->
    Project = require 'project'
    Project.registerOpener (filePath) ->
      new ArchiveEditSession(filePath) if archive.isPathSupported(filePath)

  @deserialize: ({path}={}) ->
    path = project.resolve(path)
    if fsUtils.isFileSync(path)
      new ArchiveEditSession(path)
    else
      console.warn "Could not build archive edit session for path '#{path}' because that file no longer exists"

  constructor: (@path) ->
    @file = new File(@path)

  destroy: ->
    @file?.off()

  serialize: ->
    deserializer: 'ArchiveEditSession'
    path: @getUri()

  getViewClass: ->
    require './archive-view'

  getTitle: ->
    if archivePath = @getPath()
      path.basename(archivePath)
    else
      'untitled'

  getUri: -> project?.relativize(@getPath()) ? @getPath()

  getPath: -> @path

  isEqual: (other) ->
    other instanceof ArchiveEditSession and @getUri() is other.getUri()

Add serialization version to image and archive edit sessions


ETahi.PaperManageController = Ember.ObjectController.extend
  sortedPhases: ( ->
    Ember.ArrayProxy.createWithMixins(Em.SortableMixin, {
      content: @get('model.phases')
      sortProperties: ['position']
    })
  ).property('model.phases.@each')

  actions:
    addPhase: (position) ->
      paper = @get('model')
      phase = @store.createRecord 'phase',
        position: position + 1
        name: "New Phase"
        paper: paper
      phase.save().then ->
        paper.reload()

    removePhase: (phase) ->
      paper = phase.get('paper')
      phase.destroyRecord().then ->
        paper.reload()

  refreshColumnHeights: (->
    Ember.run.next(this, Tahi.utils.resizeColumnHeaders)
  ).observes('phases.[]')

ETahi.PaperManageController = Ember.ObjectController.extend
  sortedPhases: ( ->
    Ember.ArrayProxy.createWithMixins(Em.SortableMixin, {
      content: @get('model.phases')
      sortProperties: ['position']
    })
  ).property('model.phases.@each')

  updatePositions: (phase)->
    relevantPhases = _(this.get('model.phases').content).filter((p)->
      p != phase && p.get('position') >= phase.get('position')
    )
    _(relevantPhases).each((p)->
      p.incrementProperty('position')
    )

  actions:
    addPhase: (position) ->
      paper = @get('model')
      phase = @store.createRecord 'phase',
        position: position + 1
        name: "New Phase"
        paper: paper
      @updatePositions(phase)
      phase.save().then ->
        paper.reload()

    removePhase: (phase) ->
      paper = phase.get('paper')
      phase.destroyRecord().then ->
        paper.reload()

  refreshColumnHeights: (->
    Ember.run.next(this, Tahi.utils.resizeColumnHeaders)
  ).observes('phases.[]')

Set phase positions on client side to prevent awkward position correction in callback


Meteor.startup ->
	RocketChat.settings.add 'Message_AllowPinning', true, { type: 'boolean', group: 'Message', public: true }

	RocketChat.models.Permissions.upsert('pin-message', {$addToSet: {roles: {$each: ['owner', 'moderator', 'admin']}}})

Meteor.startup ->
	RocketChat.settings.add 'Message_AllowPinning', true, { type: 'boolean', group: 'Message', public: true }
	RocketChat.models.Permissions.upsert('pin-message', { $setOnInsert: { roles: ['owner', 'moderator', 'admin'] } });

Set pin-message permissions only on insert