Rename and reorganize to update Gruntfile.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -2,17 +2,17 @@

 module.exports = (grunt) ->

-  SOURCES =
-    DOCS:
-      PRIVATE: [
-        'src/**/*.coffee'
-        'README.md'
-      ]
+  src =
+    docs: [
+      'src/**/*.coffee'
+      'README.md'
+    ]

   grunt.initConfig
     pkg: grunt.file.readJSON 'package.json'
+
     bower:
-      install:
+      lib:
         options:
           cleanBowerDir: yes
           cleanTargetDir: no
@@ -22,19 +22,18 @@
           targetDir: 'lib'
           verbose: yes
     groc:
-      private: SOURCES.DOCS.PRIVATE
       options:
         out: 'docs'
         'repository-url': 'https://bitbucket.org/hlfcoding/morning-stroll'
+      docs: src.docs
+
     watch:
       docs:
-        files: SOURCES.DOCS.PRIVATE
-        tasks: ['groc:private']
-        options:
-          spawn: no
+        files: src.docs
+        tasks: ['groc']

   grunt.loadNpmTasks plugin for plugin in matchdep.filterDev 'grunt-*'

-  grunt.registerTask 'default', ['bower:install']
-  grunt.registerTask 'docs', ['groc', 'watch:docs']
+  grunt.registerTask 'docs', ['groc:docs', 'watch:docs']
+  grunt.registerTask 'lib', ['bower:lib']


clean task added for compiled coffeescripts

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -38,13 +38,13 @@
           bare: true
         expand: true
         src: ['<%= appConfig.app %>/*.coffee', '<%= appConfig.app %>/*/coffee/{,*/}*.coffee']
-        ext: '.js'
+        ext: '.compiled.js'
       test:
         options:
           bare: true
         expand: true
         src: ['test/coffee/**/*.coffee']
-        ext: '.js'
+        ext: '.compiled.js'

     concat:
       js:
@@ -73,13 +73,17 @@

     clean:
       dist:
-        files: [
-          dot: true
-          src: [
-            '.tmp'
-            '<%= appConfig.dist %>/*'
-            '!<%= appConfig.dist %>/.git*'
-          ]
+        dot: true
+        src: [
+          '.tmp'
+          '<%= appConfig.dist %>/*'
+          '!<%= appConfig.dist %>/.git*'
+        ]
+      coffee:
+        src: [
+          '<%= appConfig.app %>/app.compiled.js',
+          '<%= appConfig.app %>/*/coffee/{,*/}*.compiled.js'
+          'test/coffee/{,*/}{,*/}{,*/}*.compiled.js'
         ]
       server: '.tmp'

@@ -180,5 +184,6 @@
   grunt.registerTask 'default', [
     'coffee'
     'test'
-    'build'
+    'build',
+    'clean:coffee'
   ]


Grunt: Specify BUILD_DIR for microflo

Grunt: Specify BUILD_DIR for microflo

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,6 +1,9 @@
 microflo_make = "make -f ./node_modules/microflo/Makefile \
-  MICROFLO_SOURCE_DIR=`pwd`/node_modules/microflo/microflo \
-  MICROFLO=./node_modules/.bin/microflo"
+  MICROFLO_SOURCE_DIR=#{process.cwd()}/node_modules/microflo/microflo \
+  MICROFLO=./node_modules/.bin/microflo
+  BUILD_DIR=#{process.cwd()}/build"
+
+#console.log microflo_make

 module.exports = ->
   # Project configuration


Run tests before releasing.

Run tests before releasing.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -114,6 +114,6 @@
   grunt.registerTask 'release', "compile coffeescript, bump version, git tag,
     git push, npm publish", (target) ->
     target ?= 'patch'
-    grunt.task.run ['coffeelint', 'coffee', "publish:#{target}"]
+    grunt.task.run ['coffeelint', 'test', 'coffee', "publish:#{target}"]


ISSUE-44

ISSUE-44

- added app tasks

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,5 +1,11 @@
 module.exports = (grunt) ->
+  config =
+    app:
+      name: 'AppRateDemoProject-test'
+      path: '../'
+
   grunt.initConfig
+    config: config
     pkg: grunt.file.readJSON 'package.json'
     coffee:
       options:
@@ -18,15 +24,19 @@
         src: ["www_src"]
     cordovacli:
       options:
-        path: 'AppRateDemoProject'
+        path: '<%= config.app.path %><%= config.app.name %>/'
       create:
         options:
-          command: ['create', 'platform', 'plugin']
+          command: ['create', 'platform']
           platforms: ['ios', 'android']
-          plugins: []
-          id: 'org.pushandplay.cordova.AppRateDemoProject'
-          name: 'AppRateDemoProject'
+          id: 'org.pushandplay.cordova.<%= config.app.name %>'
+          name: '<%= config.app.name %>'
           args: ['--link-to', 'www_app']
+      plugin:
+        options:
+          command: 'plugin'
+          action: 'add'
+          plugins: ['../cordova-plugin-apprate']
       prepare:
         options:
           command: ['prepare']
@@ -37,6 +47,7 @@
   grunt.loadNpmTasks 'grunt-codo'
   grunt.loadNpmTasks 'grunt-cordovacli'

-  grunt.registerTask 'default', ['coffee:compile', 'cordovacli:prepare']
+  grunt.registerTask 'default', ['coffee:compile', 'app:prepare']
   grunt.registerTask 'release', ['default', 'codo:docs']
-  grunt.registerTask 'app', ['cordovacli:prepare']+  grunt.registerTask 'app:create', ['cordovacli:create', 'cordovacli:plugin', 'cordovacli:prepare']
+  grunt.registerTask 'app:prepare', ['cordovacli:prepare']


trim gruntfile, remove unnecessary rules.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -51,25 +51,11 @@
         } ]

     copy:
-      arraybuffers: Rule.copyModule 'arraybuffers'
-      logging: Rule.copyModule 'logging'
-
-      freedomTypings: Rule.copyModule 'freedom/typings'
-
       diagnose: Rule.copyModule 'diagnose'
       chromeApp: Rule.copyModule 'chrome-app'
       chromeAppLib: Rule.copySampleFiles 'chrome-app'

     ts:
-      # freedom/typings only contains specs and declarations.
-      freedomTypingsSpecDecl: Rule.typescriptSpecDecl 'freedom/typings'
-
-      arraybuffers: Rule.typescriptSrc 'arraybuffers'
-      arraybuffersSpecDecl: Rule.typescriptSpecDecl 'arraybuffers'
-
-      logging: Rule.typescriptSrc 'logging'
-      loggingSpecDecl: Rule.typescriptSpecDecl 'logging'
-
       diagnose: Rule.typescriptSrc('diagnose')
       chromeApp: Rule.typescriptSrc('chrome-app')

@@ -94,30 +80,8 @@
     'symlink:freedom'
   ]

-  taskManager.add 'freedom', [
-    'base'
-    'ts:freedomTypingsSpecDecl'
-    'copy:freedomTypings'
-  ]
-
-  taskManager.add 'arraybuffers', [
-    'base'
-    'ts:arraybuffers'
-    'ts:arraybuffersSpecDecl'
-    'copy:arraybuffers'
-  ]
-
-  taskManager.add 'logging', [
-    'base'
-    'ts:logging'
-    'ts:loggingSpecDecl'
-    'copy:logging'
-  ]
-
   taskManager.add 'diagnose', [
     'base'
-    'logging'
-    'freedom'
     'ts:diagnose'
     'ts:chromeApp'
     'copy:diagnose'


version 0.2-2

version 0.2-2

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,6 +1,6 @@
 module.exports = (grunt) ->

-  # NodeBB Grunt Development  --  Version 0.2-1
+  # NodeBB Grunt Development  --  Version 0.2-2

   config = grunt.file.readJSON('grunt-development.json')


fixed name of simple destructuring test.

--- test/function_invocation.coffee
+++ test/function_invocation.coffee
@@ -320,7 +320,7 @@
   eq nonce, a
   eq nonce, b

-test "Simple Destructuring Failure", ->
+test "Simple Destructuring function arguments with same-named variables in scope", ->
   x = 1
   f = ([x]) -> x
   eq f([2]), 2


Rename _explained_deviance_train -> explained_deviance_train

--- src/ext/components/model-output.coffee
+++ src/ext/components/model-output.coffee
@@ -111,7 +111,7 @@
           renderPlot 'Scoring History', no, _.plot (g) ->
             g(
               g.path(
-                g.position 'lambdaid', '_explained_deviance_train'
+                g.position 'lambdaid', 'explained_deviance_train'
                 g.strokeColor g.value '#1f77b4'
               )
               g.path(
@@ -119,7 +119,7 @@
                 g.strokeColor g.value '#ff7f0e'
               )
               g.point(
-                g.position 'lambdaid', '_explained_deviance_train'
+                g.position 'lambdaid', 'explained_deviance_train'
                 g.strokeColor g.value '#1f77b4'
               )
               g.point(


link tooltip recog improved, empty string supplied in case no original url is found

--- client/landing/app/Applications/Activity.kdapplication/ListItems/ActivityListItemStatusUpdate.coffee
+++ client/landing/app/Applications/Activity.kdapplication/ListItems/ActivityListItemStatusUpdate.coffee
@@ -21,12 +21,13 @@

   attachTooltipAndEmbedInteractivity:=>
     @$("p.status-body a").each (i,element)=>
-      href = $(element).attr("data-original-url")
+      href = $(element).attr("data-original-url") or ""

       twOptions = (title) ->
          title : title, placement : "above", offset : 3, delayIn : 300, html : yes, animate : yes

-      unless $(element).text() is href
+      unless $(element).text().replace("/…","") is href.replace(/\/$/,"") or\
+             "http://"+$(element).text().replace("/…","") is href.replace(/\/$/,"")
         $(element).twipsy twOptions(href)

       if $(element).attr("target") is "_blank"


Remove irrelevant namespace from message bodies

Fixes https://github.com/candy-chat/candy/issues/311

--- muc/strophe.muc.coffee
+++ muc/strophe.muc.coffee
@@ -159,7 +159,7 @@
       from: @_connection.jid
       type: type
       id: msgid )
-    .c("body", xmlns: Strophe.NS.CLIENT)
+    .c("body")
     .t(message)
     msg.up()
     if html_message?


Fix small bug in `uploadTeamPhoto` when passing item instead of id.

--- src/collections/teamPreferences.coffee
+++ src/collections/teamPreferences.coffee
@@ -33,7 +33,7 @@

 exports.uploadTeamPhoto = (teamPreferencesId, file, callback) ->
   if @isItem teamPreferencesId, 'teamPreferences'
-    teamPreferencesId = teamPreferences.id
+    teamPreferencesId = teamPreferencesId.id
   if typeof FormData is 'undefined'
     @reject 'Your browser does not support the new file upload APIs.', 'file',
       callback
@@ -95,4 +95,3 @@
   params = teamPreferencesId: teamPreferencesId
   @collections.teamsPreferences.exec('removeTeamLogo', params)
   .callback callback
-


reduce timeout

reduce timeout

--- angular/test/protractor.coffee
+++ angular/test/protractor.coffee
@@ -4,7 +4,7 @@
 exports.config =
   # seleniumServerJar: '../node_modules/webdriver-manager/selenium/selenium-server-standalone-3.3.0.jar'
   # seleniumAddress: 'http://0.0.0.0:4444/wd/hub'
-  allScriptsTimeout: 60000
+  allScriptsTimeout: 30000
   # directConnect: true
   seleniumAddress: 'http://localhost:4444/wd/hub'
   capabilities:
@@ -15,7 +15,7 @@
     isVerbose: false
     showColors: true
     includeStackTrace: true
-    defaultTimeoutInterval: 60000
+    defaultTimeoutInterval: 30000
   onPrepare: ->
     jasmine.getEnv().addReporter new screenshooter
       dest: paths.protractor.screenshots


Simplify the way we track markers.

Simplify the way we track markers.

This simpler implementation just adds a "isDartMarker" attribute on
markers. That way we don't have to deal with maps of arrays of filenames
and what not.

--- lib/analysis_decorator.coffee
+++ lib/analysis_decorator.coffee
@@ -1,19 +1,18 @@
 module.exports =
 class AnalysisDecorator
-  decoratorMap: {}
   constructor: (@analysisComponent) ->
     @analysisComponent.on 'dart-tools:analysis', (result) =>
       @addDecoratorForAnalysis result
     @analysisComponent.on 'dart-tools:refresh', (fullPath) =>
       @refreshDecoratorsForPath fullPath
     that = this
+
     atom.workspace.eachEditor (editor) =>
       fullPath = editor.getPath()
-      if that.decoratorMap[fullPath]
-        results = that.analysisComponent.analysisResultsMap[fullPath] || []
-        for result in results
-          if fullPath == result.location.file
-            that.decorateEditor(result, editor)
+      results = that.analysisComponent.analysisResultsMap[fullPath] || []
+      for result in results
+        if fullPath == result.location.file
+          that.decorateEditor(result, editor)

   addDecoratorForAnalysis: (result) ->
     for editor in atom.workspace.getEditors()
@@ -34,21 +33,26 @@
       [line, col + loc.length]
     ]

-    dec1 = editor.decorateMarker marker,
+    @noteMarker(marker)
+
+    editor.decorateMarker marker,
       type: 'gutter',
       class: css

-    dec2 = editor.decorateMarker marker,
+    editor.decorateMarker marker,
       type: 'highlight',
       class: css

-    decorators = @decoratorMap[fullpath] ||= []
-    decorators.push(dec1)
-    decorators.push(dec2)
+  refreshDecoratorsForPath: (fullPath) ->
+    for editor in atom.workspace.getEditors()
+      if editor.getPath() == fullPath
+        for marker in editor.getMarkers()
+          marker.destroy() if @isDartMarker(marker)
+        return

+  noteMarker: (marker) ->
+    marker.setAttributes
+      isDartMarker: true

-  refreshDecoratorsForPath: (fullPath) ->
-    decorators = @decoratorMap[fullPath] || []
-    for dec in decorators
-      dec.destroy()
-    @decoratorMap[fullPath] = []
+  isDartMarker: (marker) ->
+    marker.getAttributes().isDartMarker == true


configure: coffee as default export format

--- lib/commands/configure.coffee
+++ lib/commands/configure.coffee
@@ -18,7 +18,7 @@
       # EXAMPLE START
       params = params.parse()
       params.output ?= 'export'
-      params.format ?= 'cson'
+      params.format ?= 'coffee'
       params.output = path.resolve process.cwd(), params.output
       params.hosts = [params.hosts] if typeof params.hosts is 'string'
       throw Error "Format not supported: #{params.format}" unless params.format in ['json','cson', 'js', 'coffee']


fixed bug with user list pagination user cells being place outside of main user list container

--- public/dreamcatcher/controllers/admin_controller.coffee
+++ public/dreamcatcher/controllers/admin_controller.coffee
@@ -15,7 +15,7 @@

   updateUsersPage: (json) ->
-    $('#userList').html(json.html)
+    $('#users').html(json.html)
     $('#allUsersIcon').show()
     $('#user-pageLoading').hide()


Added safe beach and dangerous rocks

Added safe beach and dangerous rocks

--- src/documents/games/sharkattack/script.js.coffee
+++ src/documents/games/sharkattack/script.js.coffee
@@ -2,6 +2,7 @@
   timestamp = -> window.performance.now()
 else
   timestamp = -> (new Date()).getTime()
+

 class Entity
   constructor: (@x, @y, @velocityX, @velocityY) ->
@@ -31,29 +32,42 @@
     ]

   isOutOfBounds: ->
-    #TODO should really use the width and height in $scope
     @x<-100 or @x>500 or @y<-100 or @y>500

 class Raft extends Entity
   constructor: (@hp=3, x=190, y=350, @maxHorizontalSpeed=2, @forwardSpeed=0.4) ->
     @damage = 0
+    @safe = false
+    @crashed = false
     super(x, y, 0, -1*@forwardSpeed)

   updatePosition: (goLeft, goRight) ->
-    unless @damage>=@hp
-      if goLeft and not goRight
+    unless @damage>=@hp or @safe or @crashed
+      # Adjust velocity unless it would bring the raft off screen
+      if goLeft and not goRight and @x>5
         @velocityX = -1*@maxHorizontalSpeed
-      else if goRight and not goLeft
+      else if goRight and not goLeft and @x<375
         @velocityX = @maxHorizontalSpeed
       else
         @velocityX = 0
+
+      # Call super to actually move the raft
       super()
-
+
+      # Check to see if the raft has reached the beach or the rocks
+      # These constants represent the safe area as displayed on the image
+      if @y<=32 and 121<=@x<=271
+        console.log 'safe'
+        @safe = true
+      else if @y<=38 and @x<=120 or @y<=34 and @x>=272
+        console.log 'crashed'
+        @crashed = true
+
   getClasses: ->
     [
       "frame#{Math.min(@damage, 2)}"
-      if @damage>=@hp then 'destroyed'
+      if @damage>=@hp or @crashed then 'destroyed'
     ]

   takeBite: ->
@@ -67,8 +81,8 @@
 angular.module('sharkAttackApp', []).controller('SharkAttackCtrl', ['$scope', ($scope) ->

   # Game balance variables
-  $scope.fastSharkChance = 0.3
-  $scope.sharksPerSecond = 3.0
+  $scope.fastSharkChance = 0.2
+  $scope.sharksPerSecond = 2.0
   $scope.raftHitPoints = 3

   $scope.width = 400
@@ -115,7 +129,8 @@
   $scope.frame = ->
     $scope.$apply ->
       now = timestamp()
-      $scope.dt += Math.min(1, (now-$scope.last)/1000) # Prevent too many frames occurring at once
+      # Prevent too many frames occurring at once if user tabs away
+      $scope.dt += Math.min(1, (now-$scope.last)/1000)
       while $scope.dt>$scope.step
         $scope.dt -= $scope.step
         $scope.update()


Better way of checking that string isn't empty.

--- web/js/router.coffee
+++ web/js/router.coffee
@@ -17,7 +17,7 @@
         arrivalOrDeparture: departArrive
         transportTypes: transportTypes.split(',')
         routeIndex: parseInt(routeIndex, 10)
-        legIndex: if legIndex? and legIndex.length then parseInt(legIndex, 10) else undefined
+        legIndex: if legIndex?.length > 0 then parseInt(legIndex, 10) else undefined

     navigateToRoutes: (params) ->
       path = [Utils.encodeURIComponent(params.from),


cleanup

cleanup

--- api/apps/articles/model.coffee
+++ api/apps/articles/model.coffee
@@ -163,7 +163,7 @@
       authorId = input.author_id or article.author_id
       User.findOrInsert authorId, accessToken, (err, author) ->
         return callback err if err
-        article = update (article or {}), input, author
+        article = update article, input, author
         db.articles.save _.extend(article,
           _id: id
           # TODO: https://github.com/pebble/joi-objectid/issues/2#issuecomment-75189638


Remove unneeded Properties.v_select()

Remove unneeded Properties.v_select()

--- bokehjs/src/coffee/renderer/properties.coffee
+++ bokehjs/src/coffee/renderer/properties.coffee
@@ -212,45 +212,6 @@

       # failing that, just log a problem
       logger.warn("selection for attribute '#{attrname}' failed on object: #{ obj }")
-
-    v_select: (attrname, objs) ->
-
-      # if the attribute is not on this property object at all, log a bad request
-      if not (attrname of @)
-        logger.warn("requested vector selection of unknown property '#{attrname}' on objects")
-        return
-
-      if @[attrname].typed?
-        result = new Float64Array(objs.length)
-      else
-        result = new Array(objs.length)
-
-      for i in [0...objs.length]
-
-        obj = objs[i]
-
-        # if the attribute specifies a field, and the field exists on the object, return that value
-        if @[attrname].field? and (@[attrname].field of obj)
-          result[i] = obj[@[attrname].field]
-
-        # If the user gave an explicit value, that should always be returned
-        else if @[attrname].value?
-          result[i] = @[attrname].value
-
-        # otherwise, if the attribute exists on the object, return that value
-        else if obj[attrname]?
-          result[i] = obj[attrname]
-
-        # finally, check for a default value on this property object that could be returned
-        else if @[attrname].default?
-          result[i] = @[attrname].default
-
-        # failing that, just log a problem
-        else
-          logger.warn("vector selection for attribute '#{attrname}' failed on object: #{obj}")
-          return
-
-      return result

   class LineProperties extends Properties
     constructor: (styleprovider, prefix="") ->


NEW: scrollRatio getter/setter and setFirstElementPosition setter

--- source/Art/Engine/Elements/Widgets/ScrollElement.coffee
+++ source/Art/Engine/Elements/Widgets/ScrollElement.coffee
@@ -99,6 +99,14 @@

   @getter "childrenOffset firstOnScreenChildIndex lastOnScreenChildIndex focusedChildIndex childrenSize windowSize inFlowChildren"

+
+  @getter
+    scrollRatio: -> -@childrenOffset / (@childrenSize - @windowSize)
+
+  @setter
+    scrollRatio: (r)->
+      @setFirstElementPosition -r * (@childrenSize - @windowSize)
+
   overScrollTransformation = (scrollPosition, windowSize) ->
     maxBeyond = windowSize / 3
     Math.atan(scrollPosition / maxBeyond ) * (2 / Math.PI) * maxBeyond
@@ -230,6 +238,7 @@
       )
       @queueEvent "scrollUpdate", =>
         {
+          @scrollRatio
           @childrenOffset
           @childrenSize
           windowSize
@@ -380,7 +389,6 @@
     {scrollPosition} = @
     @_validScrollPositionCheckScheduled = false
     boundedScrollPosition = @boundSp scrollPosition + desiredOffset
-    global.scrollElement = @
     if boundedScrollPosition != scrollPosition && !@_animating
       @_animating = true
       @animators = merge originialAnimators = @animators,
@@ -389,6 +397,9 @@
           @animators = originialAnimators

       @scrollPosition = boundedScrollPosition
+
+  setFirstElementPosition: (fp)->
+    @scrollPosition = @boundSp @fp2sp fp

   _scrollPositionChanged: ->
     unless @_activelyScrolling


Use double arrows where needed

Use double arrows where needed

--- lib/assets/javascripts/unpoly/classes/layer_stack.coffee
+++ lib/assets/javascripts/unpoly/classes/layer_stack.coffee
@@ -20,11 +20,11 @@
     @all[i]

   remove: (layer, options) ->
-    @asap options, ->
+    @asap options, =>
       u.remove(@all, layer)

   push: (layer, options) ->
-    @asap options, ->
+    @asap options, =>
       @all.push(layer)

   peel: (layer, options = {}) ->


Don't render the replacements when there is replace data

--- lib/project/match-view.coffee
+++ lib/project/match-view.coffee
@@ -25,7 +25,7 @@
     @subscribe @model, 'replacement-pattern-changed', @render

   render: =>
-    if @model.replacementPattern and @model.regex
+    if @model.replacementPattern and @model.regex and not @model.replacedPathCount?
       replacementText = @match.matchText.replace(@model.regex, @model.replacementPattern)
       @replacementText.text(replacementText)
       @replacementText.show()


Fix tests based on style changes

Fix tests based on style changes

--- test/tests/TextLayerTest.coffee
+++ test/tests/TextLayerTest.coffee
@@ -285,17 +285,17 @@
 				text: longText
 				textOverflow: "ellipsis"
 				height: 150
-			l.style["-webkit-line-clamp"].should.equal '3'
+			l._elementHTML.style["-webkit-line-clamp"].should.equal '3'
 			l.height = 400
-			l.style["-webkit-line-clamp"].should.equal '8'
+			l._elementHTML.style["-webkit-line-clamp"].should.equal '8'

 		it "should not set the line-clamp when not using textOverflow", ->
 			l = new TextLayer
 				text: longText
 				height: 150
-			l.style["-webkit-line-clamp"].should.equal ''
+			l._elementHTML.style["-webkit-line-clamp"].should.equal ''
 			l.height = 400
-			l.style["-webkit-line-clamp"].should.equal ''
+			l._elementHTML.style["-webkit-line-clamp"].should.equal ''

 	describe "truncate", ->
 		it "should set textOverflow to ellipsis", ->
@@ -304,6 +304,8 @@
 				truncate: true
 			l.truncate.should.equal true
 			l.textOverflow.should.equal "ellipsis"
+			l._elementHTML.style.textOverflow.should.equal "ellipsis"
 			l.truncate = false
 			l.truncate.should.equal false
 			expect(l.textOverflow).to.equal null
+			expect(l._elementHTML.style.textOverflow).to.equal ''


don't try to getLastCompressedUpdates from packs

--- app/coffee/MongoManager.coffee
+++ app/coffee/MongoManager.coffee
@@ -11,6 +11,7 @@
 			.limit(1)
 			.toArray (error, compressedUpdates) ->
 				return callback(error) if error?
+				return callback null, null if compressedUpdates[0]?.pack? # cannot pop from a pack
 				return callback null, compressedUpdates[0] or null

 	# only used in this module


fix bug in doclines check

fix bug in doclines check

--- app/coffee/RedisManager.coffee
+++ app/coffee/RedisManager.coffee
@@ -115,7 +115,7 @@
 				return callback(new Errors.NotFoundError("document not found"))

 			# doc is not in redis, bail out
-			if !lines?
+			if !docLines?
 				return callback null, docLines, version, ranges

 			# doc should be in project set, check if missing (workaround for missing docs from putDoc)


hide/show link/nodes working

hide/show link/nodes working

--- app/coffeescripts/menus.coffee
+++ app/coffeescripts/menus.coffee
@@ -1,8 +1,9 @@
+
+show_events = true
+show_controllers = true
+show_sensors = true

 triggerEvent = (eventName) ->
-  show_events = true
-  show_controllers = true
-  show_sensors = true

   switch eventName
     when 'showAllNodes'
@@ -20,7 +21,9 @@
         window.sirius.broker.trigger('map:show_event_layer')
       show_events = !show_events
     when 'showControllers'
+      alert "working!"
       if show_controllers
+        alert "working!"
         window.sirius.broker.trigger('map:hide_controller_layer')
       else
         window.sirius.broker.trigger('map:show_controller_layer')


If we can’t connect to redid, don’t worry

--- app/controllers/connect.coffee
+++ app/controllers/connect.coffee
@@ -3,8 +3,10 @@
 redis   = require 'redis'
 url     = require 'url'
 config  = require '../config'
+Log     = require '../utils/log'
+require 'knex-mssql'

-require 'knex-mssql'
+warn = Log('connect', 'red')

 ready = Promise.defer()

@@ -24,7 +26,8 @@
       port = config.redis_config.port
       hostname = config.redis_config.host

-    @redis = redis.createClient(port, hostname)
+    @redis = redis.createClient(port, hostname, max_attempts: 3)
+    @redis.on 'error', -> warn 'Could not connect to Redis'
     if auth then @redis.auth(auth.split(':')[1])

     @db = Knex.initialize


hiding footer when publish is available on exercise summary, and adding in publish action to the exercise summary

--- src/components/task-plan/homework/exercise-summary.cjsx
+++ src/components/task-plan/homework/exercise-summary.cjsx
@@ -28,6 +28,8 @@
         Tutor selections are added later to support spaced practice and personalized learning.
       </BS.Col>

+    publishable = TaskPlanStore.isValid(@props.planId) and not TaskPlanStore.isPublished(@props.id)
+
     if @props.canReview and numSelected
       buttons = <BS.Button
         bsStyle="primary"
@@ -43,9 +45,12 @@
     else if @props.canAdd
       explanation = null
       buttonColumnSize = 4
+      publishButtonClass = ['-publish-task']
+      publishButtonClass.push('disabled') unless publishable
+
       buttons = <BS.Button bsStyle="primary"
-        className="-add-exercises"
-        onClick={@props.publish}>Publish</BS.Button>
+        className={publishButtonClass.join(' ')}
+        onClick={@props.onPublish}>Publish</BS.Button>
       <BS.Button bsStyle="default"
         className="-add-exercises"
         onClick={@props.addClicked}>Add More...</BS.Button>


Remove unused variable

Remove unused variable

--- src/extensions/markdown-preview/markdown-preview.coffee
+++ src/extensions/markdown-preview/markdown-preview.coffee
@@ -1,4 +1,4 @@
-{View, $$} = require 'space-pen'
+{View} = require 'space-pen'
 fs = require 'fs'
 $ = require 'jquery'


Completely removed can_haz

Completely removed can_haz

--- app/js/jail_iframe/core.coffee
+++ app/js/jail_iframe/core.coffee
@@ -1,6 +1,5 @@
 window.FactlinkJailRoot =
   $.extend(
-    can_haz: {}
     public_events: window.Events
     core_loaded_promise: $.Deferred()
     host_ready_promise: $.Deferred()


update (#5687)

update (#5687)


changing view/tool creation so that we only bind events for newly created views this fixes the bugs where we add tool buttons every time we push plot changes out.

--- subtree/bokehjs/static/coffee/common/plot.coffee
+++ subtree/bokehjs/static/coffee/common/plot.coffee
@@ -118,8 +118,14 @@
     @keydownCallbacks = []
     @render_init()
     @render_canvas(false)
+    @atm = new ActiveToolManager(@eventSink)
+    @levels = {}
+    for level in LEVELS
+      @levels[level] = {}
     @build_levels()
     @request_render()
+    @atm.bind_bokeh_events()
+    @bind_bokeh_events()
     return this

   map_to_screen : (x, x_units, y, y_units, units) ->
@@ -147,39 +153,28 @@
     return [x, y]

   build_tools: () ->
-    build_views(@tools, @mget_obj('tools'), @view_options())
-    return this
-
-  bind_tools: () ->
-    for toolspec in @mget('tools')
-      @tools[toolspec.id].bind_events(this)
-    return this
+    return build_views(@tools, @mget_obj('tools'), @view_options())
+

   build_views: ()->
-    build_views(@renderers, @mget_obj('renderers'), @view_options())
-    return this
+    return build_views(@renderers, @mget_obj('renderers'), @view_options())

   build_levels: () ->
-    @build_views()
-    @build_tools()
-    @levels = {}
-    for level in LEVELS
-      @levels[level] = {}
-
-    for k,v of @renderers
+    # need to separate renderer/tool creation from event binding
+    # because things like box selection overlay needs to bind events
+    # on the select tool
+    #
+    # should only bind events on NEW views and tools
+    views = @build_views()
+    tools = @build_tools()
+    for v in views
       level = v.mget('level')
-      @levels[level][k] = v
-
-    for k,v of @tools
-      level = v.mget('level')
-      @levels[level][k] = v
-    @atm = new ActiveToolManager(@eventSink)
-    @atm.bind_bokeh_events()
-    @bind_bokeh_events()
-    for toolview in _.values(@tools)
-      toolview.bind_bokeh_events()
-    for view in _.values(@renderers)
-      view.bind_bokeh_events()
+      @levels[level][v.model.id] = v
+      v.bind_bokeh_events()
+    for t in tools
+      level = t.mget('level')
+      @levels[level][t.model.id] = t
+      t.bind_bokeh_events()
     return this

   bind_bokeh_events: () ->


Render manifests

Render manifests

--- app/pages/lab/subject-set.cjsx
+++ app/pages/lab/subject-set.cjsx
@@ -5,6 +5,8 @@
 ChangeListener = require '../../components/change-listener'
 Papa = require 'papaparse'

+NOOP = Function.prototype
+
 VALID_SUBJECT_EXTENSIONS = ['.jpg', '.png', '.gif', '.svg']
 INVALID_FILENAME_CHARS = [';'] # TODO: Figure out a good general way to separate filenames.

@@ -14,7 +16,7 @@
   getDefaultProps: ->
     accept: 'text/plain'
     multiple: false
-    onSelect: Function.prototype
+    onSelect: NOOP

   getInitialState: ->
     dragEntered: false
@@ -32,11 +34,12 @@

   render: ->
     style =
-      position: 'relative'
       outline: if @state.dragEntered
         '1px solid green'
       else
         '1px dashed gray'
+      padding: '0.5em 1em'
+      position: 'relative'

       <input type="file" accept={@props.accept} multiple={@props.multiple} onChange={@handleFileSelection} style={@hiddenInputStyle} />
@@ -58,6 +61,102 @@
   handleFileSelection: (e) ->
     @props.onSelect e.target.files

+ManifestView = React.createClass
+  displayName: 'ManifestView'
+
+  getDefaultProps: ->
+    name: ''
+    errors: []
+    subjects: []
+    files: {}
+    onRemove: NOOP
+
+  getInitialState: ->
+    showingErrors: false
+    showingMissing: false
+    showingReady: false
+
+  render: ->
+    missingFiles = []
+    subjectsReadyToGo = []
+    subjectsWithMissingFiles = []
+
+    for subject in @props.subjects
+      allLocationsHaveFiles = true
+      for location in subject.locations
+        unless location of @props.files
+          missingFiles.push location
+          allLocationsHaveFiles = false
+      if allLocationsHaveFiles
+        subjectsReadyToGo.push subject
+      else
+        subjectsWithMissingFiles.push subject
+
+

+
{@props.name} ({@props.subjects.length} subjects)

+
+      {unless @props.errors.length is 0
+

+
+          {@props.errors.length} parse errors{' '}
+          <button type="button" className="secret-button" onClick={@toggleState.bind this, 'showingErrors'}>
+
+          </button>
+

+          {if @state.showingErrors
+

+              {for {row, message} in @props.errors
+
Row {row}: {message}
}
+
}
+
}
+
+      {unless missingFiles.length is 0
+

+
+          {missingFiles.length} missing files from {subjectsWithMissingFiles.length} subjects{' '}
+          <button type="button" className="secret-button" onClick={@toggleState.bind this, 'showingMissing'}>
+
+          </button>
+

+          {if @state.showingMissing
+

+              {for file, i in missingFiles
+
{file}
}
+
}
+
}
+
+

+
+        {subjectsReadyToGo.length} subjects ready to load{' '}
+        <button type="button" className="secret-button" onClick={@toggleState.bind this, 'showingReady'}>
+
+        </button>
+        {if @state.showingReady
+

+            {for {locations, metadata}, i in subjectsReadyToGo
+

+                {for location in locations
+
{location}
}
+

+

+                    {for key, value of metadata
+                      {key}	}
+

+

+                    {for key, value of metadata
+                      {value}	}
+

+

+
}
+
}
+

+

+
+  toggleState: (key) ->
+    newState = {}
+    newState[key] = not @state[key]
+    @setState newState
+
 EditSubjectSetPage = React.createClass
   displayName: 'EditSubjectSetPage'

@@ -91,35 +190,12 @@

TODO: List subjects without a manifest

       else

-

-            Manifests
-

-            {for name, {subjects, errors} of @state.manifests
-

-                {name} ({subjects.length})
-                {for {row, message} in errors
-
Error on row {row}: {message}
}
-
}
-

+          Manifests

-
-

-            Subjects
-

-            {for name, {subjects} of @state.manifests
-              for {locations, metadata} in subjects
-

-                  {for file in locations
-

-                      {if file of @state.files
-
-                      else
-                        }
-                      {locations.join ';'}
-
}
-
{JSON.stringify metadata}

-
}
-

+

+            {for name, {errors, subjects} of @state.manifests
+
<ManifestView name={name} errors={errors} subjects={subjects} files={@state.files} />
}
+

}

@@ -167,6 +243,10 @@
         filesInMetadata.push filesInValue...
     filesInMetadata

+  handleRemoveManifest: (name) ->
+    delete @state.manifests[name]
+    @forceUpdate();
+
 module.exports = React.createClass
   displayName: 'EditSubjectSetPageWrapper'


update china title & desc (#5660)

update china title & desc (#5660)

--- app/locale/zh-HANS.coco.coffee
+++ app/locale/zh-HANS.coco.coffee
@@ -1,8 +1,8 @@
 module.exports = nativeDescription: "简体中文", englishDescription: "Chinese (Simplified)", translation:

   new_home:
-#    title: "CodeCombat - Coding games to learn Python and JavaScript"
-#    meta_description: "Learn typed code through a programming game. Learn Python, JavaScript, and HTML as you solve puzzles and learn to make your own coding games and websites."
+    title: "CodeCombat - 美国大学理事会认证硅谷原版青少年编程平台"
+    meta_description: "CodeCombat（中国官方网址：koudashijie.com）于2013年成立于美国硅谷，是美国大学理事会权威认证的青少年编程学习平台，学习者可以在CodeCombat学习后参加AP计算机考试，拿到美国和世界一流名校如斯坦福大学、哈佛大学、麻省理工学院等学分减免政策，是全球公认的权威计算机编程能力学习平台之一。"
     built_for_teachers_title: "一个以教师为中心的课程体系"
     built_for_teachers_blurb: "教孩子们写代码往往会觉得无从下手。 CodeCombat 旨在帮助所有的教育工作者传授学生学习和掌握当下最流行的两种计算机语言，JavaScript 和 Python。整个体系包含六门计算机科学课程，以及通过基于项目的游戏开发和网页开发单元巩固学习，教会孩子们一步步从基本语法到高级递归前进！"
     built_for_teachers_subtitle1: "计算机科学"


remove promise-renderer from EditSubjectSetPageWrapper

--- app/pages/lab/subject-set.cjsx
+++ app/pages/lab/subject-set.cjsx
@@ -331,9 +331,33 @@
   getDefaultProps: ->
     params: null

-  render: ->
-    <PromiseRenderer promise={apiClient.type('subject_sets').get @props.params.subjectSetID}>{(subjectSet) =>
+  getInitialState: ->
+    subject_set: null
+
+  componentWillReceiveProps: (nextProps) ->
+    if nextProps.params.subjectSetID isnt @props.params.subjectSetID
+      @setSubjectSet(nextProps.params.subjectSetID)
+
+
+  componentWillMount: ->
+    @setSubjectSet(@props.params.subjectSetID)
+
+
+  setSubjectSet: (subjectSetId) ->
+    subject_set = apiClient.type('subject_sets').get subjectSetId
+
+    subject_set.then( (subject_set) =>
+      @setState subject_set: subject_set
+    ).catch( (error) =>
+      console.log error
+    )
+
+  render: ->
+    if @state.subject_set
+      subjectSet = @state.subject_set
       <ChangeListener target={subjectSet}>{=>
         <EditSubjectSetPage {...@props} subjectSet={subjectSet} />
       }</ChangeListener>
-    }</PromiseRenderer>
+    else
+      null
+


JPermissionSet: fetchMainGroupAndPermissionSet: helper to fetch required data for the MAIN_GROUP and it keeps this in memory

--- workers/social/lib/social/models/group/permissionset.coffee
+++ workers/social/lib/social/models/group/permissionset.coffee
@@ -37,6 +37,8 @@
   { intersection } = require 'underscore'

   KodingError = require '../../error'
+
+  MAIN_GROUP = 'koding'

   # coffeelint: disable=indentation
   constructor: (data = {}, options = {}) ->
@@ -81,6 +83,16 @@
         group.fetchPermissionSetOrDefault (err, permissionSet) ->
           if err then callback err
           else callback null, { group, permissionSet }
+
+
+  @fetchMainGroupAndPermissionSet = (callback) ->
+
+    return callback null, @mainGroupData  if @mainGroupData
+
+    fetchGroupAndPermissionSet MAIN_GROUP, (err, res) =>
+      if err then callback err
+      else callback null, @mainGroupData = res
+
     JGroup = require '../group'
     advanced = wrapPermission advanced  if 'string' is typeof advanced
     kallback = (group, permissionSet) ->


fix comment

fix comment

--- lms/static/coffee/src/staff_grading/staff_grading.coffee
+++ lms/static/coffee/src/staff_grading/staff_grading.coffee
@@ -18,7 +18,7 @@

   mock: (cmd, data) ->
     # Return a mock response to cmd and data
-    # TODO: needs (optional?) arg for problem location
+    # should take a location as an argument
     if cmd == 'get_next'
       @mock_cnt++
       response =


KDRouter: push redirects to google analytics

--- core/KDRouter.coffee
+++ core/KDRouter.coffee
@@ -98,6 +98,9 @@
   addRoutes:(routes)->
     @addRoute route, listener  for own route, listener of routes

+  pushRoute: (path) ->
+    _gaq?.push ['_trackPageview', path]
+
   handleRoute:(userRoute, options={})->

     userRoute = userRoute.slice 1  if (userRoute.indexOf '!') is 0
@@ -128,6 +131,8 @@
     if not suppressListeners and shouldPushState and not replaceState and path is @currentPath
       @emit 'AlreadyHere', path
       return
+
+    @pushRoute path

     @currentPath = path


Fix encoding when downloading from ejudge

--- server/testSystems/ejudge/EjudgeSubmitDownloader.coffee
+++ server/testSystems/ejudge/EjudgeSubmitDownloader.coffee
@@ -100,10 +100,11 @@

     getSource: (runid) ->
         [contest, run] = @_parseRunId(runid)
-        return await @options.admin.download(
+        source = await @options.admin.download(
             "#{@options.server}/cgi-bin/new-master?action=91&run_id=#{run}",
             {encoding: null},
             "new-master")
+        return source.toString('latin1')

     getComments: (runid) ->
         [contest, run] = @_parseRunId(runid)


adding in another unit test

adding in another unit test

--- test/components/student-dashboard/event-row.cjsx
+++ test/components/student-dashboard/event-row.cjsx
@@ -4,7 +4,7 @@
 EVENT =
   "id": "118",
   "title": "Read Chapter 1",
-  "opens_at": "2016-05-16T05:01:00.000Z",
+  "opens_at": (new Date(Date.now() - 1000 * 3600 * 24)).toString(),
   "due_at": "2016-05-19T12:00:00.000Z",
   "last_worked_at": "2016-05-19T11:59:00.000Z",
   "type": "reading",
@@ -54,3 +54,6 @@
     feedback = feedbackColumn.props.children
     expect(feedback.props.children).to.equal("Withdrawn")

+  it 'disallows onclick for event row if deletable', ->
+    expect(deletedRow.props.onClick).to.be.falsy
+    expect(regularRow.props.onClick).to.not.be.falsy


run lineman grunt spec-e2e for the next error to fix

--- lineman/tasks/spec-e2e.coffee
+++ lineman/tasks/spec-e2e.coffee
@@ -10,6 +10,6 @@

   grunt.registerTask "spec-e2e", "run specs in ci mode", (target) ->
     require('coffee-script')
-    process.argv = ["doesnt", "matter", "#{process.cwd()}/config/spec-e2e.js"]
+    process.argv = ["doesnt", "matter", "#{process.cwd()}/config/spec-e2e.coffee"]
     done = @async()
     require("#{process.cwd()}/node_modules/protractor/lib/cli")


Update loading user to EmberModel's version

--- assets/scripts/app/auth.coffee
+++ assets/scripts/app/auth.coffee
@@ -80,7 +80,7 @@

   refreshUserData: (user) ->
     Travis.ajax.get "/users/#{user.id}", (data) =>
-      Travis.store.loadIncomplete(Travis.User, data.user)
+      Travis.loadOrMerge(Travis.User, data.user)
       # if user is still signed in, update saved data
       if @signedIn()
         data.user.token = user.token
@@ -95,9 +95,8 @@
     storage.setItem('travis.user', JSON.stringify(data.user))

   loadUser: (user) ->
-    store = @app.store
-    store.load(Travis.User, user.id, user)
-    user = store.find(Travis.User, user.id)
+    Travis.loadOrMerge(Travis.User, user)
+    user = Travis.User.find(user.id)
     user.get('permissions')
     user


clean up backend url serving and message calling

--- src/smc-project/jupyter/jupyter.coffee
+++ src/smc-project/jupyter/jupyter.coffee
@@ -350,34 +350,26 @@
     get_kernel_data: (cb) =>   # cb(err, kernel_data)  # see below.
         get_kernel_data(cb)

-    complete: (opts) =>
-        opts = defaults opts,
-            code       : required
-            cursor_pos : required
-            cb         : required
-        dbg = @dbg("complete")
-        dbg("code='#{opts.code}', cursor_pos='#{opts.cursor_pos}'")
+    call: (opts) =>
+        opts = defaults opts,
+            msg_type : required
+            content  : {}
+            cb       : required
         @_ensure_running (err) =>
             if err
                 opts.cb(err)
             else
-                @_complete(opts)
-
-    _complete: (opts) =>
-        if @_state == 'closed'
-            opts.cb('closed')
-            return
-        dbg = @dbg("_complete")
+                @_call(opts)
+
+    _call: (opts) =>
         message =
             header:
-                msg_id   : "complete_#{misc.uuid()}"
+                msg_id   : misc.uuid()
                 username : ''
                 session  : ''
-                msg_type : 'complete_request'
+                msg_type : opts.msg_type
                 version  : '5.0'
-            content:
-                code       : opts.code
-                cursor_pos : opts.cursor_pos
+            content: opts.content

         # setup handling of the results
         if opts.all
@@ -391,9 +383,21 @@
                     delete mesg.metadata
                 opts.cb(undefined, mesg)
         @on('shell', f)
-
-        dbg("send the message")
         @_channels.shell.next(message)
+
+    complete: (opts) =>
+        opts = defaults opts,
+            code       : required
+            cursor_pos : required
+            cb         : required
+        dbg = @dbg("complete")
+        dbg("code='#{opts.code}', cursor_pos='#{opts.cursor_pos}'")
+        @call
+            msg_type : 'complete_request'
+            content:
+                code       : opts.code
+                cursor_pos : opts.cursor_pos
+            cb : opts.cb

     introspect: (opts) =>
         opts = defaults opts,
@@ -402,42 +406,23 @@
             detail_level : required
             cb           : required
         dbg = @dbg("introspect")
-        dbg("code='#{opts.code}', cursor_pos='#{opts.cursor_pos}'")
-        @_ensure_running (err) =>
-            if err
-                opts.cb(err)
-            else
-                @_introspect(opts)
-
-    _introspect: (opts) =>
-        dbg = @dbg("_introspect")
-        message =
-            header:
-                msg_id   : "introspect_#{misc.uuid()}"
-                username : ''
-                session  : ''
-                msg_type : 'inspect_request'
-                version  : '5.0'
-            content:
+        dbg("code='#{opts.code}', cursor_pos='#{opts.cursor_pos}', detail_level=#{opts.detail_level}")
+        @call
+            msg_type : 'inspect_request'
+            content :
                 code         : opts.code
                 cursor_pos   : opts.cursor_pos
                 detail_level : opts.detail_level
-
-        # setup handling of the results
-        if opts.all
-            all_mesgs = []
-
-        f = (mesg) =>
-            if mesg.parent_header.msg_id == message.header.msg_id
-                @removeListener('shell', f)
-                mesg = misc.deep_copy(mesg.content)
-                if misc.len(mesg.metadata) == 0
-                    delete mesg.metadata
-                opts.cb(undefined, mesg)
-        @on('shell', f)
-
-        dbg("send the message")
-        @_channels.shell.next(message)
+            cb: opts.cb
+
+    kernel_info: (opts) =>
+        opts = defaults opts,
+            cb         : required
+        @call
+            msg_type : 'kernel_info_request'
+            cb       : (err, info) ->
+                info?.nodejs_version = process.version
+                opts.cb(err, info)

     http_server: (opts) =>
         opts = defaults opts,
@@ -449,6 +434,9 @@
             when 'signal'
                 @signal(opts.segments[1])
                 opts.cb(undefined, {})
+
+            when 'kernel_info'
+                @kernel_info(cb: opts.cb)

             when 'complete'
                 code = opts.query.code


Only send required link-hint properties.

There's other stuff (ports, frameIds) in @tabState[tabId] that we don't
need in the front end.  Here, we only send the three properties which we
do need.

--- background_scripts/main.coffee
+++ background_scripts/main.coffee
@@ -392,7 +392,10 @@
       @tabState[tabId].frameIds = @tabState[tabId].frameIds.filter (fId) -> fId != frameId
       console.log "postHintDescriptors", tabId, frameId, "[#{@tabState[tabId].frameIds.length}]" if @debug
       if @tabState[tabId].frameIds.length == 0
-        @sendMessage "activateMode", tabId, @tabState[tabId]
+        @sendMessage "activateMode", tabId,
+          originatingFrameId: @tabState[tabId].originatingFrameId
+          hintDescriptors: @tabState[tabId].hintDescriptors
+          modeIndex: @tabState[tabId].modeIndex

 # Port handler mapping
 portHandlers =


Guard against null or undefined entry content.

--- entry/renderEntryContent.coffee
+++ entry/renderEntryContent.coffee
@@ -5,7 +5,7 @@
 TextBlock = require './TextBlock'

 module.exports = (content) ->
-    return content.map (block) ->
+    return content?.map (block) ->
         switch block.type
             when 'text'
                 return <TextBlock block=block key=block.id />


remove 'remove panel' link

remove 'remove panel' link


activity: delegate to component creator for follow button action

--- client/activity/lib/components/chatpane/index.coffee
+++ client/activity/lib/components/chatpane/index.coffee
@@ -56,9 +56,7 @@
     </section>

-  onFollowChannelButtonClick: ->
-
-    ActivityFlux.actions.channel.followChannel @props.thread.getIn ['channel', 'id']
+  onFollowChannelButtonClick: -> @props.onFollowChannelButtonClick()

   renderFollowChannel: ->


activity: render PostPane in a modal

activity: render PostPane in a modal

--- client/activity/lib/components/postpane/index.coffee
+++ client/activity/lib/components/postpane/index.coffee
@@ -1,8 +1,9 @@
-kd              = require 'kd'
-React           = require 'kd-react'
-immutable       = require 'immutable'
-ActivityFlux    = require 'activity/flux'
-ChatPane = require 'activity/components/chatpane'
+kd           = require 'kd'
+React        = require 'kd-react'
+immutable    = require 'immutable'
+ActivityFlux = require 'activity/flux'
+ChatPane     = require 'activity/components/chatpane'
+Modal        = require 'app/components/modal'

 module.exports = class PostPane extends React.Component
@@ -37,13 +38,15 @@

   render: ->
-    <ChatPane
-      thread={@props.thread}
-      className="PostPane"
-      messages={@props.messages}
-      onSubmit={@bound 'onSubmit'}
-      onLoadMore={@bound 'onLoadMore'}
-      isParticipant={@channel 'isParticipant'}
-    />
+    <Modal isOpen={yes}>
+      <ChatPane
+        thread={@props.thread}
+        className="PostPane"
+        messages={@props.messages}
+        onSubmit={@bound 'onSubmit'}
+        onLoadMore={@bound 'onLoadMore'}
+        isParticipant={@channel 'isParticipant'}
+      />
+    </Modal>


avatarview fix

avatarview fix

--- client/app/CommonViews/avatarviews/avatarview.coffee
+++ client/app/CommonViews/avatarviews/avatarview.coffee
@@ -28,14 +28,11 @@
     return unless account
     {profile} = account
     options = @getOptions()
-    host = "#{location.protocol}//#{location.host}/"
+    fallbackUri = "#{KD.apiUri}/images/defaultavatar/default.avatar.#{options.size.width}.png"
     # @$().attr "title", options.title or "#{Encoder.htmlDecode profile.firstName}'s avatar"

     # this is a temp fix to avoid avatar flashing on every account change - Sinan 08/2012
-    bgImg = unless profile.hash
-      "url(#{KD.apiUri}/images/defaultavatar/default.avatar.#{options.size.width}.png)"
-    else
-      "url(#{location.protocol}//gravatar.com/avatar/#{profile.hash}?size=#{options.size.width}&d=#{encodeURIComponent(host + 'images/defaultavatar/default.avatar.' + options.size.width + '.png')})"
+    bgImg = "url(//gravatar.com/avatar/#{profile.hash}?size=#{options.size.width}&d=#{encodeURIComponent fallbackUri})"

     if @bgImg isnt bgImg
       @$().css "background-image", bgImg


CEL: Remove destroy event in follow events list.

--- client/app/lib/providers/computeeventlistener.coffee
+++ client/app/lib/providers/computeeventlistener.coffee
@@ -190,7 +190,7 @@

       @addListener stateEvent, machine._id

-      if stateEvent in ["build", "destroy"] and followOthers
+      if stateEvent is 'build' and followOthers
         @addListener "reinit", machine._id

       return yes


Hide snapshots

Hide snapshots

More ux discussions are taking place. Hiding snapshots.

--- client/app/lib/providers/machinesettingsmodal.coffee
+++ client/app/lib/providers/machinesettingsmodal.coffee
@@ -24,7 +24,7 @@
   { title: 'Disk Usage',    viewClass: MachineSettingsDiskUsageView }
   { title: 'Domains',       viewClass: MachineSettingsDomainsView   }
   { title: 'VM Sharing',    viewClass: MachineSettingsVMSharingView }
-  { title: 'Snapshots',     viewClass: MachineSettingsSnapshotsView }
+  #{ title: 'Snapshots',     viewClass: MachineSettingsSnapshotsView }
   { title: 'Advanced',      viewClass: MachineSettingsAdvancedView  }
   { title: 'Common guides', viewClass: MachineSettingsGuidesView    }
 ]


GEN-2356 updated jsdoc.

GEN-2356 updated jsdoc.

--- client/app/lib/providers/machinesettingsmodal.coffee
+++ client/app/lib/providers/machinesettingsmodal.coffee
@@ -95,7 +95,7 @@
         disabledTabs = disabledTabsForProviders[machine.provider]
         isDisabled = disabledTabs.indexOf(item.title) > -1

-        # 'Advanced' tab should enable for managed VMs
+        # 'Advanced' tab should be enable for managed VMs
         isDisabled = no  if item.title is 'Advanced'

       @tabView.addPane pane = new KDTabPaneView


update onboarding link

update onboarding link

--- client/home/lib/flux/stores/welcomestepsstore.coffee
+++ client/home/lib/flux/stores/welcomestepsstore.coffee
@@ -37,7 +37,7 @@
           isDone: no
           order: 3
         inviteTeam :
-          path: '/Home/My-Team'
+          path: '/Home/My-Team/send-invites'
           title: 'Invite Your Team'
           actionTitle: 'Invite'
           videoLink: ''


Add temporary shortcut to recompile icon-config

--- .config/keymap.cson
+++ .config/keymap.cson
@@ -34,6 +34,7 @@
 	"shift-cmd-M":    "file-icons:toggle-changed-only"
 	"alt-shift-T":    "file-icons:toggle-tab-icons"
 	"alt-o":          "file-icons:debug-outlines"
+	"alt-c":          "file-icons:recompile-cache"
 	"cmd-k up":       "unset!"
 	"cmd-k right":    "unset!"
 	"cmd-k left":     "unset!"


removed heart icons

removed heart icons

--- scripts/karma.coffee
+++ scripts/karma.coffee
@@ -57,7 +57,7 @@
      'Yeah, Fuck'

   doubleIncrementResponse: ->
-     'http://www.www.sluniverse.com/pics/heart-icon.png http://www.www.sluniverse.com/pics/heart-icon.png'
+     '<3 <3'

   get: (thing) ->
     k = if @cache[thing] then @cache[thing] else 0


make SafePath.coffee shareable between client and server code

--- services/web/app/coffee/Features/Project/SafePath.coffee
+++ services/web/app/coffee/Features/Project/SafePath.coffee
@@ -1,36 +1,47 @@
-BADCHAR_RX = ///
-	[
-		\/ # no slashes
-		\* # no asterisk
-		\u0000-\u001F # no control characters (0-31)
-		\u007F        # no delete
-		\u0080-\u009F # no unicode control characters (C1)
-		\uD800-\uDFFF # no unicode surrogate characters
-	]
-	///g
+# This file is shared between the frontend and server code of web, so that
+# filename validation is the same in both implementations.
+# Both copies must be kept in sync:
+#   app/coffee/Features/Project/SafePath.coffee
+#   public/coffee/ide/directives/SafePath.coffee

-BADFILE_RX = ///
-	(^\.$)      # reject . as a filename
-	|	(^\.\.$)  # reject .. as a filename
-	| (^\s+)    # reject leading space
-	| (\s+$)    # reject trailing space
-	///g
+load = () ->
+	BADCHAR_RX = ///
+		[
+			\/ # no slashes
+			\* # no asterisk
+			\u0000-\u001F # no control characters (0-31)
+			\u007F        # no delete
+			\u0080-\u009F # no unicode control characters (C1)
+			\uD800-\uDFFF # no unicode surrogate characters
+		]
+		///g

-MAX_PATH = 1024 # Maximum path length, in characters. This is fairly arbitrary.
+	BADFILE_RX = ///
+		(^\.$)      # reject . as a filename
+		|	(^\.\.$)  # reject .. as a filename
+		| (^\s+)    # reject leading space
+		| (\s+$)    # reject trailing space
+		///g

-module.exports = SafePath =
+	MAX_PATH = 1024 # Maximum path length, in characters. This is fairly arbitrary.

-	clean: (filename) ->
-		filename = filename.replace BADCHAR_RX, '_'
-		# for BADFILE_RX replace any matches with an equal number of underscores
-		filename = filename.replace BADFILE_RX, (match) ->
-			return new Array(match.length + 1).join("_")
-		return filename
+	SafePath =
+		clean: (filename) ->
+			filename = filename.replace BADCHAR_RX, '_'
+			# for BADFILE_RX replace any matches with an equal number of underscores
+			filename = filename.replace BADFILE_RX, (match) ->
+				return new Array(match.length + 1).join("_")
+			return filename

-	isCleanFilename: (filename) ->
-		return SafePath.isAllowedLength(filename) &&
-			not filename.match(BADCHAR_RX) &&
-			not filename.match(BADFILE_RX)
+		isCleanFilename: (filename) ->
+			return SafePath.isAllowedLength(filename) &&
+				not filename.match(BADCHAR_RX) &&
+				not filename.match(BADFILE_RX)

-	isAllowedLength: (pathname) ->
-		return pathname.length > 0 && pathname.length <= MAX_PATH
+		isAllowedLength: (pathname) ->
+			return pathname.length > 0 && pathname.length <= MAX_PATH
+
+if define?
+	define [], load
+else
+	module.exports = load()


hear room check

hear room check

--- scripts/team1.coffee
+++ scripts/team1.coffee
@@ -28,6 +28,9 @@

 imageTiqav = (msg, drowUrl) ->
+	room = msg.message.user.room
+	if room == 'general'
+		return
 	msg.http('http://api.tiqav.com/search.json?q='+msg)
 		.query({
 			format: 'json'
@@ -45,6 +48,9 @@

 chatDialogue = (msg, num) =>
 	rnd = Math.floor(Math.random() * num) + 1
+	room = msg.message.user.room
+	if room == 'general'
+		return
 	if rnd < 2
 		message = msg.match[0]
 		msg.http('https://api.apigw.smt.docomo.ne.jp/dialogue/v1/dialogue')


more toggl junk

more toggl junk

--- scripts/toggl.coffee
+++ scripts/toggl.coffee
@@ -34,13 +34,12 @@
               results.error.errors.forEach (err) ->
                 resp += err.message
             else
-              item = results.data[results.data.length - 1]
-              resp += item.start + " - " + item.description
-              if item.duration < 0
-                resp += " - doing"
-              else
-                resp += " - " + item.duration + " sec"
-
-            resp += "\n"
+              results.data.forEach (item) ->
+                resp += item.start + " - " + item.description
+                if item.duration < 0
+                  resp += " - doing"
+                else
+                  resp += " - " + item.duration + " sec"
+                resp += "\n"

     msg.send resp


Update browsers statistics

Update browsers statistics


Fix left curly bracket not auto-complete

--- lib/autocomplete/citation.coffee
+++ lib/autocomplete/citation.coffee
@@ -15,9 +15,13 @@
     suggestions = []
     for item in items
       if prefix.length is 0 or item.key.indexOf(prefix) > -1
+        description = item.title
+        if item.author?
+          description += """ - #{item.author.split(' and ').join('; ')}"""
         suggestions.push
           text: item.key
           type: 'tag'
+          description: description
     return suggestions

   getBibFiles: ->


instantiate ABTests in inner.coffee

instantiate ABTests in inner.coffee

--- js/inner.coffee
+++ js/inner.coffee
@@ -1408,6 +1408,7 @@
 class CircleViewModel extends Base
   constructor: ->
     observableCount = 0
+    @ab = (new ABTests(ab_test_definitions)).ab_tests
     @current_user = ko.observable(new User window.renderContext.current_user)
     @build = ko.observable()
     @builds = ko.observableArray()


Fix library loading

Fix library loading

--- js/outer.coffee
+++ js/outer.coffee
@@ -68,10 +68,7 @@
   article = new Doc("article", "Sample template for an Article")

   # Per-Page Libs
-  home.lib = placeholder
-  about.lib = placeholder
-
-  article.lib = ->
+  highlight = ->
     if !hljs?
       $.getScript("/assets/js/vendor/highlight.pack.js", ->
         $("pre code").each (i, e) -> hljs.highlightBlock e
@@ -84,6 +81,10 @@
       $.getScript("/assets/js/vendor/jquery.placeholder.js", ->
         $("input, textarea").placeholder()
       )
+
+  home.lib = placeholder
+  about.lib = placeholder
+  article.lib = highlight

   # Google analytics
   @bind 'event-context-after', ->


adds viewmodel to outerjs so that we can get ab-tests in the view

--- js/outer.coffee
+++ js/outer.coffee
@@ -8,6 +8,12 @@

 window.queryParams = queryParams

+class CircleViewModel
+  constructor: ->
+    @ab = (new ABTests(ab_test_definitions)).ab_tests
+
+window.VM = new CircleViewModel
+
 circle = $.sammy "body", ->

   # Page
@@ -25,6 +31,8 @@

       # Fetch page-specific libraries
       @lib() if @lib?
+
+      ko.applyBindings(VM)

     render: (cx) =>
       $("body").attr("id","#{@name}-page").html HAML['header'](renderContext)


Tiny bug fix

Tiny bug fix

--- templates/example-aerix/javascript/invoice-model.coffee
+++ templates/example-aerix/javascript/invoice-model.coffee
@@ -13,7 +13,7 @@
   invoiceTotal: ->  @invoiceSubtotal() + @VAT()

   # VAT over the provided value or the invoice subtotal
-  VAT: (value)-> @get("vatPercentage") * (value || @invoiceSubtotal)
+  VAT: (value)-> @get("vatPercentage") * (value or @invoiceSubtotal() )

   validate: ->
     unless _.keys(@attributes).length > 0 then throw "Provided model has no attributes. "+
@@ -52,4 +52,4 @@
       if item.discount? and (item.discount < 0 or item.discount > 1)
         throw "Discount specified out of range (must be between 0 and 1)"
       if item.quantity? and item.quantity < 1
-        throw "When specified, quantity must be greater than one"+        throw "When specified, quantity must be greater than one"


DE21556:made some of the RPB catalog tests to be chrome only in prep for jenkins move

--- test/spec/roadmapplanningboard/PlanningBoardSpec.coffee
+++ test/spec/roadmapplanningboard/PlanningBoardSpec.coffee
@@ -209,11 +209,12 @@
         expect(refreshSpy).toHaveBeenCalledOnce()

     it 'should not refresh the backlog column if the deleted column did not have features', ->
-      @createCardboard(isAdmin: true).then =>
-        refreshSpy = @spy @cardboard.getColumns()[0], 'refresh'
-        @cardboard.getColumns()[1].planRecord.set('features', []);
-        @deleteColumn 1
-        expect(refreshSpy).not.toHaveBeenCalledOnce()
+      if !Ext.isGecko
+        @createCardboard(isAdmin: true).then =>
+          refreshSpy = @spy @cardboard.getColumns()[0], 'refresh'
+          @cardboard.getColumns()[1].planRecord.set('features', []);
+          @deleteColumn 1
+          expect(refreshSpy).not.toHaveBeenCalledOnce()

     describe 'deleting all of the columns', ->

@@ -225,7 +226,8 @@
         expect(@getTimeframePlanningColumns().length).toBe 1

       it 'should add a new empty timeframe column', ->
-        expect(@cardboard.getColumns()[1].planRecord.get('features')).toEqual []
+        if !Ext.isGecko
+          expect(@cardboard.getColumns()[1].planRecord.get('features')).toEqual []

     describe 'deleting newly added columns', ->

@@ -242,7 +244,8 @@
                     @deleteColumn(5)

       it 'should remove the new plans from the plan store', ->
-        expect(@planStore.count()).toBe 4
+        if !Ext.isGecko
+          expect(@planStore.count()).toBe 4

       describe 'when the remaining columns are deleted', ->


indentation fix

indentation fix

--- tests/batman/utilities/uri_test.coffee
+++ tests/batman/utilities/uri_test.coffee
@@ -59,9 +59,9 @@
     foo: ''
     bar: null
     baz: undefined
-	qux: []
-
-  equal query, "a+phrase=this+phrase&num=1&foo=&bar&baz&qux"
+    qux: []
+
+  equal query, "a+phrase=this+phrase&num=1&foo=&bar&baz&qux[]"

 # taken from Rack:
 #


showing profile modals on groups/hubs

showing profile modals on groups/hubs

--- app/assets/javascripts/darkswarm/controllers/group_enterprises_controller.js.coffee
+++ app/assets/javascripts/darkswarm/controllers/group_enterprises_controller.js.coffee
@@ -1,9 +1,10 @@
-Darkswarm.controller "GroupEnterprisesCtrl", ($scope, Search, FilterSelectorsService) ->
+Darkswarm.controller "GroupEnterprisesCtrl", ($scope, Search, FilterSelectorsService, EnterpriseModal) ->
   $scope.totalActive =  FilterSelectorsService.totalActive
   $scope.clearAll =  FilterSelectorsService.clearAll
   $scope.filterText =  FilterSelectorsService.filterText
   $scope.FilterSelectorsService =  FilterSelectorsService
   $scope.query = Search.search()
+  $scope.openModal = EnterpriseModal.open
   $scope.activeTaxons = []
   $scope.show_profiles = false
   $scope.filtersActive = false


Remove unneeded inject

Remove unneeded inject

--- app/modules/invite-members/suggest-add-members/suggest-add-members.directive.coffee
+++ app/modules/invite-members/suggest-add-members/suggest-add-members.directive.coffee
@@ -18,8 +18,6 @@
 ###

 SuggestAddMembersDirective = (lightboxService) ->
-    @.inject = ['lightboxService']
-
     return {
         scope: {},
         templateUrl:"invite-members/suggest-add-members/suggest-add-members.html",


fixbug when on linux

fixbug when on linux

--- index.coffee
+++ index.coffee
@@ -23,11 +23,12 @@
   template_prefix: "./template"
   locale_prefix: "./locale"

-parseLanguageCode = (language) ->
-  language = language.toLowerCase().replace '-', '_'
+parseLanguageCode = (original_language) ->
+  language = original_language.toLowerCase().replace '-', '_'
   [lang, country] = language.split '_'

   return {
+    original: original_language
     language: language
     lang: lang
     country: country
@@ -37,14 +38,14 @@
   options = _.extend default_options, options
   options.language_infos = _.map options.languages, parseLanguageCode

+  for language_info in options.language_infos
+    i18n_data[language_info.language] = require "#{options.locale_prefix}/#{language_info.original}.json"
+
   mailer = nodemailer.createTransport options.account

   i18n_data = {}
   template_cache = {}
   priority_cache = {}
-
-  for language_info in options.language_infos
-    i18n_data[language_info.language] = require "#{options.locale_prefix}/#{language_info.language}.json"

   is_found_default_language = _.find options.language_infos, (language_info) ->
     return language_info.language == parseLanguageCode(options.default_language).language


All plugins now using new loading system, including voxel-land and voxel-registry

Requires deathcap/voxel-registry@09d2f0b874f0c134e344cd62667f720149324e40
Temporarily disabled voxel-workbench crafting recipe

--- index.coffee
+++ index.coffee
@@ -90,28 +90,30 @@
   console.log 'initializing plugins'
   plugins = createPlugins game, {require: require}

-  registry = plugins.load 'registry', {} # TODO
-  registry.registerBlock 'grass', {texture: ['grass_top', 'dirt', 'grass_side'], hardness:5}
-  registry.registerBlock 'dirt', {texture: 'dirt', hardness:4}
-  registry.registerBlock 'stone', {texture: 'stone', hardness:90}
-  registry.registerBlock 'logOak', {texture: ['log_oak_top', 'log_oak_top', 'log_oak'], hardness:8}
-  registry.registerBlock 'cobblestone', {texture: 'cobblestone', hardness:90}
-  registry.registerBlock 'oreCoal', {texture: 'coal_ore'}
-  registry.registerBlock 'brick', {texture: 'brick'}
-  registry.registerBlock 'obsidian', {texture: 'obsidian', hardness: 900}
-  registry.registerBlock 'leavesOak', {texture: 'leaves_oak_opaque', hardness: 2}
-  registry.registerBlock 'glass', {texture: 'glass'}
-
-  registry.registerBlock 'plankOak', {texture: 'planks_oak'}
-  registry.registerBlock 'logBirch', {texture: ['log_birch_top', 'log_birch_top', 'log_birch'], hardness:8} # TODO: generate
-
-  registry.registerItem 'pickaxeWood', {itemTexture: '../items/wood_pickaxe', speed: 2.0} # TODO: fix path
-  registry.registerItem 'pickaxeDiamond', {itemTexture: '../items/diamond_pickaxe', speed: 10.0}
-  registry.registerItem 'stick', {itemTexture: '../items/stick'}
+  plugins.preload 'registry', {registerDefaults: (registry) ->
+    registry.registerBlock 'grass', {texture: ['grass_top', 'dirt', 'grass_side'], hardness:5}
+    registry.registerBlock 'dirt', {texture: 'dirt', hardness:4}
+    registry.registerBlock 'stone', {texture: 'stone', hardness:90}
+    registry.registerBlock 'logOak', {texture: ['log_oak_top', 'log_oak_top', 'log_oak'], hardness:8}
+    registry.registerBlock 'cobblestone', {texture: 'cobblestone', hardness:90}
+    registry.registerBlock 'oreCoal', {texture: 'coal_ore'}
+    registry.registerBlock 'brick', {texture: 'brick'}
+    registry.registerBlock 'obsidian', {texture: 'obsidian', hardness: 900}
+    registry.registerBlock 'leavesOak', {texture: 'leaves_oak_opaque', hardness: 2}
+    registry.registerBlock 'glass', {texture: 'glass'}
+
+    registry.registerBlock 'plankOak', {texture: 'planks_oak'}
+    registry.registerBlock 'logBirch', {texture: ['log_birch_top', 'log_birch_top', 'log_birch'], hardness:8} # TODO: generate
+
+    registry.registerItem 'pickaxeWood', {itemTexture: '../items/wood_pickaxe', speed: 2.0} # TODO: fix path
+    registry.registerItem 'pickaxeDiamond', {itemTexture: '../items/diamond_pickaxe', speed: 10.0}
+    registry.registerItem 'stick', {itemTexture: '../items/stick'}
+  }
+

   # recipes TODO: move to registry?
-  registry.recipes = RecipeLocator
-  registry.thesaurus = CraftingThesaurus
+  #registry.recipes = RecipeLocator
+  #registry.thesaurus = CraftingThesaurus
   CraftingThesaurus.registerName 'wood.log', new ItemPile('logOak')
   CraftingThesaurus.registerName 'wood.log', new ItemPile('logBirch')
   CraftingThesaurus.registerName 'wood.plank', new ItemPile('plankOak')
@@ -132,18 +134,9 @@

   playerInventory = new Inventory(10, 5)

-  plugins.preload 'workbench', {playerInventory:playerInventory}
-
-  plugins.preload 'land', {
-    populateTrees: true
-    materials: {  # TODO: refactor
-      grass: registry.getBlockID 'grass'
-      dirt: registry.getBlockID 'dirt'
-      stone: registry.getBlockID 'stone'
-      bark: registry.getBlockID 'logOak'
-      leaves: registry.getBlockID 'leavesOak'
-    }
-  }
+  plugins.preload 'workbench', {playerInventory:playerInventory, registerRecipe:false}  # TODO: re-enable recipes
+
+  plugins.preload 'land', {populateTrees: true}

   # note: preconfigure(), not preload(), so doesn't automatically enable
   plugins.preconfigure 'oculus', { distortion: 0.2, separation: 0.5 } # TODO: switch to voxel-oculus-vr? https://github.com/vladikoff/voxel-oculus-vr?source=c - closer matches threejs example
@@ -184,14 +177,14 @@
     timeToMine: (target) =>
       # the innate difficulty of mining this block
       blockID = game.getBlock(target.voxel)
-      blockName = registry.getBlockName(blockID)
-      hardness = registry.getBlockProps(blockName)?.hardness
+      blockName = plugins.all.registry?.getBlockName(blockID)
+      hardness = plugins.all.registry?.getBlockProps(blockName)?.hardness
       hardness ?= 9

       # effectiveness of currently held tool, shortens mining time
       heldItem = plugins.all['inventory-hotbar']?.held()
       speed = 1.0
-      speed = registry.getItemProps(heldItem?.item)?.speed ? 1.0
+      speed = plugins.all.registry?.getItemProps(heldItem?.item)?.speed ? 1.0
       finalTimeToMine = Math.max(hardness / speed, 0)
       # TODO: more complex mining 'classes', e.g. shovel against dirt, axe against wood

@@ -239,21 +232,20 @@
   ## plugins are loaded from here on out ##

-
   avatar = plugins.all.player
   avatar.pov('first');
   avatar.possess()
   home(avatar)

   # load textures after all plugins loaded (since they may add their own)
-  game.materials.load registry.getBlockPropsAll 'texture'
-  global.InventoryWindow_defaultGetTexture = (itemPile) => registry.getItemPileTexture(itemPile)
+  game.materials.load plugins.all.registry.getBlockPropsAll 'texture'
+  global.InventoryWindow_defaultGetTexture = (itemPile) => plugins.all.registry.getItemPileTexture(itemPile)

   plugins.disable 'fly'

   # one of everything, please..
   creativeInventoryArray = []
-  for props in registry.blockProps
+  for props in plugins.all.registry.blockProps
     creativeInventoryArray.push(new ItemPile(props.name, Infinity)) if props.name?

   survivalInventoryArray = []


sync_

--- index.coffee
+++ index.coffee
@@ -72,7 +72,6 @@
     ret.host = (if network.host is undefined then null else network.host)

   ret.name = network.name
-
   ret.nick = (if network.nick is undefined then randnick else network.nick)
   port = (if network.ssl is true then 6697 else 6667)
   ret.port = parseInt( if network.port is undefined then port else network.port )
@@ -81,15 +80,10 @@
   ret.username = (if network.username is undefined then ret.nick else network.username)
   ret.realname = (if network.realname is undefined then ret.nick else network.realname)
   ret.pass = network.pass
-
   ret.prefix = (if network.prefix is undefined then '!' else network.prefix)
-
   ret.channels = (if network.channels is undefined then ret.channels else network.channels)
-
   ret.throttling = network.throttling
-
   ret.sasl = (if network.sasl is undefined then null else networks.nickserv)
-
   ret

 Client::_useStream = (stream, network, throttling, info) ->


Use ENV for JWT secret

Use ENV for JWT secret

--- server/config.coffee
+++ server/config.coffee
@@ -1,2 +1 @@
-# Save secret in ENV
-module.exports = secret: 'supersecretkey'
+module.exports = secret: process.env.JWT_SECRET


Added some .pipe commands to client-scripts

--- gulp/client-scripts.coffee
+++ gulp/client-scripts.coffee
@@ -31,4 +31,17 @@
     ngAnnotate = require 'gulp-ng-annotate'

     # gather all coffeescript source files in /app/...
-    gulp.src ['app/**/*.coffee'], base: './app'+    gulp.src ['app/**/*.coffee'], base: './app'
+
+    # handle and prevent crashes
+    .pipe plumber()
+
+    # check for script mistakes
+    .pipe lint()
+    .pipe lint.reporter()
+
+    # compile coffescript source
+    .pipe sourcemaps.init()
+    .pipe coffee()
+
+    # TODO: add JS files to bundle


This line is no longer being used.

This line is no longer being used.

--- keypress.coffee
+++ keypress.coffee
@@ -46,7 +46,6 @@
 _keys_down = []
 _active_combos = []
 _prevent_capture = false
-_event_classname = "keypress_events"
 _metakey = "ctrl"
 _modifier_keys = ["meta", "alt", "option", "ctrl", "shift", "cmd"]
 _valid_keys = []


Tyring for IE8 support

Tyring for IE8 support

Still learning coffeescript.
This seems to compile and work so committing the stuff.
Added Array.prototype.filter support in case of using old Javascript edition like IE8 does.

--- keypress.coffee
+++ keypress.coffee
@@ -78,11 +78,8 @@
     # one, we should prevent the default keydown event.
     if (should_prevent or keypress.suppress_event_defaults) and not keypress.force_event_defaults

-        if e.preventDefault
-    	    e.preventDefault()
-		else
-		    e.returnValue = false
-
+        if e.preventDefault then e.preventDefault() else e.returnValue = false
+
         if e.stopPropagation
             e.stopPropagation()


Add transition support to backdrop-filter

--- data/prefixes.coffee
+++ data/prefixes.coffee
@@ -129,7 +129,8 @@
 # Backdrop-filter
 feature require('caniuse-db/features-json/css-backdrop-filter'), (browsers) ->
   prefix 'backdrop-filter',
-          browsers: browsers
+          transition: true
+          browsers:   browsers

 # element() function
 feature require('caniuse-db/features-json/css-element-function'), (browsers) ->


Workspace: Added getActivePane method to TabbedEditor.

--- client/landing/app/CommonViews/workspace/panes/collaborativetabbededitorpane.coffee
+++ client/landing/app/CommonViews/workspace/panes/collaborativetabbededitorpane.coffee
@@ -37,6 +37,9 @@
   getActivePaneFileData: ->
     return @tabView.getActivePane().subViews[0].getData()

+  getActivePane: ->
+    return @tabView.getActivePane()
+
   createEditorTabs: ->
     @tabHandleContainer = new ApplicationTabHandleHolder
       delegate          : @
@@ -51,7 +54,7 @@
     @tabView.on "PaneAdded", (pane) =>
       {tabHandle} = pane
       tabHandle.on "click", =>
-        activeTab = @tabView.getActivePane()
+        activeTab = @getActivePane()
         newIndex  = @tabView.getPaneIndex activeTab
         return  if newIndex is @activeTabIndex


更改超时时间

更改超时时间

--- honey-preview/index.coffee
+++ honey-preview/index.coffee
@@ -66,7 +66,7 @@
     method: 'POST'
     json: true
     formData: formData
-    timeout: 1000 * 5
+    timeout: 1000 * 60 * 30

   _request options, (err, res, body)->
     console.log JSON.stringify(err).red if err


Add /api/reprocess-article-date-range endpoint

--- server/router.coffee
+++ server/router.coffee
@@ -570,3 +570,34 @@
         result.locations = countryCodeToCount
       return result
   }))
+
+
+###
+@api {post} reprocess-article-date-range
+@apiParam {ISODateString} startDate
+@apiParam {ISODateString} endDate
+###
+Router.route("/api/reprocess-article-date-range", where: "server")
+.get ->
+  console.log("reprocessing articles...")
+  articles = Articles.find({
+    $and: [
+      publishDate: $gt: new Date(@request.query.startDate)
+    ,
+      publishDate: $lt: new Date(@request.query.endDate)
+    ]
+  }).fetch()
+  if articles.length > 1000
+    return @response.end("Too many articles to process.")
+  articles.forEach (article)->
+    Meteor.call('getArticleEnhancementsAndUpdate', article._id, {
+      hideLogs: true
+      priority: false
+      reprocess: true
+    }, (error)->
+      if error
+        console.log article
+        console.log error
+    )
+  @response.statusCode = 200
+  @response.end("Processing #{articles.length} articles...")


Debug output

Debug output

--- server/server.coffee
+++ server/server.coffee
@@ -19,7 +19,7 @@
     logger.error r

 requireHTTPS = (req, res, next) ->
-    logger.info "secure=", req.secure, req.protocol
+    logger.info "secure=", req.secure, req.protocol, req.headers.host, req.url
     if !req.secure
         return res.redirect 'https://' + req.headers.host + req.url
     next()


Fix for vhost bug when running local.

Fix for vhost bug when running local.

--- server/server.coffee
+++ server/server.coffee
@@ -38,7 +38,7 @@
 }

 kiteBroker =\
-  if kites.vhost?
+  if kites?.vhost?
     new Broker extend {}, mq, vhost: kites.vhost
   else
     koding.mq


Remove spurious blank lines

Remove spurious blank lines

--- libraries/metrics/metrics.coffee
+++ libraries/metrics/metrics.coffee
@@ -20,8 +20,6 @@
 destructors = []

 require "./uv_threadpool_size"
-
-

 module.exports = Metrics =
 	initialize: (_name) ->


Fix erroneous if statements

Fix erroneous if statements

--- softremovable.coffee
+++ softremovable.coffee
@@ -140,7 +140,7 @@
   @collection.softRemove = (selector, callback) ->
     return 0 unless selector

-    if Meteor.isClient and not Meteor.isSimulation and not isLocalCollection
+    if Meteor.isClient and (not Meteor.isSimulation or not isLocalCollection)
       throwIfSelectorIsntId selector, 'softRemove'

     modifier =
@@ -162,7 +162,7 @@
   @collection.restore = (selector, callback) ->
     return 0 unless selector

-    if Meteor.isClient and not Meteor.isSimulation and not isLocalCollection
+    if Meteor.isClient and (not Meteor.isSimulation or not isLocalCollection)
       throwIfSelectorIsntId selector, 'restore'

     modifier =


Add spec for past player position

Add spec for past player position

--- spec/gamespec.coffee
+++ spec/gamespec.coffee
@@ -65,7 +65,18 @@
         expect(e.message).toEqual('IllegalTurnException') # TODO: Make constant

-    #it "accurately reports the position of a player at any time in the past" ->
+    it "accurately reports the position of a player at any time in the past", ->
+      pos = game.player0.currentPosition(0)
+      expect([pos.x, pos.y]).toEqual([Game.WIDTH / 2, 0])
+      pos = game.player0.currentPosition(timeToTraverse / 2)
+      expect([pos.x, pos.y]).toEqual([Game.WIDTH / 2, Game.HEIGHT / 2])
+
+      SynchronizedTime.setTimeForTesting(timeToTraverse / 2)
+      game.handle_input(0, KEY_WEST)
+      pos = game.player0.currentPosition(timeToTraverse * .75)
+      expect([pos.x, pos.y]).toEqual([Game.WIDTH *.25, Game.HEIGHT * .5])
+
+


tests: Fix FBP protocol before_all not reentrant

Had to manually delete the component dir

--- spec/protocol.coffee
+++ spec/protocol.coffee
@@ -4,9 +4,23 @@
 websocket = require 'websocket'
 fbp = require 'fbp'
 fs = require 'fs'
+path = require 'path'

 participants = require './fixtures/participants'
 Runtime = require('../src/runtime').Runtime
+
+rmrf = (dir) ->
+  return if not fs.existsSync dir
+
+  for f in fs.readdirSync dir
+    f = path.join dir, f
+    try
+      fs.unlinkSync f
+    catch e
+      if e.code == 'EISDIR'
+        rmrf f
+      else
+        throw e

 class MockUi extends EventEmitter
   constructor: ->
@@ -55,6 +69,7 @@
     componentdir: 'spec/protocoltemp'

   before (done) ->
+    rmrf options.componentdir
     fs.rmdirSync options.componentdir if fs.existsSync options.componentdir
     fs.mkdirSync options.componentdir
     runtime = new Runtime options


tweet

tweet

--- src/Landscape.coffee
+++ src/Landscape.coffee
@@ -255,13 +255,25 @@
         window.laggydash.send({ action: 'jump' })

   gameOver:=>
-    message = "You died."
+    names = ("@#{player.avatar.name}" for id, player of @players)
+    names = _.uniq(names)
+    chasers = switch names.length
+      when 0
+        "nobody at all"
+      when 1
+        names[0]
+      when 2
+        "#{names[0]} and #{names[1]}"
+      else
+        "#{names[0]}, #{names[1]} and others"
+    message = "I ate a bomb in #laggydash after collecting #{@score} coin while being chased by #{chasers}!"
     options =
       labels:
         ok: "Tweet It"
         cancel: "Play Again"
     alertify.set(options)
-    alertify.prompt message, (event, input) =>
+    alertify.confirm message, (event, input) =>
+      window.laggydash.tweet(message, names)
       @restart = true

   bang:(player, x, y)=>


Autocomplete snippets now are capture operator aware

--- lib/elixir-autocomplete-provider.coffee
+++ lib/elixir-autocomplete-provider.coffee
@@ -40,6 +40,7 @@
     textBeforeCursor = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition])
     prefix = getPrefix(textBeforeCursor)
     pipeBefore = !!textBeforeCursor.match(///\|>\s*#{prefix}$///)
+    captureBefore = !!textBeforeCursor.match(///&#{prefix}$///)

     return if !activatedManually && prefix == ""

@@ -73,7 +74,7 @@
           name = fields[0]
           if lastModuleHint && (lastModuleHint not in [name, ":#{name}"]) && modules_to_add.length > 0
             fields[0] = modules_to_add.join('.') + '.' + name
-          createSuggestion(serverSuggestion, fields, prefix, pipeBefore)
+          createSuggestion(serverSuggestion, fields, prefix, pipeBefore, captureBefore)

         suggestions = sortSuggestions(suggestions)
         resolve(suggestions)
@@ -82,7 +83,7 @@
     regex = /[\w0-9\._!\?\:@]+$/
     textBeforeCursor.match(regex)?[0] or ''

-  createSuggestion = (serverSuggestion, fields, prefix, pipeBefore) ->
+  createSuggestion = (serverSuggestion, fields, prefix, pipeBefore, captureBefore) ->
     if fields[1] == 'module'
       [name, kind, subtype, desc] = fields
     else
@@ -96,15 +97,15 @@
       when 'var'
         createSuggestionForVariable(name)
       when 'private_function'
-        createSuggestionForFunction(serverSuggestion, name, kind, signature, "", desc, spec, prefix, pipeBefore)
+        createSuggestionForFunction(serverSuggestion, name, kind, signature, "", desc, spec, prefix, pipeBefore, captureBefore)
       when 'public_function'
-        createSuggestionForFunction(serverSuggestion, name, kind, signature, "", desc, spec, prefix, pipeBefore)
+        createSuggestionForFunction(serverSuggestion, name, kind, signature, "", desc, spec, prefix, pipeBefore, captureBefore)
       when 'function'
-        createSuggestionForFunction(serverSuggestion, name, kind, signature, mod, desc, spec, prefix, pipeBefore)
+        createSuggestionForFunction(serverSuggestion, name, kind, signature, mod, desc, spec, prefix, pipeBefore, captureBefore)
       when 'public_macro'
-        createSuggestionForFunction(serverSuggestion, name, kind, signature, "", desc, spec, prefix, pipeBefore)
+        createSuggestionForFunction(serverSuggestion, name, kind, signature, "", desc, spec, prefix, pipeBefore, captureBefore)
       when 'macro'
-        createSuggestionForFunction(serverSuggestion, name, kind, signature, mod, desc, spec, prefix, pipeBefore)
+        createSuggestionForFunction(serverSuggestion, name, kind, signature, mod, desc, spec, prefix, pipeBefore, captureBefore)
       when 'module'
         createSuggestionForModule(serverSuggestion, name, desc, prefix, subtype)
       else
@@ -139,7 +140,7 @@
       rightLabel: 'variable'
     }

-  createSuggestionForFunction = (serverSuggestion, name, kind, signature, mod, desc, spec, prefix, pipeBefore) ->
+  createSuggestionForFunction = (serverSuggestion, name, kind, signature, mod, desc, spec, prefix, pipeBefore, captureBefore) ->
     args = signature.split(',')
     [func, arity] = name.split('/')
     [moduleParts..., postfix] = prefix.split('.')
@@ -159,7 +160,9 @@
     snippet_params = params
     snippet_params = snippet_params[1...] if snippet_params.length > 0 && pipeBefore

-    if snippet_params.length > 0
+    if captureBefore
+      snippet = "#{func}/#{arity}"
+    else if snippet_params.length > 0
       snippet = "#{func}(#{snippet_params.join(', ')})"

     snippet = snippet.replace(/^:/, '') + "$0"


Increase the number of tries to connect to the server, and the time in-between them.

--- src/WebSocket.coffee
+++ src/WebSocket.coffee
@@ -17,21 +17,22 @@
     connection = null
     send = null
     before (done) ->
-      tries = 5
+      tries = 10
       startServer ->
         client = new WebSocketClient
         client.on 'connect', (conn) ->
+          console.log "connected!"
           connection = conn
           done()
         client.on 'connectFailed', (err) ->
           tries--
           if tries == 0
-            console.log "failed to connect to runtime after 5 tries"
+            console.log "failed to connect to runtime after 10 tries"
             done(err)
           setTimeout(
             ->
               client.connect address, 'noflo'
-            100
+            200
           )
         console.log "connecting to", address
         client.connect address, 'noflo'


Cleanup

Cleanup

--- src/aggregate.coffee
+++ src/aggregate.coffee
@@ -8,12 +8,10 @@

   constructor: (name, definition) ->
     @_name              = name
-    @_props             = {}
+    @_definition        = definition
+    @_root              = new @_definition.root
     @_propsChanged      = {}
     @_domainEvents      = []
-    @_trackPropsChanged = true
-    @_definition        = definition
-    @_root              = new @_definition.root

     @_observerOpen()

@@ -58,15 +56,16 @@

   generateDomainEvent: (eventName, params={}) ->
+    @_observer.deliver()
+
     eventParams =
       name: eventName
       aggregate:
         id: @id
         name: @_name

-    changes = @_getChanges()
-    if Object.keys(changes).length > 0
-      eventParams.aggregate.changed = changes
+    if Object.keys(@_propsChanged).length > 0
+      eventParams.aggregate.changed = @_propsChanged

     entityMap = @_getEntityMap()
     if Object.keys(entityMap).length > 0
@@ -74,16 +73,6 @@

     domainEvent = new DomainEvent eventParams
     @_domainEvents.push domainEvent
-
-
-  _getChanges: ->
-    @_observer.deliver()
-
-    changes = {}
-    if Object.keys(@_propsChanged).length > 0
-      changes = @_propsChanged
-
-    changes

   _getEntityMap: ->


Direct message cleanup.

Direct message cleanup.

--- lib/User.coffee
+++ lib/User.coffee
@@ -104,24 +104,22 @@
     unless @enableDirectMessages
       error = new ChatServiceError 'notAllowed'
       return Promise.reject error
-    recipient = null
-    channel = null
     @processMessage msg, true
     @server.state.getUser recipientName
     .then (user) =>
       recipient = user
       channel = recipient.echoChannel
       recipient.message @userName, msg
-    .then ->
-      recipient.userState.getAllSockets() #TODO
-    .then (recipientSockets) =>
-      unless recipientSockets?.length
-        error = new ChatServiceError 'noUserOnline', recipient.userName
-        return Promise.reject error
-      @transport.sendToChannel channel, 'directMessage', msg
-      @transport.sendToOthers id, @echoChannel, 'directMessageEcho'
+      .then ->
+        recipient.userState.getAllSockets()
+      .then (recipientSockets) =>
+        unless recipientSockets?.length
+          error = new ChatServiceError 'noUserOnline', recipient.userName
+          return Promise.reject error
+        @transport.sendToChannel channel, 'directMessage', msg
+        @transport.sendToOthers id, @echoChannel, 'directMessageEcho'
         , recipientName, msg
-      Promise.resolve msg
+        Promise.resolve msg

   # @private
   directRemoveFromList : (listName, values) ->


make event accessible via vimState while running command

--- lib/base.coffee
+++ lib/base.coffee
@@ -253,10 +253,11 @@
       null

   @registerCommand: ->
-    atom.commands.add(@getCommandScope(), @getCommandName(), => @run())
-
-  @run: ->
+    atom.commands.add(@getCommandScope(), @getCommandName(), (event) => @run(event))
+
+  @run: (event) ->
     if vimState = getEditorState(atom.workspace.getActiveTextEditor())
+      vimState.domEvent = event
       # Reason: https://github.com/t9md/atom-vim-mode-plus/issues/85
       vimState.operationStack.run(this)


fix bug when there is no filename (e.g. new file)

--- lib/init.coffee
+++ lib/init.coffee
@@ -305,6 +305,8 @@

     currentFile = filename;

+    if !filename
+        return

     if debug
         safeToRun = editor.buffer.getText().match(/\/\/ safe to run/)


Modified warnings and infos Added unused_a to warnings Moved todo_comment to infos

--- lib/main.coffee
+++ lib/main.coffee
@@ -49,10 +49,11 @@
     helpers = require('atom-linter')
     #reporter = require('jshint-json') # a string path
     warnings = [
-      'todo_comment'
+      'unused_a',
     ]
     infos = [
       'nested_comment'
+      'todo_comment'
       'too_long'
     ]


Fix detect SVG editor

Fix detect SVG editor

Fixes #27 and #28

--- lib/main.coffee
+++ lib/main.coffee
@@ -81,12 +81,11 @@
     @addPreviewForEditor item

   isSvgEditor: (item) ->
-    grammars = atom.config.get('svg-preview.grammars') ? []
+    grammars = ['text.xml.svg'].concat(atom.config.get('svg-preview.grammars') ? [])
     grammar = item?.getGrammar?()?.scopeName

     return (
-      ( item?.getBuffer? and item?.getText? and item?.getGrammar?)
-      grammar is 'text.xml.svg' or
+      ( item?.getBuffer? and item?.getText? ) and
       ( grammar in grammars and item.getText().match(/<svg/) )
     )

@@ -121,8 +120,12 @@
     options =
       searchAllPanes: true
       activatePane: false
+
     if atom.config.get('svg-preview.openPreviewInSplitPane')
       options.split = 'right'
+
+    console.log 'uri', uri
+
     atom.workspace.open(uri, options).then (svgPreviewView) ->
       if isSvgPreviewView(svgPreviewView)
         previousActivePane.activate()


fix typo

fix typo

s/probbably/probably

--- lib/main.coffee
+++ lib/main.coffee
@@ -32,7 +32,7 @@
                           language/grammar. This value is updated automatically
                           by the "switch kernel" command. If you switch from
                           python2 to python3, python3 will be used the next time
-                          you open a Python file. You probabbly shouldn\'t
+                          you open a Python file. You probably shouldn\'t
                           change this by hand.'
             type: 'string'
             default: '{}'


Remove references to PEP8 (#205)

Remove references to PEP8 (#205)

--- lib/main.coffee
+++ lib/main.coffee
@@ -154,8 +154,8 @@
       default: []
       items:
         type: 'string'
-    pep8ErrorsToWarnings:
-      description: 'Convert PEP8 "E" messages to linter warnings'
+    pep8ErrorsToWarnings: # FIXME: Rename to pycodestyle in major release
+      description: 'Convert pycodestyle "E" messages to linter warnings'
       type: 'boolean'
       default: false
     flakeErrors:
@@ -216,7 +216,7 @@
         parameters.push('-')

         fs = require('fs-plus')
-        pep8warn = atom.config.get('linter-flake8.pep8ErrorsToWarnings')
+        pycodestyleWarn = atom.config.get('linter-flake8.pep8ErrorsToWarnings')
         flakeerr = atom.config.get('linter-flake8.flakeErrors')
         projDir = @getProjDir(filePath) or path.dirname(filePath)
         execPath = fs.normalize(@applySubstitutions(atom.config.get('linter-flake8.executablePath'), projDir))
@@ -234,7 +234,7 @@
             line = parseInt(match[1]) or 0
             col = parseInt(match[2]) or 0
             toReturn.push({
-              type: if match[4] is 'E' and not pep8warn or match[4] is 'F' and flakeerr then 'Error' else 'Warning'
+              type: if match[4] is 'E' and not pycodestyleWarn or match[4] is 'F' and flakeerr then 'Error' else 'Warning'
               text: match[3] + ' — ' + match[5]
               filePath
               range: extractRange({


Fix linting for inline JavaScript

Fix linting for inline JavaScript

This closest https://github.com/AtomLinter/linter-jshint/issues/20 and https://github.com/AtomLinter/linter-jshint/issues/21

--- lib/main.coffee
+++ lib/main.coffee
@@ -16,7 +16,8 @@
     else
       jshintPath = require('path').join(__dirname, '..', 'node_modules', '.bin', 'jshint')
     scopes = ['source.js', 'source.js.jsx']
-    if atom.config.get('linter-jshint.lintInlineJavaScript') is true
+    lintInlineJavaScript = atom.config.get('linter-jshint.lintInlineJavaScript')
+    if lintInlineJavaScript is true
       scopes.push('source.js.embedded.html')
     helpers = require('atom-linter')
     reporter = require('jshint-json') # a string path
@@ -28,7 +29,9 @@
         exePath = atom.config.get('linter-jshint.jshintExecutablePath') || jshintPath
         filePath = textEditor.getPath()
         text = textEditor.getText()
-        parameters = ['--reporter', reporter, '--extract', 'auto', '--filename', filePath, '-']
+        parameters = ['--reporter', reporter, '--filename', filePath, '-']
+        if lintInlineJavaScript is true and ~textEditor.getGrammar().scopeName.indexOf 'text.html'
+          parameters.splice(2, 0, '--extract', 'always')
         return helpers.exec(exePath, parameters, {stdin: text}).then (output) ->
           try
             output = JSON.parse(output).result


remove util.coffee because everything is switched to attackLogger.coffee

--- lib/util.coffee
+++ lib/util.coffee
@@ -1,22 +0,0 @@
-config = require './config'
-fs = require 'fs'
-
-module.exports =
-  logHTTP: (detective, req) ->
-    olog = fs.createWriteStream config.httplog, flags: 'a'
-    olog.write '[- ATTACK DETAILS FOR ' + Date.now + ' -]\r\n'
-    olog.write ' --> Detective: ' + detective + '\r\n'
-    olog.write ' --> Request: ' + req.method + ' ' + req.url + '\r\n'
-    olog.write ' --> IP: ' + req.connection.remoteAddress + '\r\n'
-    olog.write '[- END ATTACK DETAILS -]\r\n\r\n'
-    olog.end()
-
-  logSocket: (module, socket, msg) ->
-    olog = fs.createWriteStream config.socketlog, flags: 'a'
-    olog.write '[- ATTACK DETAILS FOR ' + new Date() + ' -]\r\n'
-    olog.write ' --> Detective: ' + module + '\r\n'
-    olog.write ' --> Socket Message: ' + msg + '\r\n'
-    olog.write ' --> IP: ' + socket.remoteAddress + '\r\n'
-    olog.write '[- END ATTACK DETAILS -]\r\n\r\n'
-    olog.end()
-


un-f editor stats spec

un-f editor stats spec

--- src/packages/editor-stats/spec/editor-stats-spec.coffee
+++ src/packages/editor-stats/spec/editor-stats-spec.coffee
@@ -2,7 +2,7 @@
 RootView = require 'root-view'
 EditorStats = require 'editor-stats/src/editor-stats-view'

-fdescribe "EditorStats", ->
+describe "EditorStats", ->
   [rootView, editorStats, editor, date, time] = []

   simulateKeyUp = (key) ->


DevTools: saveAll implemented

DevTools: saveAll implemented

--- client/DevTools/AppView.coffee
+++ client/DevTools/AppView.coffee
@@ -36,7 +36,6 @@

     toggleFullscreen.on "viewAppended", ->
       toggleFullscreen.parent.setClass "default"
-

   viewAppended:->

@@ -142,14 +141,14 @@
             _.debounce (@lazyBound 'previewApp', no), 500

           JSEditor.on "RunRequested", @lazyBound 'previewApp', yes
+          JSEditor.on "SaveAllRequested", @bound 'saveAll'
           JSEditor.on "AutoRunRequested", @bound 'toggleLiveReload'
           JSEditor.on "FocusedOnMe", => @_lastActiveEditor = JSEditor
           JSEditor.on "RecentFileLoaded", => @switchMode 'develop'

+          @on 'saveAllMenuItemClicked', @bound 'saveAll'
           @on 'saveMenuItemClicked', =>
             @_lastActiveEditor?.handleSave()
-          @on 'saveAllMenuItemClicked', =>
-            CSSEditor.handleSave(); JSEditor.handleSave()
           @on 'closeAllMenuItemClicked', =>
             delete @_lastActiveEditor
             @switchMode 'home'
@@ -166,6 +165,7 @@
             _.debounce (@lazyBound 'previewCss', no), 500

           CSSEditor.on "RunRequested", @lazyBound 'previewCss', yes
+          CSSEditor.on "SaveAllRequested", @bound 'saveAll'
           CSSEditor.on "AutoRunRequested", @bound 'toggleLiveReload'
           CSSEditor.on "FocusedOnMe", => @_lastActiveEditor = CSSEditor

@@ -229,8 +229,6 @@
         @_inprogress = no
         KD.utils.wait 700, -> PreviewPane.info.unsetClass 'in'

-
-
   previewCss:(force = no)->

     return  if not force and not @liveMode
@@ -310,6 +308,12 @@
       else
         @welcomePage.setClass 'out'
         KD.utils.wait 500, @welcomePage.bound 'hide'
+
+  saveAll:->
+
+    {JSEditor, CSSEditor} = @workspace.activePanel.panesByName
+    CSSEditor.handleSave(); JSEditor.handleSave()
+

 class DevToolsEditorPane extends CollaborativeEditorPane


FSItem: update rename and move, in theory they are same.

--- client/Finder/fs/fsitem.coffee
+++ client/Finder/fs/fsitem.coffee
@@ -232,21 +232,27 @@
     .then =>
       @emit "fs.delete.finished"

-  rename: ({ name: newName, path: newPath }, callback)->
-
-    newPath ?= FSHelper.plainPath "#{@parentPath}/#{newName}"
-
-    @emit "fs.job.started"
+  # Move file to provided path, its using ::rename
+  move: (path, callback)->
+
+    newName = FSHelper.plainPath "#{ path }/#{ @name }"
+    @rename path: newName, callback
+
+  rename: (newName, callback)->
+
+    @emit "fs.job.started"
+
+    newPath = "#{FSHelper.getParentPath @getPath()}/#{newName}"

     kite = @getKite()

     kite.vmOn()

     .then =>
-      kite.fsRename(
-        oldpath: @getPath()
-        newpath: newPath
-      )
+
+      kite.fsRename
+        oldpath : @getPath()
+        newpath : newPath

     .nodeify(callback)


 Merge branch 'master.fixunloadproblem' into 'master'

Master.Fixunloadproblem

--- client/Terminal/AppView.coffee
+++ client/Terminal/AppView.coffee
@@ -229,6 +229,8 @@
     path = location.pathname + location.search + "?"
     mainController = KD.getSingleton("mainController")
     mainController.on "accountChanged.to.loggedIn", =>
+      wc = KD.singleton 'windowController'
+      wc.clearUnloadListeners()
       location.replace path


terminal: hide message pane after clicked a new tab link

--- client/Terminal/AppView.coffee
+++ client/Terminal/AppView.coffee
@@ -70,6 +70,7 @@
           KD.singleton('appManager').quitByName 'Terminal'
         else if $(event.target).hasClass 'plus'
           @addNewTab()
+          @messagePane.hide()

   fetchStorage: (callback) ->
     storage = KD.getSingleton('appStorageController').storage 'Terminal', '1.0.1'


Prenvet errors on method refreshUnreadMark

--- client/lib/readMessages.coffee
+++ client/lib/readMessages.coffee
@@ -40,6 +40,8 @@
 		rid ?= Session.get 'openedRoom'
 		if rid?
 			subscription = ChatSubscription.findOne rid: rid
+			if not subscription? then return
+
 			room = RoomManager.openedRooms[subscription.t + subscription.name]
 			if room?
 				$roomDom = $(room.dom)


Fix character API permission checks

Fix character API permission checks

--- api/tools/gw2api.coffee
+++ api/tools/gw2api.coffee
@@ -188,11 +188,11 @@
     getAccount: (cb) ->
         requestWithCache 'account', ['account'], cb
     getCharacters: (cb) ->
-        requestWithCache 'characters?page=0', [], cb
+        requestWithCache 'characters?page=0', ['account', 'characters'], cb
     getCharacter: (name, cb) ->
         # Assuming the cache database isn't tampered with, these should always be available.
         name = encodeURIComponent name
-        requestWithCache "characters/#{name}", [], cb
+        requestWithCache "characters/#{name}", ['account', 'characters'], cb
     ErrorCode: ErrorCode
     fatalAPIErrorDefaultResponse: (err, res) ->
         if err.apiError?


don't subtract scroll offsets

don't subtract scroll offsets

this fixes mouse events on FF.

--- lib/assets/javascripts/jax/input/mouse.js.coffee
+++ lib/assets/javascripts/jax/input/mouse.js.coffee
@@ -58,10 +58,11 @@
     evt =
       base: evt
       button: evt.button
-      x: evt.clientX - rect.left - root.scrollLeft
-      y: evt.clientY - rect.top  - root.scrollTop
+      x: evt.clientX - rect.left
+      y: evt.clientY - rect.top
     evt.x *= @receiver.width / rect.width
     evt.y *= @receiver.height/ rect.height
+    console.log evt
     if @_lastx is undefined
       evt.diffx = evt.diffy = 0
     else


added event type mousewheel

added event type mousewheel

--- lib/assets/javascripts/jax/input/mouse.js.coffee
+++ lib/assets/javascripts/jax/input/mouse.js.coffee
@@ -4,6 +4,7 @@
     release: 'mouseup'
     move:    'mousemove'
     over:    'mouseover'
+    wheel:   'mousewheel'
     exit:    'mouseout'

   ###
@@ -100,7 +101,7 @@
         super 'over'
         super 'exit'
         @addEventListener 'enter', callback if callback
-      when 'move', 'click'
+      when 'move', 'click', 'wheel'
         super 'move'
         super 'press'
         super 'release'
@@ -139,7 +140,10 @@
     unless @_entered
       @_entered = true
       @fireEvent 'enter', e
-
+
+  wheel: (e) ->
+    @fireEvent 'wheel', e
+
   exit: (e) ->
     @_entered = false
     # when mouse leaves canvas, stop 'dragging'