use BS.Modal.Footer

use BS.Modal.Footer

Allows specs to target it using componentName

--- coach/src/concept-coach/error-notification.cjsx
+++ coach/src/concept-coach/error-notification.cjsx
@@ -67,9 +67,11 @@
       <BS.Modal.Body>
         {dialog.body}
       </BS.Modal.Body>
-      <div className='modal-footer'>
+
+      <BS.Modal.Footer>
         {dialog.buttons}
-      </div>
+      </BS.Modal.Footer>
+
     </BS.Modal>


minor syntax fix with router/list

minor syntax fix with router/list

--- src/router/list.coffee
+++ src/router/list.coffee
@@ -16,7 +16,7 @@

   @get '/:key', (req, res, next) ->
     match = res.locals.target["__#{req.params.key}__"]
-    if match? then
+    if match?
       res.locals.result = match
       next()
     else next 'route'


facebook fix ensureLoggedin

facebook fix ensureLoggedin

--- src/remote/services/facebook.coffee
+++ src/remote/services/facebook.coffee
@@ -15,17 +15,18 @@
     super(config, gateway)
     @loadFbSdk(@getSettings())

-  request : (args...)->
+  request : (args...)=>
     @ensureLoggedIn().then ()=> @performRequest(args...)

-  ensureLoggedIn : ()->
+  ensureLoggedIn : ()=>
+    self = this
     new Promise (resolve, reject)=>
       args = Array.prototype.slice.call(arguments)
       if @fbUser?.status=='connected'
         resolve()
       else
         FB.getLoginStatus (res)=>
-          @updateFBUserStatus(res)
+          self.updateFBUserStatus(res)
           if res.status=='connected' then resolve() else reject()
         , true
       # , true ~ Maybe we dont need a roundtrip each time.


PremiumBroker: broker name

PremiumBroker: broker name

--- config/main.vagrant.coffee
+++ config/main.vagrant.coffee
@@ -236,7 +236,7 @@
     authExchange: authExchange
     authAllExchange: authAllExchange
   premiumBroker  :
-    name        : "premiumBroker"
+    name        : "broker"
     ip          : ""
     port        : 8009
     certFile    : ""


fix dropdown events

fix dropdown events

--- components/dropdown/client/dropdown_view.coffee
+++ components/dropdown/client/dropdown_view.coffee
@@ -24,8 +24,7 @@
       @undelegateEvents()
       @delegateEvents(@mobileEvents)
     else
-      @undelegateEvents()
-      @delegateEvents(@normalEvents)
+      @delegateEvents(@desktopEvents)

   openDropdown: =>
     @$el.addClass 'dropdown--is_active'


Better granulatiry

Better granulatiry

--- library/nucleus/watch.coffee
+++ library/nucleus/watch.coffee
@@ -152,10 +152,10 @@
         dependents = _.filter registry, predicate
         dependents = _.filter dependents, originate
         return unless dependents.length > 0
+        @forcedHotSwappingInProgress = dependents
         message = "Forced watch enabled, swapping services: %s"
         logger.warn message.grey, dependents.length
         change = @hotSwappingChange.bind this
-        @forcedHotSwappingInProgress = yes
         change originate dep for dep in dependents
         delete @forcedHotSwappingInProgress


Don't use window. prefix.

Don't use window. prefix.

--- index.coffee
+++ index.coffee
@@ -32,8 +32,8 @@

 window.setInterval save, 10000

-window.addEventListener 'beforeunload', ->
-  stop() if player
+addEventListener 'beforeunload', ->
+  stop() if retro.player

 load = (file) ->
   [..., extension] = file.name.split '.'


:bug: use global MathJax object

:bug: use global MathJax object

--- index.coffee
+++ index.coffee
@@ -1,7 +1,5 @@
 require 'jquery'
 require 'bootstrap' # Attach bootstrap to jQuery
-
-MathJax = window.MathJax

 window._STORES =
   APP_CONFIG: require './src/flux/app-config'
@@ -27,12 +25,16 @@
     "#MathJax_Message":    visibility: "hidden", left: "", right: 0
     "#MathJax_MSIE_Frame": visibility: "hidden", left: "", right: 0

-if MathJax?.Hub
-  MathJax.Hub.Config(MATHJAX_CONFIG)
-  MathJax.Hub.Configured()
+if window.MathJax?.Hub
+  window.MathJax.Hub.Config(MATHJAX_CONFIG)
+  window.MathJax.Hub.Configured()
 else
+  # If the MathJax.js file has not loaded yet:
+  # Call MathJax.Configured once MathJax loads and
+  # loads this config JSON since the CDN URL
+  # says to `delayStartupUntil=configured`
   MATHJAX_CONFIG.AuthorInit = ->
-    MathJax.Hub.Configured()
+    window.MathJax.Hub.Configured()

   window.MathJax = MATHJAX_CONFIG


Better support for signals emitted with changed property

--- index.coffee
+++ index.coffee
@@ -44,8 +44,10 @@
 		Object.seal @

 	getSignalChangeListener: do ->
-		withParent = ->
-			@parent.updateItem()
+		withParent = (prop, val) ->
+			# TODO: provide special signal emitted with property changed
+			if val is undefined or typeof prop isnt 'string' or @parent.prop is prop
+				@parent.updateItem()
 			return

 		noParent = ->


Supervisord: remove mailing from memmon since we have supervisord logs on papertrail

--- deployment/supervisord.coffee
+++ deployment/supervisord.coffee
@@ -111,7 +111,7 @@

   """
   [eventlistener:memmon]
-  command=memmon -g environment=#{limit} -m #{email}
+  command=memmon -g environment=#{limit}
   events=TICK_60
   """


Fix counter

Fix counter

--- assets/data/quest_goal.cson
+++ assets/data/quest_goal.cson
@@ -92,14 +92,14 @@
   type: 2
   "create_item":
     description: "開発"
-    required: 3
-    init: 0
+    required: 4
+    init: 1
 608: # 艦娘「建造」艦隊強化！ 建造3次
   type: 2
   "create_ship":
     description: "建造"
-    required: 3
-    init: 0
+    required: 4
+    init: 1
 609: # 軍縮条約対応！ 解体2次
   type: 2
   "destroy_ship":
@@ -243,8 +243,8 @@
   "mission_success":
     description: "遠征"
     mission: ['東京急行', '東京急行(弐)']
-    required: 6
-    init: 0
+    required: 7
+    init: 1
 613: # 資源の再利用 进行废弃24次
   type: 3
   "destory_item":


sorting of projects by followers and users by logins

--- js/inner/org.coffee
+++ js/inner/org.coffee
@@ -18,7 +18,8 @@
     @project_objs = @komp =>
       for project in @projects()
         project.follower_logins = (u.login for u in project.followers)
-        project.followers = (new CI.inner.User(u) for u in project.followers)
+        project.followers = _(new CI.inner.User(u) for u in project.followers)
+          .sortBy "login"
         new CI.inner.Project(project)

     # users that have been turned into User objects
@@ -28,7 +29,13 @@
         new CI.inner.User(user)

     @projects_with_followers = @komp =>
-      _.filter @project_objs(), ((p) -> p.followers.length)
+      _.chain(@project_objs())
+        .filter((p) -> p.followers.length)
+        .sortBy((p) -> -1 * p.followers.length)
+        .value()

     @projects_without_followers = @komp =>
-      _.reject @project_objs(), ((p) -> p.followers.length)
+      _.chain(@project_objs())
+        .reject((p) -> p.followers.length)
+        .sortBy((p) -> p.repo_name())
+        .value()


latexmk: give -synctex=1 directly

latexmk: give -synctex=1 directly

--- lib/builder.coffee
+++ lib/builder.coffee
@@ -26,9 +26,9 @@

     quotes = '\"'

-    options = ["-cd", "-e", "-f", "-pdf", "-interaction=nonstopmode"]
+    options = ["-cd", "-e", "-f", "-pdf", "-interaction=nonstopmode", "-synctex=1"]

-    tex_options = ["-synctex=1"].concat(user_options)
+    tex_options = user_options
     tex_options_cmdline = ["-latexoption=\"" + texopt + "\"" for texopt in tex_options]
     options = options.concat([tex_options_cmdline])

@@ -101,9 +101,10 @@
       # cd to dir and run command; add output to console for now
       exec command, {cwd: filedir}, (err, stdout, stderr) =>
         # Parse error log
-        @ltConsole.addContent("Parsing ", br=true)
         fulllogfile = path.join(filedir, filename + ".log") # takes care of quotes
+        @ltConsole.addContent("Parsing " + fulllogfile, br=true)
         log = fs.readFileSync(fulllogfile, 'utf8')
+
         [errors, warnings] = parse_tex_log(log)

         @ltConsole.addContent("ERRORS:", br=true)


Add model property for relative path

Add model property for relative path

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -22,6 +22,7 @@
     text: -> new telepath.String('', replicated: false)
     id: -> guid.create().toString()
     filePath: null
+    relativePath: null

   @::lazyGetter 'project', -> @grandparent

@@ -108,7 +109,7 @@
         @emitModifiedStatusChanged(@isModified())

     @file.on "moved", =>
-      @state.set('relativePath', @project.relativize(@getPath()))
+      @relativePath = @project.relativize(@getPath())
       @emit "path-changed", this

   ### Public ###
@@ -154,7 +155,7 @@
     @getRelativePath()

   getRelativePath: ->
-    @state.get('relativePath')
+    @relativePath

   # Sets the path for the file.
   #
@@ -170,7 +171,7 @@
     else
       @file = null

-    @state.set('relativePath', @project.relativize(path))
+    @relativePath = @project.relativize(path)
     @emit "path-changed", this

   # Retrieves the current buffer's file extension.


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/text_effect.coffee
+++ src/text_effect.coffee
@@ -1,4 +1,13 @@
+###*
+The Sprite class provides a way to load images for use in games.

+By default, images are loaded asynchronously. A proxy object is
+returned immediately. Even though it has a draw method it will not
+draw anything to the screen until the image has been loaded.
+
+@name Sprite
+@constructor
+###
 TextEffect = (I={}) ->
   Object.reverseMerge I,
     color: Color('green')


levels and default game states

levels and default game states

--- src/text_screen.coffee
+++ src/text_screen.coffee
@@ -5,7 +5,7 @@
     fontColor: 'white'

   self = GameState(I).extend
-    centerText: (canvas, a, subtitleText) ->
+    centerText: (canvas, ) ->
       canvas.font I.font

       canvas.centerText


Prevent password prompt in clone-packages task

Also, use `git reset --hard` to prevent merge conflicts.

Signed-off-by: Antonio Scandurra <0e17b4d558b07b12cd221e35c438be378ba118cf@github.com>

--- lib/helpers.coffee
+++ lib/helpers.coffee
@@ -1,5 +1,6 @@
 require 'es6-shim'
 fs = require 'fs'
+URL = require 'url'
 path = require 'path'
 child_process = require 'child_process'
 request = require 'request'
@@ -28,28 +29,43 @@

 exports.clonePackages = (packages, packagesDirPath) ->
   progress = 0
-  logProgress = ->
-    console.log "#{++progress}/#{packages.length}"

   new Promise (resolve, reject) ->
-    async.eachLimit packages, 20,
+    async.eachLimit packages, 10,
       (pack, callback) ->
         clonePath = "#{packagesDirPath}/#{pack.name}"
-        if fs.existsSync(clonePath)
-          child_process.exec "git pull", cwd: clonePath, (error) ->
-            console.error(pack.name, error.message) if error?
-            logProgress()
+
+        done = ->
+          console.log "#{++progress}/#{packages.length} - #{pack.name}"
+          callback()
+
+        fs.exists clonePath, (exists) ->
+          if exists
+            fetch = child_process.spawn "git", ["fetch", "origin"], {cwd: clonePath, stdio: 'ignore'}
+            fetch.on 'error', (error) ->
+              console.error(pack.name, error.message)
+              done()
+            fetch.on 'exit', ->
+              reset = child_process.spawn "git", ["reset", "--hard", "origin/HEAD"], {cwd: clonePath, stdio: 'ignore'}
+              reset.on 'error', (error) ->
+                console.error(pack.name, error.message)
+                done()
+              reset.on 'exit', ->
+                done()
+
+          else if pack.repository?.url?
+            packageURL = URL.format(Object.assign(URL.parse(pack.repository.url), {auth: "some-user:some-password"}))
+            clone = child_process.spawn('git', ['clone', '--depth=1', packageURL, clonePath], {stdio: 'ignore'})
+            clone.on 'error', (error) ->
+              console.error(pack.name, error.message)
+              done()
+            clone.on 'exit', ->
+              done()
+
+          else
+            console.warn("Package '#{pack.name}' has no repository URL")
             callback()

-        else if pack.repository?.url?
-          command = "git clone --depth=1 \"#{pack.repository.url}\" \"#{clonePath}\""
-          child_process.exec command, (error) ->
-            console.error(pack.name, error.message) if error?
-            logProgress()
-            callback()
-        else
-          console.warn("Package '#{pack.name}' has no repository URL")
-          callback()
       (error) ->
         if error?
           console.log 'error', error


Add getter(s) static methods to simplify configuring getters

--- bokehjs/src/coffee/core/has_props.coffee
+++ bokehjs/src/coffee/core/has_props.coffee
@@ -7,6 +7,13 @@
 refs = require "./util/refs"

 class HasProps extends Backbone.Model
+
+  @getter: (name, get) ->
+    Object.defineProperty(this.prototype, name, { get: get })
+
+  @getters: (specs) ->
+    for name, get of specs
+      @getter(name, get)

   props: {}
   mixins: []


Don't rely on an open ssh formatted key.

Means we can parse putty-generated keys here too.

--- src/validateSSH.coffee
+++ src/validateSSH.coffee
@@ -30,15 +30,14 @@
 	(String.fromCharCode(i) for i in uintArray).join('')

 validateOpenSSHKey = (key) ->
-	key = key.replace('\n', '').split(' ')
+	# See http://crypto.stackexchange.com/a/5948.
+	key = key.replace(/\r?\n/g, '')
+	key = /AAAAB3NzaC1yc2E[A-Za-z0-9+\/]+/.exec(key)?[0]

-	if key.length < 2 or key.length > 3
-		return 'invalid key structure'
+	if !key?
+		return 'missing header'

-	if key[0] != 'ssh-rsa'
-		return "invalid key type: #{key[0]}"
-
-	uint8Array = base64binary.decode(key[1])
+	uint8Array = base64binary.decode(key)

 	if uint8ArrayToInt(uint8Array[...4]) != 7
 		return 'invalid key type length'


Issue #31 - Fix when building from outside `ActiveTextEditor`

--- lib/manager.coffee
+++ lib/manager.coffee
@@ -8,7 +8,8 @@
     @latex = latex

   loadLocalCfg: ->
-    if @lastCfgTime? and Date.now() - @lastCfgTime < 200
+    if @lastCfgTime? and Date.now() - @lastCfgTime < 200 or\
+       !atom.workspace.getActiveTextEditor()?
       return @config?
     @lastCfgTime = Date.now()
     rootDir = atom.project.relativizePath(atom.workspace.getActiveTextEditor().getPath())[0]


Wayward debug statements

Wayward debug statements

--- lib/motions.coffee
+++ lib/motions.coffee
@@ -276,9 +276,6 @@
     @editor.trigger 'vim-mode:search-complete'

   match: (count, callback) ->
-    window.matches = @matches
-    window.count = count
-
     pos = @matches[(count - 1) % @matches.length]
     if pos?
       callback(pos)


Bold branch name on commit button

Bold branch name on commit button

--- lib/commit-message-view.coffee
+++ lib/commit-message-view.coffee
@@ -5,7 +5,9 @@
 <atom-text-editor tabindex="-1" class="commit-description" gutter-hidden
   style="height: 120px"></atom-text-editor>

-  <button class="btn btn-commit">Commit</button>
+  <button class="btn btn-commit">Commit to
+
+  </button>

 """

@@ -18,7 +20,7 @@
     @innerHTML    = BaseTemplate
     @messageNode  = @querySelector('atom-text-editor')
     @messageModel = @messageNode.getModel()
-    @buttonNode   = @querySelector('.btn-commit')
+    @branchNode   = @querySelector('.branch-name')

     @messageModel.setSoftWrapped(true)
     @messageModel.setPlaceholderText(PlaceholderText)
@@ -36,7 +38,7 @@

   update: ->
     @git.getBranchName().then (name) =>
-      @buttonNode.textContent = "Commit to #{name}"
+      @branchNode.textContent = name

   focusTextArea: ->
     @messageNode.focus()


Put back multi-platform specs

Put back multi-platform specs

--- build/tasks/spec-task.coffee
+++ build/tasks/spec-task.coffee
@@ -4,6 +4,13 @@

 _ = require 'underscore-plus'
 async = require 'async'
+
+# TODO: This should really be parallel on every platform, however:
+# - On Windows, our fixtures step on each others toes.
+if process.platform is 'win32'
+  concurrency = 1
+else
+  concurrency = 2

 module.exports = (grunt) ->
   {isAtomPackage, spawn} = require('./task-helpers')(grunt)
@@ -12,7 +19,13 @@

   getAppPath = ->
     contentsDir = grunt.config.get('atom.contentsDir')
-    path.join(contentsDir, 'MacOS', 'Atom')
+    switch process.platform
+      when 'darwin'
+        path.join(contentsDir, 'MacOS', 'Atom')
+      when 'linux'
+        path.join(contentsDir, 'atom')
+      when 'win32'
+        path.join(contentsDir, 'atom.exe')

   runPackageSpecs = (callback) ->
     failedPackages = []
@@ -20,16 +33,32 @@
     resourcePath = process.cwd()
     appPath = getAppPath()

+    # Ensure application is executable on Linux
+    fs.chmodSync(appPath, '755') if process.platform is 'linux'
+
     packageSpecQueue = async.queue (packagePath, callback) ->
-      options =
-        cmd: appPath
-        args: ['--test', "--resource-path=#{resourcePath}", path.join(packagePath, 'spec')]
-        opts:
-          cwd: packagePath
-          env: _.extend({}, process.env, ELECTRON_ENABLE_LOGGING: true, ATOM_PATH: rootDir)
+      if process.platform in ['darwin', 'linux']
+        options =
+          cmd: appPath
+          args: ['--test', "--resource-path=#{resourcePath}", path.join(packagePath, 'spec')]
+          opts:
+            cwd: packagePath
+            env: _.extend({}, process.env, ELECTRON_ENABLE_LOGGING: true, ATOM_PATH: rootDir)
+      else if process.platform is 'win32'
+        options =
+          cmd: process.env.comspec
+          args: ['/c', appPath, '--test', "--resource-path=#{resourcePath}", "--log-file=ci.log", path.join(packagePath, 'spec')]
+          opts:
+            cwd: packagePath
+            env: _.extend({}, process.env, ELECTRON_ENABLE_LOGGING: true, ATOM_PATH: rootDir)

       grunt.log.ok "Launching #{path.basename(packagePath)} specs."
-      spawn options, (error) ->
+      spawn options, (error, results, code) ->
+        if process.platform is 'win32'
+          if error
+            process.stderr.write(fs.readFileSync(path.join(packagePath, 'ci.log')))
+          fs.unlinkSync(path.join(packagePath, 'ci.log'))
+
         failedPackages.push path.basename(packagePath) if error
         callback()

@@ -40,7 +69,7 @@
       continue unless isAtomPackage(packagePath)
       packageSpecQueue.push(packagePath)

-    packageSpecQueue.concurrency = 1
+    packageSpecQueue.concurrency = Math.max(1, concurrency - 1)
     packageSpecQueue.drain = -> callback(null, failedPackages)

   runCoreSpecs = (callback) ->
@@ -48,20 +77,41 @@
     resourcePath = process.cwd()
     coreSpecsPath = path.resolve('spec')

-    options =
-      cmd: appPath
-      args: ['--test', "--resource-path=#{resourcePath}", coreSpecsPath, "--user-data-dir=#{temp.mkdirSync('atom-user-data-dir')}"]
-      opts:
-        env: _.extend({}, process.env, {ATOM_INTEGRATION_TESTS_ENABLED: true, ELECTRON_ENABLE_LOGGING: true})
-        stdio: 'inherit'
+    if process.platform in ['darwin', 'linux']
+      options =
+        cmd: appPath
+        args: ['--test', "--resource-path=#{resourcePath}", coreSpecsPath, "--user-data-dir=#{temp.mkdirSync('atom-user-data-dir')}"]
+        opts:
+          env: _.extend({}, process.env, {ELECTRON_ENABLE_LOGGING: true, ATOM_INTEGRATION_TESTS_ENABLED: true})
+          stdio: 'inherit'
+
+    else if process.platform is 'win32'
+      options =
+        cmd: process.env.comspec
+        args: ['/c', appPath, '--test', "--resource-path=#{resourcePath}", '--log-file=ci.log', coreSpecsPath]
+        opts:
+          env: _.extend({}, process.env, {ELECTRON_ENABLE_LOGGING: true, ATOM_INTEGRATION_TESTS_ENABLED: true})
+          stdio: 'inherit'

     grunt.log.ok "Launching core specs."
-    spawn options, (error, results) ->
+    spawn options, (error, results, code) ->
+      if process.platform is 'win32'
+        process.stderr.write(fs.readFileSync('ci.log')) if error
+        fs.unlinkSync('ci.log')
+      else
+        # TODO: Restore concurrency on Windows
+        packageSpecQueue?.concurrency = concurrency
+
       callback(null, error)

   grunt.registerTask 'run-specs', 'Run the specs', ->
     done = @async()
     startTime = Date.now()
+    method =
+      if concurrency is 1
+        async.series
+      else
+        async.parallel

     specs =
       if process.env.ATOM_SPECS_TASK is 'packages'
@@ -71,7 +121,7 @@
       else
         [runCoreSpecs, runPackageSpecs]

-    async.series specs, (error, results) ->
+    method specs, (error, results) ->
       failedPackages = []
       coreSpecFailed = null

@@ -83,10 +133,13 @@
         [coreSpecFailed, failedPackages] = results

       elapsedTime = Math.round((Date.now() - startTime) / 100) / 10
-      grunt.log.ok("Total spec time: #{elapsedTime}s")
+      grunt.log.ok("Total spec time: #{elapsedTime}s using #{concurrency} cores")
       failures = failedPackages
       failures.push "atom core" if coreSpecFailed

       grunt.log.error("[Error]".red + " #{failures.join(', ')} spec(s) failed") if failures.length > 0

-      done(not coreSpecFailed and failedPackages.length is 0)
+      if process.platform is 'win32' and process.env.JANKY_SHA1
+        done()
+      else
+        done(not coreSpecFailed and failedPackages.length is 0)


add mention class to mockup messages containing the test user screen name

--- mockup/scripts/mockup.coffee
+++ mockup/scripts/mockup.coffee
@@ -52,10 +52,13 @@
 fillTemplate = (template, data) ->
   template.replace /%([A-Za-z]+)%/g, (m, key) -> data[key]

+containsWord = (text, word) -> ///\b#{word}\b///.test text
+
 sendMessage = (text, options = {}) ->
   type = options.type ? 'message'

   classNames = [ type ]
+  classNames.push 'mention' if containsWord text, user.screenName
   classNames.push options.status if type is 'status'
   classNames.push if options.outgoing then 'outgoing' else 'incoming'


tests for user_search

tests for user_search

--- src/smc-hub/test/api/account.coffee
+++ src/smc-hub/test/api/account.coffee
@@ -119,3 +119,44 @@
                 delete resp?.id
                 expect(resp).toEqual(event:'account_creation_failed', reason: { last_name: 'Enter your last name.' })
                 done(err)
+
+describe 'testing user_search -- ', ->
+    before(setup)
+    after(teardown)
+
+    it "searches by email", (done) ->
+        api.call
+            event : 'user_search'
+            body  :
+                query : 'cocalc@sagemath.com'
+            cb    : (err, resp) ->
+                expect(resp?.event).toBe('user_search_results')
+                expect(resp?.results?.length).toBe(1)
+                expect(resp?.results?[0].first_name).toBe('Sage')
+                expect(resp?.results?[0].last_name).toBe('CoCalc')
+                expect(resp?.results?[0].email_address).toBe('cocalc@sagemath.com')
+                done(err)
+
+
+    it "searches by first and last name prefixes", (done) ->
+        api.call
+            event : 'user_search'
+            body  :
+                query : 'coc sag'
+            cb    : (err, resp) ->
+                expect(resp?.event).toBe('user_search_results')
+                expect(resp?.results?.length).toBe(1)
+                expect(resp?.results?[0].first_name).toBe('Sage')
+                expect(resp?.results?[0].last_name).toBe('CoCalc')
+                expect(resp?.results?[0]).toExcludeKey('email_address')
+                done(err)
+
+    it "searches by email and first and last name prefixes", (done) ->
+        api.call
+            event : 'user_search'
+            body  :
+                query : 'coc sag,cocalc@sagemath.com'
+            cb    : (err, resp) ->
+                expect(resp?.event).toBe('user_search_results')
+                expect(resp?.results?.length).toBe(2)
+                done(err)


tasks: move task to top/bottom

tasks: move task to top/bottom

--- src/smc-webapp/tasks/actions.coffee
+++ src/smc-webapp/tasks/actions.coffee
@@ -1,6 +1,8 @@
 ###
 Task Actions
 ###
+
+LAST_EDITED_THRESH_S = 30

 immutable  = require('immutable')
 underscore = require('underscore')
@@ -108,10 +110,10 @@
             current_task_id : current_task_id
             counts          : c

-
-    set_local_task_state: (obj) =>
+    set_local_task_state: (task_id, obj) =>
         # Set local state related to a specific task -- this is NOT sync'd between clients
         local = @store.get('local_task_state')
+        obj.task_id = task_id
         x = local.get(obj.task_id)
         if not x?
             x = immutable.fromJS(obj)
@@ -121,11 +123,13 @@
         @setState
             local_task_state : local.set(obj.task_id, x)

-    set_local_view_state: (key, value) =>
+    set_local_view_state: (obj) =>
         # Set local state related to what we see/search for/etc.
         local = @store.get('local_view_state')
-        @setState
-            local_view_state : local.set(key, immutable.fromJS(value))
+        for key, value of obj
+            local = local.set(key, immutable.fromJS(value))
+        @setState
+            local_view_state : local
         @_update_visible()

     save: =>
@@ -158,31 +162,25 @@
         if desc.length > 0
             desc += "\n"
         desc += @store.get("search") ? ''
-        task =
-            task_id     : misc.uuid()
-            desc        : desc
-            position    : position
-            last_edited : new Date() - 0
-        @syncdb.set(task)
-
+        task_id = misc.uuid()
+        @set_task(task_id, {desc:desc, position:position})
         @set_current_task(task.task_id)
-        @set_editing(task.task_id)
+
+    set_task: (task_id, obj) =>
+        if not task_id? or not obj?
+            return
+        last_edited = @store.getIn(['tasks', task_id, 'last_edited']) ? 0
+        now = new Date() - 0
+        if now - last_edited >= LAST_EDITED_THRESH_S*1000
+            obj.last_edited = now
+        obj.task_id = task_id
+        @syncdb.set(obj)

     delete_task: (task_id) =>
-        if not task_id?
-            return
-        @syncdb.set
-            task_id     : task_id
-            deleted     : true
-            last_edited : new Date() - 0
+        @set_task(task_id, {deleted: true})

     undelete_task: (task_id) =>
-        if not task_id?
-            return
-        @syncdb.set
-            task_id     : task_id
-            deleted     : false
-            last_edited : new Date() - 0
+        @set_task(task_id, {deleted: false})

     delete_current_task: =>
         @delete_task(@store.get('current_task_id'))
@@ -191,8 +189,10 @@
         @undelete_task(@store.get('current_task_id'))

     move_task_to_top: =>
+        @set_task(@store.get('current_task_id'), {position: @store.get_positions()[0] - 1})

     move_task_to_bottom: =>
+        @set_task(@store.get('current_task_id'), {position: @store.get_positions().slice(-1)[0] + 1})

     time_travel: =>
         @redux.getProjectActions(@project_id).open_file
@@ -202,8 +202,6 @@
     help: =>
         window.open(WIKI_HELP_URL, "_blank").focus()

-    set_editing: (task_id) =>
-
     set_current_task: (task_id) =>
         @setState(current_task_id : task_id)

@@ -214,67 +212,43 @@
         @syncdb?.redo()

     set_task_not_done: (task_id) =>
-        @syncdb.set
-            task_id     : task_id
-            done        : false
-            last_edited : new Date() - 0
+        @set_task(task_id, {done:false})

     set_task_done: (task_id) =>
-        @syncdb.set
-            task_id     : task_id
-            done        : true
-            last_edited : new Date() - 0
+        @set_task(task_id, {done:true})

     stop_editing_due_date: (task_id) =>
-        @set_local_task_state
-            task_id          : task_id
-            editing_due_date : false
+        @set_local_task_state(task_id, {editing_due_date : false})

     edit_due_date: (task_id) =>
-        @set_local_task_state
-            task_id          : task_id
-            editing_due_date : true
+        @set_local_task_state(task_id, {editing_due_date : true})

     stop_editing_desc: (task_id) =>
-        @set_local_task_state
-            task_id      : task_id
-            editing_desc : false
+        @set_local_task_state(task_id, {editing_desc : false})

     edit_desc: (task_id) =>
-        @set_local_task_state
-            task_id      : task_id
-            editing_desc : true
+        @set_local_task_state(task_id, {editing_desc : true})

     set_due_date: (task_id, date) =>
-        @syncdb.set
-            task_id     : task_id
-            due_date    : date
-            last_edited : new Date() - 0
+        @set_task(task_id, {due_date:date})

     set_desc: (task_id, desc) =>
-        @syncdb.set
-            task_id     : task_id
-            desc        : desc
-            last_edited : new Date() - 0
+        @set_task(task_id, {desc:desc})

     minimize_desc: (task_id) =>
-        @set_local_task_state
-            task_id  : task_id
-            min_desc : true
+        @set_local_task_state(task_id, {min_desc : true})

     maximize_desc: (task_id) =>
-        @set_local_task_state
-            task_id  : task_id
-            min_desc : false
+        @set_local_task_state(task_id, {min_desc : false})

     show_deleted: =>
-        @set_local_view_state('show_deleted', true)
+        @set_local_view_state(show_deleted: true)

     stop_showing_deleted: =>
-        @set_local_view_state('show_deleted', false)
+        @set_local_view_state(show_deleted: false)

     show_done: =>
-        @set_local_view_state('show_done', true)
+        @set_local_view_state(show_done: true)

     stop_showing_done: =>
-        @set_local_view_state('show_done', false)
+        @set_local_view_state(show_done: false)


Retire unused _check_for_errors

Retire unused _check_for_errors

--- src/TinyMockJS.coffee
+++ src/TinyMockJS.coffee
@@ -183,10 +183,6 @@
   for outer in [0..signatures.length-2]
     for inner in [outer+1..signatures.length-1]
       _throw_duplicate_expectation("#{signatures[outer].method_name}(#{signatures[outer].args})") if signatures[outer].equals( signatures[inner] )
-
-_check_for_errors = (mock) ->
-  errors = _build_errors(mock)
-  throw errors unless errors == ""

 _current_signature = (mock) ->
   mock.signatures[0]


Disable client socket reconnect for now

Disable client socket reconnect for now

--- library/membrane/duplex.coffee
+++ library/membrane/duplex.coffee
@@ -203,7 +203,8 @@
     # all the providers residing in the current service implementation.
     # Refer to other `Duplex` methods for understanding what goes on.
     bootloader: @autocall ->
-        try @socket = io.connect @duplex catch error
+        options = new Object reconnect: no, url: @duplex
+        try @socket = io.connect @duplex, options catch error
             message = "blew up Socket.IO: #{error.message}"
             error.message = message.toString(); throw error
         foreign = (v, k) => v.socket or k in (@externals or [])


a hack with the props definition on the objects (bad, bad, bad)

--- library/nucleus/compose.coffee
+++ library/nucleus/compose.coffee
@@ -156,7 +156,7 @@
     # between the foreign and common peers in the inheritance chain. Do
     # refer to the implementation for the understanding of what happens.
     Object.defineProperty Object::, "compose",
-        enumerable: no, value: (compound, shader=cloner) ->
+        writable: yes, value: (compound, shader=cloner) ->
             assert foreign = try compound.hierarchy()
             assert identify = try compound.identify()
             cmp = (orig) -> (cs) -> cs.similarWith orig


Minor fix in the cloner

Minor fix in the cloner

--- library/nucleus/compose.coffee
+++ library/nucleus/compose.coffee
@@ -49,6 +49,7 @@
     snapshot = _.cloneDeep subject, d = (value) ->
         return unless _.isFunction value
         func = -> value.apply this, arguments
+        func.name = value.name or "<anonymous>"
         _.extend func.prototype, value.prototype
         func.constructor = value.constructor; func
     snapshot.watermark = subject; snapshot


Cleanup

Cleanup

--- lib/project.coffee
+++ lib/project.coffee
@@ -77,28 +77,28 @@

       targetFullPath = path.resolve @outPath, "#{fileInfo.targetPath}.html"

+      # Only render files whose sources are newer than output?
       if options.onlyRenderNewer
-
-      	if fs.existsSync(currentFile) and fs.existsSync(targetFullPath)
-
-	        sourceStat = fs.statSync currentFile
-	        targetStat = fs.statSync targetFullPath
-
-	        if targetStat.mtime.getTime() > sourceStat.mtime.getTime()
-	          style.files.push fileInfo
-	          return done()
+
+        if fs.existsSync(currentFile) and fs.existsSync(targetFullPath)
+
+          sourceStat = fs.statSync currentFile
+          targetStat = fs.statSync targetFullPath
+
+          # Compare timestamps.
+          if targetStat.mtime.getTime() > sourceStat.mtime.getTime()
+
+            # Mark the file as processed in the style, and return.
+            #
+            # TODO this is a bad API, "Base" style class should provide a
+            # method to this effect.
+            style.files.push fileInfo
+            return done()

       fs.readFile currentFile, 'utf-8', (error, data) =>
         if error
           @log.error "Failed to process %s: %s", currentFile, error.message
           return callback error
-
-        fileInfo =
-          language:    language
-          sourcePath:  currentFile
-          projectPath: currentFile.replace ///^#{Utils.regexpEscape @root + CompatibilityHelpers.pathSep}///, ''
-          targetPath:  if currentFile == indexPath then 'index' else fileMap[currentFile]
-          pageTitle:   if currentFile == indexPath then (options.indexPageTitle || 'index') else fileMap[currentFile]

         style.renderFile data, fileInfo, done


Remove debug code accidentally left in from commit af6a54cdbdd7d1b7ad6d9e7ed6de83b33f74324e

--- oauth2.coffee
+++ oauth2.coffee
@@ -28,15 +28,6 @@
         res.redirect '/'
       else if req.query.state is 'offline'

-        console.log "FOO-1"
-
-        # res.send "#{json.refresh_token}"
-# res.send "<!DOCTYPE html>
-#
-#
-#               <title>Application Token</title>
-#
-#
         res.send "<!DOCTYPE html>

@@ -49,12 +40,6 @@

Back Home

                   "
-#
-#
Back Home

-#
-#           "
-
-        console.log "FOO-2"

       else
         res.send "You're in!  But not sure what to do with you (state = #{req.query.state})."


request 2x dynos for run processes

request 2x dynos for run processes

--- lib/spawner.coffee
+++ lib/spawner.coffee
@@ -35,6 +35,7 @@
     data["ps_env[#{key}]"] = val for key, val of options.env # only add desired env
     data["attach"] = "true"
     data["command"] = command
+    data["size"] = 2

     request = restler.post "https://#{host}/apps/#{app}/ps",
       headers:


Saving additional. Broken

Saving additional. Broken

--- src/background.coffee
+++ src/background.coffee
@@ -15,7 +15,11 @@
           when "getCourse"
             sendResponse @coursesHolder[request.courseName]
             break
-          when "getAdditional" then @getAdditional(request.courseName, sendResponse)
+          when "getAdditional"
+            chrome.storage.local.get request.courseName, (datum) =>
+              sendResponse
+                additional: datum[request.courseName].points.additional
+            break
           when "storeAdditional"
             @storeAdditional(request.additional, request.courseName)
             break


remove no longer used keymap

remove no longer used keymap

--- keymaps/narrow.cson
+++ keymaps/narrow.cson
@@ -80,6 +80,3 @@
   'ctrl-l': 'vim-mode-plus-user:narrow:scan'
   'cmd-/': 'vim-mode-plus-user:narrow:search'
   'ctrl-cmd-/': 'vim-mode-plus-user:narrow:search-current-project'
-
-'atom-text-editor.narrow-input':
-  'ctrl-/': 'narrow-input:toggle-use-regex'


Replace duplicated code with a call to #redraw()

Signed-off-by: Marko Locher <9efe9a5a1da5f41a4eb7599f2715dc24abf5bbc8@locher.at>

--- src/d3.timeslider.coffee
+++ src/d3.timeslider.coffee
@@ -310,15 +310,8 @@
                 @options.zoom.scale(iScale(t))
                 @options.zoom.translate([ (iPan(t)), 0 ])

-                # update brush
-                @brush.x(@scales.x).extent(@brush.extent())
-
-                # repaint the axis and the brush
-                d3.select(@element).select('g.axis').call(@axis.x)
-                d3.select(@element).select('g.brush').call(@brush)
-
-                # repaint the datasets
-                @updateDataset(dataset) for dataset of @data
+                # redraw
+                @redraw()
         )

         true


Allow for programmatically zooming to a specific time range

--- src/d3.timeslider.coffee
+++ src/d3.timeslider.coffee
@@ -257,8 +257,31 @@
     center: (params...) ->
         true

-    # TODO
     zoom: (params...) ->
-        true
+        start = new Date(params[0])
+        end = new Date(params[1])
+        [ start, end ] = [ end, start ] if end < start
+
+        d3.transition().duration(750).tween('zoom', =>
+            iScale = d3.interpolate(@options.zoom.scale(),
+                (@options.domain.end - @options.domain.start) / (end - start))
+            iPan = d3.interpolate(@options.zoom.translate()[0], @options.zoom.translate()[0] - @scales.x(start))
+            return (t) =>
+                @options.zoom.scale(iScale(t))
+                @options.zoom.translate([ (iPan(t)), 0 ])
+
+                # update brush
+                @brush.x(@scales.x).extent(@brush.extent())
+
+                # repaint the axis and the brush
+                d3.select(@element).select('g.axis').call(@axis.x)
+                d3.select(@element).select('g.brush').call(@brush)
+
+                # repaint the datasets
+                @updateDataset(dataset) for dataset of @data
+        )
+
+        true
+
 # Export the TimeSlider object for use in the browser
 this.TimeSlider = TimeSlider


Move window:reload from cmd-r => ctrl-alt-cmd-r

--- keymaps/atom.cson
+++ keymaps/atom.cson
@@ -44,7 +44,7 @@
   'ctrl-w w': 'window:focus-next-pane'
   'ctrl-tab': 'window:focus-next-pane'
   'ctrl-meta-f': 'window:toggle-full-screen'
-  'meta-r': 'window:reload'
+  'ctrl-alt-meta-r': 'window:reload'
   'alt-meta-i': 'window:toggle-dev-tools'
   'meta-alt-ctrl-p': 'window:run-package-specs'


Updating

Updating

--- packages.cson
+++ packages.cson
@@ -1,7 +1,7 @@
 packages: [
   "ask-stack"
+  "atom-clang"
   "atom-oss-license"
-  "autocomplete-clang"
   "autocomplete-java"
   "autocomplete-modules"
   "autocomplete-python"
@@ -39,7 +39,6 @@
   "latex-plus"
   "line-ending-selector"
   "linter"
-  "linter-clang"
   "linter-coffeescript"
   "linter-javac"
   "linter-js-tenx"


Changed browser url on random

Changed browser url on random

--- static/js/app/lib/controller.coffee
+++ static/js/app/lib/controller.coffee
@@ -1,26 +1,26 @@
-define ['app', 'marionette', 'vent',
-        'models/mix/mixItem', 'views/mix/mixListLayout', 'views/mix/mixListView', 'views/mix/mixDetailView'
-        'views/mix/mixEditView', 'views/user/userProfileView',
-        'models/user/userItem', 'views/user/userListView', 'views/user/userEditView'],
-(App, Marionette, vent,
- MixItem, MixListLayout, MixListView, MixDetailView,
- MixEditView, UserProfileView,
- UserItem, UserListView, UserEditView)->
+define ['app', 'marionette', 'vent', 'utils'
+        'views/mix/mixListLayout', 'views/mix/mixListView', 'views/mix/mixDetailView'
+        'views/mix/mixEditView', 'views/user/userProfileView', 'views/user/userListView', 'views/user/userEditView',
+        'models/mix/mixItem', 'models/mix/mixCollection', 'models/user/userItem'],
+(App, Marionette, vent, utils,
+ MixListLayout, MixListView, MixDetailView,
+ MixEditView, UserProfileView, UserListView, UserEditView,
+ MixItem, MixCollection, UserItem)->
     class DssController extends Marionette.Controller
+
+        initialize: ->
+          @listenTo(vent, "mix:random", @showRandomMix)

         home: ->
             console.log "Controller: home"
             @showMixList()
-            true

         showMixList: (options) ->
             app = require('app')
             app.contentRegion.show(new MixListLayout(options or {order_by: 'latest'}))
-            true

         showMixListType: (type) ->
             @showMixList({order_by: type})
-            true

         showMix: (slug)->
             console.log "Controller: showMix"
@@ -29,10 +29,17 @@
             mix.fetch(
                 success: ->
                     app.contentRegion.show(new MixDetailView({model: mix}))
-                    true
             )
-            true

+        showRandomMix: ->
+            console.log "Controller: showRandomMix"
+            app = require('app')
+            mix = new MixItem({id: 'random'})
+            mix.fetch(
+                success: ->
+                    app.contentRegion.show(new MixDetailView({model: mix}))
+            )
+            Backbone.history.navigate "/random", trigger: false
         uploadMix: ->
             console.log("Controller: mixUpload")
             app = require('app')
@@ -60,6 +67,7 @@
             app = require('app')
             app.contentRegion.show(new ChatView())

+
         showUserList: ->
             console.log("Controller: showUserList")
             app = require('app')
@@ -75,7 +83,6 @@
                 error: (a, b, c) ->
                     console.log("Error fetching user")
             )
-

         showUserFavourites: (slug) ->
             console.log("Controller: showUserFavourites")
@@ -109,4 +116,4 @@
             )
             true

-    DssController+    DssController


fixing [050321c5a5a5e3b18c30a0bd5d093a6eae7c07d1]

--- app/assets/javascripts/components/commit_action_buttons_component.js.coffee
+++ app/assets/javascripts/components/commit_action_buttons_component.js.coffee
@@ -9,7 +9,7 @@

   showCommitInModal: ()->
     url = @get('commit.remoteUrl')
-    window.open(url, '', "width = " + COMMIT_PREVIEW.width + ", height = " + COMMIT_PREVIEW.height)
+    window.open(@get('commit.remoteUrl'), '', "width = #{COMMIT_PREVIEW.width}, height = #{COMMIT_PREVIEW.height}")

   actions:
     viewCommit: ()-> @showCommitInModal()


#594 fixed indentation

#594 fixed indentation

--- app/assets/javascripts/dashboard/views/explorative_tracing_list_view.coffee
+++ app/assets/javascripts/dashboard/views/explorative_tracing_list_view.coffee
@@ -14,27 +14,27 @@
     <% if (!isAdminView) {%>

         <form action="<%= jsRoutes.controllers.admin.NMLIO.upload().url %>"
-              method="POST"
-              enctype="multipart/form-data"
-              id="upload-and-explore-form"
-              class="form-inline inline-block">
-

-
-
-
-
-                  Upload NML & explore
-
-                <input type="file" name="nmlFile" multiple>
+          method="POST"
+          enctype="multipart/form-data"
+          id="upload-and-explore-form"
+          class="form-inline inline-block">
+

+
+
+
+
+                Upload NML & explore

-

+              <input type="file" name="nmlFile" multiple accept=".nml">
+
+

         </form>

         <form action="<%= jsRoutes.controllers.AnnotationController.createExplorational().url %>"
-              method="POST"
-              class="form-inline inline-block">
+            method="POST"
+            class="form-inline inline-block">
           <select id="dataSetsSelect" name="dataSetName" class="form-control">
             <% activeDataSets.forEach(function(d) { %>
               <option value="<%= d.get("name") %>"> <%= d.get("name") %> </option>


PLOT PLOT PLOT

PLOT PLOT PLOT

--- source/assets/app/javascripts/base.js.coffee
+++ source/assets/app/javascripts/base.js.coffee
@@ -1,85 +1,67 @@
 $ = jQuery
+
+radius = 10
+
+features = []
+
+loadFeatures = (e) ->
+  # console.log e
+  # for f in e.features
+  #   console.log(f)

 # console.log "Mmmh, Coffee!" if console?
 $ ->
   po = org.polymaps
-
-  radius = 10

-  features = []
+  map = po.map()
+  .container(document.getElementById("map").appendChild(po.svg("svg")))
+  .center({lon: 8.596677185140349, lat: 46.77841693384364})
+  .zoom(8)
+  .add(po.interact())
+
+  map.add(po.image()
+  .url(po.url("http://{S}tile.cloudmade.com/1a1b06b230af4efdbb989ea99e9841af/998/256/{Z}/{X}/{Y}.png")
+  .hosts(["a.", "b.", "c.", ""])))
+
+  map.add po.compass().pan("none")

   data = $.get "media/data/vbs-belastete-standorte.json", (data) ->
-    console.log data
+    # console.log data

+    i = 0
     for row in data.rows
       features.push
-        #id: "blub"
+        id: "p" + i++
+        type: "Feature"
         geometry:
-          coordinates: [row['Latitude_WGS84'], row['Longitude_WGS84']]
+          coordinates: [row['Longitude_WGS84'], row['Latitude_WGS84']]
           type: "Point"
         properties:
           data: row

     console.log features
-
-  map = po.map()
-  .container(document.getElementById("map").appendChild(po.svg("svg")))
-  .center({lon: -122.20877392578124, lat: 37.65175620758778})
-  .zoom(10)
-  .add(po.interact())
-  .on("move", move)
-  .on("resize", move)
-
-  map.add(po.image()
-  .url(po.url("http://{S}tile.cloudmade.com/1a1b06b230af4efdbb989ea99e9841af/998/256/{Z}/{X}/{Y}.png")
-  .hosts(["a.", "b.", "c.", ""])))
-
-  map.add(po.geoJson()
-    #.on("load", load)
-    #.on("show", show)
-    .features([
-      {
-        "id": "stanford",
-        "properties": {
-          "html": ""
-        },
-        "geometry": {
-          "coordinates": [-122.16894848632812, 37.42961865341856],
-          "type": "Point"
-        }
-      },
-      {
-        "id": "berkeley",
-        "properties": {
-          "html": ""
-        },
-        "geometry": {
-          "coordinates": [-122.26358225200948, 37.872092652605886],
-          "type": "Point"
-        }
-      }
-    ]))
-
-  map.add(po.compass()
-  .pan("none"))
+
+    map.add(po.geoJson()
+      .on("load", loadFeatures)
+      .features(features))
+

-  load = (e) ->
-    for f in e.features
-      f.element.setAttribute("r", radius)
-      f.element.addEventListener("mousedown", toggle(f.data), false)
-      f.element.addEventListener("dblclick", cancel, false)
+  # load = (

-  show = (e) ->
-    for f in e.features
-      tip = tips[f.data.id]
-      tip.feature = f.data
-      tip.location =
-        lat: f.data.geometry.coordinates[1],
-        lon: f.data.geometry.coordinates[0]
-      update(tip)
+      # f.element.addEventListener("mousedown", toggle(f.data), false)
+      # f.element.addEventListener("dblclick", cancel, false)
+
+  # show = (e) ->
+  #   for f in e.features
+  #     tip = tips[f.data.id]
+  #     tip.feature = f.data
+  #     tip.location =
+  #       lat: f.data.geometry.coordinates[1],
+  #       lon: f.data.geometry.coordinates[0]
+  #     update(tip)

   #
   # move = () -> update(tips[id]) for id in tips


Contact autocomplete should start after 3 strikes, refs #53

--- client/app/components/mails_input.coffee
+++ client/app/components/mails_input.coffee
@@ -23,7 +23,7 @@
     getStateFromStores: ->
         query = @refs.contactInput?.getDOMNode().value.trim()
         return {
-            #contacts: if query?.length > 0 then ContactStore.getResults() else null
+            #contacts: if query?.length > 2 then ContactStore.getResults() else null
             contacts: ContactStore.getResults()
             selected: 0
             open: false
@@ -109,7 +109,7 @@
         else if char? and typeof char is 'object'
             # always display contact list when user click on contact button
             force = true
-        if query.length > 0 or ( force and not @state.open )
+        if query.length > 2 or ( force and not @state.open )
             ContactActionCreator.searchContactLocal query
             @setState open: true
             return true


app/flux: what if team has more than one owner

--- client/app/lib/flux/teams/getters.coffee
+++ client/app/lib/flux/teams/getters.coffee
@@ -2,7 +2,7 @@
 immutable   = require 'immutable'
 isEmailValid = require 'app/util/isEmailValid'
 getFullnameFromAccount = require 'app/util/getFullnameFromAccount'
-
+whoami = require 'app/util/whoami'

 team = ['TeamStore']
 TeamMembersIdStore = ['TeamMembersIdStore']
@@ -95,7 +95,9 @@
   filteredMembersWithRole
   (users) ->
     users.filter (user) ->
-      user.get('status') isnt 'pending' and user.get('role') is 'admin'  if user
+      return  unless user
+      return no  if user.get('_id') is whoami()._id
+      user.get('status') isnt 'pending' and user.get('role') in ['admin','owner']
 ]


HTML: removed initial 'intro' class from body

--- workers/social/lib/social/render/loggedout/kodinghome.coffee
+++ workers/social/lib/social/render/loggedout/kodinghome.coffee
@@ -14,8 +14,7 @@
       #{getStyles()}
       #{getGraphMeta()}
     </head>
-    <body class='koding intro'>
-
+    <body>
       <!--[if IE]>
       <script>(function(){window.location.href='/unsupported.html'})();</script>
       <![endif]-->


missed a spot

missed a spot

--- client/assets/scripts/application.coffee
+++ client/assets/scripts/application.coffee
@@ -296,6 +296,7 @@
 		unless TIMEOUT == null
 			clearTimeout TIMEOUT
 			TIMEOUT = null
+		updates++
 		method = 'showQueue'
 		query method, (body) =>
 			return if updates > 0


Now only authors can remove their comments

--- client/components/comment/comment.coffee
+++ client/components/comment/comment.coffee
@@ -1,6 +1,7 @@
 Template.comment.events
     "click .delete": ->
-        CommentsCollection.remove this._id
+        if Meteor.userId() == this.author
+            CommentsCollection.remove this._id

 Template.comment.helpers
     authorName: ->


basic recording working

basic recording working

--- client/src/app/game/player/player.coffee
+++ client/src/app/game/player/player.coffee
@@ -21,8 +21,19 @@
         $scope.$apply ->
           # Remove frames after the current one
           gameHistory.frames.length = gameHistory.currentFrameNumber + 1
+
           # Add in the new results
-          gameHistory.frames.push(e.data.value...)
+          lastModel = gameHistory.frames[gameHistory.currentFrameNumber].model
+          for results in e.data.value
+            lastModel = GE.applyPatches(results.modelPatches, lastModel)
+            gameHistory.frames.push
+              model: lastModel
+              servicePatches: results.servicePatches
+              inputServiceData: results.inputServiceData
+
+          # Go the the last frame
+          gameHistory.currentFrameNumber = gameHistory.frames.length - 1
+

   sendMessage = (operation, value) ->
     # Note that we're sending the message to "*", rather than some specific origin.


Fix renaming.

Fix renaming.

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -90,9 +90,9 @@
     program
       .command 'delete'
       .description 'Allows to delete (all) categories of your SPHERE.IO project.'
-      .option '--csv <file>', 'processes products defined in a CSV file by either "sku" or "id". Otherwise all products are processed.'
+      .option '--csv <file>', 'processes categories defined in a CSV file by either "sku" or "id". Otherwise all categories are processed.'
       .usage '--projectKey <project-key> --clientId <client-id> --clientSecret <client-secret>'
-      .option '--continueOnProblems', "When a there is a problem on changing a product's state (400er response), ignore it and continue with the next products"
+      .option '--continueOnProblems', "When a there is a problem on deleting a category, ignore it and continue with the next category."
       .action (opts) ->

         credentialsConfig = ProjectCredentialsConfig.create()
@@ -127,7 +127,7 @@
           prompt.start()
           property =
             name: 'ask'
-            message: 'Do you really want to delete products?'
+            message: 'Do you really want to delete categories?'
             validator: /y[es]*|n[o]?/
             warning: 'Please answer with yes or no'
             default: 'no'
@@ -152,11 +152,11 @@

     program
       .command 'export'
-      .description 'Export your products from your SPHERE.IO project to CSV using.'
+      .description 'Export your categories from your SPHERE.IO project to CSV using.'
       .option '-t, --template <file>', 'CSV file containing your header that defines what you want to export'
       .option '-o, --out <file>', 'Path to the file the exporter will write the resulting CSV in'
-      .option '-j, --json <file>', 'Path to the JSON file the exporter will write the resulting products'
-      .option '-q, --queryString', 'Query string to specify the sub-set of products to export. Please note that the query must be URL encoded!', 'staged=true'
+      .option '-j, --json <file>', 'Path to the JSON file the exporter will write the resulting categories'
+      .option '-q, --queryString', 'Query string to specify the sub-set of categories to export. Please note that the query must be URL encoded!', 'staged=true'
       .option '-l, --languages [langs]', 'Language(s) used on export for category names (default is en)', 'en'
       .usage '--projectKey <project-key> --clientId <client-id> --clientSecret <client-secret> --template <file>'
       .action (opts) ->


on_remote, might return json array or one model

--- src/collection.coffee
+++ src/collection.coffee
@@ -59,8 +59,12 @@

     if options.remote
       @remote = ->
-        result = options.remote()
-        @push(klass.from_json(result, @_klasses))
+        result = JSON.parse(options.remote())
+        if (Sirius.Utils.is_array(result))
+          for model in result then @push(klass.from_json(model, @_klasses))
+        else
+          @push(klass.from_json(result, @_klasses))
+
     @_timer    = null
     _start_sync(every)


remove inputs for answered questions

remove inputs for answered questions

--- src/components.coffee
+++ src/components.coffee
@@ -188,19 +188,29 @@
     else
       isChecked = @props.answer is optionIdent

+    contents = [
+      <AnswerLabeler after=")" index={index}/>
+      {option}
+    ]
+
+
+    unless isAnswered
+      contents =
+
+          <input type={inputType}
+            ref="input"
+            name={questionId}
+            value={JSON.stringify(config.value)}
+            onChange=@onChange
+            defaultChecked={isChecked}
+          />
+          {contents}
+
+

-
-        <input type={inputType}
-          ref="input"
-          name={questionId}
-          value={JSON.stringify(config.value)}
-          onChange=@onChange
-          defaultChecked={isChecked}
-        />
-        <AnswerLabeler after=")" index={index}/>
-        {option}
-
+      {contents}

+

 MultipleChoiceOption = React.createClass
@@ -218,6 +228,7 @@
   render: ->
     {config} = @props
     isAnswered = !!config.answer
+
     questionId = config.id
     options = for option, index in config.answers
       answerState = null


Fix name conflict of view

Fix name conflict of view

--- src/controller.coffee
+++ src/controller.coffee
@@ -2457,9 +2457,9 @@
       @mainCanvas.width,
       @mainCanvas.height
     )
-    view = @view.getViewNodeFor(@lassoSegment)
-    view.absorbCache()
-    view.draw @highlightCtx, mainCanvasRectangle, {selected: Infinity}
+    lassoView = @view.getViewNodeFor(@lassoSegment)
+    lassoView.absorbCache()
+    lassoView.draw @highlightCtx, mainCanvasRectangle, {selected: Infinity}

 # Convnience function for validating
 # a lasso selection. A lasso selection


Don't change z-indices

Don't change z-indices

--- src/controller.coffee
+++ src/controller.coffee
@@ -1422,16 +1422,6 @@
         for event in editorBindings.set_palette
           event.call this

-
-  # The palette hierarchical menu is on top of the track div
-  # so that we can click it. However, we do not want this to happen
-  # when we are dragging something. So:
-  hook 'mousedown', 0, ->
-    @paletteHeader.style.zIndex = 0
-
-  hook 'mouseup', 0, ->
-    @paletteHeader.style.zIndex = 257
-
   # The next thing we need to do with the palette
   # is let people pick things up from it.
   hook 'mousedown', 6, (point, event, state) ->
@@ -3479,14 +3469,12 @@
   hook 'mousedown', -1, ->
     if @clickedBlock?
       @dragCover.style.display = 'block'
-      @dragCanvas.style.zIndex = 300

   # On mouseup, throw the drag canvas away completely.
   hook 'mouseup', 0, ->
     @dragCanvas.style.top =
       @dragCanvas.style.left = '-9999px'

-    @dragCanvas.style.zIndex = 0
     @dragCover.style.display = 'none'

   # TOUCHSCREEN SUPPORT


Remove extraneous console.log

Remove extraneous console.log

--- src/controller.coffee
+++ src/controller.coffee
@@ -1714,7 +1714,6 @@
         palettePoint = @trackerPointToPalette new @draw.Point(
             event.clientX, event.clientY)
         if @viewOrChildrenContains block, palettePoint, @paletteView
-            console.log 'highlighting'
             @clearPaletteHighlightCanvas()
             @paletteHighlightPath = @getHighlightPath block, {color: '#FF0'}, @paletteView
             @paletteHighlightPath.draw @paletteHighlightCtx


JCredential: enable custom type credentials

--- workers/social/lib/social/models/computeproviders/credential.coffee
+++ workers/social/lib/social/models/computeproviders/credential.coffee
@@ -114,7 +114,7 @@
       { provider, title, meta } = data
       originId = delegate.getId()

-      if not PROVIDERS[provider]?
+      if provider isnt 'custom' and not PROVIDERS[provider]?
         callback new KodingError 'Provider is not supported'
         return


must divide spectrum length by 2 to get histogram length

--- examples/audio/static/coffee/spectrogram.coffee
+++ examples/audio/static/coffee/spectrogram.coffee
@@ -108,7 +108,7 @@
     hist = new Float32Array(NUM_BINS)
     if @fft_xrange
       bin_min = Math.round(@fft_xrange.get('start'))
-      bin_max = Math.round(@fft_xrange.get('end'))
+      bin_max = Math.round(@fft_xrange.get('end') / 2.0)
     else
       bin_min = 0
       bin_max = 256


Handling no value in onShouldShowTooltip delegated to ide-haskell

--- lib/haskell-ghc-mod.coffee
+++ lib/haskell-ghc-mod.coffee
@@ -257,13 +257,9 @@
               infoTooltip editor, crange
             when 'Info, fallback to Type'
               infoTypeTooltip editor, crange
-            else
-              Promise.reject ignore: true #this won't set backend status
         when 'selection'
           if atom.config.get('haskell-ghc-mod.showTypeOnSelection')
             typeTooltip editor.getBuffer(), crange
-        else
-          Promise.reject ignore: true #this won't set backend status

     checkLint = (buffer, opt, fast) =>
       if atom.config.get("haskell-ghc-mod.on#{opt}Check") and


[Prefetch] Divide prefetched activity into 2

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -159,7 +159,7 @@
         group      :
           slug     : groupObj?.slug or "koding"
           id       : groupObj.getId()
-        limit      : 20
+        limit      : 50
         facets     : @getActivityFilter()
         withExempt : no

@@ -202,15 +202,34 @@
       @listController.listActivitiesFromCache sanitizedCache
       callback sanitizedCache

+
+  FETCHING_ATTEMPT = 0
   fetchPublicActivities:(options = {})->
     {CStatusActivity} = KD.remote.api

     if @getFeedFilter() is "Public" and @getActivityFilter() is "Everything"
       prefetchedActivity = KD.prefetchedFeeds["activity.main"]
-      if prefetchedActivity and ('activities.main' not in USEDFEEDS) and prefetchedActivity.activities
+      if prefetchedActivity and prefetchedActivity.activities and ('activities.main' not in USEDFEEDS)
         log "exhausting feed:", "activity.main"
-        USEDFEEDS.push 'activities.main'
-        return @prepareCacheForListing KD.prefetchedFeeds["activity.main"]
+
+        if FETCHING_ATTEMPT > 0 then USEDFEEDS.push 'activities.main'
+
+        activity = $.extend {}, KD.prefetchedFeeds["activity.main"]
+        overview = activity.overview
+        firstCount = 25
+        secondCount = 50
+
+        switch ++FETCHING_ATTEMPT
+          when 1
+            firstCount = -24
+            secondCount = 50
+          when 2
+            firstCount = -50
+            secondCount = 25
+
+        overview = overview[firstCount..secondCount]
+        activity.overview = overview
+        return @prepareCacheForListing activity

     CStatusActivity.fetchPublicActivityFeed options, (err, cache)=>
       return @emit "activitiesCouldntBeFetched", err  if err


Dropbox: Fixed issues of Dropbox uploader. Fixed popup blocking, handled error and cancel cases and possible os kite errors, added progress information to upload notification, removed created file after upload or cancel.

--- client/landing/app/MainApp/filetree/controllers/findertreecontroller.coffee
+++ client/landing/app/MainApp/filetree/controllers/findertreecontroller.coffee
@@ -787,32 +787,56 @@
           fileItemViews.push fileItemView

   saveToDropbox: (nodeView) ->
-    notification   = null
-    kiteController = KD.getSingleton "kiteController"
-    plainPath      = FSHelper.plainPath nodeView.getData().path
-    isFolder       = nodeView.getData().type is "folder"
-    timestamp      = Date.now()
-    tmpFileName    = if isFolder then "tmp#{timestamp}.zip" else "tmp#{timestamp}"
-    relativePath   = "/home/#{KD.nick()}/Web/#{tmpFileName}"
-    kallback       = ->
-      fileName     = FSHelper.getFileNameFromPath plainPath
-      fileName     = "#{fileName}.zip"  if isFolder
-      options      =
-        files      : [
-          filename : fileName
-          url      : "http://#{KD.getSingleton('vmController').defaultVmName}/#{tmpFileName}"
-        ]
-        success: ->
-          notification.notificationSetTitle "Your file has been uploaded."
-          notification.notificationSetTimer 4000
-          notification.setClass "success"
-          kiteController.run "rm #{relativePath}"
-        error: ->
-          notification.notificationSetTitle "An error occured while uploading your file."
-          notification.notificationSetTimer 4000
-          notification.setClass "error"
-
-      Dropbox.save options
+    notification     = null
+    kiteController   = KD.getSingleton "kiteController"
+    plainPath        = FSHelper.plainPath nodeView.getData().path
+    isFolder         = nodeView.getData().type is "folder"
+    timestamp        = Date.now()
+    tmpFileName      = if isFolder then "tmp#{timestamp}.zip" else "tmp#{timestamp}"
+    relativePath     = "/home/#{KD.nick()}/Web/#{tmpFileName}"
+    kallback         = ->
+      modal          = new KDBlockingModalView
+        title        : "Upload to Dropbox"
+        cssClass     : "modal-with-text"
+        content      : "
Zipping your content is done. Click \"Choose Folder\" button to choose a folder on your Dropbox to start upload.
"
+        overlay      : yes
+        buttons      :
+          "Choose"   :
+            title    : "Choose Folder"
+            style    : "modal-clean-green"
+            callback : =>
+              modal.destroy()
+              fileName     = FSHelper.getFileNameFromPath plainPath
+              fileName     = "#{fileName}.zip"  if isFolder
+              options      =
+                files      : [
+                  filename : fileName
+                  url      : "http://#{KD.getSingleton('vmController').defaultVmName}/#{tmpFileName}"
+                ]
+                success: ->
+                  notification.notificationSetTitle "Your file has been uploaded."
+                  notification.notificationSetTimer 4000
+                  notification.setClass "success"
+                  kiteController.run "rm #{relativePath}"
+                error: ->
+                  notification.notificationSetTitle "An error occured while uploading your file."
+                  notification.notificationSetTimer 4000
+                  notification.setClass "error"
+                  kiteController.run "rm #{relativePath}"
+                cancel: ->
+                  kiteController.run "rm #{relativePath}"
+                  notification.destroy()
+                progress: (progress) ->
+                  notification.notificationSetTitle "Uploading to Dropbox - #{progress * 100}% done..."
+                  notification.show()
+
+              Dropbox.save options
+
+          Cancel     :
+            style    : "modal-cancel"
+            callback : ->
+              modal.destroy()
+              kiteController.run "rm #{relativePath}"

     if isFolder
       notification = new KDNotificationView
@@ -828,6 +852,7 @@
           notification.notificationSetTimer 4000
           notification.setClass "error"
         else
+          notification.hide()
           kallback()
     else
       notification = new KDNotificationView
@@ -836,4 +861,5 @@
         duration   : 120000
       kiteController.run "mkdir -p Web ; cp #{plainPath} #{relativePath}", (err, res) =>
         return  warn err if err
+        notification.hide()
         kallback()


Added notification when creating or renaming a file/folder that exist in the current directory.

--- client/landing/app/MainApp/filetree/controllers/findertreecontroller.coffee
+++ client/landing/app/MainApp/filetree/controllers/findertreecontroller.coffee
@@ -266,6 +266,13 @@
     oldPath = nodeData.path
     nodeView.showRenameView (newValue)=>
       return if newValue is nodeData.name
+      if @nodes["#{nodeData.parentPath}/#{newValue}"]
+        caretPos = nodeView.renameView.input.getCaretPosition()
+        @notify "Same #{nodeData.type} Detected", "error"
+        return KD.utils.defer =>
+          @showRenameDialog nodeView
+          nodeView.renameView.input.setCaretPosition caretPos
+
       nodeData.rename newValue, (err)=>
         if err then @notify null, null, err
         else


Switched to prototype shorthand.

Switched to prototype shorthand.

--- js/core/src/Runtime.coffee
+++ js/core/src/Runtime.coffee
@@ -131,7 +131,7 @@
 					@_id = @id(true)

 					# Add runtime methods
-					_.extend @prototype, proto.prototype
+					_.extend @prototype, proto::

 					# Set a few event handlers
 					# Manage accounting after an entity has been persisted
@@ -150,7 +150,7 @@

 		# Set up all the required methods - id(), _type(), and the mutaccs.
 		_build_prototype = (type, definition, proto) =>
-			_.extend definition.Constructor.prototype,
+			_.extend definition.Constructor::,
 				# Get this entity's type. Use the closure'd reference.
 				_type: (full) ->
 					full = full || false
@@ -198,7 +198,7 @@
 					return min

 			# Alias _encode as toJSON for ES5 JSON.stringify()
-			definition.Constructor.prototype.toJSON = definition.Constructor.prototype._encode
+			definition.Constructor::toJSON = definition.Constructor::_encode

 			# Prepare property mutators and accsessors.
 			_build_mutacc(definition, field, property) for field, property of definition.properties
@@ -207,13 +207,13 @@
 			_build_relationship(definition, rel_name, relationship) for rel_name, relationship of definition.relationships

 			# Add any additional prototypes functions
-			if (proto) then _.extend definition.Constructor.prototype, proto.prototype
+			if (proto) then _.extend definition.Constructor::, proto::

 		# Prepare a mutacc for a specific property.
 		# The property mutacc must handle entity accounting details.
 		_build_mutacc = (definition, field, property) =>
 			# Each field name is its own function combining getters and setters, depending on arguments.
-			definition.Constructor.prototype[field] = (value) ->
+			definition.Constructor::[field] = (value) ->
 				# Overloaded getter and setter.
 				if (undefined isnt value)
 					# Value is defined, so this is a setter
@@ -223,7 +223,7 @@
 					return @[field].get.call(@)

 			# Add the actual getters and setters to the new field
-			_.extend definition.Constructor.prototype[field],
+			_.extend definition.Constructor::[field],
 				# The setter has some accounting details to handle
 				set: (value) ->
 					# Only actually update it if it is a new value.
@@ -255,7 +255,7 @@
 		#/* TODO Thoroughly debug these functions... */
 		_build_relationship = (definition, field, relationship) =>
 			# The relationship is the name of a function that acts as getter/setter
-			definition.Constructor.prototype[field] = (entity) ->
+			definition.Constructor::[field] = (entity) ->
 				# Use arguments, since we might have a few things coming.
 				if arguments.length > 0
 					set = if relationship.type is "has_many" then "add" else "set"
@@ -265,7 +265,7 @@

 			# The multiple relations functions.
 			if "has_many" is relationship.type
-				_.extend definition.Constructor.prototype[field],
+				_.extend definition.Constructor::[field],
 					# Return the set of entities in the relationship.
 					get: (longGet) ->
 						# Lazy load
@@ -312,7 +312,7 @@
 						return @
 			# Mutaccs for has_a and is_a
 			else
-				_.extend definition.Constructor.prototype[field],
+				_.extend definition.Constructor::[field],
 					get: (longGet) ->
 						#if(longGet) {
 						#	// Lazy load
@@ -368,7 +368,7 @@
 		@[type].push(entity)

 	# #### Runtime Prototype
-	root.JEFRi.Runtime.prototype = _.extend {}, JEFRi.Runtime.prototype,
+	root.JEFRi.Runtime:: = _.extend {}, JEFRi.Runtime::,
 		# Reset the runtime's data, maintains context definitions.
 		clear: () ->
 			@_modified = {}
@@ -397,7 +397,7 @@
 		# affecting _ALL_ instances, both current and future, of type.
 		extend: (type, extend) ->
 			if (@_context.entities[type])
-				_.extend @_context.entities[type].Constructor.prototype, extend.prototype
+				_.extend @_context.entities[type].Constructor::, extend::

 		# Return the canonical memory reference of the entity.
 		intern: (entity, updateOnIntern) ->


latex files graylisting: adding .fdb_latexmk and .fls

--- salvus/page/project.coffee
+++ salvus/page/project.coffee
@@ -36,7 +36,7 @@
     'class'         : 'java'
     'exe'           : 'cs'

-for ext in misc.split('blg bbl glo idx toc aux log lof ind nav snm gz xyc out ilg')  # gz really synctex.gz
+for ext in misc.split('blg bbl glo idx toc aux log lof ind nav snm gz xyc out ilg fdb_latexmk fls')  # gz really synctex.gz
     masked_file_exts[ext] = 'tex'

 #many languages such as fortran or c++ have a default file name of "a.out." when compiled, so .out extensions are not masked


adding objects by class name only

adding objects by class name only

--- src/engine_levels.coffee
+++ src/engine_levels.coffee
@@ -62,5 +62,5 @@
   @name reloadLevel
   @methodOf Engine#
   ###
-  reloadLevel: ->
+  re: ->
     loadLevel I.currentLevelName


Unnecessary EventEmitter is unnecessary

Unnecessary EventEmitter is unnecessary

--- src/hubot/hipchat.coffee
+++ src/hubot/hipchat.coffee
@@ -1,6 +1,5 @@
 Robot        = require "robot"
 HTTPS        = require "https"
-EventEmitter = require("events").EventEmitter
 Wobot        = require("wobot").Bot

 class HipChat extends Robot


Rework target expansion

Rework target expansion

--- lib/assets/javascripts/unpoly/classes/change/from_content.coffee
+++ lib/assets/javascripts/unpoly/classes/change/from_content.coffee
@@ -30,53 +30,41 @@
         # requires a { target }. We use a target that matches the parsed { fragment }.
         @options.target = @getResponseDoc().rootSelector()

-      # (1) We seek @options.target in all matching layers
-      @expandIntoPlans(@layers, @options.target)
-
-      if @options.fallback == true
-        @options.fallback = ':main'
-
-      if @options.fallback != @options.target
-        # (2) In case fallback is a selector or array of selectors, we seek the fallback
-        # in all matching layers. If fallback is false or undefined, this won't add plans.
-        @expandIntoPlans(@layers,  @options.fallback)
+      targets = u.flatten([@options.target, @options.fallback])
+      @expandIntoPlans(@layers, targets)

     return @plans

-  expandIntoPlans: (layers, givenTarget) ->
+  expandIntoPlans: (layers, targets) ->
     for layer in layers
-      for target in @expandTargets(layer, givenTarget)
+      for target in @expandTargets(targets, layer)
         # Any plans we add will inherit all properties from @options
         props = u.merge(@options, { target, layer })
-
         if layer == 'new'
           change = new up.Change.OpenLayer(props)
-          @plans.push(change)
         else
           change = new up.Change.UpdateLayer(props)
-          @plans.push(change)
+        @plans.push(change)

-  expandTargets: (layer, givenTarget) ->
-    if givenTarget == ':main'
-      targets = @mainTargets(layer)
-    else
-      targets = u.wrapList(givenTarget)
+  expandTargets: (targets, layer) ->
+    expanded = new Set()

-    # Use u.map() instead of Array#map() because sometimes targets is a jQuery
-    # collection which an incompatible #map() implementation.
-    targets = u.map targets, (target) =>
-      if target == ':layer'
-        return @firstSwappableTarget(layer)
-      if u.isElementish(target)
-        return up.fragment.toTarget(target)
+    while targets.length
+      target = targets.shift
+
+      if target == ':main' || target == true
+        targets.unshift @mainTargets(layer)...
+      else if target == ':layer' && layer != 'new'
+        targets.unshift layer.getFirstSwappableElement()
+      else if u.isElementish(target)
+        expanded.push up.fragment.toTarget(target)
       else if u.isString(target)
-        return up.fragment.resolveTarget(target, { layer, @origin })
+        expanded.push up.fragment.resolveTarget(target, { layer, @origin })
       else
         # @buildPlans() might call us with { target: false } or { target: nil }
         # In that case we don't add a plan.
-        return

-    return u.compact(targets)
+    expanded

   mainTargets: (layer) ->
     if layer == 'new'


group bundles: use nicer titles

group bundles: use nicer titles

--- client/landing/app/Applications/Groups.kdapplication/Views/groupsbundleview.coffee
+++ client/landing/app/Applications/Groups.kdapplication/Views/groupsbundleview.coffee
@@ -30,14 +30,14 @@
     disk  : 0.5

   computeUnitVisibility =
-    users       : yes
-    vms         : yes
-    cpu         : no
-    ram         : no
-    disk        : no
-    cpuPerUser  : no
-    ramPerUser  : no
-    diskPerUser : no
+    'users'         : yes
+    'vms'           : yes
+    'cpu'           : no
+    'ram'           : no
+    'disk'          : no
+    'cpu per user'  : no
+    'ram per user'  : no
+    'disk per user' : no

   mapPlansToRadios: (plan) ->
     @plansByCode[plan.code] = plan
@@ -79,6 +79,8 @@
     @plansByCode = {}

     JGroupBundle.fetchPlans (err, plans) =>
+
+      # TODO: DRY

       @computePlans.addSubView computePlans = new KDInputRadioGroup
         name    : 'computePlan'
@@ -249,18 +251,23 @@
 #     console.log this
 #     @emit 'update'

-class GroupsBundleLimitPerUserView extends JView
-
-  pistachio: ->
-    """
-    {h2{#(title)}}
-    {h3{#(usage)}}
-
/

-
{.per-user{#(perUserQuota)}} - {.total{#(quota)}}

-    {h4{#(unit)}}
-    """
+# class GroupsBundleLimitPerUserView extends JView
+
+#   pistachio: ->
+#     """
+#     {h2{#(title)}}
+#     {h3{#(usage)}}
+#
/

+#
{.per-user{#(perUserQuota)}} - {.total{#(quota)}}

+#     {h4{#(unit)}}
+#     """

 class GroupsBundleLimitView extends JView
+
+  click:->
+    limit = @getData()
+    limit.quota = prompt 'enter a value', limit.quota
+    limit.emit 'update'

   pistachio: ->
     """


webterm: Only log ignored control codes if logRawOutput flag is set.

--- client/landing/app/Applications/WebTerm.kdapplication/src/ControlCodeReader.coffee
+++ client/landing/app/Applications/WebTerm.kdapplication/src/ControlCodeReader.coffee
@@ -147,7 +147,7 @@
     f

   ignored = (str) ->
-    -> log "Ignored: " + str
+    -> log "Ignored: " + str if localStorage?["WebTerm.logRawOutput"] is "true"

   originMode = false


Added test placeholders for all config options

--- test/spec/configurationTests.coffee
+++ test/spec/configurationTests.coffee
@@ -13,16 +13,154 @@
             runWithConf()
             alchemy.conf.should.deep.equal defaultConf

+        #Helpers
+        describe "afterLoad", ->
+            "placeholder"
+
         describe "dataSource", ->
             it "can find non-default datasets", ->
                 runWithConf({dataSource:"sample_data/ego1.json"})

                 # Make sure dataSource was changed from default (null)
-                alchemy.conf.dataSource.should.equal "sample_data/ego1.json"
-
+                alchemy.conf.dataSource.should.equal("sample_data/ego1.json") and
                 # Make sure it can use the dataset to create elements
-                console.log(alchemy.nodes)
                 alchemy.nodes.length.should.not.equal 0
+
+        #Layout
+        describe "graphWidth", ->
+            "placeholder"
+
+        describe "graphHeight", ->
+            "placeholder"
+
+        describe "alpha", ->
+            "placeholder"
+
+        describe "cluster", ->
+            "placeholder"
+
+        describe "clusterColours", ->
+            "placeholder"
+
+        describe "fixNodes", ->
+            "placeholder"
+
+        describe "fixRootNodes", ->
+            "placeholder"
+
+        describe "forceLocked", ->
+            "placeholder"
+
+        describe "linkDistance", ->
+            "placeholder"
+
+        describe "nodePositions", ->
+            # not yet implemented
+            "placeholder"
+
+        #Editing
+        describe "captionToggle", ->
+            "placeholder"
+
+        describe "edgesToggle", ->
+            "placeholder"
+
+        describe "nodesToggle", ->
+            "placeholder"
+
+        describe "toggleRootNodes", ->
+            "placeholder"
+
+        describe "removeNodes", ->
+            # not yet implemented
+            "placeholder"
+
+        describe "removeEdges", ->
+            # not yet implemented
+            "placeholder"
+
+        describe "addNodes", ->
+            # not yet implemented
+            "placeholder"
+
+        describe "addEdges", ->
+            # not yet implemented
+            "placeholder"
+
+        #Control Dash
+        describe "showControlDash", ->
+            "placeholder"
+
+        #Stats
+        describe "showStats", ->
+            "placeholder"
+
+        describe "nodeStats", ->
+            "placeholder"
+
+        describe "edgeStats", ->
+            "placeholder"
+
+        #Filtering
+        describe "showFilters", ->
+            "placeholder"
+
+        describe "edgeFilters", ->
+            "placeholder"
+
+        describe "nodeFilters", ->
+            "placeholder"
+
+        #Controls
+        describe "zoomControls", ->
+            "placeholder"
+
+        #Nodes
+        describe "nodeCaption", ->
+            "placeholder"
+
+        describe "nodeColour", ->
+            "placeholder"
+
+        describe "nodeMouseOver", ->
+            "placeholder"
+
+        describe "nodeOverlap", ->
+            "placeholder"
+
+        describe "nodeRadius", ->
+            "placeholder"
+
+        describe "nodeTypes", ->
+            "placeholder"
+
+        describe "rootNoteRadius", ->
+            "placeholder"
+
+        #Edges
+        describe "edgeCaption", ->
+            # not yet implemented
+            "placeholder"
+
+        describe "edgeColour", ->
+            "placeholder"
+
+        describe "edgeTypes", ->
+            "placeholder"
+
+        #Init
+        describe "initialScale", ->
+            "placeholder"
+
+        describe "initialTranslate", ->
+            "placeholder"
+
+        describe "scaleExtent", ->
+            # not yet implemented
+            "placeholder"
+
+        describe "warningMessage", ->
+            "placeholder"

         return
     return


partial application with bind, thanks again @wkf

--- lib/compiler.coffee
+++ lib/compiler.coffee
@@ -25,7 +25,7 @@
         adapter.render(content, options)
           .tap(=> @roots.emit('compile', f))

-      pipeline(adapters.map((a,i) => _.partial(task, a).bind(@)), contents)
+      pipeline(adapters.map((a,i) => task.bind(@, a)), contents)
         .then((out) => write_file.call(@, f, out, adapters[adapters.length-1]))

   copy: (f) ->


add roomType Attribute to user object

add roomType Attribute to user object

--- src/rocketchat.coffee
+++ src/rocketchat.coffee
@@ -177,6 +177,7 @@
 							return Q()
 						).then(() =>
 							user.roomID = newmsg.rid
+							user.roomType = messageOptions.roomType

 							if newmsg.t is 'uj'
 								@robot.receive new EnterMessage user, null, newmsg._id


generate: Output built componentlib definition

This information is now dynamically constructed during build,
and clients that talk to a runtime will generally not have
enough context to rebuild this in an identical way.
Therefor we output it, such that it can be provided in the client
(currently through a side-channel).

--- lib/generate.coffee
+++ lib/generate.coffee
@@ -115,6 +115,12 @@
     out += "#include \"" + comp.filename + "\"\n"  if comp.filename and not endsWith(comp.filename, ".cpp")
   out

+generateComponentMap = (componentLib) ->
+  # TODO: store more efficiently, so can be embedded in uC?
+  # TODO: store hash/versioning, so can be found by embedding just ID in uC?
+  out = JSON.stringify componentLib.definition
+  return out
+
 macroSafeName = (str) ->
   str.split(".").join("_").split("-").join "_"

@@ -141,6 +147,7 @@
   fs.writeFileSync baseDir + "/componentlib-source.hpp", sourceOutput
   all = "\n#include \"microflo.h\"\n" + ids + ports + sourceOutput
   fs.writeFileSync baseDir + "/componentlib.hpp", all
+  fs.writeFileSync baseDir + "/componentlib-map.json", generateComponentMap componentLib

 updateDefinitions = (baseDir) ->
   fs.writeFileSync baseDir + "/commandformat-gen.h", "// !! WARNING: This file is generated from commandformat.json !!" + "\n" + generateEnum("GraphCmd", "GraphCmd", cmdFormat.commands) + "\n" + generateEnum("Msg", "Msg", cmdFormat.packetTypes) + "\n" + generateEnum("DebugLevel", "DebugLevel", cmdFormat.debugLevels) + "\n" + generateEnum("DebugId", "Debug", cmdFormat.debugPoints) + "\n" + generateEnum("IoType", "IoType", cmdFormat.ioTypes)


Parent bugfix

Parent bugfix

--- lib/messages.coffee
+++ lib/messages.coffee
@@ -309,7 +309,7 @@
   messageParent: (child, parent, position = null) ->
     ## Notably, disabling oldParent search and importing options are not
     ## allowed from client, only internal to server.
-    _messageParent id, parent, position
+    _messageParent child, parent, position

   messageEditStart: (id) ->
     check Meteor.userId(), String


Start the game without Player being in mid-air.

Happens because PlayState#startingPoint is being calculated with sprite
height and not physics height.

--- src/state-play.coffee
+++ src/state-play.coffee
@@ -17,7 +17,7 @@

   {Key, Keyboard, Physics, Point, Rectangle, State, Timer} = Phaser

-  {artH, mapH, fontLarge, fontSmall, playerH, playerW, shakeFallH, deadzoneH} = defines
+  {artH, mapH, fontLarge, fontSmall, playerH, playerW, playerYOffset, shakeFallH, deadzoneH} = defines

   {AnimationMixin, CameraMixin, DebugMixin, DebugDisplayMixin, TextMixin} = Helpers

@@ -188,7 +188,7 @@
       , @game, @gui?.addOpenFolder 'platforms'

       @endingPoint = @platforms.ledges[-1...][0].createMidpoint @platforms
-      @startingPoint = new Point playerW, @world.height - playerH
+      @startingPoint = new Point playerW, (@world.height - playerH + playerYOffset)
       # Use for debugging ending.
       # @startingPoint = @platforms.ledges[-2...-1][0].createMidpoint @platforms


Update fmt-objt interface

Update fmt-objt interface

--- lib/Format.coffee
+++ lib/Format.coffee
@@ -1,6 +1,6 @@
 'use strict'

-fmtObj = require 'fmt-obj'
+{createFormatter} = require 'fmt-obj'
 slice = require 'sliced'
 chalk = require 'chalk'

@@ -18,17 +18,23 @@

 colorize = (value, color) -> chalk[color](value)

-prettyObj = (obj, color) ->
+prettyObj = (obj, color, opts = {}) ->
   lineColor = chalk[CONST.LINE_COLOR]
+  {offset = 2, depth = Infinity} = opts

-  fmtObj(obj, Infinity,
-    punctuation: lineColor
-    annotation: lineColor
-    property: chalk[color]
-    literal: lineColor
-    number: lineColor
-    string: lineColor
-  )
+  fmtObj = createFormatter({
+    offset,
+    formatter: {
+      punctuation: lineColor
+      annotation: lineColor
+      property: chalk[color]
+      literal: lineColor
+      number: lineColor
+      string: lineColor
+    }
+  })
+
+  fmtObj(obj, depth)

 serialize = (obj, color, key) ->
   # symbols cannot be directly casted to strings
@@ -93,8 +99,8 @@
       args.unshift arg
       match
     )
+  fmt += ' ' + serialize arg, color for arg in args if args.length

-  fmt += ' ' + serialize arg, color for arg in args if args.length
   fmt = fmt.replace(ESCAPE_REGEX, '%') if fmt.replace?
   serialize fmt, color


normalise focus factor

normalise focus factor

--- web/app/models/tweets.coffee
+++ web/app/models/tweets.coffee
@@ -45,7 +45,7 @@
 	query = """
 	MATCH (h:Hashtag)<-[:has_hashtag]-(t:Tweet)<-[:tweets]-(u:User)
 	WITH u AS user, COUNT(DISTINCT h) AS num_topics, COUNT(h) AS num_per_topic
-	RETURN user, num_topics, SUM(num_per_topic) AS sum_topic_usage, SUM(num_per_topic)*1.0/num_topics AS focus_factor
+	RETURN user, num_topics, SUM(num_per_topic) AS sum_topic_usage, 1.0-num_topics*1.0/SUM(num_per_topic) AS focus_factor
 	ORDER BY focus_factor DESC SKIP {skip} LIMIT {limit}
 	"""


WHAT DO YOU EXPECT

WHAT DO YOU EXPECT

--- Resources/search_view.coffee
+++ Resources/search_view.coffee
@@ -52,9 +52,9 @@
       animate = true

       Ti.App.addEventListener 'search_complete', -> animate = false
-      a = Ti.UI.createAnimation(duration: 500, curve:Titanium.UI.ANIMATION_CURVE_EASE_IN)
+      a = Ti.UI.createAnimation(duration: 300, curve:Titanium.UI.ANIMATION_CURVE_EASE_IN)
       a.bottom = 0
-      b = Ti.UI.createAnimation(duration: 500, curve:Titanium.UI.ANIMATION_CURVE_EASE_OUT)
+      b = Ti.UI.createAnimation(duration: 300, curve:Titanium.UI.ANIMATION_CURVE_EASE_OUT)
       b.bottom = -50
       a.addEventListener 'complete', => @bimg.animate(b)
       b.addEventListener 'complete', => @bimg.animate(a) if animate


TAP5-2237: t5/core/alert module - wrong default CSS class for alert

--- tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/alert.coffee
+++ tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/alert.coffee
@@ -106,7 +106,8 @@

       return unless container

-      className = severityToClass[data.severity] or "alert"
+      # Map from severity name to a CSS class; using alert-info if no severity, or unknown severity
+      className = severityToClass[data.severity] or "alert-info"

       content = if data.markup then data.message else _.escape data.message


#265 fix

#265 fix

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -666,6 +666,10 @@
   @extend()
   target: 'MoveToRelativeLine'

+class YankToLastCharacterOfLine extends Yank
+  @extend()
+  target: 'MoveToLastCharacterOfLine'
+
 # -------------------------
 # FIXME
 # Currently native editor.joinLines() is better for cursor position setting


:fire:

:fire:

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -1,5 +1,4 @@
 LineEndingRegExp = /(?:\n|\r\n)$/
-{inspect} = require 'util'

 _ = require 'underscore-plus'
 {Point, Range, CompositeDisposable, BufferedProcess} = require 'atom'


Remove debug logger.

Remove debug logger.

--- src/testrunner.coffee
+++ src/testrunner.coffee
@@ -25,7 +25,6 @@

 getDirectorySpecs = (directory) -> (specs, file) ->
   filePath = path.join directory, file
-  console.log 'Got directory spec', filePath
   if fs.statSync(filePath).isDirectory()
     fs.readdirSync(filePath).reduce getDirectorySpecs(filePath), specs
   else


Added project for maggie flow profiles

Added project for maggie flow profiles

--- projects.cson
+++ projects.cson
@@ -20,4 +20,11 @@
       "C:\\Users\\Fabio\\latex-writing\\paper-magnetic-profiles"
     ]
   }
+  {
+    title: "Maggie Flow Profiles"
+    group: "python"
+    paths: [
+      "C:\\Users\\Fabio\\code\\maggie-flow-profiles"
+    ]
+  }
 ]


git stash apply

git stash apply

--- src/translator.coffee
+++ src/translator.coffee
@@ -243,6 +243,8 @@

 trans.translator_hash['block_string_singleq'] = translate:(ctx, node)->
   s = node.value_view[3...-3]
+  s = s.replace /^\n/g, ''
+  s = s.replace /\n$/g, ''
   s = s.replace /\n/g, '\\n'
   s = s.replace /"/g, '\\"'
   '"' + s + '"'


Add missing "err." in the js driver

Add missing "err." in the js driver

Review 879 by @neumino

--- drivers/javascript/ast.coffee
+++ drivers/javascript/ast.coffee
@@ -901,7 +901,7 @@
 # Is this JS value representable as JSON?
 isJSON = (val, nestingDepth=20) ->
     if nestingDepth <= 0
-        throw new RqlDriverError "Nesting depth limit exceeded"
+        throw new err.RqlDriverError "Nesting depth limit exceeded"

     if (val instanceof TermBase)
         false


SUCCESS! :smile:

SUCCESS! :smile:

--- drivers/javascript/net.coffee
+++ drivers/javascript/net.coffee
@@ -1571,17 +1571,17 @@
     _writeQuery: (token, data) ->
         # We use encodeURI to get the length of the unicode
         # representation of the data.
-        buf = new Buffer(encodeURI(data).split(/%..|./).length - 1 + 8)
+        buf = new Buffer(data.length + 12)

         # Again, the token is encoded as a little-endian 8 byte
         # unsigned integer.
         buf.writeUInt32LE(token & 0xFFFFFFFF, 0)
         buf.writeUInt32LE(Math.floor(token / 0xFFFFFFFF), 4)
-
+        buf.writeUInt32LE(data.length, 8)
         # Write the data out to the bufferm then invoke `write` with
         # the buffer and token
-        buf.write(data, 8)
-        @write buf, token
+        buf.write(data, 12)
+        @write buf

     # #### WebSocketConnection write method
     #
@@ -1592,8 +1592,8 @@
     #
     # Despite not starting with an underscore, this is not a public
     # method.
-    write: (chunk, token) ->
-      @socket.send(chunk)
+    write: (chunk) ->
+      @socket.send(chunk.buffer)

 # ## isConnection
 #


Updated serializing in Elyssa.Size

Updated serializing in Elyssa.Size

--- src/types/size.coffee
+++ src/types/size.coffee
@@ -11,4 +11,6 @@
       x: @w / 2
       y: @h / 2

-    toString: -> JSON.stringify {@x, @y, @z}+    toString: -> Elyssa.serialize {@x, @y, @w, @h}, defaultValue
+
+    @fromString: (rectString) -> Elyssa.deserialize rectString, @name


Use flight to transition pages in AppRouter.

--- js/routers/AppRouter.coffee
+++ js/routers/AppRouter.coffee
@@ -9,11 +9,15 @@

   homePage: ->
     @view = new app.Views.Home
-    @view.render()
+    targetPanel = @view.render().el
+    flight.goToPage {targetPanel:targetPanel}

   goToPanel: (id) ->
     @view = new app.Views["Panel_" + id]
-    @view.render(id)
+    targetPanel = @view.render(id).el
+    flight.goToPage {targetPanel:targetPanel}
+
+

 @app = window.app ? {}
 @app.Routers.AppRouter = AppRouter


Fixed bug(?).

Fixed bug(?).

--- lib/atom-code-music.coffee
+++ lib/atom-code-music.coffee
@@ -1,6 +1,6 @@
 fs = require("fs")
 path = require("path")
-{CompositeDisposable} = require("atom")
+CompositeDisposable = require("atom").CompositeDisposable
 base64Binary = require(path.join(__dirname, "base64binary"))
 timbres =
   "Piano": require(path.join(__dirname, "timbres", "acoustic_grand_piano-ogg"))
@@ -159,7 +159,7 @@
           source.stop(@context.currentTime + 0.3)
         delete @audioSources[event.code]
       when "Real Piano Mode"
-        @audioSources[event.code].stop(@context.currentTime + 0.3)
+        @audioSources[event.code]?.stop(@context.currentTime + 0.3)
         delete @audioSources[event.code]

   toggle: () =>
@@ -179,7 +179,7 @@

   convert: () ->
     oldEditor = atom.workspace.getActiveTextEditor()
-    (atom.workspace.open()).then(->
+    (atom.workspace.open()).then(() ->
       newEditor = atom.workspace.getActiveTextEditor()
       lineConverter = new Converter(oldEditor, newEditor)
       lineConverter.convert()


Groups: banner is hidden after invitation code is redeemed

--- client/Login/AppView.coffee
+++ client/Login/AppView.coffee
@@ -350,6 +350,7 @@
           duration  : 2000

         KD.getSingleton('router').clear()
+        @headBanner.hide()

         setTimeout =>
           @hide()


Add global.alwaysReportToBugsnag flag to enable report testing

--- lib/reporter.coffee
+++ lib/reporter.coffee
@@ -56,6 +56,7 @@
   request options, -> # Empty callback prevents errors from going to the console

 shouldReport = (error) ->
+  return true if global.alwaysReportToBugsnag # Used to test reports in dev mode
   return true if exports.alwaysReport # Used in specs
   return false if atom.inDevMode()


don't focus in this case either

don't focus in this case either

--- lib/console/console.coffee
+++ lib/console/console.coffee
@@ -114,7 +114,7 @@
       p.setFlexScale 1/2
     p.activateItem @view
     p.onDidActivate => setTimeout =>
-      if document.activeElement == @view[0] && @view.lastCellVisible()
+      if document.activeElement == @view[0] && @view.lastCellVisible() && @isInput
           @view.focusInput(true)

   toggle: ->


Fix for dashboard/panel/redundancy

Fix for dashboard/panel/redundancy

--- admin/static/coffee/dashboard.coffee
+++ admin/static/coffee/dashboard.coffee
@@ -27,12 +27,15 @@
                         .concatMap(identity)
                         .filter({role: "director", state: "ready"})
                         .count()
-                    num_replicas: table_config('shards').concatMap(identity)("replicas").count()
+                    num_replicas: table_config('shards').concatMap(identity)("replicas").concatMap(identity).count()
                     num_available_replicas: table_status('shards')
                         .concatMap(identity)
                         .concatMap(identity)
-                        .filter({role: "replica", state: "ready"})
-                        .count()
+                        .filter( (assignment) ->
+                            assignment("state").eq("ready").and(
+                                assignment("role").eq("replica").or(assignment("role").eq("director"))
+                            )
+                        ).count()
                     tables_with_directors_not_ready: table_status.merge( (table) ->
                         shards: table("shards").indexesOf( () -> true ).map( (position) ->
                             table("shards").nth(position).merge
@@ -75,9 +78,11 @@
                 num_non_available_tables: r.row("tables_with_directors_not_ready").count()

             driver.run query, (error, result) =>
+                ###
                 console.log '----- err, result ------'
                 console.log error
                 console.log result
+                ###
                 if error?
                     #TODO
                     console.log error
@@ -263,14 +268,18 @@
             @$('.popup_container').hide()

         render: =>
-            @$el.html @template
+            data =
                 status_is_ok: @model.get('num_available_replicas') is @model.get('num_replicas')
-                num_replicas: @model.get 'num_directors'
-                num_available_replicas: @model.get 'num_available_directors'
-                num_non_available_replicas: @model.get('num_directors')-@model.get('num_available_directors')
+                num_replicas: @model.get 'num_replicas'
+                num_available_replicas: @model.get 'num_available_replicas'
+                num_non_available_replicas: @model.get('num_replicas')-@model.get('num_available_replicas')
                 num_non_available_tables: @model.get 'num_non_available_tables'
                 num_tables: @model.get 'num_tables'
-                tables_with_replicas_not_ready: @model.get('tables_with_directors_not_ready')
+                tables_with_replicas_not_ready: @model.get('tables_with_replicas_not_ready')
+
+            console.log JSON.stringify data, null, 2
+
+            @$el.html @template data

             if @display_popup is true and @model.get('num_available_directors') isnt @model.get('num_directors')
                 # We re-display the pop up only if there are still issues


:fire:

:fire:

Removed catch clause on repo promise. No longer needed since tests were improved

--- lib/tab-view.coffee
+++ lib/tab-view.coffee
@@ -193,10 +193,6 @@
     @repoForPath(@path).then (repo) =>
       @subscribeToRepo(repo)
       @updateVcsStatus(repo)
-    .catch (err) ->
-      # We can only get here if repository is destroyed
-      # by the time the promise is resolved.
-      # This happens during tests

   # Subscribe to the project's repo for changes to the VCS status of the file.
   subscribeToRepo: (repo) ->


Fixed a bug with the message inputs method

--- js/message.coffee
+++ js/message.coffee
@@ -31,6 +31,7 @@
 					@inputs[i.name] = $("<input type=text class='message-input message-input-textbox' />")

 		# Throw all supported inputs into the loop.
+		@dom_element.children('.message-inputs').html ""
 		for k,i of @inputs
 			@dom_element.children('.message-inputs').append i

@@ -64,7 +65,7 @@
 	input_states: ->
 		state = {}
 		for k,i of @inputs
-			state[k] = i.value()
+			state[k] = i.val()
 		return state

 	respond: (response) ->


Make the git path a configuration setting.

--- lib/merge-conflicts.coffee
+++ lib/merge-conflicts.coffee
@@ -11,4 +11,7 @@

   deactivate: ->

+  configDefaults:
+    gitPath: '/usr/local/bin/git'
+
   serialize: ->


:art: no need to extend

:art: no need to extend

--- lib/move-to-symbols.coffee
+++ lib/move-to-symbols.coffee
@@ -12,7 +12,6 @@
 state = {}

 class MoveToNextSymbol extends Motion
-  @extend()
   @commandPrefix: 'vim-mode-plus-user'
   direction: 'next'
   requireInput: true
@@ -63,7 +62,6 @@
         cursor.moveToFirstCharacterOfLine()

 class MoveToPreviousSymbol extends MoveToNextSymbol
-  @extend()
   direction: 'prev'

 module.exports = {state, MoveToNextSymbol, MoveToPreviousSymbol}


minor cleanup

minor cleanup

--- lib/operator-insert.coffee
+++ lib/operator-insert.coffee
@@ -1,4 +1,5 @@
 _ = require 'underscore-plus'
+{Range} = require 'atom'

 {
   moveCursorLeft, moveCursorRight
@@ -28,8 +29,8 @@
       textByUserInput = ''
       if change = @getChangeSinceCheckpoint('insert')
         @lastChange = change
-        @vimState.mark.set('[', change.start)
-        @vimState.mark.set(']', change.start.traverse(change.newExtent))
+        changedRange = new Range(change.start, change.start.traverse(change.newExtent))
+        @vimState.mark.setRange('[', ']', changedRange)
         textByUserInput = change.newText
       @vimState.register.set('.', text: textByUserInput) # Last inserted text

@@ -45,7 +46,7 @@
   canContinueOnEmptySelection: ->
     true

-  # we have to manage two separate checkpoint for different purpose(timing is different)
+  # Two checkpoint for different purpose
   # - one for undo(handled by modeManager)
   # - one for preserve last inserted text
   setCheckpoint: (purpose) ->
@@ -66,7 +67,7 @@
     checkpoint = @getCheckpoint(purpose)
     @editor.buffer.getChangesSinceCheckpoint(checkpoint)[0]

-  # [BUG] Replaying text-deletion-operation is not compatible to pure Vim.
+  # [BUG-BUT-OK] Replaying text-deletion-operation is not compatible to pure Vim.
   # Pure Vim record all operation in insert-mode as keystroke level and can distinguish
   # character deleted by `Delete` or by `ctrl-u`.
   # But I can not and don't trying to minic this level of compatibility.