:lipstick: Rename variables

:lipstick: Rename variables

--- lib/go-to-line-view.coffee
+++ lib/go-to-line-view.coffee
@@ -49,29 +49,29 @@

     return unless editorView? and lineNumber.length

-    currentLineNum = editorView.getModel().getCursorBufferPosition().row
-    lineAndCol = lineNumber.split(':')
-    if lineAndCol[0]?.length > 0
+    currentRow = editorView.getModel().getCursorBufferPosition().row
+    [row, column] = lineNumber.split(':')
+    if row?.length > 0
       # Line number was specified
-      lineNum = parseInt(lineAndCol[0]) - 1
+      row = parseInt(row) - 1
     else
       # Line number was not specified, so assume we will be at the same line
       # as where the cursor currently is (no change)
-      lineNum = currentLineNum
+      row = currentRow

-    if lineAndCol[1]?.length > 0
+    if column?.length > 0
       # Column number was specified
-      colNum = parseInt(lineAndCol[1]) - 1
+      column = parseInt(column) - 1
     else
       # Column number was not specified, so if the line number was specified,
       # then we should assume that we're navigating to the first character
       # of the specified line.
-      colNum = -1
+      column = -1

-    linePos = new Point(lineNum, colNum)
-    editorView.scrollToBufferPosition(linePos, center: true)
-    editorView.editor.setCursorBufferPosition(linePos)
-    if colNum < 0
+    position = new Point(row, column)
+    editorView.scrollToBufferPosition(position, center: true)
+    editorView.editor.setCursorBufferPosition(position)
+    if column < 0
       editorView.editor.moveCursorToFirstCharacterOfLine()

   storeFocusedElement: ->


Go-to-decl initial

Go-to-decl initial

--- lib/haskell-ghc-mod.coffee
+++ lib/haskell-ghc-mod.coffee
@@ -152,6 +152,18 @@
           detail: detail
           tooltip: (crange) ->
             infoTooltip target.getModel().getBuffer(), crange
+      'haskell-ghc-mod:go-to-declaration': ({target, detail}) =>
+        editor = target.getModel()
+        upi.withEventRange {editor, detail}, ({crange}) =>
+          @process.getInfoInBuffer(editor.getBuffer(), crange)
+          .then ({range, info}) ->
+            res = /.*-- Defined at (.+):(\d+):(\d+)$/.exec info
+            return unless res?
+            [_, fn, line, col] = res
+            console.log fn, line, col
+            atom.workspace.open fn,
+              initialLine: parseInt(line) - 1
+              initialColumn: parseInt(col) - 1
       'haskell-ghc-mod:show-info-fallback-to-type': ({target, detail}) ->
         upi.showTooltip
           editor: target.getModel()


Grunt: Allow to grep for tests using TESTS envvar

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -9,6 +9,7 @@
         src: ['spec/*.coffee']
         options:
           reporter: 'spec'
+          grep: process.env.TESTS

     # Coding standards
     coffeelint:


Increase max cyclomatic complexity to 17

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -98,7 +98,7 @@
           value: 120
         cyclomatic_complexity:
           level: 'error'
-          value: 14
+          value: 17

       source: ['src/**/*.coffee']
       grunt: 'Gruntfile.coffee'


Fixed a bug in the build file

Fixed a bug in the build file

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -85,6 +85,10 @@
                         from : /@@bnr@@/ig
                     ,   to   : "<%= pkg.version %>"
                     }
+                ,   {
+                        from : /..\/node_modules\/baijs\//ig
+                    ,   to   : ""
+                    }
                 ]

         # Make a zipfile.


Update push and release tasks

Update push and release tasks

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -53,6 +53,7 @@
       options:
         commit: true
         push: true
+        pushTo: ''
         commitFiles: ['package.json', 'octokit.js']

@@ -75,11 +76,18 @@

   # Dist
   # -----
-  grunt.registerTask 'dist', [
+  grunt.registerTask 'release', [
     'clean'
     'coffeelint'
     'coffee'
     'bump'
+  ]
+
+  grunt.registerTask 'release-minor', [
+    'clean'
+    'coffeelint'
+    'coffee'
+    'bump:minor'
   ]

   # Default


Update Gruntfile to run in pure coffeescript mode for easier debugging.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -39,12 +39,8 @@
         tasks: ['concurrent:coffee']
       # Whenever lib or test files are updated, rerun tests
       test:
-        files: ['test/**/*.coffee', 'index.js']
+        files: ['test/**/*.coffee', 'index.coffee']
         tasks: ['test']
-      # Whenever the JS files are transpiled, rebuild the documentation
-      lib:
-        files: ['lib/**/*.js']
-        tasks: [] # ['jsdoc']
       # Whenever the docs are changed we rebuild them
       docs:
         files: ['docs/**/*.rst']
@@ -94,10 +90,9 @@
         tasks: ['watch']
         options:
           logConcurrentOutput: true
-      # This runs all the coffee related tasks in parallel, including linting,
-      # transpiling, etc.
+      # This runs all the coffee related tasks in parallel
       coffee:
-        tasks: ['coffeelint:index', 'coffeelint:test', 'newer:coffee']
+        tasks: ['coffeelint:index', 'coffeelint:test']

     # Tasks that set environment variables
     env:
@@ -108,6 +103,6 @@
   require('load-grunt-tasks') grunt;

   # Our default grunt task compiles our coffeescript lib then runs our server
-  grunt.registerTask 'default', ['coffee', 'concurrent:dev']
-  grunt.registerTask 'test', ['coffee', 'env:test', 'mochaTest']
+  grunt.registerTask 'default', ['concurrent:dev']
+  grunt.registerTask 'test', ['env:test', 'mochaTest']


Correct comment typo

Correct comment typo

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -117,7 +117,7 @@
       }
     },

-    # Watch for changes to source and vendor files
+    # Watch for file changes
     watch: {
       files: [
         'Gruntfile.coffee',


Fixed f5 task to work on client-side code

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -51,7 +51,7 @@
 				ext: ".js"

 		concurrent:
-			start: ["watch:client", "watch:server", "nodemon:dev"]
+			start: ["watch:client", "nodemon:dev"]
 			options:
 				logConcurrentOutput: true

@@ -95,12 +95,12 @@
 				script: "index.js"
 				options:
 					nodeArgs: ["--debug"]
-					ignore: ['node_modules/**', 'client/**']
+					ignore: ['node_modules/**', 'client/lib/**']

 		watch:
 			client:
-				files: ["./client/**/*.coffee", "./client/**/*.less", "./views/*.html"]
-				tasks: ["coffee:client","less:dev","copy:views"]
+				files: ["./client/**/*.coffee", "./client/**/*.less", "./config.json"]
+				tasks: ["client"]
 			server:
 				files: ["./**/*.coffee","!./client/**/*.*"]
 				tasks: ["coffee:server"]
@@ -120,4 +120,4 @@

 	grunt.registerTask 'buildweb', 'Build the project into the dist/web', ["coffee:prod","less:prod","copy:prod"]

-	grunt.registerTask 'f5', 'Build, run, and watch both client and server content', ['server', 'client', 'concurrent:start']
+	grunt.registerTask 'f5', 'Build, run, and watch application.', ["client","concurrent"]


文件头添加版本号

文件头添加版本号

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,14 +1,16 @@
 sources = require "./sources"
+
 module.exports = (grunt) ->
+	pkg = grunt.file.readJSON "package.json"
 	grunt.initConfig
-		pkg: grunt.file.readJSON "package.json"
+		pkg: pkg
 		clean:
-			build: ["dest/work", "dest/publish"]
+			build: ["dest/work", "dist"]
 			workTemp: ["dest/work/cola"]
 			dev: ["dest/dev"]
 			"core-widget": [
-				"dest/publish/cola-core.js"
-				"dest/publish/cola-widget.js"
+				"dist/cola-core.js"
+				"dist/cola-widget.js"
 			]
 		coffee:
 			dev:
@@ -29,7 +31,7 @@
 					sourceMap: false
 					join: true
 				files:
-					"dest/publish/cola-core.js": [
+					"dist/cola-core.js": [
 						"dest/work/cola/coffee/cola.coffee"
 					]
 			"cola-widget":
@@ -37,7 +39,7 @@
 					sourceMap: false
 					join: true
 				files:
-					"dest/publish/cola-widget.js": [
+					"dist/cola-widget.js": [
 						"dest/work/cola/coffee/widget.coffee"
 					]
 		less:
@@ -52,7 +54,7 @@
 					sourceMap: false
 					join: true
 				files:
-					"dest/publish/cola.css": sources.less.cola
+					"dist/cola.css": sources.less.cola
 		copy:
 			libs:
 				expand: true
@@ -63,8 +65,8 @@
 			semantic:
 				expand: true
 				cwd: "src/lib/semantic-ui"
-				src: ["themes/**", "semantic.css","semantic.js"]
-				dest: "dest/publish"
+				src: ["themes/**", "semantic.css", "semantic.js"]
+				dest: "dist"

 			apiResources:
 				expand: true
@@ -93,9 +95,9 @@
 				files: [
 					{
 						expand: true,
-						cwd: 'dest/publish',
+						cwd: 'dist',
 						src: '**/*.js'
-						dest: 'dest/publish/min'
+						dest: 'dist'
 						ext: '.min.js'
 					}
 				]
@@ -145,7 +147,7 @@
 		"cola-ui-license":
 			options:
 				license: """
-/*! Cola UI - v1.0.0
+/*! Cola UI - #{pkg.version}
  * Copyright (c) 2002-2016 BSTEK Corp. All rights reserved.
  *
  * This file is dual-licensed under the AGPLv3 (http://www.gnu.org/licenses/agpl-3.0.html)
@@ -154,31 +156,32 @@
  * If you are unsure which license is appropriate for your use, please contact the sales department
  * at http://www.bstek.com/contact.
  */
+
 """
-			js:
-				files:
-					"dest/publish/cola-core.js": "dest/publish/cola-core.js"
-					"dest/publish/cola-widget.js": "dest/publish/cola-widget.js"
+			build:
+				files:
+					"dist/cola-core.js": "dist/cola-core.js"
+					"dist/cola-widget.js": "dist/cola-widget.js"
+					"dist/cola.css": "dist/cola.css"
 		concat:
 			"3rd":
 				files:
-					"dest/publish/3rd.js": sources.lib.js
+					"dist/3rd.js": sources.lib.js

 			cola:
 				files:
-					"dest/publish/cola.js": ["dest/publish/cola-core.js", "dest/publish/cola-widget.js"]
+					"dist/cola.js": ["dist/cola-core.js", "dist/cola-widget.js"]

 		cssmin:
 			target:
 				files: [
 					{
 						expand: true
-						cwd: 'dest/publish'
+						cwd: 'dist'
 						src: ['cola.css', 'semantic.css']
-						dest: 'dest/publish/min'
+						dest: 'dist'
 						ext: '.min.css'
 					}
-
 				]
 		compress:
 			js:
@@ -187,7 +190,7 @@
 				files: [
 					{
 						expand: true
-						cwd: "dest/publish"
+						cwd: "dist"
 						src: [
 							'cola.js'
 							'cola-widget.js'
@@ -195,7 +198,7 @@
 							'3rd.js'
 							'semantic.js'
 						]
-						dest: 'dest/publish/gzip'
+						dest: 'dist/gzip'
 						ext: '.gz.js'
 					}
 				]
@@ -205,12 +208,12 @@
 				files: [
 					{
 						expand: true
-						cwd: "dest/publish"
+						cwd: "dist"
 						src: [
 							'cola.css'
 							'semantic.css'
 						]
-						dest: 'dest/publish/gzip'
+						dest: 'dist/gzip'
 						ext: '.gz.css'
 					}
 				]
@@ -239,7 +242,7 @@
 	grunt.registerTask "all", ["clean", "coffee", "less", "mochaTest", "uglify", "copy"]
 	grunt.registerTask "w", ["watch"]
 	grunt.registerTask "build", ["clean:build", "cola-ui-clean", "coffee:cola-core", "coffee:cola-widget",
-	                             "less:build", "cola-ui-license", "concat",
-	                             "clean:core-widget", "copy:semantic",
-#								 "uglify:build", "cssmin",
-	                             "clean:workTemp"]+								 "less:build", "cola-ui-license", "concat",
+								 "clean:core-widget", "copy:semantic",
+								 "uglify:build", "cssmin",
+								 "clean:workTemp"]


Extra space

Extra space

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -21,7 +21,7 @@
           browserifyOptions:
             fullPaths: true
             basedir: "./client/source/"
-            commondir: "./client/source/"
+            commondir: "./client/source/"
             extensions: ['.coffee', '.hbs']
             paths: ['./client/source', 'node_modules']
           alias: [


Add test task

Add test task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -17,6 +17,13 @@
           stdout: true
           stderr: true

+      test:
+        command: 'jasmine-node --coffee spec/'
+        options:
+          stdout: true
+          stderr: true
+
   grunt.loadNpmTasks('grunt-contrib-coffee')
   grunt.loadNpmTasks('grunt-shell')
-  grunt.registerTask('default', ['coffee', 'shell'])
+  grunt.registerTask('default', ['coffee', 'shell:rebuild'])
+  grunt.registerTask('test', ['coffee', 'shell'])


fix 'test' and 'build' tasks

fix 'test' and 'build' tasks

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -60,15 +60,11 @@

   grunt.registerTask 'test', [
     'jshint'
+    'uglify'
     'nodeunit'
   ]

-  grunt.registerTask 'build', [
-    'replace'
-    'jshint'
-    'uglify'
-    'nodeunit'
-  ]
+  grunt.registerTask 'build', ['replace', 'test']

   grunt.registerTask 'default', ['build', 'watch']


updates grunt tasks

updates grunt tasks

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -35,7 +35,7 @@
 		# boots up nodemon for us
 		nodemon:
 			dev:
-				scripts: "bin<%= path.sep =%>www"
+				scripts: "bin<%= path.sep %>www"
 				options:
 					env: nodemon.env
 					watch: [
@@ -65,7 +65,7 @@
 		wiredep:
 			tasks:
 				src: [
-					".<%= path.sep =%>views<%= path.sep =%>layout.jade"
+					"views<%= path.sep %>layout.jade"
 				]

@@ -413,9 +413,9 @@
 	# grunt.registerTask("run", ["string-replace:dev", "less:dev", "uglify:dev", "concurrent:dev"])
 	# grunt.registerTask("dev", ["string-replace:dev", "less:dev", "uglify:dev", "concurrent:backend"])
 	# grunt.registerTask("vetcss", ["clean:tmp", "less:vet", "postcss:vet", "csslint:tmp"])
-	grunt.registerTask("wiredep")
 	grunt.registerTask("default", ["availabletasks"])
 	grunt.registerTask("serve-dev", ["concurrent:dev"])
+	# grunt.registerTask("wiredep")


Add browserify to watch

Add browserify to watch

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -36,6 +36,7 @@

   config.watch.main.files.push 'src/**/*.hbs'
   config.watch.main.tasks.push 'compile-handlebars', 'browserify'
+  config.watch.coffee.tasks.push 'browserify'

   tasks =
     # Building block tasks


rearrange by affinity

rearrange by affinity

--- lib/motions/motions.coffee
+++ lib/motions/motions.coffee
@@ -732,15 +732,20 @@
     this

 module.exports = {
-  Motion, MotionWithInput, MoveLeft, MoveRight, MoveUp, MoveDown,
-  MoveToPreviousWord, MoveToPreviousWholeWord, MoveToNextWord, MoveToNextWholeWord,
-  MoveToEndOfWord, MoveToNextParagraph, MoveToPreviousParagraph, MoveToAbsoluteLine, MoveToRelativeLine, MoveToBeginningOfLine,
-  MoveToFirstCharacterOfLineUp, MoveToFirstCharacterOfLineDown,
-  MoveToFirstCharacterOfLine, MoveToFirstCharacterOfLineAndDown, MoveToLastCharacterOfLine,
-  MoveToLastNonblankCharacterOfLineAndDown, MoveToStartOfFile,
-  MoveToTopOfScreen, MoveToBottomOfScreen, MoveToMiddleOfScreen, MoveToEndOfWholeWord, MotionError,
-  ScrollHalfUpKeepCursor, ScrollFullUpKeepCursor,
-  ScrollHalfDownKeepCursor, ScrollFullDownKeepCursor
+  Motion, MotionError
+  MotionWithInput
+  MoveLeft, MoveRight, MoveUp, MoveDown
+  MoveToPreviousWord, MoveToNextWord, MoveToEndOfWord
+  MoveToPreviousWholeWord, MoveToNextWholeWord, MoveToEndOfWholeWord
+  MoveToNextParagraph, MoveToPreviousParagraph
+  MoveToAbsoluteLine, MoveToRelativeLine, MoveToBeginningOfLine
+  MoveToFirstCharacterOfLine, MoveToFirstCharacterOfLineUp
+  MoveToLastCharacterOfLine, MoveToFirstCharacterOfLineDown
+  MoveToFirstCharacterOfLineAndDown, MoveToLastNonblankCharacterOfLineAndDown
+  MoveToStartOfFile,
+  MoveToTopOfScreen, MoveToBottomOfScreen, MoveToMiddleOfScreen,
+  ScrollHalfUpKeepCursor, ScrollHalfDownKeepCursor
+  ScrollFullUpKeepCursor, ScrollFullDownKeepCursor
   Find, Till
   MoveToMark
   Search, SearchCurrentWord, BracketMatchingMotion, RepeatSearch


ComputeProvider:tests: updateGroupInstanceUsage

--- workers/social/lib/social/models/computeproviders/computeprovider.test.coffee
+++ workers/social/lib/social/models/computeproviders/computeprovider.test.coffee
@@ -252,6 +252,44 @@
           done()

+  describe '#updateGroupInstanceUsage', ->
+
+    testGroup = null
+
+    it 'should increase given group instance count', (done) ->
+
+      withConvertedUser { createGroup: yes }, ({ group }) ->
+
+        testGroup = group
+
+        ComputeProvider.updateGroupInstanceUsage testGroup, 'increment', 2, (err) ->
+
+          expect(err).to.not.exist
+
+          JCounter.count
+            namespace : testGroup.slug
+            type      : 'member_instances'
+          , (err, count) ->
+            expect(err).to.not.exist
+            expect(count).to.be.equal 2
+
+            done()
+
+    it 'should decrease given group instance count', (done) ->
+
+      ComputeProvider.updateGroupInstanceUsage testGroup, 'decrement', 1, (err) ->
+        expect(err).to.not.exist
+
+        JCounter.count
+          namespace : testGroup.slug
+          type      : 'member_instances'
+        , (err, count) ->
+          expect(err).to.not.exist
+          expect(count).to.be.equal 1
+
+          done()
+
+

   describe '#update()', ->


Compromise on handling Julian and Gregorian dates

--- Src/coffeescript/cql-execution/src/elm/quantity.coffee
+++ Src/coffeescript/cql-execution/src/elm/quantity.coffee
@@ -2,6 +2,8 @@
 { ValueSet, Code } = require '../datatypes/datatypes'
 { build } = require './builder'

+# Unit conversation is currently implemented on for time duration comparison operations
+# TODO: Implement unit conversation for time duration mathematical operations
 # TODO: Quantity should probably be available as a datatype (not just ELM expression)
 module.exports.Quantity = class Quantity extends Expression
   constructor: (json) ->
@@ -55,18 +57,27 @@
     else null

   equals: (other) ->
-    if other.unit? && other.value? then @unit == other.unit && @value == parseFloat other.value else null
+    if other instanceof Quantity and @unit == other.unit
+      @value == parseFloat other.value
+    else if other instanceof Quantity and time_units[other.unit]? and time_units[@unit]?
+      thisSmallestDuration = smallestDuration(@)
+      otherSmallestDuration = smallestDuration(other)
+      thisSmallestDuration == otherSmallestDuration
+    else null

-# Hash of time units and their UCUM equivalents
+# Hash of time units and their UCUM equivalents, both case-sensitive and case-insensitive
 # See http://unitsofmeasure.org/ucum.html#para-31
-time_units = {'years': 'year', 'year': 'year', 'a': 'year', 'ANN': 'year', 'ann': 'year'
-  , 'months': 'month', 'month':'month', 'mo': 'month', 'MO': 'month'
-  , 'weeks': 'week', 'week': 'week', 'wk', 'week'
+# The CQL specification says that dates are based on the Gregorian calendar
+time_units = {'years': 'a_g', 'year': 'a_g', 'YEARS': 'a_g', 'YEAR': 'a_g'
+  , 'a': 'a_j', 'ANN': 'a_j', 'ann': 'a_j', 'A': 'a_j'
+  , 'months': 'mo_g', 'month':'mo_g'
+  , 'mo': 'mo_j', 'MO': 'mo_j'
+  , 'weeks': 'week', 'week': 'week', 'wk', 'week', 'WK', 'week'
   , 'days': 'day', 'day':'day', 'd': 'day', 'D': 'day'
   , 'hours': 'hour', 'hour': 'hour', 'h': 'hour', 'H': 'hour'
   , 'minutes': 'minute', 'minute': 'minute', 'min': 'minute', 'MIN': 'minute'
-  , 'seconds':'second', 'second':'second', 's': 'second'
-  , 'milliseconds' : 'millisecond', 'millisecond' : 'millisecond', 'ms': 'millisecond'
+  , 'seconds':'second', 'second':'second', 's': 'second', 'S': 'second'
+  , 'milliseconds' : 'millisecond', 'millisecond' : 'millisecond', 'ms': 'millisecond', 'MS': 'millisecond'
   }

 clean_unit = (units) ->
@@ -83,10 +94,12 @@
       when clean_unit(qty.unit) == 'hour' then qty.value * 60 * 60 * 1000
       when clean_unit(qty.unit) == 'day' then qty.value * 24 * 60 * 60 * 1000
       when clean_unit(qty.unit) == 'week' then qty.value * 7 * 24 * 60 * 60 * 1000
-      # Only supporting year durations based on a Julian year
-      # See http://unitsofmeasure.org/ucum.html#para-31
-      when clean_unit(qty.unit) == 'month' then qty.value * 30.436875 * 24 * 60 * 60 * 1000  # Based on a Julian mean month length of 30.4375 days
-      when clean_unit(qty.unit) == 'year' then qty.value * 365.25 * 24 * 60 * 60 * 1000  # Based on a Julian year of 365.25 days
+      # Support for the UCUM units based on the Gregorian calendar
+      when clean_unit(qty.unit) == 'mo_g' then qty.value * 30.436875 * 24 * 60 * 60 * 1000  # Based on a Gregorian mean month length of 30.436875 days
+      when clean_unit(qty.unit) == 'a_g' then qty.value * 365.2425 * 24 * 60 * 60 * 1000  # Based on a Gregorian year of 365.2425 days
+      # Support for the UCUM units based on the Julian calendar
+      when clean_unit(qty.unit) == 'mo_j' then qty.value * 30.4375 * 24 * 60 * 60 * 1000  # Based on a Julian mean month length of 30.4375 days
+      when clean_unit(qty.unit) == 'a_j' then qty.value * 365.25 * 24 * 60 * 60 * 1000  # Based on a Julian year of 365.25 days
       else qty.value
     millivalue
   else


Disable pinch-to-zoom, fixes #8850

Disable pinch-to-zoom, fixes #8850

--- src/atom-environment.coffee
+++ src/atom-environment.coffee
@@ -1,5 +1,6 @@
 crypto = require 'crypto'
 path = require 'path'
+webFrame = require 'web-frame'

 _ = require 'underscore-plus'
 {deprecate} = require 'grim'
@@ -188,6 +189,9 @@
     @stylesElement = @styles.buildStylesElement()
     @document.head.appendChild(@stylesElement)

+    # Disable pinch-to-zoom.
+    webFrame.setZoomLevelLimits(1, 1)
+
     @keymaps.subscribeToFileReadFailure()
     @keymaps.loadBundledKeymaps()


StatusUpdate: Tags are fetched for ContentDisplayStatusUpdate

--- client/Social/Activity/AppController.coffee
+++ client/Social/Activity/AppController.coffee
@@ -317,10 +317,13 @@
     return contentDisplay

   createStatusUpdateContentDisplay:(activity)->
-    @showContentDisplay new ContentDisplayStatusUpdate
-      title : "Status Update"
-      type  : "status"
-    ,activity
+    activity.fetchTags (err, tags) =>
+      unless err
+        activity.tags = tags
+        @showContentDisplay new ContentDisplayStatusUpdate
+          title : "Status Update"
+          type  : "status"
+        ,activity

   createBlogPostContentDisplay:(activity)->
     @showContentDisplay new ContentDisplayBlogPost


Fix React warning

Fix React warning

--- client/app/components/message_header.coffee
+++ client/app/components/message_header.coffee
@@ -47,8 +47,9 @@
         format = (user) ->
             items = []
             if user.name
+                key = user.address.replace(/\W/g, '')
                 items.push "#{user.name} "
-                items.push span className: 'contact-address',
+                items.push span className: 'contact-address', key: key,
                     i className: 'fa fa-angle-left'
                     user.address
                     i className: 'fa fa-angle-right'
@@ -102,7 +103,7 @@
             i
                 className: 'btn fa fa-paperclip fa-flip-horizontal'
                 onClick: @toggleAttachments
-            div className: 'popup', 'aria-hidden': !@state.showAttachements,
+            div className: 'popup', 'aria-hidden': not @state.showAttachements,
                 ul className: null,
                     for file in attachments
                         AttachmentPreview


Teambuilder: Improved EV entry

Teambuilder: Improved EV entry

1) Clicking on a field with 0 evs blanks it. No backspace required now
2) if value is blank, it won't jump to 252 evs

--- client/app/js/views/teambuilder_view.coffee
+++ client/app/js/views/teambuilder_view.coffee
@@ -31,6 +31,8 @@
     'change .selected_gender': 'changeGender'
     'change .selected_shininess': 'changeShiny'
     'change .iv-entry': 'changeIv'
+    'focus .ev-entry': 'focusEv'
+    'blur .ev-entry': 'changeEv'
     'change .ev-entry': 'changeEv'
     'change .select-hidden-power': 'changeHiddenPower'
     'keydown .selected_moves input': 'keydownMoves'
@@ -193,12 +195,18 @@
     pokemon = @getSelectedPokemon()
     pokemon.setIv(stat, value)

+  focusEv: (e) =>
+    $input = $(e.currentTarget)
+    value = parseInt($input.val())
+    $input.val("")  if value == 0
+
   changeEv: (e) =>
     # todo: make changeIv and changeEv DRY
     $input = $(e.currentTarget)
     stat = $input.data("stat")
     value = parseInt($input.val())
-    value = 252  if isNaN(value) || value > 252 || value < 0
+    value = 252  if value > 252
+    value = 0  if isNaN(value) || value < 0

     pokemon = @getSelectedPokemon()
     value = pokemon.setEv(stat, value)


cleanup

cleanup

--- lib/operation-stack.coffee
+++ lib/operation-stack.coffee
@@ -73,11 +73,9 @@
         else
           operation = new klass(@vimState, properties)

-      isValidOperation = false
       if @isEmpty()
         isValidOperation = true
         if (@mode is 'visual' and operation.isMotion()) or operation.isTextObject()
-          # Compliment implicit Select operator
           operation = new Select(@vimState).setTarget(operation)
       else
         isValidOperation = @peekTop().isOperator() and (operation.isMotion() or operation.isTextObject())


groups: customise select bg items now have a type, for event handling

--- client/app/Applications/Groups.kdapplication/Views/groupscustomizeviews.coffee
+++ client/app/Applications/Groups.kdapplication/Views/groupscustomizeviews.coffee
@@ -126,6 +126,7 @@
         url       : '/images/bg/no.jpg'
         thumbUrl  : '/images/bg/th/no.png'
         dataIndex : -1
+        type      : 'none'
       }
     ]
     for i in [1..5]
@@ -134,6 +135,7 @@
         url       : "/images/bg/bg0#{i}.jpg"
         thumbUrl  : "/images/bg/th/bg0#{i}.png"
         dataIndex : i-1
+        type      : 'default'

     @thumbsController.instantiateListItems items
     @attachListeners()
@@ -186,12 +188,13 @@

   click: ->
     # preview live
-    if @getData().dataIndex >= 0
+    if @getData().type is 'default'
       @getDelegate().emit 'DefaultImageSelected', @
       @getSingleton('staticGroupController').setBackground @getData().url
-    else
+    else if @getData().type is 'none'
       @getDelegate().emit 'NoImageSelected', @
       @getSingleton('staticGroupController').removeBackground()
+    else log 'Something weird happened'
   viewAppended:->
       super
       @setTemplate @pistachio()


forward options

forward options

--- client/lanes/models/AssociationProxy.coffee
+++ client/lanes/models/AssociationProxy.coffee
@@ -103,7 +103,7 @@
                 Proxy = Lanes.Models.AssocationProxy.construct(klass,
                     association_name: name
                 )
-                new Proxy(@)
+                new Proxy(@, @_proxied_model::associations.getOptions(name, @))
             )


Bugfix: Always focus first input in error group (ie. so slug doesn't focus for Title missing error)

--- client/source/views/base/mixins/form.coffee
+++ client/source/views/base/mixins/form.coffee
@@ -44,4 +44,4 @@
             #{message}
           """

-      @$('.has-error').eq(0).find('[name]').focus()
+      @$('.has-error').eq(0).find('[name]').eq(0).focus()


Small improvements to FormMixin

Small improvements to FormMixin

--- client/source/views/base/mixins/form.coffee
+++ client/source/views/base/mixins/form.coffee
@@ -2,10 +2,12 @@

 module.exports =
   render: ->
-    # Automatically focus the first visible input
+    # Defer to ensure view is fully rendered
     _.defer =>
+      return if @disposed
+
+      # Automatically focus the first visible input
       @$('.form-control:visible').eq(0).focus()
-
       # Prepare any submit buttons for Ladda
       @$btn = @$('.ladda-button').ladda()

@@ -14,10 +16,10 @@
     @$el.formParams no

   submit: (promise) ->
-    @$btn.ladda 'start'
+    @$btn?.ladda 'start'

     promise.always(
-      @$btn.ladda 'stop'
+      @$btn?.ladda 'stop'
     ).fail(
       _.bind(@renderServerErrors, @)
     )


:fire: performance measurement code commented

--- lib/provider/search.coffee
+++ lib/provider/search.coffee
@@ -123,9 +123,5 @@
         items = @flattenAndInjectRange(items)
         @items = @replaceOrAppendItemsForFilePath(@items, filePath, items)
     else
-      # searcher = @getConfig('searcherCommand')
-      # t0 = performance.now()
       @searchProjects(@projects).then (items) =>
-        # t1 = performance.now()
-        # console.log "[#{searcher}: performance.now] took #{t1 - t0} msec"
         @items = @flattenAndInjectRange(items)


Add functionality for controls container to slider.

--- app/assets/javascripts/lib/components/slider.js.coffee
+++ app/assets/javascripts/lib/components/slider.js.coffee
@@ -32,6 +32,10 @@
       @$prev = $('1 of '+@$el.find(@slides).length+'')
       @$legacy = $('html.ie7, html.ie8, body.browserIE7, body.browserIE8')

+      @$slider_controls_container = $('.slider__controls-container')
+      if @$slider_controls_container.length is 0
+        @$slider_controls_container = @slides_container.addClass('slider__controls-container')
+
       @init() unless @$el.length is 0

       # Polyfill for resizer in IE7/8
@@ -40,18 +44,26 @@

     init: ->
       @$slider_controls.append(@$next).append(@$prev)
-      @slides_container.append(@$slider_controls)
+      @$slider_controls_container.append(@$slider_controls)

       i=1
       while i <= $(@slides).length
         do (i) =>
           $slideLink = $(''+i+'')
-          $slideLink.on 'click', =>
+
+          if i is 1
+            $slideLink.addClass('is-active')
+
+          $slideLink.on 'click', (e) =>
+            $('.slider__pagination--link.is-active').removeClass('is-active')
+            $('.slider__pagination--link').eq(i-1).addClass('is-active')
+
             @_goToSlide(i)
-            return false
+            e.preventDefault()
           @$slider_pagination.append($slideLink);
         i++
-      @slides_container.after(@$slider_pagination)
+
+      @$slider_controls_container.append(@$slider_pagination)

       @_setupSlideClasses()

@@ -165,9 +177,17 @@

       $('.slider__control--next, .slider__control--prev').html(current.replace(/(^[0-9]+)/, index))

+      $('.slider__pagination--link.is-active').removeClass('is-active')
+      $('.slider__pagination--link').eq(index-1).addClass('is-active')
+
     _setupSlideClasses: ->
       @$el.find(@slides+':first').addClass('is-current').next().addClass('is-next')
       @$el.find(@slides+':last').addClass('is-prev')
+
+      # Give the user a visual cue that there are controls before fading them out.
+      setTimeout =>
+        @$slider_controls.addClass('is-faded-out')
+      , 1000

     _resetSlideClasses: ->
       classes = ['is-current', 'is-next', 'is-prev', 'js-bottom-layer', 'js-middle-layer']


#90 use identity mapping

#90 use identity mapping

--- app/assets/javascripts/oxalis/model/binary/cube.coffee
+++ app/assets/javascripts/oxalis/model/binary/cube.coffee
@@ -213,12 +213,12 @@

       cube[bucketIndex] = bucketData

-      # Generate dummy mapping
+      # Generate identity mapping
       mappingIndex = @getMappingIndexByZoomedAddress(address)
       if not @mappings[mappingIndex]?
         mapping = new Array( 1 << @LOCAL_ID_SIZE_P )
         for i in [0...mapping.length]
-          mapping[i] = (i + (mappingIndex % 23)) << 16
+          mapping[i] = i
         @mappings[mappingIndex] = mapping

       @setArbitraryBucketByZoomedAddress(address, bucketData) if address[3] <= @ARBITRARY_MAX_ZOOMSTEP
@@ -364,7 +364,7 @@
           currentAddress[2] >> 1
         ]

-      @pushQueue.insert( @positionToZoomedAddress( voxel, 0 ))
+      @pushQueue.insert( @positionToZoomedAddress( voxel ))

   getDataValue : ( voxel ) ->


Revert "Redirect / to /event_searches/new."

This reverts commit 0de8612155705207d54facc388020df1ceb82141.

The scope of the hook is too broad; it applies to _every_ route below
the application route.  A different approach is needed.

--- app/assets/javascripts/routes/application_route.coffee
+++ app/assets/javascripts/routes/application_route.coffee
@@ -14,7 +14,4 @@
       editable: false
       available: Pancakes.StudyLocation.find()

-  redirect: ->
-    @transitionTo 'event_searches.new'
-
 # vim:ts=2:sw=2:et:tw=78


remove useless comments

remove useless comments

--- app/assets/javascripts/uploadcare/build/_widget.coffee
+++ app/assets/javascripts/uploadcare/build/_widget.coffee
@@ -1,6 +1,5 @@
 import '../boot.coffee'

-# todo wtf is this??
 import '../vendor/jquery-xdr.js'
 import '../utils/abilities.coffee'
 import '../utils/collection.coffee'
@@ -12,13 +11,10 @@
 import '../locale/en.js.coffee'
 import '../locale.coffee'

-# todo import all in templates.coffee
 import '../templates.coffee'

-# todo after templates
 import '../stylesheets.coffee'

-# todo wtf is this
 import '../vendor/jquery-jcrop.js'
 import '../ui/crop-widget.coffee'
 import '../files/base.coffee'
@@ -26,7 +22,6 @@
 import '../files/object.coffee'
 import '../files/input.coffee'

-# todo wtf is this??
 import '../vendor/pusher.js'
 import '../utils/pusher.coffee'
 import '../files/url.coffee'
@@ -43,7 +38,6 @@
 import '../widget/tabs/base-preview-tab.coffee'
 import '../widget/tabs/preview-tab.coffee'

-# todo wtf is this
 import '../vendor/jquery-ordering.js'
 import '../widget/tabs/preview-tab-multiple.coffee'
 import '../widget/dialog.coffee'


Fix new variable colors to use `exampleHtml`

--- lib/styleguide-view.coffee
+++ lib/styleguide-view.coffee
@@ -18,92 +18,92 @@

         @div class: 'variables-group', =>
           @h2 'Text colors'
-          @exampleCode '''
-            @div class: 'is-color text-color',           '@text-color'
-            @div class: 'is-color text-color-subtle',    '@text-color-subtle'
-            @div class: 'is-color text-color-highlight', '@text-color-highlight'
-            @div class: 'is-color text-color-selected',  '@text-color-selected'
-            @div class: 'is-color'
-            @div class: 'is-color text-color-info',      '@text-color-info'
-            @div class: 'is-color text-color-success',   '@text-color-success'
-            @div class: 'is-color text-color-warning',   '@text-color-warning'
-            @div class: 'is-color text-color-error',     '@text-color-error'
+          @exampleHtml '''
+
@text-color

+
@text-color-subtle

+
@text-color-highlight

+
@text-color-selected

+

+
@text-color-info

+
@text-color-success

+
@text-color-warning

+
@text-color-error

           '''
         @div class: 'variables-group', =>
           @h2 'Background colors'
-          @exampleCode '''
-            @div class: 'is-color background-color-info',      '@background-color-info'
-            @div class: 'is-color background-color-success',   '@background-color-success'
-            @div class: 'is-color background-color-warning',   '@background-color-warning'
-            @div class: 'is-color background-color-error',     '@background-color-error'
-            @div class: 'is-color'
-            @div class: 'is-color background-color-highlight', '@background-color-highlight'
-            @div class: 'is-color background-color-selected',  '@background-color-selected'
-            @div class: 'is-color app-background-color',       '@app-background-color'
+          @exampleHtml '''
+
@background-color-info

+
@background-color-success

+
@background-color-warning

+
@background-color-error

+

+
@background-color-highlight

+
@background-color-selected

+
@app-background-color

           '''
         @div class: 'variables-group', =>
           @h2 'Base colors'
-          @exampleCode '''
-            @div class: 'is-color base-background-color',  '@base-background-color'
-            @div class: 'is-color base-border-color',      '@base-border-color'
+          @exampleHtml '''
+
@base-background-color

+
@base-border-color

           '''
         @div class: 'variables-group', =>
           @h2 'Component colors'
-          @exampleCode '''
-            @div class: 'is-color pane-item-background-color',  '@pane-item-background-color'
-            @div class: 'is-color pane-item-border-color',      '@pane-item-border-color'
-            @div class: 'is-color'
-            @div class: 'is-color input-background-color',  '@input-background-color'
-            @div class: 'is-color input-border-color',      '@input-border-color'
-            @div class: 'is-color'
-            @div class: 'is-color tool-panel-background-color',    '@tool-panel-background-color'
-            @div class: 'is-color tool-panel-border-color',        '@tool-panel-border-color'
-            @div class: 'is-color inset-panel-background-color',   '@inset-panel-background-color'
-            @div class: 'is-color inset-panel-border-color',       '@inset-panel-border-color'
-            @div class: 'is-color panel-heading-background-color', '@panel-heading-background-color'
-            @div class: 'is-color panel-heading-border-color',     '@panel-heading-border-color'
-            @div class: 'is-color overlay-background-color',       '@overlay-background-color'
-            @div class: 'is-color overlay-border-color',           '@overlay-border-color'
-            @div class: 'is-color'
-            @div class: 'is-color button-background-color',          '@button-background-color'
-            @div class: 'is-color button-background-color-hover',    '@button-background-color-hover'
-            @div class: 'is-color button-background-color-selected', '@button-background-color-selected'
-            @div class: 'is-color button-border-color',              '@button-border-color'
-            @div class: 'color'
-            @div class: 'is-color tab-bar-background-color',     '@tab-bar-background-color'
-            @div class: 'is-color tab-bar-border-color',         '@tab-bar-border-color'
-            @div class: 'is-color tab-background-color',         '@tab-background-color'
-            @div class: 'is-color tab-background-color-active',  '@tab-background-color-active'
-            @div class: 'is-color tab-border-color',             '@tab-border-color'
-            @div class: 'is-color'
-            @div class: 'is-color tree-view-background-color',   '@tree-view-background-color'
-            @div class: 'is-color tree-view-border-color',       '@tree-view-border-color'
+          @exampleHtml '''
+
@pane-item-background-color

+
@pane-item-border-color

+

+
@input-background-color

+
@input-border-color

+

+
@tool-panel-background-color

+
@tool-panel-border-color

+
@inset-panel-background-color

+
@inset-panel-border-color

+
@panel-heading-background-color

+
@panel-heading-border-color

+
@overlay-background-color

+
@overlay-border-color

+

+
@button-background-color

+
@button-background-color-hover

+
@button-background-color-selected

+
@button-border-color

+

+
@tab-bar-background-color

+
@tab-bar-border-color

+
@tab-background-color

+
@tab-background-color-active

+
@tab-border-color

+

+
@tree-view-background-color

+
@tree-view-border-color

           '''
         @div class: 'variables-group', =>
           @h2 'Site colors'
-          @exampleCode '''
-            @div class: 'is-color ui-site-color-1',    '@ui-site-color-1'
-            @div class: 'is-color ui-site-color-2',    '@ui-site-color-2'
-            @div class: 'is-color ui-site-color-3',    '@ui-site-color-3'
-            @div class: 'is-color ui-site-color-4',    '@ui-site-color-4'
-            @div class: 'is-color ui-site-color-5',    '@ui-site-color-5'
+          @exampleHtml '''
+
@ui-site-color-1

+
@ui-site-color-2

+
@ui-site-color-3

+
@ui-site-color-4

+
@ui-site-color-5

           '''
         @div class: 'variables-group', =>
           @h2 'Sizes'
-          @exampleCode '''
-            @div class: 'is-size disclosure-arrow-size',    '@disclosure-arrow-size'
-            @div class: 'is-size component-padding',        '@component-padding'
-            @div class: 'is-size component-icon-padding',   '@component-icon-padding'
-            @div class: 'is-size component-icon-size',      '@component-icon-size'
-            @div class: 'is-size component-line-height',    '@component-line-height'
-            @div class: 'is-size tab-height',               '@tab-height'
-            @div class: 'is-size font-size',                '@font-size'
+          @exampleHtml '''
+
@disclosure-arrow-size

+
@component-padding

+
@component-icon-padding

+
@component-icon-size

+
@component-line-height

+
@tab-height

+
@font-size

           '''
         @div class: 'variables-group', =>
           @h2 'Misc'
-          @exampleCode '''
-            @div class: 'is-radius component-border-radius',  '@component-border-radius'
-            @div class: 'is-font font-family',                '@font-family'
+          @exampleHtml '''
+
@component-border-radius

+
@font-family

           '''


Fix regression when downloading a specific URL.

Copy-paste restructuring error. It used to be all in one function, now it's an ivar.

--- application/chrome/content/wesabe/io/Downloader.coffee
+++ application/chrome/content/wesabe/io/Downloader.coffee
@@ -21,7 +21,7 @@
   # Starts the download. This is the only public method for this class.
   #
   download: ->
-    @_httpChannel = ioService.newChannel @url, '', null
+    @_httpChannel = @_ioService.newChannel @url, '', null
     @_httpChannel.QueryInterface Ci.nsIHttpChannel
     @_httpChannel.asyncOpen @_downloader, null


docs(cli): tweak the cli help message for --cache-clean

--- src/cli/get-cli-opts.coffee
+++ src/cli/get-cli-opts.coffee
@@ -28,7 +28,7 @@
 	program
 		.version config.build.pkg.version
 		.option '-s, --skip-options [opts]', 'skip build option(s) ex: dev,prod', list, []
-		.option '--cache-clean [value]', "cleans #{config.build.pkg.name}'s internal cache for an app, optionally provide * to clean internal cache for all apps"
+		.option '--cache-clean [opt]', "cleans #{config.build.pkg.name}'s internal cache for an app, optionally provide * to clean internal cache for all apps"
 		.option '--quick-start [location]', 'creates a simple application structure with a couple files. optional location: client or server. defaults to both'
 		.parse process.argv


add special message for `biglearn_not_ready` errors

--- tutor/src/components/error-monitoring/handlers.cjsx
+++ tutor/src/components/error-monitoring/handlers.cjsx
@@ -78,6 +78,22 @@
       <BS.Button key='ok' onClick={-> Dialog.hide()} bsStyle='primary'>OK</BS.Button>
     ]

+  # No exercises were generated because BL was not available
+  biglearn_not_ready: (error, message, context) ->
+    navigateAction = partial(goToDashboard, context, getCurrentCourse().id)
+    title: 'No exercises are available'
+    body:
+

+

+          There are no practice questions available at this time. Please try again later.
+

+

+    buttons: [
+      <BS.Button key='ok' onClick={navigateAction} bsStyle='primary'>OK</BS.Button>
+    ]
+    onOk: navigateAction
+    onCancel: navigateAction
+
   # No exercises were found, usually for personalized practice
   no_exercises: (error, message, context) ->
     navigateAction = partial(goToDashboard, context, getCurrentCourse().id)


Don't let deleted workflows be default (#3780)

* Don't let deleted workflows be default

* Remove default_workflow property from config if removing

--- app/pages/lab/workflow.cjsx
+++ app/pages/lab/workflow.cjsx
@@ -624,7 +624,7 @@
       @props.project.update 'configuration.default_workflow': @props.workflow.id
       @props.project.save()
     else
-      @props.project.update 'configuration.default_workflow': null
+      @props.project.update 'configuration.default_workflow': undefined
       @props.project.save()

   handleTutorialToggle: (tutorial, workflowTutorials, e) ->
@@ -660,6 +660,10 @@

     if confirmed
       @setState deletionInProgress: true
+
+      if @props.workflow.id is @props.project.configuration?.default_workflow
+        @props.project.update 'configuration.default_workflow': undefined
+        @props.project.save()

       @props.workflow.delete().then =>
         @props.project.uncacheLink 'workflows'


Remove some logging

Remove some logging

--- app/pages/lab/workflow.cjsx
+++ app/pages/lab/workflow.cjsx
@@ -216,7 +216,6 @@
       @transitionTo 'edit-project-details', projectID: @props.project.id

   handleTaskChange: (taskKey, path, value) ->
-    console?.log 'Handling task change', arguments...
     changes = {}
     changes["tasks.#{taskKey}.#{path}"] = value
     @props.workflow.update changes
@@ -239,7 +238,6 @@
   render: ->
     <PromiseRenderer promise={apiClient.type('workflows').get @props.params.workflowID}>{(workflow) =>
       <ChangeListener target={workflow}>{=>
-        console.log 'Workflow changed', JSON.stringify workflow
         <EditWorkflowPage {...@props} workflow={workflow} />
       }</ChangeListener>
     }</PromiseRenderer>


Give better feedback when there are no products.

--- src/coffee/pricesync.coffee
+++ src/coffee/pricesync.coffee
@@ -85,9 +85,12 @@
         .then (payload) ->
           processes = _.map payload.results, (elem) ->
             processFn(elem)
-          Q.all(processes)
-          .then (counts) ->
-            [_.reduce(counts, ((acc, count) -> acc + count), 0), payload]
+          if page is 1 and _.isEmpty(processes)
+            deferred.reject new Error('There are no products to sync prices for.')
+          else
+            Q.all(processes)
+            .then (counts) ->
+              [_.reduce(counts, ((acc, count) -> acc + count), 0), payload]
         .then ([count, payload]) ->
           pageProcessedCb(page, count)
           pageProducts page + 1, perPage, payload.total, count + acc


Google: Fixed page title accorting to advisor comment.

--- workers/social/lib/social/render/loggedout/kodinghome.coffee
+++ workers/social/lib/social/render/loggedout/kodinghome.coffee
@@ -10,7 +10,7 @@
     <!doctype html>
     <html lang="en" prefix="og: http://ogp.me/ns#">
     <head>
-      <title>Koding</title>
+      <title>Koding | A new way for developers to work.</title>
       #{getStyles()}
       #{getGraphMeta()}
     </head>


redesign Session#create flow

redesign Session#create flow

--- app/services/session.coffee
+++ app/services/session.coffee
@@ -5,25 +5,25 @@
   new class Session
     create: (formData, options = {}) ->
       promise = $auth.submitLogin(formData)
-      promise.then (user) ->
+      promise.then (user) =>
         global.cobudgetApp.currentUserId = user.id
         membershipsLoadedDeferred = $q.defer()
         global.cobudgetApp.membershipsLoaded = membershipsLoadedDeferred.promise
         Records.users.updateProfile(utc_offset: moment().utcOffset())
-        Records.memberships.fetchMyMemberships().then (data) ->
+        Records.memberships.fetchMyMemberships().then (data) =>
           membershipsLoadedDeferred.resolve(data)
           Records.users.fetchMe().then =>
             LoadBar.stop()
-            if options.redirectTo and CurrentUser().hasMemberships()
-              switch options.redirectTo
-                when 'group'
+            switch options.redirectTo
+              when 'group'
+                if CurrentUser().hasMemberships()
                   $location.path("/groups/#{CurrentUser().primaryGroup().id}")
-                when 'group setup'
-                  $location.path("/setup_group")
-            else
-              @clear().then ->
-                $location.path('/')
-                Dialog.alert(title: 'error!', content: 'you have no active memberships')
+                else
+                  @clear().then ->
+                    $location.path('/')
+                    Dialog.alert(title: 'error!', content: 'you have no active memberships')
+              when 'group setup'
+                $location.path("/setup_group")
       promise.catch ->
         LoadBar.stop()
       promise


Catch the null case for params

Catch the null case for params

--- src/smc-hub/hub_http_server.coffee
+++ src/smc-hub/hub_http_server.coffee
@@ -328,7 +328,7 @@
     # Save other paths in # part of URL then redirect to the single page app.
     router.get ['/projects*', '/help*', '/settings*', '/admin*', '/dashboard*', '/notifications*'], (req, res) ->
         url = require('url')
-        q = url.parse(req.url, true).search # gives exactly "?key=value,key=..."
+        q = url.parse(req.url, true).search || "" # gives exactly "?key=value,key=..."
         res.redirect(opts.base_url + "/app#" + req.path.slice(1) + q)

     # Return global status information about smc


Fix html builder bug: functions are considered objects, causing attributes to get clobbered.

--- src/stdlib/template/builder.coffee
+++ src/stdlib/template/builder.coffee
@@ -41,7 +41,7 @@

   elementIsVoid: (name) ->
-    _.contains(this.constructor.elements.void, name)
+    name in @constructor.elements.void

   extractOptions: (args) ->
     options = {}
@@ -49,7 +49,7 @@
       options.content = arg if _.isFunction(arg)
       options.text = arg if _.isString(arg)
       options.text = arg.toString() if _.isNumber(arg)
-      options.attributes = arg if _.isObject(arg)
+      options.attributes = arg if _.isObject(arg) and not _.isFunction(arg)
     options

   text: (string) ->


Faster whitespace test

Faster whitespace test

--- src/trix/models/html_parser.coffee
+++ src/trix/models/html_parser.coffee
@@ -268,7 +268,7 @@

   isInsignificantTextNode = (node) ->
     return unless node?.nodeType is Node.TEXT_NODE
-    return unless /^\s*$/.test(node.data)
+    return if /\S/.test(node.data)
     return if elementCanDisplayNewlines(node.parentNode)
     not node.previousSibling or isBlockElement(node.previousSibling) or not node.nextSibling or isBlockElement(node.nextSibling)


social: fix bug were guest users were being returned

--- workers/social/lib/social/models/activeitems.coffee
+++ workers/social/lib/social/models/activeitems.coffee
@@ -59,7 +59,8 @@

     @fetchRandomUsers callback

-  @fetchRandomUsers = (callback)-> JAccount.some {}, {limit:10}, callback
+  @fetchRandomUsers = (callback)->
+    JAccount.some {"profile.username":{"$not":/guest-/}}, {limit:10}, callback

   @fetchRandomTopics = (callback, options)->
     group = options.group or "koding"


 Merge branch 'master.bridgeToNowhere' into 'master'

Master.Bridge To Nowhere

--- workers/social/lib/social/models/activeitems.coffee
+++ workers/social/lib/social/models/activeitems.coffee
@@ -36,31 +36,36 @@
   #   * Active topics in the last day
   #   * Random topics
   @fetchTopics = secure (client, options, callback)->
-    [callback, options] = [options, callback]  unless callback
-    options.name       = "topic"
-    options.group      = client.context.group
-    options.fallbackFn = @fetchRandomTopics
-    cacheId            = "#{options.group}-activeItems.fetchTopics"
-    Cache.fetch cacheId, (@fetchItems.bind this), options, callback
+    # [callback, options] = [options, callback]  unless callback
+    # options.fallbackFn = @fetchRandomTopics
+    # cacheId            = "#{options.group}-activeItems.fetchTopics"
+    # Cache.fetch cacheId, (@fetchItems.bind this), options, callback
+
+    options.name  = "topic"
+    options.group = client.context.group
+    @fetchRandomTopics callback, options

   # Returns users in following order:
   #   * Client's followers who are online
   #   * Active users in the last day
   #   * Random users
   @fetchUsers = secure (client, options, callback)->
-    [callback, options] = [options, callback]  unless callback
-    options.client      = client
-    options.group       = client.context.group
-    options.fallbackFn  = @fetchRandomUsers
+    # [callback, options] = [options, callback]  unless callback
+    # options.client      = client
+    # options.group       = client.context.group
+    # options.fallbackFn  = @fetchRandomUsers

-    Cache.fetch "activeItems.fetchUsers", (@_fetchUsers.bind this), options, callback
+    # Cache.fetch "activeItems.fetchUsers", (@_fetchUsers.bind this), options, callback
+
+    @fetchRandomUsers callback

   @fetchRandomUsers = (callback)-> JAccount.some {}, {limit:10}, callback

   @fetchRandomTopics = (callback, options)->
     group = options.group or "koding"
     {select: selector} = nameMapping.topic
-    JTag.some {group, selector}, {limit:10}, callback
+    selector.group = group
+    JTag.some selector, {limit:10}, callback

   @_fetchUsers = (options={}, callback)->
     {client}   = options


scriptblock: removed deprecated functions

--- workers/social/lib/social/render/scriptblock.coffee
+++ workers/social/lib/social/render/scriptblock.coffee
@@ -15,24 +15,6 @@

   prefetchedFeeds = {}
   {bongoModels, client, intro, landing} = options
-
-  fetchMembersFromGraph = (cb)->
-    return cb null, [] unless bongoModels
-    {JGroup}  = bongoModels
-    groupName = client?.context?.group or 'koding'
-    JGroup.one slug: groupName, (err, group)->
-      return cb null, [] if err
-      group._fetchMembersFromGraph client, {}, cb
-
-  fetchActivityFromGraph = (cb)->
-    return cb null, [] unless bongoModels
-    {CActivity} = bongoModels
-    options = facets : "Everything"
-
-    CActivity._fetchPublicActivityFeed client, options, (err, data)->
-      return cb null, [] if err
-      return cb null, data
-

   createHTML = ->
     replacer    = (k, v)-> if 'string' is typeof v then encoder.XSSEncode v else v


Upgrade tests for a single ship.

Upgrade tests for a single ship.

--- tests/test.coffee
+++ tests/test.coffee
@@ -13,6 +13,10 @@
     .then ->
         @sendKeys 'input.select2-input', search_text
         @mouseEvent 'mouseup', ".select2-match:nth-of-type(#{n})"
+
+deselect = (select2_selector) ->
+    casper.then ->
+        @mouseEvent 'mousedown', "#{select2_selector} .select2-search-choice-close"

 casper.test.begin "Page comes up", (test) ->
     casper.start "index.html", ->
@@ -46,11 +50,94 @@
         test.assertSelectorHasText '.ship-xwing0 .addon-container .select2-container:nth-of-type(2) .select2-choice', 'No Astromech Upgrade'
         test.assertSelectorHasText '.ship-xwing0 .addon-container .select2-container:nth-of-type(3) .select2-choice', 'No Modification'
         test.assertDoesntExist '.ship-xwing0 .addon-container .select2-container:nth-of-type(4)'
+    .run ->
+        test.done()
+
+casper.test.begin "Add/remove torpedo upgrade", (test) ->
+    casper.start "index.html", ->
+        # Wait for pilot selector to become visible
+        @waitUntilVisible '#rebel-builder .pilot-selector-container .select2-container .select2-choice:first-child'
+
+    selectFirstMatch('#rebel-builder .pilot-selector-container .select2-container:first-of-type', 'Rookie Pilot')
+    .then ->
+        @waitUntilVisible '#rebel-builder .ship-xwing0 .points-display-container'

     selectFirstMatch('.ship-xwing0 .addon-container .select2-container:first-of-type', 'Proton Torpedoes')
     .then ->
         test.assertSelectorHasText '#rebel-builder .ship-xwing0 .points-display-container', 25
         test.assertSelectorHasText '#rebel-builder .total-points', 25

+    deselect('.addon-container .select2-container:first-of-type')
+    .then ->
+        test.assertSelectorHasText '#rebel-builder .ship-xwing0 .points-display-container', 21
+        test.assertSelectorHasText '#rebel-builder .total-points', 21
+        test.assertSelectorHasText '.ship-xwing0 .addon-container .select2-container:first-of-type .select2-choice', 'No Torpedo Upgrade'
+
     .run ->
         test.done()
+
+casper.test.begin "Add/remove astromech upgrade", (test) ->
+    casper.start "index.html", ->
+        # Wait for pilot selector to become visible
+        @waitUntilVisible '#rebel-builder .pilot-selector-container .select2-container .select2-choice:first-child'
+
+    selectFirstMatch('#rebel-builder .pilot-selector-container .select2-container:first-of-type', 'Rookie Pilot')
+    .then ->
+        @waitUntilVisible '#rebel-builder .ship-xwing0 .points-display-container'
+
+    selectFirstMatch('.ship-xwing0 .addon-container .select2-container:nth-of-type(2)', 'R5-K6')
+    .then ->
+        test.assertSelectorHasText '#rebel-builder .ship-xwing0 .points-display-container', 23
+        test.assertSelectorHasText '#rebel-builder .total-points', 23
+
+    deselect('.addon-container .select2-container:nth-of-type(2)')
+    .then ->
+        test.assertSelectorHasText '#rebel-builder .ship-xwing0 .points-display-container', 21
+        test.assertSelectorHasText '#rebel-builder .total-points', 21
+        test.assertSelectorHasText '.ship-xwing0 .addon-container .select2-container:nth-of-type(2) .select2-choice', 'No Astromech Upgrade'
+
+    .run ->
+        test.done()
+
+casper.test.begin "Add/remove modification", (test) ->
+    casper.start "index.html", ->
+        # Wait for pilot selector to become visible
+        @waitUntilVisible '#rebel-builder .pilot-selector-container .select2-container .select2-choice:first-child'
+
+    selectFirstMatch('#rebel-builder .pilot-selector-container .select2-container:first-of-type', 'Rookie Pilot')
+    .then ->
+        @waitUntilVisible '#rebel-builder .ship-xwing0 .points-display-container'
+
+    selectFirstMatch('.ship-xwing0 .addon-container .select2-container:nth-of-type(3)', 'Engine Upgrade')
+    .then ->
+        test.assertSelectorHasText '#rebel-builder .ship-xwing0 .points-display-container', 25
+        test.assertSelectorHasText '#rebel-builder .total-points', 25
+
+    deselect('.addon-container .select2-container:nth-of-type(3)')
+    .then ->
+        test.assertSelectorHasText '#rebel-builder .ship-xwing0 .points-display-container', 21
+        test.assertSelectorHasText '#rebel-builder .total-points', 21
+        test.assertSelectorHasText '.ship-xwing0 .addon-container .select2-container:nth-of-type(3) .select2-choice', 'No Modification'
+
+    .run ->
+        test.done()
+
+casper.test.begin "Multiple upgrades", (test) ->
+    casper.start "index.html", ->
+        # Wait for pilot selector to become visible
+        @waitUntilVisible '#rebel-builder .pilot-selector-container .select2-container .select2-choice:first-child'
+
+    selectFirstMatch('#rebel-builder .pilot-selector-container .select2-container:first-of-type', 'Rookie Pilot')
+    .then ->
+        @waitUntilVisible '#rebel-builder .ship-xwing0 .points-display-container'
+
+    selectFirstMatch('.ship-xwing0 .addon-container .select2-container:first-of-type', 'Proton Torpedoes')
+    selectFirstMatch('.ship-xwing0 .addon-container .select2-container:nth-of-type(2)', 'R5-K6')
+    selectFirstMatch('.ship-xwing0 .addon-container .select2-container:nth-of-type(3)', 'Engine Upgrade')
+
+    .then ->
+        test.assertSelectorHasText '#rebel-builder .ship-xwing0 .points-display-container', 31
+        test.assertSelectorHasText '#rebel-builder .total-points', 31
+
+    .run ->
+        test.done()


replace attr(data-...) by data(...)

replace attr(data-...) by data(...)

--- MediaAdminBundle/Resources/public/coffee/MediaModalView.coffee
+++ MediaAdminBundle/Resources/public/coffee/MediaModalView.coffee
@@ -59,7 +59,7 @@
     domContainer = $(".modal-body-content", @$el)
     @listenToOnce(formChannel, 'formSubmit', @reloadFolder) if $(event.target).hasClass('media-modal-menu-new-folder')
     $.ajax
-      url: $(event.target).attr('data-url')
+      url: $(event.target).data('url')
       method: 'GET'
       success: (response) ->
         viewClass = appConfigurationView.getConfiguration('media', 'showMediaForm')


fixed a bug in the spdy agents

fixed a bug in the spdy agents

--- src/zombie/resources.coffee
+++ src/zombie/resources.coffee
@@ -634,7 +634,8 @@
     else if protocol == 'spdy'
       cagent = null
       for agent in @resources.spdy_agents
-         if (@resources.spdy_agents[i].host == httpRequest.host && @resources.spdy_agents[i].port == httpRequest.port)
+#         console.log agent
+         if (agent.options.host == httpRequest.host && agent.options.port == httpRequest.port)
             cagent = agent
             break

@@ -651,6 +652,7 @@

         # Report the new connection
         @resources.browser.emit("newConnection", {})
+        @resources.spdy_agents.push(cagent)

       httpRequest.agent = cagent
       if httpRequest.plain


Allow multiple lines in substitution

Allow multiple lines in substitution

--- annotran/static/scripts/annotator/plugin/substitution.coffee
+++ annotran/static/scripts/annotator/plugin/substitution.coffee
@@ -68,10 +68,10 @@

     # we don't preserve full formatting but we can at least put some paragraph breaks in
     originalText = originalText.replace(new RegExp('\n', 'g'), '
')
-    console.log(originalText)
+    substituteText = substituteText.replace(new RegExp('\n', 'g'), '
')

     newEle = $('')
-    newEle.text(substituteText)
+    newEle.html(substituteText)

     newEle.css({
       "background-color" : "#F5F5F5"
@@ -83,7 +83,7 @@
       newEle.html(originalText)

     newEle.mouseleave (event) ->
-      newEle.text(substituteText)
+      newEle.html(substituteText)

     return newEle


Show errors returned in json when attempting to create a new connection

--- app/assets/javascripts/admin/views/connections/new.js.coffee
+++ app/assets/javascripts/admin/views/connections/new.js.coffee
@@ -126,9 +126,9 @@
         else
           view = new Augury.Views.Connections.Select(signup: signup)
           $("#integration_main").html view.render().el
-
-      error: (xhr, textStatus, errorThrown) ->
-        Augury.Flash.error 'There was a problem logging in. Please check your credentials and try again.'
+      error: (xhr, textStatus, errorThrown) =>
+        errors = $.parseJSON(xhr.responseText).errors.join(", ")
+        Augury.Flash.error "Please fix the following errors: " + errors

   connect: ->
     @set_url()
@@ -148,6 +148,6 @@

       success: (login, response, opts)=>
         window.location.href = "/admin/integration/register?url=#{Augury.url}&env=#{login.env}&user_token=#{login.auth_token}&store_id=#{login.store_id}&user=#{login.user}"
-      error: (x,y,z) =>
-        Augury.Flash.error 'There was a problem connecting. Please check your credentials and try again.'
-
+      error: (xhr, textStatus, errorThrown) =>
+        errors = $.parseJSON(xhr.responseText).errors.join(", ")
+        Augury.Flash.error "Please fix the following errors: " + errors


waterfall chart: y-axis extra padding

waterfall chart: y-axis extra padding

So the label will always be visible. Closes #890

--- app/assets/javascripts/lib/views/waterfall_chart_view.coffee
+++ app/assets/javascripts/lib/views/waterfall_chart_view.coffee
@@ -65,6 +65,7 @@
             showGridline: false
             fontSize: '10px'
         y2axis:
+          padMax: 1.1
           rendererOptions:
             forceTickAt0: true # we always want a tick a 0
           tickOptions:


Added comments to openEntrySvc angularjs methods.

--- app/assets/javascripts/ng-services/ng-OpenEntrySvc.js.coffee
+++ app/assets/javascripts/ng-services/ng-OpenEntrySvc.js.coffee
@@ -6,6 +6,9 @@
 ['$rootScope', '$location', '$anchorScroll',
 ($rootScope, $location, $anchorScroll)->

+  #---------------------------------------------
+  # Set the currently open entry
+  #---------------------------------------------
   set: (entry)->
     $rootScope.open_entries = [entry]
     # Scroll so that the entry link is at the top of the viewport, for maximum visibility of
@@ -13,16 +16,25 @@
     $location.hash "entry-#{entry.id}-anchor"
     $anchorScroll()

+  #---------------------------------------------
+  # Unset the currently open entry
+  #---------------------------------------------
   unset: ->
     $rootScope.open_entries = []
     $location.hash('')

+  #---------------------------------------------
+  # Get the currently open entry
+  #---------------------------------------------
   get: ->
     if $rootScope.open_entries?.length > 0
       return $rootScope.open_entries[0]
     else
       return null

+  #---------------------------------------------
+  # Return true if the passed entry is open, false otherwise
+  #---------------------------------------------
   is_open: (entry)->
     if $rootScope.open_entries?.length > 0
       return $rootScope.open_entries[0].id==entry.id


FF: Restore window expando on every event listener

This should fix *most* problems from Firefox issue 1408996[1].

The root cause of the bug is that document.domain is set, and Firefox
discards the 'expando' for the window that contains all properties we
set. Technically, this can happen at any time, and so some failures are
still possible. This fixes #2799.

[1]: https://bugzilla.mozilla.org/show_bug.cgi?id=1408996

--- content_scripts/vimium_frontend.coffee
+++ content_scripts/vimium_frontend.coffee
@@ -157,6 +157,7 @@
 # Wrapper to install event listeners.  Syntactic sugar.
 installListener = (element, event, callback) ->
   element.addEventListener(event, forTrusted(->
+    root.extend window, root unless extend?
     if isEnabledForUrl then callback.apply(this, arguments) else true
   ), true)


avatar fix

avatar fix

--- packages/rocketchat-ui-account/account/avatar/avatar.coffee
+++ packages/rocketchat-ui-account/account/avatar/avatar.coffee
@@ -8,6 +8,8 @@
 			return

 		user = Meteor.users.findOne({name:username})
+		if (!user)
+			user = Meteor.users.findOne({username:username})
 		if user?.photo
 			return "background-image:url(#{user.photo});"


Various fixes

Various fixes

--- js/script.coffee
+++ js/script.coffee
@@ -85,7 +85,7 @@
 			body = ''
 			# :PAYLOAD_ID
 			# :CUSTOMERS[0]
-			body += "#{p.rocket.second_stage.payloads[0].payload_id}#{p.rocket.second_stage.payloads[0].customers[0]}	"
+			body += "#{(p.rocket.second_stage.payloads.map (p) -> p.payload_id).join ' / '}#{(p.rocket.second_stage.payloads.map (p) -> p.customers[0]).join ' / '}	"
 			# :PAYLOAD_TYPE
 			body += "#{p.rocket.second_stage.payloads[0].payload_type}"
 			# [:CAP_SERIAL]
@@ -93,13 +93,16 @@
 				body += " [#{p.rocket.second_stage.payloads[0].cap_serial}]"
 			# to :ORBIT
 			# :CAP_SERIAL
-			body += " to #{p.rocket.second_stage.payloads[0].orbit}	"
+			body += " to #{p.rocket.second_stage.payloads[0].orbit}"
+			body += "#{(p.rocket.second_stage.payloads.map (p) -> p.payload_mass_kg).join ' + '}"
+			body += "	"
 			# :ROCKET_NAME :ROCKET_TYPE [:CORE_SERIAL] to :LANDING_VEHICLE
 			# CORE_SERIAL
-			body += "#{p.rocket.rocket_name} #{p.rocket.rocket_type}#{if p.rocket.first_stage.cores[0].landing_vehicle then " to #{p.rocket.first_stage.cores[0].landing_vehicle}" else ' EXP'}"
+			body += "#{p.rocket.rocket_name} #{p.rocket.rocket_type}#{if p.rocket.first_stage.cores[0].landing_vehicle then " to #{p.rocket.first_stage.cores[0].landing_vehicle}" else ' EXP '}"
 			for c in p.rocket.first_stage.cores
-			  body += "[#{c.core_serial}] "
+			  body += "[#{c.core_serial or 'TBD'}] "
 				# body += ""
+				body += ""
 			body += "	"
 			# :LAUNCH_SITE_NAME
 			# #:FLIGHT_NUMBER :LOCAL_DATE


Add information to xblocks about when they complete initialization that's visible in javascript

--- common/static/coffee/src/xblock/core.coffee
+++ common/static/coffee/src/xblock/core.coffee
@@ -18,6 +18,8 @@
     block.element = element
     block.name = $element.data("name")

+    $element.trigger("xblock-initialized")
+    $element.data("initialized", true)
     block

   initializeBlocks: (element) ->


support enter key for token entry

support enter key for token entry

--- app/assets/javascripts/states/main/views/login_token.js.coffee
+++ app/assets/javascripts/states/main/views/login_token.js.coffee
@@ -7,6 +7,11 @@

     events:
+      "keypress input": (e) ->
+        if e.keyCode == 13
+          e.preventDefault()
+          @check()
+
       "click button.check_token": (e) ->
         @check()


Update home.cjsx

Update home.cjsx

Added call-to-action for "Best of 2015" event.

--- app/home.cjsx
+++ app/home.cjsx
@@ -26,6 +26,7 @@

Welcome to Africa—home of the chimpanzee.
Our cameras have taken thousands of videos of these and other animals.

         Now we need your help to study, explore, and learn from them.

         Get Started
+        We're collecting the best clips of 2015!
Nominate your favorites and vote today!
         Learn More


Fix map

Fix map

--- components/inquiry_questionnaire/map.coffee
+++ components/inquiry_questionnaire/map.coffee
@@ -29,7 +29,6 @@
   steps: [
     prequalify:
       true: [
-        'specialist'
         'commercial_interest'
         is_collector:
           true: [


Add tests

Add tests

--- picoCTF-web/web/coffee/management-competition-checks.coffee
+++ picoCTF-web/web/coffee/management-competition-checks.coffee
@@ -68,16 +68,62 @@
   alwaysTrue: (t, setStatus) ->
     setTimeout (setStatus.bind null, t), (Math.random() * 3000)

+  checkEnabledProblems: (setStatus) ->
+    apiCall "GET", "/api/admin/problems"
+    .done (result) ->
+      status = "failing"
+      for problem in result.data.problems
+        if problem.disabled == false
+          status = "passing"
+          break
+
+      setStatus status
+
+
+  checkProblemsAlive: (setStatus) ->
+    apiCall "GET", "/api/admin/shell_servers"
+    .done (api) ->
+      status = "passing"
+      servers = api.data
+
+      if servers.length is 0
+        status = "failing"
+
+      apiCalls = $.map(servers, (server) -> apiCall "GET", "/api/admin/shell_servers/check_status", {sid: server.sid})
+      ($.when).apply(this, apiCalls).done () ->
+        for result in $.map(arguments, _.first)
+          if result.status is 0
+            status = "failing"
+        setStatus status
+
+  checkDownloadsAccessible: (setStatus) ->
+    apiCall "GET", "/api/admin/problems"
+    .done (result) ->
+      status = "passing"
+      requests = []
+      for problem in result.data.problems
+        for instance in problem.instances
+          $("<p>"+instance.description+"</p>").find("a").each (i, a) ->
+            url = $(a).attr("href")
+            requests.push($.ajax({url: url, dataType: 'text', type: 'GET'}))
+
+      ($.when).apply(this, apiCalls).done () ->
+        for result in arguments
+          if result.status is 404
+            status = "failing"
+
+        setStatus status
+
+
   onStatusChange: (status) ->
     @setState update @state,
       competitionReadiness: $set: status

   render: ->
     sanityChecks = [
-      {name: "Just Checking 1", func: @alwaysTrue.bind null, "passing"}
-      {name: "Just Checking 2", func: @alwaysTrue.bind null, "passing"}
-      {name: "Just Checking 3", func: @alwaysTrue.bind null, "passing"}
-
+      {name: "Check Enabled Problems", func: @checkEnabledProblems}
+      {name: "Problems Alive on Shell Server", func: @checkProblemsAlive}
+      #{name: "Problem Downloads Accessible", func: @checkDownloadsAccessible}
     ]

     <div>


Client: Activity: formatting

Client: Activity: formatting

--- client/activity/lib/components/sidebarmodallist/index.coffee
+++ client/activity/lib/components/sidebarmodallist/index.coffee
@@ -23,7 +23,10 @@

     super

-    @state = { threads: @props.sidebarModalChannels.followed, noResultText: no, value: '' }
+    @state =
+      value: ''
+      noResultText: no
+      threads: @props.sidebarModalChannels.followed

   onThresholdReached: ->
@@ -66,6 +69,7 @@
     kd.utils.wait 1000, ->
       isSearching = no

+
   search: (event) ->

     { value }   = event.target
@@ -73,7 +77,7 @@

     @setState { value }

-    if value is '' then return @resetSearch()
+    return @resetSearch()  if value is ''

     isSearching = yes


utilities: tryonkoding: minor changes

utilities: tryonkoding: minor changes

--- client/home/lib/utilities/components/tryonkoding/view.coffee
+++ client/home/lib/utilities/components/tryonkoding/view.coffee
@@ -1,6 +1,6 @@
-kd               = require 'kd'
-React            = require 'kd-react'
-KodingSwitch     = require 'app/commonviews/kodingswitch'
+kd = require 'kd'
+React = require 'kd-react'
+KodingSwitch = require 'app/commonviews/kodingswitch'
 Toggle = require 'app/components/common/toggle'

 module.exports = class TryOnKodingView extends React.Component
@@ -17,13 +17,14 @@

   renderButtons: ->
+

       {@renderGuideButton()} {@renderTryOnKodingButton()}

   render: ->
-
+

       <Toggle checked={@props.checked} className='TryOnKoding-onOffButton' callback={@props.handleSwitch} />
       <Primary className={@props.primaryClassName}/>


spelling error...grrrr

spelling error...grrrr

--- client/landing/site.hackathon/coffee/home/AppViewFull.coffee
+++ client/landing/site.hackathon/coffee/home/AppViewFull.coffee
@@ -513,7 +513,7 @@
         Import.io
         Imgur

-        ...to name a few. And any metion of APIs would be incomplete without a hat tip
+        ...to name a few. And any mention of APIs would be incomplete without a hat tip
         to Programmable Web


Clickable icons

Clickable icons

--- source/assets/app/javascripts/base.js.coffee
+++ source/assets/app/javascripts/base.js.coffee
@@ -74,6 +74,7 @@
     c = f.element
     p = c.parentNode
     u = f.element = marker
+    f.icon = icon
     u.setAttribute("transform", c.getAttribute("transform"))
     icon.setAttribute("transform", c.getAttribute("transform"))
     p.removeChild(c)
@@ -87,6 +88,7 @@
 loadTooltips = (e) ->
   for f in e.features
     f.element.addEventListener("mousedown", toggleTooltip(f.data), false)
+    f.icon.addEventListener("mousedown", toggleTooltip(f.data), false)
     f.element.addEventListener("dblclick", cancelTooltip, false)

 showTooltips = (e) ->
@@ -112,7 +114,7 @@
   return unless tip.visible
   p = map.locationPoint(tip.location)
   tip.anchor.style.left = p.x - radius + "px"
-  tip.anchor.style.top = p.y - radius + "px"
+  tip.anchor.style.top = p.y - 20 + "px"
   $(tip.anchor).tipsy("show")

 toggleTooltip = (f) ->
@@ -159,7 +161,7 @@
       trigger: "manual",
       html: true
     })
-
+
   tip.toggle

 # View change


Environments: Tidy up the update calls for scene

--- client/Environments/views/environmentsmainscene.coffee
+++ client/Environments/views/environmentsmainscene.coffee
@@ -60,8 +60,7 @@
       cssClass : 'stack-toggle'
       callback : =>
         @setHeight if @getHeight() < 300 then 600 else 36
-        @scene.updateConnections()
-        KD.utils.wait 200, => @scene.updateScene()
+        KD.utils.wait 300, @bound 'updateView'

     # Main scene for DIA
     @addSubView @scene = new EnvironmentScene
@@ -74,6 +73,9 @@
     domainsContainer = new EnvironmentDomainContainer
     @scene.addContainer domainsContainer

+    domainsContainer.on 'itemRemoved', @lazyBound('updateView', yes)
+    domainsContainer.on 'itemAdded',   @lazyBound('updateView', yes)
+
     # VMs / Machines Container
     machinesContainer = new EnvironmentMachineContainer
     @scene.addContainer machinesContainer
@@ -82,5 +84,9 @@
     extrasContainer = new EnvironmentExtraContainer
     @scene.addContainer extrasContainer

-    # domainsContainer.on "itemRemoved", scene.bound 'updateConnections'
-    # KD.getSingleton("vmController").on 'VMListChanged', -> refreshContainers()+  updateView:(updateData = no)->
+
+    @scene.updateConnections()  if updateData
+
+    @scene.highlightLines()
+    @scene.updateScene()


StacksCustomViews: add credentialList custom view to list credentials for given provider

--- client/admin/lib/views/stacks/stackscustomviews.coffee
+++ client/admin/lib/views/stacks/stackscustomviews.coffee
@@ -1,8 +1,10 @@
-_                    = require 'lodash'
-kd                   = require 'kd'
-globals              = require 'globals'
-CustomViews          = require 'app/commonviews/customviews'
-ComputeController_UI = require 'app/providers/computecontroller.ui'
+_                               = require 'lodash'
+kd                              = require 'kd'
+globals                         = require 'globals'
+CustomViews                     = require 'app/commonviews/customviews'
+ComputeController_UI            = require 'app/providers/computecontroller.ui'
+AccountCredentialList           = require 'account/accountcredentiallist'
+AccountCredentialListController = require 'account/views/accountcredentiallistcontroller'

 module.exports = class StacksCustomViews extends CustomViews
@@ -84,6 +86,19 @@
       return form

+    credentialList: (provider) =>
+
+      listView   = new AccountCredentialList
+      controller = new AccountCredentialListController
+        view       : listView
+        wrapper    : no
+        scrollView : no
+        provider   : provider
+
+      __view = controller.getView()
+      return { __view, controller }
+
+
     stepSetupCredentials: (options) =>

       {data, callback, cancelCallback} = options


Add unrefined() method to refinement library; gets unrefined object

--- javascripts/lib.coffee
+++ javascripts/lib.coffee
@@ -67,7 +67,7 @@
       refinement_class = ->
       refinement_class.prototype = subject
       ret = new refinement_class
-      _.extend(ret, @methods)
+      _.extend(ret, {unrefined: -> subject }, @methods)
       ret
   class_mixer RefinementDeclaration

@@ -76,8 +76,7 @@

     isApplicable: (subject)->
       subject instanceof @type
+  class_mixer RefinementByType

-  class_mixer RefinementByType
-
   return Refinement


MachineItem: Remove popups on destroy.

MachineItem: Remove popups on destroy.

--- client/app/lib/navigation/navigationmachineitem.coffee
+++ client/app/lib/navigation/navigationmachineitem.coffee
@@ -304,6 +304,13 @@
     super

+  destroy: ->
+
+    popup.destroy()  for key, popup of popups
+
+    super
+
+
   pistachio: ->

     return """


Downgrade warning to debug

Downgrade warning to debug

--- client/app.coffee
+++ client/app.coffee
@@ -37,7 +37,7 @@

   isSelected: ->
     if !Session.get("currentSection")?
-      logger.warn("currentSection not defined in Session")
+      logger.debug("currentSection not defined in Session")
       false
     else
       Session.get("currentSection").toLowerCase() == @name.toLowerCase()


Reverting camera decomposition.

Reverting camera decomposition.

--- packages/landsofillusions/avatar/humanavatar/renderobject.coffee
+++ packages/landsofillusions/avatar/humanavatar/renderobject.coffee
@@ -313,6 +313,7 @@
     @animatedMeshes[@currentSide].update appTime

     # Avatar sprite should always face the camera.
+    camera.matrix.decompose @_cameraPosition, @_cameraRotation, @_cameraScale
     @_cameraEuler.setFromQuaternion @_cameraRotation, "YXZ"
     @rotation.y = @_cameraEuler.y


Change theodoFR watcher for delayed sprint

--- client/src/daily-report/services/report-builder.coffee
+++ client/src/daily-report/services/report-builder.coffee
@@ -123,9 +123,9 @@
       emailsTo = renderTo project
       emailsCc = renderCc project, projectAtRisk

-      # Always add CTO for Theodo.fr projects
+      # Add TheodoFR scrumble watchers for Theodo.fr projects
       if projectAtRisk and isTheodoFrSprint(emailsTo.concat emailsCc)
-        emailsCc.push 'maximet@theodo.fr'
+        emailsCc.push 'watchscrumble@theodo.fr'

       prebuildMessage =
         to: emailsTo


clicked blog post item does not have html field, generate it

--- client/landing/app/Applications/Activity.kdapplication/ContentDisplays/blogpost.coffee
+++ client/landing/app/Applications/Activity.kdapplication/ContentDisplays/blogpost.coffee
@@ -48,7 +48,10 @@
     str = @utils.applyTextExpansions str, yes

   pistachio:->
-
+    {html, body} = @getData()
+    unless html
+      html = KD.utils.applyMarkdown body
+    @getData().html = html
     """
     {{> @header}}

{{> @back}}


Explicitly handle core:move-left in StagingView

--- keymaps/git.cson
+++ keymaps/git.cson
@@ -12,6 +12,7 @@
   'tab': 'core:focus-next'
   'shift-tab': 'core:focus-previous'
   'o': 'github:open-file'
+  'left': 'core:move-left'

 '.github-StagingView .github-UnstagedChanges .github-FilePatchListView':
   'cmd-backspace': 'github:discard-changes-in-selected-files'


Map core:focus-next and core:focus-previous within the CommitView

--- keymaps/git.cson
+++ keymaps/git.cson
@@ -26,6 +26,10 @@
   'shift-tab': 'core:focus-previous'
   'o': 'github:open-file'
   'left': 'core:move-left'
+
+'.github-CommitView button':
+  'tab': 'core:focus-next'
+  'shift-tab': 'core:focus-previous'

 '.github-StagingView.unstaged-changes-focused':
   'cmd-backspace': 'github:discard-changes-in-selected-files'


Escape HTML in SVG text renderer

Escape HTML in SVG text renderer

--- src/core/svgRenderer.coffee
+++ src/core/svgRenderer.coffee
@@ -17,6 +17,19 @@
     return ""
   else
     throw "Can't render shape of type #{shape.className} to SVG"
+
+entityMap = {
+  '&': '&',
+  '<': '<',
+  '>': '>',
+  '"': '"',
+  "'": ''',
+  '/': '/',
+  '`': '`',
+  '=': '='
+}
+
+escapeHTML = (string) -> String(string).replace /[&<>"'`=\/]/g, (s) -> entityMap[s]

 defineSVGRenderer 'Rectangle', (shape) ->
@@ -171,7 +184,7 @@
       dy = if i == 0 then 0 else '1.2em'
       return "
         <tspan x='#{shape.x}' dy='#{dy}' alignment-baseline='text-before-edge'>
-          #{line}
+          #{escapeHTML(line)}
         </tspan>"
     ).join('')}
   </text>


docs(macAffix): Added parmeters

docs(macAffix): Added parmeters

--- src/directives/affix.coffee
+++ src/directives/affix.coffee
@@ -34,6 +34,9 @@
       @description
       Update top or bottom offset. This function will make sure the value is
       an integer and use default value
+      @param {String} key Offset key
+      @param {String|Integer} value Update value
+      @param {Bool} useDefault
       ###
       setOffset = (key, value, useDefault = false) ->
         value       = defaults[key] if useDefault and not value?


Update autoupdated atom config

Update autoupdated atom config

--- .atom/config.cson
+++ .atom/config.cson
@@ -4,6 +4,7 @@
     'showInvisibles': true
     'showIndentGuide': true
     'preferredLineLength': 120
+    'invisibles': {}
   'core':
     'disabledPackages': [
       'metrics'


Don't autohide menubar as it makes things difficult

--- .atom/config.cson
+++ .atom/config.cson
@@ -4,7 +4,7 @@
   welcome:
     showOnStartup: false
   core:
-    autoHideMenuBar: true
+    autoHideMenuBar: false
     themes: [
       "seti-ui"
       "seti-syntax"


atom: update config

atom: update config

--- .atom/config.cson
+++ .atom/config.cson
@@ -9,11 +9,7 @@
     preserveLastSearch: true
   "atom-material-ui":
     colors:
-      abaseColor:
-        red: 47
-        green: 128
-        blue: 152
-        alpha: 1
+      abaseColor: "#2f8098"
     fonts:
       fontSize: 14
     tabs:
@@ -44,6 +40,7 @@
       "linter-eslint"
     ]
     projectHome: "~/dev"
+    telemetryConsent: "limited"
     themes: [
       "atom-material-ui"
       "atom-material-syntax"


atom: add simple PATH to init script

atom: add simple PATH to init script

--- .atom/init.coffee
+++ .atom/init.coffee
@@ -12,3 +12,5 @@
 #   editor = editorView.getEditor()
 #   if path.extname(editor.getPath()) is '.md'
 #     editor.setSoftWrap(true)
+
+process.env['PATH'] = '$HOME/.local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin'


remove utf8 stuff

remove utf8 stuff

--- gulp/compress-build.coffee
+++ gulp/compress-build.coffee
@@ -1,10 +1,8 @@
 gulp = require 'gulp'
 gzip = require 'gulp-gzip'
-utf8 = require 'gulp-utf8-convert'

 module.exports = (paths, options = {})->
   return (cb) ->
     gulp.src "#{paths.build.root}/**/**/**/**/**/**/**/*.{js,css,html}"
-      .pipe utf8()
       .pipe gzip({ gzipOptions: { level: 9 }})
       .pipe gulp.dest(paths.build.root)


fix use jquery $.map instead of javascript map

--- app/assets/javascripts/geolocateMap.coffee
+++ app/assets/javascripts/geolocateMap.coffee
@@ -133,8 +133,8 @@
         map = @map
         bounds = new google.maps.LatLngBounds()

-        @markers = markers.map ->
-          pos = new google.maps.LatLng(this.lat, this.lng)
+        @markers = $(markers).map (i, e) ->
+          pos = new google.maps.LatLng(e.lat, e.lng)
           bounds.extend(pos)
           marker = new google.maps.Marker({
               'position': pos


Fixed a bug with missing date.

Fixed a bug with missing date.

--- app/assets/javascripts/chat.coffee
+++ app/assets/javascripts/chat.coffee
@@ -60,6 +60,7 @@
               if data.sender == $scope.recipient
                 $scope.messages.push({
                   sender: data.sender
+                  created: data.created
                   text: data.text
                 })
             )
@@ -78,4 +79,4 @@
           when "history"
             $scope.$apply(() -> $scope.messages = data.messages)
           when "delete"
-            $scope.$apply(() -> $scope.messages = [])
+            $scope.$apply(() -> $scope.messages = [])


forgot to use new var

forgot to use new var

--- app/assets/javascripts/home.coffee
+++ app/assets/javascripts/home.coffee
@@ -21,7 +21,7 @@
   twelveHour = if currentHours == 0 then 12 else currentHours

   # Compose the string for display
-  currentTimeString = "#{currentHours}:#{currentMinutes}:#{currentSeconds} #{timeOfDay}"
+  currentTimeString = "#{twelveHour}:#{currentMinutes}:#{currentSeconds} #{timeOfDay}"
   $('#clock').html currentTimeString

 pad = (number) ->


console.log color

console.log color

--- app/assets/javascripts/view.coffee
+++ app/assets/javascripts/view.coffee
@@ -98,6 +98,8 @@
 				#get colors for new coords from Model
 				Model.Binary.get(newVertices, (err, colors) ->
 					throw err if err
+					console.log "normal: " + colors[0] + " " + colors[1] + " " + colors[2] +
+														colors[128*128-3] + " " + colors[128*128-2] + " " + colors[128*128-1]
 					engine.deleteSingleBuffer g.colors.VBO
 					g.setColors (View.createArrayBufferObject colors), colors.length
 				)


BF-178

--- app/assets/javascripts/view.coffee
+++ app/assets/javascripts/view.coffee
@@ -89,6 +89,7 @@
     $(window).on("bucketloaded", => cam2d.hasChanged = true; cam2d.newBuckets = [true, true, true])

   animate : ->
+
     @renderFunction()

     window.requestAnimationFrame => @animate()
@@ -179,22 +180,12 @@

         # only the main planes
         if i<=3
-          if  cam2d.needsUpdate(plane.planeID)
-            # TODO: Why don't those lines work? it would get rid of that huge switch statement
-            #
-            #plane.props.getFkt(cam2d.getGlobalPos(), cam2d.getZoomStep(plane.props.planeID)).done (buffer) ->
-            #  plane.texture.image.data.set(buffer)
-            switch plane.planeID
-              when PLANE_XY
-                Model.Binary.getXY(cam2d.getGlobalPos(), cam2d.getZoomStep(PLANE_XY)).done (buffer) ->
-                  plane.texture.image.data.set(buffer)
-              when PLANE_YZ
-                Model.Binary.getXY(cam2d.getGlobalPos(), cam2d.getZoomStep(PLANE_XY)).done (buffer) ->
-                  plane.texture.image.data.set(buffer)
-              when PLANE_XZ
-                Model.Binary.getXY(cam2d.getGlobalPos(), cam2d.getZoomStep(PLANE_XY)).done (buffer) ->
-                  plane.texture.image.data.set(buffer)
+          if cam2d.needsUpdate plane.planeID
             cam2d.notifyNewTexture plane.planeID
+
+          Model.Binary.get(cam2d.getTexturePosition(plane.planeID), cam2d.getZoomStep(plane.planeID), [120, 120, 150, 150], plane.planeID).done (buffer) ->
+            if buffer
+              plane.texture.image.data.set(buffer)

         #only for border planes
         else if i>=7 then plane.position = new THREE.Vector3(globalPosVec.x-1, globalPosVec.y-1, globalPosVec.z-1)


Don't provide default framewrapper

Don't provide default framewrapper

--- app/components/subject-viewer.cjsx
+++ app/components/subject-viewer.cjsx
@@ -5,7 +5,6 @@
 {Markdown} = require 'markdownz'
 getSubjectLocation = require '../lib/get-subject-location'
 CollectionsManagerIcon = require '../collections/manager-icon'
-FrameAnnotator = require '../classifier/frame-annotator'

 NOOP = Function.prototype

@@ -43,7 +42,7 @@
     defaultStyle: true
     project: null
     linkToFullImage: false
-    frameWrapper: FrameAnnotator
+    frameWrapper: null

   getInitialState: ->
     loading: true


Add integration test for HEAD /CloudHealthCheck

--- test/integration/system-int.coffee
+++ test/integration/system-int.coffee
@@ -4,10 +4,13 @@
 req = request require '../../coffee/server'

 describe '/CloudHealthCheck', ->
-  it 'should return OK for MongoDB and Redis', (done) ->
+  it 'should return 200 OK for GET request', (done) ->
     req.get '/CloudHealthCheck'
       .expect 200
       .expect (res) ->
         assert.deepEqual res.body, message: 'System OK'
       .end done

+  it 'should return 200 OK for HEAD request', (done) ->
+    req.get('/CloudHealthCheck').expect(200).end done
+


prevent server break with wrong conversation ID

--- server/controllers/messages.coffee
+++ server/controllers/messages.coffee
@@ -364,21 +364,37 @@

 module.exports.fetchConversation = (req, res, next) ->
-    Message.byConversationID req.params.conversationID, (err, messages) ->
-        return next err if err
-
-        req.conversation = messages
-        next()
+    conversationID = req.params.conversationID
+    Message.byConversationID conversationID, (err, messages) ->
+        return next err if err
+
+        if messages.length is 0
+            next NotFound "conversation##{conversationID}"
+        else
+            log.debug "conversation##{conversationID}", messages.length
+            req.conversation = messages
+            next()

 module.exports.conversationGet = (req, res, next) ->
     res.send 200, req.conversation.map (msg) -> msg.toClientObject()

 module.exports.conversationDelete = (req, res, next) ->
-    async.eachSeries req.conversation, (message, cb) ->
-        message.moveToTrash cb
-    , (err) ->
-        return next err if err
-        res.send 200, []
+    accountID = req.conversation[0].accountID
+
+    Account.find accountID, (err, account) ->
+        return next err if err
+        return next new NotFound "Account##{accountID}" unless account
+
+        console.log account
+
+        unless account.trashMailbox
+            next new AccountConfigError 'trashMailbox'
+        else
+            async.eachSeries req.conversation, (message, cb) ->
+                message.moveToTrash account, cb
+            , (err) ->
+                return next err if err
+                res.send 200, []

 module.exports.conversationPatch = (req, res, next) ->


bugfix link display

bugfix link display

--- server/controllers/semantic.coffee
+++ server/controllers/semantic.coffee
@@ -34,11 +34,16 @@

         nodes = []
         links = []
+
+
+        parts = query.units[0].pattern.patterns
         #IHAVENOIDEAWHATIAMDOING
-        edges = query.units[0].pattern.patterns[0].triplesContext or
-        query.units[0].pattern.patterns[0].value.reduce (prev, block) ->
-            prev.concat block.patterns[0].triplesContext
-        , []
+        edges = parts[0].triplesContext or
+        parts[0].value?.reduce((prev, block) -> prev.concat block.patterns[0].triplesContext
+        , [])
+
+        for p in parts[1..parts.length-1] when p.token is 'optionalgraphpattern'
+            edges = edges.concat p.value.patterns[0].triplesContext

         edges = edges.filter (triple) ->
             triple.subject.token is 'var' and triple.object.token is 'var'
@@ -95,8 +100,6 @@

     executeNLP: (req, res, next) ->

-        # SIMULATE QUESTION ASKED IS ALWAYS
-        # Qui ai-je appelé en mars ?
         console.log "QUERY = '#{decodeURIComponent req.query.query }'"
         nl = decodeURIComponent req.query.query
         nl = nl.toLowerCase()


change keymaps

change keymaps

--- keymaps/spark-dev.cson
+++ keymaps/spark-dev.cson
@@ -1,9 +1,5 @@
 '.platform-darwin':
-  'cmd-u': 'spark-dev:flash-cloud'
-  'cmd-r': 'spark-dev:compile-cloud'
-  'cmd-shift-m': 'spark-dev:show-serial-monitor'
+  'cmd-shift-m': 'iot-dev:show-serial-monitor'

 '.platform-win32, .platform-linux':
-  'ctrl-u': 'spark-dev:flash-cloud'
-  'ctrl-r': 'spark-dev:compile-cloud'
-  'ctrl-shift-m': 'spark-dev:show-serial-monitor'
+  'ctrl-shift-m': 'iot-dev:show-serial-monitor'


Attempted fix for clearing hbs cache after updating a template

--- server/routes/api/templates.coffee
+++ server/routes/api/templates.coffee
@@ -28,7 +28,6 @@
         res.send(files)

   .post (req, res) ->
-
     errors = validateTemplate req.body.contents
     return res.status(400).send errors if errors

@@ -73,6 +72,7 @@
       if err
         res.status(500).send err
       else
+        hbs.cache = {} # Reset hbs internal cache
         res.status(201).send
           filename: filename
           contents: contents


Fixes event connection close error never re-establishes connection.

Fix EventListener connection close check.
console log the event on error and connection.
Reload current page on connection close error event.

--- javascripts/dashing.coffee
+++ javascripts/dashing.coffee
@@ -94,12 +94,15 @@

 source = new EventSource('events')
 source.addEventListener 'open', (e) ->
-  console.log("Connection opened")
+  console.log("Connection opened", e)

 source.addEventListener 'error', (e)->
-  console.log("Connection error")
-  if (e.readyState == EventSource.CLOSED)
+  console.log("Connection error", e)
+  if (e.currentTarget.readyState == EventSource.CLOSED)
     console.log("Connection closed")
+    setTimeout (->
+      window.location.reload()
+    ), 10000

 source.addEventListener 'message', (e) ->
   data = JSON.parse(e.data)


Scenario 37. Added

Scenario 37. Added

Scenario 37. Two tags with two attribute, a space in the first at the
beginning

--- test/scenario-based-testing.coffee
+++ test/scenario-based-testing.coffee
@@ -384,4 +384,14 @@
     # console.log output
     expectedOutput = fs.readFileSync './test/scenarios/scenario-36-expected.html', 'utf8'
     # console.log expectedOutput
+    expect(output).to.equal(expectedOutput)
+
+  it 'Scenario 37. Two tags with two attribute, a space in the first at the beginning', ->
+
+    input = fs.readFileSync './test/scenarios/scenario-37-input.cohtml', 'utf8'
+    # console.log input
+    output = toHtml5 input
+    # console.log output
+    expectedOutput = fs.readFileSync './test/scenarios/scenario-37-expected.html', 'utf8'
+    # console.log expectedOutput
     expect(output).to.equal(expectedOutput)


DE15838: App settings using fixed height breaks IE

--- kanban/SettingsSpec.coffee
+++ kanban/SettingsSpec.coffee
@@ -17,7 +17,7 @@

   helpers
     renderFieldInForm: (fieldConfig) ->
-      @form = Ext.create 'Ext.form.Panel',
+      @formPanel = Ext.create 'Ext.form.Panel',
         items: [
           fieldConfig
         ]
@@ -25,7 +25,7 @@

     assertFieldCannotBeEmpty: (fieldConfig, errMsg) ->
       @renderFieldInForm fieldConfig
-      field = @form.form.getFields().get(0)
-      expect(@form.form.isValid()).toBe false
+      field = @formPanel.getForm().getFields().get(0)
+      expect(@formPanel.getForm().isValid()).toBe false
       expect(field.getErrors().length).toBe 1
       expect(field.getErrors()[0]).toBe errMsg


Implement index.less/css fallback for themes

--- src/atom-package.coffee
+++ src/atom-package.coffee
@@ -87,8 +87,14 @@

   getStylesheetPaths: ->
     stylesheetDirPath = @getStylesheetsPath()
-    if @metadata.stylesheets
+    indexStylesheet = fsUtils.resolve(@path, 'index', ['css', 'less'])
+
+    if @metadata.stylesheetMain
+      [fsUtils.resolve(stylesheetDirPath, @metadata.stylesheetMain)]
+    else if @metadata.stylesheets
       @metadata.stylesheets.map (name) -> fsUtils.resolve(stylesheetDirPath, name, ['css', 'less', ''])
+    else if indexStylesheet
+      [indexStylesheet]
     else
       fsUtils.listSync(stylesheetDirPath, ['css', 'less'])


#162320456 add trigger change to radio, checkbox and checkboxes

--- app/assets/javascripts/hanuman/hanuman-conditional-logic.js.coffee
+++ app/assets/javascripts/hanuman/hanuman-conditional-logic.js.coffee
@@ -176,16 +176,16 @@

     switch answerType
       when 'radio'
-        $ruleElement.find('input[type="radio"][value="' + value + '"]').prop("checked", true)
+        $ruleElement.find('input[type="radio"][value="' + value + '"]').prop("checked", true).trigger('change')

       when 'checkbox'
-        $ruleElement.find('input[type="checkbox"]').prop("checked", true)
+        $ruleElement.find('input[type="checkbox"]').prop("checked", true).trigger('change')

       when 'checkboxes'
         selectedOptions = value.split(",")
         $ruleElement.find('input[type="checkbox"]').each ->
           if selectedOptions.indexOf($(this).attr('value')) != -1
-            $(this).prop("checked", true)
+            $(this).prop("checked", true).trigger('change')

       when 'chosenmultiselect'
         selectedOptions = value.split(",")


flags are clickable

flags are clickable

--- app/assets/javascripts/locomotive/views/application_view.js.coffee
+++ app/assets/javascripts/locomotive/views/application_view.js.coffee
@@ -79,7 +79,7 @@
       event.stopPropagation() & event.preventDefault()
       picker.toggle()

-    picker.find('li span.text').bind 'click', (event) ->
+    picker.find('li').bind 'click', (event) ->
       locale = $(event.target).html()
       window.addParameterToURL 'content_locale', locale


Add onDidRemoveGrammar event

Add onDidRemoveGrammar event

--- src/grammar-registry.coffee
+++ src/grammar-registry.coffee
@@ -49,6 +49,15 @@
   onDidUpdateGrammar: (callback) ->
     @emitter.on 'did-update-grammar', callback

+  # Public: Invoke the given callback when a grammar is removed from the registry.
+  #
+  # * `callback` {Function} to call when a grammar is removed.
+  #   * `grammar` {Grammar} that was removed.
+  #
+  # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
+  onDidRemoveGrammar: (callback) ->
+    @emitter.on 'did-remove-grammar', callback
+
   ###
   Section: Managing Grammars
   ###
@@ -90,6 +99,7 @@
     delete @grammarsByScopeName[grammar.scopeName]
     _.remove(@injectionGrammars, grammar)
     @grammarUpdated(grammar.scopeName)
+    @emitter.emit 'did-remove-grammar', grammar
     undefined

   # Public: Remove the grammar with the given scope name.


Expose abstract_mapper (See: #191) (#192)

--- lib/index.coffee
+++ lib/index.coffee
@@ -26,7 +26,7 @@
 # Responsible for mapping between adapters where the language name
 # does not match the node module name. direction can be "left" or "right",
 # "left" being lang name -> adapter name and right being the opposite.
-abstract_mapper = (name, direction) ->
+exports.abstract_mapper = abstract_mapper = (name, direction) ->
   name_maps = [
     ['markdown', 'marked']
     ['minify-js', 'uglify-js']
@@ -49,10 +49,10 @@

   return res or name

-adapter_to_name = (name) ->
+exports.adapter_to_name = adapter_to_name = (name) ->
   abstract_mapper(name, 'right')

-name_to_adapter = (name) ->
+exports.name_to_adapter = name_to_adapter = (name) ->
   abstract_mapper(name, 'left')

 resolve_engine_path = (name, custom_path) ->