Lazy accessor of module cache

Lazy accessor of module cache

--- bokehjs/src/coffee/common/base.coffee
+++ bokehjs/src/coffee/common/base.coffee
@@ -159,16 +159,20 @@
       result[name] = spec
   return result

-_mod_cache = null
-
-Collections = (typename) ->
+_mod_cache = null # XXX: do NOT access directly outside _get_mod_cache()
+
+_get_mod_cache = () ->
   if not _mod_cache?
     _mod_cache = make_cache(locations)
+  _mod_cache
+
+Collections = (typename) ->
+  mod_cache = _get_mod_cache()

   if collection_overrides[typename]
     return collection_overrides[typename]

-  mod = _mod_cache[typename]
+  mod = mod_cache[typename]

   if not mod?
     throw Error("module `#{typename}' does not exists")
@@ -190,11 +194,12 @@
                        # registration mechanism.

 Collections.register_plugin = (name, locations) ->
-  cache = make_cache(locations)
-
-  for own name, module of cache
-    if not _mod_cache.hasOwnProperty(name)
-      _mod_cache[name] = module
+  mod_cache = _get_mod_cache()
+  plugin_cache = make_cache(locations)
+
+  for own name, module of plugin_cache
+    if not mod_cache.hasOwnProperty(name)
+      mod_cache[name] = module
     else
       throw new Error("#{name} was already registered")

@@ -203,13 +208,14 @@
     body = coffee.compile(code, {bare: true, shiftLine: true})
     new Function("require", "module", "exports", body)

-  if not _mod_cache?
-    mod_name = "custom/#{name.toLowerCase()}"
-    [impl, deps] = mod
-    modules[mod_name] = [compile(impl), deps]
-    locations[name] = require(mod_name)
-  else
-    throw new Error("can't register new models after collections were initialized") # XXX: temporary limitation
+  mod_cache = _get_mod_cache()
+
+  mod_name = "custom/#{name.toLowerCase()}"
+  [impl, deps] = mod
+  modules[mod_name] = [compile(impl), deps]
+  _locations = {}
+  _locations[name] = require(mod_name)
+  Collections.register_plugin("custom", _locations)

 Collections.register_models = (specs) ->
   for own name, impl of specs


Terminal: loading timer cancelling added

--- client/Terminal/AppView.coffee
+++ client/Terminal/AppView.coffee
@@ -40,9 +40,10 @@
     # if we still have the same message after 15 seconds assuming
     # all checks have failed and showing a warning to make user
     # try again. - SY
-    KD.utils.wait 15000, =>
+    messageTimer = KD.utils.wait 15000, =>
       if @messagePane.$().text() is loadingPartial
         @setMessage "Couldn't open your terminal. Click here to try again.", no, yes
+    @on 'TerminalStarted', => KD.utils.killWait messageTimer

     @tabView.on 'AllTabsClosed', =>
       @setMessage "All tabs are closed. Click to open a new Terminal.", no, yes


Update switch_url to app.quickcode.io

Update switch_url to app.quickcode.io

From scraperwiki.com

--- client/code/router/main.coffee
+++ client/code/router/main.coffee
@@ -112,7 +112,7 @@
               ts_datasets: 0
               tf_datasets: 0
               cb_datasets: 0
-              switch_url: "https://scraperwiki.com/switch/" + real.shortName
+              switch_url: "https://app.quickcode.io/switch/" + real.shortName
               recurly_url: "https://scraperwiki.recurly.com/accounts/" + real.recurlyAccount
               mongo_url: "https://app.compose.io/mongohq-scraperwiki-com/deployments/cu-2015/mongodb/databases/cu-live/collections/users/documents?limit=10&find%5Bquery%5D={%22shortName%22%3A%22" + real.shortName + "%22}"


client: stacks: added welcome tab

client: stacks: added welcome tab

--- client/stacks/lib/index.coffee
+++ client/stacks/lib/index.coffee
@@ -2,6 +2,7 @@
 AdminAppController         = require 'admin/index'
 StackCatalogModalView      = require './views/customviews/stackcatalogmodalview'

+WelcomeView                = require './views/welcome/welcomeappview'
 YourStacksView             = require 'app/environment/yourstacksview'
 MyStackTemplatesView       = require './views/stacks/my/mystacktemplatesview'
 GroupStackTemplatesView    = require './views/stacks/group/groupstacktemplatesview'
@@ -19,6 +20,7 @@
     teams      :
       title    : 'Stack Catalog'
       items    : [
+        { slug : 'Welcome',                 title : 'Welcome',                viewClass : WelcomeView }
         { slug : 'My-Stacks',               title : 'My Stacks',              viewClass : YourStacksView }
         { slug : 'My-Stack-Templates',      title : 'My Stack Templates',     viewClass : MyStackTemplatesView }
         { slug : 'Group-Stack-Templates',   title : 'Team Stack Templates',   viewClass : GroupStackTemplatesView }


fix some tests

fix some tests

--- client/test/pageHandler.coffee
+++ client/test/pageHandler.coffee
@@ -75,7 +75,7 @@
       expect(whenGotten.calledOnce).to.be.true
       expect(jQuery.ajax.calledOnce).to.be.true
       expect(jQuery.ajax.args[0][0]).to.have.property('type', 'GET')
-      expect(jQuery.ajax.args[0][0].url).to.match(///^/remote/siteName/slugName\.json\?random=[a-z0-9]{8}$///)
+      expect(jQuery.ajax.args[0][0].url).to.match(///^http://siteName/slugName\.json\?random=[a-z0-9]{8}$///)

     after ->
       jQuery.ajax.restore()
@@ -92,9 +92,9 @@
         whenNotGotten: sinon.stub()

       expect(jQuery.ajax.args[0][0].url).to.match(///^/slugName\.json\?random=[a-z0-9]{8}$///)
-      expect(jQuery.ajax.args[1][0].url).to.match(///^/remote/example.com/slugName\.json\?random=[a-z0-9]{8}$///)
-      expect(jQuery.ajax.args[2][0].url).to.match(///^/remote/asdf.test/slugName\.json\?random=[a-z0-9]{8}$///)
-      expect(jQuery.ajax.args[3][0].url).to.match(///^/remote/foo.bar/slugName\.json\?random=[a-z0-9]{8}$///)
+      expect(jQuery.ajax.args[1][0].url).to.match(///^http://example.com/slugName\.json\?random=[a-z0-9]{8}$///)
+      expect(jQuery.ajax.args[2][0].url).to.match(///^http://asdf.test/slugName\.json\?random=[a-z0-9]{8}$///)
+      expect(jQuery.ajax.args[3][0].url).to.match(///^http://foo.bar/slugName\.json\?random=[a-z0-9]{8}$///)

     after ->
       jQuery.ajax.restore()


Make whole 'red button' in email a link

Make whole 'red button' in email a link

--- services/web/app/coffee/Features/Email/EmailBuilder.coffee
+++ services/web/app/coffee/Features/Email/EmailBuilder.coffee
@@ -94,15 +94,11 @@
 	compiledTemplate: _.template """

Hi, <%= owner.email %> wants to share <a href="<%= project.url %>">'<%= project.name %>' with you

-

-

-			<a href="<%= inviteUrl %>" style="text-decoration:none" target="_blank">
+			<a style="text-decoration: none; width: 200px; background-color: #a93629; border: 1px solid #e24b3b; border-radius: 3px; padding: 15px; margin: 24px; display: block;" href="<%= inviteUrl %>" style="text-decoration:none" target="_blank">

 					View Project

-

-

 Thank you

 <a href="<%= siteUrl %>">#{settings.appName}


Add tomorrow light theme to cache

Add tomorrow light theme to cache

--- build/tasks/prebuild-less-task.coffee
+++ build/tasks/prebuild-less-task.coffee
@@ -9,10 +9,12 @@
       ['atom-dark-ui', 'atom-light-syntax']
       ['atom-dark-ui', 'solarized-dark-syntax']
       ['atom-dark-ui', 'base16-tomorrow-dark-theme']
+      ['atom-dark-ui', 'base16-tomorrow-light-theme']
       ['atom-light-ui', 'atom-light-syntax']
       ['atom-light-ui', 'atom-dark-syntax']
       ['atom-light-ui', 'solarized-dark-syntax']
       ['atom-light-ui', 'base16-tomorrow-dark-theme']
+      ['atom-light-ui', 'base16-tomorrow-light-theme']
     ]

     directory = path.join(grunt.config.get('atom.appDir'), 'less-compile-cache')


Account: Password recovery is enabled for logged in users

--- client/Login/AppController.coffee
+++ client/Login/AppController.coffee
@@ -43,6 +43,10 @@
     KD.singleton('appManager').open 'Login', (app) ->
       app.prepareFinishRegistrationForm token  unless KD.isLoggedIn()

+  handleRecovery = ->
+    KD.singleton('appManager').open 'Login', (app)->
+      app.getView().animateToForm 'recover'
+
 #  handleFailureOfRestriction =->
 #    KD.utils.defer -> new KDNotificationView title: "Login restricted"

@@ -70,7 +74,7 @@
       '/:name?/Login/:token?'    : handler (app)-> app.getView().animateToForm 'login'
       '/:name?/Redeem'           : handler (app)-> app.getView().animateToForm 'redeem'
       '/:name?/Register/:token?' : handler (app)-> app.getView().animateToForm 'register'
-      '/:name?/Recover'          : handler (app)-> app.getView().animateToForm 'recover'
+      '/:name?/Recover'          : handleRecovery
       '/:name?/Reset'            : handler (app)-> app.getView().animateToForm 'reset'
       '/:name?/Reset/:token'     : handleResetRoute
       '/:name?/Confirm/:token'   : handleResetRoute


fshelper createfilefrom path vmname fix.

--- client/app/MainApp/fs/fshelper.coffee
+++ client/app/MainApp/fs/fshelper.coffee
@@ -131,9 +131,11 @@

   @createFileFromPath = (path, type = "file")->
     return warn "pass a path to create a file instance" unless path
+    vmName     = @getVMNameFromPath(path) or null
+    path       = @plainPath path  if vmName
     parentPath = @getParentPath path
     name       = @getFileNameFromPath path
-    return @createFile { path, parentPath, name, type }
+    return @createFile { path, parentPath, name, type, vmName }

   @createFile = (data)->
     unless data and data.type and data.path


FIX suppr with keyboad on draft mailbox

FIX suppr with keyboad on draft mailbox

--- client/app/utils/api_utils.coffee
+++ client/app/utils/api_utils.coffee
@@ -145,6 +145,7 @@
             direction: 'second'
             action: action
             parameters: params
+
         url = window.router.buildUrl urlOptions
         window.router.navigate url, {trigger: true}

@@ -153,8 +154,8 @@
         href = window.location.href
         href = href.replace /\/message\/[\w-]+/gi, ''
         href = href.replace /\/conversation\/[\w-]+\/[\w-]+/gi, ''
+        href = href.replace /\/edit\/[\w-]+/gi, ''
         window.location.href = href
-

     messageDeleteCurrent: ->
         messageID = MessageStore.getCurrentID()
@@ -170,8 +171,12 @@
             MessageActionCreator.delete {messageID}
             LayoutActionCreator.hideModal() if isModal

-            # Goto next message
-            Utils.messageSetCurrent next
+            if next?.size
+                # Goto next message
+                Utils.messageSetCurrent next
+            else
+                # Close 2nd panel
+                Utils.messageClose()

         settings = SettingsStore.get()


syntax highlighting for :implements and :base

--- client/codemirror-apl-mode.coffee
+++ client/codemirror-apl-mode.coffee
@@ -111,8 +111,10 @@
             ok = state.kws[-1..][0] == 'for'
           when 'case'
             ok = state.kws[-1..][0] in ['select', 'trap']
-          when 'access', 'case', 'caselist', 'continue', 'field', 'goto', 'include', 'implements', 'leave', 'return', 'until'
+          when 'access', 'base', 'caselist', 'continue', 'field', 'goto', 'include', 'leave', 'return', 'until'
             ok = 1
+          when 'implements'
+            stream.match /\s+\w+/; ok = 1
         ok && 'apl-kw' || 'apl-err'
       else if c == '⎕' then (if stream.match(/[áa-z0-9]*/i)?[0].toLowerCase() in quadNames then 'apl-quad' else 'apl-err')
       else if c == '⍞' then 'apl-quad'


catch file existing error

catch file existing error

--- lib/config-manager.coffee
+++ lib/config-manager.coffee
@@ -9,7 +9,11 @@
     fileStoragePath: path.join(__dirname, '..', 'kernel-configs')

     writeConfigFile: (onCompleted) ->
-        fs.mkdirSync(@fileStoragePath)
+        try
+            fs.mkdirSync(@fileStoragePath)
+        catch e
+            if e.code != 'EEXIST'
+                throw e
         # child_process.execSync('mkdir -p ' + @fileStoragePath)
         filename = 'kernel-' + uuid.v4() + '.json'
         portfinder.findMany 5, (ports) =>


Typo fix

Typo fix

--- components/ListenEvents.coffee
+++ components/ListenEvents.coffee
@@ -5,7 +5,7 @@
 subscribe = (instance, out) ->
   instance.setProps
     emitEvent: (events, payload, metadata) ->
-      events = [events] if typeof event is 'string'
+      events = [events] if typeof events is 'string'
       out.beginGroup event for event in events
       out.send payload
       out.endGroup event for event in events


More glance for coverage reports.

More glance for coverage reports.

--- grunt-custom-tasks/jscoverage_report.coffee
+++ grunt-custom-tasks/jscoverage_report.coffee
@@ -121,7 +121,6 @@
 		total = touched = 0
 		n = 0
 		len = tmp.length
-
 		while n < len
 			if tmp[n] isnt undefined
 				total++
@@ -145,11 +144,9 @@
 		continue  if typeof tmp is "function" or tmp.length is undefined
 		source = tmp.source
 		allcovered = true
-		console.log "[UNCOVERED CODE]", file
 		lines = []
 		n = 0
 		len = source.length
-
 		while n < len
 			if tmp[n] is 0
 				lines[n] = 1
@@ -158,7 +155,8 @@
 				lines[n] = 0
 			n++
 		if allcovered
-			console.log colorful("\t100% covered", "GREEN")
+			console.log colorful("[ 100% COVERED ]", "GREEN"), file
 		else
+			console.log colorful("[UNCOVERED CODE]", "RED"), file
 			printCoverageDetail lines, source
 	return


fix for whoami

fix for whoami

--- lib/cli.coffee
+++ lib/cli.coffee
@@ -73,7 +73,7 @@
       exit "error authenticating" if err
       log 'connected'
       next user, db
-
+
 exit = (msg) ->
   log msg if msg
   process.exit()
@@ -186,7 +186,7 @@
           # write to file
           name = "backup-#{slug}-#{Date.now()}.cson"
           fs.writeFileSync path.join(CWD, name), data
-
+
           # done
           exit "backup complete: #{name}"

@@ -449,7 +449,7 @@
   when 'docs'
     open 'http://www.dobi.io'
     exit()
-
+
   # show usage
   when 'help'
     exit USAGE
@@ -694,8 +694,8 @@

   # check your authentication status
   when 'whoami'
-    login (config) ->
-      exit JSON.stringify user, null, 2 if config.user
+    login ({user}) ->
+      exit JSON.stringify user, null, 2 if user
       exit 'not logged in. try "dobi login"'

   # invalid command


Slightly better (maybe) instructions.

Slightly better (maybe) instructions.

--- lib/cli.coffee
+++ lib/cli.coffee
@@ -24,6 +24,6 @@
       build             build the project to the `dist` directory
       spec              run the project's tests in an ongoing testem process
       spec-ci           run the project's tests in a headless CI testem process
-      config            displays the project's configuration
+      config <path>     displays the project's configuration, limited to a path if provided

   """


fixing bugs in grammar

fixing bugs in grammar

Fixes #1

--- grammars/language-stan.cson
+++ grammars/language-stan.cson
@@ -35,8 +35,8 @@
     'name': 'keyword.other.other.stan'
   }
   {
-    'comment': 'truncation operator T[]':
-    'match': '\\bT(?=\\s*\[)'
+    'comment': 'truncation operator T[]'
+    'match': '\\bT(?=\\s*\\[)'
     'name': 'keyword.other.truncation.stan'
   }
   {
@@ -52,66 +52,45 @@
     'match': '\\blp__\\b'
     'name': 'invalid.deprecated.lp.stan'
   }
+
+  {
+    'match': '\\b([a-zA-Z0-9_]*__|[0-9_][A-Za-z0-9_]+|_)\\b'
+    'name': 'invalid.illegal.variable'
+  }
+  {
+    'match': '\\b[A-Za-z][0-9A-Za-z_]*\\b'
+    'comment': 'generic variable names'
+  }
+  {
+    'include': '#numbers'
+  }
   {
     'include': '#operators'
-  }
-  {
-    'include': '#numbers'
   }
   {
     'match': ','
     'name': 'meta.delimiter.comma.stan'
   }
   {
+    'match': '[{}]'
+    'name': 'meta.brace.curly.stan'
+  }
+  {
+    'match': '\\[|\\]'
+    'name': 'meta.brace.square.stan'
+  }
+  {
+    'match': '\\(|\\)'
+    'name': 'meta.brace.round.stan'
+  }
+  {
     'match': '\\;'
     'name': 'punctuation.terminator.statement.stan'
   }
-  {
-    'begin': '{|}'
-    'name': 'meta.brace.curly.stan'
-  }
-  {
-    'begin': '\\('
-    'beginCaptures':
-      '0':
-        'name': 'meta.brace.round.stan'
-    'end': '\\)'
-    'endCaptures'
-      '0':
-        'name': 'meta.brace.round.stan'
-    'patterns': [
-      'include': '$self'
-    ]
-  }
-  {
-    'match': '\\[|\\]'
-    'name': 'meta.brace.square.stan'
-  }
 ]
 'repository':
-  'docblock':
-    'patterns': [
-      {
-        'match': '(?<!\\w)@(param|return)\\b'
-        'name': 'storage.type.class.stan'
-      }
-    ]
   'comments':
     'patterns': [
-        {
-          'begin': '/\\*\\*(?!/)'
-          'captures':
-            '0':
-              'name': 'punctuation.definition.comment.stan'
-          'patterns': [
-            {
-              'include': '#docblock'
-            }
-          ]
-          'end': '\\*/'
-          'name': 'comment.block.documentation.stan'
-        }
-      }
       {
         'comment': '/* comment block */'
         'begin': '/\\*'
@@ -172,37 +151,38 @@
         'name': 'keyword.operator.assignment.stan'
       }
       {
-        'matches': '\\|\\||&&|!'
+        'match': '\\|\\||&&'
         'name': 'keyword.operator.logical.stan'
-        'comment': 'logical operators'
-      }
-      {
-        'matches': '==|!=|<=?|>=?'
+      }
+      {
+        'match': '==|!=|<=?|>=?'
         'name': 'keyword.operator.comparison.stan'
-        'comment': 'logical operators'
-      }
-      {
-        'matches': "+|-|*|/|%|\\\\|\\.[*/]|\\^|'"
-        'name': 'keyword.operator.other.stan'
-        'comment': 'arithmetic and matrix operators'
+      }
+      {
+        'match': '!'
+        'name': 'keyword.operator.logical.stan'
+      }
+      {
+        'match': "\\+|-|\\.?\\*|\\.?/|%|\\\\|\\^|'"
+        'name': 'keyword.operator.arithmetic.stan'
       }
     ]
   'numbers':
     'patterns': [
       {
-        'match': '(?x)
+        'match': '''(?x)
         (
-        ([0-9]+)?\\.[0-9]+([eE][-]?[0-9]+)?
+        [0-9]+\\.[0-9]*([eE]-?[0-9]+)?
         |
-        [0-9]+\\.([eE][-]?[0-9]+)?
+        \\.[0-9]+([eE][-]?[0-9]+)?
         |
         [0-9]+[eE][-]?[0-9]+
-        )\\b
-        '
+        )
+        '''
         'name': 'constant.numeric.real.stan'
       }
       {
-        'match': '\\b-?\\d+'
+        'match': '[0-9]+'
         'name': 'constant.numeric.integer.stan'
       }
     ]


Don't eagerly start the server

Don't eagerly start the server

--- lib/autocomplete-dcd.coffee
+++ lib/autocomplete-dcd.coffee
@@ -1,9 +1,6 @@
 provider = require './dcd-provider'

 module.exports =
-  activate: ->
-    provider.startServer()
-
   deactivate: ->
     provider.stopServer()


Don't use textContent to verify the empty project scenario

This will make tests pass on https://github.com/atom/atom/pull/13880 and
is a more reliable approach in general.

--- spec/fuzzy-finder-spec.coffee
+++ spec/fuzzy-finder-spec.coffee
@@ -338,6 +338,7 @@

       describe "when the project has no path", ->
         beforeEach ->
+          jasmine.attachToDOM(workspaceElement)
           atom.project.setPaths([])

         it "shows an empty message with no files in the list", ->
@@ -345,8 +346,9 @@
             projectView.toggle()

           runs ->
-          expect(projectView.element.textContent).toBe 'Project is empty'
-          expect(projectView.element.querySelectorAll('li').length).toBe 0
+            expect(projectView.selectListView.refs.emptyMessage).toBeVisible()
+            expect(projectView.selectListView.refs.emptyMessage.textContent).toBe 'Project is empty'
+            expect(projectView.element.querySelectorAll('li').length).toBe 0

     describe "when a project's root path is unlinked", ->
       beforeEach ->


De-lint.

De-lint.

--- spec/fuzzy-finder-spec.coffee
+++ spec/fuzzy-finder-spec.coffee
@@ -954,7 +954,7 @@
         expect(atom.workspace.panelForItem(gitStatusView).isVisible()).toBe true

         waitsFor ->
-          gitStatusView.find('.file').length == 4
+          gitStatusView.find('.file').length is 4

         runs ->
           expect(gitStatusView.find('.status.status-modified').length).toBe 1


It looks like createRecord got moved to a "store" property on the model, but it makes no sense why I also had to provide the method with the name of the model as the first argument. SIGH.

--- spec/models/hello_spec.coffee
+++ spec/models/hello_spec.coffee
@@ -1,6 +1,6 @@
 describe "App.Hello", ->
   #FIXME: This will actually post to the server, which isn't a great time.
   # Need to figure out how to create a vanilla store for Ember Data
-  Given -> @subject = App.Hello.createRecord(greeting: "Hi", audience: "Dude")
+  Given -> @subject = App.Hello.store.createRecord('hello', greeting: "Hi", audience: "Dude")
   Then -> @subject.get('greeting') == "Hi"
   Then -> @subject.get('audience') == "Dude"


Make old test test something useful

Make old test test something useful

--- spec/find-view-spec.coffee
+++ spec/find-view-spec.coffee
@@ -133,16 +133,15 @@
       atom.commands.dispatch(findView.findEditor.element, 'window:focus-next-pane')
       expect(workspaceElement.querySelector('.find-and-replace')).not.toHaveFocus()

-  describe "when FindView's replace editor is visible", ->
-    it "keeps the replace editor visible when find-and-replace:show is triggered", ->
+  describe "find-and-replace:show-replace", ->
+    it "focuses the replace editor", ->
       atom.commands.dispatch editorView, 'find-and-replace:show-replace'

       waitsForPromise ->
         activationPromise

       runs ->
-        atom.commands.dispatch editorView, 'find-and-replace:show'
-        expect(findView.replaceEditor).toBeVisible()
+        expect(findView.replaceEditor).toHaveFocus()

   describe "core:cancel", ->
     beforeEach ->


Fix deprecations

Fix deprecations

--- spec/find-view-spec.coffee
+++ spec/find-view-spec.coffee
@@ -912,23 +912,23 @@

       it "scrolls to the first match if the settings scrollToResultOnLiveSearch is true", ->
         atom.config.set('find-and-replace.scrollToResultOnLiveSearch', true)
-        editor.setHeight(3)
+        editorView.setHeight(3)
         editor.moveToTop()
-        originalScrollPosition = editor.getScrollTop()
+        originalScrollPosition = editorView.getScrollTop()
         findView.findEditor.setText 'Array'
         advance()
-        expect(editor.getScrollTop()).not.toEqual originalScrollPosition
+        expect(editorView.getScrollTop()).not.toEqual originalScrollPosition
         expect(editor.getSelectedBufferRange()).toEqual [[11, 14], [11, 19]]
         expect(findView.findEditor).toHaveFocus()

       it "doesn't scroll to the first match if the settings scrollToResultOnLiveSearch is false", ->
         atom.config.set('find-and-replace.scrollToResultOnLiveSearch', false)
-        editor.setHeight(3)
+        editorView.setHeight(3)
         editor.moveToTop()
-        originalScrollPosition = editor.getScrollTop()
+        originalScrollPosition = editorView.getScrollTop()
         findView.findEditor.setText 'Array'
         advance()
-        expect(editor.getScrollTop()).toEqual originalScrollPosition
+        expect(editorView.getScrollTop()).toEqual originalScrollPosition
         expect(editor.getSelectedBufferRange()).toEqual []
         expect(findView.findEditor).toHaveFocus()


Update the new find-all specs to use atom.commands.dispatch

--- spec/find-view-spec.coffee
+++ spec/find-view-spec.coffee
@@ -466,13 +466,13 @@

     it "selects all matches when 'find-and-replace:find-all' is triggered and correctly focuses the editor", ->
       expect(findView).toHaveFocus()
-      findView.findEditor.trigger('find-and-replace:find-all')
+      atom.commands.dispatch findView.findEditor.element, 'find-and-replace:find-all'
       expect(editor.getSelectedBufferRanges()).toEqual [[[1, 27], [1, 22]], [[2, 8], [2, 13]], [[2, 34], [2, 39]], [[3, 16], [3, 21]], [[4,10], [4, 15]], [[5, 16], [5, 21]]]
       expect(editorView).toHaveFocus()

     it "will re-run search if 'find-and-replace:find-all' is triggered after changing the findEditor's text", ->
       findView.findEditor.setText 'sort'
-      findView.findEditor.trigger('find-and-replace:find-all')
+      atom.commands.dispatch findView.findEditor.element, 'find-and-replace:find-all'
       expect(editor.getSelectedBufferRanges()).toEqual [[[0, 9], [0, 13]], [[1, 6], [1, 10]],[[8, 11], [8, 15]], [[8, 43], [8, 47]], [[11, 9], [11, 13]]]

     it "replaces results counter with number of results found when user moves the cursor", ->


Remove old Notifications test

Remove old Notifications test

--- tests/unit/models/patient-test.coffee
+++ tests/unit/models/patient-test.coffee
@@ -145,19 +145,3 @@

     equal patient.get('conditions.length'), 2
     equal patient.get('activeConditions.length'), 1
-
-#
-# test 'notification count is correct', ->
-#   store = @store()
-#   patient = null
-#   Ember.run ->
-#     store.findAll('notificationCount')
-#     patient = store.find('patient', 1)
-#   patient.then ->
-#     Ember.RSVP.allSettled([
-#         patient.get('notificationCount'),
-#     ]).then ->
-#       hasNotifications = patient.get('hasNotifications')
-#       notificationCount = patient.get('notificationCount').get("count")
-#       equal hasNotifications, true
-#       equal notificationCount, 7


:white_check_mark: add (broken?) spec for replace with CR (#408).

--- spec/operators-spec.coffee
+++ spec/operators-spec.coffee
@@ -995,6 +995,13 @@
       commandModeInputKeydown('x')
       expect(editor.getText()).toBe 'x2\n34\n\n'

+    it "replaces a single character with a line break", ->
+      keydown('r')
+      # TODO: Is this how you input a CR?
+      commandModeInputKeydown('\n')
+      expect(editor.getText()).toBe '\n2\n34\n\n'
+      expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+
     it "composes properly with motions", ->
       keydown('2')
       keydown('r')


Remove coffeescript's 'instance' variables

--- features/step_definitions/steps.coffee
+++ features/step_definitions/steps.coffee
@@ -23,9 +23,8 @@
     callback()

   this.Given /^I dress the following JSON document with (.*?):$/, (type, doc, callback) ->
-    json = JSON.parse(doc)
-
     try
+      json = JSON.parse(doc)
       result = system.fetch(type).dress(json)
     catch e
       result = e
@@ -34,9 +33,9 @@

   this.Given /^I validate the following JSON data against (.*?)$/, (type, json, callback) ->
     type = system.fetch(type)
-    json = JSON.parse(json)

     try
+      json = JSON.parse(json)
       result = types.dress(json)
     catch e
       result = e


Look for author.profile.name as well

Look for author.profile.name as well

--- collections/post.coffee
+++ collections/post.coffee
@@ -60,7 +60,10 @@
     author = @author()

     if author
-      if author.profile and author.profile.firstName and author.profile.lastName
+      if author.profile.name
+        return author.profile.name
+
+      else if author.profile and author.profile.firstName and author.profile.lastName
         return "#{author.profile.firstName} #{author.profile.lastName}"

       else if author.profile and author.profile.twitter


registry: shells no longer provide a SummaryView

--- coffee/src/shells/registry.coffee
+++ coffee/src/shells/registry.coffee
@@ -27,7 +27,7 @@
         class shellModule[key] extends value

     # validate shellModule's Model and View properties
-    class_properties = [ 'Model', 'ContentView', 'SummaryView', 'RemixView']
+    class_properties = [ 'Model', 'ContentView', 'RemixView']
     _.each class_properties, (property) ->
       unless _.isFunction shellModule[property]
         TypeError property, 'class'


mouse.isDown logic was broken in the same way as keyboard.isDown

--- coffeescript/love.js/mouse.coffee
+++ coffeescript/love.js/mouse.coffee
@@ -54,11 +54,11 @@
     @y

   isDown: (button, others...) =>
-    if @buttonsDown[button]
-      return true
+    if !@buttonsDown[button]
+      return false
     else
       if others.length == 0
-        return false
+        return true
       else
         return @isDown(others...)


fix amounts

fix amounts

--- commands/twitter.coffee
+++ commands/twitter.coffee
@@ -32,8 +32,10 @@
     }, respond

 searchByUser = (user, plural, n, respond) ->
-    if not n
-        n = if not plural then 1 else n_tweets
+    if not n?
+        n = if plural then n_tweets else 1
+    else if isNaN n
+        n = 1
     twitterSearch {
         q   : "from:#{user.replace('@', '')}"
         rpp : n or 1


don't start server unnecesarily

don't start server unnecesarily

--- lib/connection/local.coffee
+++ lib/connection/local.coffee
@@ -11,10 +11,16 @@
 module.exports =
   server: server

+  provider: ->
+    switch atom.config.get 'julia-client.juliaOptions.bootMode'
+      when 'Server' then server
+      when 'Cycler' then cycler
+      when 'Basic' then basic
+
   activate: ->
     paths.getVersion()
       .then ->
-        server.start paths.jlpath(), client.clargs()
+        @provider().start? paths.jlpath(), client.clargs()
       .catch ->

   monitorExit: (proc) ->
@@ -71,8 +77,4 @@
     proc

   spawnJulia: (path, args) ->
-    provider = switch atom.config.get 'julia-client.juliaOptions.bootMode'
-      when 'Server' then server
-      when 'Cycler' then cycler
-      when 'Basic' then basic
-    provider.get path, args
+    @provider().get path, args


Add some ES6 shims

Add some ES6 shims

--- src/5.compatibility.coffee
+++ src/5.compatibility.coffee
@@ -9,6 +9,18 @@
     Number.isInteger = (x) ->
         typeof x == "number" and isFinite(x) and x > -9007199254740992 and
                 x < 9007199254740992 and Math.floor(x) == x
+# This is in ECMAScript 6. Only Firefox and Chrome natively support this.
+if Number.isFinite == undefined
+    Number.isFinite = (x) -> typeof x == "number" and isFinite(x)
+# This is in ECMAScript 6. Only Firefox and Chrome natively support this.
+if Number.isNaN == undefined
+    Number.isNaN = (x) -> typeof x == "number" and isNaN(x)
+# This is in ECMAScript 6. Only Firefox and Chrome natively support this.
+if Number.parseInt == undefined
+    Number.parseInt = parseInt
+# This is in ECMAScript 6. Only Firefox and Chrome natively support this.
+if Number.parseFloat == undefined
+    Number.parseFloat = parseFloat
 # This is in ECMAScript 6. Only Firefox natively supports this.
 if String::startsWith == undefined
     String::startsWith = (s) -> @indexOf(s) == 0
@@ -18,7 +30,7 @@
 # This is in ECMAScript 6. Only Firefox natively supports this.
 if String::contains == undefined
     String::contains = (s) -> @indexOf(s) != -1
-# This is in ECMAScript 6. Only Firefox and Chrome natively supports this.
+# This is in ECMAScript 6. Only Firefox and Chrome natively support this.
 if Object.is == undefined
     Object.is = (a, b) ->
         if typeof a == "number" and typeof b == "number"
@@ -63,6 +75,9 @@
                 NaN
         else
             NaN
+# This is in ECMAScript 6. Only Firefox natively supports this.
+if Math.trunc == undefined
+    Math.trunc = (x) -> if x < 0 then Math.ceil(x) else Math.floor(x)
 # Only IE natively supports this.
 if global.setImmediate == undefined
     global.setImmediate = (callback, args) -> setTimeout(callback, 0, args)


Prevent page change in locked direction

Prevent page change in locked direction

When using direction lock on page components, the component will successfully prevent you from dragging content in a locked direction, but if you are dragging your finger in the locked direction faster than the velocity threshold, when you release the page will still change to the next or previous page in the locked direction. This if statement makes sure that if that happens we just keep the user on the original page.

--- framer/Components/PageComponent.coffee
+++ framer/Components/PageComponent.coffee
@@ -152,7 +152,9 @@

 		# See if we meet the minimum velocity to scroll to the next page. If not we snap
 		# to the layer closest to the scroll point.
-		if Math.max(Math.abs(velocity.x), Math.abs(velocity.y)) < @velocityThreshold
+		if Math.max(Math.abs(velocity.x), Math.abs(velocity.y)) < @velocityThreshold or
+			(@content.draggable._directionLockEnabledX and (@direction == 'right' or @direction == 'left')) or
+			(@content.draggable._directionLockEnabledY and (@direction == 'down' or @direction == 'up'))
 			# print "velocity"
 			@snapToPage(@closestPage, true, @animationOptions)
 			return


:fire: Remove deprecations from Atom exports

--- exports/atom.coffee
+++ exports/atom.coffee
@@ -2,7 +2,6 @@
 {Point, Range} = TextBuffer
 {File, Directory} = require 'pathwatcher'
 {Emitter, Disposable, CompositeDisposable} = require 'event-kit'
-{includeDeprecatedAPIs, deprecate} = require 'grim'

 module.exports =
   BufferedNodeProcess: require '../src/buffered-node-process'
@@ -23,8 +22,3 @@
 unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE
   module.exports.Task = require '../src/task'
   module.exports.TextEditor = require '../src/text-editor'
-
-if includeDeprecatedAPIs
-  Object.defineProperty module.exports, 'Git', get: ->
-    deprecate "Please require `GitRepository` instead of `Git`: `{GitRepository} = require 'atom'`"
-    module.exports.GitRepository


Add error handling in BufferToCanvas

Add error handling in BufferToCanvas

--- components/BufferToCanvas-node.coffee
+++ components/BufferToCanvas-node.coffee
@@ -15,6 +15,8 @@
     description: 'An image buffer'
   c.outPorts.add 'canvas',
     datatype: 'object'
+  c.outPorts.add 'error',
+    datatype: 'object'

   noflo.helpers.WirePattern c,
     in: ['buffer']
@@ -24,6 +26,9 @@
   , (payload, groups, out, callback) ->
     image = new Canvas.Image
     image.src = payload
+    unless image.width > 0 and image.height > 0
+      err = new Error "Failed to convert a buffer to a canvas"
+      return callback err
     # Create a host canvas and draw on it
     canvas = new Canvas(image.width, image.height)
     ctx = canvas.getContext '2d'


'not' is better coffeescript than '!'

'not' is better coffeescript than '!'

--- lib/motions/general-motions.coffee
+++ lib/motions/general-motions.coffee
@@ -141,7 +141,7 @@

   moveCursor: (cursor, count=1) ->
     _.times count, =>
-      cursor.moveLeft() unless cursor.isAtBeginningOfLine() and !atom.config.get('vim-mode.wrapLeftRightMotion')
+      cursor.moveLeft() unless cursor.isAtBeginningOfLine() and not atom.config.get('vim-mode.wrapLeftRightMotion')
       @ensureCursorIsWithinLine(cursor)

 class MoveRight extends Motion


Fix bug

Fix bug

--- index.cjsx
+++ index.cjsx
@@ -679,7 +679,7 @@
       angle = angle + 90

     render: ->
-

+

         <link rel="stylesheet" href={join(relative(ROOT, __dirname), 'assets', 'prophet.css')} />
         <ProphetPanel
           sortieHp={@state.sortieHp}
@@ -729,7 +729,7 @@
       @setState
         prophetCondShow: !prophetCondShow
     render: ->
-

+

         <Grid>
           <Col xs={6}>
             <Button bsStyle={if @state.enableProphetDamaged then 'success' else 'danger'} onClick={@handleSetProphetDamaged} style={width: '100%'}>


Upgrade to electron 0.35

Upgrade to electron 0.35

--- index.cjsx
+++ index.cjsx
@@ -21,7 +21,7 @@
     y: config.get 'poi.window.y', 0
     width: 850
     height: 650
-  window.battleDetailWindow.loadUrl "file://#{__dirname}/views.html"
+  window.battleDetailWindow.loadURL "file://#{__dirname}/views.html"
   if process.env.DEBUG?
     window.battleDetailWindow.openDevTools
       detach: true


change star icon

change star icon

--- index.cjsx
+++ index.cjsx
@@ -581,7 +581,7 @@
                     {
                       if @state.prophetCondShow

-                          <FontAwesome key={1} name='star-o' />{@state.shipCond[i]}
+                          <FontAwesome key={1} name='star' />{@state.shipCond[i]}

                     }

@@ -620,11 +620,11 @@
           {
             if @state.getShip? && @state.enemyInfo?
               <Alert>
-                __("Admiral") + " #{@state.getShip.api_ship_type}「#{@state.getShip.api_ship_name}」" + __("Join fleet")
+                {__("Admiral") + " #{@state.getShip.api_ship_type}「#{@state.getShip.api_ship_name}」" + __("Join fleet")}
               </Alert>
             else if @state.enemyFormation != 0
               <Alert>
-                __("Admiral") + " 「#{formation[@state.enemyFormation]}」「#{intercept[@state.enemyIntercept]} | #{@state.result}」"
+                {__("Admiral") + " 「#{formation[@state.enemyFormation]}」「#{intercept[@state.enemyIntercept]} | #{@state.result}」"}
               </Alert>
           }

@@ -654,11 +654,11 @@
                     Lv. {@state.shipLv[i]} - {tmpName}
                     {
                       if @state.prophetCondShow
-
-                          <FontAwesome key={1} name='star-o' />{@state.shipCond[i]}
+                        console.log @state.prophetCondShow
+
+                          <FontAwesome key={1} name='star' />{@state.shipCond[i]}

                     }
-                    <FontAwesome key={1} name='star-o' />{@state.shipCond[i]}

                   list.push <ProgressBar bsStyle={getHpStyle @state.nowHp[i] / @state.maxHp[i] * 100} now={@state.nowHp[i] / @state.maxHp[i] * 100} label={if @state.damageHp[i] > 0 then "#{@state.nowHp[i]} / #{@state.maxHp[i]} (-#{@state.damageHp[i]})" else "#{@state.nowHp[i]} / #{@state.maxHp[i]}"} />
                 if @state.shipLv[i + 6] == -1
@@ -677,11 +677,11 @@
           {
             if @state.getShip? && @state.enemyInfo?
               <Alert>
-                __("Admiral") + " #{@state.getShip.api_ship_type}「#{@state.getShip.api_ship_name}」" + __("Join fleet")
+                {__("Admiral") + " #{@state.getShip.api_ship_type}「#{@state.getShip.api_ship_name}」" + __("Join fleet")}
               </Alert>
             else if @state.enemyFormation != 0
               <Alert>
-                __("Admiral") + " 「#{formation[@state.enemyFormation]}」「#{intercept[@state.enemyIntercept]} | #{@state.result}」"
+                {__("Admiral") + " 「#{formation[@state.enemyFormation]}」「#{intercept[@state.enemyIntercept]} | #{@state.result}」"}
               </Alert>
           }


Change if statement to one liner just because.

--- ostest.coffee
+++ ostest.coffee
@@ -47,8 +47,7 @@
   portIndex = 0
   port = ports[portIndex]

-  if host == ''
-    return
+  if host == '' then return

   hasFoundPort = () ->
     return foundPort or numberOfPortsChecked is (ports.length - 1)


Use object to dedupe languages

Use object to dedupe languages

--- src/keyboard-layout.coffee
+++ src/keyboard-layout.coffee
@@ -15,14 +15,10 @@
   # >1 layout that matches a given language (i.e. Japanese probably has Hiragana
   # and Katakana, both would correspond to the language "ja"), so we need to
   # dedupe this list.
-  rawList = observer.getInstalledKeyboardLanguages()
-
-  ret = []
-  for item in rawList
-    continue if ret.indexOf(item) >= 0
-    ret.push(item)
-
-  ret
+  languages = {}
+  for language in observer.getInstalledKeyboardLanguages()
+    languages[language] = true
+  Object.keys(languages)

 onDidChangeCurrentKeyboardLayout = (callback) ->
   emitter.on 'did-change-current-keyboard-layout', callback


Fix local var name

Fix local var name

--- lib/suggestion-list-element.coffee
+++ lib/suggestion-list-element.coffee
@@ -97,13 +97,12 @@
     @ol.className = 'list-group'

   calculateMaxListHeight: ->
-    maxItems = atom.config.get('autocomplete-plus.maxVisibleSuggestions')
+    maxVisibleItems = atom.config.get('autocomplete-plus.maxVisibleSuggestions')
     li = document.createElement('li')
     li.textContent = 'test'
     @ol.appendChild(li)
     itemHeight = li.offsetHeight
-    console.log maxItems, itemHeight
-    @ol.style['max-height'] = "#{maxItems * itemHeight}px"
+    @ol.style['max-height'] = "#{maxVisibleItems * itemHeight}px"
     li.remove()

   renderItems: ->


Client: Activity: DesktopNotifications: Added settings data to return object

--- client/activity/lib/flux/channelnotificationsettings/actions/channel.coffee
+++ client/activity/lib/flux/channelnotificationsettings/actions/channel.coffee
@@ -42,7 +42,7 @@

       dispatch LOAD_CHANNEL_NOTIFICATION_SETTINGS_SUCCESS, { channelId, channelNotificationSettings }

-      resolve { channelId }
+      resolve { channelId, settings: channelNotificationSettings }

 updateSettings = (channelId, channelNotificationSettings) ->


Proper default for billing address in credit card

--- src/script/payment-group/credit-card/credit-card-vm.coffee
+++ src/script/payment-group/credit-card/credit-card-vm.coffee
@@ -38,7 +38,10 @@
     @oldFlag = ko.observable()
     @isUsingNewCard = ko.observable()
     @cardSafetyCodeHasFocus = ko.observable()
-    @sameBillingAddress = ko.observable(not (vtexjs.checkout.orderForm.giftRegistryData?.addressId is vtexjs.checkout.orderForm.shippingData?.address?.addressId))
+    giftRegistryAddressId = vtexjs.checkout.orderForm.giftRegistryData?.addressId
+    shippingAddressId = vtexjs.checkout.orderForm.shippingData?.address?.addressId
+    shippingToGift = giftRegistryAddressId? and giftRegistryAddressId is shippingAddressId
+    @sameBillingAddress = ko.observable(not shippingToGift)
     @isCreditCardCustom = ko.observable(@paymentGroup.isCustom())
     @requiredProperties = [
       "cardNumber"


* [bug] vOwnedPermName is not defined

* [bug] vOwnedPermName is not defined

--- src/mixins/has-perm.coffee
+++ src/mixins/has-perm.coffee
@@ -16,7 +16,7 @@
   if isArray(aPerms)
     result = match aPermName, aPerms
     if aContext
-      vOwnedPermName += '.owned'
+      vOwnedPermName = aPermName + '.owned'
       aContext.owned = true if match vOwnedPermName, aPerms
       if !result and aContext.owned
         aContext['owned'] = true


Extract pieces into private methods

Extract pieces into private methods

--- app/assets/javascripts/backbone/views/product_category_view.js.coffee
+++ app/assets/javascripts/backbone/views/product_category_view.js.coffee
@@ -31,11 +31,7 @@
   open: (options = {}) ->
     return `when`.resolve(@folderView) if @isOpen()
     @model.loadProducts().then (products) =>
-      @folderView ?= new UIDemo.Views.ProductsView
-        collection: products
-        category: @model
-      @folderView.render()
-
+      @_createFolderView(products)
       @parent.activateFolder(this).then =>
         `when`.all([@transitionAddClass('active'), @folderView.open()]).then =>
           @trigger('opened', this) unless options.silent?
@@ -48,7 +44,7 @@
     return `when`.resolve() unless @isOpen()
     @folderView.close().then =>
       @transitionRemoveClass('active').then =>
-        @folderView.remove()
+        @_destroyFolderView()
         @trigger('closed', this) unless options.silent?

   toggleFolder: (event) ->
@@ -61,3 +57,16 @@
         Backbone.history.navigate @categoryUrl()
     false

+  _createFolderView: (products) ->
+    return @folderView if @folderView?
+    @folderView = new UIDemo.Views.ProductsView
+      collection: products
+      category: @model
+    @folderView.render()
+
+  _destroyFolderView: ->
+    @folderView.remove()
+    delete @['folderView']
+
+
+


comment out question saving on new question answer choice creation

--- app/assets/javascripts/hanuman/components/answer-choice-row.js.coffee
+++ app/assets/javascripts/hanuman/components/answer-choice-row.js.coffee
@@ -21,31 +21,38 @@

   actions:
     save: ->
+      @get('model').save().then(
+        =>
+          @set('model',null)
+          @send('toggleForm')
+        , ->
+          console.log('failed')
+      )
       # trying to handle situation where user save answerchoice before saving question
-      answerChoice = @get('model')
-      question = answerChoice.get('question')
-      controller = @
-      if question.get('isNew')
-        question.save().then (question) ->
-          console.log "saved"
-          answerChoice.set "question_id", question.get('id')
-          answerChoice.save().then(
-            =>
-              controller.set('model', null)
-              controller.send('toggleForm')
-            ,->
-              console.log('failed')
-          )
-        , (response) ->
-          console.log "failed"
-      else
-        answerChoice.save().then(
-          =>
-            @set('model', null)
-            @send('toggleForm')
-          ,->
-            console.log('failed')
-        )
+      # answerChoice = @get('model')
+      # question = answerChoice.get('question')
+      # controller = @
+      # if question.get('isNew')
+      #   question.save().then (question) ->
+      #     console.log "saved"
+      #     answerChoice.set "question_id", question.get('id')
+      #     answerChoice.save().then(
+      #       =>
+      #         controller.set('model', null)
+      #         controller.send('toggleForm')
+      #       ,->
+      #         console.log('failed')
+      #     )
+      #   , (response) ->
+      #     console.log "failed"
+      # else
+      #   answerChoice.save().then(
+      #     =>
+      #       @set('model', null)
+      #       @send('toggleForm')
+      #     ,->
+      #       console.log('failed')
+      #   )

     toggleForm: ->
       this.toggleProperty('isEditingAnswerChoice')


Cleanup.

Cleanup.

--- init.coffee
+++ init.coffee
@@ -171,49 +171,16 @@
 if opts._order.length is 0
 	opts.help = true

-#console.log("# opts:", opts); // debug
-
-#
-#var optimist = require('optimist');
-#var argv = optimist
-#	.alias('help', 'h')
-#	.alias('info', 'i')
-#	.alias('tables', 't')
-#	.alias('unify-validate', 'l')
-#	.alias('unify-export', 'u')
-#	.alias('optimize', 'o')
-#	.alias('optimize', 'O')
-#	.alias('test-monsters', 'm')
-#	.alias('drop', 'd')
-#	.usage('Usage: $0 <commands>')
-#	.describe('help', 'Show this text')
-#	.describe('info', 'Show current revision and status')
-#	.describe('tables', 'Migrate tables to the last revision')
-#	.describe('unify-validate', 'Validate unify files')
-#	.describe('unify-export', 'Parse unify files and push them to database')
-#	.describe('optimize', 'Optimize tables')
-#	.describe('test-monsters', 'Insert test monsters')
-#	.describe('drop', 'Drop all tables and set revision to -1')
-#	.boolean(['help','info','tables','unify-validate','unify-export','optimize','test-monsters','drop'])
-#	.argv;
-#
-#// [--database] [--tables] [--unify-validate] [--unify-export] [--optimize] [--test-monsters] [--drop]
-#
-#if (argv.help === true)
-# {
-#	optimist.showHelp();
-# }
-
-#async.parallel [
-#		(callback) ->
-#			console.log(1)
-#			callback(null)
-#		,
-#		(callback) ->
-#			console.log(2)
-#			callback(null)
-#	],
-#	(err, res) -> console.log(err, res)
+# Some other schema:
+#  'help', 'h', 'Show this text'
+#  'info', 'i', 'Show current revision and status'
+#  'tables', 't', 'Migrate tables to the last revision'
+#  'unify-validate', 'l', 'Validate unify files'
+#  'unify-export', 'u', 'Parse unify files and push them to database'
+#  'optimize', 'o', 'O', 'Optimize tables'
+#  'test-monsters', 'm', 'Insert test monsters'
+#  'drop', 'd', 'Drop all tables and set revision to -1'
+# [--database] [--tables] [--unify-validate] [--unify-export] [--optimize] [--test-monsters] [--drop]

 #if opts.create_database?
 #	func_count = 0


Avoid error if insert-date called inappropriately

--- init.coffee
+++ init.coffee
@@ -10,5 +10,5 @@

 atom.commands.add 'atom-text-editor',
   'user:insert-date': (event) ->
-    editor = @getModel()
+    return unless editor = @getModel()
     editor.insertText(new Date().toLocaleString())


Use res.send, not next

Use res.send, not next

--- services/web/app/coffee/Features/Analytics/AnalyticsController.coffee
+++ services/web/app/coffee/Features/Analytics/AnalyticsController.coffee
@@ -8,7 +8,7 @@
 			if error?
 				if error instanceof Errors.ServiceNotConfiguredError
 					# ignore, no-op
-					return next(204)
+					return res.send(204)
 				else
 					return next(error)
 			res.send 204


Wrap in IIFE.

Wrap in IIFE.

--- packages/jq_tag_strip/lib/jq_tag_strip.coffee
+++ packages/jq_tag_strip/lib/jq_tag_strip.coffee
@@ -1,48 +1,48 @@
+( ($) ->
+  $.fn.stripTags = (options) ->
+    options = options || {}
+    validOptions = [ 'whitelist', 'blacklist', 'mode' ]
+    validModes = [ 'unwrap', 'remove' ]
+    whitelist = options.whitelist || []
+    blacklist = options.blacklist || []
+    mode = options.mode || validModes[0]

-$.fn.extend( stripTags: (options) ->
-  options = options || {}
-  validOptions = [ 'whitelist', 'blacklist', 'mode' ]
-  validModes = [ 'unwrap', 'remove' ]
-  whitelist = options.whitelist || []
-  blacklist = options.blacklist || []
-  mode = options.mode || validModes[0]
+    if whitelist.length > 0 && blacklist.length > 0
+      throw TypeError("Only one option of whitelist, blacklist can be set")

-  if whitelist.length > 0 && blacklist.length > 0
-    throw TypeError("Only one option of whitelist, blacklist can be set")
+    if validModes.indexOf(mode) < 0
+      throw TypeError("invalid mode '" + mode + "'")
+    for optionKey of options
+      if validOptions.indexOf(optionKey) < 0
+        throw TypeError("invalid option '" + optionKey + "'")

-  if validModes.indexOf(mode) < 0
-    throw TypeError("invalid mode '" + mode + "'")
-  for optionKey of options
-    if validOptions.indexOf(optionKey) < 0
-      throw TypeError("invalid option '" + optionKey + "'")
+    subject = $('<div />').append(this.get())

-  subject = $('<div />').append(this.get())
+    filterNode = (node) ->
+      if mode == 'remove'
+        node.remove()
+      else
+        node.replaceWith(node.html())

-  filterNode = (node) ->
-    if mode == 'remove'
-      node.remove()
-    else
-      node.replaceWith(node.html())
+    filterableNodes = ->
+      if whitelist.length
+        # start with all nodes
+        currentScope = subject.find('*')
+        # exlude whitelisted nodes
+        for selector in whitelist
+          currentScope = currentScope.not(selector)
+      else if blacklist.length
+        # start with nothing
+        currentScope = $()
+        for selector in blacklist
+          currentScope = currentScope.add( subject.find(selector) )
+      else
+        # filter all nodes
+        currentScope = subject.find('*')
+      currentScope

-  filterableNodes = ->
-    if whitelist.length
-      # start with all nodes
-      currentScope = subject.find('*')
-      # exlude whitelisted nodes
-      for selector in whitelist
-        currentScope = currentScope.not(selector)
-    else if blacklist.length
-      # start with nothing
-      currentScope = $()
-      for selector in blacklist
-        currentScope = currentScope.add( subject.find(selector) )
-    else
-      # filter all nodes
-      currentScope = subject.find('*')
-    currentScope
+    while filterableNodes().length
+      filterNode(filterableNodes().first())

-  while filterableNodes().length
-    filterNode(filterableNodes().first())
-
-  subject
-)
+    return subject
+)(jQuery)


Remove extra space

Remove extra space

--- vendor/assets/javascripts/brainstem/storage-manager.coffee
+++ vendor/assets/javascripts/brainstem/storage-manager.coffee
@@ -50,7 +50,6 @@
   setErrorInterceptor: (interceptor) =>
     @errorInterceptor = interceptor || (handler, modelOrCollection, options, jqXHR, requestParams) -> handler?(jqXHR)

-
   loadModel: =>
     @dataLoader.loadModel.apply(@dataLoader, arguments)


File updated notification

File updated notification

--- app/mixins/socket.coffee
+++ app/mixins/socket.coffee
@@ -25,6 +25,10 @@
         .then (project) ->
           project.set "lastFile", file

+    file_updated: (data) ->
+      Notify.info "File updated"
+      file = @store.push "file", @store.normalize "file", data
+
     project_new: (data) ->
       Notify.info "New project added"
       @store.push "project", @store.normalize "project", data


finder getData of undefined error fix

finder getData of undefined error fix

--- components/list/KDListView.coffee
+++ components/list/KDListView.coffee
@@ -33,7 +33,7 @@
     itemInstance = @itemClass viewOptions, itemData
     @addItemView itemInstance, index, animation

-    itemInstance
+    return itemInstance

   addHiddenItem:(item, index, animation)->
     @_addItemHelper item, {


Client: Activity: Passed input focus functionality as a second argument to setState method

--- client/activity/lib/components/channelparticipantavatars/index.coffee
+++ client/activity/lib/components/channelparticipantavatars/index.coffee
@@ -107,9 +107,9 @@
     if @state.addNewParticipantMode is yes
     then @setState addNewParticipantMode: no
     else
-      @setState addNewParticipantMode: yes
-      textInput = React.findDOMNode @refs.textInput
-      textInput.focus()
+      @setState addNewParticipantMode: yes, ->
+        textInput = React.findDOMNode @refs.textInput
+        textInput.focus()

   onShowMoreParticipantButtonClick: (event) ->


apps logic wip

apps logic wip

--- client/landing/app/Applications/StartTab.kdapplication/AppView.coffee
+++ client/landing/app/Applications/StartTab.kdapplication/AppView.coffee
@@ -18,9 +18,9 @@
       tagName : "span"
       partial : "fetching apps..."

-    kac = @getSingleton("kodingAppsController")
-
-    kac.fetchApps (apps)=>
+    kodingAppsController = @getSingleton("kodingAppsController")
+
+    kodingAppsController.fetchApps (apps)=>

       temp.destroy()
       for app, manifest of apps
@@ -29,7 +29,7 @@
             tagName : "p"
             partial : "#{manifest.name} v#{manifest.version}"
             click   : =>
-              kac.getApp manifest.name, (appScript)=>
+              kodingAppsController.getApp manifest.name, (appScript)=>
                 mainView = @getSingleton('mainView')
                 mainView.mainTabView.showPaneByView
                   name         : manifest.name
@@ -37,10 +37,12 @@
                   type         : "application"
                 , (appView = new KDView)

-                appInstance = eval appScript
-                appInstance()
+                eval appScript
+
+                return appView
+
                 # appInstance = Function(appScript)
-                # do (appView)=> appInstance.call @, appView
+                # do (appView)=> appInstance.call null, appView

 # appView.addSubView a = new KDView
@@ -48,7 +50,9 @@
 # appView.$().css "background-color", "pink"
 # a.$().css backgroundColor : "red", color : "white"
 # return a
-
+
+# do (appView)->
+#   appView.addSubview new AceView {}, FSHelper.createFileFromPath "localfile:/Untitled.txt"


Exception with better message

Exception with better message

--- SwaggerImporter.coffee
+++ SwaggerImporter.coffee
@@ -129,7 +129,7 @@
         valid = tv4.validate swaggerCollection, JSON.parse(schema)

         if not valid
-          throw new Error "Invalid Swagger file (not a valid JSON or invalid schema)"
+          throw new Error "Invalid Swagger file (not a valid JSON or invalid schema or not Swagger 2.0)"

         if swaggerCollection


Revert "Make development environment match '172.' as well as localhost (#5475)" (#5493)

This reverts commit 2cda51e5e83cf6fa8bab819b2b94520ae964d208.

--- app/core/application.coffee
+++ app/core/application.coffee
@@ -50,7 +50,7 @@
 #    if features.codePlay and me.isAnonymous()
 #      document.location.href = '//lenovogamestate.com/login/'
     Router = require('core/Router')
-    @isProduction = -> document.location.href.search('https?://localhost') is -1 and document.location.href.search('172.') is not 0
+    @isProduction = -> document.location.href.search('https?://localhost') is -1
     Vue.config.devtools = not @isProduction()

     # propagate changes from global 'me' User to 'me' vuex module


armor against broken configs

armor against broken configs

--- lib/kernel-manager.coffee
+++ lib/kernel-manager.coffee
@@ -57,7 +57,7 @@
         return kernels

     getTrueLanguage: (language) ->
-        languageMappings = JSON.parse atom.config.get('hydrogen.languageMappings')
+        languageMappings = @getLanguageMappings()
         matchingLanguageKeys = _.filter languageMappings, (trueLanguage, languageKey) ->
             return languageKey.toLowerCase() == language.toLowerCase()

@@ -65,6 +65,13 @@
             return matchingLanguageKeys[0]
         else
             return language
+
+    getLanguageMappings: ->
+        try
+            languageMappings = JSON.parse atom.config.get('hydrogen.languageMappings')
+        catch error
+            console.error error
+            languageMappings = {}

     getKernelInfoForLanguage: (language) ->
         kernels = @getAvailableKernels()


add test for zip file contents, test zip file with unzip if exists

--- test/create_zip.coffee
+++ test/create_zip.coffee
@@ -3,8 +3,9 @@
 path = require 'path'
 async = require 'async'
 zip = require '../'
+exec = require('child_process').exec

-zipFile = path.resolve __dirname, 'tmp', 'output.zip'
+zipFilePath = path.resolve __dirname, 'tmp', 'output.zip'
 files = ['00_create.coffee', 'runner.coffee', 'zz_cleanup.coffee']

 files = files.map (f) ->
@@ -13,10 +14,9 @@
 test 'queues files for compression', (t) ->
   t.plan 4

-  zip.setOutput zipFile
+  zip.setOutput zipFilePath

   stack = []
-
   files.forEach (file) ->
     do (file) ->
       stack.push (cb) ->
@@ -29,11 +29,22 @@
     t.end()

 test 'creates zip file', (t) ->
-  t.false fs.existsSync(zipFile), 'zip file is not created'
+  t.false fs.existsSync(zipFilePath), 'zip file is not created'

   zip.compress (err, bytes) ->
     t.error err, 'zip is created'
-    t.true  fs.existsSync(zipFile), 'zip file is created'
-    t.true (bytes > 0)
+    t.true fs.existsSync(zipFilePath), 'zip file is created'
+    t.true (bytes > 0), 'file has contents'
+    t.end()

-    t.end()+test 'zip file is valid', (t) ->
+  exec 'which unzip', (err, stdout, stderr) ->
+    if not err
+      t.pass 'unzip command exists'
+
+      exec "unzip -l #{zipFilePath}", (err, stdout, stderr) ->
+        t.true stdout.match('3 files'), 'zip contains 3 files'
+        files.forEach (file) ->
+          [i..., filename] = file.split path.sep
+          t.true stdout.match(filename), "zip contains #{filename}"
+        t.end()


file: ensure parent dir mode is not changed

--- test/file/index.coffee
+++ test/file/index.coffee
@@ -228,17 +228,30 @@
   describe 'ownerships and permissions', ->

     they 'set permission', (ssh) ->
-      nikita.file
-        ssh: ssh
+      nikita
+        ssh: ssh
+      .file
         target: "#{scratch}/a_file"
         content: 'ok'
         mode: 0o0700
       .file.assert
         target: "#{scratch}/a_file"
         mode: 0o0700
-      .file.assert
-        target: "#{scratch}"
-        mode: [0o0755, 0o0775]
+      .promise()
+
+    they 'does not modify parent', (ssh) ->
+      nikita
+        ssh: ssh
+      .system.mkdir
+        target: "#{scratch}/a_dir"
+        mode: 0o0744
+      .file
+        target: "#{scratch}/a_file"
+        content: 'ok'
+        mode: 0o0700
+      .file.assert
+        target: "#{scratch}/a_dir"
+        mode: 0o0744
       .promise()

     they 'change permission', (ssh) ->


Fix player-code-immutable vector methods with new Esper

--- app/lib/world/vector.coffee
+++ app/lib/world/vector.coffee
@@ -5,14 +5,14 @@
   for name in ['add', 'subtract', 'multiply', 'divide', 'limit', 'normalize', 'rotate']
     do (name) ->
       Vector[name] = (a, b, useZ) ->
-        a.copy()["#{name}Self"](b, useZ)
+        a.copy()[name](b, useZ)
   for name in ['magnitude', 'heading', 'distance', 'dot', 'equals', 'copy', 'distanceSquared']
     do (name) ->
       Vector[name] = (a, b, useZ) ->
         a[name](b, useZ)

   isVector: true
-  apiProperties: ['x', 'y', 'z', 'magnitude', 'heading', 'distance', 'dot', 'equals', 'copy', 'distanceSquared', 'rotate', 'add', 'subtract', 'multiply', 'divide', 'limit', 'normalize', 'rotate']
+  apiProperties: ['x', 'y', 'z', 'magnitude', 'heading', 'distance', 'dot', 'equals', 'copy', 'distanceSquared', 'add', 'subtract', 'multiply', 'divide', 'limit', 'normalize', 'rotate']

   constructor: (x=0, y=0, z=0) ->
     return new Vector x, y, z unless @ instanceof Vector
@@ -24,68 +24,67 @@

   # Mutating methods:

-  normalizeSelf: (useZ) ->
+  normalize: (useZ) ->
     m = @magnitude useZ
-    @divideSelf m, useZ if m > 0
+    @divide m, useZ if m > 0
     @

-  normalize: (useZ) ->
-    # Hack to detect when we are in player code so we can avoid mutation
-    (if @__aetherAPIValue? then @copy() else @).normalizeSelf(useZ)
+  esper_normalize: (useZ) ->
+    @copy().normalize(useZ)

-  limitSelf: (max) ->
+  limit: (max) ->
     if @magnitude() > max
-      @normalizeSelf()
-      @multiplySelf(max)
+      @normalize()
+      @multiply(max)
     else
       @

-  limit: (useZ) ->
-    (if @__aetherAPIValue? then @copy() else @).limitSelf(useZ)
+  esper_limit: (max) ->
+    @copy().limit(max)

-  subtractSelf: (other, useZ) ->
+  subtract: (other, useZ) ->
     @x -= other.x
     @y -= other.y
     @z -= other.z if useZ
     @

-  subtract: (other, useZ) ->
-    (if @__aetherAPIValue? then @copy() else @).subtractSelf(other, useZ)
+  esper_subtract: (other, useZ) ->
+    @copy().subtract(other, useZ)

-  addSelf: (other, useZ) ->
+  add: (other, useZ) ->
     @x += other.x
     @y += other.y
     @z += other.z if useZ
     @

-  add: (other, useZ) ->
-    (if @__aetherAPIValue? then @copy() else @).addSelf(other, useZ)
+  esper_add: (other, useZ) ->
+    @copy().add(other, useZ)

-  divideSelf: (n, useZ) ->
+  divide: (n, useZ) ->
     [@x, @y] = [@x / n, @y / n]
     @z = @z / n if useZ
     @

-  divide: (n, useZ) ->
-    (if @__aetherAPIValue? then @copy() else @).divideSelf(n, useZ)
+  esper_divide: (n, useZ) ->
+    @copy().divide(n, useZ)

-  multiplySelf: (n, useZ) ->
+  multiply: (n, useZ) ->
     [@x, @y] = [@x * n, @y * n]
     @z = @z * n if useZ
     @

-  multiply: (n, useZ) ->
-    (if @__aetherAPIValue? then @copy() else @).multiplySelf(n, useZ)
+  esper_multiply: (n, useZ) ->
+    @copy().multiply(n, useZ)

   # Rotate it around the origin
   # If we ever want to make this also use z: https://en.wikipedia.org/wiki/Axes_conventions
-  rotateSelf: (theta) ->
+  rotate: (theta) ->
     return @ unless theta
     [@x, @y] = [Math.cos(theta) * @x - Math.sin(theta) * @y, Math.sin(theta) * @x + Math.cos(theta) * @y]
     @

-  rotate: (theta) ->
-    (if @__aetherAPIValue? then @copy() else @).rotateSelf(theta)
+  esper_rotate: (theta) ->
+    @copy().rotate(theta)

   # Non-mutating methods:

@@ -127,7 +126,7 @@

   # Not the strict projection, the other isn't converted to a unit vector first.
   projectOnto: (other, useZ) ->
-    other.copy().multiplySelf(@dot(other, useZ), useZ)
+    other.copy().multiply(@dot(other, useZ), useZ)

   isZero: (useZ) ->
     result = @x is 0 and @y is 0


allow s, v, l to be defined as percentage

--- framer/Color.coffee
+++ framer/Color.coffee
@@ -191,12 +191,15 @@

 				h = if isNumeric(color.h) then color.h else 0
 				s = if isNumeric(color.s) then convertToPercentage(color.s) else 1
+				if _.isString(color.s) then s = numberFromString(color.s)

 				if color.hasOwnProperty("v")
 					v = if isNumeric(color.v) then convertToPercentage(color.v) else 0
+					if _.isString(color.v) then v = numberFromString(color.v)
 					rgb = hsvToRgb(h, s, v)
 				else
 					l = if isNumeric(color.l) then convertToPercentage(color.l) else 50
+					if _.isString(color.l) then l = numberFromString(color.l)
 					rgb = hslToRgb(h, s, l)

 				ok = true
@@ -215,6 +218,9 @@
 	b: Math.min(255, Math.max(rgb.b, 0))
 	a: a
 	}
+
+numberFromString = (string) ->
+	return string.match(/\d+/)[0]

 # Conversion Functions
 # RGB to RGB


Drop Utils.arrayToObject

Drop Utils.arrayToObject

--- framer/Utils.coffee
+++ framer/Utils.coffee
@@ -38,14 +38,6 @@

 	return value

-Utils.arrayToObject = (arr) ->
-	obj = {}
-
-	for item in arr
-		obj[item[0]] = item[1]
-
-	obj
-
 Utils.arrayNext = (arr, item) ->
 	arr[arr.indexOf(item) + 1] or _.first arr


Test updating existing ldap index

Test updating existing ldap index

--- test/ldap_index.coffee
+++ test/ldap_index.coffee
@@ -49,3 +49,30 @@
         modified.should.eql 0
         next()

+  it 'update an existing index', (next) ->
+    mecano.ldap_index
+      ldap: client
+      name: 'olcDatabase={2}bdb,cn=config'
+      indexes:
+        aliasedEntryName: 'eq'
+    , (err, modified) ->
+      return next err if err
+      modified.should.eql 1
+      mecano.ldap_index
+        ldap: client
+        name: 'olcDatabase={2}bdb,cn=config'
+        indexes:
+          aliasedEntryName: 'pres,eq'
+      , (err, modified) ->
+        return next err if err
+        modified.should.eql 1
+        mecano.ldap_index
+          ldap: client
+          name: 'olcDatabase={2}bdb,cn=config'
+          indexes:
+            aliasedEntryName: 'pres,eq'
+        , (err, modified) ->
+          return next err if err
+          modified.should.eql 0
+          next()
+


Properly ignore empty hash links - Fix #407 again

--- lib/assets/javascripts/turbolinks.js.coffee
+++ lib/assets/javascripts/turbolinks.js.coffee
@@ -296,7 +296,7 @@
     return @original if @original.constructor is ComponentUrl
     @_parse()

-  withoutHash: -> @href.replace @hash, ''
+  withoutHash: -> @href.replace(@hash, '').replace('#', '')

   # Intention revealing function alias
   withoutHashForIE10compatibility: -> @withoutHash()
@@ -339,8 +339,8 @@
     @origin isnt (new ComponentUrl).origin

   _anchored: ->
-    ((@hash and @withoutHash()) is (current = new ComponentUrl).withoutHash()) or
-      (@href in [current.href, current.href + '#'])
+    (@hash.length > 0 or @href.charAt(@href.length - 1) is '#') and
+      (@withoutHash() is (new ComponentUrl).withoutHash())

   _nonHtml: ->
     @pathname.match(/\.[a-z]+$/g) and not @pathname.match(new RegExp("\\.(?:#{Link.HTML_EXTENSIONS.join('|')})?$", 'g'))


Log when animations are disabled

Log when animations are disabled

--- lib/assets/javascripts/unpoly/motion.coffee
+++ lib/assets/javascripts/unpoly/motion.coffee
@@ -514,9 +514,16 @@
   registerAnimation = (name, animation) ->
     namedAnimations[name] = findAnimationFn(animation)

-  snapshot = ->
+  onBooted = ->
+    # Remember original named animations so we can purge user-defined
+    # animtions during reset.
     defaultNamedAnimations = u.copy(namedAnimations)
     defaultNamedTransitions = u.copy(namedTransitions)
+
+    # Explain to the user why animations aren't working.
+    # E.g. the user might have disabled animations in her OS.
+    unless isEnabled()
+      up.puts 'up.motion', 'Animations are disabled'

   ###**
   Returns whether the given animation option will cause the animation


:bug: Fix issue with latest version that removed the find-result class on markers

--- lib/minimap-find-and-replace-binding.coffee
+++ lib/minimap-find-and-replace-binding.coffee
@@ -9,15 +9,16 @@
     @decorationsByMarkerId = {}
     @subscriptionsByMarkerId = {}

-    @discoverMarkers()
-
     if @fnrAPI?
       @layer = @fnrAPI.resultsMarkerLayerForTextEditor(@editor)
+
       @subscriptions.add @layer.onDidCreateMarker (marker) =>
         @handleCreatedMarker(marker)
     else
       @subscriptions.add @editor.displayBuffer.onDidCreateMarker (marker) =>
         @handleCreatedMarker(marker)
+
+    @discoverMarkers()

   destroy: ->
     sub.dispose() for id,sub of @subscriptionsByMarkerId
@@ -41,11 +42,15 @@
   findAndReplace: -> FindAndReplace ?= atom.packages.getLoadedPackage('find-and-replace').mainModule

   discoverMarkers: ->
-    (@layer ? @editor).findMarkers(class: 'find-result').forEach (marker) =>
-      @createDecoration(marker)
+    if @fnrAPI?
+      @layer.getMarkers().forEach (marker) => @createDecoration(marker)
+    else
+      @editor.findMarkers(class: 'find-result').forEach (marker) =>
+        @createDecoration(marker)

   handleCreatedMarker: (marker) ->
-    @createDecoration(marker) if marker.getProperties()?.class is 'find-result'
+    if @fnrAPI? or marker.getProperties()?.class is 'find-result'
+      @createDecoration(marker)

   createDecoration: (marker) ->
     return unless @findViewIsVisible()


Move positions instead of clearing them in Player#resetLine

--- player.coffee
+++ player.coffee
@@ -35,8 +35,15 @@
       @positions[@positions.length - 1]

     resetLine: ->
-      @current_line.points = []
-      @canvas.remove(@current_line)
+      new_line = @currentLine()
+      lastpos = @lastPos()
+      old_time = lastpos.time
+      for pos in new_line
+        if pos.time > lastpos.time
+          @game.move_player(this, pos.time - old_time, pos.time, lastpos)
+          old_time = pos.time
+        else
+          @unverified_positions.splice(@unverified_positions.indexOf(pos), 1)

     currentLine: ->
       @current_line.points.slice() # Copy array


:lipstick: Abstract out multiplying text

--- lib/tabs-to-spaces.coffee
+++ lib/tabs-to-spaces.coffee
@@ -17,12 +17,21 @@
     editor = atom.workspace.getActiveEditor()
     return unless editor?

-    spaces = Array(atom.config.get('editor.tabLength') + 1).join(' ')
+    @setSpaces(@multiplyText(' ', atom.config.get('editor.tabLength')))
     editor.transact =>
       editor.selectAll()
       editor.splitSelectionsIntoLines()
       @replaceTabsWithSpaces(selection) for selection in editor.getSelections()
     editor.setCursorBufferPosition([0, 0])
+
+  # Creates a string containing `text` concatenated `count` times.
+  #
+  # @private
+  # @param [String] text Text to repeat.
+  # @paarm [Number] count Number of times to repeat.
+  # @return [String] Repeated text.
+  multiplyText: (text, count) ->
+    Array(count + 1).join(text)

   # Replaces all leading tabs with spaces in the given selection.
   #
@@ -31,7 +40,7 @@
   replaceTabsWithSpaces: (selection) ->
     text = selection.getText()
     newText = text.replace /^\t+/, ''
-    indentation = Array(text.length - newText.length + 1).join(spaces)
+    indentation = @multiplyText(spaces, text.length - newText.length)
     selection.insertText(indentation + newText)
     selection.clear()


Improved dropdown positioning

Improved dropdown positioning

--- jquery.mentions-contenteditable.coffee
+++ jquery.mentions-contenteditable.coffee
@@ -47,8 +47,6 @@
             focus: @options.focus
         @options.select = $.proxy(@selectCallback, @)
         @options.focus = $.proxy(@focusCallback, @)
-
-        @marker = $("")[0]

         $.ui.autocomplete.prototype._create.call(@)
         @matcher = new RegExp(@options.matcher + '$')
@@ -83,23 +81,9 @@

             @start = match.index
             @end = match.index + match[0].length
-
-            if @options.showAtCaret
-                range = sel.getRangeAt 0
-                boundary = range.cloneRange()
-                boundary.setStart node, @start
-                boundary.collapse true
-                boundary.insertNode @marker
-                boundary.detach()
-                @options.position.of = @marker
-
+            @_setDropdownPosition node
             value = match[1]
         return $.ui.autocomplete.prototype.search.call(@, value, event)
-
-    close: (event) ->
-        @marker.remove()
-        @element.change()
-        return $.ui.autocomplete.prototype.close.call(@, event)

     _renderItem: (ul, item) ->
         li = $('
')
@@ -108,6 +92,15 @@
             anchor.append("")
         anchor.append(item.value)
         return li.appendTo(ul)
+
+    _setDropdownPosition: (node) ->
+        if @options.showAtCaret
+            boundary = document.createRange()
+            boundary.setStart node, @start
+            boundary.collapse true
+            rect = boundary.getClientRects()[0]
+            @options.position.of = document
+            @options.position.at = "left+#{rect.left} top+#{rect.top+rect.height}"
 )

@@ -129,7 +122,7 @@
             select: @_onSelect,
             source: @options.source,
             delay: @options.delay,
-            showAtCaret: true
+            showAtCaret: @options.showAtCaret
         )
         @_initValue()
         @_initEvents()


Don't reposition show buttons after they have once been shown

--- js-library/app/js/views/buttons.coffee
+++ js-library/app/js/views/buttons.coffee
@@ -31,6 +31,11 @@
     </div>
   """

+  setCoordinates: (top, left) =>
+    return if @position_already_determined
+    @position_already_determined = true
+    super
+
 class Factlink.CreateButton extends Button
   template: """
     <div class="fl-button fl-create-button">


moving genome constants to genome class

moving genome constants to genome class

--- lingsim.coffee
+++ lingsim.coffee
@@ -51,9 +51,12 @@
 Utils = new Utilities()

 class Genome
+  GENOME_SIZE: 6
+  GENOME_SPACE: 4
+
   constructor: (size, space) ->
     @genome = []
-    _.times size, => @genome.push Utils.randomAlpha(space)
+    _.times GENOME_SIZE, => @genome.push Utils.randomAlpha(GENOME_SPACE)

   randomGene: =>
     Utils.randomAlpha(GENOME_SPACE)
@@ -64,9 +67,6 @@

 class Language
-  GENOME_SIZE: 6
-  GENOME_SPACE: 4
-
   constructor: ->
     @genome = new Genome()


`build/05-stations`: entrances using OSM, WIP

--- build/05-stations.coffee
+++ build/05-stations.coffee
@@ -1,9 +1,66 @@
+oboe =			require 'oboe'
+Autocomplete =	require 'vbb-stations-autocomplete'
 path =			require 'path'
 fs =			require 'fs'
 moment =		require 'moment'
 Q =				require 'q'
 csv =			require 'csv-parse'
 ndjson =		require 'ndjson'
+
+
+
+
+
+downloadEntrances = () ->
+	deferred = Q.defer()
+	entrances = []
+
+	parser = oboe
+		url: [
+			'http://overpass.osm.rambler.ru/cgi'
+			'/interpreter?data='
+			'[out:json];'
+			'('
+			'node(around: 10000, 52.4964393, 13.3864079)["highway"="bus_stop"];'
+			'node(around: 10000, 52.4964393, 13.3864079)["railway"="tram_stop"];'
+			'node(around: 10000, 52.4964393, 13.3864079)["railway"="station"];'
+			'node(around: 10000, 52.4964393, 13.3864079)["public_transport"="stop_position"];'
+			');out;'
+		].join ''
+
+	parser.node '!.elements[*]', (node, path) ->
+		return unless node.tags
+		return unless node.tags.name
+		entrances.push
+			name:		node.tags.name
+			latitude:	node.lat
+			longitude:	node.lon
+
+	parser.done () ->
+		deferred.resolve entrances
+	return deferred.promise
+
+
+
+mapEntrances = (stations, entrances) ->
+	deferred = Q.defer()
+	autocomplete = Autocomplete 1
+
+	step = (i) ->
+		if i >= entrances.length then return deferred.resolve stations
+
+		autocomplete.suggest entrances[i].name
+		.then (results) ->
+			station = stations[results[0].id]
+			if station
+				station.entrances ?= []
+				station.entrances.push
+					latitude:	entrances[i].latitude
+					longitude:	entrances[i].longitude
+			step ++i
+
+	step 0
+	return deferred.promise

@@ -119,9 +176,8 @@
 trips = []
 stations = {}

-console.log 'Merging stations.csv & trips-stations.csv into stations.ndjson:'
-
-# read & accumulate data
+console.log 'Merging stations.csv & trips-stations.csv:'
+
 readNdjson 'lines.ndjson', processLine lines
 .then () ->
 	return readNdjson 'trips.ndjson', processTrip trips
@@ -130,8 +186,15 @@
 .then () ->
 	return readCsv 'trips-stations.csv', processTripStation lines, trips, stations

-# pass `stations` in
-.then () ->
+.then () ->
+	console.log 'Done.'
+	console.log 'Downloading & merging entrances from Open Street Map:'
+	return downloadEntrances()
+.then (entrances) ->
+	return mapEntrances stations, entrances
+.then (stations) ->
+	console.log 'Done.'
+	console.log 'Writing into stations.ndjson:'
 	return stations

 # write data


[#22] ちょいコード直した

[#22] ちょいコード直した

--- src/coffee/kazitori.coffee
+++ src/coffee/kazitori.coffee
@@ -203,16 +203,14 @@
       @.history.replaceState({}, document.title, @.root + @.fragment + @.location.search)
     #スタートイベントをディスパッチ
     @._dispatcher.dispatchEvent( new KazitoriEvent( KazitoriEvent.START, @.fragment ))
+
+    override = @.root
     if !@.options.silent
-      override = @.root
       if not @._hasPushState and atRoot
         override = @.root + @.fragment.replace(routeStripper, '')
       else if not atRoot
         override = @.fragment
-      return  @loadURL(override)
-    else
-      #んー
-      return @loadURL(@.fragment)
+    return @loadURL(override)

   #止める


fixed scope errors, headless parser works now with bcv.parse(string).headless()

--- src/core/bcv_parser.coffee
+++ src/core/bcv_parser.coffee
@@ -555,6 +555,10 @@
 	## see: https://github.com/openbibleinfo/Bible-Passage-Reference-Parser/issues/5#issuecomment-29864952

 	headless: ->
+		@set_options
+		    sequence_combination_strategy: "separate"
+		    osis_compaction_strategy: "bc"
+		    case_sensitive: "books"
 		osises = @osis_and_indices()
 		# Do numbers before verses because numbers include a chapter and could change the context for the verses.
 		for type in ["chapters", "numbers", "verses"]
@@ -607,7 +611,7 @@
 	        result.indices[0] += offset
 	        result.indices[1] += offset
 	        result.extra = true
-	        osises.push result unless index_already_exists(osises, result.indices[0])
+	        osises.push result unless @index_already_exists(osises, result.indices[0])
 	    @sort_osises(osises)
 	    @set_options consecutive_combination_strategy: "combine"
 	    osises


avoid error: modifying a collection while iterating

--- proxy.coffee
+++ proxy.coffee
@@ -11,7 +11,7 @@
   (s) -> # the actual log() function
     if (t1 = +new Date) - t > T then t = t1; n = 1 # if last message was too long ago, start counting afresh
     m = if ++n < N then "#{t1 - t0}: #{s}\n" else if n == N then '... logging temporarily suppressed\n'
-    if m then process.stdout?.write? m; for l in log.listeners then l m
+    if m then process.stdout?.write? m; for l in log.listeners[..] then l m
     return

 log.listeners = []


Avoid flicker when changing html/text, fixes #895

--- src/resources/forms/orbeon/builder/resources/cell/label-editor.coffee
+++ src/resources/forms/orbeon/builder/resources/cell/label-editor.coffee
@@ -4,7 +4,7 @@
     currentControl = null
     currentLabel = null
     isLabelHtml = -> currentControl.is('.fb-label-is-html')
-    setLabelHtml = (isHtml) -> currentControl.toggleClass('.fb-label-is-html', isHtml)
+    setLabelHtml = (isHtml) -> currentControl.toggleClass('fb-label-is-html', isHtml)
     labelValue = (value) ->
         valueAccessor = if isLabelHtml() then currentLabel.html else currentLabel.text
         valueAccessor = _.bind(valueAccessor, currentLabel)


cleanup

cleanup

--- src/core/views/base.coffee
+++ src/core/views/base.coffee
@@ -36,7 +36,7 @@
       @decorator = new ViewDecorator @

       # items to load with the view
-      @loadables = new ViewCollection([@decorator])
+      @loadables = new ViewCollection [@decorator]

       compose @, @loadables, ["load", "render", "display", "remove"]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_selector.coffee
+++ src/engine_selector.coffee
@@ -60,7 +60,7 @@
   @returns {Array} An array of the objects found
   ###
   each: (selector, fn) ->
-    self.
+    self.find(se)

   find: (selector) ->
     results = []


:art: minor refactoring

:art: minor refactoring

--- lib/visual-blockwise.coffee
+++ lib/visual-blockwise.coffee
@@ -1,7 +1,8 @@
+_ = require 'underscore-plus'
+{Range} = require 'atom'
+
 Base = require './base'
-_ = require 'underscore-plus'
 swrap = require './selection-wrapper'
-{Range} = require 'atom'

 # FIXME Currently initally multi selected situation not supported.
 class VisualBlockwise extends Base
@@ -29,12 +30,13 @@
     _.last @editor.getSelectionsOrderedByBufferPosition()

   isReversed: ->
-    if @isSingle()
+    if @isSingleLine()
       false
     else
-      @getTail().marker.isEqual @getBottom().marker
+      @getTail() is @getBottom()
+      # @getTail().marker.isEqual @getBottom().marker

-  isSingle: ->
+  isSingleLine: ->
     @editor.getSelections().length is 1

   getHead: ->
@@ -66,7 +68,7 @@
 class BlockwiseOtherEnd extends VisualBlockwise
   @extend()
   execute: ->
-    unless @isSingle()
+    unless @isSingleLine()
       @reverse()
     @vimState.reverseSelections()

@@ -89,7 +91,7 @@
   @extend()
   direction: 'Above'
   isForward: ->
-    @isSingle() or @isReversed()
+    @isSingleLine() or @isReversed()

 class BlockwiseDeleteToLastCharacterOfLine extends VisualBlockwise
   @extend()


Attempt to fix scrolling to hash for Unicode URIs

--- src/js/cts-cite-driver.coffee
+++ src/js/cts-cite-driver.coffee
@@ -147,7 +147,7 @@
   $('#translation_progress').attr('style',"width: #{progress}%;")
   $('#translation_progress').append $('<span>').text("#{translated_urns} / #{valid_urns.length} entries translated")
   if window.location.hash
-    window.scrollTo(0,$(window.location.hash).position().top - 50)
+    window.scrollTo(0,$(decodeURIComponent(window.location.hash)).position().top - 50)

 # get all data from fusion table
 get_cite_collection = (callback) ->


Store less cache in ~/.atom/compile-cache

--- src/less-compile-cache.coffee
+++ src/less-compile-cache.coffee
@@ -1,16 +1,14 @@
 path = require 'path'
-os = require 'os'
+fs = require 'fs-plus'
 LessCache = require 'less-cache'
 {Subscriber} = require 'emissary'
-
-tmpDir = if process.platform is 'win32' then os.tmpdir() else '/tmp'

 # {LessCache} wrapper used by {ThemeManager} to read stylesheets.
 module.exports =
 class LessCompileCache
   Subscriber.includeInto(this)

-  @cacheDir: path.join(tmpDir, 'atom-compile-cache', 'less')
+  @cacheDir: path.join(fs.getHomeDirectory(), 'compile-cache', 'less')

   constructor: ({resourcePath, importPaths}) ->
     @lessSearchPaths = [


Prevent endGroups being emitted if there is no group set

--- src/lib/InternalSocket.coffee
+++ src/lib/InternalSocket.coffee
@@ -105,6 +105,7 @@
     @emit 'begingroup', group

   endGroup: ->
+    return unless @groups.length
     @emit 'endgroup', @groups.pop()

   # ## Socket identifiers


Removing scoring function

Removing scoring function

--- src/query-predicate.coffee
+++ src/query-predicate.coffee
@@ -212,25 +212,16 @@
 # This function should accept an obj like this:
 # $and: [queries], $or: [queries]
 # should return false if fails
-single = (queries, getter, isScore) ->
+single = (queries, getter) ->
   getter = parseGetter(getter) if getter
-
-  if isScore
-    throw new Error("score operations currently don't work on compound queries") unless queries.length is 1
-    queryObj = queries[0]
-    throw new Error("score operations only work on $and queries (not #{queryObj.type}") unless queryObj.type is "$and"
-    (model) ->
-      model._score = performQuerySingle(queryObj.type, queryObj.parsedQuery, getter, model, true)
-      model
-  else
-    (model) ->
-      for queryObj in queries
-        # Early false return if any of the queries fail
-        return false unless performQuerySingle(queryObj.type, queryObj.parsedQuery, getter, model, isScore)
-      # All queries passes, so return true
-      true
-
-performQuerySingle = (type, query, getter, model, isScore) ->
+  (model) ->
+    for queryObj in queries
+      # Early false return if any of the queries fail
+      return false unless performQuerySingle(queryObj.type, queryObj.parsedQuery, getter, model)
+    # All queries passes, so return true
+    true
+
+performQuerySingle = (type, query, getter, model) ->
   passes = 0
   score = 0
   scoreInc = 1 / query.length
@@ -247,17 +238,15 @@
     # If the attribute test is true, perform the query
     if test
       if q.parsedQuery #nested queries
-        test = single([q], getter, isScore)(model)
+        test = single([q], getter)(model)
       else test = performQuery q.type, q.value, attr, model, getter
     if test
       passes++
-      if isScore
-        boost = q.boost ? 1
-        score += (scoreInc * boost)
+
     switch type
       when "$and"
         # Early false return for $and queries when any test fails
-        return false unless isScore or test
+        return false unless test
       when "$not"
         # Early false return for $not queries when any test passes
         return false if test
@@ -270,10 +259,8 @@
       else
         throw new Error("Invalid compound method")

-  if isScore
-    score
   # For not queries, check that all tests have failed
-  else if type is "$not"
+  if type is "$not"
     passes is 0
   # $or queries have failed as no tests have passed
   # $and queries have passed as no tests failed
@@ -313,12 +300,12 @@
 parseGetter = (getter) ->
   return if typeof getter is 'string' then (obj, key) -> obj[getter](key) else getter

-runQuery = (items, query, getter, first, isScore) ->
+runQuery = (items, query, getter, first) ->
   if arguments.length < 2
     # If no arguments or only the items are provided, then use the buildQuery interface
     return buildQuery.apply this, arguments
   if getter then getter = parseGetter(getter)
-  query = single(parseQuery(query), getter, isScore) unless (utils.getType(query) is "Function")
+  query = single(parseQuery(query), getter) unless (utils.getType(query) is "Function")

   if first
     fn = utils.find


payment: WIP

payment: WIP

I have to commit this now...  no review for this one

--- client/landing/app/MainApp/VirtualizationController.coffee
+++ client/landing/app/MainApp/VirtualizationController.coffee
@@ -360,8 +360,8 @@
         productForm.setContents 'packs', packs

     workflow = new PaymentWorkflow
-      productForm : productForm
-      confirmForm : new VmPaymentConfirmForm
+      productForm: productForm
+      confirmForm: new VmPaymentConfirmForm

     modal = new FormWorkflowModal
       title   : "Create a new VM"
@@ -370,10 +370,28 @@
       width   : 500
       overlay : yes

-    workflow.on 'DataCollected', -> debugger
+    workflow.on 'DataCollected', @bound 'provisionVm'
+
     workflow.on 'Cancel', -> modal.destroy()

     workflow.enter()
+
+  provisionVm: ({ subscription, paymentMethod, productData })->
+    { plan, pack } = productData
+
+    if paymentMethod and not subscription
+
+      plan.subscribe paymentMethod.paymentMethodId, (err, subscription) =>
+        return  if KD.showError err
+
+        @provisionVm { subscription, productData }
+
+    subscription.debit pack, (err, blah) ->
+      debugger
+
+      return  if KD.showError err
+
+      debugger

   askForApprove:(command, callback)->


Fix fogbugz script

Fix fogbugz script

API returns cases within response.

--- src/scripts/fogbugz.coffee
+++ src/scripts/fogbugz.coffee
@@ -40,7 +40,7 @@
           (new Parser()).parseString body, (err,json) ->
             truncate = (text,length=60,suffix="...") ->
               if text.length > length then (text.substr(0,length-suffix.length) + suffix) else text
-            bug = json.cases?[0].case[0]
+            bug = json.response.cases?[0].case[0]
             if bug
               msg.send "https://#{env.HUBOT_FOGBUGZ_HOST}/?#{bug.ixBug[0]}"
               details = [


Fix hireAgent

Fix hireAgent

--- assets/agent_spawner.coffee
+++ assets/agent_spawner.coffee
@@ -11,6 +11,5 @@
     , this)
   )
   hireAgent: ()->
-    agent = potential_agents[Math.floor(Math.random() * potential_agents.length())]
-    potential_agents.pop(agent)
+    agent = $.extend({}, @potential_agents[Math.floor(Math.random() * @potential_agents.length())])
     return agent


Moved tickSize to axes definition.

Moved tickSize to axes definition.

--- assets/js/lightcurve.coffee
+++ assets/js/lightcurve.coffee
@@ -38,17 +38,17 @@
         drawData: (data) ->
             @xScale.domain d3.extent data, (d) -> d.jd
             @yScale.domain d3.extent data, (d) -> d.magnitude
-            xAxis = d3.svg.axis().scale(@xScale).orient('bottom').ticks(10)
-            yAxis = d3.svg.axis().scale(@yScale).orient('left').ticks(10)
+            xAxis = d3.svg.axis().scale(@xScale).orient('bottom').ticks(10).tickSize(-@height, 0, 0)
+            yAxis = d3.svg.axis().scale(@yScale).orient('left').ticks(10).tickSize(-@width, 0, 0)
             @svg.append('g')
                 .attr
                     class: 'x axis'
                     transform: "translate(0,#{@height})"
-                .call(xAxis.tickSize(-@height, 0, 0))
+                .call(xAxis)
             @svg.append('g')
                 .attr
                     class: 'y axis'
-                .call(yAxis.tickSize(-@width, 0, 0))
+                .call(yAxis)
             @svg.selectAll('circle')
                 .data(data)
                 .enter()


added semver validation before compare

added semver validation before compare

--- rbn-pi.coffee
+++ rbn-pi.coffee
@@ -68,7 +68,7 @@
 					if pack[1][0...4] is 'rbn-'
 						console.log "#{pack[1]}: #{pack[3]} > #{pack[2]}, #{semver.gt pack[3], pack[2]}"
 						# If version can update
-						if semver.gt pack[3], pack[2]
+						if semver.valid pack[2] and semver.valid pack[3] and semver.gt pack[3], pack[2]
 							# Needs an update!
 							needsUpdate = true
 							break


storage: make sshfs mounts more frequent

--- src/smc-hub/storage.coffee
+++ src/smc-hub/storage.coffee
@@ -1350,8 +1350,8 @@
             setInterval(task_update_BUP, 1000*60*13)
             task_update_BUP()

-            # update sshfs mounts of snapshots every 7 minutes
-            setInterval(task_mount_snapshots_on_all_compute_vms, 1000*60*7)
+            # update sshfs mounts of snapshots every 3 minutes
+            setInterval(task_mount_snapshots_on_all_compute_vms, 1000*60*3)

             # update ZFS snapshots every 5 minutes
             setInterval(task_update_snapshots, 1000*60*5)


tweak other clock skew thresh

tweak other clock skew thresh

--- src/smc-util/client.coffee
+++ src/smc-util/client.coffee
@@ -314,9 +314,9 @@
         # NOTE: We DO NOT even mess with the clock at all unless it is off by
         # at least 15s.  Otherwise, we might get subtle
         # issues with random variation in the clock due to slight accuracy/ping issues.
-        # Being off by 15s would be basically OK -- much more, and we have to compensate
+        # Being off by 10s would be basically OK -- much more, and we have to compensate
         # and warn the user aggressively to avoid disaster.
-        if @_clock_skew? and Math.abs(@_clock_skew) > 15000
+        if @_clock_skew? and Math.abs(@_clock_skew) > 10000
             return new Date(new Date() - @_clock_skew)
         else
             return new Date()


Add iconfont to list of tools

Add iconfont to list of tools

--- src/javascript/view.coffee
+++ src/javascript/view.coffee
@@ -21,6 +21,7 @@
         'Live reloading across devices'
         'JS transforms and compiling'
         'CSS preprocessing: node-sass (Lightning fast libsass!)'
+        'Iconfont generation'
         'Image optimization'
         'Non common-js plugins with common-js dependencies'
         'Using modules already bundled with other modules'


x might not defined (regards touching project)

--- src/smc-util/schema.coffee
+++ src/smc-util/schema.coffee
@@ -456,7 +456,7 @@
                 # the file, which is confusing and wastes a lot of resources.
                 x = obj.users?[account_id]
                 recent = misc.minutes_ago(3)
-                if x.edit >= recent or x.chat >= recent
+                if x? and x.edit >= recent or x.chat >= recent
                     db.touch(project_id:obj.project_id, account_id:account_id)
                 cb?()


don't default to adding underscore to name

--- metrics.coffee
+++ metrics.coffee
@@ -20,7 +20,7 @@

 module.exports = Metrics =
 	initialize: (_name) ->
-		name = _name + "_"
+		name = _name
 		collectDefaultMetrics({ timeout: 5000, prefix: name+"_"})

 	registerDestructor: (func) ->


Whitespace

Whitespace

--- app/lib/animals.coffee
+++ app/lib/animals.coffee
@@ -10,7 +10,7 @@
   'buildStocky','buildTall','buildLanky','buildSmall','buildLowSlung'
 ]

-characteristics = ['like', 'coat','tail', 'build']
+characteristics = ['like', 'coat', 'tail', 'build']

 animalCharacteristics = [
   {amphibiansAndReptiles:		[0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,1,1,1,0,0,0,1,1]}


Bypass sessionStorage reference in Node

Bypass sessionStorage reference in Node

--- app/lib/session.coffee
+++ app/lib/session.coffee
@@ -1,4 +1,4 @@
-stored = sessionStorage.getItem('session_id')
+stored = sessionStorage?.getItem('session_id')

 generateSessionID = () ->
   sha2 = require('crypto').createHash('sha256')


Update test result

Update test result


Cancel tab even in markupable, append previewElement/textarea to original region div element.

--- app/assets/javascripts/mercury/regions/markupable.js.coffee
+++ app/assets/javascripts/mercury/regions/markupable.js.coffee
@@ -13,20 +13,20 @@

   build: ->
-    width = @element.width()
-    width = '100%' unless width
+    width = '100%'
     height = @element.height()

     value = @element.html().replace(/^\s+|\s+$/g, '')
     @textarea = jQuery('<textarea>', @document).val(value)
     @textarea.attr('class', @element.attr('class')).addClass('mercury-textarea')
     @textarea.css({border: 0, background: 'transparent', display: 'block', width: width, height: height, fontFamily: '"Courier New", Courier, monospace', fontSize: '14px'})
-    @element.after(@textarea)
-    @element.hide()
-    @resize()
-
-    @previewElement = @element
+    @element.empty().append(@textarea)
+    @element.removeAttr('class')
+
+    @previewElement = jQuery('<div>', @document)
+    @element.append(@previewElement)
     @element = @textarea
+    #@resize()

   focus: ->
@@ -113,6 +113,10 @@
           if event.shiftKey then @execCommand('redo') else @execCommand('undo')
           return

+        when 9 # tab
+          event.preventDefault()
+          @execCommand('insertHTML', {value: '  '})
+
       if event.metaKey
         switch event.keyCode
           when 66 # b


Remove route to /about

Remove route to /about

--- app/scripts/app.coffee
+++ app/scripts/app.coffee
@@ -29,10 +29,6 @@
         templateUrl: 'views/main.html'
         controller: 'MainCtrl'
         controllerAs: 'main'
-      .when '/about',
-        templateUrl: 'views/about.html'
-        controller: 'AboutCtrl'
-        controllerAs: 'about'
       .otherwise
         redirectTo: '/'


changed where online users name logic is so it catches all cases.

--- services/web/public/coffee/ide/online-users/OnlineUsersManager.coffee
+++ services/web/public/coffee/ide/online-users/OnlineUsersManager.coffee
@@ -21,16 +21,14 @@
 								# Don't store myself
 								continue
 							# Store data in the same format returned by clientTracking.clientUpdated
-							if user.first_name?.length == 0 and user.last_name.length == 0
-								name = user.email
-							else
-								name = "#{user.first_name} #{user.last_name}"
+
+

 							@$scope.onlineUsers[user.client_id] = {
 								id:      user.client_id
 								user_id: user.user_id
 								email:   user.email
-								name:    name
+								name:    user.first_name
 								doc_id:  user.cursorData?.doc_id
 								row:     user.cursorData?.row
 								column:  user.cursorData?.column
@@ -57,8 +55,12 @@
 			for client_id, user of @$scope.onlineUsers
 				if user.doc_id?
 					user.doc = @ide.fileTreeManager.findEntityById(user.doc_id)
+				if user.first_name?.length == 0 and user.last_name.length == 0
+					user.name = user.email
+				else
+					user.name = "#{user.first_name} #{user.last_name}"
 				@$scope.onlineUsersArray.push user
-
+
 			@$scope.onlineUserCursorHighlights = {}
 			for client_id, client of @$scope.onlineUsers
 				doc_id = client.doc_id


refacto new parcel button behavior logic

--- app/assets/javascripts/backend/purchase_process/purchase_orders.coffee
+++ app/assets/javascripts/backend/purchase_process/purchase_orders.coffee
@@ -1,8 +1,8 @@
 ((E, $) ->
   'use strict'

-  $(document).ready ->
-    $('#purchase_orders-list').each ->
+  createNewParcelReception = () ->
+    $(document).ready ->
       $newParcelBtn = $('#generate-parcel-btn')
       newParcelUrl = $newParcelBtn.prop('href')

@@ -24,27 +24,8 @@
         # !! so we're sure disabled is a Boolean and not just truthy/falsy — for jQuery
         $newParcelBtn.toggleClass('disabled', !!disabled)

-  $(document).ready ->
-    $('#unreceived_purchase_orders-list').each ->
-      $newParcelBtn = $('#generate-parcel-btn')
-      newParcelUrl = $newParcelBtn.prop('href')

-      $(document).on 'change', 'input[data-list-selector]', ->
-        selectedOrders = $('input[data-list-selector]:checked').filter ->
-                           /\d/.test($(this).data('list-selector'))
-        selectedOrdersIds = selectedOrders.map ->
-                              $(this).data('list-selector')
-                            .toArray()
-        reconciledOrders = selectedOrders.filter ->
-                             $(this).closest('tr').data('reconciliation-state') == 'reconcile'
-
-        if selectedOrdersIds.length > 0
-          $newParcelBtn.prop('href', "#{newParcelUrl}?mode=prefilled&purchase_order_ids=#{selectedOrdersIds}")
-        else
-          $newParcelBtn.prop('href', newParcelUrl)
-
-        disabled = !selectedOrders.length || reconciledOrders.length
-        # !! so we're sure disabled is a Boolean and not just truthy/falsy — for jQuery
-        $newParcelBtn.toggleClass('disabled', !!disabled)
+  $('#purchase_orders-list').each createNewParcelReception()
+  $('#unreceived_purchase_orders-list').each createNewParcelReception()

 ) ekylibre, jQuery


skeleton of `inviteToProject` function

skeleton of `inviteToProject` function

--- services/web/app/coffee/Features/Collaborators/CollaboratorsInviteController.coffee
+++ services/web/app/coffee/Features/Collaborators/CollaboratorsInviteController.coffee
@@ -1,11 +1,36 @@
+ProjectGetter = require "../Project/ProjectGetter"
+LimitationsManager = require "../Subscription/LimitationsManager"
+UserGetter = require "../User/UserGetter"
 CollaboratorsInviteHandler = require('./CollaboratorsInviteHandler')
+mimelib = require("mimelib")
+logger = require('logger-sharelatex')

 module.exports = CollaboratorsInviteController =

-	inviteToProject: (req, res) ->
+	inviteToProject: (req, res, next) ->
+		projectId = req.params.Project_id
+		email = req.body.email
+		sendingUserId = req.session?.user_id
+		logger.log {projectId, email, sendingUserId}, "inviting to project"
+		LimitationsManager.canAddXCollaborators project_id, 1, (error, allowed) =>
+			return next(error) if error?
+			if !allowed
+				logger.log {projectId, email, sendingUserId}, "not allowed to invite any more users to this project"
+				return res.json {}
+			{email, privileges} = req.body
+			email = mimelib.parseAddresses(email or "")[0]?.address?.toLowerCase()
+			if !email? or email == ""
+				logger.log {projectId, email, sendingUserId}, "invalid email address"
+				return res.status(400).send("invalid email address")
+			CollaboratorsInviteHandler.inviteToProject projectId, sendingUserId, email, priveleges, (err, invite) ->
+				if err?
+					logger.err {projectId, email, sendingUserId}, "error creating project invite"
+					return next(err)
+				logger.log {projectId, email, sendingUserId}, "invite created"
+				return res.json {inviteId: invite._id}

-	revokeInvite: (req, res) ->
+	revokeInvite: (req, res, next) ->

-	viewInvite: (req, res) ->
+	viewInvite: (req, res, next) ->

-	acceptInvite: (req, res) ->
+	acceptInvite: (req, res, next) ->


Only trigger if record_ids exists.

Only trigger if record_ids exists.

--- app/assets/javascripts/app/models/_application_model.coffee
+++ app/assets/javascripts/app/models/_application_model.coffee
@@ -545,7 +545,7 @@
           App.Collection.loadAssets(data.assets)

           # in case of no record_ids are there, no inital render is fired
-          if _.isEmpty(data.record_ids)
+          if data.record_ids && _.isEmpty(data.record_ids)
             App[@className].trigger('refresh', [])

         # find / load object