indent local identd warning

indent local identd warning

--- test/integration/api.coffee
+++ test/integration/api.coffee
@@ -468,7 +468,7 @@
             done()
           socket.on 'error', (err) =>
             if /REFUS/.test err.code # ECONNREFUSED
-              console.warn "You are not running an identd locally, so this test won't work"
+              console.warn "          You are not running an identd locally, so this test won't work"
               @skip = true
             else
               throw err


redis test

redis test

--- scripts/slack-farmer.coffee
+++ scripts/slack-farmer.coffee
@@ -246,8 +246,8 @@
         if rnd < 2 && cropAdventFlg == true
             endHarvestCrop robot

- #   robot.respond /sftes/i, (msg) ->
- #       setCrop robot
+    robot.respond /sftes/i, (msg) ->
+        setCrop robot

     robot.respond /sfget/i, (msg) ->
         harvestCrop robot, msg


Restore single-mode when exiting multi-mode in keysTree

--- muleview/app/controller/KeysTree.coffee
+++ muleview/app/controller/KeysTree.coffee
@@ -20,9 +20,19 @@
       selector: "#btnSwitchToMultiple"
   ]

-  onSelectionChange: (me, selected)->
-    return unless selected[0] and not @multiMode
-    key = selected[0].get("fullname")
+  updateSelectedKey: ->
+    # Selection doesn't apply rendering in multiMode;
+    return if @multiMode
+
+    # Get current selection:
+    selection = @getTree().getSelectionModel().getSelection()
+    selected = selection[0]
+
+    # Quit if nothing selected:
+    return unless selected
+
+    # update:
+    key = selected.get("fullname")
     Muleview.event "viewChange", key, Muleview.currentRetention

   onCheckChange: ->
@@ -33,7 +43,7 @@
     @store = @getTree().getStore()

     @getTree().on
-      selectionchange: @onSelectionChange
+      selectionchange: @updateSelectedKey
       itemexpand: @onItemExpand
       checkchange: @onCheckChange
       scope: @
@@ -64,6 +74,8 @@
       if multi
         checked = node == selectedNode
       node.set("checked", checked)
+    unless multi
+      @updateSelectedKey()

   onItemExpand: (node) ->
     # We set the node as "loading" to reflect that an asynch request is being sent to request deeper-level keys


add build/ and coverage/ to testing info

--- test/lib/test-fspath.coffee
+++ test/lib/test-fspath.coffee
@@ -62,7 +62,7 @@
     '.gitignore', '.npmignore', '.travis.yml', 'LICENSE', 'CHANGES.md',
     'README.md', 'package.json'
   ]
-  rootDirs = [ '.git', 'lib', 'test', 'node_modules' ]
+  rootDirs = [ '.git', 'lib', 'test', 'node_modules', 'build', 'coverage' ]
   rootPaths = [].concat(rootDirs).concat(rootFiles)

   # path.path


Updated comments

Updated comments

--- test/spec/lib/url.tests.coffee
+++ test/spec/lib/url.tests.coffee
@@ -423,7 +423,7 @@
     path: "/bar"

-  # KM: removed - localized urls
+  # KM: removed - localized urls - would need to bundle punycode

   # IDNA tests
   # "http://www.日本語.com/":


Fix the misnomer in the method impl

Fix the misnomer in the method impl

--- library/gearing/service.coffee
+++ library/gearing/service.coffee
@@ -73,7 +73,7 @@
         presource = (p) -> gresource = pathname.match(0)
         pdomain = _.find(domains, pdomain)
         presource = _.find(resources, presource)
-        return domains: gdomain, resources: gresource
+        return domain: gdomain, resource: gresource

     # This is a very basic method that adds the specified regular
     # expression pattern to the list of permitted resource patterns.


this scope binding fixes in the duplex

this scope binding fixes in the duplex

--- library/membrane/duplex.coffee
+++ library/membrane/duplex.coffee
@@ -70,10 +70,10 @@
         assert method.length >= 1, invalidArgs
         method.provider = Object.create {}
         method.providing = (s) -> (args..., callback) ->
-            respond = -> callback.apply this, arguments
-            respond.socket = s; method args..., respond
-        method.origin = this
-        return method
+            execute = => method.apply this, arguments
+            respond = => callback.apply this, arguments
+            respond.socket = s; execute args..., respond
+        method.origin = this; return method

     # This server side method is called on the context prior to the
     # context being compiled and flushed down to the client site. The
@@ -127,7 +127,8 @@
             providing = value?.providing or null
             return unless _.isFunction providing
             assert _.isFunction(value), internal
-            binder = (s) => s.on name, providing(s)
+            bound = (s) => providing(s).bind this
+            binder = (s) => s.on name, bound(s)
             context.on "connection", binder
         return next()


Add handshake upstream hook

Add handshake upstream hook

--- library/membrane/duplex.coffee
+++ library/membrane/duplex.coffee
@@ -69,6 +69,13 @@
     connected: (context, socket, next) -> next()

     # A usable hook that gets asynchronously invoked once a new
+    # socket connection is going to be setup during the handshake.
+    # The method gets a set of parameters that maybe be useful to
+    # have by the actual implementation. Please remember thet the
+    # method is asynchronously wired, so be sure to call `next`.
+    handshake: (context, handshake, next) -> next()
+
+    # A usable hook that gets asynchronously invoked once a new
     # channel (socket) gets past authorization phase and is rated
     # to be good to go through the screening process. This is good
     # place to implementation various schemes for authorization. If
@@ -87,19 +94,21 @@
     # during the handshake phase. This method checks that handshake
     # contains correct session requsities and restores the session!
     # The session can normally be used as you would use req session.
-    authorization: (handshake, accept) ->
+    authorization: (context) => (handshake, accept) ->
         assert _.isFunction session = @kernel.session
         assert _.isFunction cookies = @kernel.cookieParser
         handshake.originalUrl = handshake.url or "/"
         Response = class RDummy extends EventEmitter2
         Response::setHeader = (name, value) -> undefined
         Response::end = (data, encoding) -> undefined
-        cookies handshake, response = new Response, ->
-            session handshake, response, ->
+        cookies handshake, response = new Response, =>
+            session handshake, response, =>
                 session = handshake.session
                 ns = new Error "no session found"
                 return accept ns, no unless session
-                return accept undefined, yes
+                upstream = @upstreamAsync "handshake", ->
+                    return accept undefined, yes
+                return upstream handshake, context

     # An internal, static method that is used to obtain gurading
     # domains for each of the declared server site providers. Please
@@ -271,7 +280,8 @@
         context = kernel.secureSocket.of @location()
         pure = /[a-zA-Z0-9/-_]+/.test @location()
         assert pure, "location is not pure enough"
-        context.authorization @authorization.bind(@)
+        assert applied = @authorization context
+        context.authorization applied.bind this
         context.on "connection", (socket) =>
             socket.on "screening", (binder, ack) =>
                 screening = @upstreamAsync "screening", =>


Fixed broken test for revertFile.

Fixed broken test for revertFile.

--- test/src/client_test.coffee
+++ test/src/client_test.coffee
@@ -397,8 +397,8 @@
         @client.revertFile @newFile, @versionTag, (error, stat) =>
           expect(error).to.equal undefined
           expect(stat).to.be.instanceOf Dropbox.Stat
-          expect(stat.versionTag).to.equal @versionTag
           expect(stat.path).to.equal @newFile
+          expect(stat.isRemoved).to.equal false
           @client.readFile @newFile, (error, data, stat) =>
             expect(error).to.equal undefined
             expect(data).to.equal @textFileData
@@ -406,6 +406,7 @@
               # Stat is not available in the browser due to CORS restrictions.
               expect(stat).to.be.instanceOf Dropbox.Stat
               expect(stat.path).to.equal @newFile
+              expect(stat.isRemoved).to.equal false
             done()

   describe 'findByName', ->
@@ -508,7 +509,7 @@

   describe 'readThumbnail', ->
     it 'reads the image into a string', (done) ->
-      @timeout 4 * 1000  # Thumbnail generation is slow.
+      @timeout 12 * 1000  # Thumbnail generation is slow.
       @client.readThumbnail @imageFile, { png: true }, (error, data, stat) =>
         expect(error).to.equal undefined
         expect(data).to.be.a 'string'
@@ -522,7 +523,7 @@

     it 'reads the image into a Blob', (done) ->
       return done() unless Blob?
-      @timeout 4 * 1000  # Thumbnail generation is slow.
+      @timeout 12 * 1000  # Thumbnail generation is slow.
       options = { png: true, blob: true }
       @client.readThumbnail @imageFile, options, (error, blob, stat) =>
         expect(error).to.equal undefined


Improve thumbnails

Improve thumbnails

--- server/lib/thumb.coffee
+++ server/lib/thumb.coffee
@@ -62,34 +62,30 @@
         gmRunner = gm(srcPath)

         if name is 'thumb'
-            buildThumb = (width, height) ->
-                gmRunner
-                .resize(width, height)
-                .crop(300, 300, 0, 0)
-                .stream (err, stdout, stderr) ->
-                    if err
-                        # Releases stream if an error occurs
-                        releaseStream stdout
-                        callback err
-                    else
-                        # Attach resized file in document
-                        binaryManagement.addBinary file, data, stdout, (err)->
-                            return callback err if err?
+            gmRunner
+            .resize(300, 300, '^')  # Fill 300x300
+            .gravity('Center')      # Combined with extent -v
+            .extent(300, 300)       # Crop to 300x300 centered
+            .background('None')     # Preserve alpha
+            .noProfile()            # Strip EXIF
+            .stream (err, stdout, stderr) ->
+                if err
+                    # Releases stream if an error occurs
+                    releaseStream stdout
+                    callback err
+                else
+                    # Attach resized file in document
+                    binaryManagement.addBinary file, data, stdout, (err)->
+                        return callback err if err?

-                    stdout.on "end", callback
-
-            gmRunner.size (err, data) ->
-                if err
-                    callback err
-                else if data.width > data.height
-                    buildThumb null, 300
-                else
-                    buildThumb 300, null
+                stdout.on "end", callback

         else if name is 'screen'
             # Resize file
             gmRunner
-            .resize(1200, 800)
+            .resize(1200, 800)   # Fit in 1200x800
+            .background('None')  # Preserve alpha
+            .noProfile()         # Strip EXIF
             .stream (err, stdout, stderr) ->
                 if err
                     # Releases stream if an error occurs


Don’t search unless there is a find pattern

--- lib/project-find-view.coffee
+++ lib/project-find-view.coffee
@@ -235,6 +235,8 @@
     pathsPattern = @pathsEditor.getText()
     replacePattern = @replaceEditor.getText()

+    return unless findPattern
+
     @showResultPane().then =>
       try
         @model.search(findPattern, pathsPattern, replacePattern, options)


fix(next/prev): Move buttons to correct column #1721

--- internal_packages/thread-list/lib/main.cjsx
+++ internal_packages/thread-list/lib/main.cjsx
@@ -30,11 +30,11 @@
       location: WorkspaceStore.Location.DraftList.Toolbar

     ComponentRegistry.register DownButton,
-      location: WorkspaceStore.Sheet.Thread.Toolbar.Right
+      location: WorkspaceStore.Location.MessageList.Toolbar
       modes: ['list']

     ComponentRegistry.register UpButton,
-      location: WorkspaceStore.Sheet.Thread.Toolbar.Right
+      location: WorkspaceStore.Location.MessageList.Toolbar
       modes: ['list']

     ComponentRegistry.register ThreadBulkArchiveButton,


Got rid of an occasional error popping up about contexts

--- client/coffeescript/templates/tagbox.coffee
+++ client/coffeescript/templates/tagbox.coffee
@@ -40,7 +40,7 @@
             Template.tagbox.add_tag(@_id, @my_tags, tag_text, tag_box)
           when 75 #K/Kill
             Session.get("suggestions_#{ @_id }").remove(tag_text)
-            Session.get("context_#{@_id}").invalidate()
+            Session.get("context_#{@_id}")?.invalidate()

         event.stopImmediatePropagation()

@@ -66,10 +66,10 @@
           when 39 #Right-arrow: REMOVE SUGGESTED TAG
             if event.ctrlKey
               Session.get("suggestions_#{ @_id }").remove(suggested_tag)
-              Session.get("context_#{@_id}").invalidate()
+              Session.get("context_#{ @_id }")?.invalidate()
           else    #Update filter with new text
             Session.set("filter_text_#{ @_id }", entered_text)
-            Session.get("context_#{@_id}").invalidate()
+            Session.get("context_#{ @_id }")?.invalidate()

         event.stopImmediatePropagation()

@@ -87,7 +87,7 @@
         Session.set("tagging_#{ @_id }", true)  #Display tagbox

         Session.set("suggestions_#{ @_id }",
-          if @suggestions? then @suggestions else [])
+          if @suggestions? then @suggestions else ['tag'])

         event.stopImmediatePropagation()

@@ -115,7 +115,7 @@
     #clear textbox and update suggestion filter
     text_box.value = ''
     Session.set("filter_text_#{ id }", '')
-    Session.get("context_#{ id }").invalidate()
+    Session.get("context_#{ id }")?.invalidate()
 )

 Handlebars.registerHelper('tags', (context, object) ->


set default props

set default props

--- client/skr/components/SalesOrderFinder.cjsx
+++ client/skr/components/SalesOrderFinder.cjsx
@@ -2,7 +2,12 @@

     propTypes:
         onModelSet: React.PropTypes.func
-        commands: React.PropTypes.object
+        commands:   React.PropTypes.object
+        autoFocus:  React.PropTypes.bool
+
+    getDefaultProps: ->
+        autoFocus: true
+        label: 'SalesOrder ID'

     dataObjects:
         sales_order: ->
@@ -25,11 +30,10 @@
                 ]
             })

-    getDefaultProps: ->
-        label: 'SalesOrder ID'

     render: ->
-        <LC.RecordFinder ref="finder" sm=2 autoFocus
+        <LC.RecordFinder ref="finder" sm=2
+            {...@props}
             model={@props.model}
             name='visible_id'
             onModelSet={@props.onModelSet}


whitespace

whitespace

--- app/assets/javascripts/models/cliente.js.coffee
+++ app/assets/javascripts/models/cliente.js.coffee
@@ -1,9 +1,9 @@
 Bandango.Cliente = DS.Model.extend Bandango.PersonImagen,
-  tipoId: DS.attr()
+  tipoId:         DS.attr()
   identificacion: DS.attr()
-  nombres: DS.attr()
-  direccion: DS.attr()
-  telefono: DS.attr()
-  email: DS.attr()
+  nombres:        DS.attr()
+  direccion:      DS.attr()
+  telefono:       DS.attr()
+  email:          DS.attr()

-  ventas: DS.hasMany("venta")
+  ventas:         DS.hasMany("venta")


Store origined path before redirecting to login. closes #3

--- app/assets/javascripts/sprangular/module.coffee
+++ app/assets/javascripts/sprangular/module.coffee
@@ -32,19 +32,23 @@
     .hashPrefix '!'
 ]

-Sprangular.run ($rootScope, $location, Account, Cart, Flash) ->
+Sprangular.run ($rootScope, $location, Status, Account, Cart, Flash) ->

   $rootScope.$on '$routeChangeStart', (event, next, current) ->
     requirements = next.requires || {}

     if requirements.user && !Account.isLogged
+      Status.requestedPath = next.$$route.originalPath
       Flash.error('Please sign in or register to continue.')
       $location.path('/sign-in')
+      event.preventDefault()

     else if requirements.guest && Account.isLogged
       Flash.error("Sorry, that page is only available when you're signed out.")
       $location.path('/')
+      event.preventDefault()

     else if requirements.cart && Cart.items.length == 0
       Flash.error('Sorry, there are no items in your cart.')
       $location.path('/')
+      event.preventDefault()


Add origin: to unit/model/dataset updateStatus test

--- test/unit/model/dataset.coffee
+++ test/unit/model/dataset.coffee
@@ -219,6 +219,18 @@
           boxes: ['foo', 'bar']
           type: 'ok'
           message: 'I scrapped some page :D'
+          origin:
+            box: @dataset.box
+            boxServer: @dataset.boxServer
+            user: @dataset.user
+            tool: @dataset.tool
+            displayName: @dataset.displayName
+            status: @dataset.status
+            views: @dataset.views
+            boxJSON: @dataset.boxJSON
+            createdDate: @dataset.createdDate
+            creatorShortName: @dataset.creatorShortName
+            creatorDisplayName: @dataset.creatorDisplayName

         published = @publishStub.calledWith @dataset.box, channel, message
         published.should.be.true


fix autoclose tag newline insert

fix autoclose tag newline insert

--- lib/atom-react.coffee
+++ lib/atom-react.coffee
@@ -285,7 +285,7 @@
             ]
           , '', {undo: 'skip'})

-    else if eventObj?.newText is '\n'
+    else if eventObj? and eventObj.newText.match /\r?\n/
       lines = editor.buffer.getLines()
       row = eventObj.newRange.end.row
       lastLine = lines[row - 1]


:lipstick: Remove unnecessary second git path definition.

--- lib/atomatigit.coffee
+++ lib/atomatigit.coffee
@@ -8,9 +8,7 @@
   atomatigitView: null

   activate: (state) ->
-    atom_git = atom.project.getRepo()
-    path = atom_git.getWorkingDirectory()
-    @repo = new Repo({path: path})
+    @repo = new Repo
     @repo_view = new RepoView(@repo)

     @insert_commands()


Add optional pagelength config.

Add optional pagelength config.

--- app/assets/javascripts/rocket_job_mission_control/datatable.js.coffee
+++ app/assets/javascripts/rocket_job_mission_control/datatable.js.coffee
@@ -4,6 +4,7 @@
     @columns = columns
     @ordering = opts.ordering ? true
     @searching = opts.searching ? true
+    @pageLength = opts.pageLength ? 100
     @order = opts.order ? []
     @reload = $("[data-behavior='reload']")
     @initializeTable()
@@ -12,7 +13,7 @@
   initializeTable: ->
     @data = @table.DataTable
       pagingType: 'full_numbers'
-      pageLength: 100
+      pageLength: @pageLength
       responsive: true
       ajax: @table.data('source')
       processing: true


added max sockets to s3 wrapper, getting some ETIMEDOUT under laod, may be cause

--- services/filestore/app/coffee/s3Wrapper.coffee
+++ services/filestore/app/coffee/s3Wrapper.coffee
@@ -1,3 +1,5 @@
+http = require('http')
+http.globalAgent.maxSockets = 300
 settings = require("settings-sharelatex")
 request = require("request")
 logger = require("logger-sharelatex")


Fix sign indicators not sharing numeric highlighting


recompile when theme.defaults changes

recompile when theme.defaults changes

--- server/less-compiler.coffee
+++ server/less-compiler.coffee
@@ -59,5 +59,5 @@

   changed: (_id, doc) ->
     # whenever a theme changes, re-render it
-    if doc.overrides? or doc.bootswatch? or doc.customLess?
+    if doc.defaults? or doc.overrides? or doc.bootswatch? or doc.customLess?
       updateTheme(_id)


Change CoffeeScript anagram example to camelCase.

--- assignments/coffeescript/anagram/example.coffee
+++ assignments/coffeescript/anagram/example.coffee
@@ -1,11 +1,11 @@
 class Anagram
   constructor: (source) ->
-    @source = standard_form(source)
+    @source = standardForm(source)

   match: (targets) ->
     target for target in targets when @source is standard_form target

-standard_form = (word) ->
+standardForm = (word) ->
   word.toLowerCase().split("").sort().join()

 module.exports = Anagram


Add the libs

Add the libs

--- lib/csv2object.coffee
+++ lib/csv2object.coffee
@@ -1,4 +1,7 @@
 fs = require "fs"
+
+split = require "split"
+csv = require "csv"

 csv2object = (filename, callback) ->


Link out to OCLC/WorldCat

Link out to OCLC/WorldCat

--- js/book-aligner.coffee
+++ js/book-aligner.coffee
@@ -65,6 +65,7 @@
             _.values(data.records)[0].publishDates[0],
             ht_object.enumcron || '',
             null,
+            oclc_href(_.values(data.records)[0].oclcs[0]),
             score
           ]).draw(false)
           $('#table').DataTable().columns.adjust().draw()
@@ -88,6 +89,10 @@

 ht_url = (ht_id) ->
   "https://babel.hathitrust.org/cgi/pt?id=#{ht_id}"
+
+oclc_href = (oclc_id) ->
+  if oclc_id?
+    "#{oclc_id}"

 process_ht_id = (ht_id, score = 0) ->
   ht_biblio_query(ht_id, score)
@@ -114,6 +119,7 @@
             data.metadata.year || '',
             data.metadata.volume || '',
             data.metadata.imagecount || '',
+            oclc_href(data.metadata['oclc-id']),
             score
           ]).draw(false)
           $('#table').DataTable().columns.adjust().draw()
@@ -168,6 +174,7 @@
             data.volumeInfo.publishedDate,
             '',
             data.volumeInfo.pageCount || '',
+            null,
             score
           ]).draw(false)
           $('#table').DataTable().columns.adjust().draw()
@@ -201,7 +208,7 @@
   $('#table').DataTable({
     paging: false
     autoWidth: true
-    order: [[ 6, "desc" ]]
+    order: [[ 7, "desc" ]]
     columns: [
       { title: "", orderable: false }
       { title: "Identifier" }
@@ -209,6 +216,7 @@
       { title: "Year" }
       { title: "Volume" }
       { title: "Pages" }
+      { title: "OCLC" }
       { title: "Score" }
     ]
   })


lod_by_time: Remove the comment saying it's awful code; it isn't that bad

--- lod_by_time/lod_by_time.coffee
+++ lod_by_time/lod_by_time.coffee
@@ -1,6 +1,4 @@
 # Interactive lod curves and QTL effect plot for all time points
-#
-# This is awful code; I just barely know what I'm doing.

 # function that does all of the work
 draw = (data) ->


Finder: Removed Unmount VM, Open Terminal and Upload File context menu items.

--- client/finder/filetree/controllers/findercontextmenucontroller.coffee
+++ client/finder/filetree/controllers/findercontextmenucontroller.coffee
@@ -204,11 +204,11 @@
       Refresh                     :
         action                    : 'refresh'
         separator                 : yes
-      'Unmount VM'                :
-        action                    : 'unmountVm'
-      'Open terminal'             :
-        action                    : 'openMachineTerminal'
-        separator                 : yes
+      # 'Unmount VM'                :
+      #   action                    : 'unmountVm'
+      # 'Open terminal'             :
+      #   action                    : 'openMachineTerminal'
+      #   separator                 : yes
       Expand                      :
         action                    : 'expand'
         separator                 : yes
@@ -222,8 +222,8 @@
         action                    : 'createFile'
       'New folder'                :
         action                    : 'createFolder'
-      'Upload file...'            :
-        action                    : 'upload'
+      # 'Upload file...'            :
+      #   action                    : 'upload'

     if fileView.expanded
     then delete items.Expand


home: use generic list component for virtual machines

--- client/home/lib/virtualmachines/components/virtualmachineslist.coffee
+++ client/home/lib/virtualmachines/components/virtualmachineslist.coffee
@@ -2,7 +2,8 @@
 React           = require 'kd-react'
 EnvironmentFlux = require 'app/flux/environment'
 KDReactorMixin  = require 'app/flux/base/reactormixin'
-List            = require './machineslist/list'
+List            = require 'app/components/list'
+MachineItem     = require './machineslist/listitem'

 module.exports = class VirtualMachinesListContainer extends React.Component
@@ -13,18 +14,48 @@
     }

+  numberOfSections: -> 1
+
+
+  numberOfRowsInSection: ->
+
+    @state.stacks
+      .toList()
+      .filter (stack) -> stack.get('title').toLowerCase() isnt 'managed vms'
+      .size
+
+
+  renderSectionHeaderAtIndex: -> null
+
+
+  renderRowAtIndex: (sectionIndex, rowIndex) ->
+
+    stack = @state.stacks.toList().get rowIndex
+
+    stack.get 'machines'
+      .sort (a, b) -> a.get('label').localeCompare(b.get('label')) # Sorting from a to z
+      .map (machine) ->
+        <MachineItem
+          key={machine.get '_id'} stack={stack} machine={machine}
+          shouldRenderDetails={yes}
+          shouldRenderSpecs={yes}
+          shouldRenderPower={yes}
+          shouldRenderAlwaysOn={yes}
+          shouldRenderSharing={yes} />
+
+
+  renderEmptySectionAtIndex: ->
No virtual machines.

+
+
   render: ->

-    stacks = @state.stacks.toList()
-      .filter (stack) -> stack.get('title').toLowerCase() isnt 'managed vms'
-
     <List
-      stacks={stacks}
-      shouldRenderDetails={yes}
-      shouldRenderSpecs={yes}
-      shouldRenderPower={yes}
-      shouldRenderAlwaysOn={yes}
-      shouldRenderSharing={yes} />
+      numberOfSections={@bound 'numberOfSections'}
+      numberOfRowsInSection={@bound 'numberOfRowsInSection'}
+      renderSectionHeaderAtIndex={@bound 'renderSectionHeaderAtIndex'}
+      renderRowAtIndex={@bound 'renderRowAtIndex'}
+      renderEmptySectionAtIndex={@bound 'renderEmptySectionAtIndex'}
+    />

 VirtualMachinesListContainer.include [KDReactorMixin]


Update tests

Update tests

--- tests/TestRunnerTest.coffee
+++ tests/TestRunnerTest.coffee
@@ -159,49 +159,49 @@
   console.error(err.stack)

+
+
+
+
 class MultipleTestAsyncSuiteTest

   self = null

-  isValid:true
+  value: null
+
+  name: "MultipleTestAsyncSuiteTest - verify multiple calls to testAsyncMulti are run serially"
+
+  changeTestValue: (value)->
+    @value = value
+    console.log("value=",value)

   constructor:->
     self = @

-  suiteSetup:(test,onComplete)->
-    console.log("MultipleTestAsyncSuiteTest.suiteSetup")
-    self.changeIsValidValue onComplete =>
-      test.isFalse self.isValid
+  suiteSetup:(test)->
+    console.log("MultipleTestSyncSuiteTest.suiteSetup")
+    self.changeTestValue("suiteSetup")
+    test.equal self.value,"suiteSetup"

   testOne: (test,onComplete)->
     console.log("MultipleTestAsyncSuiteTest.testOne")
-    self.changeIsValidValue onComplete =>
-      test.isTrue self.isValid
+    test.equal self.value,"suiteSetup"
+    withLatency 100, onComplete ->
+      test.equal self.value,"suiteSetup"
+      self.changeTestValue("testOne")
+

   testTwo: (test,onComplete)->
     console.log("MultipleTestAsyncSuiteTest.testTwo")
-    self.changeIsValidValue onComplete =>
-      test.isFalse self.isValid
-
-  changeIsValidValue:(cb)=>
-    withLatency( 1000,->
-      if self.isValid
-        self.isValid = false
-      else
-        self.isValid = true
-      cb()
-    )
-
-  suiteTearDown:(test,onComplete)->
-    console.log("MultipleTestAsyncSuiteTest.suiteTearDown")
-    self.changeIsValidValue onComplete =>
-      test.isTrue self.isValid
+    test.equal self.value,"testOne"
+

 try
   Munit.run(new MultipleTestAsyncSuiteTest())
 catch err
   console.error(err.stack)
+


detect prefix for perspective-origin

detect prefix for perspective-origin

--- oridomi.coffee
+++ oridomi.coffee
@@ -54,6 +54,7 @@
   transitionDuration: 'transitionDuration'
   transitionEasing: 'transitionTimingFunction'
   perspective: 'perspective'
+  perspectiveOrigin: 'perspectiveOrigin'
   backface: 'backfaceVisibility'

 # This function checks for the presence of CSS properties on the test div.


transitionProp -> transitionProperty

transitionProp -> transitionProperty

--- oridomi.coffee
+++ oridomi.coffee
@@ -112,7 +112,7 @@

 # Set a `transitionEnd` property based on the browser's prefix for `transitionProperty`.
 css.transitionEnd = do ->
-  switch css.transitionProp.toLowerCase()
+  switch css.transitionProperty.toLowerCase()
     when 'transitionproperty'       then 'transitionEnd'
     when 'webkittransitionproperty' then 'webkitTransitionEnd'
     when 'moztransitionproperty'    then 'transitionend'


player: trigger show:<view> event on render

--- coffee/src/player.coffee
+++ coffee/src/player.coffee
@@ -46,11 +46,6 @@
       eventhub: @eventhub
       editable: @options.editable

-    if @acornModel.isNew()
-      @eventhub.trigger 'show:editor'
-    else
-      @eventhub.trigger 'show:splash'
-

   destroy: =>
     @view.destroy()
@@ -65,8 +60,16 @@

   render: =>
+    @showView()
     @$el.append @view.render().el
     @
+
+
+  showView: =>
+    if @acornModel.isNew()
+      @eventhub.trigger 'show:editor'
+    else
+      @eventhub.trigger 'show:splash'

   appendTo: (sel) =>


:fire: unused file

:fire: unused file

--- keymaps/open-this.cson
+++ keymaps/open-this.cson
@@ -1,13 +0,0 @@
-# I don't want to set default keymap.
-# This is my principle for every my Atom Packages and Vim plugins I've created till now.
-
-
-# 'atom-text-editor.vim-mode.command-mode':
-#   'g f':      'open-this:here'
-#   'ctrl-w f': 'open-this:split-down'
-#   'ctrl-w F': 'open-this:split-right'
-#
-# 'atom-workspace atom-text-editor:not([mini])':
-#   'cmd-k f f': 'open-this:here'
-#   'cmd-k f d': 'open-this:split-down'
-#   'cmd-k f r': 'open-this:split-right'


Use alt+shift+t on windows and linux (close #56)

--- keymaps/show-todo.cson
+++ keymaps/show-todo.cson
@@ -7,5 +7,8 @@

 # For more detailed documentation see
 # https://atom.io/docs/latest/advanced/keymaps
-'atom-workspace':
+'.platform-darwin atom-workspace':
   'ctrl-shift-t': 'todo-show:find-in-project'
+
+'.platform-win32 atom-workspace, .platform-linux atom-workspace':
+  'alt-shift-t': 'todo-show:find-in-project'


Spelling :lipstick: in spec

Spelling :lipstick: in spec

--- spec/app/edit-session-spec.coffee
+++ spec/app/edit-session-spec.coffee
@@ -1866,7 +1866,7 @@
       expect(buffer.getLineCount()).toBe(1)
       expect(buffer.getText()).toBe('')

-  describe ".tranpose()", ->
+  describe ".transpose()", ->
     it "swaps two characters", ->
       editSession.buffer.setText("abc")
       editSession.setCursorScreenPosition([0, 1])


Skip applying tooltip attachment style if there's no attachment

--- lib/violation-tooltip.coffee
+++ lib/violation-tooltip.coffee
@@ -207,11 +207,12 @@
     shadow = "0 0 3px #{editorBackgroundColor.clearer(0.1).rgbaString()}"
     $tip.find('.tooltip-inner').css('box-shadow', shadow)

-    frontColor = Color($tip.find('.tooltip-inner').css('color'))
     $pre = $tip.find('.tooltip-inner pre')
-    $pre.css('color', frontColor.clone().rgbaString())
-    $pre.css('background-color', frontColor.clone().clearer(0.96).rgbaString())
-    $pre.css('border-color', frontColor.clone().clearer(0.86).rgbaString())
+    if $pre.length > 0
+      frontColor = Color($tip.find('.tooltip-inner').css('color'))
+      $pre.css('color', frontColor.clone().rgbaString())
+      $pre.css('background-color', frontColor.clone().clearer(0.96).rgbaString())
+      $pre.css('border-color', frontColor.clone().clearer(0.86).rgbaString())

   getEditorView: ->
     @getViolationView().lintView.editorView


FIX wrong first return for id'ed undefined elements

--- app/assets/javascripts/darwin/view.coffee
+++ app/assets/javascripts/darwin/view.coffee
@@ -330,6 +330,7 @@
       if $element.get(0)
         @[ "$#{selector_name}" ] = $element
         @_cached[ selector_name ] = true
+        $element
       else
         throw new Error "Selector not found : #{selector_name}"


update paths to use root var

update paths to use root var

--- app/assets/javascripts/osc_jobs.js.coffee
+++ app/assets/javascripts/osc_jobs.js.coffee
@@ -12,7 +12,7 @@
     show_job_panel()
     $.ajax
       type: 'GET'
-      url: app_root() + Routes.osc_job_path(id)
+      url: ROOT_PATH + Routes.osc_job_path(id)
       contentType: "application/json; charset=utf-8"
       dataType: "json"
       error: (jqXHR, textStatus, errorThrown) ->
@@ -64,7 +64,7 @@

 @update_copy_button = (id) ->
   if id?
-    $("#copy_button").attr("href", app_root() + Routes.copy_osc_job_path(id))
+    $("#copy_button").attr("href", ROOT_PATH + Routes.copy_osc_job_path(id))
     $("#copy_button").data("method", "PUT")
     $("#copy_button").removeAttr("disabled")
     $("#copy_button").unbind('click', false)
@@ -75,7 +75,7 @@

 @update_submit_button = (id, status_char) ->
   if id? && !status_char?
-      $("#submit_button").attr("href", app_root() + Routes.submit_osc_job_path(id))
+      $("#submit_button").attr("href", ROOT_PATH + Routes.submit_osc_job_path(id))
       $("#submit_button").data("method", "PUT")
       $("#submit_button").removeAttr("disabled")
       $("#submit_button").unbind('click', false)
@@ -97,7 +97,7 @@

 @update_template_button = (path, host) ->
   if path?
-    $("#template_button").attr("href", app_root() + Routes.new_template_path({ path: path, host: host }))
+    $("#template_button").attr("href", ROOT_PATH + Routes.new_template_path({ path: path, host: host }))
     $("#template_button").removeAttr("disabled")
     $("#template_button").unbind('click', false)
   else
@@ -107,7 +107,7 @@

 @update_destroy_button = (id) ->
   if id?
-    $("#destroy_button").attr("href", app_root() + Routes.osc_job_path(id))
+    $("#destroy_button").attr("href", ROOT_PATH + Routes.osc_job_path(id))
     $("#destroy_button").data("method", "DELETE")
     $("#destroy_button").removeAttr("disabled")
     $("#destroy_button").unbind('click', false)


first pass at matrix grid, however will need to move away from particle cloud.

--- app/assets/javascripts/scaffold.js.coffee
+++ app/assets/javascripts/scaffold.js.coffee
@@ -10,8 +10,11 @@
     minZ : 0
     maxZ : 0
     positionOffsetZ : 250
+    matrixDimensions : [0,0] #y,x
+    colorBoxSize : 5
   }
   this.colors = []
+  this.colorsMatrix = []

   # Handle dev vs production
   if window.location.hostname == 'color.camp'
@@ -91,31 +94,78 @@
   # --- Data ---

   #
-  dataAssignCoords : (colors, startN) ->
-    return if !colors || colors.length == 0
-
-    startX = 0
-    startY = 0
-    startX = this.colors[startN].x if startN && this.colors[startN]
-    startY = this.colors[startN].y if startN && this.colors[startN]
-
+  dataResetMatrix : ->
+    this.colorsMatrix = []
+    for y in [0..(this.canvas.matrixDimensions[0]-1)]
+      this.colorsMatrix[y] = []
+      for x in [0..(this.canvas.matrixDimensions[1]-1)]
+        this.colorsMatrix[y][x] = null
+
+  #
+  dataIsMatrixFull : ->
+    for y in [0..(this.canvas.matrixDimensions[0]-1)]
+      for x in [0..(this.canvas.matrixDimensions[1]-1)]
+        return false unless this.colorsMatrix[y][x]
+    true
+
+  #
+  dataResizeMatrix : ->
+    # Extend X
+    for y in [0..(this.canvas.matrixDimensions[0]-1)]
+      this.colorsMatrix[y].unshift(null)
+      this.colorsMatrix[y].push(null)
+    this.canvas.matrixDimensions[1] += 2
+
+    # Extend Y
+    row = []
+    for x in [0..(this.canvas.matrixDimensions[1]-1)]
+      row[x] = null
+    this.colorsMatrix.unshift(row)
+    this.colorsMatrix.push(row)
+
+    this.canvas.matrixDimensions[0] += 2
+
+    # Reassociate colors
+    colorIds = $.map this.colors, (e)-> e.id
+    for y in [0..(this.canvas.matrixDimensions[0]-1)]
+      for x in [0..(this.canvas.matrixDimensions[1]-1)]
+        if this.colorsMatrix[y] && this.colorsMatrix[y][x]
+          i = colorIds.indexOf(this.colorsMatrix[y][x])
+          if i >= 0
+            this.colors[i].y += 1
+            this.colors[i].x += 1
+
+  #
+  dataGetEmptyMatrixPosition : ->
+    id = Math.ceil(Math.random() * 1000)
+    i = 0
+    while true
+      i++
+      y = Math.floor(Math.random() * this.canvas.matrixDimensions[0])
+      x = Math.floor(Math.random() * this.canvas.matrixDimensions[1])
+      return {x : x, y : y} unless this.colorsMatrix[y][x]
+      if i > 10
+        i = 0
+        this.dataResizeMatrix()
+
+  #
+  dataAssignCoords : ->
     # Set an offset if not already
-    this.canvas.offsetZ = Date.parse colors[0].created_at unless this.canvas.offsetZ
-    offsetZ = this.canvas.offsetZ
-
-    $.map colors, (n,i)->
-      return this if n.x && n.y
-      incrX = Math.ceil(Math.random() * 100) - 50
-      n.x = incrX if !n.x
-
-      incrY = Math.ceil(Math.random() * 100) - 50
-      n.y = incrY if !n.y
-
-      n.z = (Date.parse(n.created_at) - offsetZ) / 100 / 60
-
-      return this
-
-    colors
+    this.canvas.offsetZ = Date.parse this.colors[0].created_at unless this.canvas.offsetZ
+
+    # Resize matrix if the matrix is full
+    this.dataResizeMatrix() if this.dataIsMatrixFull()
+
+    # Loop through each color, set if not x & y coords
+    $.each this.colors, ((i,n)->
+      unless n.x && n.y
+        pos = this.dataGetEmptyMatrixPosition()
+        return this unless pos
+        this.colorsMatrix[pos.y][pos.x] = n.id
+        this.colors[i].x = pos.x
+        this.colors[i].y = pos.y
+        this.colors[i].z = (Date.parse(n.created_at) - this.canvas.offsetZ) / 100 / 60
+    ).bind(this)

   #
   dataAddNewColor : (color)->
@@ -131,24 +181,23 @@

   #
   dataLoadColors : (colors)->
-    return if colors.length < 1
-
-    colors = this.dataAssignCoords(colors, this.colors.length)
     $.each colors, ((i,v) ->
       this.colors[i] = v
     ).bind(this)
+    this.dataAssignCoords()

     this.canvasDrawColors() if this.canvas.scene

   #
   dataAppendColors : (colors) ->
-    colors = this.dataAssignCoords(colors, this.colors.length)
     Array.prototype.push.apply(this.colors, colors)
+    this.dataAssignCoords()

   #
   dataPrependColors : (colors) ->
-    colors = this.dataAssignCoords(colors.reverse(), 0).reverse() # reverse the order and then back
     Array.prototype.unshift.apply(this.colors, colors)
+    this.dataAssignCoords()
+    this.canvas.step_index += colors.length

   # --- Canvas ---
@@ -174,7 +223,8 @@
     this.canvas.renderer.setSize( window.innerWidth, window.innerHeight )

     this.canvas.mouse = new THREE.Vector2()
-
+    this.canvas.matrixDimensions = [6,10]#[24,36] #y,x
+    this.dataResetMatrix()
     this.canvasDrawColors()

     document.body.appendChild( this.canvas.renderer.domElement )
@@ -197,6 +247,10 @@
       #

     this.canvas.scene = null
+
+    this.colors = []
+    this.colorsMatrix = []
+
     $('canvas#colorcamp-canvas').remove()

   #
@@ -225,15 +279,15 @@
     geometryColors = []
     for i,color of this.colors
       vertex = new THREE.Vector3()
-      vertex.x = color.x
-      vertex.y = color.y
-      vertex.z = color.z
+      vertex.x = (color.x - Math.floor(this.canvas.matrixDimensions[1] / 2)) * 15 #this.canvas.colorBoxSize
+      vertex.y = (color.y - Math.floor(this.canvas.matrixDimensions[0] / 2)) * 15 #this.canvas.colorBoxSize
+      vertex.z = color.z / 100
       geometry.vertices.push( vertex );
       geometryColors[i] = new THREE.Color parseInt(color.color.hex, 16)

     geometry.colors = geometryColors

-    material = new THREE.PointCloudMaterial { size: 50, vertexColors: THREE.VertexColors }
+    material = new THREE.PointCloudMaterial { size: 20, vertexColors: THREE.VertexColors, sizeAttenuation: true }

     this.canvas.particles = new THREE.PointCloud geometry, material
     this.canvas.scene.add this.canvas.particles


Changed some variable names to be more consistent

--- metrics-ui/ui/js/script.coffee
+++ metrics-ui/ui/js/script.coffee
@@ -11,18 +11,18 @@
   Graph = React.createFactory React.createClass
     displayName: 'graph'
     getInitialState: ->
-      todoTotals: []
+      data: []
     update: (url) ->
       $.get url, ((result) ->
         data = result
-        @setState {todoTotals: data}).bind(this)
+        @setState {data: data}).bind(this)
     componentDidMount: ->
       @update("#{@props.urlbase}/#{@props.daysBack}")
     componentWillReceiveProps: (nextProps) ->
       @update("#{nextProps.urlbase}/#{nextProps.daysBack}")
     render: ->
       linechart
-        data: @state.todoTotals
+        data: @state.data
         margin:
           top: 5
           right: 15
@@ -36,18 +36,18 @@
   Histograph = React.createFactory React.createClass
     displayName: 'histograph'
     getInitialState: ->
-      todoTotals: []
+      data: []
     update: (url) ->
       $.get url, ((result) ->
         data = result
-        @setState {todoTotals: data}).bind(this)
+        @setState {data: data}).bind(this)
     componentDidMount: ->
       @update("#{@props.urlbase}/#{@props.daysBack}")
     componentWillReceiveProps: (nextProps) ->
       @update("#{nextProps.urlbase}/#{nextProps.daysBack}")
     render: ->
       histogram
-        data: @state.todoTotals
+        data: @state.data
         margin:
           top: 5
           right: 15
@@ -64,7 +64,7 @@
     render: ->
       div {}, [
         (React.DOM.label htmlFor: "timeinput", "Days back in time"),
-        (React.DOM.input
+        (React.DOM.input
           className: "u-full-width"
           placeholder: "60"
           id: "timeinput"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/clamp_bounds.coffee
+++ src/clamp_bounds.coffee
@@ -1,5 +1,5 @@
 ###*
-The ClampBounds module adds a
+The ClampBounds module adds a check

 # Health regeneration


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/clamp_bounds.coffee
+++ src/clamp_bounds.coffee
@@ -1,7 +1,8 @@
 ClampBounds = (I={}, self) ->
   Object.reverseMerge I,
     x: 0
-
+    y: 0
+    widt

   self.bind "update", ->
     I.x = I.x.clamp()


Comment temporally unused code.

Comment temporally unused code.

--- app/client/js/react/pages/editsong.coffee
+++ app/client/js/react/pages/editsong.coffee
@@ -189,8 +189,8 @@
             if song.inTrash then routes.home.redirect()

       getLyricsHistory: (song) ->
-        usersStore.getSongLyricsLocalHistory song
-          .filter (lyrics) -> lyrics != song.lyrics
+        # usersStore.getSongLyricsLocalHistory song
+        #   .filter (lyrics) -> lyrics != song.lyrics

       # onPublishTap: ->
       #   if !usersStore.isLogged()


Fixed bug with whereby implicit connections werent disconnecting

--- src/client/index.coffee
+++ src/client/index.coffee
@@ -37,6 +37,16 @@

     connections[origin]

+  # If you're using the bare API, connections are cleaned up as soon as there's no
+  # documents using them.
+  maybeClose = (c) ->
+    numDocs = 0
+    for name, doc of c.docs
+      numDocs++ if doc.state isnt 'closed' || doc.autoOpen
+
+    if numDocs == 0
+      c.disconnect()
+
   (docName, type, origin, callback) ->
     if typeof origin == 'function'
       callback = origin
@@ -47,17 +57,10 @@
     c.open docName, type, (error, doc) ->
       if error
         callback error
+        maybeClose c
       else
-        # If you're using the bare API, connections are cleaned up as soon as there's no
-        # documents using them.
-        doc.on 'closed', ->
-          numDocs = 0
-          for name, d of c.docs
-            numDocs++ if d.state isnt 'closed' || d.autoOpen
-
-          if numDocs == 0
-            c.disconnect()
-
+        doc.on 'closed', -> maybeClose c
+
         callback null, doc

     c.on 'connect failed'


fix version detection

fix version detection

--- lib/misc/paths.coffee
+++ lib/misc/paths.coffee
@@ -2,6 +2,7 @@

 path =  require 'path'
 fs =    require 'fs'
+child_process = require 'child_process'

 module.exports =

@@ -41,21 +42,18 @@
       .catch =>
         atom.notifications.addError "Couldn't find your Julia packages."

-  getVersion: (fn) ->
-    p = new Promise (resolve, reject) =>
-      # not sure if the return here is necessary, but I think the rest of the
-      # code will be unnecessarily executed otherwise
-      if @version? then resolve(@version); return
-      @checkPath(@jlpath())
-        .then =>
-          proc = child_process.spawn @jlpath(), ["--version"]
-          proc.on 'exit', () =>
-            str = proc.stdout.read().toString()
-            res = str.match /(\d+)\.(\d+)\.(\d+)/
-            if res?
-              [_, major, minor, patch] = res
-              @version = {major, minor, patch}
-              resolve @version
-            else
-              reject()
-        .catch => reject()
+  jlpath: ->
+    p = atom.config.get("julia-client.juliaPath")
+    if p == '[bundle]' then p = @bundledExe()
+    p
+
+  getVersion: (path = @jlpath()) ->
+    return Promise.resolve @version if @version?
+    new Promise (resolve, reject) =>
+      proc = child_process.spawn @jlpath(), ["--version"]
+      proc.on 'exit', () =>
+        str = proc.stdout.read().toString()
+        res = str.match /(\d+)\.(\d+)\.(\d+)/
+        return reject() unless res?
+        [_, major, minor, patch] = res
+        resolve @version = {major, minor, patch}


Add client specs for step in, out and over

--- spec/client-spec.coffee
+++ spec/client-spec.coffee
@@ -147,6 +147,21 @@
       runs ->
         expect(result).toEqual({ attrs : { no : '1', location : 'fixtures/simple.rb:1' } })

+  describe "::stepIn", ->
+    it "runs command 'step'", ->
+      @client.stepIn()
+      expect(@socket.write).toHaveBeenCalledWith("step\n")
+
+  describe "::stepOut", ->
+    it "runs command 'finish'", ->
+      @client.stepOut()
+      expect(@socket.write).toHaveBeenCalledWith("finish\n")
+
+  describe "::stepOver", ->
+    it "runs command 'next'", ->
+      @client.stepOver()
+      expect(@socket.write).toHaveBeenCalledWith("next\n")
+
   describe "::handleCmd", ->
     describe "when suspended", ->
       beforeEach ->


added tests, finally

added tests, finally

--- spec/scroll-spec.coffee
+++ spec/scroll-spec.coffee
@@ -120,3 +120,111 @@
         keydown('b')
         expect(editor.setScrollTop).toHaveBeenCalledWith(860)
         expect(editor.moveToFirstCharacterOfLine).not.toHaveBeenCalled()
+
+  describe "horizontal scroll cursor keybindings", ->
+    beforeEach ->
+      editor.setWidth(600)
+      editor.setLineHeightInPixels(10)
+      editor.setDefaultCharWidth(10)
+      text = ""
+      for i in [100..199]
+        text += "#{i} "
+      editor.setText(text)
+      editor.setCursorBufferPosition([0, 0])
+
+    describe "the zs keybinding", ->
+      zsPos = (pos) ->
+        editor.setCursorBufferPosition([0, pos])
+        keydown('z')
+        keydown('s')
+        editor.getScrollLeft()
+
+      startPosition = NaN
+
+      beforeEach ->
+        startPosition = editor.getScrollLeft()
+
+      it "does nothing near the start of the line", ->
+        pos1 = zsPos(1)
+        expect(pos1).toEqual(startPosition)
+
+      it "moves the cursor the nearest it can to the left edge of the editor", ->
+        pos10 = zsPos(10)
+        expect(pos10).toBeGreaterThan(startPosition)
+
+        pos11 = zsPos(11)
+        expect(pos11 - pos10).toEqual(10)
+
+      it "does nothing near the end of the line", ->
+        posEnd = zsPos(399)
+        expect(editor.getCursorBufferPosition()).toEqual [0, 399]
+
+        pos390 = zsPos(390)
+        expect(pos390).toEqual(posEnd)
+        expect(editor.getCursorBufferPosition()).toEqual [0, 390]
+
+        pos340 = zsPos(340)
+        expect(pos340).toBeLessThan(posEnd)
+        pos342 = zsPos(342)
+        expect(pos342 - pos340).toEqual(20)
+
+      it "does nothing if all lines are short", ->
+        editor.setText('short')
+        startPosition = editor.getScrollLeft()
+        pos1 = zsPos(1)
+        expect(pos1).toEqual(startPosition)
+        expect(editor.getCursorBufferPosition()).toEqual [0, 1]
+        pos10 = zsPos(10)
+        expect(pos10).toEqual(startPosition)
+        expect(editor.getCursorBufferPosition()).toEqual [0, 5]
+
+
+    describe "the ze keybinding", ->
+      zePos = (pos) ->
+        editor.setCursorBufferPosition([0, pos])
+        keydown('z')
+        keydown('e')
+        editor.getScrollLeft()
+
+      startPosition = NaN
+
+      beforeEach ->
+        startPosition = editor.getScrollLeft()
+
+      it "does nothing near the start of the line", ->
+        pos1 = zePos(1)
+        expect(pos1).toEqual(startPosition)
+
+        pos40 = zePos(40)
+        expect(pos40).toEqual(startPosition)
+
+      it "moves the cursor the nearest it can to the right edge of the editor", ->
+        pos110 = zePos(110)
+        expect(pos110).toBeGreaterThan(startPosition)
+
+        pos109 = zePos(109)
+        expect(pos110 - pos109).toEqual(10)
+
+      it "does nothing when very near the end of the line", ->
+        posEnd = zePos(399)
+        expect(editor.getCursorBufferPosition()).toEqual [0, 399]
+
+        pos397 = zePos(397)
+        expect(pos397).toEqual(posEnd)
+        expect(editor.getCursorBufferPosition()).toEqual [0, 397]
+
+        pos380 = zePos(380)
+        expect(pos380).toBeLessThan(posEnd)
+
+        pos382 = zePos(382)
+        expect(pos382 - pos380).toEqual(20)
+
+      it "does nothing if all lines are short", ->
+        editor.setText('short')
+        startPosition = editor.getScrollLeft()
+        pos1 = zePos(1)
+        expect(pos1).toEqual(startPosition)
+        expect(editor.getCursorBufferPosition()).toEqual [0, 1]
+        pos10 = zePos(10)
+        expect(pos10).toEqual(startPosition)
+        expect(editor.getCursorBufferPosition()).toEqual [0, 5]


Fix incorrect calculation for token position when using hard tabs.

--- lib/auto-indent.coffee
+++ lib/auto-indent.coffee
@@ -179,16 +179,19 @@
         firstCharIndentation = (@editor.indentationForBufferRow row)
         # convert the matched column position into tab indents
         if @editor.getSoftTabs()
-          tagIndentation = (matchColumn / @editor.getTabLength())
-        else tagIndentation =
-          do () ->
-            hardTabsFound = 0
+          tokenIndentation = (matchColumn / @editor.getTabLength())
+        else tokenIndentation =
+          do (@editor) ->
+            hardTabsFound = charsFound = 0
             for i in [0...matchColumn]
-              hardTabsFound += ((line.substr i, 1) is '\t')
-            hardTabsFound
+              if ((line.substr i, 1) is '\t')
+                hardTabsFound++
+              else
+                charsFound++
+            return hardTabsFound + ( charsFound / @editor.getTabLength() )

         if isFirstTokenOfLine
-          firstTagInLineIndentation =  tagIndentation
+          firstTagInLineIndentation =  tokenIndentation

         # big switch statement follows for each token. If the line is reformated
         # then we recalculate the new position.
@@ -216,7 +219,7 @@
                   parentTokenIdx? and
                   tokenStack[parentTokenIdx].type is BRACE_OPEN and
                   tokenStack[parentTokenIdx].row is ( row - 1)
-                    tagIndentation = firstCharIndentation = firstTagInLineIndentation =
+                    tokenIndentation = firstCharIndentation = firstTagInLineIndentation =
                       @eslintIndentOptions.jsxIndent[1] + @getIndentOfPreviousRow row
                     indentRecalc = @indentRow({row: row , blockIndent: firstCharIndentation })
               else if isFirstTagOfBlock and parentTokenIdx?
@@ -239,7 +242,7 @@
               name: match[2]
               row: row
               firstTagInLineIndentation: firstTagInLineIndentation
-              tagIndentation: tagIndentation
+              tokenIndentation: tokenIndentation
               firstCharIndentation: firstCharIndentation
               parentTokenIdx: parentTokenIdx
               termsThisTagsAttributesIdx: null  # ptr to > tag
@@ -363,7 +366,7 @@
               name: ''
               row: row
               firstTagInLineIndentation: firstTagInLineIndentation
-              tagIndentation: tagIndentation
+              tokenIndentation: tokenIndentation
               firstCharIndentation: firstCharIndentation
               parentTokenIdx: parentTokenIdx
               termsThisTagsAttributesIdx: null  # ptr to > tag
@@ -406,7 +409,7 @@
                   parentTokenIdx? and
                   tokenStack[parentTokenIdx].type is token and
                   tokenStack[parentTokenIdx].row is ( row - 1)
-                    tagIndentation = firstCharIndentation =
+                    tokenIndentation = firstCharIndentation =
                       @eslintIndentOptions.jsxIndent[1] + @getIndentOfPreviousRow row
                     indentRecalc = @indentRow({row: row, blockIndent: firstCharIndentation})
               else if parentTokenIdx?
@@ -426,7 +429,7 @@
               name: ''
               row: row
               firstTagInLineIndentation: firstTagInLineIndentation
-              tagIndentation: tagIndentation
+              tokenIndentation: tokenIndentation
               firstCharIndentation: firstCharIndentation
               parentTokenIdx: parentTokenIdx
               termsThisTagsAttributesIdx: null  # ptr to > tag
@@ -500,7 +503,7 @@
               name: ''
               row: row
               firstTagInLineIndentation: firstTagInLineIndentation
-              tagIndentation: tagIndentation
+              tokenIndentation: tokenIndentation
               firstCharIndentation: firstCharIndentation
               parentTokenIdx: parentTokenIdx
               termsThisTagsAttributesIdx: null  # ptr to > tag
@@ -716,7 +719,7 @@
   indentForClosingBracket: ( row, parentTag, closingBracketRule ) ->
     if @eslintIndentOptions.jsxClosingBracketLocation[0]
       if closingBracketRule is TAGALIGNED
-        @indentRow({row: row, blockIndent: parentTag.tagIndentation})
+        @indentRow({row: row, blockIndent: parentTag.tokenIndentation})
       else if closingBracketRule is LINEALIGNED
         @indentRow({row: row, blockIndent: parentTag.firstCharIndentation })
       else if closingBracketRule is AFTERPROPS


For #3453: remove unneeded file

For #3453: remove unneeded file

--- xforms/jvm/src/main/assets/ops/javascript/orbeon/xforms/server/AjaxServer/nextAjaxResponse.coffee
+++ xforms/jvm/src/main/assets/ops/javascript/orbeon/xforms/server/AjaxServer/nextAjaxResponse.coffee
@@ -1,16 +0,0 @@
-$        = ORBEON.jQuery
-AS       = -> ORBEON.xforms.server.AjaxServer
-Document = -> ORBEON.xforms.Document
-Events   = -> ORBEON.xforms.Events
-
-# Returns a deferred object that gets resolved when the next Ajax response arrives
-AS().nextAjaxResponse = (formId) ->
-    deferred = $.Deferred()
-    seqNo = -> parseInt Document().getFromClientState formId, 'sequence'
-    initialSeqNo = seqNo()
-    onAjaxResponse = ->
-        if seqNo() == initialSeqNo + 1
-            Events().ajaxResponseProcessedEvent.unsubscribe onAjaxResponse
-            deferred.resolve()
-    Events().ajaxResponseProcessedEvent.subscribe onAjaxResponse
-    deferred


cleanup pdfJs

cleanup pdfJs

--- services/web/public/coffee/ide/pdfng/directives/pdfJs.coffee
+++ services/web/public/coffee/ide/pdfng/directives/pdfJs.coffee
@@ -27,15 +27,6 @@
 					"dblClickCallback": "="
 			}
 			link: (scope, element, attrs) ->
-				# pdfListView = new PDFListView element.find(".pdfjs-viewer")[0],
-				#		textLayerBuilder: TextLayerBuilder
-				#		annotationsLayerBuilder: AnnotationsLayerBuilder
-				#		highlightsLayerBuilder: HighlightsLayerBuilder
-				#		ondblclick: (e) -> onDoubleClick(e)
-				#		# logLevel: PDFListView.Logger.DEBUG
-				# pdfListView.listView.pageWidthOffset = 20
-				# pdfListView.listView.pageHeightOffset = 20
-
 				scope.loading = false
 				scope.pleaseJumpTo = null
 				scope.scale = null
@@ -55,8 +46,6 @@
 							offset:
 								"top": +position.offset.top
 								"left": +position.offset.left
-
-					#scope.position = pdfListView.getPdfPosition(true)

 					scope.$on "$destroy", () =>
 						localStorage "pdf.scale", scope.scale
@@ -84,18 +73,9 @@
 						scope.loading = true
 						scope.loaded = false
 						scope.progress = 1
-						# console.log 'pdfSrc =', url
 						initializePosition()
 						flashControls()
-						# pdfListView
-						#		.loadPdf(url, onProgress)
-						#		.then () ->
-						#			scope.$apply () ->
-						#				scope.loading = false
-						#				delete scope.progress
-						#				initializePosition()
-						#				flashControls()
-
+
 				scope.$on "loaded", () ->
 					scope.loaded = true
 					scope.progress = 100
@@ -103,37 +83,6 @@
 						scope.loading = false
 						delete scope.progress
 					, 500
-
-				#scope.$watch "highlights", (areas) ->
-					# console.log 'got HIGHLIGHTS in pdfJS', areas
-
-					# return if !areas?
-					# highlights = for area in areas or []
-					# 	{
-					# 		page: area.page
-					# 		highlight:
-					# 			left: area.h
-					# 			top: area.v
-					# 			height: area.height
-					# 			width: area.width
-					# 	}
-
-					# if highlights.length > 0
-					# 	first = highlights[0]
-					# 	position = {
-					# 		page: first.page
-					# 		offset:
-					# 			left: first.highlight.left
-					# 			top: first.highlight.top - 80
-					# 	}
-					# 	console.log 'position is', position, 'in highlights'
-					# 	scope.pleaseJumpTo = position
-					# pdfListView.clearHighlights()
-					# pdfListView.setHighlights(highlights, true)
-
-					# setTimeout () =>
-					#		pdfListView.clearHighlights()
-					# , 1000

 				scope.fitToHeight = () ->
 					scale = angular.copy (scope.scale)
@@ -161,7 +110,6 @@
 					for event in attrs.resizeOn.split(",")
 						scope.$on event, (e) ->
 							#console.log 'got a resize event', event, e
-							#

 				scope.$on 'progress', (event, progress) ->
 					scope.$apply () ->


Change reserve scaling to be 0.1.

Change reserve scaling to be 0.1.

--- app/static/coffee/production-chart.coffee
+++ app/static/coffee/production-chart.coffee
@@ -1,5 +1,5 @@
 d3.csv "/data/ds140-bauxi-clean.csv", (data) ->
-  reserve_scale = 0.01
+  reserve_scale = 0.1

   values = _.map data, (row) ->
     x: parseInt row["Year"]


路径使用前转换为绝对路径

路径使用前转换为绝对路径

--- lib/subcmd/dir.coffee
+++ lib/subcmd/dir.coffee
@@ -47,7 +47,7 @@
         fun: ->
           ret = switch cmd
             when 'list'
-              getFileTree(args.path, args.deep)
+              getFileTree(path.resolve(args.path), args.deep)
             when 'add', 'create'
               fse.mkdirsSync(args.path)
               true


:bug:Fix managed subs next payment UI

:bug:Fix managed subs next payment UI

--- app/views/account/SubscriptionView.coffee
+++ app/views/account/SubscriptionView.coffee
@@ -306,7 +306,7 @@

     options = { cache: false, url: "/db/user/#{me.id}/stripe" }
     options.success = (info) =>
-      @sponsorSub = info.subscription
+      @sponsorSub = info.sponsorSubscription
       if card = info.card
         @card = "#{card.brand}: x#{card.last4}"
       render()


Remove some console.log.

Remove some console.log.

--- lib/transpiler.coffee
+++ lib/transpiler.coffee
@@ -200,10 +200,6 @@
     absTranspiledFile = path.join(absTranspileRoot, relSourceRootToSourceFile , parsedSourceFile.name  + '.js')
     absMapFile = path.join(absMapsRoot, relSourceRootToSourceFile , parsedSourceFile.name  + '.js.map')

-    # Code to assist when potential path traversal issues may arise
-    if sourceFile.indexOf absProjectPath != 0
-      console.log "LANGUAGE-BABEL\nProject Dir is %s\nSource File is %s", absProjectPath, sourceFile
-
     sourceFile: sourceFile
     sourceFileDir: parsedSourceFile.dir
     mapFile: absMapFile


passing tests!!!

passing tests!!!

--- lib/xml-parser.coffee
+++ lib/xml-parser.coffee
@@ -36,14 +36,14 @@
       node = stack.pop()
       result = {}
       res2 = {}
-      res2.attrs = node.attributes if node.attributes.length
+      res2.attrs = node.attributes if Object.keys(node.attributes).length
       res2.children = node.children if node.children?.length
       result[node.name] = res2
       if stack.length == 0
         @events.emit 'command', result
       else
         parent = stack[stack.length - 1]
-        parent.children = []
+        parent.children ?= []
         parent.children.push(result)
       # closed a tag. "name" is tag name
       return


discussion snapshot title markdown removed

--- client/landing/app/Applications/Activity.kdapplication/ListItems/ActivityListItemDiscussion.coffee
+++ client/landing/app/Applications/Activity.kdapplication/ListItems/ActivityListItemDiscussion.coffee
@@ -112,7 +112,7 @@
         appManager.tell "Activity", "createContentDisplay", @getData()

   applyTextExpansions:(str = "")->
-    str = @utils.expandUsernames @utils.applyMarkdown str
+    str = @utils.expandUsernames str

     if str.length > 500
       visiblePart = str.substr 0, 500


keyboard binding for black piano key

keyboard binding for black piano key


:lipstick:

:lipstick:

--- spec/app/editor-spec.coffee
+++ spec/app/editor-spec.coffee
@@ -543,6 +543,90 @@
         _.times 4, -> editor.moveCursorDown()
         expect(editor.visibleLines.find('.line:eq(0)').text()).toBe editor.buffer.lineForRow(1)
         expect(editor.visibleLines.find('.line:eq(5)').text()).toBe editor.buffer.lineForRow(6)
+
+    describe "when lines are added", ->
+      beforeEach ->
+        editor.attachToDom()
+        setEditorHeightInLines(editor, 5)
+        spyOn(editor, "scrollTo")
+
+      describe "when the change the precedes the first rendered row", ->
+        it "inserts and removes rendered lines to account for upstream change", ->
+          editor.scrollBottom(editor.scrollView.prop('scrollHeight'))
+          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8)
+          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12)
+
+          buffer.change([[1,0], [3,0]], "1\n2\n3\n")
+          expect(editor.visibleLines.find(".line").length).toBe 5
+          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8)
+          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12)
+
+      describe "when the change straddles the first rendered row", ->
+        it "doesn't render rows that were not previously rendered", ->
+          editor.scrollBottom(editor.scrollView.prop('scrollHeight'))
+          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8)
+          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12)
+
+          buffer.change([[2,0], [7,0]], "2\n3\n4\n5\n6\n7\n8\n9\n")
+          expect(editor.visibleLines.find(".line").length).toBe 5
+          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8)
+          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12)
+
+      describe "when the change the straddles the last rendered row", ->
+        it "doesn't render rows that were not previously rendered", ->
+          buffer.change([[2,0], [7,0]], "2\n3\n4\n5\n6\n7\n8\n")
+          expect(editor.visibleLines.find(".line").length).toBe 5
+          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(0)
+          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(4)
+
+      describe "when the change the follows the last rendered row", ->
+        it "does not change the rendered lines", ->
+          buffer.change([[12,0], [12,0]], "12\n13\n14\n")
+          expect(editor.visibleLines.find(".line").length).toBe 5
+          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(0)
+          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(4)
+
+    describe "when lines are removed", ->
+      beforeEach ->
+        editor.attachToDom()
+        setEditorHeightInLines(editor, 5)
+        spyOn(editor, "scrollTo")
+
+      describe "when the change the precedes the first rendered row", ->
+        it "removes rendered lines to account for upstream change", ->
+          editor.scrollBottom(editor.scrollView.prop('scrollHeight'))
+          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8)
+          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12)
+
+          buffer.change([[1,0], [2,0]], "")
+          expect(editor.visibleLines.find(".line").length).toBe 4
+          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8)
+          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(11)
+
+      describe "when the change straddles the first rendered row", ->
+        it "renders the correct rows", ->
+          editor.scrollBottom(editor.scrollView.prop('scrollHeight'))
+          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8)
+          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12)
+
+          buffer.change([[7,0], [11,0]], "1\n2\n")
+          expect(editor.visibleLines.find(".line").length).toBe 3
+          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8)
+          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(10)
+
+      describe "when the change the straddles the last rendered row", ->
+        it "renders the correct rows", ->
+          buffer.change([[2,0], [7,0]], "")
+          expect(editor.visibleLines.find(".line").length).toBe 5
+          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(0)
+          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(4)
+
+      describe "when the change the follows the last rendered row", ->
+        it "does not change the rendered lines", ->
+          buffer.change([[12,0], [12,0]], "")
+          expect(editor.visibleLines.find(".line").length).toBe 5
+          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(0)
+          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(4)

   describe "gutter rendering", ->
     beforeEach ->
@@ -2231,88 +2315,6 @@
         expect(selections[0].getBufferRange()).toEqual [[1, 12], [1, 12]]
         expect(selections[1].getBufferRange()).toEqual [[1, 30], [1, 30]]

-    describe "when lines are added", ->
-      beforeEach ->
-        setEditorHeightInLines(editor, 5)
-        spyOn(editor, "scrollTo")
-
-      describe "when the change the precedes the first rendered row", ->
-        it "inserts and removes rendered lines to account for upstream change", ->
-          editor.scrollBottom(editor.scrollView.prop('scrollHeight'))
-          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8)
-          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12)
-
-          buffer.change([[1,0], [3,0]], "1\n2\n3\n")
-          expect(editor.visibleLines.find(".line").length).toBe 5
-          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8)
-          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12)
-
-      describe "when the change straddles the first rendered row", ->
-        it "doesn't render rows that were not previously rendered", ->
-          editor.scrollBottom(editor.scrollView.prop('scrollHeight'))
-          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8)
-          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12)
-
-          buffer.change([[2,0], [7,0]], "2\n3\n4\n5\n6\n7\n8\n9\n")
-          expect(editor.visibleLines.find(".line").length).toBe 5
-          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8)
-          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12)
-
-      describe "when the change the straddles the last rendered row", ->
-        it "doesn't render rows that were not previously rendered", ->
-          buffer.change([[2,0], [7,0]], "2\n3\n4\n5\n6\n7\n8\n")
-          expect(editor.visibleLines.find(".line").length).toBe 5
-          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(0)
-          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(4)
-
-      describe "when the change the follows the last rendered row", ->
-        it "does not change the rendered lines", ->
-          buffer.change([[12,0], [12,0]], "12\n13\n14\n")
-          expect(editor.visibleLines.find(".line").length).toBe 5
-          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(0)
-          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(4)
-
-    describe "when lines are removed", ->
-      beforeEach ->
-        setEditorHeightInLines(editor, 5)
-        spyOn(editor, "scrollTo")
-
-      describe "when the change the precedes the first rendered row", ->
-        it "removes rendered lines to account for upstream change", ->
-          editor.scrollBottom(editor.scrollView.prop('scrollHeight'))
-          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8)
-          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12)
-
-          buffer.change([[1,0], [2,0]], "")
-          expect(editor.visibleLines.find(".line").length).toBe 4
-          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8)
-          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(11)
-
-      describe "when the change straddles the first rendered row", ->
-        it "renders the correct rows", ->
-          editor.scrollBottom(editor.scrollView.prop('scrollHeight'))
-          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8)
-          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(12)
-
-          buffer.change([[7,0], [11,0]], "1\n2\n")
-          expect(editor.visibleLines.find(".line").length).toBe 3
-          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(8)
-          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(10)
-
-      describe "when the change the straddles the last rendered row", ->
-        it "renders the correct rows", ->
-          buffer.change([[2,0], [7,0]], "")
-          expect(editor.visibleLines.find(".line").length).toBe 5
-          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(0)
-          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(4)
-
-      describe "when the change the follows the last rendered row", ->
-        it "does not change the rendered lines", ->
-          buffer.change([[12,0], [12,0]], "")
-          expect(editor.visibleLines.find(".line").length).toBe 5
-          expect(editor.visibleLines.find(".line:first").text()).toBe buffer.lineForRow(0)
-          expect(editor.visibleLines.find(".line:last").text()).toBe buffer.lineForRow(4)
-
   describe "when the editor is attached to the dom", ->
     it "calculates line height and char width and updates the pixel position of the cursor", ->
       expect(editor.lineHeight).toBeNull()


Fixed compiling double quote character

Fixed compiling double quote character

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -3040,7 +3040,10 @@
 charCompile = (ctx, atom, symbol) ->
   translation =
     if symbol.length is 2
-      '"' + symbol[1] + '"'
+      if symbol[1] is '"'
+        "'" + symbol[1] + "'"
+      else
+        '"' + symbol[1] + '"'
     else if symbol is "\\space"
       '" "'
     else if symbol in specialCharacters


Fixed invalid merging

Fixed invalid merging


Global cross-platform compilation

Global cross-platform compilation

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -1961,7 +1961,7 @@

 ms.global = ms_global = (ctx, call) ->
   call.tea = toConstrained markOrigin jsType, call
-  assignCompile ctx, call, (jsValue "window")
+  assignCompile ctx, call, (jsValue "global")

 callJsMethodCompile = (ctx, call) ->
   [dotMethod, object, args...] = _validTerms call
@@ -3056,7 +3056,7 @@
     malformed ctx, atom, 'Matching on namespaced not supported'
   translation:
     if /^global./.test symbol
-      "window.#{symbol['global.'.length...]}"
+      "global.#{symbol['global.'.length...]}"
     else
       symbol
   type: toConstrained markOrigin jsType, atom
@@ -3353,7 +3353,7 @@
   for className, dict of values classParams
     if chain = findSuperClassChain ctx, className, constraint.className
       return accessList dict, chain
-  throw new Error "Couldn't find dict for #{safprettyPrintForErrorePrintType constraint}"
+  throw new Error "Couldn't find dict for #{prettyPrintForError constraint}"

 findSuperClassChain = (ctx, className, targetClassName) ->
   for s in (ctx.classNamed className).supers
@@ -5613,6 +5613,7 @@
   }
 }
 ;
+var global = (1,eval)('this');
 var Shem = Shem || {};
 """

@@ -5633,7 +5634,7 @@
  * I removed the loader code for nodejs and amd because it was clashing with require js for now
  * I also changed context to window, to make sure it registers when evaling
  */
-!function(t,e){window.Immutable=e()}(this,function(){"use strict";function t(t,e){e&&(t.prototype=Object.create(e.prototype)),t.prototype.constructor=t}function e(t){return t.value=!1,t}function r(t){t&&(t.value=!0)}function n(){}function i(t,e){e=e||0;for(var r=Math.max(0,t.length-e),n=Array(r),i=0;r>i;i++)n[i]=t[i+e];return n}function o(t){return void 0===t.size&&(t.size=t.__iterate(s)),t.size}function u(t,e){return e>=0?+e:o(t)+ +e}function s(){return!0}function a(t,e,r){return(0===t||void 0!==r&&-r>=t)&&(void 0===e||void 0!==r&&e>=r)}function h(t,e){return c(t,e,0)}function f(t,e){return c(t,e,e)}function c(t,e,r){return void 0===t?r:0>t?Math.max(0,e+t):void 0===e?t:Math.min(e,t)}function _(t){return y(t)?t:O(t)}function p(t){return d(t)?t:x(t)}function v(t){return m(t)?t:k(t)}function l(t){return y(t)&&!g(t)?t:A(t)}function y(t){return!(!t||!t[vr])}function d(t){return!(!t||!t[lr])}function m(t){return!(!t||!t[yr])}function g(t){return d(t)||m(t)}function w(t){return!(!t||!t[dr])}function S(t){this.next=t}function z(t,e,r,n){var i=0===t?e:1===t?r:[e,r];return n?n.value=i:n={value:i,done:!1},n}function I(){return{value:void 0,done:!0}}function b(t){return!!M(t)}function q(t){return t&&"function"==typeof t.next}function D(t){var e=M(t);return e&&e.call(t)}function M(t){var e=t&&(Sr&&t[Sr]||t[zr]);return"function"==typeof e?e:void 0}function E(t){return t&&"number"==typeof t.length}function O(t){return null===t||void 0===t?T():y(t)?t.toSeq():C(t)}function x(t){return null===t||void 0===t?T().toKeyedSeq():y(t)?d(t)?t.toSeq():t.fromEntrySeq():W(t)}function k(t){return null===t||void 0===t?T():y(t)?d(t)?t.entrySeq():t.toIndexedSeq():B(t)}function A(t){return(null===t||void 0===t?T():y(t)?d(t)?t.entrySeq():t:B(t)).toSetSeq()}function j(t){this._array=t,this.size=t.length}function R(t){var e=Object.keys(t);this._object=t,this._keys=e,this.size=e.length}function U(t){this._iterable=t,this.size=t.length||t.size;}function K(t){this._iterator=t,this._iteratorCache=[]}function L(t){return!(!t||!t[br])}function T(){return qr||(qr=new j([]))}function W(t){var e=Array.isArray(t)?new j(t).fromEntrySeq():q(t)?new K(t).fromEntrySeq():b(t)?new U(t).fromEntrySeq():"object"==typeof t?new R(t):void 0;if(!e)throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+t);return e}function B(t){var e=J(t);if(!e)throw new TypeError("Expected Array or iterable object of values: "+t);return e}function C(t){var e=J(t)||"object"==typeof t&&new R(t);if(!e)throw new TypeError("Expected Array or iterable object of values, or keyed object: "+t);return e}function J(t){return E(t)?new j(t):q(t)?new K(t):b(t)?new U(t):void 0}function P(t,e,r,n){var i=t._cache;if(i){for(var o=i.length-1,u=0;o>=u;u++){var s=i[r?o-u:u];if(e(s[1],n?s[0]:u,t)===!1)return u+1}return u}return t.__iterateUncached(e,r)}function H(t,e,r,n){var i=t._cache;if(i){var o=i.length-1,u=0;return new S(function(){var t=i[r?o-u:u];return u++>o?I():z(e,n?t[0]:u-1,t[1])})}return t.__iteratorUncached(e,r)}function N(){throw TypeError("Abstract")}function V(){}function Y(){}function Q(){}function X(t,e){if(t===e||t!==t&&e!==e)return!0;if(!t||!e)return!1;if("function"==typeof t.valueOf&&"function"==typeof e.valueOf){if(t=t.valueOf(),e=e.valueOf(),t===e||t!==t&&e!==e)return!0;if(!t||!e)return!1}return"function"==typeof t.equals&&"function"==typeof e.equals&&t.equals(e)?!0:!1}function F(t,e){return e?G(e,t,"",{"":t}):Z(t)}function G(t,e,r,n){return Array.isArray(e)?t.call(n,r,k(e).map(function(r,n){return G(t,r,n,e)})):$(e)?t.call(n,r,x(e).map(function(r,n){return G(t,r,n,e)})):e}function Z(t){return Array.isArray(t)?k(t).map(Z).toList():$(t)?x(t).map(Z).toMap():t}function $(t){return t&&(t.constructor===Object||void 0===t.constructor)}function tt(t){return t>>>1&1073741824|3221225471&t}function et(t){if(t===!1||null===t||void 0===t)return 0;if("function"==typeof t.valueOf&&(t=t.valueOf(),t===!1||null===t||void 0===t))return 0;if(t===!0)return 1;var e=typeof t;if("number"===e){var r=0|t;for(r!==t&&(r^=4294967295*t);t>4294967295;)t/=4294967295,r^=t;return tt(r)}return"string"===e?t.length>jr?rt(t):nt(t):"function"==typeof t.hashCode?t.hashCode():it(t)}function rt(t){var e=Kr[t];return void 0===e&&(e=nt(t),Ur===Rr&&(Ur=0,Kr={}),Ur++,Kr[t]=e),e}function nt(t){for(var e=0,r=0;t.length>r;r++)e=31*e+t.charCodeAt(r)|0;return tt(e)}function it(t){var e;if(xr&&(e=Dr.get(t),void 0!==e))return e;if(e=t[Ar],void 0!==e)return e;if(!Or){if(e=t.propertyIsEnumerable&&t.propertyIsEnumerable[Ar],void 0!==e)return e;if(e=ot(t),void 0!==e)return e}if(e=++kr,1073741824&kr&&(kr=0),xr)Dr.set(t,e);else{if(void 0!==Er&&Er(t)===!1)throw Error("Non-extensible objects are not allowed as keys.");if(Or)Object.defineProperty(t,Ar,{enumerable:!1,configurable:!1,writable:!1,value:e});else if(void 0!==t.propertyIsEnumerable&&t.propertyIsEnumerable===t.constructor.prototype.propertyIsEnumerable)t.propertyIsEnumerable=function(){return this.constructor.prototype.propertyIsEnumerable.apply(this,arguments)},t.propertyIsEnumerable[Ar]=e;else{if(void 0===t.nodeType)throw Error("Unable to set a non-enumerable property on object.");t[Ar]=e}}return e}function ot(t){if(t&&t.nodeType>0)switch(t.nodeType){case 1:return t.uniqueID;case 9:return t.documentElement&&t.documentElement.uniqueID}}function ut(t,e){if(!t)throw Error(e)}function st(t){ut(t!==1/0,"Cannot perform this action with an infinite size.")}function at(t,e){this._iter=t,this._useKeys=e,this.size=t.size}function ht(t){this._iter=t,this.size=t.size}function ft(t){this._iter=t,this.size=t.size}function ct(t){this._iter=t,this.size=t.size}function _t(t){var e=jt(t);return e._iter=t,e.size=t.size,e.flip=function(){return t},e.reverse=function(){var e=t.reverse.apply(this);return e.flip=function(){return t.reverse()},e},e.has=function(e){return t.includes(e)},e.includes=function(e){return t.has(e)},e.cacheResult=Rt,e.__iterateUncached=function(e,r){var n=this;return t.__iterate(function(t,r){return e(r,t,n)!==!1},r)},e.__iteratorUncached=function(e,r){if(e===wr){
+!function(t,e){global.Immutable=e()}(this,function(){"use strict";function t(t,e){e&&(t.prototype=Object.create(e.prototype)),t.prototype.constructor=t}function e(t){return t.value=!1,t}function r(t){t&&(t.value=!0)}function n(){}function i(t,e){e=e||0;for(var r=Math.max(0,t.length-e),n=Array(r),i=0;r>i;i++)n[i]=t[i+e];return n}function o(t){return void 0===t.size&&(t.size=t.__iterate(s)),t.size}function u(t,e){return e>=0?+e:o(t)+ +e}function s(){return!0}function a(t,e,r){return(0===t||void 0!==r&&-r>=t)&&(void 0===e||void 0!==r&&e>=r)}function h(t,e){return c(t,e,0)}function f(t,e){return c(t,e,e)}function c(t,e,r){return void 0===t?r:0>t?Math.max(0,e+t):void 0===e?t:Math.min(e,t)}function _(t){return y(t)?t:O(t)}function p(t){return d(t)?t:x(t)}function v(t){return m(t)?t:k(t)}function l(t){return y(t)&&!g(t)?t:A(t)}function y(t){return!(!t||!t[vr])}function d(t){return!(!t||!t[lr])}function m(t){return!(!t||!t[yr])}function g(t){return d(t)||m(t)}function w(t){return!(!t||!t[dr])}function S(t){this.next=t}function z(t,e,r,n){var i=0===t?e:1===t?r:[e,r];return n?n.value=i:n={value:i,done:!1},n}function I(){return{value:void 0,done:!0}}function b(t){return!!M(t)}function q(t){return t&&"function"==typeof t.next}function D(t){var e=M(t);return e&&e.call(t)}function M(t){var e=t&&(Sr&&t[Sr]||t[zr]);return"function"==typeof e?e:void 0}function E(t){return t&&"number"==typeof t.length}function O(t){return null===t||void 0===t?T():y(t)?t.toSeq():C(t)}function x(t){return null===t||void 0===t?T().toKeyedSeq():y(t)?d(t)?t.toSeq():t.fromEntrySeq():W(t)}function k(t){return null===t||void 0===t?T():y(t)?d(t)?t.entrySeq():t.toIndexedSeq():B(t)}function A(t){return(null===t||void 0===t?T():y(t)?d(t)?t.entrySeq():t:B(t)).toSetSeq()}function j(t){this._array=t,this.size=t.length}function R(t){var e=Object.keys(t);this._object=t,this._keys=e,this.size=e.length}function U(t){this._iterable=t,this.size=t.length||t.size;}function K(t){this._iterator=t,this._iteratorCache=[]}function L(t){return!(!t||!t[br])}function T(){return qr||(qr=new j([]))}function W(t){var e=Array.isArray(t)?new j(t).fromEntrySeq():q(t)?new K(t).fromEntrySeq():b(t)?new U(t).fromEntrySeq():"object"==typeof t?new R(t):void 0;if(!e)throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+t);return e}function B(t){var e=J(t);if(!e)throw new TypeError("Expected Array or iterable object of values: "+t);return e}function C(t){var e=J(t)||"object"==typeof t&&new R(t);if(!e)throw new TypeError("Expected Array or iterable object of values, or keyed object: "+t);return e}function J(t){return E(t)?new j(t):q(t)?new K(t):b(t)?new U(t):void 0}function P(t,e,r,n){var i=t._cache;if(i){for(var o=i.length-1,u=0;o>=u;u++){var s=i[r?o-u:u];if(e(s[1],n?s[0]:u,t)===!1)return u+1}return u}return t.__iterateUncached(e,r)}function H(t,e,r,n){var i=t._cache;if(i){var o=i.length-1,u=0;return new S(function(){var t=i[r?o-u:u];return u++>o?I():z(e,n?t[0]:u-1,t[1])})}return t.__iteratorUncached(e,r)}function N(){throw TypeError("Abstract")}function V(){}function Y(){}function Q(){}function X(t,e){if(t===e||t!==t&&e!==e)return!0;if(!t||!e)return!1;if("function"==typeof t.valueOf&&"function"==typeof e.valueOf){if(t=t.valueOf(),e=e.valueOf(),t===e||t!==t&&e!==e)return!0;if(!t||!e)return!1}return"function"==typeof t.equals&&"function"==typeof e.equals&&t.equals(e)?!0:!1}function F(t,e){return e?G(e,t,"",{"":t}):Z(t)}function G(t,e,r,n){return Array.isArray(e)?t.call(n,r,k(e).map(function(r,n){return G(t,r,n,e)})):$(e)?t.call(n,r,x(e).map(function(r,n){return G(t,r,n,e)})):e}function Z(t){return Array.isArray(t)?k(t).map(Z).toList():$(t)?x(t).map(Z).toMap():t}function $(t){return t&&(t.constructor===Object||void 0===t.constructor)}function tt(t){return t>>>1&1073741824|3221225471&t}function et(t){if(t===!1||null===t||void 0===t)return 0;if("function"==typeof t.valueOf&&(t=t.valueOf(),t===!1||null===t||void 0===t))return 0;if(t===!0)return 1;var e=typeof t;if("number"===e){var r=0|t;for(r!==t&&(r^=4294967295*t);t>4294967295;)t/=4294967295,r^=t;return tt(r)}return"string"===e?t.length>jr?rt(t):nt(t):"function"==typeof t.hashCode?t.hashCode():it(t)}function rt(t){var e=Kr[t];return void 0===e&&(e=nt(t),Ur===Rr&&(Ur=0,Kr={}),Ur++,Kr[t]=e),e}function nt(t){for(var e=0,r=0;t.length>r;r++)e=31*e+t.charCodeAt(r)|0;return tt(e)}function it(t){var e;if(xr&&(e=Dr.get(t),void 0!==e))return e;if(e=t[Ar],void 0!==e)return e;if(!Or){if(e=t.propertyIsEnumerable&&t.propertyIsEnumerable[Ar],void 0!==e)return e;if(e=ot(t),void 0!==e)return e}if(e=++kr,1073741824&kr&&(kr=0),xr)Dr.set(t,e);else{if(void 0!==Er&&Er(t)===!1)throw Error("Non-extensible objects are not allowed as keys.");if(Or)Object.defineProperty(t,Ar,{enumerable:!1,configurable:!1,writable:!1,value:e});else if(void 0!==t.propertyIsEnumerable&&t.propertyIsEnumerable===t.constructor.prototype.propertyIsEnumerable)t.propertyIsEnumerable=function(){return this.constructor.prototype.propertyIsEnumerable.apply(this,arguments)},t.propertyIsEnumerable[Ar]=e;else{if(void 0===t.nodeType)throw Error("Unable to set a non-enumerable property on object.");t[Ar]=e}}return e}function ot(t){if(t&&t.nodeType>0)switch(t.nodeType){case 1:return t.uniqueID;case 9:return t.documentElement&&t.documentElement.uniqueID}}function ut(t,e){if(!t)throw Error(e)}function st(t){ut(t!==1/0,"Cannot perform this action with an infinite size.")}function at(t,e){this._iter=t,this._useKeys=e,this.size=t.size}function ht(t){this._iter=t,this.size=t.size}function ft(t){this._iter=t,this.size=t.size}function ct(t){this._iter=t,this.size=t.size}function _t(t){var e=jt(t);return e._iter=t,e.size=t.size,e.flip=function(){return t},e.reverse=function(){var e=t.reverse.apply(this);return e.flip=function(){return t.reverse()},e},e.has=function(e){return t.includes(e)},e.includes=function(e){return t.has(e)},e.cacheResult=Rt,e.__iterateUncached=function(e,r){var n=this;return t.__iterate(function(t,r){return e(r,t,n)!==!1},r)},e.__iteratorUncached=function(e,r){if(e===wr){
 var n=t.__iterator(e,r);return new S(function(){var t=n.next();if(!t.done){var e=t.value[0];t.value[0]=t.value[1],t.value[1]=e}return t})}return t.__iterator(e===gr?mr:gr,r)},e}function pt(t,e,r){var n=jt(t);return n.size=t.size,n.has=function(e){return t.has(e)},n.get=function(n,i){var o=t.get(n,cr);return o===cr?i:e.call(r,o,n,t)},n.__iterateUncached=function(n,i){var o=this;return t.__iterate(function(t,i,u){return n(e.call(r,t,i,u),i,o)!==!1},i)},n.__iteratorUncached=function(n,i){var o=t.__iterator(wr,i);return new S(function(){var i=o.next();if(i.done)return i;var u=i.value,s=u[0];return z(n,s,e.call(r,u[1],s,t),i)})},n}function vt(t,e){var r=jt(t);return r._iter=t,r.size=t.size,r.reverse=function(){return t},t.flip&&(r.flip=function(){var e=_t(t);return e.reverse=function(){return t.flip()},e}),r.get=function(r,n){return t.get(e?r:-1-r,n)},r.has=function(r){return t.has(e?r:-1-r)},r.includes=function(e){return t.includes(e)},r.cacheResult=Rt,r.__iterate=function(e,r){var n=this;return t.__iterate(function(t,r){return e(t,r,n)},!r)},r.__iterator=function(e,r){return t.__iterator(e,!r)},r}function lt(t,e,r,n){var i=jt(t);return n&&(i.has=function(n){var i=t.get(n,cr);return i!==cr&&!!e.call(r,i,n,t)},i.get=function(n,i){var o=t.get(n,cr);return o!==cr&&e.call(r,o,n,t)?o:i}),i.__iterateUncached=function(i,o){var u=this,s=0;return t.__iterate(function(t,o,a){return e.call(r,t,o,a)?(s++,i(t,n?o:s-1,u)):void 0},o),s},i.__iteratorUncached=function(i,o){var u=t.__iterator(wr,o),s=0;return new S(function(){for(;;){var o=u.next();if(o.done)return o;var a=o.value,h=a[0],f=a[1];if(e.call(r,f,h,t))return z(i,n?h:s++,f,o)}})},i}function yt(t,e,r){var n=Lt().asMutable();return t.__iterate(function(i,o){n.update(e.call(r,i,o,t),0,function(t){return t+1})}),n.asImmutable()}function dt(t,e,r){var n=d(t),i=(w(t)?Ie():Lt()).asMutable();t.__iterate(function(o,u){i.update(e.call(r,o,u,t),function(t){return t=t||[],t.push(n?[u,o]:o),t})});var o=At(t);return i.map(function(e){return Ot(t,o(e))})}function mt(t,e,r,n){var i=t.size;if(a(e,r,i))return t;var o=h(e,i),s=f(r,i);if(o!==o||s!==s)return mt(t.toSeq().cacheResult(),e,r,n);var c=s-o;0>c&&(c=0);var _=jt(t);return _.size=0===c?c:t.size&&c||void 0,!n&&L(t)&&c>=0&&(_.get=function(e,r){return e=u(this,e),e>=0&&c>e?t.get(e+o,r):r}),_.__iterateUncached=function(e,r){var i=this;if(0===c)return 0;if(r)return this.cacheResult().__iterate(e,r);var u=0,s=!0,a=0;return t.__iterate(function(t,r){return s&&(s=u++<o)?void 0:(a++,e(t,n?r:a-1,i)!==!1&&a!==c)}),a},_.__iteratorUncached=function(e,r){if(c&&r)return this.cacheResult().__iterator(e,r);var i=c&&t.__iterator(e,r),u=0,s=0;return new S(function(){for(;u++<o;)i.next();if(++s>c)return I();var t=i.next();return n||e===gr?t:e===mr?z(e,s-1,void 0,t):z(e,s-1,t.value[1],t)})},_}function gt(t,e,r){var n=jt(t);return n.__iterateUncached=function(n,i){var o=this;if(i)return this.cacheResult().__iterate(n,i);var u=0;return t.__iterate(function(t,i,s){return e.call(r,t,i,s)&&++u&&n(t,i,o)}),u},n.__iteratorUncached=function(n,i){var o=this;if(i)return this.cacheResult().__iterator(n,i);var u=t.__iterator(wr,i),s=!0;return new S(function(){if(!s)return I();var t=u.next();if(t.done)return t;var i=t.value,a=i[0],h=i[1];return e.call(r,h,a,o)?n===wr?t:z(n,a,h,t):(s=!1,I())})},n}function wt(t,e,r,n){var i=jt(t);return i.__iterateUncached=function(i,o){var u=this;if(o)return this.cacheResult().__iterate(i,o);var s=!0,a=0;return t.__iterate(function(t,o,h){return s&&(s=e.call(r,t,o,h))?void 0:(a++,i(t,n?o:a-1,u))}),a},i.__iteratorUncached=function(i,o){var u=this;if(o)return this.cacheResult().__iterator(i,o);var s=t.__iterator(wr,o),a=!0,h=0;return new S(function(){var t,o,f;do{if(t=s.next(),t.done)return n||i===gr?t:i===mr?z(i,h++,void 0,t):z(i,h++,t.value[1],t);var c=t.value;o=c[0],f=c[1],a&&(a=e.call(r,f,o,u))}while(a);return i===wr?t:z(i,o,f,t)})},i}function St(t,e){var r=d(t),n=[t].concat(e).map(function(t){return y(t)?r&&(t=p(t)):t=r?W(t):B(Array.isArray(t)?t:[t]),t}).filter(function(t){return 0!==t.size});if(0===n.length)return t;if(1===n.length){var i=n[0];if(i===t||r&&d(i)||m(t)&&m(i))return i;}var o=new j(n);return r?o=o.toKeyedSeq():m(t)||(o=o.toSetSeq()),o=o.flatten(!0),o.size=n.reduce(function(t,e){if(void 0!==t){var r=e.size;if(void 0!==r)return t+r}},0),o}function zt(t,e,r){var n=jt(t);return n.__iterateUncached=function(n,i){function o(t,a){var h=this;t.__iterate(function(t,i){return(!e||e>a)&&y(t)?o(t,a+1):n(t,r?i:u++,h)===!1&&(s=!0),!s},i)}var u=0,s=!1;return o(t,0),u},n.__iteratorUncached=function(n,i){var o=t.__iterator(n,i),u=[],s=0;return new S(function(){for(;o;){var t=o.next();if(t.done===!1){var a=t.value;if(n===wr&&(a=a[1]),e&&!(e>u.length)||!y(a))return r?t:z(n,s++,a,t);u.push(o),o=a.__iterator(n,i)}else o=u.pop()}return I()})},n}function It(t,e,r){var n=At(t);return t.toSeq().map(function(i,o){return n(e.call(r,i,o,t))}).flatten(!0)}function bt(t,e){var r=jt(t);return r.size=t.size&&2*t.size-1,r.__iterateUncached=function(r,n){var i=this,o=0;return t.__iterate(function(t){return(!o||r(e,o++,i)!==!1)&&r(t,o++,i)!==!1},n),o},r.__iteratorUncached=function(r,n){var i,o=t.__iterator(gr,n),u=0;return new S(function(){return(!i||u%2)&&(i=o.next(),i.done)?i:u%2?z(r,u++,e):z(r,u++,i.value,i)})},r}function qt(t,e,r){e||(e=Ut);var n=d(t),i=0,o=t.toSeq().map(function(e,n){return[n,e,i++,r?r(e,n,t):e]}).toArray();return o.sort(function(t,r){return e(t[3],r[3])||t[2]-r[2]}).forEach(n?function(t,e){o[e].length=2}:function(t,e){o[e]=t[1]}),n?x(o):m(t)?k(o):A(o)}function Dt(t,e,r){if(e||(e=Ut),r){var n=t.toSeq().map(function(e,n){return[e,r(e,n,t)]}).reduce(function(t,r){return Mt(e,t[1],r[1])?r:t});return n&&n[0]}return t.reduce(function(t,r){return Mt(e,t,r)?r:t})}function Mt(t,e,r){var n=t(r,e);return 0===n&&r!==e&&(void 0===r||null===r||r!==r)||n>0}function Et(t,e,r){var n=jt(t);return n.size=new j(r).map(function(t){return t.size}).min(),n.__iterate=function(t,e){for(var r,n=this.__iterator(gr,e),i=0;!(r=n.next()).done&&t(r.value,i++,this)!==!1;);return i},n.__iteratorUncached=function(t,n){var i=r.map(function(t){return t=_(t),D(n?t.reverse():t)}),o=0,u=!1;return new S(function(){var r;return u||(r=i.map(function(t){
 return t.next()}),u=r.some(function(t){return t.done})),u?I():z(t,o++,e.apply(null,r.map(function(t){return t.value})))})},n}function Ot(t,e){return L(t)?e:t.constructor(e)}function xt(t){if(t!==Object(t))throw new TypeError("Expected [K, V] tuple: "+t)}function kt(t){return st(t.size),o(t)}function At(t){return d(t)?p:m(t)?v:l}function jt(t){return Object.create((d(t)?x:m(t)?k:A).prototype)}function Rt(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):O.prototype.cacheResult.call(this)}function Ut(t,e){return t>e?1:e>t?-1:0}function Kt(t){var e=D(t);if(!e){if(!E(t))throw new TypeError("Expected iterable or array-like: "+t);e=D(_(t))}return e}function Lt(t){return null===t||void 0===t?Qt():Tt(t)?t:Qt().withMutations(function(e){var r=p(t);st(r.size),r.forEach(function(t,r){return e.set(r,t)})})}function Tt(t){return!(!t||!t[Lr])}function Wt(t,e){this.ownerID=t,this.entries=e}function Bt(t,e,r){this.ownerID=t,this.bitmap=e,this.nodes=r}function Ct(t,e,r){this.ownerID=t,this.count=e,this.nodes=r}function Jt(t,e,r){this.ownerID=t,this.keyHash=e,this.entries=r}function Pt(t,e,r){this.ownerID=t,this.keyHash=e,this.entry=r}function Ht(t,e,r){this._type=e,this._reverse=r,this._stack=t._root&&Vt(t._root)}function Nt(t,e){return z(t,e[0],e[1])}function Vt(t,e){return{node:t,index:0,__prev:e}}function Yt(t,e,r,n){var i=Object.create(Tr);return i.size=t,i._root=e,i.__ownerID=r,i.__hash=n,i.__altered=!1,i}function Qt(){return Wr||(Wr=Yt(0))}function Xt(t,r,n){var i,o;if(t._root){var u=e(_r),s=e(pr);if(i=Ft(t._root,t.__ownerID,0,void 0,r,n,u,s),!s.value)return t;o=t.size+(u.value?n===cr?-1:1:0)}else{if(n===cr)return t;o=1,i=new Wt(t.__ownerID,[[r,n]])}return t.__ownerID?(t.size=o,t._root=i,t.__hash=void 0,t.__altered=!0,t):i?Yt(o,i):Qt()}function Ft(t,e,n,i,o,u,s,a){return t?t.update(e,n,i,o,u,s,a):u===cr?t:(r(a),r(s),new Pt(e,i,[o,u]))}function Gt(t){return t.constructor===Pt||t.constructor===Jt}function Zt(t,e,r,n,i){if(t.keyHash===n)return new Jt(e,n,[t.entry,i]);var o,u=(0===r?t.keyHash:t.keyHash>>>r)&fr,s=(0===r?n:n>>>r)&fr,a=u===s?[Zt(t,e,r+ar,n,i)]:(o=new Pt(e,n,i),
 s>u?[t,o]:[o,t]);return new Bt(e,1<<u|1<<s,a)}function $t(t,e,r,i){t||(t=new n);for(var o=new Pt(t,et(r),[r,i]),u=0;e.length>u;u++){var s=e[u];o=o.update(t,0,void 0,s[0],s[1])}return o}function te(t,e,r,n){for(var i=0,o=0,u=Array(r),s=0,a=1,h=e.length;h>s;s++,a<<=1){var f=e[s];void 0!==f&&s!==n&&(i|=a,u[o++]=f)}return new Bt(t,i,u)}function ee(t,e,r,n,i){for(var o=0,u=Array(hr),s=0;0!==r;s++,r>>>=1)u[s]=1&r?e[o++]:void 0;return u[n]=i,new Ct(t,o+1,u)}function re(t,e,r){for(var n=[],i=0;r.length>i;i++){var o=r[i],u=p(o);y(o)||(u=u.map(function(t){return F(t)})),n.push(u)}return ie(t,e,n)}function ne(t){return function(e,r,n){return e&&e.mergeDeepWith&&y(r)?e.mergeDeepWith(t,r):t?t(e,r,n):r}}function ie(t,e,r){return r=r.filter(function(t){return 0!==t.size}),0===r.length?t:0!==t.size||t.__ownerID||1!==r.length?t.withMutations(function(t){for(var n=e?function(r,n){t.update(n,cr,function(t){return t===cr?r:e(t,r,n)})}:function(e,r){t.set(r,e)},i=0;r.length>i;i++)r[i].forEach(n)}):t.constructor(r[0])}function oe(t,e,r,n){var i=t===cr,o=e.next();if(o.done){var u=i?r:t,s=n(u);return s===u?t:s}ut(i||t&&t.set,"invalid keyPath");var a=o.value,h=i?cr:t.get(a,cr),f=oe(h,e,r,n);return f===h?t:f===cr?t.remove(a):(i?Qt():t).set(a,f)}function ue(t){return t-=t>>1&1431655765,t=(858993459&t)+(t>>2&858993459),t=t+(t>>4)&252645135,t+=t>>8,t+=t>>16,127&t}function se(t,e,r,n){var o=n?t:i(t);return o[e]=r,o}function ae(t,e,r,n){var i=t.length+1;if(n&&e+1===i)return t[e]=r,t;for(var o=Array(i),u=0,s=0;i>s;s++)s===e?(o[s]=r,u=-1):o[s]=t[s+u];return o}function he(t,e,r){var n=t.length-1;if(r&&e===n)return t.pop(),t;for(var i=Array(n),o=0,u=0;n>u;u++)u===e&&(o=1),i[u]=t[u+o];return i}function fe(t){var e=le();if(null===t||void 0===t)return e;if(ce(t))return t;var r=v(t),n=r.size;return 0===n?e:(st(n),n>0&&hr>n?ve(0,n,ar,null,new _e(r.toArray())):e.withMutations(function(t){t.setSize(n),r.forEach(function(e,r){return t.set(r,e)})}))}function ce(t){return!(!t||!t[Pr])}function _e(t,e){this.array=t,this.ownerID=e}function pe(t,e){function r(t,e,r){


Fix valid identifier to verify valid identifiers and move macro expansion to compileImpl, also add true and false for now to top scope

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -401,15 +401,26 @@
             compileFunctionCall op, args
       else if node.label is 'const'
         compileConst node
-      else if node.token.match /^"/
-        node.token
       else
-        name = validIdentifier node.token
-        if not node.label
-          defLocation = lookupIdentifier name, node
-          if not defLocation
-            throw new Error "#{name} used but not defined"
-        name
+        name = node.token
+        if name.match /^"/
+          name
+        else if macros[name]
+          expandMacro name
+        else
+          [firstChar] = name
+          if firstChar is '-'
+            "(- #{validIdentifier name[1...]})"
+          else if firstChar is '/'
+            # regex
+            name
+          else
+            escapedName = validIdentifier name
+            if not node.label
+              defLocation = lookupIdentifier escapedName, node
+              if not defLocation
+                throw new Error "#{name} used but not defined"
+            escapedName

 isList = (node) ->
   Array.isArray(node) and node[0].token is '['
@@ -443,29 +454,28 @@
   macros[macro]?() ? macro

 validIdentifier = (name) ->
-  if macros[name]
-    expandMacro name
+  [firstChar] = name
+  if firstChar is '-'
+    throw new Error "Identifier expected, but got minus #{name}"
+  else if firstChar is '/'
+    throw new Error "Identifier expected, but found regex #{name}"
   else
-    [firstChar] = name
-    if firstChar is '-'
-      "(- #{validIdentifier name[1...]})"
-    else if firstChar is '/'
-      # regex
-      name
-    else
-      name
-        .replace(/\+/g, 'plus_')
-        .replace(/\-/g, '__')
-        .replace(/\*/g, 'times_')
-        .replace(/\//g, 'over_')
-        .replace(/\√/g, 'sqrt_')
-        .replace(/\./g, 'dot_')
-        .replace(/\&/g, 'and_')
-        .replace(/\?/g, 'p_')
-        .replace(/^const$/, 'const_')
-        .replace(/^default$/, 'default_')
-        .replace(/^with$/, 'with_')
-        .replace(/^in$/, 'in_')
+    name
+      .replace(/\+/g, 'plus_')
+      .replace(/\-/g, '__')
+      .replace(/\*/g, 'times_')
+      .replace(/\//g, 'over_')
+      .replace(/\√/g, 'sqrt_')
+      .replace(/\./g, 'dot_')
+      .replace(/\&/g, 'and_')
+      .replace(/\?/g, 'p_')
+      .replace(/^const$/, 'const_')
+      .replace(/^default$/, 'default_')
+      .replace(/^with$/, 'with_')
+      .replace(/^in$/, 'in_')
+
+
+

 lookupIdentifier = (name, node) ->
   while node.parent
@@ -926,7 +936,7 @@
       "function(__a, __b){return __b #{to} __a;}"

 topScopeDefines = ->
-  ids = 'and_ $empty show__list from__nullable'.split(' ')
+  ids = 'true false and_ $empty show__list from__nullable'.split(' ')
     .concat unaryFnMapping.from,
       binaryOpMapping.from,
       binaryFnMapping.from,


Show holes before delims except for empty lists or tuples

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -73,8 +73,9 @@
 markFake = (form) ->
   [prev, rest...] = form
   for node in rest
-    if (prev.label in ['whitespace', 'indent'] or prev.symbol in leftDelims) and
-        (node.label is 'whitespace' or node.symbol in rightDelims)
+    if (prev.label in ['whitespace', 'indent'] or (delimLeft = prev.symbol in leftDelims)) and
+        (node.label is 'whitespace' or (delimRight = node.symbol in rightDelims)) and
+          not (delimLeft and delimRight and prev.symbol in ['{', '['])
       node.fake = yes
     prev = node
   return


clean popover

clean popover