Use fs global in test instead of assigning individual functions

This will make it easier to mock fs.

--- spec/text-buffer-spec.coffee
+++ spec/text-buffer-spec.coffee
@@ -1,10 +1,10 @@
-{existsSync, moveSync, readFileSync, removeSync, writeFileSync} = require 'fs-plus'
+fs = require 'fs-plus'
 {join} = require 'path'
 temp = require 'temp'
 {File} = require 'pathwatcher'
 Random = require 'random-seed'
 TextBuffer = require '../src/text-buffer'
-SampleText = readFileSync(join(__dirname, 'fixtures', 'sample.js'), 'utf8')
+SampleText = fs.readFileSync(join(__dirname, 'fixtures', 'sample.js'), 'utf8')

 describe "TextBuffer", ->
   buffer = null
@@ -59,16 +59,16 @@

       describe "when a file exists for the path", ->
         it "loads the contents of that file", ->
-          expect(buffer.getText()).toBe readFileSync(filePath, 'utf8')
+          expect(buffer.getText()).toBe fs.readFileSync(filePath, 'utf8')

         it "does not allow the initial state of the buffer to be undone", ->
           buffer.undo()
-          expect(buffer.getText()).toBe readFileSync(filePath, 'utf8')
+          expect(buffer.getText()).toBe fs.readFileSync(filePath, 'utf8')

       describe "when no file exists for the path", ->
         it "is not modified and is initially empty", ->
           filePath = "does-not-exist.txt"
-          expect(existsSync(filePath)).toBeFalsy()
+          expect(fs.existsSync(filePath)).toBeFalsy()
           buffer = new TextBuffer({filePath, load: true})
           expect(buffer.isModified()).not.toBeTruthy()
           expect(buffer.getText()).toBe ''
@@ -837,7 +837,7 @@

       beforeEach ->
         filePath = temp.openSync('atom').path
-        writeFileSync(filePath, "words")
+        fs.writeFileSync(filePath, "words")
         buffer = new TextBuffer({filePath, load: true})

         waitsFor ->
@@ -864,7 +864,7 @@
         describe "when the disk contents were changed since serialization", ->
           it "loads the disk contents instead of the previous unsaved state", ->
             buffer.setText("BUFFER CHANGE")
-            writeFileSync(filePath, "DISK CHANGE")
+            fs.writeFileSync(filePath, "DISK CHANGE")

             buffer2 = buffer.testSerialization()

@@ -937,7 +937,7 @@
     beforeEach ->
       filePath = join(__dirname, "fixtures", "manipulate-me")
       newPath = "#{filePath}-i-moved"
-      writeFileSync(filePath, "")
+      fs.writeFileSync(filePath, "")
       bufferToChange = new TextBuffer({filePath, load: true})
       eventHandler = jasmine.createSpy('eventHandler')
       bufferToChange.onDidChangePath eventHandler
@@ -947,16 +947,16 @@

     afterEach ->
       bufferToChange.destroy()
-      removeSync(filePath)
-      removeSync(newPath)
+      fs.removeSync(filePath)
+      fs.removeSync(newPath)

     it "notifies observers when the buffer is saved to a new path", ->
       bufferToChange.saveAs(newPath)
       expect(eventHandler).toHaveBeenCalledWith(newPath)

     it "notifies observers when the buffer's file is moved", ->
-      removeSync(newPath)
-      moveSync(filePath, newPath)
+      fs.removeSync(newPath)
+      fs.moveSync(filePath, newPath)

       waitsFor "buffer path change", ->
         eventHandler.callCount > 0
@@ -969,7 +969,7 @@

     beforeEach ->
       filePath = join(__dirname, "fixtures", "manipulate-me")
-      writeFileSync(filePath, "")
+      fs.writeFileSync(filePath, "")
       bufferToChange = new TextBuffer({filePath, load: true})
       eventHandler = jasmine.createSpy('eventHandler')
       bufferToChange.onWillThrowWatchError eventHandler
@@ -979,7 +979,7 @@

     afterEach ->
       bufferToChange.destroy()
-      removeSync(filePath)
+      fs.removeSync(filePath)

     it "notifies observers when the file has a watch error", ->
       bufferToChange.file.emitter.emit 'will-throw-watch-error', 'arg'
@@ -990,7 +990,7 @@

     beforeEach ->
       filePath = temp.openSync('atom').path
-      writeFileSync(filePath, "first")
+      fs.writeFileSync(filePath, "first")
       buffer = new TextBuffer({filePath, load: true})

       waitsFor ->
@@ -1013,7 +1013,7 @@
       it "changes the in-memory contents of the buffer to match the new disk contents and notifies ::onDidChange observers", ->
         changeHandler = jasmine.createSpy('changeHandler')
         buffer.onDidChange changeHandler
-        writeFileSync(filePath, "second")
+        fs.writeFileSync(filePath, "second")

         expect(changeHandler.callCount).toBe 0
         waitsFor "file to trigger change event", ->
@@ -1040,7 +1040,7 @@
         buffer.file.onDidChange fileChangeHandler

         buffer.insert([0, 0], "a change")
-        writeFileSync(filePath, "second")
+        fs.writeFileSync(filePath, "second")

         expect(fileChangeHandler.callCount).toBe 0
         waitsFor "file to notify ::onDidChange observer", ->
@@ -1055,7 +1055,7 @@
         buffer.insert([0, 0], "a second change")

         handler = jasmine.createSpy('fileChange')
-        writeFileSync(filePath, "a disk change")
+        fs.writeFileSync(filePath, "a disk change")
         buffer.onDidConflict handler

         expect(handler.callCount).toBe 0
@@ -1070,7 +1070,7 @@

     beforeEach ->
       filePath = join(temp.dir, 'atom-file-to-delete.txt')
-      writeFileSync(filePath, 'delete me')
+      fs.writeFileSync(filePath, 'delete me')
       bufferToDelete = new TextBuffer({filePath, load: true})
       filePath = bufferToDelete.getPath() # symlinks may have been converted
       expect(bufferToDelete.getPath()).toBe filePath
@@ -1088,7 +1088,7 @@

         deleteHandler = jasmine.createSpy('deleteHandler')
         bufferToDelete.file.onDidDelete deleteHandler
-        removeSync(filePath)
+        fs.removeSync(filePath)
         waitsFor "file to be deleted", ->
           deleteHandler.callCount > 0

@@ -1102,7 +1102,7 @@

         deleteHandler = jasmine.createSpy('deleteHandler')
         bufferToDelete.file.onDidDelete deleteHandler
-        removeSync(filePath)
+        fs.removeSync(filePath)
         waitsFor "file to be deleted", ->
           deleteHandler.callCount > 0

@@ -1115,17 +1115,17 @@
       it "notifies all onDidDelete listeners ", ->
         deleteHandler = jasmine.createSpy('deleteHandler')
         bufferToDelete.onDidDelete deleteHandler
-        removeSync(filePath)
+        fs.removeSync(filePath)

         waitsFor "file to be deleted", ->
           deleteHandler.callCount is 1

     it "resumes watching of the file when it is re-saved", ->
       bufferToDelete.save()
-      expect(existsSync(bufferToDelete.getPath())).toBeTruthy()
+      expect(fs.existsSync(bufferToDelete.getPath())).toBeTruthy()
       expect(bufferToDelete.isInConflict()).toBeFalsy()

-      writeFileSync(filePath, 'moo')
+      fs.writeFileSync(filePath, 'moo')

       changeHandler = jasmine.createSpy('changeHandler')
       bufferToDelete.onDidChange changeHandler
@@ -1137,7 +1137,7 @@

     beforeEach ->
       filePath = join(temp.dir, 'atom-tmp-file')
-      writeFileSync(filePath, '')
+      fs.writeFileSync(filePath, '')
       buffer = new TextBuffer({filePath, load: true})

       waitsFor ->
@@ -1234,8 +1234,8 @@

     it "reports the modified status changing to false after a buffer to a non-existent file is saved", ->
       buffer.destroy()
-      removeSync(filePath)
-      expect(existsSync(filePath)).toBeFalsy()
+      fs.removeSync(filePath)
+      expect(fs.existsSync(filePath)).toBeFalsy()

       buffer = new TextBuffer({filePath, load: true})
       modifiedHandler = jasmine.createSpy("modifiedHandler")
@@ -1256,7 +1256,7 @@
         modifiedHandler.reset()
         buffer.save()

-        expect(existsSync(filePath)).toBeTruthy()
+        expect(fs.existsSync(filePath)).toBeTruthy()
         expect(modifiedHandler).toHaveBeenCalledWith(false)
         expect(buffer.isModified()).toBe false

@@ -1307,7 +1307,7 @@
   describe "::getLines()", ->
     it "returns an array of lines in the text contents", ->
       filePath = require.resolve('./fixtures/sample.js')
-      fileContents = readFileSync(filePath, 'utf8')
+      fileContents = fs.readFileSync(filePath, 'utf8')
       buffer = new TextBuffer({filePath, load: true})

       waitsFor ->
@@ -1322,7 +1322,7 @@

     beforeEach ->
       filePath = require.resolve('./fixtures/sample.js')
-      fileContents = readFileSync(filePath, 'utf8')
+      fileContents = fs.readFileSync(filePath, 'utf8')
       buffer = new TextBuffer({filePath, load: true})

       waitsFor ->
@@ -1585,7 +1585,7 @@

       beforeEach ->
         filePath = join(temp.dir, 'temp.txt')
-        writeFileSync(filePath, "")
+        fs.writeFileSync(filePath, "")
         saveBuffer = new TextBuffer({filePath, load: true})

         waitsFor ->
@@ -1597,7 +1597,7 @@
       it "saves the contents of the buffer to the path", ->
         saveBuffer.setText 'Buffer contents!'
         saveBuffer.save()
-        expect(readFileSync(filePath, 'utf8')).toEqual 'Buffer contents!'
+        expect(fs.readFileSync(filePath, 'utf8')).toEqual 'Buffer contents!'

       it "notifies ::onWillSave and ::onDidSave observers around the call to File::writeSync", ->
         events = []
@@ -1635,7 +1635,7 @@
         saveBuffer.save()
         saveBuffer.setText('ab')

-        writeFileSync(saveBuffer.getPath(), 'c')
+        fs.writeFileSync(saveBuffer.getPath(), 'c')
         conflictHandler = jasmine.createSpy('conflictHandler')
         saveBuffer.onDidConflict conflictHandler

@@ -1661,7 +1661,7 @@
   describe "reload()", ->
     it "reloads current text from disk and clears any conflicts", ->
       filePath = require.resolve('./fixtures/sample.js')
-      fileContents = readFileSync(filePath, 'utf8')
+      fileContents = fs.readFileSync(filePath, 'utf8')
       buffer = new TextBuffer({filePath, load: true})

       waitsFor ->
@@ -1684,7 +1684,7 @@

     it "saves the contents of the buffer to the path", ->
       filePath = join(temp.dir, 'temp.txt')
-      removeSync(filePath)
+      fs.removeSync(filePath)

       saveAsBuffer = new TextBuffer()
       eventHandler = jasmine.createSpy('eventHandler')
@@ -1692,7 +1692,7 @@

       saveAsBuffer.setText 'Buffer contents!'
       saveAsBuffer.saveAs(filePath)
-      expect(readFileSync(filePath, 'utf8')).toEqual 'Buffer contents!'
+      expect(fs.readFileSync(filePath, 'utf8')).toEqual 'Buffer contents!'

       expect(eventHandler).toHaveBeenCalledWith(filePath)

@@ -1700,7 +1700,7 @@
       changeHandler = null
       originalPath = join(temp.dir, 'original.txt')
       newPath = join(temp.dir, 'new.txt')
-      writeFileSync(originalPath, "")
+      fs.writeFileSync(originalPath, "")

       saveAsBuffer = new TextBuffer({filePath: originalPath, load: true})

@@ -1713,13 +1713,13 @@
         saveAsBuffer.saveAs(newPath)
         expect(changeHandler).not.toHaveBeenCalled()

-        writeFileSync(originalPath, "should not trigger buffer event")
+        fs.writeFileSync(originalPath, "should not trigger buffer event")

       waits 20

       runs ->
         expect(changeHandler).not.toHaveBeenCalled()
-        writeFileSync(newPath, "should trigger buffer event")
+        fs.writeFileSync(newPath, "should trigger buffer event")

       waitsFor ->
         changeHandler.callCount > 0


fix cursor pointer over file tabs

fix cursor pointer over file tabs

--- src/smc-webapp/project_page.cjsx
+++ src/smc-webapp/project_page.cjsx
@@ -65,7 +65,7 @@
         @actions(project_id:@props.project_id).close_tab(path)

     render : ->
-        styles ={}
+        styles = {}

         if @props.file_tab
             styles = misc.copy(default_file_tab_styles)
@@ -87,7 +87,6 @@
             whiteSpace: 'nowrap'
             overflow: 'hidden'
             textOverflow: 'ellipsis'
-            cursor: 'pointer'

         x_button_styles =
             float:'right'
@@ -106,7 +105,7 @@
             active={@props.is_active}
             onClick={=>@actions(project_id: @props.project_id).set_active_tab(@props.name)}
         >
-

+

                     {<Icon
                         onMouseOver={@mouse_over_x} onMouseOut={@mouse_out_x}


probably fix https://github.com/sagemathinc/cocalc/issues/3450

--- src/smc-webapp/project_page.cjsx
+++ src/smc-webapp/project_page.cjsx
@@ -398,7 +398,8 @@
             return
         tabs = []
         @props.open_files_order.map (path, index) =>
-            tabs.push(@file_tab(path, index))
+            if path?  # see https://github.com/sagemathinc/cocalc/issues/3450
+                tabs.push(@file_tab(path, index))
         if @props.num_ghost_file_tabs == 0
             return tabs


webapp: disabling middle mouse click for project tabs

--- src/smc-webapp/projects_nav.cjsx
+++ src/smc-webapp/projects_nav.cjsx
@@ -72,11 +72,8 @@
         @refs.tab?.node.children[0].removeAttribute('href')
     componentDidMount: ->
         @strip_href()
-        document.addEventListener('mousedown', @onMouseDown)
     componentDidUpdate: () ->
         @strip_href()
-    componentWillUnmount: () ->
-       document.removeEventListener('mousedown', @onMouseDown)

     close_tab: (e) ->
         e.stopPropagation()
@@ -85,8 +82,8 @@

     # middle mouse click closes
     onMouseDown: (e) ->
-        if e.button == 1
-            @close_tab(e)
+        #if e.button == 1
+        #    @close_tab(e)

     render: ->
         title  = @props.project?.get('title') ? @props.public_project_titles?.get(@props.project_id)


Trying to fix issue https://github.com/cs150bf/ever-notedown/issues/2

--- lib/ever-notedown.coffee
+++ lib/ever-notedown.coffee
@@ -916,6 +916,16 @@
         @subscriptions.add window.evnd.searchResultListView.onDidConfirmSearchResult (noteLink) =>
           @importFromEvernote({noteLink: noteLink})

+
+  handleToEvernoteError: (error, noteOptions) ->
+    message = "#{error.message} when trying to send note to Evernote"
+    detail = "Note options:\n"
+    for k, v of noteOptions
+      continue if k in ["rawHTML", "text", "css"]
+      detail += "  #{k}: #{JSON.stringify(v)}\n"
+    stack = "#{error.stack}\n"
+    atom.notifications.addError(message, {stack, detail, dismissable: true})
+
   # TODO: Handles "code snippet"
   # TODO: use selection.getScreenRange() (for code annotating?)
   #
@@ -979,11 +989,13 @@
         format: noteFormat
         filePath: curFilePath
         renderOptions: renderOptions ? null
-      curNote = @toEvernote options, null
-
-      # TODO: Open the written file (in the default GIT repo)
-      # TODO: Async?
-      # @openNote(curNote)
+
+      try
+        @toEvernote options, null, (curNote) =>
+          @openNote(curNote)
+      catch error
+        @handleToEvernoteError(error, options)
+
     else
       renderer ?= require './renderer'
       renderer.toHTML newTextContent, renderOptions.mathjax, editor.getPath(),
@@ -1010,11 +1022,12 @@
               format: noteFormat
               filePath: curFilePath
               renderOptions: renderOptions ? null
-            curNote = @toEvernote options, null
-
-            # TODO: Open the written file (in the default GIT repo)
-            # TODO: Async?
-            #@openNote(curNote)
+
+            try
+              @toEvernote options, null, (curNote) =>
+                @openNote(curNote)
+            catch error
+              @handleToEvernoteError(error, options)

   file2Evernote: (editor, previewView) ->
     if window.evnd.init then @loadModule()
@@ -1118,18 +1131,21 @@
       options.fnStem = path.basename(curFilePath, path.extname(curFilePath))

     # Send content to Evernote Application (create a new note or update)
-    curNote = @toEvernote options, previewView
+    try
+      @toEvernote options, previewView, (curNote) =>
+        if options.moved then @openNote(curNote)
+    catch error
+      @handleToEvernoteError(error, options)

     # TODO: Open the written file (in the default GIT repo)
     # TODO: Async?
     if options.moved
       for editor in atom.workspace.getTextEditors() when editor.getPath() is curFilePath
         @removePreviewForEditor(editor)
-      @openNote(curNote)
     else
       @addPreviewForEditor(editor)

-  toEvernote: (options, previewView) ->
+  toEvernote: (options, previewView, callback) ->
     evernoteHelper ?= require './evernote-helper'
     window.evnd.enHelper ?= new evernoteHelper.EvernoteHelper()
     # Send resulting HTML to Evernote Application (create a new note)
@@ -1138,6 +1154,7 @@
     # TODO: tags, other implicit info encoding, etc.
     options.update ?= false
     noteHelper ?= require './note-helper'
+
     if options.update
       curNote = noteHelper.findNote(window.evnd.noteIndex, {title: options.title, fnStem: path.basename(options.filePath, path.extname(options.filePath))})
       if curNote is null
@@ -1279,7 +1296,7 @@
       else
         createNoteNormal()

-      return curNote
+      if callback? then callback(curNote)

   openNewNote: (initText, options, callback) ->
     # TODO: Template?


remove button if desabled/enabled function throws an error

--- lib/flex-tool-bar.coffee
+++ lib/flex-tool-bar.coffee
@@ -243,13 +243,14 @@
       for btn in toolBarButtons

         try
-          if ( btn.hide? && @condition(btn.hide) ) or ( btn.show? && !@condition(btn.show) )
-            continue
+          hide = ( btn.hide? && @condition(btn.hide) ) or ( btn.show? && !@condition(btn.show) )
+          disable = ( btn.disable? && @condition(btn.disable) ) or ( btn.enable? && !@condition(btn.enable) )
         catch err
           btnErrors.push "#{err.message}\n#{util.inspect(btn, depth: 4)}"
           continue

-        continue if btn.mode and btn.mode is 'dev' and not devMode
+        continue if hide
+        continue if btn.mode? and btn.mode is 'dev' and not devMode

         button = @buttonTypes[btn.type](@toolBar, btn) if @buttonTypes[btn.type]

@@ -264,11 +265,7 @@
           for val in ary
             button.element.classList.add val.trim()

-        try
-          if ( btn.disable? && @condition(btn.disable) ) or ( btn.enable? && !@condition(btn.enable) )
-            button.setEnabled false
-        catch err
-          btnErrors.push "#{err.message}\n#{util.inspect(btn, depth: 4)}"
+        button.setEnabled(false) if disable

       if btnErrors.length > 0
         atom.notifications.addError 'Invalid toolbar.json', {


Remove unused variable

Remove unused variable

--- lib/linter-csound.coffee
+++ lib/linter-csound.coffee
@@ -4,8 +4,6 @@
 {Range} = require('atom')
 SymbolTable = require(path.join(__dirname, 'csound-parser', 'symbol-table.js'))
 vm = require('vm')
-
-orchestraParserPath = path.join(__dirname, 'csound-parser', 'orchestra-parser.js')

 module.exports =
 LinterCsound =


Use name label when finding insertion point

--- lib/settings-view.coffee
+++ lib/settings-view.coffee
@@ -44,8 +44,8 @@
         @addPackagePanel(pack)

         # Move added package menu item to properly sorted location
-        for panelMenuItem in @panelPackages.children('[type=package]')
-          compare = title.localeCompare($(panelMenuItem).text())
+        for panelMenuItem in @panelPackages.children()
+          compare = title.localeCompare($(panelMenuItem).view().nameLabel.text())
           if compare > 0
             beforeElement = panelMenuItem
           else if compare is 0
@@ -162,7 +162,7 @@

   filterPackages: ->
     filterText = @filterEditor.getEditor().getText()
-    all = _.map @panelPackages.children('[type=package]'), (item) ->
+    all = _.map @panelPackages.children(), (item) ->
       element: $(item)
       text: $(item).text()
     active = fuzzaldrin.filter(all, filterText, key: 'text')


modified sppeds

modified sppeds

--- web/src/cs/views/default.coffee
+++ web/src/cs/views/default.coffee
@@ -13,7 +13,7 @@
 xRange = null
 yRange = null
 zRange = null
-zDensity = 150 # defines how much z range we want per image
+zDensity = 240 # defines how much z range we want per image
 numLoadedImages = 0
 overImage = true;
 mouse = {x:0.5, y: 0.5}
@@ -146,10 +146,9 @@
 		amount = 1
 	amount = ~~amount
 	###
-	amount = 20
+	amount = 3
 	if keymaster.isPressed(' ') or isMouseDown
-
-		amount = 2
+		amount = 100
 	move(amount)

 	camTargetX = -(mouse.x - 0.5) * 50


hybrid_udp: Improve log messages

hybrid_udp: Improve log messages

--- hybrid_udp.coffee
+++ hybrid_udp.coffee
@@ -31,7 +31,7 @@
   constructor: ->
     @pendingPackets = []
     @newPacketId = 0
-    @maxPacketSize = 8000  # good for localhost
+    @maxPacketSize = 8000  # good for LAN?
 #    @maxPacketSize = 1472  # good for internet
     @isInBlockMode = false
     @ackCallbacks = {}
@@ -64,7 +64,7 @@
       if @ackCallbacks[packetId]?
         @ackCallbacks[packetId]()
       else
-        console.log "ACK is already processed for packetId #{packetId}"
+        console.warn "ACK is already processed for packetId #{packetId}"
     else
       console.warn "unknown packet type: #{packetType} len=#{msg.length}"
       console.warn msg
@@ -137,7 +137,7 @@

     setTimeout =>
       if not isACKReceived
-        console.log "resend reset (no ACK received)"
+        console.warn "resend reset (no ACK received)"
         @resetPacketId callback
     , RESEND_TIMEOUT

@@ -147,7 +147,6 @@
   write: (buf, callback) ->
     if @isInBlockMode
       @pendingPackets.push [@write, arguments...]
-      console.log "postpone packet: #{@pendingPackets.length}"
       return

     packetId = @getNextPacketId()
@@ -166,7 +165,7 @@

     setTimeout =>
       if not isACKReceived
-        console.log "resend #{packetId} (no ACK received)"
+        console.warn "resend #{packetId} (no ACK received)"
         onTimeoutCallback()
     , RESEND_TIMEOUT

@@ -210,7 +209,6 @@
   writeReliableBlocked: (buf, callback) ->
     if @isInBlockMode
       @pendingPackets.push [@writeReliableBlocked, arguments...]
-      console.log "postpone packet: #{@pendingPackets.length}"
       return

     @isInBlockMode = true
@@ -234,8 +232,6 @@

 exports.UDPServer = class UDPServer extends events.EventEmitter
   constructor: ->
-    @name = 'UDPServerInstance'
-
     @socket = dgram.createSocket 'udp4'

     @socket.on 'error', (err) ->
@@ -330,7 +326,7 @@
       "#{zeropad 2, d.getDate()} #{zeropad 2, d.getHours()}:" +
       "#{zeropad 2, d.getMinutes()}:#{zeropad 2, d.getSeconds()}." +
       zeropad 3, d.getMilliseconds()
-    console.log "#{dateDesc} [#{@name}] #{msg}"
+    console.log "#{dateDesc} UDPServer: #{msg}"

   consumeBufferedPacketsFrom: (packetId) ->
     oldEnoughTime = Date.now() - OLD_UDP_PACKET_TIME_THRESHOLD
@@ -361,7 +357,6 @@
     oldEnoughTime = Date.now() - OLD_UDP_PACKET_TIME_THRESHOLD
     for packetId in [oldestUnprocessedPacketId...@latestPacketId]
       if not @packetLastReceiveTime[packetId]?
-        @log "did not receive packet #{packetId}"
         @packetLastReceiveTime[packetId] = Date.now()
       if @packetLastReceiveTime[packetId] <= oldEnoughTime
         # Failed to receive a packet
@@ -434,7 +429,6 @@
 # Usage

     server = new hybrid_udp.UDPServer
-    server.name = "TestServer"
     server.on 'packet', (buf) ->
       # buf is a Buffer instance
       console.log "server received: 0x#{buf.toString 'hex'}"


fix filters

fix filters

--- src/components/widgets-layouts/custom/accounts-invoices-list/accounts-invoices-list.directive.coffee
+++ src/components/widgets-layouts/custom/accounts-invoices-list/accounts-invoices-list.directive.coffee
@@ -2,8 +2,6 @@
 module.controller('WidgetAccountsInvoicesListCtrl', ($scope, $q, ImpacRoutes, BoltResources) ->

   # == Context and Helpers ========================================================================
-  $scope.hideForecast = true;
-
   setInitialParams = ->
     params =
       include: 'contact'
@@ -12,13 +10,12 @@
       sort: w.sortParamater
       currency: w.metadata.currency
       filter:
-        status: ['AUTHORISED', 'APPROVED', 'SUBMITTED', 'DRAFT', 'VOIDED', 'PAID', 'INACTIVE']
-    params.filter = { 'status.not': 'FORECAST' } if $scope.hideForecast
+        'status.not': 'FORECAST'
     return params

   w = $scope.widget
   bolts = ImpacRoutes.bolts()
-  bolt_path = _.find(bolts, {name: 'finance', provider: 'maestrano'}).path
+  bolt_path = _.find(bolts, { name: 'finance', provider: 'maestrano' }).path
   w.sortParamater = '-due_date'
   $scope.trxList = {
     display: false,
@@ -75,8 +72,8 @@
   $scope.trxList.changeOverdueFilter = (overdueFilter) ->
     return if overdueFilter == $scope.trxList.overdue
     $scope.trxList.overdue = overdueFilter
-    $scope.trxList.params.filter.balance = true if overdueFilter == 'overdue'
-    $scope.trxList.params.filter.balance = [] if overdueFilter == 'all'
+    $scope.trxList.params.filter.balance = 'gt 0' if overdueFilter == 'overdue'
+    delete $scope.trxList.params.filter.balance if overdueFilter == 'all'
     $scope.trxList.fetch()

   $scope.trxList.changeQuery = (query) ->


Use Element.matches to match selector

Use Element.matches to match selector

Is the modern way to do it.

--- src/trix/core/helpers/dom.coffee
+++ src/trix/core/helpers/dom.coffee
@@ -4,7 +4,7 @@
 import { attachmentSelector } from "trix/config/attachments"

 html = document.documentElement
-match = html.matchesSelector ? html.webkitMatchesSelector ? html.msMatchesSelector ? html.mozMatchesSelector
+match = html.matches

 export handleEvent = (eventName, {onElement, matchingSelector, withCallback, inPhase, preventDefault, times} = {}) ->
   element = onElement ? html


Emailsender: Make sure there is a response from Emailer

--- workers/emailsender/main.coffee
+++ workers/emailsender/main.coffee
@@ -45,13 +45,18 @@
     TextBody
     ReplyTo   : replyto
     Bcc       : bcc
-  }, (err, { messageId: smtpId })->
+  }, (err, res)->
+
     dateAttempted = new Date()
-    status        = 'attempted'
-    unless err then log "An e-mail sent to #{To}"
+
+    if err
+      console.warn "An error occured: #{err}"
+      status = 'failed'
+      smtpId = null
     else
-      log "An error occured: #{err}"
-      status = 'failed'
+      { smtpId } = res?.messageId
+      log "An e-mail sent to #{To}"
+      status = 'attempted'

     emailContent.update $set: {status, dateAttempted, smtpId}, (err)->
       console.error err if err


Render MOE and CNS11643 images on the frontend

--- static/js/encoding-mashup.coffee
+++ static/js/encoding-mashup.coffee
@@ -1,19 +1,39 @@
 template = Handlebars.compile("""

-

-      {{display.uni}}
-

-     <input type="text" name="cns" value="{{exact.cns}}">
+    <form>
+

+        {{{view.moe}}}
+        {{view.display}}
+        {{{view.exact}}}
+

+      <input type="text" name="cns" value="{{exact.cns}}"></input>
+      <input type="text" name="comments" value="{{comments}}"></input>
+    </form>

 """)

 onReady = ->
-
   $context = $('#context')

   $.getJSON('/api/chars/all', (data) ->
-    for k, v of data
-      html = template(v)
+    for key, datum of data
+      view = {}
+
+      # TODO: Select display from multiple sources pua -> uni -> ids
+      view.display = datum.display.uni
+
+      match = /^moe:revised\/([0-9a-f]+)$/.exec(key)
+      if match?
+        view.moe = ""
+
+      match = /^(\d+)-([0-9A-F]+$)/.exec(datum.exact.cns)
+      if match?
+        view.exact = ""
+
+      datum.view = view
+
+    for key, datum of data
+      html = template(datum)
       $context.append(html)
   )


fixes contributors

fixes contributors

--- maps/contributors.coffee
+++ maps/contributors.coffee
@@ -51,7 +51,7 @@
     name: 'Louise Drulhe'
     location: 'Paris'
     links: ['[louisedrulhe.fr](http://louisedrulhe.fr/)', '[@louisedrulhe](https://twitter.com/louisedrulhe)']
-    bio: 'Louise Drulhe is a graphic designer working in Paris. She authored #[em Critical Atlas of the Internet] (an adaption of her thesis project at École nationale supérieure des arts décoratifs, Paris)which uses spatial analysis as a tool for political and social purposes.'
+    bio: 'Louise Drulhe is a graphic designer working in Paris. She authored _Critical Atlas of the Internet_ (an adaption of her thesis project at École nationale supérieure des arts décoratifs, Paris)which uses spatial analysis as a tool for political and social purposes.'
     azone_username: ''
     azone_reviews: []
     azone_funds: []


Support simulated DOM attachment in callAttachHooks

--- src/space-pen.coffee
+++ src/space-pen.coffee
@@ -267,7 +267,15 @@
 callAttachHooks = (element) ->
   element = element[0] if element instanceof jQuery
   return unless element instanceof HTMLElement
-  onDom = document.body.contains(element)
+
+  onDom = false
+  ancestor = element.parentElement
+  while ancestor
+    if ancestor.tagName is 'HTML'
+      onDom = true
+      break
+    ancestor = ancestor.parentElement
+
   view.afterAttach?(onDom) for view in getViewsWithHooks(element, onDom)

 callRemoveHooks = (element) ->


Fixes saves not passing form data

Fixes saves not passing form data

--- models/current_user.coffee
+++ models/current_user.coffee
@@ -16,9 +16,13 @@
   defaultArtworkCollection: -> @get('artworkCollections')[0]

   # Add the access token to fetches and saves
-  sync: (method, model, options = {}) ->
-    options.data ?= if method in ['create', 'update'] then _.omit(@toJSON(), 'accessToken') else {}
-    options.data.access_token = @get 'accessToken'
+  sync: (method, model, options={}) ->
+    if method in ['create', 'update']
+      # If persisting to the server overwrite attrs
+      options.attrs = _.omit(@toJSON(), 'accessToken')
+    else
+      # Otherwise overwrite data
+      _.defaults(options, { data: { access_token: @get('accessToken') } })
     super

   # Saves the artwork to the user's saved-artwork collection. API creates colletion if user's first save.


fix cron auto start

fix cron auto start

--- scripts/notice-channel-created.coffee
+++ scripts/notice-channel-created.coffee
@@ -12,4 +12,4 @@
     if message?.type == 'channel_created'
       return if typeof robot?.send isnt 'function'
 #      robot.send message
-      robot.send {room: "general"}, "New channel <##{message.channel.id}> was created. Thanks <@#{message.channel.creator}> !! "+      robot.send {room: "general"}, "New channel <##{message.channel.id}> was created. NJ <@#{message.channel.creator}> !! "


:fire: Get rid of unused `tileId` property

--- src/tile-component.coffee
+++ src/tile-component.coffee
@@ -21,7 +21,6 @@
     @screenRowsByLineId = {}
     @lineIdsByScreenRow = {}
     @domNode ?= document.createElement("div")
-    @domNode.dataset.tileId = @id
     @domNode.style.position = "absolute"
     @domNode.style.display = "block"
     @domNode.classList.add("tile")


Typo fix

Typo fix

--- autocomplete-server.coffee
+++ autocomplete-server.coffee
@@ -1,7 +1,7 @@
 Meteor.publish 'autocomplete-recordset', (selector, options, collName) ->
   collection = global[collName]
   unless collection
-    throw new Error(collName + ' is not a defined on the global namespace of the server.')
+    throw new Error(collName + ' is not defined on the global namespace of the server.')

   # This is a semi-documented Meteor feature:
   # https://github.com/meteor/meteor/blob/devel/packages/mongo-livedata/collection.js


skip clicking on students if none have answered any questions

--- test-integration/calendar.coffee
+++ test-integration/calendar.coffee
@@ -88,12 +88,16 @@
     @forecast.el.back().click()

     @scores.goToPeriodWithWorkedAssignments()
-    # only test the 1st row of each Student Response
-    @scores.el.taskResultByRow().click()
-    # console.log 'opening Student view', courseCategory, index, 'of', total
-    @taskTeacher.waitUntilLoaded()
-    # @utils.wait.click(linkText: 'Back to Student Scores')
-    @taskTeacher.el.backToScores().click()
+    # only test the 1st row of each Student Response (skip if students have not answered something)
+    @scores.el.taskResultByRow().isDisplayed().then (isDisplayed) =>
+      if isDisplayed
+        @scores.el.taskResultByRow().click()
+        # console.log 'opening Student view', courseCategory, index, 'of', total
+        @taskTeacher.waitUntilLoaded()
+        # @utils.wait.click(linkText: 'Back to Student Scores')
+        @taskTeacher.el.backToScores().click()
+      else
+        console.log 'No clickable scores were found so Skipping (but passing the test). Probably because students did not work anything yet'

     # # BUG: Click on "Period 1"
     # @utils.wait.click({css: '.course-scores-wrap li:first-child'})


Skip finding config elements if the browser is unsupported

--- src/trix/installer.coffee
+++ src/trix/installer.coffee
@@ -3,20 +3,23 @@

 class Trix.Installer
   constructor: (@config = {}) ->
-    for key in "textarea toolbar input debug".split(" ")
-      @config["#{key}Element"] = getElement(@config[key])
-      delete @config[key]
-
-    @config.focus ?= @config.textareaElement.hasAttribute("autofocus")

   createEditor: ->
     if @browserIsSupported()
+      @getConfigElements()
+      @config.textElement = @createTextElement()
+      @config.focus ?= @config.textareaElement.hasAttribute("autofocus")
       @createStyleSheet()
-      @config.textElement = @createTextElement()
+
       new Trix.EditorController @config

   browserIsSupported: ->
     true
+
+  getConfigElements: ->
+    for key in "textarea toolbar input debug".split(" ")
+      @config["#{key}Element"] = getElement(@config[key])
+      delete @config[key]

   styleSheetId = "trix-styles"


add logging to advanced demo

add logging to advanced demo

--- demo/scripts/advanced.coffee
+++ demo/scripts/advanced.coffee
@@ -1,31 +1,20 @@
-# IE feature detection
-supportsRGBA = true
-scriptElem = document.getElementsByTagName('script')[0]
-try
-  scriptElem.style.color = 'rgba(128,128,128,0.5)'
-catch e
-  supportsRGBA = false
-finally
-  scriptElem.style.color = ""
-
-
 getColor = (id, lighten) ->
   alpha = if lighten then '0.4' else '1.0'
-  if id == 1 or id == 'quill-1'
-    return if supportsRGBA then "rgba(0,153,255,#{alpha})" else "rgb(0,153,255)"
-  else
-    return if supportsRGBA then "rgba(255,153,51,#{alpha})" else "rgb(255,153,51)"
+  return if id == 1 or id == 'quill-1' then "rgba(0,153,255,#{alpha})" else "rgba(255,153,51,#{alpha})"

 listenEditor = (source, target) ->
   source.on(Quill.events.TEXT_CHANGE, (delta, origin) ->
+    console.log source.id, 'text', delta, origin
     return if origin == 'api'
     target.updateContents(delta)
     sourceDelta = source.getContents()
     targetDelta = target.getContents()
     console.assert(sourceDelta.isEqual(targetDelta), "Editor diversion!", source, target, sourceDelta, targetDelta)
   ).on(Quill.events.SELECTION_CHANGE, (range) ->
+    console.log source.id, 'selection', range
     return unless range?
-    target.getModule('multi-cursor').moveCursor(source.id, range.end)
+    cursorManager = target.getModule('multi-cursor')
+    cursorManager.moveCursor(source.id, range.end)
   )

@@ -35,16 +24,15 @@
   $container = $('.editor-container', $wrapper)
   editor = new Quill($container.get(0), {
     modules:
-      'multi-cursor': true
       'toolbar': { container: $('.toolbar-container', $wrapper).get(0) }
       'link-tooltip': true
       'image-tooltip': true
+      'multi-cursor': true
     theme: 'snow'
   })
   authorship = editor.addModule('authorship', {
     authorId: editor.id
     color: getColor(num, true)
-    button: $('.sc-authorship', $wrapper).get(0)
   })
   editors.push(editor)


Increase informatics submit timeout

Increase informatics submit timeout

--- server/testSystems/Informatics.coffee
+++ server/testSystems/Informatics.coffee
@@ -88,7 +88,8 @@
             method: 'POST',
             headers: {'Content-Type': contentType},
             body,
-            followAllRedirects: true
+            followAllRedirects: true,
+            timeout: 30 * 1000
         })
         res = JSON.parse(page)
         if res.res != "ok"


update debug

update debug

--- src/utils/clear_db.coffee
+++ src/utils/clear_db.coffee
@@ -32,8 +32,8 @@
 start2 = () ->
   setTimeout () ->
     _removeAbandoned (err) ->
-      console.error "clear_db::removeCompleted ERROR: #{err}" if err?
-      start()
+      console.error "clear_db::removeAbandoned ERROR: #{err}" if err?
+      start2()
   , 28800000


Added download function to commander

Added download function to commander

--- source/Commander.coffee
+++ source/Commander.coffee
@@ -11,3 +11,6 @@

 # Combine arguments to single string
 specs = components['_'].join(' ')
+
+# Start download process
+Downloader.download(specs)


Changed the functions to use 'named' parameters  (objects containing variables so that they may be passed to the function in the form { currentRow: 5}). Changed wrappers to add named parameters, this also has the benefit that wrappers now no longer have to becalled in a certain order. Extracted named paramater creation and adding to separate functions. These functions currently live in the PlayerCodeEditor class but should reside elsewhere.

--- web/scripts/index.coffee
+++ web/scripts/index.coffee
@@ -90,21 +90,20 @@
         selector.change()
         return

-
-    switchUp: (currentRow, currentColumn) ->
+    switchUp: ({currentRow, currentColumn}) ->
         if currentRow > 0
             @editSession.moveLinesUp(currentRow, currentRow)
             @editor.gotoLine currentRow, currentColumn, false
         return

-    switchDown: (currentRow, currentColumn) ->
+    switchDown: ({currentRow, currentColumn}) ->
         maxRow = @editSession.getLength()
         if currentRow < maxRow - 1
             @editSession.moveLinesDown(currentRow, currentRow)
             @editor.gotoLine currentRow + 2, currentColumn, false
         return

-    deleteLine: (text, currentRow) ->
+    deleteLine: ({text, currentRow}) ->
         line = text.getLine currentRow
         if line != ""
             command = @getCommandFromLine line
@@ -116,7 +115,7 @@
             text.removeLines currentRow, currentRow
         return

-    insertLine: (text, currentRow) ->
+    insertLine: ({text, currentRow}) ->
         command = $('#commandToInsert').find(':selected').text()
         if @commands[command]['used'] < @commands[command]['maxUses']
             @commands[command]['used']++
@@ -162,7 +161,8 @@
         playerCodeEditor = @
         return ->
             currentRow = playerCodeEditor.editor.getCursorPosition().row
-            arguments[arguments.length++] = currentRow
+            @addNamedArguments arguments, {currentRow: currentRow}
+
             func.apply playerCodeEditor, arguments
             return

@@ -174,8 +174,11 @@
         playerCodeEditor = @
         return ->
             cursorPosition = playerCodeEditor.editor.getCursorPosition()
-            arguments[arguments.length++] = cursorPosition.row
-            arguments[arguments.length++] = cursorPosition.column
+            @addNamedArguments arguments, {
+                currentRow: cursorPosition.row,
+                currentColumn: cursorPosition.column
+            }
+
             func.apply playerCodeEditor, arguments
             return

@@ -187,6 +190,45 @@
         playerCodeEditor = @
         return ->
             text = playerCodeEditor.editSession.getDocument()
-            arguments[arguments.length++] = text
+            @addNamedArguments arguments, {text: text}
+
             func.apply playerCodeEditor, arguments
             return
+
+    addNamedArguments: (originalArguments, argumentDictionary) ->
+        ###
+        Adds the named arguments to the original arguments.
+        Makes changes to originalArguments, returns nothing.
+        ###
+        if originalArguments.length == 0
+            originalArguments[originalArguments.length++] = \
+                @createNamedArguments argumentDictionary
+        else
+            for argument of originalArguments
+                if @detectNamedArguments originalArguments[argument]
+                    $.extend true, originalArguments[argument],
+                        argumentDictionary
+                    break
+        return
+
+    createNamedArguments: (argumentDictionary) ->
+        ###
+        Takes in an object of key-value pairs,
+        returns an object of the Named Arguments format.
+        ###
+        argumentDictionary['namedArgumentsFlag'] = true
+        return argumentDictionary
+
+    detectNamedArguments: (argument) ->
+        ###
+        Returns whether or not the argument is of the namedArguments format.
+        ###
+        if argument == null
+            return false
+        if typeof argument != 'object'
+            return false
+        if not ('namedArgumentsFlag' of argument)
+            return false
+        if argument['namedArgumentsFlag'] != true
+            return false
+        return true


Fix :bug:

Fix :bug:

--- src/workspace-view.coffee
+++ src/workspace-view.coffee
@@ -147,7 +147,7 @@
           detailedMessage: "The shell command `#{commandName}` is installed."

     resourcePath = atom.getLoadSettings().resourcePath
-    CommandInstaller.installApmCommand resourcePath, (error) =>
+    CommandInstaller.installAtomCommand resourcePath, (error) =>
       showDialog(error, 'atom')
       unless error?
         CommandInstaller.installApmCommand resourcePath, (error) =>


Enabled broken eval test

Enabled broken eval test

--- spec/script_spec.coffee
+++ spec/script_spec.coffee
@@ -176,6 +176,7 @@
               var foo = "NotOne";
               var e_foo = e("foo");
               var qux = window.eval.call(window, "foo");
+              console.log(qux)

               document.title = eval('e_foo + bar + baz + qux');
             })();
@@ -185,7 +186,7 @@
       browser = new Browser
       browser.wants "http://localhost:3003/script/eval", @callback
     "should evaluate in global scope": (browser)->
-      #assert.equal browser.document.title, "OneTwoThreeFour"
+      assert.equal browser.document.title, "OneTwoThreeFour"

 .addBatch


Update code comments with regard to callback removal

--- src/zombie/browser.coffee
+++ src/zombie/browser.coffee
@@ -679,14 +679,14 @@
           return label.querySelector("input,textarea,select")
     return

-  # ### browser.fill(selector, value, callback) => this
+  # ### browser.fill(selector, value) => this
   #
   # Fill in a field: input field or text area.
   #
   # selector - CSS selector, field name or text of the field label
   # value - Field value
   #
-  # Without callback, returns this.
+  # Returns this.
   fill: (selector, value)->
     field = @field(selector)
     unless field && (field.tagName == "TEXTAREA" || (field.tagName == "INPUT"))
@@ -718,27 +718,27 @@
       field.click()
     return this

-  # ### browser.check(selector, callback) => this
+  # ### browser.check(selector) => this
   #
   # Checks a checkbox.
   #
   # selector - CSS selector, field name or text of the field label
   #
-  # Without callback, returns this.
+  # Returns this.
   check: (selector)->
     return @_setCheckbox(selector, true)

-  # ### browser.uncheck(selector, callback) => this
+  # ### browser.uncheck(selector) => this
   #
   # Unchecks a checkbox.
   #
   # selector - CSS selector, field name or text of the field label
   #
-  # Without callback, returns this.
+  # Returns this.
   uncheck: (selector)->
     return @_setCheckbox(selector, false)

-  # ### browser.choose(selector, callback) => this
+  # ### browser.choose(selector) => this
   #
   # Selects a radio box option.
   #
@@ -771,26 +771,26 @@
         return option
     throw new Error("No OPTION '#{value}'")

-  # ### browser.select(selector, value, callback) => this
+  # ### browser.select(selector, value) => this
   #
   # Selects an option.
   #
   # selector - CSS selector, field name or text of the field label
   # value - Value (or label) or option to select
   #
-  # Without callback, returns this.
-  select: (selector, value )->
+  # Returns this.
+  select: (selector, value)->
     option = @_findOption(selector, value)
     @selectOption(option)
     return this

-  # ### browser.selectOption(option, callback) => this
+  # ### browser.selectOption(option) => this
   #
   # Selects an option.
   #
   # option - option to select
   #
-  # Without callback, returns this.
+  # Returns this.
   selectOption: (selector)->
     option = @query(selector)
     if option && !option.getAttribute("selected")
@@ -800,26 +800,26 @@
       @fire(select, "change")
     return this

-  # ### browser.unselect(selector, value, callback) => this
+  # ### browser.unselect(selector, value) => this
   #
   # Unselects an option.
   #
   # selector - CSS selector, field name or text of the field label
   # value - Value (or label) or option to unselect
   #
-  # Without callback, returns this.
-  unselect: (selector, value )->
+  # Returns this.
+  unselect: (selector, value)->
     option = @_findOption(selector, value)
     @unselectOption(option)
     return this

-  # ### browser.unselectOption(option, callback) => this
+  # ### browser.unselectOption(option) => this
   #
   # Unselects an option.
   #
   # selector - selector or option to unselect
   #
-  # Without callback, returns this.
+  # Returns this.
   unselectOption: (selector)->
     option = @query(selector)
     if option && option.getAttribute("selected")
@@ -831,11 +831,11 @@
       @fire(select, "change")
     return this

-  # ### browser.attach(selector, filename, callback) => this
+  # ### browser.attach(selector, filename) => this
   #
   # Attaches a file to the specified input field.  The second argument is the file name.
   #
-  # Without callback, returns this.
+  # Returns this.
   attach: (selector, filename)->
     field = @field(selector)
     unless field && field.tagName == "INPUT" && field.type == "file"


Workaround for the hanging part of issue #1944 "Tests fail/hang with latest PhantomJS (2.1.1)". URL: https://github.com/philc/vimium/issues/1944

--- tests/dom_tests/dom_tests.coffee
+++ tests/dom_tests/dom_tests.coffee
@@ -104,7 +104,10 @@
     stubSettings "linkHintCharacters", "ab"

     # Every HTML5 input type except for hidden. We should be able to activate all of them with link hints.
-    inputTypes = ["button", "checkbox", "color", "date", "datetime", "datetime-local", "email", "file",
+    #
+    # TODO: Re-insert "color" into the inputTypes list when PhantomJS issue #13979 is fixed and integrated.
+    # Ref: https://github.com/ariya/phantomjs/issues/13979
+    inputTypes = ["button", "checkbox", "date", "datetime", "datetime-local", "email", "file",
       "image", "month", "number", "password", "radio", "range", "reset", "search", "submit", "tel", "text",
       "time", "url", "week"]


insert coach before first matching section for hiding, since the first one found on the dom will never be nested, unlike the last

--- src/scripts/modules/media/body/body.coffee
+++ src/scripts/modules/media/body/body.coffee
@@ -90,7 +90,7 @@

     hideExercises: ($el) ->
       hiddenClasses = @getCoach()
-      hiddenSelectors = hiddenClasses.map((name) -> ".#{name}[data-depth=1]").join(', ')
+      hiddenSelectors = hiddenClasses.map((name) -> ".#{name}").join(', ')
       $exercisesToHide = $el.find(hiddenSelectors)
       $exercisesToHide.add($exercisesToHide.siblings('[data-type=title]')).hide()

@@ -99,7 +99,7 @@
     makeRegionForCoach: ($exercises, wrapperId = 'coach-wrapper') ->
       $("##{wrapperId}").remove()
       $coachWrapper = $("

")
-      $coachWrapper.insertAfter(_.last($exercises))
+      $coachWrapper.insertBefore(_.first($exercises))

     handleCoach: ($el) ->
       return unless @isCoach()


Set Vary: Origin on CORS requests

Set Vary: Origin on CORS requests

The Access-Control-Allow-Origin header is set depending on the request's Origin
header. Although most requests also send Cache-Control headers with no-cache,
the OPTIONS requests for CORS pre-flight requests do set cache headers.

See http://www.w3.org/TR/cors/#resource-implementation

--- src/trans-xhr.coffee
+++ src/trans-xhr.coffee
@@ -62,6 +62,7 @@
         else
             origin = req.headers['origin']
         res.setHeader('Access-Control-Allow-Origin', origin)
+        res.setHeader('Vary', 'Origin')
         headers = req.headers['access-control-request-headers']
         if headers
             res.setHeader('Access-Control-Allow-Headers', headers)


Fix #133 only delay disconnect on non-websocket transports

--- src/transport.coffee
+++ src/transport.coffee
@@ -138,11 +138,15 @@
             @connection.headers = headers

     unregister: ->
+        delay = @recv.delay_disconnect
         @recv.session = null
-        @recv = null
         if @to_tref
             clearTimeout(@to_tref)
-        @to_tref = setTimeout(@timeout_cb, @disconnect_delay)
+
+        if delay
+            @to_tref = setTimeout(@timeout_cb, @disconnect_delay)
+        else
+            @timeout_cb()

     flushToRecv: (recv) ->
         if @send_buffer.length > 0
@@ -268,6 +272,7 @@
 # Write stuff to response, using chunked encoding if possible.
 class ResponseReceiver extends GenericReceiver
     max_response_size: undefined
+    delay_disconnect: true

     constructor: (@request, @response, @options) ->
         @curr_response_size = 0


debug initialization script

debug initialization script

--- init.coffee
+++ init.coffee
@@ -7,7 +7,7 @@

 todolist = new TodoList
     title: "Tutorial"
-    path: ["Tutorial"]
+    path: '["Tutorial"]'

 tasks = [
@@ -42,6 +42,9 @@
     new Task
         description: "+ button lets you create a new todo list as child of \
 currenlty selected list"
+    new Task
+        description: "Write an # at the begginning of a word and it will \
+create a shortcut for all the task that share this tag."
     ]

 saveFunc = (obj) ->


:memo: Fix a typo and make recommendation more clear

--- src/workspace.coffee
+++ src/workspace.coffee
@@ -779,9 +779,9 @@
   # Essential: Adds a panel item as a modal dialog.
   #
   # * `options` {Object}
-  #   * `item` Your panel content. It can be DOM element, a jQuery element, or
+  #   * `item` Your panel content. It can be a DOM element, a jQuery element, or
   #     a model with a view registered via {ViewRegistry::addViewProvider}. We recommend the
-  #     latter. See {ViewRegistry::addViewProvider} for more information.
+  #     model option. See {ViewRegistry::addViewProvider} for more information.
   #   * `visible` (optional) {Boolean} false if you want the panel to initially be hidden
   #     (default: true)
   #   * `priority` (optional) {Number} Determines stacking order. Lower priority items are


create border as walls (+ center area)

create border as walls (+ center area)

--- src/webapp/_scripts/r7/assetsLoader.coffee
+++ src/webapp/_scripts/r7/assetsLoader.coffee
@@ -18,13 +18,13 @@
   _area02 = {
     width : 16 #nbcell
     height : 9 #nbcell
-    cellr : 10
+    cellr : 7
     walls : {
       cells : [1,2,0,4] #cellunit [x0,y0,w0,h0, x1,y1,....]
-
     }
-    areas : [
-      { item : "targetg1", cells : [0,0,1,1, 3,4,1,1] }
+    zones : [
+      { k : "targetg1/spawn", cells : [0,0,1,1, 3,4,1,1] }
+      { k : "gate/in",    cells : [3,3,1,1] }
     ]
   }

@@ -46,40 +46,46 @@
   makeArea = (d) ->
     area = d.result
     cellr = area.cellr
-    cells2boxes3d = (cells) ->
+    cells2boxes3d = (cells, offx, offy) ->
       geometry = new THREE.Geometry()
       for i in [0 .. cells.length-1] by 4
         dx = cells[i+2] * cellr || 1
         dy = cells[i+3] * cellr || 1
         mesh = new THREE.Mesh(new THREE.CubeGeometry(dx, dy, 1), new THREE.MeshNormalMaterial())
-        mesh.position.x = cells[i+0] * cellr + dx / 2
-        mesh.position.y = cells[i+1] * cellr + dy / 2
+        mesh.position.x = cells[i+0] * cellr + dx / 2 + offx * cellr
+        mesh.position.y = cells[i+1] * cellr + dy / 2 + offy * cellr
         THREE.GeometryUtils.merge(geometry, mesh)
       walls = new THREE.Mesh(geometry, new THREE.MeshNormalMaterial())
       obj3d = new THREE.Object3D()
       obj3d.add(walls)
       obj3d
-    cells2boxes2d = (cells) ->
+    cells2boxes2d = (cells, offx, offy) ->
       body = new cp.Body(Infinity, Infinity)
       body.nodeIdleTime = Infinity
       body.shapes = []
       for i in [0 .. cells.length-1] by 4
         # TODO optim replace boxes (polyshape) by segment + thick (=> change the display)
-        offset = cp.v(cells[i+0] * cellr, cells[i+1] * cellr)
+        offset = cp.v((cells[i+0] + offx) * cellr, (cells[i+1] + offy) * cellr)
         w = cells[i+2] * cellr || 1
         h = cells[i+3] * cellr || 1
         verts = [ 0,0, 0,h, w,h, w,0 ]
         shape = new cp.PolyShape(body, verts, offset)
         body.shapes.push(shape)
       body
-    area.walls.obj3d = () -> cells2boxes3d(area.walls.cells)
-    area.walls.obj2d = () -> cells2boxes2d(area.walls.cells)
+    addBorderAsCells = (w, h, cells) ->
+      cells.push(0, 0, w, 0)
+      cells.push(0, 0, 0, h)
+      cells.push(w, 0, 0, h)
+      cells.push(0, h, w, 0)
+    addBorderAsCells(area.width, area.height, area.walls.cells)
+    area.walls.obj3d = () -> cells2boxes3d(area.walls.cells, area.width / -2, area.height/-2)
+    area.walls.obj2d = () -> cells2boxes2d(area.walls.cells, area.width / -2, area.height/-2)
     area

   makeShip = (r) ->
     r.obj2d = () ->
       body = new cp.Body(100, Infinity)
-      shape = new cp.PolyShape(body, [2, 0, -1, -1, -1, 1], cp.vzero)
+      shape = new cp.PolyShape(body, [3, 0, -1, -1, -1, 1], cp.vzero)
       shape.sensor = false
       body.shapes = [shape]
       body
@@ -111,7 +117,6 @@

         #TODO should create a new object or at least change the timestamp
         r = {}
-        console.debug("geometry", geometry)
         r.obj3d = () -> fixOrientation(new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials)))
         deferred.resolve(r)
       , texturePath)


added method getLanguage to complete reactivity

--- t9n.coffee
+++ t9n.coffee
@@ -14,7 +14,7 @@
   @depLanguage: new Deps.Dependency()
   @missingPrefix = ">"
   @missingPostfix = "<"
-
+
   @map: (language, map) ->
     if(!@maps[language])
       @maps[language] = {}


KaTeX no longer uses images

KaTeX no longer uses images

--- .Gruntfile.coffee
+++ .Gruntfile.coffee
@@ -7,11 +7,6 @@
         cwd: 'node_modules/katex/dist/fonts/'
         src: '**'
         dest: 'public/katex/fonts/'
-      katex_images:
-        expand: true
-        cwd: 'node_modules/katex/dist/images/'
-        src: '**'
-        dest: 'public/katex/images/'
       codemirror:
         expand: true
         flatten: true
@@ -28,9 +23,6 @@
           patterns: [
             match: /url\(fonts/g
             replacement: 'url(/katex/fonts'
-          ,
-            match: /url\(images/g
-            replacement: 'url(/katex/images'
           ]
         files: [
           expand: true


atom: linter-tslint: enableSemanticRules: true

--- .atom/config.cson
+++ .atom/config.cson
@@ -122,6 +122,8 @@
     lintInlineJavaScript: true
   "linter-stylint":
     onlyRunWhenConfig: true
+  "linter-tslint":
+    enableSemanticRules: true
   "linter-ui-default":
     tooltipFollows: "Keyboard"
   "linter-write-good": {}


Update Atom configurations

Update Atom configurations

--- .atom/config.cson
+++ .atom/config.cson
@@ -23,6 +23,7 @@
       "git-diff-popup"
       "cursor-history"
       "last-cursor-position"
+      "turbo-javascript"
     ]
     themes: [
       "atom-material-ui"


Don't trigger marker-added/removed events anymore

I missed this previously

--- src/app/buffer-marker.coffee
+++ src/app/buffer-marker.coffee
@@ -256,12 +256,10 @@
     delete @buffer.validMarkers[@id]
     @buffer.invalidMarkers[@id] = this
     @notifyObservers(bufferChanged: true)
-    @buffer.trigger 'marker-removed', this

   revalidate: ->
     delete @buffer.invalidMarkers[@id]
     @buffer.validMarkers[@id] = this
     @notifyObservers(bufferChanged: true)
-    @buffer.trigger 'marker-added', this

 _.extend BufferMarker.prototype, EventEmitter


Update: IE9ですべての機能が働くように

Update: IE9ですべての機能が働くように

--- src/DOMExtention.coffee
+++ src/DOMExtention.coffee
@@ -2,6 +2,12 @@
   DOM Extention
 ###
 ele = HTMLElement ? Element
+
+for key of ele::
+  if key is "classList"
+    hasClassList = true
+
+
 # child node search
 ele::childClass = ele::getElementsByClassName
 ele::childTag = ele::getElementsByTagName
@@ -32,7 +38,10 @@
 ele::attr = (a, b) ->
   return if b? then @setAttr(a, b) else @getAttr(a)
 ele::getClass = ->
-  return @classList
+  if hasClassList
+    return @classList
+  else
+    return @className.split(" ")
 ele::setClass = (a) ->
   if a instanceof Array
     return @className = a.join(" ")
@@ -42,12 +51,29 @@
 ele::class = (a) ->
   return if a? then @setClass(a) else @getClass()
 # extention
-# IE8以下では動かない
 ele::addClass = (a) ->
-  return @classList.add(a)
+  if hasClassList
+    return @classList.add(a)
+  else
+    if not @hasClass(a)
+      @className += " " + a
+    return @className.split(" ")
 ele::removeClass = (a) ->
-  return @classList.remove(a)
+  if hasClassList
+    return @classList.remove(a)
+  else
+    @className = @className.replace(///^#{a}$|#{a}\ |\ #{a}///, "", "g")
+    return @className
 ele::toggleClass = (a) ->
-  return @classList.toggle(a)
+  if hasClassList
+    return @classList.toggle(a)
+  else
+    if @hasClass(a)
+      return @removeClass(a)
+    else
+      return @addClass(a)
 ele::hasClass = (a) ->
-  return @classList.contains(a)
+  if hasClassList
+    return @classList.contains(a)
+  else
+    return @className.search(///^#{a}$|#{a}\ |\ #{a}///) isnt -1


add in if

add in if

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -13,6 +13,7 @@
   grunt.loadNpmTasks('grunt-aws')

   grunt.initConfig
+    aws: if grunt.file.exists("credentials.json") then grunt.file.readJSON("credentials.json") else {}
     bower: grunt.file.readJSON("bower.json")
     pkg: '<json:package.json>'
     srcFolder: 'src'
@@ -109,8 +110,8 @@

     s3:
       options:
-        accessKeyId: grunt.file.readJSON("credentials.json").accessKeyId
-        secretAccessKey: grunt.file.readJSON("credentials.json").secretAccessKey
+        accessKeyId: "<%= aws.accessKeyId %>"
+        secretAccessKey: "<%= aws.secretAccessKey %>"
         bucket: 'formrenderer-base'
         access: 'public-read'
         gzip: true


Fix build

Fix build

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -72,10 +72,11 @@
     jsbeautifier:
       files: ['src/scripts/**/*.js', '!src/scripts/libs/**']
       options:
-        mode: "VERIFY_ONLY"
-        space_after_anon_function: true
-        wrap_line_length: 120
-        indent_size: 2
+        js:
+          mode: "VERIFY_ONLY"
+          space_after_anon_function: true
+          wrap_line_length: 120
+          indent_size: 2

     # CoffeeLint
     coffeelint:


[moving-to-express-4] Rule for colon assignment

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -11,6 +11,11 @@
         options:
           arrow_spacing:
             level: 'error'
+          colon_assignment_spacing:
+            level: 'error'
+            spacing:
+              left: 0
+              right: 1
           empty_constructor_needs_parens:
             level: 'error'
           line_endings:


Less _'s means less keypressing!

Less _'s means less keypressing!

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -100,12 +100,12 @@

   grunt.registerTask 'default', ['build']
   grunt.registerTask 'build', ['coffee', 'compass', 'concat', 'uglify', 'cssmin']
-  grunt.registerTask 'prep_release', ['build', 'dom_munger:latest_version', 'zip:chosen','package_jquery']
+  grunt.registerTask 'prep-release', ['build', 'dom_munger:latest_version', 'zip:chosen','package-jquery']

-  grunt.registerTask 'publish_bower', ['package_bower', 'gh-pages:bower', 'cleanup_bower']
-  grunt.registerTask 'publish_release', ['gh-pages:gh-pages','publish_bower']
+  grunt.registerTask 'publish-bower', ['package-bower', 'gh-pages:bower', 'cleanup-bower']
+  grunt.registerTask 'publish-release', ['gh-pages:gh-pages','publish-bower']

-  grunt.registerTask 'package_jquery', 'Generate a jquery.json manifest file from package.json', () ->
+  grunt.registerTask 'package-jquery', 'Generate a jquery.json manifest file from package.json', () ->
     src = "package.json"
     dest = "chosen.jquery.json"
     pkg = grunt.file.readJSON(src)
@@ -119,7 +119,7 @@
     json1.author.name = pkg.author
     grunt.file.write('chosen.jquery.json', JSON.stringify(json1, null, 2) + "\n")

-  grunt.registerTask 'package_bower', 'Generate a bower.json manifest file from package.json', () ->
+  grunt.registerTask 'package-bower', 'Generate a bower.json manifest file from package.json', () ->
     pkg = grunt.file.readJSON("package.json")
     bowerjson = pkg.bowerJSON
     jqueryjson = pkg.jqueryJSON
@@ -140,5 +140,5 @@

     grunt.file.write("public/bower.json", JSON.stringify(bower, null, 2) + "\n")

-  grunt.registerTask 'cleanup_bower', 'Delete generated bower.json file', () ->
+  grunt.registerTask 'cleanup-bower', 'Delete generated bower.json file', () ->
     grunt.file.delete("public/bower.json")


fix test

fix test

--- models/partner_location.coffee
+++ models/partner_location.coffee
@@ -51,7 +51,7 @@
       @displayAddress()

   googleMapsLink: ->
-    location = @get('raw')
+    location = @getMapsLocation()
     return unless location
     getMapLink location


What docs task?

What docs task?

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -71,7 +71,7 @@
   require('load-grunt-tasks')(grunt, { scope: 'devDependencies' })
   require('time-grunt')(grunt)

-  # Docs task
+  # Dist task
   grunt.registerTask('dist-js', ['coffee:dist', 'uglify:dist'])
   grunt.registerTask('dist-css', ['less:dist', 'csscomb:dist', 'cssmin:dist'])
   grunt.registerTask('dist', ['clean', 'dist-js', 'dist-css'])


Fix imagemin outputting images with no data

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -222,6 +222,7 @@
     imagemin:
       images:
         options:
+          cache: false
           optimizationLevel: 7
         files: [{
           expand: true


Better comments for what happens in dist copy

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -86,17 +86,26 @@

         options:
           processContent: (content, path) ->
+            # Strip warnings from JavaScript
             if path.match /\.js$/
               content.replace /\n\s*warn\(.*/g, ''
+
+            # Update to minified JS paths in HTML
             else if path.match /\.html$/
               content.replace /([\w-\.]*)(\.min)?\.js/g, '$1.min.js'
+
             else
               content

       zepto:
-        files:
-          "lib/zepto/": ["submodules/zepto/dist/**"]
+        files: [
+          expand: yes
+          cwd: 'submodules/zepto/dist/'
+          src: 'zepto.js'
+          dest: 'lib/zepto'
+        ,
           "<%= dirs.build %>/src/jqtouch-jquery.js": ["submodules/zepto/src/touch.js"]
+        ]

       "jquery-bridge":
         options:


Build when watched files change

Build when watched files change

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -93,7 +93,7 @@
         'src/**/*.coffee',
         'vendor/**/*.js'
       ],
-      tasks: ['coffee', 'lint']
+      tasks: ['build']
     }
   }


have grunt clean delete the bower directory too

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -499,7 +499,7 @@
         files:
           'build/compile-src/chrome/extension/vulcanized-chrome.html': 'build/compile-src/chrome/extension/vulcanized-inline.html'

-    clean: ['build/**', '.tscache']
+    clean: ['build', '.tscache', 'third_party/lib']

  # grunt.initConfig


Avoid removing vital build/ directory in prepublish script.

I'm sure this isn't the best way to fix this problem, but without it the
pathwatcher package gets installed without a build/Release directory,
which makes the package totally unusable. The unnecessary cleaning happens
because the prepublish script always runs after npm install.

Very open to any other ways of preserving the build/ directory!

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -69,7 +69,7 @@
   grunt.registerTask('lint', ['coffeelint', 'cpplint'])
   grunt.registerTask('default', ['coffee', 'lint', 'shell:rebuild'])
   grunt.registerTask('test', ['default', 'shell:test'])
-  grunt.registerTask('prepublish', ['clean', 'coffee', 'lint', 'shell:update-atomdoc', 'atomdoc'])
+  grunt.registerTask('prepublish', ['coffee', 'lint', 'shell:update-atomdoc', 'atomdoc'])
   grunt.registerTask 'clean', ->
     rm = require('rimraf').sync
     rm 'build'


added coffeelint to Gruntfile

added coffeelint to Gruntfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -29,6 +29,25 @@
 					'!**/node_modules/**'
 				]

+		#
+		coffeelint:
+			scripts:
+				files:
+					src: ['./src/**/*.coffee', './test/**/*.coffee']
+			options:
+				no_trailing_whitespace:
+					level: 'error'
+				#Use one tab for indentation.
+				indentation:
+					value: 1
+					level: 'error'
+				# No maximum line length.
+				max_line_length:
+					level: 'ignore'
+				# Using tabs should not result in an error.
+				no_tabs:
+					level: 'ignore'
+
 		# Compile CoffeeScript (.coffee) files to JavaScript (.js).
 		coffee:
 			scripts:
@@ -304,6 +323,7 @@
 			scripts:
 				files: './src/scripts/**'
 				tasks: [
+					'coffeelint:scripts'
 					'coffee:scripts'
 					'copy:js'
 					'copy:scripts'
@@ -333,6 +353,9 @@
 	grunt.loadNpmTasks 'grunt-contrib-requirejs'
 	grunt.loadNpmTasks 'grunt-contrib-watch'

+	# coffeelint
+	grunt.loadNpmTasks('grunt-coffeelint');
+
 	# Express server + LiveReload
 	grunt.loadNpmTasks 'grunt-express'

@@ -380,6 +403,7 @@
 	# grunt
 	grunt.registerTask 'default', [
 		'clean:working'
+		'coffeelint:scripts'
 		'coffee:scripts'
 		'copy:js'
 		'less'


fix typo

fix typo

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -78,7 +78,7 @@
       release:
         src: 'CHANGELOG.md'

-  # Constructs the code, runs tests and if everyting is OK, creates a minified
+  # Constructs the code, runs tests and if everything is OK, creates a minified
   # version ready for production. This task is intended to be run manually.
   grunt.registerTask 'build', 'Bumps version and builds JS.', (version_type) ->
     version_type = 'patch' unless version_type in ['patch', 'minor', 'major']


chore(grunt): strictly enforce jshint

chore(grunt): strictly enforce jshint

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -72,7 +72,6 @@
     jshint:
       options:
         jshintrc: '.jshintrc'
-        force: yes
       watch: [ '<%= app.app %>/scripts/**/*.js' ]

     clean:


Update grunt compress task configuration to fix corrupted output files

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -23,8 +23,9 @@

         expand: true
         extDot: 'last'
+        ext: '.js.gz'
         cwd: 'dist/'
-        src: ['**/*']
+        src: ['**/*.js']
         dest: 'dist/'

     hash:


Gruntfile: Run stylus task before coffee task.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -207,9 +207,9 @@
       'clean'
       'mkdir'
       'spriteGenerator'
-      'coffee'
-      'coffee2closure'
-      'stylus'
+      'stylus'
+      'coffee'
+      'coffee2closure'
       'deps'
       'karma:headless'
       'coveralls'
@@ -223,9 +223,9 @@
       'clean'
       'mkdir'
       'spriteGenerator'
-      'coffee'
-      'coffee2closure'
-      'stylus'
+      'stylus'
+      'coffee'
+      'coffee2closure'
       'deps'
       'karma:headless'
       'builder'
@@ -237,9 +237,9 @@
       'clean'
       'mkdir'
       'spriteGenerator'
-      'coffee'
-      'coffee2closure'
-      'stylus'
+      'stylus'
+      'coffee'
+      'coffee2closure'
       'deps'
       'karma:headless'
       'builder'
@@ -253,9 +253,9 @@
       'clean'
       'mkdir'
       'spriteGenerator'
-      'coffee'
-      'coffee2closure'
-      'stylus'
+      'stylus'
+      'coffee'
+      'coffee2closure'
       'deps'
       'karma:headless'
       'http-server:debug'


fixed peerconnection copy

fixed peerconnection copy

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -94,6 +94,7 @@
           dest: 'build/'
           onlyIf: 'modified'
         } ] }
+      peerconnection: Rule.copyModule 'peerconnection'
       logger: Rule.copyModule 'logger'
       # Sample apps to demonstrate and run end-to-end tests.
       sampleChat: Rule.copySampleFiles 'peerconnection/samples/chat-webpage', 'lib'
@@ -194,6 +195,7 @@
   taskManager.add 'peerconnection', [
     'base'
     'typescript:peerconnection'
+    'copy:peerconnection'
   ]

   taskManager.add 'chat', [


Boyscout - remove extra enter

Boyscout - remove extra enter

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -31,7 +31,6 @@
         src: [
           'build/js/libs/jquery-1.7.2.js'
           'build/js/core.js'
-
           'build/js/wrap/first.js'
           'build/js/plugins/*.js'
           'build/js/classes/*.js'


Documentation for Gruntfile

Documentation for Gruntfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -122,17 +122,26 @@
   grunt.loadNpmTasks 'grunt-service'
   grunt.loadNpmTasks 'grunt-version'

+  # `compile` generates final JS code ready to be used
   grunt.registerTask 'compile', ['coffee:release', 'browserify', 'uglify:release']
+  # `css` builds sample CSS required by some elements like noUiSlider
   grunt.registerTask 'css', ['copy:build_scss', 'sass:build_scss', 'clean:build_scss']

+  # `test:mocha` runs Mocha tests in NodeJS
   grunt.registerTask 'test:mocha', ['mochaTest:release']
+  # `test:karma` runs browser tests in Karma
   grunt.registerTask 'test:karma', ['copy:karma', 'karma:test', 'clean:karma']
+  # `test` runs all tests
   grunt.registerTask 'test', ['test:mocha', 'test:karma']

+  # `dev` runs the development environment, with a server at localhost:8008
+  grunt.registerTask 'dev', ['service:http', 'compile', 'watch:default']
+  # `dev:test` runs the development environment and tests are run after compile
+  grunt.registerTask 'dev:test', ['service:http', 'compile', 'test', 'watch:test']
+  # well, the default
   grunt.registerTask 'default', ['dev']
-  grunt.registerTask 'dev', ['service:http', 'compile', 'watch:default']
-  grunt.registerTask 'dev:test', ['service:http', 'compile', 'test', 'watch:test']

+  # if you need only the server, here you are
   grunt.registerTask "serve", ["service:http"]

   #


Exclude cs and coffeescript while building minified js file.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -112,14 +112,20 @@
         name: "umobi"
         exclude: [
           "jquery"
+          "cs"
           "coffee-script"
           "depend"
           "text"
           "text!../version.txt"
         ]
+        CoffeeScript:
+          bare: true
         out: rootFile + ".compiled.js"
-
-        # wrap: { startFile: 'build/wrap.start', endFile: 'build/wrap.end' },
+        pragmasOnSave:
+          umobiBuildExclude: true
+        wrap:
+          startFile: 'build/wrap.start'
+          endFile: 'build/wrap.end'
         findNestedDependencies: true
         skipModuleInsertion: true
         optimize: "none"


wspace

wspace

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -7,9 +7,11 @@
     pkg: pkg
     cfg: cfg

+
     clean:
       compile:
         src: ['.tmp', 'public/assets']
+

     browserify:
       options:
@@ -20,6 +22,7 @@
       compile:
         src: 'app/javascripts/client/index.coffee'
         dest: 'public/assets/app.js'
+

     stylus:
       stylesheets:


CLOSE #278: change root directory in release package

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -211,7 +211,7 @@
           expand: true,
           cwd: 'package/'
           src: '**',
-          dest: './'
+          dest: 'simditor-<%= pkg.version %>/'
         }]

     clean:


Adjust gruntfile tasks

Adjust gruntfile tasks

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -10,7 +10,7 @@
       banner: "/* ===========================================================\n" +
         "# <%= pkg.name %> - v<%= pkg.version %>\n" +
         "# ==============================================================\n" +
-        "# Copyright (C) 2014 <%= pkg.author.name %>\n" +
+        "# Copyright (c) 2014 <%= pkg.author.name %>\n" +
         "# Licensed under the MIT license.\n" +
         "*/\n"

@@ -26,19 +26,21 @@
       options:
         bare: true
       default:
-        expand: true
-        flatten: true
-        cwd: "src/coffee"
-        src: ["*.coffee"]
-        dest: "lib"
-        ext: ".js"
+        files: grunt.file.expandMapping(['**/*.coffee'], 'lib/',
+          flatten: false
+          cwd: 'src/coffee'
+          ext: '.js'
+          rename: (dest, matchedSrcPath) ->
+            dest + matchedSrcPath
+          )
       test:
-        expand: true
-        flatten: true
-        cwd: "src/spec"
-        src: ["*.spec.coffee"]
-        dest: "test"
-        ext: ".spec.js"
+        files: grunt.file.expandMapping(['**/*.spec.coffee'], 'test/',
+          flatten: false
+          cwd: 'src/spec'
+          ext: '.spec.js'
+          rename: (dest, matchedSrcPath) ->
+            dest + matchedSrcPath
+          )

     concat:
       options:


Update Gruntfile to signoff on release commits

Signed-off-by: Byron Ruth <e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98@devel.io>

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -462,11 +462,11 @@
             changeVersion(fname, pkg.version)

         run 'git add bower.json package.json src/js/cilantro/core.js'
-        run "git commit -m '#{ [version.major, version.minor, version.patch].join('.') } Bump'"
+        run "git commit -s -m '#{ [version.major, version.minor, version.patch].join('.') } Bump'"

     grunt.registerTask 'tag-release', 'Create a release on master', ->
         run 'git add bower.json package.json src/js/cilantro/core.js'
-        run "git commit -m '#{ pkg.version } Release'"
+        run "git commit -s -m '#{ pkg.version } Release'"
         run "git tag #{ pkg.version }"

     grunt.registerTask 'release-binaries', 'Create a release binary for upload', ->


Add default task.

Add default task.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -98,6 +98,7 @@
   grunt.loadNpmTasks 'grunt-bump'

   # register tasks
+  grunt.registerTask 'default', ['build']
   grunt.registerTask 'build', ['clean', 'coffeelint', 'coffee', 'concat']
   grunt.registerTask 'coverage', ['build', 'shell:coverage']
   grunt.registerTask 'test', ['build', 'shell:jasmine']


Mention CSS in logged message

Mention CSS in logged message

--- benchmark/benchmark.coffee
+++ benchmark/benchmark.coffee
@@ -24,4 +24,4 @@
 tokenizeFile(path.join(__dirname, 'large.js'), jsGrammar, 'Tokenizing jQuery v2.0.3')
 tokenizeFile(path.join(__dirname, 'large.min.js'), jsGrammar, 'Tokenizing jQuery v2.0.3 minified')
 tokenizeFile(path.join(__dirname, 'bootstrap.css'), cssGrammar, 'Tokenizing Bootstrap CSS v3.1.1')
-tokenizeFile(path.join(__dirname, 'bootstrap.min.css'), cssGrammar, 'Tokenizing Bootstrap v3.1.1 minified')
+tokenizeFile(path.join(__dirname, 'bootstrap.min.css'), cssGrammar, 'Tokenizing Bootstrap CSS v3.1.1 minified')


Add dfmt

Add dfmt

--- src/beautifiers/index.coffee
+++ src/beautifiers/index.coffee
@@ -38,6 +38,7 @@
     'coffee-formatter'
     'coffee-fmt'
     'clang-format'
+    'dfmt'
     'elm-format'
     'htmlbeautifier'
     'csscomb'


Table still not working correctly, but certain elements are

--- frontend/coffeescript/app/webform/views.coffee
+++ frontend/coffeescript/app/webform/views.coffee
@@ -187,30 +187,41 @@
               # Create the table/grid!
               table_name = @input_fields[question].name + '_table'
               $('#' + @input_fields[question].name + '_field')
-                .fadeIn(1)
+                #.fadeIn(1)
                 .append($('

'))
-                .addClass('active')
+                #.addClass('active')
+
+              # Keep track of the parent question, it will be needed later
+              parent_question = @input_fields[question]

               question++
               question_info = @input_fields[question]
               # Add the First row to the table
               $('#' + table_name).append('

')
               $('#' + question_info.name + ' option').each( () ->
-                console.log($(@))
                 $('#' + table_name + ' tr').append('' + $(@)[0].innerHTML + '	')
               )

               while question_info.tree is current_tree
                 switch_question = $('#' + $($('.control-group').eq(question)[0]).data('key') + "_field")

-                # First, change the select to a list, then change the options to radio buttons
+                # First, change the start of the field to a table row
                 attrs = { }
-                $.each($('#' + switch_question[0].id + ' select')[0].attributes, (idx, attr) ->
+                $.each($('#' + switch_question[0].id)[0].attributes, (idx, attr) -> # + ' select'
                   attrs[attr.nodeName] = attr.nodeValue
                 )
-                $('#' + switch_question[0].id + ' select').replaceWith( () ->
+                $('#' + switch_question[0].id).replaceWith( () -> # + ' select'
                   return $("
", attrs).append($(@).contents()) )

+                # Remove select (But not children!), since we don't want a select list
+                selCont = $('#' + switch_question[0].id + ' select').contents()
+                $('#' + switch_question[0].id + ' select').replaceWith(selCont)
+
+                # Append the question to the table, then add the label as a cell
+                $('#' + switch_question[0].id).appendTo($('#' + table_name + ' tbody'))
+                $('#' + switch_question[0].id + ' label').wrap('')
+
+                # then change the options to radio buttons
                 $('#' + switch_question[0].id + ' option').each( (index) ->
                   attrs = { }
                   $.each($(@)[0].attributes, (idx, attr) ->
@@ -220,14 +231,20 @@
                   attrs.name = switch_question.data('key')
                   $(@).replaceWith( () ->
                     return $("<input />", attrs) ) #.append($(@).contents()) )
-                  $(@).parent().wrap('')#.parent()
-                  #$(@).appendTo($('#' + table_name)).wrap('	')
+                  $(@).wrap('').parent()
+                  #$(@).appendTo($('#' + table_name + ' tbody')).wrap('
')
                 )

-                switch_question.fadeIn(1).addClass('active')
-                $('.active input').focus()
+                console.log(switch_question)
+                #switch_question.fadeIn(1).addClass('active')
+                #$('.active input').focus()
                 question++
                 question_info = @input_fields[question]
+
+              # Finally, show the question and table
+              $('#' + parent_question.name + '_field').addClass('active').fadeIn(1)
+              #$('#' + table_name).addClass('active').fadeIn(1)
+              $('.active input').focus()

           # Assumption of a group without controls
           else


HasProperties should be able to handle missing attrs and options

--- src/coffee/model_base.coffee
+++ src/coffee/model_base.coffee
@@ -184,6 +184,10 @@
   initialize : (attrs, options) ->
     # auto generates ids if we need to, calls deferred initialize if we have
     # not done so already.   sets up datastructures for computed properties
+    if not attrs
+       attrs = {}
+    if not options
+      options = {}
     super(attrs, options)
     @properties = {}
     @property_cache = {}
@@ -554,4 +558,3 @@
     border_space : 30

   default_view : null
-


Add factor()

Add factor()

--- src/core/modules/data.coffee
+++ src/core/modules/data.coffee
@@ -70,6 +70,18 @@
   meta: meta
   fill: fill

+factor = (array) ->
+  _id = 0
+  dict = {}
+  domain = []
+  data = new Array array.length
+  for element, i in array
+    unless undefined isnt id = dict[element]
+      dict[element] = id = _id++
+      domain.push element
+    data[i] = id
+  [ domain, data ]
+
 Flow.Data =
   StringEnum: 'Enum<String>'
   String: 'String'
@@ -83,3 +95,4 @@
   DateArray: 'Array<Date>'
   Table: createTable
   createCompiledPrototype: createCompiledPrototype
+  factor: factor


hub: get rid of some stupid/useless try/catches that don't work for when connection fails, and replace by proper error handler, which is definitely the right way to do this.

--- salvus/hub.coffee
+++ salvus/hub.coffee
@@ -499,18 +499,14 @@
                 t = "ws://#{location.host}:#{location.port}"
                 proxy = _ws_proxy_servers[t]
                 if not proxy?
-                    winston.debug("websocket upgrade: not using cache")
-                    try
-                        proxy = httpProxy.createProxyServer(ws:true, target:t, timeout:0)
-                        _ws_proxy_servers[t] = proxy
-                    catch e
-                        winston.debug("websocket upgrade -- create proxy: ERROR/DEBUG/TODO -- SOMETHING WENT WRONG -- #{e}")
+                    winston.debug("websocket upgrade #{t}: not using cache")
+                    proxy = httpProxy.createProxyServer(ws:true, target:t, timeout:0)
+                    proxy.on "error", (e) ->
+                        winston.debug("websocket upgrade -- create proxy #{t} error -- #{e}")
+                    _ws_proxy_servers[t] = proxy
                 else
                     winston.debug("websocket upgrade: using cache")
-                try
-                    proxy.ws(req, socket, head)
-                catch e
-                    winston.debug("websocket upgrade -- proxy.ws: ERROR/DEBUG/TODO -- SOMETHING WENT WRONG -- #{e}")
+                proxy.ws(req, socket, head)


hub -- public raw paths with spaces (and any other encodings) in them didn't work

--- salvus/hub.coffee
+++ salvus/hub.coffee
@@ -664,7 +664,7 @@
         if not misc.is_valid_uuid_string(project_id)
             cb(undefined, false)
             return
-        path = v.slice(3).join('/')
+        path = decodeURI(v.slice(3).join('/'))
         winston.debug("public_raw: project_id=#{project_id}, path=#{path}")
         public_paths = undefined
         is_public = false
@@ -689,6 +689,7 @@
                                 setTimeout((()=>delete public_raw_paths_cache[project_id]), 15000)  # cache for 15s
                                 cb()
             (cb) ->
+                #winston.debug("public_raw -- path_is_in_public_paths(#{path}, #{misc.to_json(public_paths)})")
                 if not misc.path_is_in_public_paths(path, public_paths)
                     # The requested path is not public, so nothing to do.
                     cb()


remove redudant config file in both

remove redudant config file in both

--- both/config/_config.coffee
+++ both/config/_config.coffee
@@ -1,29 +0,0 @@
-@Config =
-	name: 'My App'
-	title: ->
-			TAPi18n.__ 'configTitle'
-	subtitle: ->
-			TAPi18n.__ 'configSubtitle'
-	logo: ->
-		'<b>' + @name + '</b>'
-	footer: ->
-		@name + ' - Copyright ' + new Date().getFullYear()
-	emails:
-		from: 'noreply@' + Meteor.absoluteUrl()
-	blog: 'http://benjaminpeterjones.com'
-	about: 'http://benjaminpeterjones.com'
-	username: false
-	homeRoute: '/dashboard'
-	socialMedia:
-		[
-			['http://facebook.com/benjaminpeterjones','facebook']
-			['http://twitter.com/BenPeterJones','twitter']
-			['http://github.com/yogiben','github']
-		]
-
-Avatar.options =
-	customImageProperty: 'profile.picture'
-
-Meteor.startup ->
-	if Meteor.isClient
-		TAPi18n.setLanguage('en')


make timeout latex more complex(slower)

make timeout latex more complex(slower)


Add visualization custom_fields in Nodes Table columns

--- app/frontend/javascripts/views/visualization-table-nodes.js.coffee
+++ app/frontend/javascripts/views/visualization-table-nodes.js.coffee
@@ -31,6 +31,12 @@

   onSync: =>
     console.log 'onSync', @model
+    # add custom_fields to table if defined
+    if @model.get('custom_fields')
+      @model.get('custom_fields').forEach (custom_field) =>
+        @tableColHeaders.splice       @tableColHeaders.length-1, 0, custom_field
+        @table_options.columns.splice @tableColHeaders.length-1, 0, { data: custom_field }
+    # get node types
     @getNodesTypes()

   # Setup Handsontable columns options


engine#add

engine#add

--- src/engine_game_state.coffee
+++ src/engine_game_state.coffee
@@ -60,10 +60,13 @@

   fadeIn: (options={}) ->
     self.cameras().invoke('fadeIn', options)
+
   fadeOut: (options={}) ->
     self.cameras().invoke('fadeOut', options)
+
   flash: (options={}) ->
     self.camera(options.camera).flash(options)
+
   objects: ->
     I.currentState.objects()
   setState: (newState) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_game_state.coffee
+++ src/engine_game_state.coffee
@@ -50,7 +50,8 @@
         return self
       else
         I.currentState.cameras()
-    fadeIn: (options=)
+    fadeIn: (options={}) ->
+
     flash: (options={}) ->
       self.camera(options.camera).flash(options)
     objects: ->


minor styling

minor styling

--- client/app/MainApp/sidebar/sidebarview.coffee
+++ client/app/MainApp/sidebar/sidebarview.coffee
@@ -41,7 +41,7 @@
     navAdditions = [
       { type  : 'separator',            order : 65 }
       # { title : 'Invite Friends', order : 66,  type : 'account',   role : 'member' }
-      { title : 'Get extra 250MB!',     order : 66,  type : 'account', promote: yes,   role : 'member' }
+      { title : 'Get extra 250MB!',     order : 66,  type : 'account promote', promote: yes,   role : 'member' }
       { title : 'Docs / Jobs',          order : 67,  type : 'account docs', docs : yes }
       { type  : 'separator',            order : 99 }
       { title : 'Logout',               order : 100, path : '/Logout',   type : 'account', loggedIn : yes }


component-lab: add a Modal variant for confirmation

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- client/component-lab/Modal/Modal.story.coffee
+++ client/component-lab/Modal/Modal.story.coffee
@@ -3,6 +3,7 @@
 Button = require 'lab/Button'
 Modal = require './Modal'
 Label = require 'lab/Text/Label'
+{ Row, Col } = require 'react-flexbox-grid'

 class ModalContainer extends React.Component
   constructor: (props) ->
@@ -33,22 +34,18 @@
     <ModalContainer>
       <Modal width="medium" height="short" showAlien={yes}>
         <Modal.Content>
-
Good News!

-

-
-              You have received $500 in free credit.
-
-

+
Are you sure?

-                Your free credit can be applied to payments for your entire team
-                after you have verified your email address and entered your
-                credit card.
+              You are about to remove your credit card. Do you want to continue?

-          <Button type="primary-1" auto={on} onClick={action 'OK, GOT IT'}>OK, GOT IT</Button>
+          <Row>
+            <Col xs><Button type="secondary" auto={on} onClick={action 'OK, GOT IT'}>CANCEL</Button></Col>
+            <Col xs><Button type="primary-1" auto={on} onClick={action 'OK, GOT IT'}>REMOVE</Button></Col>
+          </Row>
         </Modal.Content>
       </Modal>
     </ModalContainer>


I think I'm doing this right. Need to make the server component next to make sure.

--- browser/mockconsole.coffee
+++ browser/mockconsole.coffee
@@ -28,23 +28,61 @@

   outBuffer = ""
   bufferSize = 1024
+  serverPort = 8000
+  serverHost = "localhost"
+  uploading = false
+  commands = ["ls"]

-  nopCommandHandle = (line) ->
-    return
+  errorCommandHandle = (line) ->
+    sendErrorToServer "Command handle called before it was set."

   sendBufferToServer = ->
-    if outBuffer.length != 0
+    if outBuffer.length != 0 and !uploading
+      uploading = true
       # Send to server.
+      $.ajax({
+        type: "POST"
+        url: "message"
+        data: outBuffer
+      }).done(->
+        uploading = false
+        # In case we prevented further uploads...
+        if outputBuffer.length > bufferSize
+          sendBufferToServer
+      )
       # Empty buffer.
       outBuffer = ""

     return

   sendErrorToServer = (text) ->
+    # Errors aren't buffered; send 'em immediately.
+    $.ajax({
+      type: "POST"
+      url: "error"
+      data: text
+    })
+    return
+
+  sendCompleteToServer = ->
+    if !uploading
+      $.ajax({
+        type: "POST"
+        url: "complete"
+      })
+    else # Need to wait for upload to complete.
+      setTimeout sendCompleteToServer 100
     return

   runNextCommand = ->
+    if commands.length > 0
+      extern.commandHandle commands.pop()
+    else
+      # We're done. The browser can be killed now.
+      # Empty buffer first.
+      sendBufferToServer
+      sendCompleteToServer

   extern.reset = () ->
     # NOP
@@ -60,12 +98,12 @@
       outBuffer += msg
     else if $.isArray msg
       outBuffer += msg[0]
-    # jQuery console assumes input is a DOM node or something.
     else
+      # jQuery console assumes input is a DOM node or something.
       outBuffer += msg

     if outBuffer.length >= bufferSize
-      sendBufferToServer outBuffer
+      sendBufferToServer

     if !noreprompt
       extern.reprompt
@@ -75,13 +113,14 @@
   extern.reprompt = () ->
     # Clear buffer.
     sendBufferToServer
-
-    # Yield JS thread for at least 100ms. On resume, call 'runNextCommand'.
-    setTimeout runNextCommand, 100
+    # Yield thread before next command.
+    setTimeout runNextCommand 10

   extern.promptText = (text) ->
+    # We don't support promptText, since tests aren't supposed to be
+    # interactive.
     sendErrorToServer "PromptText called during a test."

-  extern.commandHandle = nopCommandHandle
+  extern.commandHandle = errorCommandHandle

-  return extern+  return extern


refactoring tests

refactoring tests

--- test/NoteTest.coffee
+++ test/NoteTest.coffee
@@ -4,38 +4,45 @@
 fs = require 'fs'
 util = require 'util'

-describe 'Note', () ->
-  describe 'without resources', () ->
+describe 'Note', ->
+  describe 'without resources', ->
     note_enml = fs.readFileSync TestConfig.fixtures['note']
     note = Note.parse note_enml
-    it '.parse() returns a note object.', () ->
-      note.should.be.an.instanceof(Note)
+    describe '.parser()', ->
+      it 'returns a note object.', ->
+        note.should.be.an.instanceof(Note)

-    it '#filename(extention) escapes title string.', () ->
-      note.filename('.md').should.equal 'a single note fixture test  1.md'
-      note.filename('.txt').should.equal 'a single note fixture test  1.txt'
+    describe '#filename(extention)', ->
+      it 'escapes title string.', ->
+        note.filename('.md').should.equal 'a single note fixture test  1.md'
+        note.filename('.txt').should.equal 'a single note fixture test  1.txt'

-    it '#title is a title string.', () ->
-      note.title.should.equal 'a single note fixture/test: 1'
+    describe '#title', ->
+      it 'is a title string.', ->
+        note.title.should.equal 'a single note fixture/test: 1'

-    it '#created is a Date object.', () ->
-      date = new Date 2013, 10, 2, 10, 7-TestConfig.TZOffsetMinutes, 9 # 20131102T100709Z
-      note.created.should.be.an.instanceof Date
-      note.created.should.eql date
+    describe '#created', ->
+      it '#created is a Date object.', ->
+        date = new Date 2013, 10, 2, 10, 7-TestConfig.TZOffsetMinutes, 9 # 20131102T100709Z
+        note.created.should.be.an.instanceof Date
+        note.created.should.eql date

-    it '#updated is a Date object.', () ->
-      date = new Date 2013, 10, 2, 10, 7-TestConfig.TZOffsetMinutes, 13  # 20131102T100713Z
-      note.updated.should.be.an.instanceof Date
-      note.updated.should.eql date
+    describe '#updated', ->
+      it '#updated is a Date object.', ->
+        date = new Date 2013, 10, 2, 10, 7-TestConfig.TZOffsetMinutes, 13  # 20131102T100713Z
+        note.updated.should.be.an.instanceof Date
+        note.updated.should.eql date

-    it '#tags is an array of string.', () ->
-      note.tags.should.be.an.instanceof Array
-      note.tags.length.should.equal 2
-      note.tags[0].should.equal 'markdown'
-      note.tags[1].should.equal 'evernote'
+    describe '#tags', ->
+      it 'is an array of string.', ->
+        note.tags.should.be.an.instanceof Array
+        note.tags.length.should.equal 2
+        note.tags[0].should.equal 'markdown'
+        note.tags[1].should.equal 'evernote'

-    it '#content is a Markdown string.', () ->
-      note.content.should.equal 'fixture content\n\n'
+    describe '#content', ->
+      it 'is a Markdown string.', ->
+        note.content.should.equal 'fixture content\n\n'

   describe 'with image resources', () ->
     note_enml = fs.readFileSync TestConfig.fixtures['image']
@@ -45,7 +52,8 @@
       '![png image][0]\n\n' +
       'The end line.\n\n\n\n' +
       '[0]: resources/' + hash + '.png'
-    it '#attachments are loaded.', () ->
-      note.content.should.equal content_expected
-      note.attachmentsLength.should.equal 1
-      note.attachments[hash].data.length.should.equal 7551
+    describe '@attachments', ->
+      it 'are loaded.', () ->
+        note.content.should.equal content_expected
+        note.attachmentsLength.should.equal 1
+        note.attachments[hash].data.length.should.equal 7551


collaboration: controller use statemachine to end

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -698,6 +698,12 @@
       when 'NotStarted' then @prepareChatSession()

+  stopCollaborationSession: ->
+
+    switch @stateMachine.state
+      when 'Active' then @stateMachine.transition 'Ending'
+
+
   showChatPane: ->

     @chat.showChatPane()
@@ -716,42 +722,6 @@
     @rtm = new RealtimeManager
     @showShareButton()
     @rtm.ready => @initCollaborationStateMachine()
-
-
-  # should clean realtime manager.
-  # should delete workspace channel id.
-  # should broadcast the session ended message.
-  # IF USER IS HOST
-  #   should delete private message.
-  #   should set machine sharing status to off.
-  # IF USER IS NOT HOST
-  #   should only call the callback without an error.
-  #   given callback should do the rest. (e.g cleaning-up, quitting..)
-  stopCollaborationSession: (callback) -> @whenRealtimeReady =>
-
-    @chat.settingsPane.endSession.disable()
-
-    return callback msg : 'no social channel'  unless @socialChannel
-
-    if @amIHost
-      @broadcastMessage { type: 'SessionEnded' }
-
-    title = @getRealtimeFileName()
-    realtimeHelpers.deleteCollaborationFile @rtm, title, (err) =>
-      return throwError err  if err
-      @setMachineSharingStatus off, (err) =>
-        return callback err  if err
-        @deletePrivateMessage (err) =>
-          return callback err  if err
-          @cleanupCollaboration { reinit: yes }
-          callback()
-
-      @statusBar.emit 'CollaborationEnded'
-      @stopChatSession()
-      @modal?.destroy()
-
-
-    @mySnapshot.clear()

   getCollaborationHost: -> if @amIHost then nick() else @collaborationHost


Don't rely on status bar for background color

--- src/packages/editor-stats/src/editor-stats-view.coffee
+++ src/packages/editor-stats/src/editor-stats-view.coffee
@@ -14,8 +14,6 @@
     super

     @command 'core:cancel', @detach
-    @statusBar = @rootView.find('.status-bar')
-    @css 'background', @statusBar.css('background-color')

   draw: ->
     @x ?= d3.scale.ordinal().domain d3.range(@stats.hours * 60)


CollaborationController: Expand host's open folders when joined a session.

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -879,6 +879,12 @@
     @on 'SetMachineUser',   @bound 'broadcastMachineUserChange'
     @on 'SnapshotUpdated',  @bound 'handleSnapshotUpdated'

+    unless @amIHost
+      openFolders = @rtm.getFromModel('commonStore').get 'openFolders'
+
+      for path in openFolders
+        @finderPane.finderController.expandFolder path
+

   transitionViewsToActive: ->


Refactor main file

Refactor main file

--- source/micro.coffee
+++ source/micro.coffee
@@ -1,74 +1,15 @@
-u = do ->
-  'use strict'
+u            = require('./micro.core.coffee')
+dom          = require('./micro.dom.coffee')
+utils        = require('./micro.utils.coffee')
+localstorage = require('./micro.localstorage.coffee')
+ajax         = require('./micro.ajax.coffee')

-  forEach = Array::forEach
-  push    = Array::push
-  slice   = Array::slice
+utils.extend u::, dom
+utils.extend u, utils, db: localstorage, ajax

-  u = window.u = (selector) ->
-    if !(this instanceof u)
-      return new u(selector)
+if typeof module isnt 'undefined'
+  if module.exports
+    exports = module.exports = u
+  exports.u = u

-    if !selector
-      return this
-
-    if selector instanceof u
-      return selector
-
-    if typeof selector is 'string'
-      return push.apply this, slice.call(document.querySelectorAll(selector))
-
-    if typeof selector is 'function'
-      u(document).ready selector
-
-  u:: =
-    length: 0
-
-    each: (callback) ->
-      forEach.call this, (el, idx) ->
-        callback.call el, idx
-      return this
-
-    ready: (callback) ->
-      if document.readyState is 'complete'
-        callback u
-
-      document.addEventListener 'DOMContentLoaded', ->
-        callback u
-
-    text: (value) ->
-      return this[0].textContent if value is undefined
-      this.each ->
-        this.textContent = value
-
-    find: (selector) ->
-      results = new u()
-      this.each ->
-        window.el = this
-        push.apply results, slice.call(this.querySelectorAll selector)
-      return results
-
-    html: (value) ->
-      return this[0].innerHTML if value is undefined
-      this.each ->
-        this.innerHTML = value
-
-    append: (el) ->
-      this.each ->
-        this.appendChild el
-
-    prepend: (el) ->
-      this.each ->
-        this.insertBefore el, this.firstChild
-
-    on: (type, func) ->
-      this.each ->
-        this.addEventListener type, func
-
-    off: (type, func) ->
-      this.each ->
-        this.removeEventListener type, func
-
-   u
-
-@u = u+window.u = u if typeof window isnt 'undefined'


Treat templates as javascript for sourcemap syntax

resolves #638

--- src/fs_utils/generate.coffee
+++ src/fs_utils/generate.coffee
@@ -158,7 +158,7 @@
   waterfall chained, callback

 generate = (path, sourceFiles, config, optimizers, callback) ->
-  type = if sourceFiles.some((file) -> file.type is 'javascript')
+  type = if sourceFiles.some((file) -> file.type in ['javascript', 'template'])
     'javascript'
   else
     'stylesheet'


Update config.cson

Update config.cson

--- dotfiles/.atom/config.cson
+++ dotfiles/.atom/config.cson
@@ -1,6 +1,5 @@
 "*":
   core:
-    openEmptyEditorOnStart: false
     telemetryConsent: "limited"
   editor:
     scrollPastEnd: true


cooldown finilazation

cooldown finilazation

--- test/cooldown.coffee
+++ test/cooldown.coffee
@@ -100,6 +100,6 @@

 # TODO: tests to make sure value isn't overwritten if the instance variable already exists
 # TODO: tests to make sure value is defaulted to 0, but only if the instance variable doesn't exist
-
+test ""

 module()