Set up and automatic refresh of labels

Set up and automatic refresh of labels

--- services/web/public/coffee/ide/labels/LabelsManager.coffee
+++ services/web/public/coffee/ide/labels/LabelsManager.coffee
@@ -1,21 +1,37 @@
 define [
 ], () ->
+
+	AUTOMATIC_REFRESH_PERIOD = 1000 * 60 * 10
+
 	class LabelsManager
 		constructor: (@ide, @$scope) ->
 			@$scope.$root._labels = this

 			@state =
-				documents: {}
+				documents: {} # map of DocId => List[Label]

 			@loadLabelsTimeout = null
+			@periodicLoadInterval = null

 			setTimeout(
 				(self) ->
+					# set up a regular re-load
+					setTimeout(
+						(self) ->
+							self.periodicLoadInterval = setInterval(
+								(self) ->
+									self.loadLabelsFromOpenDoc()
+								, AUTOMATIC_REFRESH_PERIOD
+								, self
+							)
+						, AUTOMATIC_REFRESH_PERIOD
+						, self
+					)
+					# listen for document open
 					self.$scope.$on 'document:opened', (e, doc) ->
-						# console.log ">> [LabelsManager] document opened"
 						setTimeout(
 							(self) ->
-								self.loadLabelsFromOpenDoc()
+								self.scheduleLoadLabelsFromOpenDoc()
 							, 1000
 							, self
 						)


Throw an error on a common mistake.

Throw an error on a common mistake.

--- lib.coffee
+++ lib.coffee
@@ -69,7 +69,11 @@
   _.bind view.templateInstance, view

 addEvents = (view, component) ->
-  for events in component.events()
+  eventsList = component.events()
+
+  throw new Error "'events' method from the component '#{ component.componentName() }' did not return a list of event maps." unless _.isArray eventsList
+
+  for events in eventsList
     eventMap = {}

     for spec, handler of events


experimenting with testling ci

experimenting with testling ci

--- spec/CompatibilityTest.coffee
+++ spec/CompatibilityTest.coffee
@@ -1,4 +1,9 @@
 # A pruned version of BaconSpec, just to see if it runs nicer in Testling.CI
+try
+  expect = require("chai").expect
+catch error
+  console.log error
+
 describe "Just testing", ->
   it "should always work", ->
     console.log "hooray"


Add tests for non-center workspaces

Add tests for non-center workspaces

--- spec/pane-element-spec.coffee
+++ spec/pane-element-spec.coffee
@@ -1,5 +1,3 @@
-PaneContainer = require '../src/pane-container'
-
 describe "PaneElement", ->
   [paneElement, containerElement, pane] = []

@@ -253,21 +251,36 @@
       event.dataTransfer = dataTransfer
       event

-    describe "when a file is dragged to the pane", ->
-      it "opens it", ->
-        event = buildDragEvent("drop", [
-          {kind: "file", getAsFile: () -> {path: "/fake1"}},
-          {kind: "file", getAsFile: () -> {path: "/fake2"}}
-        ])
-        paneElement.dispatchEvent(event)
-        expect(atom.applicationDelegate.open.callCount).toBe 1
-        expect(atom.applicationDelegate.open.argsForCall[0][0]).toEqual pathsToOpen: ['/fake1', '/fake2'], here: true
-
-    describe "when a non-file is dragged to the pane", ->
-      it "does nothing", ->
-        event = buildDragEvent("drop", [])
-        paneElement.dispatchEvent(event)
-        expect(atom.applicationDelegate.open).not.toHaveBeenCalled()
+    describe "when the pane is in the center workspace", ->
+      describe "when a file is dragged to the pane", ->
+        it "opens it", ->
+          event = buildDragEvent("drop", [
+            {kind: "file", getAsFile: () -> {path: "/fake1"}},
+            {kind: "file", getAsFile: () -> {path: "/fake2"}}
+          ])
+          paneElement.dispatchEvent(event)
+          expect(atom.applicationDelegate.open.callCount).toBe 1
+          expect(atom.applicationDelegate.open.argsForCall[0][0]).toEqual pathsToOpen: ['/fake1', '/fake2'], here: true
+
+      describe "when a non-file is dragged to the pane", ->
+        it "does nothing", ->
+          event = buildDragEvent("drop", [])
+          paneElement.dispatchEvent(event)
+          expect(atom.applicationDelegate.open).not.toHaveBeenCalled()
+
+    describe "when the pane is not in the center workspace", ->
+      beforeEach ->
+        pane = atom.workspace.getLeftDock().getActivePane()
+        paneElement = pane.getElement()
+
+      describe "when a drop event occurs", ->
+        it "does nothing", ->
+          event = buildDragEvent("drop", [
+            {kind: "file", getAsFile: () -> {path: "/fake1"}},
+            {kind: "file", getAsFile: () -> {path: "/fake2"}}
+          ])
+          paneElement.dispatchEvent(event)
+          expect(atom.applicationDelegate.open).not.toHaveBeenCalled()

   describe "resize", ->
     it "shrinks independently of its contents' width", ->


Add parser spec

Add parser spec

--- spec/parser/index.spec.coffee
+++ spec/parser/index.spec.coffee
@@ -88,7 +88,7 @@
     it "returns workout with time of 0", ->
       expect(workout.current().totalTime()).to.eq 0

-  describe "given interval (100 Swim @ 1:30) to parse", ->
+  describe "given interval with time (100 Swim @ 1:30) to parse", ->
     workout = null
     beforeEach ->
       workout = parse(interval)
@@ -105,6 +105,23 @@
       expect(generatedInterval.distance).to.eq 100
       expect(generatedInterval.type).to.eq "Swim"

+  describe "given interval with rest (100 Swim +30) to parse", ->
+    workout = null
+    beforeEach ->
+      workout = parse(intervalWithRest)
+
+    it "returns workout", ->
+      expect(workout).not.to.eq null
+
+    it "returns workout with set", ->
+      expect(workout.sets.length).to.eq 1
+
+    it "returns workout with set and valid interval", ->
+      generatedInterval = workout.current().current()
+      expect(generatedInterval).not.to.eq null
+      expect(generatedInterval.distance).to.eq 100
+      expect(generatedInterval.type).to.eq "Swim"
+
   describe "given intervalSet (4x100 Swim @ 1:30) to parse", ->
     workout = null
     beforeEach ->


http_ircのAPI_KEYが動いた

--- http_irc.coffee
+++ http_irc.coffee
@@ -29,7 +29,12 @@
 querystring = require 'querystring'
 SendMessage = require './send_message'
 prefix      = "[http_irc]"
-ircType     = process.env.HUBOT_IRC_TYPE
+type        = process.env.HUBOT_IRC_TYPE
+debug       = process.env.HUBOT_HTTP_IRC_DEBUG
+api_key     = process.env.HUBOT_HTTP_IRC_API_KEY
+allow       = process.env.HUBOT_HTTP_IRC_ALLOW
+deny        = process.env.HUBOT_HTTP_IRC_DENY
+allow_flag  = false

 send_message = (res, room, msg, query) ->
   # console.log "room: #{room}"
@@ -40,21 +45,58 @@
     res.end 'Error: There is no room to say.'

   # console.log "query: %j", query
+
   options = {}
   if query.color?
-    options['color'] = query.color
+    options.color = query.color
   if query.icon_emoji?
-    options['icon_emoji'] = query.icon_emoji
+    options.icon_emoji = query.icon_emoji
   if query.icon_url?
-    options['icon_url'] = query.icon_url
+    options.icon_url = query.icon_url

-  if ircType == "slack"
-    # @sm.send ["#{room}"], "", @sm.slack_attachments("", msg)
+  if type is "slack"
     @sm.send ["#{room}"], msg, query
   else
     @sm.send ["#{room}"], msg
   res.writeHead 200, {'Content-Type': 'text/plain'}
   res.end 'OK'
+
+check_ip = (req) ->
+  return true if allow_flag
+
+  remote_ip = req.connection.remoteAddress
+
+  if req.headers['X-Forwarded-For']
+    remote_ip = req.headers['X-Forwarded-For']
+
+  deny_ips  = deny.split(',')
+  allow_ips = allow.split(',')
+
+  for ip in deny_ips
+    if remote_ip is ip
+      console.log "#{prefix}: DENY #{remote_ip}."
+      return false
+
+  for ip in allow_ips
+    if remote_ip is ip
+      allow_flag = true
+
+check_api_key = (req) ->
+  console.log "headers: "
+  console.dir req.headers
+  if api_key is req.headers['hubot_http_irc_api_key']
+    allow_flag = true
+    return true
+  else
+    console.log "#{prefix}: INVALID API_KEY."
+    return false
+
+check_request = (req) ->
+  unless check_api_key req
+    unless check_ip req
+      res.writeHead 200, {'Content-Type': 'text/plain'}
+      res.end 'Not allowed to access.'
+

 module.exports = (robot) ->

@@ -63,16 +105,23 @@
   message = ''

   robot.router.get "#{path}", (req, res) ->
+    check_request req
+
     query   = querystring.parse(req._parsedUrl.query)
     room    = query.room
     message = query.message
-    console.log "query: %j", query
+    if debug
+      console.log "query: %j", query
+
     send_message(res, room, message, query)

   robot.router.post "#{path}", (req, res) ->
+    check_request req
+
     query   = querystring.parse(req._parsedUrl.query)
     room    = query.room or ''
     message = req.body.message
     room    = req.body.room unless room
+
     send_message(res, room, message, req.body)


Lower the default fee to 0.00001

Lower the default fee to 0.00001

--- client/tx/multisig.coffee
+++ client/tx/multisig.coffee
@@ -10,7 +10,7 @@
 view = require './views/multisig.jade'
 qr = require 'qruri'

-DEFAULT_FEE = Util.parseValue '0.0001'
+DEFAULT_FEE = Util.parseValue '0.00001'

 $root = $ '.content'
 display_error = error_displayer $root


s/corrent/correct

s/corrent/correct

--- src/provider.coffee
+++ src/provider.coffee
@@ -43,10 +43,10 @@
   #
   # Returns true/false if is valid LTI request
   _valid_parameters: (req) ->
-    corrent_message_type = req.body.lti_message_type is 'basic-lti-launch-request'
+    correct_message_type = req.body.lti_message_type is 'basic-lti-launch-request'
     correct_version      = require('./ims-lti').supported_versions.indexOf(req.body.lti_version) isnt -1
     has_resource_link_id = req.body.resource_link_id?
-    corrent_message_type and correct_version and has_resource_link_id
+    correct_message_type and correct_version and has_resource_link_id

   # Helper to validate the OAuth information in the request


catching more selenium errors

catching more selenium errors

--- src/selenium.coffee
+++ src/selenium.coffee
@@ -23,6 +23,8 @@
       onceCallback()

   selenium.stderr.on 'data', (data) ->
+    if "Could not find or load main class org.openqa.grid.selenium.GridLauncher" in data
+      onceCallback('Could not find or load main class org.openqa.grid.selenium.GridLauncher'
     if /^execvp\(\)/.test(data)
       onceCallback('Failed to start selenium. Please ensure that java is in your system path')


Account: convert id data to string

Account: convert id data to string

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -898,7 +898,7 @@
       cursor.each (err, account)->
         return callback err, null if err
         if account
-            ids.push account._id
+            ids.push account._id.toString()
         else
             callback null, ids


Sidebar: Define machine owner user check function

--- workers/social/lib/social/models/sidebar.coffee
+++ workers/social/lib/social/models/sidebar.coffee
@@ -113,7 +113,15 @@

     {user, machine, successFn, failureFn} = options

-    for u in machine.users when u.owner and u.id.equals user.getId()
-      return successFn()
+    if isMachineOwner user, machine
+    then successFn()
+    else failureFn()

-    failureFn()
+
+  isMachineOwner = (user, machine) ->
+
+    for u in machine.users
+      if u.owner and u.id.equals user.getId()
+        return yes
+
+    return no


Add `failed` flag to Lexer, don't compile if set.

--- src/CoffeeScriptParser.coffee
+++ src/CoffeeScriptParser.coffee
@@ -15,9 +15,15 @@
 # ## Lexer
 class Lexer extends require('./lexer').Lexer
     constructor: (report_error) ->
-        # Override `error` to just report (instead of throwing) and return to
-        # lexing.
-        @error = (message) -> report_error @line, message
+        # Override to just report (instead of throwing) and return to lexing.
+        @error = (message) ->
+            @failed = true
+            report_error @line, message
+
+    # Reset failed flag before lexing.
+    tokenize: ->
+        @failed = false
+        return super

     # Redefine `pair` to be more forgiving for unclosed parenthesis
     pair: (tag) ->
@@ -207,7 +213,7 @@

     compile: (source) ->
         ast = @nodes(source)
-        if ast and not @parser.failed
+        if ast and not (@lexer.failed or @parser.failed)
             try
                 return ast.compile(bare: true)
             catch error


context help fixes + coordination with notifications

--- src/angles-editor-view.coffee
+++ src/angles-editor-view.coffee
@@ -200,11 +200,11 @@
       # Load ace modules #

       # ext_language_tools for autocompletion
-      ace.config.set("basePath", "../deps/")
-      ace.config.loadModule 'ace/ext/angles', () =>
-
-        @$editor.setOptions
-          enableBasicAutocompletion: true
+      ace.config.set("basePath", "../deps/")
+      ace.config.loadModule 'ext/angles', () =>
+
+        @$editor.setOptions
+          enableODDAutocompletion: true

         completer =
           getCompletions: (editor, session, pos, prefix, callback) =>
@@ -260,7 +260,6 @@

               pos = editor.getCursorPosition()
               ident = _findParent(pos.row, pos.column)
-              console.log ident
               completions = []

               children = context.getChildrenOf(ident);
@@ -281,46 +280,13 @@

         @$editor.completers = [completer]

-      # @$editor.commands.addCommand
-      #   name: 'contextHelp'
-      #   bindKey:
-      #     win: 'Ctrl-Space'
-      #     mac: 'Ctrl-Space'
-      #   exec: (editor) =>
-      #       cursor = editor.getCursorPosition()
-      #       line = editor.session.getDocument().getLine(cursor.row)
-      #       sub = line.substring(0, cursor.column)
-      #       elStart = sub.lastIndexOf('<')
-
-      #       sub2 = line.substring(elStart, line.length)
-
-      #       ident = line.match("</?([^ >]+)[^<]+$")[1]
-
-      #       @dispatcher.trigger("editor:context", ident)
-      #   readOnly: false # false if this command should not apply in readOnly mode
-
-      # @$editor.commands.addCommand
-      #   name: 'newElement'
-      #   bindKey:
-      #     win: '<'
-      #     mac: '<'
-      #   exec: (editor) =>
-      #       @$editor.commands.exec @$editor.commands.commands.startAutocomplete, @$editor
-      #       editor.getSession().insert editor.getCursorPosition(), '<'
-
-      #       # console.log @$editor.completers[0].showPopup(@$editor)
-
-      #   readOnly: false
-
-      @
-
-    # addCompletions: (completions) ->
-    #   completer =
-    #     getCompletions: (editor, session, pos, prefix, callback) ->
-    #         console.log pos
-    #         callback null, completions
-
-    #   @$editor.completers.push completer
+        ace.config.on "desc", (e) =>
+          @dispatcher.trigger("editor:context", e.ident)
+
+        ace.config.on "desc:clear", (e) =>
+          @dispatcher.trigger 'notification:clear'
+
+      @

     setContent: -> @$editor.setValue @model.get('content')


Comments can now be added to highlights after creation

--- src/annotator.readmill.coffee
+++ src/annotator.readmill.coffee
@@ -93,6 +93,7 @@
         ranges: ranges
         highlightUrl: highlight.uri
         commentUrl: ""
+        commentsUrl: highlight.comments

       @client.request(url: highlight.comments).error(deferred.reject).done (comments) ->
         if comments.length
@@ -161,6 +162,7 @@
       # Need to store this rather than data.location in order to be able to
       # delete the highlight at a later date.
       annotation.highlightUrl = highlight.uri
+      annotation.commentsUrl = highlight.comments
       @client.request(url: highlight.comments).done (comments) ->
         annotation.commentUrl = comments[0].uri if comments.length

@@ -180,10 +182,15 @@
       @connect() unless @client.isAuthorized()

   _onAnnotationUpdated: (annotation) =>
+    data = @_commentFromAnnotation annotation
     if annotation.commentUrl
-      data = @_commentFromAnnotation annotation
       request = @client.updateComment annotation.commentUrl, data
-      request.error (xhr) => @error "Unable to update annotation in Readmill"
+    else if annotation.commentsUrl
+      request = @client.createComment annotation.commentsUrl, data
+      request.done (data) =>
+        annotation.commentUrl = data.location
+
+    request.fail((xhr) => @error "Unable to update annotation in Readmill") if request

   _onAnnotationDeleted: (annotation) =>
     if annotation.highlightUrl
@@ -312,8 +319,10 @@
   deleteHighlight: (url) ->
     @request type: "DELETE", url: url

+  createComment: (url, comment) ->
+    @request type: "POST", url: url, data: {comment}
+
   updateComment: (url, comment) ->
-    # Need to provide a data filter to trim the re
     @request type: "PUT", url: url, data: {comment}

   request: (options={}) ->


Removed the prototype annotator.readmill.coffee file

--- src/annotator.readmill.coffee
+++ src/annotator.readmill.coffee
@@ -1,463 +0,0 @@
-jQuery = Annotator.$
-
-class Readmill extends Annotator.Plugin
-  @API_ENDPOINT: "http://localhost:8000"
-
-  events:
-    "annotationCreated": "_onAnnotationCreated"
-    "annotationUpdated": "_onAnnotationUpdated"
-    "annotationDeleted": "_onAnnotationDeleted"
-
-  constructor: (options) ->
-    super
-
-    @user   = null
-    @book   = @options.book
-    @view   = new Readmill.View
-    @auth   = new Readmill.Auth @options
-    @store  = new Readmill.Store
-    @client = new Readmill.Client @options
-
-    @view.subscribe "connect", @connect
-    @view.subscribe "disconnect", @disconnect
-
-    token = options.accessToken || @store.get "access-token"
-    @connected(token, silent: true) if token
-    @unsaved = []
-
-  pluginInit: () ->
-    jQuery("body").append @view.render()
-    @lookupBook().done
-
-  lookupBook: ->
-    return @book.deferred if @book.deferred
-
-    @book.deferred = if @book.id
-      @client.getBook @book.id
-    else
-      @client.matchBook @book
-
-    @book.deferred.then(@_onBookSuccess, @_onBookError).done =>
-      @view.updateBook @book
-
-  lookupReading: ->
-    @lookupBook() unless @book.id
-    jQuery.when(@book.deferred).then =>
-      data = {state: Readmill.Client.READING_STATE_OPEN}
-      request = @client.createReadingForBook @book.id, data
-      request.then(@_onCreateReadingSuccess, @_onCreateReadingError)
-
-  connect: =>
-    @auth.connect().then @_onConnectSuccess, @_onConnectError
-
-  connected: (accessToken, options) ->
-    @client.authorize accessToken
-    @client.me().then(@_onMeSuccess, @_onMeError).done =>
-      @view.login @user
-
-    @store.set "access-token", accessToken, options.expires
-
-    unless options?.silent is true
-      Annotator.showNotification "Successfully connected to Readmill"
-
-  disconnect: =>
-    @client.deauthorize()
-    @store.remove "access-token"
-    @annotator.element.find(".annotator-hl").each ->
-      jQuery(this).replaceWith this.childNodes
-
-  error: (message) ->
-    Annotator.showNotification message, Annotator.Notification.ERROR
-
-  _highlightFromAnnotation: (annotation) ->
-    # See: https://github.com/Readmill/API/wiki/Readings
-    {
-      pre: JSON.stringify(annotation.ranges)
-      content: annotation.quote
-      highlighted_at: undefined
-    }
-
-  _commentFromAnnotation: (annotation) ->
-    # Documentation seems to indicate this should be wrapped in an object
-    # with a "content" property but that does not seem to work with the
-    # POST /highlights API.
-    # See: https://github.com/Readmill/API/wiki/Readings
-    {content: annotation.text}
-
-  _annotationFromHighlight: (highlight) ->
-    ranges = try JSON.parse(highlight.pre) catch e then null
-
-    if ranges
-      deferred = new jQuery.Deferred()
-      deferred.annotation = annotation =
-        quote: highlight.content
-        text: ""
-        ranges: ranges
-        highlightUrl: highlight.uri
-        commentUrl: ""
-        commentsUrl: highlight.comments
-
-      @client.request(url: highlight.comments).error(deferred.reject).done (comments) ->
-        if comments.length
-          annotation.text = comments[0].content
-          annotation.commentUrl = comments[0].uri
-        deferred.resolve annotation
-
-      deferred.promise()
-    else
-      null
-
-  _onConnectSuccess: (params) =>
-    @connected params.access_token, params
-
-  _onConnectError: (error) =>
-    @error error
-
-  _onMeSuccess: (data) =>
-    @user = data
-    @lookupReading()
-
-  _onMeError: () =>
-    @error "Unable to fetch user info from Readmill"
-
-  _onBookSuccess: (book) =>
-    jQuery.extend @book, book
-
-  _onBookError: =>
-    @error "Unable to fetch book info from Readmill"
-
-  _onCreateReadingSuccess: (body, status, jqXHR) =>
-    {location} = JSON.parse jqXHR.responseText
-
-    if location
-      request = @client.request(url: location, type: "GET")
-      request.then @_onGetReadingSuccess, @_onGetReadingError
-    else
-      @_onGetReadingError()
-
-  _onCreateReadingError: (jqXHR) =>
-    @_onCreateReadingSuccess(null, null, jqXHR) if jqXHR.status == 409
-
-  _onGetReadingSuccess: (reading) =>
-    @book.reading = reading
-    request = @client.getHighlights(reading.highlights)
-    request.then @_onGetHighlightsSuccess, @_onGetHighlightsError
-
-  _onGetReadingError: (reading) =>
-    @error "Unable to create reading for this book"
-
-  _onGetHighlightsSuccess: (highlights) =>
-    deferreds = jQuery.map highlights, jQuery.proxy(this, "_annotationFromHighlight")
-
-    # Filter out unparsable annotations.
-    deferreds = jQuery.grep deferreds, (def) -> !!def
-    jQuery.when.apply(jQuery, deferreds).done =>
-      annotations = jQuery.makeArray(arguments)
-      @annotator.loadAnnotations annotations
-
-  _onGetHighlightsError: => @error "Unable to fetch highlights for reading"
-
-  _onCreateHighlight: (annotation, data) ->
-    # Now try and get a permalink for the comment by fetching the first
-    # comment for the newly created highlight.
-    @client.request(url: data.location).done (highlight) =>
-      # Need to store this rather than data.location in order to be able to
-      # delete the highlight at a later date.
-      annotation.highlightUrl = highlight.uri
-      annotation.commentsUrl = highlight.comments
-      @client.request(url: highlight.comments).done (comments) ->
-        annotation.commentUrl = comments[0].uri if comments.length
-
-  _onAnnotationCreated: (annotation) =>
-    if @client.isAuthorized() and @book.id
-      url = @book.reading.highlights
-
-      # Need a text string here rather than an object here for some reason.
-      comment = @_commentFromAnnotation(annotation).content
-      highlight = @_highlightFromAnnotation annotation
-
-      request = @client.createHighlight url, highlight, comment
-      request.then jQuery.proxy(this, "_onCreateHighlight", annotation), =>
-        @error "Unable to send annotation to Readmill"
-    else
-      @unsaved.push annotation
-      @connect() unless @client.isAuthorized()
-
-  _onAnnotationUpdated: (annotation) =>
-    data = @_commentFromAnnotation annotation
-    if annotation.commentUrl
-      request = @client.updateComment annotation.commentUrl, data
-    else if annotation.commentsUrl
-      request = @client.createComment annotation.commentsUrl, data
-      request.done (data) =>
-        annotation.commentUrl = data.location
-
-    request.fail((xhr) => @error "Unable to update annotation in Readmill") if request
-
-  _onAnnotationDeleted: (annotation) =>
-    if annotation.highlightUrl
-      @client.deleteHighlight(annotation.highlightUrl).error =>
-        @error "Unable to update annotation in Readmill"
-
-utils =
-  serializeQueryString: (obj, sep="&", eq="=") ->
-    esc = window.encodeURIComponent
-    ("#{esc(key)}#{eq}#{esc(value)}" for own key, value of obj).join(sep)
-
-  parseQueryString: (str, sep="&", eq="=") ->
-    obj = {}
-    decode = window.decodeURIComponent
-    for param in str.split(sep)
-      [key, value] = param.split(eq)
-      obj[decode(key)] = decode value
-    obj
-
-class View extends Annotator.Plugin
-  events:
-    ".annotator-readmill-connect a click": "_onConnectClick"
-    ".annotator-readmill-logout a click":  "_onLogoutClick"
-
-  classes:
-    loggedIn: "annotator-readmill-logged-in"
-
-  template: """
-
-
-
-

-
-
-

-

-

-    Connect with Readmill
-

-

-    Log Out
-

-  """
-
-  constructor: () ->
-    super jQuery("
").html(@template)
-
-  connect: ->
-    @publish "connect", [this]
-
-  login: (user) ->
-    @updateUser(user) if user
-    @element.addClass @classes.loggedIn
-    this
-
-  logout: ->
-    @element.removeClass(@classes.loggedIn).html(@template)
-
-    @user = null
-    @updateBook()
-
-    @publish "disconnect", [this]
-
-  updateUser: (@user=@user) ->
-    if @user
-      @element.find(".annotator-readmill-fullname").escape(@user.fullname)
-      @element.find(".annotator-readmill-username").escape(@user.username)
-      @element.find(".annotator-readmill-avatar").attr("href", @user.permalink_url)
-              .find("img").attr("src", @user.avatar_url)
-    this
-
-  updateBook: (@book=@book) ->
-    if @book
-      @element.find(".annotator-readmill-book").escape(@book.title or "Loading book…")
-    this
-
-  render: ->
-    @updateBook()
-    @updateUser()
-    @element
-
-  _onConnectClick: (event) =>
-    event.preventDefault()
-    this.connect()
-
-  _onLogoutClick: (event) =>
-    event.preventDefault()
-    this.logout()
-
-class Client
-  @API_ENDPOINT: "https://api.readmill.com"
-
-  @READING_STATE_INTERESTING: 1
-  @READING_STATE_OPEN: 2,
-  @READING_STATE_FINISHED: 3
-  @READING_STATE_ABANDONED: 4
-
-  constructor: (options) ->
-    {@clientId, @accessToken, @apiEndpoint} = options
-    @apiEndpoint = Client.API_ENDPOINT unless @apiEndpoint
-
-  me: ->
-    @request url: "/me", type: "GET"
-
-  getBook: (bookId) ->
-    @request url: "/books/#{bookId}", type: "GET"
-
-  matchBook: (data) ->
-    @request url: "/books/match", type: "GET", data: {q: data}
-
-  createBook: (book) ->
-    @request url: "/books", type: "POST", data: {book}
-
-  createReadingForBook: (bookId, reading) ->
-    @request type: "POST", url: "/books/#{bookId}/readings", data: {reading}
-
-  getHighlights: (url) ->
-    @request url: url, type: "GET"
-
-  getHighlight: (url) ->
-    @request url: url, type: "GET"
-
-  createHighlight: (url, highlight, comment) ->
-    @request type: "POST", url: url, data: {highlight, comment}
-
-  deleteHighlight: (url) ->
-    @request type: "DELETE", url: url
-
-  createComment: (url, comment) ->
-    @request type: "POST", url: url, data: {comment}
-
-  updateComment: (url, comment) ->
-    @request type: "PUT", url: url, data: {comment}
-
-  request: (options={}) ->
-    xhr = null
-
-    options.type = "GET" unless options.type
-
-    if options.url.indexOf("http") != 0
-      options.url = "#{@apiEndpoint}#{options.url}"
-
-    if options.type.toUpperCase() of {"POST", "PUT", "DELETE"}
-      options.url = "#{options.url}?&client_id=#{@clientId}"
-      options.data = JSON.stringify(options.data) if options.data
-      options.dataType = "json"
-      options.contentType = "application/json"
-    else
-      options.data = jQuery.extend {client_id: @clientId}, options.data or {}
-
-    # Trim whitespace from responses before passing to JSON.parse().
-    options.dataFilter = jQuery.trim
-
-    options.beforeSend = (jqXHR) =>
-      # Set the X-Response header to return the Location header in the body.
-      jqXHR.setRequestHeader "X-Response", "Body"
-      jqXHR.setRequestHeader "Accept", "application/json"
-      jqXHR.setRequestHeader "Authorization", "OAuth #{@accessToken}" if @accessToken
-
-    # jQuery's getResponseHeader() method is broken in Firefox when it comes
-    # to accessing CORS headers as it uses the getAllResponseHeaders method
-    # which returns an empty string. So here we provide our own xhr factory to
-    # the jQuery settings and keep a reference to the original XHR object
-    # we then monkey patch the getResponseHeader() to use the native one.
-    # See: http://bugs.jquery.com/ticket/10338
-    options.xhr = -> xhr = jQuery.ajaxSettings.xhr()
-
-    request = jQuery.ajax options
-    request.xhr = xhr
-    request.getResponseHeader = (header) -> xhr.getResponseHeader(header)
-    request
-
-  authorize: (@accessToken) ->
-
-  deauthorize: -> @accessToken = null
-
-  isAuthorized: -> !!@accessToken
-
-class Store
-  @KEY_PREFIX: "annotator.readmill/"
-  @CACHE_DELIMITER: "--cache--"
-
-  @localStorage: window.localStorage
-
-  @now: -> (new Date()).getTime()
-
-  get: (key) ->
-    value = Store.localStorage.getItem @prefixed(key)
-    if value
-      value = @checkCache value
-      @remove(key) unless value
-    JSON.parse value
-
-  set: (key, value, time) ->
-    value = JSON.stringify value
-    value = (Store.now() + time) + Store.CACHE_DELIMITER + value if time
-
-    try
-      Store.localStorage.setItem @prefixed(key), value
-    catch error
-      this.trigger 'error', [error, key, value, this]
-    this
-
-  remove: (key) ->
-    Store.localStorage.removeItem @prefixed(key)
-    this
-
-  prefixed: (key) ->
-    Store.KEY_PREFIX + key
-
-  checkCache: (value) ->
-    if value.indexOf(Store.CACHE_DELIMITER) > -1
-      # If the expiry time has passed then return null.
-      cached = value.split Store.CACHE_DELIMITER
-      value = if Store.now() > cached.shift()
-      then null else cached.join(Store.CACHE_DELIMITER)
-    value
-
-class Auth
-  @AUTH_ENDPOINT: "http://localhost:8000/oauth/authorize"
-
-  @uuid: ->
-    Auth.uuid.counter = 0 unless Auth.uuid.counter
-    "state-#{Auth.uuid.counter += 1}"
-
-  constructor: (options) ->
-    {@clientId, @callbackUrl, @authEndpoint} = options
-    @authEndpoint = Auth.AUTH_ENDPOINT unless @authEndpoint
-
-  connect: ->
-    @deferred = new jQuery.Deferred()
-    @deferred.id = Auth.uuid()
-
-    params =
-      response_type: "code"
-      client_id: @clientId
-      redirect_uri: @callbackUrl
-      state: @deferred.id
-    qs = utils.serializeQueryString(params)
-
-    Auth.callback = @callback
-
-    @popup = @openWindow "#{@authEndpoint}?#{qs}"
-    @deferred.promise()
-
-  callback: =>
-    hash = @popup.location.hash.slice(1)
-    params = qs = utils.parseQueryString(hash)
-    @popup.close()
-
-    if params.access_token# and params.state is @deferred.id
-      @deferred.resolve params
-    else
-      @deferred.reject params.error# or "bad-state"
-
-  openWindow: (url, width=725, height=575) ->
-    left = window.screenX + (window.outerWidth  - width)  / 2
-    top  = window.screenY + (window.outerHeight - height) / 2
-
-    params =
-      toolbar: no, location: 1, scrollbars: yes
-      top: top, left: left, width:  width, height: height
-
-    paramString = utils.serializeQueryString(params, ",")
-    window.open url, "readmill-connect", paramString
-
-window.Annotator.Plugin.Readmill = jQuery.extend Readmill,
-  View: View, Auth: Auth, Store: Store, Client: Client, utils: utils


use compress.drop_console option

use compress.drop_console option

--- lib/carte.coffee
+++ lib/carte.coffee
@@ -74,5 +74,5 @@
     browserify
       .bundle()
       .pipe sourceStream file
-      .pipe gulpIf(!@watching, streamify(uglify()))
+      .pipe gulpIf(!@watching, streamify(uglify(compress: {drop_console: !@watching})))
       .pipe gulp.dest dir


Client: Activity: Added key props to child elements.

--- client/activity/lib/components/common/messagelikesummary.coffee
+++ client/activity/lib/components/common/messagelikesummary.coffee
@@ -41,7 +41,7 @@
       </ProfileLinkContainer>
     )
     children.push(
-
+
         {getSeparator actorsCount, linkCount, counter}

     )
@@ -50,13 +50,13 @@

   if (diff = actorsCount - linkCount) > 0
     children.push(
-
+
         {diff} other{if diff > 1 then 's' else ''}

     )

   children.push(
-     liked this.
+     liked this.
   )

   return children


Remember mounted VMs.

Remember mounted VMs.

--- client/app/MainApp/filetree/controllers/findercontroller.coffee
+++ client/app/MainApp/filetree/controllers/findercontroller.coffee
@@ -77,16 +77,37 @@
       callback?()

     if vmNames then mountVms vmNames
-    else KD.remote.api.JVM.fetchVmsByContext {}, (err, vms)->
-      return callback? err  if err
-      if not vms or vms.length is 0
-        vms = [(KD.getSingleton 'vmController').getDefaultVmName()]
-      mountVms vms
+    else
+      groupSlug  = KD.singletons.groupsController.getGroupSlug()
+      groupSlug ?= 'koding'
+      appStorage = new AppStorage "vm-list", "1.0"
+      appStorage.fetchValue groupSlug, (vms)->
+        vms or= []
+        if vms.length > 0
+          mountVms vms
+        else
+          KD.remote.api.JVM.fetchVmsByContext {}, (err, vms)->
+            return callback? err  if err
+            if not vms or vms.length is 0
+              vms = [(KD.getSingleton 'vmController').getDefaultVmName()]
+            mountVms vms

   getVmNode:(vmName)->
     return null  unless vmName
     for path, vmItem of @treeController.nodes  when vmItem.data?.type is 'vm'
       return vmItem  if vmItem.data.vmName is vmName
+
+  saveMountState:(vmName, state)->
+    groupSlug  = KD.singletons.groupsController.getGroupSlug()
+    groupSlug ?= 'koding'
+    appStorage = new AppStorage "vm-list", "1.0"
+    appStorage.fetchValue groupSlug, (vms)->
+      vms or= []
+      if state and vmName not in vms
+        vms.push vmName
+      else if not state and vmName in vms
+        vms.splice vms.indexOf(vmName), 1
+      appStorage.setValue groupSlug, vms

   mountVm:(vm, fetchContent = yes)->
     return unless KD.isLoggedIn()
@@ -97,6 +118,8 @@

     if vmItem = @getVmNode vmName
       return warn "VM #{vmName} is already mounted!"
+
+    @saveMountState vmName, yes

     @vms.push FSHelper.createFile
       name   : "#{path}"
@@ -112,6 +135,8 @@
   unmountVm:(vmName)->
     return unless KD.isLoggedIn()
     return warn 'No such VM!'  unless vmItem = @getVmNode vmName
+
+    @saveMountState vmName, no

     if vmItem
       @stopWatching vmItem.data.path


show results if you cant vote but can see

--- vue/src/shared/models/poll_model.coffee
+++ vue/src/shared/models/poll_model.coffee
@@ -168,7 +168,7 @@
   showResults: ->
     return false if !@closingAt
     return false if @hideResultsUntilClosed && !@closedAt
-    @closedAt || (@myStance() || {}).castAt || @pleaseShowResults
+    @closedAt || !@iCanVote() || (@myStance() || {}).castAt || @pleaseShowResults

   close: =>
     @processing = true


Removing appgyver_sandbox manually

Removing appgyver_sandbox manually

--- src/steroids.coffee
+++ src/steroids.coffee
@@ -124,6 +124,11 @@
           #     Error message: #{error}
           #     """
           #   )
+
+        # Temporary removing
+        else if otherOptions[0] is "provider:remove"
+          providers = new Providers
+          providers.removeProvider "appgyver_sandbox"

         else if otherOptions[0] is "resources:list"


Removed some unused methods

Removed some unused methods

--- core/app/backbone/views/activities/activities_group_view.coffee
+++ core/app/backbone/views/activities/activities_group_view.coffee
@@ -1,23 +1,10 @@
-class window.ActivitiesGroupView extends Backbone.Marionette.CompositeView
-  itemView: Backbone.View
-  itemViewContainer: ".js-region-activities"
-
-  @new: (options)->
+window.ActivitiesGroupView =
+  new: (options)->
     switch options.model.get("action")
       when "created_comment", "created_sub_comment"
         new CreatedCommentView options
       when "followed_user"
         new FollowedUserView options
-
-  templateHelpers: ->
-    user: @user?.toJSON()
-
-  constructor: (options) ->
-    options.collection = new Backbone.Collection [options.model]
-    super(options)
-    @user = new User @collection.first().get('user')
-
-  tryAppend: (model) -> return false

 class window.CreatedCommentView extends Backbone.Marionette.Layout
   className: 'feed-activity'


Update account reset endpoint.

Update account reset endpoint.

--- src/blueprints/browser.coffee
+++ src/blueprints/browser.coffee
@@ -62,7 +62,7 @@
       @deleteUserKey()

     reset:
-      url:     '/account/reset/#{x.tokenId ? x}'
+      url:     '/account/reset'
       method:  'POST'
       expects: statusOk


Route to execute with a multipart/form-data form

--- src/routes/upload.coffee
+++ src/routes/upload.coffee
@@ -5,10 +5,38 @@
 CONFIG = require '../config'
 Request = require 'superagent'

-BackendClient = require '../backend-client'
-
 module.exports = setupRoutes = (app, opts) ->
 	opts or= {}
+
+	app.post '/api/execute', (req, res, next) ->
+		form = new Form()
+		form.parse req, (err, fields, files) ->
+			if err
+				return next new Error(err)
+			executionModel = new app.infolisSchema.models.Execution()
+			executionModel.set fields
+			executionModel.set 'status', 'PENDING'
+			console.log executionModel
+			Request
+				.post("#{CONFIG.baseURI}/#{CONFIG.apiPrefix}/execution")
+				.send(executionModel)
+				.end (err, postResp) ->
+					# console.log postResp
+					if err
+						return res.send new Error(err)
+					else if postResp.status != 201
+						return next new Error(JSON.stringify(postResp, null, 2))
+					executionUri = postResp.headers['location']
+					backUri = "#{CONFIG.backendURI}/executor?id=#{executionUri}"
+					Request
+						.post(backUri)
+						.end (err, startResp) ->
+							if err
+								return next new Error(err)
+							res.header 'Location', backUri
+							res.status 200
+							return res.end()
+

 	app.post '/upload', (req, res, next) ->
 		form = new Form()


New and improved with categories and some error checking!

--- src/scripts/mywot.coffee
+++ src/scripts/mywot.coffee
@@ -13,29 +13,30 @@
 # Author:
 #   Scott J Roberts - @sroberts

-# mywot_categories = {
-#   "101": "Malware or viruses",
-#   "102": "Poor customer experience",
-#   "103": "Phishing",
-#   "104": "Scam",
-#   "105": "Potentially illegal",
-#   "201": "Misleading claims or unethical",
-#   "202": "Privacy risks",
-#   "203": "Suspicious",
-#   "204": "Hate, discrimination",
-#   "205": "Spam",
-#   "206": "Potentially unwanted programs",
-#   "207": "Ads / pop-ups",
-#   "301": "Online tracking",
-#   "302": "Alternative or controversial medicine",
-#   "303": "Opinions, religion, politics",
-#   "304": "Other",
-#   "401": "Adult content",
-# 	"402": "Incidental nudity",
-#   "403": "Gruesome or shocking",
-#   "404": "Site for kids",
-#   "501": "Good site"
-# }
+mywot_category_mapping = {
+  101: "Malware or viruses",
+  102: "Poor customer experience",
+  103: "Phishing",
+  104: "Scam",
+  105: "Potentially illegal",
+  201: "Misleading claims or unethical",
+  202: "Privacy risks",
+  203: "Suspicious",
+  204: "Hate, discrimination",
+  205: "Spam",
+  206: "Potentially unwanted programs",
+  207: "Ads / pop-ups",
+  301: "Online tracking",
+  302: "Alternative or controversial medicine",
+  303: "Opinions, religion, politics",
+  304: "Other",
+  401: "Adult content",
+  402: "Incidental nudity",
+  403: "Gruesome or shocking",
+  404: "Site for kids",
+  501: "Good site"
+}
+

 MYWOT_API_KEY = process.env.MYWOT_API_KEY

@@ -56,21 +57,26 @@
   robot.respond /mywot (.*)/i, (msg) ->
     mywot_term = msg.match[1].toLowerCase()

-    msg.send "API URL: " + mywot_url + "hosts=#{mywot_term}/"
-
     robot.http(mywot_url + "hosts=#{mywot_term}/")
       .get() (err, res, body) ->

         mywot_json = JSON.parse body

-        msg.send mywot_json[mywot_term][0]
+        if mywot_json[mywot_term][0] == undefined
+          mywot_profile = "No MyWOT information found for #{mywot_term}"

-        mywot_trustworthiness = mywot_json[mywot_term][0]
-        mywot_childfriendliness = mywot_json[mywot_term][4]
+        else
+          mywot_trustworthiness = mywot_json[mywot_term][0]
+          mywot_childfriendliness = mywot_json[mywot_term][4]
+          mywot_categories = mywot_json[mywot_term]["categories"]

-        mywot_profile = "MyWot Result for #{mywot_term}\n---------------------------\n"
+          mywot_profile = """MyWot Result for #{mywot_term}
+          ---------------------------
+          - Trustworthiness: #{reputation(mywot_trustworthiness[0])} (Confidence: #{mywot_trustworthiness[1]}%)
+          - Child Safety:    #{reputation(mywot_childfriendliness[0])} (Confidence: #{mywot_childfriendliness[1]}%)
+          - Categories: """

-        mywot_profile += "- Trustworthiness: #{reputation(mywot_trustworthiness[0])} (Confidence: #{mywot_trustworthiness[1]}%)\n"
-        mywot_profile += "- Child Safety:    #{reputation(mywot_childfriendliness[0])} (Confidence: #{mywot_childfriendliness[1]}%)\n"
+          for key, value of mywot_categories
+            mywot_profile += "\n  - #{mywot_category_mapping[key]} (Confidence: #{value}%)"

         msg.send mywot_profile


tweaked the speak script so that it works with the newer OAuth token format, couldn't figure out how to run tests opened issue https://github.com/github/hubot-scripts/issues/801

--- src/scripts/speak.coffee
+++ src/scripts/speak.coffee
@@ -5,7 +5,8 @@
 #   None
 #
 # Configuration:
-#   HUBOT_MSTRANSLATE_APIKEY
+#   HUBOT_MSTRANSLATE_CLIENT_ID
+#   HUBOT_MSTRANSLATE_CLIENT_SECRET
 #
 # Commands:
 #   speak me <phrase> - Detects the language 'phrase' is written in, then sends back a spoken version of that phrase
@@ -16,32 +17,53 @@
 module.exports = (robot) ->
   robot.hear /(speak)( me)? (.*)/i, (msg) ->
     term   = "\"#{msg.match[3]}\""
-    apiKey = process.env.HUBOT_MSTRANSLATE_APIKEY
+    clientId = process.env.HUBOT_MSTRANSLATE_CLIENT_ID
+    clientSecret = process.env.HUBOT_MSTRANSLATE_CLIENT_SECRET
     langs = ["en"]

+    #MS changed their token service, have to use this oauth thing see http://msdn.microsoft.com/en-us/library/hh454950.aspx
+    tokenService = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13"
+    tokenScope = "http://api.microsofttranslator.com"
     getLanguagesForSpeak = "http://api.microsofttranslator.com/V2/Ajax.svc/GetLanguagesForSpeak"
     detect = "http://api.microsofttranslator.com/V2/Ajax.svc/Detect"
     speak = "http://api.microsofttranslator.com/V2/Ajax.svc/Speak"

-    unless apiKey
-      msg.send "MS Translate API key isn't set, get a key at http://www.bing.com/developers/appids.aspx"
-      msg.send "Then, set the HUBOT_MSTRANSLATE_APIKEY environment variable"
+    unless clientId
+      msg.send "MS Translate Client ID isn't set, follow steps at http://msdn.microsoft.com/en-us/library/hh454950.aspx"
+      msg.send "Then, set the HUBOT_MSTRANSLATE_CLIENT_ID environment variable"
       return

-    msg.http(getLanguagesForSpeak)
-      .query({ appId: apiKey })
-      .get() (err, res, body) ->
-        langs = eval(body) unless err
+    unless clientSecret
+      msg.send "MS Translate Client Secret isn't set, follow steps at http://msdn.microsoft.com/en-us/library/hh454950.aspx"
+      msg.send "Then, set the HUBOT_MSTRANSLATE_CLIENT_SECRET environment variable"
+      return

-        msg.http(detect)
-          .query({appId: apiKey, text: term})
+    clientId = encodeURIComponent(clientId)
+    clientSecret  = encodeURIComponent(clientSecret)
+
+    msg.http(tokenService)
+      .header('Content-Type', 'application/x-www-form-urlencoded')
+      #can you do this by passing in an object literal? i tried but it didn't work...
+      .post( "client_id=#{clientId}&client_secret=#{clientSecret}&scope=#{tokenScope}&grant_type=client_credentials") (err, res, body) ->
+        parsedBody = JSON.parse(body)
+        if err or parsedBody.error
+          msg.send "Unable to get token, #{err or parsedBody.error_description}"
+          return
+        accessToken = "Bearer " + parsedBody.access_token
+        msg.http(getLanguagesForSpeak)
+          .query({ appId: accessToken })
           .get() (err, res, body) ->
-            if err or (langs.indexOf(eval(body)) == -1)
-              msg.send "Sorry, I can't speak #{err or eval(body)}"
-              return
-            lang = eval(body)
+            langs = eval(body) unless err

-            msg.http(speak)
-              .query({ appId: apiKey, text: term, language: lang, format: "audio/wav" })
+            msg.http(detect)
+              .query({appId: accessToken, text: term})
               .get() (err, res, body) ->
-                msg.send(eval(body)) unless err
+                if err or (langs.indexOf(eval(body)) == -1)
+                  msg.send "Sorry, I can't speak #{err or eval(body)}"
+                  return
+                lang = eval(body)
+
+                msg.http(speak)
+                  .query({ appId: accessToken, text: term, language: lang, format: "audio/wav" })
+                  .get() (err, res, body) ->
+                    msg.send(eval(body)) unless err


Export keyIn.

Export keyIn.

--- src/scripts/utils.coffee
+++ src/scripts/utils.coffee
@@ -17,6 +17,7 @@
 	output

 module.exports =
+	keyIn: keyIn
 	localize: localize
 	stripDbFields: (obj) ->
 		gotMap = Immutable.Map.isMap(obj)


Support --dev flag when running unlink --all

--- src/unlinker.coffee
+++ src/unlinker.coffee
@@ -30,9 +30,10 @@
       for child in fs.list(@devPackagesPath)
         packagePath = path.join(@devPackagesPath, child)
         @unlinkPath(packagePath) if fs.isLink(packagePath)
-      for child in fs.list(@packagesPath)
-        packagePath = path.join(@packagesPath, child)
-        @unlinkPath(packagePath) if fs.isLink(packagePath)
+      unless options.argv.dev
+        for child in fs.list(@packagesPath)
+          packagePath = path.join(@packagesPath, child)
+          @unlinkPath(packagePath) if fs.isLink(packagePath)
       options.callback()
     catch error
       options.callback(error)


Removed logging.

Removed logging.

--- src/userForm.coffee
+++ src/userForm.coffee
@@ -9,8 +9,6 @@

     # Don't show "user" role as an option which is added to all users.
     roles = Meteor.roles.find({name: {$not: 'user'}}).map (role) -> role.name
-    console.log('Roles', roles)
-    console.log('All roles', Meteor.roles.find({name: {$not: 'user'}}).fetch())
     schema = new SimpleSchema
       username:
         type: String


whitespace

whitespace

--- src/client/track/index.coffee
+++ src/client/track/index.coffee
@@ -1,5 +1,5 @@
-EventBus     = require '../../utils/eventbus'
-assign = require 'object-assign'
+EventBus = require '../../utils/eventbus'
+assign   = require 'object-assign'

 class Tracker


revert typo

revert typo

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -75,5 +75,5 @@
       process = spawn execPath, ['--version']

       process.on 'exit', (code) =>
-        if code includeDeprecatedAPIs 0
+        if code is 0
           fxn()


change an instance to 'running' state when all services enter their desired state.

--- imports/server/stateReconciler.coffee
+++ imports/server/stateReconciler.coffee
@@ -1,14 +1,16 @@
 _ = require 'lodash'

-SERVICE_STATES = ['starting', 'restarting', 'running', 'stopping', 'stopped', 'removed']
+SERVICE_STATES = ['starting', 'restarting', 'running', 'stopping', 'stopped', 'removed', 'failed']

 atLeastOneWithState = (services, states...) ->
   _.reduce services, ((memo, service) -> memo or service.state in states), false
 allWithState =  (services, states...) ->
   _.reduce services, ((memo, service) -> memo and service.state in states), true
+allMeetDesiredState = (services) ->
+  _.reduce services, ((memo, service) -> memo and service.state in service.desiredState), true

 runningIsDesired = (doc, services) ->
-  if allWithState services, 'running'
+  if allMeetDesiredState services
     'running'
   else if atLeastOneWithState services, 'starting'
     'starting'


fixed potential memory leak in reset function

--- src/core/methods/reset.coffee
+++ src/core/methods/reset.coffee
@@ -8,7 +8,7 @@
     obj.update = true
     obj.first = false
   for o of obj
-    reset obj[o], o  if validObject(obj[o])
+    reset obj[o], o if o.indexOf("d3plus") < 0 and validObject(obj[o])
   return

 module.exports = reset


Update tooltip.

Update tooltip.

Throw an error when user specify invalid trigger.

--- src/directives/tooltip.coffee
+++ src/directives/tooltip.coffee
@@ -79,7 +79,7 @@

           unless enabled
             unless opts.trigger in ["hover", "click"]
-              return console.error "Invalid trigger"
+              throw "Invalid trigger"

             switch opts.trigger
               when "click"


improve selecting/deselecting row logic and grouping button enabling

--- jsapp/xlform/src/view.surveyApp.coffee
+++ jsapp/xlform/src/view.surveyApp.coffee
@@ -169,17 +169,23 @@
     forceSelectRow: (evt)->
       # forceSelectRow is used to mock the multiple-select key
       @selectRow($.extend({}, evt))
+
     deselect_all_rows: () ->
       @$('.survey__row').removeClass('survey__row--selected')
+      @activateGroupButton(false)
+      return

     deselect_rows: (evt) =>
-      if @is_selecting
-        @is_selecting = false
+      # clicking on survey__row is aleady handled, so we ignore it - we only want
+      # to deselet rows when clicking elsewhere
+      $etp = $(evt.target).parents('.survey__row')
+      if !!$etp.length
+        return
       else
         @deselect_all_rows()
       return
+
     selectRow: (evt)->
-      @is_selecting = true
       $et = $(evt.target)
       if $et.hasClass('js-blur-on-select-row') || $et.hasClass('editable-wrapper')
         return
@@ -216,15 +222,14 @@
       if $group.length > 0
         @select_group_if_all_items_selected($group)

-    questionSelect: (evt)->
+    questionSelect: () ->
       @activateGroupButton(@$el.find('.survey__row--selected').length > 0)
       return

-    activateGroupButton: (active=true)->
-      @surveyStateStore.setState({
-          groupButtonIsActive: active
-        })
-      @$('.btn--group-questions').toggleClass('btn--disabled', !active)
+    activateGroupButton: (active) ->
+      @surveyStateStore.setState({groupButtonIsActive: active})
+      $('.formBuilder-header__button--group').attr('disabled', !active)
+      return

     getApp: -> @


Remove obsolete FileError object

Remove obsolete FileError object

--- src/filesystem/asyncfs.coffee
+++ src/filesystem/asyncfs.coffee
@@ -507,9 +507,9 @@
   # Internal class
   #

-  class PseudoFileError extends FileError
+  class PseudoFileError extends DOMError
     constructor: (id) ->
-      Object.defineProperty(this, "code", value: FileError[id])
+      Object.defineProperty(this, "code", value: DOMException[id])
       name = "_#{id}"
         .replace(/_ERR$/, "_ERROR").toLowerCase()
         .replace(/_([a-z])/g, (m, c) -> c.toUpperCase())


Uncommenting method intended for injecting route's params dynamically.

--- www/src/theoricus/core/route.coffee
+++ www/src/theoricus/core/route.coffee
@@ -46,10 +46,10 @@
     @api.params = params or {}

   # inject params directly into the route
-  # inject_params:( params )->
-  #   for key, val of params
-  #     unless @api.params[key]?
-  #       @api.params[key] = val
+  inject_params:( params )->
+    for key, val of params
+      unless @api.params[key]?
+        @api.params[key] = val

   ###
   @param [String] location


Revert "bab remove"

Revert "bab remove"

This reverts commit 263e68a6c15bca2d844f2622dc9cd320a197bf65.

--- routes/index.coffee
+++ routes/index.coffee
@@ -26,7 +26,7 @@

 weather = require "./weather.js"
 hottel = require "./hottel.js"
-# bab = require "./bab.js"
+bab = require "./bab.js"
 group = require "./group.coffee"
 todoist = require "./todoist.coffee"


Fix deletion

Fix deletion

--- src/watch-project.coffee
+++ src/watch-project.coffee
@@ -18,7 +18,7 @@
           stats: stats
         }
       if name is 'unlink'
-        emitEvent(stats)
+        emitEvent()
       else
         FS.stat Path.join(projectPath, relPath), (error, stats) ->
           emitEvent(stats) unless error


fix mistake

fix mistake

--- index.cjsx
+++ index.cjsx
@@ -76,7 +76,7 @@
           if window._eventMapRanks?[mapId]?
             @mapName += @mapRanks[window._eventMapRanks[mapId]]
             realMapName += @realMapRanks[window._eventMapRanks[mapId]]
-          if @finalHP[realMapName]?
+          if @finalHP[realMapName]? && body.api_eventmap?
             if body.api_eventmap.api_now_maphp <= @finalHP[realMapName] && body.api_eventmap.api_now_maphp > 0
               @mapName += __ 'Final'
               event = new CustomEvent 'poi.map-reminder',


fix: update error handler

fix: update error handler

--- index.cjsx
+++ index.cjsx
@@ -74,7 +74,7 @@
     [response, repData] = yield request.getAsync "#{REMOTE_HOST}/#{subtitles['zh-CN'].version}"
     throw "获取字幕数据失败" unless repData
     rep = JSON.parse repData
-    throw "字幕数据异常：#{rep.error}" if rep.error
+    throw "字幕数据异常：#{rep.reason}" if rep.result is 'error'
     return unless rep.version
     for shipIdOrSth,value of rep
       if typeof value isnt "object"


Fixed stupid error

Fixed stupid error

--- index.cjsx
+++ index.cjsx
@@ -5,8 +5,6 @@

 # Components
 App = require "./components/App"
-Index = require "./components/pages/Index"
-NotFound = require "./components/pages/NotFound"
 Room = require "./components/pages/Room"
 Rooms = require "./components/pages/Rooms"


Yet another way to store window position and size

--- index.cjsx
+++ index.cjsx
@@ -17,17 +17,14 @@
 i18n.setLocale(window.language)

-window.poiStatsWindow = null
+window.plugin ?= {}
+window.plugin.poiStats =
+  window: null
+  bounds: null

 handleWindowMoveResize = ->
-  b1 = window.poiStatsWindow.getBounds()
-  # console.log "Moved to: #{JSON.stringify(b1)}"
-  setTimeout(( ->
-    b2 = window.poiStatsWindow.getBounds()
-    if JSON.stringify(b2) == JSON.stringify(b1)
-      config.set 'plugin.PoiStatistics.bounds', b2
-      # console.log "   Saved:  #{JSON.stringify(b2)}"
-  ), 5000)
+  plugin.poiStats.bounds = plugin.poiStats.window.getBounds()
+  # console.log "Poi Stats Bounds: #{JSON.stringify(plugin.poiStats.bounds)}"

 initialPoiStatsWindow = ->
   defaultBounds = {x: 0, y: 0, width: 800, height: 750}
@@ -38,11 +35,16 @@
     width: b.width
     height: b.height
     realClose: true
-  window.poiStatsWindow = newWindow
+  ps = window.plugin.poiStats
+  ps.window = newWindow
   newWindow.setMinimumSize 600, 425
   newWindow.on 'move', handleWindowMoveResize
   newWindow.on 'resize', handleWindowMoveResize
-  newWindow.on 'closed', -> window.poiStatsWindow = null
+  newWindow.on 'close', ->
+    if ps.bounds?
+      config.set 'plugin.PoiStatistics.bounds', ps.bounds
+  newWindow.on 'closed', ->
+    ps.window = ps.bounds = null
   if process.env.DEBUG?
     window.log 'Poi Stats started.'
     newWindow.openDevTools
@@ -59,7 +61,7 @@
   version: '1.2.0'
   description: __ 'PluginDesc'
   handleClick: ->
-    if window.poiStatsWindow != null
-      window.poiStatsWindow.show()
+    if window.plugin.poiStats.window?
+      window.plugin.poiStats.window.show()
     else
       initialPoiStatsWindow()


rm log

rm log

--- index.cjsx
+++ index.cjsx
@@ -13,8 +13,6 @@
     indent: '\t',
     extension: '.json'
 })
-console.log join(__dirname, 'assets', 'i18n')
-console.log window.language
 i18n.setLocale(window.language)
 {__} = i18n

@@ -249,7 +247,6 @@
         fs.writeJSONSync join(APPDATA_PATH, 'hensei-nikki', "#{@state.memberId}.json"), data
       catch e
         error "Write hensei error!#{e}"
-      console.log 'save data to hensei-nikki'
       @setState
         henseiData: data
     handleSelectTab: (selectedKey) ->


Update to 2.1.1

Update to 2.1.1

--- index.cjsx
+++ index.cjsx
@@ -2,6 +2,7 @@
 Promise = require 'bluebird'
 async = Promise.coroutine
 request = Promise.promisifyAll require 'request'
+REPORTER_VERSION = '2.1.1'

 if config.get('plugin.ShipInfo.enable', true)
   # Map lv record
@@ -42,7 +43,7 @@
             form:
               data: JSON.stringify info
             headers:
-              'User-Agent': 'Reporter v2.1.0'
+              'User-Agent': "Reporter v#{REPORTER_VERSION}"
         catch err
           console.error err
       # Create ship report
@@ -69,7 +70,7 @@
             form:
               data: JSON.stringify detail
             headers:
-              'User-Agent': 'Reporter v2.1.0'
+              'User-Agent': "Reporter v#{REPORTER_VERSION}"
         catch err
           console.error err
       # Create item report
@@ -86,7 +87,7 @@
             form:
               data: JSON.stringify info
             headers:
-              'User-Agent': 'Reporter v2.1.0'
+              'User-Agent': "Reporter v#{REPORTER_VERSION}"
         catch err
           console.error err
   # Drop ship report
@@ -110,7 +111,7 @@
         form:
           data: JSON.stringify info
         headers:
-          'User-Agent': 'Reporter v2.1.0'
+          'User-Agent': "Reporter v#{REPORTER_VERSION}"
     catch err
       console.error err
     if getEventItem
@@ -124,7 +125,7 @@
           form:
             data: JSON.stringify info
           headers:
-            'User-Agent': 'Reporter v2.1.0'
+            'User-Agent': "Reporter v#{REPORTER_VERSION}"
       catch err
         console.error err

@@ -134,4 +135,4 @@
   displayName: <FontAwesome key={0} name='pie-chart' /> 数据汇报
   description: '汇报建造数据、海域掉落数据、开发数据'
   show: false
-  version: '2.1.0'
+  version: REPORTER_VERSION


Use correct ID

Use correct ID

--- index.cjsx
+++ index.cjsx
@@ -72,10 +72,10 @@
             mapLv[map.api_id] = map.api_eventmap.api_selected_rank
       # Eventmap select report
       when '/kcsapi/api_req_map/select_eventmap_rank'
-        {_teitokuLv, _nickNameId} = window
+        {_teitokuLv, _teitokuId} = window
         info =
           teitokuLv: _teitokuLv
-          teitokuId: _nickNameId
+          teitokuId: _teitokuId
           mapareaId: parseInt(postBody.api_maparea_id) * 10 + parseInt(postBody.api_map_no)
           rank: parseInt(postBody.api_rank)
         mapLv[parseInt(postBody.api_maparea_id) * 10 + parseInt(postBody.api_map_no)] = parseInt(postBody.api_rank)
@@ -134,7 +134,7 @@
   # Drop ship report
   window.addEventListener 'battle.result', async (e) ->
     {rank, boss, map, mapCell, quest, enemy, dropShipId, enemyShipId, enemyFormation, getEventItem} = e.detail
-    {_teitokuLv, _nickName, _nickNameId} = window
+    {_teitokuLv, _nickName, _teitokuId} = window
     info =
       shipId: dropShipId
       quest: quest
@@ -157,7 +157,7 @@
       console.error err
     if getEventItem
       info =
-        teitokuId: _nickNameId
+        teitokuId: _teitokuId
         teitokuLv: _teitokuLv
         teitoku: _nickName
         mapId: map


Improve error message

Improve error message

--- lisp.coffee
+++ lisp.coffee
@@ -225,7 +225,7 @@
               else
                 error NameError, "undefined function \"#{funname.name}\"", funname.pos
           else
-            error NotFunctionError, "#{JSON.stringify(funname)}(#{funname.constructor.name}) is not a function", funname.pos
+            error NotFunctionError, "#{funname.toString()}(#{funname.constructor.name}) is not a function", funname.pos
       when 'Symbol'
         if expr.quoted
           return expr


Fixed a couple of bugs in the Exo.List. Also added some debug output for further testing.

--- list.coffee
+++ list.coffee
@@ -28,6 +28,8 @@
 				child = @getOrCreateChild item
 				child.activate()

+			console.log "children before deactivate: #{@getChildren().length}" if @debug
+
 			@deactivateAndKillOrphans(@getChildren(), collection)
 			@orderChildren(@getChildren(), collection)

@@ -39,6 +41,10 @@
 			@addChild child
 			child.prepareWithModel item
 			@append child
+			console.log "child was created: #{child.id}" if @debug
+
+		else
+			console.log "child was found: #{child.id}" if @debug

 		return child

@@ -46,10 +52,13 @@
 	deactivateAndKillOrphans: (children, collection) ->
 		orphans = children.filter (child) -> child.id not in collection.map (item) -> item.id
 		for orphan in orphans
-			orphan.bind 'on_deactivated', =>
-				@removeChild orphan
-				orphan.release()
-
+			console.log "Deactivate: #{orphan.id}" if @debug
+			if orphan.isActive() and not orphan.isBusy()
+				orphan.bind 'onDeactivated', (controller) =>
+					console.log "Remove child: #{controller.id}" if @debug
+					@removeChild controller
+					controller.release()
+
 			orphan.deactivate()

 	# Reorder the children in the DOM according to the collection order.


Group Title : fixed duplicate code

Group Title : fixed duplicate code

--- client/landing/app/Applications/Activity.kdapplication/ContentDisplays/activitycontentdisplay.coffee
+++ client/landing/app/Applications/Activity.kdapplication/ContentDisplays/activitycontentdisplay.coffee
@@ -6,22 +6,23 @@

     super options, data

-    @getContentGroupLinkPartial = (groupSlug, groupName)->
-      """
-      In #{groupName}
-      """
+    currentGroup = KD.getSingleton("groupsController").getCurrentGroup()
+
+    getContentGroupLinkPartial = (groupSlug, groupName)->
+      if currentGroup?.slug is groupSlug
+      then ""
+      else "In #{groupName}"

     @contentGroupLink = new KDCustomHTMLView
       tagName     : "span"
-      partial     : @getContentGroupLinkPartial(data.group, data.group)
+      partial     : getContentGroupLinkPartial(data.group, data.group)

-    currentGroup = KD.getSingleton("groupsController").getCurrentGroup()
     if currentGroup?.slug is data.group
-      @contentGroupLink.updatePartial @getContentGroupLinkPartial(currentGroup.slug, currentGroup.title)
+      @contentGroupLink.updatePartial getContentGroupLinkPartial(currentGroup.slug, currentGroup.title)
     else
       KD.remote.api.JGroup.one {slug:data.group}, (err, group)=>
         if not err and group
-          @contentGroupLink.updatePartial @getContentGroupLinkPartial(group.slug, group.title)
+          @contentGroupLink.updatePartial getContentGroupLinkPartial(group.slug, group.title)

     @header = new HeaderViewSection
       type    : "big"


Edited app.coffee

Edited app.coffee

--- app.coffee
+++ app.coffee
@@ -1,16 +1,12 @@
-
-###
-Module dependencies.
-###
+# Generated by CoffeeScript 1.6.3
+#Module dependencies.#
 express = require("express")
 routes = require("./routes")
 user = require("./routes/user")
 http = require("http")
 path = require("path")
 app = express()
-
-# all environments
-app.set "port", process.env.PORT or 3000
+app.set "port", process.env.PORT or 7000
 app.set "views", path.join(__dirname, "views")
 app.set "view engine", "jade"
 app.use express.favicon()
@@ -22,9 +18,7 @@
 app.use express.session()
 app.use app.router
 app.use require("stylus").middleware(path.join(__dirname, "public"))
-app.use express.static(path.join(__dirname, "public"))
-
-# development only
+app.use express["static"](path.join(__dirname, "public"))
 app.use express.errorHandler()  if "development" is app.get("env")
 app.get "/", routes.index
 app.get "/users", user.list


fixbug in csrf token

fixbug in csrf token

--- app.coffee
+++ app.coffee
@@ -90,13 +90,13 @@

       resave: true
       saveUninitialized: true
-      secret: fs.readFileSync path.join __dirname, 'session.key'
+      secret: fs.readFileSync(path.join __dirname, 'session.key').toString()

     app.use (req, res, next) ->
       unless req.session.csrf_secret
         csrf.secret (err, secret) ->
           req.session.csrf_secret = secret
-          req.session.csrf_token = csrf.token secret
+          req.session.csrf_token = csrf.create secret
           next()

       next()


actually send a response lol

actually send a response lol

--- app.coffee
+++ app.coffee
@@ -10,6 +10,10 @@
 app.use bodyParser.urlencoded
   extended: true

+send_xml = (res) =>
+  res.set 'Content-Type', 'text/xml'
+  res.send new Buffer twiml.toString()
+
 app.get '/', (req, res) ->
   twiml = new twilio.TwimlResponse()
   twiml.say 'Yo, Yolk!'
@@ -19,8 +23,7 @@
     action: '/ringonce'
   .say 'Ringing yolk'

-  res.set 'Content-Type', 'text/xml'
-  res.send new Buffer twiml.toString()
+  do send_xml res

 app.post '/ringonce', (req, res) ->
   twiml = new twilio.TwimlResponse()
@@ -29,6 +32,7 @@
     timeout: 10
     timeLimit: 5
     action: '/ring'
+  do send_xml res

 app.post '/ring', (req, res) ->
   twiml = new twilio.TwimlResponse()


settings

settings

--- app.coffee
+++ app.coffee
@@ -1,5 +1,8 @@
 ####### Framer Settings #######
 Framer.Device.deviceType = "fullscreen"
+# Remove framer cursor
+document.body.style.cursor = "auto"
+

 ####### Setup MQTT #######
 mqtt = require("npm").mqtt
@@ -75,7 +78,8 @@
 	width: Screen.width
 	y: 100
 	height: 0
-	html: 'MQTT controller Demo: http://explorative-environments.net/MQTT_for_FramerJS/'
+	html: 'MQTT controller Demo explorative-environments.net/MQTT_for_FramerJS'
+info.ignoreEvents = false


Add slide animation

Add slide animation

--- src/jquery.superslides.coffee
+++ src/jquery.superslides.coffee
@@ -11,7 +11,6 @@
     scrollable: true
   , options

-  that       = this
   $window    = $(window)
   $container = $(".#{@options.container_class}")
   $children  = $container.children()
@@ -32,6 +31,7 @@
     $control = $container.wrap($control).parent('.slides-control')

     setupContainers()
+    setupChildren()

     @start()
     this
@@ -49,7 +49,7 @@
       position: 'absolute'
       overflow: 'hidden'
       top: 0
-      # left: width
+      left: width
       zIndex: 0

     adjustSlidesSize $children
@@ -93,7 +93,6 @@
     setVerticalPosition($img)

   adjustSlidesSize = ($el) =>
-    that = this
     $el.each (i) ->
       $(this).width(width).height(height)

@@ -146,36 +145,40 @@

   animator = (upcoming_slide, callback) =>
     that           = this
-    position       = width
+    position       = width * 2
     offset         = -position
     outgoing_slide = @current
+
+    if upcoming_slide < outgoing_slide
+      position = 0
+      offset   = 0

     $children
       .removeClass('current')
       .eq(upcoming_slide)
         .addClass('current')
         .css
-          # left: position
+          left: (if init then position else width)
           display: 'block'

     $control.animate
       useTranslate3d: @mobile
-      left: 0
+      left: offset
     , @options.slide_speed
     , @options.slide_easing
     , =>
       positions(upcoming_slide)

-      # $control.css
-      #   left: -width
-
-      # $children.eq(next_index).css
-      #   left: width
-      #   zIndex: 2
+      $control.css
+        left: -width
+
+      $children.eq(upcoming_slide).css
+        left: width
+        zIndex: 2

       # reset last slide
       $children.eq(outgoing_slide).css
-        # left: width
+        left: width
         display: 'none'
         zIndex: 0

@@ -239,7 +242,7 @@
   $window
   .on 'hashchange', (e) ->
     index = parseHash()
-    that.animate(index) if index
+    _this.animate(index) if index

   .on 'resize', (e) ->
     width = $window.width()
@@ -251,11 +254,11 @@
   $(document)
   .on 'click', ".#{@options.nav_class} a", (e) ->
     e.preventDefault()
-    that.stop()
+    _this.stop()
     if $(this).hasClass('next')
-      that.animate 'next'
+      _this.animate 'next'
     else
-      that.animate 'prev'
+      _this.animate 'prev'

   initialize()


Add permissions

Add permissions

--- client/config.coffee
+++ client/config.coffee
@@ -1,3 +0,0 @@
-Accounts.ui.config
-  requestPermissions:
-    facebook: ['email', 'create_event', 'user_events']


slingshot cleanups

slingshot cleanups

--- client/images.coffee
+++ client/images.coffee
@@ -76,14 +76,11 @@

   return {}

-get_id_from_downloadUrl = (url) ->
-  parser = document.createElement('a')
-  parser.href = url
-  arr = parser.pathname.split("/")
-  if arr.length == 4
-    if arr[0] == ""
-      if arr[1]=="images"
-        _id = arr[2]
+get_id_from_key = (key) ->
+  arr = key.split("/")
+  if arr.length == 3
+    if arr[0]=="images"
+      _id = arr[1]
   return _id

 insert_image_save_func = (info, template) ->
@@ -102,19 +99,23 @@
   window.uploader.send upload_file, (error, downloadUrl) ->
     # This callback is called when the upload is complete (or on error).
     $("#show_upload_progress_id").modal("hide")
-    window.uploader = null

     if error?
+      window.uploader = null
       alert("There was an error uploading the file")
       # FIXME: do something on error
-      console.log("ERROR", error)
+      console.error(error)
       return

-    _id = get_id_from_downloadUrl( downloadUrl )
+    s3_key = window.uploader.param('key')
+    window.uploader = null
+
+    _id = get_id_from_key( s3_key )
     updater_doc =
       $set:
         status: "uploaded"
         secure_url: downloadUrl
+        s3_key: s3_key
     BinaryData.update _id, updater_doc

     # get information from referencing collection


Deleted trailing whitespace.

Deleted trailing whitespace.

--- client/import.coffee
+++ client/import.coffee
@@ -223,7 +223,7 @@
 Template.importCancelButton.events =
   'click .canceled': (e) ->
     e.preventDefault()
-    # We stop event propagation to prevent the cancel from bubbling up
+    # We stop event propagation to prevent the cancel from bubbling up
     # to hide the overlay.
     e.stopPropagation()


Add scroll class to container

Add scroll class to container

--- js/outer.coffee
+++ js/outer.coffee
@@ -141,7 +141,7 @@
   home.lib = =>
     trigger = =>
       $("#testimonials").waypoint ((event, direction) ->
-        $(".customer a").addClass("scrolled")
+        $("#testimonials").addClass("scrolled")
       ),
         offset: "80%"


New ticket URL.

New ticket URL.

--- lib.coffee
+++ lib.coffee
@@ -12,7 +12,7 @@
     # So we can use view's autorun only for the first. This means that in a computed field
     # nested inside a view's autorun one cannot access Template.instance().
     # TODO: Fix this. Remove "not Tracker.active" condition.
-    #       See: https://github.com/meteor/meteor/issues/4494
+    #       See: https://github.com/meteor/blaze/issues/148
     #       See: https://github.com/peerlibrary/meteor-computed-field/issues/5
     if not Tracker.active and currentView = Package.blaze?.Blaze?.currentView
       autorun = (f) ->


Bugfix.

Bugfix.

--- lib.coffee
+++ lib.coffee
@@ -438,8 +438,9 @@

 # We copy utility methods ($, findAll, autorun, subscribe, etc.) from the template instance prototype.
 for methodName, method of Blaze.TemplateInstance::
-  BlazeComponent::[methodName] = (args...) ->
-    @templateInstance[methodName] args...
+  do (methodName, method) ->
+    BlazeComponent::[methodName] = (args...) ->
+      @templateInstance[methodName] args...

 argumentsConstructor = ->
   # This class should never really be created.


fix for firefox WYSIWYG pasting woes

fix for firefox WYSIWYG pasting woes

--- @client/form.coffee
+++ @client/form.coffee
@@ -355,9 +355,6 @@
   min-height: 120px;
   outline: none;
 }
-.ql-clipboard {
-  display: none;
-}
 .ql-editor.ql-blank::before{
   content: attr(data-placeholder);
   pointer-events: none;


remove buildKiteJs task from Cakefile

remove buildKiteJs task from Cakefile

--- Builder.coffee
+++ Builder.coffee
@@ -65,25 +65,17 @@
       retina   : '@2x'
     , (err, helper)=>
       spriteHelper = helper
-      @buildKiteJs =>
-        @buildClient options
+      @buildClient options

   buildFramework:->

     @config ?= require('koding-config-manager').load("main.#{options.configFile}")
-    cmd = "cd client/Framework;npm i;gulp compile --buildVersion=#{@config.client.version} --outputDir=../../website/a/"
+    cmd = "cd client/Framework && npm i && gulp compile --buildVersion=#{@config.client.version} --outputDir=../../website/a/"
     exec cmd, (err, stdout, stderr)->
       console.warn "------------------------ FRAMEWORK COMPILED -------------------------- "
       console.warn " To use watcher for Framework use following command in different tab:  "
       console.log  " $ #{cmd.replace 'compile ', ''} "
       console.warn "------------------------ FRAMEWORK COMPILED -------------------------- "
-
-  buildKiteJs: (callback) ->
-    exec "cd kite.js;npm i;gulp", (err, stdout, stderr) ->
-      console.log "------------------------- KITEJS COMPILED -------------------------"
-      console.log "STDOUT: #{ stdout }"
-      console.log "STDERR: #{ stderr }"
-      callback()

   buildClient: (options) ->
     @config ?= require('koding-config-manager').load("main.#{options.configFile}")


put back debugging in test environment

put back debugging in test environment

This commit fixes a previous wrong commit that was removing the capability to
display logs in test environment.

--- main.coffee
+++ main.coffee
@@ -62,16 +62,16 @@
         text

     info: (texts...) ->
-        if process.env.NODE_ENV isnt 'test'
+        if process.env.DEBUG or process.env.NODE_ENV isnt 'test'
             console.info @format 'info', texts

     warn: (texts...) ->
-        if process.env.NODE_ENV isnt 'test'
+        if process.env.DEBUG or pprocess.env.NODE_ENV isnt 'test'
             console.info @format 'warn', texts
             console.warn @format 'warn', texts

     error: (texts...) ->
-        if process.env.NODE_ENV isnt 'test'
+        if process.env.DEBUG or process.env.NODE_ENV isnt 'test'
             console.info @format 'error', texts
             console.error @format 'error', texts


More understandable variable name

More understandable variable name

--- main.coffee
+++ main.coffee
@@ -36,11 +36,11 @@
         nodeReg = /at\s+(.*)\s+\((.*):(\d*):(\d*)\)/gi;
         browserReg = /at\s+()(.*):(\d*):(\d*)/gi;

-        s = stacklist[0]
-        sp = nodeReg.exec(s) or browserReg2.exec(s)
+        firstLineStack = stacklist[0]
+        fileAndLineInfos = nodeReg.exec(firstLineStack) or browserReg2.exec(firstLineStack)

-        filePath = sp[2].substr(process.cwd().length)
-        line = sp[3]
+        filePath = fileAndLineInfos[2].substr(process.cwd().length)
+        line = fileAndLineInfos[3]
         return ".#{filePath}:#{line} |"

     format: (level, texts) ->


Never stop attempting to reconnect

Never stop attempting to reconnect

--- lib/assets/javascripts/cable.js.coffee
+++ lib/assets/javascripts/cable.js.coffee
@@ -2,7 +2,6 @@
 #= require_tree .

 class @Cable
-  MAX_CONNECTION_ATTEMPTS: 10
   MAX_CONNECTION_INTERVAL: 5 * 1000
   PING_STALE_INTERVAL: 6

@@ -53,11 +52,8 @@
     @disconnected()

     setTimeout =>
-      if @isMaxConnectionAttemptsReached()
-        @giveUp()
-      else
-        @incrementConnectionAttemptsCount()
-        @connect()
+      @incrementConnectionAttemptsCount()
+      @connect()
     , @generateReconnectInterval()

   resetConnectionAttemptsCount: =>
@@ -65,9 +61,6 @@

   incrementConnectionAttemptsCount: =>
     @connectionAttempts += 1
-
-  isMaxConnectionAttemptsReached: =>
-    @connectionAttempts > @MAX_CONNECTION_ATTEMPTS

   generateReconnectInterval: () ->
     interval = (Math.pow(2, @connectionAttempts) - 1) * 1000


Optimize coffee

Optimize coffee

--- test/decomposer_test.coffee
+++ test/decomposer_test.coffee
@@ -1,9 +1,9 @@
-expect = require('chai').expect
+{expect} = require('chai')
 through = require('through2')
 path = require('path')
 fs = require('fs')
 mocks = require('mocks')
-loadFile = mocks.loadFile
+{loadFile} = mocks
 gulp = require('gulp')
 decomposer = require('../decomposer')


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -18,7 +18,7 @@
     x: 5
     y: 10

-
+  engin

 test "#find", ->
   engine = Engine()


Move log to after successful sign up

Move log to after successful sign up

--- src/smc-webapp/r_account.cjsx
+++ src/smc-webapp/r_account.cjsx
@@ -186,8 +186,6 @@
             return
         @setState
             state : 'saving'
-        if @props.is_anonymous
-            log("email_sign_up", {source: "anonymous_account"});
         webapp_client.change_email
             new_email_address : @state.email_address
             password          : @state.password
@@ -199,6 +197,8 @@
                         state    : 'edit'
                         error    : "Error -- #{err}"
                 else
+                    if @props.is_anonymous
+                        log("email_sign_up", {source: "anonymous_account"});
                     @setState
                         state    : 'view'
                         error    : ''


better error handling for listing resources

--- src/steroids/Providers.coffee
+++ src/steroids/Providers.coffee
@@ -146,21 +146,30 @@
       )

   resources: () =>
+    console.log "Listing all resources..."
     @config_api.get("/app/#{@getAppId()}/service_providers.json", (err, req, res, obj) =>
-      obj.forEach (providerObject) =>
-        console.log 'Listing all resources...'
-
-        @config_api.get("/app/#{@getAppId()}/service_providers/#{providerObject.uid}/resources.json", (err, req, res, obj) =>
-          console.log "\nProvider: #{providerObject.name}"
-
-          obj.forEach (resource) ->
-            console.log "  #{resource.name}"
-            resource.columns.forEach (column) ->
-              console.log "    #{column.name}:#{column.type}"
-
-          console.log ''
-          @config_api.close()
-        )
+      if err?
+        errorObject = JSON.parse(err)
+        Help.error()
+        console.log "Could not list resources. Error message: "
+      else if obj.length is 0
+        Help.error()
+        console.log "No providers found. Add a provider to list resources."
+        process.exit(1)
+      else
+        obj.forEach (providerObject) =>
+
+          @config_api.get("/app/#{@getAppId()}/service_providers/#{providerObject.uid}/resources.json", (err, req, res, obj) =>
+            console.log "\nProvider: #{providerObject.name}"
+
+            obj.forEach (resource) ->
+              console.log "  #{resource.name}"
+              resource.columns.forEach (column) ->
+                console.log "    #{column.name}:#{column.type}"
+
+            console.log ''
+            @config_api.close()
+          )
     )

   addResource: (provider_name, params) =>


Fail with an error if provider cannot be added

--- src/steroids/Providers.coffee
+++ src/steroids/Providers.coffee
@@ -87,7 +87,8 @@
         # TODO: deside what to do with empty provider
       (error) =>
         console.log "SandboxDB provider not found."
-        self.addProvider(provider_name).then (provider)->
+        self.addProvider(provider_name).then( (provider)->
+          # What to do when successfully added
           self.initResourceProvider(provider).then( ->
             deferred.resolve(provider)
           ).fail (err)->
@@ -95,13 +96,16 @@
           # TODO: remove the remove when finished
           self.removeProvider("appgyver_sandbox").then (res)->
             console.log 'pr removed'
+        ).fail (err)->
+          # What to do if adding a provider failed
+          deferred.reject err
     )
     deferred.promise

   addProvider: (provider_name, data) =>
     deferred = q.defer()

-    console.log "Adding a provider"
+    console.log "Adding a provider..."

     data = data || @_getDefaultProviderData(provider_name)


tasks: a little more to make it readonly -- don't allow creating new tasks (obviously)

--- salvus/page/tasks.coffee
+++ salvus/page/tasks.coffee
@@ -56,6 +56,7 @@
                     @readonly = @db.readonly
                     if @readonly
                         @save_button.text("Readonly")
+                        @element.find("a[href=#create-task]").remove()
                     @tasks = @db.select()
                     @render_task_list()
                     @set_clean()
@@ -508,6 +509,8 @@
         @element.find(".salvus-tasks-create-button").addClass('disabled')

     create_task: () =>
+        if @readonly
+            return
         if @tasks.length == 0
             position = 0
         else
@@ -566,9 +569,13 @@
         @set_showing_deleted(@showing_deleted)
         @element.find(".salvus-task-search-not-deleted").click(=> @set_showing_deleted(true))
         @element.find(".salvus-task-search-deleted").click(=> @set_showing_deleted(false))
+        if @readonly
+            return
         @element.find(".salvus-task-empty-trash").click(@empty_trash)

     empty_trash: () =>
+        if @readonly
+            return
         bootbox.confirm "

Permanently erase the deleted items?

 Old versions of this list are available as snapshots.  ", (result) =>
             if result == true
                 @db.delete


:palm_tree::guitar: Updated in browser at strd6.github.io/editor

--- pixie.cson
+++ pixie.cson
@@ -1,4 +1,4 @@
-version: "0.3.1-pre.6"
+version: "0.3.1"
 dependencies:
   composition: "distri/compositions:v0.1.1"
   observable: "distri/observable:v0.1.1"


:a::warning: Updated in browser at strd6.github.io/editor

--- pixie.cson
+++ pixie.cson
@@ -1,6 +1,7 @@
-version: "0.2.0"
+version: "0.2.1"
 remoteDependencies: [
   "//code.jquery.com/jquery-1.10.1.min.js"
+  "//cdnjs.cloudflare.com/ajax/libs/coffee-script/1.6.3/coffee-script.min.js"
   "http://strd6.github.io/tempest/javascripts/envweb.js"
-  "http://strd6.github.io/require/v0.2.1.js"
+  "http://strd6.github.io/require/v0.2.2.js"
 ]


planメッセージへのリアクションにのみ反応するよう修正

planメッセージへのリアクションにのみ反応するよう修正

--- scripts/plan.coffee
+++ scripts/plan.coffee
@@ -41,15 +41,6 @@
       if item.id == new_id
         new_id += 1

-    party = {
-      'id': new_id,
-      'day': day,
-      'time': time,
-      'min': min,
-      'max': max
-    }
-    storage.registParty(robot, party)
-
     msg.send "#{day}の#{time}からボードゲーム会を開催します。\n
 最小開催人数は#{min}人、最大人数は#{max}人までとなります。"

@@ -60,9 +51,33 @@
         curr.then(-> addReaction(name, channelId, res.ts))
       , Promise.resolve())

+      party = {
+        'id': new_id,
+        'day': day,
+        'time': time,
+        'min': min,
+        'max': max,
+        'timestamp': res.ts,
+        'channelId': channelId
+      }
+      storage.registParty(robot, party)
+      console.log("registParty timestamp: #{res.ts}, channelId: #{channelId}")
+
   robot.adapter.client.on 'raw_message', (message) ->
+    return if message.type isnt 'reaction_added' && message.type isnt 'reaction_removed'
+
     robotId = robot.adapter.client.getUserByName(robot.name).id
     user = robot.adapter.client.getUserByID(message.user)
+    parties = storage.getParties(robot)
+    ts = message.item.ts
+    channelId = message.item.channel
+    found = false
+    parties.map (item) ->
+      if item.timestamp is ts && item.channelId is channelId
+        found = true
+
+    if found == false
+      return
     if (/^reaction_added$/.test message.type) && (message.user isnt robotId)
       if /^o$/.test message.reaction
         ms = "#{user.name}さんが参加しました。"


toggle issue state fix

toggle issue state fix

--- lib/moustache-issue-detail-view.coffee
+++ lib/moustache-issue-detail-view.coffee
@@ -56,6 +56,7 @@
     _toggleButton = @toggleButton

     if moustacheIssue.state == "open"
+      moustacheIssue.state = "closed"
       _toggleButton.addClass("open").text("Reopen Issue")
       moustacheGithub.issues.edit {
         user:moustacheRepository.owner.login,
@@ -65,6 +66,7 @@
         }, (err) ->
           console.log "Issue closed"
     else
+      moustacheIssue.state = "open"
       _toggleButton.removeClass("open").text("Close Issue")
       moustacheGithub.issues.edit {
         user:moustacheRepository.owner.login,


make sure @data exists

make sure @data exists

--- lib/psd/layer_info/placed_layer.coffee
+++ lib/psd/layer_info/placed_layer.coffee
@@ -9,6 +9,7 @@
       @file.seek 12, true # Skip sig
       @data = new Descriptor(@file).parse()
     catch e
+      @data = {}
       @data['Trnf'] = [0, 0, 0, 0, 0, 0, 0, 0]

   transform: ->


starts a manager if configured and shares it on the definition config

--- src/management/manager.coffee
+++ src/management/manager.coffee
@@ -42,4 +42,6 @@
             transport, address, port

-    return api = register: local.register+    return api =
+        register: local.register
+


Notify AppCache updates on load

Notify AppCache updates on load

--- assets/javascripts/app/appcache.coffee
+++ assets/javascripts/app/appcache.coffee
@@ -8,6 +8,7 @@

   constructor: ->
     @cache = applicationCache
+    @notifyUpdate = true
     @onUpdateReady() if @cache.status is @cache.UPDATEREADY

     $.on @cache, 'progress', @onProgress


The modeling source has a key variable.

The modeling source has a key variable.

--- test/unit/routerSpec.coffee
+++ test/unit/routerSpec.coffee
@@ -25,30 +25,34 @@
           birth_date: moment([1961, 6, 7]).valueOf()
           scan_sets:
             t1:
-              modeling:
+              modeling: [
                 results: [
                   delta_k_trans:
                     average: 2.6
                 ]
+              ]
             t2:
-              modeling:
+              modeling: [
                 results: [
                   delta_k_trans:
                     average: 2.4
                 ]
+              ]
           registration_configurations:
             reg_01:
-              modeling:
+              modeling: [
                 results: [
                   delta_k_trans:
                     average: 2.3
                 ]
+              ]
             reg_02:
-              modeling:
+              modeling: [
                 results: [
                   delta_k_trans:
                     average: 2.1
                 ]
+              ]
           sessions: [
             number: 1
             acquisition_date: moment('July 1, 2013').valueOf()
@@ -148,11 +152,11 @@
           # The first modeling is for the T1 scan.
           mdl = subject.modeling[0]
           expect(mdl.source, "Modeling is missing a source").to.exist
-          expect(mdl.source.scanType, "Modeling source is missing the scan type")
+          expect(mdl.source.key, "Modeling source is missing the key")
             .to.exist
-          expect(mdl.source.scanType, "Modeling source scan type is incorrect")
+          expect(mdl.source.key, "Modeling source key is incorrect")
             .to.equal('t1')
-          mockMdl = mock.subject_detail.scan_sets.t1.modeling
+          mockMdl = mock.subject_detail.scan_sets.t1.modeling[0]
           expect(mdl.results, "Modeling is missing results").to.exist
           expect(mdl.results.length, "Modeling results count is incorrect").
             to.equal(1)
@@ -184,17 +188,17 @@
           $httpBackend.flush()

       describe 'Session Detail', ->
-        mock_session = null
+        mockSession = null

         # Validates the resolved session.
         validate = (session) ->
           scan = session.scans.t1
           expect(scan, "Session is missing the T1 scan").to.exist
-          mock_scan = mock.session_detail.scans.t1
+          mockScan = mock.session_detail.scans.t1
           expect(scan.intensity, "Session scan is incorrect")
-            .to.deep.eql(mock_scan.intensity)
+            .to.deep.eql(mockScan.intensity)
           expect(scan.intensity, "Session scan is incorrect")
-            .to.deep.eql(mock_scan.intensity)
+            .to.deep.eql(mockScan.intensity)
           reg = scan.registrations[0]
           expect(reg, "Session is missing the T1 registration").to.exist
           mock_reg = mock.session_detail.scans.t1.registrations[0]
@@ -202,43 +206,43 @@
             .to.deep.eql(mock_reg.intensity)

         beforeEach ->
-          mock_session = mock.subject_detail.sessions[0]
+          mockSession = mock.subject_detail.sessions[0]
           # The session subject reference is set in the routes
           # session resolution.
-          mock_session.subject = mock.subject
+          mockSession.subject = mock.subject

         it 'should fetch the session detail', ->
-          session = _.clone(mock_session)
+          session = _.clone(mockSession)
           Router.getSessionDetail(session).then ->
             validate(session)
           # Dispatch the backend request.
           $httpBackend.flush()

       describe 'Scan', ->
-        mock_session = null
+        mockSession = null

         beforeEach ->
-          mock_session = _.clone(mock.subject_detail.sessions[0])
+          mockSession = _.clone(mock.subject_detail.sessions[0])
           # The session subject reference is set in the routes
           # session resolution.
-          mock_session.subject = mock.subject
+          mockSession.subject = mock.subject

         it 'should find the scan container in a fetched session', ->
-          session = _.clone(mock_session)
+          session = _.clone(mockSession)
           session.scans = mock.session_detail.scans
-          mock_scan = mock.session_detail.scans.t1
-          actual = Router.getScan(session, mock_scan.name)
+          mockScan = mock.session_detail.scans.t1
+          actual = Router.getScan(session, mockScan.name)
           expect(actual, 'Image container missing').to.exist
           expect(actual, 'Image container incorrect')
             .to.equal(mock.session_detail.scans.t1)

         it 'should fetch a session if necessary to obtain the scan', ->
-          session = _.clone(mock_session)
-          mock_scan = mock.session_detail.scans.t1
-          Router.getScan(session, mock_scan.name).then (container) ->
+          session = _.clone(mockSession)
+          mockScan = mock.session_detail.scans.t1
+          Router.getScan(session, mockScan.name).then (container) ->
             expect(container, 'Image container missing').to.exist
             expect(container.intensity, 'Image container intensity incorrect').
-              to.eql(mock_scan.intensity)
+              to.eql(mockScan.intensity)

           # Dispatch the backend request.
           $httpBackend.flush()


fixed extra space

fixed extra space

--- lib/Acho.coffee
+++ lib/Acho.coffee
@@ -76,4 +76,4 @@
     messageType = @colorize colorType, messageType
     message     = @outputMessage message
     message     = @colorize @types.line.color, message
-    console.log "#{messageType} #{message}"
+    console.log messageType + message


Reset settingsActive flag in router

Reset settingsActive flag in router

--- lib/base.coffee
+++ lib/base.coffee
@@ -13,6 +13,7 @@
     searchFocused: false
     adminActive: false
     libraryActive: false
+    settingsActive: false
     currentCollectionId: null
     currentCollectionSlug: null
     currentPublicationId: null


Updated model required field for path and method

--- src/model/transactions.coffee
+++ src/model/transactions.coffee
@@ -3,11 +3,11 @@

 # Request Schema definition
 RequestDef =
-  "path" :{ type: String, required: true }
+  "path" :{ type: String, required: false }
   "headers": {type: Object}
   "querystring": { type: String }
   "body":{ type: String}
-  "method":{ type: String, required: true }
+  "method":{ type: String, required: false }
   "timestamp":{ type: Date, required: true }

 # Response Schema definition


Fix #14

Fix #14

--- lib/ghci.coffee
+++ lib/ghci.coffee
@@ -115,7 +115,8 @@
     @history.curr = ''
     @history.item = @history.back.length
     @timeout = setTimeout (=>
-      tkill @ghci.pid, 'SIGINT'
+      if @ghci?
+        tkill @ghci.pid, 'SIGINT'
       @finished = true
       @timeout = null
       ), 1000


Curry instead of the traditional closure

--- components/Collate.coffee
+++ components/Collate.coffee
@@ -2,28 +2,29 @@

 # The actual collation algorithm, returns a closure with the control fields
 # that can be used with Array.prototype.sort()
-sortByControlFields = (fields) ->
-  (a, b) ->
-    # If there are no control fields specified we can't sort
-    return 0 unless fields.length
+sortByControlFields = (fields, a, b) ->
+  # If there are no control fields specified we can't sort
+  return 0 unless fields.length

-    # Comparison of a single control field
-    sort = (left, right) ->
-      # Lowercase strings to they always sort correctly
-      left = left.toLowerCase() if typeof left is 'string'
-      right = right.toLowerCase() if typeof right is 'string'
+  # Comparison of a single control field
+  sort = (left, right) ->
+    # Lowercase strings to they always sort correctly
+    left = left.toLowerCase() if typeof left is 'string'
+    right = right.toLowerCase() if typeof right is 'string'

-      return 0 if left is right
-      return 1 if left > right
-      -1
+    return 0 if left is right
+    return 1 if left > right
+    -1

-    # Traverse the fields until you find one to sort by
-    for field in fields
-      order = sort a.payload[field], b.payload[field]
-      return order unless order is 0
+  # Traverse the fields until you find one to sort by
+  for field in fields
+    order = sort a.payload[field], b.payload[field]
+    return order unless order is 0

-    # All fields were the same, so order doesn't matter
-    0
+  # All fields were the same, send in order of appearance
+  if @indexOf(a) < @indexOf(b)
+    return -1
+  1

 # Sending the collated objects to the output port together with bracket IPs
 sendWithGroups = (packets, fields, port) ->
@@ -85,7 +86,8 @@
       # Receive the packets
       packets = c.inPorts.in.buffer.filter (packet) -> packet.event is 'data'
       # Sort them by control fields if there are any
-      packets.sort sortByControlFields fields if fields.length
+      original = packets.slice 0
+      packets.sort sortByControlFields.bind original, fields
       # Send them out
       sendWithGroups packets, fields, c.outPorts.out
       # Prepare for the next set to collate


Fix a coffeescript lint warning

Fix a coffeescript lint warning

--- lib/init.coffee
+++ lib/init.coffee
@@ -277,7 +277,7 @@
       # Return fast parser flag and the result.
       return [fastParser, result]

-    ), (err) =>
+    ), (err) ->
       # Well, Well, Well... something went wrong.
       # Instead of crashing or giving cryptic error message, let's try to be user friendly.
       if err.message.match /^Failed\sto\sspawn\scommand\s.+\.\sMake\ssure\s.+\sis\sinstalled\sand\son\syour\sPATH$/gi


Added protocol to dependencies

Added protocol to dependencies

--- pixie.cson
+++ pixie.cson
@@ -1,8 +1,8 @@
 version: "0.1.0"
 entryPoint: "editor"
 remoteDependencies: [
-  "//code.jquery.com/jquery-1.10.1.min.js"
-  "//cdnjs.cloudflare.com/ajax/libs/coffee-script/1.6.3/coffee-script.min.js"
+  "https://code.jquery.com/jquery-1.10.1.min.js"
+  "https://cdnjs.cloudflare.com/ajax/libs/coffee-script/1.6.3/coffee-script.min.js"
   "https://pixipaint.net/envweb-v0.4.7.js"
 ]
 dependencies:


Basic OPTIONS support for CORS

Basic OPTIONS support for CORS

--- server/files.coffee
+++ server/files.coffee
@@ -18,9 +18,16 @@
     url = url.parse req.url, true
     req.query = url.query
     match = url.path.match fileRe
-    unless req.method in ['GET', 'HEAD'] and match?
+    unless req.method in ['GET', 'HEAD', 'OPTIONS'] and match?
       return next()
     msgId = match[1]
+
+    if req.method == 'OPTIONS'
+      res.writeHead 204,
+        Allow: 'OPTIONS, GET, HEAD'
+        'Cache-Control': 'stale-while-revalidate'
+        'Access-Control-Allow-Origin': allowCors
+      return res.end()

     ## handle_auth()
     req.cookies = cookie.parse req.headers.cookie if req.headers.cookie?


Register command on .editor selector

Register command on .editor selector

--- lib/link.coffee
+++ lib/link.coffee
@@ -1,6 +1,6 @@
 module.exports =
   activate: ->
-    atom.workspaceView.command 'link:open', ->
+    atom.workspaceView.command 'link:open', '.editor', ->
       editor = atom.workspaceView.getActivePaneItem()
       return unless editor?


expose commands to control list selection

--- lib/main.coffee
+++ lib/main.coffee
@@ -16,6 +16,8 @@
     atom.commands.add 'atom-workspace', {
       'recent-files-fuzzy-finder:toggle-finder': => @createRecentFilesView().toggle()
       'recent-files-fuzzy-finder:remove-closed-files': => @recentFiles.removeClosed()
+      'recent-files-fuzzy-finder:select-next-item': => @createRecentFilesView().selectNextItemView()
+      'recent-files-fuzzy-finder:confirm-selection': => @createRecentFilesView().confirmSelection()
     }

   createRecentFiles: (state) ->


cleanup

cleanup

--- lib/main.coffee
+++ lib/main.coffee
@@ -50,13 +50,11 @@
     @history = new History
     @emitter = new Emitter

-    # @subscriptions.add atom.commands.add 'atom-workspace',
-    # @subscriptions.add atom.commands.add 'atom-workspace',
     @subscriptions.add atom.commands.add 'atom-text-editor',
-      'cursor-history:next': ({target}) => @jump(target.getModel(), 'next')
-      'cursor-history:prev': ({target}) => @jump(target.getModel(), 'prev')
-      'cursor-history:next-within-editor': ({target}) => @jump(target.getModel(), 'next', withinEditor: true)
-      'cursor-history:prev-within-editor': ({target}) => @jump(target.getModel(), 'prev', withinEditor: true)
+      'cursor-history:next': ({target}) => @jump(target, 'next')
+      'cursor-history:prev': ({target}) => @jump(target, 'prev')
+      'cursor-history:next-within-editor': ({target}) => @jump(target, 'next', withinEditor: true)
+      'cursor-history:prev-within-editor': ({target}) => @jump(target, 'prev', withinEditor: true)
       'cursor-history:clear': => @history.clear()
       'cursor-history:toggle-debug': -> settings.toggle 'debug', log: true

@@ -153,7 +151,8 @@
     else
       @emitter.emit('did-unfocus', {oldLocation})

-  jump: (editor, direction, {withinEditor}={}) ->
+  jump: (editorElement, direction, {withinEditor}={}) ->
+    editor = editorElement.getModel()
     wasAtHead = @history.isAtHead()
     if withinEditor
       entry = @history.get(direction, URI: editor.getURI())


disable unused

disable unused

--- lib/main.coffee
+++ lib/main.coffee
@@ -1,10 +1,10 @@
 {CompositeDisposable, Color} = require 'atom'

-Config =
-  invalidate:
-    type: 'string'
-    default: 'inside'
-    enum: ['never', 'surround', 'overlap', 'inside', 'touch']
+# Config =
+#   invalidate:
+#     type: 'string'
+#     default: 'inside'
+#     enum: ['never', 'surround', 'overlap', 'inside', 'touch']

 module.exports =
   config: Config


Fix build-after-save not working bug

Fix build-after-save not working bug

--- lib/main.coffee
+++ lib/main.coffee
@@ -48,7 +48,7 @@
         if atom.config.get('atom-latex.build_after_save') and \
             editor.buffer.file?.path and \
             path.extname(editor.buffer.file?.path) == '.tex'
-          @latex.builder.build
+          @latex.builder.build()

   deactivate: ->
     @latex?.dispose()


Don't watch editors more than once if they're re-added to the workspace

--- lib/main.coffee
+++ lib/main.coffee
@@ -42,6 +42,8 @@
     @viewsByEditor = new WeakMap
     @contextMenuEntries = []
     @subs.add atom.workspace.observeTextEditors (editor) =>
+      return if @viewsByEditor.has(editor)
+
       # For now, just don't spell check large files.
       return if editor.largeFileMode


fix context test

fix context test

--- test/context.test.coffee
+++ test/context.test.coffee
@@ -3,6 +3,8 @@
 CoffeeScriptCell = require '../app/coffeescript_cell'
 React = require 'react'
 $ = require 'jquery'
+
+require('../app/modules').init_modules([])

 eval_coffeescript_in_context = (run_context, string) ->
   Context.run_in_context run_context, CoffeeScriptCell.create_fn string


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -1,6 +1,6 @@
 module "Controllable"

 test "should move player left while key is pressed", ->
-  one = 1
+  player

 module()