fixed onProgress issue

fixed onProgress issue

--- packages/crypto/client/crypto.coffee
+++ packages/crypto/client/crypto.coffee
@@ -115,8 +115,7 @@
         params.size = params.data.byteLength
       if not params.transfer
         params.transfer = false
-      if not params.onDone
-        params.onDone = ->
+      params.onDone = callback
       @worker.queue params

     finalize: (callback) ->
@@ -133,6 +132,7 @@
       params =
         message: 'finalize'
         onDone: callback
+        size: 0
       @worker.queue params

     destroy: ->
@@ -151,7 +151,7 @@

     @handler =
       progress: ->
-        progress = @totalSizeProcessed / (@totalSize or @totalSizeQueued)
+        progress = self.totalSizeProcessed / (self.totalSize or self.totalSizeQueued)
         self.onProgress? progress
         self.busy = false
         self.flush()


Inline most one-property objects

Inline most one-property objects

Just newline them if they are themselves property values.

This will preserve the desired round-trip behavior with fewer unneeded
side-effects

--- lib/js2coffee.coffee
+++ lib/js2coffee.coffee
@@ -497,7 +497,10 @@
   # left is a `identifier`, right can be anything.

   'property_init': (n) ->
-    "#{@property_identifier n.left()}: #{@build n.right()}"
+    left = n.left()
+    right = n.right()
+    right.is_property_value = true
+    "#{@property_identifier left}: #{@build right}"

   # `object_init`
   # An object initializer.
@@ -506,6 +509,9 @@
   'object_init': (n, options={}) ->
     if n.children.length == 0
       "{}"
+
+    else if n.children.length == 1 and not n.is_property_value
+      @build n.children[0]

     else
       list = _.map n.children, (item) => @build item


Add additional test coverage for CRC32 checks

--- test/services/dynamodb.spec.coffee
+++ test/services/dynamodb.spec.coffee
@@ -31,23 +31,33 @@
       expect(ddb().retryDelays()).to.eql(delays)

   describe 'CRC32 check', ->
-    dynamo = null;
+    dynamo = null

     beforeEach ->
       dynamo = ddb(accessKeyId: 'AKID', secretAccessKey: 'SECRET', region: 'us-west-2')

     if AWS.util.isNode()
       it 'does not verify response checksum when streaming', ->
-        spy = helpers.spyOn(dynamo, 'crc32IsValid')
-        helpers.mockHttpResponse 200, {'x-amz-crc32': '1575962599'}, """{"TableNames":["mock-table"]}"""
+        helpers.mockHttpResponse 200, {'x-amz-crc32': '0'}, """{"TableNames":["mock-table"]}"""
         request = dynamo.makeRequest('listTables')
         s = request.createReadStream()
         s.on 'end', ->
-          expect(spy.calls.length).to.eql(0)
+          expect(request.error).to.eql(null)

     it 'does verify response checksum when not streaming', ->
-      spy = helpers.spyOn(dynamo, 'crc32IsValid').andCallThrough()
       helpers.mockHttpResponse 200, {'x-amz-crc32': '1575962599'}, """{"TableNames":["mock-table"]}"""
       request = dynamo.makeRequest('listTables')
       request.send (err, data) ->
-        expect(spy.calls.length).to.eql(1)
+        expect(err).to.eql(null)
+
+    it 'does not verify response checksum when no [x-amz-crc32] header', ->
+      helpers.mockHttpResponse 200, {}, """{"TableNames":["mock-table"]}"""
+      request = dynamo.makeRequest('listTables')
+      request.send (err, data) ->
+        expect(err).to.eql(null)
+
+    it 'throws an error when response checksum does not match', ->
+      helpers.mockHttpResponse 200, {'x-amz-crc32': '0'}, """{"TableNames":["mock-table"]}"""
+      request = dynamo.makeRequest('listTables')
+      request.send (err, data) ->
+        expect(err.code).to.eql('CRC32CheckFailed')


Close #1358; Fix misspelling

Close #1358; Fix misspelling

--- packages/rocketchat-lib/server/models/_Base.coffee
+++ packages/rocketchat-lib/server/models/_Base.coffee
@@ -29,7 +29,7 @@
 		return @model.allow.apply @model, arguments

 	deny: ->
-		return @model.allow.apply @model, arguments
+		return @model.deny.apply @model, arguments

 	ensureIndex: ->
 		return @model._ensureIndex.apply @model, arguments


:bug: for adjustCursor

:bug: for adjustCursor

--- lib/operators.coffee
+++ lib/operators.coffee
@@ -159,16 +159,14 @@
   linewiseAlias: true
   # [FIXME] duplicate to Yank, need to consolidate as like adjustCursor().
   execute: ->
-    adjustCursor = @adjustCursor and
-      (@target.isLinewise?() or settings.get('stayOnTransformString'))
+    if @target.isLinewise?() or settings.get('stayOnTransformString')
+      points = _.pluck(@editor.getSelectedBufferRanges(), 'start')
     if _.any @target.select()
       @withFlashing =>
         for selection in @editor.getSelections()
-          if adjustCursor
-            point = selection.getBufferRange().start
           range = selection.insertText @getNewText(selection.getText())
-          if adjustCursor
-            selection.cursor.setBufferPosition(point ? range.start)
+          if @adjustCursor
+            selection.cursor.setBufferPosition(points.shift() ? range.start)
     @vimState.activateNormalMode()

 class ToggleCase extends TransformString


Beep on all --watched compile errors

Beep on all --watched compile errors

--- src/command.coffee
+++ src/command.coffee
@@ -136,18 +136,9 @@
   catch err
     CoffeeScript.emit 'failure', err, task
     return if CoffeeScript.listeners('failure').length
-    if o.watch
-      debouncedBeep()
-      return printLine err.message
+    return printLine err.message + '\007' if o.watch
     printWarn err instanceof Error and err.stack or "ERROR: #{err}"
     process.exit 1
-
-# Beep once if there are errors in compilation of watched script(s)
-beepTimeout = null
-debouncedBeep = ->
-  clearTimeout beepTimeout
-  beepTimeout = wait 100, ->
-    process.stdout.write '\007'

 # Attach the appropriate listeners to compile scripts incoming over **stdin**,
 # and write them back to **stdout**.


Add condition to prevent exception.

Add condition to prevent exception.

--- src/control.coffee
+++ src/control.coffee
@@ -361,17 +361,18 @@
                             else
                                 position = exactMatch[0]

-                        slug = position.slugifyAddress()
-                        newMunicipality = slug.split('/')[0]
-                        if newMunicipality != municipality
-                            # If the original slug was in the wrong language, run full
-                            # command cycle including URL navigation to change the URL language.
-                            # For example in Finland, the slug should be in Swedish if the UI is in Swedish,
-                            # otherwise in Finnish (the default).
-                            @selectPosition(position).done =>
-                                @router.navigate "address/#{slug}", replace: true
-                        else
-                            @selectPosition position
+                        if position?
+                            slug = position.slugifyAddress()
+                            newMunicipality = slug.split('/')[0]
+                            if newMunicipality != municipality
+                                # If the original slug was in the wrong language, run full
+                                # command cycle including URL navigation to change the URL language.
+                                # For example in Finland, the slug should be in Swedish if the UI is in Swedish,
+                                # otherwise in Finnish (the default).
+                                @selectPosition(position).done =>
+                                    @router.navigate "address/#{slug}", replace: true
+                            else
+                                @selectPosition position
                     catch err

                         addressInfo =


Fix early binding Layout to document.body.

--- src/chaplin/views/layout.coffee
+++ src/chaplin/views/layout.coffee
@@ -14,9 +14,6 @@
   # This should be set in your app-specific Application class
   # and passed as an option.
   title: ''
-
-  # Bind to document body by default.
-  el: document.body

   # Override default view behavior, we don’t want document.body to be removed.
   keepElement: true
@@ -35,6 +32,9 @@
     '!region:unregister mediator': 'unregisterRegionHandler'

   constructor: (options = {}) ->
+    # Bind to document body by default.
+    options.el ?= document.body
+
     @globalRegions = []
     @title = options.title
     @regions = options.regions if options.regions


Replaced lodash each in translate with .forEach to fix dependency order issue

--- src/code/utils/translate.coffee
+++ src/code/utils/translate.coffee
@@ -1,22 +1,20 @@
 urlParams = require './url-params'

-languageFiles = {
-  'de': require './lang/de'
-  'en-US': require './lang/en-US'
-  'es': require './lang/es'
-  'he': require './lang/he'
-  'tr': require './lang/tr'
-  'zh-TW': require './lang/zh-TW'
-}
+languageFiles = [
+  {key: 'de' ,   contents: require './lang/de'}
+  {key: 'en-US', contents: require './lang/en-US'}
+  {key: 'es' ,   contents: require './lang/es'}
+  {key: 'he' ,   contents: require './lang/he'}
+  {key: 'tr' ,   contents: require './lang/tr'}
+  {key: 'zh-TW', contents: require './lang/zh-TW'}
+]

 translations =  {}
-_.each languageFiles, (langContents, langKey) ->
-  translations[langKey] = langContents
+languageFiles.forEach (lang) ->
+  translations[lang.key] = lang.contents
   # accept full key with region code or just the language code
-  if (dashLoc = langKey.indexOf('-')) > 0
-    lang = langKey.substring(0, dashLoc)
-    translations[lang] = langContents
-  return
+  if (dashLoc = lang.key.indexOf('-')) > 0
+    translations[lang.key.substring(0, dashLoc)] = lang.contents

 defaultLang = null
 # default to English unless the user expresses another preference (via URL param for now)


remove console.log in decompress event

remove console.log in decompress event

--- src/coffee/ezBreadcrumbs.coffee
+++ src/coffee/ezBreadcrumbs.coffee
@@ -225,8 +225,6 @@
                         else
                             return false

-                    console.log releaseItems
-
                     if releaseItems.length > 0
                         # trigger first the beforeDecompress callback
                         o.opts.onBeforeDecompress(_this) if $.isFunction(o.opts.onBeforeDecompress)


Refactor transpile

Refactor transpile

--- lib/transpile.coffee
+++ lib/transpile.coffee
@@ -30,68 +30,94 @@

 transpile = (rules, options, callback) ->
   # console.dir rules, colors: true, depth:10
-
-  # detect composable external rules
-  options.composable = []
+  try
+    options = _.clone options
+    options.composable = detectComposableRules rules, options
+
+    result = concat [
+      'Header'
+      'ModuleBegin'
+      'GeneralRuntime'
+      'RulesDefinitions'
+      'EntryInvocation'
+      'ModuleEnd'
+    ].map (section) -> sections[section] rules, options
+
+  catch err
+    return callback err
+
+  # console.log result.join ''
+  callback null, result.join ''
+
+detectComposableRules = (rules, options) ->
+  result = []
   _.each options.externals, (fn, name) ->
     if isTransformation fn.toString()
-      options.composable.push name
+      result.push name
   _.each rules, (rule, name) ->
     if rule.expr?.type is 'CodeBlock' and isTransformation rule.expr.code
-      options.composable.push name
-
-  result = []
-
-  # header
-  result.push '// Generated by ', product.name, ' ', product.version, '\n'
-  result.push '// ', options.header, '\n' if options.header?
-
-  # exportable
-  result.push 'module.exports = function ($options, callback) {\n'
-
-  # fallback
-  result.push """
-    if (callback == null) {
-      callback = $options;
-      $options = {};
-    } else {
-      $options = $options || {};
-    }\n"""
-
-  # runtime
-  result.push 'var slice = Array.prototype.slice;\n'
-  result.push runtime.toString().replace(/^function \(\) {/g, '').replace(/}$/g, '')
-  result.push '\n\n'
-
-  # root scope
-  result.push 'var ', ROOT_SCOPE_VAR, ' = new $Scope();\n\n'
-  _.each rules, (rule, name) ->
-    return if name is '$entry'
-    push.apply result, concat [
-      generateRuleDefinition ROOT_SCOPE_VAR, rule, options
-      '();\n\n'
-    ]
-
-  # kick off
-  entry = options.entry ? rules.$entry
-  result.push 'var result;\n'
-  result.push 'try {\n'
-  if entry?
-    result.push 'result = '
-    result.push ROOT_SCOPE_VAR
-    result.push '.invoke($options.entry || "', entry, '")();\n'
-  else
-    result.push 'if ($options.entry != null) result = '
-    result.push ROOT_SCOPE_VAR
-    result.push '.invoke($options.entry)();\n'
-  result.push '} catch (err) { return callback(err); }\n'
-  result.push 'callback(null, result);\n'
-
-  # done!
-  result.push '};'
-
-  # console.log result.join ''
-  callback null, result.join ''
+      result.push name
+  result
+
+sections =
+
+  Header: (rules, options) ->
+    concat [
+      '// Generated by ', product.name, ' ', product.version, '\n'
+      if options.header? then '// ' + options.header + '\n'
+    ]
+
+  ModuleBegin: (rules, options) ->
+    """
+    module.exports = function ($options, callback) {
+      if (callback == null) {
+        callback = $options;
+        $options = {};
+      } else {
+        $options = $options || {};
+      }
+    \n
+    """
+
+  ModuleEnd: (rules, options) ->
+    '};\n'
+
+  GeneralRuntime: (rules, options) ->
+    concat [
+      'var slice = Array.prototype.slice;\n'
+      runtime.toString().replace(/^function \(\) {/g, '').replace(/}$/g, '')
+      '\n\n'
+    ]
+
+  RulesDefinitions: (rules, options) ->
+    result = []
+    result.push 'var ', ROOT_SCOPE_VAR, ' = new $Scope();\n\n'
+    _.each rules, (rule, name) ->
+      return if name is '$entry'
+      push.apply result, concat [
+        generateRuleDefinition ROOT_SCOPE_VAR, rule, options
+        '();\n\n'
+      ]
+    result
+
+  EntryInvocation: (rules, options) ->
+    entry = options.entry ? rules.$entry
+    concat [
+      'var result;\n'
+      'try {\n'
+      if entry? then concat [
+        'result = '
+        ROOT_SCOPE_VAR
+        '.invoke($options.entry || "', entry, '")();\n'
+      ]
+      else concat [
+        'if ($options.entry != null) result = '
+        ROOT_SCOPE_VAR
+        '.invoke($options.entry)();\n'
+      ]
+      '} catch (err) { return callback(err); }\n'
+      'callback(null, result);\n'
+    ]

 types =


Fix copy/pasting files under certain scenarios

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -588,8 +588,15 @@
             if initialPathIsDirectory
               newPath = "#{originalNewPath}#{fileCounter.toString()}"
             else
-              fileArr = originalNewPath.split('.')
-              newPath = "#{fileArr[0]}#{fileCounter.toString()}.#{fileArr[1]}"
+              if process.platform is 'win32'
+                fileArr = /(.+)\.(?!.*\\)(.+)/.exec(originalNewPath)
+              else
+                fileArr = /(.+)\.(?!.*\/)(.+)/.exec(originalNewPath)
+
+              if fileArr?
+                newPath = "#{fileArr[1]}#{fileCounter.toString()}.#{fileArr[2]}"
+              else # File didn't have an extension so don't attempt to append a nonexistent one
+                newPath = "#{originalNewPath}#{fileCounter.toString()}"
             fileCounter += 1

           if fs.isDirectorySync(initialPath)


Don't exit early if no _layout directory exists (matches Jekyll behavior). Closes #4

--- src/enfield.coffee
+++ src/enfield.coffee
@@ -446,8 +446,9 @@
   layoutDir = path.join config.source, config.layout

   unless fs.existsSync layoutDir
-    console.error "Error: Missing _layouts directory".red
-    process.exit -1
+    console.warn "Warning: Missing _layouts directory".yellow
+    callback null, {}
+    return

   fileData = {}
   fileContents = {}


Set log level to info by default

Set log level to info by default

--- src/enfield.coffee
+++ src/enfield.coffee
@@ -49,7 +49,8 @@
     if parsed.log
       log.level = parsed.log
       log.verbose "Set log level: #{parsed.log}"
-
+    else
+      log.level = 'info'

     command = parsed.argv.remain[0]
     switch command


dev config buildclient

dev config buildclient

--- config/dev.coffee
+++ config/dev.coffee
@@ -21,7 +21,7 @@
   mongo         : mongo
   runBroker     : no
   configureBroker: no
-  buildClient   : no
+  buildClient   : yes
   social        :
     numberOfWorkers: 1
     watch       : yes


team-grid: display only new items based on class not number of cols

--- app/_src/components/grid.coffee
+++ app/_src/components/grid.coffee
@@ -28,6 +28,7 @@
 	SELECTOR_IMAGES = '.team-grid__image'

 	CLASS_CELL_ITEM = 'team-grid__item team-grid__item--appended'
+	CLASS_CELL_VISIBLE = 'team-grid__item--visible'
 	CLASS_IMG = 'team-grid__image'
 	CLASS_ANCHOR = 'team-grid__link'
 	CLASS_ANIMATE_ITEM = 'team-grid__item--animate'
@@ -56,6 +57,8 @@
 	debouncedResizeFn = null
 	spinnerTimerId = null
 	watcher = null
+	isWorking = false
+	hasBeenShown = false
 	imagesLoaded = null

 	###
@@ -195,12 +198,11 @@
 		Callback for when it's visible on the page view
 	###
 	onEnterViewport: () =>
-		return if @hasBeenShown
-		@isShowing = true
+		return if hasBeenShown
 		@showGrid()
 		@addEventListeners()
 		@onWindowResize() # in case we have resized before all this happened
-		@hasBeenShown = true
+		hasBeenShown = true

 	###
 		Shows all items when the images have been loaded.
@@ -212,11 +214,9 @@
 			if spinnerTimerId isnt null
 				clearTimeout spinnerTimerId
 				@spinner.hide(=>
-					@$context.addClass CLASS_READY
 					@showItems( @$context.find( SELETOR_CELL_APPENDED_ITEM ) )
 				)
 			else
-				@$context.addClass CLASS_READY
 				@showItems( @$context.find( SELETOR_CELL_APPENDED_ITEM ) )

 	###
@@ -245,18 +245,16 @@
 		Callback for when it's exiting the page view
 	###
 	onExitViewport: () =>
-		if @hasBeenShown then @removeWatcherListeners()
+		if hasBeenShown then @removeWatcherListeners()

 	###
 		Callback for when the window is resized
 	###
 	onWindowResize: () =>

-		return if @isShowing
-
 		@checkBreakpoint()

-		if currentBreakpointKey is null
+		if currentBreakpointKey is null or isWorking
 			# if we are getting out of a grid
 			# at least resize the whole container down to it's normal size
 			@updateContextHeight true
@@ -293,12 +291,12 @@
 			if spinnerTimerId isnt null
 				clearTimeout spinnerTimerId
 				@spinner.hide(=>
-					@showItems @$context.find(SELETOR_CELL_ITEM + ':gt(' + currentNumberOfCols + ')')
+					@showItems @$context.find(SELETOR_CELL_APPENDED_ITEM).not(CLASS_CELL_VISIBLE)
 				)
 			else
 				# Show only the new ones
 				# with "greater than" what we currently have
-				@showItems @$context.find(SELETOR_CELL_ITEM + ':gt(' + currentNumberOfCols + ')')
+				@showItems @$context.find(SELETOR_CELL_APPENDED_ITEM).not(CLASS_CELL_VISIBLE)

 	###
@@ -615,15 +613,21 @@
 	###
 		Shows a group of items by calling show on each of them.
 	###
-	showItems: ( $items ) ->
+	showItems: ( $items ) =>
 		numItems = $items.length
 		i = 0
+
+		isWorking = true
+
+		unless @$context.hasClass CLASS_READY
+			@$context.addClass CLASS_READY

 		while i < numItems
 			@showItem $items.eq( i )
 			i++

-		@isShowing = false
+		isWorking = false
+

 	###
 		Shows an item by setting an active class.
@@ -647,6 +651,7 @@
 		setTimeout ->
 			$item
 				.addClass( CLASS_ANIMATE_ITEM )
+				.addCLass( CLASS_CELL_VISIBLE )
 				.css( Modernizr.prefixed('transitionDelay'), delay + 'ms' )
 		, 50


Make Slot show up.

Make Slot show up.

--- app/assets/asset/zipView.coffee
+++ app/assets/asset/zipView.coffee
@@ -1,11 +1,11 @@
 'use strict'

 app.controller 'asset/zipView', [
-  '$scope', 'displayService', 'asset',
+  '$scope', 'displayService', 'asset','slot'
   ($scope, display, asset, slot) ->
     $scope.close = ->
       window.history.back()
-    $scope.asset = slot
+    $scope.asset = slot
     console.log $scope.asset
     display.title = asset.displayName
     $scope.volume = asset.volume


Fix: multiple ajax select might not work properly

--- vendor/assets/javascripts/auto_select2/multi_ajax_select2_value_parser.js.coffee
+++ vendor/assets/javascripts/auto_select2/multi_ajax_select2_value_parser.js.coffee
@@ -1,16 +1,15 @@
 jQuery ($) ->
-  # Tune input fields before submitting form, change csv values into input elements with array names
-  $('input.auto-ajax-select2.multiple').closest('form').submit ->
-    $form = $(@) # save a reference to the form for later usage
+# Tune input fields before submitting form, change csv values into input elements with array names
+  $(document).on 'submit', 'form', ->
+    $form = $(@)
     $('input.auto-ajax-select2.multiple').each ->
       $multi = $(@)
       data = $multi.select2('data')
       name = $multi.attr('name')
       $multi.remove()
       # Create hidden fields with array like names
-      $form.append $.map(data, (obj)->
-        $('<input/>', type: 'hidden', name: "#{name}[]", value: obj.id)
-      )
+      for item in data
+        $form.append $('<input/>', type: 'hidden', name: "#{name}[]", value: item.id)
       return
     return
   return


Fix previews to use https

Fix previews to use https

--- app/classify.cjsx
+++ app/classify.cjsx
@@ -90,11 +90,16 @@
     @removeClassesForGuide()

   onSubjectSelect: (e, subject) ->
-    previews = subject.location.previews
     randomInt = if subject.location.previews.length is 2 then Math.round(Math.random()) else Math.round(Math.random() * (2 - 0)) + 0
+    previews = subject.location.previews[randomInt]
+
+    if window.location.hostname is "www.chimpandsee.org"
+      previews = previews.map (preview, i) ->
+        preview = preview.replace("http", "https")
+
     @setState({
       video: subject.location.standard
-      previews: previews[randomInt]
+      previews: previews
       location: subject.group.name
       classification: new Classification {subject}
     }, => @onSubjectUpdate(randomInt))


Fixes node.js errors

Fixes node.js errors

--- src/hydrate.coffee
+++ src/hydrate.coffee
@@ -143,7 +143,7 @@
               if cons == "" && !input.hasOwnProperty("constructor_name")
                 cons = input.constructor_name
               if !cons?
-                @errorHandler AnonymousConstructorError(input)
+                @errorHandler new Hydrate.AnonymousConstructorError(input)
               unless cons == "Object"
                 output.__hydrate_cons = cons

@@ -283,7 +283,7 @@
         when "function"
           klass = klass.name
           if klass == ""
-            @errorHandler new AnonymousConstructorError(klass)
+            @errorHandler new Hydrate.AnonymousConstructorError(klass)
         when "string"
           null
         else


fix deploy success rate class

fix deploy success rate class

--- client/components/flow-status/flow-status-gauge.cjsx
+++ client/components/flow-status/flow-status-gauge.cjsx
@@ -9,7 +9,7 @@
     "#{percentage.toFixed 3}%"

   render: ->
-

+

Flow Deploy Success Rate

{@formatPercentage @props.successPercentage}


revert that, keyup is the correct one

revert that, keyup is the correct one

--- client/landing/app/MainApp/login/registerform.coffee
+++ client/landing/app/MainApp/login/registerform.coffee
@@ -93,9 +93,9 @@
             rangeLength  : "Username should be minimum 4 maximum 25 chars!"
           events         :
             required     : "blur"
-            rangeLength  : "keypress"
-            regExp       : "keypress"
-            usernameCheck: "keypress"
+            rangeLength  : "keyup"
+            regExp       : "keyup"
+            usernameCheck: "keyup"
             finalCheck   : "blur"
         iconOptions      :
           tooltip        :


:fire: CoffeeScript version of the quick settings view

--- lib/minimap-quick-settings-element.coffee
+++ lib/minimap-quick-settings-element.coffee
@@ -1,158 +0,0 @@
-{registerOrUpdateElement, EventsDelegation, SpacePenDSL} = require 'atom-utils'
-{CompositeDisposable, Emitter} = require 'atom'
-
-Main = require './main'
-
-# Internal: The {MinimapQuickSettingsElement} class is used to display
-# the Minimap quick settings when clicking on the corresponding button.
-class MinimapQuickSettingsElement
-  SpacePenDSL.includeInto(this)
-  EventsDelegation.includeInto(this)
-
-  @content: ->
-    @div class: 'select-list popover-list minimap-quick-settings', =>
-      @input type: 'text', class: 'hidden-input', outlet: 'hiddenInput'
-      @ol class: 'list-group mark-active', outlet: 'list', =>
-        @li class: 'separator', outlet: 'separator'
-        @li class: 'code-highlights', outlet: 'codeHighlights', 'code-highlights'
-        @li class: 'absolute-mode', outlet: 'absoluteMode', 'absolute-mode'
-      @div class: 'btn-group', =>
-        @button class: 'btn btn-default', outlet: 'onLeftButton', 'On Left'
-        @button class: 'btn btn-default', outlet: 'onRightButton', 'On Right'
-
-  selectedItem: null
-
-  setModel: (@minimap) ->
-    @emitter = new Emitter
-    @subscriptions = new CompositeDisposable
-    @plugins = {}
-    @itemsActions = new WeakMap
-
-    @subscriptions.add Main.onDidAddPlugin ({name, plugin}) =>
-      @addItemFor(name, plugin)
-    @subscriptions.add Main.onDidRemovePlugin ({name, plugin}) =>
-      @removeItemFor(name, plugin)
-    @subscriptions.add Main.onDidActivatePlugin ({name, plugin}) =>
-      @activateItem(name, plugin)
-    @subscriptions.add Main.onDidDeactivatePlugin ({name, plugin}) =>
-      @deactivateItem(name, plugin)
-
-    @subscriptions.add atom.commands.add 'minimap-quick-settings',
-      'core:move-up': => @selectPreviousItem()
-      'core:move-down': => @selectNextItem()
-      'core:move-left': -> atom.config.set('minimap.displayMinimapOnLeft', true)
-      'core:move-right': -> atom.config.set('minimap.displayMinimapOnLeft', false)
-      'core:cancel': => @destroy()
-      'core:confirm': => @toggleSelectedItem()
-
-    @codeHighlights.classList.toggle('active', @minimap.displayCodeHighlights)
-    @subscriptions.add @subscribeTo @codeHighlights,
-      'mousedown': (e) =>
-        e.preventDefault()
-        atom.config.set('minimap.displayCodeHighlights', !@minimap.displayCodeHighlights)
-
-    @itemsActions.set @codeHighlights, =>
-      atom.config.set('minimap.displayCodeHighlights', !@minimap.displayCodeHighlights)
-
-    @subscriptions.add @subscribeTo @absoluteMode,
-      'mousedown': (e) =>
-        e.preventDefault()
-        atom.config.set('minimap.absoluteMode', !atom.config.get('minimap.absoluteMode'))
-
-    @itemsActions.set @absoluteMode, =>
-      atom.config.set('minimap.absoluteMode', !atom.config.get('minimap.absoluteMode'))
-
-    @subscriptions.add @subscribeTo @hiddenInput,
-      'focusout': (e) =>
-        @destroy()
-
-    @subscriptions.add @subscribeTo @onLeftButton,
-      'mousedown': (e) ->
-        e.preventDefault()
-        atom.config.set('minimap.displayMinimapOnLeft', true)
-
-    @subscriptions.add @subscribeTo @onRightButton,
-      'mousedown': (e) ->
-        e.preventDefault()
-        atom.config.set('minimap.displayMinimapOnLeft', false)
-
-    @subscriptions.add atom.config.observe 'minimap.displayCodeHighlights', (bool) =>
-      @codeHighlights.classList.toggle('active', bool)
-
-    @subscriptions.add atom.config.observe 'minimap.absoluteMode', (bool) =>
-      @absoluteMode.classList.toggle('active', bool)
-
-    @subscriptions.add atom.config.observe 'minimap.displayMinimapOnLeft', (bool) =>
-      @onLeftButton.classList.toggle('selected', bool)
-      @onRightButton.classList.toggle('selected', not bool)
-
-    @initList()
-
-  onDidDestroy: (callback) ->
-    @emitter.on 'did-destroy', callback
-
-  attach: ->
-    workspaceElement = atom.views.getView(atom.workspace)
-    workspaceElement.appendChild this
-    @hiddenInput.focus()
-
-  destroy: ->
-    @emitter.emit('did-destroy')
-    @subscriptions.dispose()
-    @parentNode.removeChild(this)
-
-  initList: ->
-    @itemsDisposables = new WeakMap
-    @addItemFor(name, plugin) for name, plugin of Main.plugins
-
-  toggleSelectedItem: => @itemsActions.get(@selectedItem)?()
-
-  selectNextItem: ->
-    @selectedItem.classList.remove('selected')
-    if @selectedItem.nextSibling?
-      @selectedItem = @selectedItem.nextSibling
-      @selectedItem = @selectedItem.nextSibling if @selectedItem.matches('.separator')
-    else
-      @selectedItem = @list.firstChild
-    @selectedItem.classList.add('selected')
-
-  selectPreviousItem: ->
-    @selectedItem.classList.remove('selected')
-    if @selectedItem.previousSibling?
-      @selectedItem = @selectedItem.previousSibling
-      @selectedItem = @selectedItem.previousSibling if @selectedItem.matches('.separator')
-    else
-      @selectedItem = @list.lastChild
-    @selectedItem.classList.add('selected')
-
-  addItemFor: (name, plugin) ->
-    item = document.createElement('li')
-    item.classList.add('active') if plugin.isActive()
-    item.textContent = name
-
-    action = => Main.togglePluginActivation(name)
-
-    @itemsActions.set(item, action)
-    @itemsDisposables.set item, @addDisposableEventListener item, 'mousedown', (e) =>
-      e.preventDefault()
-      action()
-
-    @plugins[name] = item
-    @list.insertBefore item, @separator
-
-    unless @selectedItem?
-      @selectedItem = item
-      @selectedItem.classList.add('selected')
-
-  removeItemFor: (name, plugin) ->
-    try @list.removeChild(@plugins[name])
-    delete @plugins[name]
-
-  activateItem: (name, plugin) ->
-    @plugins[name].classList.add('active')
-
-  deactivateItem: (name, plugin) ->
-    @plugins[name].classList.remove('active')
-
-module.exports =
-MinimapQuickSettingsElement = registerOrUpdateElement 'minimap-quick-settings', MinimapQuickSettingsElement.prototype


Only load ngrok/tty on demand.

Only load ngrok/tty on demand.

--- src/lib/tty.coffee
+++ src/lib/tty.coffee
@@ -1,7 +1,14 @@
+_ = require 'lodash'
 Promise = require 'bluebird'
-ngrok = Promise.promisifyAll require 'ngrok'
-tty = Promise.promisifyAll require 'tty.js'
 TypedError = require 'typed-error'
+
+# Only load ngrok/tty when they are actually needed,
+# to reduce memory in the likely case they are never used.
+ngrok = null
+tty = null
+init = _.once ->
+	ngrok = Promise.promisifyAll require 'ngrok'
+	tty = Promise.promisifyAll require 'tty.js'

 class DisconnectedError extends TypedError

@@ -10,6 +17,7 @@
 apps = {}
 nextPort = 81
 exports.start = (app) ->
+	init()
 	apps[app.id] ?= Promise.rejected()
 	return apps[app.id] = apps[app.id].catch ->
 		port = nextPort++
@@ -27,6 +35,7 @@
 	if !apps[appId]?
 		return Promise.resolve()
 	apps[appId] = apps[appId].then (url) ->
+		# ngrok must have been loaded already or we wouldn't have a url to disconnect from.
 		ngrok.disconnectAsync(url)
 		.then ->
 			# We throw an error so that `.start` will catch and restart the session.


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -135,7 +135,7 @@
     height - Height of the meter
     position - An x, y object representing the position of the meter
     radius - Border radius of the meter
-    showName -
+    showName - Boolean to
   ###
   meter: (options={}) ->
     Object.reverseMerge options,


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -186,7 +186,27 @@
   ###
   showMeter: (name) ->
     setMeter(name, true)
-
+
+  ###*
+  Shows the named meter
+
+

+  player = GameObject
+
+  player.include Metered
+
+  # creates a health meter but disables visibility
+  player.meter 'health'
+    show: false
+
+  # enables visibility for the meter named 'health'
+  player.showMeter 'health'
+

+
+  @name meter
+  @methodOf Metered#
+  @param {String} name The name of the meter to toggle
+  ###
   hideMeter: (name) ->
     setMeter(name, false)


using explicit returns vs lots of "else if"s

--- src/methods.coffee
+++ src/methods.coffee
@@ -198,22 +198,24 @@
       trace "#{padding}resuming method #{sig}"
       @run_manually cf.resume, runtime_state
       cf.resume = null
-    else if trapped_methods[sig]
+      return
+    if trapped_methods[sig]
       trace "#{padding}entering trapped method #{sig}"
-      @run_manually trapped_methods[sig], runtime_state
-    else if @access_flags.native
+      return @run_manually trapped_methods[sig], runtime_state
+    if @access_flags.native
       if sig.indexOf('::registerNatives()V',1) >= 0 or sig.indexOf('::initIDs()V',1) >= 0
-        @run_manually ((rs)->), runtime_state # these are all just NOPs
-      else if native_methods[sig]
+        ms.pop() # these are all just NOPs
+        return
+      if native_methods[sig]
         trace "#{padding}entering native method #{sig}"
-        @run_manually native_methods[sig], runtime_state
-      else
-        try
-          util.java_throw runtime_state, 'java/lang/Error', "native method NYI: #{sig}"
-        finally
-          runtime_state.meta_stack().pop()
-    else if @access_flags.abstract
+        return @run_manually native_methods[sig], runtime_state
+      try
+        util.java_throw runtime_state, 'java/lang/Error', "native method NYI: #{sig}"
+      finally
+        runtime_state.meta_stack().pop()
+    if @access_flags.abstract
       util.java_throw runtime_state, 'java/lang/Error', "called abstract method: #{sig}"
-    else
-      trace "#{padding}entering method #{sig}"
-      @run_bytecode runtime_state, padding
+
+    # Finally, the normal case: running a Java method
+    trace "#{padding}entering method #{sig}"
+    @run_bytecode runtime_state, padding


adding native method FileInputStream::skip

--- src/natives.coffee
+++ src/natives.coffee
@@ -506,6 +506,19 @@
               else
                 throw e
         o 'close0()V', (rs, _this) -> _this.fields.$file = null
+        o 'skip(J)J', (rs, _this, n_bytes) ->
+            if (file = _this.fields.$file)?
+              bytes_left = fs.fstatSync(file).size - _this.fields.$pos
+              to_skip = Math.min(n_bytes.toNumber(), bytes_left)
+              _this.fields.$pos += to_skip
+              return gLong.fromNumber(to_skip)
+            # reading from System.in, do it async
+            num_skipped = null # will be filled in after the yield
+            rs.curr_frame().resume = -> gLong.fromNumber(num_skipped)
+            throw new util.YieldIOException (cb) ->
+              rs.async_input n_bytes.toNumber(), (bytes) ->
+                num_skipped = bytes.length  # we don't care about what the input actually was
+                cb()
       ]
       ObjectStreamClass: [
         o 'initNative()V', (rs) ->  # NOP


Add iban filter

Add iban filter

--- src/ng-iban.coffee
+++ src/ng-iban.coffee
@@ -43,3 +43,18 @@
           else modelValue

       return
+
+  .filter 'iban', ->
+    (string, separator) ->
+      parseIban = (value) ->
+        if value? then value.toUpperCase().replace /\s/g, '' else undefined
+
+      isValidIban = (value) ->
+        iban = parseIban(value)
+        IBAN.isValid iban
+
+      valid = isValidIban string
+      if valid
+        IBAN.printFormat string, separator
+      else
+        string


JProvisioner: update permissions, add accessLevel feature

--- workers/social/lib/social/models/computeproviders/provisioner.coffee
+++ workers/social/lib/social/models/computeproviders/provisioner.coffee
@@ -1,10 +1,10 @@

 jraphical   = require 'jraphical'
+crypto      = require 'crypto'
+
 JName       = require '../name'
 JUser       = require '../user'
 JGroup      = require '../group'
-JCredential = require './credential'
-crypto      = require 'crypto'

 module.exports = class JProvisioner extends jraphical.Module

@@ -24,15 +24,20 @@
     softDelete        : yes

     permissions       :
-      'create provisioner' : ['member']
-      'update provisioner' : ['member']
-      'list provisioners'  : ['member']
-      'delete provisioner' : ['member']
+
+      'create provisioner'     : ['member']
+      'list provisioners'      : ['member']
+
+      'update own provisioner' : ['member']
+      'delete own provisioner' : ['member']
+
+      'update provisioner'     : []
+      'delete provisioner'     : []

     sharedMethods     :
       static          :
         one           :
-          (signature String, Function)
+          (signature Object, Function)
         create        :
           (signature Object, Function)
         some          : [
@@ -42,12 +47,8 @@
       instance        :
         delete        :
           (signature Function)
-        shareWith     :
-          (signature Object, Function)
-        fetchUsers    :
-          (signature Function)
-        fetchData     :
-          (signature Function)
+        setAccess     :
+          (signature String, Function)
         update        :
           (signature Object, Function)

@@ -86,13 +87,19 @@
         type          : Object
         default       : -> { }

+      accessLevel     :
+        type          : String
+        enum          : ["Wrong access level specified!",
+          ["private", "group", "public"]
+        ]
+        default       : "private"
+
       originId        :
         type          : ObjectId
         required      : yes

       meta            : require 'bongo/bundles/meta'

-  @getName = -> 'JProvisioner'

   checkContent = (type, content)->

@@ -106,7 +113,7 @@
       switch type
         when "shell"
           unless content.script?
-            err = "Type 'shell' requires a 'script'"
+            err = "Type shell requires a `script`"
           else

             contentSum = crypto.createHash 'sha1'
@@ -169,92 +176,71 @@
           callback null, provisioner

-  @fetchBySlug = (client, slug, callback)->
-
-    options =
-      limit         : 1
-      targetOptions : selector : { slug }
-
-    {delegate} = client.connection
-    delegate.fetchProvisioner { }, options, (err, res)->
-
-      return callback err        if err?
-      return callback null, res  if res?
-
-      { group } = client.context
-      JGroup.one slug: group, (err, group)->
-        return callback err  if err?
-
-        group.fetchProvisioner { }, options, (err, res)->
-          callback err, res
-
-
-  @one$ = permit 'list provisioners',
-
-    success: (client, slug, callback)->
-
-      @fetchBySlug client, slug, callback
-
-
-  @some$ = permit 'list provisioners', success: JCredential.someHelper
-
-
-  fetchUsers: JCredential::fetchUsers
-
-
-  setPermissionFor: (target, {user, owner}, callback)->
-
-    Relationship.remove
-      targetId : @getId()
-      sourceId : target.getId()
-    , (err)=>
-
-      if user
-        as = if owner then 'owner' else 'user'
-        target.addProvisioner this, { as }, (err)-> callback err
-      else
-        callback err
-
-  # .share can be used like this:
-  #
-  # JProvisionerInstance.share { user: yes, owner: no, target: "gokmen"}, cb
-  #                                       group or user slug -> ^^^^^^
-
-  shareWith$: permit
+  someHelper = (client, selector, options, callback)->
+
+    [options, callback] = [callback, options]  unless callback
+    options ?= {}
+
+    { delegate } = client.connection
+
+    unless typeof selector is 'object'
+      return callback new KodingError "Invalid query"
+
+    selector.$and ?= []
+    selector.$and.push
+      $or : [
+        { originId      : delegate.getId() }
+        { accessLevel   : 'public' }
+        {
+          $and          : [
+            accessLevel : 'group'
+            group       : client.context.group
+          ]
+        }
+      ]
+
+    JProvisioner.some selector, options, (err, templates)->
+      callback err, templates
+
+
+  @some$ = permit 'list provisioners', success: someHelper
+
+  @one$  = permit 'list provisioners',
+
+    success: (client, selector, callback)->
+
+      someHelper client, selector, limit: 1, (err, provisioners)->
+        callback err, (provisioners[0]  if provisioners?)
+
+
+  delete: permit
+
+    advanced: [
+      { permission: 'delete own provisioner', validateWith: Validators.own }
+      # { permission: 'delete provisioner' }
+    ]
+
+    success: (client, callback)-> @remove callback
+
+
+  setAccess: permit
+
+    advanced: [
+      { permission: 'update own provisioner', validateWith: Validators.own }
+      # { permission: 'update provisioner' }
+    ]
+
+    success: (client, accessLevel, callback)->
+
+      @update $set: { accessLevel }, callback
+
+
+  update$: permit

     advanced: [
       { permission: 'update provisioner', validateWith: Validators.own }
     ]

-    success: JCredential::shareWith
-
-
-  delete: permit 'delete provisioner',
-
-    success: (client, callback)->
-
-      { delegate } = client.connection
-
-      Relationship.one
-        targetId : @getId()
-        sourceId : delegate.getId()
-      , (err, rel)=>
-
-        return callback err   if err?
-        return callback null  unless rel?
-
-        if rel.data.as is 'owner'
-          @remove callback
-        else
-          rel.remove callback
-
-
-  update$: permit
-
-    advanced: [
-      { permission: 'update provisioner', validateWith: Validators.own }
-    ]
-
     success: (client, options, callback)->

       {content, label} = options


Floats don't need that extra logic anymore since we use JavaScript NaNs. :)

--- src/opcodes.coffee
+++ src/opcodes.coffee
@@ -471,31 +471,19 @@
   95: new root.Opcode 'swap', {execute: (rs) -> v2=rs.pop(); v1=rs.pop(); rs.push2(v2,v1)}
   96: new root.Opcode 'iadd', { execute: (rs) -> rs.push (rs.pop()+rs.pop())|0 }
   97: new root.Opcode 'ladd', { execute: (rs) -> rs.push2(rs.pop2().add(rs.pop2()), null) }
-  98: new root.Opcode 'fadd', { execute: (rs) ->
-    a=rs.pop()
-    b=rs.pop()
-    rs.push if Number.isNaN(a) or Number.isNaN(b) then NaN else util.wrap_float(a+b) }
+  98: new root.Opcode 'fadd', { execute: (rs) -> rs.push util.wrap_float(rs.pop()+rs.pop()) }
   99: new root.Opcode 'dadd', { execute: (rs) -> rs.push2(rs.pop2()+rs.pop2(), null) }
   100: new root.Opcode 'isub', { execute: (rs) -> rs.push (-rs.pop()+rs.pop())|0 }
   101: new root.Opcode 'lsub', { execute: (rs) -> rs.push2(rs.pop2().negate().add(rs.pop2()), null) }
-  102: new root.Opcode 'fsub', { execute: (rs) ->
-    a=rs.pop()
-    b=rs.pop()
-    rs.push if Number.isNaN(a) or Number.isNaN(b) then NaN else util.wrap_float(-a+b) }
+  102: new root.Opcode 'fsub', { execute: (rs) -> rs.push util.wrap_float(-rs.pop()+rs.pop()) }
   103: new root.Opcode 'dsub', { execute: (rs) -> rs.push2(-rs.pop2()+rs.pop2(), null) }
   104: new root.Opcode 'imul', { execute: (rs) -> rs.push gLong.fromInt(rs.pop()).multiply(gLong.fromInt rs.pop()).toInt() }
   105: new root.Opcode 'lmul', { execute: (rs) -> rs.push2(rs.pop2().multiply(rs.pop2()), null) }
-  106: new root.Opcode 'fmul', { execute: (rs) ->
-    a=rs.pop()
-    b=rs.pop()
-    rs.push if Number.isNaN(a) or Number.isNaN(b) then NaN else util.wrap_float(a*b) }
+  106: new root.Opcode 'fmul', { execute: (rs) -> rs.push util.wrap_float(rs.pop()*rs.pop()) }
   107: new root.Opcode 'dmul', { execute: (rs) -> rs.push2(rs.pop2()*rs.pop2(), null) }
   108: new root.Opcode 'idiv', { execute: (rs) -> v=rs.pop();rs.push(util.int_div rs, rs.pop(), v) }
   109: new root.Opcode 'ldiv', { execute: (rs) -> v=rs.pop2();rs.push2(util.long_div(rs, rs.pop2(), v), null) }
-  110: new root.Opcode 'fdiv', { execute: (rs) ->
-    a=rs.pop()
-    b=rs.pop()
-    rs.push if Number.isNaN(a) or Number.isNaN(b) then NaN else util.wrap_float(b/a) }
+  110: new root.Opcode 'fdiv', { execute: (rs) -> a=rs.pop();rs.push util.wrap_float(rs.pop()/a) }
   111: new root.Opcode 'ddiv', { execute: (rs) -> v=rs.pop2();rs.push2(rs.pop2()/v, null) }
   112: new root.Opcode 'irem', { execute: (rs) -> v2=rs.pop();  rs.push util.int_mod(rs,rs.pop(),v2) }
   113: new root.Opcode 'lrem', { execute: (rs) -> v2=rs.pop2(); rs.push2 util.long_mod(rs,rs.pop2(),v2), null }


dont think we need that

dont think we need that

--- src/require.coffee
+++ src/require.coffee
@@ -32,7 +32,7 @@
     """
     eval src

-    module.exports or exports
+    module.exports

 resolve = (file) ->
   if /!/.test file


Fix SFUCopyrightComplianceNotice import in course form

--- gems/plugins/sfu_course_form/app/coffeescripts/bundles/course_request_form.coffee
+++ gems/plugins/sfu_course_form/app/coffeescripts/bundles/course_request_form.coffee
@@ -259,7 +259,7 @@

   ReactDOM.render(
     React.createElement(
-      SFUCopyrightComplianceNotice.default,
+      SFUCopyrightComplianceNotice,
       { className: 'sfu-ic-wizard-box__message-text__copyright_compliance' }
     ),
     document.getElementsByClassName('copyright')[0]


make warnings more clear

make warnings more clear

--- src/sequent.coffee
+++ src/sequent.coffee
@@ -62,7 +62,7 @@
                     else
                         console.log "wait ready but callback has already been executed" if DEBUG
                 else if finished > waits
-                    console.log "[warn] extra complete: #{finished} > #{waits}"
+                    console.log "[WARN] extra done() has been called: done=#{finished} > wait=#{waits}"
         else
             throw new Error "You have to specify the number of waits"

@@ -94,7 +94,7 @@
                 else
                     console.log "done but callback has already been executed" if DEBUG
             else if finished > waits
-                console.log "[warn] extra complete: #{finished} > #{waits}"
+                console.log "[WARN] extra done() has been called: done=#{finished} > wait=#{waits}"
         else
             console.log "done but callback hasn't ready" if DEBUG


fixed adding cahce newly

fixed adding cahce newly

--- src/service.coffee
+++ src/service.coffee
@@ -224,7 +224,7 @@
       if cache?.data?
         isCallCallback = true
         makeResult lang, cache.data, callback
-      if cache? and (new Date) - cache.ts > 1800000
+      if not cache? or (new Date) - cache?.ts > 1800000
         persistence.findScore lang, (err, cursor) ->
           if err?
             logger.error 'findScore', {err: err}
@@ -242,7 +242,7 @@
                 dailyData.push score

               # caching
-              persistence.upsertScoreCache dailyData, lang, ->
+              persistence.upsertScoreCache dailyData, lang, (err) ->

               makeResult(lang, dailyData, callback) if not isCallCallback
             else


some progress

some progress

--- src/setup_key.iced
+++ src/setup_key.iced
@@ -2,6 +2,7 @@
 {keyring} = require 'gpg-wrapper'
 {constants} = require './constants'
 {log} = require './log'
+{make_esc} = require 'iced-error'

 ##========================================================================

@@ -10,13 +11,14 @@
   constructor : () ->
     @master = keyring.master_ring()
     @id_email = "code@keybase.io"
+    @_key = null

   #------------

   run : (cb) ->
-    await @find_latest_key defer err, key
-    unless key?
-      await @install_key defer err, key
+    esc = make_esc cb, "SetupKeyRunner::run"
+    await @find_latest_key esc defer()
+    await @install_key esc defer() unless @_key?
     cb err

   #------------
@@ -24,7 +26,7 @@
   find_latest_key : (cb) ->
     esc = make_esc cb, "SetupKeyRunner::find_latest_key"
     em = constants.uid_email.code
-    err = ret = null
+    err = null
     await @master.read_uids_from_key {}, esc defer uids
     comments = (uid.comment for uid in uids when (uid.email is em))
     versions = (parseInt(m[1]) for c in comments when (m = c.match /v(\d+)/))
@@ -34,12 +36,13 @@
       max = Math.max versions...
       query = "(#{max}) <#{em}>"
       await @master.find_keys { query }, esc defer out
-      msg = if out.length is 0 then "Didn't find any key for query #{query}"
-      else if out.length > 1 then "Found too many keys that matched #{query}"
-      else null
-      if msg? then err = new Error msg
-      else ret = ids64[0]
-    cb err, ret
+      if out.length is 0     then err = new Error "Didn't find any key for query #{query}"
+      else if out.length > 1 then err = new Error "Found too many keys that matched #{query}"
+      else
+        @_key = @master.make_key { key_id_64 : ids64[0], username : em }
+        await @_key.load defer err
+        @_key = null if err
+    cb err

   #------------


Add submenu to context menu + Rename menu items

--- menus/list-edit.cson
+++ menus/list-edit.cson
@@ -2,22 +2,25 @@
 # TODO: Do we really want these in the context menu? Might be too much clutter.
 'context-menu':
   'atom-text-editor': [
-    {
-      'label': 'Select List Element'
-      'command': 'list-edit:select'
-    }
-    {
-      'label': 'Cut List Element'
-      'command': 'list-edit:cut'
-    }
-    {
-      'label': 'Copy List Element'
-      'command': 'list-edit:copy'
-    }
-    {
-      'label': 'Paste List Element'
-      'command': 'list-edit:paste'
-    }
+    label: 'List edit'
+    submenu: [
+      {
+        'label': 'Select'
+        'command': 'list-edit:select'
+      }
+      {
+        'label': 'Cut'
+        'command': 'list-edit:cut'
+      }
+      {
+        'label': 'Copy'
+        'command': 'list-edit:copy'
+      }
+      {
+        'label': 'Paste'
+        'command': 'list-edit:paste'
+      }
+    ]
   ]
 'menu': [
   {
@@ -35,19 +38,19 @@
       'label': 'List Edit'
       'submenu': [
         {
-          'label': 'Select List Element'
+          'label': 'List Select'
           'command': 'list-edit:select'
         }
         {
-          'label': 'Cut List Element'
+          'label': 'List Cut'
           'command': 'list-edit:cut'
         }
         {
-          'label': 'Copy List Element'
+          'label': 'List Copy'
           'command': 'list-edit:copy'
         }
         {
-          'label': 'Paste List Element'
+          'label': 'List Paste'
           'command': 'list-edit:paste'
         }
       ]


bound the definition class closer to the instance class so we can merge them

--- core/app/backbone/factlink/tooltip_definition.coffee
+++ core/app/backbone/factlink/tooltip_definition.coffee
@@ -28,24 +28,28 @@
       out: @_hoverTarget false

   close: ->
-    @instance?.remove()
-    @_open_tooltip = null #reuse hereafter is a bug: this causes a crash then.
+    @removeTooltip()
+    delete @_open_tooltip #reuse hereafter is a bug: this causes a crash then.
     @_options.$container.off(".hoverIntent") #unfortunately, we can't do better than this.

   _openInstance: ($target) ->
-    @_open_tooltip =
-      new TooltipInstance @_options, $target, ($tooltip) =>
-        delete @_open_tooltip
-        @_options.removeTooltip @_options.$container, $target, $tooltip
+    @$target = $target
+    @_open_tooltip = new Tooltip @_options, @$target, => @removeTooltip()
+
+  removeTooltip: =>
+    @_options.removeTooltip @_options.$container, @$target, @_open_tooltip._$tooltip
+    delete @_open_tooltip

   _hoverTarget: (target_is_hovered) -> (e) =>
-      $target = $(e.currentTarget)
-      if @_open_tooltip
-        @_open_tooltip.setTargetHover target_is_hovered
-      else if target_is_hovered
-        @_openInstance $target
+    if @_open_tooltip
+      setTargetHover target_is_hovered
+    else if target_is_hovered
+      @_openInstance $(e.currentTarget)

-class TooltipInstance #only local!
+  setTargetHover: (target_is_hovered) ->
+    @_open_tooltip.setTargetHover target_is_hovered
+
+class Tooltip #only local!
   constructor: (@_options, @$target, @closeCallback) ->
     @_inTooltip = false
     @_inTarget = true #opened on hover over target


fix twitter konnector counter

fix twitter konnector counter

--- server/konnectors/twitter.coffee
+++ server/konnectors/twitter.coffee
@@ -58,7 +58,7 @@

 saveTweets = (requiredFields, callback) ->
     url = "https://api.twitter.com/1.1/"
-    client = requestJson.newClient url
+    client = requestJson.createClient url
     client.options =
         oauth:
             consumer_key: requiredFields["consumerKey"]
@@ -110,7 +110,7 @@
             log.info 'No new tweet to import'
             callback null, 0
         else
-            log.info "#{tweets.length - tweetLength} tweet(s) to import"
+            log.info "#{tweets.length} tweet(s) fetched."
             tweets = tweets.reverse()
             tweets.pop() if path.indexOf('max_id') isnt -1

@@ -141,4 +141,5 @@
                 else
                     cb()
             , (err) ->
+                log.info "#{numItems} tweets imported"
                 callback err, numItems


add stub

add stub

--- apps/articles/test/client.coffee
+++ apps/articles/test/client.coffee
@@ -29,6 +29,7 @@
         sd: {}
         embedVideo: require('embed-video')
         moment: require('moment')
+        resize: sinon.stub()
       }, =>
         done()


Better logging when plan is null.

Better logging when plan is null.

--- services/web/app/coffee/Features/Subscription/SubscriptionViewModelBuilder.coffee
+++ services/web/app/coffee/Features/Subscription/SubscriptionViewModelBuilder.coffee
@@ -4,6 +4,7 @@
 SubscriptionFormatters = require("./SubscriptionFormatters")
 LimitationsManager = require("./LimitationsManager")
 SubscriptionLocator = require("./SubscriptionLocator")
+logger = require('logger-sharelatex')
 _ = require("underscore")

 module.exports =
@@ -17,7 +18,8 @@
 					return callback(error) if error?
 					plan = PlansLocator.findLocalPlanInSettings(subscription.planCode)
 					if !plan?
-						err = new Error('No plan found for planCode "#{subscription.planCode}"')
+						err = new Error("No plan found for planCode '#{subscription.planCode}'")
+						logger.error {user_id: user._id, err}, "error getting subscription plan for user"
 						return callback(err)
 					RecurlyWrapper.getSubscription subscription.recurlySubscription_id, (err, recurlySubscription)->
 						tax = recurlySubscription?.tax_in_cents || 0


Handle bogus IDB ConstraintError

Handle bogus IDB ConstraintError

--- assets/javascripts/app/db.coffee
+++ assets/javascripts/app/db.coffee
@@ -62,7 +62,7 @@
     objectStoreNames = $.makeArray(db.objectStoreNames)

     unless $.arrayDelete(objectStoreNames, 'docs')
-      db.createObjectStore('docs')
+      try db.createObjectStore('docs')

     for doc in app.docs.all() when not $.arrayDelete(objectStoreNames, doc.slug)
       try db.createObjectStore(doc.slug)


[hack] avoid  re: angular/angular.js#2491, angular/angular.js#1522. Re: filearts/plunker#49

--- assets/js/panes/previewer.coffee
+++ assets/js/panes/previewer.coffee
@@ -77,7 +77,7 @@
       $scope.expand = -> $scope.mode = "windowed"
       $scope.contract = -> $scope.mode = if pane.active then "embedded" else "disabled"

-      $scope.refresh = -> $timeout ->
+      $scope.refresh = ->
         return if $scope.mode is "disabled"

         form = document.createElement("form")
@@ -105,21 +105,26 @@
           when "embedded"
             childWindow?.close()
             childWindow = null
-
-            $timeout -> $scope.refresh() # Refresh on next tick to make sure DOM udpated first
           when "windowed"
             childWindow = window.open "about:blank", "plunkerPreviewTarget", "resizable=yes,scrollbars=yes,status=yes,toolbar=yes"
-            $timeout -> $scope.refresh() # Refresh on next tick to make sure DOM udpated first
+          else
+            return # Return to prevent refresh
+
+        # Refresh on next tick to make sure DOM udpated first
+        setTimeout -> $scope.$apply -> $scope.refresh()

       $scope.$watch "pane.active", (active) ->
         if !active and $scope.mode is "embedded" then $scope.mode = "disabled"
-        if active and $scope.mode is "disabled" then $timeout ->
+        if active and $scope.mode is "disabled" then setTimeout -> $scope.$apply ->
           $scope.mode = "embedded"

       $scope.$watch "session.updated_at", ->
         $scope.refresh() if settings.previewer.auto_refresh and (pane.active or $scope.mode is "windowed")
+
+      @startInterval($scope)

+    startInterval: ($scope) ->
       setInterval ->
         if $scope.mode is "windowed" and childWindow?.closed
           $scope.$apply ->


Minor stress test cleanup

Minor stress test cleanup

--- stress_test.coffee
+++ stress_test.coffee
@@ -1,3 +1,4 @@
+# vim: ts=2:sw=2:sta
 io = require('socket.io-client')

 url = process.env.TEST_TARGET || 'https://snap-backend-dev.herokuapp.com' #'http://localhost:8080'
@@ -6,8 +7,6 @@

 randWord = (len) ->
   Math.random().toString(36).substr(2, len)
-
-

 startPlayer = (name) ->
   socket = io(url)
@@ -21,7 +20,7 @@
       setInterval () ->
         w = randWord(2)
         socket.emit 'new word', w
-        #console.log "#{name} wrote: #{w}"
+        console.log "#{name} wrote: #{w}"
       , 1000
     , Math.random()*2000


check notification when ending game by agreement

Since I've messed with the code for generating the game-ended
notifications I thought I'd better add more testing in that regard.

--- app/coffee/tests/browser.coffee
+++ app/coffee/tests/browser.coffee
@@ -815,11 +815,22 @@
 class PassAndScoringTest extends BrowserTest
   names: ['PassAndScoringTest', 'pass', 'score', 'scoring']
   description: "pass moves and scoring system"
-  numTests: 62
+  numTests: 63
   testBody: (test) =>
     BLACK_EMAIL = 'black@schwarz.de'
     WHITE_EMAIL = 'white@wit.nl'
     clearGamesForPlayer p for p in [BLACK_EMAIL, WHITE_EMAIL]
+
+    casper.thenOpen serverUrl + "/logout"
+    casper.thenClick '#new_account', =>
+      @registerNewAccount BLACK_EMAIL, 'password'
+    casper.waitForSelector '#logout', ->
+      casper.click '#logout'
+
+    casper.thenOpen serverUrl
+    casper.thenClick '#new_account', =>
+      @registerNewAccount WHITE_EMAIL, 'password'
+
     createGame BLACK_EMAIL, WHITE_EMAIL, ['....b',
                                           'bb.wb',
                                           '...wb',
@@ -1005,6 +1016,14 @@
       test.assertDoesntExist (@lastGameSelector true), "no games on turn"
       test.assertDoesntExist (@lastGameSelector false), "no games off turn"

+    # White logs in and sees a notification about the finished game
+    createLoginSession WHITE_EMAIL
+    casper.thenOpen serverUrl
+    casper.waitForSelector '.notification', ->
+      test.assertSelectorHasText '.notification',
+        'Your game has ended, black won on points.',
+        'Check that White has the correct notification'
+
 registerTest new PassAndScoringTest

@@ -1063,8 +1082,8 @@

     # White logs in an is greated by a notification that they have won a game
     createLoginSession WHITE_EMAIL
-    casper.thenOpen serverUrl, ->
-      casper.waitForSelector '.unread.notification'
+    casper.thenOpen serverUrl
+    casper.waitForSelector '.unread.notification', ->
       test.assertSelectorHasText '.unread.notification',
         'Your game has ended, white won by resignation.',
         'Check that white has an unread notification'


breaknsplit: added menu entry

breaknsplit: added menu entry

--- app/layout.coffee
+++ app/layout.coffee
@@ -57,6 +57,9 @@
                     <li <%=(if @page == "bubbles" then 'class=active' else "") %> >
                     Bubbles

+                    <li <%=(if @page == "breaknsplit" then 'class=active' else "") %> >
+                    Break'n'split

+
                     <li <%=(if @page == "horizon" then 'class=active' else "") %> >
                     Horizon


fix hover on filtered options in popup

fix hover on filtered options in popup

--- app/components/eui-popup.coffee
+++ app/components/eui-popup.coffee
@@ -217,7 +217,7 @@
         @get('controller').hide()

       mouseEnter: ->
-        options = @get('controller.options')
+        options = @get('controller.filteredOptions')
         hoveredOption = @get('content')

         for option, index in options


Use elt instead of elem

Use elt instead of elem

--- test/spec/test_reactive.coffee
+++ test/spec/test_reactive.coffee
@@ -356,14 +356,13 @@

 describe 'rxv', ->
   describe 'object creation', ->
-    elem = null
+    elt = null
     beforeEach ->
-      elem = rxv.tags.rect {x:10, y:20, height:100, width:100, fill:"blue", stroke: "red"}
+      elt = rxv.tags.rect {x:10, y:20, height:100, width:100, fill:"blue", stroke: "red"}
     it 'should have the right tag', ->
-      expect(elem).not.toBeUndefined()
-      expect(elem instanceof SVGRectElement).toBe(true)
+      expect(elt).not.toBeUndefined()
+      expect(elt instanceof SVGRectElement).toBe(true)
     it 'should have the set attributes', ->
-      expect(true).toBe(true)
       #expect(elt.prop('class')).toBe('my-class')
       #expect(elt.prop('id')).toBe('my-elt')
       #expect(elt.hasClass('my-class')).toBe(true)


avoid unnecessary call to insert packs into index

--- app/coffee/PackManager.coffee
+++ app/coffee/PackManager.coffee
@@ -261,7 +261,7 @@
 		# find all packs prior to current pack
 		PackManager.findUnindexedPacks project_id, doc_id, (err, newPacks) ->
 			return callback(err) if err?
-			return callback() if not newPacks?
+			return callback() if not newPacks? or newPacks.length is 0
 			PackManager.insertPacksIntoIndexWithLock project_id, doc_id, newPacks, (err) ->
 				return callback(err) if err?
 				logger.log {project_id, doc_id, newPacks}, "added new packs to index"


Make compatible w/ Atom 1.15 and below

Make compatible w/ Atom 1.15 and below

--- spec/autosave-spec.coffee
+++ spec/autosave-spec.coffee
@@ -17,9 +17,14 @@
       initialActiveItem = atom.workspace.getActiveTextEditor()

     waitsForPromise ->
-      atom.workspace.createItemForURI('sample.coffee').then (o) ->
-        otherItem1 = o
-        otherItem2 = otherItem1.copy()
+      if atom.workspace.createItemForURI?
+        atom.workspace.createItemForURI('sample.coffee').then (o) ->
+          otherItem1 = o
+          otherItem2 = otherItem1.copy()
+      else
+        atom.workspace.open('sample.coffee', {activateItem: false}).then (o) ->
+          otherItem1 = o
+          otherItem2 = otherItem1.copy()

     runs ->
       spyOn(initialActiveItem, 'save')


Added metalsmith minify to the build command.

--- .metalsmith/gulpfile.coffee
+++ .metalsmith/gulpfile.coffee
@@ -137,5 +137,7 @@

 gulp.task 'watch', ['watch:md']
 gulp.task 'watch:all', ['watch:md', 'watch:code', 'watch:sass']
-gulp.task 'build', ['metalsmith', 'sass', 'static', 'staticDocuments', 'coffee']
+gulp.task 'build', [
+  'metalsmith:minify'
+  'sass', 'static', 'staticDocuments', 'coffee']
 gulp.task 'default', ['build']


Updated test to utilize new time period inference functionality

--- tests/models/test_punch.coffee
+++ tests/models/test_punch.coffee
@@ -90,8 +90,9 @@
       now = moment()
       amPm = now.format('A')
       expectedTime = moment("9:15 #{amPm}", 'h:mm A')
-      if expectedTime.isAfter(now) and amPm is 'PM'
-        expectedTime.subtract(12, 'hours')
+      if expectedTime.isAfter now
+        if expectedTime.diff(now, 'hours', true) > 6
+          expectedTime.subtract(12, 'hours')
       expect(punch.times[0].format('hh:mm:ss A')).to.equal expectedTime.format('hh:mm:ss A')
       expect(punch).to.have.property 'projects'
       expect(punch.projects).to.be.empty


changes to the server start ups to accomodate for upcoming


adds setTimeout+

adds setTimeout+

--- spec/HelloWorldSpec.coffee
+++ spec/HelloWorldSpec.coffee
@@ -1,13 +1,16 @@
 describe 'Hello Word', ->
-  beforeEach ->
+  $widget = null
+
+  beforeEach (done) ->
     jasmine.getFixtures().set(
       '
FIXTURE
' +
-      '' +
-      ''
+      ''
     )
+    $widget = $j('.eps-site-widget')
+    expect($widget.text()).toEqual("FIXTURE")
+    setTimeout(done, 100)

   it 'works', ->
-    $widget = $j('.eps-site-widget')
     expect($widget).toExist()
     expect($widget.data("shopid")).toEqual("DemoShop")
-    expect($widget.text()).toEqual("ASDF")
+    expect($widget.text()).toEqual("WIDGET")


More error handling

More error handling

--- spec/RemoteRuntimes.coffee
+++ spec/RemoteRuntimes.coffee
@@ -36,6 +36,7 @@
         done()
       else
         utils.createServer port, (err, s) ->
+          return done err if err
           console.log "WebSocket runtime running in port #{port}"
           server = s
           done()
@@ -182,6 +183,7 @@
         done()
       else
         utils.createNoFloServer port, (err, s) ->
+          return done err if err
           console.log "NoFlo server running in port #{port}"
           server = s
           done()


Stop using Project::open

Stop using Project::open

--- spec/find-view-spec.coffee
+++ spec/find-view-spec.coffee
@@ -704,7 +704,7 @@
           newEditor = null

           waitsForPromise ->
-            atom.project.open('sample.coffee').then (o) -> newEditor = o
+            atom.workspace.open('sample.coffee', activateItem: false).then (o) -> newEditor = o

           runs ->
             newEditor = atom.workspace.paneForItem(editor).splitRight(items: [newEditor]).getActiveItem()


add test for the issue

add test for the issue

--- spec/operators-spec.coffee
+++ spec/operators-spec.coffee
@@ -946,6 +946,15 @@
         it "inserts the contents", ->
           expect(editor.getText()).toBe "034512\n"
           expect(editor.getCursorScreenPosition()).toEqual [0, 3]
+
+      describe "at the end of a line", ->
+        beforeEach ->
+          editor.setCursorScreenPosition [0, 2]
+          keydown('p')
+
+        it "positions cursor correctly", ->
+          expect(editor.getText()).toBe "012345\n"
+          expect(editor.getCursorScreenPosition()).toEqual [0, 5]

       describe "when useClipboardAsDefaultRegister enabled", ->
         it "inserts contents from clipboard", ->


Wait before clean up due to async nature of tests

This fixes uncaught exceptions about pane being destroyed.

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -47,6 +47,7 @@
       expect(treeView.roots[0].directory.watchSubscription).toBeTruthy()

   afterEach ->
+    waits 10 # The async nature of specs causes uncaught exceptions, so wait a little bit before we cleanup
     temp.cleanup()

   describe ".initialize(project)", ->


Drop space-pen-extensions from WorkspaceSpec

--- spec/workspace-spec.coffee
+++ spec/workspace-spec.coffee
@@ -2,7 +2,6 @@
 temp = require 'temp'
 Workspace = require '../src/workspace'
 Pane = require '../src/pane'
-{View} = require '../src/space-pen-extensions'
 platform = require './spec-helper-platform'
 _ = require 'underscore-plus'
 fstream = require 'fstream'


:fire: Remove test focus as it's no longer available on Travis

--- spec/pigments-spec.coffee
+++ spec/pigments-spec.coffee
@@ -3,7 +3,7 @@

 SERIALIZE_VERSION = "1.0.1"

-fdescribe "Pigments", ->
+describe "Pigments", ->
   [workspaceElement, pigments, project] = []

   beforeEach ->


No velocity

No velocity

--- source/quo.gestures.swipe.coffee
+++ source/quo.gestures.swipe.coffee
@@ -18,14 +18,10 @@

   handler : do (base = Quo.Gestures) ->
     GAP = (if window.devicePixelRatio >= 2 then 15 else 20)
-    VELOCITY_SMOOTHING = .5
     _target = null
     _start = null
     _start_axis = null
     _last = null
-    is_first = _last is null
-    _timeStamp = new Date().getTime()
-    dt = (if is_first then 1 else _timeStamp - _last.timeStamp)

     start = (target, data) ->
       if data.length is 1
@@ -35,10 +31,9 @@

     move = (target, data) ->
       if data.length is 1
-        delta = x: (data[0].x - _start.x), y: (data[0].y - _start.y),
-          vx: (if is_first then (data[0].x - _start.x) / dt else _last.delta.vx * VELOCITY_SMOOTHING + ((data[0].x - _last.x) / dt) * (1 - VELOCITY_SMOOTHING)),
-          vy: (if is_first then (data[0].x - _start.y) / dt else _last.delta.vy * VELOCITY_SMOOTHING + ((data[0].y - _last.y) / dt) * (1 - VELOCITY_SMOOTHING))
-        _last = x: data[0].x, y: data[0].y, timeStamp: _timeStamp, delta: delta
+        delta = x: (data[0].x - _start.x), y: (data[0].y - _start.y)
+        is_first = _last is null
+        _last = x: data[0].x, y: data[0].y, delta: delta
         _check(true, is_first)
       else
         _last = null


Fixed test to correctly check quoted bracket behavior.

--- spec/bracket-matcher-spec.coffee
+++ spec/bracket-matcher-spec.coffee
@@ -117,7 +117,7 @@
         editor.setCursorBufferPosition([0, 0])
         expectHighlights([0, 0], [0, 4])

-        editor.setCursorBufferPosition([0, 4])
+        editor.setCursorBufferPosition([0, 5])
         expectHighlights([0, 4], [0, 0])

         editor.setCursorBufferPosition([0, 2])
@@ -127,7 +127,7 @@
         editor.setCursorBufferPosition([0, 0])
         expectHighlights([0, 0], [0, 4])

-        editor.setCursorBufferPosition([0, 4])
+        editor.setCursorBufferPosition([0, 5])
         expectHighlights([0, 4], [0, 0])

         editor.setCursorBufferPosition([0, 2])


payment: show payment workflow when a plan is selected in pricing page.

--- client/Pricing/AppView.coffee
+++ client/Pricing/AppView.coffee
@@ -44,7 +44,11 @@

   initEvents: ->
-    @plans.on 'PlanSelected', ->
+    @plans.on 'PlanSelected', @bound 'planSelected'
+
+
+  planSelected: (name, price) ->
+    workflowController = new PaymentWorkflow {name, price, view: this}

   pistachio: ->


Fix Computed Prices When Price is Unknown

--- client/app/books/model.coffee
+++ client/app/books/model.coffee
@@ -11,13 +11,13 @@

   currentPrice: (->
     ps = @get('prices')
-    ps[ps.length - 1]?.value or 0
+    ps?[ps.length - 1]?.value or 0
   ).property('prices')

   # second to last price
   penultimatePrice: (->
     ps = @get('prices')
-    ps[ps.length - 2]?.value or 0
+    ps?[ps.length - 2]?.value or 0
   ).property('prices')

   trend: (->


:lipstick: Avoid storing plugin in the test

--- spec/minimap-plugins-spec.coffee
+++ spec/minimap-plugins-spec.coffee
@@ -2,6 +2,7 @@
 {WorkspaceView} = require 'atom'

 describe "Minimap Plugins", ->
+  [plugin] = []
   beforeEach ->
     runs ->
       atom.workspaceView = new WorkspaceView
@@ -16,14 +17,14 @@
       atom.config.set 'minimap.displayPluginsControls', true
       atom.config.set 'minimap.plugins.dummy', undefined

-    @plugin =
+    plugin =
       active: false
       activatePlugin: -> @active = true
       deactivatePlugin: -> @active = false
       isActive: -> @active

-    spyOn(@plugin, 'activatePlugin').andCallThrough()
-    spyOn(@plugin, 'deactivatePlugin').andCallThrough()
+    spyOn(plugin, 'activatePlugin').andCallThrough()
+    spyOn(plugin, 'deactivatePlugin').andCallThrough()

     @registerHandler = jasmine.createSpy('register handler')
     @unregisterHandler = jasmine.createSpy('unregister handler')
@@ -32,10 +33,10 @@

     beforeEach ->
       Minimap.onDidAddPlugin @registerHandler
-      Minimap.registerPlugin 'dummy', @plugin
+      Minimap.registerPlugin 'dummy', plugin

     it 'should be available in the minimap', ->
-      expect(Minimap.plugins['dummy']).toBe(@plugin)
+      expect(Minimap.plugins['dummy']).toBe(plugin)

     it 'should have emit an event', ->
       expect(@registerHandler).toHaveBeenCalled()
@@ -51,7 +52,7 @@
         atom.workspaceView.trigger 'minimap:toggle-dummy'

       it 'should have received a deactivation call', ->
-        expect(@plugin.deactivatePlugin).toHaveBeenCalled()
+        expect(plugin.deactivatePlugin).toHaveBeenCalled()

     describe 'and then unregistered', ->
       beforeEach ->
@@ -65,28 +66,28 @@
           atom.config.set 'minimap.plugins.dummy', false

         it 'should not receive an activation call', ->
-          expect(@plugin.deactivatePlugin).not.toHaveBeenCalled()
+          expect(plugin.deactivatePlugin).not.toHaveBeenCalled()

   describe 'the registered plugin', ->
     it 'should have received an activation call', ->
-      Minimap.registerPlugin 'dummy', @plugin
-      expect(@plugin.activatePlugin).toHaveBeenCalled()
+      Minimap.registerPlugin 'dummy', plugin
+      expect(plugin.activatePlugin).toHaveBeenCalled()

     describe 'when the config for it is false', ->
       beforeEach ->
         atom.config.set 'minimap.plugins.dummy', false
-        Minimap.registerPlugin 'dummy', @plugin
+        Minimap.registerPlugin 'dummy', plugin

       it 'should not have received an activation call', ->
-        expect(@plugin.activatePlugin).not.toHaveBeenCalled()
+        expect(plugin.activatePlugin).not.toHaveBeenCalled()

     describe 'when the config is modified after registration', ->
       beforeEach ->
-        Minimap.registerPlugin 'dummy', @plugin
+        Minimap.registerPlugin 'dummy', plugin
         atom.config.set 'minimap.plugins.dummy', false

       it 'should have received a deactivation call', ->
-        expect(@plugin.deactivatePlugin).toHaveBeenCalled()
+        expect(plugin.deactivatePlugin).toHaveBeenCalled()

   describe 'on minimap activation', ->
     beforeEach ->


Update atom config

Update atom config

--- .atom/config.cson
+++ .atom/config.cson
@@ -5,9 +5,14 @@
     'softWrapped': true
     'showIndentGuide': true
     'fontSize': 12
+    'invisibles': {}
   'core':
     'disabledPackages': [
       'metrics'
+    ]
+    'themes': [
+      'atom-dark-ui'
+      'glitterbomb'
     ]
   'exception-reporting':
     'userId': '2f6c1fe2-cfdb-87de-4712-11b4b505fed4'
@@ -15,4 +20,3 @@
     'showOnStartup': false
   'atom-beautify':
     '_analyticsUserId': '1d4703af-c0bf-4e61-8894-f8865aaabf2b'
-  'autoprefixer': {}


Add DisplayLayer::refreshMarkerScreenPositions

--- src/display-marker-layer.coffee
+++ src/display-marker-layer.coffee
@@ -23,6 +23,11 @@

   onDidCreateMarker: (callback) ->
     @emitter.on('did-create-marker', callback)
+
+  refreshMarkerScreenPositions: ->
+    for marker in @getMarkers()
+      marker.notifyObservers(textChanged: false)
+    return

   markScreenRange: (screenRange, properties) ->
     screenRange = Range.fromObject(screenRange)


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -20,7 +20,7 @@
     height = 450 #height
     radiuso = 150 #outer radius
     radiusi = 100 #inner radius
-    labelRadius = 250
+    labelRadius = 175

     #color = d3.scale.ordinal()
       #.domain([1,10])


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -19,7 +19,8 @@
     width = 750 #width
     height = 450 #height
     radiuso = 150 #outer radius
-    radiusi = 60 #inner radius
+    radiusi = 100 #inner radius
+    labelRadius = 180

     color = d3.scale.ordinal()
       #.domain([1,10])
@@ -87,7 +88,6 @@
         .text(sum)
         .attr("filter","url(#dropshadow)")

-      labelr = 120
       arcs.append("svg:text").attr("transform", (d, i) ->
         #procent_val = Math.round(data[i].value/sum * 100)
         #d.innerRadius = (radiuso * (100-procent_val)/100) - radiuso/2 #45  #45=max text size/2
@@ -98,7 +98,7 @@
         x = c[0]
         y = c[1]
         h = Math.sqrt(x*x + y*y)
-        return "translate(" + (x/h * labelr) + "," + (y/h * labelr) + ")"
+        return "translate(" + (x/h * labelRadius) + "," + (y/h * labelRadius) + ")"
         )
         .attr('fill', "#fff")
         .attr("text-anchor", "middle").text((d, i) ->


Fixed problems with reset_password and change_passwords urls

--- clients/antranet/app/coffee/services/apiurl.coffee
+++ clients/antranet/app/coffee/services/apiurl.coffee
@@ -3,8 +3,8 @@
         urls =
             "login": "/api/v1/auth/login/"
             "logout": "/api/v1/auth/logout/"
-            "reset-password": "/api/v1/auth/reset-password/"
-            "change-password": "/api/v1/auth/change-password/"
+            "reset-password": "/api/v1/auth/reset_password/"
+            "change-password": "/api/v1/auth/change_password/"
             "parts": "/api/v1/parts/"
             "talks": "/api/v1/talks/"
             "projects": "/api/v1/projects/"


Add alignment options to the TinyMCE platform plugin

--- common/lib/xmodule/xmodule/js/src/html/edit.coffee
+++ common/lib/xmodule/xmodule/js/src/html/edit.coffee
@@ -83,6 +83,7 @@
         image_advtab: true,
         # We may want to add "styleselect" when we collect all styles used throughout the LMS
         toolbar: "formatselect | fontselect | bold italic underline forecolor wrapAsCode | " +
+          "alignleft aligncenter alignright alignjustify | " +
           "bullist numlist outdent indent blockquote | link unlink " +
           "#{if @new_image_modal then 'insertImage' else 'image'} | code",
         block_formats: interpolate("%(paragraph)s=p;%(preformatted)s=pre;%(heading3)s=h3;%(heading4)s=h4;%(heading5)s=h5;%(heading6)s=h6", {


Label: Fixed "parseLinks" function for "https://", added email recognition for "mailto://".

--- src/elements/Label/Label.coffee
+++ src/elements/Label/Label.coffee
@@ -287,19 +287,28 @@
 			return

 		for el, idx in txt.split(/(\n| )/)
-			m = el.match("^(?:(www)\..+|http:\/\/|https:\/\//)")
-			if not m
+
+			if CUI.EmailInput.regexp.exec(el)
+				a_node = CUI.dom.element("a", href: "mailto:"+el)
+			else
+				m = el.match("^(?:www\..+\..+|(http(?:s|)):\/\/)")
+				if not m
+					a_node = null
+				else
+					if not m[1]
+						prefix = "http://"
+					else
+						prefix = "" # use the given prefix
+
+					a_node = CUI.dom.element("a", target: "_blank", href: prefix+el)
+
+			if not a_node
 				text.push(el)
 				continue

 			# append the link
 			append_text()
-			if m[1]
-				prefix = "http://"
-			else
-				prefix = ""
-
-			a_node = CUI.dom.element("a", target: "_blank", href: prefix+el)
+
 			a_node.textContent = el
 			nodes.push(a_node)


Fix root route with ember.

Fix root route with ember.

For some reason / was being transformed to /root this aims to work around that. I don't know enough about the integration of everything to make this more elegant.

--- packages/tower-net/client/states.coffee
+++ packages/tower-net/client/states.coffee
@@ -120,7 +120,8 @@

         # @todo tmp hack
         if r[i] == r[0] || r[i] == 'new'
-          routeName += r[i]
+          if r[i] != 'root'
+            routeName += r[i]

         # @todo tmp hack
         # Basically, create methods like `showUser` and `indexUsers`, which


Update error codes for gplus and facebook

--- app/views/core/AuthModal.coffee
+++ app/views/core/AuthModal.coffee
@@ -104,7 +104,7 @@

   onGPlusLoginError: (res) ->
     @$('#unknown-error-alert').addClass('hide')
-    if res.errorID and res.errorID is 'no-individuals'
+    if res.errorID and res.errorID is 'individuals-not-supported'
       forms.setErrorToProperty(@$el, 'emailOrUsername', $.i18n.t('login.individual_users_not_supported'))
       showingError = true
       @$('#unknown-error-alert').removeClass('hide')
@@ -141,7 +141,7 @@

   onFacebookLoginError: (res) ->
     @$('#unknown-error-alert').addClass('hide')
-    if res.errorID and res.errorID is 'no-individuals'
+    if res.errorID and res.errorID is 'individuals-not-supported'
       forms.setErrorToProperty(@$el, 'emailOrUsername', $.i18n.t('login.individual_users_not_supported'))
       showingError = true
       @$('#unknown-error-alert').removeClass('hide')


RandomArray is Node.js compatible now

RandomArray is Node.js compatible now

--- components/RandomArray.coffee
+++ components/RandomArray.coffee
@@ -12,19 +12,14 @@
     @seed = null
     @prng = seedrandom('')

-    @inPorts = new noflo.InPorts
-      seed:
-        datatype: 'string'
-      min:
-        datatype: 'number'
-      max:
-        datatype: 'number'
-      count:
-        datatype: 'int'
+    @inPorts =
+      seed: new noflo.Port 'string'
+      min: new noflo.Port 'number'
+      max: new noflo.Port 'number'
+      count: new noflo.Port 'int'

-    @outPorts = new noflo.OutPorts
-      numbers:
-        datatype: 'array'
+    @outPorts =
+      numbers: new noflo.Port 'array'

     @inPorts.seed.on 'data', (data) =>
       @seed = data


Delete configuration of request

Delete configuration of request

--- components/SendRequest.coffee
+++ components/SendRequest.coffee
@@ -1,5 +1,4 @@
 noflo = require 'noflo'
-coap = require 'coap'

 # @runtime noflo-nodejs

@@ -9,49 +8,11 @@
   c.description = 'Send response'

   # Add input ports
-  c.inPorts.add 'hostname',
-    datatype: 'string'
+  c.inPorts.add 'url',
+    datatype: 'object'
     require: yes
-    default: 'localhost'
-  c.inPorts.add 'port',
-    datatype: 'number'
-    require: yes
-    default: 5683
-  c.inPorts.add 'method',
-    datatype: 'string'
-    require: yes
-    values: ['GET', 'POST', 'PUT', 'DELETE']
-    default: 'GET'
-  c.inPorts.add 'confirmable',
-    datatype: 'boolean'
-    require: yes
-    default: true
-  c.inPorts.add 'observe',
-    datatype: 'boolean'
-    require: yes
-    default: false
-  c.inPorts.add 'pathname',
-    datatype: 'string'
-    require: yes
-    default: '/'
-  c.inPorts.add 'query',
-    datatype: 'string'
-    require: yes
-    default: ''
-  c.inPorts.add 'options',
+  c.inPorts.add 'coap',
     datatype: 'object'
-  c.inPorts.add 'blockwise',
-    datatype: 'boolean'
-    default: false
-  c.inPorts.add 'agent',
-    datatype: 'string'
-    require: yes
-    values: ['undefined', 'udp4', 'udp6', 'false']
-    default: 'undefined'
-  c.inPorts.add 'proxy_uri',
-    datatype: 'string'
-  c.inPorts.add 'start',
-    datatype: 'bang'
     require: yes

   # Add output ports
@@ -59,42 +20,16 @@
     datatype: 'object'

   noflo.helpers.WirePattern c,
-    in: ['hostname', 'port']
-    params: [
-      'method'
-      'confirmable'
-      'observe'
-      'pathname'
-      'query'
-      'options'
-      'agent'
-      'proxy_uri'
-      'blockwise'
-    ]
+    in: 'coap'
+    params: 'url'
     out: 'request'
     forwardGroups: true
-    ordered: true
   , (data, groups, out) ->
-    req =
-      hostname: data.hostname
-      port: data.port
-      method: c.params.method
-      confirmable: c.params.confirmable
-      observe: c.params.observe
-      pathname: c.params.pathname
-      query: c.params.query
-      options: c.params.options
-      agent: c.params.agent
-      proxyUri: c.params.proxy_uri

-    blockwise = c.params.blockwise
+    req = data.request c.params.url

-    console.log(req, blockwise)
-
-    request = coap.request('coap://localhost/Matteo')
-
-    request.end ->
-      out.send request
+    req.end ->
+      out.send req

   # Finally return the component instance
   c


Clean up setupParse() output handling

Clean up setupParse() output handling

--- src/ext/modules/routines.coffee
+++ src/ext/modules/routines.coffee
@@ -1108,11 +1108,21 @@
       else
         assist importFiles

+  extendParseSetupResults = (parseSetupResults) ->
+    render_ parseSetupResults, -> H2O.SetupParseOutput _, parseSetupResults
+
+  requestParseSetup = (sourceKeys, go) ->
+    _.requestParseSetup sourceKeys, (error, parseSetupResults) ->
+      if error
+        go error
+      else
+        go null, extendParseSetupResults parseSetupResults
+
+
   setupParse = (sourceKeys) ->
     switch typeOf sourceKeys
       when 'Array'
-        renderable _.requestParseSetup, sourceKeys, (parseSetupResults, go) ->
-          go null, H2O.SetupParseOutput _, parseSetupResults
+        _fork requestParseSetup, sourceKeys
       else
         assist setupParse


clean up test

clean up test

--- test/client.coffee
+++ test/client.coffee
@@ -460,23 +460,17 @@
         test.done()

   'Can open a document after closing a document': (test) ->
-    console.log "PORT: #{@port}"
-    client.open @name, 'text', "http://localhost:#{@port}/sjs", (error, doc1) =>
-      console.log "ERROR: #{error} DOC1: #{doc1}"
+    port = @port
+    name = @name
+    client.open name, 'text', "http://localhost:#{port}/sjs", (error, doc1) =>
       test.ifError error
       test.ok doc1
       doc1.close ->
-        console.log "PORT: #{@port}"
-        client.open @name + '2', 'text', "http://localhost:#{@port}/sjs", (error, doc2) =>
-          console.log "ERROR: #{error} DOC2: #{doc2}"
+        client.open name + '1', 'text', "http://localhost:#{port}/sjs", (error, doc2) ->
           test.ifError error
           test.ok doc2
           doc2.close ->
             test.done()
-        setTimeout ->
-          test.ok false
-          test.done()
-        , 1000

    # ** This is missing tests for submitOp receiving an error through its callback

@@ -491,4 +485,4 @@
 #      test.done()

 exports.native = genTests nativeclient
-exports.webclient = genTests webclient
+# exports.webclient = genTests webclient


h2 added, cart items rearranged

h2 added, cart items rearranged

--- public/views/cart_detail_view.coffee
+++ public/views/cart_detail_view.coffee
@@ -10,11 +10,11 @@
     "change .epages-cart-overlay-line-item-quantity": "sync"

   template: _.template """
-

-

+

+

-      <% if (failedToCreateCart) { %>
-

+    <% if (failedToCreateCart) { %>
+

       <% } %>

@@ -22,11 +22,16 @@

-

+
+<<<<<<< HEAD

-
+
+=======
+
+
+>>>>>>> styles-and-optimisation

@@ -34,35 +39,34 @@
             <% if (deliveryPrice) { %>

+
                 <%= deliveryPrice.formatted %>
+

+              <% } %>
+

+
+

+

+                     <a href="<%= shippingUrl %>" target="_blank" data-i18n='shipping'>.
+

+

+
+                  <%= subTotal %>
+

-            <% } %>
-

-
-

-

-                   <a href="<%= shippingUrl %>" target="_blank" data-i18n='shipping'>.
-

-
-
-                <%= subTotal %>
-
-
-

-

-

-
-

-
-        <button class="epages-cart-overlay-checkout-button" data-i18n='checkout'></button>
+

+

+

+

+

+          <button class="epages-cart-overlay-checkout-button" data-i18n='checkout'></button>

+

+

+

+

-
-

-

-

-

-  """
+"""

   render: ->
     @$el.html @template


Workspace: An intentional error check for tabbed editor.

--- client/landing/app/CommonViews/workspace/panes/collaborativetabbededitorpane.coffee
+++ client/landing/app/CommonViews/workspace/panes/collaborativetabbededitorpane.coffee
@@ -116,6 +116,7 @@
   openFile: CollaborativeTabbedEditorPane::createEditorInstance

   handlePaneResized: ->
+    return unless @parent
     @tabView.setHeight @parent.getHeight() - 22
     for pane in @tabView.panes
       pane.subViews[0].codeMirrorEditor.refresh()


Consolidated error messages

Consolidated error messages

--- src/ko-singlepage-router.coffee
+++ src/ko-singlepage-router.coffee
@@ -1,9 +1,14 @@
 class KnockoutSinglePageRouter
+	errorMessages:
+		invalidRoute: 'Invalid route'
+		invalidRouteName: 'Route has no name'
+		invalidRouteUrl: 'Route has an invalid URL'
+
 	constructor: (routes) ->
-		@routes = routes.map (route) ->
-			throw 'Invalid route' unless route
-			throw 'Route has no name' unless route.name
-			throw 'Route has an invalid URL' unless route.url
+		@routes = routes.map (route) =>
+			throw @errorMessages.invalidRoute unless route
+			throw @errorMessages.invalidRouteName unless route.name
+			throw @errorMessages.invalidRouteUrl unless route.url

 			paramRegex = /:([a-z][a-z0-9]+)/ig
 			url = (if route.url[0] == '/' then route.url else '/' + route.url).trim()
@@ -11,9 +16,8 @@
 			parameters = route.url.match(paramRegex)
 			name = route.name.trim()

-			throw 'Invalid route' unless route
-			throw 'Route has no name' unless name
-			throw 'Route has an invalid URL' unless url
+			throw @errorMessages.invalidRouteName unless name
+			throw @errorMessages.invalidRouteUrl unless url

 			ko.components.register route.name, route.component if route.component


model_postgres walkListenerArchive: replace DISTINCT with GROUP BY

--- src/local/model_postgres.coffee
+++ src/local/model_postgres.coffee
@@ -626,7 +626,7 @@
                 cb2()

         async.parallel [ (cb2) ->
-            db.query "SELECT DISTINCT node FROM node_config WHERE node in (SELECT node FROM subscriptions WHERE listener=$1) #{conds} ORDER BY updated DESC #{limit}", params
+            db.query "SELECT node, MAX(updated) AS updated FROM node_config WHERE node in (SELECT node FROM subscriptions WHERE listener=$1) #{conds} GROUP BY node ORDER BY updated DESC #{limit}", params
             , (err, res) =>
                 if err
                     return cb2 err


Clean up math me

Clean up math me

--- src/hubot/scripts/math.coffee
+++ src/hubot/scripts/math.coffee
@@ -6,11 +6,14 @@
   robot.respond /(calc|calculate|convert|math)( me)? (.*)/i, (msg) ->
     msg
       .http('http://www.google.com/ig/calculator')
-      .query(hl: 'en', q: msg.match[3])
+      .query
+        hl: 'en'
+        q: msg.match[3]
       .headers
-        'Accept-Language' : 'en-us,en;q=0.5',
-        'Accept-Charset'  : 'utf-8',
-        'User-Agent' : "Mozilla/5.0 (X11; Linux x86_64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"
+        'Accept-Language': 'en-us,en;q=0.5',
+        'Accept-Charset': 'utf-8',
+        'User-Agent': "Mozilla/5.0 (X11; Linux x86_64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"
       .get() (err, res, body) ->
-        json = eval("(#{body})") # non-string json keys
-        msg.send json.rhs
+        # Response includes non-string keys, so we can't use JSON.parse here.
+        json = eval("(#{body})")
+        msg.send json.rhs || 'Could not compute.'


Improve coding 1. Remove unnecessary {} from object literals 2. Rename unnecessary variables

--- src/account/summary.coffee
+++ src/account/summary.coffee
@@ -40,9 +40,8 @@
 	'''
 	refresh_fines_summary = ->
 		$('#fines_summary').openils 'fines summary', 'actor.user.fines.summary.authoritative', (o) ->
-			$('.summary_line', @).text tpl_fines_summary {
-				nf:  nf = o.balance_owed
-			}
+			$('.summary_line', @).text tpl_fines_summary
+				nf: nf = o.balance_owed
 		return false

 	tpl_checkouts_summary = _.template '''
@@ -52,11 +51,10 @@
 	'''
 	refresh_checkouts_summary = ->
 		$('#checkouts_summary').openils 'checkouts summary', 'actor.user.checked_out.count.authoritative', (o) ->
-			$('.summary_line', @).text tpl_checkouts_summary {
+			$('.summary_line', @).text tpl_checkouts_summary
 				nco: nco = o.out
 				nod: nod = o.overdue
 				nxx: nxx = o.total - nco - nod
-			}
 		return false

 	tpl_holds_summary = _.template '''
@@ -64,9 +62,8 @@
 	'''
 	refresh_holds_summary = ->
 		$('#holds_summary').openils 'holds summary', 'circ.holds.id_list.retrieve.authoritative', (o) ->
-			$('.summary_line', @).text tpl_holds_summary {
-				nh:  nh = o.length
-			}
+			$('.summary_line', @).text tpl_holds_summary
+				nh: nh = o.length
 		return false

 	tpl_bookbags_summary = _.template '''
@@ -74,9 +71,8 @@
 	'''
 	refresh_bookbags_summary = ->
 		$('#bookbags_summary').openils 'bookbags summary', 'actor.container.retrieve_by_class', (o) ->
-			$('.summary_line', @).text tpl_bookbags_summary {
+			$('.summary_line', @).text tpl_bookbags_summary
 				nbb: nbb = o.length
-			}
 		return false

 	# ***
@@ -140,26 +136,24 @@
 		# we will refresh the line and its inner plugins.
 		.live 'expand', (e, ui) ->
 			$(@).publish $('h3', @).prop 'id' # The h3 element id is the name of the data channel to publish on
-			$ps = $('.plugin', @).refresh()
+			$('.plugin', @).refresh()
 			return false

 		# Upon the user collapsing a summary line,
 		# we will empty its inner plugin content.
 		.live 'collapse', (e, ui) ->
-			$ps = $('.plugin', @).empty()
+			$('.plugin', @).empty()
 			return false

 		# Upon the user logging in,
 		# we will refresh a summary line's inner plugin content if the summary line is not collapsed.
 		.subscribe 'login_event', ->
-			# Refresh any inner plugin content if not collapsed
 			$(ps).refresh() for ps in $('.plugin', @) when $(ps).closest('.ui-collapsible-content').prop('aria-hidden') is 'false'
 			return false

 		# Upon the user logging out,
 		# we will empty a summary line's inner plugin content if the summary line is not collapsed.
 		.subscribe 'logout_event', ->
-			# Empty any inner plugin content if not collapsed
 			$(ps).empty() for ps in $('.plugin', @) when $(ps).closest('.ui-collapsible-content').prop('aria-hidden') is 'false'
 			return false


fix a comment error

fix a comment error

--- app/myutil.coffee
+++ app/myutil.coffee
@@ -17,7 +17,7 @@

 # Wrapper around React.createElement.
 # $.div args... -> React.createElement('div', args...)
-# $ $, args... -> React.createElement(React.Fragment, args...)
+# $ $, args... -> React.createElement(React.Fragment, 0, args...)
 # $ Element, args... -> React.createElement(Element, args...)
 export $ = new Proxy createElement,
   get: (target, prop) -> (args...) -> target prop, args...


Adapt routes

Adapt routes

--- app/routes.coffee
+++ app/routes.coffee
@@ -1,3 +1,4 @@
 module.exports = (match) ->
   match '', 'home#index'
   match 'u/6045251/shiny-wight/index.html', 'home#index'
+  match 'shiny-wight/index.html', 'home#index'


Removed the default value allowedOrientation for a web view.

--- src/models/views/WebView.coffee
+++ src/models/views/WebView.coffee
@@ -26,13 +26,8 @@

     @params = @getParams()

-    # Sets the WebView to rotate to portait orientation only by default.
-    # User can override this behavior by setting window.AG_allowedRotationsDefaults
-    # before loading Steroids.js.
-
-    allowedRotations = window.AG_allowedRotationsDefaults ? [0]
-    @setAllowedRotations(allowedRotations)
-
+    if options.allowedRotations?
+      @setAllowedRotations(options.allowedRotations)

   preload: (options={}, callbacks={}) ->
     steroids.debug "preload called for WebView #{JSON.stringify @}"


Be careful to only ask for @range.end if @range is not null

--- src/modules/link-tooltip.coffee
+++ src/modules/link-tooltip.coffee
@@ -55,15 +55,15 @@

   saveLink: ->
     url = this._normalizeURL(@textbox.value)
-    end = @range.end
     if @range?
+      end = @range.end
       if @range.isCollapsed()
         anchor = this._findAnchor(@range)
         anchor.href = url if anchor?
       else
         @quill.formatText(@range, 'link', url, 'user')
+      @quill.setSelection(end, end)
     this.setMode(url, false)
-    @quill.setSelection(end, end)

   removeLink: (range) ->
     # Expand range to the entire leaf


remove unsupported love callbacks

remove unsupported love callbacks

--- coffeescript/love.js/love.coffee
+++ coffeescript/love.js/love.coffee
@@ -40,9 +40,5 @@
   mousereleased: (x, y, button) ->
   keypressed: (key, unicode) ->
   keyreleased: (key, unicode) ->
-  joystickpressed: (joystick, button) ->
-  joystickreleased: (joystick, button) ->
-  textinput: (text) ->
   draw: () ->
-  focus: (has_focus) ->
   quit: () ->


Support adding collaborator via email

Support adding collaborator via email

--- collections/collaborators.coffee
+++ collections/collaborators.coffee
@@ -12,6 +12,8 @@
   model: User

   url: -> "#{sd.API_URL}/channels/#{@channel_slug}/collaborators"
+
+  inviteUrl: -> "#{sd.API_URL}/channels/#{@channel_slug}/collaborators/invite"

   parse: (data) -> data.users

@@ -29,7 +31,7 @@
       success: (response) =>
         @reset response.users

-  _add: (collaborator)->
+  _add: (collaborator) ->
     mediator.trigger 'collaborator:added'
     @add collaborator
     $.ajax
@@ -38,3 +40,15 @@
       data: { ids: [collaborator.id] }
       success: (response) =>
         @reset response.users
+
+  _invite: (email) ->
+    user = new User username: email
+    mediator.trigger 'collaborator:added'
+    @add user
+    $.ajax
+      type: 'POST'
+      url: @inviteUrl()
+      data:
+        email: email
+
+


Fix returning bubble bug (a little)

Fix returning bubble bug (a little)

--- source/assets/javascripts/app.js.coffee
+++ source/assets/javascripts/app.js.coffee
@@ -202,6 +202,8 @@
           title.attr { transform: 'translate('+item.initial+') scale('+scale.initial+')' }
           setTimeout restart, 250
   returnButtons = (element, item, callback) ->
+    element.mouseover ->
+      null
     element.animate { transform: 'translate('+item.initial+') scale('+scale.initial+')' }, item.speed/2, mina.easeout, ->
       setTimeout callback, 1500 - item.speed


Preserve path on mkpath

Preserve path on mkpath

--- src/objects/folder.proto.coffee
+++ src/objects/folder.proto.coffee
@@ -21,13 +21,17 @@
 # mkdir -p
 exports.mk = ->
   q = []
-  f = @abs()
-  until f.y()
-    q.push f
-    f = f.up()
-  while f = q.pop()
-    fs.CreateFolder f
-  @
+  _ = @_
+  try
+    f = @abs()
+    until f.y()
+      q.push f
+      f = f.up()
+    while f = q.pop()
+      fs.CreateFolder f
+    @
+  finally
+    @_ = _

 # ActiveX Folder object
 exports.$ = ->


atom config changed

atom config changed

--- .atom/config.cson
+++ .atom/config.cson
@@ -2,6 +2,8 @@
   "autocomplete-go": {}
   "autocomplete-plus":
     autoActivationDelay: 500
+  "autocomplete-python":
+    useKite: false
   "bracket-matcher":
     autocompleteBrackets: false
     autocompleteSmartQuotes: false
@@ -12,6 +14,7 @@
     disabledPackages: [
       "symbols-view"
       "spell-check"
+      "autocomplete-python"
     ]
     openEmptyEditorOnStart: false
     telemetryConsent: "limited"
@@ -21,7 +24,7 @@
     ]
   editor:
     fontFamily: "Fira Code"
-    fontSize: 11
+    fontSize: 13
     showInvisibles: true
     zoomFontWhenCtrlScrolling: false
   "exception-reporting":
@@ -41,7 +44,7 @@
       runTestsWithShortFlag: false
   "goto-definition": {}
   linter:
-    errorPanelHeight: 83
+    errorPanelHeight: 51
   minimap:
     plugins:
       bookmarks: true
@@ -50,6 +53,10 @@
       "find-and-replaceDecorationsZIndex": 0
       "minimap-autohide": true
       "minimap-autohideDecorationsZIndex": 0
+  "open-recent":
+    listDirectoriesAddedToProject: true
+    maxRecentFiles: 0
+    replaceNewWindowOnOpenDirectory: false
   "tree-view":
     hideVcsIgnoredFiles: true
     squashDirectoryNames: true


2 spaces in ts code.

2 spaces in ts code.

--- .atom/config.cson
+++ .atom/config.cson
@@ -309,6 +309,10 @@
   editor:
     softTabs: true
     tabLength: 2
+".source.ts":
+  editor:
+    softTabs: true
+    tabLength: 2
 ".source.yaml":
   editor:
     tabLength: 2


:+1: Update keymap for Atom editor

:+1: Update keymap for Atom editor

--- .atom/keymap.cson
+++ .atom/keymap.cson
@@ -30,3 +30,6 @@
 # If you are unfamiliar with CSON, you can read more about it in the
 # Atom Flight Manual:
 # https://atom.io/docs/latest/using-atom-basic-customization#cson
+# 自動インデント整形
+'atom-text-editor':
+    'ctrl-i': 'editor:auto-indent'


Fixed broken connectTo

Fixed broken connectTo

--- src/coffee/services.coffee
+++ src/coffee/services.coffee
@@ -106,17 +106,19 @@
         currentName = "#{ baseName } (#{ suffix++ })"
       return currentName

+  createConnection = (conf) ->
+    s = imjs.Service.connect conf
+    s.name = conf.name
+    return s
+
   # Provide a function for connecting to a mine by URL.
   Services.factory 'connectTo', Array 'Mines', (mines) -> (root) ->
-    mines.atURL(root).then imjs.Service.connect conf
+    mines.atURL(root).then createConnection

   # Connect to a mine by name
   Services.factory 'connect', Array 'Mines', (Mines) ->
     cache = {}
-    (name) -> cache[name] ?= Mines.get(name).then (conf) ->
-      s = imjs.Service.connect conf
-      s.name = conf.name
-      return s
+    (name) -> cache[name] ?= Mines.get(name).then createConnection

   # Provide a function that will yield a value suitable for identifying
   # which version of a service was contacted at a particular time.